From be545b7d3d1947b08cf42818b4eed2456845789a Mon Sep 17 00:00:00 2001 From: Matt Mastracci Date: Fri, 1 Mar 2024 10:55:02 -0700 Subject: [PATCH] feat: Add string access methods to OneByteConst (#1409) --- src/string.rs | 55 ++++++++++++++++++++++++++++++++--------------- tests/test_api.rs | 1 + 2 files changed, 39 insertions(+), 17 deletions(-) diff --git a/src/string.rs b/src/string.rs index 5ecb85a4..e8baabe3 100644 --- a/src/string.rs +++ b/src/string.rs @@ -119,13 +119,34 @@ pub struct ExternalStringResource(Opaque); pub struct ExternalOneByteStringResourceBase(Opaque); #[repr(C)] -#[derive(Debug)] +#[derive(Copy, Clone, Debug)] pub struct OneByteConst { vtable: *const OneByteConstNoOp, cached_data: *const char, length: usize, } +impl AsRef for OneByteConst { + fn as_ref(&self) -> &str { + // SAFETY: We know this is ASCII + unsafe { std::str::from_utf8_unchecked(AsRef::<[u8]>::as_ref(self)) } + } +} + +impl AsRef<[u8]> for OneByteConst { + fn as_ref(&self) -> &[u8] { + // SAFETY: Returning to the slice from which this came + unsafe { std::slice::from_raw_parts(self.cached_data as _, self.length) } + } +} + +impl std::ops::Deref for OneByteConst { + type Target = str; + fn deref(&self) -> &Self::Target { + self.as_ref() + } +} + // SAFETY: The vtable for OneByteConst is an immutable static and all // of the included functions are thread-safe, the cached_data pointer // is never changed and points to a static ASCII string, and the @@ -201,19 +222,6 @@ const ONE_BYTE_CONST_VTABLE: OneByteConstVtable = OneByteConstVtable { length: one_byte_const_length, }; -/// Compile-time function to determine if a string is ASCII. Note that UTF-8 chars -/// longer than one byte have the high-bit set and thus, are not ASCII. -const fn is_ascii(s: &'static [u8]) -> bool { - let mut i = 0; - while i < s.len() { - if !s[i].is_ascii() { - return false; - } - i += 1; - } - true -} - #[repr(C)] #[derive(Debug, Default)] pub enum NewStringType { @@ -456,15 +464,28 @@ impl String { Self::new_from_utf8(scope, value.as_ref(), NewStringType::Normal) } - // Compile-time function to create an external string resource. - // The buffer is checked to contain only ASCII characters. + /// Compile-time function to create an external string resource. + /// The buffer is checked to contain only ASCII characters. #[inline(always)] pub const fn create_external_onebyte_const( buffer: &'static [u8], ) -> OneByteConst { // Assert that the buffer contains only ASCII, and that the // length is less or equal to (64-bit) v8::String::kMaxLength. - assert!(is_ascii(buffer) && buffer.len() <= ((1 << 29) - 24)); + assert!(buffer.is_ascii() && buffer.len() <= ((1 << 29) - 24)); + OneByteConst { + vtable: &ONE_BYTE_CONST_VTABLE.delete1, + cached_data: buffer.as_ptr() as *const char, + length: buffer.len(), + } + } + + /// Compile-time function to create an external string resource which + /// skips the ASCII and length checks. + #[inline(always)] + pub const unsafe fn create_external_onebyte_const_unchecked( + buffer: &'static [u8], + ) -> OneByteConst { OneByteConst { vtable: &ONE_BYTE_CONST_VTABLE.delete1, cached_data: buffer.as_ptr() as *const char, diff --git a/tests/test_api.rs b/tests/test_api.rs index 13746701..a17ee1b7 100644 --- a/tests/test_api.rs +++ b/tests/test_api.rs @@ -9000,6 +9000,7 @@ fn external_strings() { assert!(latin1.contains_only_onebyte()); // one-byte "const" test + assert_eq!(EXAMPLE_STRING.as_bytes(), b"const static"); let const_ref_string = v8::String::new_from_onebyte_const(scope, &EXAMPLE_STRING).unwrap(); assert!(const_ref_string.is_external());