1#![deny(
9 unsafe_op_in_unsafe_fn,
10 clippy::undocumented_unsafe_blocks,
11 clippy::missing_safety_doc
12)]
13#![allow(clippy::module_name_repetitions)]
14
15mod builder;
16mod code_point;
17mod common;
18mod display;
19mod iter;
20mod str;
21mod r#type;
22mod vtable;
23
24#[cfg(test)]
25mod tests;
26
27use self::iter::Windows;
28use crate::display::{JsStrDisplayEscaped, JsStrDisplayLossy, JsStringDebugInfo};
29use crate::iter::CodePointsIter;
30use crate::r#type::{Latin1, Utf16};
31pub use crate::vtable::StaticString;
32use crate::vtable::{SequenceString, SliceString};
33#[doc(inline)]
34pub use crate::{
35 builder::{CommonJsStringBuilder, Latin1JsStringBuilder, Utf16JsStringBuilder},
36 code_point::CodePoint,
37 common::StaticJsStrings,
38 iter::Iter,
39 str::{JsStr, JsStrVariant},
40};
41use std::marker::PhantomData;
42use std::{borrow::Cow, mem::ManuallyDrop};
43use std::{
44 convert::Infallible,
45 hash::{Hash, Hasher},
46 ptr::{self, NonNull},
47 str::FromStr,
48};
49use vtable::JsStringVTable;
50
51fn alloc_overflow() -> ! {
52 panic!("detected overflow during string allocation")
53}
54
55pub(crate) const fn is_trimmable_whitespace(c: char) -> bool {
57 matches!(
66 c,
67 '\u{0009}' | '\u{000B}' | '\u{000C}' | '\u{0020}' | '\u{00A0}' | '\u{FEFF}' |
68 '\u{1680}' | '\u{2000}'
70 ..='\u{200A}' | '\u{202F}' | '\u{205F}' | '\u{3000}' |
71 '\u{000A}' | '\u{000D}' | '\u{2028}' | '\u{2029}'
73 )
74}
75
76pub(crate) const fn is_trimmable_whitespace_latin1(c: u8) -> bool {
78 matches!(
87 c,
88 0x09 | 0x0B | 0x0C | 0x20 | 0xA0 |
89 0x0A | 0x0D
91 )
92}
93
94#[allow(missing_copy_implementations, missing_debug_implementations)]
96pub struct RawJsString {
97 phantom_data: PhantomData<*mut ()>,
99}
100
101#[derive(Debug, Clone, Copy, Eq, PartialEq)]
104#[repr(u8)]
105pub(crate) enum JsStringKind {
106 Latin1Sequence = 0,
108
109 Utf16Sequence = 1,
111
112 Slice = 2,
114
115 Static = 3,
117}
118
119#[allow(clippy::module_name_repetitions)]
138pub struct JsString {
139 ptr: NonNull<JsStringVTable>,
142}
143
144static_assertions::assert_eq_size!(JsString, *const ());
146
147impl<'a> From<&'a JsString> for JsStr<'a> {
148 #[inline]
149 fn from(value: &'a JsString) -> Self {
150 value.as_str()
151 }
152}
153
154impl<'a> IntoIterator for &'a JsString {
155 type Item = u16;
156 type IntoIter = Iter<'a>;
157
158 #[inline]
159 fn into_iter(self) -> Self::IntoIter {
160 self.iter()
161 }
162}
163
164impl JsString {
165 #[inline]
167 #[must_use]
168 pub fn iter(&self) -> Iter<'_> {
169 self.as_str().iter()
170 }
171
172 #[inline]
174 #[must_use]
175 pub fn windows(&self, size: usize) -> Windows<'_> {
176 self.as_str().windows(size)
177 }
178
179 #[inline]
182 #[must_use]
183 pub fn to_std_string_escaped(&self) -> String {
184 self.display_escaped().to_string()
185 }
186
187 #[inline]
190 #[must_use]
191 pub fn to_std_string_lossy(&self) -> String {
192 self.display_lossy().to_string()
193 }
194
195 #[inline]
202 pub fn to_std_string(&self) -> Result<String, std::string::FromUtf16Error> {
203 self.as_str().to_std_string()
204 }
205
206 #[inline]
209 #[allow(clippy::missing_panics_doc)]
210 pub fn to_std_string_with_surrogates(
211 &self,
212 ) -> impl Iterator<Item = Result<String, u16>> + use<'_> {
213 let mut iter = self.code_points().peekable();
214
215 std::iter::from_fn(move || {
216 let cp = iter.next()?;
217 let char = match cp {
218 CodePoint::Unicode(c) => c,
219 CodePoint::UnpairedSurrogate(surr) => return Some(Err(surr)),
220 };
221
222 let mut string = String::from(char);
223
224 while let Some(cp) = iter.peek().and_then(|cp| match cp {
225 CodePoint::Unicode(c) => Some(*c),
226 CodePoint::UnpairedSurrogate(_) => None,
227 }) {
228 string.push(cp);
229 iter.next().expect("iter.peek() ensures that next is Some");
230 }
231
232 Some(Ok(string))
233 })
234 }
235
236 #[inline]
238 #[must_use]
239 pub fn map_valid_segments<F>(&self, mut f: F) -> Self
240 where
241 F: FnMut(String) -> String,
242 {
243 let mut text = Vec::new();
244
245 for part in self.to_std_string_with_surrogates() {
246 match part {
247 Ok(string) => text.extend(f(string).encode_utf16()),
248 Err(surr) => text.push(surr),
249 }
250 }
251
252 Self::from(&text[..])
253 }
254
255 #[inline]
257 #[must_use]
258 pub fn code_points(&self) -> CodePointsIter<'_> {
259 (self.vtable().code_points)(self.ptr)
260 }
261
262 #[inline]
264 #[must_use]
265 pub fn variant(&self) -> JsStrVariant<'_> {
266 self.as_str().variant()
267 }
268
269 #[inline]
279 #[must_use]
280 pub fn index_of(&self, search_value: JsStr<'_>, from_index: usize) -> Option<usize> {
281 self.as_str().index_of(search_value, from_index)
282 }
283
284 #[inline]
301 #[must_use]
302 pub fn code_point_at(&self, position: usize) -> CodePoint {
303 self.as_str().code_point_at(position)
304 }
305
306 #[inline]
313 #[must_use]
314 pub fn to_number(&self) -> f64 {
315 self.as_str().to_number()
316 }
317
318 #[inline]
320 #[must_use]
321 pub fn len(&self) -> usize {
322 self.vtable().len
323 }
324
325 #[inline]
327 #[must_use]
328 pub fn is_empty(&self) -> bool {
329 self.len() == 0
330 }
331
332 #[inline]
334 #[must_use]
335 pub fn to_vec(&self) -> Vec<u16> {
336 self.as_str().to_vec()
337 }
338
339 #[inline]
341 #[must_use]
342 pub fn contains(&self, element: u8) -> bool {
343 self.as_str().contains(element)
344 }
345
346 #[inline]
348 #[must_use]
349 pub fn trim(&self) -> JsString {
350 let (start, end) = match self.variant() {
352 JsStrVariant::Latin1(v) => {
353 let Some(start) = v.iter().position(|c| !is_trimmable_whitespace_latin1(*c)) else {
354 return StaticJsStrings::EMPTY_STRING;
355 };
356 let end = v
357 .iter()
358 .rposition(|c| !is_trimmable_whitespace_latin1(*c))
359 .unwrap_or(start);
360 (start, end)
361 }
362 JsStrVariant::Utf16(v) => {
363 let Some(start) = v.iter().copied().position(|r| {
364 !char::from_u32(u32::from(r)).is_some_and(is_trimmable_whitespace)
365 }) else {
366 return StaticJsStrings::EMPTY_STRING;
367 };
368 let end = v
369 .iter()
370 .copied()
371 .rposition(|r| {
372 !char::from_u32(u32::from(r)).is_some_and(is_trimmable_whitespace)
373 })
374 .unwrap_or(start);
375 (start, end)
376 }
377 };
378
379 unsafe { Self::slice_unchecked(self, start, end + 1) }
381 }
382
383 #[inline]
385 #[must_use]
386 pub fn trim_start(&self) -> JsString {
387 let Some(start) = (match self.variant() {
388 JsStrVariant::Latin1(v) => v.iter().position(|c| !is_trimmable_whitespace_latin1(*c)),
389 JsStrVariant::Utf16(v) => v
390 .iter()
391 .copied()
392 .position(|r| !char::from_u32(u32::from(r)).is_some_and(is_trimmable_whitespace)),
393 }) else {
394 return StaticJsStrings::EMPTY_STRING;
395 };
396
397 unsafe { Self::slice_unchecked(self, start, self.len()) }
399 }
400
401 #[inline]
403 #[must_use]
404 pub fn trim_end(&self) -> JsString {
405 let Some(end) = (match self.variant() {
406 JsStrVariant::Latin1(v) => v.iter().rposition(|c| !is_trimmable_whitespace_latin1(*c)),
407 JsStrVariant::Utf16(v) => v
408 .iter()
409 .copied()
410 .rposition(|r| !char::from_u32(u32::from(r)).is_some_and(is_trimmable_whitespace)),
411 }) else {
412 return StaticJsStrings::EMPTY_STRING;
413 };
414
415 unsafe { Self::slice_unchecked(self, 0, end + 1) }
418 }
419
420 #[inline]
422 #[must_use]
423 #[allow(clippy::missing_panics_doc)]
425 pub fn starts_with(&self, needle: JsStr<'_>) -> bool {
426 self.as_str().starts_with(needle)
427 }
428
429 #[inline]
431 #[must_use]
432 #[allow(clippy::missing_panics_doc)]
434 pub fn ends_with(&self, needle: JsStr<'_>) -> bool {
435 self.as_str().ends_with(needle)
436 }
437
438 #[inline]
441 #[must_use]
442 pub fn code_unit_at(&self, index: usize) -> Option<u16> {
443 self.as_str().get(index)
444 }
445
446 #[inline]
448 #[must_use]
449 pub fn get<I>(&self, index: I) -> Option<JsString>
450 where
451 I: JsStringSliceIndex,
452 {
453 index.get(self)
454 }
455
456 #[inline]
461 #[must_use]
462 pub fn get_expect<I>(&self, index: I) -> JsString
463 where
464 I: JsStringSliceIndex,
465 {
466 index.get(self).expect("Unexpected get()")
467 }
468
469 #[inline]
473 #[must_use]
474 pub fn display_escaped(&self) -> JsStrDisplayEscaped<'_> {
475 JsStrDisplayEscaped::from(self)
476 }
477
478 #[inline]
481 #[must_use]
482 pub fn display_lossy(&self) -> JsStrDisplayLossy<'_> {
483 self.as_str().display_lossy()
484 }
485
486 #[inline]
488 #[must_use]
489 pub fn debug_info(&self) -> JsStringDebugInfo<'_> {
490 self.into()
491 }
492
493 #[inline]
498 #[must_use]
499 pub fn into_raw(self) -> NonNull<RawJsString> {
500 ManuallyDrop::new(self).ptr.cast()
501 }
502
503 #[inline]
513 #[must_use]
514 pub const unsafe fn from_raw(ptr: NonNull<RawJsString>) -> Self {
515 Self { ptr: ptr.cast() }
516 }
517
518 #[inline]
525 #[must_use]
526 pub(crate) const unsafe fn from_ptr(ptr: NonNull<JsStringVTable>) -> Self {
527 Self { ptr }
528 }
529}
530
531static_assertions::const_assert!(align_of::<*const JsStr<'static>>() >= 2);
533
534impl JsString {
536 #[inline]
538 #[must_use]
539 pub fn is_static(&self) -> bool {
540 self.vtable().kind == JsStringKind::Static
542 }
543
544 #[inline]
546 #[must_use]
547 const fn vtable(&self) -> &JsStringVTable {
548 unsafe { self.ptr.as_ref() }
550 }
551
552 #[inline]
556 #[must_use]
557 pub const fn from_static(str: &'static StaticString) -> Self {
558 Self {
559 ptr: NonNull::from_ref(str).cast(),
560 }
561 }
562
563 #[inline]
572 #[must_use]
573 pub unsafe fn slice_unchecked(data: &JsString, start: usize, end: usize) -> Self {
574 let slice = Box::new(unsafe { SliceString::new(data, start, end) });
576
577 Self {
578 ptr: NonNull::from(Box::leak(slice)).cast(),
579 }
580 }
581
582 #[inline]
585 #[must_use]
586 pub fn slice(&self, p1: usize, mut p2: usize) -> JsString {
587 if p2 > self.len() {
588 p2 = self.len();
589 }
590 if p1 >= p2 {
591 StaticJsStrings::EMPTY_STRING
592 } else {
593 unsafe { Self::slice_unchecked(self, p1, p2) }
595 }
596 }
597
598 #[inline]
600 #[must_use]
601 pub(crate) fn kind(&self) -> JsStringKind {
602 self.vtable().kind
603 }
604
605 #[inline]
611 pub(crate) unsafe fn as_inner<T>(&self) -> &T {
612 unsafe { self.ptr.cast::<T>().as_ref() }
614 }
615}
616
617impl JsString {
618 #[inline]
620 #[must_use]
621 pub fn as_str(&self) -> JsStr<'_> {
622 (self.vtable().as_str)(self.ptr)
623 }
624
625 #[inline]
627 #[must_use]
628 pub fn concat(x: JsStr<'_>, y: JsStr<'_>) -> Self {
629 Self::concat_array(&[x, y])
630 }
631
632 #[inline]
635 #[must_use]
636 pub fn concat_array(strings: &[JsStr<'_>]) -> Self {
637 let mut latin1_encoding = true;
638 let mut full_count = 0usize;
639 for string in strings {
640 let Some(sum) = full_count.checked_add(string.len()) else {
641 alloc_overflow()
642 };
643 if !string.is_latin1() {
644 latin1_encoding = false;
645 }
646 full_count = sum;
647 }
648
649 let (ptr, data_offset) = if latin1_encoding {
650 let p = SequenceString::<Latin1>::allocate(full_count);
651 (p.cast::<u8>(), size_of::<SequenceString<Latin1>>())
652 } else {
653 let p = SequenceString::<Utf16>::allocate(full_count);
654 (p.cast::<u8>(), size_of::<SequenceString<Utf16>>())
655 };
656
657 let string = {
658 let mut data = unsafe {
660 let seq_ptr = ptr.as_ptr();
661 seq_ptr.add(data_offset)
662 };
663 for &string in strings {
664 unsafe {
675 #[allow(clippy::cast_ptr_alignment)]
677 match (latin1_encoding, string.variant()) {
678 (true, JsStrVariant::Latin1(s)) => {
679 let count = s.len();
680 ptr::copy_nonoverlapping(s.as_ptr(), data.cast::<u8>(), count);
681 data = data.cast::<u8>().add(count).cast::<u8>();
682 }
683 (false, JsStrVariant::Latin1(s)) => {
684 let count = s.len();
685 for (i, byte) in s.iter().enumerate() {
686 *data.cast::<u16>().add(i) = u16::from(*byte);
687 }
688 data = data.cast::<u16>().add(count).cast::<u8>();
689 }
690 (false, JsStrVariant::Utf16(s)) => {
691 let count = s.len();
692 ptr::copy_nonoverlapping(s.as_ptr(), data.cast::<u16>(), count);
693 data = data.cast::<u16>().add(count).cast::<u8>();
694 }
695 (true, JsStrVariant::Utf16(_)) => {
696 unreachable!("Already checked that it's latin1 encoding")
697 }
698 }
699 }
700 }
701
702 Self { ptr: ptr.cast() }
703 };
704
705 StaticJsStrings::get_string(&string.as_str()).unwrap_or(string)
706 }
707
708 fn from_slice_skip_interning(string: JsStr<'_>) -> Self {
710 let count = string.len();
711
712 unsafe {
721 #[allow(clippy::cast_ptr_alignment)]
723 match string.variant() {
724 JsStrVariant::Latin1(s) => {
725 let ptr = SequenceString::<Latin1>::allocate(count);
726 let data = (&raw mut (*ptr.as_ptr()).data)
727 .cast::<<Latin1 as r#type::StringType>::Byte>();
728 ptr::copy_nonoverlapping(s.as_ptr(), data, count);
729 Self { ptr: ptr.cast() }
730 }
731 JsStrVariant::Utf16(s) => {
732 let ptr = SequenceString::<Utf16>::allocate(count);
733 let data = (&raw mut (*ptr.as_ptr()).data)
734 .cast::<<Utf16 as r#type::StringType>::Byte>();
735 ptr::copy_nonoverlapping(s.as_ptr(), data, count);
736 Self { ptr: ptr.cast() }
737 }
738 }
739 }
740 }
741
742 fn from_js_str(string: JsStr<'_>) -> Self {
744 if let Some(s) = StaticJsStrings::get_string(&string) {
745 return s;
746 }
747 Self::from_slice_skip_interning(string)
748 }
749
750 #[inline]
752 #[must_use]
753 pub fn refcount(&self) -> Option<usize> {
754 (self.vtable().refcount)(self.ptr)
755 }
756}
757
758impl Clone for JsString {
759 #[inline]
760 fn clone(&self) -> Self {
761 (self.vtable().clone)(self.ptr)
762 }
763}
764
765impl Default for JsString {
766 #[inline]
767 fn default() -> Self {
768 StaticJsStrings::EMPTY_STRING
769 }
770}
771
772impl Drop for JsString {
773 #[inline]
774 fn drop(&mut self) {
775 (self.vtable().drop)(self.ptr);
776 }
777}
778
779impl std::fmt::Debug for JsString {
780 #[inline]
781 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
782 f.debug_tuple("JsString")
783 .field(&self.display_escaped().to_string())
784 .finish()
785 }
786}
787
788impl Eq for JsString {}
789
790macro_rules! impl_from_number_for_js_string {
791 ($($module: ident => $($ty:ty),+)+) => {
792 $(
793 $(
794 impl From<$ty> for JsString {
795 #[inline]
796 fn from(value: $ty) -> Self {
797 JsString::from_slice_skip_interning(JsStr::latin1(
798 $module::Buffer::new().format(value).as_bytes(),
799 ))
800 }
801 }
802 )+
803 )+
804 };
805}
806
807impl_from_number_for_js_string!(
808 itoa => i8, i16, i32, i64, i128, u8, u16, u32, u64, u128, isize, usize
809 ryu_js => f32, f64
810);
811
812impl From<&[u16]> for JsString {
813 #[inline]
814 fn from(s: &[u16]) -> Self {
815 JsString::from_js_str(JsStr::utf16(s))
816 }
817}
818
819impl From<&str> for JsString {
820 #[inline]
821 fn from(s: &str) -> Self {
822 if s.is_ascii() {
823 let js_str = JsStr::latin1(s.as_bytes());
824 return StaticJsStrings::get_string(&js_str)
825 .unwrap_or_else(|| JsString::from_slice_skip_interning(js_str));
826 }
827 if s.chars().all(|c| c as u32 <= 0xFF) {
829 let bytes: Vec<u8> = s.chars().map(|c| c as u8).collect();
830 let js_str = JsStr::latin1(&bytes);
831 return StaticJsStrings::get_string(&js_str)
832 .unwrap_or_else(|| JsString::from_slice_skip_interning(js_str));
833 }
834 let s = s.encode_utf16().collect::<Vec<_>>();
835 JsString::from_slice_skip_interning(JsStr::utf16(&s[..]))
836 }
837}
838
839impl From<JsStr<'_>> for JsString {
840 #[inline]
841 fn from(value: JsStr<'_>) -> Self {
842 StaticJsStrings::get_string(&value)
843 .unwrap_or_else(|| JsString::from_slice_skip_interning(value))
844 }
845}
846
847impl From<&[JsString]> for JsString {
848 #[inline]
849 fn from(value: &[JsString]) -> Self {
850 Self::concat_array(&value.iter().map(Self::as_str).collect::<Vec<_>>()[..])
851 }
852}
853
854impl<const N: usize> From<&[JsString; N]> for JsString {
855 #[inline]
856 fn from(value: &[JsString; N]) -> Self {
857 Self::concat_array(&value.iter().map(Self::as_str).collect::<Vec<_>>()[..])
858 }
859}
860
861impl From<String> for JsString {
862 #[inline]
863 fn from(s: String) -> Self {
864 Self::from(s.as_str())
865 }
866}
867
868impl<'a> From<Cow<'a, str>> for JsString {
869 #[inline]
870 fn from(s: Cow<'a, str>) -> Self {
871 match s {
872 Cow::Borrowed(s) => s.into(),
873 Cow::Owned(s) => s.into(),
874 }
875 }
876}
877
878impl<const N: usize> From<&[u16; N]> for JsString {
879 #[inline]
880 fn from(s: &[u16; N]) -> Self {
881 Self::from(&s[..])
882 }
883}
884
885impl Hash for JsString {
886 #[inline]
887 fn hash<H: Hasher>(&self, state: &mut H) {
888 self.as_str().hash(state);
889 }
890}
891
892impl PartialOrd for JsStr<'_> {
893 #[inline]
894 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
895 Some(self.cmp(other))
896 }
897}
898
899impl Ord for JsString {
900 #[inline]
901 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
902 self.as_str().cmp(&other.as_str())
903 }
904}
905
906impl PartialEq for JsString {
907 #[inline]
908 fn eq(&self, other: &Self) -> bool {
909 self.as_str() == other.as_str()
910 }
911}
912
913impl PartialEq<JsString> for [u16] {
914 #[inline]
915 fn eq(&self, other: &JsString) -> bool {
916 if self.len() != other.len() {
917 return false;
918 }
919 for (x, y) in self.iter().copied().zip(other.iter()) {
920 if x != y {
921 return false;
922 }
923 }
924 true
925 }
926}
927
928impl<const N: usize> PartialEq<JsString> for [u16; N] {
929 #[inline]
930 fn eq(&self, other: &JsString) -> bool {
931 self[..] == *other
932 }
933}
934
935impl PartialEq<[u16]> for JsString {
936 #[inline]
937 fn eq(&self, other: &[u16]) -> bool {
938 other == self
939 }
940}
941
942impl<const N: usize> PartialEq<[u16; N]> for JsString {
943 #[inline]
944 fn eq(&self, other: &[u16; N]) -> bool {
945 *self == other[..]
946 }
947}
948
949impl PartialEq<str> for JsString {
950 #[inline]
951 fn eq(&self, other: &str) -> bool {
952 self.as_str() == other
953 }
954}
955
956impl PartialEq<&str> for JsString {
957 #[inline]
958 fn eq(&self, other: &&str) -> bool {
959 self.as_str() == *other
960 }
961}
962
963impl PartialEq<JsString> for str {
964 #[inline]
965 fn eq(&self, other: &JsString) -> bool {
966 other == self
967 }
968}
969
970impl PartialEq<JsStr<'_>> for JsString {
971 #[inline]
972 fn eq(&self, other: &JsStr<'_>) -> bool {
973 self.as_str() == *other
974 }
975}
976
977impl PartialEq<JsString> for JsStr<'_> {
978 #[inline]
979 fn eq(&self, other: &JsString) -> bool {
980 other == self
981 }
982}
983
984impl PartialOrd for JsString {
985 #[inline]
986 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
987 Some(self.cmp(other))
988 }
989}
990
991impl FromStr for JsString {
992 type Err = Infallible;
993
994 #[inline]
995 fn from_str(s: &str) -> Result<Self, Self::Err> {
996 Ok(Self::from(s))
997 }
998}
999
1000pub trait JsStringSliceIndex {
1003 fn get(self, str: &JsString) -> Option<JsString>;
1005}
1006
1007macro_rules! impl_js_string_slice_index {
1008 ($($type:ty),+ $(,)?) => {
1009 $(
1010 impl JsStringSliceIndex for $type {
1011 fn get(self, str: &JsString) -> Option<JsString> {
1012 let start = match std::ops::RangeBounds::<usize>::start_bound(&self) {
1013 std::ops::Bound::Included(start) => *start,
1014 std::ops::Bound::Excluded(start) => *start + 1,
1015 std::ops::Bound::Unbounded => 0,
1016 };
1017
1018 let end = match std::ops::RangeBounds::<usize>::end_bound(&self) {
1019 std::ops::Bound::Included(end) => *end + 1,
1020 std::ops::Bound::Excluded(end) => *end,
1021 std::ops::Bound::Unbounded => str.len(),
1022 };
1023
1024 if end > str.len() || start > end {
1025 None
1026 } else {
1027 Some(unsafe { JsString::slice_unchecked(str, start, end) })
1029 }
1030 }
1031 }
1032 )+
1033 };
1034}
1035
1036impl_js_string_slice_index!(
1037 std::ops::Range<usize>,
1038 std::ops::RangeInclusive<usize>,
1039 std::ops::RangeTo<usize>,
1040 std::ops::RangeToInclusive<usize>,
1041 std::ops::RangeFrom<usize>,
1042 std::ops::RangeFull,
1043);