Skip to main content

icydb_schema/
int_big.rs

1//! Canonical arbitrary-precision signed-integer atom.
2
3use crate::{Decimal, NumericValue};
4use candid::{CandidType, Int as WrappedInt};
5use derive_more::{Add, AddAssign, Sub, SubAssign};
6use num_bigint::BigInt;
7use serde::{Deserialize, Serialize};
8use std::{
9    fmt,
10    iter::{Product, Sum},
11    ops::{Div, DivAssign, Mul, MulAssign, Neg},
12    str::FromStr,
13};
14
15//
16// IntBig
17//
18
19#[derive(
20    Add,
21    AddAssign,
22    CandidType,
23    Clone,
24    Debug,
25    Default,
26    Eq,
27    PartialEq,
28    Hash,
29    Ord,
30    PartialOrd,
31    Serialize,
32    Deserialize,
33    Sub,
34    SubAssign,
35)]
36/// Arbitrary-precision signed integer used by schema and typed values.
37pub struct IntBig(WrappedInt);
38
39impl IntBig {
40    /// Return the magnitude's bit length without allocating an encoded copy.
41    #[must_use]
42    pub fn magnitude_bits(&self) -> u64 {
43        self.0.0.bits()
44    }
45
46    /// Construct from the canonical Candid signed-integer representation.
47    #[must_use]
48    pub const fn from_candid(value: WrappedInt) -> Self {
49        Self(value)
50    }
51
52    /// Construct from a `num_bigint` signed integer.
53    #[must_use]
54    pub fn from_bigint(value: BigInt) -> Self {
55        Self::from_candid(WrappedInt::from(value))
56    }
57
58    /// Return sign and base-2^32 magnitude limbs for decimal key encoding.
59    ///
60    /// This allocates for the returned limb vector.
61    #[must_use]
62    pub fn sign_and_u32_digits(&self) -> (bool, Vec<u32>) {
63        (
64            self.0.0.cmp(&0.into()).is_lt(),
65            self.0.0.magnitude().to_u32_digits(),
66        )
67    }
68
69    /// Convert to `i128` when the value is in range.
70    #[must_use]
71    pub fn to_i128(&self) -> Option<i128> {
72        let big = &self.0.0;
73
74        i128::try_from(big).ok()
75    }
76
77    /// Convert to `i64` when the value is in range.
78    #[must_use]
79    pub fn to_i64(&self) -> Option<i64> {
80        let big = &self.0.0;
81
82        i64::try_from(big).ok()
83    }
84
85    /// Serialize this arbitrary-precision integer for internal hash and sort-key framing.
86    #[must_use]
87    pub fn to_leb128(&self) -> Vec<u8> {
88        let mut out = Vec::new();
89        let encoded = self.0.encode(&mut out);
90        debug_assert!(encoded.is_ok(), "Vec-backed signed LEB128 encoding failed");
91
92        out
93    }
94
95    pub(crate) fn to_sign_and_magnitude_bytes(&self) -> (bool, Vec<u8>) {
96        let (sign, magnitude) = self.0.0.to_bytes_be();
97        (sign == num_bigint::Sign::Minus, magnitude)
98    }
99
100    pub(crate) fn from_sign_and_magnitude_bytes(negative: bool, magnitude: &[u8]) -> Self {
101        let sign = if magnitude.is_empty() {
102            num_bigint::Sign::NoSign
103        } else if negative {
104            num_bigint::Sign::Minus
105        } else {
106            num_bigint::Sign::Plus
107        };
108        Self::from_bigint(BigInt::from_bytes_be(sign, magnitude))
109    }
110
111    /// Saturating addition (unbounded; equivalent to normal addition).
112    #[must_use]
113    pub fn saturating_add(self, rhs: Self) -> Self {
114        Self(self.0 + rhs.0)
115    }
116
117    /// Saturating subtraction (unbounded; equivalent to normal subtraction).
118    #[must_use]
119    pub fn saturating_sub(self, rhs: Self) -> Self {
120        Self(self.0 - rhs.0)
121    }
122}
123
124impl fmt::Display for IntBig {
125    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
126        self.0.fmt(f)
127    }
128}
129
130impl FromStr for IntBig {
131    type Err = <WrappedInt as FromStr>::Err;
132
133    fn from_str(s: &str) -> Result<Self, Self::Err> {
134        WrappedInt::from_str(s).map(Self::from_candid)
135    }
136}
137
138impl Div for IntBig {
139    type Output = Self;
140
141    fn div(self, other: Self) -> Self::Output {
142        Self(self.0 / other.0)
143    }
144}
145
146impl DivAssign for IntBig {
147    fn div_assign(&mut self, other: Self) {
148        self.0 /= other.0;
149    }
150}
151
152impl From<i32> for IntBig {
153    fn from(n: i32) -> Self {
154        Self::from_candid(WrappedInt::from(n))
155    }
156}
157
158impl From<i64> for IntBig {
159    fn from(n: i64) -> Self {
160        Self::from_candid(WrappedInt::from(n))
161    }
162}
163
164impl Mul for IntBig {
165    type Output = Self;
166
167    fn mul(self, other: Self) -> Self::Output {
168        Self(self.0 * other.0)
169    }
170}
171
172impl MulAssign for IntBig {
173    fn mul_assign(&mut self, other: Self) {
174        self.0 *= other.0;
175    }
176}
177
178impl Neg for IntBig {
179    type Output = Self;
180
181    fn neg(self) -> Self::Output {
182        Self::from_bigint(-self.0.0)
183    }
184}
185
186impl NumericValue for IntBig {
187    fn try_to_decimal(&self) -> Option<Decimal> {
188        self.to_i128().and_then(Decimal::from_i128)
189    }
190
191    fn try_from_decimal(value: Decimal) -> Option<Self> {
192        value.to_i128().map(WrappedInt::from).map(Self::from_candid)
193    }
194}
195
196impl Product for IntBig {
197    fn product<I: Iterator<Item = Self>>(iter: I) -> Self {
198        iter.fold(Self::from(1), |acc, value| acc * value)
199    }
200}
201
202impl Sum for IntBig {
203    fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
204        iter.fold(Self::default(), |acc, x| acc + x)
205    }
206}