Skip to main content

bucket_vec/
lib.rs

1//! # Bucket Vector
2//!
3//! 100% `unsafe` Rust free!
4//!
5//! ## Description
6//!
7//! A vector-like data structure that organizes its elements into a set of buckets
8//! of fixed-capacity in order to guarantee that mutations to the bucket vector
9//! never moves elements and thus invalidates references to them.
10//!
11//! This is comparable to a `Vec<Box<T>>` but a lot more efficient.
12//!
13//! ## Under the Hood
14//!
15//! The `BucketVec` is really just a vector of `Bucket` instances.
16//! Whenever an element is pushed to the `BucketVec` the element is pushed onto
17//! the last `Bucket` if it isn't filled, yet.
18//! If the last `Bucket` is filled a new `Bucket` is pushed onto the `BucketVec`
19//! with a new capacity determined by the used bucket vector configuration.
20//!
21//! This way the `BucketVec` never moves elements around upon inserting new elements
22//! in order to preserve references. When a normal `Vec` is modified it can potentially
23//! invalidate references because of reallocation of the internal buffer which
24//! might cause severe bugs if references to the internal elements are stored
25//! outside the `Vec`. Note that normally Rust prevents such situations so the
26//! `BucketVec` is mainly used in the area of `unsafe` Rust where a developer
27//! actively decides that they want or need pinned references into another data
28//! structure.
29//!
30//! For the same reasons as stated above the `BucketVec` does not allow to remove
31//! or swap elements.
32//!
33//! ## Example
34//!
35//! Looking at an example `BucketVec<i32>` with the following configuration:
36//!
37//! - `start_capacity := 1`
38//! - `growth_rate := 2`
39//!
40//! We have already pushed the elements `A`,.., `K` onto it.
41//!
42//! ```no_compile
43//! [ [A], [B, C], [D, E, F, G], [H, I, J, K, _, _, _, _] ]
44//! ```
45//!
46//! Where `_` refers to a vacant bucket entry.
47//!
48//! Pushing another `L`,.., `O` onto the same `BucketVec` results in:
49//!
50//! ```no_compile
51//! [ [A], [B, C], [D, E, F, G], [H, I, J, K, L, M, N, O] ]
52//! ```
53//!
54//! So we are full on capacity for all buckets.
55//! The next time we push another element onto the `BucketVec` it will create a new `Bucket` with a capacity of `16` since `growth_rate == 2` and our current latest bucket already has a capacity of `8`.
56//!
57//! ```no_compile
58//! [ [A], [B, C], [D, E, F, G], [H, I, J, K, L, M, N, O], [P, 15 x _] ]
59//! ```
60//!
61//! Where `15 x _` denotes 15 consecutive vacant entries.
62
63#![cfg_attr(not(feature = "std"), no_std)]
64
65#[cfg(not(feature = "std"))]
66extern crate alloc;
67
68#[cfg(not(feature = "std"))]
69use alloc::vec::Vec;
70
71mod bucket;
72mod config;
73mod iter;
74mod math;
75mod scale;
76
77#[cfg(test)]
78mod tests;
79
80use self::bucket::Bucket;
81use self::math::FloatExt;
82pub use self::{
83    config::{BucketVecConfig, DefaultConfig},
84    iter::{IntoIter, Iter, IterMut},
85};
86use core::marker::PhantomData;
87
88/// A vector-like data structure that never moves its contained elements.
89///
90/// This is solved by using internal fixed-capacity buckets instead of boxing
91/// all elements in isolation.
92///
93/// # Formulas
94///
95/// ## Definitions
96///
97/// In the following we define
98///
99/// - `N := START_CAPACITY` and
100/// - `a := GROWTH_RATE`
101///
102/// ## Bucket Capacity
103///
104/// ### For `a != 1`:
105///
106/// The total capacity of all buckets until bucket `i` (not including `i`)
107/// is expressed as:
108///
109/// ```no_compile
110/// capacity_until(i) := N * (a^i - 1) / (a-1)
111/// ```
112///
113/// The capacity of the `i`th bucket is then calculated by:
114///
115/// ```no_compile
116/// capacity(i) := floor(capacity_until(i+1)) - floor(capacity_until(i))
117/// ```
118///
119/// Where `floor: f64 -> f64` rounds the `f64` down to the next even `f64`
120/// for positive `f64`.
121///
122/// Note that `capacity(i)` is approximately `capacity(i)' := N * a^i`.
123///
124/// ### For `a == 1`:
125///
126/// This case is trivial and all buckets are equally sized to have a
127/// capacity of `N`.
128///
129/// ## Accessing Elements by Index
130///
131/// Accessing the `i`th element of a `BucketVec` can be expressed by the
132/// following formulas:
133///
134/// ### For `a != 1`:
135///
136/// First we define the inverted capacity function for which
137/// `1 == capacity(i) * inv_capacity(i)` forall `i`.
138/// ```no_compile
139/// inv_capacity(i) = ceil(log(1 + (i + 1) * (a - 1) / N, a)) - 1
140/// ```
141/// Where `ceil: f64 -> f64` rounds the `f64` up to the next even `f64`
142/// for positive `f64`.
143///
144/// Having this the `bucket_index` and the `entry_index` inside the bucket
145/// indexed by `bucket_index` is expressed as:
146/// ```no_compile
147/// bucket_index(i) = inv_capacity(i)
148/// entry_index(i) = i - floor(capacity_until(bucket_index(i)))
149/// ```
150///
151/// ### For `a == 1`:
152///
153/// This case is very easy and we can simply calculate the `bucket_index` and
154/// `entry_index` by:
155///
156/// ```no_compile
157/// bucket_index(i) = i / N
158/// entry_index(i) = i % N
159/// ```
160#[derive(Debug)]
161pub struct BucketVec<T, C = DefaultConfig> {
162    /// The number of elements stored in the bucket vector.
163    len: usize,
164    /// The entry vector.
165    buckets: Vec<Bucket<T>>,
166    /// The config phantom data.
167    config: PhantomData<fn() -> C>,
168}
169
170impl<T, C> IntoIterator for BucketVec<T, C> {
171    type Item = T;
172    type IntoIter = IntoIter<T>;
173
174    fn into_iter(self) -> Self::IntoIter {
175        IntoIter::new(self)
176    }
177}
178
179impl<'a, T, C> IntoIterator for &'a BucketVec<T, C> {
180    type Item = &'a T;
181    type IntoIter = Iter<'a, T>;
182
183    fn into_iter(self) -> Self::IntoIter {
184        Iter::new(self)
185    }
186}
187
188impl<'a, T, C> IntoIterator for &'a mut BucketVec<T, C> {
189    type Item = &'a mut T;
190    type IntoIter = IterMut<'a, T>;
191
192    fn into_iter(self) -> Self::IntoIter {
193        IterMut::new(self)
194    }
195}
196
197impl<T, C> Clone for BucketVec<T, C>
198where
199    T: Clone,
200{
201    fn clone(&self) -> Self {
202        Self {
203            len: self.len(),
204            buckets: self.buckets.clone(),
205            config: Default::default(),
206        }
207    }
208}
209
210impl<T, C> PartialEq for BucketVec<T, C>
211where
212    T: PartialEq,
213{
214    fn eq(&self, other: &Self) -> bool {
215        self.iter().zip(other.iter()).all(|(lhs, rhs)| lhs == rhs)
216    }
217}
218
219impl<T, C> Eq for BucketVec<T, C> where T: Eq {}
220
221impl<T, C> core::cmp::PartialOrd for BucketVec<T, C>
222where
223    T: core::cmp::PartialOrd,
224{
225    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
226        for (lhs, rhs) in self.iter().zip(other.iter()) {
227            match lhs.partial_cmp(rhs) {
228                Some(core::cmp::Ordering::Equal) => (),
229                non_eq => return non_eq,
230            }
231        }
232        self.len().partial_cmp(&other.len())
233    }
234}
235
236impl<T, C> core::cmp::Ord for BucketVec<T, C>
237where
238    T: core::cmp::Ord,
239{
240    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
241        for (lhs, rhs) in self.iter().zip(other.iter()) {
242            match lhs.cmp(rhs) {
243                core::cmp::Ordering::Equal => (),
244                non_eq => return non_eq,
245            }
246        }
247        self.len().cmp(&other.len())
248    }
249}
250
251impl<T, C> core::hash::Hash for BucketVec<T, C>
252where
253    T: core::hash::Hash,
254{
255    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
256        self.len().hash(state);
257        for elem in self.iter() {
258            elem.hash(state);
259        }
260    }
261}
262
263/// Accessor into a recently pushed element.
264pub struct Access<'a, T> {
265    /// Access by index.
266    index: usize,
267    /// Access by exclusive reference.
268    reference: &'a mut T,
269}
270
271impl<'a, T> Access<'a, T> {
272    /// Creates a new accessor to the given index and reference.
273    pub(crate) fn new(index: usize, reference: &'a mut T) -> Self {
274        Self { index, reference }
275    }
276
277    /// Returns the index of the recently pushed element.
278    pub fn index(&self) -> usize {
279        self.index
280    }
281
282    /// Returns a shared reference to the recently pushed element.
283    pub fn into_ref(self) -> &'a T {
284        self.reference
285    }
286
287    /// Returns an exclusive reference to the recently pushed element.
288    pub fn into_mut(self) -> &'a mut T {
289        self.reference
290    }
291}
292
293impl<T> Default for BucketVec<T, DefaultConfig> {
294    fn default() -> Self {
295        Self::new()
296    }
297}
298
299impl<T, C> BucketVec<T, C> {
300    /// Creates a new empty bucket vector.
301    ///
302    /// # Note
303    ///
304    /// This does not allocate any heap memory.
305    pub fn new() -> Self {
306        Self {
307            len: 0,
308            buckets: Vec::new(),
309            config: Default::default(),
310        }
311    }
312
313    /// Returns the number of elements stored in the bucket vector.
314    pub fn len(&self) -> usize {
315        self.len
316    }
317
318    /// Returns `true` if the bucket vector is empty.
319    pub fn is_empty(&self) -> bool {
320        self.len() == 0
321    }
322
323    /// Returns an iterator that yields shared references to the elements of the bucket vector.
324    pub fn iter(&self) -> Iter<T> {
325        Iter::new(self)
326    }
327
328    /// Returns an iterator that yields exclusive reference to the elements of the bucket vector.
329    pub fn iter_mut(&mut self) -> IterMut<T> {
330        IterMut::new(self)
331    }
332
333    /// Returns a shared reference to the first element of the bucket vector.
334    pub fn first(&self) -> Option<&T> {
335        if self.is_empty() {
336            return None
337        }
338        Some(&self.buckets[0][0])
339    }
340
341    /// Returns an exclusive reference to the first element of the bucket vector.
342    pub fn first_mut(&mut self) -> Option<&mut T> {
343        if self.is_empty() {
344            return None
345        }
346        Some(&mut self.buckets[0][0])
347    }
348
349    /// Returns a shared reference to the last element of the bucket vector.
350    pub fn last(&self) -> Option<&T> {
351        if self.is_empty() {
352            return None
353        }
354        let len_buckets = self.buckets.len();
355        let len_entries = self.buckets[len_buckets - 1].len();
356        Some(&self.buckets[len_buckets - 1][len_entries - 1])
357    }
358
359    /// Returns an exclusive reference to the last element of the bucket vector.
360    pub fn last_mut(&mut self) -> Option<&mut T> {
361        if self.is_empty() {
362            return None
363        }
364        let len_buckets = self.buckets.len();
365        let len_entries = self.buckets[len_buckets - 1].len();
366        Some(&mut self.buckets[len_buckets - 1][len_entries - 1])
367    }
368}
369
370impl<T, C> BucketVec<T, C>
371where
372    C: BucketVecConfig,
373{
374    /// Returns the bucket index and its internal entry index for the given
375    /// bucket vector index into an element.
376    ///
377    /// Returns `None` if the index is out of bounds.
378    fn bucket_entry_indices(&self, index: usize) -> Option<(usize, usize)> {
379        if index >= self.len() {
380            return None;
381        }
382        Some(config::bucket_entry_indices::<C>(index))
383    }
384
385    /// Returns a shared reference to the element at the given index if any.
386    pub fn get(&self, index: usize) -> Option<&T> {
387        self.bucket_entry_indices(index)
388            .and_then(|(x, y)| self.buckets[x].get(y))
389    }
390
391    /// Returns an exclusive reference to the element at the given index if any.
392    pub fn get_mut(&mut self, index: usize) -> Option<&mut T> {
393        self.bucket_entry_indices(index)
394            .and_then(move |(x, y)| self.buckets[x].get_mut(y))
395    }
396
397    /// Pushes a new bucket containing the new value onto the bucket vector.
398    fn push_bucket(&mut self, new_value: T) {
399        let len_buckets = self.buckets.len();
400        let new_capacity = config::bucket_capacity::<C>(len_buckets);
401        let mut new_bucket = Bucket::new(new_capacity);
402        new_bucket.push(new_value);
403        self.buckets.push(new_bucket);
404        self.len += 1;
405    }
406
407    /// Pushes a new element onto the bucket vector.
408    ///
409    /// # Note
410    ///
411    /// This operation will never move other elements, reallocates or otherwise
412    /// invalidate pointers of elements contained by the bucket vector.
413    pub fn push(&mut self, new_value: T) {
414        if let Some(bucket) = self.buckets.last_mut() {
415            if bucket.len() < bucket.capacity() {
416                bucket.push(new_value);
417                self.len += 1;
418                return;
419            }
420        }
421        self.push_bucket(new_value);
422    }
423
424    /// Pushes a new element onto the bucket vector and returns access to it.
425    ///
426    /// # Note
427    ///
428    /// This operation will never move other elements, reallocates or otherwise
429    /// invalidate pointers of elements contained by the bucket vector.
430    pub fn push_get(&mut self, new_value: T) -> Access<T> {
431        let index = self.len();
432        self.push(new_value);
433        let len_buckets = self.buckets.len();
434        let len_entries = self.buckets[len_buckets - 1].len();
435        Access::new(index, &mut self.buckets[len_buckets - 1][len_entries - 1])
436    }
437}
438
439impl<T, C> core::iter::FromIterator<T> for BucketVec<T, C>
440where
441    C: BucketVecConfig,
442{
443    fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
444        let mut vec = Self::new();
445        <Self as core::iter::Extend<T>>::extend(&mut vec, iter);
446        vec
447    }
448}
449
450impl<T, C> core::iter::Extend<T> for BucketVec<T, C>
451where
452    C: BucketVecConfig,
453{
454    fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) {
455        for item in iter {
456            self.push(item)
457        }
458    }
459}