Skip to main content

arena_lib/
arena.rs

1//! Generational arena and its [`Index`] handle.
2//!
3//! [`Arena<T>`] is a slab-style container that hands out stable [`Index`]
4//! handles. When a slot is freed, its generation counter advances, so any
5//! still-held handle pointing at that slot is detected and rejected — no
6//! use-after-free, no dangling references, all in safe Rust.
7//!
8//! # Cost summary
9//!
10//! - `insert`: amortized O(1).
11//! - `remove`: O(1).
12//! - `get` / `get_mut` / `contains`: O(1).
13//! - `iter` / `iter_mut`: O(capacity) — skips vacant slots.
14
15use alloc::vec::Vec;
16
17use crate::error::{Error, Result};
18
19/// Stable handle into an [`Arena`].
20///
21/// An `Index` is `Copy`, cheap to pass by value, and remains valid until the
22/// element it points at is removed. Once removed, the handle becomes stale
23/// and all lookups against the arena return `None` (or [`Error::StaleIndex`]
24/// from fallible variants). Reusing the underlying slot under a new
25/// generation does **not** revive the original handle.
26///
27/// # Layout
28///
29/// `Index` is 8 bytes (two `u32`s) and packs trivially into structs.
30#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
31pub struct Index {
32    generation: u32,
33    slot: u32,
34}
35
36impl Index {
37    /// Returns the slot number this handle points at.
38    ///
39    /// Useful for diagnostics; do not use the slot number as a substitute for
40    /// the handle itself — slots are reused under new generations.
41    #[inline]
42    pub const fn slot(self) -> u32 {
43        self.slot
44    }
45
46    /// Returns the generation counter recorded when this handle was issued.
47    #[inline]
48    pub const fn generation(self) -> u32 {
49        self.generation
50    }
51}
52
53enum Occupant<T> {
54    Occupied(T),
55    Vacant { next_free: Option<u32> },
56}
57
58struct Slot<T> {
59    generation: u32,
60    occupant: Occupant<T>,
61}
62
63/// Generational arena. See the [module-level docs](self).
64///
65/// # Examples
66///
67/// ```
68/// use arena_lib::Arena;
69///
70/// let mut arena = Arena::new();
71/// let a = arena.insert("alpha");
72/// let b = arena.insert("bravo");
73///
74/// assert_eq!(arena.len(), 2);
75/// assert_eq!(arena.get(a), Some(&"alpha"));
76///
77/// let removed = arena.remove(a);
78/// assert_eq!(removed, Some("alpha"));
79/// assert!(arena.get(a).is_none()); // stale handle
80/// assert_eq!(arena.get(b), Some(&"bravo"));
81/// ```
82pub struct Arena<T> {
83    slots: Vec<Slot<T>>,
84    free_head: Option<u32>,
85    len: usize,
86}
87
88impl<T> Arena<T> {
89    /// Creates an empty arena that performs no allocation until first insert.
90    #[inline]
91    #[must_use]
92    pub const fn new() -> Self {
93        Self {
94            slots: Vec::new(),
95            free_head: None,
96            len: 0,
97        }
98    }
99
100    /// Creates an empty arena with space pre-reserved for `capacity` elements.
101    ///
102    /// The arena will still grow on demand; this is a hint, not a hard cap.
103    #[inline]
104    #[must_use]
105    pub fn with_capacity(capacity: usize) -> Self {
106        Self {
107            slots: Vec::with_capacity(capacity),
108            free_head: None,
109            len: 0,
110        }
111    }
112
113    /// Reserves capacity for at least `additional` more elements.
114    #[inline]
115    pub fn reserve(&mut self, additional: usize) {
116        self.slots.reserve(additional);
117    }
118
119    /// Number of live elements currently in the arena.
120    #[inline]
121    #[must_use]
122    pub const fn len(&self) -> usize {
123        self.len
124    }
125
126    /// Returns `true` when the arena holds no live elements.
127    #[inline]
128    #[must_use]
129    pub const fn is_empty(&self) -> bool {
130        self.len == 0
131    }
132
133    /// Returns the number of slots the arena can hold before reallocating.
134    ///
135    /// This counts both occupied and vacant slots.
136    #[inline]
137    #[must_use]
138    pub fn capacity(&self) -> usize {
139        self.slots.capacity()
140    }
141
142    /// Returns `true` if the index refers to a live element.
143    #[inline]
144    #[must_use]
145    pub fn contains(&self, idx: Index) -> bool {
146        self.get(idx).is_some()
147    }
148
149    /// Returns a shared reference to the element behind `idx`, or `None`
150    /// if the handle is stale.
151    #[inline]
152    #[must_use]
153    pub fn get(&self, idx: Index) -> Option<&T> {
154        let slot = self.slots.get(idx.slot as usize)?;
155        if slot.generation != idx.generation {
156            return None;
157        }
158        match &slot.occupant {
159            Occupant::Occupied(value) => Some(value),
160            Occupant::Vacant { .. } => None,
161        }
162    }
163
164    /// Returns a unique reference to the element behind `idx`, or `None`
165    /// if the handle is stale.
166    #[inline]
167    #[must_use]
168    pub fn get_mut(&mut self, idx: Index) -> Option<&mut T> {
169        let slot = self.slots.get_mut(idx.slot as usize)?;
170        if slot.generation != idx.generation {
171            return None;
172        }
173        match &mut slot.occupant {
174            Occupant::Occupied(value) => Some(value),
175            Occupant::Vacant { .. } => None,
176        }
177    }
178
179    /// Inserts `value` and returns a fresh [`Index`].
180    ///
181    /// Panics on the catastrophic case where the slot counter would overflow
182    /// `u32::MAX`. Use [`Arena::try_insert`] for an explicit fallible variant.
183    #[inline]
184    pub fn insert(&mut self, value: T) -> Index {
185        match self.try_insert(value) {
186            Ok(idx) => idx,
187            Err(_) => panic!("arena slot counter overflow (u32::MAX slots)"),
188        }
189    }
190
191    /// Inserts `value`, returning an [`Index`] on success or
192    /// [`Error::CounterOverflow`] if the arena cannot represent more slots.
193    pub fn try_insert(&mut self, value: T) -> Result<Index> {
194        if let Some(slot_id) = self.free_head {
195            let slot = match self.slots.get_mut(slot_id as usize) {
196                Some(s) => s,
197                None => return Err(Error::CounterOverflow),
198            };
199            let next_free = match &slot.occupant {
200                Occupant::Vacant { next_free } => *next_free,
201                Occupant::Occupied(_) => return Err(Error::CounterOverflow),
202            };
203            self.free_head = next_free;
204            slot.occupant = Occupant::Occupied(value);
205            self.len += 1;
206            Ok(Index {
207                generation: slot.generation,
208                slot: slot_id,
209            })
210        } else {
211            let slot_idx = self.slots.len();
212            if slot_idx > u32::MAX as usize {
213                return Err(Error::CounterOverflow);
214            }
215            self.slots.push(Slot {
216                generation: 1,
217                occupant: Occupant::Occupied(value),
218            });
219            self.len += 1;
220            Ok(Index {
221                generation: 1,
222                slot: slot_idx as u32,
223            })
224        }
225    }
226
227    /// Removes the element behind `idx` and returns it, or `None` if the
228    /// handle is stale.
229    pub fn remove(&mut self, idx: Index) -> Option<T> {
230        let slot = self.slots.get_mut(idx.slot as usize)?;
231        if slot.generation != idx.generation {
232            return None;
233        }
234        if matches!(slot.occupant, Occupant::Vacant { .. }) {
235            return None;
236        }
237
238        let vacated = Occupant::Vacant {
239            next_free: self.free_head,
240        };
241        let prior = core::mem::replace(&mut slot.occupant, vacated);
242        slot.generation = slot.generation.wrapping_add(1);
243        self.free_head = Some(idx.slot);
244        self.len -= 1;
245
246        match prior {
247            Occupant::Occupied(value) => Some(value),
248            Occupant::Vacant { .. } => None,
249        }
250    }
251
252    /// Removes every element and resets the free list.
253    ///
254    /// Underlying capacity is retained. Generation counters are preserved,
255    /// so handles issued before the clear remain stale afterwards.
256    pub fn clear(&mut self) {
257        let total = self.slots.len();
258        for (i, slot) in self.slots.iter_mut().enumerate() {
259            if matches!(slot.occupant, Occupant::Occupied(_)) {
260                slot.generation = slot.generation.wrapping_add(1);
261            }
262            slot.occupant = Occupant::Vacant {
263                next_free: if i + 1 < total {
264                    Some((i + 1) as u32)
265                } else {
266                    None
267                },
268            };
269        }
270        self.free_head = if total == 0 { None } else { Some(0) };
271        self.len = 0;
272    }
273
274    /// Returns an iterator over `(Index, &T)` for every live element.
275    #[inline]
276    pub fn iter(&self) -> Iter<'_, T> {
277        Iter {
278            slots: self.slots.iter().enumerate(),
279        }
280    }
281
282    /// Returns an iterator over `(Index, &mut T)` for every live element.
283    #[inline]
284    pub fn iter_mut(&mut self) -> IterMut<'_, T> {
285        IterMut {
286            slots: self.slots.iter_mut().enumerate(),
287        }
288    }
289}
290
291impl<T> Default for Arena<T> {
292    #[inline]
293    fn default() -> Self {
294        Self::new()
295    }
296}
297
298impl<T: core::fmt::Debug> core::fmt::Debug for Arena<T> {
299    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
300        f.debug_struct("Arena")
301            .field("len", &self.len)
302            .field("capacity", &self.slots.capacity())
303            .finish()
304    }
305}
306
307/// Iterator over `(Index, &T)` pairs returned by [`Arena::iter`].
308pub struct Iter<'a, T> {
309    slots: core::iter::Enumerate<core::slice::Iter<'a, Slot<T>>>,
310}
311
312impl<'a, T> Iterator for Iter<'a, T> {
313    type Item = (Index, &'a T);
314
315    fn next(&mut self) -> Option<Self::Item> {
316        for (i, slot) in self.slots.by_ref() {
317            if let Occupant::Occupied(value) = &slot.occupant {
318                return Some((
319                    Index {
320                        generation: slot.generation,
321                        slot: i as u32,
322                    },
323                    value,
324                ));
325            }
326        }
327        None
328    }
329}
330
331/// Iterator over `(Index, &mut T)` pairs returned by [`Arena::iter_mut`].
332pub struct IterMut<'a, T> {
333    slots: core::iter::Enumerate<core::slice::IterMut<'a, Slot<T>>>,
334}
335
336impl<'a, T> Iterator for IterMut<'a, T> {
337    type Item = (Index, &'a mut T);
338
339    fn next(&mut self) -> Option<Self::Item> {
340        for (i, slot) in self.slots.by_ref() {
341            let generation = slot.generation;
342            if let Occupant::Occupied(value) = &mut slot.occupant {
343                return Some((
344                    Index {
345                        generation,
346                        slot: i as u32,
347                    },
348                    value,
349                ));
350            }
351        }
352        None
353    }
354}
355
356#[cfg(test)]
357mod tests {
358    use super::*;
359
360    #[test]
361    fn insert_and_get() {
362        let mut a = Arena::new();
363        let i = a.insert(42);
364        assert_eq!(a.get(i), Some(&42));
365        assert_eq!(a.len(), 1);
366        assert!(!a.is_empty());
367    }
368
369    #[test]
370    fn remove_invalidates_handle() {
371        let mut a = Arena::new();
372        let i = a.insert("hello");
373        assert_eq!(a.remove(i), Some("hello"));
374        assert!(a.get(i).is_none());
375        assert!(a.remove(i).is_none());
376    }
377
378    #[test]
379    fn slot_reuse_bumps_generation() {
380        let mut a = Arena::new();
381        let i1 = a.insert(1);
382        let _ = a.remove(i1);
383        let i2 = a.insert(2);
384        assert_eq!(i1.slot(), i2.slot());
385        assert_ne!(i1.generation(), i2.generation());
386        assert!(a.get(i1).is_none());
387        assert_eq!(a.get(i2), Some(&2));
388    }
389
390    #[test]
391    fn iter_yields_only_live() {
392        let mut a = Arena::new();
393        let i1 = a.insert("a");
394        let i2 = a.insert("b");
395        let i3 = a.insert("c");
396        let _ = a.remove(i2);
397        let values: Vec<_> = a.iter().map(|(_, v)| *v).collect();
398        assert_eq!(values, vec!["a", "c"]);
399        let _ = (i1, i3);
400    }
401
402    #[test]
403    fn clear_resets_len_and_invalidates_handles() {
404        let mut a = Arena::new();
405        let i = a.insert(7);
406        a.clear();
407        assert_eq!(a.len(), 0);
408        assert!(a.get(i).is_none());
409    }
410
411    #[test]
412    fn get_mut_mutates() {
413        let mut a = Arena::new();
414        let i = a.insert(10);
415        if let Some(v) = a.get_mut(i) {
416            *v = 99;
417        }
418        assert_eq!(a.get(i), Some(&99));
419    }
420
421    #[test]
422    fn contains_reflects_liveness() {
423        let mut a = Arena::new();
424        let i = a.insert(0_u8);
425        assert!(a.contains(i));
426        let _ = a.remove(i);
427        assert!(!a.contains(i));
428    }
429}