cobs/
enc.rs

1#[cfg(feature = "use_std")]
2use crate::max_encoding_length;
3
4/// The [`CobsEncoder`] type is used to encode a stream of bytes to a
5/// given mutable output slice. This is often useful when heap data
6/// structures are not available, or when not all message bytes are
7/// received at a single point in time.
8#[derive(Debug)]
9pub struct CobsEncoder<'a> {
10    dest: &'a mut [u8],
11    dest_idx: usize,
12    state: EncoderState,
13}
14
15/// The [`EncoderState`] is used to track the current state of a
16/// streaming encoder. This struct does not contain the output buffer
17/// (or a reference to one), and can be used when streaming the encoded
18/// output to a custom data type
19///
20/// **IMPORTANT NOTE**: When implementing a custom streaming encoder,
21/// the [`EncoderState`] state machine assumes that the output buffer
22/// **ALREADY** contains a single placeholder byte, and no other bytes.
23/// This placeholder byte will be later modified with the first distance
24/// to the next header/zero byte.
25#[derive(Clone, Debug)]
26pub struct EncoderState {
27    code_idx: usize,
28    num_bt_sent: u8,
29    offset_idx: u8,
30}
31
32/// [`PushResult`] is used to represent the changes to an (encoded)
33/// output data buffer when an unencoded byte is pushed into [`EncoderState`].
34pub enum PushResult {
35    /// The returned byte should be placed at the current end of the data buffer
36    AddSingle(u8),
37
38    /// The byte at the given index should be replaced with the given byte.
39    /// Additionally, a placeholder byte should be inserted at the current
40    /// end of the output buffer to be later modified
41    ModifyFromStartAndSkip((usize, u8)),
42
43    /// The byte at the given index should be replaced with the given byte.
44    /// Then, the last u8 in this tuple should be inserted at the end of the
45    /// current output buffer. Finally, a placeholder byte should be inserted at
46    /// the current end of the output buffer to be later modified
47    ModifyFromStartAndPushAndSkip((usize, u8, u8))
48}
49
50impl Default for EncoderState {
51    /// Create a default initial state representation for a COBS encoder
52    fn default() -> Self {
53        Self {
54            code_idx: 0,
55            num_bt_sent: 1,
56            offset_idx: 1,
57        }
58    }
59}
60
61impl EncoderState {
62    /// Push a single unencoded byte into the encoder state machine
63    pub fn push(&mut self, data: u8) -> PushResult {
64        if data == 0 {
65            let ret = PushResult::ModifyFromStartAndSkip((self.code_idx, self.num_bt_sent));
66            self.code_idx += usize::from(self.offset_idx);
67            self.num_bt_sent = 1;
68            self.offset_idx = 1;
69            ret
70        } else {
71            self.num_bt_sent += 1;
72            self.offset_idx += 1;
73
74            if 0xFF == self.num_bt_sent {
75                let ret = PushResult::ModifyFromStartAndPushAndSkip((self.code_idx, self.num_bt_sent, data));
76                self.num_bt_sent = 1;
77                self.code_idx += usize::from(self.offset_idx);
78                self.offset_idx = 1;
79                ret
80            } else {
81                PushResult::AddSingle(data)
82            }
83        }
84    }
85
86    /// Finalize the encoding process for a single message.
87    /// The byte at the given index should be replaced with the given value,
88    /// and the sentinel value (typically 0u8) must be inserted at the current
89    /// end of the output buffer, serving as a framing byte.
90    pub fn finalize(self) -> (usize, u8) {
91        (self.code_idx, self.num_bt_sent)
92    }
93}
94
95impl<'a> CobsEncoder<'a> {
96
97    /// Create a new streaming Cobs Encoder
98    pub fn new(out_buf: &'a mut [u8]) -> CobsEncoder<'a> {
99        CobsEncoder {
100            dest: out_buf,
101            dest_idx: 1,
102            state: EncoderState::default(),
103        }
104    }
105
106    /// Push a slice of data to be encoded
107    pub fn push(&mut self, data: &[u8]) -> Result<(), ()> {
108        // TODO: could probably check if this would fit without
109        // iterating through all data
110        for x in data {
111            use PushResult::*;
112            match self.state.push(*x) {
113                AddSingle(y) => {
114                    *self.dest.get_mut(self.dest_idx)
115                        .ok_or_else(|| ())? = y;
116                }
117                ModifyFromStartAndSkip((idx, mval)) => {
118                    *self.dest.get_mut(idx)
119                        .ok_or_else(|| ())? = mval;
120                }
121                ModifyFromStartAndPushAndSkip((idx, mval, nval1)) => {
122                    *self.dest.get_mut(idx)
123                        .ok_or_else(|| ())? = mval;
124                    *self.dest.get_mut(self.dest_idx)
125                        .ok_or_else(|| ())? = nval1;
126                    self.dest_idx += 1;
127                }
128            }
129
130            // All branches above require advancing the pointer at least once
131            self.dest_idx += 1;
132        }
133
134        Ok(())
135    }
136
137    /// Complete encoding of the output message. Does NOT terminate
138    /// the message with the sentinel value
139    pub fn finalize(self) -> Result<usize, ()> {
140        if self.dest_idx == 1 {
141            return Ok(0);
142        }
143
144        // Get the last index that needs to be fixed
145        let (idx, mval) = self.state.finalize();
146
147        // If the current code index is outside of the destination slice,
148        // we do not need to write it out
149        if let Some(i) = self.dest.get_mut(idx) {
150            *i = mval;
151        }
152
153        return Ok(self.dest_idx);
154    }
155}
156
157/// Encodes the `source` buffer into the `dest` buffer.
158///
159/// This function uses the typical sentinel value of 0. It returns the number of bytes
160/// written to in the `dest` buffer.
161///
162/// # Panics
163///
164/// This function will panic if the `dest` buffer is not large enough for the
165/// encoded message. You can calculate the size the `dest` buffer needs to be with
166/// the `max_encoding_length` function.
167pub fn encode(source: &[u8], dest: &mut[u8]) -> usize {
168    let mut enc = CobsEncoder::new(dest);
169    enc.push(source).unwrap();
170    enc.finalize().unwrap()
171}
172
173/// Attempts to encode the `source` buffer into the `dest` buffer.
174///
175/// This function uses the typical sentinel value of 0. It returns the number of bytes
176/// written to in the `dest` buffer.
177///
178/// If the destination buffer does not have enough room, an error will be returned
179pub fn try_encode(source: &[u8], dest: &mut[u8]) -> Result<usize, ()> {
180    let mut enc = CobsEncoder::new(dest);
181    enc.push(source)?;
182    enc.finalize()
183}
184
185/// Encodes the `source` buffer into the `dest` buffer using an
186/// arbitrary sentinel value.
187///
188/// This is done by first encoding the message with the typical sentinel value
189/// of 0, then XOR-ing each byte of the encoded message with the chosen sentinel
190/// value. This will ensure that the sentinel value doesn't show up in the encoded
191/// message. See the paper "Consistent Overhead Byte Stuffing" for details.
192pub fn encode_with_sentinel(source: &[u8], dest: &mut[u8], sentinel: u8) -> usize {
193    let encoded_size = encode(source, dest);
194    for x in &mut dest[..encoded_size] {
195        *x ^= sentinel;
196    }
197    return encoded_size;
198}
199
200#[cfg(feature = "use_std")]
201/// Encodes the `source` buffer into a vector.
202pub fn encode_vec(source: &[u8]) -> Vec<u8> {
203    let mut encoded = vec![0; max_encoding_length(source.len())];
204    let encoded_len = encode(source, &mut encoded[..]);
205    encoded.truncate(encoded_len);
206    return encoded;
207}
208
209#[cfg(feature = "use_std")]
210/// Encodes the `source` buffer into a vector with an arbitrary sentinel value.
211pub fn encode_vec_with_sentinel(source: &[u8], sentinel: u8) -> Vec<u8> {
212    let mut encoded = vec![0; max_encoding_length(source.len())];
213    let encoded_len = encode_with_sentinel(source, &mut encoded[..], sentinel);
214    encoded.truncate(encoded_len);
215    return encoded;
216}