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