diff --git a/crates/neon/src/types_impl/extract/buffer.rs b/crates/neon/src/types_impl/extract/buffer.rs new file mode 100644 index 000000000..6ae0a18a5 --- /dev/null +++ b/crates/neon/src/types_impl/extract/buffer.rs @@ -0,0 +1,221 @@ +use crate::{ + context::Cx, + handle::Handle, + result::{JsResult, NeonResult}, + types::{ + buffer::{Binary, TypedArray}, + extract::{private, TryFromJs, TryIntoJs, TypeExpected}, + JsArrayBuffer, JsBigInt64Array, JsBigUint64Array, JsBuffer, JsFloat32Array, JsFloat64Array, + JsInt16Array, JsInt32Array, JsInt8Array, JsTypedArray, JsUint16Array, JsUint32Array, + JsUint8Array, JsValue, Value, + }, +}; + +/// Wrapper for converting between bytes and [`JsArrayBuffer`](JsArrayBuffer) +pub struct ArrayBuffer(pub B); + +impl<'cx, B> TryFromJs<'cx> for ArrayBuffer +where + for<'b> B: From<&'b [u8]>, +{ + type Error = TypeExpected; + + fn try_from_js( + cx: &mut Cx<'cx>, + v: Handle<'cx, JsValue>, + ) -> NeonResult> { + let v = match v.downcast::(cx) { + Ok(v) => v, + Err(_) => return Ok(Err(Self::Error::new())), + }; + + Ok(Ok(ArrayBuffer(B::from(v.as_slice(cx))))) + } +} + +impl<'cx, B> TryIntoJs<'cx> for ArrayBuffer +where + B: AsRef<[u8]>, +{ + type Value = JsArrayBuffer; + + fn try_into_js(self, cx: &mut Cx<'cx>) -> JsResult<'cx, Self::Value> { + JsArrayBuffer::from_slice(cx, self.0.as_ref()) + } +} + +impl private::Sealed for ArrayBuffer {} + +/// Wrapper for converting between bytes and [`JsBuffer`](JsBuffer) +pub struct Buffer(pub B); + +impl<'cx, B> TryFromJs<'cx> for Buffer +where + for<'b> B: From<&'b [u8]>, +{ + type Error = TypeExpected; + + fn try_from_js( + cx: &mut Cx<'cx>, + v: Handle<'cx, JsValue>, + ) -> NeonResult> { + let v = match v.downcast::(cx) { + Ok(v) => v, + Err(_) => return Ok(Err(Self::Error::new())), + }; + + Ok(Ok(Buffer(B::from(v.as_slice(cx))))) + } +} + +impl<'cx, B> TryIntoJs<'cx> for Buffer +where + B: AsRef<[u8]>, +{ + type Value = JsBuffer; + + fn try_into_js(self, cx: &mut Cx<'cx>) -> JsResult<'cx, Self::Value> { + JsBuffer::from_slice(cx, self.0.as_ref()) + } +} + +impl private::Sealed for Buffer {} + +impl<'cx, T> TryIntoJs<'cx> for Vec +where + JsTypedArray: Value, + T: Binary, +{ + type Value = JsTypedArray; + + fn try_into_js(self, cx: &mut Cx<'cx>) -> JsResult<'cx, Self::Value> { + JsTypedArray::from_slice(cx, self.as_slice()) + } +} + +impl<'cx, T> TryFromJs<'cx> for Vec +where + JsTypedArray: Value, + T: Binary, +{ + type Error = TypeExpected>; + + fn try_from_js( + cx: &mut Cx<'cx>, + v: Handle<'cx, JsValue>, + ) -> NeonResult> { + let v = match v.downcast::, _>(cx) { + Ok(v) => v, + Err(_) => return Ok(Err(Self::Error::new())), + }; + + Ok(Ok(v.as_slice(cx).to_vec())) + } +} + +impl private::Sealed for Vec +where + JsTypedArray: Value, + T: Binary, +{ +} + +impl<'cx, T, const N: usize> TryIntoJs<'cx> for [T; N] +where + JsTypedArray: Value, + T: Binary, +{ + type Value = JsTypedArray; + + fn try_into_js(self, cx: &mut Cx<'cx>) -> JsResult<'cx, Self::Value> { + JsTypedArray::from_slice(cx, self.as_slice()) + } +} + +impl private::Sealed for [T; N] +where + JsTypedArray: Value, + T: Binary, +{ +} + +impl<'cx, T> TryIntoJs<'cx> for &[T] +where + JsTypedArray: Value, + T: Binary, +{ + type Value = JsTypedArray; + + fn try_into_js(self, cx: &mut Cx<'cx>) -> JsResult<'cx, Self::Value> { + JsTypedArray::from_slice(cx, self) + } +} + +impl private::Sealed for &[T] +where + JsTypedArray: Value, + T: Binary, +{ +} + +macro_rules! typed_array { + ($js:ident, $name:ident, $type:ty) => { + #[doc = concat!( + "Wrapper for converting between a Rust `[", + stringify!($type), + "]` array type and a [`", + stringify!($js), + "`]", + )] + pub struct $name(pub T); + + impl<'cx, T> TryIntoJs<'cx> for $name + where + T: AsRef<[$type]>, + { + type Value = $js; + + fn try_into_js(self, cx: &mut Cx<'cx>) -> JsResult<'cx, Self::Value> { + $js::from_slice(cx, self.0.as_ref()) + } + } + + impl<'cx, T> TryFromJs<'cx> for $name + where + for<'a> T: From<&'a [$type]>, + { + type Error = TypeExpected<$js>; + + fn try_from_js( + cx: &mut Cx<'cx>, + v: Handle<'cx, JsValue>, + ) -> NeonResult> { + let v = match v.downcast::<$js, _>(cx) { + Ok(v) => v, + Err(_) => return Ok(Err(TypeExpected::new())), + }; + + Ok(Ok(Self(T::from(v.as_slice(cx))))) + } + } + + impl private::Sealed for $name {} + }; + + ($(($js:ident, $name:ident, $type:ty),)*) => { + $(typed_array!($js, $name, $type);)* + }; +} + +typed_array![ + (JsInt8Array, Int8Array, i8), + (JsUint8Array, Uint8Array, u8), + (JsInt16Array, Int16Array, i16), + (JsUint16Array, Uint16Array, u16), + (JsInt32Array, Int32Array, i32), + (JsUint32Array, Uint32Array, u32), + (JsFloat32Array, Float32Array, f32), + (JsFloat64Array, Float64Array, f64), + (JsBigInt64Array, BigInt64Array, i64), + (JsBigUint64Array, BigUint64Array, u64), +]; diff --git a/crates/neon/src/types_impl/extract/mod.rs b/crates/neon/src/types_impl/extract/mod.rs index 48957cb54..4813cb8f1 100644 --- a/crates/neon/src/types_impl/extract/mod.rs +++ b/crates/neon/src/types_impl/extract/mod.rs @@ -108,6 +108,10 @@ use crate::{ pub use self::{ boxed::Boxed, + buffer::{ + ArrayBuffer, BigInt64Array, BigUint64Array, Buffer, Float32Array, Float64Array, Int16Array, + Int32Array, Int8Array, Uint16Array, Uint32Array, Uint8Array, + }, error::{Error, TypeExpected}, with::With, }; @@ -121,6 +125,7 @@ pub use self::json::Json; pub mod json; mod boxed; +mod buffer; mod either; mod error; mod private; @@ -171,12 +176,6 @@ where /// Wrapper for converting between [`f64`] and [`JsDate`](super::JsDate) pub struct Date(pub f64); -/// Wrapper for converting between [`Vec`] and [`JsArrayBuffer`](super::JsArrayBuffer) -pub struct ArrayBuffer(pub Vec); - -/// Wrapper for converting between [`Vec`] and [`JsBuffer`](super::JsBuffer) -pub struct Buffer(pub Vec); - /// Trait specifying values that may be extracted from function arguments. /// /// **Note:** This trait is implemented for tuples of up to 32 values, but for diff --git a/crates/neon/src/types_impl/extract/private.rs b/crates/neon/src/types_impl/extract/private.rs index af3687402..b795d9b38 100644 --- a/crates/neon/src/types_impl/extract/private.rs +++ b/crates/neon/src/types_impl/extract/private.rs @@ -1,14 +1,11 @@ -use std::sync::Arc; - use crate::{ context::FunctionContext, handle::{Handle, Root}, object::Object, result::{NeonResult, Throw}, types::{ - buffer::Binary, - extract::{ArrayBuffer, Buffer, Date, Error, TryIntoJs}, - JsTypedArray, Value, + extract::{Date, Error, TryIntoJs}, + Value, }, }; @@ -46,59 +43,6 @@ impl Sealed for Option {} impl Sealed for Result {} -impl Sealed for Vec -where - JsTypedArray: Value, - T: Binary, -{ -} - -impl Sealed for Box<[T]> -where - JsTypedArray: Value, - T: Binary, -{ -} - -impl Sealed for [T; N] -where - JsTypedArray: Value, - T: Binary, -{ -} - -impl Sealed for &Vec -where - JsTypedArray: Value, - T: Binary, -{ -} - -impl Sealed for &[T] -where - JsTypedArray: Value, - T: Binary, -{ -} - -impl<'cx, T> Sealed for Arc where for<'a> &'a T: TryIntoJs<'cx> {} - impl<'cx, T> Sealed for Box where T: TryIntoJs<'cx> {} -impl_sealed!( - u8, - u16, - u32, - i8, - i16, - i32, - f32, - f64, - bool, - String, - Date, - Buffer, - ArrayBuffer, - Throw, - Error, -); +impl_sealed!(u8, u16, u32, i8, i16, i32, f32, f64, bool, String, Date, Throw, Error,); diff --git a/crates/neon/src/types_impl/extract/try_from_js.rs b/crates/neon/src/types_impl/extract/try_from_js.rs index 8d0e65013..5a10803f7 100644 --- a/crates/neon/src/types_impl/extract/try_from_js.rs +++ b/crates/neon/src/types_impl/extract/try_from_js.rs @@ -12,10 +12,9 @@ use crate::{ result::{NeonResult, Throw}, sys, types::{ - buffer::{Binary, TypedArray}, - extract::{ArrayBuffer, Buffer, Date, TryFromJs, TypeExpected}, + extract::{Date, TryFromJs, TypeExpected}, private::ValueInternal, - JsArrayBuffer, JsBoolean, JsBuffer, JsNumber, JsString, JsTypedArray, JsValue, Value, + JsBoolean, JsNumber, JsString, JsValue, Value, }, }; @@ -200,58 +199,6 @@ impl<'cx> TryFromJs<'cx> for () { } } -impl<'cx, T> TryFromJs<'cx> for Vec -where - JsTypedArray: Value, - T: Binary, -{ - type Error = TypeExpected>; - - fn try_from_js( - cx: &mut Cx<'cx>, - v: Handle<'cx, JsValue>, - ) -> NeonResult> { - let v = match v.downcast::, _>(cx) { - Ok(v) => v, - Err(_) => return Ok(Err(Self::Error::new())), - }; - - Ok(Ok(v.as_slice(cx).to_vec())) - } -} - -impl<'cx> TryFromJs<'cx> for Buffer { - type Error = TypeExpected; - - fn try_from_js( - cx: &mut Cx<'cx>, - v: Handle<'cx, JsValue>, - ) -> NeonResult> { - let v = match v.downcast::(cx) { - Ok(v) => v, - Err(_) => return Ok(Err(Self::Error::new())), - }; - - Ok(Ok(Buffer(v.as_slice(cx).to_vec()))) - } -} - -impl<'cx> TryFromJs<'cx> for ArrayBuffer { - type Error = TypeExpected; - - fn try_from_js( - cx: &mut Cx<'cx>, - v: Handle<'cx, JsValue>, - ) -> NeonResult> { - let v = match v.downcast::(cx) { - Ok(v) => v, - Err(_) => return Ok(Err(Self::Error::new())), - }; - - Ok(Ok(ArrayBuffer(v.as_slice(cx).to_vec()))) - } -} - fn is_null_or_undefined(cx: &mut Cx, v: Handle) -> NeonResult where V: Value, diff --git a/crates/neon/src/types_impl/extract/try_into_js.rs b/crates/neon/src/types_impl/extract/try_into_js.rs index e1151a746..5be0e4dfd 100644 --- a/crates/neon/src/types_impl/extract/try_into_js.rs +++ b/crates/neon/src/types_impl/extract/try_into_js.rs @@ -1,15 +1,11 @@ -use std::sync::Arc; - -use crate::prelude::Object; use crate::{ context::{Context, Cx}, handle::{Handle, Root}, + object::Object, result::{JsResult, ResultExt, Throw}, types::{ - buffer::Binary, - extract::{ArrayBuffer, Buffer, Date, TryIntoJs}, - JsArrayBuffer, JsBoolean, JsBuffer, JsDate, JsNumber, JsString, JsTypedArray, JsUndefined, - JsValue, Value, + extract::{Date, TryIntoJs}, + JsBoolean, JsDate, JsNumber, JsString, JsUndefined, JsValue, Value, }, }; @@ -88,18 +84,6 @@ where } } -impl<'cx, T, V> TryIntoJs<'cx> for Arc -where - for<'a> &'a T: TryIntoJs<'cx, Value = V>, - V: Value, -{ - type Value = V; - - fn try_into_js(self, cx: &mut Cx<'cx>) -> JsResult<'cx, Self::Value> { - self.as_ref().try_into_js(cx) - } -} - macro_rules! impl_number { ($ty:ident) => { impl<'cx> TryIntoJs<'cx> for $ty { @@ -144,66 +128,6 @@ impl<'a, 'cx> TryIntoJs<'cx> for &'a String { } } -impl<'cx, T> TryIntoJs<'cx> for Vec -where - JsTypedArray: Value, - T: Binary, -{ - type Value = JsTypedArray; - - fn try_into_js(self, cx: &mut Cx<'cx>) -> JsResult<'cx, Self::Value> { - JsTypedArray::from_slice(cx, &self) - } -} - -impl<'cx, T> TryIntoJs<'cx> for Box<[T]> -where - JsTypedArray: Value, - T: Binary, -{ - type Value = JsTypedArray; - - fn try_into_js(self, cx: &mut Cx<'cx>) -> JsResult<'cx, Self::Value> { - JsTypedArray::from_slice(cx, &self) - } -} - -impl<'cx, T, const N: usize> TryIntoJs<'cx> for [T; N] -where - JsTypedArray: Value, - T: Binary, -{ - type Value = JsTypedArray; - - fn try_into_js(self, cx: &mut Cx<'cx>) -> JsResult<'cx, Self::Value> { - JsTypedArray::from_slice(cx, self.as_slice()) - } -} - -impl<'a, 'cx, T> TryIntoJs<'cx> for &'a Vec -where - JsTypedArray: Value, - T: Binary, -{ - type Value = JsTypedArray; - - fn try_into_js(self, cx: &mut Cx<'cx>) -> JsResult<'cx, Self::Value> { - JsTypedArray::from_slice(cx, self) - } -} - -impl<'a, 'cx, T> TryIntoJs<'cx> for &'a [T] -where - JsTypedArray: Value, - T: Binary, -{ - type Value = JsTypedArray; - - fn try_into_js(self, cx: &mut Cx<'cx>) -> JsResult<'cx, Self::Value> { - JsTypedArray::from_slice(cx, self) - } -} - impl<'cx> TryIntoJs<'cx> for bool { type Value = JsBoolean; @@ -220,22 +144,6 @@ impl<'cx> TryIntoJs<'cx> for () { } } -impl<'cx> TryIntoJs<'cx> for ArrayBuffer { - type Value = JsArrayBuffer; - - fn try_into_js(self, cx: &mut Cx<'cx>) -> JsResult<'cx, Self::Value> { - JsArrayBuffer::from_slice(cx, &self.0) - } -} - -impl<'cx> TryIntoJs<'cx> for Buffer { - type Value = JsBuffer; - - fn try_into_js(self, cx: &mut Cx<'cx>) -> JsResult<'cx, Self::Value> { - JsBuffer::from_slice(cx, &self.0) - } -} - impl<'cx> TryIntoJs<'cx> for Date { type Value = JsDate; diff --git a/test/napi/lib/extract.js b/test/napi/lib/extract.js index 62926e3da..acc8f4f95 100644 --- a/test/napi/lib/extract.js +++ b/test/napi/lib/extract.js @@ -58,6 +58,18 @@ describe("Extractors", () => { test(Float64Array); }); + it("TypedArray", () => { + assert.deepStrictEqual( + Buffer.from(addon.bufferConcat(Buffer.from("abc"), Buffer.from("def"))), + Buffer.from("abcdef") + ); + + assert.deepStrictEqual( + Buffer.from(addon.stringToBuf("Hello, World!")), + Buffer.from("Hello, World!") + ); + }); + it("JSON", () => { assert.strictEqual(addon.extract_json_sum([1, 2, 3, 4]), 10); assert.strictEqual(addon.extract_json_sum([8, 16, 18]), 42); diff --git a/test/napi/src/js/extract.rs b/test/napi/src/js/extract.rs index a3c1da5dd..8587eb4ca 100644 --- a/test/napi/src/js/extract.rs +++ b/test/napi/src/js/extract.rs @@ -22,9 +22,9 @@ pub fn extract_values(mut cx: FunctionContext) -> JsResult { String, Date, Handle, - ArrayBuffer, + ArrayBuffer>, Vec, - Buffer, + Buffer>, Option, Option, ) = cx.args()?; @@ -145,3 +145,16 @@ pub fn extract_either(either: Either) -> String { Either::Right(n) => format!("Number: {n}"), } } + +#[neon::export] +// TypedArrays can be extracted and returned +pub fn buffer_concat(mut a: Vec, Uint8Array(b): Uint8Array>) -> ArrayBuffer> { + a.extend(b); + ArrayBuffer(a) +} + +#[neon::export] +// Extractors work with anything that can be used as slice of the correct type +pub fn string_to_buf(s: String) -> Uint8Array { + Uint8Array(s) +}