neon/types_impl/
bigint.rs

1//! Types for working with [`JsBigInt`].
2
3use std::{error, fmt, mem::MaybeUninit};
4
5use crate::{
6    context::{
7        Context, Cx,
8        internal::{ContextInternal, Env},
9    },
10    handle::{Handle, internal::TransparentNoCopyWrapper},
11    result::{NeonResult, ResultExt},
12    sys::{self, raw},
13    types::{JsBigInt, Value, private},
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 { Ok(n) } else { Err(RangeError(n)) }
236    }
237
238    /// Reads a `u64` from a `BigInt`.
239    ///
240    /// Fails on overflow or a negative sign.
241    pub fn to_u64<'cx, C>(&self, cx: &mut C) -> Result<u64, RangeError<u64>>
242    where
243        C: Context<'cx>,
244    {
245        let mut n = 0;
246        let mut lossless = false;
247
248        unsafe {
249            sys::get_value_bigint_uint64(cx.env().to_raw(), self.0, &mut n, &mut lossless).unwrap();
250        }
251
252        if lossless { Ok(n) } else { Err(RangeError(n)) }
253    }
254
255    /// Reads an `i128` from a `BigInt`.
256    ///
257    /// Fails on overflow and underflow.
258    pub fn to_i128<'cx, C>(&self, cx: &mut C) -> Result<i128, RangeError<i128>>
259    where
260        C: Context<'cx>,
261    {
262        let mut digits = [0; 2];
263        let (sign, num_digits) = self.read_digits_le(cx, &mut digits);
264
265        // Cast digits into a `u128` magnitude
266        let n = (digits[0] as u128) | ((digits[1] as u128) << 64);
267        let n = u128::from_le(n);
268
269        // Verify that the magnitude leaves room for the sign bit
270        let n = match sign {
271            Sign::Positive => {
272                if n > (i128::MAX as u128) {
273                    return Err(RangeError(i128::MAX));
274                } else {
275                    n as i128
276                }
277            }
278            Sign::Negative => {
279                if n > (i128::MAX as u128) + 1 {
280                    return Err(RangeError(i128::MIN));
281                } else {
282                    (n as i128).wrapping_neg()
283                }
284            }
285        };
286
287        // Leading zeroes are truncated and never returned. If there are additional
288        // digits, the number is out of range.
289        if num_digits > digits.len() {
290            Err(RangeError(n))
291        } else {
292            Ok(n)
293        }
294    }
295
296    /// Reads a `u128` from a `BigInt`.
297    ///
298    /// Fails on overflow or a negative sign.
299    pub fn to_u128<'cx, C>(&self, cx: &mut C) -> Result<u128, RangeError<u128>>
300    where
301        C: Context<'cx>,
302    {
303        let mut digits = [0; 2];
304        let (sign, num_digits) = self.read_digits_le(cx, &mut digits);
305
306        // Cast digits into a `u128` magnitude
307        let n = (digits[0] as u128) | ((digits[1] as u128) << 64);
308        let n = u128::from_le(n);
309
310        // Leading zeroes are truncated and never returned. If there are additional
311        // digits, the number is out of range.
312        if matches!(sign, Sign::Negative) || num_digits > digits.len() {
313            Err(RangeError(n))
314        } else {
315            Ok(n)
316        }
317    }
318
319    /// Gets a signed magnitude pair from a `BigInt`.
320    ///
321    /// The `BigInt` is calculated as:\
322    /// `Sign * (digit[0] x (2⁶⁴)⁰ + digit[0] x (2⁶⁴)¹ + digit[0] x (2⁶⁴)² ...)`
323    pub fn to_digits_le<'cx, C>(&self, cx: &mut C) -> (Sign, Vec<u64>)
324    where
325        C: Context<'cx>,
326    {
327        let mut v = vec![0; self.len(cx)];
328        let (sign, len) = self.read_digits_le(cx, &mut v);
329
330        // It shouldn't be possible for the number of digits to change. If it
331        // it does, it's a correctness issue and not a soundness bug.
332        debug_assert_eq!(v.len(), len);
333
334        (sign, v)
335    }
336
337    /// Gets the sign from a `BigInt` and reads digits into a buffer.
338    /// The returned `usize` is the total number of digits in the `BigInt`.
339    ///
340    /// # Example
341    ///
342    /// Read a `u256` from a `BigInt`.
343    ///
344    /// ```
345    /// # use std::error::Error;
346    /// # use neon::{prelude::*, types::JsBigInt};
347    /// fn bigint_to_u256(cx: &mut FunctionContext, n: Handle<JsBigInt>) -> NeonResult<[u64; 4]> {
348    ///     let mut digits = [0; 4];
349    ///     let (sign, num_digits) = n.read_digits_le(cx, &mut digits);
350    ///
351    ///     if sign == JsBigInt::NEGATIVE {
352    ///         return cx.throw_error("Underflow reading u256 from BigInt");
353    ///     }
354    ///
355    ///     if num_digits > digits.len() {
356    ///         return cx.throw_error("Overflow reading u256 from BigInt");
357    ///     }
358    ///
359    ///     Ok(digits)
360    /// }
361    /// ```
362    pub fn read_digits_le<'cx, C>(&self, cx: &mut C, digits: &mut [u64]) -> (Sign, usize)
363    where
364        C: Context<'cx>,
365    {
366        let mut sign_bit = 0;
367        let mut word_count = digits.len();
368
369        unsafe {
370            sys::get_value_bigint_words(
371                cx.env().to_raw(),
372                self.0,
373                &mut sign_bit,
374                &mut word_count,
375                digits.as_mut_ptr(),
376            )
377            .unwrap();
378        }
379
380        let sign = if sign_bit == 0 {
381            Sign::Positive
382        } else {
383            Sign::Negative
384        };
385
386        (sign, word_count)
387    }
388
389    /// Gets the number of `u64` digits in a `BigInt`
390    pub fn len<'cx, C>(&self, cx: &mut C) -> usize
391    where
392        C: Context<'cx>,
393    {
394        // Get the length by reading into an empty slice and ignoring the sign
395        self.read_digits_le(cx, &mut []).1
396    }
397}
398
399impl Value for JsBigInt {}
400
401unsafe impl TransparentNoCopyWrapper for JsBigInt {
402    type Inner = raw::Local;
403
404    fn into_inner(self) -> Self::Inner {
405        self.0
406    }
407}
408
409impl private::ValueInternal for JsBigInt {
410    fn name() -> &'static str {
411        "BigInt"
412    }
413
414    fn is_typeof<Other: Value>(cx: &mut Cx, other: &Other) -> bool {
415        unsafe { sys::tag::is_bigint(cx.env().to_raw(), other.to_local()) }
416    }
417
418    fn to_local(&self) -> raw::Local {
419        self.0
420    }
421
422    unsafe fn from_local(_env: Env, h: raw::Local) -> Self {
423        Self(h)
424    }
425}