napi_h/bindgen_runtime/js_values/
buffer.rs

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 = 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        "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 = val.clone();
378    unsafe { ToNapiValue::to_napi_value(env, 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 = val.clone();
385    unsafe { ToNapiValue::to_napi_value(env, 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 = 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        "Expected a Buffer value".to_owned(),
400      ));
401    }
402    Ok(ptr::null_mut())
403  }
404}