1use 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
20pub type RawBigInt = num_bigint::BigInt;
22
23#[cfg(feature = "deser")]
24use serde::{Deserialize, Serialize};
25
26#[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
33unsafe impl Trace for JsBigInt {
36 empty_trace!();
37}
38
39impl JsBigInt {
40 #[inline]
42 pub fn new<T: Into<Self>>(value: T) -> Self {
43 value.into()
44 }
45
46 #[inline]
48 pub fn zero() -> Self {
49 Self {
50 inner: Rc::new(RawBigInt::zero()),
51 }
52 }
53
54 #[inline]
56 pub fn is_zero(&self) -> bool {
57 self.inner.is_zero()
58 }
59
60 #[inline]
62 pub fn one() -> Self {
63 Self {
64 inner: Rc::new(RawBigInt::one()),
65 }
66 }
67
68 #[inline]
70 pub fn is_one(&self) -> bool {
71 self.inner.is_one()
72 }
73
74 #[inline]
76 pub fn to_string_radix(&self, radix: u32) -> String {
77 self.inner.to_str_radix(radix)
78 }
79
80 #[inline]
84 pub fn to_f64(&self) -> f64 {
85 self.inner.to_f64().unwrap_or(f64::INFINITY)
86 }
87
88 #[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 #[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 #[inline]
132 pub fn same_value_zero(x: &Self, y: &Self) -> bool {
133 Self::equal(x, y)
135 }
136
137 #[inline]
145 pub fn same_value(x: &Self, y: &Self) -> bool {
146 Self::equal(x, y)
148 }
149
150 #[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 #[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 !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}