1#![doc = include_str!("../README.md")]
6#![cfg_attr(not(feature = "std"), no_std)]
8
9#[cfg(test)]
11extern crate std;
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
15pub enum EncodeError {
16 #[error("buffer too small: have {have} bytes, want {want} bytes")]
21 BufferTooSmall { have: usize, want: usize },
22}
23
24#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
26pub enum DecodeError {
27 #[error("empty input")]
30 EmptyInput,
31
32 #[error("buffer too small: have {have} bytes, want {want} bytes")]
37 BufferTooSmall { have: usize, want: usize },
38
39 #[error("zero marker at position {at}")]
43 ZeroMarker { at: usize },
44
45 #[error("zero byte in data at position {at}")]
49 ZeroBinary { at: usize },
50
51 #[error("chunk overflow at position {at}: chunk {marker} exceeds data length {len}")]
55 ChunkOverflow { at: usize, marker: u8, len: usize },
56}
57
58#[inline]
60pub const fn encode_buffer(size: usize) -> usize {
61 size + size.div_ceil(254) + 1
62}
63
64#[inline]
66pub const fn decode_buffer(size: usize) -> usize {
67 if size == 0 {
68 return 0;
74 }
75 size - 1
76}
77
78#[inline]
82pub fn encode(data: &[u8], encoded: &mut [u8]) -> Result<usize, EncodeError> {
83 let want = encode_buffer(data.len());
84 if encoded.len() < want {
85 return Err(EncodeError::BufferTooSmall {
86 have: encoded.len(),
87 want,
88 });
89 }
90 Ok(unsafe { encode_unchecked(data, encoded) })
92}
93
94#[inline]
100pub unsafe fn encode_unchecked(data: &[u8], encoded: &mut [u8]) -> usize {
101 if data.is_empty() {
103 encoded[0] = 0x01;
104 return 1;
105 }
106 debug_assert!(encoded.len() >= encode_buffer(data.len()));
108
109 unsafe {
112 let mut input_pos = 0usize;
113 let mut output_pos = 0usize;
114
115 loop {
116 let run = if *data.get_unchecked(input_pos) == 0 {
118 Some(0)
119 } else {
120 memchr::memchr(0, data.get_unchecked(input_pos..))
121 };
122 let mut rem = run.unwrap_or(data.len() - input_pos);
124 while rem >= 254 {
125 *encoded.get_unchecked_mut(output_pos) = 0xff;
126 core::ptr::copy_nonoverlapping(
127 data.as_ptr().add(input_pos),
128 encoded.as_mut_ptr().add(output_pos + 1),
129 254,
130 );
131 input_pos += 254;
132 output_pos += 255;
133 rem -= 254;
134 }
135 if run.is_some() {
136 *encoded.get_unchecked_mut(output_pos) = rem as u8 + 1;
138 core::ptr::copy_nonoverlapping(
139 data.as_ptr().add(input_pos),
140 encoded.as_mut_ptr().add(output_pos + 1),
141 rem,
142 );
143 input_pos += rem + 1;
144 output_pos += rem + 1;
145
146 if input_pos == data.len() {
148 *encoded.get_unchecked_mut(output_pos) = 0x01;
149 return output_pos + 1;
150 }
151 } else {
152 if rem > 0 {
155 *encoded.get_unchecked_mut(output_pos) = rem as u8 + 1;
156 core::ptr::copy_nonoverlapping(
157 data.as_ptr().add(input_pos),
158 encoded.as_mut_ptr().add(output_pos + 1),
159 rem,
160 );
161 output_pos += rem + 1;
162 }
163 return output_pos;
164 }
165 }
166 }
167}
168
169#[inline]
173pub fn decode(data: &[u8], decoded: &mut [u8]) -> Result<usize, DecodeError> {
174 if data.is_empty() {
175 return Err(DecodeError::EmptyInput);
176 }
177 if data.len() > 1 {
178 let want = decode_buffer(data.len());
179 if decoded.len() < want {
180 return Err(DecodeError::BufferTooSmall {
181 have: decoded.len(),
182 want,
183 });
184 }
185 }
186 unsafe { decode_unchecked(data, decoded) }
189}
190
191#[inline]
197pub unsafe fn decode_unchecked(data: &[u8], decoded: &mut [u8]) -> Result<usize, DecodeError> {
198 if data.is_empty() {
200 return Err(DecodeError::EmptyInput);
201 }
202 if data.len() == 1 && data[0] == 0x01 {
204 return Ok(0);
205 }
206 debug_assert!(decoded.len() >= decode_buffer(data.len()));
208
209 if memchr::memchr(0, data).is_some() {
214 return decode_scalar(data, decoded);
215 }
216 decode_chunked::<false>(data, decoded)
217}
218
219#[inline]
227pub fn decode_nonzero(data: &[u8], decoded: &mut [u8]) -> Result<usize, DecodeError> {
228 if data.is_empty() {
229 return Err(DecodeError::EmptyInput);
230 }
231 if data.len() > 1 {
232 let want = decode_buffer(data.len());
233 if decoded.len() < want {
234 return Err(DecodeError::BufferTooSmall {
235 have: decoded.len(),
236 want,
237 });
238 }
239 }
240 unsafe { decode_nonzero_unchecked(data, decoded) }
243}
244
245#[inline]
252pub unsafe fn decode_nonzero_unchecked(
253 data: &[u8],
254 decoded: &mut [u8],
255) -> Result<usize, DecodeError> {
256 if data.is_empty() {
258 return Err(DecodeError::EmptyInput);
259 }
260 if data.len() == 1 && data[0] == 0x01 {
262 return Ok(0);
263 }
264 debug_assert!(decoded.len() >= decode_buffer(data.len()));
266
267 decode_chunked::<true>(data, decoded)
268}
269
270#[inline]
280fn decode_chunked<const CHECKED: bool>(
281 data: &[u8],
282 decoded: &mut [u8],
283) -> Result<usize, DecodeError> {
284 unsafe {
285 let mut input_pos = 0usize;
286 let mut output_pos = 0usize;
287
288 while input_pos + 255 < data.len() {
298 let marker = *data.get_unchecked(input_pos);
299 if CHECKED && marker == 0 {
300 return Err(DecodeError::ZeroMarker { at: input_pos });
301 }
302 let chunk = marker as usize - 1;
303 input_pos += 1;
304
305 core::ptr::copy_nonoverlapping(
306 data.as_ptr().add(input_pos),
307 decoded.as_mut_ptr().add(output_pos),
308 16,
309 );
310 if chunk > 16 {
311 core::ptr::copy_nonoverlapping(
312 data.as_ptr().add(input_pos),
313 decoded.as_mut_ptr().add(output_pos),
314 254,
315 );
316 }
317 input_pos += chunk;
318 output_pos += chunk;
319
320 *decoded.get_unchecked_mut(output_pos) = 0;
321 output_pos += (marker != 0xff) as usize;
322 }
323 loop {
325 let marker = *data.get_unchecked(input_pos);
327 if CHECKED && marker == 0 {
328 return Err(DecodeError::ZeroMarker { at: input_pos });
329 }
330 let chunk = marker as usize - 1;
331 input_pos += 1;
332
333 if input_pos + chunk > data.len() {
334 return Err(DecodeError::ChunkOverflow {
335 at: input_pos - 1,
336 marker,
337 len: data.len(),
338 });
339 }
340 core::ptr::copy_nonoverlapping(
342 data.as_ptr().add(input_pos),
343 decoded.as_mut_ptr().add(output_pos),
344 chunk,
345 );
346 input_pos += chunk;
347 output_pos += chunk;
348
349 if input_pos == data.len() {
351 return Ok(output_pos);
352 }
353 if marker != 0xff {
355 *decoded.get_unchecked_mut(output_pos) = 0;
356 output_pos += 1;
357 }
358 }
359 }
360}
361
362#[cold]
369#[inline(never)]
370fn decode_scalar(data: &[u8], decoded: &mut [u8]) -> Result<usize, DecodeError> {
371 unsafe {
373 let mut output_pos = 0usize;
374 let mut i = 0usize;
375
376 while i < data.len() {
377 let marker = *data.get_unchecked(i);
379 if marker == 0 {
380 return Err(DecodeError::ZeroMarker { at: i });
381 }
382 i += 1;
383
384 if i + (marker as usize) - 1 > data.len() {
386 return Err(DecodeError::ChunkOverflow {
387 at: i - 1,
388 marker,
389 len: data.len(),
390 });
391 }
392 for _ in 1..marker {
394 let b = *data.get_unchecked(i);
395 if b == 0 {
396 return Err(DecodeError::ZeroBinary { at: i });
397 }
398 *decoded.get_unchecked_mut(output_pos) = b;
399 output_pos += 1;
400 i += 1;
401 }
402 if i < data.len() && marker != 0xff {
404 *decoded.get_unchecked_mut(output_pos) = 0;
405 output_pos += 1;
406 }
407 }
408 Ok(output_pos)
409 }
410}
411
412#[cfg(test)]
413mod tests {
414 use super::*;
415 use std::vec;
416 use std::vec::Vec;
417
418 #[test]
419 fn test_roundtrip_empty() {
420 let data = [];
421 let mut enc_buf = [0u8; 1];
422 let len = encode(&data, &mut enc_buf).unwrap();
423 assert_eq!(len, 1);
424 assert_eq!(enc_buf[0], 0x01);
425
426 let mut dec_buf = [0u8; 0];
427 let dec_len = decode(&enc_buf[..len], &mut dec_buf).unwrap();
428 assert_eq!(dec_len, 0);
429 }
430
431 #[test]
432 fn test_roundtrip_no_zeros() {
433 let data = [1, 2, 3, 4, 5];
434 let mut enc_buf = [0u8; encode_buffer(5)];
435 let len = encode(&data, &mut enc_buf).unwrap();
436
437 let mut dec_buf = [0u8; decode_buffer(encode_buffer(5))];
438 let dec_len = decode(&enc_buf[..len], &mut dec_buf).unwrap();
439 assert_eq!(&dec_buf[..dec_len], &data);
440 }
441
442 #[test]
443 fn test_roundtrip_with_zeros() {
444 let data = [0, 1, 0, 2, 0, 0, 3];
445 let mut enc_buf = [0u8; encode_buffer(7)];
446 let len = encode(&data, &mut enc_buf).unwrap();
447
448 let mut dec_buf = [0u8; decode_buffer(encode_buffer(7))];
449 let dec_len = decode(&enc_buf[..len], &mut dec_buf).unwrap();
450 assert_eq!(&dec_buf[..dec_len], &data);
451 }
452
453 #[test]
454 fn test_roundtrip_254_nonzero() {
455 let data: Vec<u8> = (1..=254).collect();
456 let mut enc_buf = vec![0u8; encode_buffer(254)];
457 let len = encode(&data, &mut enc_buf).unwrap();
458
459 let mut dec_buf = vec![0u8; decode_buffer(enc_buf.len())];
460 let dec_len = decode(&enc_buf[..len], &mut dec_buf).unwrap();
461 assert_eq!(&dec_buf[..dec_len], &data[..]);
462 }
463
464 #[test]
465 fn test_roundtrip_255_nonzero() {
466 let data: Vec<u8> = (1..=254).chain(std::iter::once(1)).collect();
467 let mut enc_buf = vec![0u8; encode_buffer(255)];
468 let len = encode(&data, &mut enc_buf).unwrap();
469
470 let mut dec_buf = vec![0u8; decode_buffer(enc_buf.len())];
471 let dec_len = decode(&enc_buf[..len], &mut dec_buf).unwrap();
472 assert_eq!(&dec_buf[..dec_len], &data[..]);
473 }
474
475 #[test]
476 fn test_roundtrip_chunk_boundaries() {
477 let sizes: Vec<usize> = if cfg!(miri) {
478 (0..=64)
479 .chain([253, 254, 255, 256, 507, 508, 509, 510, 1021, 1024])
480 .collect()
481 } else {
482 (0..=515)
483 .chain([1021, 1024, 4093, 4096, 8191, 65536])
484 .collect()
485 };
486 for size in sizes {
487 for period in [1usize, 2, 3, 253, 254, 255, 256] {
488 for phase in [0, period - 1] {
489 let data: Vec<u8> = (0..size)
490 .map(|i| {
491 if i % period == phase {
492 0
493 } else {
494 (i % 251 + 1) as u8
495 }
496 })
497 .collect();
498 roundtrip_reference(&data);
499 }
500 }
501 let data: Vec<u8> = (0..size).map(|i| (i % 251 + 1) as u8).collect();
502 roundtrip_reference(&data);
503 }
504 }
505
506 fn roundtrip_reference(data: &[u8]) {
509 let mut encoded = vec![0u8; encode_buffer(data.len())];
510 let encoded_len = encode(data, &mut encoded).unwrap();
511
512 let mut reference = vec![0u8; cobs::max_encoding_length(data.len())];
513 let reference_len = cobs::encode(data, &mut reference);
514 assert_eq!(&encoded[..encoded_len], &reference[..reference_len]);
515
516 let mut decoded = vec![0u8; decode_buffer(encoded_len)];
517 let decoded_len = decode(&encoded[..encoded_len], &mut decoded).unwrap();
518 assert_eq!(&decoded[..decoded_len], data);
519
520 let mut nonzero = vec![0u8; decode_buffer(encoded_len)];
521 let nonzero_len = decode_nonzero(&encoded[..encoded_len], &mut nonzero).unwrap();
522 assert_eq!(&nonzero[..nonzero_len], data);
523 }
524
525 #[test]
526 fn test_decode_malformed() {
527 let mut buffer = [0u8; 16];
528
529 assert_eq!(decode(&[], &mut buffer), Err(DecodeError::EmptyInput));
530 assert_eq!(
531 decode(&[0x00], &mut buffer),
532 Err(DecodeError::ZeroMarker { at: 0 })
533 );
534 assert_eq!(
535 decode(&[0x02, 0x41, 0x00], &mut buffer),
536 Err(DecodeError::ZeroMarker { at: 2 })
537 );
538 assert_eq!(
539 decode(&[0x02, 0x00], &mut buffer),
540 Err(DecodeError::ZeroBinary { at: 1 })
541 );
542 assert_eq!(
543 decode(&[0x03, 0x41, 0x00, 0x41], &mut buffer),
544 Err(DecodeError::ZeroBinary { at: 2 })
545 );
546 assert_eq!(
547 decode(&[0x03, 0x41], &mut buffer),
548 Err(DecodeError::ChunkOverflow {
549 at: 0,
550 marker: 3,
551 len: 2
552 })
553 );
554 assert_eq!(
555 decode(&[0x05, 0x41, 0x00, 0x41], &mut buffer),
556 Err(DecodeError::ChunkOverflow {
557 at: 0,
558 marker: 5,
559 len: 4
560 })
561 );
562 }
563
564 #[test]
565 fn test_decode_nonzero_malformed() {
566 let mut buffer = [0u8; 128];
567
568 assert_eq!(
569 decode_nonzero(&[], &mut buffer),
570 Err(DecodeError::EmptyInput)
571 );
572 assert_eq!(
573 decode_nonzero(&[0x03, 0x41], &mut buffer),
574 Err(DecodeError::ChunkOverflow {
575 at: 0,
576 marker: 3,
577 len: 2
578 })
579 );
580 let mut long = Vec::new();
583 for _ in 0..26 {
584 long.extend_from_slice(&[0x03, 0x41, 0x42]);
585 }
586 long.extend_from_slice(&[0x05, 0x41]);
587 assert_eq!(
588 decode_nonzero(&long, &mut buffer),
589 decode(&long, &mut [0u8; 128])
590 );
591 long[40] = 0;
594 let _ = decode_nonzero(&long, &mut buffer);
595 }
596
597 #[test]
598 fn test_buffer_too_small() {
599 let mut buffer = [0u8; 2];
600
601 assert_eq!(
602 encode(&[1, 2, 3], &mut buffer),
603 Err(EncodeError::BufferTooSmall {
604 have: 2,
605 want: encode_buffer(3)
606 })
607 );
608 assert_eq!(
609 decode(&[0x02, 0x41, 0x02, 0x42], &mut buffer),
610 Err(DecodeError::BufferTooSmall { have: 2, want: 3 })
611 );
612 }
613}