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