Skip to content

Commit

Permalink
fix(napi): also apply electron external data fallback to lowlevel APIs
Browse files Browse the repository at this point in the history
  • Loading branch information
Brooooooklyn committed Jan 28, 2023
1 parent fde2d64 commit aed2540
Showing 1 changed file with 77 additions and 20 deletions.
97 changes: 77 additions & 20 deletions crates/napi/src/env.rs
Expand Up @@ -262,21 +262,38 @@ impl Env {
let length = data.len();
let mut raw_value = ptr::null_mut();
let data_ptr = data.as_mut_ptr();
let hint_ptr = Box::into_raw(Box::new((length, data.capacity())));
check_status!(unsafe {
if length == 0 {
// Rust uses 0x1 as the data pointer for empty buffers,
// but NAPI/V8 only allows multiple buffers to have
// the same data pointer if it's 0x0.
sys::napi_create_buffer(self.0, length, ptr::null_mut(), &mut raw_value)
} else {
sys::napi_create_external_buffer(
let status = sys::napi_create_external_buffer(
self.0,
length,
data_ptr as *mut c_void,
data_ptr.cast(),
Some(drop_buffer),
Box::into_raw(Box::new((length, data.capacity()))) as *mut c_void,
hint_ptr.cast(),
&mut raw_value,
)
);
// electron doesn't support external buffers
if status == sys::Status::napi_no_external_buffers_allowed {
drop(Box::from_raw(hint_ptr));
let mut dest_data_ptr = ptr::null_mut();
let status = sys::napi_create_buffer_copy(
self.0,
length,
data.as_ptr().cast(),
&mut dest_data_ptr,
&mut raw_value,
);
data = Vec::from_raw_parts(dest_data_ptr.cast(), length, length);
status
} else {
status
}
}
})?;
Ok(JsBufferValue::new(
Expand All @@ -297,7 +314,7 @@ impl Env {
/// You can pass in `noop_finalize` if you have nothing to do in finalize phase.
pub unsafe fn create_buffer_with_borrowed_data<Hint, Finalize>(
&self,
data: *const u8,
mut data: *const u8,
length: usize,
hint: Hint,
finalize_callback: Finalize,
Expand All @@ -312,8 +329,9 @@ impl Env {
"Borrowed data should not be null".to_owned(),
));
}
check_status!(unsafe {
sys::napi_create_external_buffer(
let hint_ptr = Box::into_raw(Box::new((hint, finalize_callback)));
unsafe {
let status = sys::napi_create_external_buffer(
self.0,
length,
data as *mut c_void,
Expand All @@ -325,10 +343,26 @@ impl Env {
finalize_hint: *mut c_void,
),
),
Box::into_raw(Box::new((hint, finalize_callback))) as *mut c_void,
hint_ptr.cast(),
&mut raw_value,
)
})?;
);
if status == sys::Status::napi_no_external_buffers_allowed {
let (hint, finalize) = *Box::from_raw(hint_ptr);
let mut result_data = ptr::null_mut();
let status = sys::napi_create_buffer_copy(
self.0,
length,
data.cast(),
&mut result_data,
&mut raw_value,
);
data = result_data.cast();
finalize(hint, self.clone());
check_status!(status)?;
} else {
check_status!(status)?;
}
};
Ok(JsBufferValue::new(
JsBuffer(Value {
env: self.0,
Expand Down Expand Up @@ -405,14 +439,25 @@ impl Env {
// the same data pointer if it's 0x0.
sys::napi_create_arraybuffer(self.0, length, ptr::null_mut(), &mut raw_value)
} else {
sys::napi_create_external_arraybuffer(
let hint_ptr = Box::into_raw(Box::new((length, data.capacity())));
let status = sys::napi_create_external_arraybuffer(
self.0,
data_ptr as *mut c_void,
data_ptr.cast(),
length,
Some(drop_buffer),
Box::into_raw(Box::new((length, data.capacity()))) as *mut c_void,
hint_ptr.cast(),
&mut raw_value,
)
);
if status == sys::Status::napi_no_external_buffers_allowed {
drop(Box::from_raw(hint_ptr));
let mut underlying_data = ptr::null_mut();
let status =
sys::napi_create_arraybuffer(self.0, length, &mut underlying_data, &mut raw_value);
ptr::swap(underlying_data.cast(), data_ptr);
status
} else {
status
}
}
})?;

Expand All @@ -436,7 +481,7 @@ impl Env {
/// You can pass in `noop_finalize` if you have nothing to do in finalize phase.
pub unsafe fn create_arraybuffer_with_borrowed_data<Hint, Finalize>(
&self,
data: *const u8,
mut data: *const u8,
length: usize,
hint: Hint,
finalize_callback: Finalize,
Expand All @@ -445,8 +490,9 @@ impl Env {
Finalize: FnOnce(Hint, Env),
{
let mut raw_value = ptr::null_mut();
check_status!(unsafe {
sys::napi_create_external_arraybuffer(
let hint_ptr = Box::into_raw(Box::new((hint, finalize_callback)));
unsafe {
let status = sys::napi_create_external_arraybuffer(
self.0,
if length == 0 {
// Rust uses 0x1 as the data pointer for empty buffers,
Expand All @@ -465,10 +511,21 @@ impl Env {
finalize_hint: *mut c_void,
),
),
Box::into_raw(Box::new((hint, finalize_callback))) as *mut c_void,
hint_ptr.cast(),
&mut raw_value,
)
})?;
);
if status == sys::Status::napi_no_external_buffers_allowed {
let (hint, finalize) = *Box::from_raw(hint_ptr);
let mut underlying_data = ptr::null_mut();
let status =
sys::napi_create_arraybuffer(self.0, length, &mut underlying_data, &mut raw_value);
data = underlying_data.cast();
finalize(hint, self.clone());
check_status!(status)?;
} else {
check_status!(status)?;
}
};
Ok(JsArrayBufferValue::new(
JsArrayBuffer(Value {
env: self.0,
Expand Down

1 comment on commit aed2540

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Benchmark

Benchmark suite Current: aed2540 Previous: fde2d64 Ratio
noop#napi-rs 51179117 ops/sec (±1.16%) 64268786 ops/sec (±0.27%) 1.26
noop#JavaScript 639735563 ops/sec (±0.72%) 710252276 ops/sec (±0.1%) 1.11
Plus number#napi-rs 15113122 ops/sec (±1.6%) 19701768 ops/sec (±0.6%) 1.30
Plus number#JavaScript 635987575 ops/sec (±0.94%) 708450052 ops/sec (±0.14%) 1.11
Create buffer#napi-rs 311410 ops/sec (±10.23%) 365131 ops/sec (±10.52%) 1.17
Create buffer#JavaScript 1587329 ops/sec (±10.06%) 1769743 ops/sec (±6.98%) 1.11
createArray#createArrayJson 34756 ops/sec (±1.14%) 38725 ops/sec (±0.11%) 1.11
createArray#create array for loop 5732 ops/sec (±0.89%) 7410 ops/sec (±0.12%) 1.29
createArray#create array with serde trait 5708 ops/sec (±1.59%) 7427 ops/sec (±0.12%) 1.30
getArrayFromJs#get array from json string 14361 ops/sec (±1.24%) 16367 ops/sec (±0.27%) 1.14
getArrayFromJs#get array from serde 7793 ops/sec (±1.34%) 10023 ops/sec (±0.04%) 1.29
getArrayFromJs#get array with for loop 10054 ops/sec (±0.89%) 12256 ops/sec (±0.1%) 1.22
Get Set property#Get Set from native#u32 365483 ops/sec (±6.5%) 415294 ops/sec (±6.57%) 1.14
Get Set property#Get Set from JavaScript#u32 270588 ops/sec (±6.18%) 357080 ops/sec (±6.76%) 1.32
Get Set property#Get Set from native#string 316430 ops/sec (±6.18%) 368692 ops/sec (±6.5%) 1.17
Get Set property#Get Set from JavaScript#string 282779 ops/sec (±6.1%) 341740 ops/sec (±6.86%) 1.21
Async task#spawn task 27321 ops/sec (±3.19%) 37096 ops/sec (±1.46%) 1.36
Async task#ThreadSafeFunction 1905 ops/sec (±6.76%) 1140 ops/sec (±23.02%) 0.60
Async task#Tokio future to Promise 25958 ops/sec (±3.93%) 31816 ops/sec (±0.65%) 1.23
Query#query * 100 1586 ops/sec (±2.67%) 2058 ops/sec (±3.31%) 1.30
Query#query * 1 23135 ops/sec (±3.2%) 31192 ops/sec (±0.3%) 1.35

This comment was automatically generated by workflow using github-action-benchmark.

Please sign in to comment.