1use crate::{CoapError, CoapResult};
30
31pub const DEFAULT_SZX: u8 = 5;
36
37pub const MAX_SZX: u8 = 6;
39
40#[derive(Debug, Clone, Copy, PartialEq, Eq)]
48pub struct BlockOption {
49 pub num: u32,
51 pub more: bool,
53 pub szx: u8,
55}
56
57impl BlockOption {
58 pub fn decode(value: u32) -> Self {
64 let szx = (value & 0x07) as u8;
65 let more = (value & 0x08) != 0;
66 let num = value >> 4;
67 Self { num, more, szx }
68 }
69
70 pub fn encode(&self) -> u32 {
74 let mut value = self.num << 4;
75 if self.more {
76 value |= 0x08;
77 }
78 value |= (self.szx as u32) & 0x07;
79 value
80 }
81
82 pub fn block_size(&self) -> usize {
84 block_size_from_szx(self.szx)
85 }
86
87 pub fn offset(&self) -> usize {
89 self.num as usize * self.block_size()
90 }
91}
92
93pub fn block_size_from_szx(szx: u8) -> usize {
98 let clamped = szx.min(MAX_SZX);
99 1 << (clamped as usize + 4)
100}
101
102pub fn szx_from_block_size(size: usize) -> Option<u8> {
107 match size {
108 16 => Some(0),
109 32 => Some(1),
110 64 => Some(2),
111 128 => Some(3),
112 256 => Some(4),
113 512 => Some(5),
114 1024 => Some(6),
115 _ => None,
116 }
117}
118
119#[derive(Debug)]
125pub struct BlockAssembler {
126 buffer: Vec<u8>,
128 next_num: u32,
130 szx: u8,
132 max_payload: usize,
134 complete: bool,
136}
137
138impl BlockAssembler {
139 pub fn new(max_payload: usize) -> Self {
146 Self {
147 buffer: Vec::new(),
148 next_num: 0,
149 szx: DEFAULT_SZX,
150 max_payload,
151 complete: false,
152 }
153 }
154
155 pub fn process_block(&mut self, block: &BlockOption, data: &[u8]) -> CoapResult<bool> {
163 if self.complete {
164 return Err(CoapError::BlockTransferError(
165 "Transfer already complete".to_string(),
166 ));
167 }
168
169 if block.num != self.next_num {
170 return Err(CoapError::BlockTransferError(format!(
171 "Expected block {}, received block {}",
172 self.next_num, block.num
173 )));
174 }
175
176 if block.num == 0 {
178 self.szx = block.szx;
179 }
180
181 let new_size = self.buffer.len() + data.len();
182 if new_size > self.max_payload {
183 return Err(CoapError::PayloadTooLarge {
184 size: new_size,
185 max: self.max_payload,
186 });
187 }
188
189 self.buffer.extend_from_slice(data);
190 self.next_num = block.num + 1;
191
192 if !block.more {
193 self.complete = true;
194 }
195
196 Ok(self.complete)
197 }
198
199 pub fn payload(&self) -> Option<&[u8]> {
201 if self.complete {
202 Some(&self.buffer)
203 } else {
204 None
205 }
206 }
207
208 pub fn into_payload(self) -> CoapResult<Vec<u8>> {
212 if self.complete {
213 Ok(self.buffer)
214 } else {
215 Err(CoapError::BlockTransferError(format!(
216 "Transfer incomplete: received {} blocks, waiting for more",
217 self.next_num
218 )))
219 }
220 }
221
222 pub fn szx(&self) -> u8 {
224 self.szx
225 }
226
227 pub fn is_complete(&self) -> bool {
229 self.complete
230 }
231
232 pub fn reset(&mut self) {
234 self.buffer.clear();
235 self.next_num = 0;
236 self.szx = DEFAULT_SZX;
237 self.complete = false;
238 }
239}
240
241#[derive(Debug)]
247pub struct BlockDisassembler {
248 payload: Vec<u8>,
250 szx: u8,
252}
253
254impl BlockDisassembler {
255 pub fn new(payload: Vec<u8>, szx: u8) -> Self {
262 Self {
263 payload,
264 szx: szx.min(MAX_SZX),
265 }
266 }
267
268 pub fn get_block(&self, block_num: u32) -> Option<(Vec<u8>, BlockOption)> {
273 let block_size = block_size_from_szx(self.szx);
274 let offset = block_num as usize * block_size;
275
276 if offset >= self.payload.len() {
277 return None;
278 }
279
280 let end = (offset + block_size).min(self.payload.len());
281 let data = self.payload[offset..end].to_vec();
282 let more = end < self.payload.len();
283
284 let option = BlockOption {
285 num: block_num,
286 more,
287 szx: self.szx,
288 };
289
290 Some((data, option))
291 }
292
293 pub fn total_blocks(&self) -> u32 {
295 let block_size = block_size_from_szx(self.szx);
296 if self.payload.is_empty() {
297 return 1; }
299 ((self.payload.len() + block_size - 1) / block_size) as u32
300 }
301
302 pub fn payload_len(&self) -> usize {
304 self.payload.len()
305 }
306
307 pub fn szx(&self) -> u8 {
309 self.szx
310 }
311}
312
313#[cfg(test)]
314mod tests {
315 use super::*;
316
317 #[test]
318 fn test_block_size_from_szx() {
319 assert_eq!(block_size_from_szx(0), 16);
320 assert_eq!(block_size_from_szx(1), 32);
321 assert_eq!(block_size_from_szx(2), 64);
322 assert_eq!(block_size_from_szx(3), 128);
323 assert_eq!(block_size_from_szx(4), 256);
324 assert_eq!(block_size_from_szx(5), 512);
325 assert_eq!(block_size_from_szx(6), 1024);
326 }
327
328 #[test]
329 fn test_block_size_from_szx_clamps() {
330 assert_eq!(block_size_from_szx(7), 1024);
332 assert_eq!(block_size_from_szx(255), 1024);
333 }
334
335 #[test]
336 fn test_szx_from_block_size() {
337 assert_eq!(szx_from_block_size(16), Some(0));
338 assert_eq!(szx_from_block_size(32), Some(1));
339 assert_eq!(szx_from_block_size(64), Some(2));
340 assert_eq!(szx_from_block_size(128), Some(3));
341 assert_eq!(szx_from_block_size(256), Some(4));
342 assert_eq!(szx_from_block_size(512), Some(5));
343 assert_eq!(szx_from_block_size(1024), Some(6));
344 }
345
346 #[test]
347 fn test_szx_from_block_size_invalid() {
348 assert_eq!(szx_from_block_size(0), None);
349 assert_eq!(szx_from_block_size(8), None);
350 assert_eq!(szx_from_block_size(100), None);
351 assert_eq!(szx_from_block_size(2048), None);
352 }
353
354 #[test]
355 fn test_block_option_decode_encode_roundtrip() {
356 let opt = BlockOption {
358 num: 0,
359 more: true,
360 szx: 5,
361 };
362 let encoded = opt.encode();
363 let decoded = BlockOption::decode(encoded);
364 assert_eq!(decoded, opt);
365
366 let opt = BlockOption {
368 num: 7,
369 more: false,
370 szx: 3,
371 };
372 let encoded = opt.encode();
373 let decoded = BlockOption::decode(encoded);
374 assert_eq!(decoded, opt);
375
376 let opt = BlockOption {
378 num: 1000,
379 more: true,
380 szx: 6,
381 };
382 let encoded = opt.encode();
383 let decoded = BlockOption::decode(encoded);
384 assert_eq!(decoded, opt);
385 }
386
387 #[test]
388 fn test_block_option_offset() {
389 let opt = BlockOption {
390 num: 3,
391 more: true,
392 szx: 5,
393 };
394 assert_eq!(opt.offset(), 1536);
396 }
397
398 #[test]
399 fn test_block_option_block_size() {
400 let opt = BlockOption {
401 num: 0,
402 more: false,
403 szx: 4,
404 };
405 assert_eq!(opt.block_size(), 256);
406 }
407
408 #[test]
409 fn test_assembler_single_block() {
410 let mut assembler = BlockAssembler::new(4096);
411 let block = BlockOption {
412 num: 0,
413 more: false,
414 szx: 5,
415 };
416 let data = b"hello coap";
417
418 let complete = assembler.process_block(&block, data).unwrap();
419 assert!(complete);
420 assert!(assembler.is_complete());
421 assert_eq!(assembler.payload(), Some(data.as_slice()));
422 }
423
424 #[test]
425 fn test_assembler_multi_block() {
426 let mut assembler = BlockAssembler::new(4096);
427
428 let block0 = BlockOption {
429 num: 0,
430 more: true,
431 szx: 5,
432 };
433 let block1 = BlockOption {
434 num: 1,
435 more: true,
436 szx: 5,
437 };
438 let block2 = BlockOption {
439 num: 2,
440 more: false,
441 szx: 5,
442 };
443
444 assert!(!assembler.process_block(&block0, b"aaa").unwrap());
445 assert!(!assembler.process_block(&block1, b"bbb").unwrap());
446 assert!(assembler.process_block(&block2, b"ccc").unwrap());
447
448 assert_eq!(assembler.payload(), Some(b"aaabbbccc".as_slice()));
449 }
450
451 #[test]
452 fn test_assembler_out_of_order() {
453 let mut assembler = BlockAssembler::new(4096);
454
455 let block0 = BlockOption {
456 num: 0,
457 more: true,
458 szx: 5,
459 };
460 let block2 = BlockOption {
461 num: 2,
462 more: false,
463 szx: 5,
464 };
465
466 assembler.process_block(&block0, b"aaa").unwrap();
467 let err = assembler.process_block(&block2, b"ccc").unwrap_err();
469 assert!(matches!(err, CoapError::BlockTransferError(_)));
470 }
471
472 #[test]
473 fn test_assembler_payload_too_large() {
474 let mut assembler = BlockAssembler::new(5);
475
476 let block = BlockOption {
477 num: 0,
478 more: false,
479 szx: 5,
480 };
481
482 let err = assembler.process_block(&block, b"too large").unwrap_err();
483 assert!(matches!(err, CoapError::PayloadTooLarge { .. }));
484 }
485
486 #[test]
487 fn test_assembler_into_payload_incomplete() {
488 let assembler = BlockAssembler::new(4096);
489 let err = assembler.into_payload().unwrap_err();
490 assert!(matches!(err, CoapError::BlockTransferError(_)));
491 }
492
493 #[test]
494 fn test_assembler_reset() {
495 let mut assembler = BlockAssembler::new(4096);
496 let block = BlockOption {
497 num: 0,
498 more: false,
499 szx: 5,
500 };
501 assembler.process_block(&block, b"data").unwrap();
502 assert!(assembler.is_complete());
503
504 assembler.reset();
505 assert!(!assembler.is_complete());
506 assert_eq!(assembler.payload(), None);
507 }
508
509 #[test]
510 fn test_disassembler_single_block() {
511 let payload = b"small".to_vec();
512 let disasm = BlockDisassembler::new(payload.clone(), DEFAULT_SZX);
513
514 assert_eq!(disasm.total_blocks(), 1);
515
516 let (data, opt) = disasm.get_block(0).unwrap();
517 assert_eq!(data, payload);
518 assert!(!opt.more);
519 assert_eq!(opt.num, 0);
520
521 assert!(disasm.get_block(1).is_none());
522 }
523
524 #[test]
525 fn test_disassembler_multi_block() {
526 let payload = vec![0xAB; 100];
528 let disasm = BlockDisassembler::new(payload, 2);
529
530 assert_eq!(disasm.total_blocks(), 2);
531
532 let (data0, opt0) = disasm.get_block(0).unwrap();
533 assert_eq!(data0.len(), 64);
534 assert!(opt0.more);
535 assert_eq!(opt0.num, 0);
536
537 let (data1, opt1) = disasm.get_block(1).unwrap();
538 assert_eq!(data1.len(), 36);
539 assert!(!opt1.more);
540 assert_eq!(opt1.num, 1);
541
542 assert!(disasm.get_block(2).is_none());
543 }
544
545 #[test]
546 fn test_disassembler_exact_boundary() {
547 let payload = vec![0xCD; 128];
549 let disasm = BlockDisassembler::new(payload, 3);
550
551 assert_eq!(disasm.total_blocks(), 1);
552
553 let (data, opt) = disasm.get_block(0).unwrap();
554 assert_eq!(data.len(), 128);
555 assert!(!opt.more);
556 }
557
558 #[test]
559 fn test_disassembler_empty_payload() {
560 let disasm = BlockDisassembler::new(Vec::new(), DEFAULT_SZX);
561 assert_eq!(disasm.total_blocks(), 1);
562 }
563
564 #[test]
565 fn test_assembler_disassembler_roundtrip() {
566 let original = vec![0x42; 2000]; let disasm = BlockDisassembler::new(original.clone(), DEFAULT_SZX);
568 let mut assembler = BlockAssembler::new(4096);
569
570 for i in 0..disasm.total_blocks() {
571 let (data, opt) = disasm.get_block(i).unwrap();
572 assembler.process_block(&opt, &data).unwrap();
573 }
574
575 assert!(assembler.is_complete());
576 let reassembled = assembler.into_payload().unwrap();
577 assert_eq!(reassembled, original);
578 }
579}