| 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 From<String> for Buffer { |
| 238 | fn from(inner: String) -> Self { |
| 239 | Buffer::from(inner.into_bytes()) |
| 240 | } |
| 241 | } |
| 242 | |
| 243 | impl 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 | |
| 250 | impl 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 | |
| 259 | impl Deref for Buffer { |
| 260 | type Target = [u8]; |
| 261 | |
| 262 | fn deref(&self) -> &Self::Target { |
| 263 | self.as_ref() |
| 264 | } |
| 265 | } |
| 266 | |
| 267 | impl DerefMut for Buffer { |
| 268 | fn deref_mut(&mut self) -> &mut Self::Target { |
| 269 | self.as_mut() |
| 270 | } |
| 271 | } |
| 272 | |
| 273 | impl 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 | |
| 283 | impl 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 | |
| 320 | impl 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 | |
| 381 | impl 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 | |
| 388 | impl 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 | |
| 395 | impl 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 | |