napi/js_values/
bigint.rs

1use std::convert::TryFrom;
2use std::ptr;
3
4use super::*;
5use crate::{
6  bindgen_runtime::{FromNapiValue, TypeName},
7  check_status, sys, Result,
8};
9
10#[deprecated(since = "3.0.0", note = "Use `napi::bindgen_prelude::BigInt` instead")]
11#[derive(Clone, Copy)]
12pub struct JsBigInt {
13  pub(crate) raw: Value,
14  pub word_count: usize,
15}
16
17impl TypeName for JsBigInt {
18  fn type_name() -> &'static str {
19    "BigInt"
20  }
21
22  fn value_type() -> ValueType {
23    ValueType::BigInt
24  }
25}
26
27impl ValidateNapiValue for JsBigInt {}
28
29impl JsValue<'_> for JsBigInt {
30  fn value(&self) -> Value {
31    self.raw
32  }
33}
34
35impl FromNapiValue for JsBigInt {
36  unsafe fn from_napi_value(env: sys::napi_env, napi_val: sys::napi_value) -> Result<Self> {
37    let mut word_count = 0usize;
38    check_status!(unsafe {
39      sys::napi_get_value_bigint_words(
40        env,
41        napi_val,
42        ptr::null_mut(),
43        &mut word_count,
44        ptr::null_mut(),
45      )
46    })?;
47    Ok(JsBigInt {
48      raw: Value {
49        env,
50        value: napi_val,
51        value_type: ValueType::BigInt,
52      },
53      word_count,
54    })
55  }
56}
57
58impl JsBigInt {
59  pub(crate) fn from_raw_unchecked(
60    env: sys::napi_env,
61    value: sys::napi_value,
62    word_count: usize,
63  ) -> Self {
64    Self {
65      raw: Value {
66        env,
67        value,
68        value_type: ValueType::Object,
69      },
70      word_count,
71    }
72  }
73}
74
75impl NapiRaw for JsBigInt {
76  unsafe fn raw(&self) -> sys::napi_value {
77    self.raw.value
78  }
79}
80
81impl NapiRaw for &JsBigInt {
82  unsafe fn raw(&self) -> sys::napi_value {
83    self.raw.value
84  }
85}
86
87impl NapiValue for JsBigInt {
88  unsafe fn from_raw(env: sys::napi_env, value: sys::napi_value) -> Result<Self> {
89    let mut word_count = 0usize;
90    check_status!(unsafe {
91      sys::napi_get_value_bigint_words(
92        env,
93        value,
94        ptr::null_mut(),
95        &mut word_count,
96        ptr::null_mut(),
97      )
98    })?;
99    Ok(JsBigInt {
100      raw: Value {
101        env,
102        value,
103        value_type: ValueType::BigInt,
104      },
105      word_count,
106    })
107  }
108
109  unsafe fn from_raw_unchecked(env: sys::napi_env, value: sys::napi_value) -> Self {
110    let mut word_count = 0usize;
111    let status = unsafe {
112      sys::napi_get_value_bigint_words(
113        env,
114        value,
115        ptr::null_mut(),
116        &mut word_count,
117        ptr::null_mut(),
118      )
119    };
120    debug_assert!(
121      Status::from(status) == Status::Ok,
122      "napi_get_value_bigint_words failed"
123    );
124    JsBigInt {
125      raw: Value {
126        env,
127        value,
128        value_type: ValueType::BigInt,
129      },
130      word_count,
131    }
132  }
133}
134
135/// The BigInt will be converted losslessly when the value is over what an int64 could hold.
136impl TryFrom<JsBigInt> for i64 {
137  type Error = Error;
138
139  fn try_from(value: JsBigInt) -> Result<i64> {
140    value.get_i64().map(|(v, _)| v)
141  }
142}
143
144/// The BigInt will be converted losslessly when the value is over what an uint64 could hold.
145impl TryFrom<JsBigInt> for u64 {
146  type Error = Error;
147
148  fn try_from(value: JsBigInt) -> Result<u64> {
149    value.get_u64().map(|(v, _)| v)
150  }
151}
152
153impl JsBigInt {
154  /// <https://nodejs.org/api/n-api.html#n_api_napi_get_value_bigint_words>
155  pub fn get_words(&mut self) -> Result<(bool, Vec<u64>)> {
156    let mut words: Vec<u64> = Vec::with_capacity(self.word_count);
157    let word_count = &mut self.word_count;
158    let mut sign_bit = 0;
159    check_status!(unsafe {
160      sys::napi_get_value_bigint_words(
161        self.raw.env,
162        self.raw.value,
163        &mut sign_bit,
164        word_count,
165        words.as_mut_ptr(),
166      )
167    })?;
168
169    unsafe {
170      words.set_len(self.word_count);
171    };
172
173    Ok((sign_bit == 1, words))
174  }
175
176  pub fn get_u64(&self) -> Result<(u64, bool)> {
177    let mut val: u64 = 0;
178    let mut lossless = false;
179    check_status!(unsafe {
180      sys::napi_get_value_bigint_uint64(self.raw.env, self.raw.value, &mut val, &mut lossless)
181    })?;
182
183    Ok((val, lossless))
184  }
185
186  pub fn get_i64(&self) -> Result<(i64, bool)> {
187    let mut val: i64 = 0;
188    let mut lossless: bool = false;
189    check_status!(unsafe {
190      sys::napi_get_value_bigint_int64(self.raw.env, self.raw.value, &mut val, &mut lossless)
191    })?;
192    Ok((val, lossless))
193  }
194
195  pub fn get_i128(&mut self) -> Result<(i128, bool)> {
196    let (signed, words) = self.get_words()?;
197
198    let low_part = words.first().copied().unwrap_or(0).to_ne_bytes();
199    let high_part = words.get(1).copied().unwrap_or(0).to_ne_bytes();
200
201    let mut val = [0_u8; std::mem::size_of::<i128>()];
202    let high_val: &mut [u8];
203    let low_val: &mut [u8];
204    if cfg!(target_endian = "little") {
205      (low_val, high_val) = val.split_at_mut(low_part.len());
206    } else {
207      (high_val, low_val) = val.split_at_mut(low_part.len());
208    }
209
210    high_val.copy_from_slice(&high_part);
211    low_val.copy_from_slice(&low_part);
212
213    let mut val = i128::from_ne_bytes(val);
214
215    let mut loss = words.len() > 2;
216    let mut overflow = false;
217
218    if signed {
219      let result = val.overflowing_neg();
220      val = result.0;
221      overflow = result.1;
222    }
223
224    loss = overflow || loss;
225
226    Ok((val, loss))
227  }
228
229  pub fn get_u128(&mut self) -> Result<(bool, u128, bool)> {
230    let (signed, words) = self.get_words()?;
231
232    let low_part = words.first().copied().unwrap_or(0).to_ne_bytes();
233    let high_part = words.get(1).copied().unwrap_or(0).to_ne_bytes();
234
235    let mut val = [0_u8; std::mem::size_of::<i128>()];
236    let high_val: &mut [u8];
237    let low_val: &mut [u8];
238    if cfg!(target_endian = "little") {
239      (low_val, high_val) = val.split_at_mut(low_part.len());
240    } else {
241      (high_val, low_val) = val.split_at_mut(low_part.len());
242    }
243
244    high_val.copy_from_slice(&high_part);
245    low_val.copy_from_slice(&low_part);
246
247    let val = u128::from_ne_bytes(val);
248
249    let len = words.len();
250
251    Ok((signed, val, len > 2))
252  }
253}