cobs_io_async/codec/
decode.rs1use core::convert::Infallible;
2
3use crate::error::*;
4use crate::sync::DecodedFrame;
5
6#[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 pub consumed: u64,
25
26 pub written: u64,
32
33 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
87pub(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}