feat: new_backing_store_from_bytes and empty for ArrayBuffer and SharedArrayBuffer (#1334)

This commit is contained in:
Matt Mastracci 2023-10-02 12:08:51 -06:00 committed by GitHub
parent 12dca0cf03
commit bf277f4f8e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 230 additions and 64 deletions

7
Cargo.lock generated
View File

@ -127,6 +127,12 @@ version = "1.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
[[package]]
name = "bytes"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223"
[[package]]
name = "calloop"
version = "0.9.3"
@ -1315,6 +1321,7 @@ version = "0.78.0"
dependencies = [
"align-data",
"bitflags 1.3.2",
"bytes",
"fslock",
"once_cell",
"trybuild",

View File

@ -88,6 +88,7 @@ fslock = "0.1.8"
which = "4.2.5"
[dev-dependencies]
bytes = "1"
align-data = "0.1.0"
fslock = "0.1.8"
trybuild = "1.0.61"

View File

@ -5,7 +5,6 @@ use std::ffi::c_void;
use std::ops::Deref;
use std::ptr;
use std::ptr::null;
use std::ptr::null_mut;
use std::ptr::NonNull;
use std::slice;
@ -60,6 +59,7 @@ extern "C" {
deleter: BackingStoreDeleterCallback,
deleter_data: *mut c_void,
) -> *mut BackingStore;
fn v8__BackingStore__EmptyBackingStore(shared: bool) -> *mut BackingStore;
fn v8__BackingStore__Data(this: *const BackingStore) -> *mut c_void;
fn v8__BackingStore__ByteLength(this: *const BackingStore) -> usize;
@ -246,25 +246,50 @@ pub type BackingStoreDeleterCallback = unsafe extern "C" fn(
deleter_data: *mut c_void,
);
pub unsafe extern "C" fn boxed_slice_deleter_callback(
data: *mut c_void,
byte_length: usize,
_deleter_data: *mut c_void,
) {
let slice_ptr = ptr::slice_from_raw_parts_mut(data as *mut u8, byte_length);
let b = Box::from_raw(slice_ptr);
drop(b);
pub(crate) mod sealed {
pub trait Rawable<T: ?Sized> {
fn into_raw(self) -> *const ();
unsafe fn drop_raw(ptr: *const (), size: usize);
}
}
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))
impl sealed::Rawable<[u8]> for Vec<u8> {
unsafe fn drop_raw(ptr: *const (), size: usize) {
<Box<[u8]> as sealed::Rawable<[u8]>>::drop_raw(ptr, size);
}
fn into_raw(self) -> *const () {
self.into_boxed_slice().into_raw()
}
}
macro_rules! rawable {
($container:ident) => {
impl<T: Sized> sealed::Rawable<T> for $container<T> {
fn into_raw(self) -> *const () {
Self::into_raw(self) as _
}
unsafe fn drop_raw(ptr: *const (), _len: usize) {
_ = Self::from_raw(ptr as _);
}
}
impl sealed::Rawable<[u8]> for $container<[u8]> {
fn into_raw(self) -> *const () {
Self::into_raw(self) as _
}
unsafe fn drop_raw(ptr: *const (), len: usize) {
_ = Self::from_raw(ptr::slice_from_raw_parts_mut(ptr as _, len));
}
}
};
}
// Implement Rawable for single-ownership container types
rawable!(Box);
/// 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.
///
@ -396,6 +421,16 @@ impl ArrayBuffer {
.unwrap()
}
/// Create a new, empty ArrayBuffer.
#[inline(always)]
pub fn empty<'s>(scope: &mut HandleScope<'s>) -> Local<'s, ArrayBuffer> {
// SAFETY: This is a v8-provided empty backing store
let backing_store = unsafe {
UniqueRef::from_raw(v8__BackingStore__EmptyBackingStore(false))
};
Self::with_backing_store(scope, &backing_store.make_shared())
}
/// Data length in bytes.
#[inline(always)]
pub fn byte_length(&self) -> usize {
@ -489,16 +524,7 @@ impl ArrayBuffer {
pub fn new_backing_store_from_boxed_slice(
data: Box<[u8]>,
) -> UniqueRef<BackingStore> {
let byte_length = data.len();
let data_ptr = Box::into_raw(data) as *mut c_void;
unsafe {
UniqueRef::from_raw(v8__ArrayBuffer__NewBackingStore__with_data(
data_ptr,
byte_length,
boxed_slice_deleter_callback,
null_mut(),
))
}
Self::new_backing_store_from_bytes(data)
}
/// Returns a new standalone BackingStore that takes over the ownership of
@ -509,20 +535,59 @@ impl ArrayBuffer {
/// 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.
#[inline(always)]
pub fn new_backing_store_from_vec(
mut data: Vec<u8>,
) -> UniqueRef<BackingStore> {
let byte_length = data.len();
let capacity = data.capacity();
let data_ptr = data.as_mut_ptr() as *mut c_void;
std::mem::forget(data);
pub fn new_backing_store_from_vec(data: Vec<u8>) -> UniqueRef<BackingStore> {
Self::new_backing_store_from_bytes(data)
}
/// Returns a new standalone BackingStore backed by a container that dereferences
/// to a mutable slice of bytes. The object is dereferenced once, and the resulting slice's
/// memory is used for the lifetime of the buffer.
///
/// This method may be called with most single-ownership containers that implement `AsMut<[u8]>`, including
/// `Box<[u8]>`, and `Vec<u8>`. This will also support most other mutable bytes containers (including `bytes::BytesMut`),
/// though these buffers will need to be boxed to manage ownership of memory.
///
/// ```
/// // Vector of bytes
/// let backing_store = v8::ArrayBuffer::new_backing_store_from_bytes(vec![1, 2, 3]);
/// // Boxes slice of bytes
/// let boxed_slice: Box<[u8]> = vec![1, 2, 3].into_boxed_slice();
/// let backing_store = v8::ArrayBuffer::new_backing_store_from_bytes(boxed_slice);
/// // BytesMut from bytes crate
/// let backing_store = v8::ArrayBuffer::new_backing_store_from_bytes(Box::new(bytes::BytesMut::new()));
/// ```
#[inline(always)]
pub fn new_backing_store_from_bytes<T, U>(
mut bytes: T,
) -> UniqueRef<BackingStore>
where
U: ?Sized,
U: AsMut<[u8]>,
T: AsMut<U>,
T: sealed::Rawable<U>,
{
let len = bytes.as_mut().as_mut().len();
let slice = bytes.as_mut().as_mut().as_mut_ptr();
let ptr = T::into_raw(bytes);
extern "C" fn drop_rawable<T: sealed::Rawable<U>, U: ?Sized>(
_ptr: *mut c_void,
len: usize,
data: *mut c_void,
) {
// SAFETY: We know that data is a raw T from above
unsafe { <T as sealed::Rawable<U>>::drop_raw(data as _, len) }
}
// SAFETY: We are extending the lifetime of a slice, but we're locking away the box that we
// derefed from so there's no way to get another mutable reference.
unsafe {
UniqueRef::from_raw(v8__ArrayBuffer__NewBackingStore__with_data(
data_ptr,
byte_length,
vec_deleter_callback,
capacity as *mut c_void,
))
Self::new_backing_store_from_ptr(
slice as _,
len,
drop_rawable::<T, U>,
ptr as _,
)
}
}

View File

@ -866,6 +866,12 @@ two_pointers_t v8__ArrayBuffer__GetBackingStore(const v8::ArrayBuffer& self) {
return make_pod<two_pointers_t>(ptr_to_local(&self)->GetBackingStore());
}
v8::BackingStore* v8__BackingStore__EmptyBackingStore(bool shared) {
std::unique_ptr<i::BackingStoreBase> u =
i::BackingStore::EmptyBackingStore(shared ? i::SharedFlag::kShared : i::SharedFlag::kNotShared);
return static_cast<v8::BackingStore*>(u.release());
}
bool v8__BackingStore__IsResizableByUserJavaScript(
const v8::BackingStore& self) {
return ptr_to_local(&self)->IsResizableByUserJavaScript();

View File

@ -1,10 +1,7 @@
// Copyright 2019-2021 the Deno authors. All rights reserved. MIT license.
use std::ffi::c_void;
use std::ptr::null_mut;
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;
@ -38,6 +35,7 @@ extern "C" {
deleter: BackingStoreDeleterCallback,
deleter_data: *mut c_void,
) -> *mut BackingStore;
fn v8__BackingStore__EmptyBackingStore(shared: bool) -> *mut BackingStore;
}
impl SharedArrayBuffer {
@ -76,6 +74,17 @@ impl SharedArrayBuffer {
.unwrap()
}
/// Create a new, empty SharedArrayBuffer.
#[inline(always)]
pub fn empty<'s>(
scope: &mut HandleScope<'s>,
) -> Local<'s, SharedArrayBuffer> {
// SAFETY: This is a v8-provided empty backing store
let backing_store =
unsafe { UniqueRef::from_raw(v8__BackingStore__EmptyBackingStore(true)) };
Self::with_backing_store(scope, &backing_store.make_shared())
}
/// Data length in bytes.
#[inline(always)]
pub fn byte_length(&self) -> usize {
@ -124,16 +133,7 @@ impl SharedArrayBuffer {
pub fn new_backing_store_from_boxed_slice(
data: Box<[u8]>,
) -> UniqueRef<BackingStore> {
let byte_length = data.len();
let data_ptr = Box::into_raw(data) as *mut c_void;
unsafe {
UniqueRef::from_raw(v8__SharedArrayBuffer__NewBackingStore__with_data(
data_ptr,
byte_length,
boxed_slice_deleter_callback,
null_mut(),
))
}
Self::new_backing_store_from_bytes(data)
}
/// Returns a new standalone BackingStore that takes over the ownership of
@ -144,19 +144,83 @@ impl SharedArrayBuffer {
/// 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.
#[inline(always)]
pub fn new_backing_store_from_vec(
mut data: Vec<u8>,
) -> UniqueRef<BackingStore> {
let byte_length = data.len();
let data_ptr = data.as_mut_ptr() as *mut c_void;
std::mem::forget(data);
pub fn new_backing_store_from_vec(data: Vec<u8>) -> UniqueRef<BackingStore> {
Self::new_backing_store_from_bytes(data)
}
/// Returns a new standalone BackingStore backed by a container that dereferences
/// to a mutable slice of bytes. The object is dereferenced once, and the resulting slice's
/// memory is used for the lifetime of the buffer.
///
/// This method may be called with most single-ownership containers that implement `AsMut<[u8]>`, including
/// `Box<[u8]>`, and `Vec<u8>`. This will also support most other mutable bytes containers (including `bytes::BytesMut`),
/// though these buffers will need to be boxed to manage ownership of memory.
///
/// ```
/// // Vector of bytes
/// let backing_store = v8::ArrayBuffer::new_backing_store_from_bytes(vec![1, 2, 3]);
/// // Boxes slice of bytes
/// let boxed_slice: Box<[u8]> = vec![1, 2, 3].into_boxed_slice();
/// let backing_store = v8::ArrayBuffer::new_backing_store_from_bytes(boxed_slice);
/// // BytesMut from bytes crate
/// let backing_store = v8::ArrayBuffer::new_backing_store_from_bytes(Box::new(bytes::BytesMut::new()));
/// ```
#[inline(always)]
pub fn new_backing_store_from_bytes<T, U>(
mut bytes: T,
) -> UniqueRef<BackingStore>
where
U: ?Sized,
U: AsMut<[u8]>,
T: AsMut<U>,
T: crate::array_buffer::sealed::Rawable<U>,
{
let len = bytes.as_mut().as_mut().len();
let slice = bytes.as_mut().as_mut().as_mut_ptr();
let ptr = T::into_raw(bytes);
extern "C" fn drop_rawable<
T: crate::array_buffer::sealed::Rawable<U>,
U: ?Sized,
>(
_ptr: *mut c_void,
len: usize,
data: *mut c_void,
) {
// SAFETY: We know that data is a raw T from above
unsafe {
<T as crate::array_buffer::sealed::Rawable<U>>::drop_raw(data as _, len)
}
}
// SAFETY: We are extending the lifetime of a slice, but we're locking away the box that we
// derefed from so there's no way to get another mutable reference.
unsafe {
UniqueRef::from_raw(v8__SharedArrayBuffer__NewBackingStore__with_data(
data_ptr,
byte_length,
vec_deleter_callback,
null_mut(),
))
Self::new_backing_store_from_ptr(
slice as _,
len,
drop_rawable::<T, U>,
ptr as _,
)
}
}
/// Returns a new standalone shared BackingStore backed by given ptr.
///
/// SAFETY: This API consumes raw pointers so is inherently
/// unsafe. Usually you should use new_backing_store_from_boxed_slice.
#[inline(always)]
pub unsafe fn new_backing_store_from_ptr(
data_ptr: *mut c_void,
byte_length: usize,
deleter_callback: BackingStoreDeleterCallback,
deleter_data: *mut c_void,
) -> UniqueRef<BackingStore> {
UniqueRef::from_raw(v8__SharedArrayBuffer__NewBackingStore__with_data(
data_ptr,
byte_length,
deleter_callback,
deleter_data,
))
}
}

View File

@ -828,6 +828,24 @@ 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);
// Empty
let ab = v8::ArrayBuffer::empty(scope);
assert_eq!(0, ab.byte_length());
assert!(!ab.get_backing_store().is_shared());
// From a bytes::BytesMut
let mut data = bytes::BytesMut::new();
data.extend_from_slice(&[0; 16]);
data[0] = 1;
let unique_bs =
v8::ArrayBuffer::new_backing_store_from_bytes(Box::new(data));
assert_eq!(unique_bs.get(0).unwrap().get(), 1);
let ab =
v8::ArrayBuffer::with_backing_store(scope, &unique_bs.make_shared());
assert_eq!(ab.byte_length(), 16);
assert_eq!(ab.get_backing_store().get(0).unwrap().get(), 1);
}
}
@ -5607,6 +5625,11 @@ fn shared_array_buffer() {
assert_eq!(shared_bs_3.byte_length(), 10);
assert_eq!(shared_bs_3[0].get(), 0);
assert_eq!(shared_bs_3[9].get(), 9);
// Empty
let ab = v8::SharedArrayBuffer::empty(scope);
assert_eq!(ab.byte_length(), 0);
assert!(ab.get_backing_store().is_shared());
}
}