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::Sum,
11    ops::{Div, DivAssign, Mul, MulAssign},
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    /// Construct from the canonical Candid signed-integer representation.
41    #[must_use]
42    pub const fn from_candid(value: WrappedInt) -> Self {
43        Self(value)
44    }
45
46    /// Construct from a `num_bigint` signed integer.
47    #[must_use]
48    pub fn from_bigint(value: BigInt) -> Self {
49        Self::from_candid(WrappedInt::from(value))
50    }
51
52    /// Return sign and base-2^32 magnitude limbs for decimal key encoding.
53    ///
54    /// This allocates for the returned limb vector.
55    #[must_use]
56    pub fn sign_and_u32_digits(&self) -> (bool, Vec<u32>) {
57        (
58            self.0.0.cmp(&0.into()).is_lt(),
59            self.0.0.magnitude().to_u32_digits(),
60        )
61    }
62
63    /// Convert to `i128` when the value is in range.
64    #[must_use]
65    pub fn to_i128(&self) -> Option<i128> {
66        let big = &self.0.0;
67
68        i128::try_from(big).ok()
69    }
70
71    /// Convert to `i64` when the value is in range.
72    #[must_use]
73    pub fn to_i64(&self) -> Option<i64> {
74        let big = &self.0.0;
75
76        i64::try_from(big).ok()
77    }
78
79    /// Serialize this arbitrary-precision integer for internal hash and sort-key framing.
80    #[must_use]
81    pub fn to_leb128(&self) -> Vec<u8> {
82        let mut out = Vec::new();
83        let encoded = self.0.encode(&mut out);
84        debug_assert!(encoded.is_ok(), "Vec-backed signed LEB128 encoding failed");
85
86        out
87    }
88
89    /// Saturating addition (unbounded; equivalent to normal addition).
90    #[must_use]
91    pub fn saturating_add(self, rhs: Self) -> Self {
92        Self(self.0 + rhs.0)
93    }
94
95    /// Saturating subtraction (unbounded; equivalent to normal subtraction).
96    #[must_use]
97    pub fn saturating_sub(self, rhs: Self) -> Self {
98        Self(self.0 - rhs.0)
99    }
100}
101
102impl fmt::Display for IntBig {
103    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
104        self.0.fmt(f)
105    }
106}
107
108impl FromStr for IntBig {
109    type Err = <WrappedInt as FromStr>::Err;
110
111    fn from_str(s: &str) -> Result<Self, Self::Err> {
112        WrappedInt::from_str(s).map(Self::from_candid)
113    }
114}
115
116impl Div for IntBig {
117    type Output = Self;
118
119    fn div(self, other: Self) -> Self::Output {
120        Self(self.0 / other.0)
121    }
122}
123
124impl DivAssign for IntBig {
125    fn div_assign(&mut self, other: Self) {
126        self.0 /= other.0;
127    }
128}
129
130impl From<i32> for IntBig {
131    fn from(n: i32) -> Self {
132        Self::from_candid(WrappedInt::from(n))
133    }
134}
135
136impl From<i64> for IntBig {
137    fn from(n: i64) -> Self {
138        Self::from_candid(WrappedInt::from(n))
139    }
140}
141
142impl Mul for IntBig {
143    type Output = Self;
144
145    fn mul(self, other: Self) -> Self::Output {
146        Self(self.0 * other.0)
147    }
148}
149
150impl MulAssign for IntBig {
151    fn mul_assign(&mut self, other: Self) {
152        self.0 *= other.0;
153    }
154}
155
156impl NumericValue for IntBig {
157    fn try_to_decimal(&self) -> Option<Decimal> {
158        self.to_i128().and_then(Decimal::from_i128)
159    }
160
161    fn try_from_decimal(value: Decimal) -> Option<Self> {
162        value.to_i128().map(WrappedInt::from).map(Self::from_candid)
163    }
164}
165
166impl Sum for IntBig {
167    fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
168        iter.fold(Self::default(), |acc, x| acc + x)
169    }
170}