diff --git a/napi/src/env.rs b/napi/src/env.rs index 46e4aa44..937b1260 100644 --- a/napi/src/env.rs +++ b/napi/src/env.rs @@ -417,7 +417,8 @@ impl Env { Ok(JsArrayBufferValue::new( unsafe { JsArrayBuffer::from_raw_unchecked(self.0, raw_value) }, - mem::ManuallyDrop::new(data), + data_ptr as *mut c_void, + length, )) } @@ -437,13 +438,15 @@ impl Env { ) })?; + mem::forget(data); Ok(JsArrayBufferValue::new( JsArrayBuffer(Value { env: self.0, value: raw_value, value_type: ValueType::Object, }), - mem::ManuallyDrop::new(data), + data_ptr as *mut c_void, + length, )) } @@ -486,7 +489,8 @@ impl Env { value: raw_value, value_type: ValueType::Object, }), - mem::ManuallyDrop::new(Vec::from_raw_parts(data as *mut u8, length, length)), + data as *mut c_void, + length, )) } diff --git a/napi/src/js_values/arraybuffer.rs b/napi/src/js_values/arraybuffer.rs index cfd91a14..8b0303d7 100644 --- a/napi/src/js_values/arraybuffer.rs +++ b/napi/src/js_values/arraybuffer.rs @@ -1,4 +1,3 @@ -use std::mem; use std::ops::{Deref, DerefMut}; use std::os::raw::c_void; use std::ptr; @@ -10,7 +9,8 @@ pub struct JsArrayBuffer(pub(crate) Value); pub struct JsArrayBufferValue { pub(crate) value: JsArrayBuffer, - data: mem::ManuallyDrop>, + len: usize, + data: *mut c_void, } pub struct JsTypedArray(pub(crate) Value); @@ -34,6 +34,7 @@ pub struct JsDataViewValue { #[repr(i32)] #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +#[non_exhaustive] pub enum TypedArrayType { Int8 = 0, Uint8, @@ -44,7 +45,9 @@ pub enum TypedArrayType { Uint32, Float32, Float64, + #[cfg(feature = "napi6")] BigInt64, + #[cfg(feature = "napi6")] BigUint64, /// compatible with higher versions @@ -63,7 +66,9 @@ impl From for TypedArrayType { sys::TypedarrayType::napi_uint32_array => Self::Uint32, sys::TypedarrayType::napi_float32_array => Self::Float32, sys::TypedarrayType::napi_float64_array => Self::Float64, + #[cfg(feature = "napi6")] sys::TypedarrayType::napi_bigint64_array => Self::BigInt64, + #[cfg(feature = "napi6")] sys::TypedarrayType::napi_biguint64_array => Self::BigUint64, _ => Self::Unknown, } @@ -72,7 +77,7 @@ impl From for TypedArrayType { impl From for sys::napi_typedarray_type { fn from(value: TypedArrayType) -> sys::napi_typedarray_type { - value as _ + value as i32 } } @@ -98,16 +103,12 @@ impl JsArrayBuffer { let mut data = ptr::null_mut(); let mut len: usize = 0; check_status!(unsafe { - sys::napi_get_arraybuffer_info( - self.0.env, - self.0.value, - &mut data, - &mut len as *mut usize as *mut _, - ) + sys::napi_get_arraybuffer_info(self.0.env, self.0.value, &mut data, &mut len as *mut usize) })?; Ok(JsArrayBufferValue { - data: mem::ManuallyDrop::new(unsafe { Vec::from_raw_parts(data as *mut _, len, len) }), + data, value: self, + len, }) } @@ -163,8 +164,8 @@ impl JsArrayBuffer { impl JsArrayBufferValue { #[inline] - pub fn new(value: JsArrayBuffer, data: mem::ManuallyDrop>) -> Self { - JsArrayBufferValue { value, data } + pub fn new(value: JsArrayBuffer, data: *mut c_void, len: usize) -> Self { + JsArrayBufferValue { value, len, data } } #[inline] @@ -180,7 +181,13 @@ impl JsArrayBufferValue { impl AsRef<[u8]> for JsArrayBufferValue { fn as_ref(&self) -> &[u8] { - self.data.as_slice() + unsafe { slice::from_raw_parts(self.data as *const u8, self.len) } + } +} + +impl AsMut<[u8]> for JsArrayBufferValue { + fn as_mut(&mut self) -> &mut [u8] { + unsafe { slice::from_raw_parts_mut(self.data as *mut u8, self.len) } } } @@ -188,13 +195,13 @@ impl Deref for JsArrayBufferValue { type Target = [u8]; fn deref(&self) -> &[u8] { - self.data.as_slice() + self.as_ref() } } impl DerefMut for JsArrayBufferValue { fn deref_mut(&mut self) -> &mut Self::Target { - self.data.as_mut_slice() + self.as_mut() } } @@ -218,7 +225,7 @@ impl JsTypedArray { &mut len as *mut u64 as *mut _, &mut data, &mut arraybuffer_value, - &mut byte_offset as *mut u64 as *mut _, + &mut byte_offset as *mut u64 as *mut usize, ) })?; @@ -232,26 +239,34 @@ impl JsTypedArray { } } -impl JsTypedArrayValue { - #[inline(always)] - fn as_slice(&self) -> &[u8] { - unsafe { slice::from_raw_parts(self.data as *const u8, self.length as usize) } - } +macro_rules! impl_as_ref { + ($ref_type:ident) => { + impl AsRef<[$ref_type]> for JsTypedArrayValue { + fn as_ref(&self) -> &[$ref_type] { + unsafe { slice::from_raw_parts(self.data as *const $ref_type, self.length as usize) } + } + } + + impl AsMut<[$ref_type]> for JsTypedArrayValue { + fn as_mut(&mut self) -> &mut [$ref_type] { + unsafe { slice::from_raw_parts_mut(self.data as *mut $ref_type, self.length as usize) } + } + } + }; } -impl AsRef<[u8]> for JsTypedArrayValue { - fn as_ref(&self) -> &[u8] { - self.as_slice() - } -} - -impl Deref for JsTypedArrayValue { - type Target = [u8]; - - fn deref(&self) -> &[u8] { - self.as_slice() - } -} +impl_as_ref!(u8); +impl_as_ref!(i8); +impl_as_ref!(u16); +impl_as_ref!(i16); +impl_as_ref!(u32); +impl_as_ref!(i32); +impl_as_ref!(f32); +impl_as_ref!(f64); +#[cfg(feature = "napi6")] +impl_as_ref!(i64); +#[cfg(feature = "napi6")] +impl_as_ref!(u64); impl JsDataView { #[inline] diff --git a/sys/src/lib.rs b/sys/src/lib.rs index 27a4e698..9f6a2f2d 100644 --- a/sys/src/lib.rs +++ b/sys/src/lib.rs @@ -98,7 +98,9 @@ pub mod TypedarrayType { pub const napi_uint32_array: i32 = 6; pub const napi_float32_array: i32 = 7; pub const napi_float64_array: i32 = 8; + #[cfg(feature = "napi6")] pub const napi_bigint64_array: i32 = 9; + #[cfg(feature = "napi6")] pub const napi_biguint64_array: i32 = 10; } diff --git a/test_module/__test__/arraybuffer.spec.ts b/test_module/__test__/arraybuffer.spec.ts index 10916708..100dc70b 100644 --- a/test_module/__test__/arraybuffer.spec.ts +++ b/test_module/__test__/arraybuffer.spec.ts @@ -1,8 +1,48 @@ -import test from 'ava' +import ava from 'ava' + +import { napiVersion } from './napi-version' const bindings = require('../index.node') +const test = napiVersion >= 6 ? ava : ava.skip + test('should get arraybuffer length', (t) => { const fixture = Buffer.from('wow, hello') t.is(bindings.getArraybufferLength(fixture.buffer), fixture.buffer.byteLength) }) + +test('should be able to mutate Uint8Array', (t) => { + const fixture = new Uint8Array([0, 1, 2]) + bindings.mutateUint8Array(fixture) + t.is(fixture[0], 42) +}) + +test('should be able to mutate Uint16Array', (t) => { + const fixture = new Uint16Array([0, 1, 2]) + bindings.mutateUint16Array(fixture) + t.is(fixture[0], 65535) +}) + +test('should be able to mutate Int16Array', (t) => { + const fixture = new Int16Array([0, 1, 2]) + bindings.mutateInt16Array(fixture) + t.is(fixture[0], 32767) +}) + +test('should be able to mutate Float32Array', (t) => { + const fixture = new Float32Array([0, 1, 2]) + bindings.mutateFloat32Array(fixture) + t.true(Math.abs(fixture[0] - 3.14) <= 0.0001) +}) + +test('should be able to mutate Float64Array', (t) => { + const fixture = new Float64Array([0, 1, 2]) + bindings.mutateFloat64Array(fixture) + t.true(Math.abs(fixture[0] - Math.PI) <= 0.0000001) +}) + +test('should be able to mutate BigInt64Array', (t) => { + const fixture = new BigInt64Array([BigInt(0), BigInt(1), BigInt(2)]) + bindings.mutateI64Array(fixture) + t.deepEqual(fixture[0], BigInt('9223372036854775807')) +}) diff --git a/test_module/src/arraybuffer.rs b/test_module/src/arraybuffer.rs index ad38efd2..4ebb1348 100644 --- a/test_module/src/arraybuffer.rs +++ b/test_module/src/arraybuffer.rs @@ -1,6 +1,7 @@ +use std::f64::consts::PI; use std::str; -use napi::{CallContext, JsArrayBuffer, JsNumber, JsObject, Result}; +use napi::{CallContext, JsArrayBuffer, JsNumber, JsObject, JsTypedArray, JsUndefined, Result}; #[js_function(1)] pub fn get_arraybuffer_length(ctx: CallContext) -> Result { @@ -8,7 +9,63 @@ pub fn get_arraybuffer_length(ctx: CallContext) -> Result { ctx.env.create_uint32((&buffer).len() as u32) } +#[js_function(1)] +pub fn mutate_uint8_array(ctx: CallContext) -> Result { + let mut buffer = ctx.get::(0)?.into_value()?; + let buffer_mut_ref: &mut [u8] = buffer.as_mut(); + buffer_mut_ref[0] = 42; + ctx.env.get_undefined() +} + +#[js_function(1)] +pub fn mutate_uint16_array(ctx: CallContext) -> Result { + let mut buffer = ctx.get::(0)?.into_value()?; + let buffer_mut_ref: &mut [u16] = buffer.as_mut(); + buffer_mut_ref[0] = 65535; + ctx.env.get_undefined() +} + +#[js_function(1)] +pub fn mutate_int16_array(ctx: CallContext) -> Result { + let mut buffer = ctx.get::(0)?.into_value()?; + let buffer_mut_ref: &mut [i16] = buffer.as_mut(); + buffer_mut_ref[0] = 32767; + ctx.env.get_undefined() +} + +#[js_function(1)] +pub fn mutate_float32_array(ctx: CallContext) -> Result { + let mut buffer = ctx.get::(0)?.into_value()?; + let buffer_mut_ref: &mut [f32] = buffer.as_mut(); + buffer_mut_ref[0] = 3.14; + ctx.env.get_undefined() +} + +#[js_function(1)] +pub fn mutate_float64_array(ctx: CallContext) -> Result { + let mut buffer = ctx.get::(0)?.into_value()?; + let buffer_mut_ref: &mut [f64] = buffer.as_mut(); + buffer_mut_ref[0] = PI; + ctx.env.get_undefined() +} + +#[js_function(1)] +#[cfg(feature = "latest")] +pub fn mutate_i64_array(ctx: CallContext) -> Result { + let mut buffer = ctx.get::(0)?.into_value()?; + let buffer_mut_ref: &mut [i64] = buffer.as_mut(); + buffer_mut_ref[0] = 9223372036854775807; + ctx.env.get_undefined() +} + pub fn register_js(exports: &mut JsObject) -> Result<()> { exports.create_named_method("getArraybufferLength", get_arraybuffer_length)?; + exports.create_named_method("mutateUint8Array", mutate_uint8_array)?; + exports.create_named_method("mutateUint16Array", mutate_uint16_array)?; + exports.create_named_method("mutateInt16Array", mutate_int16_array)?; + exports.create_named_method("mutateFloat32Array", mutate_float32_array)?; + exports.create_named_method("mutateFloat64Array", mutate_float64_array)?; + #[cfg(feature = "latest")] + exports.create_named_method("mutateI64Array", mutate_i64_array)?; Ok(()) }