Skip to main content

pack_io/
view.rs

1//! Zero-copy decoding: the [`DeserializeView`] trait, the [`decode_view`]
2//! free function, and built-in impls for the types that can borrow directly
3//! from the input buffer.
4//!
5//! ## The two decode surfaces
6//!
7//! [`Deserialize`] produces an **owning** value — `String`s and `Vec<u8>`s
8//! are allocated, the input buffer can be dropped immediately after the
9//! call. Use it when the input is transient (a network buffer being
10//! recycled, a file being streamed) or when downstream code holds the
11//! decoded value for an unbounded lifetime.
12//!
13//! [`DeserializeView`] produces a **borrowed** value — `&'a str` and
14//! `&'a [u8]` fields point directly into the input slice. No per-field
15//! allocation. Use it when the input buffer outlives the decoded value
16//! (e.g. a memory-mapped file, an arena, a request-lifetime byte buffer).
17//!
18//! Both surfaces share the same on-wire format. A value encoded with
19//! [`crate::encode`] can be read with either [`crate::decode`] (owning) or
20//! [`decode_view`] (borrowed); choose the one that matches the lifetime
21//! relationship the caller has with the source bytes.
22//!
23//! ## Built-in implementors
24//!
25//! - `&'a str`, `&'a [u8]` — the headline zero-copy types.
26//! - Every primitive (`u8` … `u128`, `i8` … `i128`, `usize`, `isize`,
27//!   `bool`, `f32`, `f64`, `()`) — `DeserializeView` reduces to
28//!   [`Deserialize`] for fixed-size scalars, no borrow involved.
29//! - `Option<T>`, `Result<T, E>`, tuples `(T1, …, Tn)` (arity 1–12),
30//!   fixed-size arrays `[T; N]`.
31//! - `Vec<T>` and the standard `BTreeMap` / `BTreeSet` / `HashMap` /
32//!   `HashSet` — these have to allocate the container itself, but their
33//!   element / key / value types may still be borrows.
34//!
35//! User-defined types implement [`DeserializeView`] directly or via the
36//! `#[derive(DeserializeView)]` macro (`feature = "derive"`).
37
38use alloc::collections::{BTreeMap, BTreeSet};
39use alloc::string::String;
40use alloc::vec::Vec;
41#[cfg(feature = "std")]
42use std::collections::{HashMap, HashSet};
43#[cfg(feature = "std")]
44use std::hash::{BuildHasher, Hash};
45
46use crate::codec::{Decode, Decoder};
47use crate::error::{Result, SerialError};
48use crate::traits::Deserialize;
49
50// ---------------------------------------------------------------------------
51// Trait + free function
52// ---------------------------------------------------------------------------
53
54/// Types that decode by **borrowing** directly from the input slice rather
55/// than allocating owned copies.
56///
57/// `'a` is the lifetime of the underlying byte buffer; every borrowed field
58/// in the decoded value points into that buffer. The borrow checker
59/// guarantees the value cannot outlive its source.
60///
61/// # Examples
62///
63/// Borrow string and byte fields out of a request buffer:
64///
65/// ```
66/// use pack_io::{decode_view, Decode, Decoder, DeserializeView, Result, Serialize, Encode};
67///
68/// // Pair of borrowed and owned versions for round-tripping.
69/// struct OwnedMsg { id: u64, text: String, payload: Vec<u8> }
70/// struct ViewMsg<'a> { id: u64, text: &'a str, payload: &'a [u8] }
71///
72/// impl Serialize for OwnedMsg {
73///     fn serialize<E: Encode + ?Sized>(&self, e: &mut E) -> Result<()> {
74///         self.id.serialize(e)?; self.text.serialize(e)?; self.payload.serialize(e)
75///     }
76/// }
77/// impl<'a> DeserializeView<'a> for ViewMsg<'a> {
78///     fn deserialize_view(d: &mut Decoder<'a>) -> Result<Self> {
79///         Ok(ViewMsg {
80///             id:      u64::deserialize_view(d)?,
81///             text:    <&str>::deserialize_view(d)?,
82///             payload: <&[u8]>::deserialize_view(d)?,
83///         })
84///     }
85/// }
86///
87/// let bytes = pack_io::encode(&OwnedMsg {
88///     id: 7,
89///     text: "hello".into(),
90///     payload: vec![1, 2, 3],
91/// }).unwrap();
92///
93/// let view: ViewMsg<'_> = decode_view(&bytes).unwrap();
94/// assert_eq!(view.text, "hello");        // &str borrowed from `bytes`
95/// assert_eq!(view.payload, &[1, 2, 3]);  // &[u8] borrowed from `bytes`
96/// ```
97pub trait DeserializeView<'a>: Sized {
98    /// Read a value of `Self` from `decoder`, borrowing from its
99    /// underlying input slice where possible.
100    ///
101    /// # Errors
102    ///
103    /// Any [`crate::SerialError`] the underlying byte reads surface.
104    fn deserialize_view(decoder: &mut Decoder<'a>) -> Result<Self>;
105}
106
107/// Decode a borrowed value of type `T` from `bytes`, requiring the input to
108/// be fully consumed.
109///
110/// This is the **Tier-1** zero-copy entry point — the symmetric counterpart
111/// to [`crate::decode`]. The decoded value borrows from `bytes`; the
112/// borrow checker enforces that `bytes` outlives the result.
113///
114/// # Examples
115///
116/// ```
117/// let bytes = pack_io::encode(&"hello").unwrap();
118/// let view: &str = pack_io::decode_view(&bytes).unwrap();
119/// assert_eq!(view, "hello");
120/// ```
121///
122/// # Errors
123///
124/// - Returns [`SerialError::TrailingBytes`] when extra bytes follow the value.
125/// - Propagates any [`SerialError`] from the type's [`DeserializeView`] impl.
126#[inline]
127pub fn decode_view<'a, T: DeserializeView<'a>>(bytes: &'a [u8]) -> Result<T> {
128    let mut dec = Decoder::new(bytes);
129    let value = T::deserialize_view(&mut dec)?;
130    let remaining = dec.remaining();
131    if remaining != 0 {
132        return Err(SerialError::TrailingBytes { remaining });
133    }
134    Ok(value)
135}
136
137// ---------------------------------------------------------------------------
138// Borrowed primitives
139// ---------------------------------------------------------------------------
140
141impl<'a> DeserializeView<'a> for &'a str {
142    #[inline]
143    fn deserialize_view(decoder: &mut Decoder<'a>) -> Result<Self> {
144        let bytes = decoder.read_length_prefixed_borrowed()?;
145        core::str::from_utf8(bytes).map_err(|_| SerialError::InvalidUtf8)
146    }
147}
148
149impl<'a> DeserializeView<'a> for &'a [u8] {
150    #[inline]
151    fn deserialize_view(decoder: &mut Decoder<'a>) -> Result<Self> {
152        decoder.read_length_prefixed_borrowed()
153    }
154}
155
156// ---------------------------------------------------------------------------
157// Fixed-size scalars: DeserializeView ≡ Deserialize
158// ---------------------------------------------------------------------------
159
160macro_rules! view_via_owned {
161    ($($t:ty),+ $(,)?) => {
162        $(
163            impl<'a> DeserializeView<'a> for $t {
164                #[inline]
165                fn deserialize_view(decoder: &mut Decoder<'a>) -> Result<Self> {
166                    <$t as Deserialize>::deserialize(decoder)
167                }
168            }
169        )+
170    };
171}
172
173view_via_owned!(
174    u8,
175    u16,
176    u32,
177    u64,
178    u128,
179    usize,
180    i8,
181    i16,
182    i32,
183    i64,
184    i128,
185    isize,
186    bool,
187    f32,
188    f64,
189    (),
190    String,
191);
192
193// ---------------------------------------------------------------------------
194// Containers: Option, Result, tuples, arrays — propagate the lifetime
195// ---------------------------------------------------------------------------
196
197impl<'a, T: DeserializeView<'a>> DeserializeView<'a> for Option<T> {
198    #[inline]
199    fn deserialize_view(decoder: &mut Decoder<'a>) -> Result<Self> {
200        match decoder.read_byte()? {
201            0x00 => Ok(None),
202            0x01 => Ok(Some(T::deserialize_view(decoder)?)),
203            tag => Err(SerialError::InvalidTag {
204                kind: "Option",
205                tag,
206            }),
207        }
208    }
209}
210
211impl<'a, T: DeserializeView<'a>, E: DeserializeView<'a>> DeserializeView<'a>
212    for core::result::Result<T, E>
213{
214    #[inline]
215    fn deserialize_view(decoder: &mut Decoder<'a>) -> Result<Self> {
216        match decoder.read_byte()? {
217            0x00 => Ok(Ok(T::deserialize_view(decoder)?)),
218            0x01 => Ok(Err(E::deserialize_view(decoder)?)),
219            tag => Err(SerialError::InvalidTag {
220                kind: "Result",
221                tag,
222            }),
223        }
224    }
225}
226
227impl<'a, T: DeserializeView<'a>, const N: usize> DeserializeView<'a> for [T; N] {
228    fn deserialize_view(decoder: &mut Decoder<'a>) -> Result<Self> {
229        let mut out: Vec<T> = Vec::with_capacity(N);
230        for _ in 0..N {
231            out.push(T::deserialize_view(decoder)?);
232        }
233        out.try_into().map_err(|_| SerialError::IntegerOutOfRange)
234    }
235}
236
237impl<'a, T: DeserializeView<'a>> DeserializeView<'a> for Vec<T> {
238    fn deserialize_view(decoder: &mut Decoder<'a>) -> Result<Self> {
239        let declared = <Decoder<'a> as crate::Decode>::read_varint_u64(decoder)?;
240        let max = <Decoder<'a> as crate::Decode>::max_alloc(decoder) as u64;
241        if declared > max {
242            return Err(SerialError::InvalidLength {
243                declared,
244                remaining: 0,
245            });
246        }
247        let len = usize::try_from(declared).map_err(|_| SerialError::IntegerOutOfRange)?;
248        let initial = len.min(4096);
249        let mut out = Vec::with_capacity(initial);
250        for _ in 0..len {
251            out.push(T::deserialize_view(decoder)?);
252        }
253        Ok(out)
254    }
255}
256
257macro_rules! view_tuple {
258    ($($name:ident),+) => {
259        impl<'a, $($name: DeserializeView<'a>),+> DeserializeView<'a> for ($($name,)+) {
260            #[inline]
261            fn deserialize_view(decoder: &mut Decoder<'a>) -> Result<Self> {
262                Ok(( $( $name::deserialize_view(decoder)?, )+ ))
263            }
264        }
265    };
266}
267
268view_tuple!(T0);
269view_tuple!(T0, T1);
270view_tuple!(T0, T1, T2);
271view_tuple!(T0, T1, T2, T3);
272view_tuple!(T0, T1, T2, T3, T4);
273view_tuple!(T0, T1, T2, T3, T4, T5);
274view_tuple!(T0, T1, T2, T3, T4, T5, T6);
275view_tuple!(T0, T1, T2, T3, T4, T5, T6, T7);
276view_tuple!(T0, T1, T2, T3, T4, T5, T6, T7, T8);
277view_tuple!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9);
278view_tuple!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10);
279view_tuple!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11);
280
281// ---------------------------------------------------------------------------
282// Map / set collections: the container is allocated, but K / V may borrow.
283// ---------------------------------------------------------------------------
284
285impl<'a, K, V> DeserializeView<'a> for BTreeMap<K, V>
286where
287    K: DeserializeView<'a> + Ord,
288    V: DeserializeView<'a>,
289{
290    fn deserialize_view(decoder: &mut Decoder<'a>) -> Result<Self> {
291        let declared = <Decoder<'a> as crate::Decode>::read_varint_u64(decoder)?;
292        let max = <Decoder<'a> as crate::Decode>::max_alloc(decoder) as u64;
293        if declared > max {
294            return Err(SerialError::InvalidLength {
295                declared,
296                remaining: 0,
297            });
298        }
299        let len = usize::try_from(declared).map_err(|_| SerialError::IntegerOutOfRange)?;
300        let mut out = BTreeMap::new();
301        for _ in 0..len {
302            let k = K::deserialize_view(decoder)?;
303            let v = V::deserialize_view(decoder)?;
304            let _ = out.insert(k, v);
305        }
306        Ok(out)
307    }
308}
309
310impl<'a, T> DeserializeView<'a> for BTreeSet<T>
311where
312    T: DeserializeView<'a> + Ord,
313{
314    fn deserialize_view(decoder: &mut Decoder<'a>) -> Result<Self> {
315        let declared = <Decoder<'a> as crate::Decode>::read_varint_u64(decoder)?;
316        let max = <Decoder<'a> as crate::Decode>::max_alloc(decoder) as u64;
317        if declared > max {
318            return Err(SerialError::InvalidLength {
319                declared,
320                remaining: 0,
321            });
322        }
323        let len = usize::try_from(declared).map_err(|_| SerialError::IntegerOutOfRange)?;
324        let mut out = BTreeSet::new();
325        for _ in 0..len {
326            let _ = out.insert(T::deserialize_view(decoder)?);
327        }
328        Ok(out)
329    }
330}
331
332#[cfg(feature = "std")]
333impl<'a, K, V, S> DeserializeView<'a> for HashMap<K, V, S>
334where
335    K: DeserializeView<'a> + Hash + Eq,
336    V: DeserializeView<'a>,
337    S: BuildHasher + Default,
338{
339    fn deserialize_view(decoder: &mut Decoder<'a>) -> Result<Self> {
340        let declared = <Decoder<'a> as crate::Decode>::read_varint_u64(decoder)?;
341        let max = <Decoder<'a> as crate::Decode>::max_alloc(decoder) as u64;
342        if declared > max {
343            return Err(SerialError::InvalidLength {
344                declared,
345                remaining: 0,
346            });
347        }
348        let len = usize::try_from(declared).map_err(|_| SerialError::IntegerOutOfRange)?;
349        let initial = len.min(4096);
350        let mut out = HashMap::with_capacity_and_hasher(initial, S::default());
351        for _ in 0..len {
352            let k = K::deserialize_view(decoder)?;
353            let v = V::deserialize_view(decoder)?;
354            let _ = out.insert(k, v);
355        }
356        Ok(out)
357    }
358}
359
360#[cfg(feature = "std")]
361impl<'a, T, S> DeserializeView<'a> for HashSet<T, S>
362where
363    T: DeserializeView<'a> + Hash + Eq,
364    S: BuildHasher + Default,
365{
366    fn deserialize_view(decoder: &mut Decoder<'a>) -> Result<Self> {
367        let declared = <Decoder<'a> as crate::Decode>::read_varint_u64(decoder)?;
368        let max = <Decoder<'a> as crate::Decode>::max_alloc(decoder) as u64;
369        if declared > max {
370            return Err(SerialError::InvalidLength {
371                declared,
372                remaining: 0,
373            });
374        }
375        let len = usize::try_from(declared).map_err(|_| SerialError::IntegerOutOfRange)?;
376        let initial = len.min(4096);
377        let mut out = HashSet::with_capacity_and_hasher(initial, S::default());
378        for _ in 0..len {
379            let _ = out.insert(T::deserialize_view(decoder)?);
380        }
381        Ok(out)
382    }
383}
384
385#[cfg(test)]
386mod tests {
387    use super::*;
388    use crate::encode;
389    use alloc::string::ToString;
390    use alloc::vec;
391
392    #[test]
393    fn borrowed_str_round_trips() {
394        let bytes = encode(&"hello").unwrap();
395        let view: &str = decode_view(&bytes).unwrap();
396        assert_eq!(view, "hello");
397    }
398
399    #[test]
400    fn borrowed_bytes_round_trip() {
401        let bytes = encode(&vec![1u8, 2, 3, 4, 5]).unwrap();
402        let view: &[u8] = decode_view(&bytes).unwrap();
403        assert_eq!(view, &[1, 2, 3, 4, 5]);
404    }
405
406    #[test]
407    fn primitive_view_decodes_like_owning() {
408        let bytes = encode(&42_u64).unwrap();
409        let n: u64 = decode_view(&bytes).unwrap();
410        assert_eq!(n, 42);
411    }
412
413    #[test]
414    fn option_borrowed_view_round_trips() {
415        let bytes = encode(&Some(String::from("hi"))).unwrap();
416        let v: Option<&str> = decode_view(&bytes).unwrap();
417        assert_eq!(v, Some("hi"));
418
419        let none_bytes = encode::<Option<String>>(&None).unwrap();
420        let v: Option<&str> = decode_view(&none_bytes).unwrap();
421        assert_eq!(v, None);
422    }
423
424    #[test]
425    fn tuple_with_borrowed_str_round_trips() {
426        let owned = (7_u64, String::from("hello"), true);
427        let bytes = encode(&owned).unwrap();
428        let view: (u64, &str, bool) = decode_view(&bytes).unwrap();
429        assert_eq!(view, (7, "hello", true));
430    }
431
432    #[test]
433    fn vec_of_borrowed_str_round_trips() {
434        let owned = vec![
435            String::from("alpha"),
436            String::from("beta"),
437            String::from("gamma"),
438        ];
439        let bytes = encode(&owned).unwrap();
440        let view: Vec<&str> = decode_view(&bytes).unwrap();
441        assert_eq!(view, vec!["alpha", "beta", "gamma"]);
442    }
443
444    #[test]
445    fn decode_view_rejects_trailing_bytes() {
446        let mut bytes = encode(&"hi").unwrap();
447        bytes.push(0xff);
448        let err = decode_view::<&str>(&bytes).expect_err("trailing bytes");
449        assert!(matches!(err, SerialError::TrailingBytes { remaining: 1 }));
450    }
451
452    #[test]
453    fn borrowed_str_with_invalid_utf8_is_rejected() {
454        let bytes = [0x02u8, 0xff, 0xff];
455        let err = decode_view::<&str>(&bytes).expect_err("invalid utf-8");
456        assert!(matches!(err, SerialError::InvalidUtf8));
457    }
458
459    #[test]
460    fn map_with_borrowed_keys_round_trips() {
461        let mut owned: BTreeMap<String, u32> = BTreeMap::new();
462        let _ = owned.insert("alpha".to_string(), 1);
463        let _ = owned.insert("beta".to_string(), 2);
464        let bytes = encode(&owned).unwrap();
465        let view: BTreeMap<&str, u32> = decode_view(&bytes).unwrap();
466        assert_eq!(view.get("alpha"), Some(&1));
467        assert_eq!(view.get("beta"), Some(&2));
468    }
469}