Skip to content

Commit 4249615

Browse files
Auto merge of #145024 - Kmeakin:km/optimize-slice-index/v3, r=<try>
Optimize indexing slices and strs with inclusive ranges
2 parents ca77504 + bd63d02 commit 4249615

File tree

7 files changed

+100
-118
lines changed

7 files changed

+100
-118
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/slice/index.rs

Lines changed: 78 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -67,20 +67,6 @@ const fn slice_index_order_fail(index: usize, end: usize) -> ! {
6767
)
6868
}
6969

70-
#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold)]
71-
#[cfg_attr(feature = "panic_immediate_abort", inline)]
72-
#[track_caller]
73-
const fn slice_start_index_overflow_fail() -> ! {
74-
panic!("attempted to index slice from after maximum usize");
75-
}
76-
77-
#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold)]
78-
#[cfg_attr(feature = "panic_immediate_abort", inline)]
79-
#[track_caller]
80-
const fn slice_end_index_overflow_fail() -> ! {
81-
panic!("attempted to index slice up to maximum usize");
82-
}
83-
8470
// The UbChecks are great for catching bugs in the unsafe methods, but including
8571
// them in safe indexing is unnecessary and hurts inlining and debug runtime perf.
8672
// Both the safe and unsafe public methods share these helpers,
@@ -658,7 +644,6 @@ unsafe impl<T> const SliceIndex<[T]> for ops::RangeFull {
658644
}
659645

