Skip to main content

multi_trait/
encoded_bytes.rs

1// SPDX-License-Identifier: Apache-2.0
2//! Validated newtype for varint-encoded byte sequences.
3//!
4//! This module provides the [`EncodedBytes`] type, a validated wrapper around
5//! `Vec<u8>` that guarantees the bytes represent a valid, complete varint-encoded
6//! value. This follows the "parse, don't validate" principle by making invalid
7//! states unrepresentable at the type level.
8
9#[cfg(not(feature = "std"))]
10use alloc::{format, vec::Vec};
11
12use crate::error::Error;
13use core::ops::Deref;
14use unsigned_varint::decode;
15
16/// A validated wrapper for varint-encoded bytes.
17///
18/// This newtype ensures that the contained bytes form a valid, complete
19/// varint-encoded value. The validation is performed at construction time,
20/// making it impossible to create an `EncodedBytes` instance with invalid data.
21///
22/// # Type Safety Benefits
23///
24/// Using `EncodedBytes` provides several advantages:
25///
26/// - **Validation at the boundary**: Invalid data is rejected at construction
27/// - **Type-level guarantees**: If you have an `EncodedBytes`, it's valid
28/// - **Clear API contracts**: Functions accepting `EncodedBytes` don't need validation
29/// - **Zero-cost abstraction**: No runtime overhead after construction
30///
31/// # Thread Safety
32///
33/// `EncodedBytes` is both `Send` and `Sync`, making it safe to share across
34/// thread boundaries. The contained data is immutable after validation.
35///
36/// # Examples
37///
38/// ## Creating from valid data
39///
40/// ```rust
41/// use multi_trait::EncodedBytes;
42///
43/// // Valid varint encoding of 42
44/// let bytes = vec![42u8];
45/// let encoded = EncodedBytes::try_from(bytes).unwrap();
46/// assert_eq!(encoded.as_ref(), &[42]);
47/// ```
48///
49/// ## Validation catches invalid data
50///
51/// ```rust
52/// use multi_trait::EncodedBytes;
53///
54/// // Invalid: continuation bit set but no following byte
55/// let invalid = vec![0x80];
56/// let result = EncodedBytes::try_from(invalid);
57/// assert!(result.is_err());
58/// ```
59///
60/// ## Zero-cost unwrapping
61///
62/// ```rust
63/// use multi_trait::EncodedBytes;
64///
65/// let bytes = vec![42u8];
66/// let encoded = EncodedBytes::try_from(bytes).unwrap();
67///
68/// // Convert back to Vec<u8> with no allocation
69/// let original: Vec<u8> = encoded.into();
70/// assert_eq!(original, vec![42]);
71/// ```
72#[derive(Debug, Clone, PartialEq, Eq, Hash)]
73pub struct EncodedBytes(Vec<u8>);
74
75impl EncodedBytes {
76    /// Create a new `EncodedBytes` from a byte slice, validating the data.
77    ///
78    /// This performs the same validation as `TryFrom<Vec<u8>>` but works
79    /// with slices, requiring an allocation.
80    ///
81    /// # Errors
82    ///
83    /// Returns an error if the bytes don't form a valid, complete varint.
84    ///
85    /// # Examples
86    ///
87    /// ```rust
88    /// use multi_trait::EncodedBytes;
89    ///
90    /// let encoded = EncodedBytes::new(&[42]).unwrap();
91    /// assert_eq!(encoded.as_ref(), &[42]);
92    /// ```
93    pub fn new(bytes: &[u8]) -> Result<Self, Error> {
94        Self::try_from(bytes.to_vec())
95    }
96
97    /// Returns the length of the encoded data in bytes.
98    ///
99    /// # Examples
100    ///
101    /// ```rust
102    /// use multi_trait::EncodedBytes;
103    ///
104    /// let encoded = EncodedBytes::new(&[42]).unwrap();
105    /// assert_eq!(encoded.len(), 1);
106    /// ```
107    #[inline]
108    pub fn len(&self) -> usize {
109        self.0.len()
110    }
111
112    /// Returns `true` if the encoded data is empty.
113    ///
114    /// Note: Empty `EncodedBytes` cannot be constructed through normal means,
115    /// as empty bytes don't represent a valid varint. This method exists for
116    /// API completeness.
117    ///
118    /// # Examples
119    ///
120    /// ```rust
121    /// use multi_trait::EncodedBytes;
122    ///
123    /// let encoded = EncodedBytes::new(&[42]).unwrap();
124    /// assert!(!encoded.is_empty());
125    /// ```
126    #[inline]
127    pub fn is_empty(&self) -> bool {
128        self.0.is_empty()
129    }
130
131    /// Returns a reference to the underlying bytes.
132    ///
133    /// # Examples
134    ///
135    /// ```rust
136    /// use multi_trait::EncodedBytes;
137    ///
138    /// let encoded = EncodedBytes::new(&[42]).unwrap();
139    /// assert_eq!(encoded.as_bytes(), &[42]);
140    /// ```
141    #[inline]
142    pub fn as_bytes(&self) -> &[u8] {
143        &self.0
144    }
145
146    /// Consumes the `EncodedBytes` and returns the underlying `Vec<u8>`.
147    ///
148    /// This is a zero-cost operation that transfers ownership without cloning.
149    ///
150    /// # Examples
151    ///
152    /// ```rust
153    /// use multi_trait::EncodedBytes;
154    ///
155    /// let encoded = EncodedBytes::new(&[42]).unwrap();
156    /// let bytes = encoded.into_vec();
157    /// assert_eq!(bytes, vec![42]);
158    /// ```
159    #[inline]
160    pub fn into_vec(self) -> Vec<u8> {
161        self.0
162    }
163}
164
165impl TryFrom<Vec<u8>> for EncodedBytes {
166    type Error = Error;
167
168    /// Attempt to create `EncodedBytes` from a `Vec<u8>`, validating the data.
169    ///
170    /// This validates that the bytes form a complete, valid varint encoding.
171    /// The validation checks:
172    /// 1. The bytes are not empty
173    /// 2. The varint can be successfully decoded
174    /// 3. All bytes are consumed (no trailing data)
175    ///
176    /// # Errors
177    ///
178    /// Returns [`Error::InsufficientData`] if the bytes are empty or incomplete.
179    /// Returns [`Error::UnsignedVarintDecode`] if the varint encoding is invalid.
180    /// Returns [`Error::InvalidEncoding`] if there are trailing bytes after a
181    /// valid varint.
182    ///
183    /// # Examples
184    ///
185    /// ```rust
186    /// use multi_trait::EncodedBytes;
187    ///
188    /// // Valid single-byte encoding
189    /// let valid = vec![42];
190    /// assert!(EncodedBytes::try_from(valid).is_ok());
191    ///
192    /// // Invalid: continuation bit without following byte
193    /// let invalid = vec![0x80];
194    /// assert!(EncodedBytes::try_from(invalid).is_err());
195    /// ```
196    fn try_from(bytes: Vec<u8>) -> Result<Self, Self::Error> {
197        // Check for empty input
198        if bytes.is_empty() {
199            return Err(Error::InsufficientData {
200                expected: 1,
201                actual: 0,
202            });
203        }
204
205        // Validate that bytes form a complete varint by attempting decode
206        // We use u128 as it can represent any varint value
207        let remaining = match decode::u128(&bytes) {
208            Ok((_, remaining)) => remaining,
209            Err(source) => {
210                #[cfg(feature = "std")]
211                {
212                    return Err(Error::UnsignedVarintDecode { source });
213                }
214                #[cfg(not(feature = "std"))]
215                {
216                    return Err(Error::UnsignedVarintDecode {
217                        message: format!("{:?}", source),
218                    });
219                }
220            }
221        };
222
223        // Ensure no trailing bytes
224        if !remaining.is_empty() {
225            return Err(Error::InvalidEncoding {
226                reason: format!(
227                    "trailing bytes after valid varint: {} bytes remaining",
228                    remaining.len()
229                ),
230            });
231        }
232
233        Ok(EncodedBytes(bytes))
234    }
235}
236
237impl From<EncodedBytes> for Vec<u8> {
238    /// Convert `EncodedBytes` back into a `Vec<u8>`.
239    ///
240    /// This is a zero-cost operation that consumes the `EncodedBytes`.
241    ///
242    /// # Examples
243    ///
244    /// ```rust
245    /// use multi_trait::EncodedBytes;
246    ///
247    /// let encoded = EncodedBytes::new(&[42]).unwrap();
248    /// let bytes: Vec<u8> = encoded.into();
249    /// assert_eq!(bytes, vec![42]);
250    /// ```
251    #[inline]
252    fn from(encoded: EncodedBytes) -> Self {
253        encoded.0
254    }
255}
256
257impl AsRef<[u8]> for EncodedBytes {
258    /// Returns a reference to the encoded bytes as a slice.
259    ///
260    /// # Examples
261    ///
262    /// ```rust
263    /// use multi_trait::EncodedBytes;
264    ///
265    /// let encoded = EncodedBytes::new(&[42]).unwrap();
266    /// let slice: &[u8] = encoded.as_ref();
267    /// assert_eq!(slice, &[42]);
268    /// ```
269    #[inline]
270    fn as_ref(&self) -> &[u8] {
271        &self.0
272    }
273}
274
275impl Deref for EncodedBytes {
276    type Target = [u8];
277
278    /// Dereferences to the underlying byte slice.
279    ///
280    /// This allows `EncodedBytes` to be used anywhere a `&[u8]` is expected.
281    ///
282    /// # Examples
283    ///
284    /// ```rust
285    /// use multi_trait::EncodedBytes;
286    ///
287    /// let encoded = EncodedBytes::new(&[42]).unwrap();
288    /// assert_eq!(encoded[0], 42); // Deref enables indexing
289    /// ```
290    #[inline]
291    fn deref(&self) -> &Self::Target {
292        &self.0
293    }
294}
295
296// Explicitly document Send + Sync bounds
297//
298// SAFETY: EncodedBytes is Send + Sync because:
299// 1. It contains only a Vec<u8>, which is Send + Sync
300// 2. The data is immutable after validation
301// 3. No interior mutability is used
302// 4. All validation happens at construction time
303unsafe impl Send for EncodedBytes {}
304unsafe impl Sync for EncodedBytes {}
305
306#[cfg(test)]
307mod tests {
308    use super::*;
309
310    #[test]
311    fn test_valid_single_byte() {
312        let bytes = vec![42];
313        let encoded = EncodedBytes::try_from(bytes).unwrap();
314        assert_eq!(encoded.as_ref(), &[42]);
315        assert_eq!(encoded.len(), 1);
316        assert!(!encoded.is_empty());
317    }
318
319    #[test]
320    fn test_valid_multi_byte() {
321        // Varint encoding of 128 requires 2 bytes
322        let bytes = vec![0x80, 0x01];
323        let encoded = EncodedBytes::try_from(bytes).unwrap();
324        assert_eq!(encoded.len(), 2);
325    }
326
327    #[test]
328    fn test_empty_bytes_rejected() {
329        let empty: Vec<u8> = vec![];
330        let result = EncodedBytes::try_from(empty);
331        assert!(result.is_err());
332        if let Err(Error::InsufficientData { expected, actual }) = result {
333            assert_eq!(expected, 1);
334            assert_eq!(actual, 0);
335        } else {
336            panic!("Expected InsufficientData error");
337        }
338    }
339
340    #[test]
341    fn test_truncated_varint_rejected() {
342        // Continuation bit set but no following byte
343        let truncated = vec![0x80];
344        let result = EncodedBytes::try_from(truncated);
345        assert!(result.is_err());
346    }
347
348    #[test]
349    fn test_trailing_bytes_rejected() {
350        // Valid varint followed by extra bytes
351        let mut bytes = vec![42];
352        bytes.extend_from_slice(&[0xFF, 0xEE]);
353        let result = EncodedBytes::try_from(bytes);
354        assert!(result.is_err());
355        if let Err(Error::InvalidEncoding { reason }) = result {
356            assert!(reason.contains("trailing bytes"));
357        } else {
358            panic!("Expected InvalidEncoding error");
359        }
360    }
361
362    #[test]
363    fn test_into_vec() {
364        // Use proper varint encoding
365        let original = vec![0x80, 0x01]; // Varint encoding of 128
366        let encoded = EncodedBytes::try_from(original.clone()).unwrap();
367        let recovered: Vec<u8> = encoded.into();
368        assert_eq!(recovered, original);
369    }
370
371    #[test]
372    fn test_deref() {
373        let encoded = EncodedBytes::new(&[42]).unwrap();
374        assert_eq!(encoded[0], 42); // Tests Deref
375    }
376
377    #[test]
378    fn test_clone() {
379        let encoded = EncodedBytes::new(&[42]).unwrap();
380        let cloned = encoded.clone();
381        assert_eq!(encoded, cloned);
382    }
383
384    #[test]
385    fn test_debug() {
386        let encoded = EncodedBytes::new(&[42]).unwrap();
387        let debug_str = format!("{:?}", encoded);
388        assert!(debug_str.contains("EncodedBytes"));
389    }
390
391    // Compile-time verification of Send + Sync
392    #[test]
393    fn assert_send_sync() {
394        fn is_send<T: Send>() {}
395        fn is_sync<T: Sync>() {}
396        is_send::<EncodedBytes>();
397        is_sync::<EncodedBytes>();
398    }
399}