Skip to main content

safe_bump/
arena.rs

1use crate::{Checkpoint, Idx, IterIndexed, IterIndexedMut};
2
3/// Single-thread typed arena allocator.
4///
5/// Stores values of type `T` in a contiguous buffer, returning stable
6/// [`Idx<T>`] handles for O(1) access. Values are dropped when the arena
7/// is dropped, reset, or rolled back past their allocation point.
8///
9/// For thread-safe concurrent allocation, see [`SharedArena`](crate::SharedArena).
10pub struct Arena<T> {
11    items: Vec<T>,
12}
13
14impl<T> Arena<T> {
15    /// Creates an empty arena.
16    #[must_use]
17    pub const fn new() -> Self {
18        Self { items: Vec::new() }
19    }
20
21    /// Creates an arena with pre-allocated capacity for `capacity` items.
22    #[must_use]
23    pub fn with_capacity(capacity: usize) -> Self {
24        Self {
25            items: Vec::with_capacity(capacity),
26        }
27    }
28
29    /// Allocates a value in the arena, returning its stable index.
30    ///
31    /// O(1) amortized (backed by [`Vec::push`]).
32    pub fn alloc(&mut self, value: T) -> Idx<T> {
33        let index = self.items.len();
34        self.items.push(value);
35        Idx::from_raw(index)
36    }
37
38    /// Returns a reference to the value at `idx`.
39    ///
40    /// # Panics
41    ///
42    /// Panics if `idx` is out of bounds (stale after rollback/reset).
43    #[must_use]
44    pub fn get(&self, idx: Idx<T>) -> &T {
45        &self.items[idx.into_raw()]
46    }
47
48    /// Returns a mutable reference to the value at `idx`.
49    ///
50    /// # Panics
51    ///
52    /// Panics if `idx` is out of bounds (stale after rollback/reset).
53    #[must_use]
54    pub fn get_mut(&mut self, idx: Idx<T>) -> &mut T {
55        &mut self.items[idx.into_raw()]
56    }
57
58    /// Returns the number of allocated items.
59    #[must_use]
60    pub const fn len(&self) -> usize {
61        self.items.len()
62    }
63
64    /// Returns `true` if the arena contains no items.
65    #[must_use]
66    pub const fn is_empty(&self) -> bool {
67        self.items.is_empty()
68    }
69
70    /// Returns the current capacity in items.
71    #[must_use]
72    pub const fn capacity(&self) -> usize {
73        self.items.capacity()
74    }
75
76    /// Saves the current allocation state.
77    ///
78    /// Use with [`rollback`](Arena::rollback) to discard allocations
79    /// made after this point.
80    #[must_use]
81    pub const fn checkpoint(&self) -> Checkpoint<T> {
82        Checkpoint::from_len(self.items.len())
83    }
84
85    /// Rolls back to a previous checkpoint, dropping all values
86    /// allocated after it.
87    ///
88    /// O(k) where k = number of items dropped (destructors run).
89    ///
90    /// # Panics
91    ///
92    /// Panics if `cp` points beyond the current length.
93    pub fn rollback(&mut self, cp: Checkpoint<T>) {
94        assert!(
95            cp.len() <= self.items.len(),
96            "checkpoint {} beyond current length {}",
97            cp.len(),
98            self.items.len(),
99        );
100        self.items.truncate(cp.len());
101    }
102
103    /// Removes all items, running their destructors.
104    ///
105    /// Retains allocated memory for reuse.
106    pub fn reset(&mut self) {
107        self.items.clear();
108    }
109
110    /// Returns an iterator over all allocated items.
111    pub fn iter(&self) -> std::slice::Iter<'_, T> {
112        self.items.iter()
113    }
114
115    /// Returns a mutable iterator over all allocated items.
116    pub fn iter_mut(&mut self) -> std::slice::IterMut<'_, T> {
117        self.items.iter_mut()
118    }
119
120    /// Allocates multiple values from an iterator, returning the index
121    /// of the first allocated item.
122    ///
123    /// Returns `None` if the iterator is empty.
124    ///
125    /// O(n) where n = items yielded by the iterator.
126    pub fn alloc_extend(&mut self, iter: impl IntoIterator<Item = T>) -> Option<Idx<T>> {
127        let start = self.items.len();
128        self.items.extend(iter);
129        if self.items.len() > start {
130            Some(Idx::from_raw(start))
131        } else {
132            None
133        }
134    }
135
136    /// Returns `true` if `idx` points to a valid item in this arena.
137    ///
138    /// An index becomes invalid after [`rollback`](Arena::rollback) or
139    /// [`reset`](Arena::reset) removes the item it pointed to.
140    #[must_use]
141    pub const fn is_valid(&self, idx: Idx<T>) -> bool {
142        idx.into_raw() < self.items.len()
143    }
144
145    /// Returns a reference to the value at `idx`, or `None` if the
146    /// index is out of bounds.
147    #[must_use]
148    pub fn try_get(&self, idx: Idx<T>) -> Option<&T> {
149        self.items.get(idx.into_raw())
150    }
151
152    /// Returns a mutable reference to the value at `idx`, or `None`
153    /// if the index is out of bounds.
154    #[must_use]
155    pub fn try_get_mut(&mut self, idx: Idx<T>) -> Option<&mut T> {
156        self.items.get_mut(idx.into_raw())
157    }
158
159    /// Removes all items, returning an iterator that yields them
160    /// in allocation order.
161    ///
162    /// The arena is empty after the iterator is consumed or dropped.
163    /// Capacity is retained.
164    pub fn drain(&mut self) -> std::vec::Drain<'_, T> {
165        self.items.drain(..)
166    }
167
168    /// Returns an iterator yielding `(Idx<T>, &T)` pairs in allocation order.
169    #[must_use]
170    pub fn iter_indexed(&self) -> IterIndexed<'_, T> {
171        IterIndexed::new(self.items.iter().enumerate())
172    }
173
174    /// Returns a mutable iterator yielding `(Idx<T>, &mut T)` pairs in
175    /// allocation order.
176    pub fn iter_indexed_mut(&mut self) -> IterIndexedMut<'_, T> {
177        IterIndexedMut::new(self.items.iter_mut().enumerate())
178    }
179
180    /// Reserves capacity for at least `additional` more items.
181    pub fn reserve(&mut self, additional: usize) {
182        self.items.reserve(additional);
183    }
184
185    /// Shrinks the backing storage to fit the current number of items.
186    pub fn shrink_to_fit(&mut self) {
187        self.items.shrink_to_fit();
188    }
189}
190
191impl<T> Default for Arena<T> {
192    fn default() -> Self {
193        Self::new()
194    }
195}
196
197impl<T> std::ops::Index<Idx<T>> for Arena<T> {
198    type Output = T;
199
200    fn index(&self, idx: Idx<T>) -> &T {
201        self.get(idx)
202    }
203}
204
205impl<T> std::ops::IndexMut<Idx<T>> for Arena<T> {
206    fn index_mut(&mut self, idx: Idx<T>) -> &mut T {
207        self.get_mut(idx)
208    }
209}
210
211impl<'a, T> IntoIterator for &'a Arena<T> {
212    type Item = &'a T;
213    type IntoIter = std::slice::Iter<'a, T>;
214
215    fn into_iter(self) -> Self::IntoIter {
216        self.iter()
217    }
218}
219
220impl<'a, T> IntoIterator for &'a mut Arena<T> {
221    type Item = &'a mut T;
222    type IntoIter = std::slice::IterMut<'a, T>;
223
224    fn into_iter(self) -> Self::IntoIter {
225        self.iter_mut()
226    }
227}
228
229impl<T> Extend<T> for Arena<T> {
230    fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) {
231        self.items.extend(iter);
232    }
233}
234
235impl<T> std::iter::FromIterator<T> for Arena<T> {
236    fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
237        Self {
238            items: iter.into_iter().collect(),
239        }
240    }
241}
242
243impl<T> IntoIterator for Arena<T> {
244    type Item = T;
245    type IntoIter = std::vec::IntoIter<T>;
246
247    fn into_iter(self) -> Self::IntoIter {
248        self.items.into_iter()
249    }
250}