Skip to main content

deser_core/ext/
bigint.rs

1use alloc::string::ToString;
2use alloc::vec::Vec;
3use core::cmp::Ordering;
4use core::fmt;
5use core::str::FromStr;
6
7use crate::error::Error;
8use crate::event::Atom;
9use crate::ext::Extension;
10use crate::ext::known::{WellKnown, impl_well_known, invalid};
11
12/// An integer of arbitrary size.
13///
14/// This is a well-known extension type (see [`ext`](crate::ext)) for
15/// integers that do not fit into 128 bits.  It holds the sign and the
16/// magnitude as big-endian bytes.  Leading zero bytes are permitted, zero is
17/// never negative.
18///
19/// The fallback is the decimal representation as string.  Integers that
20/// fit into 64 or 128 bits are not represented as [`BigInt`]: they are
21/// passed through deser as `U64`, `I64`, `u128` or `i128` instead.  That is
22/// what the `num-bigint` support does.  When deserializing, all of these as
23/// well as strings are accepted.
24///
25/// ```
26/// use deser::ext::BigInt;
27///
28/// let value: BigInt =
29///     "-340282366920938463463374607431768211456".parse().unwrap();
30/// assert!(value.negative);
31/// assert_eq!(
32///     value.magnitude,
33///     [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
34/// );
35/// assert_eq!(value.to_string(), "-340282366920938463463374607431768211456");
36/// ```
37///
38/// Values are compared and hashed by their numeric value.
39#[derive(Clone, Default)]
40pub struct BigInt {
41    /// `true` if the integer is negative.
42    pub negative: bool,
43    /// The magnitude as big-endian bytes.
44    pub magnitude: Vec<u8>,
45}
46
47impl BigInt {
48    /// Creates a big integer from an `i128`.
49    pub fn from_i128(value: i128) -> BigInt {
50        let mut rv = BigInt::from_u128(value.unsigned_abs());
51        rv.negative = value < 0;
52        rv
53    }
54
55    /// Creates a big integer from an `u128`.
56    pub fn from_u128(value: u128) -> BigInt {
57        let bytes = value.to_be_bytes();
58        let skip = (value.leading_zeros() / 8) as usize;
59        BigInt {
60            negative: false,
61            magnitude: bytes[skip..].to_vec(),
62        }
63    }
64
65    /// Returns the magnitude without leading zero bytes.
66    pub fn significant_magnitude(&self) -> &[u8] {
67        let skip = self.magnitude.iter().take_while(|&&x| x == 0).count();
68        &self.magnitude[skip..]
69    }
70
71    /// Returns `true` if the value is zero.
72    pub fn is_zero(&self) -> bool {
73        self.significant_magnitude().is_empty()
74    }
75
76    /// Returns `true` if the value is negative (and not zero).
77    pub fn is_negative(&self) -> bool {
78        self.negative && !self.is_zero()
79    }
80
81    /// Returns the magnitude as `u128` if it fits.
82    fn magnitude_u128(&self) -> Option<u128> {
83        let significant = self.significant_magnitude();
84        if significant.len() > 16 {
85            return None;
86        }
87        let mut buf = [0u8; 16];
88        buf[16 - significant.len()..].copy_from_slice(significant);
89        Some(u128::from_be_bytes(buf))
90    }
91
92    /// Returns the value as `u128` if it fits.
93    pub fn to_u128(&self) -> Option<u128> {
94        if self.is_negative() {
95            None
96        } else {
97            self.magnitude_u128()
98        }
99    }
100
101    /// Returns the value as `i128` if it fits.
102    pub fn to_i128(&self) -> Option<i128> {
103        let magnitude = self.magnitude_u128()?;
104        if self.is_negative() {
105            0i128.checked_sub_unsigned(magnitude)
106        } else {
107            i128::try_from(magnitude).ok()
108        }
109    }
110
111    /// Converts the value into the smallest atom that can hold it.
112    ///
113    /// This is `U64` or `I64` if the value fits into 64 bits, an `u128` or
114    /// `i128` extension value if it fits into 128 bits and a [`BigInt`]
115    /// extension value otherwise.
116    pub fn into_atom(self) -> Atom<'static> {
117        use crate::ext::ExtValue;
118        if let Some(value) = self.to_u128() {
119            match u64::try_from(value) {
120                Ok(value) => Atom::U64(value),
121                Err(_) => Atom::Ext(ExtValue::owned(value)),
122            }
123        } else if let Some(value) = self.to_i128() {
124            match i64::try_from(value) {
125                Ok(value) => Atom::I64(value),
126                Err(_) => Atom::Ext(ExtValue::owned(value)),
127            }
128        } else {
129            Atom::Ext(ExtValue::owned(self))
130        }
131    }
132}
133
134impl PartialEq for BigInt {
135    fn eq(&self, other: &BigInt) -> bool {
136        self.is_negative() == other.is_negative()
137            && self.significant_magnitude() == other.significant_magnitude()
138    }
139}
140
141impl Eq for BigInt {}
142
143impl core::hash::Hash for BigInt {
144    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
145        self.is_negative().hash(state);
146        self.significant_magnitude().hash(state);
147    }
148}
149
150impl PartialOrd for BigInt {
151    fn partial_cmp(&self, other: &BigInt) -> Option<Ordering> {
152        Some(self.cmp(other))
153    }
154}
155
156impl Ord for BigInt {
157    fn cmp(&self, other: &BigInt) -> Ordering {
158        let (a, b) = (self.significant_magnitude(), other.significant_magnitude());
159        let magnitude = a.len().cmp(&b.len()).then_with(|| a.cmp(b));
160        match (self.is_negative(), other.is_negative()) {
161            (false, false) => magnitude,
162            (true, true) => magnitude.reverse(),
163            (false, true) => Ordering::Greater,
164            (true, false) => Ordering::Less,
165        }
166    }
167}
168
169impl From<i128> for BigInt {
170    fn from(value: i128) -> BigInt {
171        BigInt::from_i128(value)
172    }
173}
174
175impl From<u128> for BigInt {
176    fn from(value: u128) -> BigInt {
177        BigInt::from_u128(value)
178    }
179}
180
181impl From<i64> for BigInt {
182    fn from(value: i64) -> BigInt {
183        BigInt::from_i128(value.into())
184    }
185}
186
187impl From<u64> for BigInt {
188    fn from(value: u64) -> BigInt {
189        BigInt::from_u128(value.into())
190    }
191}
192
193impl fmt::Display for BigInt {
194    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
195        // repeatedly divide by 10^9 and collect the remainders
196        let mut value = self.significant_magnitude().to_vec();
197        let mut chunks = Vec::new();
198        while !value.is_empty() {
199            let mut remainder = 0u64;
200            for byte in value.iter_mut() {
201                let current = remainder << 8 | u64::from(*byte);
202                *byte = (current / 1_000_000_000) as u8;
203                remainder = current % 1_000_000_000;
204            }
205            chunks.push(remainder as u32);
206            let skip = value.iter().take_while(|&&x| x == 0).count();
207            value.drain(..skip);
208        }
209        if self.is_negative() {
210            f.write_str("-")?;
211        }
212        match chunks.pop() {
213            None => f.write_str("0"),
214            Some(first) => {
215                write!(f, "{}", first)?;
216                for chunk in chunks.iter().rev() {
217                    write!(f, "{:09}", chunk)?;
218                }
219                Ok(())
220            }
221        }
222    }
223}
224
225impl fmt::Debug for BigInt {
226    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
227        write!(f, "BigInt({})", self)
228    }
229}
230
231impl FromStr for BigInt {
232    type Err = Error;
233
234    /// Parses a decimal integer with an optional sign.
235    fn from_str(s: &str) -> Result<BigInt, Error> {
236        let (negative, digits) = match s.as_bytes().first() {
237            Some(b'-') => (true, &s[1..]),
238            Some(b'+') => (false, &s[1..]),
239            _ => (false, s),
240        };
241        if digits.is_empty() || !digits.bytes().all(|x| x.is_ascii_digit()) {
242            return Err(invalid("invalid integer"));
243        }
244        // multiply by 10^9 and add, working on chunks of nine digits
245        let mut magnitude: Vec<u8> = Vec::new();
246        let first = digits.len() % 9;
247        let chunks = (first != 0).then(|| &digits[..first]).into_iter().chain(
248            digits.as_bytes()[first..].chunks(9).map(|x| {
249                // the digits are ASCII
250                core::str::from_utf8(x).unwrap()
251            }),
252        );
253        for chunk in chunks {
254            let factor = 10u64.pow(chunk.len() as u32);
255            let mut carry: u64 = chunk.parse().unwrap();
256            for byte in magnitude.iter_mut().rev() {
257                let current = u64::from(*byte) * factor + carry;
258                *byte = current as u8;
259                carry = current >> 8;
260            }
261            while carry != 0 {
262                magnitude.insert(0, carry as u8);
263                carry >>= 8;
264            }
265        }
266        let rv = BigInt {
267            negative,
268            magnitude,
269        };
270        Ok(BigInt {
271            negative: rv.is_negative(),
272            ..rv
273        })
274    }
275}
276
277impl Extension for BigInt {
278    fn name(&self) -> &str {
279        "big integer"
280    }
281
282    fn fallback(&self) -> Atom<'_> {
283        Atom::Str(self.to_string().into())
284    }
285}
286
287impl WellKnown for BigInt {
288    const EXPECTING: &'static str = "integer";
289
290    /// Accepts integers of all sizes and strings.
291    fn from_atom(atom: &Atom) -> Result<Option<BigInt>, Error> {
292        Ok(Some(match *atom {
293            Atom::Ext(ref ext) => {
294                if let Some(value) = ext.downcast_ref::<BigInt>() {
295                    value.clone()
296                } else if let Some(&value) = ext.downcast_ref::<u128>() {
297                    BigInt::from(value)
298                } else if let Some(&value) = ext.downcast_ref::<i128>() {
299                    BigInt::from(value)
300                } else if let Some(value) = ext
301                    .downcast_value_ref::<crate::ext::Number>()
302                    .filter(|x| x.is_integer())
303                {
304                    // integer literals that do not fit into 128 bits
305                    value.as_str().parse()?
306                } else {
307                    return Ok(None);
308                }
309            }
310            Atom::U64(value) => BigInt::from(value),
311            Atom::I64(value) => BigInt::from(value),
312            Atom::Str(ref value) => value.parse()?,
313            _ => return Ok(None),
314        }))
315    }
316}
317
318impl_well_known!(BigInt);
319
320#[test]
321fn test_bigint() {
322    for s in [
323        "0",
324        "1",
325        "-1",
326        "255",
327        "256",
328        "999999999",
329        "1000000000",
330        "-18446744073709551616",
331        "340282366920938463463374607431768211456",
332        "-123456789012345678901234567890123456789012345678901234567890",
333    ] {
334        let value: BigInt = s.parse().unwrap();
335        assert_eq!(value.to_string(), s);
336    }
337    assert_eq!("-0".parse::<BigInt>().unwrap().to_string(), "0");
338    assert_eq!("+007".parse::<BigInt>().unwrap().to_string(), "7");
339    assert_eq!(BigInt::from(i128::MIN).to_i128(), Some(i128::MIN));
340    assert_eq!(BigInt::from(u128::MAX).to_u128(), Some(u128::MAX));
341    assert_eq!(BigInt::from(u128::MAX).to_i128(), None);
342    assert_eq!(BigInt::from(-1i64).to_u128(), None);
343    assert_eq!(BigInt::from(-1i64).into_atom(), Atom::I64(-1));
344    for invalid in ["", "-", "1.0", "1e5", " 1", "0x10"] {
345        assert!(invalid.parse::<BigInt>().is_err());
346    }
347    let n = |s: &str| s.parse::<BigInt>().unwrap();
348    assert_eq!(
349        BigInt {
350            negative: true,
351            magnitude: vec![0, 0]
352        },
353        n("0")
354    );
355    assert_eq!(
356        BigInt {
357            negative: false,
358            magnitude: vec![0, 1]
359        },
360        n("1")
361    );
362    let mut values = vec![n("256"), n("-1"), n("0"), n("-256"), n("255"), n("1")];
363    values.sort();
364    assert_eq!(
365        values,
366        [n("-256"), n("-1"), n("0"), n("1"), n("255"), n("256")]
367    );
368}