compio_io\framed/
frame.rs

1//! Traits and implementations for frame extraction and enclosing
2
3use std::io;
4
5use compio_buf::{
6    IoBufMut,
7    bytes::{Buf, BufMut},
8};
9
10/// An extracted frame
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub struct Frame {
13    /// Offset where the frame payload begins
14    prefix: usize,
15
16    /// Length of the frame payload
17    payload: usize,
18
19    /// Suffix length of the frame
20    suffix: usize,
21}
22
23impl Frame {
24    /// Create a new [`Frame`] with the specified prefix, payload, and suffix
25    /// lengths.
26    pub fn new(prefix: usize, payload: usize, suffix: usize) -> Self {
27        Self {
28            prefix,
29            payload,
30            suffix,
31        }
32    }
33
34    /// Length of the entire frame
35    pub fn len(&self) -> usize {
36        self.prefix + self.payload + self.suffix
37    }
38
39    /// If the frame is empty
40    pub fn is_empty(&self) -> bool {
41        self.len() == 0
42    }
43
44    /// Slice payload out of the buffer
45    pub fn payload<'a>(&self, buf: &'a [u8]) -> &'a [u8] {
46        &buf[self.prefix..self.prefix + self.payload]
47    }
48}
49
50/// Enclosing and extracting frames in a buffer.
51pub trait Framer {
52    /// Enclose a frame in the given buffer.
53    ///
54    /// All initialized bytes in `buf` (`buf[0..buf.len()]`) are valid and
55    /// required to be enclosed. All modifications should happen in-place; one
56    /// can use [`slice::copy_within`] or a temporary buffer if prepending data
57    /// is necessary.
58    ///
59    /// [`slice::copy_within`]: https://doc.rust-lang.org/std/primitive.slice.html#method.copy_within
60    fn enclose(&mut self, buf: &mut Vec<u8>);
61
62    /// Extract a frame from the given buffer.
63    ///
64    /// # Returns
65    /// - `Ok(Some(frame))` if a complete frame is found.
66    /// - `Ok(None)` if no complete frame is found.
67    /// - `Err(io::Error)` if an error occurs during extraction.
68    fn extract(&mut self, buf: &[u8]) -> io::Result<Option<Frame>>;
69}
70
71/// A simple extractor that frames data by its length.
72///
73/// It uses 8 bytes to represent the length of the data at the beginning.
74#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
75pub struct LengthDelimited {
76    length_field_len: usize,
77    length_field_is_big_endian: bool,
78}
79
80impl Default for LengthDelimited {
81    fn default() -> Self {
82        Self {
83            length_field_len: 4,
84            length_field_is_big_endian: true,
85        }
86    }
87}
88
89impl LengthDelimited {
90    /// Creates a new `LengthDelimited` framer.
91    pub fn new() -> Self {
92        Self::default()
93    }
94
95    /// Returns the length of the length field in bytes.
96    pub fn length_field_len(&self) -> usize {
97        self.length_field_len
98    }
99
100    /// Sets the length of the length field in bytes.
101    pub fn set_length_field_len(mut self, len_field_len: usize) -> Self {
102        self.length_field_len = len_field_len;
103        self
104    }
105
106    /// Returns whether the length field is big-endian.
107    pub fn length_field_is_big_endian(&self) -> bool {
108        self.length_field_is_big_endian
109    }
110
111    /// Sets whether the length field is big-endian.
112    pub fn set_length_field_is_big_endian(mut self, big_endian: bool) -> Self {
113        self.length_field_is_big_endian = big_endian;
114        self
115    }
116}
117
118impl Framer for LengthDelimited {
119    fn enclose(&mut self, buf: &mut Vec<u8>) {
120        let len = buf.len();
121
122        buf.reserve(self.length_field_len);
123        IoBufMut::as_uninit(buf).copy_within(0..len, self.length_field_len); // Shift existing data
124        unsafe { buf.set_len(len + self.length_field_len) };
125
126        // Write the length at the beginning
127        if self.length_field_is_big_endian {
128            (&mut buf[0..self.length_field_len]).put_uint(len as _, self.length_field_len);
129        } else {
130            (&mut buf[0..self.length_field_len]).put_uint_le(len as _, self.length_field_len);
131        }
132    }
133
134    fn extract(&mut self, mut buf: &[u8]) -> io::Result<Option<Frame>> {
135        if buf.len() < self.length_field_len {
136            return Ok(None);
137        }
138
139        let len = if self.length_field_is_big_endian {
140            buf.get_uint(self.length_field_len)
141        } else {
142            buf.get_uint_le(self.length_field_len)
143        } as usize;
144
145        if buf.len() < len {
146            return Ok(None);
147        }
148
149        Ok(Some(Frame::new(self.length_field_len, len, 0)))
150    }
151}
152
153/// A generic delimiter that uses a single character.
154#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
155pub struct CharDelimited<const C: char> {}
156
157impl<const C: char> CharDelimited<C> {
158    /// Creates a new `CharDelimited`
159    pub fn new() -> Self {
160        Self {}
161    }
162}
163
164impl<const C: char> Framer for CharDelimited<C> {
165    fn enclose(&mut self, buf: &mut Vec<u8>) {
166        buf.push(C as u8);
167    }
168
169    fn extract(&mut self, buf: &[u8]) -> io::Result<Option<Frame>> {
170        if buf.is_empty() {
171            return Ok(None);
172        }
173
174        Ok(buf
175            .iter()
176            .position(|&b| b == C as u8)
177            .map(|pos| Frame::new(0, pos, 1)))
178    }
179}
180
181/// A generic delimiter that uses any sequence of bytes as a delimiter.
182#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
183pub struct AnyDelimited<'a> {
184    bytes: &'a [u8],
185}
186
187impl<'a> AnyDelimited<'a> {
188    /// Creates a new `AnyDelimited` with the specified delimiter bytes.
189    pub fn new(bytes: &'a [u8]) -> Self {
190        Self { bytes }
191    }
192}
193
194impl Framer for AnyDelimited<'_> {
195    fn extract(&mut self, buf: &[u8]) -> io::Result<Option<Frame>> {
196        if buf.is_empty() {
197            return Ok(None);
198        }
199
200        // Search for the first occurrence of any byte in `self.bytes`
201        // TODO(George-Miao): Optimize if performance is a concern
202        if let Some(pos) = buf
203            .windows(self.bytes.len())
204            .position(|window| window == self.bytes)
205        {
206            Ok(Some(Frame::new(0, pos, self.bytes.len())))
207        } else {
208            Ok(None)
209        }
210    }
211
212    fn enclose(&mut self, buf: &mut Vec<u8>) {
213        buf.extend_from_slice(self.bytes);
214    }
215}
216
217/// Delimiter that uses newline characters (`\n`) as delimiters.
218pub type LineDelimited = CharDelimited<'\n'>;