1use crate::{JsData, JsResult, JsString, builtins::Number, error::JsNativeError};
4use boa_gc::{Finalize, Trace};
5use num_integer::Integer;
6use num_traits::{FromPrimitive, One, ToPrimitive, Zero, pow::Pow};
7use std::{
8 fmt::{self, Display},
9 ops::{Add, BitAnd, BitOr, BitXor, Div, Mul, Neg, Rem, Shl, Shr, Sub},
10 rc::Rc,
11};
12
13pub type RawBigInt = num_bigint::BigInt;
15
16#[cfg(feature = "deser")]
17use serde::{Deserialize, Serialize};
18
19#[allow(
21 clippy::unsafe_derive_deserialize,
22 reason = "unsafe methods do not add invariants that need to be held"
23)]
24#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))]
25#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Trace, Finalize, JsData)]
26#[boa_gc(unsafe_empty_trace)]
28pub struct JsBigInt {
29 inner: Rc<RawBigInt>,
30}
31
32impl JsBigInt {
33 #[must_use]
35 pub fn new<T: Into<Self>>(value: T) -> Self {
36 value.into()
37 }
38
39 #[inline]
41 #[must_use]
42 pub fn zero() -> Self {
43 Self {
44 inner: Rc::new(RawBigInt::zero()),
45 }
46 }
47
48 #[inline]
50 #[must_use]
51 pub fn is_zero(&self) -> bool {
52 self.inner.is_zero()
53 }
54
55 #[inline]
57 #[must_use]
58 pub fn one() -> Self {
59 Self {
60 inner: Rc::new(RawBigInt::one()),
61 }
62 }
63
64 #[inline]
66 #[must_use]
67 pub fn is_one(&self) -> bool {
68 self.inner.is_one()
69 }
70
71 #[inline]
73 #[must_use]
74 pub fn to_string_radix(&self, radix: u32) -> String {
75 self.inner.to_str_radix(radix)
76 }
77
78 #[inline]
82 #[must_use]
83 pub fn to_f64(&self) -> f64 {
84 self.inner.to_f64().unwrap_or(f64::INFINITY)
85 }
86
87 #[inline]
91 #[must_use]
92 pub fn to_i128(&self) -> i128 {
93 self.inner.to_i128().unwrap_or(i128::MAX)
94 }
95
96 #[inline]
98 #[must_use]
99 pub fn from_string_radix(buf: &str, radix: u32) -> Option<Self> {
100 Some(Self {
101 inner: Rc::new(RawBigInt::parse_bytes(buf.as_bytes(), radix)?),
102 })
103 }
104
105 pub(crate) fn from_js_string(string: &JsString) -> Option<JsBigInt> {
112 JsBigInt::from_string(string.to_std_string().ok().as_ref()?)
119 }
120
121 #[inline]
128 #[must_use]
129 pub fn from_string(mut string: &str) -> Option<Self> {
130 string = string.trim();
131
132 if string.is_empty() {
133 return Some(Self::zero());
134 }
135
136 let mut radix = 10;
137 if string.starts_with("0b") || string.starts_with("0B") {
138 radix = 2;
139 string = &string[2..];
140 } else if string.starts_with("0x") || string.starts_with("0X") {
141 radix = 16;
142 string = &string[2..];
143 } else if string.starts_with("0o") || string.starts_with("0O") {
144 radix = 8;
145 string = &string[2..];
146 }
147
148 Self::from_string_radix(string, radix)
149 }
150
151 #[inline]
158 #[must_use]
159 pub fn same_value_zero(x: &Self, y: &Self) -> bool {
160 Self::equal(x, y)
162 }
163
164 #[inline]
172 #[must_use]
173 pub fn same_value(x: &Self, y: &Self) -> bool {
174 Self::equal(x, y)
176 }
177
178 #[inline]
188 #[must_use]
189 pub fn equal(x: &Self, y: &Self) -> bool {
190 x == y
191 }
192
193 #[inline]
195 pub fn pow(x: &Self, y: &Self) -> JsResult<Self> {
196 let y = y
197 .inner
198 .to_biguint()
199 .ok_or_else(|| JsNativeError::range().with_message("BigInt negative exponent"))?;
200
201 let num_bits = (x.inner.bits() as f64
202 * y.to_f64().expect("Unable to convert from BigUInt to f64"))
203 .floor()
204 + 1f64;
205
206 if num_bits > 1_000_000_000f64 {
207 return Err(JsNativeError::range()
208 .with_message("Maximum BigInt size exceeded")
209 .into());
210 }
211
212 Ok(Self::new(x.inner.as_ref().clone().pow(y)))
213 }
214
215 #[inline]
217 pub fn shift_right(x: &Self, y: &Self) -> JsResult<Self> {
218 match y.inner.to_i32() {
219 Some(n) if n > 0 => Ok(Self::new(x.inner.as_ref().clone().shr(n as usize))),
220 Some(n) => Ok(Self::new(x.inner.as_ref().clone().shl(n.unsigned_abs()))),
221 None => Err(JsNativeError::range()
222 .with_message("Maximum BigInt size exceeded")
223 .into()),
224 }
225 }
226
227 #[inline]
229 pub fn shift_left(x: &Self, y: &Self) -> JsResult<Self> {
230 match y.inner.to_i32() {
231 Some(n) if n > 0 => Ok(Self::new(x.inner.as_ref().clone().shl(n as usize))),
232 Some(n) => Ok(Self::new(x.inner.as_ref().clone().shr(n.unsigned_abs()))),
233 None => Err(JsNativeError::range()
234 .with_message("Maximum BigInt size exceeded")
235 .into()),
236 }
237 }
238
239 #[inline]
248 #[must_use]
249 pub fn mod_floor(x: &Self, y: &Self) -> Self {
250 Self::new(x.inner.mod_floor(&y.inner))
251 }
252
253 #[inline]
255 #[must_use]
256 pub fn add(x: &Self, y: &Self) -> Self {
257 Self::new(x.inner.as_ref().clone().add(y.inner.as_ref()))
258 }
259
260 #[inline]
262 #[must_use]
263 pub fn sub(x: &Self, y: &Self) -> Self {
264 Self::new(x.inner.as_ref().clone().sub(y.inner.as_ref()))
265 }
266
267 #[inline]
269 #[must_use]
270 pub fn mul(x: &Self, y: &Self) -> Self {
271 Self::new(x.inner.as_ref().clone().mul(y.inner.as_ref()))
272 }
273
274 #[inline]
276 #[must_use]
277 pub fn div(x: &Self, y: &Self) -> Self {
278 Self::new(x.inner.as_ref().clone().div(y.inner.as_ref()))
279 }
280
281 #[inline]
283 #[must_use]
284 pub fn rem(x: &Self, y: &Self) -> Self {
285 Self::new(x.inner.as_ref().clone().rem(y.inner.as_ref()))
286 }
287
288 #[inline]
290 #[must_use]
291 pub fn bitand(x: &Self, y: &Self) -> Self {
292 Self::new(x.inner.as_ref().clone().bitand(y.inner.as_ref()))
293 }
294
295 #[inline]
297 #[must_use]
298 pub fn bitor(x: &Self, y: &Self) -> Self {
299 Self::new(x.inner.as_ref().clone().bitor(y.inner.as_ref()))
300 }
301
302 #[inline]
304 #[must_use]
305 pub fn bitxor(x: &Self, y: &Self) -> Self {
306 Self::new(x.inner.as_ref().clone().bitxor(y.inner.as_ref()))
307 }
308
309 #[inline]
311 #[must_use]
312 pub fn neg(x: &Self) -> Self {
313 Self::new(x.as_inner().neg())
314 }
315
316 #[inline]
318 #[must_use]
319 pub fn not(x: &Self) -> Self {
320 Self::new(!x.as_inner())
321 }
322
323 #[inline]
325 #[must_use]
326 pub fn as_inner(&self) -> &RawBigInt {
327 &self.inner
328 }
329
330 #[inline]
335 #[must_use]
336 #[allow(unused, reason = "only used in nan-boxed implementation of JsValue")]
337 pub(crate) fn into_raw(self) -> *const RawBigInt {
338 Rc::into_raw(self.inner)
339 }
340
341 #[inline]
351 #[must_use]
352 #[allow(unused, reason = "only used in nan-boxed implementation of JsValue")]
353 pub(crate) unsafe fn from_raw(ptr: *const RawBigInt) -> Self {
354 Self {
355 inner: unsafe { Rc::from_raw(ptr) },
357 }
358 }
359}
360
361impl Display for JsBigInt {
362 #[inline]
363 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
364 Display::fmt(&self.inner, f)
365 }
366}
367
368impl From<RawBigInt> for JsBigInt {
369 #[inline]
370 fn from(value: RawBigInt) -> Self {
371 Self {
372 inner: Rc::new(value),
373 }
374 }
375}
376
377impl From<Box<RawBigInt>> for JsBigInt {
378 #[inline]
379 fn from(value: Box<RawBigInt>) -> Self {
380 Self {
381 inner: value.into(),
382 }
383 }
384}
385
386impl From<i8> for JsBigInt {
387 #[inline]
388 fn from(value: i8) -> Self {
389 Self {
390 inner: Rc::new(RawBigInt::from(value)),
391 }
392 }
393}
394
395impl From<u8> for JsBigInt {
396 #[inline]
397 fn from(value: u8) -> Self {
398 Self {
399 inner: Rc::new(RawBigInt::from(value)),
400 }
401 }
402}
403
404impl From<i16> for JsBigInt {
405 #[inline]
406 fn from(value: i16) -> Self {
407 Self {
408 inner: Rc::new(RawBigInt::from(value)),
409 }
410 }
411}
412
413impl From<u16> for JsBigInt {
414 #[inline]
415 fn from(value: u16) -> Self {
416 Self {
417 inner: Rc::new(RawBigInt::from(value)),
418 }
419 }
420}
421
422impl From<i32> for JsBigInt {
423 #[inline]
424 fn from(value: i32) -> Self {
425 Self {
426 inner: Rc::new(RawBigInt::from(value)),
427 }
428 }
429}
430
431impl From<u32> for JsBigInt {
432 #[inline]
433 fn from(value: u32) -> Self {
434 Self {
435 inner: Rc::new(RawBigInt::from(value)),
436 }
437 }
438}
439
440impl From<i64> for JsBigInt {
441 #[inline]
442 fn from(value: i64) -> Self {
443 Self {
444 inner: Rc::new(RawBigInt::from(value)),
445 }
446 }
447}
448
449impl From<u64> for JsBigInt {
450 #[inline]
451 fn from(value: u64) -> Self {
452 Self {
453 inner: Rc::new(RawBigInt::from(value)),
454 }
455 }
456}
457
458impl From<i128> for JsBigInt {
459 #[inline]
460 fn from(value: i128) -> Self {
461 Self {
462 inner: Rc::new(RawBigInt::from(value)),
463 }
464 }
465}
466
467impl From<u128> for JsBigInt {
468 #[inline]
469 fn from(value: u128) -> Self {
470 Self {
471 inner: Rc::new(RawBigInt::from(value)),
472 }
473 }
474}
475
476impl From<isize> for JsBigInt {
477 #[inline]
478 fn from(value: isize) -> Self {
479 Self {
480 inner: Rc::new(RawBigInt::from(value)),
481 }
482 }
483}
484
485impl From<usize> for JsBigInt {
486 #[inline]
487 fn from(value: usize) -> Self {
488 Self {
489 inner: Rc::new(RawBigInt::from(value)),
490 }
491 }
492}
493
494#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
496pub struct TryFromF64Error;
497
498impl Display for TryFromF64Error {
499 #[inline]
500 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
501 write!(f, "Could not convert f64 value to a BigInt type")
502 }
503}
504
505impl TryFrom<f64> for JsBigInt {
506 type Error = TryFromF64Error;
507
508 #[inline]
509 fn try_from(n: f64) -> Result<Self, Self::Error> {
510 if !Number::equal(n.trunc(), n) {
514 return Err(TryFromF64Error);
515 }
516 RawBigInt::from_f64(n).map_or(Err(TryFromF64Error), |bigint| Ok(Self::new(bigint)))
517 }
518}
519
520impl PartialEq<i32> for JsBigInt {
521 #[inline]
522 fn eq(&self, other: &i32) -> bool {
523 self.inner.as_ref() == &RawBigInt::from(*other)
524 }
525}
526
527impl PartialEq<JsBigInt> for i32 {
528 #[inline]
529 fn eq(&self, other: &JsBigInt) -> bool {
530 &RawBigInt::from(*self) == other.inner.as_ref()
531 }
532}
533
534impl PartialEq<f64> for JsBigInt {
535 #[inline]
536 fn eq(&self, other: &f64) -> bool {
537 other.fract().is_zero()
538 && RawBigInt::from_f64(*other).is_some_and(|bigint| self.inner.as_ref() == &bigint)
539 }
540}
541
542impl PartialEq<JsBigInt> for f64 {
543 #[inline]
544 fn eq(&self, other: &JsBigInt) -> bool {
545 self.fract().is_zero()
546 && RawBigInt::from_f64(*self).is_some_and(|bigint| other.inner.as_ref() == &bigint)
547 }
548}