cobs/
dec.rs

1/// The [`CobsDecoder`] type is used to decode a stream of bytes to a
2/// given mutable output slice. This is often useful when heap data
3/// structures are not available, or when not all message bytes are
4/// received at a single point in time.
5#[derive(Debug)]
6pub struct CobsDecoder<'a> {
7    /// Destination slice for decoded message
8    dest: &'a mut [u8],
9
10    /// Index of next byte to write in `dest`
11    dest_idx: usize,
12
13    /// Decoder state as an enum
14    state: DecoderState,
15}
16
17/// The [`DecoderState`] is used to track the current state of a
18/// streaming decoder. This struct does not contain the output buffer
19/// (or a reference to one), and can be used when streaming the decoded
20/// output to a custom data type.
21#[derive(Debug)]
22pub enum DecoderState {
23    /// State machine has not received any non-zero bytes
24    Idle,
25
26    /// 1-254 bytes, can be header or 00
27    Grab(u8),
28
29    /// 255 bytes, will be a header next
30    GrabChain(u8),
31}
32
33fn add(to: &mut [u8], idx: usize, data: u8) -> Result<(), DecodeError> {
34    *to.get_mut(idx).ok_or(DecodeError::TargetBufTooSmall)? = data;
35    Ok(())
36}
37
38/// [`DecodeResult`] represents the possible non-error outcomes of
39/// pushing an encoded data byte into the [`DecoderState`] state machine
40pub enum DecodeResult {
41    /// The given input byte did not prompt an output byte, either because the
42    /// state machine is still idle, or we have just processed a header byte.
43    /// More data is needed to complete the message.
44    NoData,
45
46    /// We have received a complete and well-encoded COBS message. The
47    /// contents of the associated output buffer may now be used
48    DataComplete,
49
50    /// The following byte should be appended to the current end of the decoded
51    /// output buffer.
52    /// More data is needed to complete the message.
53    DataContinue(u8),
54}
55
56#[derive(Debug, PartialEq, Eq, thiserror::Error)]
57#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
58#[cfg_attr(feature = "defmt", derive(defmt::Format))]
59pub enum DecodeError {
60    #[error("empty input frame")]
61    EmptyFrame,
62    #[error("frame with invalid format, written {decoded_bytes:?} to decoded buffer")]
63    InvalidFrame {
64        /// Number of bytes written to the decoded buffer.
65        decoded_bytes: usize,
66    },
67    #[error("target buffer too small")]
68    TargetBufTooSmall,
69}
70
71impl DecoderState {
72    /// Push a single encoded byte into the state machine. If the input was
73    /// unexpected, such as an early end of a framed message segment, an Error will
74    /// be returned, and the current associated output buffer contents should be discarded.
75    ///
76    /// If a complete message is indicated, the decoding state machine will automatically
77    /// reset itself to the Idle state, and may be used to begin decoding another message.
78    ///
79    /// NOTE: Sentinel value must be included in the input to this function for the
80    /// decoding to complete
81    pub fn feed(&mut self, data: u8) -> Result<DecodeResult, DecodeError> {
82        use DecodeResult::*;
83        use DecoderState::*;
84        let (ret, state) = match (&self, data) {
85            // Currently Idle, received a terminator, ignore, stay idle
86            (Idle, 0x00) => (Ok(NoData), Idle),
87
88            // Currently Idle, received a byte indicating the
89            // next 255 bytes have no zeroes, so we will have 254 unmodified
90            // data bytes, then an overhead byte
91            (Idle, 0xFF) => (Ok(NoData), GrabChain(0xFE)),
92
93            // Currently Idle, received a byte indicating there will be a
94            // zero that must be modified in the next 1..=254 bytes
95            (Idle, n) => (Ok(NoData), Grab(n - 1)),
96
97            // We have reached the end of a data run indicated by an overhead
98            // byte, AND we have recieved the message terminator. This was a
99            // well framed message!
100            (Grab(0), 0x00) => (Ok(DataComplete), Idle),
101
102            // We have reached the end of a data run indicated by an overhead
103            // byte, and the next segment of 254 bytes will have no modified
104            // sentinel bytes
105            (Grab(0), 0xFF) => (Ok(DataContinue(0)), GrabChain(0xFE)),
106
107            // We have reached the end of a data run indicated by an overhead
108            // byte, and we will treat this byte as a modified sentinel byte.
109            // place the sentinel byte in the output, and begin processing the
110            // next non-sentinel sequence
111            (Grab(0), n) => (Ok(DataContinue(0)), Grab(n - 1)),
112
113            // We were not expecting the sequence to terminate, but here we are.
114            // Report an error due to early terminated message
115            (Grab(_), 0) => (Err(DecodeError::InvalidFrame { decoded_bytes: 0 }), Idle),
116
117            // We have not yet reached the end of a data run, decrement the run
118            // counter, and place the byte into the decoded output
119            (Grab(i), n) => (Ok(DataContinue(n)), Grab(*i - 1)),
120
121            // We have reached the end of a data run indicated by an overhead
122            // byte, AND we have recieved the message terminator. This was a
123            // well framed message!
124            (GrabChain(0), 0x00) => (Ok(DataComplete), Idle),
125
126            // We have reached the end of a data run, and we will begin another
127            // data run with an overhead byte expected at the end
128            (GrabChain(0), 0xFF) => (Ok(NoData), GrabChain(0xFE)),
129
130            // We have reached the end of a data run, and we will expect `n` data
131            // bytes unmodified, followed by a sentinel byte that must be modified
132            (GrabChain(0), n) => (Ok(NoData), Grab(n - 1)),
133
134            // We were not expecting the sequence to terminate, but here we are.
135            // Report an error due to early terminated message
136            (GrabChain(_), 0) => (Err(DecodeError::InvalidFrame { decoded_bytes: 0 }), Idle),
137
138            // We have not yet reached the end of a data run, decrement the run
139            // counter, and place the byte into the decoded output
140            (GrabChain(i), n) => (Ok(DataContinue(n)), GrabChain(*i - 1)),
141        };
142
143        *self = state;
144        ret
145    }
146}
147
148impl<'a> CobsDecoder<'a> {
149    /// Create a new streaming Cobs Decoder. Provide the output buffer
150    /// for the decoded message to be placed in
151    pub fn new(dest: &'a mut [u8]) -> CobsDecoder<'a> {
152        CobsDecoder {
153            dest,
154            dest_idx: 0,
155            state: DecoderState::Idle,
156        }
157    }
158
159    /// Push a single byte into the streaming CobsDecoder. Return values mean:
160    ///
161    /// * Ok(None) - State machine okay, more data needed
162    /// * Ok(Some(N)) - A message of N bytes was successfully decoded
163    /// * Err([DecodeError]) - Message decoding failed
164    ///
165    /// NOTE: Sentinel value must be included in the input to this function for the
166    /// decoding to complete
167    pub fn feed(&mut self, data: u8) -> Result<Option<usize>, DecodeError> {
168        match self.state.feed(data) {
169            Err(_) => Err(DecodeError::InvalidFrame {
170                decoded_bytes: self.dest_idx,
171            }),
172            Ok(DecodeResult::NoData) => Ok(None),
173            Ok(DecodeResult::DataContinue(n)) => {
174                add(self.dest, self.dest_idx, n)?;
175                self.dest_idx += 1;
176                Ok(None)
177            }
178            Ok(DecodeResult::DataComplete) => Ok(Some(self.dest_idx)),
179        }
180    }
181
182    /// Push a slice of bytes into the streaming CobsDecoder. Return values mean:
183    ///
184    /// * Ok(None) - State machine okay, more data needed
185    /// * Ok(Some((N, M))) - A message of N bytes was successfully decoded,
186    ///     using M bytes from `data` (and earlier data)
187    /// * Err([DecodeError]) - Message decoding failed
188    ///
189    /// NOTE: Sentinel value must be included in the input to this function for the
190    /// decoding to complete
191    pub fn push(&mut self, data: &[u8]) -> Result<Option<(usize, usize)>, DecodeError> {
192        for (consumed_idx, d) in data.iter().enumerate() {
193            let opt_decoded_bytes = self.feed(*d)?;
194            if let Some(decoded_bytes_ct) = opt_decoded_bytes {
195                // convert from index to number of bytes consumed
196                return Ok(Some((decoded_bytes_ct, consumed_idx + 1)));
197            }
198        }
199
200        Ok(None)
201    }
202}
203
204/// Decodes the `source` buffer into the `dest` buffer.
205///
206/// This function uses the typical sentinel value of 0.
207///
208/// # Failures
209///
210/// This will return `Err(())` if there was a decoding error. Otherwise,
211/// it will return `Ok(n)` where `n` is the length of the decoded message.
212pub fn decode(source: &[u8], dest: &mut [u8]) -> Result<usize, DecodeError> {
213    if source.is_empty() {
214        return Err(DecodeError::EmptyFrame);
215    }
216
217    let mut dec = CobsDecoder::new(dest);
218
219    // Did we decode a message, using some or all of the buffer?
220    if let Some((d_used, _s_used)) = dec.push(source)? {
221        return Ok(d_used);
222    }
223
224    // If we consumed the entire buffer, but did NOT get a message,
225    // AND the message did not end with a zero, try providing one to
226    // complete the decoding.
227    if source.last() != Some(&0) {
228        // Explicitly push sentinel of zero
229        if let Some((d_used, _s_used)) = dec.push(&[0])? {
230            return Ok(d_used);
231        }
232    }
233
234    // Nope, no early message, no missing terminator, just failed to decode
235    Err(DecodeError::InvalidFrame {
236        decoded_bytes: dec.dest_idx,
237    })
238}
239
240/// A report of the source and destination bytes used during in-place decoding
241#[derive(Debug, Copy, Clone)]
242#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
243#[cfg_attr(feature = "defmt", derive(defmt::Format))]
244pub struct DecodeReport {
245    // The number of source bytes used, NOT INCLUDING the sentinel byte,
246    // if there was one.
247    pub src_used: usize,
248
249    // The number of bytes of the source buffer that now include the
250    // decoded result
251    pub dst_used: usize,
252}
253
254/// Decodes a message in-place.
255///
256/// This is the same function as [decode_in_place], but provides a report
257/// of both the number of source bytes consumed as well as the size of the
258/// destination used.
259pub fn decode_in_place_report(buf: &mut [u8]) -> Result<DecodeReport, DecodeError> {
260    let mut source_index = 0;
261    let mut dest_index = 0;
262
263    if buf.is_empty() {
264        return Err(DecodeError::EmptyFrame);
265    }
266
267    // Stop at the first terminator, if any
268    let src_end = if let Some(end) = buf.iter().position(|b| *b == 0) {
269        end
270    } else {
271        buf.len()
272    };
273
274    while source_index < src_end {
275        let code = buf[source_index];
276
277        if source_index + code as usize > src_end && code != 1 {
278            return Err(DecodeError::InvalidFrame {
279                decoded_bytes: dest_index,
280            });
281        }
282
283        source_index += 1;
284
285        for _ in 1..code {
286            *buf.get_mut(dest_index)
287                .ok_or(DecodeError::TargetBufTooSmall)? = buf[source_index];
288            source_index += 1;
289            dest_index += 1;
290        }
291
292        if 0xFF != code && source_index < src_end {
293            *buf.get_mut(dest_index)
294                .ok_or(DecodeError::TargetBufTooSmall)? = 0;
295            dest_index += 1;
296        }
297    }
298
299    Ok(DecodeReport {
300        dst_used: dest_index,
301        src_used: source_index,
302    })
303}
304
305/// Decodes a message in-place.
306///
307/// This is the same function as [decode], but replaces the encoded message
308/// with the decoded message instead of writing to another buffer.
309///
310/// The returned `usize` is the number of bytes used for the DECODED value,
311/// NOT the number of source bytes consumed during decoding.
312pub fn decode_in_place(buff: &mut [u8]) -> Result<usize, DecodeError> {
313    decode_in_place_report(buff).map(|r| r.dst_used)
314}
315
316/// Decodes the `source` buffer into the `dest` buffer using an arbitrary sentinel value.
317///
318/// This is done by XOR-ing each byte of the source message with the chosen sentinel value,
319/// which transforms the message into the same message encoded with a sentinel value of 0.
320/// Then the regular decoding transformation is performed.
321///
322/// The returned `usize` is the number of bytes used for the DECODED value,
323/// NOT the number of source bytes consumed during decoding.
324pub fn decode_with_sentinel(
325    source: &[u8],
326    dest: &mut [u8],
327    sentinel: u8,
328) -> Result<usize, DecodeError> {
329    for (x, y) in source.iter().zip(dest.iter_mut()) {
330        *y = *x ^ sentinel;
331    }
332    decode_in_place(dest)
333}
334
335/// Decodes a message in-place using an arbitrary sentinel value.
336///
337/// The returned `usize` is the number of bytes used for the DECODED value,
338/// NOT the number of source bytes consumed during decoding.
339pub fn decode_in_place_with_sentinel(buff: &mut [u8], sentinel: u8) -> Result<usize, DecodeError> {
340    for x in buff.iter_mut() {
341        *x ^= sentinel;
342    }
343    decode_in_place(buff)
344}
345
346#[cfg(feature = "alloc")]
347/// Decodes the `source` buffer into a vector.
348pub fn decode_vec(source: &[u8]) -> Result<alloc::vec::Vec<u8>, DecodeError> {
349    let mut decoded = alloc::vec![0; source.len()];
350    let n = decode(source, &mut decoded[..])?;
351    decoded.truncate(n);
352    Ok(decoded)
353}
354
355#[cfg(feature = "alloc")]
356/// Decodes the `source` buffer into a vector with an arbitrary sentinel value.
357pub fn decode_vec_with_sentinel(
358    source: &[u8],
359    sentinel: u8,
360) -> Result<alloc::vec::Vec<u8>, DecodeError> {
361    let mut decoded = alloc::vec![0; source.len()];
362    let n = decode_with_sentinel(source, &mut decoded[..], sentinel)?;
363    decoded.truncate(n);
364    Ok(decoded)
365}
366
367#[cfg(test)]
368mod tests {
369
370    use super::*;
371
372    #[test]
373    fn decode_malforemd() {
374        let malformed_buf: [u8; 32] = [
375            68, 69, 65, 68, 66, 69, 69, 70, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
376            0, 0, 0, 0, 0, 0,
377        ];
378        let mut dest_buf: [u8; 32] = [0; 32];
379        if let Err(DecodeError::InvalidFrame { decoded_bytes }) =
380            decode(&malformed_buf, &mut dest_buf)
381        {
382            assert_eq!(decoded_bytes, 7);
383        } else {
384            panic!("decoding worked when it should not have");
385        }
386    }
387
388    #[test]
389    fn decode_empty() {
390        #[cfg(feature = "alloc")]
391        matches!(decode_vec(&[]).unwrap_err(), DecodeError::EmptyFrame);
392        matches!(
393            decode_in_place(&mut []).unwrap_err(),
394            DecodeError::EmptyFrame
395        );
396        matches!(
397            decode(&[], &mut [0; 256]).unwrap_err(),
398            DecodeError::EmptyFrame
399        );
400    }
401
402    #[test]
403    #[cfg(feature = "alloc")]
404    fn decode_target_buf_too_small() {
405        let encoded = &[3, 10, 11, 2, 12];
406        let expected_decoded_len = 4;
407        for i in 0..expected_decoded_len - 1 {
408            let mut dest = alloc::vec![0; i];
409            let result = decode(encoded, &mut dest);
410            assert_eq!(result, Err(DecodeError::TargetBufTooSmall));
411        }
412    }
413}