1use crate::buf::WriteBuf;
8use crate::error::{Error, Result};
9use crate::framing::{Frame, Framer};
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub enum LengthWidth {
14 U8,
16 U16,
18 U32,
20}
21
22impl LengthWidth {
23 #[inline]
25 pub const fn header_size(self) -> usize {
26 match self {
27 LengthWidth::U8 => 1,
28 LengthWidth::U16 => 2,
29 LengthWidth::U32 => 4,
30 }
31 }
32
33 #[inline]
35 pub const fn max_payload(self) -> u64 {
36 match self {
37 LengthWidth::U8 => u8::MAX as u64,
38 LengthWidth::U16 => u16::MAX as u64,
39 LengthWidth::U32 => u32::MAX as u64,
40 }
41 }
42}
43
44#[derive(Debug, Clone, Copy, PartialEq, Eq)]
46pub enum Endian {
47 Big,
49 Little,
51}
52
53#[derive(Debug, Clone, Copy)]
74pub struct LengthPrefixed {
75 width: LengthWidth,
76 endian: Endian,
77 max_payload: u64,
78}
79
80impl LengthPrefixed {
81 #[inline]
86 pub const fn new(width: LengthWidth, endian: Endian) -> Self {
87 Self {
88 width,
89 endian,
90 max_payload: width.max_payload(),
91 }
92 }
93
94 #[inline]
99 #[must_use]
100 pub const fn with_max_payload(mut self, max: u64) -> Self {
101 let cap = self.width.max_payload();
102 self.max_payload = if max < cap { max } else { cap };
103 self
104 }
105
106 #[inline]
108 pub const fn width(&self) -> LengthWidth {
109 self.width
110 }
111
112 #[inline]
114 pub const fn endian(&self) -> Endian {
115 self.endian
116 }
117
118 #[inline]
120 pub const fn max_payload(&self) -> u64 {
121 self.max_payload
122 }
123
124 fn read_prefix(&self, header: &[u8]) -> u64 {
125 match (self.width, self.endian) {
126 (LengthWidth::U8, _) => u64::from(header[0]),
127 (LengthWidth::U16, Endian::Big) => {
128 u64::from(u16::from_be_bytes([header[0], header[1]]))
129 }
130 (LengthWidth::U16, Endian::Little) => {
131 u64::from(u16::from_le_bytes([header[0], header[1]]))
132 }
133 (LengthWidth::U32, Endian::Big) => u64::from(u32::from_be_bytes([
134 header[0], header[1], header[2], header[3],
135 ])),
136 (LengthWidth::U32, Endian::Little) => u64::from(u32::from_le_bytes([
137 header[0], header[1], header[2], header[3],
138 ])),
139 }
140 }
141
142 fn write_prefix(&self, len: u64, out: &mut WriteBuf<'_>) -> Result<()> {
143 match (self.width, self.endian) {
144 (LengthWidth::U8, _) => out.write_u8(len as u8),
145 (LengthWidth::U16, Endian::Big) => out.write_u16_be(len as u16),
146 (LengthWidth::U16, Endian::Little) => out.write_u16_le(len as u16),
147 (LengthWidth::U32, Endian::Big) => out.write_u32_be(len as u32),
148 (LengthWidth::U32, Endian::Little) => out.write_u32_le(len as u32),
149 }
150 }
151}
152
153impl Framer for LengthPrefixed {
154 fn next_frame<'a>(&self, input: &'a [u8]) -> Result<Option<Frame<'a>>> {
155 let header_size = self.width.header_size();
156 if input.len() < header_size {
157 return Ok(None);
158 }
159 let len = self.read_prefix(&input[..header_size]);
160 if len > self.max_payload {
161 return Err(Error::FrameTooLarge {
162 len: len as usize,
163 limit: self.max_payload as usize,
164 });
165 }
166 let consumed = header_size + (len as usize);
167 if input.len() < consumed {
168 return Ok(None);
169 }
170 let payload = &input[header_size..consumed];
171 Ok(Some(Frame::new(payload, consumed)))
172 }
173
174 fn write_frame(&self, payload: &[u8], out: &mut WriteBuf<'_>) -> Result<()> {
175 let len = payload.len() as u64;
176 if len > self.max_payload {
177 return Err(Error::FrameTooLarge {
178 len: payload.len(),
179 limit: self.max_payload as usize,
180 });
181 }
182 self.write_prefix(len, out)?;
183 out.write_bytes(payload)
184 }
185}
186
187#[cfg(test)]
188mod tests {
189 use super::*;
190
191 #[test]
192 fn u8_big_round_trip() {
193 let framer = LengthPrefixed::new(LengthWidth::U8, Endian::Big);
194 let mut out = [0u8; 32];
195 let mut buf = WriteBuf::new(&mut out);
196 framer.write_frame(b"abc", &mut buf).unwrap();
197 let n = buf.position();
198 assert_eq!(&out[..n], &[0x03, b'a', b'b', b'c']);
199
200 let frame = framer.next_frame(&out[..n]).unwrap().unwrap();
201 assert_eq!(frame.payload(), b"abc");
202 assert_eq!(frame.consumed(), 4);
203 }
204
205 #[test]
206 fn partial_frame_returns_none() {
207 let framer = LengthPrefixed::new(LengthWidth::U16, Endian::Big);
208 let input = &[0x00, 0x0A, 0x01, 0x02, 0x03];
210 assert_eq!(framer.next_frame(input).unwrap(), None);
211 }
212
213 #[test]
214 fn empty_input_returns_none() {
215 let framer = LengthPrefixed::new(LengthWidth::U16, Endian::Big);
216 assert_eq!(framer.next_frame(&[]).unwrap(), None);
217 }
218
219 #[test]
220 fn exceeds_max_payload() {
221 let framer = LengthPrefixed::new(LengthWidth::U16, Endian::Big).with_max_payload(4);
222 let input = &[0x00, 0x05, 0, 0, 0, 0, 0];
223 assert!(matches!(
224 framer.next_frame(input),
225 Err(Error::FrameTooLarge { len: 5, limit: 4 })
226 ));
227 }
228
229 #[test]
230 fn write_rejects_oversize_payload() {
231 let framer = LengthPrefixed::new(LengthWidth::U8, Endian::Big).with_max_payload(3);
232 let mut out = [0u8; 16];
233 let mut buf = WriteBuf::new(&mut out);
234 assert!(matches!(
235 framer.write_frame(&[0u8; 4], &mut buf),
236 Err(Error::FrameTooLarge { len: 4, limit: 3 })
237 ));
238 }
239}