From 39631b31d60abb1f49bc916949640a9094d2b19d Mon Sep 17 00:00:00 2001 From: snek Date: Wed, 24 Jul 2024 07:59:18 -0700 Subject: [PATCH] feat: add String::ValueView (#1543) --- src/binding.cc | 27 +++++++++++++++++ src/binding.hpp | 2 ++ src/lib.rs | 2 ++ src/string.rs | 75 ++++++++++++++++++++++++++++++++++++++++++----- tests/test_api.rs | 31 ++++++++++++++++++++ 5 files changed, 130 insertions(+), 7 deletions(-) diff --git a/src/binding.cc b/src/binding.cc index 9ea16e55..e024954e 100644 --- a/src/binding.cc +++ b/src/binding.cc @@ -1197,6 +1197,33 @@ bool v8__String__ContainsOnlyOneByte(const v8::String& self) { return self.ContainsOnlyOneByte(); } +void v8__String__ValueView__CONSTRUCT(uninit_t* buf, + v8::Isolate* isolate, + const v8::String& string) { + construct_in_place(buf, isolate, + ptr_to_local(&string)); +} + +void v8__String__ValueView__DESTRUCT(v8::String::ValueView* self) { + self->~ValueView(); +} + +bool v8__String__ValueView__is_one_byte(const v8::String::ValueView& self) { + return self.is_one_byte(); +} + +const void* v8__String__ValueView__data(const v8::String::ValueView& self) { + if (self.is_one_byte()) { + return reinterpret_cast(self.data8()); + } else { + return reinterpret_cast(self.data16()); + } +} + +int v8__String__ValueView__length(const v8::String::ValueView& self) { + return self.length(); +} + const v8::Symbol* v8__Symbol__New(v8::Isolate* isolate, const v8::String* description) { return local_to_ptr(v8::Symbol::New(isolate, ptr_to_local(description))); diff --git a/src/binding.hpp b/src/binding.hpp index 31e16b09..2bdd308e 100644 --- a/src/binding.hpp +++ b/src/binding.hpp @@ -19,3 +19,5 @@ static size_t RUST_cppgc__WeakMember_SIZE = sizeof(cppgc::WeakMember); static size_t RUST_v8__TracedReference_SIZE = sizeof(v8::TracedReference); + +static size_t RUST_v8__String__ValueView_SIZE = sizeof(v8::String::ValueView); diff --git a/src/lib.rs b/src/lib.rs index bf66e06c..d1819b88 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -149,6 +149,8 @@ pub use snapshot::StartupData; pub use string::Encoding; pub use string::NewStringType; pub use string::OneByteConst; +pub use string::ValueView; +pub use string::ValueViewData; pub use string::WriteOptions; pub use support::SharedPtr; pub use support::SharedRef; diff --git a/src/string.rs b/src/string.rs index 1cd60e6c..08814437 100644 --- a/src/string.rs +++ b/src/string.rs @@ -1,10 +1,3 @@ -use std::borrow::Cow; -use std::convert::TryInto; -use std::default::Default; -use std::mem::MaybeUninit; -use std::ptr::NonNull; -use std::slice; - use crate::support::char; use crate::support::int; use crate::support::size_t; @@ -13,6 +6,14 @@ use crate::HandleScope; use crate::Isolate; use crate::Local; use crate::String; +use std::borrow::Cow; +use std::convert::TryInto; +use std::default::Default; +use std::ffi::c_void; +use std::marker::PhantomData; +use std::mem::MaybeUninit; +use std::ptr::NonNull; +use std::slice; extern "C" { fn v8__String__kMaxLength() -> size_t; @@ -116,6 +117,16 @@ extern "C" { fn v8__ExternalOneByteStringResource__length( this: *const ExternalOneByteStringResource, ) -> size_t; + + fn v8__String__ValueView__CONSTRUCT( + buf: *mut ValueView, + isolate: *mut Isolate, + string: *const String, + ); + fn v8__String__ValueView__DESTRUCT(this: *mut ValueView); + fn v8__String__ValueView__is_one_byte(this: *const ValueView) -> bool; + fn v8__String__ValueView__data(this: *const ValueView) -> *const c_void; + fn v8__String__ValueView__length(this: *const ValueView) -> int; } #[derive(PartialEq, Debug)] @@ -939,3 +950,53 @@ pub extern "C" fn free_rust_external_onebyte(s: *mut char, len: usize) { drop(Box::from_raw(slice)); } } + +#[derive(Debug, PartialEq)] +pub enum ValueViewData<'s> { + OneByte(&'s [u8]), + TwoByte(&'s [u16]), +} + +/// Returns a view onto a string's contents. +/// +/// WARNING: This does not copy the string's contents, and will therefore be +/// invalidated if the GC can move the string while the ValueView is alive. It +/// is therefore required that no GC or allocation can happen while there is an +/// active ValueView. This requirement may be relaxed in the future. +/// +/// V8 strings are either encoded as one-byte or two-bytes per character. +#[repr(C)] +pub struct ValueView<'s>( + [u8; crate::binding::RUST_v8__String__ValueView_SIZE], + PhantomData<&'s ()>, +); + +impl<'s> ValueView<'s> { + #[inline(always)] + pub fn new(isolate: &mut Isolate, string: Local<'s, String>) -> Self { + let mut v = std::mem::MaybeUninit::uninit(); + unsafe { + v8__String__ValueView__CONSTRUCT(v.as_mut_ptr(), isolate, &*string); + v.assume_init() + } + } + + #[inline(always)] + pub fn data(&self) -> ValueViewData<'_> { + unsafe { + let data = v8__String__ValueView__data(self); + let length = v8__String__ValueView__length(self) as usize; + if v8__String__ValueView__is_one_byte(self) { + ValueViewData::OneByte(std::slice::from_raw_parts(data as _, length)) + } else { + ValueViewData::TwoByte(std::slice::from_raw_parts(data as _, length)) + } + } + } +} + +impl<'s> Drop for ValueView<'s> { + fn drop(&mut self) { + unsafe { v8__String__ValueView__DESTRUCT(self) } + } +} diff --git a/tests/test_api.rs b/tests/test_api.rs index d0fb787f..b63b436d 100644 --- a/tests/test_api.rs +++ b/tests/test_api.rs @@ -11779,3 +11779,34 @@ fn clear_slots_annex_uninitialized() { // initialized. context.clear_all_slots(&mut scope); } + +#[test] +fn string_valueview() { + let _setup_guard = setup::parallel_test(); + let mut isolate = v8::Isolate::new(Default::default()); + let mut scope = v8::HandleScope::new(&mut isolate); + let context = v8::Context::new(&mut scope); + let scope = &mut v8::ContextScope::new(&mut scope, context); + + { + let one_byte = v8::String::new_from_one_byte( + scope, + &[1, 2, 3], + v8::NewStringType::Normal, + ) + .unwrap(); + let view = v8::ValueView::new(scope, one_byte); + assert_eq!(view.data(), v8::ValueViewData::OneByte(&[1, 2, 3])); + } + + { + let two_byte = v8::String::new_from_two_byte( + scope, + &[1, 0x1FF, 3], + v8::NewStringType::Normal, + ) + .unwrap(); + let view = v8::ValueView::new(scope, two_byte); + assert_eq!(view.data(), v8::ValueViewData::TwoByte(&[1, 0x1FF, 3])); + } +}