diff --git a/crates/neon/src/types_impl/buffer/mod.rs b/crates/neon/src/types_impl/buffer/mod.rs index 6942e33b8..4f9f8ccce 100644 --- a/crates/neon/src/types_impl/buffer/mod.rs +++ b/crates/neon/src/types_impl/buffer/mod.rs @@ -14,7 +14,7 @@ use crate::{ result::{JsResult, NeonResult, ResultExt}, types::{ buffer::lock::{Ledger, Lock}, - JsArrayBuffer, JsTypedArray, + JsArrayBuffer, JsTypedArray, Value, }, }; @@ -47,7 +47,7 @@ pub use types::Binary; /// ``` /// /// [typed-arrays]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Typed_arrays -pub trait TypedArray: private::Sealed { +pub trait TypedArray: Value { type Item: Binary; /// Statically checked immutable borrow of binary data. @@ -93,6 +93,11 @@ pub trait TypedArray: private::Sealed { fn size<'cx, C>(&self, cx: &mut C) -> usize where C: Context<'cx>; + + /// Constructs an instance from a slice by copying its contents. + fn from_slice<'cx, C>(cx: &mut C, slice: &[Self::Item]) -> JsResult<'cx, Self> + where + C: Context<'cx>; } #[derive(Debug)] diff --git a/crates/neon/src/types_impl/buffer/types.rs b/crates/neon/src/types_impl/buffer/types.rs index a84c964c3..3c927a625 100644 --- a/crates/neon/src/types_impl/buffer/types.rs +++ b/crates/neon/src/types_impl/buffer/types.rs @@ -60,6 +60,17 @@ impl JsBuffer { } } + /// Constructs a `JsBuffer` from a slice by copying its contents. + /// + /// This method is defined on `JsBuffer` as a convenience and delegates to + /// [`TypedArray::from_slice`][TypedArray::from_slice]. + pub fn from_slice<'cx, C>(cx: &mut C, slice: &[u8]) -> JsResult<'cx, Self> + where + C: Context<'cx>, + { + ::from_slice(cx, slice) + } + /// Constructs a new `Buffer` object with uninitialized memory pub unsafe fn uninitialized<'a, C: Context<'a>>(cx: &mut C, len: usize) -> JsResult<'a, Self> { let result = sys::buffer::uninitialized(cx.env().to_raw(), len); @@ -169,6 +180,16 @@ impl TypedArray for JsBuffer { fn size<'cx, C: Context<'cx>>(&self, cx: &mut C) -> usize { unsafe { sys::buffer::size(cx.env().to_raw(), self.to_raw()) } } + + fn from_slice<'cx, C>(cx: &mut C, slice: &[u8]) -> JsResult<'cx, Self> + where + C: Context<'cx>, + { + let mut buffer = cx.buffer(slice.len())?; + let target = buffer.as_mut_slice(cx); + target.copy_from_slice(slice); + Ok(buffer) + } } /// The standard JS [`ArrayBuffer`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer) type. @@ -206,6 +227,17 @@ impl JsArrayBuffer { } } + /// Constructs a `JsArrayBuffer` from a slice by copying its contents. + /// + /// This method is defined on `JsArrayBuffer` as a convenience and delegates to + /// [`TypedArray::from_slice`][TypedArray::from_slice]. + pub fn from_slice<'cx, C>(cx: &mut C, slice: &[u8]) -> JsResult<'cx, Self> + where + C: Context<'cx>, + { + ::from_slice(cx, slice) + } + /// Construct a new `JsArrayBuffer` from bytes allocated by Rust pub fn external<'a, C, T>(cx: &mut C, data: T) -> Handle<'a, Self> where @@ -339,12 +371,23 @@ impl TypedArray for JsArrayBuffer { fn size<'cx, C: Context<'cx>>(&self, cx: &mut C) -> usize { unsafe { sys::arraybuffer::size(cx.env().to_raw(), self.to_raw()) } } + + fn from_slice<'cx, C>(cx: &mut C, slice: &[u8]) -> JsResult<'cx, Self> + where + C: Context<'cx>, + { + let len = slice.len(); + let mut buffer = JsArrayBuffer::new(cx, len)?; + let target = buffer.as_mut_slice(cx); + target.copy_from_slice(slice); + Ok(buffer) + } } /// A marker trait for all possible element types of binary buffers. /// /// This trait can only be implemented within the Neon library. -pub trait Binary: private::Sealed + Clone { +pub trait Binary: private::Sealed + Copy { /// The internal Node-API enum value for this binary type. const TYPE_TAG: TypedArrayType; } @@ -475,7 +518,11 @@ impl Managed for JsTypedArray { } } -impl TypedArray for JsTypedArray { +impl TypedArray for JsTypedArray +where + T: Binary, + Self: Value, +{ type Item = T; fn as_slice<'cx, 'a, C>(&self, cx: &'a C) -> &'a [Self::Item] @@ -547,6 +594,37 @@ impl TypedArray for JsTypedArray { fn size<'cx, C: Context<'cx>>(&self, cx: &mut C) -> usize { self.len(cx) * std::mem::size_of::() } + + fn from_slice<'cx, C>(cx: &mut C, slice: &[T]) -> JsResult<'cx, Self> + where + C: Context<'cx>, + { + let elt_size = std::mem::size_of::(); + let size = slice.len() * elt_size; + let buffer = cx.array_buffer(size)?; + + let mut array = Self::from_buffer(cx, buffer)?; + let target = array.as_mut_slice(cx); + target.copy_from_slice(slice); + + Ok(array) + } +} + +impl JsTypedArray +where + JsTypedArray: Value, +{ + /// Constructs an instance from a slice by copying its contents. + /// + /// This method is defined on `JsTypedArray` as a convenience and delegates to + /// [`TypedArray::from_slice`][TypedArray::from_slice]. + pub fn from_slice<'cx, C>(cx: &mut C, slice: &[T]) -> JsResult<'cx, Self> + where + C: Context<'cx>, + { + as TypedArray>::from_slice(cx, slice) + } } impl JsTypedArray { diff --git a/test/napi/lib/typedarrays.js b/test/napi/lib/typedarrays.js index e53fc0ed4..17795bdc7 100644 --- a/test/napi/lib/typedarrays.js +++ b/test/napi/lib/typedarrays.js @@ -152,6 +152,14 @@ describe("Typed arrays", function () { assert.ok(b.equals(Buffer.alloc(16))); }); + it("gets a 16-byte buffer initialized from a slice", function () { + var b = addon.return_array_buffer_from_slice(16); + var a = new Uint8Array(b); + for (var i = 0; i < 16; i++) { + assert.strictEqual(a[i], i); + } + }); + it("gets an external Buffer", function () { var expected = "String to copy"; var buf = addon.return_external_buffer(expected); @@ -242,6 +250,13 @@ describe("Typed arrays", function () { ); }); + it("gets a typed array copied from a slice", function () { + var i32 = addon.return_int32array_from_slice(16); + for (var i = 0; i < 16; i++) { + assert.strictEqual(i32[i], i); + } + }); + it("gets correct typed array info", function () { var buf = new ArrayBuffer(128); diff --git a/test/napi/src/js/typedarrays.rs b/test/napi/src/js/typedarrays.rs index 572a4e7f2..23ac74ec1 100644 --- a/test/napi/src/js/typedarrays.rs +++ b/test/napi/src/js/typedarrays.rs @@ -8,6 +8,13 @@ pub fn return_array_buffer(mut cx: FunctionContext) -> JsResult { Ok(b) } +pub fn return_array_buffer_from_slice(mut cx: FunctionContext) -> JsResult { + let len = cx.argument::(0)?.value(&mut cx) as usize; + let v = (0..len).map(|i| i as u8).collect::>(); + + JsArrayBuffer::from_slice(&mut cx, &v) +} + pub fn read_array_buffer_with_lock(mut cx: FunctionContext) -> JsResult { let buf = cx.argument::>(0)?; let i = cx.argument::(1)?.value(&mut cx) as usize; @@ -150,6 +157,18 @@ pub fn return_new_int32array(mut cx: FunctionContext) -> JsResult JsInt32Array::new(&mut cx, len) } +pub fn return_int32array_from_slice(mut cx: FunctionContext) -> JsResult { + let len: Handle = cx.argument(0)?; + let len: f64 = len.value(&mut cx); + let len: u32 = len as u32; + let mut v: Vec = Vec::new(); + for i in 0..len { + v.push(i as i32); + } + let a: Handle = JsInt32Array::from_slice(&mut cx, &v)?; + Ok(a) +} + pub fn return_uint32array_from_arraybuffer_region( mut cx: FunctionContext, ) -> JsResult { @@ -174,6 +193,7 @@ fn typed_array_info<'cx, C, T: Binary>( ) -> JsResult<'cx, JsObject> where C: Context<'cx>, + JsTypedArray: Value, { let offset = a.offset(cx); let offset = cx.number(offset as u32); diff --git a/test/napi/src/lib.rs b/test/napi/src/lib.rs index 607baa48a..58f1a1ba2 100644 --- a/test/napi/src/lib.rs +++ b/test/napi/src/lib.rs @@ -212,6 +212,10 @@ fn main(mut cx: ModuleContext) -> NeonResult<()> { cx.export_function("seal_js_object", seal_js_object)?; cx.export_function("return_array_buffer", return_array_buffer)?; + cx.export_function( + "return_array_buffer_from_slice", + return_array_buffer_from_slice, + )?; cx.export_function("read_array_buffer_with_lock", read_array_buffer_with_lock)?; cx.export_function( "read_array_buffer_with_borrow", @@ -254,6 +258,7 @@ fn main(mut cx: ModuleContext) -> NeonResult<()> { return_biguint64array_from_arraybuffer, )?; cx.export_function("return_new_int32array", return_new_int32array)?; + cx.export_function("return_int32array_from_slice", return_int32array_from_slice)?; cx.export_function( "return_uint32array_from_arraybuffer_region", return_uint32array_from_arraybuffer_region,