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}