[go: up one dir, main page]

cobs/
enc.rs

1#[derive(Debug, PartialEq, Eq, thiserror::Error)]
2#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
3#[cfg_attr(feature = "defmt", derive(defmt::Format))]
4#[error("out of bounds error during encoding")]
5pub struct DestBufTooSmallError;
6
7/// The [`EncoderState`] is used to track the current state of a streaming encoder. This struct
8/// does not contain the output buffer (or a reference to one), and can be used when streaming the
9/// encoded output to a custom data type
10///
11/// **IMPORTANT NOTE**: When implementing a custom streaming encoder,
12/// the [`EncoderState`] state machine assumes that the output buffer
13/// **ALREADY** contains a single placeholder byte, and no other bytes.
14/// This placeholder byte will be later modified with the first distance
15/// to the next header/zero byte.
16#[derive(Clone, Debug)]
17pub struct EncoderState {
18    code_idx: usize,
19    num_bt_sent: u8,
20    offset_idx: u8,
21}
22
23/// [`PushResult`] is used to represent the changes to an (encoded)
24/// output data buffer when an unencoded byte is pushed into [`EncoderState`].
25pub enum PushResult {
26    /// The returned byte should be placed at the current end of the data buffer
27    AddSingle(u8),
28
29    /// The byte at the given index should be replaced with the given byte.
30    /// Additionally, a placeholder byte should be inserted at the current
31    /// end of the output buffer to be later modified
32    ModifyFromStartAndSkip((usize, u8)),
33
34    /// The byte at the given index should be replaced with the given byte.
35    /// Then, the last u8 in this tuple should be inserted at the end of the
36    /// current output buffer. Finally, a placeholder byte should be inserted at
37    /// the current end of the output buffer to be later modified if the encoding process is
38    /// not done yet.
39    ModifyFromStartAndPushAndSkip((usize, u8, u8)),
40}
41
42impl Default for EncoderState {
43    /// Create a default initial state representation for a COBS encoder
44    fn default() -> Self {
45        Self {
46            code_idx: 0,
47            num_bt_sent: 1,
48            offset_idx: 1,
49        }
50    }
51}
52
53impl EncoderState {
54    /// Push a single unencoded byte into the encoder state machine
55    pub fn push(&mut self, data: u8) -> PushResult {
56        if data == 0 {
57            let ret = PushResult::ModifyFromStartAndSkip((self.code_idx, self.num_bt_sent));
58            self.code_idx += usize::from(self.offset_idx);
59            self.num_bt_sent = 1;
60            self.offset_idx = 1;
61            ret
62        } else {
63            self.num_bt_sent += 1;
64            self.offset_idx += 1;
65
66            if 0xFF == self.num_bt_sent {
67                let ret = PushResult::ModifyFromStartAndPushAndSkip((
68                    self.code_idx,
69                    self.num_bt_sent,
70                    data,
71                ));
72                self.num_bt_sent = 1;
73                self.code_idx += usize::from(self.offset_idx);
74                self.offset_idx = 1;
75                ret
76            } else {
77                PushResult::AddSingle(data)
78            }
79        }
80    }
81
82    /// Finalize the encoding process for a single message.
83    /// The byte at the given index should be replaced with the given value,
84    /// and the sentinel value (typically 0u8) must be inserted at the current
85    /// end of the output buffer, serving as a framing byte.
86    pub fn finalize(self) -> (usize, u8) {
87        (self.code_idx, self.num_bt_sent)
88    }
89}
90
91/// The [`CobsEncoder`] type is used to encode a stream of bytes to a given mutable output slice.
92///
93/// This is often useful when heap data structures are not available, or when not all message bytes
94/// are received at a single point in time.
95#[derive(Debug)]
96pub struct CobsEncoder<'a> {
97    dest: &'a mut [u8],
98    dest_idx: usize,
99    state: EncoderState,
100    might_be_done: bool,
101}
102
103impl<'a> CobsEncoder<'a> {
104    /// Create a new streaming Cobs Encoder.
105    pub fn new(out_buf: &'a mut [u8]) -> CobsEncoder<'a> {
106        CobsEncoder {
107            dest: out_buf,
108            dest_idx: 1,
109            state: EncoderState::default(),
110            might_be_done: false,
111        }
112    }
113
114    /// Push a slice of data to be encoded
115    pub fn push(&mut self, data: &[u8]) -> Result<(), DestBufTooSmallError> {
116        // TODO: could probably check if this would fit without
117        // iterating through all data
118
119        // There was the possibility that the encoding process is done, but more data is pushed
120        // instead of a `finalize` call, so the destination index needs to be incremented.
121        if self.might_be_done {
122            self.dest_idx += 1;
123            self.might_be_done = false;
124        }
125        for (slice_idx, val) in data.iter().enumerate() {
126            use PushResult::*;
127            match self.state.push(*val) {
128                AddSingle(y) => {
129                    *self
130                        .dest
131                        .get_mut(self.dest_idx)
132                        .ok_or(DestBufTooSmallError)? = y;
133                }
134                ModifyFromStartAndSkip((idx, mval)) => {
135                    *self.dest.get_mut(idx).ok_or(DestBufTooSmallError)? = mval;
136                }
137                ModifyFromStartAndPushAndSkip((idx, mval, nval1)) => {
138                    *self.dest.get_mut(idx).ok_or(DestBufTooSmallError)? = mval;
139                    *self
140                        .dest
141                        .get_mut(self.dest_idx)
142                        .ok_or(DestBufTooSmallError)? = nval1;
143                    // Do not increase index if these is the possibility that we are finished.
144                    if slice_idx == data.len() - 1 {
145                        // If push is called again, the index will be incremented. If finalize
146                        // is called, there is no need to increment the index.
147                        self.might_be_done = true;
148                    } else {
149                        self.dest_idx += 1;
150                    }
151                }
152            }
153
154            // All branches above require advancing the pointer at least once
155            self.dest_idx += 1;
156        }
157
158        Ok(())
159    }
160
161    /// Complete encoding of the output message. Does NOT terminate the message with the sentinel
162    /// value.
163    pub fn finalize(self) -> usize {
164        // Get the last index that needs to be fixed
165        let (idx, mval) = if self.dest_idx == 0 {
166            (0, 0x01)
167        } else {
168            self.state.finalize()
169        };
170
171        // If the current code index is outside of the destination slice,
172        // we do not need to write it out
173        if let Some(i) = self.dest.get_mut(idx) {
174            *i = mval;
175        }
176
177        self.dest_idx
178    }
179}
180
181/// Encodes the `source` buffer into the `dest` buffer.
182///
183/// This function assumes the typical sentinel value of 0, but does not terminate the encoded
184/// message with the sentinel value. This should be done by the caller to ensure proper framing.
185///
186/// # Returns
187///
188/// The number of bytes written to in the `dest` buffer.
189///
190/// # Panics
191///
192/// This function will panic if the `dest` buffer is not large enough for the
193/// encoded message. You can calculate the size the `dest` buffer needs to be with
194/// the [crate::max_encoding_length] function.
195pub fn encode(source: &[u8], dest: &mut [u8]) -> usize {
196    let mut enc = CobsEncoder::new(dest);
197    enc.push(source).unwrap();
198    enc.finalize()
199}
200
201/// Encodes the `source` buffer into the `dest` buffer, including the default sentinel values 0
202/// around the encoded frame.
203///
204/// # Returns
205///
206/// The number of bytes written to in the `dest` buffer.
207///
208/// # Panics
209///
210/// This function will panic if the `dest` buffer is not large enough for the
211/// encoded message. You can calculate the size the `dest` buffer needs to be with
212/// the [crate::max_encoding_length] function.
213pub fn encode_including_sentinels(source: &[u8], dest: &mut [u8]) -> usize {
214    if dest.len() < 2 {
215        panic!("destination buffer too small");
216    }
217
218    dest[0] = 0;
219    let mut enc = CobsEncoder::new(&mut dest[1..]);
220    enc.push(source).unwrap();
221    let encoded_len = enc.finalize();
222    dest[encoded_len + 1] = 0;
223    encoded_len + 2
224}
225
226/// Attempts to encode the `source` buffer into the `dest` buffer.
227///
228/// This function assumes the typical sentinel value of 0, but does not terminate the encoded
229/// message with the sentinel value. This should be done by the caller to ensure proper framing.
230///
231/// # Returns
232///
233/// The number of bytes written to in the `dest` buffer.
234///
235/// If the destination buffer does not have enough room, an error will be returned.
236pub fn try_encode(source: &[u8], dest: &mut [u8]) -> Result<usize, DestBufTooSmallError> {
237    let mut enc = CobsEncoder::new(dest);
238    enc.push(source)?;
239    Ok(enc.finalize())
240}
241
242/// Encodes the `source` buffer into the `dest` buffer, including the default sentinel values 0
243/// around the encoded frame.
244///
245/// # Returns
246///
247/// The number of bytes written to in the `dest` buffer.
248///
249/// If the destination buffer does not have enough room, an error will be returned.
250pub fn try_encode_including_sentinels(
251    source: &[u8],
252    dest: &mut [u8],
253) -> Result<usize, DestBufTooSmallError> {
254    if dest.len() < 2 {
255        return Err(DestBufTooSmallError);
256    }
257    dest[0] = 0;
258    let mut enc = CobsEncoder::new(&mut dest[1..]);
259    enc.push(source)?;
260    let encoded_len = enc.finalize();
261    dest[encoded_len + 1] = 0;
262    Ok(encoded_len + 2)
263}
264
265/// Encodes the `source` buffer into the `dest` buffer using an
266/// arbitrary sentinel value.
267///
268/// This is done by first encoding the message with the typical sentinel value
269/// of 0, then XOR-ing each byte of the encoded message with the chosen sentinel
270/// value. This will ensure that the sentinel value doesn't show up in the encoded
271/// message. See the paper "Consistent Overhead Byte Stuffing" for details.
272///
273/// This function does not terminate the encoded message with the sentinel value. This should be
274/// done by the caller to ensure proper framing.
275///
276/// # Returns
277///
278/// The number of bytes written to in the `dest` buffer.
279pub fn encode_with_sentinel(source: &[u8], dest: &mut [u8], sentinel: u8) -> usize {
280    let encoded_size = encode(source, dest);
281    for x in &mut dest[..encoded_size] {
282        *x ^= sentinel;
283    }
284    encoded_size
285}
286
287#[cfg(feature = "alloc")]
288/// Encodes the `source` buffer into a vector, using the [encode] function.
289pub fn encode_vec(source: &[u8]) -> alloc::vec::Vec<u8> {
290    let mut encoded = alloc::vec![0; crate::max_encoding_length(source.len())];
291    let encoded_len = encode(source, &mut encoded[..]);
292    encoded.truncate(encoded_len);
293    encoded
294}
295
296#[cfg(feature = "alloc")]
297/// Encodes the `source` buffer into a vector, using the [encode] function, while also adding
298/// the sentinels around the encoded frame.
299pub fn encode_vec_including_sentinels(source: &[u8]) -> alloc::vec::Vec<u8> {
300    let mut encoded = alloc::vec![0; crate::max_encoding_length(source.len()) + 2];
301    let encoded_len = encode_including_sentinels(source, &mut encoded);
302    encoded.truncate(encoded_len + 2);
303    encoded
304}
305
306#[cfg(feature = "alloc")]
307/// Encodes the `source` buffer into a vector with an arbitrary sentinel value, using the
308/// [encode_with_sentinel] function.
309pub fn encode_vec_with_sentinel(source: &[u8], sentinel: u8) -> alloc::vec::Vec<u8> {
310    let mut encoded = alloc::vec![0; crate::max_encoding_length(source.len())];
311    let encoded_len = encode_with_sentinel(source, &mut encoded[..], sentinel);
312    encoded.truncate(encoded_len);
313    encoded
314}
315
316#[cfg(test)]
317mod tests {
318    use super::*;
319    use crate::{
320        decode_vec,
321        tests::{test_decode_in_place, test_encode_decode_free_functions},
322    };
323
324    #[test]
325    fn test_encode_0() {
326        // An empty input is encoded as no characters.
327        let mut output = [0xFFu8; 16];
328        let used = encode(&[], &mut output);
329        assert_eq!(used, 1);
330        assert_eq!(output[0], 0x01);
331    }
332
333    fn test_pair(source: &[u8], encoded: &[u8]) {
334        test_encode_decode_free_functions(source, encoded);
335        test_decode_in_place(source, encoded);
336    }
337
338    #[test]
339    fn test_encode_1() {
340        test_pair(&[10, 11, 0, 12], &[3, 10, 11, 2, 12])
341    }
342
343    #[test]
344    fn test_encode_empty() {
345        test_pair(&[], &[1])
346    }
347
348    #[test]
349    fn test_encode_2() {
350        test_pair(&[0, 0, 1, 0], &[1, 1, 2, 1, 1])
351    }
352
353    #[test]
354    fn test_encode_3() {
355        test_pair(&[255, 0], &[2, 255, 1])
356    }
357
358    #[test]
359    fn test_encode_4() {
360        test_pair(&[1], &[2, 1])
361    }
362
363    #[test]
364    fn encode_target_buf_too_small() {
365        let source = &[10, 11, 0, 12];
366        let expected = &[3, 10, 11, 2, 12];
367        for len in 0..expected.len() {
368            let mut dest = alloc::vec![0; len];
369            matches!(
370                try_encode(source, &mut dest).unwrap_err(),
371                DestBufTooSmallError
372            );
373        }
374    }
375
376    #[test]
377    fn try_encode_with_sentinels() {
378        let source = &[10, 11, 0, 12];
379        let expected = &[0, 3, 10, 11, 2, 12, 0];
380        let mut dest = alloc::vec![0; expected.len()];
381        let encoded_len = try_encode_including_sentinels(source, &mut dest).unwrap();
382        assert_eq!(encoded_len, expected.len());
383        assert_eq!(dest[0], 0);
384        assert_eq!(dest[expected.len() - 1], 0);
385        assert_eq!(decode_vec(&dest).unwrap(), source);
386    }
387
388    #[test]
389    fn test_encoding_including_sentinels() {
390        let data = [1, 2, 3];
391        let encoded = encode_vec_including_sentinels(&data);
392        assert_eq!(*encoded.first().unwrap(), 0);
393        assert_eq!(*encoded.last().unwrap(), 0);
394        let data_decoded = decode_vec(&encoded).unwrap();
395        assert_eq!(data_decoded, data);
396        let data_decoded = decode_vec(&encoded[1..]).unwrap();
397        assert_eq!(data_decoded, data);
398        let data_decoded = decode_vec(&encoded[1..encoded.len() - 1]).unwrap();
399        assert_eq!(data_decoded, data);
400    }
401
402    #[test]
403    #[should_panic]
404    fn encode_target_buf_too_small_panicking() {
405        let source = &[10, 11, 0, 12];
406        let expected = &[3, 10, 11, 2, 12];
407        encode(source, &mut alloc::vec![0; expected.len() - 1]);
408    }
409}