1#![cfg_attr(not(any(test, feature = "std")), no_std)]
2
3use core::ops::DerefMut;
4
5struct Overflow;
7
8pub struct CobsAccumulator<B: DerefMut<Target = [u8]>> {
10 buf: B,
11 idx: usize,
12 in_overflow: bool,
13}
14
15#[derive(Debug)]
17pub enum FeedResult<'input, 'buf> {
18 Consumed,
20
21 OverFull(&'input mut [u8]),
23
24 DecodeError(&'input mut [u8]),
27
28 Success {
31 data: &'buf [u8],
33
34 remaining: &'input mut [u8],
36 },
37
38 SuccessInput {
41 data: &'input [u8],
43
44 remaining: &'input mut [u8],
46 },
47}
48
49#[cfg(any(feature = "std", test))]
50impl CobsAccumulator<Box<[u8]>> {
51 pub fn new_boxslice(len: usize) -> Self {
52 Self::new(vec![0u8; len].into_boxed_slice())
53 }
54}
55
56impl<B: DerefMut<Target = [u8]>> CobsAccumulator<B> {
57 pub fn new(b: B) -> Self {
59 CobsAccumulator {
60 buf: b,
61 idx: 0,
62 in_overflow: false,
63 }
64 }
65
66 pub fn feed_raw<'me, 'input>(
73 &'me mut self,
74 input: &'input mut [u8],
75 ) -> FeedResult<'input, 'me> {
76 if input.is_empty() {
78 return FeedResult::Consumed;
79 }
80
81 let zero_pos = input.iter().position(|&i| i == 0);
83 let Some(n) = zero_pos else {
84 if self.in_overflow {
88 return FeedResult::OverFull(&mut []);
91 }
92
93 return match self.push(input) {
95 Ok(()) => FeedResult::Consumed,
97 Err(Overflow) => {
101 self.in_overflow = true;
102 FeedResult::OverFull(&mut [])
103 }
104 };
105 };
106
107 let (take, release) = input.split_at_mut(n + 1);
111
112 if self.in_overflow {
116 self.in_overflow = false;
117 return FeedResult::OverFull(release);
118 }
119
120 if self.idx == 0 {
124 return match cobs::decode_in_place(take) {
125 Ok(ct) => FeedResult::SuccessInput {
126 data: &take[..ct],
127 remaining: release,
128 },
129 Err(_) => FeedResult::DecodeError(release),
130 };
131 }
132
133 let Ok(used) = self.push_reset(take) else {
136 return FeedResult::OverFull(release);
141 };
142
143 match cobs::decode_in_place(used) {
145 Ok(ct) => FeedResult::Success {
147 data: &used[..ct],
148 remaining: release,
149 },
150 Err(_) => FeedResult::DecodeError(release),
152 }
153 }
154
155 #[inline]
156 fn push(&mut self, data: &[u8]) -> Result<(), Overflow> {
157 let old_idx = self.idx;
158 let new_end = old_idx + data.len();
159 if let Some(sli) = self.buf.get_mut(old_idx..new_end) {
160 sli.copy_from_slice(data);
161 self.idx = self.buf.len().min(new_end);
162 Ok(())
163 } else {
164 self.idx = 0;
165 Err(Overflow)
166 }
167 }
168
169 #[inline]
170 fn push_reset(&'_ mut self, data: &[u8]) -> Result<&'_ mut [u8], Overflow> {
171 let old_idx = self.idx;
172 let new_end = old_idx + data.len();
173
174 let res = if let Some(sli) = self.buf.get_mut(..new_end) {
175 sli[old_idx..].copy_from_slice(data);
176 Ok(sli)
177 } else {
178 Err(Overflow)
179 };
180 self.idx = 0;
181 res
182 }
183
184 #[doc(hidden)]
185 #[cfg(test)]
186 pub fn contents(&self) -> Option<&[u8]> {
187 if self.in_overflow {
188 None
189 } else {
190 Some(&self.buf[..self.idx])
191 }
192 }
193}
194
195#[cfg(all(test, feature = "std"))]
196mod test {
197 use crate::{CobsAccumulator, FeedResult};
198
199 #[test]
200 fn smoke() {
201 let mut acc = CobsAccumulator::new_boxslice(16);
202 let mut input = vec![];
203 for i in 0..6 {
204 input.push(0);
205 input.push(i);
206 }
207 let mut inenc = cobs::encode_vec(&input);
208 inenc.push(0);
209 assert_eq!(inenc.len(), 14);
210 let inenc = inenc;
211
212 for chsz in 1..inenc.len() {
214 let mut inenc = inenc.clone();
215 let mut got_data = None;
216 let mut fed = 0;
217 for ch in inenc.chunks_mut(chsz) {
218 fed += ch.len();
219 match acc.feed_raw(ch) {
220 FeedResult::Consumed => {}
221 FeedResult::Success { data, remaining } => {
222 assert!(remaining.is_empty());
223 got_data = Some(data.to_vec());
224 break;
225 }
226 _ => panic!(),
227 }
228 }
229 assert_eq!(fed, 14);
230 let got = got_data.unwrap();
231 assert_eq!(got, input);
232 }
233
234 let mut twoenc = vec![];
236 twoenc.extend_from_slice(&inenc);
237 twoenc.extend_from_slice(&inenc);
238 let twoenc = twoenc;
239
240 for chsz in 1..twoenc.len() {
241 let mut twoenc = twoenc.clone();
242 let mut got_data = 0;
243 let mut fed = 0;
244 for mut ch in twoenc.chunks_mut(chsz) {
245 fed += ch.len();
246 'feed: loop {
247 match acc.feed_raw(ch) {
248 FeedResult::Consumed => break 'feed,
249 FeedResult::Success { data, remaining } => {
250 assert_eq!(data, &input);
251 got_data += 1;
252 ch = remaining;
253 }
254 FeedResult::SuccessInput { data, remaining } => {
255 assert_eq!(data, &input);
256 got_data += 1;
257 ch = remaining;
258 }
259 e => panic!("{e:?}"),
260 }
261 }
262 }
263 assert_eq!(fed, 28);
264 assert_eq!(got_data, 2);
265 }
266 }
267
268 #[test]
269 fn decode_err() {
270 let mut acc = CobsAccumulator::new_boxslice(16);
271 let mut input = vec![];
272 for i in 0..6 {
273 input.push(0);
274 input.push(i);
275 }
276 let mut inenc = cobs::encode_vec(&input);
277 let mut badenc = inenc.clone();
278 inenc.push(0);
279 badenc.push(4);
280 badenc.push(0);
281 assert_eq!(inenc.len(), 14);
282 assert_eq!(badenc.len(), 15);
283 let inenc = inenc;
284 let badenc = badenc;
285
286 let mut sandwich = vec![];
290 sandwich.extend_from_slice(&inenc);
291 sandwich.extend_from_slice(&badenc);
292 sandwich.extend_from_slice(&inenc);
293 let sandwich = sandwich;
294
295 for chsz in 1..sandwich.len() {
296 let mut sandwich = sandwich.clone();
297 let mut got_data = 0;
298 let mut bad_data = 0;
299 let mut fed = 0;
300 for mut ch in sandwich.chunks_mut(chsz) {
301 fed += ch.len();
302 'feed: loop {
303 match acc.feed_raw(ch) {
304 FeedResult::Consumed => break 'feed,
305 FeedResult::Success { data, remaining } => {
306 assert_eq!(data, &input);
307 got_data += 1;
308 ch = remaining;
309 }
310 FeedResult::SuccessInput { data, remaining } => {
311 assert_eq!(data, &input);
312 got_data += 1;
313 ch = remaining;
314 }
315 FeedResult::DecodeError(remaining) => {
316 bad_data += 1;
317 ch = remaining;
318 }
319 e => panic!("{e:?}"),
320 }
321 }
322 }
323 assert_eq!(fed, inenc.len() * 2 + badenc.len());
324 assert_eq!(got_data, 2);
325 assert_eq!(bad_data, 1);
326 }
327 }
328
329 #[test]
330 fn overflow_err() {
331 let mut acc = CobsAccumulator::new_boxslice(16);
332 let mut input = vec![];
333 for i in 0..6 {
334 input.push(0);
335 input.push(i);
336 }
337 let mut inenc = cobs::encode_vec(&input);
338 inenc.push(0);
339
340 let mut biginput = vec![];
341 for i in 0..25 {
342 biginput.push(0);
343 biginput.push(i);
344 }
345 let mut bigenc = cobs::encode_vec(&biginput);
346 bigenc.push(0);
347
348 assert_eq!(inenc.len(), 14);
349 assert_eq!(bigenc.len(), 52);
350 let inenc = inenc;
351 let bigenc = bigenc;
352
353 let mut sandwich = vec![];
357 sandwich.extend_from_slice(&inenc);
358 sandwich.extend_from_slice(&bigenc);
359 sandwich.extend_from_slice(&inenc);
360 let sandwich = sandwich;
361
362 for chsz in 1..16 {
366 let mut sandwich = sandwich.clone();
367 let mut got_data = 0;
368 let mut fed = 0;
369 for mut ch in sandwich.chunks_mut(chsz) {
370 fed += ch.len();
371 println!("CH: {ch:?}");
372 'feed: loop {
373 println!("{:?} <- {ch:?}", acc.contents());
374 match acc.feed_raw(ch) {
375 FeedResult::Consumed => break 'feed,
376 FeedResult::Success { data, remaining } => {
377 assert_eq!(data, &input);
378 got_data += 1;
379 ch = remaining;
380 }
381 FeedResult::SuccessInput { data, remaining } => {
382 assert_eq!(data, &input);
383 got_data += 1;
384 ch = remaining;
385 }
386 FeedResult::OverFull(remaining) => {
387 ch = remaining;
388 }
389 e => panic!("{e:?}"),
390 }
391 }
392 }
393 assert_eq!(fed, inenc.len() * 2 + bigenc.len());
394 assert_eq!(got_data, 2);
395 }
396 }
397
398 #[test]
399 fn permute_256() {
400 let mut acc = CobsAccumulator::new_boxslice(16);
401
402 let mut input = vec![];
404 for i in 0..6 {
405 input.push(0);
406 input.push(i);
407 }
408 let mut inenc = cobs::encode_vec(&input);
409 inenc.push(0);
410 let inenc = inenc;
411
412 let mut binput = vec![];
414 for i in 0..6 {
415 binput.push(0);
416 binput.push(i);
417 }
418 let mut badenc = cobs::encode_vec(&binput);
419 badenc.push(4);
420 badenc.push(0);
421 let badenc = badenc;
422
423 let empenc = vec![0u8];
425
426 let mut biginput = vec![];
428 for i in 0..25 {
429 biginput.push(0);
430 biginput.push(i);
431 }
432 let mut bigenc = cobs::encode_vec(&biginput);
433 bigenc.push(0);
434 let bigenc = bigenc;
435
436 for mut scenario_byte in 0u8..=255u8 {
439 let mut input_stream = vec![];
440 let mut good_emptys = 0;
441 let mut good_data = 0;
442 let mut bad_dec = 0;
443 for _ in 0..4 {
444 let scen = scenario_byte & 0b11;
445 scenario_byte >>= 2;
446 match scen {
447 0b00 => {
448 input_stream.extend_from_slice(&inenc);
449 good_data += 1;
450 }
451 0b01 => {
452 input_stream.extend_from_slice(&badenc);
453 bad_dec += 1;
454 }
455 0b10 => {
456 input_stream.extend_from_slice(&empenc);
457 good_emptys += 1;
458 }
459 _ => {
460 input_stream.extend_from_slice(&bigenc);
461 }
462 }
463 }
464
465 for chsz in 1..bigenc.len() {
469 let mut input_stream = input_stream.clone();
470 let mut got_data = 0;
471 let mut got_empty = 0;
472 let mut got_bads = 0;
473 let mut fed = 0;
474 for mut ch in input_stream.chunks_mut(chsz) {
475 fed += ch.len();
476 println!("CH: {ch:?}");
477 'feed: loop {
478 println!("{:?} <- {ch:?}", acc.contents());
479 match acc.feed_raw(ch) {
480 FeedResult::Consumed => break 'feed,
481 FeedResult::Success { data, remaining } => {
482 if data.is_empty() {
483 got_empty += 1;
484 } else {
485 assert_eq!(data, &input);
486 got_data += 1;
487 }
488 ch = remaining;
489 }
490 FeedResult::SuccessInput { data, remaining } => {
491 if data.is_empty() {
492 got_empty += 1;
493 } else {
494 assert_eq!(data, &input);
495 got_data += 1;
496 }
497 ch = remaining;
498 }
499 FeedResult::DecodeError(remaining) => {
500 got_bads += 1;
501 ch = remaining;
502 }
503 FeedResult::OverFull(remaining) => {
504 ch = remaining;
505 }
506 }
507 }
508 }
509 assert_eq!(fed, input_stream.len());
510 assert_eq!(got_data, good_data);
511 assert_eq!(got_bads, bad_dec);
512 assert_eq!(got_empty, good_emptys);
513 }
514 }
515 }
516}