Skip to content

Commit

Permalink
feat(neon): Add extractor for TypedArray
Browse files Browse the repository at this point in the history
  • Loading branch information
kjvalencik committed Jan 22, 2025
1 parent e996288 commit efa0877
Show file tree
Hide file tree
Showing 6 changed files with 255 additions and 217 deletions.
240 changes: 240 additions & 0 deletions crates/neon/src/types_impl/extract/buffer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,240 @@
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<B>(pub B);

impl<'cx, B> TryFromJs<'cx> for ArrayBuffer<B>
where
for<'b> B: From<&'b [u8]>,
{
type Error = TypeExpected<JsBuffer>;

fn try_from_js(
cx: &mut Cx<'cx>,
v: Handle<'cx, JsValue>,
) -> NeonResult<Result<Self, Self::Error>> {
let v = match v.downcast::<JsArrayBuffer, _>(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<B>
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<B> private::Sealed for ArrayBuffer<B> {}

/// Wrapper for converting between bytes and [`JsBuffer`](JsBuffer)
pub struct Buffer<B>(pub B);

impl<'cx, B> TryFromJs<'cx> for Buffer<B>
where
for<'b> B: From<&'b [u8]>,
{
type Error = TypeExpected<JsBuffer>;

fn try_from_js(
cx: &mut Cx<'cx>,
v: Handle<'cx, JsValue>,
) -> NeonResult<Result<Self, Self::Error>> {
let v = match v.downcast::<JsBuffer, _>(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<B>
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<B> private::Sealed for Buffer<B> {}

impl<'cx, T> TryIntoJs<'cx> for Vec<T>
where
JsTypedArray<T>: Value,
T: Binary,
{
type Value = JsTypedArray<T>;

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<T>
where
JsTypedArray<T>: Value,
T: Binary,
{
type Error = TypeExpected<JsTypedArray<T>>;

fn try_from_js(
cx: &mut Cx<'cx>,
v: Handle<'cx, JsValue>,
) -> NeonResult<Result<Self, Self::Error>> {
let v = match v.downcast::<JsTypedArray<T>, _>(cx) {
Ok(v) => v,
Err(_) => return Ok(Err(Self::Error::new())),
};

Ok(Ok(v.as_slice(cx).to_vec()))
}
}

impl<T> private::Sealed for Vec<T>
where
JsTypedArray<T>: Value,
T: Binary,
{
}

impl<'cx, T> TryIntoJs<'cx> for Box<[T]>
where
JsTypedArray<T>: Value,
T: Binary,
{
type Value = JsTypedArray<T>;

fn try_into_js(self, cx: &mut Cx<'cx>) -> JsResult<'cx, Self::Value> {
JsTypedArray::from_slice(cx, &self)
}
}

impl<T> private::Sealed for Box<[T]>
where
JsTypedArray<T>: Value,
T: Binary,
{
}

impl<'cx, T, const N: usize> TryIntoJs<'cx> for [T; N]
where
JsTypedArray<T>: Value,
T: Binary,
{
type Value = JsTypedArray<T>;

fn try_into_js(self, cx: &mut Cx<'cx>) -> JsResult<'cx, Self::Value> {
JsTypedArray::from_slice(cx, self.as_slice())
}
}

impl<T, const N: usize> private::Sealed for [T; N]
where
JsTypedArray<T>: Value,
T: Binary,
{
}

impl<'cx, T> TryIntoJs<'cx> for &[T]
where
JsTypedArray<T>: Value,
T: Binary,
{
type Value = JsTypedArray<T>;

fn try_into_js(self, cx: &mut Cx<'cx>) -> JsResult<'cx, Self::Value> {
JsTypedArray::from_slice(cx, self)
}
}

impl<T> private::Sealed for &[T]
where
JsTypedArray<T>: 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<T>(pub T);

impl<'cx, T> TryIntoJs<'cx> for $name<T>
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<T>
where
for<'a> T: From<&'a [$type]>,
{
type Error = TypeExpected<$js>;

fn try_from_js(
cx: &mut Cx<'cx>,
v: Handle<'cx, JsValue>,
) -> NeonResult<Result<Self, Self::Error>> {
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<T> private::Sealed for $name<T> {}
};

($(($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),
];
11 changes: 5 additions & 6 deletions crates/neon/src/types_impl/extract/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};
Expand All @@ -121,6 +125,7 @@ pub use self::json::Json;
pub mod json;

mod boxed;
mod buffer;
mod either;
mod error;
mod private;
Expand Down Expand Up @@ -171,12 +176,6 @@ where
/// Wrapper for converting between [`f64`] and [`JsDate`](super::JsDate)
pub struct Date(pub f64);

/// Wrapper for converting between [`Vec<u8>`] and [`JsArrayBuffer`](super::JsArrayBuffer)
pub struct ArrayBuffer(pub Vec<u8>);

/// Wrapper for converting between [`Vec<u8>`] and [`JsBuffer`](super::JsBuffer)
pub struct Buffer(pub Vec<u8>);

/// Trait specifying values that may be extracted from function arguments.
///
/// **Note:** This trait is implemented for tuples of up to 32 values, but for
Expand Down
62 changes: 3 additions & 59 deletions crates/neon/src/types_impl/extract/private.rs
Original file line number Diff line number Diff line change
@@ -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,
},
};

Expand Down Expand Up @@ -46,59 +43,6 @@ impl<T> Sealed for Option<T> {}

impl<T, E> Sealed for Result<T, E> {}

impl<T> Sealed for Vec<T>
where
JsTypedArray<T>: Value,
T: Binary,
{
}

impl<T> Sealed for Box<[T]>
where
JsTypedArray<T>: Value,
T: Binary,
{
}

impl<T, const N: usize> Sealed for [T; N]
where
JsTypedArray<T>: Value,
T: Binary,
{
}

impl<T> Sealed for &Vec<T>
where
JsTypedArray<T>: Value,
T: Binary,
{
}

impl<T> Sealed for &[T]
where
JsTypedArray<T>: Value,
T: Binary,
{
}

impl<'cx, T> Sealed for Arc<T> where for<'a> &'a T: TryIntoJs<'cx> {}

impl<'cx, T> Sealed for Box<T> 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,);
Loading

0 comments on commit efa0877

Please sign in to comment.