use crate::Isolate;
use crate::Local;
use crate::String;
use crate::binding::v8__String__kMaxLength;
use crate::isolate::RealIsolate;
use crate::scope::PinScope;
use crate::support::Opaque;
use crate::support::char;
use crate::support::int;
use crate::support::size_t;
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;
#[inline(always)]
pub unsafe fn latin1_to_utf8(
input_length: usize,
inbuf: *const u8,
outbuf: *mut u8,
) -> usize {
unsafe {
let mut output = 0;
let mut input = 0;
while input + 8 <= input_length {
let chunk = (inbuf.add(input) as *const u64).read_unaligned();
if chunk & 0x8080_8080_8080_8080 == 0 {
(outbuf.add(output) as *mut u64).write_unaligned(chunk);
input += 8;
output += 8;
} else {
let end = input + 8;
while input < end {
let byte = *(inbuf.add(input));
if byte < 0x80 {
*(outbuf.add(output)) = byte;
output += 1;
} else {
*(outbuf.add(output)) = (byte >> 6) | 0b1100_0000;
*(outbuf.add(output + 1)) = (byte & 0b0011_1111) | 0b1000_0000;
output += 2;
}
input += 1;
}
}
}
while input < input_length {
let byte = *(inbuf.add(input));
if byte < 0x80 {
*(outbuf.add(output)) = byte;
output += 1;
} else {
*(outbuf.add(output)) = (byte >> 6) | 0b1100_0000;
*(outbuf.add(output + 1)) = (byte & 0b0011_1111) | 0b1000_0000;
output += 2;
}
input += 1;
}
output
}
}
unsafe extern "C" {
fn v8__String__Empty(isolate: *mut RealIsolate) -> *const String;
fn v8__String__NewFromUtf8(
isolate: *mut RealIsolate,
data: *const char,
new_type: NewStringType,
length: int,
) -> *const String;
fn v8__String__NewFromOneByte(
isolate: *mut RealIsolate,
data: *const u8,
new_type: NewStringType,
length: int,
) -> *const String;
fn v8__String__NewFromTwoByte(
isolate: *mut RealIsolate,
data: *const u16,
new_type: NewStringType,
length: int,
) -> *const String;
fn v8__String__Length(this: *const String) -> int;
fn v8__String__Utf8Length(
this: *const String,
isolate: *mut RealIsolate,
) -> int;
fn v8__String__Write_v2(
this: *const String,
isolate: *mut RealIsolate,
offset: u32,
length: u32,
buffer: *mut u16,
flags: int,
);
fn v8__String__WriteOneByte_v2(
this: *const String,
isolate: *mut RealIsolate,
offset: u32,
length: u32,
buffer: *mut u8,
flags: int,
);
fn v8__String__WriteUtf8_v2(
this: *const String,
isolate: *mut RealIsolate,
buffer: *mut char,
capacity: size_t,
flags: int,
processed_characters_return: *mut size_t,
) -> int;
fn v8__String__GetExternalStringResource(
this: *const String,
) -> *mut ExternalStringResource;
fn v8__String__GetExternalStringResourceBase(
this: *const String,
encoding: *mut Encoding,
) -> *mut ExternalStringResourceBase;
fn v8__String__NewExternalOneByteConst(
isolate: *mut RealIsolate,
onebyte_const: *const OneByteConst,
) -> *const String;
fn v8__String__NewExternalOneByteStatic(
isolate: *mut RealIsolate,
buffer: *const char,
length: int,
) -> *const String;
fn v8__String__NewExternalOneByte(
isolate: *mut RealIsolate,
buffer: *mut char,
length: size_t,
free: unsafe extern "C" fn(*mut char, size_t),
) -> *const String;
fn v8__String__NewExternalTwoByteStatic(
isolate: *mut RealIsolate,
buffer: *const u16,
length: int,
) -> *const String;
fn v8__String__NewExternalTwoByte(
isolate: *mut RealIsolate,
buffer: *mut u16,
length: size_t,
free: unsafe extern "C" fn(*mut u16, size_t),
) -> *const String;
#[allow(dead_code)]
fn v8__String__IsExternal(this: *const String) -> bool;
fn v8__String__IsExternalOneByte(this: *const String) -> bool;
fn v8__String__IsExternalTwoByte(this: *const String) -> bool;
#[allow(dead_code)]
fn v8__String__IsOneByte(this: *const String) -> bool;
fn v8__String__ContainsOnlyOneByte(this: *const String) -> bool;
fn v8__ExternalOneByteStringResource__data(
this: *const ExternalOneByteStringResource,
) -> *const char;
fn v8__ExternalOneByteStringResource__length(
this: *const ExternalOneByteStringResource,
) -> size_t;
fn v8__String__ValueView__CONSTRUCT(
buf: *mut ValueView,
isolate: *mut RealIsolate,
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)]
#[repr(C)]
pub enum Encoding {
Unknown = 0x1,
TwoByte = 0x2,
OneByte = 0x8,
}
#[repr(C)]
pub struct ExternalStringResource(Opaque);
#[repr(C)]
pub struct ExternalStringResourceBase(Opaque);
#[repr(C)]
pub struct ExternalOneByteStringResource(Opaque);
impl ExternalOneByteStringResource {
#[inline]
pub fn data(&self) -> *const char {
unsafe { v8__ExternalOneByteStringResource__data(self) }
}
#[inline]
pub fn length(&self) -> usize {
unsafe { v8__ExternalOneByteStringResource__length(self) }
}
#[inline]
pub fn as_bytes(&self) -> &[u8] {
let len = self.length();
if len == 0 {
&[]
} else {
unsafe { std::slice::from_raw_parts(self.data().cast(), len) }
}
}
}
#[repr(C)]
#[derive(Copy, Clone, Debug)]
pub struct OneByteConst {
vtable: *const OneByteConstNoOp,
cached_data: *const char,
length: usize,
}
impl OneByteConst {
#[inline(always)]
pub const fn as_str(&self) -> &str {
if self.length == 0 {
""
} else {
unsafe {
std::str::from_utf8_unchecked(std::slice::from_raw_parts(
self.cached_data as _,
self.length,
))
}
}
}
}
impl AsRef<str> for OneByteConst {
#[inline(always)]
fn as_ref(&self) -> &str {
self.as_str()
}
}
impl AsRef<[u8]> for OneByteConst {
#[inline(always)]
fn as_ref(&self) -> &[u8] {
self.as_str().as_bytes()
}
}
impl std::ops::Deref for OneByteConst {
type Target = str;
#[inline(always)]
fn deref(&self) -> &Self::Target {
self.as_ref()
}
}
unsafe impl Sync for OneByteConst {}
unsafe extern "C" fn one_byte_const_no_op(_this: *const OneByteConst) {}
unsafe extern "C" fn one_byte_const_is_cacheable(
_this: *const OneByteConst,
) -> bool {
true
}
unsafe extern "C" fn one_byte_const_data(
this: *const OneByteConst,
) -> *const char {
unsafe { (*this).cached_data }
}
unsafe extern "C" fn one_byte_const_length(this: *const OneByteConst) -> usize {
unsafe { (*this).length }
}
unsafe extern "C" fn one_byte_const_unaccount(
_this: *const OneByteConst,
_isolate: *mut RealIsolate,
) {
}
unsafe extern "C" fn one_byte_const_estimate_memory_usage(
_this: *const OneByteConst,
) -> size_t {
usize::MAX }
unsafe extern "C" fn one_byte_const_estimate_shared_memory_usage(
_this: *const OneByteConst,
_recorder: *mut (),
) {
}
type OneByteConstNoOp = unsafe extern "C" fn(*const OneByteConst);
type OneByteConstIsCacheable =
unsafe extern "C" fn(*const OneByteConst) -> bool;
type OneByteConstData =
unsafe extern "C" fn(*const OneByteConst) -> *const char;
type OneByteConstLength = unsafe extern "C" fn(*const OneByteConst) -> usize;
type OneByteConstUnaccount =
unsafe extern "C" fn(*const OneByteConst, *mut RealIsolate);
type OneByteConstEstimateMemoryUsage =
unsafe extern "C" fn(*const OneByteConst) -> size_t;
type OneByteConstEstimateSharedMemoryUsage =
unsafe extern "C" fn(*const OneByteConst, *mut ());
#[repr(C)]
struct OneByteConstVtable {
#[cfg(target_family = "windows")]
_offset_to_top: usize,
_typeinfo: *const (),
delete1: OneByteConstNoOp,
#[cfg(not(target_family = "windows"))]
delete2: OneByteConstNoOp,
is_cacheable: OneByteConstIsCacheable,
unaccount: OneByteConstUnaccount,
estimate_memory_usage: OneByteConstEstimateMemoryUsage,
estimate_shared_memory_usage: OneByteConstEstimateSharedMemoryUsage,
dispose: OneByteConstNoOp,
lock: OneByteConstNoOp,
unlock: OneByteConstNoOp,
data: OneByteConstData,
length: OneByteConstLength,
}
const ONE_BYTE_CONST_VTABLE: OneByteConstVtable = OneByteConstVtable {
#[cfg(target_family = "windows")]
_offset_to_top: 0,
_typeinfo: std::ptr::null(),
delete1: one_byte_const_no_op,
#[cfg(not(target_family = "windows"))]
delete2: one_byte_const_no_op,
is_cacheable: one_byte_const_is_cacheable,
unaccount: one_byte_const_unaccount,
estimate_memory_usage: one_byte_const_estimate_memory_usage,
estimate_shared_memory_usage: one_byte_const_estimate_shared_memory_usage,
dispose: one_byte_const_no_op,
lock: one_byte_const_no_op,
unlock: one_byte_const_no_op,
data: one_byte_const_data,
length: one_byte_const_length,
};
#[repr(C)]
#[derive(Debug, Default)]
pub enum NewStringType {
#[default]
Normal,
Internalized,
}
bitflags! {
#[derive(Clone, Copy, Default)]
#[repr(transparent)]
pub struct WriteOptions: int {
const NO_OPTIONS = 0;
const HINT_MANY_WRITES_EXPECTED = 1;
const NO_NULL_TERMINATION = 2;
const PRESERVE_ONE_BYTE_NULL = 4;
const REPLACE_INVALID_UTF8 = 8;
}
}
bitflags! {
#[derive(Clone, Copy, Default)]
#[repr(transparent)]
pub struct WriteFlags: int {
const kNullTerminate = crate::binding::v8_String_WriteFlags_kNullTerminate as _;
const kReplaceInvalidUtf8 = crate::binding::v8_String_WriteFlags_kReplaceInvalidUtf8 as _;
}
}
impl String {
pub const MAX_LENGTH: usize = v8__String__kMaxLength as _;
#[inline(always)]
pub fn empty<'s>(scope: &PinScope<'s, '_, ()>) -> Local<'s, String> {
unsafe { scope.cast_local(|sd| v8__String__Empty(sd.get_isolate_ptr())) }
.unwrap()
}
#[inline(always)]
pub fn new_from_utf8<'s>(
scope: &PinScope<'s, '_, ()>,
buffer: &[u8],
new_type: NewStringType,
) -> Option<Local<'s, String>> {
if buffer.is_empty() {
return Some(Self::empty(scope));
}
let buffer_len = buffer.len().try_into().ok()?;
unsafe {
scope.cast_local(|sd| {
v8__String__NewFromUtf8(
sd.get_isolate_ptr(),
buffer.as_ptr() as *const char,
new_type,
buffer_len,
)
})
}
}
#[inline(always)]
pub fn new_from_one_byte<'s>(
scope: &PinScope<'s, '_, ()>,
buffer: &[u8],
new_type: NewStringType,
) -> Option<Local<'s, String>> {
let buffer_len = buffer.len().try_into().ok()?;
unsafe {
scope.cast_local(|sd| {
v8__String__NewFromOneByte(
sd.get_isolate_ptr(),
buffer.as_ptr(),
new_type,
buffer_len,
)
})
}
}
#[inline(always)]
pub fn new_from_two_byte<'s>(
scope: &PinScope<'s, '_, ()>,
buffer: &[u16],
new_type: NewStringType,
) -> Option<Local<'s, String>> {
let buffer_len = buffer.len().try_into().ok()?;
unsafe {
scope.cast_local(|sd| {
v8__String__NewFromTwoByte(
sd.get_isolate_ptr(),
buffer.as_ptr(),
new_type,
buffer_len,
)
})
}
}
#[inline(always)]
pub fn length(&self) -> usize {
unsafe { v8__String__Length(self) as usize }
}
#[inline(always)]
pub fn utf8_length(&self, scope: &Isolate) -> usize {
unsafe { v8__String__Utf8Length(self, scope.as_real_ptr()) as usize }
}
#[inline(always)]
pub fn write_v2(
&self,
scope: &Isolate,
offset: u32,
buffer: &mut [u16],
flags: WriteFlags,
) {
unsafe {
v8__String__Write_v2(
self,
scope.as_real_ptr(),
offset,
self.length().min(buffer.len()) as _,
buffer.as_mut_ptr(),
flags.bits(),
)
}
}
#[inline(always)]
pub fn write_one_byte_v2(
&self,
scope: &Isolate,
offset: u32,
buffer: &mut [u8],
flags: WriteFlags,
) {
unsafe {
v8__String__WriteOneByte_v2(
self,
scope.as_real_ptr(),
offset,
self.length().min(buffer.len()) as _,
buffer.as_mut_ptr(),
flags.bits(),
)
}
}
#[inline(always)]
pub fn write_one_byte_uninit_v2(
&self,
scope: &Isolate,
offset: u32,
buffer: &mut [MaybeUninit<u8>],
flags: WriteFlags,
) {
unsafe {
v8__String__WriteOneByte_v2(
self,
scope.as_real_ptr(),
offset,
self.length().min(buffer.len()) as _,
buffer.as_mut_ptr() as _,
flags.bits(),
)
}
}
#[inline(always)]
pub fn write_utf8_v2(
&self,
scope: &Isolate,
buffer: &mut [u8],
flags: WriteFlags,
processed_characters_return: Option<&mut usize>,
) -> usize {
unsafe {
let buffer = {
let len = buffer.len();
let data = buffer.as_mut_ptr().cast();
slice::from_raw_parts_mut(data, len)
};
self.write_utf8_uninit_v2(
scope,
buffer,
flags,
processed_characters_return,
)
}
}
pub fn write_utf8_uninit_v2(
&self,
scope: &Isolate,
buffer: &mut [MaybeUninit<u8>],
flags: WriteFlags,
processed_characters_return: Option<&mut usize>,
) -> usize {
let bytes = unsafe {
v8__String__WriteUtf8_v2(
self,
scope.as_real_ptr(),
buffer.as_mut_ptr() as _,
buffer.len(),
flags.bits(),
processed_characters_return
.map(|p| p as *mut _)
.unwrap_or(std::ptr::null_mut()),
)
};
bytes as usize
}
#[inline(always)]
pub fn new<'s>(
scope: &PinScope<'s, '_, ()>,
value: &str,
) -> Option<Local<'s, String>> {
Self::new_from_utf8(scope, value.as_ref(), NewStringType::Normal)
}
#[inline(always)]
pub const fn create_external_onebyte_const(
buffer: &'static [u8],
) -> OneByteConst {
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(),
}
}
#[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,
length: buffer.len(),
}
}
#[inline(always)]
pub fn new_from_onebyte_const<'s>(
scope: &PinScope<'s, '_, ()>,
onebyte_const: &'static OneByteConst,
) -> Option<Local<'s, String>> {
unsafe {
scope.cast_local(|sd| {
v8__String__NewExternalOneByteConst(sd.get_isolate_ptr(), onebyte_const)
})
}
}
#[inline(always)]
pub fn new_external_onebyte_static<'s>(
scope: &PinScope<'s, '_, ()>,
buffer: &'static [u8],
) -> Option<Local<'s, String>> {
let buffer_len = buffer.len().try_into().ok()?;
unsafe {
scope.cast_local(|sd| {
v8__String__NewExternalOneByteStatic(
sd.get_isolate_ptr(),
buffer.as_ptr() as *const char,
buffer_len,
)
})
}
}
#[inline(always)]
pub fn new_external_onebyte<'s>(
scope: &PinScope<'s, '_, ()>,
buffer: Box<[u8]>,
) -> Option<Local<'s, String>> {
let buffer_len = buffer.len();
unsafe {
scope.cast_local(|sd| {
v8__String__NewExternalOneByte(
sd.get_isolate_ptr(),
Box::into_raw(buffer).cast::<char>(),
buffer_len,
free_rust_external_onebyte,
)
})
}
}
#[inline(always)]
pub unsafe fn new_external_onebyte_raw<'s>(
scope: &PinScope<'s, '_, ()>,
buffer: *mut char,
buffer_len: usize,
destructor: unsafe extern "C" fn(*mut char, usize),
) -> Option<Local<'s, String>> {
unsafe {
scope.cast_local(|sd| {
v8__String__NewExternalOneByte(
sd.get_isolate_ptr(),
buffer,
buffer_len,
destructor,
)
})
}
}
#[inline(always)]
pub fn new_external_twobyte_static<'s>(
scope: &PinScope<'s, '_, ()>,
buffer: &'static [u16],
) -> Option<Local<'s, String>> {
let buffer_len = buffer.len().try_into().ok()?;
unsafe {
scope.cast_local(|sd| {
v8__String__NewExternalTwoByteStatic(
sd.get_isolate_ptr(),
buffer.as_ptr(),
buffer_len,
)
})
}
}
#[inline(always)]
pub fn new_external_twobyte<'s>(
scope: &PinScope<'s, '_, ()>,
buffer: Box<[u16]>,
) -> Option<Local<'s, String>> {
let buffer_len = buffer.len();
unsafe {
scope.cast_local(|sd| {
v8__String__NewExternalTwoByte(
sd.get_isolate_ptr(),
Box::into_raw(buffer).cast::<u16>(),
buffer_len,
free_rust_external_twobyte,
)
})
}
}
#[inline(always)]
pub unsafe fn new_external_twobyte_raw<'s>(
scope: &PinScope<'s, '_, ()>,
buffer: *mut u16,
buffer_len: usize,
destructor: unsafe extern "C" fn(*mut u16, usize),
) -> Option<Local<'s, String>> {
unsafe {
scope.cast_local(|sd| {
v8__String__NewExternalTwoByte(
sd.get_isolate_ptr(),
buffer,
buffer_len,
destructor,
)
})
}
}
#[inline]
pub fn get_external_string_resource(
&self,
) -> Option<NonNull<ExternalStringResource>> {
NonNull::new(unsafe { v8__String__GetExternalStringResource(self) })
}
#[inline]
pub fn get_external_onebyte_string_resource(
&self,
) -> Option<NonNull<ExternalOneByteStringResource>> {
let (base, encoding) = self.get_external_string_resource_base();
let base = base?;
if encoding != Encoding::OneByte {
return None;
}
Some(base.cast())
}
pub fn get_external_string_resource_base(
&self,
) -> (Option<NonNull<ExternalStringResourceBase>>, Encoding) {
let mut encoding = Encoding::Unknown;
(
NonNull::new(unsafe {
v8__String__GetExternalStringResourceBase(self, &mut encoding)
}),
encoding,
)
}
#[inline(always)]
pub fn is_external(&self) -> bool {
self.is_external_onebyte() || self.is_external_twobyte()
}
#[inline(always)]
pub fn is_external_onebyte(&self) -> bool {
unsafe { v8__String__IsExternalOneByte(self) }
}
#[inline(always)]
pub fn is_external_twobyte(&self) -> bool {
unsafe { v8__String__IsExternalTwoByte(self) }
}
#[inline(always)]
pub fn is_onebyte(&self) -> bool {
unsafe { v8__String__IsOneByte(self) }
}
#[inline(always)]
pub fn contains_only_onebyte(&self) -> bool {
unsafe { v8__String__ContainsOnlyOneByte(self) }
}
pub fn to_rust_string_lossy(&self, scope: &Isolate) -> std::string::String {
if self.length() == 0 {
return std::string::String::new();
}
let view = unsafe { ValueView::new_from_ref(scope, self) };
match view.data() {
ValueViewData::OneByte(bytes) => {
if bytes.is_ascii() {
unsafe { std::str::from_utf8_unchecked(bytes) }.to_owned()
} else {
latin1_to_string(bytes)
}
}
ValueViewData::TwoByte(units) => wtf16_to_string(units),
}
}
pub fn write_utf8_into(
&self,
scope: &mut Isolate,
buf: &mut std::string::String,
) {
buf.clear();
let len = self.length();
if len == 0 {
return;
}
let view = unsafe { ValueView::new_from_ref(scope, self) };
match view.data() {
ValueViewData::OneByte(bytes) => {
if bytes.is_ascii() {
buf.reserve(bytes.len());
unsafe {
let vec = buf.as_mut_vec();
std::ptr::copy_nonoverlapping(
bytes.as_ptr(),
vec.as_mut_ptr(),
bytes.len(),
);
vec.set_len(bytes.len());
}
} else {
let max_utf8_len = bytes.len() * 2;
buf.reserve(max_utf8_len);
unsafe {
let vec = buf.as_mut_vec();
let written =
latin1_to_utf8(bytes.len(), bytes.as_ptr(), vec.as_mut_ptr());
vec.set_len(written);
}
}
}
ValueViewData::TwoByte(units) => {
wtf16_into_string(units, buf);
}
}
}
pub fn to_rust_cow_lossy<'a, const N: usize>(
&self,
scope: &mut Isolate,
buffer: &'a mut [MaybeUninit<u8>; N],
) -> Cow<'a, str> {
let len = self.length();
if len == 0 {
return "".into();
}
let view = unsafe { ValueView::new_from_ref(scope, self) };
match view.data() {
ValueViewData::OneByte(bytes) => {
if bytes.is_ascii() {
if bytes.len() <= N {
unsafe {
std::ptr::copy_nonoverlapping(
bytes.as_ptr(),
buffer.as_mut_ptr() as *mut u8,
bytes.len(),
);
let buf = &mut buffer[..bytes.len()];
let buf = &mut *(buf as *mut [_] as *mut [u8]);
Cow::Borrowed(std::str::from_utf8_unchecked(buf))
}
} else {
unsafe {
Cow::Owned(std::string::String::from_utf8_unchecked(
bytes.to_vec(),
))
}
}
} else {
latin1_to_cow_str(bytes, buffer)
}
}
ValueViewData::TwoByte(units) => wtf16_to_cow_str(units, buffer),
}
}
}
#[inline]
pub unsafe extern "C" fn free_rust_external_onebyte(s: *mut char, len: usize) {
unsafe {
let slice = std::slice::from_raw_parts_mut(s, len);
drop(Box::from_raw(slice));
}
}
#[inline]
pub unsafe extern "C" fn free_rust_external_twobyte(s: *mut u16, len: usize) {
unsafe {
let slice = std::slice::from_raw_parts_mut(s, len);
drop(Box::from_raw(slice));
}
}
#[derive(Debug, PartialEq)]
pub enum ValueViewData<'s> {
OneByte(&'s [u8]),
TwoByte(&'s [u16]),
}
#[repr(C)]
pub struct ValueView<'s>(
[u8; crate::binding::v8__String__ValueView_SIZE],
PhantomData<&'s ()>,
);
impl<'s> ValueView<'s> {
#[inline(always)]
pub fn new(isolate: &mut Isolate, string: Local<'s, String>) -> Self {
let string_ref: &'s String = unsafe { &*((&*string) as *const String) };
unsafe { Self::new_from_ref(isolate, string_ref) }
}
#[inline(always)]
pub(crate) unsafe fn new_from_ref(
isolate: &Isolate,
string: &'s String,
) -> Self {
let mut v = std::mem::MaybeUninit::uninit();
unsafe {
v8__String__ValueView__CONSTRUCT(
v.as_mut_ptr(),
isolate.as_real_ptr(),
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))
}
}
}
#[inline(always)]
pub fn as_str(&self) -> Option<&str> {
match self.data() {
ValueViewData::OneByte(bytes) => {
if bytes.is_ascii() {
Some(unsafe { std::str::from_utf8_unchecked(bytes) })
} else {
None
}
}
ValueViewData::TwoByte(_) => None,
}
}
#[inline(always)]
pub fn to_cow_lossy(&self) -> Cow<'_, str> {
match self.data() {
ValueViewData::OneByte(bytes) => {
if bytes.is_ascii() {
Cow::Borrowed(unsafe { std::str::from_utf8_unchecked(bytes) })
} else {
Cow::Owned(latin1_to_string(bytes))
}
}
ValueViewData::TwoByte(units) => Cow::Owned(wtf16_to_string(units)),
}
}
}
#[cfg(feature = "simdutf")]
const WTF16_SIMD_THRESHOLD: usize = 96;
#[inline(always)]
fn latin1_to_string(bytes: &[u8]) -> std::string::String {
debug_assert!(!bytes.is_ascii());
#[cfg(feature = "simdutf")]
{
let utf8_len = crate::simdutf::utf8_length_from_latin1(bytes);
let mut buf: Vec<u8> = Vec::with_capacity(utf8_len);
unsafe {
let out = std::slice::from_raw_parts_mut(buf.as_mut_ptr(), utf8_len);
let written = crate::simdutf::convert_latin1_to_utf8(bytes, out);
debug_assert_eq!(written, utf8_len);
buf.set_len(written);
std::string::String::from_utf8_unchecked(buf)
}
}
#[cfg(not(feature = "simdutf"))]
{
let max_utf8_len = bytes.len() * 2;
let mut buf: Vec<u8> = Vec::with_capacity(max_utf8_len);
unsafe {
let written =
latin1_to_utf8(bytes.len(), bytes.as_ptr(), buf.as_mut_ptr());
buf.set_len(written);
std::string::String::from_utf8_unchecked(buf)
}
}
}
#[inline(always)]
fn wtf16_to_string(units: &[u16]) -> std::string::String {
#[cfg(feature = "simdutf")]
{
if units.len() >= WTF16_SIMD_THRESHOLD
&& crate::simdutf::validate_utf16le(units)
{
let utf8_len = crate::simdutf::utf8_length_from_utf16le(units);
let mut buf: Vec<u8> = Vec::with_capacity(utf8_len);
unsafe {
let out = std::slice::from_raw_parts_mut(buf.as_mut_ptr(), utf8_len);
let written = crate::simdutf::convert_utf16le_to_utf8(units, out);
debug_assert_eq!(written, utf8_len);
buf.set_len(written);
return std::string::String::from_utf8_unchecked(buf);
}
}
}
let mut buf = std::string::String::with_capacity(units.len() * 3);
for result in std::char::decode_utf16(units.iter().copied()) {
buf.push(result.unwrap_or('\u{FFFD}'));
}
buf
}
#[inline(always)]
fn wtf16_into_string(units: &[u16], buf: &mut std::string::String) {
#[cfg(feature = "simdutf")]
{
if units.len() >= WTF16_SIMD_THRESHOLD
&& crate::simdutf::validate_utf16le(units)
{
let utf8_len = crate::simdutf::utf8_length_from_utf16le(units);
buf.reserve(utf8_len);
unsafe {
let vec = buf.as_mut_vec();
let out = std::slice::from_raw_parts_mut(vec.as_mut_ptr(), utf8_len);
let written = crate::simdutf::convert_utf16le_to_utf8(units, out);
debug_assert_eq!(written, utf8_len);
vec.set_len(written);
}
return;
}
}
buf.reserve(units.len() * 3);
for result in std::char::decode_utf16(units.iter().copied()) {
buf.push(result.unwrap_or('\u{FFFD}'));
}
}
#[inline(always)]
fn latin1_to_cow_str<'a, const N: usize>(
bytes: &[u8],
buffer: &'a mut [MaybeUninit<u8>; N],
) -> Cow<'a, str> {
#[cfg(feature = "simdutf")]
let utf8_len = crate::simdutf::utf8_length_from_latin1(bytes);
#[cfg(not(feature = "simdutf"))]
let utf8_len = bytes.len() * 2;
if utf8_len <= N {
#[cfg(feature = "simdutf")]
let written = unsafe {
let out = std::slice::from_raw_parts_mut(
buffer.as_mut_ptr() as *mut u8,
utf8_len,
);
crate::simdutf::convert_latin1_to_utf8(bytes, out)
};
#[cfg(not(feature = "simdutf"))]
let written = unsafe {
latin1_to_utf8(
bytes.len(),
bytes.as_ptr(),
buffer.as_mut_ptr() as *mut u8,
)
};
unsafe {
let buf = &mut buffer[..written];
let buf = &mut *(buf as *mut [_] as *mut [u8]);
Cow::Borrowed(std::str::from_utf8_unchecked(buf))
}
} else {
Cow::Owned(latin1_to_string(bytes))
}
}
#[inline(always)]
fn wtf16_to_cow_str<'a, const N: usize>(
units: &[u16],
buffer: &'a mut [MaybeUninit<u8>; N],
) -> Cow<'a, str> {
#[cfg(feature = "simdutf")]
{
if units.len() >= WTF16_SIMD_THRESHOLD
&& crate::simdutf::validate_utf16le(units)
{
let utf8_len = crate::simdutf::utf8_length_from_utf16le(units);
if utf8_len <= N {
let written = unsafe {
let out = std::slice::from_raw_parts_mut(
buffer.as_mut_ptr() as *mut u8,
utf8_len,
);
crate::simdutf::convert_utf16le_to_utf8(units, out)
};
return unsafe {
let buf = &mut buffer[..written];
let buf = &mut *(buf as *mut [_] as *mut [u8]);
Cow::Borrowed(std::str::from_utf8_unchecked(buf))
};
}
return Cow::Owned(wtf16_to_string(units));
}
}
let mut pos = 0;
let mut tmp = [0u8; 4];
let mut all_fit = true;
for result in std::char::decode_utf16(units.iter().copied()) {
let c = result.unwrap_or('\u{FFFD}');
let encoded = c.encode_utf8(&mut tmp);
if pos + encoded.len() > N {
all_fit = false;
break;
}
unsafe {
std::ptr::copy_nonoverlapping(
encoded.as_ptr(),
(buffer.as_mut_ptr() as *mut u8).add(pos),
encoded.len(),
);
}
pos += encoded.len();
}
if all_fit {
unsafe {
let buf = &mut buffer[..pos];
let buf = &mut *(buf as *mut [_] as *mut [u8]);
Cow::Borrowed(std::str::from_utf8_unchecked(buf))
}
} else {
Cow::Owned(std::string::String::from_utf16_lossy(units))
}
}
impl Drop for ValueView<'_> {
fn drop(&mut self) {
unsafe { v8__String__ValueView__DESTRUCT(self) }
}
}