Skip to main content

aver_memory/
lib.rs

1//! NaN-boxed compact Value representation (8 bytes per value).
2//!
3//! Layout: every value is a `u64` interpreted as an IEEE 754 `f64`.
4//!
5//! - **Float**: any f64 that is NOT a quiet NaN with our marker -> stored directly.
6//! - **Everything else**: encoded as a quiet NaN with tag + payload in the mantissa.
7//!
8//! IEEE 754 quiet NaN: exponent=0x7FF (all 1s), quiet bit=1, plus our marker bit.
9//! We use `0x7FFC` as 14-bit prefix (bits 63-50), leaving bits 49-0 free.
10//!
11//! ```text
12//! 63      50 49  46 45                    0
13//! +--------+------+------------------------+
14//! | 0x7FFC | tag  |       payload          |
15//! | 14 bits| 4 bit|       46 bits          |
16//! +--------+------+------------------------+
17//! ```
18//!
19//! Tag map:
20//!   0  = Immediate       payload 0-2: false/true/unit
21//!   1  = Symbol          payload bits 0-1: fn/builtin/namespace/nullary-variant; rest=symbol index
22//!   2  = Int             payload bit45: 0=inline(45-bit signed), 1=arena index
23//!   3  = String          payload bit45: 0=inline small string (len + 5 bytes), 1=arena index
24//!   4  = Some            payload bit45: 0=inline inner, 1=arena index
25//!   5  = None            singleton
26//!   6  = Ok              payload bit45: 0=inline inner, 1=arena index
27//!   7  = Err             payload bit45: 0=inline inner, 1=arena index
28//!   8  = List            payload bit45: 0=empty list, 1=arena index
29//!   9  = Vector          payload bit45: 0=empty vector, 1=arena index
30//!   10 = Map             payload bit45: 0=empty map, 1=arena index
31//!   11 = Record          payload bit45: 1=arena index
32//!   12 = Variant         payload bit45: 1=arena index
33//!   13 = Tuple           payload bit45: 1=arena index
34//!   14 = InlineVariant  [45:30]=ctor_id, [29]=kind(0=int,1=imm), [28:0]=value
35//!   15 = (reserved)
36
37#![cfg_attr(not(feature = "std"), no_std)]
38
39extern crate alloc;
40
41use alloc::format;
42use alloc::string::String;
43use alloc::sync::Arc as Rc;
44use alloc::vec::Vec;
45use core::cmp::Ordering;
46use core::hash::{Hash, Hasher};
47use core::ops::Deref;
48
49// ---------------------------------------------------------------------------
50// ArenaTypes trait — parameterises the arena over consumer-specific types
51// ---------------------------------------------------------------------------
52
53/// Trait that defines the function and map types used by the arena.
54///
55/// The arena stores function values and persistent maps, but their concrete
56/// types depend on the consumer (VM, WASM runtime, codegen, etc.).
57pub trait ArenaTypes: Clone + core::fmt::Debug + 'static {
58    /// The function value type (e.g. `FunctionValue` in the VM).
59    type Fn: Clone + core::fmt::Debug + FnValueName;
60    /// The persistent map type (e.g. `AverMap<u64, (NanValue, NanValue)>`).
61    type Map: Clone + core::fmt::Debug + MapLike;
62}
63
64/// Trait for extracting a display name from a function value.
65pub trait FnValueName {
66    fn name(&self) -> &str;
67}
68
69/// Trait abstracting the persistent-map operations needed by the arena.
70///
71/// Implementors provide a hash-keyed map from `u64` to `(NanValue, NanValue)`.
72pub trait MapLike: Sized {
73    fn new() -> Self;
74    fn get(&self, key: &u64) -> Option<&(NanValue, NanValue)>;
75    fn insert(&self, key: u64, value: (NanValue, NanValue)) -> Self;
76    /// Insert with owned self — avoids clone when sole owner.
77    fn insert_owned(self, key: u64, value: (NanValue, NanValue)) -> Self {
78        self.insert(key, value) // default: fall back to &self version
79    }
80    /// Rewrite NanValue pairs in place — avoids rebuilding the hash table.
81    /// Uses copy-on-write: O(1) when sole owner, O(n) clone when shared.
82    fn rewrite_values_mut(&mut self, f: impl FnMut(&mut (NanValue, NanValue)));
83    fn len(&self) -> usize;
84    fn is_empty(&self) -> bool {
85        self.len() == 0
86    }
87    fn iter(&self) -> impl Iterator<Item = (&u64, &(NanValue, NanValue))>;
88    fn values(&self) -> impl Iterator<Item = &(NanValue, NanValue)>;
89}
90
91// ---------------------------------------------------------------------------
92// Bit layout constants
93// ---------------------------------------------------------------------------
94
95const QNAN: u64 = 0x7FFC_0000_0000_0000;
96const QNAN_MASK: u64 = 0xFFFC_0000_0000_0000;
97const QNAN_MARKER_BIT: u64 = 1u64 << 50;
98const TAG_SHIFT: u32 = 46;
99const TAG_MASK: u64 = 0xF;
100const PAYLOAD_MASK: u64 = (1u64 << 46) - 1;
101
102pub const TAG_IMMEDIATE: u64 = 0;
103pub const TAG_SYMBOL: u64 = 1;
104pub const TAG_INT: u64 = 2;
105pub const TAG_STRING: u64 = 3;
106pub const TAG_SOME: u64 = 4;
107pub const TAG_NONE: u64 = 5;
108pub const TAG_OK: u64 = 6;
109pub const TAG_ERR: u64 = 7;
110pub const TAG_LIST: u64 = 8;
111pub const TAG_VECTOR: u64 = 9;
112pub const TAG_MAP: u64 = 10;
113pub const TAG_RECORD: u64 = 11;
114pub const TAG_VARIANT: u64 = 12;
115pub const TAG_TUPLE: u64 = 13;
116pub const TAG_INLINE_VARIANT: u64 = 14;
117
118pub const SYMBOL_FN: u64 = 0;
119pub const SYMBOL_BUILTIN: u64 = 1;
120pub const SYMBOL_NAMESPACE: u64 = 2;
121pub const SYMBOL_NULLARY_VARIANT: u64 = 3;
122const SYMBOL_KIND_MASK: u64 = 0b11;
123
124pub const IMM_FALSE: u64 = 0;
125pub const IMM_TRUE: u64 = 1;
126pub const IMM_UNIT: u64 = 2;
127
128pub const WRAP_SOME: u64 = 0;
129pub const WRAP_OK: u64 = 1;
130pub const WRAP_ERR: u64 = 2;
131const WRAPPER_INLINE_KIND_SHIFT: u32 = 43;
132const WRAPPER_INLINE_KIND_MASK: u64 = 0b11 << WRAPPER_INLINE_KIND_SHIFT;
133const WRAPPER_INLINE_PAYLOAD_MASK: u64 = (1u64 << WRAPPER_INLINE_KIND_SHIFT) - 1;
134const WRAPPER_INLINE_IMMEDIATE: u64 = 0;
135const WRAPPER_INLINE_INT: u64 = 1;
136const WRAPPER_INLINE_NONE: u64 = 2;
137const WRAPPER_INT_INLINE_MASK: u64 = WRAPPER_INLINE_PAYLOAD_MASK;
138const WRAPPER_INT_INLINE_MAX: i64 = (1i64 << 42) - 1;
139const WRAPPER_INT_INLINE_MIN: i64 = -(1i64 << 42);
140
141pub const ARENA_REF_BIT: u64 = 1u64 << 45;
142const INT_BIG_BIT: u64 = ARENA_REF_BIT;
143const INT_INLINE_MASK: u64 = (1u64 << 45) - 1;
144pub const INT_INLINE_MAX: i64 = (1i64 << 44) - 1;
145pub const INT_INLINE_MIN: i64 = -(1i64 << 44);
146
147const STRING_ARENA_BIT: u64 = ARENA_REF_BIT;
148const STRING_INLINE_LEN_SHIFT: u32 = 40;
149const STRING_INLINE_LEN_MASK: u64 = 0b111 << STRING_INLINE_LEN_SHIFT;
150const STRING_INLINE_MAX_BYTES: usize = 5;
151
152// -- Inline variant layout --------------------------------------------------
153const IV_CTOR_SHIFT: u32 = 30;
154const IV_CTOR_MASK: u64 = 0xFFFF;
155const IV_KIND_BIT: u64 = 1 << 29;
156const IV_INT_MASK: u64 = (1u64 << 29) - 1;
157const IV_INT_SIGN_BIT: u64 = 1u64 << 28;
158const IV_INT_MAX: i64 = (1i64 << 28) - 1;
159const IV_INT_MIN: i64 = -(1i64 << 28);
160const IV_IMM_SHIFT: u32 = 27;
161const IV_IMM_FALSE: u64 = 0;
162const IV_IMM_TRUE: u64 = 1;
163const IV_IMM_UNIT: u64 = 2;
164const IV_IMM_NONE: u64 = 3;
165
166// ---------------------------------------------------------------------------
167// NanValue - the 8-byte compact value
168// ---------------------------------------------------------------------------
169
170#[derive(Clone, Copy)]
171pub struct NanValue(u64);
172
173#[derive(Clone, Copy, Debug)]
174pub enum NanString<'a> {
175    Borrowed(&'a str),
176    Inline {
177        len: u8,
178        bytes: [u8; STRING_INLINE_MAX_BYTES],
179    },
180}
181
182impl<'a> NanString<'a> {
183    #[inline]
184    pub fn as_str(&self) -> &str {
185        match self {
186            NanString::Borrowed(s) => s,
187            NanString::Inline { len, bytes } => core::str::from_utf8(&bytes[..*len as usize])
188                .expect("NanString inline payload must be valid UTF-8"),
189        }
190    }
191}
192
193impl Deref for NanString<'_> {
194    type Target = str;
195
196    #[inline]
197    fn deref(&self) -> &Self::Target {
198        self.as_str()
199    }
200}
201
202impl PartialEq for NanString<'_> {
203    #[inline]
204    fn eq(&self, other: &Self) -> bool {
205        self.as_str() == other.as_str()
206    }
207}
208
209impl Eq for NanString<'_> {}
210
211impl PartialEq<&str> for NanString<'_> {
212    #[inline]
213    fn eq(&self, other: &&str) -> bool {
214        self.as_str() == *other
215    }
216}
217
218impl PartialEq<NanString<'_>> for &str {
219    #[inline]
220    fn eq(&self, other: &NanString<'_>) -> bool {
221        *self == other.as_str()
222    }
223}
224
225impl PartialOrd for NanString<'_> {
226    #[inline]
227    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
228        Some(self.cmp(other))
229    }
230}
231
232impl Ord for NanString<'_> {
233    #[inline]
234    fn cmp(&self, other: &Self) -> Ordering {
235        self.as_str().cmp(other.as_str())
236    }
237}
238
239impl Hash for NanString<'_> {
240    #[inline]
241    fn hash<H: Hasher>(&self, state: &mut H) {
242        self.as_str().hash(state);
243    }
244}
245
246impl core::fmt::Display for NanString<'_> {
247    #[inline]
248    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
249        f.write_str(self.as_str())
250    }
251}
252
253// -- Encoding / decoding ---------------------------------------------------
254
255impl NanValue {
256    #[inline]
257    fn decode_inline_int_payload(payload: u64) -> i64 {
258        debug_assert!(payload & INT_BIG_BIT == 0);
259        let raw = payload & INT_INLINE_MASK;
260        if raw & (1u64 << 44) != 0 {
261            (raw | !INT_INLINE_MASK) as i64
262        } else {
263            raw as i64
264        }
265    }
266
267    #[inline]
268    pub fn encode(tag: u64, payload: u64) -> Self {
269        debug_assert!(tag <= TAG_MASK);
270        debug_assert!(payload <= PAYLOAD_MASK);
271        NanValue(QNAN | (tag << TAG_SHIFT) | payload)
272    }
273
274    #[inline]
275    pub fn is_nan_boxed(self) -> bool {
276        (self.0 & QNAN_MASK) == QNAN
277    }
278
279    #[inline]
280    pub fn tag(self) -> u64 {
281        (self.0 >> TAG_SHIFT) & TAG_MASK
282    }
283
284    #[inline]
285    pub fn payload(self) -> u64 {
286        self.0 & PAYLOAD_MASK
287    }
288
289    // -- Constructors ------------------------------------------------------
290
291    #[inline]
292    pub fn new_float(f: f64) -> Self {
293        let bits = f.to_bits();
294        if (bits & QNAN_MASK) == QNAN {
295            // A colliding pattern is already a quiet NaN. Clear only Aver's
296            // marker bit: the result remains a quiet NaN, retains its sign and
297            // remaining payload, and can no longer be mistaken for a box.
298            NanValue(bits & !QNAN_MARKER_BIT)
299        } else {
300            NanValue(bits)
301        }
302    }
303
304    #[inline]
305    pub fn as_float(self) -> f64 {
306        f64::from_bits(self.0)
307    }
308
309    #[inline]
310    pub fn new_int_inline(i: i64) -> Self {
311        debug_assert!((INT_INLINE_MIN..=INT_INLINE_MAX).contains(&i));
312        let payload = (i as u64) & INT_INLINE_MASK;
313        Self::encode(TAG_INT, payload)
314    }
315
316    #[inline]
317    pub fn new_int_arena(arena_index: u32) -> Self {
318        Self::encode(TAG_INT, INT_BIG_BIT | (arena_index as u64))
319    }
320
321    #[inline]
322    pub fn new_int<T: ArenaTypes>(i: i64, arena: &mut Arena<T>) -> Self {
323        if (INT_INLINE_MIN..=INT_INLINE_MAX).contains(&i) {
324            Self::new_int_inline(i)
325        } else {
326            let idx = arena.push_i64(i);
327            Self::new_int_arena(idx)
328        }
329    }
330
331    /// Store an arbitrary-precision integer and return its NaN-box. This is a
332    /// canonical-form boundary: a payload that fits `i64` is demoted to the
333    /// inline / `i64`-overflow representation rather than boxed, so a
334    /// numerically-`Small` value can never enter the value space wearing a
335    /// `BigInt` slot (which would break `Eq`/`Ord`/`Hash` and Map/Set keying).
336    /// Only a genuinely out-of-`i64`-range value allocates a `BigInt` slot.
337    #[inline]
338    pub fn new_big_int<T: ArenaTypes>(value: num_bigint::BigInt, arena: &mut Arena<T>) -> Self {
339        match i64::try_from(&value) {
340            Ok(n) => Self::new_int(n, arena),
341            Err(_) => Self::new_int_arena(arena.push_bigint(value)),
342        }
343    }
344
345    /// Materialize the stored value as `i64`. Valid only for inline ints and
346    /// the `i64`-overflow arena slot; panics on a ℤ-overflow (`BigInt`) slot,
347    /// which cannot be represented. Use `int_ref` when the value may be Big.
348    #[inline]
349    pub fn as_int<T: ArenaTypes>(self, arena: &Arena<T>) -> i64 {
350        let p = self.payload();
351        if p & INT_BIG_BIT != 0 {
352            let idx = (p & !INT_BIG_BIT) as u32;
353            arena.get_i64(idx)
354        } else {
355            Self::decode_inline_int_payload(p)
356        }
357    }
358
359    /// Borrow the stored integer, discriminating inline / `i64`-overflow /
360    /// ℤ-overflow without losing precision. The runtime crate maps this to a
361    /// canonical `AverInt`.
362    #[inline]
363    pub fn int_ref<T: ArenaTypes>(self, arena: &Arena<T>) -> ArenaIntRef<'_> {
364        let p = self.payload();
365        if p & INT_BIG_BIT != 0 {
366            arena.int_ref_at((p & !INT_BIG_BIT) as u32)
367        } else {
368            ArenaIntRef::Small(Self::decode_inline_int_payload(p))
369        }
370    }
371
372    #[inline]
373    pub fn inline_int_payload(self) -> Option<u64> {
374        (self.is_nan_boxed() && self.tag() == TAG_INT && self.payload() & INT_BIG_BIT == 0)
375            .then_some(self.payload())
376    }
377
378    #[inline]
379    pub fn inline_int_value(self) -> Option<i64> {
380        self.inline_int_payload()
381            .map(Self::decode_inline_int_payload)
382    }
383
384    // -- Immediates --------------------------------------------------------
385
386    pub const FALSE: NanValue = NanValue(QNAN | (TAG_IMMEDIATE << TAG_SHIFT) | IMM_FALSE);
387    pub const TRUE: NanValue = NanValue(QNAN | (TAG_IMMEDIATE << TAG_SHIFT) | IMM_TRUE);
388    pub const UNIT: NanValue = NanValue(QNAN | (TAG_IMMEDIATE << TAG_SHIFT) | IMM_UNIT);
389    pub const NONE: NanValue = NanValue(QNAN | (TAG_NONE << TAG_SHIFT));
390    pub const EMPTY_LIST: NanValue = NanValue(QNAN | (TAG_LIST << TAG_SHIFT));
391    pub const EMPTY_MAP: NanValue = NanValue(QNAN | (TAG_MAP << TAG_SHIFT));
392    pub const EMPTY_VECTOR: NanValue = NanValue(QNAN | (TAG_VECTOR << TAG_SHIFT));
393    pub const EMPTY_STRING: NanValue = NanValue(QNAN | (TAG_STRING << TAG_SHIFT));
394
395    #[inline]
396    pub fn new_bool(b: bool) -> Self {
397        if b { Self::TRUE } else { Self::FALSE }
398    }
399
400    #[inline]
401    pub fn as_bool(self) -> bool {
402        self.0 == Self::TRUE.0
403    }
404
405    #[inline]
406    pub fn plain_immediate_payload(self) -> Option<u64> {
407        (self.is_nan_boxed() && self.tag() == TAG_IMMEDIATE && self.payload() <= IMM_UNIT)
408            .then_some(self.payload())
409    }
410
411    #[inline]
412    pub fn wrapper_kind(self) -> u64 {
413        match self.tag() {
414            TAG_SOME => WRAP_SOME,
415            TAG_OK => WRAP_OK,
416            TAG_ERR => WRAP_ERR,
417            _ => panic!("wrapper_kind() called on non-wrapper"),
418        }
419    }
420
421    #[inline]
422    fn wrapper_inline_kind(self) -> Option<u64> {
423        if !self.is_nan_boxed() {
424            return None;
425        }
426        match self.tag() {
427            TAG_SOME | TAG_OK | TAG_ERR if self.payload() & ARENA_REF_BIT == 0 => {
428                Some((self.payload() & WRAPPER_INLINE_KIND_MASK) >> WRAPPER_INLINE_KIND_SHIFT)
429            }
430            _ => None,
431        }
432    }
433
434    #[inline]
435    fn decode_wrapper_inline_int_payload(payload: u64) -> i64 {
436        let raw = payload & WRAPPER_INT_INLINE_MASK;
437        if raw & (1u64 << 42) != 0 {
438            (raw | !WRAPPER_INT_INLINE_MASK) as i64
439        } else {
440            raw as i64
441        }
442    }
443
444    #[inline]
445    fn encode_wrapper_inline_int(i: i64) -> u64 {
446        debug_assert!((WRAPPER_INT_INLINE_MIN..=WRAPPER_INT_INLINE_MAX).contains(&i));
447        (i as u64) & WRAPPER_INT_INLINE_MASK
448    }
449
450    #[inline]
451    pub fn wrapper_inline_inner(self) -> Option<NanValue> {
452        let kind = self.wrapper_inline_kind()?;
453        let payload = self.payload() & WRAPPER_INLINE_PAYLOAD_MASK;
454        match kind {
455            WRAPPER_INLINE_IMMEDIATE => Some(Self::encode(TAG_IMMEDIATE, payload)),
456            WRAPPER_INLINE_INT => Some(Self::new_int_inline(
457                Self::decode_wrapper_inline_int_payload(payload),
458            )),
459            WRAPPER_INLINE_NONE => Some(Self::NONE),
460            _ => None,
461        }
462    }
463
464    #[inline]
465    fn new_inline_wrapper(tag: u64, inline_kind: u64, payload: u64) -> Self {
466        debug_assert!(matches!(tag, TAG_SOME | TAG_OK | TAG_ERR));
467        debug_assert!(inline_kind <= WRAPPER_INLINE_NONE);
468        debug_assert!(payload <= WRAPPER_INLINE_PAYLOAD_MASK);
469        Self::encode(tag, (inline_kind << WRAPPER_INLINE_KIND_SHIFT) | payload)
470    }
471
472    #[inline]
473    pub fn wrapper_parts<T: ArenaTypes>(self, arena: &Arena<T>) -> Option<(u64, NanValue)> {
474        if !self.is_nan_boxed() {
475            return None;
476        }
477        match self.tag() {
478            TAG_SOME | TAG_OK | TAG_ERR if self.payload() & ARENA_REF_BIT != 0 => {
479                Some((self.wrapper_kind(), arena.get_boxed(self.arena_index())))
480            }
481            TAG_SOME | TAG_OK | TAG_ERR => self
482                .wrapper_inline_inner()
483                .map(|inner| (self.wrapper_kind(), inner)),
484            _ => None,
485        }
486    }
487
488    // -- Wrappers (Some/Ok/Err) -------------------------------------------
489
490    #[inline]
491    pub fn new_some(inner_index: u32) -> Self {
492        Self::encode(TAG_SOME, ARENA_REF_BIT | (inner_index as u64))
493    }
494
495    #[inline]
496    pub fn new_ok(inner_index: u32) -> Self {
497        Self::encode(TAG_OK, ARENA_REF_BIT | (inner_index as u64))
498    }
499
500    #[inline]
501    pub fn new_err(inner_index: u32) -> Self {
502        Self::encode(TAG_ERR, ARENA_REF_BIT | (inner_index as u64))
503    }
504
505    #[inline]
506    pub fn wrapper_index(self) -> u32 {
507        debug_assert!(
508            self.is_nan_boxed()
509                && matches!(self.tag(), TAG_SOME | TAG_OK | TAG_ERR)
510                && self.payload() & ARENA_REF_BIT != 0
511        );
512        self.arena_index()
513    }
514
515    #[inline]
516    pub fn wrapper_inner<T: ArenaTypes>(self, arena: &Arena<T>) -> NanValue {
517        self.wrapper_parts(arena)
518            .map(|(_, inner)| inner)
519            .expect("wrapper_inner() called on non-wrapper")
520    }
521
522    #[inline]
523    fn wrap_value<T: ArenaTypes>(kind: u64, inner: NanValue, arena: &mut Arena<T>) -> Self {
524        if let Some(payload) = inner.plain_immediate_payload() {
525            let tag = match kind {
526                WRAP_SOME => TAG_SOME,
527                WRAP_OK => TAG_OK,
528                WRAP_ERR => TAG_ERR,
529                _ => unreachable!("invalid wrapper kind"),
530            };
531            Self::new_inline_wrapper(tag, WRAPPER_INLINE_IMMEDIATE, payload)
532        } else if inner.is_none() {
533            let tag = match kind {
534                WRAP_SOME => TAG_SOME,
535                WRAP_OK => TAG_OK,
536                WRAP_ERR => TAG_ERR,
537                _ => unreachable!("invalid wrapper kind"),
538            };
539            Self::new_inline_wrapper(tag, WRAPPER_INLINE_NONE, 0)
540        } else if let Some(value) = inner.inline_int_value() {
541            if (WRAPPER_INT_INLINE_MIN..=WRAPPER_INT_INLINE_MAX).contains(&value) {
542                let tag = match kind {
543                    WRAP_SOME => TAG_SOME,
544                    WRAP_OK => TAG_OK,
545                    WRAP_ERR => TAG_ERR,
546                    _ => unreachable!("invalid wrapper kind"),
547                };
548                return Self::new_inline_wrapper(
549                    tag,
550                    WRAPPER_INLINE_INT,
551                    Self::encode_wrapper_inline_int(value),
552                );
553            }
554            let idx = arena.push_boxed(inner);
555            match kind {
556                WRAP_SOME => Self::new_some(idx),
557                WRAP_OK => Self::new_ok(idx),
558                WRAP_ERR => Self::new_err(idx),
559                _ => unreachable!("invalid wrapper kind"),
560            }
561        } else {
562            let idx = arena.push_boxed(inner);
563            match kind {
564                WRAP_SOME => Self::new_some(idx),
565                WRAP_OK => Self::new_ok(idx),
566                WRAP_ERR => Self::new_err(idx),
567                _ => unreachable!("invalid wrapper kind"),
568            }
569        }
570    }
571
572    #[inline]
573    pub fn new_some_value<T: ArenaTypes>(inner: NanValue, arena: &mut Arena<T>) -> Self {
574        Self::wrap_value(WRAP_SOME, inner, arena)
575    }
576
577    #[inline]
578    pub fn new_ok_value<T: ArenaTypes>(inner: NanValue, arena: &mut Arena<T>) -> Self {
579        Self::wrap_value(WRAP_OK, inner, arena)
580    }
581
582    #[inline]
583    pub fn new_err_value<T: ArenaTypes>(inner: NanValue, arena: &mut Arena<T>) -> Self {
584        Self::wrap_value(WRAP_ERR, inner, arena)
585    }
586
587    // -- Arena-backed constructors -----------------------------------------
588
589    #[inline]
590    pub fn new_string(arena_index: u32) -> Self {
591        Self::encode(TAG_STRING, STRING_ARENA_BIT | (arena_index as u64))
592    }
593
594    #[inline]
595    fn new_small_string_bytes(bytes: &[u8]) -> Self {
596        debug_assert!(bytes.len() <= STRING_INLINE_MAX_BYTES);
597        let mut payload = (bytes.len() as u64) << STRING_INLINE_LEN_SHIFT;
598        for (idx, byte) in bytes.iter().enumerate() {
599            payload |= (*byte as u64) << (idx * 8);
600        }
601        Self::encode(TAG_STRING, payload)
602    }
603
604    #[inline]
605    pub fn small_string(self) -> Option<NanString<'static>> {
606        if !self.is_nan_boxed()
607            || self.tag() != TAG_STRING
608            || self.payload() & STRING_ARENA_BIT != 0
609        {
610            return None;
611        }
612        let payload = self.payload();
613        let len = ((payload & STRING_INLINE_LEN_MASK) >> STRING_INLINE_LEN_SHIFT) as u8;
614        if len as usize > STRING_INLINE_MAX_BYTES {
615            return None;
616        }
617        let mut bytes = [0u8; STRING_INLINE_MAX_BYTES];
618        for (idx, slot) in bytes.iter_mut().take(len as usize).enumerate() {
619            *slot = ((payload >> (idx * 8)) & 0xFF) as u8;
620        }
621        Some(NanString::Inline { len, bytes })
622    }
623
624    #[inline]
625    pub fn new_string_value<T: ArenaTypes>(s: &str, arena: &mut Arena<T>) -> Self {
626        if s.len() <= STRING_INLINE_MAX_BYTES {
627            Self::new_small_string_bytes(s.as_bytes())
628        } else {
629            Self::new_string(arena.push_string(s))
630        }
631    }
632
633    #[inline]
634    pub fn new_list(arena_index: u32) -> Self {
635        Self::encode(TAG_LIST, ARENA_REF_BIT | (arena_index as u64))
636    }
637
638    #[inline]
639    pub fn new_tuple(arena_index: u32) -> Self {
640        Self::encode(TAG_TUPLE, ARENA_REF_BIT | (arena_index as u64))
641    }
642
643    #[inline]
644    pub fn new_map(arena_index: u32) -> Self {
645        Self::encode(TAG_MAP, ARENA_REF_BIT | (arena_index as u64))
646    }
647
648    #[inline]
649    pub fn new_vector(arena_index: u32) -> Self {
650        Self::encode(TAG_VECTOR, ARENA_REF_BIT | (arena_index as u64))
651    }
652
653    #[inline]
654    pub fn new_record(arena_index: u32) -> Self {
655        Self::encode(TAG_RECORD, ARENA_REF_BIT | (arena_index as u64))
656    }
657
658    #[inline]
659    pub fn new_variant(arena_index: u32) -> Self {
660        Self::encode(TAG_VARIANT, ARENA_REF_BIT | (arena_index as u64))
661    }
662
663    #[inline]
664    fn new_symbol(symbol_kind: u64, symbol_index: u32) -> Self {
665        Self::encode(TAG_SYMBOL, symbol_kind | ((symbol_index as u64) << 2))
666    }
667
668    #[inline]
669    pub fn symbol_kind(self) -> u64 {
670        debug_assert!(self.is_nan_boxed() && self.tag() == TAG_SYMBOL);
671        self.payload() & SYMBOL_KIND_MASK
672    }
673
674    #[inline]
675    pub fn symbol_index(self) -> u32 {
676        debug_assert!(self.is_nan_boxed() && self.tag() == TAG_SYMBOL);
677        (self.payload() >> 2) as u32
678    }
679
680    #[inline]
681    pub fn new_nullary_variant(symbol_index: u32) -> Self {
682        Self::new_symbol(SYMBOL_NULLARY_VARIANT, symbol_index)
683    }
684
685    #[inline]
686    pub fn try_new_inline_variant(ctor_id: u32, inner: NanValue) -> Option<Self> {
687        if ctor_id > IV_CTOR_MASK as u32 {
688            return None;
689        }
690        let ctor_bits = (ctor_id as u64) << IV_CTOR_SHIFT;
691
692        if inner.is_nan_boxed() {
693            match inner.tag() {
694                TAG_INT if inner.payload() & INT_BIG_BIT == 0 => {
695                    let i = Self::decode_inline_int_payload(inner.payload());
696                    if (IV_INT_MIN..=IV_INT_MAX).contains(&i) {
697                        let int_bits = (i as u64) & IV_INT_MASK;
698                        return Some(Self::encode(TAG_INLINE_VARIANT, ctor_bits | int_bits));
699                    }
700                }
701                TAG_IMMEDIATE => {
702                    let imm = match inner.payload() {
703                        IMM_FALSE => IV_IMM_FALSE,
704                        IMM_TRUE => IV_IMM_TRUE,
705                        IMM_UNIT => IV_IMM_UNIT,
706                        _ => return None,
707                    };
708                    return Some(Self::encode(
709                        TAG_INLINE_VARIANT,
710                        ctor_bits | IV_KIND_BIT | (imm << IV_IMM_SHIFT),
711                    ));
712                }
713                TAG_NONE => {
714                    return Some(Self::encode(
715                        TAG_INLINE_VARIANT,
716                        ctor_bits | IV_KIND_BIT | (IV_IMM_NONE << IV_IMM_SHIFT),
717                    ));
718                }
719                _ => {}
720            }
721        }
722        None
723    }
724
725    #[inline]
726    pub fn inline_variant_ctor_id(self) -> u32 {
727        debug_assert!(self.is_nan_boxed() && self.tag() == TAG_INLINE_VARIANT);
728        ((self.payload() >> IV_CTOR_SHIFT) & IV_CTOR_MASK) as u32
729    }
730
731    #[inline]
732    pub fn inline_variant_inner(self) -> NanValue {
733        debug_assert!(self.is_nan_boxed() && self.tag() == TAG_INLINE_VARIANT);
734        let payload = self.payload();
735        if payload & IV_KIND_BIT == 0 {
736            let raw = payload & IV_INT_MASK;
737            let i = if raw & IV_INT_SIGN_BIT != 0 {
738                (raw | !IV_INT_MASK) as i64
739            } else {
740                raw as i64
741            };
742            Self::new_int_inline(i)
743        } else {
744            let imm = (payload >> IV_IMM_SHIFT) & 0b11;
745            match imm {
746                IV_IMM_FALSE => Self::FALSE,
747                IV_IMM_TRUE => Self::TRUE,
748                IV_IMM_UNIT => Self::UNIT,
749                IV_IMM_NONE => Self::NONE,
750                _ => unreachable!(),
751            }
752        }
753    }
754
755    #[inline]
756    pub fn new_fn(arena_index: u32) -> Self {
757        Self::new_symbol(SYMBOL_FN, arena_index)
758    }
759
760    #[inline]
761    pub fn new_builtin(arena_index: u32) -> Self {
762        Self::new_symbol(SYMBOL_BUILTIN, arena_index)
763    }
764
765    #[inline]
766    pub fn new_namespace(arena_index: u32) -> Self {
767        Self::new_symbol(SYMBOL_NAMESPACE, arena_index)
768    }
769
770    #[inline]
771    pub fn arena_index(self) -> u32 {
772        (self.payload() & !ARENA_REF_BIT) as u32
773    }
774
775    #[inline]
776    pub fn heap_index(self) -> Option<u32> {
777        if !self.is_nan_boxed() {
778            return None;
779        }
780        match self.tag() {
781            TAG_INT => {
782                let p = self.payload();
783                if p & INT_BIG_BIT != 0 {
784                    Some((p & !INT_BIG_BIT) as u32)
785                } else {
786                    None
787                }
788            }
789            TAG_STRING | TAG_SOME | TAG_OK | TAG_ERR | TAG_LIST | TAG_TUPLE | TAG_MAP
790            | TAG_RECORD | TAG_VARIANT | TAG_VECTOR => {
791                (self.payload() & ARENA_REF_BIT != 0).then_some(self.arena_index())
792            }
793            _ => None,
794        }
795    }
796
797    #[inline]
798    pub fn with_heap_index(self, index: u32) -> Self {
799        if !self.is_nan_boxed() {
800            return self;
801        }
802        match self.tag() {
803            TAG_INT => {
804                debug_assert!(self.payload() & INT_BIG_BIT != 0);
805                Self::new_int_arena(index)
806            }
807            TAG_SOME => Self::new_some(index),
808            TAG_OK => Self::new_ok(index),
809            TAG_ERR => Self::new_err(index),
810            TAG_STRING => Self::new_string(index),
811            TAG_LIST => Self::new_list(index),
812            TAG_TUPLE => Self::new_tuple(index),
813            TAG_MAP => Self::new_map(index),
814            TAG_VECTOR => Self::new_vector(index),
815            TAG_RECORD => Self::new_record(index),
816            TAG_VARIANT => Self::new_variant(index),
817            _ => self,
818        }
819    }
820
821    // -- Type checks -------------------------------------------------------
822
823    #[inline]
824    pub fn is_float(self) -> bool {
825        !self.is_nan_boxed()
826    }
827
828    #[inline]
829    pub fn is_int(self) -> bool {
830        self.is_nan_boxed() && self.tag() == TAG_INT
831    }
832
833    #[inline]
834    pub fn is_bool(self) -> bool {
835        self.is_nan_boxed()
836            && self.tag() == TAG_IMMEDIATE
837            && (self.payload() == IMM_TRUE || self.payload() == IMM_FALSE)
838    }
839
840    #[inline]
841    pub fn is_unit(self) -> bool {
842        self.0 == Self::UNIT.0
843    }
844
845    #[inline]
846    pub fn is_none(self) -> bool {
847        self.0 == Self::NONE.0
848    }
849
850    #[inline]
851    pub fn is_some(self) -> bool {
852        self.is_nan_boxed() && self.tag() == TAG_SOME
853    }
854
855    #[inline]
856    pub fn is_ok(self) -> bool {
857        self.is_nan_boxed() && self.tag() == TAG_OK
858    }
859
860    #[inline]
861    pub fn is_err(self) -> bool {
862        self.is_nan_boxed() && self.tag() == TAG_ERR
863    }
864
865    #[inline]
866    pub fn is_string(self) -> bool {
867        self.is_nan_boxed() && self.tag() == TAG_STRING
868    }
869
870    pub fn string_eq<T: ArenaTypes>(self, other: NanValue, arena: &Arena<T>) -> bool {
871        if self.bits() == other.bits() {
872            return true;
873        }
874        if !self.is_string() || !other.is_string() {
875            return false;
876        }
877        arena.get_string_value(self).as_str() == arena.get_string_value(other).as_str()
878    }
879
880    #[inline]
881    pub fn is_list(self) -> bool {
882        self.is_nan_boxed() && self.tag() == TAG_LIST
883    }
884
885    #[inline]
886    pub fn is_record(self) -> bool {
887        self.is_nan_boxed() && self.tag() == TAG_RECORD
888    }
889
890    #[inline]
891    pub fn is_fn(self) -> bool {
892        self.is_nan_boxed() && self.tag() == TAG_SYMBOL && self.symbol_kind() == SYMBOL_FN
893    }
894
895    #[inline]
896    pub fn is_variant(self) -> bool {
897        self.is_nan_boxed()
898            && (self.tag() == TAG_VARIANT
899                || self.tag() == TAG_INLINE_VARIANT
900                || (self.tag() == TAG_SYMBOL && self.symbol_kind() == SYMBOL_NULLARY_VARIANT))
901    }
902
903    #[inline]
904    pub fn is_inline_variant(self) -> bool {
905        self.is_nan_boxed() && self.tag() == TAG_INLINE_VARIANT
906    }
907
908    #[inline]
909    pub fn is_map(self) -> bool {
910        self.is_nan_boxed() && self.tag() == TAG_MAP
911    }
912
913    #[inline]
914    pub fn is_vector(self) -> bool {
915        self.is_nan_boxed() && self.tag() == TAG_VECTOR
916    }
917
918    #[inline]
919    pub fn is_empty_vector_immediate(self) -> bool {
920        self.is_nan_boxed() && self.tag() == TAG_VECTOR && self.payload() & ARENA_REF_BIT == 0
921    }
922
923    #[inline]
924    pub fn is_tuple(self) -> bool {
925        self.is_nan_boxed() && self.tag() == TAG_TUPLE
926    }
927
928    #[inline]
929    pub fn is_builtin(self) -> bool {
930        self.is_nan_boxed() && self.tag() == TAG_SYMBOL && self.symbol_kind() == SYMBOL_BUILTIN
931    }
932
933    #[inline]
934    pub fn is_namespace(self) -> bool {
935        self.is_nan_boxed() && self.tag() == TAG_SYMBOL && self.symbol_kind() == SYMBOL_NAMESPACE
936    }
937
938    #[inline]
939    pub fn is_empty_list_immediate(self) -> bool {
940        self.is_nan_boxed() && self.tag() == TAG_LIST && self.payload() & ARENA_REF_BIT == 0
941    }
942
943    #[inline]
944    pub fn is_empty_map_immediate(self) -> bool {
945        self.is_nan_boxed() && self.tag() == TAG_MAP && self.payload() & ARENA_REF_BIT == 0
946    }
947
948    pub fn type_name(self) -> &'static str {
949        if self.is_float() {
950            return "Float";
951        }
952        match self.tag() {
953            TAG_INT => "Int",
954            TAG_IMMEDIATE => match self.payload() {
955                IMM_FALSE | IMM_TRUE => "Bool",
956                IMM_UNIT => "Unit",
957                _ => "Unknown",
958            },
959            TAG_SOME => "Option.Some",
960            TAG_NONE => "Option.None",
961            TAG_OK => "Result.Ok",
962            TAG_ERR => "Result.Err",
963            TAG_STRING => "String",
964            TAG_LIST => "List",
965            TAG_TUPLE => "Tuple",
966            TAG_MAP => "Map",
967            TAG_VECTOR => "Vector",
968            TAG_RECORD => "Record",
969            TAG_VARIANT | TAG_INLINE_VARIANT => "Variant",
970            TAG_SYMBOL => match self.symbol_kind() {
971                SYMBOL_FN => "Fn",
972                SYMBOL_BUILTIN => "Builtin",
973                SYMBOL_NAMESPACE => "Namespace",
974                SYMBOL_NULLARY_VARIANT => "Variant",
975                _ => "Unknown",
976            },
977            _ => "Unknown",
978        }
979    }
980
981    #[inline]
982    pub fn variant_ctor_id<T: ArenaTypes>(self, arena: &Arena<T>) -> Option<u32> {
983        if !self.is_nan_boxed() {
984            return None;
985        }
986        match self.tag() {
987            TAG_INLINE_VARIANT => Some(self.inline_variant_ctor_id()),
988            TAG_VARIANT => {
989                let (type_id, variant_id, _) = arena.get_variant(self.arena_index());
990                arena.find_ctor_id(type_id, variant_id)
991            }
992            TAG_SYMBOL if self.symbol_kind() == SYMBOL_NULLARY_VARIANT => {
993                Some(arena.get_nullary_variant_ctor(self.symbol_index()))
994            }
995            _ => None,
996        }
997    }
998
999    #[inline]
1000    pub fn variant_parts<T: ArenaTypes>(self, arena: &Arena<T>) -> Option<(u32, u16, &[NanValue])> {
1001        if !self.is_nan_boxed() {
1002            return None;
1003        }
1004        match self.tag() {
1005            TAG_VARIANT => {
1006                let (type_id, variant_id, fields) = arena.get_variant(self.arena_index());
1007                Some((type_id, variant_id, fields))
1008            }
1009            TAG_SYMBOL if self.symbol_kind() == SYMBOL_NULLARY_VARIANT => {
1010                let (type_id, variant_id) =
1011                    arena.get_ctor_parts(arena.get_nullary_variant_ctor(self.symbol_index()));
1012                Some((type_id, variant_id, &[]))
1013            }
1014            _ => None,
1015        }
1016    }
1017
1018    #[inline]
1019    pub fn variant_single_field<T: ArenaTypes>(self, arena: &Arena<T>) -> NanValue {
1020        if self.tag() == TAG_INLINE_VARIANT {
1021            self.inline_variant_inner()
1022        } else {
1023            let (_, _, fields) = arena.get_variant(self.arena_index());
1024            debug_assert_eq!(fields.len(), 1);
1025            fields[0]
1026        }
1027    }
1028
1029    #[inline]
1030    pub fn inline_variant_info<T: ArenaTypes>(
1031        self,
1032        arena: &Arena<T>,
1033    ) -> Option<(u32, u16, NanValue)> {
1034        if !self.is_nan_boxed() || self.tag() != TAG_INLINE_VARIANT {
1035            return None;
1036        }
1037        let ctor_id = self.inline_variant_ctor_id();
1038        let (type_id, variant_id) = arena.get_ctor_parts(ctor_id);
1039        Some((type_id, variant_id, self.inline_variant_inner()))
1040    }
1041
1042    #[inline]
1043    pub fn bits(self) -> u64 {
1044        self.0
1045    }
1046
1047    #[inline]
1048    pub fn from_bits(bits: u64) -> Self {
1049        NanValue(bits)
1050    }
1051
1052    pub fn map_key_hash<T: ArenaTypes>(self, arena: &Arena<T>) -> u64 {
1053        if self.is_string() {
1054            use core::hash::{Hash, Hasher};
1055            let mut hasher = DefaultHasher::new();
1056            3u8.hash(&mut hasher);
1057            arena.get_string_value(self).hash(&mut hasher);
1058            hasher.finish()
1059        } else if self.is_int() && self.heap_index().is_some() {
1060            // An arena-backed int (i64-overflow or ℤ-overflow) carries the
1061            // arena INDEX in its bits, not the value — two equal ints at
1062            // different slots would mis-key. Hash the value structurally.
1063            // Inline ints encode the value directly, so they skip this.
1064            self.map_key_hash_deep(arena)
1065        } else {
1066            self.bits()
1067        }
1068    }
1069
1070    /// Structural hash that respects `eq_in` for every value shape:
1071    /// two equal heap values (variants/tuples/records/lists/etc.) always
1072    /// produce the same u64 regardless of arena layout. Used by Map
1073    /// when the key type is anything beyond inline scalars.
1074    pub fn map_key_hash_deep<T: ArenaTypes>(self, arena: &Arena<T>) -> u64 {
1075        use core::hash::Hasher;
1076        let mut hasher = DefaultHasher::new();
1077        self.hash_in(&mut hasher, arena);
1078        hasher.finish()
1079    }
1080}
1081
1082// -- Debug -----------------------------------------------------------------
1083
1084impl core::fmt::Debug for NanValue {
1085    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1086        if self.is_float() {
1087            return write!(f, "Float({})", self.as_float());
1088        }
1089        match self.tag() {
1090            TAG_INT => {
1091                if self.payload() & INT_BIG_BIT != 0 {
1092                    write!(f, "Int(arena:{})", (self.payload() & !INT_BIG_BIT) as u32)
1093                } else {
1094                    write!(
1095                        f,
1096                        "Int({})",
1097                        Self::decode_inline_int_payload(self.payload())
1098                    )
1099                }
1100            }
1101            TAG_IMMEDIATE => match self.payload() {
1102                IMM_FALSE => write!(f, "False"),
1103                IMM_TRUE => write!(f, "True"),
1104                IMM_UNIT => write!(f, "Unit"),
1105                _ => write!(f, "Immediate({})", self.payload()),
1106            },
1107            TAG_NONE => write!(f, "None"),
1108            TAG_SOME | TAG_OK | TAG_ERR => {
1109                let kind = match self.tag() {
1110                    TAG_SOME => "Some",
1111                    TAG_OK => "Ok",
1112                    TAG_ERR => "Err",
1113                    _ => "?",
1114                };
1115                if self.payload() & ARENA_REF_BIT != 0 {
1116                    write!(f, "{}(arena:{})", kind, self.arena_index())
1117                } else if let Some(inner) = self.wrapper_inline_inner() {
1118                    write!(f, "{}({:?})", kind, inner)
1119                } else {
1120                    write!(f, "{}(?)", kind)
1121                }
1122            }
1123            TAG_SYMBOL => match self.symbol_kind() {
1124                SYMBOL_FN => write!(f, "Fn(symbol:{})", self.symbol_index()),
1125                SYMBOL_BUILTIN => write!(f, "Builtin(symbol:{})", self.symbol_index()),
1126                SYMBOL_NAMESPACE => write!(f, "Namespace(symbol:{})", self.symbol_index()),
1127                SYMBOL_NULLARY_VARIANT => {
1128                    write!(f, "NullaryVariant(symbol:{})", self.symbol_index())
1129                }
1130                _ => write!(f, "Symbol({})", self.payload()),
1131            },
1132            TAG_STRING => {
1133                if let Some(s) = self.small_string() {
1134                    write!(f, "String({:?})", s.as_str())
1135                } else {
1136                    write!(f, "String(arena:{})", self.arena_index())
1137                }
1138            }
1139            TAG_INLINE_VARIANT => {
1140                let ctor = self.inline_variant_ctor_id();
1141                let inner = self.inline_variant_inner();
1142                write!(f, "InlineVariant(ctor:{}, {:?})", ctor, inner)
1143            }
1144            TAG_LIST if self.is_empty_list_immediate() => write!(f, "EmptyList"),
1145            TAG_MAP if self.is_empty_map_immediate() => write!(f, "EmptyMap"),
1146            TAG_VECTOR if self.is_empty_vector_immediate() => write!(f, "EmptyVector"),
1147            _ => write!(f, "{}(arena:{})", self.type_name(), self.arena_index()),
1148        }
1149    }
1150}
1151
1152// ---------------------------------------------------------------------------
1153// A simple DefaultHasher for no_std — mirrors std::collections::hash_map::DefaultHasher
1154// ---------------------------------------------------------------------------
1155
1156/// SipHasher-like hasher for use in no_std contexts.
1157/// When std is available, delegates to `std::collections::hash_map::DefaultHasher`.
1158struct DefaultHasher {
1159    #[cfg(feature = "std")]
1160    inner: std::collections::hash_map::DefaultHasher,
1161    #[cfg(not(feature = "std"))]
1162    state: u64,
1163}
1164
1165impl DefaultHasher {
1166    fn new() -> Self {
1167        #[cfg(feature = "std")]
1168        {
1169            Self {
1170                inner: std::collections::hash_map::DefaultHasher::new(),
1171            }
1172        }
1173        #[cfg(not(feature = "std"))]
1174        {
1175            Self {
1176                state: 0xcbf29ce484222325,
1177            }
1178        }
1179    }
1180}
1181
1182impl Hasher for DefaultHasher {
1183    #[cfg(feature = "std")]
1184    fn finish(&self) -> u64 {
1185        self.inner.finish()
1186    }
1187    #[cfg(feature = "std")]
1188    fn write(&mut self, bytes: &[u8]) {
1189        self.inner.write(bytes)
1190    }
1191
1192    #[cfg(not(feature = "std"))]
1193    fn finish(&self) -> u64 {
1194        self.state
1195    }
1196    #[cfg(not(feature = "std"))]
1197    fn write(&mut self, bytes: &[u8]) {
1198        for &b in bytes {
1199            self.state ^= b as u64;
1200            self.state = self.state.wrapping_mul(0x100000001b3);
1201        }
1202    }
1203}
1204
1205// ---------------------------------------------------------------------------
1206// Arena
1207// ---------------------------------------------------------------------------
1208
1209#[derive(Debug, Clone)]
1210pub struct Arena<T: ArenaTypes> {
1211    young_entries: Vec<ArenaEntry<T>>,
1212    yard_entries: Vec<ArenaEntry<T>>,
1213    handoff_entries: Vec<ArenaEntry<T>>,
1214    stable_entries: Vec<ArenaEntry<T>>,
1215    scratch_young: Vec<u32>,
1216    scratch_yard: Vec<u32>,
1217    scratch_handoff: Vec<u32>,
1218    scratch_stable: Vec<u32>,
1219    peak_usage: ArenaUsage,
1220    alloc_space: AllocSpace,
1221    pub type_names: Vec<String>,
1222    pub type_field_names: Vec<Vec<String>>,
1223    pub type_variant_names: Vec<Vec<String>>,
1224    pub type_variant_ctor_ids: Vec<Vec<u32>>,
1225    pub ctor_to_type_variant: Vec<(u32, u16)>,
1226    pub symbol_entries: Vec<ArenaSymbol<T>>,
1227    /// Qualified-name aliases for types (e.g. "Data.Shape" → type_id for "Shape").
1228    pub type_aliases: Vec<(String, u32)>,
1229}
1230
1231#[derive(Debug, Clone)]
1232pub enum ArenaEntry<T: ArenaTypes> {
1233    /// An `i64`-fitting integer that overflowed the 45-bit inline NaN-box.
1234    /// Also the cheap zero-filler used by the GC during compaction.
1235    Int(i64),
1236    /// An integer outside the `i64` range (mathematical ℤ overflow slot).
1237    /// Always holds a value not representable as `i64`, so the runtime's
1238    /// canonical-form invariant holds across inline / arena-i64 / arena-big.
1239    BigInt(Box<num_bigint::BigInt>),
1240    String(Rc<str>),
1241    List(ArenaList),
1242    Tuple(Vec<NanValue>),
1243    Map(T::Map),
1244    Vector(Vec<NanValue>),
1245    Record {
1246        type_id: u32,
1247        fields: Vec<NanValue>,
1248    },
1249    Variant {
1250        type_id: u32,
1251        variant_id: u16,
1252        fields: Vec<NanValue>,
1253    },
1254    Fn(Rc<T::Fn>),
1255    Builtin(Rc<str>),
1256    Namespace {
1257        name: Rc<str>,
1258        members: Vec<(Rc<str>, NanValue)>,
1259    },
1260    Boxed(NanValue),
1261}
1262
1263/// A borrowed view of an arena-stored integer, discriminating the
1264/// `i64`-overflow slot from the ℤ-overflow slot without materializing.
1265/// The runtime crate reconstructs a canonical `AverInt` from this.
1266#[derive(Debug, Clone, Copy)]
1267pub enum ArenaIntRef<'a> {
1268    Small(i64),
1269    Big(&'a num_bigint::BigInt),
1270}
1271
1272#[derive(Debug, Clone)]
1273pub enum ArenaSymbol<T: ArenaTypes> {
1274    Fn(Rc<T::Fn>),
1275    Builtin(Rc<str>),
1276    Namespace {
1277        name: Rc<str>,
1278        members: Vec<(Rc<str>, NanValue)>,
1279    },
1280    NullaryVariant {
1281        ctor_id: u32,
1282    },
1283}
1284
1285#[derive(Debug, Clone)]
1286pub enum ArenaList {
1287    Flat {
1288        items: Rc<Vec<NanValue>>,
1289        start: usize,
1290    },
1291    Prepend {
1292        head: NanValue,
1293        tail: NanValue,
1294        len: usize,
1295    },
1296    Concat {
1297        left: NanValue,
1298        right: NanValue,
1299        len: usize,
1300    },
1301    Segments {
1302        current: NanValue,
1303        rest: Rc<Vec<NanValue>>,
1304        start: usize,
1305        len: usize,
1306    },
1307}
1308
1309const LIST_APPEND_CHUNK_LIMIT: usize = 128;
1310
1311#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1312pub(crate) enum HeapSpace {
1313    Young = 0,
1314    Yard = 1,
1315    Handoff = 2,
1316    Stable = 3,
1317}
1318
1319#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1320pub enum AllocSpace {
1321    Young,
1322    Yard,
1323    Handoff,
1324}
1325
1326#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
1327pub struct ArenaUsage {
1328    pub young: usize,
1329    pub yard: usize,
1330    pub handoff: usize,
1331    pub stable: usize,
1332}
1333
1334impl ArenaUsage {
1335    pub fn total(self) -> usize {
1336        self.young + self.yard + self.handoff + self.stable
1337    }
1338}
1339
1340pub(crate) const HEAP_SPACE_SHIFT: u32 = 30;
1341pub(crate) const HEAP_SPACE_MASK_U32: u32 = 0b11 << HEAP_SPACE_SHIFT;
1342pub(crate) const HEAP_INDEX_MASK_U32: u32 = (1 << HEAP_SPACE_SHIFT) - 1;
1343
1344mod arena;
1345mod compare;
1346mod lists;
1347mod memory;
1348
1349// ---------------------------------------------------------------------------
1350// Feature-gated MapLike impl for aver_rt::AverMap
1351// ---------------------------------------------------------------------------
1352
1353#[cfg(feature = "runtime")]
1354/// `PersistentMap` type alias used by the VM arena.
1355pub type PersistentMap = aver_rt::AverMap<u64, (NanValue, NanValue)>;
1356
1357#[cfg(feature = "runtime")]
1358impl MapLike for aver_rt::AverMap<u64, (NanValue, NanValue)> {
1359    fn new() -> Self {
1360        aver_rt::AverMap::new()
1361    }
1362
1363    fn get(&self, key: &u64) -> Option<&(NanValue, NanValue)> {
1364        aver_rt::AverMap::get(self, key)
1365    }
1366
1367    fn insert(&self, key: u64, value: (NanValue, NanValue)) -> Self {
1368        aver_rt::AverMap::insert(self, key, value)
1369    }
1370
1371    fn insert_owned(self, key: u64, value: (NanValue, NanValue)) -> Self {
1372        aver_rt::AverMap::insert_owned(self, key, value)
1373    }
1374
1375    fn rewrite_values_mut(&mut self, f: impl FnMut(&mut (NanValue, NanValue))) {
1376        self.rewrite_values_in_place(f)
1377    }
1378
1379    fn len(&self) -> usize {
1380        aver_rt::AverMap::len(self)
1381    }
1382
1383    fn is_empty(&self) -> bool {
1384        aver_rt::AverMap::is_empty(self)
1385    }
1386
1387    fn iter(&self) -> impl Iterator<Item = (&u64, &(NanValue, NanValue))> {
1388        aver_rt::AverMap::iter(self)
1389    }
1390
1391    fn values(&self) -> impl Iterator<Item = &(NanValue, NanValue)> {
1392        aver_rt::AverMap::values(self)
1393    }
1394}
1395
1396// ---------------------------------------------------------------------------
1397// Stub PersistentMap when runtime is off (BTreeMap-based)
1398// ---------------------------------------------------------------------------
1399
1400#[cfg(not(feature = "runtime"))]
1401/// Stub `PersistentMap` for non-runtime builds (e.g. wasm-compile only).
1402#[derive(Clone, Debug)]
1403pub struct PersistentMap(alloc::collections::BTreeMap<u64, (NanValue, NanValue)>);
1404
1405#[cfg(not(feature = "runtime"))]
1406impl MapLike for PersistentMap {
1407    fn new() -> Self {
1408        PersistentMap(alloc::collections::BTreeMap::new())
1409    }
1410
1411    fn get(&self, key: &u64) -> Option<&(NanValue, NanValue)> {
1412        self.0.get(key)
1413    }
1414
1415    fn insert(&self, key: u64, value: (NanValue, NanValue)) -> Self {
1416        let mut m = self.0.clone();
1417        m.insert(key, value);
1418        PersistentMap(m)
1419    }
1420
1421    fn rewrite_values_mut(&mut self, mut f: impl FnMut(&mut (NanValue, NanValue))) {
1422        for value in self.0.values_mut() {
1423            f(value);
1424        }
1425    }
1426
1427    fn len(&self) -> usize {
1428        self.0.len()
1429    }
1430
1431    fn is_empty(&self) -> bool {
1432        self.0.is_empty()
1433    }
1434
1435    fn iter(&self) -> impl Iterator<Item = (&u64, &(NanValue, NanValue))> {
1436        self.0.iter()
1437    }
1438
1439    fn values(&self) -> impl Iterator<Item = &(NanValue, NanValue)> {
1440        self.0.values()
1441    }
1442}