From 8ca1967bd896ede46c6afdf1487a198692a07d8b Mon Sep 17 00:00:00 2001 From: LongYinan Date: Sun, 25 Feb 2024 01:00:28 +0800 Subject: [PATCH] feat(napi): impl BufferSlice and Uint8ClampedSlice (#1979) --- .../bindgen_runtime/js_values/arraybuffer.rs | 183 +++++++++++++++++- .../src/bindgen_runtime/js_values/buffer.rs | 91 +++++++++ .../__snapshots__/typegen.spec.ts.md | 10 + .../__snapshots__/typegen.spec.ts.snap | Bin 4598 -> 4638 bytes examples/napi/__tests__/strict.spec.ts | 19 +- examples/napi/__tests__/values.spec.ts | 13 +- examples/napi/index.d.ts | 10 + examples/napi/src/fn_strict.rs | 15 ++ examples/napi/src/typed_array.rs | 10 + 9 files changed, 347 insertions(+), 4 deletions(-) diff --git a/crates/napi/src/bindgen_runtime/js_values/arraybuffer.rs b/crates/napi/src/bindgen_runtime/js_values/arraybuffer.rs index 9eff82cb..3bfb450e 100644 --- a/crates/napi/src/bindgen_runtime/js_values/arraybuffer.rs +++ b/crates/napi/src/bindgen_runtime/js_values/arraybuffer.rs @@ -10,7 +10,7 @@ use std::sync::{ #[cfg(all(feature = "napi4", not(feature = "noop"), not(target_family = "wasm")))] use crate::bindgen_prelude::{CUSTOM_GC_TSFN, CUSTOM_GC_TSFN_DESTROYED, THREADS_CAN_ACCESS_ENV}; pub use crate::js_values::TypedArrayType; -use crate::{check_status, sys, Error, Result, Status}; +use crate::{check_status, sys, Error, Result, Status, ValueType}; use super::{FromNapiValue, ToNapiValue, TypeName, ValidateNapiValue}; @@ -480,6 +480,27 @@ macro_rules! impl_from_slice { }, "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(if length == 0 { + &mut [] + } else { + unsafe { core::slice::from_raw_parts_mut(data as *mut $rust_type, length) } + }) + } + } + + impl FromNapiValue for &[$rust_type] { + unsafe fn from_napi_value(env: sys::napi_env, napi_val: sys::napi_value) -> Result { + 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; check_status!( unsafe { sys::napi_get_typedarray_info( @@ -500,7 +521,65 @@ macro_rules! impl_from_slice { format!("Expected $name, got {}", typed_array_type), )); } - Ok(unsafe { core::slice::from_raw_parts_mut(data as *mut $rust_type, length) }) + Ok(if length == 0 { + &[] + } else { + unsafe { core::slice::from_raw_parts_mut(data as *mut $rust_type, length) } + }) + } + } + + impl TypeName for &mut [$rust_type] { + fn type_name() -> &'static str { + concat!("TypedArray<", stringify!($rust_type), ">") + } + + fn value_type() -> crate::ValueType { + crate::ValueType::Object + } + } + + impl TypeName for &[$rust_type] { + fn type_name() -> &'static str { + concat!("TypedArray<", stringify!($rust_type), ">") + } + + fn value_type() -> crate::ValueType { + crate::ValueType::Object + } + } + + impl ValidateNapiValue for &[$rust_type] { + unsafe fn validate(env: sys::napi_env, napi_val: sys::napi_value) -> Result { + let mut is_typed_array = false; + check_status!( + unsafe { sys::napi_is_typedarray(env, napi_val, &mut is_typed_array) }, + "Failed to validate napi typed array" + )?; + if !is_typed_array { + return Err(Error::new( + Status::InvalidArg, + "Expected a TypedArray value".to_owned(), + )); + } + Ok(ptr::null_mut()) + } + } + + impl ValidateNapiValue for &mut [$rust_type] { + unsafe fn validate(env: sys::napi_env, napi_val: sys::napi_value) -> Result { + let mut is_typed_array = false; + check_status!( + unsafe { sys::napi_is_typedarray(env, napi_val, &mut is_typed_array) }, + "Failed to validate napi typed array" + )?; + if !is_typed_array { + return Err(Error::new( + Status::InvalidArg, + "Expected a TypedArray value".to_owned(), + )); + } + Ok(ptr::null_mut()) } } }; @@ -568,6 +647,106 @@ impl_typed_array!(BigUint64Array, u64, TypedArrayType::BigUint64); #[cfg(feature = "napi6")] impl_from_slice!(BigUint64Array, u64, TypedArrayType::BigUint64); +/// Zero copy Uint8ClampedArray slice shared between Rust and Node.js. +/// It can only be used in non-async context and the lifetime is bound to the fn closure. +/// If you want to use Node.js `Uint8ClampedArray` in async context or want to extend the lifetime, use `Uint8ClampedArray` instead. +pub struct Uint8ClampedSlice<'scope> { + pub(crate) inner: &'scope mut [u8], + raw_value: sys::napi_value, +} + +impl<'scope> FromNapiValue for Uint8ClampedSlice<'scope> { + unsafe fn from_napi_value(env: sys::napi_env, napi_val: sys::napi_value) -> Result { + 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; + 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 != TypedArrayType::Uint8Clamped as i32 { + return Err(Error::new( + Status::InvalidArg, + format!("Expected $name, got {}", typed_array_type), + )); + } + Ok(Self { + inner: if length == 0 { + &mut [] + } else { + unsafe { core::slice::from_raw_parts_mut(data.cast(), length) } + }, + raw_value: napi_val, + }) + } +} + +impl ToNapiValue for Uint8ClampedSlice<'_> { + #[allow(unused_variables)] + unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> Result { + Ok(val.raw_value) + } +} + +impl TypeName for Uint8ClampedSlice<'_> { + fn type_name() -> &'static str { + "Uint8ClampedArray" + } + + fn value_type() -> ValueType { + ValueType::Object + } +} + +impl ValidateNapiValue for Uint8ClampedSlice<'_> { + unsafe fn validate(env: sys::napi_env, napi_val: sys::napi_value) -> Result { + let mut is_typedarray = false; + check_status!( + unsafe { sys::napi_is_typedarray(env, napi_val, &mut is_typedarray) }, + "Failed to validate typed buffer" + )?; + if !is_typedarray { + return Err(Error::new( + Status::InvalidArg, + "Expected a TypedArray value".to_owned(), + )); + } + Ok(ptr::null_mut()) + } +} + +impl AsRef<[u8]> for Uint8ClampedSlice<'_> { + fn as_ref(&self) -> &[u8] { + self.inner + } +} + +impl<'scope> Deref for Uint8ClampedSlice<'scope> { + type Target = [u8]; + + fn deref(&self) -> &Self::Target { + self.inner + } +} + +impl<'scope> DerefMut for Uint8ClampedSlice<'scope> { + fn deref_mut(&mut self) -> &mut Self::Target { + self.inner + } +} + impl>> From for Uint8Array { fn from(data: T) -> Self { Uint8Array::new(data.into()) diff --git a/crates/napi/src/bindgen_runtime/js_values/buffer.rs b/crates/napi/src/bindgen_runtime/js_values/buffer.rs index 4ef145a0..d5857ec6 100644 --- a/crates/napi/src/bindgen_runtime/js_values/buffer.rs +++ b/crates/napi/src/bindgen_runtime/js_values/buffer.rs @@ -18,7 +18,98 @@ thread_local! { pub (crate) static BUFFER_DATA: Mutex> = Default::default(); } +/// Zero copy buffer slice shared between Rust and Node.js. +/// It can only be used in non-async context and the lifetime is bound to the fn closure. +/// If you want to use Node.js Buffer in async context or want to extend the lifetime, use `Buffer` instead. +pub struct BufferSlice<'scope> { + pub(crate) inner: &'scope mut [u8], + raw_value: sys::napi_value, +} + +impl<'scope> FromNapiValue for BufferSlice<'scope> { + unsafe fn from_napi_value(env: sys::napi_env, napi_val: sys::napi_value) -> Result { + let mut buf = ptr::null_mut(); + let mut len = 0usize; + check_status!( + unsafe { sys::napi_get_buffer_info(env, napi_val, &mut buf, &mut len) }, + "Failed to get Buffer pointer and length" + )?; + // From the docs of `napi_get_buffer_info`: + // > [out] data: The underlying data buffer of the node::Buffer. If length is 0, this may be + // > NULL or any other pointer value. + // + // In order to guarantee that `slice::from_raw_parts` is sound, the pointer must be non-null, so + // let's make sure it always is, even in the case of `napi_get_buffer_info` returning a null + // ptr. + Ok(Self { + inner: if len == 0 { + &mut [] + } else { + unsafe { slice::from_raw_parts_mut(buf.cast(), len) } + }, + raw_value: napi_val, + }) + } +} + +impl ToNapiValue for BufferSlice<'_> { + #[allow(unused_variables)] + unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> Result { + Ok(val.raw_value) + } +} + +impl TypeName for BufferSlice<'_> { + fn type_name() -> &'static str { + "Buffer" + } + + fn value_type() -> ValueType { + ValueType::Object + } +} + +impl ValidateNapiValue for BufferSlice<'_> { + unsafe fn validate(env: sys::napi_env, napi_val: sys::napi_value) -> Result { + let mut is_buffer = false; + check_status!( + unsafe { sys::napi_is_buffer(env, napi_val, &mut is_buffer) }, + "Failed to validate napi buffer" + )?; + if !is_buffer { + return Err(Error::new( + Status::InvalidArg, + "Expected a Buffer value".to_owned(), + )); + } + Ok(ptr::null_mut()) + } +} + +impl AsRef<[u8]> for BufferSlice<'_> { + fn as_ref(&self) -> &[u8] { + self.inner + } +} + +impl<'scope> Deref for BufferSlice<'scope> { + type Target = [u8]; + + fn deref(&self) -> &Self::Target { + self.inner + } +} + +impl<'scope> DerefMut for BufferSlice<'scope> { + fn deref_mut(&mut self) -> &mut Self::Target { + self.inner + } +} + /// Zero copy u8 vector shared between rust and napi. +/// It's designed to be used in `async` context, so it contains overhead to ensure the underlying data is not dropped. +/// For non-async context, use `BufferRef` instead. +/// /// Auto reference the raw JavaScript value, and release it when dropped. /// 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`. diff --git a/examples/napi/__tests__/__snapshots__/typegen.spec.ts.md b/examples/napi/__tests__/__snapshots__/typegen.spec.ts.md index d82ccfe6..d25a3cc1 100644 --- a/examples/napi/__tests__/__snapshots__/typegen.spec.ts.md +++ b/examples/napi/__tests__/__snapshots__/typegen.spec.ts.md @@ -255,6 +255,10 @@ Generated by [AVA](https://avajs.dev). ␊ export function acceptThreadsafeFunctionTupleArgs(func: (err: Error | null, arg0: number, arg1: boolean, arg2: string) => any): void␊ ␊ + export function acceptUint8ClampedSlice(input: Uint8ClampedArray): bigint␊ + ␊ + export function acceptUint8ClampedSliceAndBufferSlice(a: Buffer, b: Uint8ClampedArray): bigint␊ + ␊ export function add(a: number, b: number): number␊ ␊ export const enum ALIAS {␊ @@ -672,6 +676,8 @@ Generated by [AVA](https://avajs.dev). ␊ export function validateBuffer(b: Buffer): number␊ ␊ + export function validateBufferSlice(input: Buffer): number␊ + ␊ export function validateDate(d: Date): number␊ ␊ export function validateDateTime(d: Date): number␊ @@ -696,6 +702,10 @@ Generated by [AVA](https://avajs.dev). ␊ export function validateTypedArray(input: Uint8Array): number␊ ␊ + export function validateTypedArraySlice(input: Uint8Array): number␊ + ␊ + export function validateUint8ClampedSlice(input: Uint8ClampedArray): number␊ + ␊ export function validateUndefined(i: undefined): boolean␊ ␊ export function withAbortController(a: number, b: number, signal: AbortSignal): Promise␊ diff --git a/examples/napi/__tests__/__snapshots__/typegen.spec.ts.snap b/examples/napi/__tests__/__snapshots__/typegen.spec.ts.snap index a5c5f0a0580df5e3d5a4a00a7d70c6b0cae6e8d9..9d2db18fde4cf2a96e1447aa8bbd710a09983114 100644 GIT binary patch delta 4343 zcmVQ8?I&2$D=|Don0&r&i24w7mhiI5T!P4sFBB!wxNvJ9Bw zbgAY-t3UlUO{u28JNeZ=fAg>3;6K0mmv4UkPyc=V;4Y!L7WX_*N+v6EGJJP*?`%Zw zKG=G2mw?KD7}xhwrsyOCyt%anS4$x^NiwPwd3mKla+;k^K0%_t8$TnTw+N9yQ^B(p zxpQY=Zq#n&oc)voQLDv7WajaQHv@P1aX`jfgnZrly7l1h-7RvL{Fn%?w9FGN}^}j3s@95qb{tk|`jX&Ins9Gcx6b zDiXjW-M@d}bjfi5sY03C(y9Pwblp@25EI`jiYLS$tnkD3t={Z6KXxjrf zfh|IRW}pprZ`-3ItG`(v6{%6tklM{i`C9?Ll@DKSAf&xq*!22Kt4VnTck&1XMzokop~%tp=9t_a8of{`%negKxk6 zY;SxmrXoS-lAoe39-tzjY%jzt^5lD_=LfmcVsQ{?#P)#fGl^3X7a%1|;fSZ~f~7gl zhH`ewkBtKKxR48)jaPoj`kp=oj(0zE|KzKO{JIrWaTMbPOlh9!Kn|n$p>Avur{+9= zUra#O^f;vKR|vNw!-`@J4uyQl@XCxABDV7>HPWjLMsomd*idCvzlXcX}8MPchBE9Hi&N@#~hYy6JtIgaiYr#kR!WdZgqPz+qPq4!w1l7j;2 z3*mW|#fMRT990_#bs+Xy+uoy-;6UVmTnBSAH&5sShHAwVeDF?+1yk^yWqYQTlLaO$e?_kg@-Tt6L3Nmt=wp(7xpg*az?Ml>jGtiPF*^Ihl>>K1xGqvx=@{~M~j z{)F}rR=smr4II_v4d93smMuc^WlA;l(0vZbem`wE#cNER^;v}X3{;-!Yav+uth4AjQ_cAcuHnjV?gd><-~=FjJ|GhzGN62m zkcm-?P<10aH4yp*lVSvC7Be)aami9W_a?1R=D=oi-R=XO!+o!l-vlLp9x(gI0rT?~ zPtYMnze|XIrwsj05hCj(X4i9Bb$~-QF#sP%=J0W!X>=z-enb^Hau?ru;n4E;ov6>& z+Rj5Z+5H9J&J-jP&<81i(9l9%q648^>_GkwOqA8b?pzDE zoPW{ojiz+UvsLeUi&B3DBgmi#)rCw!?ys6bm@-t6`$h`_@h;~ep-sW#@clS?zXsl~ zVK+7R*0(lyXc1l?Tb4u-h?3}bw)uK_`WuMTT)6Ks3N)+btb5cUSDu9+jBAinngAIZ zkf{*iDcMm?bDo$3ad67+P1%*sW&MObVSh8mbsOh+ju}ivr*P;V^pIjedLZS1yp&Ru z%R%L=5Si>fBb2YaG1?gZkZPLwF>V8JoG&vN%9-jG{9&mURL*t^S8FbJi+!+}6lQ@B zGP+noYS*6eWv<=Iy9b-SfCki%r}k86fs3R>pG>ZapQgy|XFUlbOQ%nT;-X>_;D7mo z4Bs9NM@I36WRE=TZxOOj_Q+0LLo-H|*NcMLwHHaxMiv4^rNI7D2;Iokbf9Lu8SL{li>#zfARJjyv}rVLHt&*w8@U|@>xXT_l<&2sO-{byOoD_ z9OxO0hWF*?rpEjPl9j^@OiLzkl+0sH_LUP8re;iIPne6zNXs4V^c8k|yh(k?mfu2jj#S5xon5H|&jOV(D z!|?95{a+X3SRAt~W2(m1`eqH|3DxWZ3_Dwx!V%nvv-BN!#~*K+tb!+0zonY-ot|nQ zqFWe7T3&_029l#eRlhx7V6r)(GaQo!i7kGwIi2acXewmahUgUir8m zClV}J8{_HZ6H*E@N(T`n+GcDq35&;E<_^~nAta# zoS8`zmo;S)^Y8}te+;G|u|MOKM|^-Y5#2Mi$!(MBwCzZpbcdM!1(k%m!Leah_IDyD ziR})NK+jEskJ7L-T_sqwrX16;W~VToi(J`7xF1%p%M+I^DxKtumw1J7&nX8ya^cx) zd@0Bt*)>1s67c>byg0;^@E&=Lzentf?2#w<^CecC$R2r$e-|gr;QXgy29A&qqj8iG zV5F(e)qzM&Mz~^e%eV{;mGb#T=?vx;A;2*SYAzxJq{hcOvWs^sKd4n94JIy$2 zycXi|S_tw5e=WmWpnV~-Q(5d-hgxFv+Cqs|hi`H5=wdBX;kJk53)v@M$U%qS3Yg7{ zvZb6_)hWpJwjtQpdn&8yTwAQp6wAxSQoFX0<`_I*jK$HT-5w08mZ4+4r_KrZbG?)4 zFcWlhhdO=oxD~`b+5m)21gA;DdVGUGdCJcq0d@hYe@i4`08+^*^$ECyQmazTK<{e_ za7>p=bhX<9at4Wz>33zKw?$fHwONFYd=Yj(U8Z3K+&Iv5MyWH<=7n!GQW1QB`_UG6 zbgb$O^s7uvsBw4L;vlAEW^P1jk9Z1KH(R_La)~t0;E-idD?i4FPe5s7t%ov*6+V2# zf8z{!f0kLoSS)P_>R8tMyuz+I(*$vK_DYbCgu>9V{Ykl~*WhdvD`9;l39#jr0&TNZHPlnBh3$+C5mmSS)V9j;?VYU~%(h z1OJWf7kkRM9>(KL)4lFn+4reDSGIuw+iP5=-!{3EtqdQ3r#G-tJu&&@1N8Okw4h7ppf5}n zwun9=RoH6vunCpx@IbfDreagfKvADx+iJ_})6aS}T(^AOO$tPRF zY=ky#-E}q3+0v8rtb7*q>LET$+8)u#hcp{UuWrmt-lX? z32LJ!+y<^~yJ!(V+9hF=mYYT^wMLAa4rG(szi`6wPalc{1sD3X_}Zb9at$+oxc^C2 zn&V5NAkK*O9$9c87a*0H`_!Z}9IQ3-?peT<0+wBYCk)i{hWn!2bYQ1&@nSt)WScH2 zXGwc#vdnUI%E2|LnQn`YaA#>t3rF0Z@Vu!mJV$#G(w$VD8^+JY$R6gi+)80qhD?EK z)8V*BK76Y{rf?76#J3;G*I4*}hOQ#>~-nGuZ0nwn|%vXw|GXFw~;a zoJvR!@C#ww==QRW;k7E9@OEoOu#LIx;+kGrO2Ge?hm%C4-G`G_VDVb5sV8eSDo=sYdZ@YLtu-C$n`&FB%^sQ{KM;89^eej zM^hBT%Af(=gojpfg7kZ7%Et%?^YeI;87rh4 zXPNVOmibz3CwXg{8DZ*qX4`2JgNf#uD^0y|LCUqh{T$+dZGE5-d3`3X!bUAy=|)Z6 z)X#a=eY2KDc(<-0wqUa%T90iM5*A#pxj1NAT`OPEv+uQHQ{ETm4-l+vA`8iF>)FOE zWfJ@b6-5tnlMYPJi+?5Im@fU&TsuheIOh1k2HsPxeUrXSzrn%Gvv`AKW$nzt^ogO_ zeI7P8L>%gWS~0y_x>;MiN4drUxL~oJQ7Sk#tQEFd`H1~Vj}6$HpU!NGajg$<)`Ats zO*SasUe`+CpKcgVgv7s=qov3)kkLg?eYh_u;1F5{Iox@ znma#u%I4qau-x{~y;#oTkH6GKF;OHwPuUDq*kDS`pLwbrl80}PUY#7jJQ)X5$C0kC l<~zGj%jt*0hHHW!Y_zOJd=3BbzguxR47xV~Z};FcZaLjGGqifhS&}Vj))tU^%v+xGa=811_Ys1B;-7AIH@jz~)ggJvF0eb@ zUG-y;U92irRqM~wNhXqW^~b+}W;%nbe^YajXDOKh2T3)ML`VsVCVI65lERcsSq4mT zx>R$a)gS+yrc~44z5CC9_~xI!!GHelAHVtMzyIakqx*#BT0HPTDVeOu$?*NrgR>F2 z|7h#ceF7?fV_ZK-nWB>n@aEPQTrGvvB*~~!5}z=yW5|(aO#zb4>?=V%uMdl{rlv5##17ugxcHpABC%M&UiX7B#wGK zXNoXI^c=#`En9G-S910VrwbSmr6uFD?HUjzQ;G~LvH(37sYOznYL<{orsoI|pluJ_ z1hxo&nSnOgy={+(AfOVugw*fAY&EcCzyJ8@%k`t*55E2O zi@kAOOhtmuB|k-7JU~T4*in6yxxkbIEwKKrZmrVAcs->P&c-SQ*)kw zFD4*sdK^;rD}>vTVMVb9heEz)cxA>55!?Bc8tGLAqd9;!Y^bs_&%mtpfl_CA2Je{C zTh|`ISWeGB8U_4&QP}#(O1Yt`6565B8o#G{j-$K9sSf;BS%5tY6a!an=si}hu)CId>49zx`p1)=sE1}|As2B zKcPK@Rqq^D14lLa9&p48%N8N|GNl@N=spKzKc7y`Dqjee*4EacxUu$XKvB5`zK0e0 zvXlJ-D>`Y6#cNER^+kmD3{;-!8zETzth4AjQ_cAcuHnjV?gd><-~=FjIUo}uGN62m zkcm-?P<10aH4yq0lVSvC77H|{ami9W_a?1R=D=oi-R=XO!~LL>-vlLp88G|D0rSfi zPtYMnze|XIrwsj05hCj(W;b(Lb$~-QF#sP&=J4^5X>=z-enJ&Fau?ru;n4E;ov1I? z+Rj5Z+5HvZ&J-jP&<81i(9!1GXFO`E3hs!g%H1TI3hqao= zRGlA)%pNQXd0Yfnxo3|jc;xD#r?mdE&265D|F>Q1Y}bj!hdFr>o(5u95a}TPT|l!=pn^`^gzl1c`cX5INcMLwHH4&XkyP~LnILBsTyMH>r4 zWRZsDD8(V%N6m4NV@#NjFGXcblz&omGr!Mrwe$FKZ`okj7~%}lJb~y--wa?()wx%` zGZRMk8oX{lc0v4Bu(ZjJ@A5@N;rES#PpItDXKMh5cH`O$vvKXu4hw1TA4;(}+Sz^H z+l=zr)AfXJK!14lbf~Ob=}lN5jN7J7&bZDt-jcl$TdV`OSif{F7Vfjhy?;&iZq3|P z5O&rhRJ}jCMRamM5;DQPN3DdKdbNm>rb z*zlqXZx3n^8wxb!>1atWdGE72EF9CQ3ZxrQFIn*i@_+y8Yjj>R#{GNx*5 zt#8&ao>0v$z_7E0sm8#qI7{Dwcl?>5$trk4^*gE=-|4C5$+U%Gq<`fNs6XiG&a+MF zHDz4&l*nZg*PfFi-8=CN7%EX%>r9gKM{Z6}a(g38`JQ?aG3+F`iC7Bc(8- zbPz$JZ5k9an|O9aE`NL9T$%?y8`yz^An&DEg49ODFaBg1gqeL)9+{ckYgwozF%NHG z&tM7?n;uSi#0NMN(LF<(F*f-_+vC$ocZfMvP)WEO92=%Oe?k^xSmKC=E-~ zs)3bY$}z`jb_(OU$dz4$hha&$JaO4d%}Ks^jaL}=oN~Y;7k{3;#+QQZkzMn1E&=a9 z!HYx8v+j|n_dT3%PSN0o6CcYF8rqNa4=wM5l{T`28kbcnsL^6EyUBc z5acUbhP6QZLS(11*s%_^#OSq!60Hv3;^NW8TBgEn56M@uPrj0a4!;#Jn-^v4Behaa zkU(uiu&?)2R@J$-Se+@B*Ndfgy%x;h7kNWuW5l2hsva0#VWrI>-<*An2EE|=(P zw+G}55+T#?%Is;2w8(0+2p#z%Y$>`-!w9%>py`ZKXQ0gs-)N*F_yG5#E$--8)fwnF znV3-H?tidvKupQZ+=$X1@f5CZw|KYY5^0{nAA>PO zTysdN3)|T1R*w(f#&yE|AY>Y8%xRFaoo6w_aeu_Md$4}7Sloae-QYaH;^xZ+{u|pb z_Pj-o=bHlHZd%YKCcm^xeigNj_$KMtJ*bzd4QH(DFma?ZeZoGvRJeM|#$#kE#ezJJ z$C;)j-1mp?Q+cjzRUUg*T&CZ4j%S%76DCvl{D5j7jhZNpD!p=?d2)`?Y0QyjOzG3f z=YQ{ErFvrW*$3$B)oDSO&Ou+8(q<8TL`s|0l3Wui*WrO~olV6~lgr=Y^TKn*H;2Uy z&SG(g+Y&=pdlCLm#ipf@r<2dNF4zcd+Q_3PMVrQDGHtYU*Lks`jn?a}Z^6kc{X*U8 z%Sdf5rjuodL5nxHDZb8n`dWV<6zFTC+$IIC?PX{YKiYa=la`xCE4A*1+YV%t+P`wb z@$U(W0|giQwD^RelZg#8e|Y#=RXWZ~q9D$QHS}0;AQvE&nfuhFGaRfn^X^%|l>(Ms zfhP>q%ZB@++_W{PaPewAU1XasDQ8K0XtK<5b;`kYeVG=9j&NsbOAANbp76Y>E<8tj z5z?Jhog2n)u*e?fv)oEyR)$Q0YST8jM?QY5K&J2j-^90{$aO4yf5f~x2ENUG!;>pK^^?l5h9^`+`F*H!!~#Dyx6 z>vgXnnk%-Zsy$)sf4}Hz1I4f;(N0}da4{g^qjj>mWcwyn4C!;qLsizK>YMU!Qy#R9 zt1)x5-3+!mxvkRHAzC%74GgttG^Y~M1N`h3H@dxSV|c3yC%oNS5o}}bKA>Bg4GyR^ z!lUUCzn~dQYk9`5GE^Icvy2|8?hm3aqJb@Po@&XKo0j2Zf6hmVWJ}#s=6?Z;Yq@Un zDA2ZGam4Y^Z)+63R?)19ltF=gy^+Shph2)aINHj9p?)u!(zdK{&;P!MC&PDwrl`1k z();KJ_?e@7T`?6aBmE$<~HOqtNwTn8!?-H!#rnO9c#9hj}>$9WpP0q)C*16$|! zjg2?#5@^Dj-pgaGMwwFI-QgKGct@bv-gNn^0L7&V&MZc27g?2~AnrgNt+=LFmJ;y4 z<>4d|Y4_oz6G8 z#t_)zA#(k<8Odm#3;*!?jt4jc^Cc3+urlcCHsPUFoFM&Pn)0#2q}ain@|kllSbuP* zTPW0~di%8ejKMvR*!(=6WX1~V##!b(o@KsK+ezM>ike^*~UL|&hXtFTeaR=QDBH}!L#b>FOI5#Ft9h%MM`h}L5pg@gr{Yc39&R@ce* z%6yr>wh=5Ox9wvavy_SKTT}%-$ZfhPJum+GfMdG!@8HIPj>j=a05#;)z#d7VI2Y6#V;S$e2)`leDdR3Nv`9vJSob3KjMY`=F6mX zS^Xi;-1*T{Hvdk9<+guz#BvsYgrqKt3B2%m%4VR#o=jr?+)(9QIed5Y=H&SG$vBug pj&yZ3-`Ra$PCpbjToe4pp=B-NYxsv}PdgUxe*m*F#S_#_002I+n>hdg diff --git a/examples/napi/__tests__/strict.spec.ts b/examples/napi/__tests__/strict.spec.ts index 5e8acbf2..be7efd72 100644 --- a/examples/napi/__tests__/strict.spec.ts +++ b/examples/napi/__tests__/strict.spec.ts @@ -3,6 +3,8 @@ import test from 'ava' const { validateArray, validateTypedArray, + validateTypedArraySlice, + validateBufferSlice, validateBigint, validateBuffer, validateBoolean, @@ -38,6 +40,21 @@ test('should validate arraybuffer', (t) => { code: 'InvalidArg', message: 'Expected a TypedArray value', }) + + t.is(validateTypedArraySlice(new Uint8Array([1, 2, 3])), 3) + + // @ts-expect-error + t.throws(() => validateTypedArraySlice(1), { + code: 'InvalidArg', + message: 'Expected a TypedArray value', + }) + + t.is(validateBufferSlice(Buffer.from('hello')), 5) + // @ts-expect-error + t.throws(() => validateBufferSlice(2), { + code: 'InvalidArg', + message: 'Expected a Buffer value', + }) }) test('should validate BigInt', (t) => { @@ -123,7 +140,7 @@ test('should validate Map', (t) => { }) }) -test.only('should validate promise', async (t) => { +test('should validate promise', async (t) => { t.is( await validatePromise( new Promise((resolve) => { diff --git a/examples/napi/__tests__/values.spec.ts b/examples/napi/__tests__/values.spec.ts index ecc6458b..d826e170 100644 --- a/examples/napi/__tests__/values.spec.ts +++ b/examples/napi/__tests__/values.spec.ts @@ -4,7 +4,7 @@ import { fileURLToPath } from 'node:url' import { spy } from 'sinon' -import type { AliasedStruct, Animal as AnimalClass } from '../index.js' +import { type AliasedStruct, type Animal as AnimalClass } from '../index.js' import { test } from './test.framework.js' @@ -105,6 +105,8 @@ const { i64ArrayToArray, f32ArrayToArray, f64ArrayToArray, + acceptUint8ClampedSlice, + acceptUint8ClampedSliceAndBufferSlice, convertU32Array, createExternalTypedArray, mutateTypedArray, @@ -694,6 +696,15 @@ test('TypedArray', (t) => { const bird = new Bird('Carolyn') t.is(bird.acceptSliceMethod(new Uint8Array([1, 2, 3])), 3) + + t.is(acceptUint8ClampedSlice(new Uint8ClampedArray([1, 2, 3])), 3n) + t.is( + acceptUint8ClampedSliceAndBufferSlice( + Buffer.from([1, 2, 3]), + new Uint8ClampedArray([1, 2, 3]), + ), + 6n, + ) }) test('reset empty buffer', (t) => { diff --git a/examples/napi/index.d.ts b/examples/napi/index.d.ts index 800e9771..15894b04 100644 --- a/examples/napi/index.d.ts +++ b/examples/napi/index.d.ts @@ -245,6 +245,10 @@ export function acceptThreadsafeFunctionFatal(func: (arg: number) => any): void export function acceptThreadsafeFunctionTupleArgs(func: (err: Error | null, arg0: number, arg1: boolean, arg2: string) => any): void +export function acceptUint8ClampedSlice(input: Uint8ClampedArray): bigint + +export function acceptUint8ClampedSliceAndBufferSlice(a: Buffer, b: Uint8ClampedArray): bigint + export function add(a: number, b: number): number export const enum ALIAS { @@ -662,6 +666,8 @@ export function validateBoolean(i: boolean): boolean export function validateBuffer(b: Buffer): number +export function validateBufferSlice(input: Buffer): number + export function validateDate(d: Date): number export function validateDateTime(d: Date): number @@ -686,6 +692,10 @@ export function validateSymbol(s: symbol): boolean export function validateTypedArray(input: Uint8Array): number +export function validateTypedArraySlice(input: Uint8Array): number + +export function validateUint8ClampedSlice(input: Uint8ClampedArray): number + export function validateUndefined(i: undefined): boolean export function withAbortController(a: number, b: number, signal: AbortSignal): Promise diff --git a/examples/napi/src/fn_strict.rs b/examples/napi/src/fn_strict.rs index 0d4de886..7a8ffae2 100644 --- a/examples/napi/src/fn_strict.rs +++ b/examples/napi/src/fn_strict.rs @@ -18,6 +18,21 @@ fn validate_typed_array(input: Uint8Array) -> u32 { input.len() as u32 } +#[napi(strict)] +fn validate_typed_array_slice(input: &[u8]) -> u32 { + input.len() as u32 +} + +#[napi(strict)] +fn validate_uint8_clamped_slice(input: Uint8ClampedSlice) -> u32 { + input.len() as u32 +} + +#[napi(strict)] +fn validate_buffer_slice(input: BufferSlice) -> u32 { + input.len() as u32 +} + #[napi(strict)] fn validate_bigint(input: BigInt) -> i128 { input.get_i128().0 diff --git a/examples/napi/src/typed_array.rs b/examples/napi/src/typed_array.rs index e814aa08..620e250b 100644 --- a/examples/napi/src/typed_array.rs +++ b/examples/napi/src/typed_array.rs @@ -104,6 +104,16 @@ fn i64_array_to_array(input: &[i64]) -> Vec { input.to_vec() } +#[napi] +fn accept_uint8_clamped_slice(input: Uint8ClampedSlice) -> usize { + input.len() +} + +#[napi] +fn accept_uint8_clamped_slice_and_buffer_slice(a: BufferSlice, b: Uint8ClampedSlice) -> usize { + a.len() + b.len() +} + struct AsyncBuffer { buf: Buffer, }