Merge pull request #1224 from napi-rs/clone-ref

feat(napi): clone reference for TypedArray/Buffer
This commit is contained in:
LongYinan 2022-07-06 19:49:06 +08:00 committed by GitHub
commit 2bc9218547
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 267 additions and 32 deletions

View file

@ -10,6 +10,19 @@ use crate::{
}; };
static NAPI_IMPL_ID: AtomicU32 = AtomicU32::new(0); static NAPI_IMPL_ID: AtomicU32 = AtomicU32::new(0);
const TYPED_ARRAY_TYPE: &[&str] = &[
"Int8Array",
"Uint8Array",
"Uint8ClampedArray",
"Int16Array",
"Uint16Array",
"Int32Array",
"Uint32Array",
"Float32Array",
"Float64Array",
"BigInt64Array",
"BigUint64Array",
];
// Generate trait implementations for given Struct. // Generate trait implementations for given Struct.
fn gen_napi_value_map_impl(name: &Ident, to_napi_val_impl: TokenStream) -> TokenStream { fn gen_napi_value_map_impl(name: &Ident, to_napi_val_impl: TokenStream) -> TokenStream {
@ -584,6 +597,30 @@ impl NapiStruct {
let setter_name = Ident::new(&format!("set_{}", field_name), Span::call_site()); let setter_name = Ident::new(&format!("set_{}", field_name), Span::call_site());
if field.getter { if field.getter {
let default_to_napi_value_convert = quote! {
let val = obj.#field_ident.to_owned();
unsafe { <#ty as napi::bindgen_prelude::ToNapiValue>::to_napi_value(env, val) }
};
let to_napi_value_convert = if let syn::Type::Path(syn::TypePath {
path: syn::Path { segments, .. },
..
}) = ty
{
if let Some(syn::PathSegment { ident, .. }) = segments.last() {
if TYPED_ARRAY_TYPE.iter().any(|name| ident == name) || ident == "Buffer" {
quote! {
let val = &mut obj.#field_ident;
unsafe { <&mut #ty as napi::bindgen_prelude::ToNapiValue>::to_napi_value(env, val) }
}
} else {
default_to_napi_value_convert
}
} else {
default_to_napi_value_convert
}
} else {
default_to_napi_value_convert
};
getters_setters.push(( getters_setters.push((
field.js_name.clone(), field.js_name.clone(),
quote! { quote! {
@ -592,10 +629,9 @@ impl NapiStruct {
cb: napi::bindgen_prelude::sys::napi_callback_info cb: napi::bindgen_prelude::sys::napi_callback_info
) -> napi::bindgen_prelude::sys::napi_value { ) -> napi::bindgen_prelude::sys::napi_value {
napi::bindgen_prelude::CallbackInfo::<0>::new(env, cb, Some(0)) napi::bindgen_prelude::CallbackInfo::<0>::new(env, cb, Some(0))
.and_then(|mut cb| unsafe { cb.unwrap_borrow::<#struct_name>() }) .and_then(|mut cb| unsafe { cb.unwrap_borrow_mut::<#struct_name>() })
.and_then(|obj| { .and_then(|obj| {
let val = obj.#field_ident.to_owned(); #to_napi_value_convert
unsafe { <#ty as napi::bindgen_prelude::ToNapiValue>::to_napi_value(env, val) }
}) })
.unwrap_or_else(|e| { .unwrap_or_else(|e| {
unsafe { napi::bindgen_prelude::JsError::from(e).throw_into(env) }; unsafe { napi::bindgen_prelude::JsError::from(e).throw_into(env) };

View file

@ -2,31 +2,87 @@ use std::ffi::c_void;
use std::mem; use std::mem;
use std::ops::{Deref, DerefMut}; use std::ops::{Deref, DerefMut};
use std::ptr; use std::ptr;
use std::sync::Arc;
pub use crate::js_values::TypedArrayType; pub use crate::js_values::TypedArrayType;
use crate::{check_status, sys, Error, Result, Status}; use crate::{check_status, sys, Error, Result, Status};
use super::{FromNapiValue, ToNapiValue, TypeName, ValidateNapiValue}; use super::{FromNapiValue, ToNapiValue, TypeName, ValidateNapiValue};
trait Finalizer {
type RustType;
fn finalizer(&mut self) -> Box<dyn FnOnce(*mut Self::RustType, usize)>;
fn data_managed_type(&self) -> &DataManagedType;
fn len(&self) -> &usize;
fn ref_count(&self) -> usize;
}
macro_rules! impl_typed_array { macro_rules! impl_typed_array {
($name:ident, $rust_type:ident, $typed_array_type:expr) => { ($name:ident, $rust_type:ident, $typed_array_type:expr) => {
pub struct $name { pub struct $name {
/// Used for `clone_reference` Owned | External TypedArray
data_reference: Option<Arc<()>>,
data: *mut $rust_type, data: *mut $rust_type,
length: usize, length: usize,
data_managed_type: DataManagedType, data_managed_type: DataManagedType,
byte_offset: usize, byte_offset: usize,
raw: Option<(crate::sys::napi_ref, crate::sys::napi_env)>,
finalizer_notify: Box<dyn FnOnce(*mut $rust_type, usize)>, finalizer_notify: Box<dyn FnOnce(*mut $rust_type, usize)>,
} }
unsafe impl Send for $name {}
impl Finalizer for $name {
type RustType = $rust_type;
fn finalizer(&mut self) -> Box<dyn FnOnce(*mut Self::RustType, usize)> {
std::mem::replace(&mut self.finalizer_notify, Box::new($name::noop_finalize))
}
fn data_managed_type(&self) -> &DataManagedType {
&self.data_managed_type
}
fn len(&self) -> &usize {
&self.length
}
fn ref_count(&self) -> usize {
if let Some(inner) = &self.data_reference {
Arc::strong_count(inner)
} else {
0
}
}
}
impl Drop for $name {
fn drop(&mut self) {
if let Some((ref_, env)) = self.raw {
crate::check_status_or_throw!(
env,
unsafe { sys::napi_reference_unref(env, ref_, &mut 0) },
"Failed to delete Buffer reference in drop"
);
}
}
}
impl $name { impl $name {
fn noop_finalize(_data: *mut $rust_type, _length: usize) {} fn noop_finalize(_data: *mut $rust_type, _length: usize) {}
pub fn new(mut data: Vec<$rust_type>) -> Self { pub fn new(mut data: Vec<$rust_type>) -> Self {
let ret = $name { let ret = $name {
data_reference: Some(Arc::new(())),
data: data.as_mut_ptr(), data: data.as_mut_ptr(),
length: data.len(), length: data.len(),
data_managed_type: DataManagedType::Owned, data_managed_type: DataManagedType::Owned,
byte_offset: 0, byte_offset: 0,
raw: None,
finalizer_notify: Box::new(Self::noop_finalize), finalizer_notify: Box::new(Self::noop_finalize),
}; };
mem::forget(data); mem::forget(data);
@ -39,10 +95,12 @@ macro_rules! impl_typed_array {
{ {
let mut data_copied = data.as_ref().to_vec(); let mut data_copied = data.as_ref().to_vec();
let ret = $name { let ret = $name {
data_reference: Some(Arc::new(())),
data: data_copied.as_mut_ptr(), data: data_copied.as_mut_ptr(),
length: data.as_ref().len(), length: data.as_ref().len(),
data_managed_type: DataManagedType::Owned, data_managed_type: DataManagedType::Owned,
finalizer_notify: Box::new(Self::noop_finalize), finalizer_notify: Box::new(Self::noop_finalize),
raw: None,
byte_offset: 0, byte_offset: 0,
}; };
mem::forget(data_copied); mem::forget(data_copied);
@ -57,13 +115,40 @@ macro_rules! impl_typed_array {
F: 'static + FnOnce(*mut $rust_type, usize), F: 'static + FnOnce(*mut $rust_type, usize),
{ {
$name { $name {
data_reference: Some(Arc::new(())),
data, data,
length, length,
data_managed_type: DataManagedType::External, data_managed_type: DataManagedType::External,
finalizer_notify: Box::new(notify), finalizer_notify: Box::new(notify),
raw: None,
byte_offset: 0, byte_offset: 0,
} }
} }
/// Clone reference, the inner data is not copied nor moved
pub fn clone(&mut self, env: crate::Env) -> crate::Result<$name> {
let raw = if let Some((ref_, _)) = self.raw {
crate::check_status!(
unsafe { sys::napi_reference_ref(env.0, ref_, &mut 0) },
"Failed to ref Buffer reference in TypedArray::clone"
)?;
Some((ref_, env.0))
} else {
None
};
Ok(Self {
data_reference: match self.data_managed_type {
DataManagedType::Owned | DataManagedType::External => self.data_reference.clone(),
_ => None,
},
data: self.data,
length: self.length,
data_managed_type: self.data_managed_type,
finalizer_notify: Box::new(Self::noop_finalize),
raw,
byte_offset: self.byte_offset,
})
}
} }
impl Deref for $name { impl Deref for $name {
@ -110,7 +195,7 @@ macro_rules! impl_typed_array {
unsafe fn validate( unsafe fn validate(
env: sys::napi_env, env: sys::napi_env,
napi_val: sys::napi_value, napi_val: sys::napi_value,
) -> Result<$crate::sys::napi_value> { ) -> Result<crate::sys::napi_value> {
let mut is_typed_array = false; let mut is_typed_array = false;
check_status!( check_status!(
unsafe { sys::napi_is_typedarray(env, napi_val, &mut is_typed_array) }, unsafe { sys::napi_is_typedarray(env, napi_val, &mut is_typed_array) },
@ -133,6 +218,11 @@ macro_rules! impl_typed_array {
let mut data = ptr::null_mut(); let mut data = ptr::null_mut();
let mut array_buffer = ptr::null_mut(); let mut array_buffer = ptr::null_mut();
let mut byte_offset = 0; 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!( check_status!(
unsafe { unsafe {
sys::napi_get_typedarray_info( sys::napi_get_typedarray_info(
@ -154,9 +244,11 @@ macro_rules! impl_typed_array {
)); ));
} }
Ok($name { Ok($name {
data_reference: None,
data: data as *mut $rust_type, data: data as *mut $rust_type,
length, length,
byte_offset, byte_offset,
raw: Some((ref_, env)),
data_managed_type: DataManagedType::Vm, data_managed_type: DataManagedType::Vm,
finalizer_notify: Box::new(Self::noop_finalize), finalizer_notify: Box::new(Self::noop_finalize),
}) })
@ -164,15 +256,22 @@ macro_rules! impl_typed_array {
} }
impl ToNapiValue for $name { impl ToNapiValue for $name {
unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> Result<sys::napi_value> { unsafe fn to_napi_value(env: sys::napi_env, mut val: Self) -> Result<sys::napi_value> {
if let Some((ref_, _)) = val.raw {
let mut napi_value = std::ptr::null_mut();
check_status!(
unsafe { sys::napi_get_reference_value(env, ref_, &mut napi_value) },
"Failed to delete reference from Buffer"
)?;
val.raw = None;
return Ok(napi_value);
}
let mut arraybuffer_value = ptr::null_mut(); let mut arraybuffer_value = ptr::null_mut();
let ratio = mem::size_of::<$rust_type>() / mem::size_of::<u8>(); let ratio = mem::size_of::<$rust_type>() / mem::size_of::<u8>();
let length = val.length * ratio; let length = val.length * ratio;
let hint_ptr = Box::into_raw(Box::new(( let val_data = val.data;
val.data_managed_type, let val_length = val.length;
val.length, let hint_ptr = Box::into_raw(Box::new(val));
val.finalizer_notify,
)));
check_status!( check_status!(
if length == 0 { if length == 0 {
// Rust uses 0x1 as the data pointer for empty buffers, // Rust uses 0x1 as the data pointer for empty buffers,
@ -185,9 +284,9 @@ macro_rules! impl_typed_array {
unsafe { unsafe {
sys::napi_create_external_arraybuffer( sys::napi_create_external_arraybuffer(
env, env,
val.data as *mut c_void, val_data as *mut c_void,
length, length,
Some(finalizer::<$rust_type>), Some(finalizer::<$rust_type, $name>),
hint_ptr as *mut c_void, hint_ptr as *mut c_void,
&mut arraybuffer_value, &mut arraybuffer_value,
) )
@ -201,7 +300,7 @@ macro_rules! impl_typed_array {
sys::napi_create_typedarray( sys::napi_create_typedarray(
env, env,
$typed_array_type as i32, $typed_array_type as i32,
val.length, val_length,
arraybuffer_value, arraybuffer_value,
0, 0,
&mut napi_val, &mut napi_val,
@ -212,31 +311,64 @@ macro_rules! impl_typed_array {
Ok(napi_val) Ok(napi_val)
} }
} }
impl ToNapiValue for &mut $name {
unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> Result<sys::napi_value> {
if let Some((ref_, _)) = val.raw {
let mut napi_value = std::ptr::null_mut();
check_status!(
unsafe { sys::napi_get_reference_value(env, ref_, &mut napi_value) },
"Failed to delete reference from Buffer"
)?;
Ok(napi_value)
} else {
let cloned_value = $name {
data_reference: match val.data_managed_type {
DataManagedType::Owned | DataManagedType::External => val.data_reference.clone(),
_ => None,
},
data: val.data,
length: val.length,
data_managed_type: val.data_managed_type,
finalizer_notify: Box::new($name::noop_finalize),
raw: None,
byte_offset: val.byte_offset,
};
unsafe { ToNapiValue::to_napi_value(env, cloned_value) }
}
}
}
}; };
} }
unsafe extern "C" fn finalizer<T>( unsafe extern "C" fn finalizer<Data, T: Finalizer<RustType = Data>>(
_env: sys::napi_env, _env: sys::napi_env,
finalize_data: *mut c_void, finalize_data: *mut c_void,
finalize_hint: *mut c_void, finalize_hint: *mut c_void,
) { ) {
let (data_managed_type, length, finalizer_notify) = unsafe { let mut data = unsafe { *Box::from_raw(finalize_hint as *mut T) };
*Box::from_raw(finalize_hint as *mut (DataManagedType, usize, Box<dyn FnOnce(*mut T, usize)>)) let data_managed_type = *data.data_managed_type();
}; let length = *data.len();
let finalizer_notify = data.finalizer();
match data_managed_type { match data_managed_type {
DataManagedType::Vm => { DataManagedType::Vm => {
// do nothing // do nothing
} }
DataManagedType::Owned => { DataManagedType::Owned => {
let length = length; if data.ref_count() == 0 {
unsafe { Vec::from_raw_parts(finalize_data as *mut T, length, length) }; let length = length;
unsafe { Vec::from_raw_parts(finalize_data as *mut Data, length, length) };
}
} }
DataManagedType::External => { DataManagedType::External => {
(finalizer_notify)(finalize_data as *mut T, length); if data.ref_count() == 0 {
(finalizer_notify)(finalize_data as *mut Data, length);
}
} }
} }
} }
#[derive(PartialEq, Eq, Clone, Copy)]
enum DataManagedType { enum DataManagedType {
/// Vm managed data, passed in from JavaScript /// Vm managed data, passed in from JavaScript
Vm, Vm,

View file

@ -4,6 +4,7 @@ use std::mem;
use std::ops::{Deref, DerefMut}; use std::ops::{Deref, DerefMut};
use std::ptr; use std::ptr;
use std::slice; use std::slice;
use std::sync::Arc;
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
use std::sync::Mutex; use std::sync::Mutex;
@ -19,6 +20,7 @@ thread_local! {
/// So it is safe to use it in `async fn`, the `&[u8]` under the hood will not be dropped until the `drop` called. /// So it is safe to use it in `async fn`, the `&[u8]` under the hood will not be dropped until the `drop` called.
/// Clone will create a new `Reference` to the same underlying `JavaScript Buffer`. /// Clone will create a new `Reference` to the same underlying `JavaScript Buffer`.
pub struct Buffer { pub struct Buffer {
data_reference: Option<Arc<()>>,
inner: &'static mut [u8], inner: &'static mut [u8],
capacity: usize, capacity: usize,
raw: Option<(sys::napi_ref, sys::napi_env)>, raw: Option<(sys::napi_ref, sys::napi_env)>,
@ -40,22 +42,21 @@ unsafe impl Send for Buffer {}
impl Buffer { impl Buffer {
pub fn clone(&mut self, env: &Env) -> Result<Self> { pub fn clone(&mut self, env: &Env) -> Result<Self> {
if let Some((ref_, _)) = self.raw { let raw = if let Some((ref_, _)) = self.raw {
check_status!( check_status!(
unsafe { sys::napi_reference_ref(env.0, ref_, &mut 0) }, unsafe { sys::napi_reference_ref(env.0, ref_, &mut 0) },
"Failed to ref Buffer reference in Buffer::clone" "Failed to ref Buffer reference in Buffer::clone"
)?; )?;
Ok(Self { Some((ref_, env.0))
inner: unsafe { slice::from_raw_parts_mut(self.inner.as_mut_ptr(), self.inner.len()) },
capacity: self.capacity,
raw: Some((ref_, env.0)),
})
} else { } else {
Err(Error::new( None
Status::InvalidArg, };
"clone only available when the buffer is created from a JavaScript Buffer".to_owned(), Ok(Self {
)) data_reference: self.data_reference.clone(),
} inner: unsafe { slice::from_raw_parts_mut(self.inner.as_mut_ptr(), self.inner.len()) },
capacity: self.capacity,
raw,
})
} }
} }
@ -76,6 +77,7 @@ impl From<Vec<u8>> for Buffer {
let capacity = data.capacity(); let capacity = data.capacity();
mem::forget(data); mem::forget(data);
Buffer { Buffer {
data_reference: Some(Arc::new(())),
inner: unsafe { slice::from_raw_parts_mut(inner_ptr, len) }, inner: unsafe { slice::from_raw_parts_mut(inner_ptr, len) },
capacity, capacity,
raw: None, raw: None,
@ -146,6 +148,7 @@ impl FromNapiValue for Buffer {
)?; )?;
Ok(Self { Ok(Self {
data_reference: None,
inner: unsafe { slice::from_raw_parts_mut(buf as *mut _, len) }, inner: unsafe { slice::from_raw_parts_mut(buf as *mut _, len) },
capacity: len, capacity: len,
raw: Some((ref_, env)), raw: Some((ref_, env)),
@ -196,6 +199,32 @@ impl ToNapiValue for Buffer {
} }
} }
impl ToNapiValue for &mut Buffer {
unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> Result<sys::napi_value> {
// From Node.js value, not from `Vec<u8>`
if let Some((ref_, _)) = val.raw {
let mut buf = ptr::null_mut();
check_status!(
unsafe { sys::napi_get_reference_value(env, ref_, &mut buf) },
"Failed to get Buffer value from reference"
)?;
check_status!(
unsafe { sys::napi_delete_reference(env, ref_) },
"Failed to delete Buffer reference"
)?;
Ok(buf)
} else {
let buf = Buffer {
data_reference: val.data_reference.clone(),
inner: unsafe { slice::from_raw_parts_mut(val.inner.as_mut_ptr(), val.capacity) },
capacity: val.capacity,
raw: None,
};
unsafe { ToNapiValue::to_napi_value(env, buf) }
}
}
}
impl ValidateNapiValue for Buffer { impl ValidateNapiValue for Buffer {
unsafe fn validate(env: sys::napi_env, napi_val: sys::napi_value) -> Result<sys::napi_value> { unsafe fn validate(env: sys::napi_env, napi_val: sys::napi_value) -> Result<sys::napi_value> {
let mut is_buffer = false; let mut is_buffer = false;

View file

@ -187,6 +187,7 @@ Generated by [AVA](https://avajs.dev).
export function mutateTypedArray(input: Float32Array): void␊ export function mutateTypedArray(input: Float32Array): void␊
export function derefUint8Array(a: Uint8Array, b: Uint8ClampedArray): number␊ export function derefUint8Array(a: Uint8Array, b: Uint8ClampedArray): number␊
export function bufferPassThrough(buf: Buffer): Promise<Buffer> export function bufferPassThrough(buf: Buffer): Promise<Buffer>
export function arrayBufferPassThrough(buf: Uint8Array): Promise<Uint8Array>
export function asyncReduceBuffer(buf: Buffer): Promise<number> export function asyncReduceBuffer(buf: Buffer): Promise<number>
/**␊ /**␊
* \`constructor\` option for \`struct\` requires all fields to be public,␊ * \`constructor\` option for \`struct\` requires all fields to be public,␊
@ -240,8 +241,10 @@ Generated by [AVA](https://avajs.dev).
export class Blake2BKey { }␊ export class Blake2BKey { }␊
export class Context {␊ export class Context {␊
maybeNeed?: boolean␊ maybeNeed?: boolean␊
buffer: Uint8Array␊
constructor()␊ constructor()␊
static withData(data: string): Context␊ static withData(data: string): Context␊
static withBuffer(buf: Uint8Array): Context␊
method(): string␊ method(): string␊
}␊ }␊
export class AnimalWithDefaultConstructor {␊ export class AnimalWithDefaultConstructor {␊

View file

@ -90,6 +90,7 @@ import {
derefUint8Array, derefUint8Array,
chronoDateAdd1Minute, chronoDateAdd1Minute,
bufferPassThrough, bufferPassThrough,
arrayBufferPassThrough,
JsRepo, JsRepo,
CssStyleSheet, CssStyleSheet,
asyncReduceBuffer, asyncReduceBuffer,
@ -208,6 +209,14 @@ test('class constructor return Result', (t) => {
t.is(c.method(), 'not empty') t.is(c.method(), 'not empty')
}) })
test('class default field is TypedArray', (t) => {
const c = new Context()
t.deepEqual(c.buffer, new Uint8Array([0, 1, 2, 3]))
const fixture = new Uint8Array([0, 1, 2, 3, 4, 5, 6])
const c2 = Context.withBuffer(fixture)
t.is(c2.buffer, fixture)
})
test('class Factory return Result', (t) => { test('class Factory return Result', (t) => {
const c = Context.withData('not empty') const c = Context.withData('not empty')
t.is(c.method(), 'not empty') t.is(c.method(), 'not empty')
@ -479,6 +488,12 @@ test('buffer passthrough', async (t) => {
t.deepEqual(ret, fixture) t.deepEqual(ret, fixture)
}) })
test('arraybuffer passthrough', async (t) => {
const fixture = new Uint8Array([1, 2, 3, 4, 5])
const ret = await arrayBufferPassThrough(fixture)
t.deepEqual(ret, fixture)
})
test('async reduce buffer', async (t) => { test('async reduce buffer', async (t) => {
const input = [1, 2, 3, 4, 5, 6] const input = [1, 2, 3, 4, 5, 6]
const fixture = Buffer.from(input) const fixture = Buffer.from(input)

View file

@ -177,6 +177,7 @@ export function createExternalTypedArray(): Uint32Array
export function mutateTypedArray(input: Float32Array): void export function mutateTypedArray(input: Float32Array): void
export function derefUint8Array(a: Uint8Array, b: Uint8ClampedArray): number export function derefUint8Array(a: Uint8Array, b: Uint8ClampedArray): number
export function bufferPassThrough(buf: Buffer): Promise<Buffer> export function bufferPassThrough(buf: Buffer): Promise<Buffer>
export function arrayBufferPassThrough(buf: Uint8Array): Promise<Uint8Array>
export function asyncReduceBuffer(buf: Buffer): Promise<number> export function asyncReduceBuffer(buf: Buffer): Promise<number>
/** /**
* `constructor` option for `struct` requires all fields to be public, * `constructor` option for `struct` requires all fields to be public,
@ -230,8 +231,10 @@ export type Blake2bKey = Blake2BKey
export class Blake2BKey { } export class Blake2BKey { }
export class Context { export class Context {
maybeNeed?: boolean maybeNeed?: boolean
buffer: Uint8Array
constructor() constructor()
static withData(data: string): Context static withData(data: string): Context
static withBuffer(buf: Uint8Array): Context
method(): string method(): string
} }
export class AnimalWithDefaultConstructor { export class AnimalWithDefaultConstructor {

View file

@ -1,5 +1,5 @@
use napi::{ use napi::{
bindgen_prelude::{Buffer, ClassInstance, This}, bindgen_prelude::{Buffer, ClassInstance, This, Uint8Array},
Env, Result, Env, Result,
}; };
@ -159,6 +159,7 @@ impl Blake2bKey {
pub struct Context { pub struct Context {
data: String, data: String,
pub maybe_need: Option<bool>, pub maybe_need: Option<bool>,
pub buffer: Uint8Array,
} }
// Test for return `napi::Result` and `Result` // Test for return `napi::Result` and `Result`
@ -169,6 +170,7 @@ impl Context {
Ok(Self { Ok(Self {
data: "not empty".into(), data: "not empty".into(),
maybe_need: None, maybe_need: None,
buffer: Uint8Array::new(vec![0, 1, 2, 3]),
}) })
} }
@ -177,9 +179,19 @@ impl Context {
Ok(Self { Ok(Self {
data, data,
maybe_need: Some(true), maybe_need: Some(true),
buffer: Uint8Array::new(vec![0, 1, 2, 3]),
}) })
} }
#[napi(factory)]
pub fn with_buffer(buf: Uint8Array) -> Self {
Self {
data: "not empty".into(),
maybe_need: None,
buffer: buf,
}
}
#[napi] #[napi]
pub fn method(&self) -> String { pub fn method(&self) -> String {
self.data.clone() self.data.clone()

View file

@ -44,6 +44,11 @@ async fn buffer_pass_through(buf: Buffer) -> Result<Buffer> {
Ok(buf) Ok(buf)
} }
#[napi]
async fn array_buffer_pass_through(buf: Uint8Array) -> Result<Uint8Array> {
Ok(buf)
}
struct AsyncBuffer { struct AsyncBuffer {
buf: Buffer, buf: Buffer,
} }