Skip to content

Commit 7bc62e2

Browse files
committed
Optimize SliceIndex<str> for RangeInclusive
1 parent 5982f03 commit 7bc62e2

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
@@ -333,15 +333,6 @@ impl<Idx: Step> RangeInclusive<Idx> {
333333
}
334334
}
335335

336-
impl RangeInclusive<usize> {
337-
/// Converts to an exclusive `Range` for `SliceIndex` implementations.
338-
/// The caller is responsible for dealing with `end == usize::MAX`.
339-
#[inline]
340-
pub(crate) const fn into_slice_range(self) -> Range<usize> {
341-
Range { start: self.start, end: self.end + 1 }
342-
}
343-
}
344-
345336
#[unstable(feature = "new_range_api", issue = "125687")]
346337
impl<T> RangeBounds<T> for RangeInclusive<T> {
347338
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
@@ -75,13 +75,6 @@ where
7575
}
7676
}
7777

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

@@ -677,35 +690,29 @@ unsafe impl const SliceIndex<str> for range::RangeInclusive<usize> {
677690
type Output = str;
678691
#[inline]
679692
fn get(self, slice: &str) -> Option<&Self::Output> {
680-
if self.end == usize::MAX { None } else { self.into_slice_range().get(slice) }
693+
ops::RangeInclusive::from(self).get(slice)
681694
}
682695
#[inline]
683696
fn get_mut(self, slice: &mut str) -> Option<&mut Self::Output> {
684-
if self.end == usize::MAX { None } else { self.into_slice_range().get_mut(slice) }
697+
ops::RangeInclusive::from(self).get_mut(slice)
685698
}
686699
#[inline]
687700
unsafe fn get_unchecked(self, slice: *const str) -> *const Self::Output {
688701
// SAFETY: the caller must uphold the safety contract for `get_unchecked`.
689-
unsafe { self.into_slice_range().get_unchecked(slice) }
702+
unsafe { ops::RangeInclusive::from(self).get_unchecked(slice) }
690703
}
691704
#[inline]
692705
unsafe fn get_unchecked_mut(self, slice: *mut str) -> *mut Self::Output {
693706
// SAFETY: the caller must uphold the safety contract for `get_unchecked_mut`.
694-
unsafe { self.into_slice_range().get_unchecked_mut(slice) }
707+
unsafe { ops::RangeInclusive::from(self).get_unchecked_mut(slice) }
695708
}
696709
#[inline]
697710
fn index(self, slice: &str) -> &Self::Output {
698-
if self.end == usize::MAX {
699-
str_index_overflow_fail();
700-
}
701-
self.into_slice_range().index(slice)
711+
ops::RangeInclusive::from(self).index(slice)
702712
}
703713
#[inline]
704714
fn index_mut(self, slice: &mut str) -> &mut Self::Output {
705-
if self.end == usize::MAX {
706-
str_index_overflow_fail();
707-
}
708-
self.into_slice_range().index_mut(slice)
715+
ops::RangeInclusive::from(self).index_mut(slice)
709716
}
710717
}
711718

0 commit comments

Comments
 (0)