napi_h/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`
94  /// return true if the value is lossless
95  /// or the value is truncated
96  pub fn get_i64(&self) -> (i64, bool) {
97    let val = self.words[0] as i64;
98    (val, val as u64 == self.words[0] && self.words.len() == 1)
99  }
100
101  /// (value, lossless)
102  /// get the first two words of the BigInt as `i128`
103  /// return true if the value is lossless
104  /// or the value is truncated
105  pub fn get_i128(&self) -> (i128, bool) {
106    let len = self.words.len();
107    if len == 1 {
108      (self.words[0] as i128, false)
109    } else {
110      let i128_words: [i64; 2] = [self.words[0] as _, self.words[1] as _];
111      let mut val = unsafe { ptr::read(i128_words.as_ptr() as *const i128) };
112      if self.sign_bit {
113        val = -val;
114      }
115      (val, len > 2)
116    }
117  }
118
119  /// (signed, value, lossless)
120  /// get the first two words of the BigInt as `u128`
121  /// return true if the value is lossless
122  /// or the value is truncated
123  pub fn get_u128(&self) -> (bool, u128, bool) {
124    let len = self.words.len();
125    if len == 1 {
126      (self.sign_bit, self.words[0] as u128, false)
127    } else {
128      let u128_words: [u64; 2] = [self.words[0], self.words[1]];
129      let val = unsafe { ptr::read(u128_words.as_ptr() as *const u128) };
130      (self.sign_bit, val, len > 2)
131    }
132  }
133}
134
135impl ToNapiValue for BigInt {
136  unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> crate::Result<sys::napi_value> {
137    let mut raw_value = ptr::null_mut();
138    let len = val.words.len();
139    check_status!(unsafe {
140      sys::napi_create_bigint_words(
141        env,
142        match val.sign_bit {
143          true => 1,
144          false => 0,
145        },
146        len,
147        val.words.as_ptr(),
148        &mut raw_value,
149      )
150    })?;
151    Ok(raw_value)
152  }
153}
154
155impl ToNapiValue for i128 {
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 sign_bit = i32::from(val <= 0);
159    let words = &val as *const i128 as *const u64;
160    check_status!(unsafe {
161      sys::napi_create_bigint_words(env, sign_bit, 2, words, &mut raw_value)
162    })?;
163    Ok(raw_value)
164  }
165}
166
167impl ToNapiValue for u128 {
168  unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> crate::Result<sys::napi_value> {
169    let mut raw_value = ptr::null_mut();
170    let words = &val as *const u128 as *const u64;
171    check_status!(unsafe { sys::napi_create_bigint_words(env, 0, 2, words, &mut raw_value) })?;
172    Ok(raw_value)
173  }
174}
175
176impl ToNapiValue for i64n {
177  unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> crate::Result<sys::napi_value> {
178    let mut raw_value = ptr::null_mut();
179    check_status!(unsafe { sys::napi_create_bigint_int64(env, val.0, &mut raw_value) })?;
180    Ok(raw_value)
181  }
182}
183
184impl ToNapiValue for u64 {
185  unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> crate::Result<sys::napi_value> {
186    let mut raw_value = ptr::null_mut();
187    check_status!(unsafe { sys::napi_create_bigint_uint64(env, val, &mut raw_value) })?;
188    Ok(raw_value)
189  }
190}
191
192impl ToNapiValue for usize {
193  unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> crate::Result<sys::napi_value> {
194    let mut raw_value = ptr::null_mut();
195    check_status!(unsafe { sys::napi_create_bigint_uint64(env, val as u64, &mut raw_value) })?;
196    Ok(raw_value)
197  }
198}
199
200impl ToNapiValue for isize {
201  unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> crate::Result<sys::napi_value> {
202    let mut raw_value = ptr::null_mut();
203    check_status!(unsafe { sys::napi_create_bigint_int64(env, val as i64, &mut raw_value) })?;
204    Ok(raw_value)
205  }
206}
207
208impl From<i64> for BigInt {
209  fn from(val: i64) -> Self {
210    BigInt {
211      sign_bit: val < 0,
212      words: vec![val as u64],
213    }
214  }
215}
216
217impl From<u64> for BigInt {
218  fn from(val: u64) -> Self {
219    BigInt {
220      sign_bit: false,
221      words: vec![val],
222    }
223  }
224}
225
226impl From<i128> for BigInt {
227  fn from(val: i128) -> Self {
228    let sign_bit = val < 0;
229    let words = (if sign_bit { -val } else { val }).to_ne_bytes();
230    BigInt {
231      sign_bit,
232      words: unsafe { std::slice::from_raw_parts(words.as_ptr() as *mut _, 2).to_vec() },
233    }
234  }
235}
236
237impl From<u128> for BigInt {
238  fn from(val: u128) -> Self {
239    let words = val.to_ne_bytes();
240    BigInt {
241      sign_bit: false,
242      words: unsafe { std::slice::from_raw_parts(words.as_ptr() as *mut _, 2).to_vec() },
243    }
244  }
245}