postcard_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(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/// Encodes the `source` buffer into the `dest` buffer using an
174/// arbitrary sentinel value.
175///
176/// This is done by first encoding the message with the typical sentinel value
177/// of 0, then XOR-ing each byte of the encoded message with the chosen sentinel
178/// value. This will ensure that the sentinel value doesn't show up in the encoded
179/// message. See the paper "Consistent Overhead Byte Stuffing" for details.
180pub fn encode_with_sentinel(source: &[u8], dest: &mut[u8], sentinel: u8) -> usize {
181 let encoded_size = encode(source, dest);
182 for x in &mut dest[..encoded_size] {
183 *x ^= sentinel;
184 }
185 return encoded_size;
186}
187
188#[cfg(feature = "use_std")]
189/// Encodes the `source` buffer into a vector.
190pub fn encode_vec(source: &[u8]) -> Vec<u8> {
191 let mut encoded = vec![0; max_encoding_length(source.len())];
192 let encoded_len = encode(source, &mut encoded[..]);
193 encoded.truncate(encoded_len);
194 return encoded;
195}
196
197#[cfg(feature = "use_std")]
198/// Encodes the `source` buffer into a vector with an arbitrary sentinel value.
199pub fn encode_vec_with_sentinel(source: &[u8], sentinel: u8) -> Vec<u8> {
200 let mut encoded = vec![0; max_encoding_length(source.len())];
201 let encoded_len = encode_with_sentinel(source, &mut encoded[..], sentinel);
202 encoded.truncate(encoded_len);
203 return encoded;
204}