From 7772779210263cf110091db98a3f5c7cd018262b Mon Sep 17 00:00:00 2001 From: Aaron O'Mullan Date: Wed, 20 Apr 2022 14:36:10 -0300 Subject: [PATCH] feat: v8::ArrayBuffer::new_backing_store_from_vec (#946) So we can avoid `.into_boxed_slice()` calls which may require a (costly) memmove when instantiating from a Vec --- src/array_buffer.rs | 37 +++++++++++++++++++++++++++++++++++-- src/shared_array_buffer.rs | 28 ++++++++++++++++++++++++++-- tests/test_api.rs | 35 +++++++++++++++++++++++++++++++++++ 3 files changed, 96 insertions(+), 4 deletions(-) diff --git a/src/array_buffer.rs b/src/array_buffer.rs index 6f15c2a9..5ccf392b 100644 --- a/src/array_buffer.rs +++ b/src/array_buffer.rs @@ -232,7 +232,7 @@ pub type BackingStoreDeleterCallback = unsafe extern "C" fn( deleter_data: *mut c_void, ); -pub unsafe extern "C" fn backing_store_deleter_callback( +pub unsafe extern "C" fn boxed_slice_deleter_callback( data: *mut c_void, byte_length: usize, _deleter_data: *mut c_void, @@ -242,6 +242,15 @@ pub unsafe extern "C" fn backing_store_deleter_callback( drop(b); } +pub unsafe extern "C" fn vec_deleter_callback( + data: *mut c_void, + byte_length: usize, + deleter_data: *mut c_void, +) { + let capacity = deleter_data as usize; + drop(Vec::from_raw_parts(data as *mut u8, byte_length, capacity)) +} + /// A wrapper around the backing store (i.e. the raw memory) of an array buffer. /// See a document linked in http://crbug.com/v8/9908 for more information. /// @@ -425,12 +434,36 @@ impl ArrayBuffer { UniqueRef::from_raw(v8__ArrayBuffer__NewBackingStore__with_data( data_ptr, byte_length, - backing_store_deleter_callback, + boxed_slice_deleter_callback, null_mut(), )) } } + /// Returns a new standalone BackingStore that takes over the ownership of + /// the given buffer. + /// + /// The destructor of the BackingStore frees owned buffer memory. + /// + /// The result can be later passed to ArrayBuffer::New. The raw pointer + /// to the buffer must not be passed again to any V8 API function. + pub fn new_backing_store_from_vec( + mut data: Vec, + ) -> UniqueRef { + let byte_length = data.len(); + let capacity = data.capacity(); + let data_ptr = data.as_mut_ptr() as *mut c_void; + std::mem::forget(data); + unsafe { + UniqueRef::from_raw(v8__ArrayBuffer__NewBackingStore__with_data( + data_ptr, + byte_length, + vec_deleter_callback, + capacity as *mut c_void, + )) + } + } + /// Returns a new standalone BackingStore backed by given ptr. /// /// SAFETY: This API consumes raw pointers so is inherently diff --git a/src/shared_array_buffer.rs b/src/shared_array_buffer.rs index f6403bb6..9affd7a9 100644 --- a/src/shared_array_buffer.rs +++ b/src/shared_array_buffer.rs @@ -3,7 +3,8 @@ use std::ffi::c_void; use std::ptr::null_mut; -use crate::array_buffer::backing_store_deleter_callback; +use crate::array_buffer::boxed_slice_deleter_callback; +use crate::array_buffer::vec_deleter_callback; use crate::support::SharedRef; use crate::support::UniqueRef; use crate::BackingStore; @@ -123,7 +124,30 @@ impl SharedArrayBuffer { UniqueRef::from_raw(v8__SharedArrayBuffer__NewBackingStore__with_data( data_ptr, byte_length, - backing_store_deleter_callback, + boxed_slice_deleter_callback, + null_mut(), + )) + } + } + + /// Returns a new standalone BackingStore that takes over the ownership of + /// the given buffer. + /// + /// The destructor of the BackingStore frees owned buffer memory. + /// + /// The result can be later passed to SharedArrayBuffer::New. The raw pointer + /// to the buffer must not be passed again to any V8 API function. + pub fn new_backing_store_from_vec( + mut data: Vec, + ) -> UniqueRef { + let byte_length = data.len(); + let data_ptr = data.as_mut_ptr() as *mut c_void; + std::mem::forget(data); + unsafe { + UniqueRef::from_raw(v8__SharedArrayBuffer__NewBackingStore__with_data( + data_ptr, + byte_length, + vec_deleter_callback, null_mut(), )) } diff --git a/tests/test_api.rs b/tests/test_api.rs index 156ad5fd..bd16232f 100644 --- a/tests/test_api.rs +++ b/tests/test_api.rs @@ -570,6 +570,7 @@ fn array_buffer() { assert_eq!(unique_bs[0].get(), 0); assert_eq!(unique_bs[9].get(), 9); + // From Box<[u8]> let data: Box<[u8]> = vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9].into_boxed_slice(); let unique_bs = v8::ArrayBuffer::new_backing_store_from_boxed_slice(data); assert_eq!(10, unique_bs.byte_length()); @@ -588,6 +589,26 @@ fn array_buffer() { assert_eq!(10, shared_bs_2.byte_length()); assert_eq!(shared_bs_2[0].get(), 0); assert_eq!(shared_bs_2[9].get(), 9); + + // From Vec + let data = vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + let unique_bs = v8::ArrayBuffer::new_backing_store_from_vec(data); + assert_eq!(10, unique_bs.byte_length()); + assert!(!unique_bs.is_shared()); + assert_eq!(unique_bs[0].get(), 0); + assert_eq!(unique_bs[9].get(), 9); + + let shared_bs_1 = unique_bs.make_shared(); + assert_eq!(10, shared_bs_1.byte_length()); + assert!(!shared_bs_1.is_shared()); + assert_eq!(shared_bs_1[0].get(), 0); + assert_eq!(shared_bs_1[9].get(), 9); + + let ab = v8::ArrayBuffer::with_backing_store(scope, &shared_bs_1); + let shared_bs_2 = ab.get_backing_store(); + assert_eq!(10, shared_bs_2.byte_length()); + assert_eq!(shared_bs_2[0].get(), 0); + assert_eq!(shared_bs_2[9].get(), 9); } } @@ -6078,6 +6099,20 @@ fn backing_store_from_empty_boxed_slice() { let _ = v8::ArrayBuffer::with_backing_store(&mut scope, &store); } +#[test] +fn backing_store_from_empty_vec() { + let _setup_guard = setup(); + + let mut isolate = v8::Isolate::new(Default::default()); + let mut scope = v8::HandleScope::new(&mut isolate); + let context = v8::Context::new(&mut scope); + let mut scope = v8::ContextScope::new(&mut scope, context); + + let store = + v8::ArrayBuffer::new_backing_store_from_vec(Vec::new()).make_shared(); + let _ = v8::ArrayBuffer::with_backing_store(&mut scope, &store); +} + #[test] fn current_stack_trace() { // Setup isolate