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