feat(napi): add BigInt related apis

This commit is contained in:
Ouyang Yadong 2020-07-10 19:14:32 +08:00 committed by LongYinan
parent 468c98dea1
commit 14dd8b83e6
No known key found for this signature in database
GPG key ID: C3666B7FC82ADAD7
7 changed files with 229 additions and 4 deletions

View file

@ -92,6 +92,45 @@ impl Env {
Ok(JsNumber::from_raw_unchecked(self.0, raw_value))
}
#[cfg(napi6)]
#[inline]
/// https://nodejs.org/api/n-api.html#n_api_napi_create_bigint_int64
pub fn create_bigint_from_i64(&self, value: i64) -> Result<JsBigint> {
let mut raw_value = ptr::null_mut();
check_status(unsafe { sys::napi_create_bigint_int64(self.0, value, &mut raw_value) })?;
Ok(JsBigint::from_raw_unchecked(self.0, raw_value))
}
#[cfg(napi6)]
#[inline]
/// https://nodejs.org/api/n-api.html#n_api_napi_create_bigint_words
pub fn create_bigint_from_u64(&self, value: u64) -> Result<JsBigint> {
let mut raw_value = ptr::null_mut();
check_status(unsafe { sys::napi_create_bigint_uint64(self.0, value, &mut raw_value) })?;
Ok(JsBigint::from_raw_unchecked(self.0, raw_value))
}
#[cfg(napi6)]
#[inline]
/// https://nodejs.org/api/n-api.html#n_api_napi_create_bigint_words
/// The resulting BigInt will be negative when sign_bit is true.
pub fn create_bigint_from_words(&self, sign_bit: bool, words: Vec<u64>) -> Result<JsBigint> {
let mut raw_value = ptr::null_mut();
check_status(unsafe {
sys::napi_create_bigint_words(
self.0,
match sign_bit {
true => 1,
false => 0,
},
words.len() as u64,
words.as_ptr(),
&mut raw_value,
)
})?;
Ok(JsBigint::from_raw_unchecked(self.0, raw_value))
}
#[inline]
pub fn create_string(&self, s: &str) -> Result<JsString> {
self.create_string_from_chars(s.as_ptr() as *const _, s.len() as u64)

View file

@ -0,0 +1,85 @@
use std::convert::TryFrom;
use std::ptr;
use super::{Error, Value};
use crate::error::check_status;
use crate::{sys, Result};
#[derive(Debug)]
pub struct JsBigint(pub(crate) Value);
/// The BigInt will be converted losslessly when the value is over what an int64 could hold.
impl TryFrom<JsBigint> for i64 {
type Error = Error;
fn try_from(value: JsBigint) -> Result<i64> {
value.get_i64().map(|(v, _)| v)
}
}
/// The BigInt will be converted losslessly when the value is over what an uint64 could hold.
impl TryFrom<JsBigint> for u64 {
type Error = Error;
fn try_from(value: JsBigint) -> Result<u64> {
value.get_u64().map(|(v, _)| v)
}
}
impl JsBigint {
/// https://nodejs.org/api/n-api.html#n_api_napi_get_value_bigint_words
pub fn get_words(&self, sign_bit: bool) -> Result<Vec<u64>> {
let mut word_count: u64 = 0;
check_status(unsafe {
sys::napi_get_value_bigint_words(
self.0.env,
self.0.value,
ptr::null_mut(),
&mut word_count,
ptr::null_mut(),
)
})?;
let mut words: Vec<u64> = Vec::with_capacity(word_count as usize);
let mut sign_bit = match sign_bit {
true => 1,
false => 0,
};
check_status(unsafe {
sys::napi_get_value_bigint_words(
self.0.env,
self.0.value,
&mut sign_bit,
&mut word_count,
words.as_mut_ptr(),
)
})?;
unsafe {
words.set_len(word_count as usize);
};
Ok(words)
}
#[inline]
pub fn get_u64(&self) -> Result<(u64, bool)> {
let mut val: u64 = 0;
let mut loss = false;
check_status(unsafe {
sys::napi_get_value_bigint_uint64(self.0.env, self.0.value, &mut val, &mut loss)
})?;
Ok((val, loss))
}
#[inline]
pub fn get_i64(&self) -> Result<(i64, bool)> {
let mut val: i64 = 0;
let mut loss: bool = false;
check_status(unsafe {
sys::napi_get_value_bigint_int64(self.0.env, self.0.value, &mut val, &mut loss)
})?;
Ok((val, loss))
}
}

View file

@ -5,6 +5,8 @@ use crate::error::check_status;
use crate::{sys, Error, Result, Status};
mod arraybuffer;
#[cfg(napi6)]
mod bigint;
mod boolean;
mod buffer;
mod class_property;
@ -20,6 +22,8 @@ mod value_ref;
mod value_type;
pub use arraybuffer::JsArrayBuffer;
#[cfg(napi6)]
pub use bigint::JsBigint;
pub use boolean::JsBoolean;
pub use buffer::JsBuffer;
pub use class_property::Property;
@ -41,10 +45,6 @@ pub struct JsUnknown(pub(crate) Value);
#[derive(Debug)]
pub struct JsNull(pub(crate) Value);
#[cfg(napi6)]
#[derive(Debug)]
pub struct JsBigint(pub(crate) Value);
#[derive(Debug)]
pub struct JsSymbol(pub(crate) Value);

View file

@ -0,0 +1,30 @@
const test = require('ava')
const napiVersion = require('../napi-version')
const bindings = require('../../index.node')
test('should create bigints', (t) => {
if (napiVersion >= 6) {
t.is(bindings.testCreateBigintFromI64(), BigInt('9223372036854775807'))
t.is(bindings.testCreateBigintFromU64(), BigInt('18446744073709551615'))
t.is(
bindings.testCreateBigintFromWords(),
BigInt('-340282366920938463463374607431768211455'),
)
} else {
t.is(bindings.testCreateBigintFromI64, undefined)
}
})
test('should get integers from bigints', (t) => {
if (napiVersion >= 6) {
t.is(bindings.testGetBigintI64(BigInt('-123')), -123)
t.is(bindings.testGetBigintU64(BigInt(123)), 123)
t.deepEqual(bindings.testGetBigintWords(), [
BigInt('9223372036854775807'),
BigInt('9223372036854775807'),
])
} else {
t.is(bindings.testGetBigintI64, undefined)
}
})

View file

@ -13,6 +13,8 @@ mod napi4;
mod napi5;
#[cfg(napi4)]
mod tokio_rt;
#[cfg(napi6)]
mod napi6;
mod buffer;
mod class;
@ -39,6 +41,15 @@ use symbol::{create_named_symbol, create_symbol_from_js_string, create_unnamed_s
use task::test_spawn_thread;
#[cfg(napi4)]
use tokio_rt::{error_from_tokio_future, test_execute_tokio_readfile};
#[cfg(napi6)]
use napi6::bigint::{
test_create_bigint_from_i64,
test_create_bigint_from_u64,
test_create_bigint_from_words,
test_get_bigint_i64,
test_get_bigint_u64,
test_get_bigint_words,
};
register_module!(test_module, init);
@ -74,6 +85,18 @@ fn init(module: &mut Module) -> Result<()> {
module.create_named_method("uvReadFile", uv_read_file)?;
#[cfg(napi5)]
module.create_named_method("testObjectIsDate", test_object_is_date)?;
#[cfg(napi6)]
module.create_named_method("testCreateBigintFromI64", test_create_bigint_from_i64)?;
#[cfg(napi6)]
module.create_named_method("testCreateBigintFromU64", test_create_bigint_from_u64)?;
#[cfg(napi6)]
module.create_named_method("testCreateBigintFromWords", test_create_bigint_from_words)?;
#[cfg(napi6)]
module.create_named_method("testGetBigintI64", test_get_bigint_i64)?;
#[cfg(napi6)]
module.create_named_method("testGetBigintU64", test_get_bigint_u64)?;
#[cfg(napi6)]
module.create_named_method("testGetBigintWords", test_get_bigint_words)?;
Ok(())
}

View file

@ -0,0 +1,47 @@
use std::convert::TryFrom;
use napi::{CallContext, JsBigint, Result, JsNumber, JsObject};
#[js_function(0)]
pub fn test_create_bigint_from_i64(ctx: CallContext) -> Result<JsBigint> {
ctx.env.create_bigint_from_i64(i64::max_value())
}
#[js_function(0)]
pub fn test_create_bigint_from_u64(ctx: CallContext) -> Result<JsBigint> {
ctx.env.create_bigint_from_u64(u64::max_value())
}
#[js_function(0)]
pub fn test_create_bigint_from_words(ctx: CallContext) -> Result<JsBigint> {
ctx.env.create_bigint_from_words(true, vec![u64::max_value(), u64::max_value()])
}
#[js_function(1)]
pub fn test_get_bigint_i64(ctx: CallContext) -> Result<JsNumber> {
let js_bigint = ctx.get::<JsBigint>(0)?;
let val = i64::try_from(js_bigint)?;
ctx.env.create_int32(val as i32)
}
#[js_function(1)]
pub fn test_get_bigint_u64(ctx: CallContext) -> Result<JsNumber> {
let js_bigint = ctx.get::<JsBigint>(0)?;
let val = u64::try_from(js_bigint)?;
ctx.env.create_int32(val as i32)
}
#[js_function(0)]
pub fn test_get_bigint_words(ctx: CallContext) -> Result<JsObject> {
let js_bigint = ctx.env.create_bigint_from_words(true, vec![i64::max_value() as u64, i64::max_value() as u64])?;
let mut js_arr = ctx.env.create_array_with_length(2)?;
let words = js_bigint.get_words(true)?;
js_arr.set_number_indexed_property(
ctx.env.create_int64(0)?,
ctx.env.create_bigint_from_u64(words[0])?
)?;
js_arr.set_number_indexed_property(
ctx.env.create_int64(1)?,
ctx.env.create_bigint_from_u64(words[1])?
)?;
Ok(js_arr)
}

View file

@ -0,0 +1 @@
pub mod bigint;