Skip to main content

aver_memory/
arena.rs

1use super::*;
2
3impl<T: ArenaTypes> Arena<T> {
4    pub fn new() -> Self {
5        Arena {
6            young_entries: Vec::with_capacity(256),
7            yard_entries: Vec::with_capacity(64),
8            handoff_entries: Vec::with_capacity(64),
9            stable_entries: Vec::with_capacity(64),
10            scratch_young: Vec::new(),
11            scratch_yard: Vec::new(),
12            scratch_handoff: Vec::new(),
13            scratch_stable: Vec::new(),
14            peak_usage: ArenaUsage::default(),
15            alloc_space: AllocSpace::Young,
16            type_names: Vec::new(),
17            type_field_names: Vec::new(),
18            type_variant_names: Vec::new(),
19            type_variant_ctor_ids: Vec::new(),
20            ctor_to_type_variant: Vec::new(),
21            symbol_entries: Vec::new(),
22            type_aliases: Vec::new(),
23        }
24    }
25
26    /// Create a fresh Arena with only the static context (symbols, type metadata,
27    /// stable constants) from this Arena. Dynamic runtime entries are empty.
28    /// Used for independent product threads: each gets a clean Arena with just the
29    /// compile-time context needed to execute functions and builtins.
30    pub fn clone_static(&self) -> Self {
31        Arena {
32            young_entries: Vec::with_capacity(64),
33            yard_entries: Vec::new(),
34            handoff_entries: Vec::new(),
35            stable_entries: self.stable_entries.clone(),
36            scratch_young: Vec::new(),
37            scratch_yard: Vec::new(),
38            scratch_handoff: Vec::new(),
39            scratch_stable: Vec::new(),
40            peak_usage: ArenaUsage::default(),
41            alloc_space: AllocSpace::Young,
42            type_names: self.type_names.clone(),
43            type_field_names: self.type_field_names.clone(),
44            type_variant_names: self.type_variant_names.clone(),
45            type_variant_ctor_ids: self.type_variant_ctor_ids.clone(),
46            ctor_to_type_variant: self.ctor_to_type_variant.clone(),
47            symbol_entries: self.symbol_entries.clone(),
48            type_aliases: self.type_aliases.clone(),
49        }
50    }
51
52    /// Deep-import a NanValue from `source` arena into `self`.
53    /// Inline values (int, float, bool, unit, none, empty containers) are returned as-is.
54    /// Heap-referenced values are recursively copied into `self` with new indices.
55    pub fn deep_import(&mut self, value: NanValue, source: &Arena<T>) -> NanValue {
56        // Not NaN-boxed = plain float, return as-is
57        if !value.is_nan_boxed() {
58            return value;
59        }
60        // Check if it has a heap index — if not, it's inline
61        let heap_idx = match value.heap_index() {
62            Some(idx) => idx,
63            None => return value, // inline int, bool, unit, none, empty list/map/etc
64        };
65
66        let entry = source.get(heap_idx).clone();
67        match entry {
68            ArenaEntry::Int(i) => NanValue::new_int(i, self),
69            ArenaEntry::BigInt(b) => NanValue::new_big_int(*b, self),
70            ArenaEntry::String(s) => {
71                let idx = self.push(ArenaEntry::String(s));
72                NanValue::new_string(idx)
73            }
74            ArenaEntry::Tuple(items) => {
75                let imported: Vec<NanValue> =
76                    items.iter().map(|v| self.deep_import(*v, source)).collect();
77                let idx = self.push_tuple(imported);
78                NanValue::new_tuple(idx)
79            }
80            ArenaEntry::List(_) => {
81                // Flatten list and re-import as a fresh flat list
82                let flat = source.list_to_vec_value(value);
83                let imported: Vec<NanValue> =
84                    flat.iter().map(|v| self.deep_import(*v, source)).collect();
85                if imported.is_empty() {
86                    NanValue::EMPTY_LIST
87                } else {
88                    let rc_items = Rc::new(imported);
89                    let idx = self.push(ArenaEntry::List(ArenaList::Flat {
90                        items: rc_items,
91                        start: 0,
92                    }));
93                    NanValue::new_list(idx)
94                }
95            }
96            ArenaEntry::Map(map) => {
97                let mut new_map = T::Map::new();
98                for (hash, (k, v)) in map.iter() {
99                    let ik = self.deep_import(*k, source);
100                    let iv = self.deep_import(*v, source);
101                    new_map = new_map.insert(*hash, (ik, iv));
102                }
103                let idx = self.push(ArenaEntry::Map(new_map));
104                NanValue::new_map(idx)
105            }
106            ArenaEntry::Vector(items) => {
107                let imported: Vec<NanValue> =
108                    items.iter().map(|v| self.deep_import(*v, source)).collect();
109                let idx = self.push(ArenaEntry::Vector(imported));
110                NanValue::new_vector(idx)
111            }
112            ArenaEntry::Record { type_id, fields } => {
113                let imported: Vec<NanValue> = fields
114                    .iter()
115                    .map(|v| self.deep_import(*v, source))
116                    .collect();
117                let idx = self.push(ArenaEntry::Record {
118                    type_id,
119                    fields: imported,
120                });
121                NanValue::new_record(idx)
122            }
123            ArenaEntry::Variant {
124                type_id,
125                variant_id,
126                fields,
127            } => {
128                let imported: Vec<NanValue> = fields
129                    .iter()
130                    .map(|v| self.deep_import(*v, source))
131                    .collect();
132                let idx = self.push(ArenaEntry::Variant {
133                    type_id,
134                    variant_id,
135                    fields: imported,
136                });
137                NanValue::new_variant(idx)
138            }
139            ArenaEntry::Boxed(inner) => {
140                let imported = self.deep_import(inner, source);
141                let idx = self.push(ArenaEntry::Boxed(imported));
142                NanValue::encode(value.tag(), ARENA_REF_BIT | (idx as u64))
143            }
144            // Fn/Builtin/Namespace — should not appear in independent product results
145            ArenaEntry::Fn(_) | ArenaEntry::Builtin(_) | ArenaEntry::Namespace { .. } => value,
146        }
147    }
148
149    #[inline]
150    pub fn push(&mut self, entry: ArenaEntry<T>) -> u32 {
151        match &entry {
152            ArenaEntry::Fn(_) | ArenaEntry::Builtin(_) | ArenaEntry::Namespace { .. } => {}
153            _ => {
154                return match self.alloc_space {
155                    AllocSpace::Young => {
156                        let idx = self.young_entries.len() as u32;
157                        self.young_entries.push(entry);
158                        self.note_peak_usage();
159                        Self::encode_index(HeapSpace::Young, idx)
160                    }
161                    AllocSpace::Yard => {
162                        let idx = self.yard_entries.len() as u32;
163                        self.yard_entries.push(entry);
164                        self.note_peak_usage();
165                        Self::encode_index(HeapSpace::Yard, idx)
166                    }
167                    AllocSpace::Handoff => {
168                        let idx = self.handoff_entries.len() as u32;
169                        self.handoff_entries.push(entry);
170                        self.note_peak_usage();
171                        Self::encode_index(HeapSpace::Handoff, idx)
172                    }
173                };
174            }
175        }
176        match entry {
177            ArenaEntry::Fn(f) => self.push_symbol(ArenaSymbol::Fn(f)),
178            ArenaEntry::Builtin(name) => self.push_symbol(ArenaSymbol::Builtin(name)),
179            ArenaEntry::Namespace { name, members } => {
180                self.push_symbol(ArenaSymbol::Namespace { name, members })
181            }
182            _ => unreachable!("non-symbol entry already returned above"),
183        }
184    }
185
186    #[inline]
187    pub fn push_symbol(&mut self, symbol: ArenaSymbol<T>) -> u32 {
188        let idx = self.symbol_entries.len() as u32;
189        self.symbol_entries.push(symbol);
190        idx
191    }
192
193    #[inline]
194    pub fn get(&self, index: u32) -> &ArenaEntry<T> {
195        let (space, raw_index) = Self::decode_index(index);
196        match space {
197            HeapSpace::Young => &self.young_entries[raw_index as usize],
198            HeapSpace::Yard => &self.yard_entries[raw_index as usize],
199            HeapSpace::Handoff => &self.handoff_entries[raw_index as usize],
200            HeapSpace::Stable => &self.stable_entries[raw_index as usize],
201        }
202    }
203
204    #[inline]
205    pub fn get_mut(&mut self, index: u32) -> &mut ArenaEntry<T> {
206        let (space, raw_index) = Self::decode_index(index);
207        match space {
208            HeapSpace::Young => &mut self.young_entries[raw_index as usize],
209            HeapSpace::Yard => &mut self.yard_entries[raw_index as usize],
210            HeapSpace::Handoff => &mut self.handoff_entries[raw_index as usize],
211            HeapSpace::Stable => &mut self.stable_entries[raw_index as usize],
212        }
213    }
214
215    #[inline]
216    pub(crate) fn encode_index(space: HeapSpace, index: u32) -> u32 {
217        ((space as u32) << HEAP_SPACE_SHIFT) | index
218    }
219
220    #[inline]
221    pub(crate) fn encode_yard_index(index: u32) -> u32 {
222        Self::encode_index(HeapSpace::Yard, index)
223    }
224
225    #[inline]
226    pub(crate) fn encode_stable_index(index: u32) -> u32 {
227        Self::encode_index(HeapSpace::Stable, index)
228    }
229
230    #[inline]
231    pub(crate) fn encode_handoff_index(index: u32) -> u32 {
232        Self::encode_index(HeapSpace::Handoff, index)
233    }
234
235    #[inline]
236    pub(crate) fn decode_index(index: u32) -> (HeapSpace, u32) {
237        let space = match (index & HEAP_SPACE_MASK_U32) >> HEAP_SPACE_SHIFT {
238            0 => HeapSpace::Young,
239            1 => HeapSpace::Yard,
240            2 => HeapSpace::Handoff,
241            3 => HeapSpace::Stable,
242            _ => unreachable!("invalid heap space bits"),
243        };
244        (space, index & HEAP_INDEX_MASK_U32)
245    }
246
247    #[inline]
248    pub fn is_stable_index(index: u32) -> bool {
249        matches!(Self::decode_index(index).0, HeapSpace::Stable)
250    }
251
252    #[inline]
253    pub fn is_yard_index_in_region(&self, index: u32, mark: u32) -> bool {
254        let (space, raw_index) = Self::decode_index(index);
255        matches!(space, HeapSpace::Yard)
256            && raw_index >= mark
257            && raw_index < self.yard_entries.len() as u32
258    }
259
260    #[inline]
261    pub fn is_handoff_index_in_region(&self, index: u32, mark: u32) -> bool {
262        let (space, raw_index) = Self::decode_index(index);
263        matches!(space, HeapSpace::Handoff)
264            && raw_index >= mark
265            && raw_index < self.handoff_entries.len() as u32
266    }
267
268    #[inline]
269    pub fn is_young_index_in_region(&self, index: u32, mark: u32) -> bool {
270        let (space, raw_index) = Self::decode_index(index);
271        matches!(space, HeapSpace::Young)
272            && raw_index >= mark
273            && raw_index < self.young_entries.len() as u32
274    }
275
276    #[inline]
277    pub fn young_len(&self) -> usize {
278        self.young_entries.len()
279    }
280
281    #[inline]
282    pub fn yard_len(&self) -> usize {
283        self.yard_entries.len()
284    }
285
286    #[inline]
287    pub fn handoff_len(&self) -> usize {
288        self.handoff_entries.len()
289    }
290
291    #[inline]
292    pub fn stable_len(&self) -> usize {
293        self.stable_entries.len()
294    }
295
296    #[inline]
297    pub fn usage(&self) -> ArenaUsage {
298        ArenaUsage {
299            young: self.young_entries.len(),
300            yard: self.yard_entries.len(),
301            handoff: self.handoff_entries.len(),
302            stable: self.stable_entries.len(),
303        }
304    }
305
306    #[inline]
307    pub fn peak_usage(&self) -> ArenaUsage {
308        self.peak_usage
309    }
310
311    #[inline]
312    pub(crate) fn note_peak_usage(&mut self) {
313        let usage = self.usage();
314        self.peak_usage.young = self.peak_usage.young.max(usage.young);
315        self.peak_usage.yard = self.peak_usage.yard.max(usage.yard);
316        self.peak_usage.handoff = self.peak_usage.handoff.max(usage.handoff);
317        self.peak_usage.stable = self.peak_usage.stable.max(usage.stable);
318    }
319
320    #[inline]
321    pub(crate) fn take_u32_scratch(slot: &mut Vec<u32>, len: usize) -> Vec<u32> {
322        let mut scratch = core::mem::take(slot);
323        scratch.clear();
324        scratch.resize(len, u32::MAX);
325        scratch
326    }
327
328    #[inline]
329    pub(crate) fn recycle_u32_scratch(slot: &mut Vec<u32>, mut scratch: Vec<u32>) {
330        scratch.clear();
331        *slot = scratch;
332    }
333
334    #[inline]
335    pub fn is_frame_local_index(
336        &self,
337        index: u32,
338        arena_mark: u32,
339        yard_mark: u32,
340        handoff_mark: u32,
341    ) -> bool {
342        self.is_young_index_in_region(index, arena_mark)
343            || self.is_yard_index_in_region(index, yard_mark)
344            || self.is_handoff_index_in_region(index, handoff_mark)
345    }
346
347    pub fn with_alloc_space<R>(
348        &mut self,
349        space: AllocSpace,
350        f: impl FnOnce(&mut Arena<T>) -> R,
351    ) -> R {
352        let prev = self.alloc_space;
353        self.alloc_space = space;
354        let out = f(self);
355        self.alloc_space = prev;
356        out
357    }
358
359    /// Push an entry, inheriting the allocation space from a source value.
360    /// If the source lives in yard or handoff, the result is placed there too,
361    /// avoiding a pointless young→yard/handoff promotion later.
362    pub fn push_inheriting_source_space(&mut self, entry: ArenaEntry<T>, source: NanValue) -> u32 {
363        if let Some(index) = source.heap_index() {
364            let (space, _) = Self::decode_index(index);
365            let target = match space {
366                HeapSpace::Yard => Some(AllocSpace::Yard),
367                HeapSpace::Handoff => Some(AllocSpace::Handoff),
368                _ => None,
369            };
370            if let Some(target) = target {
371                let prev = self.alloc_space;
372                self.alloc_space = target;
373                let idx = self.push(entry);
374                self.alloc_space = prev;
375                return idx;
376            }
377        }
378        self.push(entry)
379    }
380
381    // -- Typed push helpers ------------------------------------------------
382
383    pub fn push_i64(&mut self, val: i64) -> u32 {
384        self.push(ArenaEntry::Int(val))
385    }
386    /// Store an arbitrary-precision integer, upholding the canonical-form
387    /// invariant: a payload that fits `i64` is stored as `ArenaEntry::Int`
388    /// (so `int_ref_at` reports it as `Small`), never as a `BigInt` slot.
389    /// Only a genuinely out-of-`i64`-range value allocates a `BigInt`. The
390    /// `i64`-fitting case should normally be demoted to the inline NaN-box one
391    /// layer up (`NanValue::new_big_int`); this is the backstop.
392    pub fn push_bigint(&mut self, val: num_bigint::BigInt) -> u32 {
393        match i64::try_from(&val) {
394            Ok(n) => self.push(ArenaEntry::Int(n)),
395            Err(_) => self.push(ArenaEntry::BigInt(Box::new(val))),
396        }
397    }
398    pub fn push_string(&mut self, s: &str) -> u32 {
399        self.push(ArenaEntry::String(Rc::from(s)))
400    }
401    pub fn push_boxed(&mut self, val: NanValue) -> u32 {
402        self.push(ArenaEntry::Boxed(val))
403    }
404    pub fn push_record(&mut self, type_id: u32, fields: Vec<NanValue>) -> u32 {
405        self.push(ArenaEntry::Record { type_id, fields })
406    }
407    pub fn push_variant(&mut self, type_id: u32, variant_id: u16, fields: Vec<NanValue>) -> u32 {
408        self.push(ArenaEntry::Variant {
409            type_id,
410            variant_id,
411            fields,
412        })
413    }
414    pub fn push_list(&mut self, items: Vec<NanValue>) -> u32 {
415        self.push(ArenaEntry::List(ArenaList::Flat {
416            items: Rc::new(items),
417            start: 0,
418        }))
419    }
420    pub fn push_map(&mut self, map: T::Map) -> u32 {
421        self.push(ArenaEntry::Map(map))
422    }
423    pub fn push_tuple(&mut self, items: Vec<NanValue>) -> u32 {
424        self.push(ArenaEntry::Tuple(items))
425    }
426    pub fn push_vector(&mut self, items: Vec<NanValue>) -> u32 {
427        self.push(ArenaEntry::Vector(items))
428    }
429    pub fn push_fn(&mut self, f: Rc<T::Fn>) -> u32 {
430        self.push_symbol(ArenaSymbol::Fn(f))
431    }
432    pub fn push_builtin(&mut self, name: &str) -> u32 {
433        self.push_symbol(ArenaSymbol::Builtin(Rc::from(name)))
434    }
435    pub fn push_nullary_variant_symbol(&mut self, ctor_id: u32) -> u32 {
436        self.push_symbol(ArenaSymbol::NullaryVariant { ctor_id })
437    }
438
439    // -- Typed getters -----------------------------------------------------
440
441    pub fn get_i64(&self, index: u32) -> i64 {
442        match self.get(index) {
443            ArenaEntry::Int(i) => *i,
444            _ => panic!("Arena: expected Int at {}", index),
445        }
446    }
447    /// Borrow the out-of-range integer at `index`.
448    pub fn get_bigint(&self, index: u32) -> &num_bigint::BigInt {
449        match self.get(index) {
450            ArenaEntry::BigInt(b) => b,
451            _ => panic!("Arena: expected BigInt at {}", index),
452        }
453    }
454    /// Discriminate an arena-stored integer (i64-overflow vs ℤ-overflow)
455    /// without materializing it. The runtime side reconstructs the canonical
456    /// `AverInt` from this.
457    pub fn int_ref_at(&self, index: u32) -> ArenaIntRef<'_> {
458        match self.get(index) {
459            ArenaEntry::Int(i) => ArenaIntRef::Small(*i),
460            ArenaEntry::BigInt(b) => ArenaIntRef::Big(b),
461            other => panic!(
462                "Arena: expected an integer at {} but found {:?}",
463                index, other
464            ),
465        }
466    }
467    pub fn get_string(&self, index: u32) -> &str {
468        match self.get(index) {
469            ArenaEntry::String(s) => s,
470            other => panic!("Arena: expected String at {} but found {:?}", index, other),
471        }
472    }
473    pub fn get_string_value(&self, value: NanValue) -> NanString<'_> {
474        if let Some(s) = value.small_string() {
475            s
476        } else {
477            NanString::Borrowed(self.get_string(value.arena_index()))
478        }
479    }
480    pub fn get_boxed(&self, index: u32) -> NanValue {
481        match self.get(index) {
482            ArenaEntry::Boxed(v) => *v,
483            _ => panic!("Arena: expected Boxed at {}", index),
484        }
485    }
486    pub fn get_record(&self, index: u32) -> (u32, &[NanValue]) {
487        match self.get(index) {
488            ArenaEntry::Record { type_id, fields } => (*type_id, fields),
489            _ => panic!("Arena: expected Record at {}", index),
490        }
491    }
492    pub fn get_variant(&self, index: u32) -> (u32, u16, &[NanValue]) {
493        match self.get(index) {
494            ArenaEntry::Variant {
495                type_id,
496                variant_id,
497                fields,
498            } => (*type_id, *variant_id, fields),
499            other => panic!("Arena: expected Variant at {} but found {:?}", index, other),
500        }
501    }
502    pub fn get_list(&self, index: u32) -> &ArenaList {
503        match self.get(index) {
504            ArenaEntry::List(items) => items,
505            _ => panic!("Arena: expected List at {}", index),
506        }
507    }
508    pub fn get_tuple(&self, index: u32) -> &[NanValue] {
509        match self.get(index) {
510            ArenaEntry::Tuple(items) => items,
511            _ => panic!("Arena: expected Tuple at {}", index),
512        }
513    }
514    pub fn get_vector(&self, index: u32) -> &[NanValue] {
515        match self.get(index) {
516            ArenaEntry::Vector(items) => items,
517            _ => panic!("Arena: expected Vector at {}", index),
518        }
519    }
520    pub fn get_vector_mut(&mut self, index: u32) -> &mut Vec<NanValue> {
521        match self.get_mut(index) {
522            ArenaEntry::Vector(items) => items,
523            _ => panic!("Arena: expected Vector at {}", index),
524        }
525    }
526    pub fn vector_ref_value(&self, value: NanValue) -> &[NanValue] {
527        if value.is_empty_vector_immediate() {
528            return &[];
529        }
530        self.get_vector(value.arena_index())
531    }
532    pub fn clone_vector_value(&self, value: NanValue) -> Vec<NanValue> {
533        if value.is_empty_vector_immediate() {
534            Vec::new()
535        } else {
536            self.get_vector(value.arena_index()).to_vec()
537        }
538    }
539    /// Take ownership of a vector, replacing the arena slot with an empty vec.
540    pub fn take_vector_value(&mut self, value: NanValue) -> Vec<NanValue> {
541        if value.is_empty_vector_immediate() {
542            Vec::new()
543        } else {
544            let index = value.arena_index();
545            std::mem::take(self.get_vector_mut(index))
546        }
547    }
548    pub fn get_map(&self, index: u32) -> &T::Map {
549        match self.get(index) {
550            ArenaEntry::Map(map) => map,
551            _ => panic!("Arena: expected Map at {}", index),
552        }
553    }
554    pub fn get_map_mut(&mut self, index: u32) -> &mut T::Map {
555        match self.get_mut(index) {
556            ArenaEntry::Map(map) => map,
557            _ => panic!("Arena: expected Map at {}", index),
558        }
559    }
560    pub fn map_ref_value(&self, map: NanValue) -> &T::Map {
561        if map.is_empty_map_immediate() {
562            // Use a leaked singleton for the empty map reference.
563            // This avoids thread_local! which is not available in no_std.
564            use core::sync::atomic::{AtomicPtr, Ordering as AtomicOrdering};
565            static EMPTY_MAP_PTR: AtomicPtr<()> = AtomicPtr::new(core::ptr::null_mut());
566
567            let ptr = EMPTY_MAP_PTR.load(AtomicOrdering::Acquire);
568            if !ptr.is_null() {
569                // SAFETY: ptr was allocated via Box::leak and is valid for 'static
570                return unsafe { &*(ptr as *const T::Map) };
571            }
572            let boxed = alloc::boxed::Box::new(T::Map::new());
573            let leaked: &'static T::Map = alloc::boxed::Box::leak(boxed);
574            let new_ptr = leaked as *const T::Map as *mut ();
575            // If another thread raced us, that's fine — we just leak one extra allocation
576            EMPTY_MAP_PTR.store(new_ptr, AtomicOrdering::Release);
577            leaked
578        } else {
579            self.get_map(map.arena_index())
580        }
581    }
582    pub fn clone_map_value(&self, map: NanValue) -> T::Map {
583        if map.is_empty_map_immediate() {
584            T::Map::new()
585        } else {
586            self.get_map(map.arena_index()).clone()
587        }
588    }
589    /// Take ownership of a map value, replacing it with an empty map in the arena.
590    /// Use when the caller is the sole owner (reuse analysis says `owned = true`).
591    /// Avoids the O(n) clone — the original slot becomes empty.
592    pub fn take_map_value(&mut self, map: NanValue) -> T::Map {
593        if map.is_empty_map_immediate() {
594            T::Map::new()
595        } else {
596            let index = map.arena_index();
597            std::mem::replace(self.get_map_mut(index), T::Map::new())
598        }
599    }
600    pub fn get_fn(&self, index: u32) -> &T::Fn {
601        match &self.symbol_entries[index as usize] {
602            ArenaSymbol::Fn(f) => f,
603            _ => panic!("Arena: expected Fn symbol at {}", index),
604        }
605    }
606    pub fn get_fn_rc(&self, index: u32) -> &Rc<T::Fn> {
607        match &self.symbol_entries[index as usize] {
608            ArenaSymbol::Fn(f) => f,
609            _ => panic!("Arena: expected Fn symbol at {}", index),
610        }
611    }
612    pub fn get_builtin(&self, index: u32) -> &str {
613        match &self.symbol_entries[index as usize] {
614            ArenaSymbol::Builtin(s) => s,
615            _ => panic!("Arena: expected Builtin symbol at {}", index),
616        }
617    }
618    pub fn get_namespace(&self, index: u32) -> (&str, &[(Rc<str>, NanValue)]) {
619        match &self.symbol_entries[index as usize] {
620            ArenaSymbol::Namespace { name, members } => (name, members),
621            _ => panic!("Arena: expected Namespace symbol at {}", index),
622        }
623    }
624    pub fn get_nullary_variant_ctor(&self, index: u32) -> u32 {
625        match &self.symbol_entries[index as usize] {
626            ArenaSymbol::NullaryVariant { ctor_id } => *ctor_id,
627            _ => panic!("Arena: expected NullaryVariant symbol at {}", index),
628        }
629    }
630
631    // -- Type registry -----------------------------------------------------
632
633    pub fn register_record_type(&mut self, name: &str, field_names: Vec<String>) -> u32 {
634        let id = self.type_names.len() as u32;
635        self.type_names.push(String::from(name));
636        self.type_field_names.push(field_names);
637        self.type_variant_names.push(Vec::new());
638        self.type_variant_ctor_ids.push(Vec::new());
639        id
640    }
641
642    pub fn register_sum_type(&mut self, name: &str, variant_names: Vec<String>) -> u32 {
643        let id = self.type_names.len() as u32;
644        self.type_names.push(String::from(name));
645        self.type_field_names.push(Vec::new());
646        let ctor_ids: Vec<u32> = (0..variant_names.len())
647            .map(|variant_idx| {
648                let ctor_id = self.ctor_to_type_variant.len() as u32;
649                self.ctor_to_type_variant.push((id, variant_idx as u16));
650                ctor_id
651            })
652            .collect();
653        self.type_variant_names.push(variant_names);
654        self.type_variant_ctor_ids.push(ctor_ids);
655        id
656    }
657
658    pub fn register_variant_name(&mut self, type_id: u32, variant_name: String) -> u16 {
659        let variants = &mut self.type_variant_names[type_id as usize];
660        let variant_id = variants.len() as u16;
661        variants.push(variant_name);
662
663        let ctor_id = self.ctor_to_type_variant.len() as u32;
664        self.ctor_to_type_variant.push((type_id, variant_id));
665        self.type_variant_ctor_ids[type_id as usize].push(ctor_id);
666
667        variant_id
668    }
669
670    pub fn get_type_name(&self, type_id: u32) -> &str {
671        &self.type_names[type_id as usize]
672    }
673    pub fn type_count(&self) -> u32 {
674        self.type_names.len() as u32
675    }
676    pub fn get_field_names(&self, type_id: u32) -> &[String] {
677        &self.type_field_names[type_id as usize]
678    }
679    pub fn get_variant_name(&self, type_id: u32, variant_id: u16) -> &str {
680        &self.type_variant_names[type_id as usize][variant_id as usize]
681    }
682    pub fn register_type_alias(&mut self, alias: &str, type_id: u32) {
683        self.type_aliases.push((alias.to_string(), type_id));
684    }
685
686    pub fn find_type_id(&self, name: &str) -> Option<u32> {
687        self.type_names
688            .iter()
689            .position(|n| n == name)
690            .map(|i| i as u32)
691            .or_else(|| {
692                self.type_aliases
693                    .iter()
694                    .find(|(alias, _)| alias == name)
695                    .map(|(_, id)| *id)
696            })
697    }
698    pub fn find_variant_id(&self, type_id: u32, variant_name: &str) -> Option<u16> {
699        self.type_variant_names
700            .get(type_id as usize)?
701            .iter()
702            .position(|n| n == variant_name)
703            .map(|i| i as u16)
704    }
705
706    pub fn find_ctor_id(&self, type_id: u32, variant_id: u16) -> Option<u32> {
707        self.type_variant_ctor_ids
708            .get(type_id as usize)?
709            .get(variant_id as usize)
710            .copied()
711    }
712
713    pub fn get_ctor_parts(&self, ctor_id: u32) -> (u32, u16) {
714        self.ctor_to_type_variant
715            .get(ctor_id as usize)
716            .copied()
717            .unwrap_or_else(|| panic!("Arena: expected ctor id {} to be registered", ctor_id))
718    }
719
720    pub fn len(&self) -> usize {
721        self.young_entries.len()
722            + self.yard_entries.len()
723            + self.handoff_entries.len()
724            + self.stable_entries.len()
725    }
726    pub fn is_empty(&self) -> bool {
727        self.young_entries.is_empty()
728            && self.yard_entries.is_empty()
729            && self.handoff_entries.is_empty()
730            && self.stable_entries.is_empty()
731    }
732}
733
734impl<T: ArenaTypes> Default for Arena<T> {
735    fn default() -> Self {
736        Self::new()
737    }
738}