1#[cfg(all(debug_assertions, not(windows)))]
2use std::collections::HashSet;
3use std::ffi::c_void;
4use std::mem;
5use std::ops::{Deref, DerefMut};
6use std::ptr::{self, NonNull};
7use std::slice;
8use std::sync::Arc;
9#[cfg(all(debug_assertions, not(windows)))]
10use std::sync::Mutex;
11
12#[cfg(all(feature = "napi4", not(feature = "noop"), not(target_family = "wasm")))]
13use crate::bindgen_prelude::{CUSTOM_GC_TSFN, CUSTOM_GC_TSFN_DESTROYED, THREADS_CAN_ACCESS_ENV};
14use crate::{bindgen_prelude::*, check_status, sys, Result, ValueType};
15
16#[cfg(all(debug_assertions, not(windows)))]
17thread_local! {
18 pub (crate) static BUFFER_DATA: Mutex<HashSet<*mut u8>> = Default::default();
19}
20
21/// Zero copy buffer slice shared between Rust and Node.js.
22/// It can only be used in non-async context and the lifetime is bound to the fn closure.
23/// If you want to use Node.js Buffer in async context or want to extend the lifetime, use `Buffer` instead.
24pub struct BufferSlice<'scope> {
25 pub(crate) inner: &'scope mut [u8],
26 raw_value: sys::napi_value,
27}
28
29impl<'scope> FromNapiValue for BufferSlice<'scope> {
30 unsafe fn from_napi_value(env: sys::napi_env, napi_val: sys::napi_value) -> Result<Self> {
31 let mut buf = ptr::null_mut();
32 let mut len = 0usize;
33 check_status!(
34 unsafe { sys::napi_get_buffer_info(env, napi_val, &mut buf, &mut len) },
35 "Failed to get Buffer pointer and length"
36 )?;
37 // From the docs of `napi_get_buffer_info`:
38 // > [out] data: The underlying data buffer of the node::Buffer. If length is 0, this may be
39 // > NULL or any other pointer value.
40 //
41 // In order to guarantee that `slice::from_raw_parts` is sound, the pointer must be non-null, so
42 // let's make sure it always is, even in the case of `napi_get_buffer_info` returning a null
43 // ptr.
44 Ok(Self {
45 inner: if len == 0 {
46 &mut []
47 } else {
48 unsafe { slice::from_raw_parts_mut(buf.cast(), len) }
49 },
50 raw_value: napi_val,
51 })
52 }
53}
54
55impl ToNapiValue for BufferSlice<'_> {
56 #[allow(unused_variables)]
57 unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> Result<sys::napi_value> {
58 Ok(val.raw_value)
59 }
60}
61
62impl TypeName for BufferSlice<'_> {
63 fn type_name() -> &'static str {
64 "Buffer"
65 }
66
67 fn value_type() -> ValueType {
68 ValueType::Object
69 }
70}
71
72impl ValidateNapiValue for BufferSlice<'_> {
73 unsafe fn validate(env: sys::napi_env, napi_val: sys::napi_value) -> Result<sys::napi_value> {
74 let mut is_buffer: bool = false;
75 check_status!(
76 unsafe { sys::napi_is_buffer(env, napi_val, &mut is_buffer) },
77 "Failed to validate napi buffer"
78 )?;
79 if !is_buffer {
80 return Err(Error::new(
81 Status::InvalidArg,
82 reason:"Expected a Buffer value".to_owned(),
83 ));
84 }
85 Ok(ptr::null_mut())
86 }
87}
88
89impl AsRef<[u8]> for BufferSlice<'_> {
90 fn as_ref(&self) -> &[u8] {
91 self.inner
92 }
93}
94
95impl<'scope> Deref for BufferSlice<'scope> {
96 type Target = [u8];
97
98 fn deref(&self) -> &Self::Target {
99 self.inner
100 }
101}
102
103impl<'scope> DerefMut for BufferSlice<'scope> {
104 fn deref_mut(&mut self) -> &mut Self::Target {
105 self.inner
106 }
107}
108
109/// Zero copy u8 vector shared between rust and napi.
110/// It's designed to be used in `async` context, so it contains overhead to ensure the underlying data is not dropped.
111/// For non-async context, use `BufferRef` instead.
112///
113/// Auto reference the raw JavaScript value, and release it when dropped.
114/// So it is safe to use it in `async fn`, the `&[u8]` under the hood will not be dropped until the `drop` called.
115/// Clone will create a new `Reference` to the same underlying `JavaScript Buffer`.
116pub struct Buffer {
117 pub(crate) inner: NonNull<u8>,
118 pub(crate) len: usize,
119 pub(crate) capacity: usize,
120 raw: Option<(sys::napi_ref, sys::napi_env)>,
121 pub(crate) ref_count: Arc<()>,
122}
123
124impl Drop for Buffer {
125 fn drop(&mut self) {
126 if Arc::strong_count(&self.ref_count) == 1 {
127 if let Some((ref_, env)) = self.raw {
128 if ref_.is_null() {
129 return;
130 }
131 #[cfg(all(feature = "napi4", not(feature = "noop"), not(target_family = "wasm")))]
132 {
133 if CUSTOM_GC_TSFN_DESTROYED.load(std::sync::atomic::Ordering::SeqCst) {
134 return;
135 }
136 if !THREADS_CAN_ACCESS_ENV.borrow_mut(|m| m.get(&std::thread::current().id()).is_some()) {
137 let status = unsafe {
138 sys::napi_call_threadsafe_function(
139 CUSTOM_GC_TSFN.load(std::sync::atomic::Ordering::SeqCst),
140 ref_.cast(),
141 1,
142 )
143 };
144 assert!(
145 status == sys::Status::napi_ok || status == sys::Status::napi_closing,
146 "Call custom GC in Buffer::drop failed {}",
147 Status::from(status)
148 );
149 return;
150 }
151 }
152 let mut ref_count = 0;
153 check_status_or_throw!(
154 env,
155 unsafe { sys::napi_reference_unref(env, ref_, &mut ref_count) },
156 "Failed to unref Buffer reference in drop"
157 );
158 debug_assert!(
159 ref_count == 0,
160 "Buffer reference count in Buffer::drop is not zero"
161 );
162 check_status_or_throw!(
163 env,
164 unsafe { sys::napi_delete_reference(env, ref_) },
165 "Failed to delete Buffer reference in drop"
166 );
167 } else {
168 unsafe { Vec::from_raw_parts(self.inner.as_ptr(), self.len, self.capacity) };
169 }
170 }
171 }
172}
173
174// SAFETY: This is undefined behavior, as the JS side may always modify the underlying buffer,
175// without synchronization. Also see the docs for the `AsMut` impl.
176unsafe impl Send for Buffer {}
177
178impl Clone for Buffer {
179 fn clone(&self) -> Self {
180 Self {
181 inner: self.inner,
182 len: self.len,
183 capacity: self.capacity,
184 raw: self.raw,
185 ref_count: self.ref_count.clone(),
186 }
187 }
188}
189
190impl Default for Buffer {
191 fn default() -> Self {
192 Self::from(Vec::default())
193 }
194}
195
196impl From<Vec<u8>> for Buffer {
197 fn from(mut data: Vec<u8>) -> Self {
198 let inner_ptr = data.as_mut_ptr();
199 #[cfg(all(debug_assertions, not(windows)))]
200 {
201 let is_existed = BUFFER_DATA.with(|buffer_data| {
202 let buffer = buffer_data.lock().expect("Unlock buffer data failed");
203 buffer.contains(&inner_ptr)
204 });
205 if is_existed {
206 panic!("Share the same data between different buffers is not allowed, see: https://github.com/nodejs/node/issues/32463#issuecomment-631974747");
207 }
208 }
209 let len = data.len();
210 let capacity = data.capacity();
211 mem::forget(data);
212 Buffer {
213 // SAFETY: `Vec`'s docs guarantee that its pointer is never null (it's a dangling ptr if not
214 // allocated):
215 // > The pointer will never be null, so this type is null-pointer-optimized.
216 inner: unsafe { NonNull::new_unchecked(inner_ptr) },
217 len,
218 capacity,
219 raw: None,
220 ref_count: Arc::new(()),
221 }
222 }
223}
224
225impl From<Buffer> for Vec<u8> {
226 fn from(buf: Buffer) -> Self {
227 buf.as_ref().to_vec()
228 }
229}
230
231impl From<&[u8]> for Buffer {
232 fn from(inner: &[u8]) -> Self {
233 Buffer::from(inner.to_owned())
234 }
235}
236
237impl AsRef<[u8]> for Buffer {
238 fn as_ref(&self) -> &[u8] {
239 // SAFETY: the pointer is guaranteed to be non-null, and guaranteed to be valid if `len` is not 0.
240 unsafe { slice::from_raw_parts(self.inner.as_ptr(), self.len) }
241 }
242}
243
244impl AsMut<[u8]> for Buffer {
245 fn as_mut(&mut self) -> &mut [u8] {
246 // SAFETY: This is literally undefined behavior. `Buffer::clone` allows you to create shared
247 // access to the underlying data, but `as_mut` and `deref_mut` allow unsynchronized mutation of
248 // that data (not to speak of the JS side having write access as well, at the same time).
249 unsafe { slice::from_raw_parts_mut(self.inner.as_ptr(), self.len) }
250 }
251}
252
253impl Deref for Buffer {
254 type Target = [u8];
255
256 fn deref(&self) -> &Self::Target {
257 self.as_ref()
258 }
259}
260
261impl DerefMut for Buffer {
262 fn deref_mut(&mut self) -> &mut Self::Target {
263 self.as_mut()
264 }
265}
266
267impl TypeName for Buffer {
268 fn type_name() -> &'static str {
269 "Vec<u8>"
270 }
271
272 fn value_type() -> ValueType {
273 ValueType::Object
274 }
275}
276
277impl FromNapiValue for Buffer {
278 unsafe fn from_napi_value(env: sys::napi_env, napi_val: sys::napi_value) -> Result<Self> {
279 let mut buf = ptr::null_mut();
280 let mut len = 0;
281 let mut ref_ = ptr::null_mut();
282 check_status!(
283 unsafe { sys::napi_create_reference(env, napi_val, 1, &mut ref_) },
284 "Failed to create reference from Buffer"
285 )?;
286 check_status!(
287 unsafe { sys::napi_get_buffer_info(env, napi_val, &mut buf, &mut len as *mut usize) },
288 "Failed to get Buffer pointer and length"
289 )?;
290
291 // From the docs of `napi_get_buffer_info`:
292 // > [out] data: The underlying data buffer of the node::Buffer. If length is 0, this may be
293 // > NULL or any other pointer value.
294 //
295 // In order to guarantee that `slice::from_raw_parts` is sound, the pointer must be non-null, so
296 // let's make sure it always is, even in the case of `napi_get_buffer_info` returning a null
297 // ptr.
298 let buf = NonNull::new(buf as *mut u8);
299 let inner = match buf {
300 Some(buf) if len != 0 => buf,
301 _ => NonNull::dangling(),
302 };
303
304 Ok(Self {
305 inner,
306 len,
307 capacity: len,
308 raw: Some((ref_, env)),
309 ref_count: Arc::new(()),
310 })
311 }
312}
313
314impl ToNapiValue for Buffer {
315 unsafe fn to_napi_value(env: sys::napi_env, mut val: Self) -> Result<sys::napi_value> {
316 // From Node.js value, not from `Vec<u8>`
317 if let Some((ref_, _)) = val.raw {
318 let mut buf = ptr::null_mut();
319 check_status!(
320 unsafe { sys::napi_get_reference_value(env, ref_, &mut buf) },
321 "Failed to get Buffer value from reference"
322 )?;
323 // fast path for Buffer::drop
324 if Arc::strong_count(&val.ref_count) == 1 {
325 check_status!(
326 unsafe { sys::napi_delete_reference(env, ref_) },
327 "Failed to delete Buffer reference in Buffer::to_napi_value"
328 )?;
329 val.raw = Some((ptr::null_mut(), ptr::null_mut()));
330 }
331 return Ok(buf);
332 }
333 let len = val.len;
334 let mut ret = ptr::null_mut();
335 check_status!(
336 if len == 0 {
337 // Rust uses 0x1 as the data pointer for empty buffers,
338 // but NAPI/V8 only allows multiple buffers to have
339 // the same data pointer if it's 0x0.
340 unsafe { sys::napi_create_buffer(env, len, ptr::null_mut(), &mut ret) }
341 } else {
342 let value_ptr = val.inner.as_ptr();
343 let val_box_ptr = Box::into_raw(Box::new(val));
344 let mut status = unsafe {
345 sys::napi_create_external_buffer(
346 env,
347 len,
348 value_ptr as *mut c_void,
349 Some(drop_buffer),
350 val_box_ptr as *mut c_void,
351 &mut ret,
352 )
353 };
354 if status == napi_sys::Status::napi_no_external_buffers_allowed {
355 let value = unsafe { Box::from_raw(val_box_ptr) };
356 status = unsafe {
357 sys::napi_create_buffer_copy(
358 env,
359 len,
360 value.inner.as_ptr() as *mut c_void,
361 ptr::null_mut(),
362 &mut ret,
363 )
364 };
365 }
366 status
367 },
368 "Failed to create napi buffer"
369 )?;
370
371 Ok(ret)
372 }
373}
374
375impl ToNapiValue for &Buffer {
376 unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> Result<sys::napi_value> {
377 let buf: Buffer = val.clone();
378 unsafe { ToNapiValue::to_napi_value(env, val:buf) }
379 }
380}
381
382impl ToNapiValue for &mut Buffer {
383 unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> Result<sys::napi_value> {
384 let buf: Buffer = val.clone();
385 unsafe { ToNapiValue::to_napi_value(env, val:buf) }
386 }
387}
388
389impl ValidateNapiValue for Buffer {
390 unsafe fn validate(env: sys::napi_env, napi_val: sys::napi_value) -> Result<sys::napi_value> {
391 let mut is_buffer: bool = false;
392 check_status!(
393 unsafe { sys::napi_is_buffer(env, napi_val, &mut is_buffer) },
394 "Failed to validate napi buffer"
395 )?;
396 if !is_buffer {
397 return Err(Error::new(
398 Status::InvalidArg,
399 reason:"Expected a Buffer value".to_owned(),
400 ));
401 }
402 Ok(ptr::null_mut())
403 }
404}
405