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}