Skip to main content

napi/bindgen_runtime/js_values/
bigint.rs

1/// We don't implement `FromNapiValue` for `i64` `u64` `i128` `u128` `isize` `usize` here
2/// Because converting directly from `JsBigInt` to these values may result in a loss of precision and thus unintended behavior
3/// ```rust
4/// use napi::{bindgen_prelude::*, JsBigint};
5///
6/// #[napi]
7/// fn bigint_add(mut a: Bigint, mut b: Bigint) -> u128 {
8///     a.get_u128().1 + b.get_u128().1 // We have opportunity to check if the `u128` has lost precision
9/// }
10/// ```
11use std::{cmp::max, ptr};
12
13use crate::{check_status, sys};
14
15use super::{FromNapiValue, ToNapiValue, TypeName, ValidateNapiValue};
16
17/// i64 is converted to `Number`
18#[repr(transparent)]
19#[allow(non_camel_case_types)]
20pub struct i64n(pub i64);
21
22/// <https://nodejs.org/api/n-api.html#napi_create_bigint_words>
23/// The resulting BigInt is calculated as: (–1)^sign_bit (words\[0\] × (2^64)^0 + words\[1\] × (2^64)^1 + …)
24#[derive(Debug, Clone, Eq)]
25pub struct BigInt {
26  /// true for negative numbers
27  pub sign_bit: bool,
28  pub words: Vec<u64>,
29}
30
31impl PartialEq for BigInt {
32  fn eq(&self, other: &Self) -> bool {
33    for i in 0..max(self.words.len(), other.words.len()) {
34      if self.words.get(i).unwrap_or(&0) != other.words.get(i).unwrap_or(&0) {
35        return false;
36      }
37    }
38    self.sign_bit == other.sign_bit
39  }
40}
41
42impl TypeName for BigInt {
43  fn type_name() -> &'static str {
44    "BigInt"
45  }
46
47  fn value_type() -> crate::ValueType {
48    crate::ValueType::BigInt
49  }
50}
51
52impl ValidateNapiValue for BigInt {}
53
54impl FromNapiValue for BigInt {
55  unsafe fn from_napi_value(env: sys::napi_env, napi_val: sys::napi_value) -> crate::Result<Self> {
56    let mut word_count = 0usize;
57    check_status!(unsafe {
58      sys::napi_get_value_bigint_words(
59        env,
60        napi_val,
61        ptr::null_mut(),
62        &mut word_count,
63        ptr::null_mut(),
64      )
65    })?;
66    let mut words: Vec<u64> = Vec::with_capacity(word_count);
67    let mut sign_bit = 0;
68
69    unsafe {
70      check_status!(sys::napi_get_value_bigint_words(
71        env,
72        napi_val,
73        &mut sign_bit,
74        &mut word_count,
75        words.as_mut_ptr(),
76      ))?;
77
78      words.set_len(word_count);
79    }
80    if word_count == 0 {
81      words = vec![0];
82    }
83    Ok(BigInt {
84      sign_bit: sign_bit == 1,
85      words,
86    })
87  }
88}
89
90impl BigInt {
91  /// (signed, value, lossless)
92  /// get the first word of the BigInt as `u64`
93  /// return true in the last element of tuple if the value is lossless
94  /// or the value is truncated
95  pub fn get_u64(&self) -> (bool, u64, bool) {
96    (
97      self.sign_bit,
98      self.words[0],
99      !self.sign_bit && self.words.len() == 1,
100    )
101  }
102
103  /// (value, lossless)
104  /// get the first word of the BigInt as `i64` with the sign applied
105  /// return true if the value is lossless
106  /// or the value is truncated
107  pub fn get_i64(&self) -> (i64, bool) {
108    if self.sign_bit && self.words[0] == i64::MIN.unsigned_abs() {
109      return (i64::MIN, self.words.len() == 1);
110    }
111    (
112      self.words[0] as i64 * if self.sign_bit { -1 } else { 1 },
113      self.words.len() == 1 && self.words[0] as i64 >= 0,
114    )
115  }
116
117  /// (value, lossless)
118  /// get the first two words of the BigInt as `i128` with the sign applied
119  /// return true if the value is lossless
120  /// or the value is truncated
121  pub fn get_i128(&self) -> (i128, bool) {
122    let len = self.words.len();
123    if len == 1 {
124      (
125        self.words[0] as i128 * if self.sign_bit { -1 } else { 1 },
126        true,
127      )
128    } else {
129      let val = self.words[0] as u128 + ((self.words[1] as u128) << 64);
130      if self.sign_bit && val == i128::MIN.unsigned_abs() {
131        return (i128::MIN, len > 2);
132      }
133      (
134        val as i128 * if self.sign_bit { -1 } else { 1 },
135        len == 2 && self.words[1] as i64 >= 0,
136      )
137    }
138  }
139
140  /// (signed, value, lossless)
141  /// get the first two words of the BigInt as `u128`
142  /// return true if the value is lossless
143  /// or the value is truncated
144  pub fn get_u128(&self) -> (bool, u128, bool) {
145    let len = self.words.len();
146    if len == 1 {
147      (self.sign_bit, self.words[0] as u128, true)
148    } else {
149      let val = self.words[0] as u128 + ((self.words[1] as u128) << 64);
150      (self.sign_bit, val, len == 2)
151    }
152  }
153}
154
155impl ToNapiValue for BigInt {
156  unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> crate::Result<sys::napi_value> {
157    let mut raw_value = ptr::null_mut();
158    let len = val.words.len();
159    check_status!(unsafe {
160      sys::napi_create_bigint_words(
161        env,
162        match val.sign_bit {
163          true => 1,
164          false => 0,
165        },
166        len,
167        val.words.as_ptr(),
168        &mut raw_value,
169      )
170    })?;
171    Ok(raw_value)
172  }
173}
174
175pub(crate) unsafe fn u128_with_sign_to_napi_value(
176  env: sys::napi_env,
177  val: u128,
178  sign_bit: i32,
179) -> crate::Result<sys::napi_value> {
180  let mut raw_value = ptr::null_mut();
181  if cfg!(target_endian = "little") {
182    let words = &val as *const u128 as *const u64;
183    check_status!(
184      unsafe { sys::napi_create_bigint_words(env, sign_bit, 2, words, &mut raw_value) },
185      "Failed to create BigInt from u128"
186    )?;
187    return Ok(raw_value);
188  }
189
190  let arr: [u64; 2] = [val as _, (val >> 64) as _];
191  let words = &arr as *const u64;
192  check_status!(
193    unsafe { sys::napi_create_bigint_words(env, sign_bit, 2, words, &mut raw_value) },
194    "Failed to create BigInt from u128"
195  )?;
196  Ok(raw_value)
197}
198
199impl ToNapiValue for i128 {
200  unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> crate::Result<sys::napi_value> {
201    let sign_bit = i32::from(val <= 0);
202    let val = val.unsigned_abs();
203    u128_with_sign_to_napi_value(env, val, sign_bit)
204  }
205}
206
207impl ToNapiValue for &i128 {
208  unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> crate::Result<sys::napi_value> {
209    ToNapiValue::to_napi_value(env, *val)
210  }
211}
212
213impl ToNapiValue for &mut i128 {
214  unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> crate::Result<sys::napi_value> {
215    ToNapiValue::to_napi_value(env, *val)
216  }
217}
218
219impl ToNapiValue for u128 {
220  unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> crate::Result<sys::napi_value> {
221    u128_with_sign_to_napi_value(env, val, 0)
222  }
223}
224
225impl ToNapiValue for &u128 {
226  unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> crate::Result<sys::napi_value> {
227    ToNapiValue::to_napi_value(env, *val)
228  }
229}
230
231impl ToNapiValue for &mut u128 {
232  unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> crate::Result<sys::napi_value> {
233    ToNapiValue::to_napi_value(env, *val)
234  }
235}
236
237impl ToNapiValue for i64n {
238  unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> crate::Result<sys::napi_value> {
239    let mut raw_value = ptr::null_mut();
240    check_status!(unsafe { sys::napi_create_bigint_int64(env, val.0, &mut raw_value) })?;
241    Ok(raw_value)
242  }
243}
244
245impl ToNapiValue for &i64n {
246  unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> crate::Result<sys::napi_value> {
247    ToNapiValue::to_napi_value(env, i64n(val.0))
248  }
249}
250
251impl ToNapiValue for &mut i64n {
252  unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> crate::Result<sys::napi_value> {
253    ToNapiValue::to_napi_value(env, i64n(val.0))
254  }
255}
256
257impl ToNapiValue for u64 {
258  unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> crate::Result<sys::napi_value> {
259    let mut raw_value = ptr::null_mut();
260    check_status!(
261      unsafe { sys::napi_create_bigint_uint64(env, val, &mut raw_value) },
262      "Failed to create BigInt from u64"
263    )?;
264    Ok(raw_value)
265  }
266}
267
268impl ToNapiValue for &u64 {
269  unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> crate::Result<sys::napi_value> {
270    ToNapiValue::to_napi_value(env, *val)
271  }
272}
273
274impl ToNapiValue for &mut u64 {
275  unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> crate::Result<sys::napi_value> {
276    ToNapiValue::to_napi_value(env, *val)
277  }
278}
279
280impl ToNapiValue for usize {
281  unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> crate::Result<sys::napi_value> {
282    let mut raw_value = ptr::null_mut();
283    check_status!(unsafe { sys::napi_create_bigint_uint64(env, val as u64, &mut raw_value) })?;
284    Ok(raw_value)
285  }
286}
287
288impl ToNapiValue for isize {
289  unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> crate::Result<sys::napi_value> {
290    let mut raw_value = ptr::null_mut();
291    check_status!(unsafe { sys::napi_create_bigint_int64(env, val as i64, &mut raw_value) })?;
292    Ok(raw_value)
293  }
294}
295
296impl ToNapiValue for &usize {
297  unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> crate::Result<sys::napi_value> {
298    ToNapiValue::to_napi_value(env, *val)
299  }
300}
301
302impl ToNapiValue for &mut usize {
303  unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> crate::Result<sys::napi_value> {
304    ToNapiValue::to_napi_value(env, *val)
305  }
306}
307
308impl From<i64> for BigInt {
309  fn from(val: i64) -> Self {
310    BigInt {
311      sign_bit: val < 0,
312      words: vec![val.unsigned_abs()],
313    }
314  }
315}
316
317impl From<u64> for BigInt {
318  fn from(val: u64) -> Self {
319    BigInt {
320      sign_bit: false,
321      words: vec![val],
322    }
323  }
324}
325
326impl From<i128> for BigInt {
327  fn from(val: i128) -> Self {
328    let sign_bit = val < 0;
329    let val = val.unsigned_abs();
330    BigInt {
331      sign_bit,
332      words: vec![val as _, (val >> 64) as _],
333    }
334  }
335}
336
337impl From<u128> for BigInt {
338  fn from(val: u128) -> Self {
339    BigInt {
340      sign_bit: false,
341      words: vec![val as _, (val >> 64) as _],
342    }
343  }
344}
345
346#[test]
347fn test_bigint_comparison() {
348  assert_eq!(BigInt::from(1_i64), BigInt::from(1_i64));
349  assert_eq!(BigInt::from(1_i64), BigInt::from(1_i128));
350  assert_eq!(BigInt::from(1_i64), BigInt::from(1_u128));
351  assert_eq!(BigInt::from(1_i64), BigInt::from(1_u64));
352
353  assert_eq!(BigInt::from(-1_i64), BigInt::from(-1_i128));
354
355  assert_ne!(BigInt::from(1_i64), BigInt::from(-1_i128));
356  assert_ne!(BigInt::from(1_i64), BigInt::from(2_i64));
357
358  assert_eq!(BigInt::from(i128::MAX), BigInt::from(i128::MAX));
359  assert_ne!(BigInt::from(i64::MAX), BigInt::from(i128::MAX));
360}