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}