Skip to main content

icydb_schema/
nat_big.rs

1//! Canonical arbitrary-precision unsigned-integer atom.
2
3use crate::{
4    Decimal, NumericValue,
5    integer_wire::{self, IntegerWire},
6};
7use candid::{CandidType, Nat as WrappedNat};
8use derive_more::{Add, AddAssign, Sub, SubAssign};
9use num_bigint::BigUint;
10use serde::{Deserialize, Serialize};
11use std::{
12    fmt,
13    iter::{Product, Sum},
14    ops::{Div, DivAssign, Mul, MulAssign},
15    str::FromStr,
16};
17
18//
19// NatBig
20//
21
22#[derive(
23    Add,
24    AddAssign,
25    CandidType,
26    Clone,
27    Debug,
28    Default,
29    Eq,
30    PartialEq,
31    Hash,
32    Ord,
33    PartialOrd,
34    Sub,
35    SubAssign,
36)]
37/// Arbitrary-precision unsigned integer used by schema and typed values.
38///
39/// Candid uses its native integer type; human-readable Serde uses decimal text.
40/// Binary Serde uses native small integers and tagged little-endian wide bytes.
41pub struct NatBig(WrappedNat);
42
43impl NatBig {
44    /// Return the magnitude's bit length without allocating an encoded copy.
45    #[must_use]
46    pub fn magnitude_bits(&self) -> u64 {
47        self.0.0.bits()
48    }
49
50    /// Return the exact unsigned LEB128 byte length without allocating or encoding.
51    #[must_use]
52    pub fn leb128_len(&self) -> u64 {
53        self.magnitude_bits().div_ceil(7).max(1)
54    }
55
56    /// Construct from the canonical Candid natural-number representation.
57    #[must_use]
58    pub const fn from_candid(value: WrappedNat) -> Self {
59        Self(value)
60    }
61
62    /// Construct from a `num_bigint` unsigned integer.
63    #[must_use]
64    pub fn from_biguint(value: BigUint) -> Self {
65        Self::from_candid(WrappedNat::from(value))
66    }
67
68    /// Borrow little-endian base-2^32 limbs without allocation.
69    #[must_use]
70    pub fn u32_digits(&self) -> impl DoubleEndedIterator<Item = u32> + ExactSizeIterator + '_ {
71        self.0.0.iter_u32_digits()
72    }
73
74    /// Convert to `u128` when the value is in range.
75    #[must_use]
76    pub fn to_u128(&self) -> Option<u128> {
77        let big = &self.0.0;
78
79        u128::try_from(big).ok()
80    }
81
82    /// Convert to `u64` when the value is in range.
83    #[must_use]
84    pub fn to_u64(&self) -> Option<u64> {
85        let big = &self.0.0;
86
87        u64::try_from(big).ok()
88    }
89
90    /// Serialize this arbitrary-precision natural for internal hash and sort-key framing.
91    #[must_use]
92    pub fn to_leb128(&self) -> Vec<u8> {
93        self.leb128_bytes().collect()
94    }
95
96    /// Iterate canonical unsigned LEB128 bytes with constant scratch and no allocation.
97    pub fn leb128_bytes(&self) -> impl Iterator<Item = u8> + '_ {
98        crate::leb128::bytes(self.u32_digits(), false, self.leb128_len())
99    }
100
101    pub(crate) fn to_magnitude_bytes(&self) -> Vec<u8> {
102        self.0.0.to_bytes_be()
103    }
104
105    pub(crate) fn from_magnitude_bytes(magnitude: &[u8]) -> Self {
106        Self::from_biguint(BigUint::from_bytes_be(magnitude))
107    }
108
109    /// Saturating addition (unbounded; equivalent to normal addition).
110    #[must_use]
111    pub fn saturating_add(self, rhs: Self) -> Self {
112        Self(self.0 + rhs.0)
113    }
114
115    /// Saturating subtraction; clamps at zero on underflow.
116    #[must_use]
117    pub fn saturating_sub(self, rhs: Self) -> Self {
118        if rhs > self {
119            return Self::default();
120        }
121
122        Self(self.0 - rhs.0)
123    }
124}
125
126impl<'de> Deserialize<'de> for NatBig {
127    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
128        integer_wire::deserialize_integer(deserializer)
129    }
130}
131
132impl fmt::Display for NatBig {
133    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
134        self.0.fmt(f)
135    }
136}
137
138impl FromStr for NatBig {
139    type Err = <WrappedNat as FromStr>::Err;
140
141    fn from_str(s: &str) -> Result<Self, Self::Err> {
142        WrappedNat::from_str(s).map(Self::from_candid)
143    }
144}
145
146impl Div for NatBig {
147    type Output = Self;
148
149    fn div(self, other: Self) -> Self::Output {
150        Self(self.0 / other.0)
151    }
152}
153
154impl DivAssign for NatBig {
155    fn div_assign(&mut self, other: Self) {
156        self.0 /= other.0;
157    }
158}
159
160impl From<u64> for NatBig {
161    fn from(n: u64) -> Self {
162        Self::from_candid(WrappedNat::from(n))
163    }
164}
165
166impl From<u32> for NatBig {
167    fn from(n: u32) -> Self {
168        Self::from_candid(WrappedNat::from(n))
169    }
170}
171
172impl IntegerWire for NatBig {
173    fn from_signed(value: i64) -> Option<Self> {
174        u64::try_from(value).ok().map(Self::from)
175    }
176
177    fn from_unsigned(value: u64) -> Self {
178        Self::from(value)
179    }
180
181    fn from_wire_bytes(value: &[u8]) -> Option<Self> {
182        integer_wire::unsigned_body(value)
183            .map(|body| Self::from_biguint(BigUint::from_bytes_le(body)))
184    }
185}
186
187impl Mul for NatBig {
188    type Output = Self;
189
190    fn mul(self, other: Self) -> Self::Output {
191        Self(self.0 * other.0)
192    }
193}
194
195impl MulAssign for NatBig {
196    fn mul_assign(&mut self, other: Self) {
197        self.0 *= other.0;
198    }
199}
200
201impl NumericValue for NatBig {
202    fn try_to_decimal(&self) -> Option<Decimal> {
203        self.to_u128().and_then(Decimal::from_u128)
204    }
205
206    fn try_from_decimal(value: Decimal) -> Option<Self> {
207        value.to_u128().map(WrappedNat::from).map(Self::from_candid)
208    }
209}
210
211impl Product for NatBig {
212    fn product<I: Iterator<Item = Self>>(iter: I) -> Self {
213        iter.fold(Self::from(1_u32), |acc, value| acc * value)
214    }
215}
216
217impl Serialize for NatBig {
218    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
219        if serializer.is_human_readable() {
220            return serializer.collect_str(&self.0.0);
221        }
222        if let Some(value) = self.to_u64() {
223            return serializer.serialize_u64(value);
224        }
225        serializer.serialize_bytes(&integer_wire::unsigned_bytes(self.u32_digits()))
226    }
227}
228
229impl Sum for NatBig {
230    fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
231        iter.fold(Self::default(), |acc, x| acc + x)
232    }
233}
234
235impl TryFrom<i32> for NatBig {
236    type Error = std::num::TryFromIntError;
237
238    fn try_from(n: i32) -> Result<Self, Self::Error> {
239        let v = Self::from_candid(WrappedNat::from(u32::try_from(n)?));
240        Ok(v)
241    }
242}