1pub const FLAG: u8 = 0x7E;
10pub const ESCAPE: u8 = 0x7D;
12pub const ESCAPE_XOR: u8 = 0x20;
14
15const XON: u8 = 0x11;
16const XOFF: u8 = 0x13;
17
18const fn needs_escape(byte: u8) -> bool {
19 matches!(byte, FLAG | ESCAPE | XON | XOFF)
20}
21
22pub fn crc16(data: &[u8]) -> u16 {
24 let mut fcs = 0xFFFFu16;
25 for &byte in data {
26 fcs ^= u16::from(byte);
27 for _ in 0..8 {
28 if fcs & 1 != 0 {
29 fcs = (fcs >> 1) ^ 0x8408;
30 } else {
31 fcs >>= 1;
32 }
33 }
34 }
35 !fcs
36}
37
38pub const fn max_encoded_len(payload_len: usize) -> usize {
43 2 + (payload_len + 2) * 2
44}
45
46#[derive(Clone, Copy, Debug, PartialEq, Eq)]
47pub enum EncodeError {
48 BufferTooSmall,
49}
50
51struct Sink<'a> {
52 out: &'a mut [u8],
53 len: usize,
54}
55
56impl Sink<'_> {
57 fn push(&mut self, byte: u8) -> Result<(), EncodeError> {
58 if self.len >= self.out.len() {
59 return Err(EncodeError::BufferTooSmall);
60 }
61 self.out[self.len] = byte;
62 self.len += 1;
63 Ok(())
64 }
65
66 fn push_escaped(&mut self, byte: u8) -> Result<(), EncodeError> {
67 if needs_escape(byte) {
68 self.push(ESCAPE)?;
69 self.push(byte ^ ESCAPE_XOR)
70 } else {
71 self.push(byte)
72 }
73 }
74}
75
76pub fn encode_frame(payload: &[u8], out: &mut [u8]) -> Result<usize, EncodeError> {
81 let mut sink = Sink { out, len: 0 };
82 sink.push(FLAG)?;
83 for &byte in payload {
84 sink.push_escaped(byte)?;
85 }
86 for byte in crc16(payload).to_le_bytes() {
87 sink.push_escaped(byte)?;
88 }
89 sink.push(FLAG)?;
90 Ok(sink.len)
91}
92
93#[derive(Clone, Copy, Debug, PartialEq, Eq)]
94pub enum DecodeError {
95 Crc,
97 TooShort,
99 TooLong,
101 AbortedEscape,
103}
104
105pub struct Decoder<const N: usize> {
113 buf: [u8; N],
114 len: usize,
115 escaped: bool,
116 overflow: bool,
117}
118
119impl<const N: usize> Default for Decoder<N> {
120 fn default() -> Self {
121 Self::new()
122 }
123}
124
125impl<const N: usize> Decoder<N> {
126 pub const fn new() -> Self {
127 Self {
128 buf: [0; N],
129 len: 0,
130 escaped: false,
131 overflow: false,
132 }
133 }
134
135 pub fn reset(&mut self) {
137 self.len = 0;
138 self.escaped = false;
139 self.overflow = false;
140 }
141
142 pub fn push(&mut self, byte: u8) -> Option<Result<&[u8], DecodeError>> {
148 if byte == FLAG {
149 let escaped = core::mem::replace(&mut self.escaped, false);
150 let overflow = core::mem::replace(&mut self.overflow, false);
151 let len = core::mem::replace(&mut self.len, 0);
152 if escaped {
153 return Some(Err(DecodeError::AbortedEscape));
154 }
155 if len == 0 {
156 return None;
158 }
159 if overflow {
160 return Some(Err(DecodeError::TooLong));
161 }
162 if len < 2 {
163 return Some(Err(DecodeError::TooShort));
164 }
165 let payload_len = len - 2;
166 let received_fcs = [self.buf[payload_len], self.buf[payload_len + 1]];
167 if crc16(&self.buf[..payload_len]).to_le_bytes() != received_fcs {
168 return Some(Err(DecodeError::Crc));
169 }
170 return Some(Ok(&self.buf[..payload_len]));
171 }
172
173 let byte = if byte == ESCAPE {
174 self.escaped = true;
175 return None;
176 } else if core::mem::replace(&mut self.escaped, false) {
177 byte ^ ESCAPE_XOR
178 } else {
179 byte
180 };
181
182 if self.len < N {
183 self.buf[self.len] = byte;
184 self.len += 1;
185 } else {
186 self.overflow = true;
187 }
188 None
189 }
190}
191
192#[cfg(test)]
193mod tests {
194 use super::*;
195
196 fn decode_all<const N: usize>(
198 decoder: &mut Decoder<N>,
199 bytes: &[u8],
200 ) -> Option<Result<Vec<u8>, DecodeError>> {
201 let mut result = None;
202 for &byte in bytes {
203 if let Some(outcome) = decoder.push(byte) {
204 assert!(result.is_none(), "more than one frame in input");
205 result = Some(outcome.map(<[u8]>::to_vec));
206 }
207 }
208 result
209 }
210
211 #[test]
212 fn crc16_check_value() {
213 assert_eq!(crc16(b"123456789"), 0x906E);
215 }
216
217 #[test]
218 fn round_trip_plain() {
219 let payload = [0x81u8, 0x02, 0x00];
220 let mut wire = [0u8; max_encoded_len(3)];
221 let len = encode_frame(&payload, &mut wire).unwrap();
222 assert_eq!(wire[0], FLAG);
223 assert_eq!(wire[len - 1], FLAG);
224
225 let mut decoder = Decoder::<16>::new();
226 let frame = decode_all(&mut decoder, &wire[..len]).unwrap().unwrap();
227 assert_eq!(frame, payload);
228 }
229
230 #[test]
231 fn round_trip_escaped_bytes() {
232 let payload = [FLAG, ESCAPE, XON, XOFF, 0x00, 0xFF];
233 let mut wire = [0u8; max_encoded_len(6)];
234 let len = encode_frame(&payload, &mut wire).unwrap();
235 for &byte in &wire[1..len - 1] {
237 assert_ne!(byte, FLAG);
238 assert!(!needs_escape(byte) || byte == ESCAPE);
239 }
240
241 let mut decoder = Decoder::<16>::new();
242 let frame = decode_all(&mut decoder, &wire[..len]).unwrap().unwrap();
243 assert_eq!(frame, payload);
244 }
245
246 #[test]
247 fn round_trip_empty_payload() {
248 let mut wire = [0u8; max_encoded_len(0)];
249 let len = encode_frame(&[], &mut wire).unwrap();
250 let mut decoder = Decoder::<8>::new();
251 let frame = decode_all(&mut decoder, &wire[..len]).unwrap().unwrap();
252 assert!(frame.is_empty());
253 }
254
255 #[test]
256 fn escaped_fcs_survives() {
257 for candidate in 0u8..=255 {
261 let payload = [candidate];
262 let fcs = crc16(&payload).to_le_bytes();
263 if fcs.iter().copied().any(needs_escape) {
264 let mut wire = [0u8; max_encoded_len(1)];
265 let len = encode_frame(&payload, &mut wire).unwrap();
266 let mut decoder = Decoder::<8>::new();
267 let frame = decode_all(&mut decoder, &wire[..len]).unwrap().unwrap();
268 assert_eq!(frame, payload);
269 return;
270 }
271 }
272 panic!("no candidate produced an FCS needing escape");
273 }
274
275 #[test]
276 fn back_to_back_frames_share_flag() {
277 let mut wire = Vec::new();
280 let mut scratch = [0u8; 32];
281 for payload in [&[0x01u8][..], &[0x02u8][..]] {
282 let len = encode_frame(payload, &mut scratch).unwrap();
283 wire.extend_from_slice(&scratch[..len]);
284 }
285 let mut collapsed = wire.clone();
287 collapsed.dedup_by(|a, b| *a == FLAG && *b == FLAG);
288
289 for input in [wire, collapsed] {
290 let mut decoder = Decoder::<8>::new();
291 let mut frames = Vec::new();
292 for byte in input {
293 if let Some(outcome) = decoder.push(byte) {
294 frames.push(outcome.unwrap().to_vec());
295 }
296 }
297 assert_eq!(frames, [[0x01].to_vec(), [0x02].to_vec()]);
298 }
299 }
300
301 #[test]
302 fn corrupt_frame_reports_crc_error() {
303 let mut wire = [0u8; max_encoded_len(3)];
304 let len = encode_frame(&[0x81, 0x02, 0x00], &mut wire).unwrap();
305 wire[1] ^= 0x01;
306 let mut decoder = Decoder::<16>::new();
307 assert_eq!(
308 decode_all(&mut decoder, &wire[..len]),
309 Some(Err(DecodeError::Crc))
310 );
311 }
312
313 #[test]
314 fn recovers_after_garbage() {
315 let mut decoder = Decoder::<16>::new();
316 for byte in [0xAAu8, 0xBB, 0xCC] {
320 assert_eq!(decoder.push(byte), None);
321 }
322 assert!(matches!(
323 decoder.push(FLAG),
324 Some(Err(DecodeError::Crc | DecodeError::TooShort))
325 ));
326
327 let mut wire = [0u8; max_encoded_len(1)];
328 let len = encode_frame(&[0x42], &mut wire).unwrap();
329 let frame = decode_all(&mut decoder, &wire[..len]).unwrap().unwrap();
330 assert_eq!(frame, [0x42]);
331 }
332
333 #[test]
334 fn oversized_frame_reports_too_long() {
335 let payload = [0u8; 16];
336 let mut wire = [0u8; max_encoded_len(16)];
337 let len = encode_frame(&payload, &mut wire).unwrap();
338 let mut decoder = Decoder::<8>::new();
340 assert_eq!(
341 decode_all(&mut decoder, &wire[..len]),
342 Some(Err(DecodeError::TooLong))
343 );
344 let len = encode_frame(&[0x01], &mut wire).unwrap();
346 let frame = decode_all(&mut decoder, &wire[..len]).unwrap().unwrap();
347 assert_eq!(frame, [0x01]);
348 }
349
350 #[test]
351 fn escape_then_flag_aborts() {
352 let mut decoder = Decoder::<8>::new();
353 assert_eq!(decoder.push(FLAG), None);
354 assert_eq!(decoder.push(0x42), None);
355 assert_eq!(decoder.push(ESCAPE), None);
356 assert_eq!(decoder.push(FLAG), Some(Err(DecodeError::AbortedEscape)));
357 }
358
359 #[test]
360 fn encode_error_on_small_buffer() {
361 let mut wire = [0u8; 4];
362 assert_eq!(
363 encode_frame(&[0x01, 0x02, 0x03], &mut wire),
364 Err(EncodeError::BufferTooSmall)
365 );
366 }
367}