1use crate::{JsData, JsResult, JsString, builtins::Number, error::JsNativeError};
4use boa_gc::{Finalize, Trace};
5use num_bigint::Sign;
6use num_integer::Integer;
7use num_traits::{FromPrimitive, One, ToPrimitive, Zero, pow::Pow};
8use std::{
9 fmt::{self, Display},
10 ops::{Add, BitAnd, BitOr, BitXor, Div, Mul, Neg, Rem, Shl, Shr, Sub},
11 ptr::NonNull,
12 rc::Rc,
13};
14
15pub type RawBigInt = num_bigint::BigInt;
17
18#[cfg(feature = "deser")]
19use serde::{Deserialize, Serialize};
20
21#[allow(
23 clippy::unsafe_derive_deserialize,
24 reason = "unsafe methods do not add invariants that need to be held"
25)]
26#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))]
27#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Trace, Finalize, JsData)]
28#[boa_gc(unsafe_empty_trace)]
30pub struct JsBigInt {
31 inner: Rc<RawBigInt>,
32}
33
34impl JsBigInt {
35 #[must_use]
37 pub fn new<T: Into<Self>>(value: T) -> Self {
38 value.into()
39 }
40
41 #[inline]
43 #[must_use]
44 pub fn zero() -> Self {
45 Self {
46 inner: Rc::new(RawBigInt::zero()),
47 }
48 }
49
50 #[inline]
52 #[must_use]
53 pub fn is_zero(&self) -> bool {
54 self.inner.is_zero()
55 }
56
57 #[inline]
59 #[must_use]
60 pub fn one() -> Self {
61 Self {
62 inner: Rc::new(RawBigInt::one()),
63 }
64 }
65
66 #[inline]
68 #[must_use]
69 pub fn is_one(&self) -> bool {
70 self.inner.is_one()
71 }
72
73 #[inline]
75 #[must_use]
76 pub fn to_string_radix(&self, radix: u32) -> String {
77 self.inner.to_str_radix(radix)
78 }
79
80 #[inline]
84 #[must_use]
85 pub fn to_f64(&self) -> f64 {
86 self.inner.to_f64().unwrap_or(f64::INFINITY)
87 }
88
89 #[inline]
93 #[must_use]
94 pub fn to_i128(&self) -> i128 {
95 self.inner.to_i128().unwrap_or(i128::MAX)
96 }
97
98 #[inline]
100 #[must_use]
101 pub fn from_string_radix(buf: &str, radix: u32) -> Option<Self> {
102 Some(Self {
103 inner: Rc::new(RawBigInt::parse_bytes(buf.as_bytes(), radix)?),
104 })
105 }
106
107 pub(crate) fn from_js_string(string: &JsString) -> Option<JsBigInt> {
114 JsBigInt::from_string(string.to_std_string().ok().as_ref()?)
121 }
122
123 #[inline]
130 #[must_use]
131 pub fn from_string(mut string: &str) -> Option<Self> {
132 string = string.trim();
133
134 if string.is_empty() {
135 return Some(Self::zero());
136 }
137
138 let mut radix = 10;
139 if string.starts_with("0b") || string.starts_with("0B") {
140 radix = 2;
141 string = &string[2..];
142 } else if string.starts_with("0x") || string.starts_with("0X") {
143 radix = 16;
144 string = &string[2..];
145 } else if string.starts_with("0o") || string.starts_with("0O") {
146 radix = 8;
147 string = &string[2..];
148 }
149
150 Self::from_string_radix(string, radix)
151 }
152
153 #[inline]
160 #[must_use]
161 pub fn same_value_zero(x: &Self, y: &Self) -> bool {
162 Self::equal(x, y)
164 }
165
166 #[inline]
174 #[must_use]
175 pub fn same_value(x: &Self, y: &Self) -> bool {
176 Self::equal(x, y)
178 }
179
180 #[inline]
190 #[must_use]
191 pub fn equal(x: &Self, y: &Self) -> bool {
192 x == y
193 }
194
195 #[inline]
197 pub fn pow(x: &Self, y: &Self) -> JsResult<Self> {
198 let y = y
199 .inner
200 .to_biguint()
201 .ok_or_else(|| JsNativeError::range().with_message("BigInt negative exponent"))?;
202
203 let num_bits = (x.inner.bits() as f64
204 * y.to_f64().expect("Unable to convert from BigUInt to f64"))
205 .floor()
206 + 1f64;
207
208 if num_bits > 1_000_000_000f64 {
209 return Err(JsNativeError::range()
210 .with_message("Maximum BigInt size exceeded")
211 .into());
212 }
213
214 Ok(Self::new(x.inner.as_ref().clone().pow(y)))
215 }
216
217 #[inline]
219 pub fn shift_right(x: &Self, y: &Self) -> JsResult<Self> {
220 match y.inner.to_i32() {
221 Some(n) if n > 0 => Ok(Self::new(x.inner.as_ref().clone().shr(n as usize))),
222 Some(n) => Ok(Self::new(x.inner.as_ref().clone().shl(n.unsigned_abs()))),
223 None => match (x.inner.sign(), y.inner.sign()) {
232 (Sign::Minus, Sign::Plus) => Ok(Self::new(RawBigInt::from(-1))),
234 (_, Sign::Plus) => Ok(Self::zero()),
235 (_, _) => Err(JsNativeError::range()
237 .with_message("Maximum BigInt size exceeded")
238 .into()),
239 },
240 }
241 }
242
243 #[inline]
245 pub fn shift_left(x: &Self, y: &Self) -> JsResult<Self> {
246 match y.inner.to_i32() {
247 Some(n) if n > 0 => Ok(Self::new(x.inner.as_ref().clone().shl(n as usize))),
248 Some(n) => Ok(Self::new(x.inner.as_ref().clone().shr(n.unsigned_abs()))),
249 None => match (x.inner.sign(), y.inner.sign()) {
254 (Sign::Minus, Sign::Minus) => Ok(Self::new(RawBigInt::from(-1))),
256 (_, Sign::Minus) => Ok(Self::zero()),
257 (_, _) => Err(JsNativeError::range()
259 .with_message("Maximum BigInt size exceeded")
260 .into()),
261 },
262 }
263 }
264
265 #[inline]
274 #[must_use]
275 pub fn mod_floor(x: &Self, y: &Self) -> Self {
276 Self::new(x.inner.mod_floor(&y.inner))
277 }
278
279 #[inline]
281 #[must_use]
282 pub fn add(x: &Self, y: &Self) -> Self {
283 Self::new(x.inner.as_ref().clone().add(y.inner.as_ref()))
284 }
285
286 #[inline]
288 #[must_use]
289 pub fn sub(x: &Self, y: &Self) -> Self {
290 Self::new(x.inner.as_ref().clone().sub(y.inner.as_ref()))
291 }
292
293 #[inline]
295 #[must_use]
296 pub fn mul(x: &Self, y: &Self) -> Self {
297 Self::new(x.inner.as_ref().clone().mul(y.inner.as_ref()))
298 }
299
300 #[inline]
302 #[must_use]
303 pub fn div(x: &Self, y: &Self) -> Self {
304 Self::new(x.inner.as_ref().clone().div(y.inner.as_ref()))
305 }
306
307 #[inline]
309 #[must_use]
310 pub fn rem(x: &Self, y: &Self) -> Self {
311 Self::new(x.inner.as_ref().clone().rem(y.inner.as_ref()))
312 }
313
314 #[inline]
316 #[must_use]
317 pub fn bitand(x: &Self, y: &Self) -> Self {
318 Self::new(x.inner.as_ref().clone().bitand(y.inner.as_ref()))
319 }
320
321 #[inline]
323 #[must_use]
324 pub fn bitor(x: &Self, y: &Self) -> Self {
325 Self::new(x.inner.as_ref().clone().bitor(y.inner.as_ref()))
326 }
327
328 #[inline]
330 #[must_use]
331 pub fn bitxor(x: &Self, y: &Self) -> Self {
332 Self::new(x.inner.as_ref().clone().bitxor(y.inner.as_ref()))
333 }
334
335 #[inline]
337 #[must_use]
338 pub fn neg(x: &Self) -> Self {
339 Self::new(x.as_inner().neg())
340 }
341
342 #[inline]
344 #[must_use]
345 pub fn not(x: &Self) -> Self {
346 Self::new(!x.as_inner())
347 }
348
349 #[inline]
351 #[must_use]
352 pub fn as_inner(&self) -> &RawBigInt {
353 &self.inner
354 }
355
356 #[inline]
361 #[must_use]
362 #[allow(unused, reason = "only used in nan-boxed implementation of JsValue")]
363 pub(crate) fn into_raw(self) -> NonNull<RawBigInt> {
364 unsafe { NonNull::new_unchecked(Rc::into_raw(self.inner).cast_mut()) }
366 }
367
368 #[inline]
378 #[must_use]
379 #[allow(unused, reason = "only used in nan-boxed implementation of JsValue")]
380 pub(crate) unsafe fn from_raw(ptr: *const RawBigInt) -> Self {
381 Self {
382 inner: unsafe { Rc::from_raw(ptr) },
384 }
385 }
386}
387
388impl Display for JsBigInt {
389 #[inline]
390 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
391 Display::fmt(&self.inner, f)
392 }
393}
394
395impl From<RawBigInt> for JsBigInt {
396 #[inline]
397 fn from(value: RawBigInt) -> Self {
398 Self {
399 inner: Rc::new(value),
400 }
401 }
402}
403
404impl From<Box<RawBigInt>> for JsBigInt {
405 #[inline]
406 fn from(value: Box<RawBigInt>) -> Self {
407 Self {
408 inner: value.into(),
409 }
410 }
411}
412
413impl From<i8> for JsBigInt {
414 #[inline]
415 fn from(value: i8) -> Self {
416 Self {
417 inner: Rc::new(RawBigInt::from(value)),
418 }
419 }
420}
421
422impl From<u8> for JsBigInt {
423 #[inline]
424 fn from(value: u8) -> Self {
425 Self {
426 inner: Rc::new(RawBigInt::from(value)),
427 }
428 }
429}
430
431impl From<i16> for JsBigInt {
432 #[inline]
433 fn from(value: i16) -> Self {
434 Self {
435 inner: Rc::new(RawBigInt::from(value)),
436 }
437 }
438}
439
440impl From<u16> for JsBigInt {
441 #[inline]
442 fn from(value: u16) -> Self {
443 Self {
444 inner: Rc::new(RawBigInt::from(value)),
445 }
446 }
447}
448
449impl From<i32> for JsBigInt {
450 #[inline]
451 fn from(value: i32) -> Self {
452 Self {
453 inner: Rc::new(RawBigInt::from(value)),
454 }
455 }
456}
457
458impl From<u32> for JsBigInt {
459 #[inline]
460 fn from(value: u32) -> Self {
461 Self {
462 inner: Rc::new(RawBigInt::from(value)),
463 }
464 }
465}
466
467impl From<i64> for JsBigInt {
468 #[inline]
469 fn from(value: i64) -> Self {
470 Self {
471 inner: Rc::new(RawBigInt::from(value)),
472 }
473 }
474}
475
476impl From<u64> for JsBigInt {
477 #[inline]
478 fn from(value: u64) -> Self {
479 Self {
480 inner: Rc::new(RawBigInt::from(value)),
481 }
482 }
483}
484
485impl From<i128> for JsBigInt {
486 #[inline]
487 fn from(value: i128) -> Self {
488 Self {
489 inner: Rc::new(RawBigInt::from(value)),
490 }
491 }
492}
493
494impl From<u128> for JsBigInt {
495 #[inline]
496 fn from(value: u128) -> Self {
497 Self {
498 inner: Rc::new(RawBigInt::from(value)),
499 }
500 }
501}
502
503impl From<isize> for JsBigInt {
504 #[inline]
505 fn from(value: isize) -> Self {
506 Self {
507 inner: Rc::new(RawBigInt::from(value)),
508 }
509 }
510}
511
512impl From<usize> for JsBigInt {
513 #[inline]
514 fn from(value: usize) -> Self {
515 Self {
516 inner: Rc::new(RawBigInt::from(value)),
517 }
518 }
519}
520
521#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
523pub struct TryFromF64Error;
524
525impl Display for TryFromF64Error {
526 #[inline]
527 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
528 write!(f, "Could not convert f64 value to a BigInt type")
529 }
530}
531
532impl TryFrom<f64> for JsBigInt {
533 type Error = TryFromF64Error;
534
535 #[inline]
536 fn try_from(n: f64) -> Result<Self, Self::Error> {
537 if !Number::equal(n.trunc(), n) {
541 return Err(TryFromF64Error);
542 }
543 RawBigInt::from_f64(n).map_or(Err(TryFromF64Error), |bigint| Ok(Self::new(bigint)))
544 }
545}
546
547impl PartialEq<i32> for JsBigInt {
548 #[inline]
549 fn eq(&self, other: &i32) -> bool {
550 self.inner.as_ref() == &RawBigInt::from(*other)
551 }
552}
553
554impl PartialEq<JsBigInt> for i32 {
555 #[inline]
556 fn eq(&self, other: &JsBigInt) -> bool {
557 &RawBigInt::from(*self) == other.inner.as_ref()
558 }
559}
560
561impl PartialEq<f64> for JsBigInt {
562 #[inline]
563 fn eq(&self, other: &f64) -> bool {
564 other.fract().is_zero()
565 && RawBigInt::from_f64(*other).is_some_and(|bigint| self.inner.as_ref() == &bigint)
566 }
567}
568
569impl PartialEq<JsBigInt> for f64 {
570 #[inline]
571 fn eq(&self, other: &JsBigInt) -> bool {
572 self.fract().is_zero()
573 && RawBigInt::from_f64(*self).is_some_and(|bigint| other.inner.as_ref() == &bigint)
574 }
575}