Skip to content

Commit 6f5164d

Browse files
committed
Optimize SliceIndex<str> for RangeInclusive
1 parent 0973498 commit 6f5164d

File tree

4 files changed

+37
-39
lines changed

4 files changed

+37
-39
lines changed

library/alloctests/tests/str.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -631,13 +631,13 @@ mod slice_index {
631631
// note: using 0 specifically ensures that the result of overflowing is 0..0,
632632
// so that `get` doesn't simply return None for the wrong reason.
633633
bad: data[0..=usize::MAX];
634-
message: "maximum usize";
634+
message: "out of bounds";
635635
}
636636

637637
in mod rangetoinclusive {
638638
data: "hello";
639639
bad: data[..=usize::MAX];
640-
message: "maximum usize";
640+
message: "out of bounds";
641641
}
642642
}
643643
}

library/core/src/range.rs

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -332,15 +332,6 @@ impl<Idx: Step> RangeInclusive<Idx> {
332332
}
333333
}
334334

335-
impl RangeInclusive<usize> {
336-
/// Converts to an exclusive `Range` for `SliceIndex` implementations.
337-
/// The caller is responsible for dealing with `last == usize::MAX`.
338-
#[inline]
339-
pub(crate) const fn into_slice_range(self) -> Range<usize> {
340-
Range { start: self.start, end: self.last + 1 }
341-
}
342-
}
343-
344335
#[unstable(feature = "new_range_api", issue = "125687")]
345336
impl<T> RangeBounds<T> for RangeInclusive<T> {
346337
fn start_bound(&self) -> Bound<&T> {

library/core/src/str/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ fn slice_error_fail_rt(s: &str, begin: usize, end: usize) -> ! {
8787
let ellipsis = if trunc_len < s.len() { "[...]" } else { "" };
8888

8989
// 1. out of bounds
90-
if begin > s.len() || end > s.len() {
90+
if begin > s.len() || end > s.len() || (end == s.len() && s.is_char_boundary(begin)) {
9191
let oob_index = if begin > s.len() { begin } else { end };
9292
panic!("byte index {oob_index} is out of bounds of `{s_trunc}`{ellipsis}");
9393
}

library/core/src/str/traits.rs

Lines changed: 34 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -76,13 +76,6 @@ where
7676
}
7777
}
7878

79-
#[inline(never)]
80-
#[cold]
81-
#[track_caller]
82-
const fn str_index_overflow_fail() -> ! {
83-
panic!("attempted to index str up to maximum usize");
84-
}
85-
8679
/// Implements substring slicing with syntax `&self[..]` or `&mut self[..]`.
8780
///
8881
/// Returns a slice of the whole string, i.e., returns `&self` or `&mut
@@ -640,11 +633,11 @@ unsafe impl const SliceIndex<str> for ops::RangeInclusive<usize> {
640633
type Output = str;
641634
#[inline]
642635
fn get(self, slice: &str) -> Option<&Self::Output> {
643-
if *self.end() == usize::MAX { None } else { self.into_slice_range().get(slice) }
636+
if *self.end() >= slice.len() { None } else { self.into_slice_range().get(slice) }
644637
}
645638
#[inline]
646639
fn get_mut(self, slice: &mut str) -> Option<&mut Self::Output> {
647-
if *self.end() == usize::MAX { None } else { self.into_slice_range().get_mut(slice) }
640+
if *self.end() >= slice.len() { None } else { self.into_slice_range().get_mut(slice) }
648641
}
649642
#[inline]
650643
unsafe fn get_unchecked(self, slice: *const str) -> *const Self::Output {
@@ -658,17 +651,37 @@ unsafe impl const SliceIndex<str> for ops::RangeInclusive<usize> {
658651
}
659652
#[inline]
660653
fn index(self, slice: &str) -> &Self::Output {
661-
if *self.end() == usize::MAX {
662-
str_index_overflow_fail();
654+
let Self { mut start, mut end, exhausted } = self;
655+
let len = slice.len();
656+
if end < len {
657+
end = end + 1;
658+
start = if exhausted { end } else { start };
659+
if start <= end && slice.is_char_boundary(start) && slice.is_char_boundary(end) {
660+
// SAFETY: just checked that `start` and `end` are on a char boundary,
661+
// and we are passing in a safe reference, so the return value will also be one.
662+
// We also checked char boundaries, so this is valid UTF-8.
663+
unsafe { return &*(start..end).get_unchecked(slice) };
664+
}
663665
}
664-
self.into_slice_range().index(slice)
666+
667+
super::slice_error_fail(slice, start, end);
665668
}
666669
#[inline]
667670
fn index_mut(self, slice: &mut str) -> &mut Self::Output {
668-
if *self.end() == usize::MAX {
669-
str_index_overflow_fail();
671+
let Self { mut start, mut end, exhausted } = self;
672+
let len = slice.len();
673+
if end < len {
674+
end = end + 1;
675+
start = if exhausted { end } else { start };
676+
if start <= end && slice.is_char_boundary(start) && slice.is_char_boundary(end) {
677+
// SAFETY: just checked that `start` and `end` are on a char boundary,
678+
// and we are passing in a safe reference, so the return value will also be one.
679+
// We also checked char boundaries, so this is valid UTF-8.
680+
unsafe { return &mut *(start..end).get_unchecked_mut(slice) };
681+
}
670682
}
671-
self.into_slice_range().index_mut(slice)
683+
684+
super::slice_error_fail(slice, start, end);
672685
}
673686
}
674687

@@ -678,35 +691,29 @@ unsafe impl const SliceIndex<str> for range::RangeInclusive<usize> {
678691
type Output = str;
679692
#[inline]
680693
fn get(self, slice: &str) -> Option<&Self::Output> {
681-
if self.last == usize::MAX { None } else { self.into_slice_range().get(slice) }
694+
ops::RangeInclusive::from(self).get(slice)
682695
}
683696
#[inline]
684697
fn get_mut(self, slice: &mut str) -> Option<&mut Self::Output> {
685-
if self.last == usize::MAX { None } else { self.into_slice_range().get_mut(slice) }
698+
ops::RangeInclusive::from(self).get_mut(slice)
686699
}
687700
#[inline]
688701
unsafe fn get_unchecked(self, slice: *const str) -> *const Self::Output {
689702
// SAFETY: the caller must uphold the safety contract for `get_unchecked`.
690-
unsafe { self.into_slice_range().get_unchecked(slice) }
703+
unsafe { ops::RangeInclusive::from(self).get_unchecked(slice) }
691704
}
692705
#[inline]
693706
unsafe fn get_unchecked_mut(self, slice: *mut str) -> *mut Self::Output {
694707
// SAFETY: the caller must uphold the safety contract for `get_unchecked_mut`.
695-
unsafe { self.into_slice_range().get_unchecked_mut(slice) }
708+
unsafe { ops::RangeInclusive::from(self).get_unchecked_mut(slice) }
696709
}
697710
#[inline]
698711
fn index(self, slice: &str) -> &Self::Output {
699-
if self.last == usize::MAX {
700-
str_index_overflow_fail();
701-
}
702-
self.into_slice_range().index(slice)
712+
ops::RangeInclusive::from(self).index(slice)
703713
}
704714
#[inline]
705715
fn index_mut(self, slice: &mut str) -> &mut Self::Output {
706-
if self.last == usize::MAX {
707-
str_index_overflow_fail();
708-
}
709-
self.into_slice_range().index_mut(slice)
716+
ops::RangeInclusive::from(self).index_mut(slice)
710717
}
711718
}
712719

0 commit comments

Comments
 (0)