Skip to main content

bincode_next/features/
bounded.rs

1//! Bounded types for compile-time size guarantees.
2//!
3//! These types enforce capacity limits both at construction time and during
4//! decoding, preventing Denial-of-Service attacks via unbounded allocations.
5
6#![cfg(feature = "alloc")]
7
8use crate::de::BorrowDecode;
9use crate::de::BorrowDecoder;
10use crate::de::Decode;
11use crate::de::Decoder;
12use crate::de::read::Reader;
13use crate::enc::Encode;
14use crate::enc::Encoder;
15use crate::error::DecodeError;
16use crate::error::EncodeError;
17use crate::static_size::StaticSize;
18use crate::static_size::helpers::VARINT_MAX_64;
19use alloc::string::String;
20use alloc::vec::Vec;
21
22/// A `Vec` wrapper with a compile-time capacity limit.
23///
24/// During decoding, the length is validated *before* allocation to prevent
25/// OOM attacks from malicious length prefixes.
26#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
27pub struct BoundedVec<T, const CAP: usize>(pub Vec<T>);
28
29impl<T, const CAP: usize> StaticSize for BoundedVec<T, CAP>
30where
31    T: StaticSize,
32{
33    const MAX_SIZE: usize = VARINT_MAX_64 + T::MAX_SIZE * CAP;
34}
35
36impl<T: Encode, const CAP: usize> Encode for BoundedVec<T, CAP> {
37    #[inline]
38    fn encode<E: Encoder>(
39        &self,
40        encoder: &mut E,
41    ) -> Result<(), EncodeError> {
42        if self.0.len() > CAP {
43            return crate::error::cold_encode_error_other("BoundedVec exceeds capacity");
44        }
45        self.0.encode(encoder)
46    }
47}
48
49impl<Context, T: Decode<Context>, const CAP: usize> Decode<Context> for BoundedVec<T, CAP> {
50    #[inline]
51    fn decode<D: Decoder<Context = Context>>(decoder: &mut D) -> Result<Self, DecodeError> {
52        // Decode length first and validate BEFORE allocating
53        let len = crate::de::decode_slice_len(decoder)?;
54        if len > CAP {
55            return crate::error::cold_decode_error_other("BoundedVec length exceeds capacity");
56        }
57
58        decoder.claim_container_read::<T>(len)?;
59        let mut vec = Vec::with_capacity(len);
60        for _ in 0..len {
61            decoder.unclaim_bytes_read(core::mem::size_of::<T>());
62            vec.push(T::decode(decoder)?);
63        }
64        Ok(Self(vec))
65    }
66}
67
68impl<'de, Context, T: BorrowDecode<'de, Context>, const CAP: usize> BorrowDecode<'de, Context>
69    for BoundedVec<T, CAP>
70{
71    fn borrow_decode<D: BorrowDecoder<'de, Context = Context>>(
72        decoder: &mut D
73    ) -> Result<Self, DecodeError> {
74        // Decode length first and validate BEFORE allocating
75        let len = crate::de::decode_slice_len(decoder)?;
76        if len > CAP {
77            return crate::error::cold_decode_error_other("BoundedVec length exceeds capacity");
78        }
79
80        decoder.claim_container_read::<T>(len)?;
81        let mut vec = Vec::with_capacity(len);
82        for _ in 0..len {
83            decoder.unclaim_bytes_read(core::mem::size_of::<T>());
84            vec.push(T::borrow_decode(decoder)?);
85        }
86        Ok(Self(vec))
87    }
88}
89
90/// A `String` wrapper with a compile-time byte-length capacity limit.
91///
92/// During decoding, the byte length is validated *before* allocation to
93/// prevent OOM attacks from malicious length prefixes.
94#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
95pub struct BoundedString<const CAP: usize>(pub String);
96
97impl<const CAP: usize> StaticSize for BoundedString<CAP> {
98    const MAX_SIZE: usize = VARINT_MAX_64 + CAP;
99}
100
101impl<const CAP: usize> Encode for BoundedString<CAP> {
102    #[inline]
103    fn encode<E: Encoder>(
104        &self,
105        encoder: &mut E,
106    ) -> Result<(), EncodeError> {
107        if self.0.len() > CAP {
108            return crate::error::cold_encode_error_other("BoundedString exceeds capacity");
109        }
110        self.0.encode(encoder)
111    }
112}
113
114impl<Context, const CAP: usize> Decode<Context> for BoundedString<CAP> {
115    #[inline]
116    fn decode<D: Decoder<Context = Context>>(decoder: &mut D) -> Result<Self, DecodeError> {
117        // Decode byte length first and validate BEFORE allocating
118        let len = crate::de::decode_slice_len(decoder)?;
119        if len > CAP {
120            return crate::error::cold_decode_error_other("BoundedString length exceeds capacity");
121        }
122
123        decoder.claim_container_read::<u8>(len)?;
124        let mut bytes = alloc::vec![0u8; len];
125        decoder.reader().read(&mut bytes)?;
126        let s = String::from_utf8(bytes)
127            .map_err(|e| crate::error::cold_decode_error_utf8::<()>(e.utf8_error()).unwrap_err())?;
128        Ok(Self(s))
129    }
130}
131
132impl<'de, Context, const CAP: usize> BorrowDecode<'de, Context> for BoundedString<CAP> {
133    fn borrow_decode<D: BorrowDecoder<'de, Context = Context>>(
134        decoder: &mut D
135    ) -> Result<Self, DecodeError> {
136        // Decode byte length first and validate BEFORE allocating
137        let len = crate::de::decode_slice_len(decoder)?;
138        if len > CAP {
139            return crate::error::cold_decode_error_other("BoundedString length exceeds capacity");
140        }
141
142        decoder.claim_container_read::<u8>(len)?;
143        let mut bytes = alloc::vec![0u8; len];
144        decoder.reader().read(&mut bytes)?;
145        let s = String::from_utf8(bytes)
146            .map_err(|e| crate::error::cold_decode_error_utf8::<()>(e.utf8_error()).unwrap_err())?;
147        Ok(Self(s))
148    }
149}
150
151/// Error returned when a value exceeds a bounded type's capacity.
152#[derive(Debug, Clone, PartialEq, Eq)]
153pub struct BoundsExceeded;
154
155impl core::fmt::Display for BoundsExceeded {
156    #[inline]
157    fn fmt(
158        &self,
159        f: &mut core::fmt::Formatter<'_>,
160    ) -> core::fmt::Result {
161        write!(f, "value exceeds bounded capacity")
162    }
163}
164
165impl<T, const CAP: usize> TryFrom<Vec<T>> for BoundedVec<T, CAP> {
166    type Error = BoundsExceeded;
167
168    #[inline]
169    fn try_from(v: Vec<T>) -> Result<Self, Self::Error> {
170        if v.len() > CAP {
171            Err(BoundsExceeded)
172        } else {
173            Ok(Self(v))
174        }
175    }
176}
177
178impl<const CAP: usize> TryFrom<String> for BoundedString<CAP> {
179    type Error = BoundsExceeded;
180
181    #[inline]
182    fn try_from(s: String) -> Result<Self, Self::Error> {
183        if s.len() > CAP {
184            Err(BoundsExceeded)
185        } else {
186            Ok(Self(s))
187        }
188    }
189}
190
191impl<T, const CAP: usize> BoundedVec<T, CAP> {
192    /// Create a new empty `BoundedVec`.
193    #[must_use]
194    #[inline(always)]
195    pub const fn new() -> Self {
196        Self(Vec::new())
197    }
198
199    /// Try to push an element, returning an error if the capacity would be exceeded.
200    ///
201    /// # Errors
202    ///
203    /// Returns `BoundsExceeded` if the vector is already at its maximum capacity.
204    #[inline]
205    pub fn try_push(
206        &mut self,
207        value: T,
208    ) -> Result<(), BoundsExceeded> {
209        if self.0.len() >= CAP {
210            Err(BoundsExceeded)
211        } else {
212            self.0.push(value);
213            Ok(())
214        }
215    }
216
217    /// Returns the inner `Vec<T>`.
218    #[must_use]
219    #[inline(always)]
220    pub fn into_inner(self) -> Vec<T> {
221        self.0
222    }
223}
224
225impl<T, const CAP: usize> Default for BoundedVec<T, CAP> {
226    #[inline(always)]
227    fn default() -> Self {
228        Self::new()
229    }
230}
231
232impl<const CAP: usize> BoundedString<CAP> {
233    /// Create a new empty `BoundedString`.
234    #[must_use]
235    #[inline(always)]
236    pub const fn new() -> Self {
237        Self(String::new())
238    }
239
240    /// Returns the inner `String`.
241    #[must_use]
242    #[inline(always)]
243    pub fn into_inner(self) -> String {
244        self.0
245    }
246}
247
248impl<const CAP: usize> Default for BoundedString<CAP> {
249    #[inline(always)]
250    fn default() -> Self {
251        Self::new()
252    }
253}
254
255impl<T, const CAP: usize> core::ops::Deref for BoundedVec<T, CAP> {
256    type Target = Vec<T>;
257
258    #[inline(always)]
259    fn deref(&self) -> &Self::Target {
260        &self.0
261    }
262}
263
264impl<const CAP: usize> core::ops::Deref for BoundedString<CAP> {
265    type Target = String;
266
267    #[inline(always)]
268    fn deref(&self) -> &Self::Target {
269        &self.0
270    }
271}