fix(napi): arraybuffer implement

close https://github.com/napi-rs/napi-rs/issues/68
This commit is contained in:
LongYinan 2020-09-30 18:22:48 +08:00
parent c1b957a268
commit 3eecf1fc5f
No known key found for this signature in database
GPG key ID: A3FFE134A3E20881
6 changed files with 120 additions and 180 deletions

View file

@ -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> {

View file

@ -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()
} }
} }

View file

@ -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);

View 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)
})

View 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(())
}

View file

@ -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)?;