Skip to main content

cobs_io_async/codec/
decode.rs

1use core::convert::Infallible;
2
3use crate::error::*;
4use crate::sync::DecodedFrame;
5
6/// Progress reported with [`DecodeError::InvalidFrame`](crate::DecodeError::InvalidFrame).
7///
8/// Describes how far decoding got before a delimiter arrived inside an
9/// incomplete COBS block.
10///
11/// The default value contains zero counts and `frame_len: None`.
12///
13/// The `serde` feature enables serialization and deserialization. The `defmt`
14/// feature enables compact diagnostic formatting.
15#[must_use = "Inspect consumed input and frame completion before processing the next chunk"]
16#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Default)]
17#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
18#[cfg_attr(feature = "defmt", derive(defmt::Format))]
19pub struct DecodeProgress {
20    /// Number of encoded bytes processed, including zero padding and the
21    /// invalidating delimiter.
22    ///
23    /// The next frame starts at `source[consumed as usize..]`.
24    pub consumed: u64,
25
26    /// Number of decoded bytes written to the destination before the frame
27    /// was found invalid.
28    ///
29    /// Includes reconstructed payload zeros. The bytes remain in the
30    /// destination.
31    pub written: u64,
32
33    /// Decoded length of a frame completed by a delimiter.
34    ///
35    /// Always `None` in an [`InvalidFrame`](crate::DecodeError::InvalidFrame)
36    /// error.
37    pub frame_len: Option<u64>,
38}
39
40#[derive(Clone, Debug)]
41struct DecoderState {
42    next_sentinel_idx: u8,
43    current_block_is_full: bool,
44}
45
46enum PushResult {
47    AddSingle,
48    WriteSentinel,
49    InvalidFrame,
50    EndOfFrame,
51    Skip,
52}
53
54impl DecoderState {
55    fn new() -> Self {
56        Self {
57            next_sentinel_idx: 0,
58            current_block_is_full: false,
59        }
60    }
61    #[inline]
62    fn push(&mut self, byte: u8) -> PushResult {
63        if byte == 0 {
64            if self.next_sentinel_idx > 1 {
65                self.next_sentinel_idx = 0;
66                return PushResult::InvalidFrame;
67            }
68            self.next_sentinel_idx = 0;
69            return PushResult::EndOfFrame;
70        }
71        if self.next_sentinel_idx <= 1 {
72            let ret = if self.next_sentinel_idx == 0 || self.current_block_is_full {
73                PushResult::Skip
74            } else {
75                PushResult::WriteSentinel
76            };
77            self.current_block_is_full = byte == 0xFF;
78            self.next_sentinel_idx = byte;
79            ret
80        } else {
81            self.next_sentinel_idx -= 1;
82            PushResult::AddSingle
83        }
84    }
85}
86
87/// Decodes the first frame of `source` into `dest`.
88pub(crate) fn decode_slice(
89    source: &[u8],
90    dest: &mut [u8],
91) -> Result<DecodedFrame, DecodeError<Infallible, SeekableError>> {
92    let mut state = DecoderState::new();
93    let mut started = false;
94    let mut written = 0;
95    for (idx, &byte) in source.iter().enumerate() {
96        if !started && byte == 0 {
97            continue;
98        }
99        started = true;
100        let value = match state.push(byte) {
101            PushResult::Skip => continue,
102            PushResult::AddSingle => byte,
103            PushResult::WriteSentinel => 0,
104            PushResult::EndOfFrame => {
105                return Ok(DecodedFrame {
106                    len: written,
107                    consumed: idx + 1,
108                });
109            }
110            PushResult::InvalidFrame => {
111                return Err(DecodeError::InvalidFrame(DecodeProgress {
112                    consumed: idx as u64 + 1,
113                    written: written as u64,
114                    frame_len: None,
115                }));
116            }
117        };
118        *dest
119            .get_mut(written)
120            .ok_or(DecodeError::Destination(SeekableError::OutOfBounds))? = value;
121        written += 1;
122    }
123    if state.next_sentinel_idx > 1 {
124        return Err(DecodeError::UnexpectedSourceEof);
125    }
126    if !started {
127        return Err(DecodeError::EmptyFrame);
128    }
129    Ok(DecodedFrame {
130        len: written,
131        consumed: source.len(),
132    })
133}