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}