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 #[must_use]
109 pub fn len(&self) -> usize {
110 self.0.len()
111 }
112
113 /// Returns `true` if the encoded data is empty.
114 ///
115 /// Note: Empty `EncodedBytes` cannot be constructed through normal means,
116 /// as empty bytes don't represent a valid varint. This method exists for
117 /// API completeness.
118 ///
119 /// # Examples
120 ///
121 /// ```rust
122 /// use multi_trait::EncodedBytes;
123 ///
124 /// let encoded = EncodedBytes::new(&[42]).unwrap();
125 /// assert!(!encoded.is_empty());
126 /// ```
127 #[inline]
128 #[must_use]
129 pub fn is_empty(&self) -> bool {
130 self.0.is_empty()
131 }
132
133 /// Returns a reference to the underlying bytes.
134 ///
135 /// # Examples
136 ///
137 /// ```rust
138 /// use multi_trait::EncodedBytes;
139 ///
140 /// let encoded = EncodedBytes::new(&[42]).unwrap();
141 /// assert_eq!(encoded.as_bytes(), &[42]);
142 /// ```
143 #[inline]
144 #[must_use]
145 pub fn as_bytes(&self) -> &[u8] {
146 &self.0
147 }
148
149 /// Consumes the `EncodedBytes` and returns the underlying `Vec<u8>`.
150 ///
151 /// This is a zero-cost operation that transfers ownership without cloning.
152 ///
153 /// # Examples
154 ///
155 /// ```rust
156 /// use multi_trait::EncodedBytes;
157 ///
158 /// let encoded = EncodedBytes::new(&[42]).unwrap();
159 /// let bytes = encoded.into_vec();
160 /// assert_eq!(bytes, vec![42]);
161 /// ```
162 #[inline]
163 #[must_use]
164 pub fn into_vec(self) -> Vec<u8> {
165 self.0
166 }
167}
168
169impl TryFrom<Vec<u8>> for EncodedBytes {
170 type Error = Error;
171
172 /// Attempt to create `EncodedBytes` from a `Vec<u8>`, validating the data.
173 ///
174 /// This validates that the bytes form a complete, valid varint encoding.
175 /// The validation checks:
176 /// 1. The bytes are not empty
177 /// 2. The varint can be successfully decoded
178 /// 3. All bytes are consumed (no trailing data)
179 ///
180 /// # Errors
181 ///
182 /// Returns [`Error::InsufficientData`] if the bytes are empty or incomplete.
183 /// Returns [`Error::UnsignedVarintDecode`] if the varint encoding is invalid.
184 /// Returns [`Error::InvalidEncoding`] if there are trailing bytes after a
185 /// valid varint.
186 ///
187 /// # Examples
188 ///
189 /// ```rust
190 /// use multi_trait::EncodedBytes;
191 ///
192 /// // Valid single-byte encoding
193 /// let valid = vec![42];
194 /// assert!(EncodedBytes::try_from(valid).is_ok());
195 ///
196 /// // Invalid: continuation bit without following byte
197 /// let invalid = vec![0x80];
198 /// assert!(EncodedBytes::try_from(invalid).is_err());
199 /// ```
200 fn try_from(bytes: Vec<u8>) -> Result<Self, Self::Error> {
201 // Check for empty input
202 if bytes.is_empty() {
203 return Err(Error::InsufficientData {
204 expected: 1,
205 actual: 0,
206 });
207 }
208
209 // Validate that bytes form a complete varint by attempting decode
210 // We use u128 as it can represent any varint value
211 let remaining = match decode::u128(&bytes) {
212 Ok((_, remaining)) => remaining,
213 Err(source) => {
214 #[cfg(feature = "std")]
215 {
216 return Err(Error::UnsignedVarintDecode { source });
217 }
218 #[cfg(not(feature = "std"))]
219 {
220 return Err(Error::UnsignedVarintDecode {
221 message: format!("{:?}", source),
222 });
223 }
224 }
225 };
226
227 // Ensure no trailing bytes
228 if !remaining.is_empty() {
229 return Err(Error::InvalidEncoding {
230 reason: format!(
231 "trailing bytes after valid varint: {} bytes remaining",
232 remaining.len()
233 ),
234 });
235 }
236
237 Ok(Self(bytes))
238 }
239}
240
241impl From<EncodedBytes> for Vec<u8> {
242 /// Convert `EncodedBytes` back into a `Vec<u8>`.
243 ///
244 /// This is a zero-cost operation that consumes the `EncodedBytes`.
245 ///
246 /// # Examples
247 ///
248 /// ```rust
249 /// use multi_trait::EncodedBytes;
250 ///
251 /// let encoded = EncodedBytes::new(&[42]).unwrap();
252 /// let bytes: Vec<u8> = encoded.into();
253 /// assert_eq!(bytes, vec![42]);
254 /// ```
255 #[inline]
256 fn from(encoded: EncodedBytes) -> Self {
257 encoded.0
258 }
259}
260
261impl AsRef<[u8]> for EncodedBytes {
262 /// Returns a reference to the encoded bytes as a slice.
263 ///
264 /// # Examples
265 ///
266 /// ```rust
267 /// use multi_trait::EncodedBytes;
268 ///
269 /// let encoded = EncodedBytes::new(&[42]).unwrap();
270 /// let slice: &[u8] = encoded.as_ref();
271 /// assert_eq!(slice, &[42]);
272 /// ```
273 #[inline]
274 fn as_ref(&self) -> &[u8] {
275 &self.0
276 }
277}
278
279impl Deref for EncodedBytes {
280 type Target = [u8];
281
282 /// Dereferences to the underlying byte slice.
283 ///
284 /// This allows `EncodedBytes` to be used anywhere a `&[u8]` is expected.
285 ///
286 /// # Examples
287 ///
288 /// ```rust
289 /// use multi_trait::EncodedBytes;
290 ///
291 /// let encoded = EncodedBytes::new(&[42]).unwrap();
292 /// assert_eq!(encoded[0], 42); // Deref enables indexing
293 /// ```
294 #[inline]
295 fn deref(&self) -> &Self::Target {
296 &self.0
297 }
298}
299
300// `EncodedBytes` is `Send + Sync` automatically because its only field is a
301// `Vec<u8>` (which is `Send + Sync`), the data is immutable after
302// construction, and there is no interior mutability. No `unsafe impl` is
303// required; this is documented here because an earlier revision carried
304// redundant `unsafe impl Send/Sync` that violated the crate's
305// `#![deny(unsafe_code)]` policy.
306
307#[cfg(test)]
308mod tests {
309 use super::*;
310
311 #[test]
312 fn test_valid_single_byte() {
313 let bytes = vec![42];
314 let encoded = EncodedBytes::try_from(bytes).unwrap();
315 assert_eq!(encoded.as_ref(), &[42]);
316 assert_eq!(encoded.len(), 1);
317 assert!(!encoded.is_empty());
318 }
319
320 #[test]
321 fn test_valid_multi_byte() {
322 // Varint encoding of 128 requires 2 bytes
323 let bytes = vec![0x80, 0x01];
324 let encoded = EncodedBytes::try_from(bytes).unwrap();
325 assert_eq!(encoded.len(), 2);
326 }
327
328 #[test]
329 fn test_empty_bytes_rejected() {
330 let empty: Vec<u8> = vec![];
331 let result = EncodedBytes::try_from(empty);
332 assert!(result.is_err());
333 if let Err(Error::InsufficientData { expected, actual }) = result {
334 assert_eq!(expected, 1);
335 assert_eq!(actual, 0);
336 } else {
337 panic!("Expected InsufficientData error");
338 }
339 }
340
341 #[test]
342 fn test_truncated_varint_rejected() {
343 // Continuation bit set but no following byte
344 let truncated = vec![0x80];
345 let result = EncodedBytes::try_from(truncated);
346 assert!(result.is_err());
347 }
348
349 #[test]
350 fn test_trailing_bytes_rejected() {
351 // Valid varint followed by extra bytes
352 let mut bytes = vec![42];
353 bytes.extend_from_slice(&[0xFF, 0xEE]);
354 let result = EncodedBytes::try_from(bytes);
355 assert!(result.is_err());
356 if let Err(Error::InvalidEncoding { reason }) = result {
357 assert!(reason.contains("trailing bytes"));
358 } else {
359 panic!("Expected InvalidEncoding error");
360 }
361 }
362
363 #[test]
364 fn test_into_vec() {
365 // Use proper varint encoding
366 let original = vec![0x80, 0x01]; // Varint encoding of 128
367 let encoded = EncodedBytes::try_from(original.clone()).unwrap();
368 let recovered: Vec<u8> = encoded.into();
369 assert_eq!(recovered, original);
370 }
371
372 #[test]
373 fn test_deref() {
374 let encoded = EncodedBytes::new(&[42]).unwrap();
375 assert_eq!(encoded[0], 42); // Tests Deref
376 }
377
378 #[test]
379 fn test_clone() {
380 let encoded = EncodedBytes::new(&[42]).unwrap();
381 let cloned = encoded.clone();
382 assert_eq!(encoded, cloned);
383 }
384
385 #[test]
386 fn test_debug() {
387 let encoded = EncodedBytes::new(&[42]).unwrap();
388 let debug_str = format!("{encoded:?}");
389 assert!(debug_str.contains("EncodedBytes"));
390 }
391
392 // Compile-time verification of Send + Sync
393 #[test]
394 fn assert_send_sync() {
395 fn is_send<T: Send>() {}
396 fn is_sync<T: Sync>() {}
397 is_send::<EncodedBytes>();
398 is_sync::<EncodedBytes>();
399 }
400}