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    ///
230    /// The slot's generation counter advances so the consumed handle —
231    /// and any other handle previously issued for the same slot — never
232    /// resolves again, even if the slot is reused by a later `insert`.
233    ///
234    /// # Examples
235    ///
236    /// ```
237    /// use arena_lib::Arena;
238    ///
239    /// let mut arena = Arena::new();
240    /// let h = arena.insert("payload");
241    ///
242    /// assert_eq!(arena.remove(h), Some("payload"));
243    /// assert_eq!(arena.remove(h), None); // stale on every subsequent call
244    /// ```
245    pub fn remove(&mut self, idx: Index) -> Option<T> {
246        let slot = self.slots.get_mut(idx.slot as usize)?;
247        if slot.generation != idx.generation {
248            return None;
249        }
250        if matches!(slot.occupant, Occupant::Vacant { .. }) {
251            return None;
252        }
253
254        let vacated = Occupant::Vacant {
255            next_free: self.free_head,
256        };
257        let prior = core::mem::replace(&mut slot.occupant, vacated);
258        slot.generation = slot.generation.wrapping_add(1);
259        self.free_head = Some(idx.slot);
260        self.len -= 1;
261
262        match prior {
263            Occupant::Occupied(value) => Some(value),
264            Occupant::Vacant { .. } => None,
265        }
266    }
267
268    /// Removes every element and resets the free list.
269    ///
270    /// Underlying capacity is retained. Generation counters are preserved,
271    /// so handles issued before the clear remain stale afterwards.
272    pub fn clear(&mut self) {
273        let total = self.slots.len();
274        for (i, slot) in self.slots.iter_mut().enumerate() {
275            if matches!(slot.occupant, Occupant::Occupied(_)) {
276                slot.generation = slot.generation.wrapping_add(1);
277            }
278            slot.occupant = Occupant::Vacant {
279                next_free: if i + 1 < total {
280                    Some((i + 1) as u32)
281                } else {
282                    None
283                },
284            };
285        }
286        self.free_head = if total == 0 { None } else { Some(0) };
287        self.len = 0;
288    }
289
290    /// Returns an iterator over `(Index, &T)` for every live element.
291    ///
292    /// Vacant slots are skipped; iteration order follows slot indices,
293    /// which is *not* the insertion order if any removals have happened.
294    ///
295    /// # Examples
296    ///
297    /// ```
298    /// use arena_lib::Arena;
299    ///
300    /// let mut arena = Arena::new();
301    /// let _a = arena.insert("alpha");
302    /// let killed = arena.insert("dead");
303    /// let _b = arena.insert("bravo");
304    /// arena.remove(killed);
305    ///
306    /// let live: Vec<&&str> = arena.iter().map(|(_idx, value)| value).collect();
307    /// assert_eq!(live, vec![&"alpha", &"bravo"]);
308    /// ```
309    #[inline]
310    pub fn iter(&self) -> Iter<'_, T> {
311        Iter {
312            slots: self.slots.iter().enumerate(),
313        }
314    }
315
316    /// Returns an iterator over `(Index, &mut T)` for every live element.
317    #[inline]
318    pub fn iter_mut(&mut self) -> IterMut<'_, T> {
319        IterMut {
320            slots: self.slots.iter_mut().enumerate(),
321        }
322    }
323}
324
325impl<T> Default for Arena<T> {
326    #[inline]
327    fn default() -> Self {
328        Self::new()
329    }
330}
331
332impl<T: core::fmt::Debug> core::fmt::Debug for Arena<T> {
333    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
334        f.debug_struct("Arena")
335            .field("len", &self.len)
336            .field("capacity", &self.slots.capacity())
337            .finish()
338    }
339}
340
341/// Iterator over `(Index, &T)` pairs returned by [`Arena::iter`].
342pub struct Iter<'a, T> {
343    slots: core::iter::Enumerate<core::slice::Iter<'a, Slot<T>>>,
344}
345
346impl<'a, T> Iterator for Iter<'a, T> {
347    type Item = (Index, &'a T);
348
349    fn next(&mut self) -> Option<Self::Item> {
350        for (i, slot) in self.slots.by_ref() {
351            if let Occupant::Occupied(value) = &slot.occupant {
352                return Some((
353                    Index {
354                        generation: slot.generation,
355                        slot: i as u32,
356                    },
357                    value,
358                ));
359            }
360        }
361        None
362    }
363}
364
365/// Iterator over `(Index, &mut T)` pairs returned by [`Arena::iter_mut`].
366pub struct IterMut<'a, T> {
367    slots: core::iter::Enumerate<core::slice::IterMut<'a, Slot<T>>>,
368}
369
370impl<'a, T> Iterator for IterMut<'a, T> {
371    type Item = (Index, &'a mut T);
372
373    fn next(&mut self) -> Option<Self::Item> {
374        for (i, slot) in self.slots.by_ref() {
375            let generation = slot.generation;
376            if let Occupant::Occupied(value) = &mut slot.occupant {
377                return Some((
378                    Index {
379                        generation,
380                        slot: i as u32,
381                    },
382                    value,
383                ));
384            }
385        }
386        None
387    }
388}
389
390#[cfg(test)]
391mod tests {
392    use super::*;
393
394    #[test]
395    fn insert_and_get() {
396        let mut a = Arena::new();
397        let i = a.insert(42);
398        assert_eq!(a.get(i), Some(&42));
399        assert_eq!(a.len(), 1);
400        assert!(!a.is_empty());
401    }
402
403    #[test]
404    fn remove_invalidates_handle() {
405        let mut a = Arena::new();
406        let i = a.insert("hello");
407        assert_eq!(a.remove(i), Some("hello"));
408        assert!(a.get(i).is_none());
409        assert!(a.remove(i).is_none());
410    }
411
412    #[test]
413    fn slot_reuse_bumps_generation() {
414        let mut a = Arena::new();
415        let i1 = a.insert(1);
416        let _ = a.remove(i1);
417        let i2 = a.insert(2);
418        assert_eq!(i1.slot(), i2.slot());
419        assert_ne!(i1.generation(), i2.generation());
420        assert!(a.get(i1).is_none());
421        assert_eq!(a.get(i2), Some(&2));
422    }
423
424    #[test]
425    fn iter_yields_only_live() {
426        let mut a = Arena::new();
427        let i1 = a.insert("a");
428        let i2 = a.insert("b");
429        let i3 = a.insert("c");
430        let _ = a.remove(i2);
431        let values: Vec<_> = a.iter().map(|(_, v)| *v).collect();
432        assert_eq!(values, vec!["a", "c"]);
433        let _ = (i1, i3);
434    }
435
436    #[test]
437    fn clear_resets_len_and_invalidates_handles() {
438        let mut a = Arena::new();
439        let i = a.insert(7);
440        a.clear();
441        assert_eq!(a.len(), 0);
442        assert!(a.get(i).is_none());
443    }
444
445    #[test]
446    fn get_mut_mutates() {
447        let mut a = Arena::new();
448        let i = a.insert(10);
449        if let Some(v) = a.get_mut(i) {
450            *v = 99;
451        }
452        assert_eq!(a.get(i), Some(&99));
453    }
454
455    #[test]
456    fn contains_reflects_liveness() {
457        let mut a = Arena::new();
458        let i = a.insert(0_u8);
459        assert!(a.contains(i));
460        let _ = a.remove(i);
461        assert!(!a.contains(i));
462    }
463}