660646
/// The methods `index` and `index_mut` panic if:
661-
/// - the end of the range is `usize::MAX` or
662647
/// - the start of the range is greater than the end of the range or
663648
/// - the end of the range is out of bounds.
664649
#[stable(feature = "inclusive_range", since = "1.26.0")]
@@ -668,12 +653,14 @@ unsafe impl<T> const SliceIndex<[T]> for ops::RangeInclusive<usize> {
668653

669654
#[inline]
670655
fn get(self, slice: &[T]) -> Option<&[T]> {
671-
if *self.end() == usize::MAX { None } else { self.into_slice_range().get(slice) }
656+
// `self.into_slice_range()` cannot overflow, because `*self.end() <
657+
// slice.len()` implies `*self.end() < usize::MAX`.
658+
if *self.end() >= slice.len() { None } else { self.into_slice_range().get(slice) }
672659
}
673660

674661
#[inline]
675662
fn get_mut(self, slice: &mut [T]) -> Option<&mut [T]> {
676-
if *self.end() == usize::MAX { None } else { self.into_slice_range().get_mut(slice) }
663+
if *self.end() >= slice.len() { None } else { self.into_slice_range().get_mut(slice) }
677664
}
678665

679666
#[inline]
@@ -690,16 +677,16 @@ unsafe impl<T> const SliceIndex<[T]> for ops::RangeInclusive<usize> {
690677

691678
#[inline]
692679
fn index(self, slice: &[T]) -> &[T] {
693-
if *self.end() == usize::MAX {
694-
slice_end_index_overflow_fail();
680+
if *self.end() >= slice.len() {
681+
slice_end_index_len_fail(*self.end(), slice.len());
695682
}
696683
self.into_slice_range().index(slice)
697684
}
698685

699686
#[inline]
700687
fn index_mut(self, slice: &mut [T]) -> &mut [T] {
701-
if *self.end() == usize::MAX {
702-
slice_end_index_overflow_fail();
688+
if *self.end() >= slice.len() {
689+
slice_end_index_len_fail(*self.end(), slice.len());
703690
}
704691
self.into_slice_range().index_mut(slice)
705692
}
@@ -852,28 +839,27 @@ where
852839
{
853840
let len = bounds.end;
854841

855-
let start = match range.start_bound() {
856-
ops::Bound::Included(&start) => start,
857-
ops::Bound::Excluded(start) => {
858-
start.checked_add(1).unwrap_or_else(|| slice_start_index_overflow_fail())
859-
}
860-
ops::Bound::Unbounded => 0,
861-
};
862-
863842
let end = match range.end_bound() {
864-
ops::Bound::Included(end) => {
865-
end.checked_add(1).unwrap_or_else(|| slice_end_index_overflow_fail())
866-
}
843+
ops::Bound::Included(&end) if end >= len => slice_end_index_len_fail(end, len),
844+
// Cannot overflow because `end < len` implies `end < usize::MAX`.
845+
ops::Bound::Included(&end) => end + 1,
846+
847+
ops::Bound::Excluded(&end) if end > len => slice_end_index_len_fail(end, len),
867848
ops::Bound::Excluded(&end) => end,
849+
868850
ops::Bound::Unbounded => len,
869851
};
870852

871-
if start > end {
872-
slice_index_order_fail(start, end);
873-
}
874-
if end > len {
875-
slice_end_index_len_fail(end, len);
876-
}
853+
let start = match range.start_bound() {
854+
ops::Bound::Excluded(&start) if start >= end => slice_index_order_fail(start, end),
855+
// Cannot overflow because `start < end` implies `start < usize::MAX`.
856+
ops::Bound::Excluded(&start) => start + 1,
857+
858+
ops::Bound::Included(&start) if start > end => slice_index_order_fail(start, end),
859+
ops::Bound::Included(&start) => start,
860+
861+
ops::Bound::Unbounded => 0,
862+
};
877863

878864
ops::Range { start, end }
879865
}
@@ -916,19 +902,29 @@ where
916902
{
917903
let len = bounds.end;
918904

919-
let start = match range.start_bound() {
920-
ops::Bound::Included(&start) => start,
921-
ops::Bound::Excluded(start) => start.checked_add(1)?,
922-
ops::Bound::Unbounded => 0,
923-
};
924-
925905
let end = match range.end_bound() {
926-
ops::Bound::Included(end) => end.checked_add(1)?,
906+
ops::Bound::Included(&end) if end >= len => return None,
907+
// Cannot overflow because `end < len` implies `end < usize::MAX`.
908+
ops::Bound::Included(end) => end + 1,
909+
910+
ops::Bound::Excluded(&end) if end > len => return None,
927911
ops::Bound::Excluded(&end) => end,
912+
928913
ops::Bound::Unbounded => len,
929914
};
930915

931-
if start > end || end > len { None } else { Some(ops::Range { start, end }) }
916+
let start = match range.start_bound() {
917+
ops::Bound::Excluded(&start) if start >= end => return None,
918+
// Cannot overflow because `start < end` implies `start < usize::MAX`.
919+
ops::Bound::Excluded(&start) => start + 1,
920+
921+
ops::Bound::Included(&start) if start > end => return None,
922+
ops::Bound::Included(&start) => start,
923+
924+
ops::Bound::Unbounded => 0,
925+
};
926+
927+
Some(ops::Range { start, end })
932928
}
933929

934930
/// Converts a pair of `ops::Bound`s into `ops::Range` without performing any
@@ -957,21 +953,27 @@ pub(crate) fn into_range(
957953
len: usize,
958954
(start, end): (ops::Bound<usize>, ops::Bound<usize>),
959955
) -> Option<ops::Range<usize>> {
960-
use ops::Bound;
961-
let start = match start {
962-
Bound::Included(start) => start,
963-
Bound::Excluded(start) => start.checked_add(1)?,
964-
Bound::Unbounded => 0,
965-
};
966-
967956
let end = match end {
968-
Bound::Included(end) => end.checked_add(1)?,
969-
Bound::Excluded(end) => end,
970-
Bound::Unbounded => len,
957+
ops::Bound::Included(end) if end >= len => return None,
958+
// Cannot overflow because `end < len` implies `end < usize::MAX`.
959+
ops::Bound::Included(end) => end + 1,
960+
961+
ops::Bound::Excluded(end) if end > len => return None,
962+
ops::Bound::Excluded(end) => end,
963+
964+
ops::Bound::Unbounded => len,
971965
};
972966

973-
// Don't bother with checking `start < end` and `end <= len`
974-
// since these checks are handled by `Range` impls
967+
let start = match start {
968+
ops::Bound::Excluded(start) if start >= end => return None,
969+
// Cannot overflow because `start < end` implies `start < usize::MAX`.
970+
ops::Bound::Excluded(start) => start + 1,
971+
972+
ops::Bound::Included(start) if start > end => return None,
973+
ops::Bound::Included(start) => start,
974+
975+
ops::Bound::Unbounded => 0,
976+
};
975977

976978
Some(start..end)
977979
}
@@ -982,25 +984,27 @@ pub(crate) fn into_slice_range(
982984
len: usize,
983985
(start, end): (ops::Bound<usize>, ops::Bound<usize>),
984986
) -> ops::Range<usize> {
985-
use ops::Bound;
986-
let start = match start {
987-
Bound::Included(start) => start,
988-
Bound::Excluded(start) => {
989-
start.checked_add(1).unwrap_or_else(|| slice_start_index_overflow_fail())
990-
}
991-
Bound::Unbounded => 0,
992-
};
993-
994987
let end = match end {
995-
Bound::Included(end) => {
996-
end.checked_add(1).unwrap_or_else(|| slice_end_index_overflow_fail())
997-
}
998-
Bound::Excluded(end) => end,
999-
Bound::Unbounded => len,
988+
ops::Bound::Included(end) if end >= len => slice_end_index_len_fail(end, len),
989+
// Cannot overflow because `end < len` implies `end < usize::MAX`.
990+
ops::Bound::Included(end) => end + 1,
991+
992+
ops::Bound::Excluded(end) if end > len => slice_end_index_len_fail(end, len),
993+
ops::Bound::Excluded(end) => end,
994+
995+
ops::Bound::Unbounded => len,
1000996
};
1001997

1002-
// Don't bother with checking `start < end` and `end <= len`
1003-
// since these checks are handled by `Range` impls
998+
let start = match start {
999+
ops::Bound::Excluded(start) if start >= end => slice_index_order_fail(start, end),
1000+
// Cannot overflow because `start < end` implies `start < usize::MAX`.
1001+
ops::Bound::Excluded(start) => start + 1,
1002+
1003+
ops::Bound::Included(start) if start > end => slice_index_order_fail(start, end),
1004+
ops::Bound::Included(start) => start,
1005+
1006+
ops::Bound::Unbounded => 0,
1007+
};
10041008

10051009
start..end
10061010
}

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: 12 additions & 25 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,15 +650,15 @@ 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+
if *self.end() >= slice.len() {
654+
super::slice_error_fail(slice, *self.start(), *self.end());
662655
}
663656
self.into_slice_range().index(slice)
664657
}
665658
#[inline]
666659
fn index_mut(self, slice: &mut str) -> &mut Self::Output {
667-
if *self.end() == usize::MAX {
668-
str_index_overflow_fail();
660+
if *self.end() >= slice.len() {
661+
super::slice_error_fail(slice, *self.start(), *self.end());
669662
}
670663
self.into_slice_range().index_mut(slice)
671664
}
@@ -677,35 +670,29 @@ unsafe impl const SliceIndex<str> for range::RangeInclusive<usize> {
677670
type Output = str;
678671
#[inline]
679672
fn get(self, slice: &str) -> Option<&Self::Output> {
680-
if self.end == usize::MAX { None } else { self.into_slice_range().get(slice) }
673+
ops::RangeInclusive::from(self).get(slice)
681674
}
682675
#[inline]
683676
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) }
677+
ops::RangeInclusive::from(self).get_mut(slice)
685678
}
686679
#[inline]
687680
unsafe fn get_unchecked(self, slice: *const str) -> *const Self::Output {
688681
// SAFETY: the caller must uphold the safety contract for `get_unchecked`.
689-
unsafe { self.into_slice_range().get_unchecked(slice) }
682+
unsafe { ops::RangeInclusive::from(self).get_unchecked(slice) }
690683
}
691684
#[inline]
692685
unsafe fn get_unchecked_mut(self, slice: *mut str) -> *mut Self::Output {
693686
// SAFETY: the caller must uphold the safety contract for `get_unchecked_mut`.
694-
unsafe { self.into_slice_range().get_unchecked_mut(slice) }
687+
unsafe { ops::RangeInclusive::from(self).get_unchecked_mut(slice) }
695688
}
696689
#[inline]
697690
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)
691+
ops::RangeInclusive::from(self).index(slice)
702692
}
703693
#[inline]
704694
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)
695+
ops::RangeInclusive::from(self).index_mut(slice)
709696
}
710697
}
711698

0 commit comments

Comments
 (0)