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}