cobs/
lib.rs

1//! # `cobs`
2//!
3//! This is an implementation of the Consistent Overhead Byte Stuffing (COBS) algorithm in Rust.
4//!
5//! COBS is an algorithm for transforming a message into an encoding where a specific value (the
6//! "sentinel" value) is not used. This value can then be used to mark frame boundaries in a serial
7//! communication channel.
8//!
9//! See the [wikipedia article](https://www.wikipedia.org/wiki/Consistent_Overhead_Byte_Stuffing) for details.
10//!
11//! ## Features
12//!
13//! `cobs` supports various runtime environments and is also suitable for `no_std` environments.
14//!
15//! ### Default features
16//!
17//! - [`std`](https://doc.rust-lang.org/std/): Enables functionality relying on the standard library
18//!   and also activates the `alloc` feature. Currently only adds [std::error::Error] support for the
19//!   library error types.
20//! - [`alloc`](https://doc.rust-lang.org/alloc/): Enables features which operate on containers
21//!   like [alloc::vec::Vec](https://doc.rust-lang.org/beta/alloc/vec/struct.Vec.html).
22//!   Enabled by the `std` feature.
23//!
24//! ### Optional features
25//!
26//! - [`defmt`](https://docs.rs/defmt/latest/defmt/): Adds `defmt::Format` derives on some data
27//!   structures and error types.
28//! - [`serde`](https://serde.rs/): Adds `serde` derives on some data structures and error types.
29#![no_std]
30#![cfg_attr(docsrs, feature(doc_cfg))]
31#[cfg(feature = "alloc")]
32extern crate alloc;
33#[cfg(feature = "std")]
34extern crate std;
35
36// In the future, don't do this.
37mod dec;
38mod enc;
39pub use crate::dec::*;
40pub use crate::enc::*;
41
42/// Calculates the maximum overhead when encoding a message with the given length.
43/// The overhead is a maximum of [n/254] bytes (one in 254 bytes) rounded up.
44#[inline]
45pub const fn max_encoding_overhead(source_len: usize) -> usize {
46    if source_len == 0 {
47        return 1;
48    }
49    source_len.div_ceil(254)
50}
51
52/// Calculates the maximum possible size of an encoded message given the length
53/// of the source message. This may be useful for calculating how large the
54/// `dest` buffer needs to be in the encoding functions.
55#[inline]
56pub const fn max_encoding_length(source_len: usize) -> usize {
57    source_len + max_encoding_overhead(source_len)
58}
59
60#[cfg(test)]
61mod tests {
62    use super::*;
63    use std::vec;
64    use std::vec::Vec;
65
66    // Usable in const context
67    const ENCODED_BUF: [u8; max_encoding_length(5)] = [0; max_encoding_length(5)];
68
69    pub(crate) fn test_encode_decode_free_functions(source: &[u8], encoded: &[u8]) {
70        let mut test_encoded = encoded.to_vec();
71        let mut test_decoded = source.to_vec();
72
73        // Mangle data to ensure data is re-populated correctly
74        test_encoded.iter_mut().for_each(|i| *i = 0x80);
75        encode(source, &mut test_encoded[..]);
76        test_encoded.push(0x00);
77
78        // Mangle data to ensure data is re-populated correctly
79        test_decoded.iter_mut().for_each(|i| *i = 0x80);
80        decode(&test_encoded, &mut test_decoded[..]).unwrap();
81        assert_eq!(encoded, &test_encoded[..test_encoded.len() - 1]);
82        assert_eq!(source, test_decoded);
83    }
84
85    pub(crate) fn test_decode_in_place(source: &[u8], encoded: &[u8]) {
86        let mut test_encoded = encoded.to_vec();
87        let report = decode_in_place_report(&mut test_encoded).unwrap();
88        assert_eq!(&test_encoded[0..report.frame_size()], source);
89        assert_eq!(report.parsed_size(), encoded.len());
90
91        test_encoded = encoded.to_vec();
92        let result = decode_in_place(&mut test_encoded).unwrap();
93        assert_eq!(&test_encoded[0..result], source);
94    }
95
96    pub(crate) fn test_pair(source: &[u8], encoded: &[u8]) {
97        test_encode_decode_free_functions(source, encoded);
98        test_decode_in_place(source, encoded);
99    }
100
101    #[test]
102    fn test_buf_len() {
103        assert_eq!(ENCODED_BUF.len(), 6);
104    }
105
106    #[test]
107    fn test_overhead_empty() {
108        assert_eq!(max_encoding_overhead(0), 1);
109    }
110
111    #[test]
112    fn test_overhead_one() {
113        assert_eq!(max_encoding_overhead(1), 1);
114    }
115
116    #[test]
117    fn test_overhead_larger() {
118        assert_eq!(max_encoding_overhead(253), 1);
119        assert_eq!(max_encoding_overhead(254), 1);
120    }
121
122    #[test]
123    fn test_overhead_two() {
124        assert_eq!(max_encoding_overhead(255), 2);
125    }
126
127    #[test]
128    fn stream_chunk_with_2_frames() {
129        let mut dest: [u8; 32] = [0; 32];
130        let data = b"hello world";
131        let data2 = b"second";
132        let mut encoded_data: [u8; 32] = [0; 32];
133        // Sentinel byte at start.
134        encoded_data[0] = 0x00;
135        let mut encoded_len = 1;
136        encoded_len += encode(data, &mut encoded_data[encoded_len..]);
137        // Sentinel byte at end.
138        encoded_data[encoded_len] = 0x00;
139        encoded_len += 1;
140        let first_frame_len = encoded_len;
141        encoded_data[encoded_len] = 0x00;
142        encoded_len += 1;
143        encoded_len += encode(data2, &mut encoded_data[encoded_len..]);
144        encoded_data[encoded_len] = 0x00;
145        encoded_len += 1;
146        let second_frame_len = encoded_len - first_frame_len;
147        let mut decoder = CobsDecoder::new(&mut dest);
148        let result = decoder.push(&encoded_data[0..encoded_len]).unwrap();
149        assert!(result.is_some());
150        let frame_report = result.unwrap();
151        assert_eq!(frame_report.frame_size(), data.len());
152        assert_eq!(frame_report.parsed_size(), first_frame_len);
153        // Now insert the rest of the data.
154        let result = decoder
155            .push(&encoded_data[frame_report.parsed_size()..])
156            .unwrap();
157        assert!(result.is_some());
158        let frame_report = result.unwrap();
159        assert_eq!(frame_report.frame_size(), data2.len());
160        assert_eq!(frame_report.parsed_size(), second_frame_len);
161    }
162
163    #[test]
164    fn decoding_broken_packet() {
165        let mut dest: [u8; 32] = [0; 32];
166        let data = b"hello world";
167        let mut encoded_data: [u8; 32] = [0; 32];
168        encode(data, &mut encoded_data[1..]);
169        let mut encoded_len = encode(data, &mut encoded_data[6..]);
170        // Sentinel byte at start and end.
171        encoded_data[0] = 0x00;
172        // Another frame abruptly starts. This simulates a broken frame, and the streaming decoder
173        // should be able to recover from this.
174        encoded_data[5] = 0x00;
175        encoded_data[5 + encoded_len + 1] = 0x00;
176        encoded_len = 5 + encoded_len + 2;
177        let mut decoder = CobsDecoder::new(&mut dest);
178        for (idx, byte) in encoded_data.iter().take(encoded_len - 1).enumerate() {
179            if idx == 5 {
180                if let Err(DecodeError::InvalidFrame { decoded_bytes }) = decoder.push(&[*byte]) {
181                    assert_eq!(decoded_bytes, 3);
182                }
183            } else {
184                decoder.push(&[*byte]).unwrap();
185            }
186        }
187        if let Ok(Some(msg_size)) = decoder.feed(encoded_data[encoded_len - 1]) {
188            assert_eq!(msg_size, data.len());
189            assert_eq!(data, &decoder.dest()[0..msg_size]);
190        } else {
191            panic!("decoding call did not yield expected frame");
192        }
193    }
194
195    #[test]
196    fn stream_roundtrip() {
197        for ct in 1..=1000 {
198            let source: alloc::vec::Vec<u8> =
199                (ct..2 * ct).map(|x: usize| (x & 0xFF) as u8).collect();
200
201            let mut dest = alloc::vec![0u8; max_encoding_length(source.len())];
202
203            let encoded_size = {
204                let mut encoder = CobsEncoder::new(&mut dest);
205
206                for chunk in source.chunks(17) {
207                    encoder.push(chunk).unwrap();
208                }
209                encoder.finalize()
210            };
211
212            let mut decoded = source.clone();
213            decoded.iter_mut().for_each(|i| *i = 0x80);
214            let decoded_size = {
215                let mut decoder = CobsDecoder::new(&mut decoded);
216
217                for chunk in dest[0..encoded_size].chunks(11) {
218                    decoder.push(chunk).unwrap();
219                }
220
221                match decoder.feed(0) {
222                    Ok(sz_msg) => sz_msg.unwrap(),
223                    Err(written) => panic!("decoding failed, {} bytes written to output", written),
224                }
225            };
226
227            assert_eq!(decoded_size, source.len());
228            assert_eq!(source, decoded);
229        }
230    }
231
232    #[test]
233    fn test_max_encoding_length() {
234        assert_eq!(max_encoding_length(0), 1);
235        assert_eq!(max_encoding_length(253), 254);
236        assert_eq!(max_encoding_length(254), 255);
237        assert_eq!(max_encoding_length(255), 257);
238        assert_eq!(max_encoding_length(254 * 2), 255 * 2);
239        assert_eq!(max_encoding_length(254 * 2 + 1), 256 * 2);
240    }
241
242    #[test]
243    fn wikipedia_ex_6() {
244        let mut unencoded: Vec<u8> = vec![];
245
246        (1..=0xFE).for_each(|i| unencoded.push(i));
247
248        // NOTE: trailing 0x00 is implicit
249        let mut encoded: Vec<u8> = vec![];
250        encoded.push(0xFF);
251        (1..=0xFE).for_each(|i| encoded.push(i));
252
253        test_pair(&unencoded, &encoded);
254    }
255
256    #[test]
257    fn wikipedia_ex_7() {
258        let mut unencoded: Vec<u8> = vec![];
259
260        (0..=0xFE).for_each(|i| unencoded.push(i));
261
262        // NOTE: trailing 0x00 is implicit
263        let mut encoded: Vec<u8> = vec![];
264        encoded.push(0x01);
265        encoded.push(0xFF);
266        (1..=0xFE).for_each(|i| encoded.push(i));
267
268        test_pair(&unencoded, &encoded);
269    }
270
271    #[test]
272    fn wikipedia_ex_8() {
273        let mut unencoded: Vec<u8> = vec![];
274
275        (1..=0xFF).for_each(|i| unencoded.push(i));
276
277        // NOTE: trailing 0x00 is implicit
278        let mut encoded: Vec<u8> = vec![];
279        encoded.push(0xFF);
280        (1..=0xFE).for_each(|i| encoded.push(i));
281        encoded.push(0x02);
282        encoded.push(0xFF);
283
284        test_pair(&unencoded, &encoded);
285    }
286
287    #[test]
288    fn wikipedia_ex_9() {
289        let mut unencoded: Vec<u8> = vec![];
290
291        (2..=0xFF).for_each(|i| unencoded.push(i));
292        unencoded.push(0x00);
293
294        // NOTE: trailing 0x00 is implicit
295        let mut encoded: Vec<u8> = vec![];
296        encoded.push(0xFF);
297        (2..=0xFF).for_each(|i| encoded.push(i));
298        encoded.push(0x01);
299        encoded.push(0x01);
300
301        test_pair(&unencoded, &encoded);
302    }
303
304    #[test]
305    fn wikipedia_ex_10() {
306        let mut unencoded: Vec<u8> = vec![];
307
308        (3..=0xFF).for_each(|i| unencoded.push(i));
309        unencoded.push(0x00);
310        unencoded.push(0x01);
311
312        // NOTE: trailing 0x00 is implicit
313        let mut encoded: Vec<u8> = vec![];
314        encoded.push(0xFE);
315        (3..=0xFF).for_each(|i| encoded.push(i));
316        encoded.push(0x02);
317        encoded.push(0x01);
318
319        test_pair(&unencoded, &encoded);
320    }
321
322    #[test]
323    fn issue_15() {
324        // Reported: https://github.com/awelkie/cobs.rs/issues/15
325
326        let my_string_buf = b"\x00\x11\x00\x22";
327        let max_len = max_encoding_length(my_string_buf.len());
328        assert!(max_len < 128);
329        let mut buf = [0u8; 128];
330
331        let len = encode_with_sentinel(my_string_buf, &mut buf, b'\x00');
332
333        let cobs_buf = &buf[0..len];
334
335        let mut decoded_dest_buf = [0u8; 128];
336        let new_len = decode_with_sentinel(cobs_buf, &mut decoded_dest_buf, b'\x00').unwrap();
337        let decoded_buf = &decoded_dest_buf[0..new_len];
338
339        assert_eq!(my_string_buf, decoded_buf);
340    }
341
342    #[test]
343    fn issue_19_test_254_block_all_ones() {
344        let src: [u8; 254] = [1; 254];
345        let mut dest: [u8; 256] = [0; 256];
346        let encode_len = encode(&src, &mut dest);
347        assert_eq!(encode_len, 255);
348        let mut decoded: [u8; 254] = [1; 254];
349        let result = decode(&dest, &mut decoded).expect("decoding failed");
350        assert_eq!(result.frame_size(), 254);
351        assert_eq!(result.parsed_size(), 256);
352        assert_eq!(&src, &decoded);
353    }
354
355    #[cfg(feature = "alloc")]
356    mod alloc_tests {
357        use super::*;
358        use quickcheck::{TestResult, quickcheck};
359
360        #[test]
361        fn test_roundtrip_1() {
362            test_roundtrip(&[1, 2, 3]);
363        }
364
365        #[test]
366        fn test_roundtrip_2() {
367            for i in 0..5usize {
368                let mut v = Vec::new();
369                for j in 0..252 + i {
370                    v.push(j as u8);
371                }
372                test_roundtrip(&v);
373            }
374        }
375
376        fn identity(source: Vec<u8>, sentinel: u8) -> TestResult {
377            let encoded = encode_vec_with_sentinel(&source[..], sentinel);
378
379            if source.is_empty() {
380                return TestResult::passed();
381            }
382
383            // Check that the sentinel doesn't show up in the encoded message
384            for x in encoded.iter() {
385                if *x == sentinel {
386                    return TestResult::error("Sentinel found in encoded message.");
387                }
388            }
389
390            // Check that the decoding the encoded message returns the original message
391            match decode_vec_with_sentinel(&encoded[..], sentinel) {
392                Ok(decoded) => {
393                    if source == decoded {
394                        TestResult::passed()
395                    } else {
396                        TestResult::failed()
397                    }
398                }
399                Err(_) => TestResult::error("decoding Error"),
400            }
401        }
402
403        #[test]
404        fn test_encode_decode_with_sentinel() {
405            quickcheck(identity as fn(Vec<u8>, u8) -> TestResult);
406        }
407
408        #[test]
409        fn test_encode_decode() {
410            fn identity_default_sentinel(source: Vec<u8>) -> TestResult {
411                identity(source, 0)
412            }
413            quickcheck(identity_default_sentinel as fn(Vec<u8>) -> TestResult);
414        }
415
416        fn test_roundtrip(source: &[u8]) {
417            let mut encoded = encode_vec(source);
418            // Terminate the frame.
419            encoded.push(0x00);
420            let decoded = decode_vec(&encoded).expect("decode_vec");
421            assert_eq!(source, decoded);
422        }
423    }
424}