aver/nan_value/int_ext.rs
1//! Bridge between the storage-layer `NanValue`/`Arena` (in `aver_memory`)
2//! and the runtime's arbitrary-precision integer `AverInt` (in `aver_rt`).
3//!
4//! `aver_memory` deliberately does not depend on `aver_rt`: it stores the raw
5//! `num_bigint::BigInt` payload in the ℤ-overflow arena slot and exposes it as
6//! an `ArenaIntRef`. This trait, defined on the `aver_lang` side (which sees
7//! both crates), reconstructs the canonical `AverInt` and writes it back,
8//! preserving the small-int fast path: an `i64`-fitting value never allocates.
9
10use aver_rt::AverInt;
11use num_bigint::BigInt;
12
13use super::{Arena, NanValue};
14use aver_memory::ArenaIntRef;
15
16/// Read/write `AverInt` values through a `NanValue` integer.
17pub trait NanIntExt {
18 /// Materialize the integer as a canonical `AverInt`. Inline and
19 /// `i64`-overflow slots become `Small`; the ℤ-overflow slot becomes `Big`.
20 // Takes `self` by value because `NanValue` is `Copy` (8 bytes), matching
21 // the existing `NanValue::as_int(self, ...)` / `as_float(self)` convention.
22 #[allow(clippy::wrong_self_convention)]
23 fn as_aver_int(self, arena: &Arena) -> AverInt;
24
25 /// Store an `AverInt`, keeping the representation canonical: a `Small`
26 /// goes through the inline / `i64`-overflow path, a `Big` is boxed into
27 /// the arena. Only values genuinely outside `i64` allocate.
28 fn from_aver_int(value: AverInt, arena: &mut Arena) -> NanValue;
29}
30
31impl NanIntExt for NanValue {
32 #[inline]
33 fn as_aver_int(self, arena: &Arena) -> AverInt {
34 match self.int_ref(arena) {
35 ArenaIntRef::Small(n) => AverInt::from_i64(n),
36 ArenaIntRef::Big(b) => big_to_aver_int(b),
37 }
38 }
39
40 #[inline]
41 fn from_aver_int(value: AverInt, arena: &mut Arena) -> NanValue {
42 match value {
43 AverInt::Small(n) => NanValue::new_int(n, arena),
44 AverInt::Big(b) => NanValue::new_big_int(*b, arena),
45 }
46 }
47}
48
49/// Rebuild an `AverInt` from an arena `BigInt`. The arena only ever stores a
50/// `BigInt` when it does not fit `i64`, so this is normally `Big` — but route
51/// through `AverInt::from_bigint` so an in-range payload (e.g. a stale slot)
52/// canonicalizes to `Small`, never a non-canonical `Big`.
53#[inline]
54fn big_to_aver_int(b: &BigInt) -> AverInt {
55 AverInt::from_bigint(b.clone())
56}