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 From<String> for Buffer {
238 fn from(inner: String) -> Self {
239 Buffer::from(inner.into_bytes())
240 }
241}
242
243impl AsRef<[u8]> for Buffer {
244 fn as_ref(&self) -> &[u8] {
245 // SAFETY: the pointer is guaranteed to be non-null, and guaranteed to be valid if `len` is not 0.
246 unsafe { slice::from_raw_parts(self.inner.as_ptr(), self.len) }
247 }
248}
249
250impl AsMut<[u8]> for Buffer {
251 fn as_mut(&mut self) -> &mut [u8] {
252 // SAFETY: This is literally undefined behavior. `Buffer::clone` allows you to create shared
253 // access to the underlying data, but `as_mut` and `deref_mut` allow unsynchronized mutation of
254 // that data (not to speak of the JS side having write access as well, at the same time).
255 unsafe { slice::from_raw_parts_mut(self.inner.as_ptr(), self.len) }
256 }
257}
258
259impl Deref for Buffer {
260 type Target = [u8];
261
262 fn deref(&self) -> &Self::Target {
263 self.as_ref()
264 }
265}
266
267impl DerefMut for Buffer {
268 fn deref_mut(&mut self) -> &mut Self::Target {
269 self.as_mut()
270 }
271}
272
273impl TypeName for Buffer {
274 fn type_name() -> &'static str {
275 "Vec<u8>"
276 }
277
278 fn value_type() -> ValueType {
279 ValueType::Object
280 }
281}
282
283impl FromNapiValue for Buffer {
284 unsafe fn from_napi_value(env: sys::napi_env, napi_val: sys::napi_value) -> Result<Self> {
285 let mut buf = ptr::null_mut();
286 let mut len = 0;
287 let mut ref_ = ptr::null_mut();
288 check_status!(
289 unsafe { sys::napi_create_reference(env, napi_val, 1, &mut ref_) },
290 "Failed to create reference from Buffer"
291 )?;
292 check_status!(
293 unsafe { sys::napi_get_buffer_info(env, napi_val, &mut buf, &mut len as *mut usize) },
294 "Failed to get Buffer pointer and length"
295 )?;
296
297 // From the docs of `napi_get_buffer_info`:
298 // > [out] data: The underlying data buffer of the node::Buffer. If length is 0, this may be
299 // > NULL or any other pointer value.
300 //
301 // In order to guarantee that `slice::from_raw_parts` is sound, the pointer must be non-null, so
302 // let's make sure it always is, even in the case of `napi_get_buffer_info` returning a null
303 // ptr.
304 let buf = NonNull::new(buf as *mut u8);
305 let inner = match buf {
306 Some(buf) if len != 0 => buf,
307 _ => NonNull::dangling(),
308 };
309
310 Ok(Self {
311 inner,
312 len,
313 capacity: len,
314 raw: Some((ref_, env)),
315 ref_count: Arc::new(()),
316 })
317 }
318}
319
320impl ToNapiValue for Buffer {
321 unsafe fn to_napi_value(env: sys::napi_env, mut val: Self) -> Result<sys::napi_value> {
322 // From Node.js value, not from `Vec<u8>`
323 if let Some((ref_, _)) = val.raw {
324 let mut buf = ptr::null_mut();
325 check_status!(
326 unsafe { sys::napi_get_reference_value(env, ref_, &mut buf) },
327 "Failed to get Buffer value from reference"
328 )?;
329 // fast path for Buffer::drop
330 if Arc::strong_count(&val.ref_count) == 1 {
331 check_status!(
332 unsafe { sys::napi_delete_reference(env, ref_) },
333 "Failed to delete Buffer reference in Buffer::to_napi_value"
334 )?;
335 val.raw = Some((ptr::null_mut(), ptr::null_mut()));
336 }
337 return Ok(buf);
338 }
339 let len = val.len;
340 let mut ret = ptr::null_mut();
341 check_status!(
342 if len == 0 {
343 // Rust uses 0x1 as the data pointer for empty buffers,
344 // but NAPI/V8 only allows multiple buffers to have
345 // the same data pointer if it's 0x0.
346 unsafe { sys::napi_create_buffer(env, len, ptr::null_mut(), &mut ret) }
347 } else {
348 let value_ptr = val.inner.as_ptr();
349 let val_box_ptr = Box::into_raw(Box::new(val));
350 let mut status = unsafe {
351 sys::napi_create_external_buffer(
352 env,
353 len,
354 value_ptr as *mut c_void,
355 Some(drop_buffer),
356 val_box_ptr as *mut c_void,
357 &mut ret,
358 )
359 };
360 if status == napi_sys::Status::napi_no_external_buffers_allowed {
361 let value = unsafe { Box::from_raw(val_box_ptr) };
362 status = unsafe {
363 sys::napi_create_buffer_copy(
364 env,
365 len,
366 value.inner.as_ptr() as *mut c_void,
367 ptr::null_mut(),
368 &mut ret,
369 )
370 };
371 }
372 status
373 },
374 "Failed to create napi buffer"
375 )?;
376
377 Ok(ret)
378 }
379}
380
381impl ToNapiValue for &Buffer {
382 unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> Result<sys::napi_value> {
383 let buf: Buffer = val.clone();
384 unsafe { ToNapiValue::to_napi_value(env, val:buf) }
385 }
386}
387
388impl ToNapiValue for &mut Buffer {
389 unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> Result<sys::napi_value> {
390 let buf: Buffer = val.clone();
391 unsafe { ToNapiValue::to_napi_value(env, val:buf) }
392 }
393}
394
395impl ValidateNapiValue for Buffer {
396 unsafe fn validate(env: sys::napi_env, napi_val: sys::napi_value) -> Result<sys::napi_value> {
397 let mut is_buffer: bool = false;
398 check_status!(
399 unsafe { sys::napi_is_buffer(env, napi_val, &mut is_buffer) },
400 "Failed to validate napi buffer"
401 )?;
402 if !is_buffer {
403 return Err(Error::new(
404 Status::InvalidArg,
405 reason:"Expected a Buffer value".to_owned(),
406 ));
407 }
408 Ok(ptr::null_mut())
409 }
410}
411

Provided by KDAB

Privacy Policy