neon/types_impl/bigint.rs
1//! Types for working with [`JsBigInt`].
2
3use std::{error, fmt, mem::MaybeUninit};
4
5use crate::{
6 context::{
7 internal::{ContextInternal, Env},
8 Context, Cx,
9 },
10 handle::{internal::TransparentNoCopyWrapper, Handle},
11 result::{NeonResult, ResultExt},
12 sys::{self, raw},
13 types::{private, JsBigInt, Value},
14};
15
16#[derive(Copy, Clone, Debug, PartialEq, Eq)]
17/// Indicates if a `JsBigInt` is positive or negative
18pub enum Sign {
19 Positive,
20 Negative,
21}
22
23#[derive(Copy, Clone, Debug, PartialEq, Eq)]
24/// Indicates a lossless conversion from a [`JsBigInt`] to a Rust integer
25/// could not be performed.
26///
27/// Failures include:
28/// * Negative sign on an unsigned int
29/// * Overflow of an int
30/// * Underflow of a signed int
31pub struct RangeError<T>(T);
32
33impl<T> RangeError<T> {
34 /// Get the lossy value read from a `BigInt`. It may be truncated,
35 /// sign extended or wrapped.
36 pub fn into_inner(self) -> T {
37 self.0
38 }
39}
40
41impl<T> fmt::Display for RangeError<T>
42where
43 T: fmt::Display,
44{
45 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
46 write!(f, "Loss of precision reading BigInt ({})", self.0)
47 }
48}
49
50impl<T> error::Error for RangeError<T> where T: fmt::Display + fmt::Debug {}
51
52impl<T, E> ResultExt<T> for Result<T, RangeError<E>>
53where
54 E: fmt::Display,
55{
56 fn or_throw<'a, C: Context<'a>>(self, cx: &mut C) -> NeonResult<T> {
57 self.or_else(|err| cx.throw_range_error(err.to_string()))
58 }
59}
60
61impl JsBigInt {
62 pub const POSITIVE: Sign = Sign::Positive;
63 pub const NEGATIVE: Sign = Sign::Negative;
64
65 /// Creates a `BigInt` from an [`i64`].
66 ///
67 /// # Example
68 ///
69 /// ```
70 /// # use neon::{prelude::*, types::JsBigInt};
71 /// # fn example(mut cx: FunctionContext) -> JsResult<JsBigInt> {
72 /// let value: Handle<JsBigInt> = JsBigInt::from_i64(&mut cx, 42);
73 /// # Ok(value)
74 /// # }
75 /// ```
76 pub fn from_i64<'cx, C>(cx: &mut C, n: i64) -> Handle<'cx, Self>
77 where
78 C: Context<'cx>,
79 {
80 let mut v = MaybeUninit::uninit();
81 let v = unsafe {
82 sys::create_bigint_int64(cx.env().to_raw(), n, v.as_mut_ptr()).unwrap();
83
84 v.assume_init()
85 };
86
87 Handle::new_internal(Self(v))
88 }
89
90 /// Creates a `BigInt` from a [`u64`].
91 ///
92 /// # Example
93 ///
94 /// ```
95 /// # use neon::{prelude::*, types::JsBigInt};
96 /// # fn example(mut cx: FunctionContext) -> JsResult<JsBigInt> {
97 /// let value: Handle<JsBigInt> = JsBigInt::from_u64(&mut cx, 42);
98 /// # Ok(value)
99 /// # }
100 /// ```
101 pub fn from_u64<'cx, C>(cx: &mut C, n: u64) -> Handle<'cx, Self>
102 where
103 C: Context<'cx>,
104 {
105 let mut v = MaybeUninit::uninit();
106 let v = unsafe {
107 sys::create_bigint_uint64(cx.env().to_raw(), n, v.as_mut_ptr()).unwrap();
108
109 v.assume_init()
110 };
111
112 Handle::new_internal(Self(v))
113 }
114
115 // Internal helper for creating a _signed_ `BigInt` from a [`u128`] magnitude
116 fn from_u128_sign<'cx, C>(cx: &mut C, sign: Sign, n: u128) -> Handle<'cx, Self>
117 where
118 C: Context<'cx>,
119 {
120 let n = n.to_le();
121 let digits = [n as u64, (n >> 64) as u64];
122
123 Self::from_digits_le(cx, sign, &digits)
124 }
125
126 /// Creates a `BigInt` from an [`i128`].
127 ///
128 /// # Example
129 ///
130 /// ```
131 /// # use neon::{prelude::*, types::JsBigInt};
132 /// # fn example(mut cx: FunctionContext) -> JsResult<JsBigInt> {
133 /// let value: Handle<JsBigInt> = JsBigInt::from_i128(&mut cx, 42);
134 /// # Ok(value)
135 /// # }
136 /// ```
137 pub fn from_i128<'cx, C>(cx: &mut C, n: i128) -> Handle<'cx, Self>
138 where
139 C: Context<'cx>,
140 {
141 if n >= 0 {
142 return Self::from_u128(cx, n as u128);
143 }
144
145 // Get the magnitude from a two's compliment negative
146 let n = u128::MAX - (n as u128) + 1;
147
148 Self::from_u128_sign(cx, Self::NEGATIVE, n)
149 }
150
151 /// Creates a `BigInt` from a [`u128`].
152 ///
153 /// # Example
154 ///
155 /// ```
156 /// # use neon::{prelude::*, types::JsBigInt};
157 /// # fn example(mut cx: FunctionContext) -> JsResult<JsBigInt> {
158 /// let value: Handle<JsBigInt> = JsBigInt::from_u128(&mut cx, 42);
159 /// # Ok(value)
160 /// # }
161 /// ```
162 pub fn from_u128<'cx, C>(cx: &mut C, n: u128) -> Handle<'cx, Self>
163 where
164 C: Context<'cx>,
165 {
166 Self::from_u128_sign(cx, Self::POSITIVE, n)
167 }
168
169 /// Creates a `BigInt` from a signed magnitude. The `BigInt` is calculated as:\
170 /// `Sign * (digit[0] x (2⁶⁴)⁰ + digit[0] x (2⁶⁴)¹ + digit[0] x (2⁶⁴)² ...)`
171 ///
172 /// # Example
173 ///
174 /// ```
175 /// # use neon::{prelude::*, types::JsBigInt};
176 /// # fn example(mut cx: FunctionContext) -> JsResult<JsBigInt> {
177 /// // Creates a `BigInt` equal to `2n ** 128n`
178 /// let value: Handle<JsBigInt> = JsBigInt::from_digits_le(
179 /// &mut cx,
180 /// JsBigInt::POSITIVE,
181 /// &[0, 0, 1],
182 /// );
183 /// # Ok(value)
184 /// # }
185 /// ```
186 //
187 // XXX: It's unclear if individual digits are expected to be little endian or native.
188 // The current code assumes _native_. Neon modules are currently broken on big-endian
189 // platforms. If this is fixed in the future, unit tests will determine if this
190 // assumption is accurate.
191 pub fn from_digits_le<'cx, C>(cx: &mut C, sign: Sign, digits: &[u64]) -> Handle<'cx, Self>
192 where
193 C: Context<'cx>,
194 {
195 let sign_bit = match sign {
196 Sign::Positive => 0,
197 Sign::Negative => 1,
198 };
199
200 let mut v = MaybeUninit::uninit();
201 let v = unsafe {
202 sys::create_bigint_words(
203 cx.env().to_raw(),
204 sign_bit,
205 digits.len(),
206 digits.as_ptr(),
207 v.as_mut_ptr(),
208 )
209 .unwrap();
210
211 v.assume_init()
212 };
213
214 Handle::new_internal(Self(v))
215 }
216
217 /// Reads an `i64` from a `BigInt`.
218 ///
219 /// Fails on overflow and underflow.
220 ///
221 /// # Example
222 ///
223 /// See [`JsBigInt`].
224 pub fn to_i64<'cx, C>(&self, cx: &mut C) -> Result<i64, RangeError<i64>>
225 where
226 C: Context<'cx>,
227 {
228 let mut n = 0;
229 let mut lossless = false;
230
231 unsafe {
232 sys::get_value_bigint_int64(cx.env().to_raw(), self.0, &mut n, &mut lossless).unwrap();
233 }
234
235 if lossless {
236 Ok(n)
237 } else {
238 Err(RangeError(n))
239 }
240 }
241
242 /// Reads a `u64` from a `BigInt`.
243 ///
244 /// Fails on overflow or a negative sign.
245 pub fn to_u64<'cx, C>(&self, cx: &mut C) -> Result<u64, RangeError<u64>>
246 where
247 C: Context<'cx>,
248 {
249 let mut n = 0;
250 let mut lossless = false;
251
252 unsafe {
253 sys::get_value_bigint_uint64(cx.env().to_raw(), self.0, &mut n, &mut lossless).unwrap();
254 }
255
256 if lossless {
257 Ok(n)
258 } else {
259 Err(RangeError(n))
260 }
261 }
262
263 /// Reads an `i128` from a `BigInt`.
264 ///
265 /// Fails on overflow and underflow.
266 pub fn to_i128<'cx, C>(&self, cx: &mut C) -> Result<i128, RangeError<i128>>
267 where
268 C: Context<'cx>,
269 {
270 let mut digits = [0; 2];
271 let (sign, num_digits) = self.read_digits_le(cx, &mut digits);
272
273 // Cast digits into a `u128` magnitude
274 let n = (digits[0] as u128) | ((digits[1] as u128) << 64);
275 let n = u128::from_le(n);
276
277 // Verify that the magnitude leaves room for the sign bit
278 let n = match sign {
279 Sign::Positive => {
280 if n > (i128::MAX as u128) {
281 return Err(RangeError(i128::MAX));
282 } else {
283 n as i128
284 }
285 }
286 Sign::Negative => {
287 if n > (i128::MAX as u128) + 1 {
288 return Err(RangeError(i128::MIN));
289 } else {
290 (n as i128).wrapping_neg()
291 }
292 }
293 };
294
295 // Leading zeroes are truncated and never returned. If there are additional
296 // digits, the number is out of range.
297 if num_digits > digits.len() {
298 Err(RangeError(n))
299 } else {
300 Ok(n)
301 }
302 }
303
304 /// Reads a `u128` from a `BigInt`.
305 ///
306 /// Fails on overflow or a negative sign.
307 pub fn to_u128<'cx, C>(&self, cx: &mut C) -> Result<u128, RangeError<u128>>
308 where
309 C: Context<'cx>,
310 {
311 let mut digits = [0; 2];
312 let (sign, num_digits) = self.read_digits_le(cx, &mut digits);
313
314 // Cast digits into a `u128` magnitude
315 let n = (digits[0] as u128) | ((digits[1] as u128) << 64);
316 let n = u128::from_le(n);
317
318 // Leading zeroes are truncated and never returned. If there are additional
319 // digits, the number is out of range.
320 if matches!(sign, Sign::Negative) || num_digits > digits.len() {
321 Err(RangeError(n))
322 } else {
323 Ok(n)
324 }
325 }
326
327 /// Gets a signed magnitude pair from a `BigInt`.
328 ///
329 /// The `BigInt` is calculated as:\
330 /// `Sign * (digit[0] x (2⁶⁴)⁰ + digit[0] x (2⁶⁴)¹ + digit[0] x (2⁶⁴)² ...)`
331 pub fn to_digits_le<'cx, C>(&self, cx: &mut C) -> (Sign, Vec<u64>)
332 where
333 C: Context<'cx>,
334 {
335 let mut v = vec![0; self.len(cx)];
336 let (sign, len) = self.read_digits_le(cx, &mut v);
337
338 // It shouldn't be possible for the number of digits to change. If it
339 // it does, it's a correctness issue and not a soundness bug.
340 debug_assert_eq!(v.len(), len);
341
342 (sign, v)
343 }
344
345 /// Gets the sign from a `BigInt` and reads digits into a buffer.
346 /// The returned `usize` is the total number of digits in the `BigInt`.
347 ///
348 /// # Example
349 ///
350 /// Read a `u256` from a `BigInt`.
351 ///
352 /// ```
353 /// # use std::error::Error;
354 /// # use neon::{prelude::*, types::JsBigInt};
355 /// fn bigint_to_u256(cx: &mut FunctionContext, n: Handle<JsBigInt>) -> NeonResult<[u64; 4]> {
356 /// let mut digits = [0; 4];
357 /// let (sign, num_digits) = n.read_digits_le(cx, &mut digits);
358 ///
359 /// if sign == JsBigInt::NEGATIVE {
360 /// return cx.throw_error("Underflow reading u256 from BigInt");
361 /// }
362 ///
363 /// if num_digits > digits.len() {
364 /// return cx.throw_error("Overflow reading u256 from BigInt");
365 /// }
366 ///
367 /// Ok(digits)
368 /// }
369 /// ```
370 pub fn read_digits_le<'cx, C>(&self, cx: &mut C, digits: &mut [u64]) -> (Sign, usize)
371 where
372 C: Context<'cx>,
373 {
374 let mut sign_bit = 0;
375 let mut word_count = digits.len();
376
377 unsafe {
378 sys::get_value_bigint_words(
379 cx.env().to_raw(),
380 self.0,
381 &mut sign_bit,
382 &mut word_count,
383 digits.as_mut_ptr(),
384 )
385 .unwrap();
386 }
387
388 let sign = if sign_bit == 0 {
389 Sign::Positive
390 } else {
391 Sign::Negative
392 };
393
394 (sign, word_count)
395 }
396
397 /// Gets the number of `u64` digits in a `BigInt`
398 pub fn len<'cx, C>(&self, cx: &mut C) -> usize
399 where
400 C: Context<'cx>,
401 {
402 // Get the length by reading into an empty slice and ignoring the sign
403 self.read_digits_le(cx, &mut []).1
404 }
405}
406
407impl Value for JsBigInt {}
408
409unsafe impl TransparentNoCopyWrapper for JsBigInt {
410 type Inner = raw::Local;
411
412 fn into_inner(self) -> Self::Inner {
413 self.0
414 }
415}
416
417impl private::ValueInternal for JsBigInt {
418 fn name() -> &'static str {
419 "BigInt"
420 }
421
422 fn is_typeof<Other: Value>(cx: &mut Cx, other: &Other) -> bool {
423 unsafe { sys::tag::is_bigint(cx.env().to_raw(), other.to_local()) }
424 }
425
426 fn to_local(&self) -> raw::Local {
427 self.0
428 }
429
430 unsafe fn from_local(_env: Env, h: raw::Local) -> Self {
431 Self(h)
432 }
433}