feat(napi): accept slice as TypedArray (#1951)

This commit is contained in:
LongYinan 2024-02-15 23:40:45 +08:00 committed by GitHub
parent d7dc4dc5a2
commit 5ac153388f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 244 additions and 12 deletions

View file

@ -268,7 +268,7 @@ impl NapiFn {
if let Some(p) = path.path.segments.first() {
if p.ident == *self.parent.as_ref().unwrap() {
args.push(
quote! { napi::bindgen_prelude::Reference::from_value_ptr(this_ptr as *mut std::ffi::c_void, env)? },
quote! { napi::bindgen_prelude::Reference::from_value_ptr(this_ptr.cast(), env)? },
);
skipped_arg_count += 1;
continue;
@ -344,7 +344,7 @@ impl NapiFn {
}
}
}
let (arg_conversion, arg_type) = self.gen_ty_arg_conversion(&ident, i, path);
let (arg_conversion, arg_type) = self.gen_ty_arg_conversion(&ident, i, path)?;
if NapiArgType::MutRef == arg_type {
mut_ref_spans.push(path.ty.span());
}
@ -378,7 +378,7 @@ impl NapiFn {
arg_name: &Ident,
index: usize,
path: &syn::PatType,
) -> (TokenStream, NapiArgType) {
) -> BindgenResult<(TokenStream, NapiArgType)> {
let ty = &*path.ty;
let type_check = if self.return_if_invalid {
quote! {
@ -402,6 +402,15 @@ impl NapiFn {
};
match ty {
syn::Type::Reference(syn::TypeReference {
lifetime: Some(lifetime),
..
}) => {
return Err(Diagnostic::span_error(
lifetime.span(),
"lifetime is not allowed in napi function arguments",
));
}
syn::Type::Reference(syn::TypeReference {
mutability: Some(_),
elem,
@ -413,16 +422,52 @@ impl NapiFn {
<#elem as napi::bindgen_prelude::FromNapiMutRef>::from_napi_mut_ref(env, cb.get_arg(#index))?
};
};
(q, NapiArgType::MutRef)
Ok((q, NapiArgType::MutRef))
}
syn::Type::Reference(syn::TypeReference { elem, .. }) => {
let q = quote! {
let #arg_name = {
#type_check
<#elem as napi::bindgen_prelude::FromNapiRef>::from_napi_ref(env, cb.get_arg(#index))?
};
syn::Type::Reference(syn::TypeReference {
mutability, elem, ..
}) => {
if let syn::Type::Slice(slice) = &**elem {
if let syn::Type::Path(ele) = &*slice.elem {
if let Some(syn::PathSegment { ident, .. }) = ele.path.segments.first() {
static TYPEDARRAY_SLICE_TYPES: &[&str] = &[
"u8", "i8", "u16", "i16", "u32", "i32", "f32", "f64", "u64", "i64",
];
if TYPEDARRAY_SLICE_TYPES.contains(&&*ident.to_string()) {
let q = quote! {
let #arg_name = {
#type_check
<&mut #elem as napi::bindgen_prelude::FromNapiValue>::from_napi_value(env, cb.get_arg(#index))?
};
};
return Ok((q, NapiArgType::Ref));
}
}
}
}
let q = if mutability.is_some() {
quote! {
let #arg_name = {
#type_check
<#elem as napi::bindgen_prelude::FromNapiMutRef>::from_napi_mut_ref(env, cb.get_arg(#index))?
}
}
} else {
quote! {
let #arg_name = {
#type_check
<#elem as napi::bindgen_prelude::FromNapiRef>::from_napi_ref(env, cb.get_arg(#index))?
};
}
};
(q, NapiArgType::Ref)
Ok((
q,
if mutability.is_some() {
NapiArgType::MutRef
} else {
NapiArgType::Ref
},
))
}
_ => {
let q = quote! {
@ -431,7 +476,7 @@ impl NapiFn {
<#ty as napi::bindgen_prelude::FromNapiValue>::from_napi_value(env, cb.get_arg(#index))?
};
};
(q, NapiArgType::Value)
Ok((q, NapiArgType::Value))
}
}
}

View file

@ -457,6 +457,46 @@ macro_rules! impl_typed_array {
};
}
macro_rules! impl_from_slice {
($name:ident, $rust_type:ident, $typed_array_type:expr) => {
impl FromNapiValue for &mut [$rust_type] {
unsafe fn from_napi_value(env: sys::napi_env, napi_val: sys::napi_value) -> Result<Self> {
let mut typed_array_type = 0;
let mut length = 0;
let mut data = ptr::null_mut();
let mut array_buffer = ptr::null_mut();
let mut byte_offset = 0;
let mut ref_ = ptr::null_mut();
check_status!(
unsafe { sys::napi_create_reference(env, napi_val, 1, &mut ref_) },
"Failed to create reference from Buffer"
)?;
check_status!(
unsafe {
sys::napi_get_typedarray_info(
env,
napi_val,
&mut typed_array_type,
&mut length,
&mut data,
&mut array_buffer,
&mut byte_offset,
)
},
"Get TypedArray info failed"
)?;
if typed_array_type != $typed_array_type as i32 {
return Err(Error::new(
Status::InvalidArg,
format!("Expected $name, got {}", typed_array_type),
));
}
Ok(unsafe { core::slice::from_raw_parts_mut(data as *mut $rust_type, length) })
}
}
};
}
unsafe extern "C" fn finalizer<Data, T: Finalizer<RustType = Data>>(
_env: sys::napi_env,
finalize_data: *mut c_void,
@ -494,18 +534,30 @@ enum DataManagedType {
}
impl_typed_array!(Int8Array, i8, TypedArrayType::Int8);
impl_from_slice!(Int8Array, i8, TypedArrayType::Int8);
impl_typed_array!(Uint8Array, u8, TypedArrayType::Uint8);
impl_from_slice!(Uint8Array, u8, TypedArrayType::Uint8);
impl_typed_array!(Uint8ClampedArray, u8, TypedArrayType::Uint8Clamped);
impl_typed_array!(Int16Array, i16, TypedArrayType::Int16);
impl_from_slice!(Int16Array, i16, TypedArrayType::Int16);
impl_typed_array!(Uint16Array, u16, TypedArrayType::Uint16);
impl_from_slice!(Uint16Array, u16, TypedArrayType::Uint16);
impl_typed_array!(Int32Array, i32, TypedArrayType::Int32);
impl_from_slice!(Int32Array, i32, TypedArrayType::Int32);
impl_typed_array!(Uint32Array, u32, TypedArrayType::Uint32);
impl_from_slice!(Uint32Array, u32, TypedArrayType::Uint32);
impl_typed_array!(Float32Array, f32, TypedArrayType::Float32);
impl_from_slice!(Float32Array, f32, TypedArrayType::Float32);
impl_typed_array!(Float64Array, f64, TypedArrayType::Float64);
impl_from_slice!(Float64Array, f64, TypedArrayType::Float64);
#[cfg(feature = "napi6")]
impl_typed_array!(BigInt64Array, i64, TypedArrayType::BigInt64);
#[cfg(feature = "napi6")]
impl_from_slice!(BigInt64Array, i64, TypedArrayType::BigInt64);
#[cfg(feature = "napi6")]
impl_typed_array!(BigUint64Array, u64, TypedArrayType::BigUint64);
#[cfg(feature = "napi6")]
impl_from_slice!(BigUint64Array, u64, TypedArrayType::BigUint64);
impl<T: Into<Vec<u8>>> From<T> for Uint8Array {
fn from(data: T) -> Self {

View file

@ -85,6 +85,7 @@ Generated by [AVA](https://avajs.dev).
constructor(name: string)␊
getCount(): number␊
getNameAsync(): Promise<string>
acceptSliceMethod(slice: any): number␊
}␊
/** Smoking test for type generation */␊
@ -221,6 +222,8 @@ Generated by [AVA](https://avajs.dev).
foo: number␊
}␊
export function acceptSlice(fixture: any): bigint␊
export function acceptThreadsafeFunction(func: (err: Error | null, arg: number) => any): void␊
export function acceptThreadsafeFunctionFatal(func: (arg: number) => any): void␊
@ -388,6 +391,10 @@ Generated by [AVA](https://avajs.dev).
export function enumToI32(e: CustomNumEnum): number␊
export function f32ArrayToArray(input: any): Array<number>
export function f64ArrayToArray(input: any): Array<number>
export function fibonacci(n: number): number␊
export function fnReceivedAliased(s: AliasedStruct, e: ALIAS): void␊
@ -429,6 +436,14 @@ Generated by [AVA](https://avajs.dev).
export function getWords(): Array<string>
export function i16ArrayToArray(input: any): Array<number>
export function i32ArrayToArray(input: any): Array<number>
export function i64ArrayToArray(input: any): Array<number>
export function i8ArrayToArray(input: any): Array<number>
export function indexmapPassthrough(fixture: IndexMap): IndexMap␊
/** default enum values are continuos i32s start from 0 */␊
@ -595,6 +610,14 @@ Generated by [AVA](https://avajs.dev).
typeOverrideOptional?: object␊
}␊
export function u16ArrayToArray(input: any): Array<number>
export function u32ArrayToArray(input: any): Array<number>
export function u64ArrayToArray(input: any): Array<bigint>
export function u8ArrayToArray(input: any): Array<number>
export function validateArray(arr: Array<number>): number␊
export function validateBigint(input: bigint): bigint␊

View file

@ -94,6 +94,17 @@ const {
xxh3,
xxh64Alias,
tsRename,
acceptSlice,
u8ArrayToArray,
i8ArrayToArray,
u16ArrayToArray,
i16ArrayToArray,
u32ArrayToArray,
i32ArrayToArray,
u64ArrayToArray,
i64ArrayToArray,
f32ArrayToArray,
f64ArrayToArray,
convertU32Array,
createExternalTypedArray,
mutateTypedArray,
@ -665,6 +676,24 @@ test('buffer', (t) => {
t.true(Array.isArray(asyncBufferToArray(Buffer.from([1, 2, 3]).buffer)))
})
test('TypedArray', (t) => {
t.is(acceptSlice(new Uint8Array([1, 2, 3])), 3n)
t.deepEqual(u8ArrayToArray(new Uint8Array([1, 2, 3])), [1, 2, 3])
t.deepEqual(i8ArrayToArray(new Int8Array([1, 2, 3])), [1, 2, 3])
t.deepEqual(u16ArrayToArray(new Uint16Array([1, 2, 3])), [1, 2, 3])
t.deepEqual(i16ArrayToArray(new Int16Array([1, 2, 3])), [1, 2, 3])
t.deepEqual(u32ArrayToArray(new Uint32Array([1, 2, 3])), [1, 2, 3])
t.deepEqual(i32ArrayToArray(new Int32Array([1, 2, 3])), [1, 2, 3])
t.deepEqual(u64ArrayToArray(new BigUint64Array([1n, 2n, 3n])), [1n, 2n, 3n])
t.deepEqual(i64ArrayToArray(new BigInt64Array([1n, 2n, 3n])), [1, 2, 3])
t.deepEqual(f32ArrayToArray(new Float32Array([1, 2, 3])), [1, 2, 3])
t.deepEqual(f64ArrayToArray(new Float64Array([1, 2, 3])), [1, 2, 3])
const bird = new Bird('Carolyn')
t.is(bird.acceptSliceMethod(new Uint8Array([1, 2, 3])), 3)
})
test('reset empty buffer', (t) => {
const empty = getEmptyBuffer()

View file

@ -75,6 +75,7 @@ export class Bird {
constructor(name: string)
getCount(): number
getNameAsync(): Promise<string>
acceptSliceMethod(slice: any): number
}
/** Smoking test for type generation */
@ -211,6 +212,8 @@ export interface A {
foo: number
}
export function acceptSlice(fixture: any): bigint
export function acceptThreadsafeFunction(func: (err: Error | null, arg: number) => any): void
export function acceptThreadsafeFunctionFatal(func: (arg: number) => any): void
@ -378,6 +381,10 @@ export const enum Empty {
export function enumToI32(e: CustomNumEnum): number
export function f32ArrayToArray(input: any): Array<number>
export function f64ArrayToArray(input: any): Array<number>
export function fibonacci(n: number): number
export function fnReceivedAliased(s: AliasedStruct, e: ALIAS): void
@ -419,6 +426,14 @@ export function getUndefined(): void
export function getWords(): Array<string>
export function i16ArrayToArray(input: any): Array<number>
export function i32ArrayToArray(input: any): Array<number>
export function i64ArrayToArray(input: any): Array<number>
export function i8ArrayToArray(input: any): Array<number>
export function indexmapPassthrough(fixture: IndexMap): IndexMap
/** default enum values are continuos i32s start from 0 */
@ -585,6 +600,14 @@ export interface TsTypeChanged {
typeOverrideOptional?: object
}
export function u16ArrayToArray(input: any): Array<number>
export function u32ArrayToArray(input: any): Array<number>
export function u64ArrayToArray(input: any): Array<bigint>
export function u8ArrayToArray(input: any): Array<number>
export function validateArray(arr: Array<number>): number
export function validateBigint(input: bigint): bigint

View file

@ -130,6 +130,11 @@ impl Bird {
tokio::time::sleep(std::time::Duration::new(1, 0)).await;
self.name.as_str()
}
#[napi]
pub fn accept_slice_method(&self, slice: &[u8]) -> u32 {
slice.len() as u32
}
}
/// Smoking test for type generation

View file

@ -49,6 +49,61 @@ async fn array_buffer_pass_through(buf: Uint8Array) -> Result<Uint8Array> {
Ok(buf)
}
#[napi]
fn accept_slice(fixture: &[u8]) -> usize {
fixture.len()
}
#[napi]
fn u8_array_to_array(input: &[u8]) -> Vec<u8> {
input.to_vec()
}
#[napi]
fn i8_array_to_array(input: &[i8]) -> Vec<i8> {
input.to_vec()
}
#[napi]
fn u16_array_to_array(input: &[u16]) -> Vec<u16> {
input.to_vec()
}
#[napi]
fn i16_array_to_array(input: &[i16]) -> Vec<i16> {
input.to_vec()
}
#[napi]
fn u32_array_to_array(input: &[u32]) -> Vec<u32> {
input.to_vec()
}
#[napi]
fn i32_array_to_array(input: &[i32]) -> Vec<i32> {
input.to_vec()
}
#[napi]
fn f32_array_to_array(input: &[f32]) -> Vec<f32> {
input.to_vec()
}
#[napi]
fn f64_array_to_array(input: &[f64]) -> Vec<f64> {
input.to_vec()
}
#[napi]
fn u64_array_to_array(input: &[u64]) -> Vec<u64> {
input.to_vec()
}
#[napi]
fn i64_array_to_array(input: &[i64]) -> Vec<i64> {
input.to_vec()
}
struct AsyncBuffer {
buf: Buffer,
}