Skip to main content

arena_lang/
arena.rs

1//! The typed, append-only arena.
2
3use alloc::vec::Vec;
4use core::fmt;
5
6use crate::{ArenaError, Id};
7
8/// A typed arena that allocates values and hands back stable [`Id`] handles.
9///
10/// `Arena<T>` is the allocation floor a tree of nodes is built on. Allocate a
11/// value with [`alloc`](Arena::alloc) and you get an [`Id<T>`](Id) — a small,
12/// `Copy`, type-tagged handle that stays valid for the life of the arena. Resolve
13/// it back to the value with [`get`](Arena::get). Values are never freed
14/// individually; the whole arena is released at once when it is dropped, which is
15/// the allocation pattern an AST or IR wants — many nodes allocated forward during
16/// a pass, none removed, all gone together at the end.
17///
18/// The handle, not a raw pointer, is the stable address: storing an `Id<T>` in one
19/// node to point at another is how a tree is wired, and the handle keeps resolving
20/// to the same value no matter how many later allocations grow the arena.
21///
22/// # Examples
23///
24/// ```
25/// use arena_lang::Arena;
26///
27/// // A tiny expression tree, wired by handle.
28/// enum Expr {
29///     Int(i64),
30///     Add(arena_lang::Id<Expr>, arena_lang::Id<Expr>),
31/// }
32///
33/// let mut arena = Arena::new();
34/// let one = arena.alloc(Expr::Int(1));
35/// let two = arena.alloc(Expr::Int(2));
36/// let sum = arena.alloc(Expr::Add(one, two));
37///
38/// // The parent's handles still resolve after it was itself allocated.
39/// match arena.get(sum) {
40///     Some(Expr::Add(l, r)) => {
41///         assert!(matches!(arena.get(*l), Some(Expr::Int(1))));
42///         assert!(matches!(arena.get(*r), Some(Expr::Int(2))));
43///     }
44///     _ => unreachable!(),
45/// }
46/// ```
47pub struct Arena<T> {
48    items: Vec<T>,
49}
50
51impl<T> Arena<T> {
52    /// Creates an empty arena. `const`, so it can initialise a `static`.
53    ///
54    /// No allocation happens until the first value is added.
55    ///
56    /// # Examples
57    ///
58    /// ```
59    /// use arena_lang::Arena;
60    ///
61    /// let arena: Arena<u32> = Arena::new();
62    /// assert!(arena.is_empty());
63    /// ```
64    #[inline]
65    #[must_use]
66    pub const fn new() -> Self {
67        Self { items: Vec::new() }
68    }
69
70    /// Creates an empty arena with room for `capacity` values preallocated.
71    ///
72    /// A hint only: it reserves backing storage so the first `capacity`
73    /// allocations do not reallocate. Sizing it to the expected node count — for
74    /// instance, a multiple of the token count of the source — keeps allocation on
75    /// the flat part of the cost curve.
76    ///
77    /// # Examples
78    ///
79    /// ```
80    /// use arena_lang::Arena;
81    ///
82    /// let mut arena = Arena::with_capacity(3);
83    /// let ids = [arena.alloc('a'), arena.alloc('b'), arena.alloc('c')];
84    /// assert_eq!(arena.len(), 3);
85    /// assert_eq!(ids.map(|id| *arena.get(id).unwrap()), ['a', 'b', 'c']);
86    /// ```
87    #[inline]
88    #[must_use]
89    pub fn with_capacity(capacity: usize) -> Self {
90        Self {
91            items: Vec::with_capacity(capacity),
92        }
93    }
94
95    /// Reserves capacity for at least `additional` more values.
96    ///
97    /// Use it before a burst of allocations whose count is known, to fold what
98    /// would be several incremental growths into one.
99    ///
100    /// # Examples
101    ///
102    /// ```
103    /// use arena_lang::Arena;
104    ///
105    /// let mut arena: Arena<u8> = Arena::new();
106    /// arena.reserve(128);
107    /// assert!(arena.capacity() >= 128);
108    /// ```
109    #[inline]
110    pub fn reserve(&mut self, additional: usize) {
111        self.items.reserve(additional);
112    }
113
114    /// Allocates `value` and returns a stable [`Id`] handle to it.
115    ///
116    /// This is the hot path. The handle is valid for the life of the arena and
117    /// keeps resolving to this value through every later allocation.
118    ///
119    /// # Panics
120    ///
121    /// Panics only if the arena has already allocated `u32::MAX` values and cannot
122    /// represent another handle — a ceiling of more than four billion live nodes,
123    /// unreachable for any real tree. Use [`try_alloc`](Arena::try_alloc) for an
124    /// explicit non-panicking path.
125    ///
126    /// # Examples
127    ///
128    /// ```
129    /// use arena_lang::Arena;
130    ///
131    /// let mut arena = Arena::new();
132    /// let id = arena.alloc(42);
133    /// assert_eq!(arena.get(id), Some(&42));
134    /// ```
135    #[inline]
136    pub fn alloc(&mut self, value: T) -> Id<T> {
137        match self.try_alloc(value) {
138            Ok(id) => id,
139            Err(_) => panic!("arena is full: cannot allocate beyond u32::MAX values"),
140        }
141    }
142
143    /// Allocates `value`, returning its [`Id`] or an error if the arena is full.
144    ///
145    /// The non-panicking counterpart to [`alloc`](Arena::alloc): identical on
146    /// success, but it returns [`ArenaError::CapacityExhausted`] instead of
147    /// panicking at the `u32::MAX`-value ceiling. Prefer it when building a tree
148    /// from untrusted input whose size you do not control.
149    ///
150    /// # Errors
151    ///
152    /// Returns [`ArenaError::CapacityExhausted`] when the arena's slot space is
153    /// full. The arena is left unchanged.
154    ///
155    /// # Examples
156    ///
157    /// ```
158    /// use arena_lang::Arena;
159    ///
160    /// let mut arena = Arena::new();
161    /// let id = arena.try_alloc("ok")?;
162    /// assert_eq!(arena.get(id), Some(&"ok"));
163    /// # Ok::<(), arena_lang::ArenaError>(())
164    /// ```
165    #[inline]
166    pub fn try_alloc(&mut self, value: T) -> Result<Id<T>, ArenaError> {
167        // The next handle is the current length; if that no longer fits in a
168        // `u32`, the arena is full. Checked before the push, so a rejected
169        // allocation leaves the arena untouched.
170        let raw = u32::try_from(self.items.len()).map_err(|_| ArenaError::CapacityExhausted)?;
171        self.items.push(value);
172        Ok(Id::new(raw))
173    }
174
175    /// Borrows the value behind `id`, or `None` if the handle does not name a live
176    /// value in this arena.
177    ///
178    /// Resolution is a direct slot lookup, not a search. A handle from this arena
179    /// always resolves; the `None` case guards against an out-of-range handle, so
180    /// resolving one never reads outside the arena's storage.
181    ///
182    /// # Examples
183    ///
184    /// ```
185    /// use arena_lang::Arena;
186    ///
187    /// let mut arena = Arena::new();
188    /// let id = arena.alloc(vec![1, 2, 3]);
189    /// assert_eq!(arena.get(id).map(Vec::len), Some(3));
190    /// ```
191    #[inline]
192    #[must_use]
193    pub fn get(&self, id: Id<T>) -> Option<&T> {
194        self.items.get(id.raw() as usize)
195    }
196
197    /// Mutably borrows the value behind `id`, or `None` if the handle does not name
198    /// a live value in this arena.
199    ///
200    /// Useful for back-patching a node after it is allocated — resolving a forward
201    /// reference, or filling in a parent link once the parent exists.
202    ///
203    /// # Examples
204    ///
205    /// ```
206    /// use arena_lang::Arena;
207    ///
208    /// let mut arena = Arena::new();
209    /// let id = arena.alloc(0_u32);
210    /// if let Some(slot) = arena.get_mut(id) {
211    ///     *slot = 99;
212    /// }
213    /// assert_eq!(arena.get(id), Some(&99));
214    /// ```
215    #[inline]
216    pub fn get_mut(&mut self, id: Id<T>) -> Option<&mut T> {
217        self.items.get_mut(id.raw() as usize)
218    }
219
220    /// Returns `true` if `id` names a live value in this arena.
221    ///
222    /// # Examples
223    ///
224    /// ```
225    /// use arena_lang::Arena;
226    ///
227    /// let mut arena = Arena::new();
228    /// let id = arena.alloc("x");
229    /// assert!(arena.contains(id));
230    /// ```
231    #[inline]
232    #[must_use]
233    pub fn contains(&self, id: Id<T>) -> bool {
234        (id.raw() as usize) < self.items.len()
235    }
236
237    /// Returns the number of values in the arena.
238    ///
239    /// # Examples
240    ///
241    /// ```
242    /// use arena_lang::Arena;
243    ///
244    /// let mut arena = Arena::new();
245    /// assert_eq!(arena.len(), 0);
246    /// arena.alloc(());
247    /// assert_eq!(arena.len(), 1);
248    /// ```
249    #[inline]
250    #[must_use]
251    pub fn len(&self) -> usize {
252        self.items.len()
253    }
254
255    /// Returns `true` if the arena holds no values.
256    ///
257    /// # Examples
258    ///
259    /// ```
260    /// use arena_lang::Arena;
261    ///
262    /// let mut arena = Arena::new();
263    /// assert!(arena.is_empty());
264    /// arena.alloc(());
265    /// assert!(!arena.is_empty());
266    /// ```
267    #[inline]
268    #[must_use]
269    pub fn is_empty(&self) -> bool {
270        self.items.is_empty()
271    }
272
273    /// Returns the number of values the arena can hold before it must grow.
274    ///
275    /// # Examples
276    ///
277    /// ```
278    /// use arena_lang::Arena;
279    ///
280    /// let arena: Arena<u64> = Arena::with_capacity(8);
281    /// assert!(arena.capacity() >= 8);
282    /// ```
283    #[inline]
284    #[must_use]
285    pub fn capacity(&self) -> usize {
286        self.items.capacity()
287    }
288
289    /// Iterates over every value in the arena, paired with its handle.
290    ///
291    /// Values are visited in allocation order — the order their ids were minted —
292    /// so the first pair is `(Id 0, first value)`. Useful for a pass that walks all
293    /// nodes without following the tree's edges.
294    ///
295    /// # Examples
296    ///
297    /// ```
298    /// use arena_lang::Arena;
299    ///
300    /// let mut arena = Arena::new();
301    /// let a = arena.alloc(10);
302    /// let b = arena.alloc(20);
303    ///
304    /// // Allocation order, with the matching handles.
305    /// let pairs: Vec<_> = arena.iter().collect();
306    /// assert_eq!(pairs, vec![(a, &10), (b, &20)]);
307    ///
308    /// let total: i32 = arena.iter().map(|(_, v)| *v).sum();
309    /// assert_eq!(total, 30);
310    /// ```
311    pub fn iter(&self) -> impl Iterator<Item = (Id<T>, &T)> + '_ {
312        self.items
313            .iter()
314            .enumerate()
315            .map(|(i, value)| (Id::new(i as u32), value))
316    }
317}
318
319impl<T> Default for Arena<T> {
320    #[inline]
321    fn default() -> Self {
322        Self::new()
323    }
324}
325
326impl<T> fmt::Debug for Arena<T> {
327    /// Shows the arena's size, not its contents — an arena can hold millions of
328    /// nodes, and dumping them all is rarely what a debug print wants. This also
329    /// keeps `Arena<T>: Debug` free of any `T: Debug` bound.
330    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
331        f.debug_struct("Arena")
332            .field("len", &self.items.len())
333            .field("capacity", &self.items.capacity())
334            .finish()
335    }
336}
337
338#[cfg(test)]
339mod tests {
340    extern crate alloc;
341    use alloc::vec::Vec;
342
343    use super::*;
344
345    #[test]
346    fn test_alloc_then_get_round_trips() {
347        let mut arena = Arena::new();
348        let id = arena.alloc("payload");
349        assert_eq!(arena.get(id), Some(&"payload"));
350        assert!(arena.contains(id));
351        assert_eq!(arena.len(), 1);
352    }
353
354    #[test]
355    fn test_handles_stay_valid_as_the_arena_grows() {
356        let mut arena = Arena::new();
357        let first = arena.alloc(1);
358        for n in 2..=1000 {
359            let _ = arena.alloc(n);
360        }
361        // The very first handle still resolves to its original value.
362        assert_eq!(arena.get(first), Some(&1));
363        assert_eq!(arena.len(), 1000);
364    }
365
366    #[test]
367    fn test_get_mut_back_patches_in_place() {
368        let mut arena = Arena::new();
369        let id = arena.alloc(0_u32);
370        *arena.get_mut(id).expect("live handle") = 7;
371        assert_eq!(arena.get(id), Some(&7));
372    }
373
374    #[test]
375    fn test_out_of_range_handle_resolves_to_none() {
376        // A handle minted by a larger arena names a slot the small one lacks.
377        let mut big = Arena::new();
378        let mut last = big.alloc(0);
379        for n in 1..50 {
380            last = big.alloc(n);
381        }
382        let small: Arena<i32> = Arena::new();
383        assert_eq!(small.get(last), None);
384        assert!(!small.contains(last));
385    }
386
387    #[test]
388    fn test_iter_visits_every_value_once() {
389        let mut arena = Arena::new();
390        let mut expected = Vec::new();
391        for n in 0..32 {
392            let _ = arena.alloc(n);
393            expected.push(n);
394        }
395        let mut seen: Vec<_> = arena.iter().map(|(_, v)| *v).collect();
396        seen.sort_unstable();
397        assert_eq!(seen, expected);
398    }
399
400    #[test]
401    fn test_try_alloc_succeeds_on_a_fresh_arena() {
402        let mut arena = Arena::new();
403        let id = arena.try_alloc(123).expect("first slot is free");
404        assert_eq!(arena.get(id), Some(&123));
405    }
406
407    #[test]
408    fn test_default_is_empty() {
409        let arena: Arena<u8> = Arena::default();
410        assert!(arena.is_empty());
411        assert!(arena.capacity() >= arena.len());
412    }
413}