boa/
bigint.rs

1//! This module implements the JavaScript bigint primitive rust type.
2
3use crate::{
4    builtins::Number,
5    gc::{empty_trace, Finalize, Trace},
6    Context, JsValue,
7};
8
9use std::{
10    convert::TryFrom,
11    fmt::{self, Display},
12    ops::{Add, BitAnd, BitOr, BitXor, Div, Mul, Neg, Rem, Shl, Shr, Sub},
13    rc::Rc,
14};
15
16use num_integer::Integer;
17use num_traits::pow::Pow;
18use num_traits::{FromPrimitive, One, ToPrimitive, Zero};
19
20/// The raw bigint type.
21pub type RawBigInt = num_bigint::BigInt;
22
23#[cfg(feature = "deser")]
24use serde::{Deserialize, Serialize};
25
26/// JavaScript bigint primitive rust type.
27#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))]
28#[derive(Debug, Finalize, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
29pub struct JsBigInt {
30    inner: Rc<RawBigInt>,
31}
32
33// Safety: BigInt does not contain any objects which needs to be traced,
34// so this is safe.
35unsafe impl Trace for JsBigInt {
36    empty_trace!();
37}
38
39impl JsBigInt {
40    /// Create a new [`JsBigInt`].
41    #[inline]
42    pub fn new<T: Into<Self>>(value: T) -> Self {
43        value.into()
44    }
45
46    /// Create a [`JsBigInt`] with value `0`.
47    #[inline]
48    pub fn zero() -> Self {
49        Self {
50            inner: Rc::new(RawBigInt::zero()),
51        }
52    }
53
54    /// Check if is zero.
55    #[inline]
56    pub fn is_zero(&self) -> bool {
57        self.inner.is_zero()
58    }
59
60    /// Create a [`JsBigInt`] with value `1`.
61    #[inline]
62    pub fn one() -> Self {
63        Self {
64            inner: Rc::new(RawBigInt::one()),
65        }
66    }
67
68    /// Check if is one.
69    #[inline]
70    pub fn is_one(&self) -> bool {
71        self.inner.is_one()
72    }
73
74    /// Convert bigint to string with radix.
75    #[inline]
76    pub fn to_string_radix(&self, radix: u32) -> String {
77        self.inner.to_str_radix(radix)
78    }
79
80    /// Converts the BigInt to a f64 type.
81    ///
82    /// Returns `f64::INFINITY` if the BigInt is too big.
83    #[inline]
84    pub fn to_f64(&self) -> f64 {
85        self.inner.to_f64().unwrap_or(f64::INFINITY)
86    }
87
88    /// Converts a string to a BigInt with the specified radix.
89    #[inline]
90    pub fn from_string_radix(buf: &str, radix: u32) -> Option<Self> {
91        Some(Self {
92            inner: Rc::new(RawBigInt::parse_bytes(buf.as_bytes(), radix)?),
93        })
94    }
95
96    /// This function takes a string and conversts it to BigInt type.
97    ///
98    /// More information:
99    ///  - [ECMAScript reference][spec]
100    ///
101    /// [spec]: https://tc39.es/ecma262/#sec-stringtobigint
102    #[inline]
103    pub fn from_string(mut string: &str) -> Option<Self> {
104        string = string.trim();
105
106        if string.is_empty() {
107            return Some(JsBigInt::zero());
108        }
109
110        let mut radix = 10;
111        if string.starts_with("0b") || string.starts_with("0B") {
112            radix = 2;
113            string = &string[2..];
114        } else if string.starts_with("0x") || string.starts_with("0X") {
115            radix = 16;
116            string = &string[2..];
117        } else if string.starts_with("0o") || string.starts_with("0O") {
118            radix = 8;
119            string = &string[2..];
120        }
121
122        Self::from_string_radix(string, radix)
123    }
124
125    /// Checks for `SameValueZero` equality.
126    ///
127    /// More information:
128    ///  - [ECMAScript reference][spec]
129    ///
130    /// [spec]: https://tc39.es/ecma262/#sec-numeric-types-bigint-equal
131    #[inline]
132    pub fn same_value_zero(x: &Self, y: &Self) -> bool {
133        // Return BigInt::equal(x, y)
134        Self::equal(x, y)
135    }
136
137    /// Checks for `SameValue` equality.
138    ///
139    ///
140    /// More information:
141    ///  - [ECMAScript reference][spec]
142    ///
143    /// [spec]: https://tc39.es/ecma262/#sec-numeric-types-bigint-sameValue
144    #[inline]
145    pub fn same_value(x: &Self, y: &Self) -> bool {
146        // Return BigInt::equal(x, y)
147        Self::equal(x, y)
148    }
149
150    /// Checks for mathematical equality.
151    ///
152    /// The abstract operation BigInt::equal takes arguments x (a `BigInt`) and y (a `BigInt`).
153    /// It returns `true` if x and y have the same mathematical integer value and false otherwise.
154    ///
155    /// More information:
156    ///  - [ECMAScript reference][spec]
157    ///
158    /// [spec]: https://tc39.es/ecma262/#sec-numeric-types-bigint-sameValueZero
159    #[inline]
160    pub fn equal(x: &Self, y: &Self) -> bool {
161        x == y
162    }
163
164    #[inline]
165    pub fn pow(x: &Self, y: &Self, context: &mut Context) -> Result<Self, JsValue> {
166        let y = if let Some(y) = y.inner.to_biguint() {
167            y
168        } else {
169            return Err(context.construct_range_error("BigInt negative exponent"));
170        };
171
172        let num_bits = (x.inner.bits() as f64
173            * y.to_f64().expect("Unable to convert from BigUInt to f64"))
174        .floor()
175            + 1f64;
176
177        if num_bits > 1_000_000_000f64 {
178            return Err(context.construct_range_error("Maximum BigInt size exceeded"));
179        }
180
181        Ok(Self::new(x.inner.as_ref().clone().pow(y)))
182    }
183
184    #[inline]
185    pub fn shift_right(x: &Self, y: &Self, context: &mut Context) -> Result<Self, JsValue> {
186        if let Some(n) = y.inner.to_i32() {
187            let inner = if n > 0 {
188                x.inner.as_ref().clone().shr(n as usize)
189            } else {
190                x.inner.as_ref().clone().shl(n.abs() as usize)
191            };
192
193            Ok(Self::new(inner))
194        } else {
195            Err(context.construct_range_error("Maximum BigInt size exceeded"))
196        }
197    }
198
199    #[inline]
200    pub fn shift_left(x: &Self, y: &Self, context: &mut Context) -> Result<Self, JsValue> {
201        if let Some(n) = y.inner.to_i32() {
202            let inner = if n > 0 {
203                x.inner.as_ref().clone().shl(n as usize)
204            } else {
205                x.inner.as_ref().clone().shr(n.abs() as usize)
206            };
207
208            Ok(Self::new(inner))
209        } else {
210            Err(context.construct_range_error("Maximum BigInt size exceeded"))
211        }
212    }
213
214    /// Floored integer modulo.
215    ///
216    /// # Examples
217    /// ```
218    /// # use num_integer::Integer;
219    /// assert_eq!((8).mod_floor(&3), 2);
220    /// assert_eq!((8).mod_floor(&-3), -1);
221    /// ```
222    #[inline]
223    pub fn mod_floor(x: &Self, y: &Self) -> Self {
224        Self::new(x.inner.mod_floor(&y.inner))
225    }
226
227    #[inline]
228    pub fn add(x: &Self, y: &Self) -> Self {
229        Self::new(x.inner.as_ref().clone().add(y.inner.as_ref()))
230    }
231
232    #[inline]
233    pub fn sub(x: &Self, y: &Self) -> Self {
234        Self::new(x.inner.as_ref().clone().sub(y.inner.as_ref()))
235    }
236
237    #[inline]
238    pub fn mul(x: &Self, y: &Self) -> Self {
239        Self::new(x.inner.as_ref().clone().mul(y.inner.as_ref()))
240    }
241
242    #[inline]
243    pub fn div(x: &Self, y: &Self) -> Self {
244        Self::new(x.inner.as_ref().clone().div(y.inner.as_ref()))
245    }
246
247    #[inline]
248    pub fn rem(x: &Self, y: &Self) -> Self {
249        Self::new(x.inner.as_ref().clone().rem(y.inner.as_ref()))
250    }
251
252    #[inline]
253    pub fn bitand(x: &Self, y: &Self) -> Self {
254        Self::new(x.inner.as_ref().clone().bitand(y.inner.as_ref()))
255    }
256
257    #[inline]
258    pub fn bitor(x: &Self, y: &Self) -> Self {
259        Self::new(x.inner.as_ref().clone().bitor(y.inner.as_ref()))
260    }
261
262    #[inline]
263    pub fn bitxor(x: &Self, y: &Self) -> Self {
264        Self::new(x.inner.as_ref().clone().bitxor(y.inner.as_ref()))
265    }
266
267    #[inline]
268    pub fn neg(x: &Self) -> Self {
269        Self::new(x.as_inner().neg())
270    }
271
272    #[inline]
273    pub fn not(x: &Self) -> Self {
274        Self::new(!x.as_inner())
275    }
276
277    #[inline]
278    pub(crate) fn as_inner(&self) -> &RawBigInt {
279        &self.inner
280    }
281}
282
283impl Display for JsBigInt {
284    #[inline]
285    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
286        Display::fmt(&self.inner, f)
287    }
288}
289
290impl From<RawBigInt> for JsBigInt {
291    #[inline]
292    fn from(value: RawBigInt) -> Self {
293        Self {
294            inner: Rc::new(value),
295        }
296    }
297}
298
299impl From<i8> for JsBigInt {
300    #[inline]
301    fn from(value: i8) -> Self {
302        Self {
303            inner: Rc::new(RawBigInt::from(value)),
304        }
305    }
306}
307
308impl From<u8> for JsBigInt {
309    #[inline]
310    fn from(value: u8) -> Self {
311        Self {
312            inner: Rc::new(RawBigInt::from(value)),
313        }
314    }
315}
316
317impl From<i16> for JsBigInt {
318    #[inline]
319    fn from(value: i16) -> Self {
320        Self {
321            inner: Rc::new(RawBigInt::from(value)),
322        }
323    }
324}
325
326impl From<u16> for JsBigInt {
327    #[inline]
328    fn from(value: u16) -> Self {
329        Self {
330            inner: Rc::new(RawBigInt::from(value)),
331        }
332    }
333}
334
335impl From<i32> for JsBigInt {
336    #[inline]
337    fn from(value: i32) -> Self {
338        Self {
339            inner: Rc::new(RawBigInt::from(value)),
340        }
341    }
342}
343
344impl From<u32> for JsBigInt {
345    #[inline]
346    fn from(value: u32) -> Self {
347        Self {
348            inner: Rc::new(RawBigInt::from(value)),
349        }
350    }
351}
352
353impl From<i64> for JsBigInt {
354    #[inline]
355    fn from(value: i64) -> Self {
356        Self {
357            inner: Rc::new(RawBigInt::from(value)),
358        }
359    }
360}
361
362impl From<u64> for JsBigInt {
363    #[inline]
364    fn from(value: u64) -> Self {
365        Self {
366            inner: Rc::new(RawBigInt::from(value)),
367        }
368    }
369}
370
371impl From<isize> for JsBigInt {
372    #[inline]
373    fn from(value: isize) -> Self {
374        Self {
375            inner: Rc::new(RawBigInt::from(value)),
376        }
377    }
378}
379
380impl From<usize> for JsBigInt {
381    #[inline]
382    fn from(value: usize) -> Self {
383        Self {
384            inner: Rc::new(RawBigInt::from(value)),
385        }
386    }
387}
388
389#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
390pub struct TryFromF64Error;
391
392impl Display for TryFromF64Error {
393    #[inline]
394    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
395        write!(f, "Could not convert f64 value to a BigInt type")
396    }
397}
398
399impl TryFrom<f64> for JsBigInt {
400    type Error = TryFromF64Error;
401
402    #[inline]
403    fn try_from(n: f64) -> Result<Self, Self::Error> {
404        // If the truncated version of the number is not the
405        // same as the non-truncated version then the floating-point
406        // number conains a fractional part.
407        if !Number::equal(n.trunc(), n) {
408            return Err(TryFromF64Error);
409        }
410        match RawBigInt::from_f64(n) {
411            Some(bigint) => Ok(Self::new(bigint)),
412            None => Err(TryFromF64Error),
413        }
414    }
415}
416
417impl PartialEq<i32> for JsBigInt {
418    #[inline]
419    fn eq(&self, other: &i32) -> bool {
420        self.inner.as_ref() == &RawBigInt::from(*other)
421    }
422}
423
424impl PartialEq<JsBigInt> for i32 {
425    #[inline]
426    fn eq(&self, other: &JsBigInt) -> bool {
427        &RawBigInt::from(*self) == other.inner.as_ref()
428    }
429}
430
431impl PartialEq<f64> for JsBigInt {
432    #[inline]
433    fn eq(&self, other: &f64) -> bool {
434        if other.fract() != 0.0 {
435            return false;
436        }
437
438        self.inner.as_ref() == &RawBigInt::from(*other as i64)
439    }
440}
441
442impl PartialEq<JsBigInt> for f64 {
443    #[inline]
444    fn eq(&self, other: &JsBigInt) -> bool {
445        if self.fract() != 0.0 {
446            return false;
447        }
448
449        &RawBigInt::from(*self as i64) == other.inner.as_ref()
450    }
451}