slipstream/
lib.rs

1#![doc = include_str!("../README.md")]
2
3use std::error::Error;
4use std::fmt;
5use std::io::{self, Read, Write};
6use memchr::{memchr2, memchr2_iter};
7
8#[cfg(feature = "async-codec")]
9pub mod async_codec;
10#[cfg(feature = "tokio-codec")]
11pub mod tokio_codec;
12
13/// SLIP END byte (0xC0).
14pub const END: u8 = 0xC0;
15/// SLIP ESC byte (0xDB).
16pub const ESC: u8 = 0xDB;
17/// SLIP ESC END byte (0xDC).
18pub const ESC_END: u8 = 0xDC;
19/// SLIP ESC ESC byte (0xDD).
20pub const ESC_ESC: u8 = 0xDD;
21
22/// Convenient result alias used throughout the crate.
23pub type Result<T> = std::result::Result<T, SlipError>;
24
25/// Captures decoded bytes that were buffered when a stream ended without a
26/// terminating [`END`] byte.
27#[derive(Debug, Default, Clone, PartialEq, Eq)]
28pub struct FrameRemainder {
29    /// Decoded payload bytes collected before the unexpected end of stream.
30    pub decoded: Vec<u8>,
31    /// `true` if the decoder ended while waiting for the second byte of an escape sequence.
32    pub escape_pending: bool,
33}
34
35impl FrameRemainder {
36    /// Returns the number of decoded bytes that were buffered.
37    pub fn len(&self) -> usize {
38        self.decoded.len()
39    }
40
41    /// Returns `true` when there is no buffered payload and no pending escape sequence.
42    pub fn is_empty(&self) -> bool {
43        self.decoded.is_empty() && !self.escape_pending
44    }
45}
46
47/// Error type for SLIP encoding and decoding operations.
48#[derive(Debug)]
49#[non_exhaustive]
50pub enum SlipError {
51    /// Wrapper around [`std::io::Error`] originating from the underlying reader or writer.
52    Io(io::Error),
53    /// Encountered bytes that were not terminated by an [`END`] delimiter.
54    UnexpectedEndOfFrame,
55    /// Encountered an [`ESC`] byte at the end of a stream without a following escape code.
56    IncompleteEscape,
57    /// Encountered an invalid escape sequence while decoding.
58    InvalidEscape(u8),
59    /// No complete SLIP frame was present in the input while one was expected.
60    MissingFrame,
61    /// More frames than expected were present in the input.
62    MultipleFrames(usize),
63}
64
65impl fmt::Display for SlipError {
66    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
67        match self {
68            SlipError::Io(err) => write!(f, "I/O error: {err}"),
69            SlipError::UnexpectedEndOfFrame => write!(f, "encountered unexpected end of frame"),
70            SlipError::IncompleteEscape => write!(f, "encountered incomplete escape sequence"),
71            SlipError::InvalidEscape(code) => {
72                write!(f, "encountered invalid escape sequence 0x{code:02X}")
73            }
74            SlipError::MissingFrame => write!(f, "no complete SLIP frame found in input"),
75            SlipError::MultipleFrames(count) => {
76                write!(f, "expected a single frame but found {count}")
77            }
78        }
79    }
80}
81
82impl Error for SlipError {
83    fn source(&self) -> Option<&(dyn Error + 'static)> {
84        match self {
85            SlipError::Io(err) => Some(err),
86            _ => None,
87        }
88    }
89}
90
91impl From<io::Error> for SlipError {
92    fn from(value: io::Error) -> Self {
93        SlipError::Io(value)
94    }
95}
96
97/// Encode arbitrary bytes as a SLIP frame and return the encoded data as a newly allocated [`Vec`].
98///
99/// The returned frame always ends with the [`END`] delimiter. See `examples/basic.rs`
100/// for an end-to-end demonstration.
101pub fn encode_frame(data: &[u8]) -> Vec<u8> {
102    // Fast path for slices: pre-size and scan using memchr2.
103    let mut out = Vec::with_capacity(encoded_len_bytes(data));
104    let mut start = 0usize;
105    for pos in memchr2_iter(END, ESC, data) {
106        if pos > start {
107            out.extend_from_slice(&data[start..pos]);
108        }
109        match data[pos] {
110            END => out.extend_from_slice(&[ESC, ESC_END]),
111            ESC => out.extend_from_slice(&[ESC, ESC_ESC]),
112            _ => unreachable!(),
113        }
114        start = pos + 1;
115    }
116    if start < data.len() {
117        out.extend_from_slice(&data[start..]);
118    }
119    out.push(END);
120    out
121}
122
123/// Encode an arbitrary iterator of bytes as a SLIP frame and return the encoded data.
124///
125/// This helper is generic over any iterator to make it easy to encode common Rust collections.
126pub fn encode_iter<I>(input: I) -> Vec<u8>
127where
128    I: IntoIterator<Item = u8>,
129{
130    let mut out = Vec::new();
131    encode_into_writer(input, &mut out).expect("writing to Vec<u8> cannot fail");
132    out
133}
134
135/// Encode bytes as SLIP and write the result directly into the provided writer.
136///
137/// The writer receives the escaped payload followed by the trailing [`END`] delimiter.
138/// Refer to `examples/basic.rs` for a runnable usage sample.
139pub fn encode_into_writer<I, W>(input: I, writer: &mut W) -> Result<()>
140where
141    I: IntoIterator<Item = u8>,
142    W: Write,
143{
144    for byte in input {
145        match byte {
146            END => writer.write_all(&[ESC, ESC_END])?,
147            ESC => writer.write_all(&[ESC, ESC_ESC])?,
148            value => writer.write_all(&[value])?,
149        }
150    }
151    writer.write_all(&[END])?;
152    Ok(())
153}
154
155/// Decode all SLIP frames contained in the provided byte slice.
156///
157/// The function returns a vector containing one decoded frame per [`END`] delimiter.
158/// Frames are returned in the order they appear in the input.
159/// A complete example is available in `examples/basic.rs`.
160pub fn decode_frames(bytes: &[u8]) -> Result<Vec<Vec<u8>>> {
161    let (frames, remainder) = decode_frames_with_remainder(bytes)?;
162    if remainder.escape_pending {
163        return Err(SlipError::IncompleteEscape);
164    }
165    if !remainder.decoded.is_empty() {
166        return Err(SlipError::UnexpectedEndOfFrame);
167    }
168    Ok(frames)
169}
170
171/// Decode all SLIP frames produced by the given iterator over bytes.
172pub fn decode_frames_iter<I>(input: I) -> Result<Vec<Vec<u8>>>
173where
174    I: IntoIterator<Item = u8>,
175{
176    let (frames, remainder) = decode_frames_iter_with_remainder(input)?;
177    if remainder.escape_pending {
178        return Err(SlipError::IncompleteEscape);
179    }
180    if !remainder.decoded.is_empty() {
181        return Err(SlipError::UnexpectedEndOfFrame);
182    }
183    Ok(frames)
184}
185
186/// Decode SLIP frames and also return any buffered remainder when the input ends without a trailing [`END`].
187///
188/// ```
189/// use slipstream::{decode_frames_with_remainder, encode_frame};
190///
191/// let mut truncated = encode_frame(b"hi");
192/// truncated.pop();
193/// let (frames, remainder) = decode_frames_with_remainder(&truncated).unwrap();
194/// assert!(frames.is_empty());
195/// assert_eq!(remainder.decoded, b"hi");
196/// assert!(!remainder.escape_pending);
197/// ```
198pub fn decode_frames_with_remainder(bytes: &[u8]) -> Result<(Vec<Vec<u8>>, FrameRemainder)> {
199    let mut frames: Vec<Vec<u8>> = Vec::new();
200    let mut buffer: Vec<u8> = Vec::new();
201    let mut i = 0usize;
202    let mut escape_pending = false;
203
204    while i < bytes.len() {
205        if escape_pending {
206            let code = bytes[i];
207            match code {
208                ESC_END => buffer.push(END),
209                ESC_ESC => buffer.push(ESC),
210                invalid => return Err(SlipError::InvalidEscape(invalid)),
211            }
212            escape_pending = false;
213            i += 1;
214            continue;
215        }
216
217        match memchr2(END, ESC, &bytes[i..]) {
218            Some(rel) => {
219                let pos = i + rel;
220                if pos > i {
221                    buffer.extend_from_slice(&bytes[i..pos]);
222                }
223                match bytes[pos] {
224                    END => {
225                        frames.push(std::mem::take(&mut buffer));
226                    }
227                    ESC => {
228                        escape_pending = true;
229                    }
230                    _ => unreachable!(),
231                }
232                i = pos + 1;
233            }
234            None => {
235                // No more specials: copy remainder and finish
236                buffer.extend_from_slice(&bytes[i..]);
237                i = bytes.len();
238            }
239        }
240    }
241
242    Ok((
243        frames,
244        FrameRemainder {
245            decoded: buffer,
246            escape_pending,
247        },
248    ))
249}
250
251/// Iterator variant of [`decode_frames_with_remainder`].
252pub fn decode_frames_iter_with_remainder<I>(input: I) -> Result<(Vec<Vec<u8>>, FrameRemainder)>
253where
254    I: IntoIterator<Item = u8>,
255{
256    let mut frames = Vec::new();
257    let mut buffer = Vec::new();
258    let mut state = DecoderState::default();
259
260    for byte in input {
261        let completed = process_byte(&mut state, byte, |value| buffer.push(value))?;
262        if completed {
263            frames.push(std::mem::take(&mut buffer));
264        }
265    }
266
267    Ok((
268        frames,
269        FrameRemainder {
270            decoded: buffer,
271            escape_pending: state.last_was_esc,
272        },
273    ))
274}
275
276/// Compute the encoded length (including the trailing [`END`] delimiter) without allocating.
277///
278/// ```
279/// use slipstream::{encoded_len, END, ESC};
280///
281/// assert_eq!(encoded_len([END, ESC, 0x01]), 6);
282/// ```
283pub fn encoded_len<I>(input: I) -> usize
284where
285    I: IntoIterator<Item = u8>,
286{
287    let mut len = 1; // Account for the final END delimiter.
288    for byte in input {
289        len += match byte {
290            END | ESC => 2,
291            _ => 1,
292        };
293    }
294    len
295}
296
297/// Optimized encoded length for byte slices.
298fn encoded_len_bytes(bytes: &[u8]) -> usize {
299    // Each END/ESC expands to two bytes; others stay as one. Add 1 for trailing END.
300    let mut count = 0usize;
301    for _ in memchr2_iter(END, ESC, bytes) {
302        count += 1;
303    }
304    bytes.len() + count + 1
305}
306
307/// Determine the decoded length of each SLIP frame in the provided input without materialising the payloads.
308///
309/// ```
310/// use slipstream::{decoded_lengths, encode_frame};
311///
312/// let encoded = [encode_frame(b"hi"), encode_frame(&[])].concat();
313/// assert_eq!(decoded_lengths(&encoded).unwrap(), vec![2, 0]);
314/// ```
315pub fn decoded_lengths(bytes: &[u8]) -> Result<Vec<usize>> {
316    let mut lengths: Vec<usize> = Vec::new();
317    let mut current = 0usize;
318    let mut i = 0usize;
319    let mut escape_pending = false;
320
321    while i < bytes.len() {
322        if escape_pending {
323            let code = bytes[i];
324            match code {
325                ESC_END | ESC_ESC => {
326                    current += 1;
327                }
328                invalid => return Err(SlipError::InvalidEscape(invalid)),
329            }
330            escape_pending = false;
331            i += 1;
332            continue;
333        }
334
335        match memchr2(END, ESC, &bytes[i..]) {
336            Some(rel) => {
337                let pos = i + rel;
338                // bytes between i..pos are payload
339                current += pos - i;
340                match bytes[pos] {
341                    END => {
342                        lengths.push(current);
343                        current = 0;
344                    }
345                    ESC => {
346                        escape_pending = true;
347                    }
348                    _ => unreachable!(),
349                }
350                i = pos + 1;
351            }
352            None => {
353                // No more specials: remaining are payload
354                current += bytes.len() - i;
355                i = bytes.len();
356            }
357        }
358    }
359
360    if escape_pending {
361        return Err(SlipError::IncompleteEscape);
362    }
363    if current != 0 {
364        return Err(SlipError::UnexpectedEndOfFrame);
365    }
366    Ok(lengths)
367}
368
369/// Iterator variant of [`decoded_lengths`].
370pub fn decoded_lengths_iter<I>(input: I) -> Result<Vec<usize>>
371where
372    I: IntoIterator<Item = u8>,
373{
374    let mut lengths = Vec::new();
375    let mut current = 0usize;
376    let mut state = DecoderState::default();
377
378    for byte in input {
379        let completed = process_byte(&mut state, byte, |_| current += 1)?;
380        if completed {
381            lengths.push(current);
382            current = 0;
383        }
384    }
385
386    if state.last_was_esc {
387        return Err(SlipError::IncompleteEscape);
388    }
389
390    if current != 0 {
391        return Err(SlipError::UnexpectedEndOfFrame);
392    }
393
394    Ok(lengths)
395}
396
397/// Decode a single SLIP frame from the provided bytes.
398///
399/// # Errors
400///
401/// * [`SlipError::MissingFrame`] if no complete frame was found.
402/// * [`SlipError::MultipleFrames`] if more than one frame was present.
403pub fn decode_frame(bytes: &[u8]) -> Result<Vec<u8>> {
404    let mut frames = decode_frames(bytes)?;
405    match frames.len() {
406        0 => Err(SlipError::MissingFrame),
407        1 => Ok(frames.remove(0)),
408        count => Err(SlipError::MultipleFrames(count)),
409    }
410}
411
412/// Writer wrapper that encodes outgoing frames as SLIP before forwarding them to the underlying writer.
413///
414/// The wrapper does not buffer beyond the escaping that SLIP requires. Each call to [`write_frame`](SlipWriter::write_frame)
415/// appends a single SLIP frame to the wrapped writer. See `examples/stream.rs` for a runnable demonstration.
416pub struct SlipWriter<W> {
417    inner: W,
418}
419
420impl<W> SlipWriter<W> {
421    /// Construct a new SLIP writer around the provided sink.
422    pub fn new(inner: W) -> Self {
423        Self { inner }
424    }
425
426    /// Retrieve an immutable reference to the underlying writer.
427    pub fn get_ref(&self) -> &W {
428        &self.inner
429    }
430
431    /// Retrieve a mutable reference to the underlying writer.
432    pub fn get_mut(&mut self) -> &mut W {
433        &mut self.inner
434    }
435
436    /// Consume the wrapper and return the inner writer.
437    pub fn into_inner(self) -> W {
438        self.inner
439    }
440}
441
442impl<W: Write> SlipWriter<W> {
443    /// Encode the provided payload as a SLIP frame and write it to the underlying sink.
444    pub fn write_frame(&mut self, payload: &[u8]) -> Result<()> {
445        // Use the optimized slice-based encoder and write once to reduce syscall overhead.
446        let frame = encode_frame(payload);
447        self.inner.write_all(&frame).map_err(SlipError::from)
448    }
449
450    /// Encode any iterator of bytes as a SLIP frame and write it to the underlying sink.
451    pub fn write_frame_iter<I>(&mut self, payload: I) -> Result<()>
452    where
453        I: IntoIterator<Item = u8>,
454    {
455        encode_into_writer(payload, &mut self.inner)
456    }
457
458    /// Flush the underlying writer.
459    pub fn flush(&mut self) -> Result<()> {
460        self.inner.flush().map_err(SlipError::from)
461    }
462}
463
464/// Reader wrapper that decodes SLIP frames from an underlying byte stream.
465///
466/// A full streaming example is provided in `examples/stream.rs`. Use
467/// [`SlipReader::take_remainder`] to inspect buffered data when a stream ends
468/// mid-frame.
469pub struct SlipReader<R> {
470    inner: R,
471    state: DecoderState,
472    pending: Vec<u8>,
473}
474
475impl<R> SlipReader<R> {
476    /// Construct a new `SlipReader` around the provided source.
477    pub fn new(inner: R) -> Self {
478        Self {
479            inner,
480            state: DecoderState::default(),
481            pending: Vec::new(),
482        }
483    }
484
485    /// Borrow the underlying reader.
486    pub fn get_ref(&self) -> &R {
487        &self.inner
488    }
489
490    /// Borrow the underlying reader mutably.
491    pub fn get_mut(&mut self) -> &mut R {
492        &mut self.inner
493    }
494
495    /// Consume the wrapper and return the inner reader.
496    pub fn into_inner(self) -> R {
497        self.inner
498    }
499
500    /// Consume the wrapper and return both the inner reader and any buffered remainder.
501    pub fn into_inner_with_remainder(self) -> (R, FrameRemainder) {
502        (
503            self.inner,
504            FrameRemainder {
505                decoded: self.pending,
506                escape_pending: self.state.last_was_esc,
507            },
508        )
509    }
510}
511
512impl<R: Read> SlipReader<R> {
513    /// Read the next SLIP frame into the supplied buffer.
514    ///
515    /// On success the buffer is populated with the decoded payload and the function returns the frame length.
516    /// When the end of the underlying reader is reached without another complete frame, `Ok(None)` is returned.
517    pub fn read_frame_into(&mut self, buffer: &mut Vec<u8>) -> Result<Option<usize>> {
518        buffer.clear();
519
520        loop {
521            let mut byte = [0u8; 1];
522            match self.inner.read(&mut byte) {
523                Ok(0) => {
524                    if self.state.last_was_esc {
525                        return Err(SlipError::IncompleteEscape);
526                    }
527                    if !self.pending.is_empty() {
528                        return Err(SlipError::UnexpectedEndOfFrame);
529                    }
530                    return Ok(None);
531                }
532                Ok(_) => {
533                    let completed =
534                        process_byte(&mut self.state, byte[0], |value| self.pending.push(value))?;
535                    if completed {
536                        buffer.extend_from_slice(&self.pending);
537                        let len = buffer.len();
538                        self.pending.clear();
539                        return Ok(Some(len));
540                    }
541                }
542                Err(err) => return Err(SlipError::Io(err)),
543            }
544        }
545    }
546
547    /// Read the next SLIP frame and return it as a freshly allocated [`Vec`].
548    pub fn read_frame(&mut self) -> Result<Option<Vec<u8>>> {
549        let mut frame = Vec::new();
550        match self.read_frame_into(&mut frame)? {
551            Some(_) => Ok(Some(frame)),
552            None => Ok(None),
553        }
554    }
555
556    /// Read the next SLIP frame and return only its decoded length.
557    ///
558    /// ```
559    /// use slipstream::{SlipReader, encode_frame, Result};
560    /// use std::io::Cursor;
561    ///
562    /// # fn main() -> Result<()> {
563    /// let encoded = [encode_frame(b"foo"), encode_frame(&[1])].concat();
564    /// let mut reader = SlipReader::new(Cursor::new(encoded));
565    /// assert_eq!(reader.read_frame_length()?, Some(3));
566    /// assert_eq!(reader.read_frame_length()?, Some(1));
567    /// assert!(reader.read_frame_length()?.is_none());
568    /// # Ok(())
569    /// # }
570    /// ```
571    pub fn read_frame_length(&mut self) -> Result<Option<usize>> {
572        let mut length = 0usize;
573
574        loop {
575            let mut byte = [0u8; 1];
576            match self.inner.read(&mut byte) {
577                Ok(0) => {
578                    if self.state.last_was_esc {
579                        return Err(SlipError::IncompleteEscape);
580                    }
581                    if !self.pending.is_empty() {
582                        return Err(SlipError::UnexpectedEndOfFrame);
583                    }
584                    return Ok(None);
585                }
586                Ok(_) => {
587                    let completed = process_byte(&mut self.state, byte[0], |value| {
588                        self.pending.push(value);
589                        length += 1;
590                    })?;
591
592                    if completed {
593                        self.pending.clear();
594                        return Ok(Some(length));
595                    }
596                }
597                Err(err) => return Err(SlipError::Io(err)),
598            }
599        }
600    }
601
602    /// Take ownership of any pending decoded bytes accumulated for the current, incomplete frame.
603    ///
604    /// ```
605    /// use slipstream::{SlipReader, encode_frame, SlipError, Result};
606    /// use std::io::Cursor;
607    ///
608    /// # fn main() -> Result<()> {
609    /// let mut encoded = encode_frame(b"data");
610    /// encoded.pop(); // remove END terminator
611    /// let mut reader = SlipReader::new(Cursor::new(encoded));
612    /// let mut frame = Vec::new();
613    /// assert!(matches!(
614    ///     reader.read_frame_into(&mut frame),
615    ///     Err(SlipError::UnexpectedEndOfFrame)
616    /// ));
617    /// let remainder = reader.take_remainder();
618    /// assert_eq!(remainder.decoded, b"data");
619    /// assert!(!remainder.escape_pending);
620    /// # Ok(())
621    /// # }
622    /// ```
623    pub fn take_remainder(&mut self) -> FrameRemainder {
624        let remainder = FrameRemainder {
625            decoded: std::mem::take(&mut self.pending),
626            escape_pending: self.state.last_was_esc,
627        };
628        self.state.last_was_esc = false;
629        remainder
630    }
631
632    /// Check if an incomplete frame is currently buffered.
633    pub fn has_remainder(&self) -> bool {
634        !self.pending.is_empty() || self.state.last_was_esc
635    }
636}
637
638#[derive(Default)]
639pub(crate) struct DecoderState {
640    pub(crate) last_was_esc: bool,
641}
642
643fn process_byte<F>(state: &mut DecoderState, byte: u8, mut on_byte: F) -> Result<bool>
644where
645    F: FnMut(u8),
646{
647    if state.last_was_esc {
648        state.last_was_esc = false;
649        match byte {
650            ESC_END => on_byte(END),
651            ESC_ESC => on_byte(ESC),
652            invalid => return Err(SlipError::InvalidEscape(invalid)),
653        }
654        return Ok(false);
655    }
656
657    match byte {
658        END => {
659            state.last_was_esc = false;
660            Ok(true)
661        }
662        ESC => {
663            state.last_was_esc = true;
664            Ok(false)
665        }
666        value => {
667            on_byte(value);
668            Ok(false)
669        }
670    }
671}
672
673#[cfg(test)]
674mod tests {
675    use super::*;
676    use std::io::Cursor;
677
678    #[test]
679    fn encode_simple() {
680        let encoded = encode_frame(b"abc");
681        assert_eq!(encoded, vec![b'a', b'b', b'c', END]);
682    }
683
684    #[test]
685    fn encode_escapes() {
686        let encoded = encode_frame(&[END, ESC, 0x01]);
687        assert_eq!(encoded, vec![ESC, ESC_END, ESC, ESC_ESC, 0x01, END]);
688    }
689
690    #[test]
691    fn decode_single_frame() {
692        let frame = encode_frame(b"payload");
693        let decoded = decode_frame(&frame).unwrap();
694        assert_eq!(decoded, b"payload");
695    }
696
697    #[test]
698    fn decode_multiple_frames() {
699        let encoded = [encode_frame(b"one"), encode_frame(&[END])].concat();
700        let frames = decode_frames(&encoded).unwrap();
701        assert_eq!(frames.len(), 2);
702        assert_eq!(frames[0], b"one");
703        assert_eq!(frames[1], vec![END]);
704    }
705
706    #[test]
707    fn reader_writer_roundtrip() {
708        let mut writer = SlipWriter::new(Vec::new());
709        writer.write_frame(b"first").unwrap();
710        writer.write_frame(&[END]).unwrap();
711        let encoded = writer.into_inner();
712
713        let mut reader = SlipReader::new(Cursor::new(encoded));
714        let mut frame = Vec::new();
715        assert_eq!(reader.read_frame_into(&mut frame).unwrap(), Some(5));
716        assert_eq!(frame, b"first");
717        assert_eq!(reader.read_frame_into(&mut frame).unwrap(), Some(1));
718        assert_eq!(frame, vec![END]);
719        assert!(reader.read_frame_into(&mut frame).unwrap().is_none());
720    }
721
722    #[test]
723    fn decode_invalid_escape() {
724        let err = decode_frames(&[ESC, 0x01, END]).unwrap_err();
725        assert!(matches!(err, SlipError::InvalidEscape(0x01)));
726    }
727
728    #[test]
729    fn reader_incomplete_escape() {
730        let data = vec![ESC];
731        let mut reader = SlipReader::new(Cursor::new(data));
732        let mut frame = Vec::new();
733        let err = reader.read_frame_into(&mut frame).unwrap_err();
734        assert!(matches!(err, SlipError::IncompleteEscape));
735    }
736
737    #[test]
738    fn decode_frames_remainder_incomplete() {
739        let (frames, remainder) = decode_frames_with_remainder(&[0x01, 0x02]).unwrap();
740        assert!(frames.is_empty());
741        assert_eq!(remainder.decoded, vec![0x01, 0x02]);
742        assert!(!remainder.escape_pending);
743    }
744
745    #[test]
746    fn decode_frames_remainder_escape_pending() {
747        let input = [b'X', ESC];
748        let (frames, remainder) = decode_frames_with_remainder(&input).unwrap();
749        assert!(frames.is_empty());
750        assert_eq!(remainder.decoded, vec![b'X']);
751        assert!(remainder.escape_pending);
752    }
753
754    #[test]
755    fn decoded_lengths_multiple_frames() {
756        let encoded = [
757            encode_frame(b"foo"),
758            encode_frame(&[]),
759            encode_frame(&[END]),
760        ]
761        .concat();
762        let lengths = decoded_lengths(&encoded).unwrap();
763        assert_eq!(lengths, vec![3, 0, 1]);
764    }
765
766    #[test]
767    fn decoded_lengths_incomplete_frame_error() {
768        let mut encoded = encode_frame(b"broken");
769        encoded.pop(); // drop END terminator
770        let err = decoded_lengths(&encoded).unwrap_err();
771        assert!(matches!(err, SlipError::UnexpectedEndOfFrame));
772    }
773
774    #[test]
775    fn encoded_len_counts_escapes() {
776        let len = encoded_len([END, ESC, 0x01]);
777        assert_eq!(len, 6);
778    }
779
780    #[test]
781    fn reader_take_remainder_after_eof() {
782        let mut encoded = encode_frame(b"chunk");
783        encoded.pop();
784        let mut reader = SlipReader::new(Cursor::new(encoded));
785        let mut frame = Vec::new();
786        let err = reader.read_frame_into(&mut frame).unwrap_err();
787        assert!(matches!(err, SlipError::UnexpectedEndOfFrame));
788        assert!(frame.is_empty());
789        assert!(reader.has_remainder());
790        let remainder = reader.take_remainder();
791        assert_eq!(remainder.decoded, b"chunk");
792        assert!(!remainder.escape_pending);
793        assert!(!reader.has_remainder());
794    }
795
796    #[test]
797    fn reader_frame_length_only() {
798        let encoded = [
799            encode_frame(b"first"),
800            encode_frame(&[END]),
801            encode_frame(b""),
802        ]
803        .concat();
804        let mut reader = SlipReader::new(Cursor::new(encoded));
805        assert_eq!(reader.read_frame_length().unwrap(), Some(5));
806        assert_eq!(reader.read_frame_length().unwrap(), Some(1));
807        assert_eq!(reader.read_frame_length().unwrap(), Some(0));
808        assert!(reader.read_frame_length().unwrap().is_none());
809    }
810
811    #[test]
812    fn reader_frame_length_incomplete() {
813        let mut encoded = encode_frame(b"oops");
814        encoded.pop();
815        let mut reader = SlipReader::new(Cursor::new(encoded));
816        let err = reader.read_frame_length().unwrap_err();
817        assert!(matches!(err, SlipError::UnexpectedEndOfFrame));
818        let remainder = reader.take_remainder();
819        assert_eq!(remainder.decoded, b"oops");
820        assert!(!remainder.escape_pending);
821    }
822}