wire_codec/framing/varint_prefixed.rs
1//! Varint length-prefixed framing.
2//!
3//! Each frame is a LEB128 varint giving the payload length, followed by the
4//! payload. This is the "length-delimited" stream format of Protocol Buffers
5//! (`writeDelimitedTo` / `parseDelimitedFrom`, `prost::encode_length_delimited`)
6//! and of several RPC transports. Small messages pay a single byte of
7//! overhead; the prefix grows by one byte per seven bits of length.
8
9use crate::buf::WriteBuf;
10use crate::error::{Error, Result};
11use crate::framing::{Frame, Framer};
12use crate::varint::{self, MAX_LEN_U64};
13
14/// Varint length-prefixed framer.
15///
16/// Unlike the deprecated `LengthPrefixed::new`, this framer is bounded by
17/// default: [`VarintPrefixed::new`] caps payloads
18/// at [`VarintPrefixed::DEFAULT_MAX_PAYLOAD`], and it rejects overlong
19/// (non-canonical) prefixes so every frame has exactly one valid encoding.
20/// Both can be relaxed.
21///
22/// # Example
23///
24/// ```
25/// use wire_codec::WriteBuf;
26/// use wire_codec::framing::{Framer, VarintPrefixed};
27///
28/// let framer = VarintPrefixed::new();
29///
30/// let mut out = [0u8; 16];
31/// let mut buf = WriteBuf::new(&mut out);
32/// framer.write_frame(b"hello", &mut buf).unwrap();
33/// assert_eq!(buf.written(), b"\x05hello");
34///
35/// let frame = framer.next_frame(&out[..6]).unwrap().unwrap();
36/// assert_eq!(frame.payload(), b"hello");
37/// assert_eq!(frame.consumed(), 6);
38/// ```
39#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
40pub struct VarintPrefixed {
41 max_payload: usize,
42 canonical: bool,
43}
44
45impl VarintPrefixed {
46 /// Default payload limit: 8 MiB, matching
47 /// [`LengthPrefixed::DEFAULT_MAX_PAYLOAD`][`crate::framing::LengthPrefixed::DEFAULT_MAX_PAYLOAD`].
48 pub const DEFAULT_MAX_PAYLOAD: usize = 8 * 1024 * 1024;
49
50 /// Build a framer with the default payload limit that rejects
51 /// non-canonical prefixes.
52 ///
53 /// # Example
54 ///
55 /// ```
56 /// use wire_codec::framing::VarintPrefixed;
57 ///
58 /// let framer = VarintPrefixed::new();
59 /// assert_eq!(framer.max_payload(), VarintPrefixed::DEFAULT_MAX_PAYLOAD);
60 /// assert!(framer.is_canonical());
61 /// ```
62 #[inline]
63 #[must_use]
64 pub const fn new() -> Self {
65 Self {
66 max_payload: Self::DEFAULT_MAX_PAYLOAD,
67 canonical: true,
68 }
69 }
70
71 /// Set the payload size limit enforced when reading and writing frames.
72 ///
73 /// # Example
74 ///
75 /// ```
76 /// use wire_codec::framing::{Framer, VarintPrefixed};
77 /// use wire_codec::Error;
78 ///
79 /// let framer = VarintPrefixed::new().with_max_payload(4);
80 /// assert!(matches!(
81 /// framer.next_frame(&[0x05, 1, 2, 3, 4, 5]),
82 /// Err(Error::FrameTooLarge { len: 5, limit: 4 })
83 /// ));
84 /// ```
85 #[inline]
86 #[must_use]
87 pub const fn with_max_payload(mut self, max: usize) -> Self {
88 self.max_payload = max;
89 self
90 }
91
92 /// Choose whether overlong prefixes (such as `[0x85, 0x00]` for length 5)
93 /// are rejected with [`Error::InvalidEncoding`] (`true`, the default) or
94 /// accepted the way protobuf parsers do (`false`).
95 ///
96 /// # Example
97 ///
98 /// ```
99 /// use wire_codec::framing::{Framer, VarintPrefixed};
100 /// use wire_codec::Error;
101 ///
102 /// let overlong = [0x81, 0x00, b'x'];
103 /// assert_eq!(VarintPrefixed::new().next_frame(&overlong), Err(Error::InvalidEncoding));
104 /// let lenient = VarintPrefixed::new().with_canonical(false);
105 /// assert_eq!(lenient.next_frame(&overlong).unwrap().unwrap().payload(), b"x");
106 /// ```
107 #[inline]
108 #[must_use]
109 pub const fn with_canonical(mut self, canonical: bool) -> Self {
110 self.canonical = canonical;
111 self
112 }
113
114 /// Configured maximum payload size in bytes.
115 #[inline]
116 #[must_use]
117 pub const fn max_payload(&self) -> usize {
118 self.max_payload
119 }
120
121 /// Whether overlong prefixes are rejected.
122 #[inline]
123 #[must_use]
124 pub const fn is_canonical(&self) -> bool {
125 self.canonical
126 }
127
128 fn too_large(&self, len: u64) -> Error {
129 Error::FrameTooLarge {
130 len: usize::try_from(len).unwrap_or(usize::MAX),
131 limit: self.max_payload,
132 }
133 }
134}
135
136impl Default for VarintPrefixed {
137 #[inline]
138 fn default() -> Self {
139 Self::new()
140 }
141}
142
143impl Framer for VarintPrefixed {
144 /// Extract one frame if the varint prefix and the full payload are present.
145 ///
146 /// # Errors
147 ///
148 /// - [`Error::VarintOverflow`] if the prefix runs past ten bytes or
149 /// overflows `u64`.
150 /// - [`Error::InvalidEncoding`] if the prefix is overlong and the framer
151 /// is canonical.
152 /// - [`Error::FrameTooLarge`] if the length exceeds the configured limit.
153 fn next_frame<'a>(&self, input: &'a [u8]) -> Result<Option<Frame<'a>>> {
154 let (len, header_size) =
155 match varint::decode_slice(input, MAX_LEN_U64, u64::BITS, self.canonical) {
156 Ok(parsed) => parsed,
157 Err(Error::UnexpectedEof) => return Ok(None),
158 Err(e) => return Err(e),
159 };
160 let consumed = usize::try_from(len)
161 .ok()
162 .filter(|&len| len <= self.max_payload)
163 .and_then(|len| len.checked_add(header_size))
164 .ok_or_else(|| self.too_large(len))?;
165 let Some(payload) = input.get(header_size..consumed) else {
166 return Ok(None);
167 };
168 Ok(Some(Frame::new(payload, consumed)))
169 }
170
171 /// Write the varint prefix and the payload.
172 ///
173 /// # Errors
174 ///
175 /// - [`Error::FrameTooLarge`] if the payload exceeds the configured limit.
176 /// - [`Error::BufferFull`] if `out` cannot hold the whole frame.
177 ///
178 /// Nothing is written on error.
179 fn write_frame(&self, payload: &[u8], out: &mut WriteBuf<'_>) -> Result<()> {
180 if payload.len() > self.max_payload {
181 return Err(Error::FrameTooLarge {
182 len: payload.len(),
183 limit: self.max_payload,
184 });
185 }
186 let total = self.max_frame_len(payload.len()).ok_or(Error::BufferFull)?;
187 if total > out.remaining() {
188 return Err(Error::BufferFull);
189 }
190 varint::encode_usize(payload.len(), out)?;
191 out.write_bytes(payload)
192 }
193
194 #[inline]
195 fn max_frame_len(&self, payload_len: usize) -> Option<usize> {
196 payload_len.checked_add(varint::encoded_len_usize(payload_len))
197 }
198}
199
200#[cfg(test)]
201mod tests {
202 use super::*;
203
204 #[test]
205 fn round_trip_across_prefix_sizes() {
206 let framer = VarintPrefixed::new();
207 let mut out = vec![0u8; 20_000];
208 for len in [0usize, 1, 127, 128, 16_383, 16_384] {
209 let payload = vec![0x5Au8; len];
210 let mut buf = WriteBuf::new(&mut out);
211 framer.write_frame(&payload, &mut buf).unwrap();
212 let n = buf.position();
213 assert_eq!(Some(n), framer.max_frame_len(len));
214 let frame = framer.next_frame(&out[..n]).unwrap().unwrap();
215 assert_eq!(frame.payload(), payload.as_slice());
216 assert_eq!(frame.consumed(), n);
217 // Every strict prefix is incomplete.
218 for cut in 0..n {
219 assert_eq!(
220 framer.next_frame(&out[..cut]),
221 Ok(None),
222 "len={len} cut={cut}"
223 );
224 }
225 }
226 }
227
228 #[test]
229 fn limit_boundaries() {
230 let framer = VarintPrefixed::new().with_max_payload(3);
231 assert_eq!(
232 framer.next_frame(&[3, 1, 2, 3]).unwrap().unwrap().payload(),
233 &[1, 2, 3]
234 );
235 assert_eq!(
236 framer.next_frame(&[4]),
237 Err(Error::FrameTooLarge { len: 4, limit: 3 })
238 );
239 let mut out = [0u8; 8];
240 let mut buf = WriteBuf::new(&mut out);
241 assert_eq!(
242 framer.write_frame(&[0; 4], &mut buf),
243 Err(Error::FrameTooLarge { len: 4, limit: 3 })
244 );
245 assert_eq!(buf.position(), 0);
246 }
247
248 #[test]
249 fn hostile_prefixes() {
250 let framer = VarintPrefixed::new().with_max_payload(usize::MAX);
251 // Eleven continuation bytes: overflow, not "need more".
252 assert_eq!(framer.next_frame(&[0x80; 11]), Err(Error::VarintOverflow));
253 assert_eq!(framer.next_frame(&[0x80; 10]), Err(Error::VarintOverflow));
254 // Nine continuation bytes: still a possible prefix.
255 assert_eq!(framer.next_frame(&[0x80; 9]), Ok(None));
256 // A u64::MAX length cannot be addressed even with no limit set:
257 // the header-size addition overflows on 64-bit targets and the
258 // conversion to usize fails on 32-bit ones. Never a panic.
259 let max = [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01];
260 assert!(matches!(
261 framer.next_frame(&max),
262 Err(Error::FrameTooLarge { .. })
263 ));
264 }
265
266 #[test]
267 fn write_is_atomic_on_buffer_full() {
268 let framer = VarintPrefixed::new();
269 let mut out = [0xEEu8; 3];
270 let mut buf = WriteBuf::new(&mut out);
271 assert_eq!(framer.write_frame(b"abc", &mut buf), Err(Error::BufferFull));
272 assert_eq!(out, [0xEE; 3]);
273 }
274
275 #[test]
276 fn accessors() {
277 let framer = VarintPrefixed::new()
278 .with_max_payload(5)
279 .with_canonical(false);
280 assert_eq!(framer.max_payload(), 5);
281 assert!(!framer.is_canonical());
282 assert!(VarintPrefixed::new().is_canonical());
283 }
284
285 #[test]
286 fn default_matches_new() {
287 assert_eq!(VarintPrefixed::default(), VarintPrefixed::new());
288 assert_eq!(VarintPrefixed::new().max_frame_len(usize::MAX), None);
289 }
290}