1use alloc::vec::Vec;
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20#[repr(u16)]
21pub enum BlockOption {
22 Block1 = 27,
24 Block2 = 23,
26}
27
28#[derive(Debug, Clone, Copy, PartialEq, Eq)]
30pub struct BlockValue {
31 pub num: u32,
33 pub more: bool,
35 pub szx: u8,
37}
38
39#[derive(Debug, Clone, Copy, PartialEq, Eq)]
41pub enum BlockError {
42 ReservedSzx,
44 NumTooLarge,
46 EncodingTooLong,
48 BadEncodingLength,
50}
51
52impl BlockValue {
53 pub fn block_size(&self) -> Result<usize, BlockError> {
58 if self.szx > 6 {
59 return Err(BlockError::ReservedSzx);
60 }
61 Ok(1usize << (self.szx + 4))
62 }
63
64 pub fn encode(&self) -> Result<Vec<u8>, BlockError> {
69 if self.szx > 6 {
70 return Err(BlockError::ReservedSzx);
71 }
72 if self.num >= (1 << 20) {
73 return Err(BlockError::NumTooLarge);
74 }
75 let raw = (self.num << 4) | (u32::from(self.more) << 3) | u32::from(self.szx);
76 let mut out = Vec::with_capacity(3);
77 if raw < 0x100 {
78 out.push(raw as u8);
79 } else if raw < 0x1_0000 {
80 out.push(((raw >> 8) & 0xff) as u8);
81 out.push((raw & 0xff) as u8);
82 } else {
83 out.push(((raw >> 16) & 0xff) as u8);
84 out.push(((raw >> 8) & 0xff) as u8);
85 out.push((raw & 0xff) as u8);
86 }
87 Ok(out)
88 }
89
90 pub fn decode(bytes: &[u8]) -> Result<Self, BlockError> {
95 let raw: u32 = match bytes.len() {
96 1 => u32::from(bytes[0]),
97 2 => (u32::from(bytes[0]) << 8) | u32::from(bytes[1]),
98 3 => (u32::from(bytes[0]) << 16) | (u32::from(bytes[1]) << 8) | u32::from(bytes[2]),
99 _ => return Err(BlockError::BadEncodingLength),
100 };
101 let szx = (raw & 0x7) as u8;
102 if szx == 7 {
103 return Err(BlockError::ReservedSzx);
104 }
105 let more = (raw >> 3) & 0x1 != 0;
106 let num = raw >> 4;
107 Ok(Self { num, more, szx })
108 }
109}
110
111#[derive(Debug, Clone, PartialEq, Eq, Default)]
113pub struct BlockReassembler {
114 block_size: usize,
116 buf: Vec<u8>,
118 next_num: u32,
120 complete: bool,
122}
123
124impl BlockReassembler {
125 #[must_use]
127 pub fn new(block_size: usize) -> Self {
128 Self {
129 block_size,
130 buf: Vec::new(),
131 next_num: 0,
132 complete: false,
133 }
134 }
135
136 pub fn accept(&mut self, value: BlockValue, payload: &[u8]) -> Result<(), &'static str> {
143 if self.complete {
144 return Err("reassembler already complete");
145 }
146 let size = value.block_size().map_err(|_| "block size invalid")?;
147 if value.num != self.next_num {
148 return Err("block out of order");
149 }
150 if size != self.block_size {
151 if self.next_num == 0 {
153 self.block_size = size;
154 } else {
155 return Err("block size changed mid-transfer");
156 }
157 }
158 if value.more && payload.len() != size {
159 return Err("intermediate block size mismatch");
160 }
161 if !value.more && payload.len() > size {
162 return Err("final block too large");
163 }
164 self.buf.extend_from_slice(payload);
165 self.next_num += 1;
166 if !value.more {
167 self.complete = true;
168 }
169 Ok(())
170 }
171
172 #[must_use]
174 pub fn is_complete(&self) -> bool {
175 self.complete
176 }
177
178 #[must_use]
180 pub fn into_payload(self) -> Vec<u8> {
181 self.buf
182 }
183
184 #[must_use]
186 pub fn len(&self) -> usize {
187 self.buf.len()
188 }
189
190 #[must_use]
192 pub fn is_empty(&self) -> bool {
193 self.buf.is_empty()
194 }
195}
196
197#[cfg(test)]
198#[allow(clippy::expect_used, clippy::unwrap_used, clippy::panic)]
199mod tests {
200 use super::*;
201
202 #[test]
203 fn block_size_matches_spec_szx_table() {
204 let expected = [16, 32, 64, 128, 256, 512, 1024];
206 for (szx, sz) in expected.iter().enumerate() {
207 let v = BlockValue {
208 num: 0,
209 more: false,
210 szx: szx as u8,
211 };
212 assert_eq!(v.block_size().unwrap(), *sz);
213 }
214 }
215
216 #[test]
217 fn szx_7_rejected() {
218 let v = BlockValue {
219 num: 0,
220 more: false,
221 szx: 7,
222 };
223 assert_eq!(v.block_size(), Err(BlockError::ReservedSzx));
224 }
225
226 #[test]
227 fn round_trip_small_block_value() {
228 let v = BlockValue {
229 num: 0,
230 more: true,
231 szx: 6,
232 };
233 let bytes = v.encode().unwrap();
234 let back = BlockValue::decode(&bytes).unwrap();
235 assert_eq!(v, back);
236 }
237
238 #[test]
239 fn round_trip_large_block_value() {
240 let v = BlockValue {
241 num: 0xfffff,
242 more: false,
243 szx: 0,
244 };
245 let bytes = v.encode().unwrap();
246 let back = BlockValue::decode(&bytes).unwrap();
247 assert_eq!(v, back);
248 }
249
250 #[test]
251 fn num_too_large_rejected() {
252 let v = BlockValue {
253 num: 1 << 20,
254 more: false,
255 szx: 0,
256 };
257 assert_eq!(v.encode(), Err(BlockError::NumTooLarge));
258 }
259
260 #[test]
261 fn decode_rejects_bad_length() {
262 assert_eq!(
263 BlockValue::decode(&[0; 4]),
264 Err(BlockError::BadEncodingLength)
265 );
266 assert_eq!(BlockValue::decode(&[]), Err(BlockError::BadEncodingLength));
267 }
268
269 #[test]
270 fn reassembler_combines_blocks_in_order() {
271 let mut r = BlockReassembler::new(16);
272 r.accept(
273 BlockValue {
274 num: 0,
275 more: true,
276 szx: 0,
277 },
278 &[1u8; 16],
279 )
280 .unwrap();
281 r.accept(
282 BlockValue {
283 num: 1,
284 more: true,
285 szx: 0,
286 },
287 &[2u8; 16],
288 )
289 .unwrap();
290 r.accept(
291 BlockValue {
292 num: 2,
293 more: false,
294 szx: 0,
295 },
296 &[3u8; 8],
297 )
298 .unwrap();
299 assert!(r.is_complete());
300 let buf = r.into_payload();
301 assert_eq!(buf.len(), 16 + 16 + 8);
302 }
303
304 #[test]
305 fn out_of_order_block_rejected() {
306 let mut r = BlockReassembler::new(16);
307 let err = r
308 .accept(
309 BlockValue {
310 num: 5,
311 more: true,
312 szx: 0,
313 },
314 &[1u8; 16],
315 )
316 .unwrap_err();
317 assert_eq!(err, "block out of order");
318 }
319
320 #[test]
321 fn intermediate_block_size_must_match() {
322 let mut r = BlockReassembler::new(16);
323 r.accept(
324 BlockValue {
325 num: 0,
326 more: true,
327 szx: 0,
328 },
329 &[1u8; 16],
330 )
331 .unwrap();
332 let err = r
333 .accept(
334 BlockValue {
335 num: 1,
336 more: true,
337 szx: 0,
338 },
339 &[2u8; 8],
340 )
341 .unwrap_err();
342 assert_eq!(err, "intermediate block size mismatch");
343 }
344
345 #[test]
346 fn final_block_can_be_smaller() {
347 let mut r = BlockReassembler::new(16);
348 r.accept(
349 BlockValue {
350 num: 0,
351 more: false,
352 szx: 0,
353 },
354 &[7u8; 5],
355 )
356 .unwrap();
357 assert!(r.is_complete());
358 assert_eq!(r.len(), 5);
359 }
360}