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::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)]
25pub struct BigInt {
26  /// true for negative numbers
27  pub sign_bit: bool,
28  pub words: Vec<u64>,
29}
30
31impl TypeName for BigInt {
32  fn type_name() -> &'static str {
33    "BigInt"
34  }
35
36  fn value_type() -> crate::ValueType {
37    crate::ValueType::BigInt
38  }
39}
40
41impl ValidateNapiValue for BigInt {}
42
43impl FromNapiValue for BigInt {
44  unsafe fn from_napi_value(env: sys::napi_env, napi_val: sys::napi_value) -> crate::Result<Self> {
45    let mut word_count = 0usize;
46    check_status!(unsafe {
47      sys::napi_get_value_bigint_words(
48        env,
49        napi_val,
50        ptr::null_mut(),
51        &mut word_count,
52        ptr::null_mut(),
53      )
54    })?;
55    let mut words: Vec<u64> = Vec::with_capacity(word_count);
56    let mut sign_bit = 0;
57
58    unsafe {
59      check_status!(sys::napi_get_value_bigint_words(
60        env,
61        napi_val,
62        &mut sign_bit,
63        &mut word_count,
64        words.as_mut_ptr(),
65      ))?;
66
67      words.set_len(word_count);
68    }
69    if word_count == 0 {
70      words = vec![0];
71    }
72    Ok(BigInt {
73      sign_bit: sign_bit == 1,
74      words,
75    })
76  }
77}
78
79impl BigInt {
80  /// (signed, value, lossless)
81  /// get the first word of the BigInt as `u64`
82  /// return true in the last element of tuple if the value is lossless
83  /// or the value is truncated
84  pub fn get_u64(&self) -> (bool, u64, bool) {
85    (
86      self.sign_bit,
87      self.words[0],
88      !self.sign_bit && self.words.len() == 1,
89    )
90  }
91
92  /// (value, lossless)
93  /// get the first word of the BigInt as `i64` with the sign applied
94  /// return true if the value is lossless
95  /// or the value is truncated
96  pub fn get_i64(&self) -> (i64, bool) {
97    if self.sign_bit && self.words[0] == i64::MIN.unsigned_abs() {
98      return (i64::MIN, self.words.len() == 1);
99    }
100    (
101      self.words[0] as i64 * if self.sign_bit { -1 } else { 1 },
102      self.words.len() == 1 && self.words[0] as i64 >= 0,
103    )
104  }
105
106  /// (value, lossless)
107  /// get the first two words of the BigInt as `i128` with the sign applied
108  /// return true if the value is lossless
109  /// or the value is truncated
110  pub fn get_i128(&self) -> (i128, bool) {
111    let len = self.words.len();
112    if len == 1 {
113      (
114        self.words[0] as i128 * if self.sign_bit { -1 } else { 1 },
115        true,
116      )
117    } else {
118      let val = self.words[0] as u128 + ((self.words[1] as u128) << 64);
119      if self.sign_bit && val == i128::MIN.unsigned_abs() {
120        return (i128::MIN, len > 2);
121      }
122      (
123        val as i128 * if self.sign_bit { -1 } else { 1 },
124        len == 2 && self.words[1] as i64 >= 0,
125      )
126    }
127  }
128
129  /// (signed, value, lossless)
130  /// get the first two words of the BigInt as `u128`
131  /// return true if the value is lossless
132  /// or the value is truncated
133  pub fn get_u128(&self) -> (bool, u128, bool) {
134    let len = self.words.len();
135    if len == 1 {
136      (self.sign_bit, self.words[0] as u128, true)
137    } else {
138      let val = self.words[0] as u128 + ((self.words[1] as u128) << 64);
139      (self.sign_bit, val, len == 2)
140    }
141  }
142}
143
144impl ToNapiValue for BigInt {
145  unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> crate::Result<sys::napi_value> {
146    let mut raw_value = ptr::null_mut();
147    let len = val.words.len();
148    check_status!(unsafe {
149      sys::napi_create_bigint_words(
150        env,
151        match val.sign_bit {
152          true => 1,
153          false => 0,
154        },
155        len,
156        val.words.as_ptr(),
157        &mut raw_value,
158      )
159    })?;
160    Ok(raw_value)
161  }
162}
163
164pub(crate) unsafe fn u128_with_sign_to_napi_value(
165  env: sys::napi_env,
166  val: u128,
167  sign_bit: i32,
168) -> crate::Result<sys::napi_value> {
169  let mut raw_value = ptr::null_mut();
170  if cfg!(target_endian = "little") {
171    let words = &val as *const u128 as *const u64;
172    check_status!(
173      unsafe { sys::napi_create_bigint_words(env, sign_bit, 2, words, &mut raw_value) },
174      "Failed to create BigInt from u128"
175    )?;
176    return Ok(raw_value);
177  }
178
179  let arr: [u64; 2] = [val as _, (val >> 64) as _];
180  let words = &arr as *const u64;
181  check_status!(
182    unsafe { sys::napi_create_bigint_words(env, sign_bit, 2, words, &mut raw_value) },
183    "Failed to create BigInt from u128"
184  )?;
185  Ok(raw_value)
186}
187
188impl ToNapiValue for i128 {
189  unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> crate::Result<sys::napi_value> {
190    let sign_bit = i32::from(val <= 0);
191    let val = val.unsigned_abs();
192    u128_with_sign_to_napi_value(env, val, sign_bit)
193  }
194}
195
196impl ToNapiValue for &i128 {
197  unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> crate::Result<sys::napi_value> {
198    ToNapiValue::to_napi_value(env, *val)
199  }
200}
201
202impl ToNapiValue for &mut i128 {
203  unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> crate::Result<sys::napi_value> {
204    ToNapiValue::to_napi_value(env, *val)
205  }
206}
207
208impl ToNapiValue for u128 {
209  unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> crate::Result<sys::napi_value> {
210    u128_with_sign_to_napi_value(env, val, 0)
211  }
212}
213
214impl ToNapiValue for &u128 {
215  unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> crate::Result<sys::napi_value> {
216    ToNapiValue::to_napi_value(env, *val)
217  }
218}
219
220impl ToNapiValue for &mut u128 {
221  unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> crate::Result<sys::napi_value> {
222    ToNapiValue::to_napi_value(env, *val)
223  }
224}
225
226impl ToNapiValue for i64n {
227  unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> crate::Result<sys::napi_value> {
228    let mut raw_value = ptr::null_mut();
229    check_status!(unsafe { sys::napi_create_bigint_int64(env, val.0, &mut raw_value) })?;
230    Ok(raw_value)
231  }
232}
233
234impl ToNapiValue for &i64n {
235  unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> crate::Result<sys::napi_value> {
236    ToNapiValue::to_napi_value(env, i64n(val.0))
237  }
238}
239
240impl ToNapiValue for &mut i64n {
241  unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> crate::Result<sys::napi_value> {
242    ToNapiValue::to_napi_value(env, i64n(val.0))
243  }
244}
245
246impl ToNapiValue for u64 {
247  unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> crate::Result<sys::napi_value> {
248    let mut raw_value = ptr::null_mut();
249    check_status!(
250      unsafe { sys::napi_create_bigint_uint64(env, val, &mut raw_value) },
251      "Failed to create BigInt from u64"
252    )?;
253    Ok(raw_value)
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    ToNapiValue::to_napi_value(env, *val)
260  }
261}
262
263impl ToNapiValue for &mut u64 {
264  unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> crate::Result<sys::napi_value> {
265    ToNapiValue::to_napi_value(env, *val)
266  }
267}
268
269impl ToNapiValue for usize {
270  unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> crate::Result<sys::napi_value> {
271    let mut raw_value = ptr::null_mut();
272    check_status!(unsafe { sys::napi_create_bigint_uint64(env, val as u64, &mut raw_value) })?;
273    Ok(raw_value)
274  }
275}
276
277impl ToNapiValue for isize {
278  unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> crate::Result<sys::napi_value> {
279    let mut raw_value = ptr::null_mut();
280    check_status!(unsafe { sys::napi_create_bigint_int64(env, val as i64, &mut raw_value) })?;
281    Ok(raw_value)
282  }
283}
284
285impl ToNapiValue for &usize {
286  unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> crate::Result<sys::napi_value> {
287    ToNapiValue::to_napi_value(env, *val)
288  }
289}
290
291impl ToNapiValue for &mut usize {
292  unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> crate::Result<sys::napi_value> {
293    ToNapiValue::to_napi_value(env, *val)
294  }
295}
296
297impl From<i64> for BigInt {
298  fn from(val: i64) -> Self {
299    BigInt {
300      sign_bit: val < 0,
301      words: vec![val.unsigned_abs()],
302    }
303  }
304}
305
306impl From<u64> for BigInt {
307  fn from(val: u64) -> Self {
308    BigInt {
309      sign_bit: false,
310      words: vec![val],
311    }
312  }
313}
314
315impl From<i128> for BigInt {
316  fn from(val: i128) -> Self {
317    let sign_bit = val < 0;
318    let val = val.unsigned_abs();
319    BigInt {
320      sign_bit,
321      words: vec![val as _, (val >> 64) as _],
322    }
323  }
324}
325
326impl From<u128> for BigInt {
327  fn from(val: u128) -> Self {
328    BigInt {
329      sign_bit: false,
330      words: vec![val as _, (val >> 64) as _],
331    }
332  }
333}