use std::convert::TryFrom;
use std::ptr;
use super::*;
use crate::{
bindgen_runtime::{FromNapiValue, TypeName},
check_status, sys, Result,
};
#[deprecated(since = "3.0.0", note = "Use `napi::bindgen_prelude::BigInt` instead")]
#[derive(Clone, Copy)]
pub struct JsBigInt {
pub(crate) raw: Value,
pub word_count: usize,
}
impl TypeName for JsBigInt {
fn type_name() -> &'static str {
"BigInt"
}
fn value_type() -> ValueType {
ValueType::BigInt
}
}
impl ValidateNapiValue for JsBigInt {}
impl JsValue<'_> for JsBigInt {
fn value(&self) -> Value {
self.raw
}
}
impl FromNapiValue for JsBigInt {
unsafe fn from_napi_value(env: sys::napi_env, napi_val: sys::napi_value) -> Result<Self> {
let mut word_count = 0usize;
check_status!(unsafe {
sys::napi_get_value_bigint_words(
env,
napi_val,
ptr::null_mut(),
&mut word_count,
ptr::null_mut(),
)
})?;
Ok(JsBigInt {
raw: Value {
env,
value: napi_val,
value_type: ValueType::BigInt,
},
word_count,
})
}
}
impl JsBigInt {
pub(crate) fn from_raw_unchecked(
env: sys::napi_env,
value: sys::napi_value,
word_count: usize,
) -> Self {
Self {
raw: Value {
env,
value,
value_type: ValueType::Object,
},
word_count,
}
}
}
impl NapiRaw for JsBigInt {
unsafe fn raw(&self) -> sys::napi_value {
self.raw.value
}
}
impl NapiRaw for &JsBigInt {
unsafe fn raw(&self) -> sys::napi_value {
self.raw.value
}
}
impl NapiValue for JsBigInt {
unsafe fn from_raw(env: sys::napi_env, value: sys::napi_value) -> Result<Self> {
let mut word_count = 0usize;
check_status!(unsafe {
sys::napi_get_value_bigint_words(
env,
value,
ptr::null_mut(),
&mut word_count,
ptr::null_mut(),
)
})?;
Ok(JsBigInt {
raw: Value {
env,
value,
value_type: ValueType::BigInt,
},
word_count,
})
}
unsafe fn from_raw_unchecked(env: sys::napi_env, value: sys::napi_value) -> Self {
let mut word_count = 0usize;
let status = unsafe {
sys::napi_get_value_bigint_words(
env,
value,
ptr::null_mut(),
&mut word_count,
ptr::null_mut(),
)
};
debug_assert!(
Status::from(status) == Status::Ok,
"napi_get_value_bigint_words failed"
);
JsBigInt {
raw: Value {
env,
value,
value_type: ValueType::BigInt,
},
word_count,
}
}
}
impl TryFrom<JsBigInt> for i64 {
type Error = Error;
fn try_from(value: JsBigInt) -> Result<i64> {
value.get_i64().map(|(v, _)| v)
}
}
impl TryFrom<JsBigInt> for u64 {
type Error = Error;
fn try_from(value: JsBigInt) -> Result<u64> {
value.get_u64().map(|(v, _)| v)
}
}
impl JsBigInt {
pub fn get_words(&mut self) -> Result<(bool, Vec<u64>)> {
let mut words: Vec<u64> = Vec::with_capacity(self.word_count);
let word_count = &mut self.word_count;
let mut sign_bit = 0;
check_status!(unsafe {
sys::napi_get_value_bigint_words(
self.raw.env,
self.raw.value,
&mut sign_bit,
word_count,
words.as_mut_ptr(),
)
})?;
unsafe {
words.set_len(self.word_count);
};
Ok((sign_bit == 1, words))
}
pub fn get_u64(&self) -> Result<(u64, bool)> {
let mut val: u64 = 0;
let mut lossless = false;
check_status!(unsafe {
sys::napi_get_value_bigint_uint64(self.raw.env, self.raw.value, &mut val, &mut lossless)
})?;
Ok((val, lossless))
}
pub fn get_i64(&self) -> Result<(i64, bool)> {
let mut val: i64 = 0;
let mut lossless: bool = false;
check_status!(unsafe {
sys::napi_get_value_bigint_int64(self.raw.env, self.raw.value, &mut val, &mut lossless)
})?;
Ok((val, lossless))
}
pub fn get_i128(&mut self) -> Result<(i128, bool)> {
let (signed, words) = self.get_words()?;
let low_part = words.first().copied().unwrap_or(0).to_ne_bytes();
let high_part = words.get(1).copied().unwrap_or(0).to_ne_bytes();
let mut val = [0_u8; std::mem::size_of::<i128>()];
let high_val: &mut [u8];
let low_val: &mut [u8];
if cfg!(target_endian = "little") {
(low_val, high_val) = val.split_at_mut(low_part.len());
} else {
(high_val, low_val) = val.split_at_mut(low_part.len());
}
high_val.copy_from_slice(&high_part);
low_val.copy_from_slice(&low_part);
let mut val = i128::from_ne_bytes(val);
let mut loss = words.len() > 2;
let mut overflow = false;
if signed {
let result = val.overflowing_neg();
val = result.0;
overflow = result.1;
}
loss = overflow || loss;
Ok((val, loss))
}
pub fn get_u128(&mut self) -> Result<(bool, u128, bool)> {
let (signed, words) = self.get_words()?;
let low_part = words.first().copied().unwrap_or(0).to_ne_bytes();
let high_part = words.get(1).copied().unwrap_or(0).to_ne_bytes();
let mut val = [0_u8; std::mem::size_of::<i128>()];
let high_val: &mut [u8];
let low_val: &mut [u8];
if cfg!(target_endian = "little") {
(low_val, high_val) = val.split_at_mut(low_part.len());
} else {
(high_val, low_val) = val.split_at_mut(low_part.len());
}
high_val.copy_from_slice(&high_part);
low_val.copy_from_slice(&low_part);
let val = u128::from_ne_bytes(val);
let len = words.len();
Ok((signed, val, len > 2))
}
}