Skip to main content

v8_goose/
string.rs

1use crate::Isolate;
2use crate::Local;
3use crate::String;
4use crate::binding::v8__String__kMaxLength;
5use crate::isolate::RealIsolate;
6use crate::scope::PinScope;
7use crate::support::Opaque;
8use crate::support::char;
9use crate::support::int;
10use crate::support::size_t;
11use std::borrow::Cow;
12use std::convert::TryInto;
13use std::default::Default;
14use std::ffi::c_void;
15use std::marker::PhantomData;
16use std::mem::MaybeUninit;
17use std::ptr::NonNull;
18use std::slice;
19
20unsafe extern "C" {
21  fn v8__String__Empty(isolate: *mut RealIsolate) -> *const String;
22
23  fn v8__String__NewFromUtf8(
24    isolate: *mut RealIsolate,
25    data: *const char,
26    new_type: NewStringType,
27    length: int,
28  ) -> *const String;
29
30  fn v8__String__NewFromOneByte(
31    isolate: *mut RealIsolate,
32    data: *const u8,
33    new_type: NewStringType,
34    length: int,
35  ) -> *const String;
36
37  fn v8__String__NewFromTwoByte(
38    isolate: *mut RealIsolate,
39    data: *const u16,
40    new_type: NewStringType,
41    length: int,
42  ) -> *const String;
43
44  fn v8__String__Length(this: *const String) -> int;
45
46  fn v8__String__Utf8Length(
47    this: *const String,
48    isolate: *mut RealIsolate,
49  ) -> int;
50
51  fn v8__String__Write_v2(
52    this: *const String,
53    isolate: *mut RealIsolate,
54    offset: u32,
55    length: u32,
56    buffer: *mut u16,
57    flags: int,
58  );
59
60  fn v8__String__WriteOneByte_v2(
61    this: *const String,
62    isolate: *mut RealIsolate,
63    offset: u32,
64    length: u32,
65    buffer: *mut u8,
66    flags: int,
67  );
68
69  fn v8__String__WriteUtf8_v2(
70    this: *const String,
71    isolate: *mut RealIsolate,
72    buffer: *mut char,
73    capacity: size_t,
74    flags: int,
75    processed_characters_return: *mut size_t,
76  ) -> int;
77
78  fn v8__String__GetExternalStringResource(
79    this: *const String,
80  ) -> *mut ExternalStringResource;
81  fn v8__String__GetExternalStringResourceBase(
82    this: *const String,
83    encoding: *mut Encoding,
84  ) -> *mut ExternalStringResourceBase;
85
86  fn v8__String__NewExternalOneByteConst(
87    isolate: *mut RealIsolate,
88    onebyte_const: *const OneByteConst,
89  ) -> *const String;
90
91  fn v8__String__NewExternalOneByteStatic(
92    isolate: *mut RealIsolate,
93    buffer: *const char,
94    length: int,
95  ) -> *const String;
96
97  fn v8__String__NewExternalOneByte(
98    isolate: *mut RealIsolate,
99    buffer: *mut char,
100    length: size_t,
101    free: unsafe extern "C" fn(*mut char, size_t),
102  ) -> *const String;
103
104  fn v8__String__NewExternalTwoByteStatic(
105    isolate: *mut RealIsolate,
106    buffer: *const u16,
107    length: int,
108  ) -> *const String;
109
110  #[allow(dead_code)]
111  fn v8__String__IsExternal(this: *const String) -> bool;
112  fn v8__String__IsExternalOneByte(this: *const String) -> bool;
113  fn v8__String__IsExternalTwoByte(this: *const String) -> bool;
114  #[allow(dead_code)]
115  fn v8__String__IsOneByte(this: *const String) -> bool;
116  fn v8__String__ContainsOnlyOneByte(this: *const String) -> bool;
117  fn v8__ExternalOneByteStringResource__data(
118    this: *const ExternalOneByteStringResource,
119  ) -> *const char;
120  fn v8__ExternalOneByteStringResource__length(
121    this: *const ExternalOneByteStringResource,
122  ) -> size_t;
123
124  fn v8__String__ValueView__CONSTRUCT(
125    buf: *mut ValueView,
126    isolate: *mut RealIsolate,
127    string: *const String,
128  );
129  fn v8__String__ValueView__DESTRUCT(this: *mut ValueView);
130  fn v8__String__ValueView__is_one_byte(this: *const ValueView) -> bool;
131  fn v8__String__ValueView__data(this: *const ValueView) -> *const c_void;
132  fn v8__String__ValueView__length(this: *const ValueView) -> int;
133}
134
135#[derive(PartialEq, Debug)]
136#[repr(C)]
137pub enum Encoding {
138  Unknown = 0x1,
139  TwoByte = 0x2,
140  OneByte = 0x8,
141}
142
143#[repr(C)]
144pub struct ExternalStringResource(Opaque);
145
146#[repr(C)]
147pub struct ExternalStringResourceBase(Opaque);
148
149#[repr(C)]
150/// An external, one-byte string resource.
151/// This corresponds with `v8::String::ExternalOneByteStringResource`.
152///
153/// Note: The data contained in a one-byte string resource is guaranteed to be
154/// Latin-1 data. It is not safe to assume that it is valid UTF-8, as Latin-1
155/// only has commonality with UTF-8 in the ASCII range and differs beyond that.
156pub struct ExternalOneByteStringResource(Opaque);
157
158impl ExternalOneByteStringResource {
159  /// Returns a pointer to the data owned by this resource.
160  /// This pointer is valid as long as the resource is alive.
161  /// The data is guaranteed to be Latin-1.
162  #[inline]
163  pub fn data(&self) -> *const char {
164    unsafe { v8__ExternalOneByteStringResource__data(self) }
165  }
166
167  /// Returns the length of the data owned by this resource.
168  #[inline]
169  pub fn length(&self) -> usize {
170    unsafe { v8__ExternalOneByteStringResource__length(self) }
171  }
172
173  /// Returns the data owned by this resource as a string slice.
174  /// The data is guaranteed to be Latin-1.
175  #[inline]
176  pub fn as_bytes(&self) -> &[u8] {
177    let len = self.length();
178    if len == 0 {
179      &[]
180    } else {
181      // SAFETY: We know this is Latin-1
182      unsafe { std::slice::from_raw_parts(self.data().cast(), len) }
183    }
184  }
185}
186
187/// A static ASCII string resource for usage in V8, created at build time.
188#[repr(C)]
189#[derive(Copy, Clone, Debug)]
190pub struct OneByteConst {
191  vtable: *const OneByteConstNoOp,
192  cached_data: *const char,
193  length: usize,
194}
195
196impl OneByteConst {
197  /// `const` function that returns this string as a string reference.
198  #[inline(always)]
199  pub const fn as_str(&self) -> &str {
200    if self.length == 0 {
201      ""
202    } else {
203      // SAFETY: We know this is ASCII and length > 0
204      unsafe {
205        std::str::from_utf8_unchecked(std::slice::from_raw_parts(
206          self.cached_data as _,
207          self.length,
208        ))
209      }
210    }
211  }
212}
213
214impl AsRef<str> for OneByteConst {
215  #[inline(always)]
216  fn as_ref(&self) -> &str {
217    self.as_str()
218  }
219}
220
221impl AsRef<[u8]> for OneByteConst {
222  #[inline(always)]
223  fn as_ref(&self) -> &[u8] {
224    self.as_str().as_bytes()
225  }
226}
227
228impl std::ops::Deref for OneByteConst {
229  type Target = str;
230  #[inline(always)]
231  fn deref(&self) -> &Self::Target {
232    self.as_ref()
233  }
234}
235
236// SAFETY: The vtable for OneByteConst is an immutable static and all
237// of the included functions are thread-safe, the cached_data pointer
238// is never changed and points to a static ASCII string, and the
239// length is likewise never changed. Thus, it is safe to share the
240// OneByteConst across threads. This means that multiple isolates
241// can use the same OneByteConst statics simultaneously.
242unsafe impl Sync for OneByteConst {}
243
244unsafe extern "C" fn one_byte_const_no_op(_this: *const OneByteConst) {}
245unsafe extern "C" fn one_byte_const_is_cacheable(
246  _this: *const OneByteConst,
247) -> bool {
248  true
249}
250unsafe extern "C" fn one_byte_const_data(
251  this: *const OneByteConst,
252) -> *const char {
253  // SAFETY: Only called from C++ with a valid OneByteConst pointer.
254  unsafe { (*this).cached_data }
255}
256unsafe extern "C" fn one_byte_const_length(this: *const OneByteConst) -> usize {
257  // SAFETY: Only called from C++ with a valid OneByteConst pointer.
258  unsafe { (*this).length }
259}
260unsafe extern "C" fn one_byte_const_unaccount(
261  _this: *const OneByteConst,
262  _isolate: *mut RealIsolate,
263) {
264}
265unsafe extern "C" fn one_byte_const_estimate_memory_usage(
266  _this: *const OneByteConst,
267) -> size_t {
268  usize::MAX // ExternalStringResource::kDefaultMemoryEstimate
269}
270unsafe extern "C" fn one_byte_const_estimate_shared_memory_usage(
271  _this: *const OneByteConst,
272  _recorder: *mut (),
273) {
274}
275
276type OneByteConstNoOp = unsafe extern "C" fn(*const OneByteConst);
277type OneByteConstIsCacheable =
278  unsafe extern "C" fn(*const OneByteConst) -> bool;
279type OneByteConstData =
280  unsafe extern "C" fn(*const OneByteConst) -> *const char;
281type OneByteConstLength = unsafe extern "C" fn(*const OneByteConst) -> usize;
282type OneByteConstUnaccount =
283  unsafe extern "C" fn(*const OneByteConst, *mut RealIsolate);
284type OneByteConstEstimateMemoryUsage =
285  unsafe extern "C" fn(*const OneByteConst) -> size_t;
286type OneByteConstEstimateSharedMemoryUsage =
287  unsafe extern "C" fn(*const OneByteConst, *mut ());
288
289#[repr(C)]
290struct OneByteConstVtable {
291  #[cfg(target_family = "windows")]
292  // In SysV / Itanium ABI -0x10 offset of the vtable
293  // tells how many bytes the vtable pointer pointing to
294  // this vtable is offset from the base class. For
295  // single inheritance this is always 0.
296  _offset_to_top: usize,
297  // In Itanium ABI the -0x08 offset contains the type_info
298  // pointer, and in MSVC it contains the RTTI Complete Object
299  // Locator pointer. V8 is normally compiled with `-fno-rtti`
300  // meaning that this pointer is a nullptr on both
301  // Itanium and MSVC.
302  _typeinfo: *const (),
303  // After the metadata fields come the virtual function
304  // pointers. The vtable pointer in a class instance points
305  // to the first virtual function pointer, making this
306  // the 0x00 offset of the table.
307  // The order of the virtual function pointers is determined
308  // by their order of declaration in the classes.
309  delete1: OneByteConstNoOp,
310  // In SysV / Itanium ABI, a class vtable includes the
311  // deleting destructor and the compete object destructor.
312  // In MSVC, it only includes the deleting destructor.
313  #[cfg(not(target_family = "windows"))]
314  delete2: OneByteConstNoOp,
315  is_cacheable: OneByteConstIsCacheable,
316  unaccount: OneByteConstUnaccount,
317  estimate_memory_usage: OneByteConstEstimateMemoryUsage,
318  estimate_shared_memory_usage: OneByteConstEstimateSharedMemoryUsage,
319  dispose: OneByteConstNoOp,
320  lock: OneByteConstNoOp,
321  unlock: OneByteConstNoOp,
322  data: OneByteConstData,
323  length: OneByteConstLength,
324}
325
326const ONE_BYTE_CONST_VTABLE: OneByteConstVtable = OneByteConstVtable {
327  #[cfg(target_family = "windows")]
328  _offset_to_top: 0,
329  _typeinfo: std::ptr::null(),
330  delete1: one_byte_const_no_op,
331  #[cfg(not(target_family = "windows"))]
332  delete2: one_byte_const_no_op,
333  is_cacheable: one_byte_const_is_cacheable,
334  unaccount: one_byte_const_unaccount,
335  estimate_memory_usage: one_byte_const_estimate_memory_usage,
336  estimate_shared_memory_usage: one_byte_const_estimate_shared_memory_usage,
337  dispose: one_byte_const_no_op,
338  lock: one_byte_const_no_op,
339  unlock: one_byte_const_no_op,
340  data: one_byte_const_data,
341  length: one_byte_const_length,
342};
343
344#[repr(C)]
345#[derive(Debug, Default)]
346pub enum NewStringType {
347  #[default]
348  Normal,
349  Internalized,
350}
351
352bitflags! {
353  #[derive(Clone, Copy, Default)]
354  #[repr(transparent)]
355  pub struct WriteOptions: int {
356    const NO_OPTIONS = 0;
357    const HINT_MANY_WRITES_EXPECTED = 1;
358    const NO_NULL_TERMINATION = 2;
359    const PRESERVE_ONE_BYTE_NULL = 4;
360    // Used by WriteUtf8 to replace orphan surrogate code units with the
361    // unicode replacement character. Needs to be set to guarantee valid UTF-8
362    // output.
363    const REPLACE_INVALID_UTF8 = 8;
364  }
365}
366
367bitflags! {
368  #[derive(Clone, Copy, Default)]
369  #[repr(transparent)]
370  pub struct WriteFlags: int {
371    const kNullTerminate = crate::binding::v8_String_WriteFlags_kNullTerminate as _;
372    const kReplaceInvalidUtf8 = crate::binding::v8_String_WriteFlags_kReplaceInvalidUtf8 as _;
373  }
374}
375
376impl String {
377  /// The maximum length (in bytes) of a buffer that a v8::String can be built
378  /// from. Attempting to create a v8::String from a larger buffer will result
379  /// in None being returned.
380  pub const MAX_LENGTH: usize = v8__String__kMaxLength as _;
381
382  #[inline(always)]
383  pub fn empty<'s>(scope: &PinScope<'s, '_, ()>) -> Local<'s, String> {
384    // FIXME(bnoordhuis) v8__String__Empty() is infallible so there
385    // is no need to box up the result, only to unwrap it again.
386    unsafe { scope.cast_local(|sd| v8__String__Empty(sd.get_isolate_ptr())) }
387      .unwrap()
388  }
389
390  /// Allocates a new string from UTF-8 data. Only returns an empty value when
391  /// length > kMaxLength
392  #[inline(always)]
393  pub fn new_from_utf8<'s>(
394    scope: &PinScope<'s, '_, ()>,
395    buffer: &[u8],
396    new_type: NewStringType,
397  ) -> Option<Local<'s, String>> {
398    if buffer.is_empty() {
399      return Some(Self::empty(scope));
400    }
401    let buffer_len = buffer.len().try_into().ok()?;
402    unsafe {
403      scope.cast_local(|sd| {
404        v8__String__NewFromUtf8(
405          sd.get_isolate_ptr(),
406          buffer.as_ptr() as *const char,
407          new_type,
408          buffer_len,
409        )
410      })
411    }
412  }
413
414  /// Allocates a new string from Latin-1 data.  Only returns an empty value when
415  /// length > kMaxLength.
416  #[inline(always)]
417  pub fn new_from_one_byte<'s>(
418    scope: &PinScope<'s, '_, ()>,
419    buffer: &[u8],
420    new_type: NewStringType,
421  ) -> Option<Local<'s, String>> {
422    let buffer_len = buffer.len().try_into().ok()?;
423    unsafe {
424      scope.cast_local(|sd| {
425        v8__String__NewFromOneByte(
426          sd.get_isolate_ptr(),
427          buffer.as_ptr(),
428          new_type,
429          buffer_len,
430        )
431      })
432    }
433  }
434
435  /// Allocates a new string from UTF-16 data. Only returns an empty value when
436  /// length > kMaxLength.
437  #[inline(always)]
438  pub fn new_from_two_byte<'s>(
439    scope: &PinScope<'s, '_, ()>,
440    buffer: &[u16],
441    new_type: NewStringType,
442  ) -> Option<Local<'s, String>> {
443    let buffer_len = buffer.len().try_into().ok()?;
444    unsafe {
445      scope.cast_local(|sd| {
446        v8__String__NewFromTwoByte(
447          sd.get_isolate_ptr(),
448          buffer.as_ptr(),
449          new_type,
450          buffer_len,
451        )
452      })
453    }
454  }
455
456  /// Returns the number of characters (UTF-16 code units) in this string.
457  #[inline(always)]
458  pub fn length(&self) -> usize {
459    unsafe { v8__String__Length(self) as usize }
460  }
461
462  /// Returns the number of bytes in the UTF-8 encoded representation of this
463  /// string.
464  #[inline(always)]
465  pub fn utf8_length(&self, scope: &Isolate) -> usize {
466    unsafe { v8__String__Utf8Length(self, scope.as_real_ptr()) as usize }
467  }
468
469  /// Writes the contents of the string to an external buffer, as 16-bit
470  /// (UTF-16) character codes.
471  #[inline(always)]
472  pub fn write_v2(
473    &self,
474    scope: &Isolate,
475    offset: u32,
476    buffer: &mut [u16],
477    flags: WriteFlags,
478  ) {
479    unsafe {
480      v8__String__Write_v2(
481        self,
482        scope.as_real_ptr(),
483        offset,
484        self.length().min(buffer.len()) as _,
485        buffer.as_mut_ptr(),
486        flags.bits(),
487      )
488    }
489  }
490
491  /// Writes the contents of the string to an external buffer, as one-byte
492  /// (Latin-1) characters.
493  #[inline(always)]
494  pub fn write_one_byte_v2(
495    &self,
496    scope: &Isolate,
497    offset: u32,
498    buffer: &mut [u8],
499    flags: WriteFlags,
500  ) {
501    unsafe {
502      v8__String__WriteOneByte_v2(
503        self,
504        scope.as_real_ptr(),
505        offset,
506        self.length().min(buffer.len()) as _,
507        buffer.as_mut_ptr(),
508        flags.bits(),
509      )
510    }
511  }
512
513  /// Writes the contents of the string to an external [`MaybeUninit`] buffer, as one-byte
514  /// (Latin-1) characters.
515  #[inline(always)]
516  pub fn write_one_byte_uninit_v2(
517    &self,
518    scope: &Isolate,
519    offset: u32,
520    buffer: &mut [MaybeUninit<u8>],
521    flags: WriteFlags,
522  ) {
523    unsafe {
524      v8__String__WriteOneByte_v2(
525        self,
526        scope.as_real_ptr(),
527        offset,
528        self.length().min(buffer.len()) as _,
529        buffer.as_mut_ptr() as _,
530        flags.bits(),
531      )
532    }
533  }
534
535  /// Writes the contents of the string to an external buffer, as UTF-8.
536  #[inline(always)]
537  pub fn write_utf8_v2(
538    &self,
539    scope: &Isolate,
540    buffer: &mut [u8],
541    flags: WriteFlags,
542    processed_characters_return: Option<&mut usize>,
543  ) -> usize {
544    unsafe {
545      // SAFETY:
546      // We assume that v8 will overwrite the buffer without de-initializing any byte in it.
547      // So the type casting of the buffer is safe.
548
549      let buffer = {
550        let len = buffer.len();
551        let data = buffer.as_mut_ptr().cast();
552        slice::from_raw_parts_mut(data, len)
553      };
554      self.write_utf8_uninit_v2(
555        scope,
556        buffer,
557        flags,
558        processed_characters_return,
559      )
560    }
561  }
562
563  /// Writes the contents of the string to an external [`MaybeUninit`] buffer, as UTF-8.
564  pub fn write_utf8_uninit_v2(
565    &self,
566    scope: &Isolate,
567    buffer: &mut [MaybeUninit<u8>],
568    flags: WriteFlags,
569    processed_characters_return: Option<&mut usize>,
570  ) -> usize {
571    let bytes = unsafe {
572      v8__String__WriteUtf8_v2(
573        self,
574        scope.as_real_ptr(),
575        buffer.as_mut_ptr() as _,
576        buffer.len(),
577        flags.bits(),
578        processed_characters_return
579          .map(|p| p as *mut _)
580          .unwrap_or(std::ptr::null_mut()),
581      )
582    };
583    bytes as usize
584  }
585
586  // Convenience function not present in the original V8 API.
587  #[inline(always)]
588  pub fn new<'s>(
589    scope: &PinScope<'s, '_, ()>,
590    value: &str,
591  ) -> Option<Local<'s, String>> {
592    Self::new_from_utf8(scope, value.as_ref(), NewStringType::Normal)
593  }
594
595  /// Compile-time function to create an external string resource.
596  /// The buffer is checked to contain only ASCII characters.
597  #[inline(always)]
598  pub const fn create_external_onebyte_const(
599    buffer: &'static [u8],
600  ) -> OneByteConst {
601    // Assert that the buffer contains only ASCII, and that the
602    // length is less or equal to (64-bit) v8::String::kMaxLength.
603    assert!(buffer.is_ascii() && buffer.len() <= ((1 << 29) - 24));
604    OneByteConst {
605      vtable: &ONE_BYTE_CONST_VTABLE.delete1,
606      cached_data: buffer.as_ptr() as *const char,
607      length: buffer.len(),
608    }
609  }
610
611  /// Compile-time function to create an external string resource which
612  /// skips the ASCII and length checks.
613  ///
614  /// ## Safety
615  ///
616  /// The passed in buffer must contain only ASCII data. Note that while V8
617  /// allows OneByte string resources to contain Latin-1 data, the OneByteConst
618  /// struct does not allow it.
619  #[inline(always)]
620  pub const unsafe fn create_external_onebyte_const_unchecked(
621    buffer: &'static [u8],
622  ) -> OneByteConst {
623    OneByteConst {
624      vtable: &ONE_BYTE_CONST_VTABLE.delete1,
625      cached_data: buffer.as_ptr() as *const char,
626      length: buffer.len(),
627    }
628  }
629
630  /// Creates a v8::String from a `&'static OneByteConst`
631  /// which is guaranteed to be ASCII.
632  ///
633  /// Note that OneByteConst guarantees ASCII even though V8 would allow
634  /// OneByte string resources to contain Latin-1.
635  #[inline(always)]
636  pub fn new_from_onebyte_const<'s>(
637    scope: &PinScope<'s, '_, ()>,
638    onebyte_const: &'static OneByteConst,
639  ) -> Option<Local<'s, String>> {
640    unsafe {
641      scope.cast_local(|sd| {
642        v8__String__NewExternalOneByteConst(sd.get_isolate_ptr(), onebyte_const)
643      })
644    }
645  }
646
647  /// Creates a v8::String from a `&'static [u8]`,
648  /// must be Latin-1 or ASCII, not UTF-8!
649  #[inline(always)]
650  pub fn new_external_onebyte_static<'s>(
651    scope: &PinScope<'s, '_, ()>,
652    buffer: &'static [u8],
653  ) -> Option<Local<'s, String>> {
654    let buffer_len = buffer.len().try_into().ok()?;
655    unsafe {
656      scope.cast_local(|sd| {
657        v8__String__NewExternalOneByteStatic(
658          sd.get_isolate_ptr(),
659          buffer.as_ptr() as *const char,
660          buffer_len,
661        )
662      })
663    }
664  }
665
666  /// Creates a `v8::String` from owned bytes.
667  /// The bytes must be Latin-1 or ASCII.
668  /// V8 will take ownership of the buffer and free it when the string is garbage collected.
669  #[inline(always)]
670  pub fn new_external_onebyte<'s>(
671    scope: &PinScope<'s, '_, ()>,
672    buffer: Box<[u8]>,
673  ) -> Option<Local<'s, String>> {
674    let buffer_len = buffer.len();
675    unsafe {
676      scope.cast_local(|sd| {
677        v8__String__NewExternalOneByte(
678          sd.get_isolate_ptr(),
679          Box::into_raw(buffer).cast::<char>(),
680          buffer_len,
681          free_rust_external_onebyte,
682        )
683      })
684    }
685  }
686
687  /// Creates a `v8::String` from owned bytes, length, and a custom destructor.
688  /// The bytes must be Latin-1 or ASCII.
689  /// V8 will take ownership of the buffer and free it when the string is garbage collected.
690  ///
691  /// SAFETY: `buffer` must be owned (valid for the lifetime of the string), and
692  /// `destructor` must be a valid function pointer that can free the buffer.
693  /// The destructor will be called with the buffer and length when the string is garbage collected.
694  #[inline(always)]
695  pub unsafe fn new_external_onebyte_raw<'s>(
696    scope: &PinScope<'s, '_, ()>,
697    buffer: *mut char,
698    buffer_len: usize,
699    destructor: unsafe extern "C" fn(*mut char, usize),
700  ) -> Option<Local<'s, String>> {
701    unsafe {
702      scope.cast_local(|sd| {
703        v8__String__NewExternalOneByte(
704          sd.get_isolate_ptr(),
705          buffer,
706          buffer_len,
707          destructor,
708        )
709      })
710    }
711  }
712
713  /// Creates a v8::String from a `&'static [u16]`.
714  #[inline(always)]
715  pub fn new_external_twobyte_static<'s>(
716    scope: &PinScope<'s, '_, ()>,
717    buffer: &'static [u16],
718  ) -> Option<Local<'s, String>> {
719    let buffer_len = buffer.len().try_into().ok()?;
720    unsafe {
721      scope.cast_local(|sd| {
722        v8__String__NewExternalTwoByteStatic(
723          sd.get_isolate_ptr(),
724          buffer.as_ptr(),
725          buffer_len,
726        )
727      })
728    }
729  }
730
731  /// Get the ExternalStringResource for an external string.
732  ///
733  /// Returns None if is_external() doesn't return true.
734  #[inline]
735  pub fn get_external_string_resource(
736    &self,
737  ) -> Option<NonNull<ExternalStringResource>> {
738    NonNull::new(unsafe { v8__String__GetExternalStringResource(self) })
739  }
740
741  /// Get the ExternalOneByteStringResource for an external one-byte string.
742  ///
743  /// Returns None if is_external_onebyte() doesn't return true.
744  #[inline]
745  pub fn get_external_onebyte_string_resource(
746    &self,
747  ) -> Option<NonNull<ExternalOneByteStringResource>> {
748    let (base, encoding) = self.get_external_string_resource_base();
749    let base = base?;
750    if encoding != Encoding::OneByte {
751      return None;
752    }
753
754    Some(base.cast())
755  }
756
757  /// Get the ExternalStringResourceBase for an external string.
758  /// Note this is just the base class, and isn't very useful on its own.
759  /// You'll want to downcast to one of its subclasses, for instance
760  /// with `get_external_onebyte_string_resource`.
761  pub fn get_external_string_resource_base(
762    &self,
763  ) -> (Option<NonNull<ExternalStringResourceBase>>, Encoding) {
764    let mut encoding = Encoding::Unknown;
765    (
766      NonNull::new(unsafe {
767        v8__String__GetExternalStringResourceBase(self, &mut encoding)
768      }),
769      encoding,
770    )
771  }
772
773  /// True if string is external
774  #[inline(always)]
775  pub fn is_external(&self) -> bool {
776    // TODO: re-enable on next v8-release
777    // Right now it fallbacks to Value::IsExternal, which is incorrect
778    // See: https://source.chromium.org/chromium/_/chromium/v8/v8.git/+/1dd8624b524d14076160c1743f7da0b20fbe68e0
779    // unsafe { v8__String__IsExternal(self) }
780
781    // Fallback for now (though functionally identical)
782    self.is_external_onebyte() || self.is_external_twobyte()
783  }
784
785  /// True if string is external & one-byte
786  /// (e.g: created with new_external_onebyte_static)
787  #[inline(always)]
788  pub fn is_external_onebyte(&self) -> bool {
789    unsafe { v8__String__IsExternalOneByte(self) }
790  }
791
792  /// True if string is external & two-byte
793  /// (e.g: created with new_external_twobyte_static)
794  #[inline(always)]
795  pub fn is_external_twobyte(&self) -> bool {
796    unsafe { v8__String__IsExternalTwoByte(self) }
797  }
798
799  /// Will return true if and only if string is known for certain to contain only one-byte data,
800  /// ie: Latin-1, a.k.a. ISO-8859-1 code points. Doesn't read the string so can return false
801  /// negatives, and a return value of false does not mean this string is not one-byte data.
802  ///
803  /// For a method that will not return false negatives at the cost of
804  /// potentially reading the entire string, use [`contains_only_onebyte()`].
805  ///
806  /// [`contains_only_onebyte()`]: String::contains_only_onebyte
807  #[inline(always)]
808  pub fn is_onebyte(&self) -> bool {
809    unsafe { v8__String__IsOneByte(self) }
810  }
811
812  /// True if the string contains only one-byte data.
813  /// Will read the entire string in some cases.
814  #[inline(always)]
815  pub fn contains_only_onebyte(&self) -> bool {
816    unsafe { v8__String__ContainsOnlyOneByte(self) }
817  }
818
819  /// Creates a copy of a [`crate::String`] in a [`std::string::String`].
820  /// Convenience function not present in the original V8 API.
821  pub fn to_rust_string_lossy(&self, scope: &Isolate) -> std::string::String {
822    let len_utf16 = self.length();
823
824    // No need to allocate or do any work for zero-length strings
825    if len_utf16 == 0 {
826      return std::string::String::new();
827    }
828
829    let len_utf8 = self.utf8_length(scope);
830
831    // If len_utf8 == len_utf16 and the string is one-byte, we can take the fast memcpy path. This is true iff the
832    // string is 100% 7-bit ASCII.
833    if self.is_onebyte() && len_utf8 == len_utf16 {
834      unsafe {
835        // Create an uninitialized buffer of `capacity` bytes. We need to be careful here to avoid
836        // accidentally creating a slice of u8 which would be invalid.
837        let layout = std::alloc::Layout::from_size_align(len_utf16, 1).unwrap();
838        let data = std::alloc::alloc(layout) as *mut MaybeUninit<u8>;
839        let buffer = std::ptr::slice_from_raw_parts_mut(data, len_utf16);
840
841        // Write to this MaybeUninit buffer, assuming we're going to fill this entire buffer
842        self.write_one_byte_uninit_v2(
843          scope,
844          0,
845          &mut *buffer,
846          WriteFlags::kReplaceInvalidUtf8,
847        );
848
849        // Return an owned string from this guaranteed now-initialized data
850        let buffer = data as *mut u8;
851        return std::string::String::from_raw_parts(
852          buffer, len_utf16, len_utf16,
853        );
854      }
855    }
856
857    // SAFETY: This allocates a buffer manually using the default allocator using the string's capacity.
858    // We have a large number of invariants to uphold, so please check changes to this code carefully
859    unsafe {
860      // Create an uninitialized buffer of `capacity` bytes. We need to be careful here to avoid
861      // accidentally creating a slice of u8 which would be invalid.
862      let layout = std::alloc::Layout::from_size_align(len_utf8, 1).unwrap();
863      let data = std::alloc::alloc(layout) as *mut MaybeUninit<u8>;
864      let buffer = std::ptr::slice_from_raw_parts_mut(data, len_utf8);
865
866      // Write to this MaybeUninit buffer, assuming we're going to fill this entire buffer
867      let length = self.write_utf8_uninit_v2(
868        scope,
869        &mut *buffer,
870        WriteFlags::kReplaceInvalidUtf8,
871        None,
872      );
873      debug_assert!(length == len_utf8);
874
875      // Return an owned string from this guaranteed now-initialized data
876      let buffer = data as *mut u8;
877      std::string::String::from_raw_parts(buffer, length, len_utf8)
878    }
879  }
880
881  /// Converts a [`crate::String`] to either an owned [`std::string::String`], or a borrowed [`str`], depending on whether it fits into the
882  /// provided buffer.
883  pub fn to_rust_cow_lossy<'a, const N: usize>(
884    &self,
885    scope: &mut Isolate,
886    buffer: &'a mut [MaybeUninit<u8>; N],
887  ) -> Cow<'a, str> {
888    let len_utf16 = self.length();
889
890    // No need to allocate or do any work for zero-length strings
891    if len_utf16 == 0 {
892      return "".into();
893    }
894
895    // TODO(mmastrac): Ideally we should be able to access the string's internal representation
896    let len_utf8 = self.utf8_length(scope);
897
898    // If len_utf8 == len_utf16 and the string is one-byte, we can take the fast memcpy path. This is true iff the
899    // string is 100% 7-bit ASCII.
900    if self.is_onebyte() && len_utf8 == len_utf16 {
901      if len_utf16 <= N {
902        self.write_one_byte_uninit_v2(scope, 0, buffer, WriteFlags::empty());
903        unsafe {
904          // Get a slice of &[u8] of what we know is initialized now
905          let buffer = &mut buffer[..len_utf16];
906          let buffer = &mut *(buffer as *mut [_] as *mut [u8]);
907
908          // We know it's valid UTF-8, so make a string
909          return Cow::Borrowed(std::str::from_utf8_unchecked(buffer));
910        }
911      }
912
913      unsafe {
914        // Create an uninitialized buffer of `capacity` bytes. We need to be careful here to avoid
915        // accidentally creating a slice of u8 which would be invalid.
916        let layout = std::alloc::Layout::from_size_align(len_utf16, 1).unwrap();
917        let data = std::alloc::alloc(layout) as *mut MaybeUninit<u8>;
918        let buffer = std::ptr::slice_from_raw_parts_mut(data, len_utf16);
919
920        // Write to this MaybeUninit buffer, assuming we're going to fill this entire buffer
921        self.write_one_byte_uninit_v2(
922          scope,
923          0,
924          &mut *buffer,
925          WriteFlags::kReplaceInvalidUtf8,
926        );
927
928        // Return an owned string from this guaranteed now-initialized data
929        let buffer = data as *mut u8;
930        return Cow::Owned(std::string::String::from_raw_parts(
931          buffer, len_utf16, len_utf16,
932        ));
933      }
934    }
935
936    if len_utf8 <= N {
937      // No malloc path
938      let length = self.write_utf8_uninit_v2(
939        scope,
940        buffer,
941        WriteFlags::kReplaceInvalidUtf8,
942        None,
943      );
944      debug_assert!(length == len_utf8);
945
946      // SAFETY: We know that we wrote `length` UTF-8 bytes. See `slice_assume_init_mut` for additional guarantee information.
947      unsafe {
948        // Get a slice of &[u8] of what we know is initialized now
949        let buffer = &mut buffer[..length];
950        let buffer = &mut *(buffer as *mut [_] as *mut [u8]);
951
952        // We know it's valid UTF-8, so make a string
953        return Cow::Borrowed(std::str::from_utf8_unchecked(buffer));
954      }
955    }
956
957    // SAFETY: This allocates a buffer manually using the default allocator using the string's capacity.
958    // We have a large number of invariants to uphold, so please check changes to this code carefully
959    unsafe {
960      // Create an uninitialized buffer of `capacity` bytes. We need to be careful here to avoid
961      // accidentally creating a slice of u8 which would be invalid.
962      let layout = std::alloc::Layout::from_size_align(len_utf8, 1).unwrap();
963      let data = std::alloc::alloc(layout) as *mut MaybeUninit<u8>;
964      let buffer = std::ptr::slice_from_raw_parts_mut(data, len_utf8);
965
966      // Write to this MaybeUninit buffer, assuming we're going to fill this entire buffer
967      let length = self.write_utf8_uninit_v2(
968        scope,
969        &mut *buffer,
970        WriteFlags::kReplaceInvalidUtf8,
971        None,
972      );
973      debug_assert!(length == len_utf8);
974
975      // Return an owned string from this guaranteed now-initialized data
976      let buffer = data as *mut u8;
977      Cow::Owned(std::string::String::from_raw_parts(
978        buffer, length, len_utf8,
979      ))
980    }
981  }
982}
983
984#[inline]
985pub unsafe extern "C" fn free_rust_external_onebyte(s: *mut char, len: usize) {
986  unsafe {
987    let slice = std::slice::from_raw_parts_mut(s, len);
988
989    // Drop the slice
990    drop(Box::from_raw(slice));
991  }
992}
993
994#[derive(Debug, PartialEq)]
995pub enum ValueViewData<'s> {
996  OneByte(&'s [u8]),
997  TwoByte(&'s [u16]),
998}
999
1000/// Returns a view onto a string's contents.
1001///
1002/// WARNING: This does not copy the string's contents, and will therefore be
1003/// invalidated if the GC can move the string while the ValueView is alive. It
1004/// is therefore required that no GC or allocation can happen while there is an
1005/// active ValueView. This requirement may be relaxed in the future.
1006///
1007/// V8 strings are either encoded as one-byte or two-bytes per character.
1008#[repr(C)]
1009pub struct ValueView<'s>(
1010  [u8; crate::binding::v8__String__ValueView_SIZE],
1011  PhantomData<&'s ()>,
1012);
1013
1014impl<'s> ValueView<'s> {
1015  #[inline(always)]
1016  pub fn new(isolate: &mut Isolate, string: Local<'s, String>) -> Self {
1017    let mut v = std::mem::MaybeUninit::uninit();
1018    unsafe {
1019      v8__String__ValueView__CONSTRUCT(
1020        v.as_mut_ptr(),
1021        isolate.as_real_ptr(),
1022        &*string,
1023      );
1024      v.assume_init()
1025    }
1026  }
1027
1028  #[inline(always)]
1029  pub fn data(&self) -> ValueViewData<'_> {
1030    unsafe {
1031      let data = v8__String__ValueView__data(self);
1032      let length = v8__String__ValueView__length(self) as usize;
1033      if v8__String__ValueView__is_one_byte(self) {
1034        ValueViewData::OneByte(std::slice::from_raw_parts(data as _, length))
1035      } else {
1036        ValueViewData::TwoByte(std::slice::from_raw_parts(data as _, length))
1037      }
1038    }
1039  }
1040}
1041
1042impl Drop for ValueView<'_> {
1043  fn drop(&mut self) {
1044    unsafe { v8__String__ValueView__DESTRUCT(self) }
1045  }
1046}