1use crate::{builtins::Number, error::JsNativeError, JsData, JsResult, JsString};
4use boa_gc::{Finalize, Trace};
5use num_integer::Integer;
6use num_traits::{pow::Pow, FromPrimitive, One, ToPrimitive, Zero};
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#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))]
21#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Trace, Finalize, JsData)]
22#[boa_gc(unsafe_empty_trace)]
24pub struct JsBigInt {
25 inner: Rc<RawBigInt>,
26}
27
28impl JsBigInt {
29 #[must_use]
31 pub fn new<T: Into<Self>>(value: T) -> Self {
32 value.into()
33 }
34
35 #[inline]
37 #[must_use]
38 pub fn zero() -> Self {
39 Self {
40 inner: Rc::new(RawBigInt::zero()),
41 }
42 }
43
44 #[inline]
46 #[must_use]
47 pub fn is_zero(&self) -> bool {
48 self.inner.is_zero()
49 }
50
51 #[inline]
53 #[must_use]
54 pub fn one() -> Self {
55 Self {
56 inner: Rc::new(RawBigInt::one()),
57 }
58 }
59
60 #[inline]
62 #[must_use]
63 pub fn is_one(&self) -> bool {
64 self.inner.is_one()
65 }
66
67 #[inline]
69 #[must_use]
70 pub fn to_string_radix(&self, radix: u32) -> String {
71 self.inner.to_str_radix(radix)
72 }
73
74 #[inline]
78 #[must_use]
79 pub fn to_f64(&self) -> f64 {
80 self.inner.to_f64().unwrap_or(f64::INFINITY)
81 }
82
83 #[inline]
85 #[must_use]
86 pub fn from_string_radix(buf: &str, radix: u32) -> Option<Self> {
87 Some(Self {
88 inner: Rc::new(RawBigInt::parse_bytes(buf.as_bytes(), radix)?),
89 })
90 }
91
92 pub(crate) fn from_js_string(string: &JsString) -> Option<JsBigInt> {
99 JsBigInt::from_string(string.to_std_string().ok().as_ref()?)
106 }
107
108 #[inline]
115 #[must_use]
116 pub fn from_string(mut string: &str) -> Option<Self> {
117 string = string.trim();
118
119 if string.is_empty() {
120 return Some(Self::zero());
121 }
122
123 let mut radix = 10;
124 if string.starts_with("0b") || string.starts_with("0B") {
125 radix = 2;
126 string = &string[2..];
127 } else if string.starts_with("0x") || string.starts_with("0X") {
128 radix = 16;
129 string = &string[2..];
130 } else if string.starts_with("0o") || string.starts_with("0O") {
131 radix = 8;
132 string = &string[2..];
133 }
134
135 Self::from_string_radix(string, radix)
136 }
137
138 #[inline]
145 #[must_use]
146 pub fn same_value_zero(x: &Self, y: &Self) -> bool {
147 Self::equal(x, y)
149 }
150
151 #[inline]
159 #[must_use]
160 pub fn same_value(x: &Self, y: &Self) -> bool {
161 Self::equal(x, y)
163 }
164
165 #[inline]
175 #[must_use]
176 pub fn equal(x: &Self, y: &Self) -> bool {
177 x == y
178 }
179
180 #[inline]
182 pub fn pow(x: &Self, y: &Self) -> JsResult<Self> {
183 let y = y
184 .inner
185 .to_biguint()
186 .ok_or_else(|| JsNativeError::range().with_message("BigInt negative exponent"))?;
187
188 let num_bits = (x.inner.bits() as f64
189 * y.to_f64().expect("Unable to convert from BigUInt to f64"))
190 .floor()
191 + 1f64;
192
193 if num_bits > 1_000_000_000f64 {
194 return Err(JsNativeError::range()
195 .with_message("Maximum BigInt size exceeded")
196 .into());
197 }
198
199 Ok(Self::new(x.inner.as_ref().clone().pow(y)))
200 }
201
202 #[inline]
204 pub fn shift_right(x: &Self, y: &Self) -> JsResult<Self> {
205 match y.inner.to_i32() {
206 Some(n) if n > 0 => Ok(Self::new(x.inner.as_ref().clone().shr(n as usize))),
207 Some(n) => Ok(Self::new(x.inner.as_ref().clone().shl(n.unsigned_abs()))),
208 None => Err(JsNativeError::range()
209 .with_message("Maximum BigInt size exceeded")
210 .into()),
211 }
212 }
213
214 #[inline]
216 pub fn shift_left(x: &Self, y: &Self) -> JsResult<Self> {
217 match y.inner.to_i32() {
218 Some(n) if n > 0 => Ok(Self::new(x.inner.as_ref().clone().shl(n as usize))),
219 Some(n) => Ok(Self::new(x.inner.as_ref().clone().shr(n.unsigned_abs()))),
220 None => Err(JsNativeError::range()
221 .with_message("Maximum BigInt size exceeded")
222 .into()),
223 }
224 }
225
226 #[inline]
235 #[must_use]
236 pub fn mod_floor(x: &Self, y: &Self) -> Self {
237 Self::new(x.inner.mod_floor(&y.inner))
238 }
239
240 #[inline]
242 #[must_use]
243 pub fn add(x: &Self, y: &Self) -> Self {
244 Self::new(x.inner.as_ref().clone().add(y.inner.as_ref()))
245 }
246
247 #[inline]
249 #[must_use]
250 pub fn sub(x: &Self, y: &Self) -> Self {
251 Self::new(x.inner.as_ref().clone().sub(y.inner.as_ref()))
252 }
253
254 #[inline]
256 #[must_use]
257 pub fn mul(x: &Self, y: &Self) -> Self {
258 Self::new(x.inner.as_ref().clone().mul(y.inner.as_ref()))
259 }
260
261 #[inline]
263 #[must_use]
264 pub fn div(x: &Self, y: &Self) -> Self {
265 Self::new(x.inner.as_ref().clone().div(y.inner.as_ref()))
266 }
267
268 #[inline]
270 #[must_use]
271 pub fn rem(x: &Self, y: &Self) -> Self {
272 Self::new(x.inner.as_ref().clone().rem(y.inner.as_ref()))
273 }
274
275 #[inline]
277 #[must_use]
278 pub fn bitand(x: &Self, y: &Self) -> Self {
279 Self::new(x.inner.as_ref().clone().bitand(y.inner.as_ref()))
280 }
281
282 #[inline]
284 #[must_use]
285 pub fn bitor(x: &Self, y: &Self) -> Self {
286 Self::new(x.inner.as_ref().clone().bitor(y.inner.as_ref()))
287 }
288
289 #[inline]
291 #[must_use]
292 pub fn bitxor(x: &Self, y: &Self) -> Self {
293 Self::new(x.inner.as_ref().clone().bitxor(y.inner.as_ref()))
294 }
295
296 #[inline]
298 #[must_use]
299 pub fn neg(x: &Self) -> Self {
300 Self::new(x.as_inner().neg())
301 }
302
303 #[inline]
305 #[must_use]
306 pub fn not(x: &Self) -> Self {
307 Self::new(!x.as_inner())
308 }
309
310 pub(crate) fn as_inner(&self) -> &RawBigInt {
311 &self.inner
312 }
313}
314
315impl Display for JsBigInt {
316 #[inline]
317 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
318 Display::fmt(&self.inner, f)
319 }
320}
321
322impl From<RawBigInt> for JsBigInt {
323 #[inline]
324 fn from(value: RawBigInt) -> Self {
325 Self {
326 inner: Rc::new(value),
327 }
328 }
329}
330
331impl From<Box<RawBigInt>> for JsBigInt {
332 #[inline]
333 fn from(value: Box<RawBigInt>) -> Self {
334 Self {
335 inner: value.into(),
336 }
337 }
338}
339
340impl From<i8> for JsBigInt {
341 #[inline]
342 fn from(value: i8) -> Self {
343 Self {
344 inner: Rc::new(RawBigInt::from(value)),
345 }
346 }
347}
348
349impl From<u8> for JsBigInt {
350 #[inline]
351 fn from(value: u8) -> Self {
352 Self {
353 inner: Rc::new(RawBigInt::from(value)),
354 }
355 }
356}
357
358impl From<i16> for JsBigInt {
359 #[inline]
360 fn from(value: i16) -> Self {
361 Self {
362 inner: Rc::new(RawBigInt::from(value)),
363 }
364 }
365}
366
367impl From<u16> for JsBigInt {
368 #[inline]
369 fn from(value: u16) -> Self {
370 Self {
371 inner: Rc::new(RawBigInt::from(value)),
372 }
373 }
374}
375
376impl From<i32> for JsBigInt {
377 #[inline]
378 fn from(value: i32) -> Self {
379 Self {
380 inner: Rc::new(RawBigInt::from(value)),
381 }
382 }
383}
384
385impl From<u32> for JsBigInt {
386 #[inline]
387 fn from(value: u32) -> Self {
388 Self {
389 inner: Rc::new(RawBigInt::from(value)),
390 }
391 }
392}
393
394impl From<i64> for JsBigInt {
395 #[inline]
396 fn from(value: i64) -> Self {
397 Self {
398 inner: Rc::new(RawBigInt::from(value)),
399 }
400 }
401}
402
403impl From<u64> for JsBigInt {
404 #[inline]
405 fn from(value: u64) -> Self {
406 Self {
407 inner: Rc::new(RawBigInt::from(value)),
408 }
409 }
410}
411
412impl From<i128> for JsBigInt {
413 #[inline]
414 fn from(value: i128) -> Self {
415 Self {
416 inner: Rc::new(RawBigInt::from(value)),
417 }
418 }
419}
420
421impl From<u128> for JsBigInt {
422 #[inline]
423 fn from(value: u128) -> Self {
424 Self {
425 inner: Rc::new(RawBigInt::from(value)),
426 }
427 }
428}
429
430impl From<isize> for JsBigInt {
431 #[inline]
432 fn from(value: isize) -> Self {
433 Self {
434 inner: Rc::new(RawBigInt::from(value)),
435 }
436 }
437}
438
439impl From<usize> for JsBigInt {
440 #[inline]
441 fn from(value: usize) -> Self {
442 Self {
443 inner: Rc::new(RawBigInt::from(value)),
444 }
445 }
446}
447
448#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
450pub struct TryFromF64Error;
451
452impl Display for TryFromF64Error {
453 #[inline]
454 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
455 write!(f, "Could not convert f64 value to a BigInt type")
456 }
457}
458
459impl TryFrom<f64> for JsBigInt {
460 type Error = TryFromF64Error;
461
462 #[inline]
463 fn try_from(n: f64) -> Result<Self, Self::Error> {
464 if !Number::equal(n.trunc(), n) {
468 return Err(TryFromF64Error);
469 }
470 RawBigInt::from_f64(n).map_or(Err(TryFromF64Error), |bigint| Ok(Self::new(bigint)))
471 }
472}
473
474impl PartialEq<i32> for JsBigInt {
475 #[inline]
476 fn eq(&self, other: &i32) -> bool {
477 self.inner.as_ref() == &RawBigInt::from(*other)
478 }
479}
480
481impl PartialEq<JsBigInt> for i32 {
482 #[inline]
483 fn eq(&self, other: &JsBigInt) -> bool {
484 &RawBigInt::from(*self) == other.inner.as_ref()
485 }
486}
487
488impl PartialEq<f64> for JsBigInt {
489 #[inline]
490 fn eq(&self, other: &f64) -> bool {
491 other.fract().is_zero()
492 && RawBigInt::from_f64(*other).map_or(false, |bigint| self.inner.as_ref() == &bigint)
493 }
494}
495
496impl PartialEq<JsBigInt> for f64 {
497 #[inline]
498 fn eq(&self, other: &JsBigInt) -> bool {
499 self.fract().is_zero()
500 && RawBigInt::from_f64(*self).map_or(false, |bigint| other.inner.as_ref() == &bigint)
501 }
502}