X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=library%2Fcore%2Fsrc%2Fslice%2Findex.rs;h=f722430354991961f314ba0091a414b2b6d9dbe8;hb=6763a40eff7221fc23f22cdac453e2843344a357;hp=c92b37b14be4fa91d021d66822095ff000600db4;hpb=409920873cf8a95739a55dc5fe5adb05e1b4758e;p=rust.git diff --git a/library/core/src/slice/index.rs b/library/core/src/slice/index.rs index c92b37b14be..f7224303549 100644 --- a/library/core/src/slice/index.rs +++ b/library/core/src/slice/index.rs @@ -81,6 +81,8 @@ impl Sealed for ops::RangeFull {} impl Sealed for ops::RangeInclusive {} #[stable(feature = "slice_get_slice", since = "1.28.0")] impl Sealed for ops::RangeToInclusive {} + #[stable(feature = "slice_index_with_ops_bound_pair", since = "1.53.0")] + impl Sealed for (ops::Bound, ops::Bound) {} } /// A helper trait used for indexing operations. @@ -546,3 +548,113 @@ pub fn range(range: R, bounds: ops::RangeTo) -> ops::Range ops::Range { start, end } } + +/// Convert pair of `ops::Bound`s into `ops::Range` without performing any bounds checking and (in debug) overflow checking +fn into_range_unchecked( + len: usize, + (start, end): (ops::Bound, ops::Bound), +) -> ops::Range { + use ops::Bound; + let start = match start { + Bound::Included(i) => i, + Bound::Excluded(i) => i + 1, + Bound::Unbounded => 0, + }; + let end = match end { + Bound::Included(i) => i + 1, + Bound::Excluded(i) => i, + Bound::Unbounded => len, + }; + start..end +} + +/// Convert pair of `ops::Bound`s into `ops::Range`. +/// Returns `None` on overflowing indices. +fn into_range( + len: usize, + (start, end): (ops::Bound, ops::Bound), +) -> Option> { + use ops::Bound; + let start = match start { + Bound::Included(start) => start, + Bound::Excluded(start) => start.checked_add(1)?, + Bound::Unbounded => 0, + }; + + let end = match end { + Bound::Included(end) => end.checked_add(1)?, + Bound::Excluded(end) => end, + Bound::Unbounded => len, + }; + + // Don't bother with checking `start < end` and `end <= len` + // since these checks are handled by `Range` impls + + Some(start..end) +} + +/// Convert pair of `ops::Bound`s into `ops::Range`. +/// Panics on overflowing indices. +fn into_slice_range( + len: usize, + (start, end): (ops::Bound, ops::Bound), +) -> ops::Range { + use ops::Bound; + let start = match start { + Bound::Included(start) => start, + Bound::Excluded(start) => { + start.checked_add(1).unwrap_or_else(|| slice_start_index_overflow_fail()) + } + Bound::Unbounded => 0, + }; + + let end = match end { + Bound::Included(end) => { + end.checked_add(1).unwrap_or_else(|| slice_end_index_overflow_fail()) + } + Bound::Excluded(end) => end, + Bound::Unbounded => len, + }; + + // Don't bother with checking `start < end` and `end <= len` + // since these checks are handled by `Range` impls + + start..end +} + +#[stable(feature = "slice_index_with_ops_bound_pair", since = "1.53.0")] +unsafe impl SliceIndex<[T]> for (ops::Bound, ops::Bound) { + type Output = [T]; + + #[inline] + fn get(self, slice: &[T]) -> Option<&Self::Output> { + into_range(slice.len(), self)?.get(slice) + } + + #[inline] + fn get_mut(self, slice: &mut [T]) -> Option<&mut Self::Output> { + into_range(slice.len(), self)?.get_mut(slice) + } + + #[inline] + unsafe fn get_unchecked(self, slice: *const [T]) -> *const Self::Output { + // SAFETY: the caller has to uphold the safety contract for `get_unchecked`. + unsafe { into_range_unchecked(slice.len(), self).get_unchecked(slice) } + } + + #[inline] + unsafe fn get_unchecked_mut(self, slice: *mut [T]) -> *mut Self::Output { + // SAFETY: the caller has to uphold the safety contract for `get_unchecked_mut`. + unsafe { into_range_unchecked(slice.len(), self).get_unchecked_mut(slice) } + } + + #[inline] + fn index(self, slice: &[T]) -> &Self::Output { + into_slice_range(slice.len(), self).index(slice) + } + + #[inline] + fn index_mut(self, slice: &mut [T]) -> &mut Self::Output { + into_slice_range(slice.len(), self).index_mut(slice) + } +}