Skip to main content

radicle_protocol/
bounded.rs

1use std::{
2    collections::BTreeSet,
3    ops::{self, RangeBounds},
4};
5
6#[derive(thiserror::Error, Debug)]
7pub enum Error {
8    #[error("invalid size: expected {expected}, got {actual}")]
9    InvalidSize { expected: usize, actual: usize },
10}
11
12/// A vector with an upper limit on its size using type level constants.
13#[derive(Default, Clone, PartialEq, Eq)]
14pub struct BoundedVec<T, const N: usize> {
15    v: Vec<T>,
16}
17
18impl<T, const N: usize> BoundedVec<T, N> {
19    /// Create a new empty `BoundedVec<T,N>`.
20    pub fn new() -> Self {
21        BoundedVec {
22            v: Vec::with_capacity(N),
23        }
24    }
25
26    /// Build a `BoundedVec` by consuming from the given iterator up to its limit.
27    ///
28    /// # Examples
29    ///
30    /// ```
31    /// use radicle_protocol::bounded;
32    ///
33    /// let mut iter = (0..4).into_iter();
34    /// let bounded: bounded::BoundedVec<i32,3> = bounded::BoundedVec::collect_from(&mut iter);
35    ///
36    /// assert_eq!(bounded.len(), 3);
37    /// assert_eq!(iter.count(), 1);
38    /// ```
39    pub fn collect_from<I: IntoIterator<Item = T>>(iter: I) -> Self {
40        BoundedVec {
41            v: iter.into_iter().take(N).collect(),
42        }
43    }
44
45    /// Create a new `BoundedVec<T,N>` which takes upto the first N values of its argument, taking
46    /// ownership.
47    ///
48    /// # Examples
49    ///
50    /// ```
51    /// use radicle_protocol::bounded;
52    ///
53    /// let mut vec = vec![1, 2, 3];
54    /// let bounded = bounded::BoundedVec::<_, 2>::truncate(vec);
55    /// assert_eq!(bounded.len(), 2);
56    /// ```
57    pub fn truncate(mut v: Vec<T>) -> Self {
58        v.truncate(N);
59        BoundedVec { v }
60    }
61
62    /// Like [`Vec::with_capacity`] but returns an error if the allocation size exceeds the limit.
63    ///
64    /// # Examples
65    ///
66    /// ```
67    /// use radicle_protocol::bounded;
68    ///
69    /// let vec = bounded::BoundedVec::<i32, 11>::with_capacity(10).unwrap();
70    ///
71    /// // The vector contains no items, even though it has capacity for more
72    /// assert_eq!(vec.len(), 0);
73    /// assert!(vec.capacity() >= 10);
74    ///
75    /// // A vector with a capacity over its limit will result in error.
76    /// let vec_res = bounded::BoundedVec::<i32, 10>::with_capacity(11);
77    /// assert!(vec_res.is_err());
78    /// ```
79    #[inline]
80    pub fn with_capacity(capacity: usize) -> Result<Self, Error> {
81        if capacity > N {
82            return Err(Error::InvalidSize {
83                expected: N,
84                actual: capacity,
85            });
86        }
87        Ok(Self {
88            v: Vec::with_capacity(capacity),
89        })
90    }
91
92    /// Return the maximum number of elements BoundedVec can contain.
93    ///
94    /// # Examples
95    ///
96    /// ```
97    /// use radicle_protocol::bounded;
98    ///
99    /// type Inventory = bounded::BoundedVec<(), 10>;
100    /// assert_eq!(Inventory::max(), 10);
101    /// ```
102    #[inline]
103    pub fn max() -> usize {
104        N
105    }
106
107    /// Extracts a slice containing the entire bounded vector.
108    #[inline]
109    pub fn as_slice(&self) -> &[T] {
110        self.v.as_slice()
111    }
112
113    /// Returns the number of elements the bounded vector can hold without reallocating.
114    pub fn capacity(&self) -> usize {
115        self.v.capacity()
116    }
117
118    /// Like [`Vec::push`] but returns an error if the limit is exceeded.
119    ///
120    /// # Examples
121    ///
122    /// ```
123    /// use radicle_protocol::bounded;
124    ///
125    /// let mut vec: bounded::BoundedVec<_,3> = vec![1, 2].try_into().unwrap();
126    /// vec.push(3).expect("within limit");
127    /// assert_eq!(vec, vec![1, 2, 3].try_into().unwrap());
128    ///
129    /// // ...but this will exceed its limit, returning an error.
130    /// vec.push(4).expect_err("limit exceeded");
131    /// assert_eq!(vec.len(), 3);
132    /// ```
133    #[inline]
134    pub fn push(&mut self, item: T) -> Result<(), Error> {
135        if self.len() >= N {
136            return Err(Error::InvalidSize {
137                expected: N,
138                actual: N + 1,
139            });
140        }
141        self.v.push(item);
142        Ok(())
143    }
144
145    /// Return the underlying vector without an upper limit.
146    ///
147    /// # Examples
148    ///
149    /// ```
150    /// use radicle_protocol::bounded;
151    ///
152    /// let mut bounded: bounded::BoundedVec<_,3> = vec![1, 2, 3].try_into().unwrap();
153    /// let mut vec = bounded.unbound();
154    ///
155    /// vec.push(4);
156    /// assert_eq!(vec.len(), 4);
157    /// ```
158    pub fn unbound(self) -> Vec<T> {
159        self.v
160    }
161
162    /// Calls [`std::vec::Drain`].
163    pub fn drain<R: RangeBounds<usize>>(&mut self, range: R) -> std::vec::Drain<'_, T> {
164        self.v.drain(range)
165    }
166}
167
168impl<T: Clone, const N: usize> BoundedVec<T, N> {
169    /// Like [`Vec::extend_from_slice`] but returns an error if out of bounds.
170    pub fn extend_from_slice(&mut self, slice: &[T]) -> Result<(), Error> {
171        if self.len() + slice.len() > N {
172            return Err(Error::InvalidSize {
173                expected: N,
174                actual: self.len() + slice.len(),
175            });
176        }
177        self.v.extend_from_slice(slice);
178
179        Ok(())
180    }
181}
182
183impl<T, const N: usize> IntoIterator for BoundedVec<T, N> {
184    type Item = T;
185    type IntoIter = std::vec::IntoIter<T>;
186
187    fn into_iter(self) -> Self::IntoIter {
188        self.v.into_iter()
189    }
190}
191
192impl<T, const N: usize> ops::Deref for BoundedVec<T, N> {
193    type Target = [T];
194
195    fn deref(&self) -> &Self::Target {
196        self.v.as_slice()
197    }
198}
199
200impl<T, const N: usize> From<Option<T>> for BoundedVec<T, N> {
201    fn from(value: Option<T>) -> Self {
202        let v = match value {
203            None => vec![],
204            Some(v) => vec![v],
205        };
206        BoundedVec { v }
207    }
208}
209
210impl<T, const N: usize> TryFrom<Vec<T>> for BoundedVec<T, N> {
211    type Error = Error;
212
213    fn try_from(value: Vec<T>) -> Result<Self, Self::Error> {
214        if value.len() > N {
215            return Err(Error::InvalidSize {
216                expected: N,
217                actual: value.len(),
218            });
219        }
220        Ok(BoundedVec { v: value })
221    }
222}
223
224impl<T, const N: usize> TryFrom<BTreeSet<T>> for BoundedVec<T, N> {
225    type Error = Error;
226
227    fn try_from(value: BTreeSet<T>) -> Result<Self, Self::Error> {
228        if value.len() > N {
229            return Err(Error::InvalidSize {
230                expected: N,
231                actual: value.len(),
232            });
233        }
234        Ok(BoundedVec {
235            v: value.into_iter().collect(),
236        })
237    }
238}
239
240impl<T, const N: usize> From<BoundedVec<T, N>> for Vec<T> {
241    fn from(value: BoundedVec<T, N>) -> Self {
242        value.v
243    }
244}
245
246impl<T: std::fmt::Debug, const N: usize> std::fmt::Debug for BoundedVec<T, N> {
247    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
248        self.v.fmt(f)
249    }
250}
251
252unsafe impl<const N: usize> bytes::BufMut for BoundedVec<u8, N> {
253    fn remaining_mut(&self) -> usize {
254        N - self.v.len()
255    }
256
257    unsafe fn advance_mut(&mut self, cnt: usize) {
258        let len = {
259            let len = self.v.len();
260            let remaining = N - len;
261
262            if remaining >= cnt {
263                len + cnt
264            } else {
265                panic!("advance out of bounds: have {remaining} remaining, but advancing by {cnt}",);
266            }
267        };
268
269        debug_assert!(len <= N);
270
271        // Addition will not overflow since the sum is at most the capacity.
272        self.v.set_len(len);
273    }
274
275    fn chunk_mut(&mut self) -> &mut bytes::buf::UninitSlice {
276        let len = self.v.len();
277
278        // If the vector is full, we double its capacity using `reserve`, but not beyond the limit.
279        if self.v.capacity() == len {
280            self.v.reserve(std::cmp::min(len, N - len));
281        }
282
283        let cap = self.v.capacity();
284
285        debug_assert!(cap <= N);
286        debug_assert!(len <= cap);
287
288        let ptr = self.v.as_mut_ptr();
289
290        // SAFETY: Since `ptr` is valid for `cap` bytes, `ptr.add(len)` must be
291        // valid for `cap - len` bytes. The subtraction will not underflow since
292        // `len <= cap`.
293        unsafe { bytes::buf::UninitSlice::from_raw_parts_mut(ptr.add(len), cap - len) }
294    }
295}
296
297#[cfg(any(test, feature = "test"))]
298impl<T, const N: usize> qcheck::Arbitrary for BoundedVec<T, N>
299where
300    T: qcheck::Arbitrary + Eq,
301{
302    fn arbitrary(g: &mut qcheck::Gen) -> Self {
303        let mut v: Vec<T> = qcheck::Arbitrary::arbitrary(g);
304        v.truncate(N);
305        v.try_into().expect("size within bounds")
306    }
307}