Skip to main content

noalloc_slip_rs/
slip.rs

1use core::ops::Deref;
2
3use noalloc_vec_rs::vec::Vec;
4
5/// Marks the start and end of a SLIP frame.
6pub const END_CHAR: u8 = 0xC0;
7
8/// Signals that the next byte is an escaped special byte.
9pub const ESC_CHAR: u8 = 0xDB;
10
11/// Escaped representation of `END_CHAR` inside a frame.
12pub const ESC_END_CHAR: u8 = 0xDC;
13
14/// Escaped representation of `ESC_CHAR` inside a frame.
15pub const ESC_ESC_CHAR: u8 = 0xDD;
16
17/// A SLIP encoder.
18///
19/// This struct provides a method to encode a packet using the SLIP protocol.
20pub struct SlipEncoder;
21
22impl SlipEncoder {
23    /// Encodes `vec` in place as a SLIP frame.
24    ///
25    /// Returns `Ok(())` on success, or `Err(())` if `vec` lacks capacity for the framing overhead.
26    #[allow(clippy::result_unit_err)]
27    pub fn encode<const MAX_LENGTH: usize>(vec: &mut Vec<u8, MAX_LENGTH>) -> Result<(), ()> {
28        // Begin the SLIP frame
29        vec.insert(0, END_CHAR).map_err(|_| ())?;
30
31        let mut index = 1;
32        while index < vec.len() {
33            match vec[index] {
34                END_CHAR => {
35                    vec.insert(index, ESC_CHAR).map_err(|_| ())?;
36                    vec.write(index + 1, ESC_END_CHAR).map_err(|_| ())?;
37                    index += 2;
38                }
39                ESC_CHAR => {
40                    vec.insert(index, ESC_CHAR).map_err(|_| ())?;
41                    vec.write(index + 1, ESC_ESC_CHAR).map_err(|_| ())?;
42                    index += 2;
43                }
44                _ => {
45                    index += 1;
46                }
47            }
48        }
49
50        // End the SLIP frame
51        vec.insert(vec.len(), END_CHAR).map_err(|_| ())?;
52
53        Ok(())
54    }
55}
56
57/// The state of the SLIP decoder.
58#[derive(Debug, Default, PartialEq)]
59enum SlipDecoderState {
60    /// Waiting for the opening `END_CHAR` of a frame.
61    #[default]
62    Start,
63    /// Received the closing `END_CHAR`; frame is complete.
64    End,
65    /// Accumulating payload bytes.
66    Append,
67    /// Received `ESC_CHAR`; next byte is an escaped value.
68    Escape,
69}
70
71/// A SLIP decoder.
72///
73/// This struct provides methods to decode a packet using the SLIP protocol.
74#[derive(Default)]
75pub struct SlipDecoder<const MAX_LENGTH: usize> {
76    state: SlipDecoderState,
77    buffer: Vec<u8, MAX_LENGTH>,
78}
79
80impl<const MAX_LENGTH: usize> SlipDecoder<MAX_LENGTH> {
81    /// Feeds `value` into the decoder state machine.
82    ///
83    /// Returns `Ok(())` on success, or `Err(value)` if the byte is unexpected or the buffer is full.
84    #[allow(clippy::result_unit_err)]
85    pub fn insert(&mut self, value: u8) -> Result<(), u8> {
86        match self.state {
87            SlipDecoderState::Start => {
88                if value == END_CHAR {
89                    self.state = SlipDecoderState::Append;
90                }
91
92                Ok(())
93            }
94            SlipDecoderState::Append => {
95                match value {
96                    END_CHAR => {
97                        self.state = SlipDecoderState::End;
98                    }
99                    ESC_CHAR => {
100                        self.state = SlipDecoderState::Escape;
101                    }
102                    _ => {
103                        self.buffer.push(value)?;
104                    }
105                }
106
107                Ok(())
108            }
109            SlipDecoderState::Escape => {
110                self.state = SlipDecoderState::Append;
111
112                match value {
113                    ESC_END_CHAR => {
114                        self.buffer.push(END_CHAR)?;
115
116                        Ok(())
117                    }
118                    ESC_ESC_CHAR => {
119                        self.buffer.push(ESC_CHAR)?;
120
121                        Ok(())
122                    }
123                    _ => Err(value),
124                }
125            }
126            SlipDecoderState::End => Err(value),
127        }
128    }
129
130    /// Resets the decoder to its initial state.
131    pub fn reset(&mut self) {
132        self.state = SlipDecoderState::Start;
133        self.buffer.clear();
134    }
135
136    /// Returns `true` if the decoder has received a complete SLIP frame.
137    #[must_use]
138    pub fn is_buffer_completed(&self) -> bool {
139        self.state == SlipDecoderState::End
140    }
141
142    /// Returns a slice of the decoded bytes accumulated so far.
143    #[must_use]
144    pub const fn get_buffer(&self) -> &[u8] {
145        self.buffer.as_slice()
146    }
147}
148
149/// Implementation of `Deref` for `SlipDecoder`.
150///
151/// This allows treating a `SlipDecoder` as a byte slice of the decoded buffer.
152impl<const MAX_LENGTH: usize> Deref for SlipDecoder<MAX_LENGTH> {
153    type Target = [u8];
154
155    /// Dereferences to the decoded buffer slice.
156    fn deref(&self) -> &Self::Target {
157        self.get_buffer()
158    }
159}
160
161#[cfg(test)]
162mod tests {
163    use crate::slip::END_CHAR;
164    use crate::slip::ESC_CHAR;
165    use crate::slip::ESC_END_CHAR;
166    use crate::slip::ESC_ESC_CHAR;
167    use crate::slip::SlipDecoder;
168    use crate::slip::SlipDecoderState;
169    use crate::slip::SlipEncoder;
170    use noalloc_vec_rs::vec::Vec;
171
172    #[test]
173    fn test_encode() {
174        let mut array = Vec::<u8, 12>::from([0x00, 0x01, 0x02, 0x03]);
175
176        let result = SlipEncoder::encode(&mut array);
177
178        assert!(result.is_ok());
179        assert_eq!(*array, [END_CHAR, 0x00, 0x01, 0x02, 0x03, END_CHAR]);
180    }
181
182    #[test]
183    fn test_encode_empty() {
184        let mut array = Vec::<u8, 12>::new();
185
186        let result = SlipEncoder::encode(&mut array);
187
188        assert!(result.is_ok());
189        assert_eq!(*array, [END_CHAR, END_CHAR]);
190    }
191
192    #[test]
193    fn test_encode_with_escape_characters() {
194        let mut array = Vec::<u8, 12>::from([END_CHAR, ESC_CHAR, ESC_END_CHAR, ESC_ESC_CHAR]);
195
196        let result = SlipEncoder::encode(&mut array);
197
198        assert!(result.is_ok());
199        assert_eq!(
200            *array,
201            [
202                END_CHAR,
203                ESC_CHAR,
204                ESC_END_CHAR,
205                ESC_CHAR,
206                ESC_ESC_CHAR,
207                ESC_END_CHAR,
208                ESC_ESC_CHAR,
209                END_CHAR
210            ]
211        );
212    }
213
214    #[test]
215    fn test_decode() {
216        let mut slip_decoder = SlipDecoder::<1>::default();
217
218        assert_eq!(slip_decoder.state, SlipDecoderState::Start);
219
220        let result = slip_decoder.insert(END_CHAR);
221        assert!(result.is_ok());
222        assert_eq!(slip_decoder.state, SlipDecoderState::Append);
223
224        let result = slip_decoder.insert(0x00);
225        assert!(result.is_ok());
226        assert_eq!(slip_decoder.state, SlipDecoderState::Append);
227
228        let result = slip_decoder.insert(END_CHAR);
229        assert!(result.is_ok());
230        assert_eq!(slip_decoder.state, SlipDecoderState::End);
231
232        assert!(slip_decoder.is_buffer_completed());
233
234        assert_eq!(slip_decoder.get_buffer(), &[0x00]);
235    }
236
237    #[test]
238    fn test_decode_with_escape_characters() {
239        let mut slip_decoder = SlipDecoder::<6>::default();
240
241        assert_eq!(slip_decoder.state, SlipDecoderState::Start);
242
243        let result = slip_decoder.insert(END_CHAR);
244        assert!(result.is_ok());
245        assert_eq!(slip_decoder.state, SlipDecoderState::Append);
246
247        let result = slip_decoder.insert(ESC_CHAR);
248        assert!(result.is_ok());
249        assert_eq!(slip_decoder.state, SlipDecoderState::Escape);
250
251        let result = slip_decoder.insert(ESC_END_CHAR);
252        assert!(result.is_ok());
253        assert_eq!(slip_decoder.state, SlipDecoderState::Append);
254
255        let result = slip_decoder.insert(ESC_CHAR);
256        assert!(result.is_ok());
257        assert_eq!(slip_decoder.state, SlipDecoderState::Escape);
258
259        let result = slip_decoder.insert(ESC_ESC_CHAR);
260        assert!(result.is_ok());
261        assert_eq!(slip_decoder.state, SlipDecoderState::Append);
262
263        let result = slip_decoder.insert(END_CHAR);
264        assert!(result.is_ok());
265        assert_eq!(slip_decoder.state, SlipDecoderState::End);
266
267        assert!(slip_decoder.is_buffer_completed());
268
269        assert_eq!(slip_decoder.get_buffer(), &[END_CHAR, ESC_CHAR]);
270    }
271
272    #[test]
273    fn test_decode_with_bad_escape_character() {
274        let mut slip_decoder = SlipDecoder::<1>::default();
275
276        assert_eq!(slip_decoder.state, SlipDecoderState::Start);
277
278        let result = slip_decoder.insert(END_CHAR);
279        assert!(result.is_ok());
280        assert_eq!(slip_decoder.state, SlipDecoderState::Append);
281
282        let result = slip_decoder.insert(ESC_CHAR);
283        assert!(result.is_ok());
284        assert_eq!(slip_decoder.state, SlipDecoderState::Escape);
285
286        let result = slip_decoder.insert(0x00);
287        assert!(result.is_err());
288    }
289
290    #[test]
291    fn test_decode_empty() {
292        let mut slip_decoder = SlipDecoder::<0>::default();
293
294        assert_eq!(slip_decoder.state, SlipDecoderState::Start);
295
296        let result = slip_decoder.insert(END_CHAR);
297        assert!(result.is_ok());
298        assert_eq!(slip_decoder.state, SlipDecoderState::Append);
299
300        let result = slip_decoder.insert(END_CHAR);
301        assert!(result.is_ok());
302        assert_eq!(slip_decoder.state, SlipDecoderState::End);
303
304        assert!(slip_decoder.is_buffer_completed());
305
306        assert_eq!(slip_decoder.get_buffer(), &[]);
307    }
308
309    #[test]
310    fn test_decode_and_reset() {
311        let mut slip_decoder = SlipDecoder::<1>::default();
312
313        assert_eq!(slip_decoder.state, SlipDecoderState::Start);
314
315        let result = slip_decoder.insert(END_CHAR);
316        assert!(result.is_ok());
317        assert_eq!(slip_decoder.state, SlipDecoderState::Append);
318
319        let result = slip_decoder.insert(0x00);
320        assert!(result.is_ok());
321        assert_eq!(slip_decoder.state, SlipDecoderState::Append);
322
323        let result = slip_decoder.insert(END_CHAR);
324        assert!(result.is_ok());
325        assert_eq!(slip_decoder.state, SlipDecoderState::End);
326
327        assert!(slip_decoder.is_buffer_completed());
328
329        assert_eq!(slip_decoder.get_buffer(), &[0x00]);
330
331        slip_decoder.reset();
332        assert_eq!(slip_decoder.state, SlipDecoderState::Start);
333        assert!(!slip_decoder.is_buffer_completed());
334    }
335
336    #[test]
337    fn test_decode_with_not_enough_space() {
338        let mut slip_decoder = SlipDecoder::<1>::default();
339
340        assert_eq!(slip_decoder.state, SlipDecoderState::Start);
341
342        let result = slip_decoder.insert(END_CHAR);
343        assert!(result.is_ok());
344        assert_eq!(slip_decoder.state, SlipDecoderState::Append);
345
346        let result = slip_decoder.insert(0x00);
347        assert!(result.is_ok());
348        assert_eq!(slip_decoder.state, SlipDecoderState::Append);
349
350        let result = slip_decoder.insert(0x00);
351        assert!(result.is_err());
352    }
353}