wire_codec/framing/
delimiter.rs1use crate::buf::WriteBuf;
9use crate::error::{Error, Result};
10use crate::framing::{Frame, Framer};
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
34pub struct Delimited<'d> {
35 delimiter: &'d [u8],
36 max_payload: usize,
37}
38
39impl<'d> Delimited<'d> {
40 #[inline]
47 pub const fn new(delimiter: &'d [u8]) -> Result<Self> {
48 if delimiter.is_empty() {
49 return Err(Error::EmptyDelimiter);
50 }
51 Ok(Self {
52 delimiter,
53 max_payload: usize::MAX,
54 })
55 }
56
57 #[inline]
60 #[must_use]
61 pub const fn with_max_payload(mut self, max: usize) -> Self {
62 self.max_payload = max;
63 self
64 }
65
66 #[inline]
68 pub const fn delimiter(&self) -> &'d [u8] {
69 self.delimiter
70 }
71
72 #[inline]
74 pub const fn max_payload(&self) -> usize {
75 self.max_payload
76 }
77
78 fn find_delimiter(&self, haystack: &[u8]) -> Option<usize> {
79 let needle = self.delimiter;
80 if haystack.len() < needle.len() {
81 return None;
82 }
83 let last = haystack.len() - needle.len();
84 let mut i = 0;
85 while i <= last {
86 if &haystack[i..i + needle.len()] == needle {
87 return Some(i);
88 }
89 i += 1;
90 }
91 None
92 }
93}
94
95impl<'d> Framer for Delimited<'d> {
96 fn next_frame<'a>(&self, input: &'a [u8]) -> Result<Option<Frame<'a>>> {
97 let window_cap = self.max_payload.saturating_add(self.delimiter.len());
100 let scan_window = if input.len() > window_cap {
101 &input[..window_cap]
102 } else {
103 input
104 };
105 match self.find_delimiter(scan_window) {
106 Some(pos) => {
107 if pos > self.max_payload {
108 return Err(Error::FrameTooLarge {
109 len: pos,
110 limit: self.max_payload,
111 });
112 }
113 let consumed = pos + self.delimiter.len();
114 Ok(Some(Frame::new(&input[..pos], consumed)))
115 }
116 None => {
117 if input.len() > window_cap {
120 return Err(Error::FrameTooLarge {
121 len: input.len(),
122 limit: self.max_payload,
123 });
124 }
125 Ok(None)
126 }
127 }
128 }
129
130 fn write_frame(&self, payload: &[u8], out: &mut WriteBuf<'_>) -> Result<()> {
131 if payload.len() > self.max_payload {
132 return Err(Error::FrameTooLarge {
133 len: payload.len(),
134 limit: self.max_payload,
135 });
136 }
137 out.write_bytes(payload)?;
138 out.write_bytes(self.delimiter)
139 }
140}
141
142#[cfg(test)]
143mod tests {
144 use super::*;
145
146 #[test]
147 fn newline_round_trip() {
148 let framer = Delimited::new(b"\n").unwrap();
149 let mut storage = [0u8; 32];
150 let mut buf = WriteBuf::new(&mut storage);
151 framer.write_frame(b"hello", &mut buf).unwrap();
152 let n = buf.position();
153 assert_eq!(&storage[..n], b"hello\n");
154
155 let frame = framer.next_frame(&storage[..n]).unwrap().unwrap();
156 assert_eq!(frame.payload(), b"hello");
157 assert_eq!(frame.consumed(), 6);
158 }
159
160 #[test]
161 fn multi_byte_delimiter() {
162 let framer = Delimited::new(b"\r\n").unwrap();
163 let frame = framer.next_frame(b"GET /\r\nrest").unwrap().unwrap();
164 assert_eq!(frame.payload(), b"GET /");
165 assert_eq!(frame.consumed(), 7);
166 }
167
168 #[test]
169 fn no_delimiter_returns_none() {
170 let framer = Delimited::new(b"\n").unwrap();
171 assert_eq!(framer.next_frame(b"no terminator here").unwrap(), None);
172 }
173
174 #[test]
175 fn exceeds_max_payload_short_circuits_search() {
176 let framer = Delimited::new(b"\n").unwrap().with_max_payload(3);
180 let result = framer.next_frame(b"abcde\n");
181 assert!(matches!(result, Err(Error::FrameTooLarge { limit: 3, .. })));
182 }
183
184 #[test]
185 fn empty_payload_is_valid() {
186 let framer = Delimited::new(b"\n").unwrap();
187 let frame = framer.next_frame(b"\nrest").unwrap().unwrap();
188 assert_eq!(frame.payload(), b"");
189 assert_eq!(frame.consumed(), 1);
190 }
191
192 #[test]
193 fn empty_delimiter_rejected() {
194 let result = Delimited::new(b"");
195 assert!(matches!(result, Err(Error::EmptyDelimiter)));
196 }
197}