fix(napi): arraybuffer implement
close https://github.com/napi-rs/napi-rs/issues/68
This commit is contained in:
parent
c1b957a268
commit
3eecf1fc5f
6 changed files with 120 additions and 180 deletions
|
@ -264,22 +264,22 @@ impl Env {
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_arraybuffer(&self, length: u64) -> Result<JsArrayBuffer> {
|
pub fn create_arraybuffer(&self, length: u64) -> Result<JsArrayBufferValue> {
|
||||||
let mut raw_value = ptr::null_mut();
|
let mut raw_value = ptr::null_mut();
|
||||||
let mut data = Vec::with_capacity(length as usize);
|
let mut data: Vec<u8> = Vec::with_capacity(length as usize);
|
||||||
let mut data_ptr = data.as_mut_ptr();
|
let mut data_ptr = data.as_mut_ptr() as *mut c_void;
|
||||||
check_status(unsafe {
|
check_status(unsafe {
|
||||||
sys::napi_create_arraybuffer(self.0, length, &mut data_ptr, &mut raw_value)
|
sys::napi_create_arraybuffer(self.0, length, &mut data_ptr, &mut raw_value)
|
||||||
})?;
|
})?;
|
||||||
mem::forget(data);
|
|
||||||
let mut array_buffer = JsArrayBuffer::from_raw_unchecked(self.0, raw_value);
|
Ok(JsArrayBufferValue::new(
|
||||||
array_buffer.data = data_ptr as *const u8;
|
JsArrayBuffer::from_raw_unchecked(self.0, raw_value),
|
||||||
array_buffer.len = length;
|
data,
|
||||||
Ok(array_buffer)
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_arraybuffer_with_data(&self, data: Vec<u8>) -> Result<JsArrayBuffer> {
|
pub fn create_arraybuffer_with_data(&self, data: Vec<u8>) -> Result<JsArrayBufferValue> {
|
||||||
let length = data.len() as u64;
|
let mut length = data.len() as u64;
|
||||||
let mut raw_value = ptr::null_mut();
|
let mut raw_value = ptr::null_mut();
|
||||||
let data_ptr = data.as_ptr();
|
let data_ptr = data.as_ptr();
|
||||||
check_status(unsafe {
|
check_status(unsafe {
|
||||||
|
@ -288,17 +288,21 @@ impl Env {
|
||||||
data_ptr as *mut c_void,
|
data_ptr as *mut c_void,
|
||||||
length,
|
length,
|
||||||
Some(drop_buffer),
|
Some(drop_buffer),
|
||||||
&length as *const _ as *mut c_void,
|
&mut length as *mut u64 as *mut c_void,
|
||||||
&mut raw_value,
|
&mut raw_value,
|
||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
let mut changed = 0;
|
let mut changed = 0;
|
||||||
check_status(unsafe { sys::napi_adjust_external_memory(self.0, length as i64, &mut changed) })?;
|
check_status(unsafe { sys::napi_adjust_external_memory(self.0, length as i64, &mut changed) })?;
|
||||||
mem::forget(data);
|
|
||||||
let mut array_buffer = JsArrayBuffer::from_raw_unchecked(self.0, raw_value);
|
Ok(JsArrayBufferValue::new(
|
||||||
array_buffer.data = data_ptr as *const u8;
|
JsArrayBuffer(Value {
|
||||||
array_buffer.len = length;
|
env: self.0,
|
||||||
Ok(array_buffer)
|
value: raw_value,
|
||||||
|
value_type: ValueType::Object,
|
||||||
|
}),
|
||||||
|
data,
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_function(&self, name: &str, callback: Callback) -> Result<JsFunction> {
|
pub fn create_function(&self, name: &str, callback: Callback) -> Result<JsFunction> {
|
||||||
|
|
|
@ -1,182 +1,69 @@
|
||||||
use std::convert::TryFrom;
|
use std::mem;
|
||||||
|
use std::ops::Deref;
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
|
|
||||||
use super::{JsNumber, JsObject, JsString, JsUnknown, NapiValue, Status, Value, ValueType};
|
use super::Value;
|
||||||
use crate::error::check_status;
|
use crate::error::check_status;
|
||||||
use crate::{sys, Error, Result};
|
use crate::{sys, JsUnknown, Ref, Result};
|
||||||
|
|
||||||
|
#[repr(transparent)]
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct JsArrayBuffer(pub(crate) Value);
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct JsArrayBuffer {
|
pub struct JsArrayBufferValue {
|
||||||
pub value: JsObject,
|
pub(crate) value: JsArrayBuffer,
|
||||||
pub data: *const u8,
|
data: mem::ManuallyDrop<Vec<u8>>,
|
||||||
pub len: u64,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl JsArrayBuffer {
|
impl JsArrayBuffer {
|
||||||
pub(crate) fn from_raw_unchecked(env: sys::napi_env, value: sys::napi_value) -> Self {
|
pub fn into_value(self) -> Result<JsArrayBufferValue> {
|
||||||
Self {
|
|
||||||
value: JsObject(Value {
|
|
||||||
env,
|
|
||||||
value,
|
|
||||||
value_type: ValueType::Object,
|
|
||||||
}),
|
|
||||||
data: ptr::null(),
|
|
||||||
len: 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn into_unknown(self) -> Result<JsUnknown> {
|
|
||||||
JsUnknown::from_raw(self.value.0.env, self.value.0.value)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn coerce_to_number(self) -> Result<JsNumber> {
|
|
||||||
let mut new_raw_value = ptr::null_mut();
|
|
||||||
check_status(unsafe {
|
|
||||||
sys::napi_coerce_to_number(self.value.0.env, self.value.0.value, &mut new_raw_value)
|
|
||||||
})?;
|
|
||||||
Ok(JsNumber(Value {
|
|
||||||
env: self.value.0.env,
|
|
||||||
value: new_raw_value,
|
|
||||||
value_type: ValueType::Number,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn coerce_to_string(self) -> Result<JsString> {
|
|
||||||
let mut new_raw_value = ptr::null_mut();
|
|
||||||
check_status(unsafe {
|
|
||||||
sys::napi_coerce_to_string(self.value.0.env, self.value.0.value, &mut new_raw_value)
|
|
||||||
})?;
|
|
||||||
Ok(JsString(Value {
|
|
||||||
env: self.value.0.env,
|
|
||||||
value: new_raw_value,
|
|
||||||
value_type: ValueType::String,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn coerce_to_object(self) -> Result<JsObject> {
|
|
||||||
let mut new_raw_value = ptr::null_mut();
|
|
||||||
check_status(unsafe {
|
|
||||||
sys::napi_coerce_to_object(self.value.0.env, self.value.0.value, &mut new_raw_value)
|
|
||||||
})?;
|
|
||||||
Ok(JsObject(Value {
|
|
||||||
env: self.value.0.env,
|
|
||||||
value: new_raw_value,
|
|
||||||
value_type: ValueType::Object,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
#[cfg(napi5)]
|
|
||||||
pub fn is_date(&self) -> Result<bool> {
|
|
||||||
let mut is_date = true;
|
|
||||||
check_status(unsafe { sys::napi_is_date(self.value.0.env, self.value.0.value, &mut is_date) })?;
|
|
||||||
Ok(is_date)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn is_error(&self) -> Result<bool> {
|
|
||||||
let mut result = false;
|
|
||||||
check_status(unsafe { sys::napi_is_error(self.value.0.env, self.value.0.value, &mut result) })?;
|
|
||||||
Ok(result)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn is_typedarray(&self) -> Result<bool> {
|
|
||||||
let mut result = false;
|
|
||||||
check_status(unsafe {
|
|
||||||
sys::napi_is_typedarray(self.value.0.env, self.value.0.value, &mut result)
|
|
||||||
})?;
|
|
||||||
Ok(result)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn is_dataview(&self) -> Result<bool> {
|
|
||||||
let mut result = false;
|
|
||||||
check_status(unsafe {
|
|
||||||
sys::napi_is_dataview(self.value.0.env, self.value.0.value, &mut result)
|
|
||||||
})?;
|
|
||||||
Ok(result)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn is_array(&self) -> Result<bool> {
|
|
||||||
let mut is_array = false;
|
|
||||||
check_status(unsafe {
|
|
||||||
sys::napi_is_array(self.value.0.env, self.value.0.value, &mut is_array)
|
|
||||||
})?;
|
|
||||||
Ok(is_array)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn is_buffer(&self) -> Result<bool> {
|
|
||||||
let mut is_buffer = false;
|
|
||||||
check_status(unsafe {
|
|
||||||
sys::napi_is_buffer(self.value.0.env, self.value.0.value, &mut is_buffer)
|
|
||||||
})?;
|
|
||||||
Ok(is_buffer)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn instanceof<Constructor: NapiValue>(&self, constructor: Constructor) -> Result<bool> {
|
|
||||||
let mut result = false;
|
|
||||||
check_status(unsafe {
|
|
||||||
sys::napi_instanceof(
|
|
||||||
self.value.0.env,
|
|
||||||
self.value.0.value,
|
|
||||||
constructor.raw(),
|
|
||||||
&mut result,
|
|
||||||
)
|
|
||||||
})?;
|
|
||||||
Ok(result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl NapiValue for JsArrayBuffer {
|
|
||||||
fn raw(&self) -> sys::napi_value {
|
|
||||||
self.value.0.value
|
|
||||||
}
|
|
||||||
|
|
||||||
fn from_raw(env: sys::napi_env, value: sys::napi_value) -> Result<Self> {
|
|
||||||
let mut data = ptr::null_mut();
|
let mut data = ptr::null_mut();
|
||||||
let mut len: u64 = 0;
|
let mut len: u64 = 0;
|
||||||
check_status(unsafe { sys::napi_get_arraybuffer_info(env, value, &mut data, &mut len) })?;
|
check_status(unsafe {
|
||||||
Ok(JsArrayBuffer {
|
sys::napi_get_arraybuffer_info(self.0.env, self.0.value, &mut data, &mut len)
|
||||||
value: JsObject(Value {
|
})?;
|
||||||
env,
|
Ok(JsArrayBufferValue {
|
||||||
value,
|
data: mem::ManuallyDrop::new(unsafe {
|
||||||
value_type: ValueType::Object,
|
Vec::from_raw_parts(data as *mut _, len as usize, len as usize)
|
||||||
}),
|
}),
|
||||||
data: data as *const u8,
|
value: self,
|
||||||
len,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_raw_unchecked(env: sys::napi_env, value: sys::napi_value) -> Self {
|
#[inline]
|
||||||
let mut data = ptr::null_mut();
|
pub fn into_ref(self) -> Result<Ref<JsArrayBufferValue>> {
|
||||||
let mut len: u64 = 0;
|
Ref::new(self.0, 1, self.into_value()?)
|
||||||
let status = unsafe { sys::napi_get_arraybuffer_info(env, value, &mut data, &mut len) };
|
|
||||||
debug_assert!(
|
|
||||||
Status::from(status) == Status::Ok,
|
|
||||||
"napi_get_arraybuffer_info failed"
|
|
||||||
);
|
|
||||||
JsArrayBuffer {
|
|
||||||
value: JsObject(Value {
|
|
||||||
env,
|
|
||||||
value,
|
|
||||||
value_type: ValueType::Object,
|
|
||||||
}),
|
|
||||||
data: data as *const u8,
|
|
||||||
len,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<JsUnknown> for JsArrayBuffer {
|
impl JsArrayBufferValue {
|
||||||
type Error = Error;
|
pub fn new(value: JsArrayBuffer, data: Vec<u8>) -> Self {
|
||||||
fn try_from(value: JsUnknown) -> Result<JsArrayBuffer> {
|
JsArrayBufferValue {
|
||||||
JsArrayBuffer::from_raw(value.0.env, value.0.value)
|
value,
|
||||||
|
data: mem::ManuallyDrop::new(data),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn into_raw(self) -> JsArrayBuffer {
|
||||||
|
self.value
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn into_unknown(self) -> Result<JsUnknown> {
|
||||||
|
self.value.into_unknown()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsRef<[u8]> for JsArrayBufferValue {
|
||||||
|
fn as_ref(&self) -> &[u8] {
|
||||||
|
self.data.as_slice()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for JsArrayBufferValue {
|
||||||
|
type Target = [u8];
|
||||||
|
|
||||||
|
fn deref(&self) -> &[u8] {
|
||||||
|
self.data.as_slice()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,7 @@ mod value;
|
||||||
mod value_ref;
|
mod value_ref;
|
||||||
mod value_type;
|
mod value_type;
|
||||||
|
|
||||||
pub use arraybuffer::JsArrayBuffer;
|
pub use arraybuffer::*;
|
||||||
#[cfg(napi6)]
|
#[cfg(napi6)]
|
||||||
pub use bigint::JsBigint;
|
pub use bigint::JsBigint;
|
||||||
pub use boolean::JsBoolean;
|
pub use boolean::JsBoolean;
|
||||||
|
@ -231,6 +231,7 @@ impl_js_value_methods!(JsUnknown);
|
||||||
impl_js_value_methods!(JsUndefined);
|
impl_js_value_methods!(JsUndefined);
|
||||||
impl_js_value_methods!(JsNull);
|
impl_js_value_methods!(JsNull);
|
||||||
impl_js_value_methods!(JsBoolean);
|
impl_js_value_methods!(JsBoolean);
|
||||||
|
impl_js_value_methods!(JsArrayBuffer);
|
||||||
impl_js_value_methods!(JsBuffer);
|
impl_js_value_methods!(JsBuffer);
|
||||||
impl_js_value_methods!(JsNumber);
|
impl_js_value_methods!(JsNumber);
|
||||||
impl_js_value_methods!(JsString);
|
impl_js_value_methods!(JsString);
|
||||||
|
@ -245,6 +246,7 @@ impl_napi_value_trait!(JsUndefined, Undefined);
|
||||||
impl_napi_value_trait!(JsNull, Null);
|
impl_napi_value_trait!(JsNull, Null);
|
||||||
impl_napi_value_trait!(JsBoolean, Boolean);
|
impl_napi_value_trait!(JsBoolean, Boolean);
|
||||||
impl_napi_value_trait!(JsBuffer, Object);
|
impl_napi_value_trait!(JsBuffer, Object);
|
||||||
|
impl_napi_value_trait!(JsArrayBuffer, Object);
|
||||||
impl_napi_value_trait!(JsNumber, Number);
|
impl_napi_value_trait!(JsNumber, Number);
|
||||||
impl_napi_value_trait!(JsString, String);
|
impl_napi_value_trait!(JsString, String);
|
||||||
impl_napi_value_trait!(JsObject, Object);
|
impl_napi_value_trait!(JsObject, Object);
|
||||||
|
|
8
test_module/__test__/arraybuffer.spec.ts
Normal file
8
test_module/__test__/arraybuffer.spec.ts
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
import test from 'ava'
|
||||||
|
|
||||||
|
const bindings = require('../index.node')
|
||||||
|
|
||||||
|
test('should get arraybuffer length', (t) => {
|
||||||
|
const fixture = Buffer.from('wow, hello')
|
||||||
|
t.is(bindings.getArraybufferLength(fixture.buffer), fixture.buffer.byteLength)
|
||||||
|
})
|
37
test_module/src/arraybuffer.rs
Normal file
37
test_module/src/arraybuffer.rs
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
use std::ffi::CStr;
|
||||||
|
use std::str;
|
||||||
|
|
||||||
|
use napi::{CallContext, JsArrayBuffer, JsNumber, JsString, Module, Result};
|
||||||
|
|
||||||
|
#[js_function(1)]
|
||||||
|
pub fn get_arraybuffer_length(ctx: CallContext) -> Result<JsNumber> {
|
||||||
|
let buffer = ctx.get::<JsArrayBuffer>(0)?.into_value()?;
|
||||||
|
ctx.env.create_uint32((&buffer).len() as u32)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[js_function(1)]
|
||||||
|
pub fn arraybuffer_to_string(ctx: CallContext) -> Result<JsString> {
|
||||||
|
let buffer = ctx.get::<JsArrayBuffer>(0)?.into_value()?;
|
||||||
|
ctx
|
||||||
|
.env
|
||||||
|
.create_string(str_from_null_terminated_utf8_safe(&buffer))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn str_from_null_terminated_utf8_safe(s: &[u8]) -> &str {
|
||||||
|
if s.iter().any(|&x| x == 0) {
|
||||||
|
unsafe { str_from_null_terminated_utf8(s) }
|
||||||
|
} else {
|
||||||
|
str::from_utf8(s).unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// unsafe: s must contain a null byte
|
||||||
|
unsafe fn str_from_null_terminated_utf8(s: &[u8]) -> &str {
|
||||||
|
CStr::from_ptr(s.as_ptr() as *const _).to_str().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn register_js(module: &mut Module) -> Result<()> {
|
||||||
|
module.create_named_method("getArraybufferLength", get_arraybuffer_length)?;
|
||||||
|
module.create_named_method("arraybufferToString", arraybuffer_to_string)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
|
@ -18,6 +18,7 @@ mod napi6;
|
||||||
#[cfg(napi4)]
|
#[cfg(napi4)]
|
||||||
mod tokio_rt;
|
mod tokio_rt;
|
||||||
|
|
||||||
|
mod arraybuffer;
|
||||||
mod buffer;
|
mod buffer;
|
||||||
mod class;
|
mod class;
|
||||||
mod either;
|
mod either;
|
||||||
|
@ -43,6 +44,7 @@ fn init(module: &mut Module) -> Result<()> {
|
||||||
serde::register_js(module)?;
|
serde::register_js(module)?;
|
||||||
task::register_js(module)?;
|
task::register_js(module)?;
|
||||||
external::register_js(module)?;
|
external::register_js(module)?;
|
||||||
|
arraybuffer::register_js(module)?;
|
||||||
buffer::register_js(module)?;
|
buffer::register_js(module)?;
|
||||||
either::register_js(module)?;
|
either::register_js(module)?;
|
||||||
symbol::register_js(module)?;
|
symbol::register_js(module)?;
|
||||||
|
|
Loading…
Reference in a new issue