feat(napi): implement TypedArray related APIs
This commit is contained in:
parent
3da8daf192
commit
0deb9f7755
3 changed files with 128 additions and 26 deletions
|
@ -2,7 +2,7 @@ use std::mem;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
|
|
||||||
use super::Value;
|
use super::{Value, ValueType};
|
||||||
use crate::error::check_status;
|
use crate::error::check_status;
|
||||||
use crate::{sys, JsUnknown, NapiValue, Ref, Result};
|
use crate::{sys, JsUnknown, NapiValue, Ref, Result};
|
||||||
|
|
||||||
|
@ -16,6 +16,70 @@ pub struct JsArrayBufferValue {
|
||||||
data: mem::ManuallyDrop<Vec<u8>>,
|
data: mem::ManuallyDrop<Vec<u8>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[repr(transparent)]
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct JsTypedArray(pub(crate) Value);
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct JsTypedArrayValue<T> {
|
||||||
|
pub arraybuffer: JsArrayBuffer,
|
||||||
|
pub data: mem::ManuallyDrop<Vec<T>>,
|
||||||
|
pub byte_offset: u64,
|
||||||
|
pub length: u64,
|
||||||
|
pub typedarray_type: TypedArrayType,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum TypedArrayType {
|
||||||
|
Int8,
|
||||||
|
Uint8,
|
||||||
|
Uint8Clamped,
|
||||||
|
Int16,
|
||||||
|
Uint16,
|
||||||
|
Int32,
|
||||||
|
Uint32,
|
||||||
|
Float32,
|
||||||
|
Float64,
|
||||||
|
BigInt64,
|
||||||
|
BigUint64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<sys::napi_typedarray_type> for TypedArrayType {
|
||||||
|
fn from(value: sys::napi_typedarray_type) -> Self {
|
||||||
|
match value {
|
||||||
|
sys::napi_typedarray_type::napi_int8_array => Self::Int8,
|
||||||
|
sys::napi_typedarray_type::napi_uint8_array => Self::Uint8,
|
||||||
|
sys::napi_typedarray_type::napi_uint8_clamped_array => Self::Uint8Clamped,
|
||||||
|
sys::napi_typedarray_type::napi_int16_array => Self::Int16,
|
||||||
|
sys::napi_typedarray_type::napi_uint16_array => Self::Uint16,
|
||||||
|
sys::napi_typedarray_type::napi_int32_array => Self::Int32,
|
||||||
|
sys::napi_typedarray_type::napi_uint32_array => Self::Uint32,
|
||||||
|
sys::napi_typedarray_type::napi_float32_array => Self::Float32,
|
||||||
|
sys::napi_typedarray_type::napi_float64_array => Self::Float64,
|
||||||
|
sys::napi_typedarray_type::napi_bigint64_array => Self::BigInt64,
|
||||||
|
sys::napi_typedarray_type::napi_biguint64_array => Self::BigUint64,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<TypedArrayType> for sys::napi_typedarray_type {
|
||||||
|
fn from(value: TypedArrayType) -> Self {
|
||||||
|
match value {
|
||||||
|
TypedArrayType::Int8 => sys::napi_typedarray_type::napi_int8_array,
|
||||||
|
TypedArrayType::Uint8 => sys::napi_typedarray_type::napi_uint8_array,
|
||||||
|
TypedArrayType::Uint8Clamped => sys::napi_typedarray_type::napi_uint8_clamped_array,
|
||||||
|
TypedArrayType::Int16 => sys::napi_typedarray_type::napi_int16_array,
|
||||||
|
TypedArrayType::Uint16 => sys::napi_typedarray_type::napi_uint16_array,
|
||||||
|
TypedArrayType::Int32 => sys::napi_typedarray_type::napi_int32_array,
|
||||||
|
TypedArrayType::Uint32 => sys::napi_typedarray_type::napi_uint32_array,
|
||||||
|
TypedArrayType::Float32 => sys::napi_typedarray_type::napi_float32_array,
|
||||||
|
TypedArrayType::Float64 => sys::napi_typedarray_type::napi_float64_array,
|
||||||
|
TypedArrayType::BigInt64 => sys::napi_typedarray_type::napi_bigint64_array,
|
||||||
|
TypedArrayType::BigUint64 => sys::napi_typedarray_type::napi_biguint64_array,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl JsArrayBuffer {
|
impl JsArrayBuffer {
|
||||||
pub fn into_value(self) -> Result<JsArrayBufferValue> {
|
pub fn into_value(self) -> Result<JsArrayBufferValue> {
|
||||||
let mut data = ptr::null_mut();
|
let mut data = ptr::null_mut();
|
||||||
|
@ -31,6 +95,30 @@ impl JsArrayBuffer {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn create_typed_array(
|
||||||
|
&self,
|
||||||
|
typedarray_type: TypedArrayType,
|
||||||
|
length: u64,
|
||||||
|
byte_offset: u64,
|
||||||
|
) -> Result<JsTypedArray> {
|
||||||
|
let mut typedarray_value = ptr::null_mut();
|
||||||
|
check_status(unsafe {
|
||||||
|
sys::napi_create_typedarray(
|
||||||
|
self.0.env,
|
||||||
|
typedarray_type.into(),
|
||||||
|
length,
|
||||||
|
self.0.value,
|
||||||
|
byte_offset,
|
||||||
|
&mut typedarray_value,
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
Ok(JsTypedArray(Value {
|
||||||
|
env: self.0.env,
|
||||||
|
value: typedarray_value,
|
||||||
|
value_type: ValueType::Object,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn into_ref(self) -> Result<Ref<JsArrayBufferValue>> {
|
pub fn into_ref(self) -> Result<Ref<JsArrayBufferValue>> {
|
||||||
Ref::new(self.0, 1, self.into_value()?)
|
Ref::new(self.0, 1, self.into_value()?)
|
||||||
|
@ -67,3 +155,38 @@ impl Deref for JsArrayBufferValue {
|
||||||
self.data.as_slice()
|
self.data.as_slice()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl JsTypedArray {
|
||||||
|
/// get TypeArray info
|
||||||
|
/// https://nodejs.org/api/n-api.html#n_api_napi_get_typedarray_info
|
||||||
|
///
|
||||||
|
/// ***Warning***: Use caution while using this API since the underlying data buffer is managed by the VM.
|
||||||
|
pub fn into_value<T>(self) -> Result<JsTypedArrayValue<T>> {
|
||||||
|
let mut typedarray_type = sys::napi_typedarray_type::napi_int8_array;
|
||||||
|
let mut len = 0u64;
|
||||||
|
let mut data = ptr::null_mut();
|
||||||
|
let mut array_buffer = ptr::null_mut();
|
||||||
|
let mut byte_offset = 0u64;
|
||||||
|
check_status(unsafe {
|
||||||
|
sys::napi_get_typedarray_info(
|
||||||
|
self.0.env,
|
||||||
|
self.0.value,
|
||||||
|
&mut typedarray_type,
|
||||||
|
&mut len,
|
||||||
|
&mut data,
|
||||||
|
&mut array_buffer,
|
||||||
|
&mut byte_offset,
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(JsTypedArrayValue {
|
||||||
|
data: mem::ManuallyDrop::new(unsafe {
|
||||||
|
Vec::from_raw_parts(data as *mut T, len as usize, len as usize)
|
||||||
|
}),
|
||||||
|
length: len,
|
||||||
|
byte_offset,
|
||||||
|
typedarray_type: typedarray_type.into(),
|
||||||
|
arraybuffer: JsArrayBuffer::from_raw_unchecked(self.0.env, array_buffer),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -443,8 +443,9 @@ 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!(JsArrayBuffer);
|
||||||
|
impl_js_value_methods!(JsTypedArray);
|
||||||
impl_js_value_methods!(JsNumber);
|
impl_js_value_methods!(JsNumber);
|
||||||
impl_js_value_methods!(JsString);
|
impl_js_value_methods!(JsString);
|
||||||
impl_js_value_methods!(JsObject);
|
impl_js_value_methods!(JsObject);
|
||||||
|
@ -468,6 +469,7 @@ 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!(JsArrayBuffer, Object);
|
||||||
|
impl_napi_value_trait!(JsTypedArray, 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);
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
use std::ffi::CStr;
|
|
||||||
use std::str;
|
use std::str;
|
||||||
|
|
||||||
use napi::{CallContext, JsArrayBuffer, JsNumber, JsString, Module, Result};
|
use napi::{CallContext, JsArrayBuffer, JsNumber, Module, Result};
|
||||||
|
|
||||||
#[js_function(1)]
|
#[js_function(1)]
|
||||||
pub fn get_arraybuffer_length(ctx: CallContext) -> Result<JsNumber> {
|
pub fn get_arraybuffer_length(ctx: CallContext) -> Result<JsNumber> {
|
||||||
|
@ -9,29 +8,7 @@ pub fn get_arraybuffer_length(ctx: CallContext) -> Result<JsNumber> {
|
||||||
ctx.env.create_uint32((&buffer).len() as u32)
|
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<()> {
|
pub fn register_js(module: &mut Module) -> Result<()> {
|
||||||
module.create_named_method("getArraybufferLength", get_arraybuffer_length)?;
|
module.create_named_method("getArraybufferLength", get_arraybuffer_length)?;
|
||||||
module.create_named_method("arraybufferToString", arraybuffer_to_string)?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue