From bb70e52f5f5ee25012193f9f4f4372702b97b20e Mon Sep 17 00:00:00 2001 From: dylni <46035563+dylni@users.noreply.github.com> Date: Wed, 5 Aug 2020 22:32:45 -0400 Subject: [PATCH] Add `slice::check_range` --- library/core/src/slice/mod.rs | 93 ++++++++++++++++++++++++++++++++--- 1 file changed, 87 insertions(+), 6 deletions(-) diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index 93608a1ce48..d5e07629a52 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -30,7 +30,7 @@ use crate::intrinsics::{assume, exact_div, is_aligned_and_not_null, unchecked_su use crate::iter::*; use crate::marker::{self, Copy, Send, Sized, Sync}; use crate::mem; -use crate::ops::{self, FnMut, Range}; +use crate::ops::{self, Bound, FnMut, Range, RangeBounds}; use crate::option::Option; use crate::option::Option::{None, Some}; use crate::ptr::{self, NonNull}; @@ -350,6 +350,80 @@ impl [T] { unsafe { &mut *index.get_unchecked_mut(self) } } + /// Converts a range over this slice to [`Range`]. + /// + /// The returned range is safe to pass to [`get_unchecked`] and [`get_unchecked_mut`]. + /// + /// [`get_unchecked`]: #method.get_unchecked + /// [`get_unchecked_mut`]: #method.get_unchecked_mut + /// [`Range`]: ../ops/struct.Range.html + /// + /// # Panics + /// + /// Panics if the range is out of bounds. + /// + /// # Examples + /// + /// ``` + /// #![feature(slice_check_range)] + /// + /// let v = [10, 40, 30]; + /// assert_eq!(1..2, v.check_range(1..2)); + /// assert_eq!(0..2, v.check_range(..2)); + /// assert_eq!(1..3, v.check_range(1..)); + /// ``` + /// + /// Panics when [`Index::index`] would panic: + /// + /// ```should_panic + /// #![feature(slice_check_range)] + /// + /// [10, 40, 30].check_range(2..1); + /// ``` + /// + /// ```should_panic + /// #![feature(slice_check_range)] + /// + /// [10, 40, 30].check_range(1..4); + /// ``` + /// + /// ```should_panic + /// #![feature(slice_check_range)] + /// + /// [10, 40, 30].check_range(1..=usize::MAX); + /// ``` + /// + /// [`Index::index`]: ../ops/trait.Index.html#tymethod.index + #[track_caller] + #[unstable(feature = "slice_check_range", issue = "none")] + pub fn check_range>(&self, range: R) -> Range { + let start = match range.start_bound() { + Bound::Included(&start) => start, + Bound::Excluded(start) => { + start.checked_add(1).unwrap_or_else(|| slice_start_index_overflow_fail()) + } + Bound::Unbounded => 0, + }; + + let len = self.len(); + let end = match range.end_bound() { + Bound::Included(end) => { + end.checked_add(1).unwrap_or_else(|| slice_end_index_overflow_fail()) + } + Bound::Excluded(&end) => end, + Bound::Unbounded => len, + }; + + if start > end { + slice_index_order_fail(start, end); + } + if end > len { + slice_end_index_len_fail(end, len); + } + + Range { start, end } + } + /// Returns a raw pointer to the slice's buffer. /// /// The caller must ensure that the slice outlives the pointer this @@ -2445,13 +2519,13 @@ impl [T] { let src_start = match src.start_bound() { ops::Bound::Included(&n) => n, ops::Bound::Excluded(&n) => { - n.checked_add(1).unwrap_or_else(|| slice_index_overflow_fail()) + n.checked_add(1).unwrap_or_else(|| slice_start_index_overflow_fail()) } ops::Bound::Unbounded => 0, }; let src_end = match src.end_bound() { ops::Bound::Included(&n) => { - n.checked_add(1).unwrap_or_else(|| slice_index_overflow_fail()) + n.checked_add(1).unwrap_or_else(|| slice_end_index_overflow_fail()) } ops::Bound::Excluded(&n) => n, ops::Bound::Unbounded => self.len(), @@ -3034,7 +3108,14 @@ fn slice_index_order_fail(index: usize, end: usize) -> ! { #[inline(never)] #[cold] #[track_caller] -fn slice_index_overflow_fail() -> ! { +fn slice_start_index_overflow_fail() -> ! { + panic!("attempted to index slice from after maximum usize"); +} + +#[inline(never)] +#[cold] +#[track_caller] +fn slice_end_index_overflow_fail() -> ! { panic!("attempted to index slice up to maximum usize"); } @@ -3370,7 +3451,7 @@ unsafe impl SliceIndex<[T]> for ops::RangeInclusive { #[inline] fn index(self, slice: &[T]) -> &[T] { if *self.end() == usize::MAX { - slice_index_overflow_fail(); + slice_end_index_overflow_fail(); } (*self.start()..self.end() + 1).index(slice) } @@ -3378,7 +3459,7 @@ unsafe impl SliceIndex<[T]> for ops::RangeInclusive { #[inline] fn index_mut(self, slice: &mut [T]) -> &mut [T] { if *self.end() == usize::MAX { - slice_index_overflow_fail(); + slice_end_index_overflow_fail(); } (*self.start()..self.end() + 1).index_mut(slice) }