Skip to main content

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 [`LengthPrefixed::new`][`crate::framing::LengthPrefixed::new`],
17/// this framer is bounded by 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}