@@ -63,6 +63,28 @@ pub fn encode(src: &[u8], dst: &mut [u8]) -> usize {
6363 write_index
6464}
6565
66+ /// Encodes `src` with basic COBS into `dst` using an arbitrary `sentinel` byte
67+ /// instead of `0x00`, returning the number of bytes written.
68+ ///
69+ /// The output never contains the `sentinel` byte, so `sentinel` (rather than
70+ /// `0x00`) may be used to delimit packets. Choosing a sentinel that rarely
71+ /// occurs in your payload minimises overhead. `sentinel == 0` is identical to
72+ /// [`encode`].
73+ ///
74+ /// # Panics
75+ ///
76+ /// Panics if `dst` is shorter than [`max_encoded_len`]`(src.len())`.
77+ #[ must_use]
78+ pub fn encode_with_sentinel ( src : & [ u8 ] , dst : & mut [ u8 ] , sentinel : u8 ) -> usize {
79+ let n = encode ( src, dst) ;
80+ if sentinel != 0 {
81+ for byte in & mut dst[ ..n] {
82+ * byte ^= sentinel;
83+ }
84+ }
85+ n
86+ }
87+
6688/// Decodes basic-COBS `src` into `dst`, returning the number of bytes written.
6789///
6890/// The empty input decodes to nothing (returns `0`). `src` must be a single
@@ -74,6 +96,23 @@ pub fn encode(src: &[u8], dst: &mut [u8]) -> usize {
7496/// not valid COBS, or [`DecodeError::OutputTooSmall`] if `dst` is shorter than
7597/// the decoded output (which never exceeds `src.len()`).
7698pub fn decode ( src : & [ u8 ] , dst : & mut [ u8 ] ) -> Result < usize , DecodeError > {
99+ decode_with_sentinel ( src, dst, 0 )
100+ }
101+
102+ /// Decodes basic-COBS `src` that was encoded with an arbitrary `sentinel` byte
103+ /// (see [`encode_with_sentinel`]) into `dst`, returning the number of bytes
104+ /// written. `sentinel == 0` is identical to [`decode`].
105+ ///
106+ /// # Errors
107+ ///
108+ /// Returns [`DecodeError::ZeroByte`] (the encoded input contained the
109+ /// `sentinel`) or [`DecodeError::Truncated`] if `src` is not valid, or
110+ /// [`DecodeError::OutputTooSmall`] if `dst` is shorter than the decoded output.
111+ pub fn decode_with_sentinel (
112+ src : & [ u8 ] ,
113+ dst : & mut [ u8 ] ,
114+ sentinel : u8 ,
115+ ) -> Result < usize , DecodeError > {
77116 let src_len = src. len ( ) ;
78117 if src_len == 0 {
79118 return Ok ( 0 ) ;
@@ -83,15 +122,15 @@ pub fn decode(src: &[u8], dst: &mut [u8]) -> Result<usize, DecodeError> {
83122 let mut index = 0 ;
84123
85124 loop {
86- let code = src[ index] ;
125+ let code = src[ index] ^ sentinel ;
87126 if code == 0 {
88127 return Err ( DecodeError :: ZeroByte { index } ) ;
89128 }
90129 index += 1 ;
91130 let block_end = index + usize:: from ( code) - 1 ;
92131 let copy_end = block_end. min ( src_len) ;
93132 while index < copy_end {
94- let byte = src[ index] ;
133+ let byte = src[ index] ^ sentinel ;
95134 if byte == 0 {
96135 return Err ( DecodeError :: ZeroByte { index } ) ;
97136 }
@@ -119,6 +158,74 @@ pub fn decode(src: &[u8], dst: &mut [u8]) -> Result<usize, DecodeError> {
119158 Ok ( write_index)
120159}
121160
161+ /// Decodes basic-COBS data in place, overwriting `buf` with the decoded output
162+ /// and returning its length. The decoded bytes occupy `buf[..len]`.
163+ ///
164+ /// This needs no output buffer: COBS decoding never expands, so the write
165+ /// position always trails the read position.
166+ ///
167+ /// # Errors
168+ ///
169+ /// Returns [`DecodeError::ZeroByte`] or [`DecodeError::Truncated`] if `buf` is
170+ /// not valid COBS. [`DecodeError::OutputTooSmall`] is never returned.
171+ pub fn decode_in_place ( buf : & mut [ u8 ] ) -> Result < usize , DecodeError > {
172+ decode_in_place_with_sentinel ( buf, 0 )
173+ }
174+
175+ /// Decodes basic-COBS data that was encoded with an arbitrary `sentinel` byte in
176+ /// place, overwriting `buf` with the decoded output and returning its length.
177+ /// `sentinel == 0` is identical to [`decode_in_place`].
178+ ///
179+ /// # Errors
180+ ///
181+ /// Returns [`DecodeError::ZeroByte`] or [`DecodeError::Truncated`] if `buf` is
182+ /// not valid. [`DecodeError::OutputTooSmall`] is never returned.
183+ pub fn decode_in_place_with_sentinel ( buf : & mut [ u8 ] , sentinel : u8 ) -> Result < usize , DecodeError > {
184+ let src_len = buf. len ( ) ;
185+ if src_len == 0 {
186+ return Ok ( 0 ) ;
187+ }
188+
189+ let mut write_index = 0 ;
190+ let mut index = 0 ;
191+
192+ loop {
193+ let code = buf[ index] ^ sentinel;
194+ if code == 0 {
195+ return Err ( DecodeError :: ZeroByte { index } ) ;
196+ }
197+ index += 1 ;
198+ let block_end = index + usize:: from ( code) - 1 ;
199+ let copy_end = block_end. min ( src_len) ;
200+ while index < copy_end {
201+ let byte = buf[ index] ^ sentinel;
202+ if byte == 0 {
203+ return Err ( DecodeError :: ZeroByte { index } ) ;
204+ }
205+ // write_index < index throughout, so this never clobbers unread
206+ // input.
207+ buf[ write_index] = byte;
208+ write_index += 1 ;
209+ index += 1 ;
210+ }
211+ if block_end > src_len {
212+ return Err ( DecodeError :: Truncated {
213+ index : block_end - usize:: from ( code) ,
214+ } ) ;
215+ }
216+ if block_end < src_len {
217+ if code < 0xFF {
218+ buf[ write_index] = 0 ;
219+ write_index += 1 ;
220+ }
221+ } else {
222+ break ;
223+ }
224+ }
225+
226+ Ok ( write_index)
227+ }
228+
122229/// Encodes `src` with basic COBS, returning a newly allocated [`Vec`].
123230#[ cfg( feature = "alloc" ) ]
124231#[ must_use]
@@ -129,6 +236,17 @@ pub fn encode_to_vec(src: &[u8]) -> Vec<u8> {
129236 dst
130237}
131238
239+ /// Encodes `src` with basic COBS and an arbitrary `sentinel` byte, returning a
240+ /// newly allocated [`Vec`].
241+ #[ cfg( feature = "alloc" ) ]
242+ #[ must_use]
243+ pub fn encode_to_vec_with_sentinel ( src : & [ u8 ] , sentinel : u8 ) -> Vec < u8 > {
244+ let mut dst = alloc:: vec![ 0u8 ; max_encoded_len( src. len( ) ) ] ;
245+ let n = encode_with_sentinel ( src, & mut dst, sentinel) ;
246+ dst. truncate ( n) ;
247+ dst
248+ }
249+
132250/// Decodes basic-COBS `src`, returning a newly allocated [`Vec`].
133251///
134252/// # Errors
@@ -141,3 +259,17 @@ pub fn decode_to_vec(src: &[u8]) -> Result<Vec<u8>, DecodeError> {
141259 dst. truncate ( n) ;
142260 Ok ( dst)
143261}
262+
263+ /// Decodes basic-COBS `src` that was encoded with an arbitrary `sentinel` byte,
264+ /// returning a newly allocated [`Vec`].
265+ ///
266+ /// # Errors
267+ ///
268+ /// Returns a [`DecodeError`] if `src` is not valid.
269+ #[ cfg( feature = "alloc" ) ]
270+ pub fn decode_to_vec_with_sentinel ( src : & [ u8 ] , sentinel : u8 ) -> Result < Vec < u8 > , DecodeError > {
271+ let mut dst = alloc:: vec![ 0u8 ; src. len( ) ] ;
272+ let n = decode_with_sentinel ( src, & mut dst, sentinel) ?;
273+ dst. truncate ( n) ;
274+ Ok ( dst)
275+ }
0 commit comments