Skip to main content

multi_trait/
try_decode_from.rs

1// SPDX-License-Identifier: Apache-2.0
2use crate::Error;
3use unsigned_varint::decode;
4
5/// Trait for fallibly decoding values from byte slices.
6///
7/// This trait provides zero-copy parsing of varint-encoded values from byte slices,
8/// returning both the decoded value and the remaining unconsumed bytes. This design
9/// enables efficient sequential parsing of multiple values from a single buffer.
10///
11/// # Lifetime Parameter
12///
13/// The `'a` lifetime parameter ties the returned slice reference to the input buffer,
14/// preventing dangling references and enabling zero-copy parsing.
15///
16/// # Error Handling
17///
18/// Decoding can fail for several reasons:
19/// - Insufficient bytes in the input
20/// - Invalid varint encoding
21/// - Truncated data
22///
23/// Errors are returned as structured [`Error`] types with proper
24/// source chains for debugging.
25///
26/// # Performance
27///
28/// Decoding is highly optimized:
29/// - Zero allocations (returns slice references)
30/// - No data copying
31/// - Constant-time validation
32///
33/// # Thread Safety
34///
35/// This trait is `Send + Sync` safe. Implementations are stateless and can be
36/// called concurrently from multiple threads.
37///
38/// # Examples
39///
40/// ## Basic Decoding
41///
42/// ```rust
43/// use multi_trait::TryDecodeFrom;
44///
45/// // Decode a single value
46/// let bytes = vec![42];
47/// let (value, remaining) = u8::try_decode_from(&bytes).unwrap();
48/// assert_eq!(value, 42);
49/// assert!(remaining.is_empty());
50/// ```
51///
52/// ## Sequential Decoding
53///
54/// ```rust
55/// use multi_trait::TryDecodeFrom;
56///
57/// // Decode multiple values from one buffer
58/// let bytes = vec![0x01, 0x02, 0x03];
59/// let (first, rest) = u8::try_decode_from(&bytes).unwrap();
60/// let (second, rest) = u8::try_decode_from(rest).unwrap();
61/// let (third, rest) = u8::try_decode_from(rest).unwrap();
62///
63/// assert_eq!(first, 1);
64/// assert_eq!(second, 2);
65/// assert_eq!(third, 3);
66/// assert!(rest.is_empty());
67/// ```
68///
69/// ## Error Handling
70///
71/// ```rust
72/// use multi_trait::{TryDecodeFrom, Error};
73///
74/// // Handle decode errors
75/// let empty: &[u8] = &[];
76/// match u32::try_decode_from(empty) {
77///     Ok((value, _)) => println!("Decoded: {}", value),
78///     Err(Error::UnsignedVarintDecode { .. }) => {
79///         // Expected for empty input
80///     }
81///     Err(e) => panic!("Unexpected error: {}", e),
82/// }
83/// ```
84///
85/// # Implemented For
86///
87/// - `bool`: Decodes 0 as false, non-zero as true
88/// - `u8`, `u16`, `u32`, `u64`, `u128`: Variable-length decoding
89/// - `usize`: Platform-dependent (32-bit or 64-bit)
90///
91/// # Length Bounds
92///
93/// Integer decoders delegate to the `unsigned-varint` crate, which enforces
94/// type-specific maximum byte counts (10 bytes for `u64`, 19 for `u128`).
95/// These bounds prevent unbounded varint expansion but do **not** cap the
96/// size of higher-level structures built on top of varints. Callers that
97/// decode length-prefixed payloads (e.g. `Varbytes` in `multi-util`) should
98/// enforce their own upper bound on the claimed length before allocating.
99pub trait TryDecodeFrom<'a>: Sized {
100    /// The error type returned on decoding failure.
101    ///
102    /// For built-in implementations, this is [`Error`].
103    type Error;
104
105    /// Try to decode a value from a byte slice.
106    ///
107    /// On success, returns a tuple containing:
108    /// 1. The decoded value
109    /// 2. A slice reference to the remaining unconsumed bytes
110    ///
111    /// The remaining bytes slice shares the same lifetime as the input,
112    /// enabling zero-copy sequential parsing.
113    ///
114    /// # Errors
115    ///
116    /// Returns an error if:
117    /// - The input slice is too short
118    /// - The varint encoding is invalid
119    /// - The encoded value is truncated
120    ///
121    /// # Examples
122    ///
123    /// ```rust
124    /// use multi_trait::TryDecodeFrom;
125    ///
126    /// let bytes = vec![0xFF, 0x01]; // Varint encoding of 255
127    /// let (value, remaining) = u8::try_decode_from(&bytes).unwrap();
128    /// assert_eq!(value, 255);
129    /// assert!(remaining.is_empty());
130    /// ```
131    fn try_decode_from(bytes: &'a [u8]) -> Result<(Self, &'a [u8]), Self::Error>;
132}
133
134/// Macro to implement `TryDecodeFrom` for unsigned integer types using varint decoding.
135///
136/// This macro eliminates code duplication by generating identical implementations
137/// for different numeric types. Each implementation:
138/// 1. Calls the appropriate decode function from `unsigned_varint`
139/// 2. Maps any decode error to a properly structured Error with source chain
140/// 3. Returns the decoded value and remaining bytes
141///
142/// # Usage
143///
144/// ```text
145/// impl_try_decode_from! {
146///     u8 => u8;
147///     u16 => u16;
148/// }
149/// ```
150///
151/// # Error Handling
152///
153/// The macro properly constructs errors with source chains for debugging,
154/// following Rust error handling best practices.
155///
156/// # Hygiene
157///
158/// This macro uses fully qualified paths to ensure proper hygiene and avoid
159/// namespace collisions with user code.
160macro_rules! impl_try_decode_from {
161    ($($type:ty => $decode_fn:ident);+ $(;)?) => {
162        $(
163            #[doc = concat!("Try to decode a varuint encoded ", stringify!($type))]
164            impl<'a> TryDecodeFrom<'a> for $type {
165                type Error = Error;
166
167                #[inline]
168                fn try_decode_from(bytes: &'a [u8]) -> Result<(Self, &'a [u8]), Self::Error> {
169                    decode::$decode_fn(bytes)
170                        .map_err(|source| {
171                            #[cfg(feature = "std")]
172                            { Self::Error::UnsignedVarintDecode { source } }
173                            #[cfg(not(feature = "std"))]
174                            { Self::Error::UnsignedVarintDecode { message: alloc::format!("{:?}", source) } }
175                        })
176                }
177            }
178        )+
179    };
180}
181
182/// Try to decode a varuint encoded bool
183impl<'a> TryDecodeFrom<'a> for bool {
184    type Error = Error;
185
186    #[inline]
187    fn try_decode_from(bytes: &'a [u8]) -> Result<(Self, &'a [u8]), Self::Error> {
188        let (v, ptr) = decode::u8(bytes).map_err(|source| {
189            #[cfg(feature = "std")]
190            {
191                Self::Error::UnsignedVarintDecode { source }
192            }
193            #[cfg(not(feature = "std"))]
194            {
195                Self::Error::UnsignedVarintDecode {
196                    message: alloc::format!("{:?}", source),
197                }
198            }
199        })?;
200        Ok(((v != 0), ptr))
201    }
202}
203
204// Implement TryDecodeFrom for all unsigned integer types using the macro
205impl_try_decode_from! {
206    u8 => u8;
207    u16 => u16;
208    u32 => u32;
209    u64 => u64;
210    u128 => u128;
211    usize => usize;
212}
213
214/// Decode a fixed-length byte array (reads N bytes; used for BLS share identifiers).
215impl<'a, const N: usize> TryDecodeFrom<'a> for [u8; N] {
216    type Error = Error;
217
218    #[inline]
219    fn try_decode_from(bytes: &'a [u8]) -> Result<([u8; N], &'a [u8]), Self::Error> {
220        if bytes.len() < N {
221            return Err(Error::InsufficientData {
222                expected: N,
223                actual: bytes.len(),
224            });
225        }
226        let (head, rest) = bytes.split_at(N);
227        let arr = <[u8; N]>::try_from(head).map_err(|_| Error::InsufficientData {
228            expected: N,
229            actual: bytes.len(),
230        })?;
231        Ok((arr, rest))
232    }
233}