Coverage Report

Created: 2026-10-28 07:08

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/cobs-0.3.0/src/dec.rs
Line
Count
Source
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)]
6
pub 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)]
22
pub 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
33
0
fn add(to: &mut [u8], idx: usize, data: u8) -> Result<(), DecodeError> {
34
0
    *to.get_mut(idx).ok_or(DecodeError::TargetBufTooSmall)? = data;
35
0
    Ok(())
36
0
}
37
38
/// [`DecodeResult`] represents the possible non-error outcomes of
39
/// pushing an encoded data byte into the [`DecoderState`] state machine
40
pub 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))]
59
pub 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
71
impl 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
0
    pub fn feed(&mut self, data: u8) -> Result<DecodeResult, DecodeError> {
82
        use DecodeResult::*;
83
        use DecoderState::*;
84
0
        let (ret, state) = match (&self, data) {
85
            // Currently Idle, received a terminator, ignore, stay idle
86
0
            (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
0
            (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
0
            (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
0
            (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
0
            (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
0
            (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
0
            (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
0
            (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
0
            (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
0
            (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
0
            (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
0
            (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
0
            (GrabChain(i), n) => (Ok(DataContinue(n)), GrabChain(*i - 1)),
141
        };
142
143
0
        *self = state;
144
0
        ret
145
0
    }
146
}
147
148
impl<'a> CobsDecoder<'a> {
149
    /// Create a new streaming Cobs Decoder. Provide the output buffer
150
    /// for the decoded message to be placed in
151
0
    pub fn new(dest: &'a mut [u8]) -> CobsDecoder<'a> {
152
0
        CobsDecoder {
153
0
            dest,
154
0
            dest_idx: 0,
155
0
            state: DecoderState::Idle,
156
0
        }
157
0
    }
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
0
    pub fn feed(&mut self, data: u8) -> Result<Option<usize>, DecodeError> {
168
0
        match self.state.feed(data) {
169
0
            Err(_) => Err(DecodeError::InvalidFrame {
170
0
                decoded_bytes: self.dest_idx,
171
0
            }),
172
0
            Ok(DecodeResult::NoData) => Ok(None),
173
0
            Ok(DecodeResult::DataContinue(n)) => {
174
0
                add(self.dest, self.dest_idx, n)?;
175
0
                self.dest_idx += 1;
176
0
                Ok(None)
177
            }
178
0
            Ok(DecodeResult::DataComplete) => Ok(Some(self.dest_idx)),
179
        }
180
0
    }
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
0
    pub fn push(&mut self, data: &[u8]) -> Result<Option<(usize, usize)>, DecodeError> {
192
0
        for (consumed_idx, d) in data.iter().enumerate() {
193
0
            let opt_decoded_bytes = self.feed(*d)?;
194
0
            if let Some(decoded_bytes_ct) = opt_decoded_bytes {
195
                // convert from index to number of bytes consumed
196
0
                return Ok(Some((decoded_bytes_ct, consumed_idx + 1)));
197
0
            }
198
        }
199
200
0
        Ok(None)
201
0
    }
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.
212
0
pub fn decode(source: &[u8], dest: &mut [u8]) -> Result<usize, DecodeError> {
213
0
    if source.is_empty() {
214
0
        return Err(DecodeError::EmptyFrame);
215
0
    }
216
217
0
    let mut dec = CobsDecoder::new(dest);
218
219
    // Did we decode a message, using some or all of the buffer?
220
0
    if let Some((d_used, _s_used)) = dec.push(source)? {
221
0
        return Ok(d_used);
222
0
    }
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
0
    if source.last() != Some(&0) {
228
        // Explicitly push sentinel of zero
229
0
        if let Some((d_used, _s_used)) = dec.push(&[0])? {
230
0
            return Ok(d_used);
231
0
        }
232
0
    }
233
234
    // Nope, no early message, no missing terminator, just failed to decode
235
0
    Err(DecodeError::InvalidFrame {
236
0
        decoded_bytes: dec.dest_idx,
237
0
    })
238
0
}
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))]
244
pub 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.
259
0
pub fn decode_in_place_report(buf: &mut [u8]) -> Result<DecodeReport, DecodeError> {
260
0
    let mut source_index = 0;
261
0
    let mut dest_index = 0;
262
263
0
    if buf.is_empty() {
264
0
        return Err(DecodeError::EmptyFrame);
265
0
    }
266
267
    // Stop at the first terminator, if any
268
0
    let src_end = if let Some(end) = buf.iter().position(|b| *b == 0) {
269
0
        end
270
    } else {
271
0
        buf.len()
272
    };
273
274
0
    while source_index < src_end {
275
0
        let code = buf[source_index];
276
277
0
        if source_index + code as usize > src_end && code != 1 {
278
0
            return Err(DecodeError::InvalidFrame {
279
0
                decoded_bytes: dest_index,
280
0
            });
281
0
        }
282
283
0
        source_index += 1;
284
285
0
        for _ in 1..code {
286
0
            *buf.get_mut(dest_index)
287
0
                .ok_or(DecodeError::TargetBufTooSmall)? = buf[source_index];
288
0
            source_index += 1;
289
0
            dest_index += 1;
290
        }
291
292
0
        if 0xFF != code && source_index < src_end {
293
0
            *buf.get_mut(dest_index)
294
0
                .ok_or(DecodeError::TargetBufTooSmall)? = 0;
295
0
            dest_index += 1;
296
0
        }
297
    }
298
299
0
    Ok(DecodeReport {
300
0
        dst_used: dest_index,
301
0
        src_used: source_index,
302
0
    })
303
0
}
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.
312
0
pub fn decode_in_place(buff: &mut [u8]) -> Result<usize, DecodeError> {
313
0
    decode_in_place_report(buff).map(|r| r.dst_used)
314
0
}
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.
324
0
pub fn decode_with_sentinel(
325
0
    source: &[u8],
326
0
    dest: &mut [u8],
327
0
    sentinel: u8,
328
0
) -> Result<usize, DecodeError> {
329
0
    for (x, y) in source.iter().zip(dest.iter_mut()) {
330
0
        *y = *x ^ sentinel;
331
0
    }
332
0
    decode_in_place(dest)
333
0
}
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.
339
0
pub fn decode_in_place_with_sentinel(buff: &mut [u8], sentinel: u8) -> Result<usize, DecodeError> {
340
0
    for x in buff.iter_mut() {
341
0
        *x ^= sentinel;
342
0
    }
343
0
    decode_in_place(buff)
344
0
}
345
346
#[cfg(feature = "alloc")]
347
/// Decodes the `source` buffer into a vector.
348
pub 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.
357
pub 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)]
368
mod 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
}