1 | #[cfg (all(debug_assertions, not(windows)))] |
2 | use std::collections::HashSet; |
3 | use std::ffi::c_void; |
4 | use std::mem; |
5 | use std::ops::{Deref, DerefMut}; |
6 | use std::ptr::{self, NonNull}; |
7 | use std::slice; |
8 | use std::sync::Arc; |
9 | #[cfg (all(debug_assertions, not(windows)))] |
10 | use std::sync::Mutex; |
11 | |
12 | #[cfg (all(feature = "napi4" , not(feature = "noop" ), not(target_family = "wasm" )))] |
13 | use crate::bindgen_prelude::{CUSTOM_GC_TSFN, CUSTOM_GC_TSFN_DESTROYED, THREADS_CAN_ACCESS_ENV}; |
14 | use crate::{bindgen_prelude::*, check_status, sys, Result, ValueType}; |
15 | |
16 | #[cfg (all(debug_assertions, not(windows)))] |
17 | thread_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. |
24 | pub struct BufferSlice<'scope> { |
25 | pub(crate) inner: &'scope mut [u8], |
26 | raw_value: sys::napi_value, |
27 | } |
28 | |
29 | impl<'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 | |
55 | impl 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 | |
62 | impl TypeName for BufferSlice<'_> { |
63 | fn type_name() -> &'static str { |
64 | "Buffer" |
65 | } |
66 | |
67 | fn value_type() -> ValueType { |
68 | ValueType::Object |
69 | } |
70 | } |
71 | |
72 | impl 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 | |
89 | impl AsRef<[u8]> for BufferSlice<'_> { |
90 | fn as_ref(&self) -> &[u8] { |
91 | self.inner |
92 | } |
93 | } |
94 | |
95 | impl<'scope> Deref for BufferSlice<'scope> { |
96 | type Target = [u8]; |
97 | |
98 | fn deref(&self) -> &Self::Target { |
99 | self.inner |
100 | } |
101 | } |
102 | |
103 | impl<'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`. |
116 | pub 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 | |
124 | impl 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. |
176 | unsafe impl Send for Buffer {} |
177 | |
178 | impl 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 | |
190 | impl Default for Buffer { |
191 | fn default() -> Self { |
192 | Self::from(Vec::default()) |
193 | } |
194 | } |
195 | |
196 | impl 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 | |
225 | impl From<Buffer> for Vec<u8> { |
226 | fn from(buf: Buffer) -> Self { |
227 | buf.as_ref().to_vec() |
228 | } |
229 | } |
230 | |
231 | impl From<&[u8]> for Buffer { |
232 | fn from(inner: &[u8]) -> Self { |
233 | Buffer::from(inner.to_owned()) |
234 | } |
235 | } |
236 | |
237 | impl 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 | |
244 | impl 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 | |
253 | impl Deref for Buffer { |
254 | type Target = [u8]; |
255 | |
256 | fn deref(&self) -> &Self::Target { |
257 | self.as_ref() |
258 | } |
259 | } |
260 | |
261 | impl DerefMut for Buffer { |
262 | fn deref_mut(&mut self) -> &mut Self::Target { |
263 | self.as_mut() |
264 | } |
265 | } |
266 | |
267 | impl 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 | |
277 | impl 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 | |
314 | impl 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 | |
375 | impl 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 | |
382 | impl 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 | |
389 | impl 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 | |