From 3eecf1fc5fd7a304bf4d1a068d7da2f2888cfefd Mon Sep 17 00:00:00 2001 From: LongYinan Date: Wed, 30 Sep 2020 18:22:48 +0800 Subject: [PATCH] fix(napi): arraybuffer implement close https://github.com/napi-rs/napi-rs/issues/68 --- napi/src/env.rs | 36 ++-- napi/src/js_values/arraybuffer.rs | 213 ++++++----------------- napi/src/js_values/mod.rs | 4 +- test_module/__test__/arraybuffer.spec.ts | 8 + test_module/src/arraybuffer.rs | 37 ++++ test_module/src/lib.rs | 2 + 6 files changed, 120 insertions(+), 180 deletions(-) create mode 100644 test_module/__test__/arraybuffer.spec.ts create mode 100644 test_module/src/arraybuffer.rs diff --git a/napi/src/env.rs b/napi/src/env.rs index 0dc961e7..7a270583 100644 --- a/napi/src/env.rs +++ b/napi/src/env.rs @@ -264,22 +264,22 @@ impl Env { )) } - pub fn create_arraybuffer(&self, length: u64) -> Result { + pub fn create_arraybuffer(&self, length: u64) -> Result { let mut raw_value = ptr::null_mut(); - let mut data = Vec::with_capacity(length as usize); - let mut data_ptr = data.as_mut_ptr(); + let mut data: Vec = Vec::with_capacity(length as usize); + let mut data_ptr = data.as_mut_ptr() as *mut c_void; check_status(unsafe { 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); - array_buffer.data = data_ptr as *const u8; - array_buffer.len = length; - Ok(array_buffer) + + Ok(JsArrayBufferValue::new( + JsArrayBuffer::from_raw_unchecked(self.0, raw_value), + data, + )) } - pub fn create_arraybuffer_with_data(&self, data: Vec) -> Result { - let length = data.len() as u64; + pub fn create_arraybuffer_with_data(&self, data: Vec) -> Result { + let mut length = data.len() as u64; let mut raw_value = ptr::null_mut(); let data_ptr = data.as_ptr(); check_status(unsafe { @@ -288,17 +288,21 @@ impl Env { data_ptr as *mut c_void, length, Some(drop_buffer), - &length as *const _ as *mut c_void, + &mut length as *mut u64 as *mut c_void, &mut raw_value, ) })?; let mut changed = 0; 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); - array_buffer.data = data_ptr as *const u8; - array_buffer.len = length; - Ok(array_buffer) + + Ok(JsArrayBufferValue::new( + JsArrayBuffer(Value { + env: self.0, + value: raw_value, + value_type: ValueType::Object, + }), + data, + )) } pub fn create_function(&self, name: &str, callback: Callback) -> Result { diff --git a/napi/src/js_values/arraybuffer.rs b/napi/src/js_values/arraybuffer.rs index d290aa1a..0e257696 100644 --- a/napi/src/js_values/arraybuffer.rs +++ b/napi/src/js_values/arraybuffer.rs @@ -1,182 +1,69 @@ -use std::convert::TryFrom; +use std::mem; +use std::ops::Deref; use std::ptr; -use super::{JsNumber, JsObject, JsString, JsUnknown, NapiValue, Status, Value, ValueType}; +use super::Value; 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)] -pub struct JsArrayBuffer { - pub value: JsObject, - pub data: *const u8, - pub len: u64, +pub struct JsArrayBufferValue { + pub(crate) value: JsArrayBuffer, + data: mem::ManuallyDrop>, } impl JsArrayBuffer { - pub(crate) fn from_raw_unchecked(env: sys::napi_env, value: sys::napi_value) -> Self { - Self { - value: JsObject(Value { - env, - value, - value_type: ValueType::Object, - }), - data: ptr::null(), - len: 0, - } - } - - #[inline] - pub fn into_unknown(self) -> Result { - JsUnknown::from_raw(self.value.0.env, self.value.0.value) - } - - #[inline] - pub fn coerce_to_number(self) -> Result { - 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 { - 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 { - 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 { - 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 { - 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 { - 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 { - 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 { - 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 { - 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(&self, constructor: Constructor) -> Result { - 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 { + pub fn into_value(self) -> Result { let mut data = ptr::null_mut(); let mut len: u64 = 0; - check_status(unsafe { sys::napi_get_arraybuffer_info(env, value, &mut data, &mut len) })?; - Ok(JsArrayBuffer { - value: JsObject(Value { - env, - value, - value_type: ValueType::Object, + check_status(unsafe { + sys::napi_get_arraybuffer_info(self.0.env, self.0.value, &mut data, &mut len) + })?; + Ok(JsArrayBufferValue { + data: mem::ManuallyDrop::new(unsafe { + Vec::from_raw_parts(data as *mut _, len as usize, len as usize) }), - data: data as *const u8, - len, + value: self, }) } - fn from_raw_unchecked(env: sys::napi_env, value: sys::napi_value) -> Self { - let mut data = ptr::null_mut(); - let mut len: u64 = 0; - 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, - } + #[inline] + pub fn into_ref(self) -> Result> { + Ref::new(self.0, 1, self.into_value()?) } } -impl TryFrom for JsArrayBuffer { - type Error = Error; - fn try_from(value: JsUnknown) -> Result { - JsArrayBuffer::from_raw(value.0.env, value.0.value) +impl JsArrayBufferValue { + pub fn new(value: JsArrayBuffer, data: Vec) -> Self { + JsArrayBufferValue { + value, + data: mem::ManuallyDrop::new(data), + } + } + + pub fn into_raw(self) -> JsArrayBuffer { + self.value + } + + pub fn into_unknown(self) -> Result { + 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() } } diff --git a/napi/src/js_values/mod.rs b/napi/src/js_values/mod.rs index 9ed42f30..5075f4ae 100644 --- a/napi/src/js_values/mod.rs +++ b/napi/src/js_values/mod.rs @@ -27,7 +27,7 @@ mod value; mod value_ref; mod value_type; -pub use arraybuffer::JsArrayBuffer; +pub use arraybuffer::*; #[cfg(napi6)] pub use bigint::JsBigint; pub use boolean::JsBoolean; @@ -231,6 +231,7 @@ impl_js_value_methods!(JsUnknown); impl_js_value_methods!(JsUndefined); impl_js_value_methods!(JsNull); impl_js_value_methods!(JsBoolean); +impl_js_value_methods!(JsArrayBuffer); impl_js_value_methods!(JsBuffer); impl_js_value_methods!(JsNumber); 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!(JsBoolean, Boolean); impl_napi_value_trait!(JsBuffer, Object); +impl_napi_value_trait!(JsArrayBuffer, Object); impl_napi_value_trait!(JsNumber, Number); impl_napi_value_trait!(JsString, String); impl_napi_value_trait!(JsObject, Object); diff --git a/test_module/__test__/arraybuffer.spec.ts b/test_module/__test__/arraybuffer.spec.ts new file mode 100644 index 00000000..10916708 --- /dev/null +++ b/test_module/__test__/arraybuffer.spec.ts @@ -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) +}) diff --git a/test_module/src/arraybuffer.rs b/test_module/src/arraybuffer.rs new file mode 100644 index 00000000..e5931f73 --- /dev/null +++ b/test_module/src/arraybuffer.rs @@ -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 { + let buffer = ctx.get::(0)?.into_value()?; + ctx.env.create_uint32((&buffer).len() as u32) +} + +#[js_function(1)] +pub fn arraybuffer_to_string(ctx: CallContext) -> Result { + let buffer = ctx.get::(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(()) +} diff --git a/test_module/src/lib.rs b/test_module/src/lib.rs index 5d30ecc5..25435cf9 100644 --- a/test_module/src/lib.rs +++ b/test_module/src/lib.rs @@ -18,6 +18,7 @@ mod napi6; #[cfg(napi4)] mod tokio_rt; +mod arraybuffer; mod buffer; mod class; mod either; @@ -43,6 +44,7 @@ fn init(module: &mut Module) -> Result<()> { serde::register_js(module)?; task::register_js(module)?; external::register_js(module)?; + arraybuffer::register_js(module)?; buffer::register_js(module)?; either::register_js(module)?; symbol::register_js(module)?;