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
20#[inline(always)]
32pub unsafe fn latin1_to_utf8(
33 input_length: usize,
34 inbuf: *const u8,
35 outbuf: *mut u8,
36) -> usize {
37 unsafe {
38 let mut output = 0;
39 let mut input = 0;
40
41 while input + 8 <= input_length {
43 let chunk = (inbuf.add(input) as *const u64).read_unaligned();
44 if chunk & 0x8080_8080_8080_8080 == 0 {
45 (outbuf.add(output) as *mut u64).write_unaligned(chunk);
47 input += 8;
48 output += 8;
49 } else {
50 let end = input + 8;
52 while input < end {
53 let byte = *(inbuf.add(input));
54 if byte < 0x80 {
55 *(outbuf.add(output)) = byte;
56 output += 1;
57 } else {
58 *(outbuf.add(output)) = (byte >> 6) | 0b1100_0000;
60 *(outbuf.add(output + 1)) = (byte & 0b0011_1111) | 0b1000_0000;
61 output += 2;
62 }
63 input += 1;
64 }
65 }
66 }
67
68 while input < input_length {
70 let byte = *(inbuf.add(input));
71 if byte < 0x80 {
72 *(outbuf.add(output)) = byte;
73 output += 1;
74 } else {
75 *(outbuf.add(output)) = (byte >> 6) | 0b1100_0000;
76 *(outbuf.add(output + 1)) = (byte & 0b0011_1111) | 0b1000_0000;
77 output += 2;
78 }
79 input += 1;
80 }
81 output
82 }
83}
84
85unsafe extern "C" {
86 fn v8__String__Empty(isolate: *mut RealIsolate) -> *const String;
87
88 fn v8__String__NewFromUtf8(
89 isolate: *mut RealIsolate,
90 data: *const char,
91 new_type: NewStringType,
92 length: int,
93 ) -> *const String;
94
95 fn v8__String__NewFromOneByte(
96 isolate: *mut RealIsolate,
97 data: *const u8,
98 new_type: NewStringType,
99 length: int,
100 ) -> *const String;
101
102 fn v8__String__NewFromTwoByte(
103 isolate: *mut RealIsolate,
104 data: *const u16,
105 new_type: NewStringType,
106 length: int,
107 ) -> *const String;
108
109 fn v8__String__Concat(
110 isolate: *mut RealIsolate,
111 left: *const String,
112 right: *const String,
113 ) -> *const String;
114
115 fn v8__String__Length(this: *const String) -> int;
116
117 fn v8__String__Utf8Length(
118 this: *const String,
119 isolate: *mut RealIsolate,
120 ) -> int;
121
122 fn v8__String__Write_v2(
123 this: *const String,
124 isolate: *mut RealIsolate,
125 offset: u32,
126 length: u32,
127 buffer: *mut u16,
128 flags: int,
129 );
130
131 fn v8__String__WriteOneByte_v2(
132 this: *const String,
133 isolate: *mut RealIsolate,
134 offset: u32,
135 length: u32,
136 buffer: *mut u8,
137 flags: int,
138 );
139
140 fn v8__String__WriteUtf8_v2(
141 this: *const String,
142 isolate: *mut RealIsolate,
143 buffer: *mut char,
144 capacity: size_t,
145 flags: int,
146 processed_characters_return: *mut size_t,
147 ) -> int;
148
149 fn v8__String__GetExternalStringResource(
150 this: *const String,
151 ) -> *mut ExternalStringResource;
152 fn v8__String__GetExternalStringResourceBase(
153 this: *const String,
154 encoding: *mut Encoding,
155 ) -> *mut ExternalStringResourceBase;
156
157 fn v8__String__NewExternalOneByteConst(
158 isolate: *mut RealIsolate,
159 onebyte_const: *const OneByteConst,
160 ) -> *const String;
161
162 fn v8__String__NewExternalOneByteStatic(
163 isolate: *mut RealIsolate,
164 buffer: *const char,
165 length: int,
166 ) -> *const String;
167
168 fn v8__String__NewExternalOneByte(
169 isolate: *mut RealIsolate,
170 buffer: *mut char,
171 length: size_t,
172 free: unsafe extern "C" fn(*mut char, size_t),
173 ) -> *const String;
174
175 fn v8__String__NewExternalTwoByteStatic(
176 isolate: *mut RealIsolate,
177 buffer: *const u16,
178 length: int,
179 ) -> *const String;
180
181 fn v8__String__NewExternalTwoByte(
182 isolate: *mut RealIsolate,
183 buffer: *mut u16,
184 length: size_t,
185 free: unsafe extern "C" fn(*mut u16, size_t),
186 ) -> *const String;
187
188 #[allow(dead_code)]
189 fn v8__String__IsExternal(this: *const String) -> bool;
190 fn v8__String__IsExternalOneByte(this: *const String) -> bool;
191 fn v8__String__IsExternalTwoByte(this: *const String) -> bool;
192 #[allow(dead_code)]
193 fn v8__String__IsOneByte(this: *const String) -> bool;
194 fn v8__String__ContainsOnlyOneByte(this: *const String) -> bool;
195 fn v8__ExternalOneByteStringResource__data(
196 this: *const ExternalOneByteStringResource,
197 ) -> *const char;
198 fn v8__ExternalOneByteStringResource__length(
199 this: *const ExternalOneByteStringResource,
200 ) -> size_t;
201
202 fn v8__String__ValueView__CONSTRUCT(
203 buf: *mut ValueView,
204 isolate: *mut RealIsolate,
205 string: *const String,
206 );
207 fn v8__String__ValueView__DESTRUCT(this: *mut ValueView);
208 fn v8__String__ValueView__is_one_byte(this: *const ValueView) -> bool;
209 fn v8__String__ValueView__data(this: *const ValueView) -> *const c_void;
210 fn v8__String__ValueView__length(this: *const ValueView) -> int;
211}
212
213#[derive(PartialEq, Debug)]
214#[repr(C)]
215pub enum Encoding {
216 Unknown = 0x1,
217 TwoByte = 0x2,
218 OneByte = 0x8,
219}
220
221#[repr(C)]
222pub struct ExternalStringResource(Opaque);
223
224#[repr(C)]
225pub struct ExternalStringResourceBase(Opaque);
226
227#[repr(C)]
228pub struct ExternalOneByteStringResource(Opaque);
235
236impl ExternalOneByteStringResource {
237 #[inline]
241 pub fn data(&self) -> *const char {
242 unsafe { v8__ExternalOneByteStringResource__data(self) }
243 }
244
245 #[inline]
247 pub fn length(&self) -> usize {
248 unsafe { v8__ExternalOneByteStringResource__length(self) }
249 }
250
251 #[inline]
254 pub fn as_bytes(&self) -> &[u8] {
255 let len = self.length();
256 if len == 0 {
257 &[]
258 } else {
259 unsafe { std::slice::from_raw_parts(self.data().cast(), len) }
261 }
262 }
263}
264
265#[repr(C)]
267#[derive(Copy, Clone, Debug)]
268pub struct OneByteConst {
269 vtable: *const OneByteConstNoOp,
270 cached_data: *const char,
271 length: usize,
272}
273
274impl OneByteConst {
275 #[inline(always)]
277 pub const fn as_str(&self) -> &str {
278 if self.length == 0 {
279 ""
280 } else {
281 unsafe {
283 std::str::from_utf8_unchecked(std::slice::from_raw_parts(
284 self.cached_data as _,
285 self.length,
286 ))
287 }
288 }
289 }
290}
291
292impl AsRef<str> for OneByteConst {
293 #[inline(always)]
294 fn as_ref(&self) -> &str {
295 self.as_str()
296 }
297}
298
299impl AsRef<[u8]> for OneByteConst {
300 #[inline(always)]
301 fn as_ref(&self) -> &[u8] {
302 self.as_str().as_bytes()
303 }
304}
305
306impl std::ops::Deref for OneByteConst {
307 type Target = str;
308 #[inline(always)]
309 fn deref(&self) -> &Self::Target {
310 self.as_ref()
311 }
312}
313
314unsafe impl Sync for OneByteConst {}
321
322unsafe extern "C" fn one_byte_const_no_op(_this: *const OneByteConst) {}
323unsafe extern "C" fn one_byte_const_is_cacheable(
324 _this: *const OneByteConst,
325) -> bool {
326 true
327}
328unsafe extern "C" fn one_byte_const_data(
329 this: *const OneByteConst,
330) -> *const char {
331 unsafe { (*this).cached_data }
333}
334unsafe extern "C" fn one_byte_const_length(this: *const OneByteConst) -> usize {
335 unsafe { (*this).length }
337}
338unsafe extern "C" fn one_byte_const_unaccount(
339 _this: *const OneByteConst,
340 _isolate: *mut RealIsolate,
341) {
342}
343unsafe extern "C" fn one_byte_const_estimate_memory_usage(
344 _this: *const OneByteConst,
345) -> size_t {
346 usize::MAX }
348unsafe extern "C" fn one_byte_const_estimate_shared_memory_usage(
349 _this: *const OneByteConst,
350 _recorder: *mut (),
351) {
352}
353
354type OneByteConstNoOp = unsafe extern "C" fn(*const OneByteConst);
355type OneByteConstIsCacheable =
356 unsafe extern "C" fn(*const OneByteConst) -> bool;
357type OneByteConstData =
358 unsafe extern "C" fn(*const OneByteConst) -> *const char;
359type OneByteConstLength = unsafe extern "C" fn(*const OneByteConst) -> usize;
360type OneByteConstUnaccount =
361 unsafe extern "C" fn(*const OneByteConst, *mut RealIsolate);
362type OneByteConstEstimateMemoryUsage =
363 unsafe extern "C" fn(*const OneByteConst) -> size_t;
364type OneByteConstEstimateSharedMemoryUsage =
365 unsafe extern "C" fn(*const OneByteConst, *mut ());
366
367#[repr(C)]
368struct OneByteConstVtable {
369 #[cfg(target_family = "windows")]
370 _offset_to_top: usize,
375 _typeinfo: *const (),
381 delete1: OneByteConstNoOp,
388 #[cfg(not(target_family = "windows"))]
392 delete2: OneByteConstNoOp,
393 is_cacheable: OneByteConstIsCacheable,
394 unaccount: OneByteConstUnaccount,
395 estimate_memory_usage: OneByteConstEstimateMemoryUsage,
396 estimate_shared_memory_usage: OneByteConstEstimateSharedMemoryUsage,
397 dispose: OneByteConstNoOp,
398 lock: OneByteConstNoOp,
399 unlock: OneByteConstNoOp,
400 data: OneByteConstData,
401 length: OneByteConstLength,
402}
403
404const ONE_BYTE_CONST_VTABLE: OneByteConstVtable = OneByteConstVtable {
405 #[cfg(target_family = "windows")]
406 _offset_to_top: 0,
407 _typeinfo: std::ptr::null(),
408 delete1: one_byte_const_no_op,
409 #[cfg(not(target_family = "windows"))]
410 delete2: one_byte_const_no_op,
411 is_cacheable: one_byte_const_is_cacheable,
412 unaccount: one_byte_const_unaccount,
413 estimate_memory_usage: one_byte_const_estimate_memory_usage,
414 estimate_shared_memory_usage: one_byte_const_estimate_shared_memory_usage,
415 dispose: one_byte_const_no_op,
416 lock: one_byte_const_no_op,
417 unlock: one_byte_const_no_op,
418 data: one_byte_const_data,
419 length: one_byte_const_length,
420};
421
422#[repr(C)]
423#[derive(Debug, Default)]
424pub enum NewStringType {
425 #[default]
426 Normal,
427 Internalized,
428}
429
430bitflags! {
431 #[derive(Clone, Copy, Default)]
432 #[repr(transparent)]
433 pub struct WriteOptions: int {
434 const NO_OPTIONS = 0;
435 const HINT_MANY_WRITES_EXPECTED = 1;
436 const NO_NULL_TERMINATION = 2;
437 const PRESERVE_ONE_BYTE_NULL = 4;
438 const REPLACE_INVALID_UTF8 = 8;
442 }
443}
444
445bitflags! {
446 #[derive(Clone, Copy, Default)]
447 #[repr(transparent)]
448 pub struct WriteFlags: int {
449 const kNullTerminate = crate::binding::v8_String_WriteFlags_kNullTerminate as _;
450 const kReplaceInvalidUtf8 = crate::binding::v8_String_WriteFlags_kReplaceInvalidUtf8 as _;
451 }
452}
453
454impl String {
455 pub const MAX_LENGTH: usize = v8__String__kMaxLength as _;
459
460 #[inline(always)]
461 pub fn empty<'s>(scope: &PinScope<'s, '_, ()>) -> Local<'s, String> {
462 unsafe { scope.cast_local(|sd| v8__String__Empty(sd.get_isolate_ptr())) }
465 .unwrap()
466 }
467
468 #[inline(always)]
471 pub fn new_from_utf8<'s>(
472 scope: &PinScope<'s, '_, ()>,
473 buffer: &[u8],
474 new_type: NewStringType,
475 ) -> Option<Local<'s, String>> {
476 if buffer.is_empty() {
477 return Some(Self::empty(scope));
478 }
479 let buffer_len = buffer.len().try_into().ok()?;
480 unsafe {
481 scope.cast_local(|sd| {
482 v8__String__NewFromUtf8(
483 sd.get_isolate_ptr(),
484 buffer.as_ptr() as *const char,
485 new_type,
486 buffer_len,
487 )
488 })
489 }
490 }
491
492 #[inline(always)]
495 pub fn new_from_one_byte<'s>(
496 scope: &PinScope<'s, '_, ()>,
497 buffer: &[u8],
498 new_type: NewStringType,
499 ) -> Option<Local<'s, String>> {
500 let buffer_len = buffer.len().try_into().ok()?;
501 unsafe {
502 scope.cast_local(|sd| {
503 v8__String__NewFromOneByte(
504 sd.get_isolate_ptr(),
505 buffer.as_ptr(),
506 new_type,
507 buffer_len,
508 )
509 })
510 }
511 }
512
513 #[inline(always)]
516 pub fn new_from_two_byte<'s>(
517 scope: &PinScope<'s, '_, ()>,
518 buffer: &[u16],
519 new_type: NewStringType,
520 ) -> Option<Local<'s, String>> {
521 let buffer_len = buffer.len().try_into().ok()?;
522 unsafe {
523 scope.cast_local(|sd| {
524 v8__String__NewFromTwoByte(
525 sd.get_isolate_ptr(),
526 buffer.as_ptr(),
527 new_type,
528 buffer_len,
529 )
530 })
531 }
532 }
533
534 #[inline(always)]
536 pub fn length(&self) -> usize {
537 unsafe { v8__String__Length(self) as usize }
538 }
539
540 #[inline(always)]
543 pub fn utf8_length(&self, scope: &Isolate) -> usize {
544 unsafe { v8__String__Utf8Length(self, scope.as_real_ptr()) as usize }
545 }
546
547 #[inline(always)]
550 pub fn write_v2(
551 &self,
552 scope: &Isolate,
553 offset: u32,
554 buffer: &mut [u16],
555 flags: WriteFlags,
556 ) {
557 unsafe {
558 v8__String__Write_v2(
559 self,
560 scope.as_real_ptr(),
561 offset,
562 self.length().min(buffer.len()) as _,
563 buffer.as_mut_ptr(),
564 flags.bits(),
565 )
566 }
567 }
568
569 #[inline(always)]
572 pub fn write_one_byte_v2(
573 &self,
574 scope: &Isolate,
575 offset: u32,
576 buffer: &mut [u8],
577 flags: WriteFlags,
578 ) {
579 unsafe {
580 v8__String__WriteOneByte_v2(
581 self,
582 scope.as_real_ptr(),
583 offset,
584 self.length().min(buffer.len()) as _,
585 buffer.as_mut_ptr(),
586 flags.bits(),
587 )
588 }
589 }
590
591 #[inline(always)]
594 pub fn write_one_byte_uninit_v2(
595 &self,
596 scope: &Isolate,
597 offset: u32,
598 buffer: &mut [MaybeUninit<u8>],
599 flags: WriteFlags,
600 ) {
601 unsafe {
602 v8__String__WriteOneByte_v2(
603 self,
604 scope.as_real_ptr(),
605 offset,
606 self.length().min(buffer.len()) as _,
607 buffer.as_mut_ptr() as _,
608 flags.bits(),
609 )
610 }
611 }
612
613 #[inline(always)]
615 pub fn write_utf8_v2(
616 &self,
617 scope: &Isolate,
618 buffer: &mut [u8],
619 flags: WriteFlags,
620 processed_characters_return: Option<&mut usize>,
621 ) -> usize {
622 unsafe {
623 let buffer = {
628 let len = buffer.len();
629 let data = buffer.as_mut_ptr().cast();
630 slice::from_raw_parts_mut(data, len)
631 };
632 self.write_utf8_uninit_v2(
633 scope,
634 buffer,
635 flags,
636 processed_characters_return,
637 )
638 }
639 }
640
641 pub fn write_utf8_uninit_v2(
643 &self,
644 scope: &Isolate,
645 buffer: &mut [MaybeUninit<u8>],
646 flags: WriteFlags,
647 processed_characters_return: Option<&mut usize>,
648 ) -> usize {
649 let bytes = unsafe {
650 v8__String__WriteUtf8_v2(
651 self,
652 scope.as_real_ptr(),
653 buffer.as_mut_ptr() as _,
654 buffer.len(),
655 flags.bits(),
656 processed_characters_return
657 .map(|p| p as *mut _)
658 .unwrap_or(std::ptr::null_mut()),
659 )
660 };
661 bytes as usize
662 }
663
664 #[inline(always)]
666 pub fn new<'s>(
667 scope: &PinScope<'s, '_, ()>,
668 value: &str,
669 ) -> Option<Local<'s, String>> {
670 Self::new_from_utf8(scope, value.as_ref(), NewStringType::Normal)
671 }
672
673 #[inline(always)]
677 pub fn concat<'s>(
678 scope: &PinScope<'s, '_, ()>,
679 left: Local<String>,
680 right: Local<String>,
681 ) -> Option<Local<'s, String>> {
682 unsafe {
683 scope.cast_local(|sd| {
684 v8__String__Concat(sd.get_isolate_ptr(), &*left, &*right)
685 })
686 }
687 }
688
689 #[inline(always)]
692 pub const fn create_external_onebyte_const(
693 buffer: &'static [u8],
694 ) -> OneByteConst {
695 assert!(buffer.is_ascii() && buffer.len() <= ((1 << 29) - 24));
698 OneByteConst {
699 vtable: &ONE_BYTE_CONST_VTABLE.delete1,
700 cached_data: buffer.as_ptr() as *const char,
701 length: buffer.len(),
702 }
703 }
704
705 #[inline(always)]
714 pub const unsafe fn create_external_onebyte_const_unchecked(
715 buffer: &'static [u8],
716 ) -> OneByteConst {
717 OneByteConst {
718 vtable: &ONE_BYTE_CONST_VTABLE.delete1,
719 cached_data: buffer.as_ptr() as *const char,
720 length: buffer.len(),
721 }
722 }
723
724 #[inline(always)]
730 pub fn new_from_onebyte_const<'s>(
731 scope: &PinScope<'s, '_, ()>,
732 onebyte_const: &'static OneByteConst,
733 ) -> Option<Local<'s, String>> {
734 unsafe {
735 scope.cast_local(|sd| {
736 v8__String__NewExternalOneByteConst(sd.get_isolate_ptr(), onebyte_const)
737 })
738 }
739 }
740
741 #[inline(always)]
744 pub fn new_external_onebyte_static<'s>(
745 scope: &PinScope<'s, '_, ()>,
746 buffer: &'static [u8],
747 ) -> Option<Local<'s, String>> {
748 let buffer_len = buffer.len().try_into().ok()?;
749 unsafe {
750 scope.cast_local(|sd| {
751 v8__String__NewExternalOneByteStatic(
752 sd.get_isolate_ptr(),
753 buffer.as_ptr() as *const char,
754 buffer_len,
755 )
756 })
757 }
758 }
759
760 #[inline(always)]
764 pub fn new_external_onebyte<'s>(
765 scope: &PinScope<'s, '_, ()>,
766 buffer: Box<[u8]>,
767 ) -> Option<Local<'s, String>> {
768 let buffer_len = buffer.len();
769 unsafe {
770 scope.cast_local(|sd| {
771 v8__String__NewExternalOneByte(
772 sd.get_isolate_ptr(),
773 Box::into_raw(buffer).cast::<char>(),
774 buffer_len,
775 free_rust_external_onebyte,
776 )
777 })
778 }
779 }
780
781 #[inline(always)]
789 pub unsafe fn new_external_onebyte_raw<'s>(
790 scope: &PinScope<'s, '_, ()>,
791 buffer: *mut char,
792 buffer_len: usize,
793 destructor: unsafe extern "C" fn(*mut char, usize),
794 ) -> Option<Local<'s, String>> {
795 unsafe {
796 scope.cast_local(|sd| {
797 v8__String__NewExternalOneByte(
798 sd.get_isolate_ptr(),
799 buffer,
800 buffer_len,
801 destructor,
802 )
803 })
804 }
805 }
806
807 #[inline(always)]
809 pub fn new_external_twobyte_static<'s>(
810 scope: &PinScope<'s, '_, ()>,
811 buffer: &'static [u16],
812 ) -> Option<Local<'s, String>> {
813 let buffer_len = buffer.len().try_into().ok()?;
814 unsafe {
815 scope.cast_local(|sd| {
816 v8__String__NewExternalTwoByteStatic(
817 sd.get_isolate_ptr(),
818 buffer.as_ptr(),
819 buffer_len,
820 )
821 })
822 }
823 }
824
825 #[inline(always)]
829 pub fn new_external_twobyte<'s>(
830 scope: &PinScope<'s, '_, ()>,
831 buffer: Box<[u16]>,
832 ) -> Option<Local<'s, String>> {
833 let buffer_len = buffer.len();
834 unsafe {
835 scope.cast_local(|sd| {
836 v8__String__NewExternalTwoByte(
837 sd.get_isolate_ptr(),
838 Box::into_raw(buffer).cast::<u16>(),
839 buffer_len,
840 free_rust_external_twobyte,
841 )
842 })
843 }
844 }
845
846 #[inline(always)]
858 pub unsafe fn new_external_twobyte_raw<'s>(
859 scope: &PinScope<'s, '_, ()>,
860 buffer: *mut u16,
861 buffer_len: usize,
862 destructor: unsafe extern "C" fn(*mut u16, usize),
863 ) -> Option<Local<'s, String>> {
864 unsafe {
865 scope.cast_local(|sd| {
866 v8__String__NewExternalTwoByte(
867 sd.get_isolate_ptr(),
868 buffer,
869 buffer_len,
870 destructor,
871 )
872 })
873 }
874 }
875
876 #[inline]
880 pub fn get_external_string_resource(
881 &self,
882 ) -> Option<NonNull<ExternalStringResource>> {
883 NonNull::new(unsafe { v8__String__GetExternalStringResource(self) })
884 }
885
886 #[inline]
890 pub fn get_external_onebyte_string_resource(
891 &self,
892 ) -> Option<NonNull<ExternalOneByteStringResource>> {
893 let (base, encoding) = self.get_external_string_resource_base();
894 let base = base?;
895 if encoding != Encoding::OneByte {
896 return None;
897 }
898
899 Some(base.cast())
900 }
901
902 pub fn get_external_string_resource_base(
907 &self,
908 ) -> (Option<NonNull<ExternalStringResourceBase>>, Encoding) {
909 let mut encoding = Encoding::Unknown;
910 (
911 NonNull::new(unsafe {
912 v8__String__GetExternalStringResourceBase(self, &mut encoding)
913 }),
914 encoding,
915 )
916 }
917
918 #[inline(always)]
920 pub fn is_external(&self) -> bool {
921 self.is_external_onebyte() || self.is_external_twobyte()
928 }
929
930 #[inline(always)]
933 pub fn is_external_onebyte(&self) -> bool {
934 unsafe { v8__String__IsExternalOneByte(self) }
935 }
936
937 #[inline(always)]
940 pub fn is_external_twobyte(&self) -> bool {
941 unsafe { v8__String__IsExternalTwoByte(self) }
942 }
943
944 #[inline(always)]
953 pub fn is_onebyte(&self) -> bool {
954 unsafe { v8__String__IsOneByte(self) }
955 }
956
957 #[inline(always)]
960 pub fn contains_only_onebyte(&self) -> bool {
961 unsafe { v8__String__ContainsOnlyOneByte(self) }
962 }
963
964 pub fn to_rust_string_lossy(&self, scope: &Isolate) -> std::string::String {
971 if self.length() == 0 {
972 return std::string::String::new();
973 }
974
975 let view = unsafe { ValueView::new_from_ref(scope, self) };
977
978 match view.data() {
979 ValueViewData::OneByte(bytes) => {
980 if bytes.is_ascii() {
981 unsafe { std::str::from_utf8_unchecked(bytes) }.to_owned()
983 } else {
984 latin1_to_string(bytes)
985 }
986 }
987 ValueViewData::TwoByte(units) => wtf16_to_string(units),
988 }
989 }
990
991 pub fn write_utf8_into(
1001 &self,
1002 scope: &mut Isolate,
1003 buf: &mut std::string::String,
1004 ) {
1005 buf.clear();
1006 let len = self.length();
1007 if len == 0 {
1008 return;
1009 }
1010
1011 let view = unsafe { ValueView::new_from_ref(scope, self) };
1014
1015 match view.data() {
1016 ValueViewData::OneByte(bytes) => {
1017 if bytes.is_ascii() {
1018 buf.reserve(bytes.len());
1020 unsafe {
1021 let vec = buf.as_mut_vec();
1022 std::ptr::copy_nonoverlapping(
1023 bytes.as_ptr(),
1024 vec.as_mut_ptr(),
1025 bytes.len(),
1026 );
1027 vec.set_len(bytes.len());
1028 }
1029 } else {
1030 let max_utf8_len = bytes.len() * 2;
1032 buf.reserve(max_utf8_len);
1033 unsafe {
1034 let vec = buf.as_mut_vec();
1035 let written =
1036 latin1_to_utf8(bytes.len(), bytes.as_ptr(), vec.as_mut_ptr());
1037 vec.set_len(written);
1038 }
1039 }
1040 }
1041 ValueViewData::TwoByte(units) => {
1042 wtf16_into_string(units, buf);
1043 }
1044 }
1045 }
1046
1047 pub fn to_rust_cow_lossy<'a, const N: usize>(
1055 &self,
1056 scope: &mut Isolate,
1057 buffer: &'a mut [MaybeUninit<u8>; N],
1058 ) -> Cow<'a, str> {
1059 let len = self.length();
1060 if len == 0 {
1061 return "".into();
1062 }
1063
1064 let view = unsafe { ValueView::new_from_ref(scope, self) };
1068
1069 match view.data() {
1070 ValueViewData::OneByte(bytes) => {
1071 if bytes.is_ascii() {
1072 if bytes.len() <= N {
1074 unsafe {
1075 std::ptr::copy_nonoverlapping(
1076 bytes.as_ptr(),
1077 buffer.as_mut_ptr() as *mut u8,
1078 bytes.len(),
1079 );
1080 let buf = &mut buffer[..bytes.len()];
1081 let buf = &mut *(buf as *mut [_] as *mut [u8]);
1082 Cow::Borrowed(std::str::from_utf8_unchecked(buf))
1083 }
1084 } else {
1085 unsafe {
1087 Cow::Owned(std::string::String::from_utf8_unchecked(
1088 bytes.to_vec(),
1089 ))
1090 }
1091 }
1092 } else {
1093 latin1_to_cow_str(bytes, buffer)
1094 }
1095 }
1096 ValueViewData::TwoByte(units) => wtf16_to_cow_str(units, buffer),
1097 }
1098 }
1099}
1100
1101#[inline]
1102pub unsafe extern "C" fn free_rust_external_onebyte(s: *mut char, len: usize) {
1103 unsafe {
1104 let slice = std::slice::from_raw_parts_mut(s, len);
1105
1106 drop(Box::from_raw(slice));
1108 }
1109}
1110
1111#[inline]
1112pub unsafe extern "C" fn free_rust_external_twobyte(s: *mut u16, len: usize) {
1113 unsafe {
1114 let slice = std::slice::from_raw_parts_mut(s, len);
1115 drop(Box::from_raw(slice));
1116 }
1117}
1118
1119#[derive(Debug, PartialEq)]
1120pub enum ValueViewData<'s> {
1121 OneByte(&'s [u8]),
1122 TwoByte(&'s [u16]),
1123}
1124
1125#[repr(C)]
1134pub struct ValueView<'s>(
1135 [u8; crate::binding::v8__String__ValueView_SIZE],
1136 PhantomData<&'s ()>,
1137);
1138
1139impl<'s> ValueView<'s> {
1140 #[inline(always)]
1141 pub fn new(isolate: &mut Isolate, string: Local<'s, String>) -> Self {
1142 let string_ref: &'s String = unsafe { &*((&*string) as *const String) };
1146 unsafe { Self::new_from_ref(isolate, string_ref) }
1147 }
1148
1149 #[inline(always)]
1158 pub(crate) unsafe fn new_from_ref(
1159 isolate: &Isolate,
1160 string: &'s String,
1161 ) -> Self {
1162 let mut v = std::mem::MaybeUninit::uninit();
1163 unsafe {
1164 v8__String__ValueView__CONSTRUCT(
1165 v.as_mut_ptr(),
1166 isolate.as_real_ptr(),
1167 string,
1168 );
1169 v.assume_init()
1170 }
1171 }
1172
1173 #[inline(always)]
1174 pub fn data(&self) -> ValueViewData<'_> {
1175 unsafe {
1176 let data = v8__String__ValueView__data(self);
1177 let length = v8__String__ValueView__length(self) as usize;
1178 if v8__String__ValueView__is_one_byte(self) {
1179 ValueViewData::OneByte(std::slice::from_raw_parts(data as _, length))
1180 } else {
1181 ValueViewData::TwoByte(std::slice::from_raw_parts(data as _, length))
1182 }
1183 }
1184 }
1185
1186 #[inline(always)]
1194 pub fn as_str(&self) -> Option<&str> {
1195 match self.data() {
1196 ValueViewData::OneByte(bytes) => {
1197 if bytes.is_ascii() {
1198 Some(unsafe { std::str::from_utf8_unchecked(bytes) })
1200 } else {
1201 None
1202 }
1203 }
1204 ValueViewData::TwoByte(_) => None,
1205 }
1206 }
1207
1208 #[inline(always)]
1218 pub fn to_cow_lossy(&self) -> Cow<'_, str> {
1219 match self.data() {
1220 ValueViewData::OneByte(bytes) => {
1221 if bytes.is_ascii() {
1222 Cow::Borrowed(unsafe { std::str::from_utf8_unchecked(bytes) })
1224 } else {
1225 Cow::Owned(latin1_to_string(bytes))
1226 }
1227 }
1228 ValueViewData::TwoByte(units) => Cow::Owned(wtf16_to_string(units)),
1229 }
1230 }
1231}
1232
1233#[cfg(feature = "simdutf")]
1243const WTF16_SIMD_THRESHOLD: usize = 96;
1244
1245#[inline(always)]
1247fn latin1_to_string(bytes: &[u8]) -> std::string::String {
1248 debug_assert!(!bytes.is_ascii());
1249 #[cfg(feature = "simdutf")]
1250 {
1251 let utf8_len = crate::simdutf::utf8_length_from_latin1(bytes);
1252 let mut buf: Vec<u8> = Vec::with_capacity(utf8_len);
1253 unsafe {
1254 let out = std::slice::from_raw_parts_mut(buf.as_mut_ptr(), utf8_len);
1255 let written = crate::simdutf::convert_latin1_to_utf8(bytes, out);
1256 debug_assert_eq!(written, utf8_len);
1257 buf.set_len(written);
1258 std::string::String::from_utf8_unchecked(buf)
1259 }
1260 }
1261 #[cfg(not(feature = "simdutf"))]
1262 {
1263 let max_utf8_len = bytes.len() * 2;
1264 let mut buf: Vec<u8> = Vec::with_capacity(max_utf8_len);
1265 unsafe {
1266 let written =
1267 latin1_to_utf8(bytes.len(), bytes.as_ptr(), buf.as_mut_ptr());
1268 buf.set_len(written);
1269 std::string::String::from_utf8_unchecked(buf)
1270 }
1271 }
1272}
1273
1274#[inline(always)]
1277fn wtf16_to_string(units: &[u16]) -> std::string::String {
1278 #[cfg(feature = "simdutf")]
1279 {
1280 if units.len() >= WTF16_SIMD_THRESHOLD
1282 && crate::simdutf::validate_utf16le(units)
1283 {
1284 let utf8_len = crate::simdutf::utf8_length_from_utf16le(units);
1285 let mut buf: Vec<u8> = Vec::with_capacity(utf8_len);
1286 unsafe {
1287 let out = std::slice::from_raw_parts_mut(buf.as_mut_ptr(), utf8_len);
1288 let written = crate::simdutf::convert_utf16le_to_utf8(units, out);
1289 debug_assert_eq!(written, utf8_len);
1290 buf.set_len(written);
1291 return std::string::String::from_utf8_unchecked(buf);
1292 }
1293 }
1294 }
1295 let mut buf = std::string::String::with_capacity(units.len() * 3);
1298 for result in std::char::decode_utf16(units.iter().copied()) {
1299 buf.push(result.unwrap_or('\u{FFFD}'));
1300 }
1301 buf
1302}
1303
1304#[inline(always)]
1306fn wtf16_into_string(units: &[u16], buf: &mut std::string::String) {
1307 #[cfg(feature = "simdutf")]
1308 {
1309 if units.len() >= WTF16_SIMD_THRESHOLD
1310 && crate::simdutf::validate_utf16le(units)
1311 {
1312 let utf8_len = crate::simdutf::utf8_length_from_utf16le(units);
1313 buf.reserve(utf8_len);
1314 unsafe {
1315 let vec = buf.as_mut_vec();
1316 let out = std::slice::from_raw_parts_mut(vec.as_mut_ptr(), utf8_len);
1317 let written = crate::simdutf::convert_utf16le_to_utf8(units, out);
1318 debug_assert_eq!(written, utf8_len);
1319 vec.set_len(written);
1320 }
1321 return;
1322 }
1323 }
1324 buf.reserve(units.len() * 3);
1326 for result in std::char::decode_utf16(units.iter().copied()) {
1327 buf.push(result.unwrap_or('\u{FFFD}'));
1328 }
1329}
1330
1331#[inline(always)]
1334fn latin1_to_cow_str<'a, const N: usize>(
1335 bytes: &[u8],
1336 buffer: &'a mut [MaybeUninit<u8>; N],
1337) -> Cow<'a, str> {
1338 #[cfg(feature = "simdutf")]
1339 let utf8_len = crate::simdutf::utf8_length_from_latin1(bytes);
1340 #[cfg(not(feature = "simdutf"))]
1341 let utf8_len = bytes.len() * 2; if utf8_len <= N {
1344 #[cfg(feature = "simdutf")]
1345 let written = unsafe {
1346 let out = std::slice::from_raw_parts_mut(
1347 buffer.as_mut_ptr() as *mut u8,
1348 utf8_len,
1349 );
1350 crate::simdutf::convert_latin1_to_utf8(bytes, out)
1351 };
1352 #[cfg(not(feature = "simdutf"))]
1353 let written = unsafe {
1354 latin1_to_utf8(
1355 bytes.len(),
1356 bytes.as_ptr(),
1357 buffer.as_mut_ptr() as *mut u8,
1358 )
1359 };
1360
1361 unsafe {
1362 let buf = &mut buffer[..written];
1363 let buf = &mut *(buf as *mut [_] as *mut [u8]);
1364 Cow::Borrowed(std::str::from_utf8_unchecked(buf))
1365 }
1366 } else {
1367 Cow::Owned(latin1_to_string(bytes))
1368 }
1369}
1370
1371#[inline(always)]
1374fn wtf16_to_cow_str<'a, const N: usize>(
1375 units: &[u16],
1376 buffer: &'a mut [MaybeUninit<u8>; N],
1377) -> Cow<'a, str> {
1378 #[cfg(feature = "simdutf")]
1379 {
1380 if units.len() >= WTF16_SIMD_THRESHOLD
1381 && crate::simdutf::validate_utf16le(units)
1382 {
1383 let utf8_len = crate::simdutf::utf8_length_from_utf16le(units);
1384
1385 if utf8_len <= N {
1386 let written = unsafe {
1387 let out = std::slice::from_raw_parts_mut(
1388 buffer.as_mut_ptr() as *mut u8,
1389 utf8_len,
1390 );
1391 crate::simdutf::convert_utf16le_to_utf8(units, out)
1392 };
1393 return unsafe {
1394 let buf = &mut buffer[..written];
1395 let buf = &mut *(buf as *mut [_] as *mut [u8]);
1396 Cow::Borrowed(std::str::from_utf8_unchecked(buf))
1397 };
1398 }
1399
1400 return Cow::Owned(wtf16_to_string(units));
1402 }
1403 }
1404
1405 let mut pos = 0;
1407 let mut tmp = [0u8; 4];
1408 let mut all_fit = true;
1409 for result in std::char::decode_utf16(units.iter().copied()) {
1410 let c = result.unwrap_or('\u{FFFD}');
1411 let encoded = c.encode_utf8(&mut tmp);
1412 if pos + encoded.len() > N {
1413 all_fit = false;
1414 break;
1415 }
1416 unsafe {
1417 std::ptr::copy_nonoverlapping(
1418 encoded.as_ptr(),
1419 (buffer.as_mut_ptr() as *mut u8).add(pos),
1420 encoded.len(),
1421 );
1422 }
1423 pos += encoded.len();
1424 }
1425 if all_fit {
1426 unsafe {
1427 let buf = &mut buffer[..pos];
1428 let buf = &mut *(buf as *mut [_] as *mut [u8]);
1429 Cow::Borrowed(std::str::from_utf8_unchecked(buf))
1430 }
1431 } else {
1432 Cow::Owned(std::string::String::from_utf16_lossy(units))
1433 }
1434}
1435
1436impl Drop for ValueView<'_> {
1437 fn drop(&mut self) {
1438 unsafe { v8__String__ValueView__DESTRUCT(self) }
1439 }
1440}