Skip to main content

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