about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--library/core/src/slice/index.rs112
-rw-r--r--library/core/tests/slice.rs43
2 files changed, 155 insertions, 0 deletions
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 @@ mod private_slice_index {
     impl Sealed for ops::RangeInclusive<usize> {}
     #[stable(feature = "slice_get_slice", since = "1.28.0")]
     impl Sealed for ops::RangeToInclusive<usize> {}
+    #[stable(feature = "slice_index_with_ops_bound_pair", since = "1.53.0")]
+    impl Sealed for (ops::Bound<usize>, ops::Bound<usize>) {}
 }
 
 /// A helper trait used for indexing operations.
@@ -546,3 +548,113 @@ where
 
     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<usize>, ops::Bound<usize>),
+) -> ops::Range<usize> {
+    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<usize>, ops::Bound<usize>),
+) -> Option<ops::Range<usize>> {
+    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<usize>, ops::Bound<usize>),
+) -> ops::Range<usize> {
+    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<T> SliceIndex<[T]> for (ops::Bound<usize>, ops::Bound<usize>) {
+    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)
+    }
+}
diff --git a/library/core/tests/slice.rs b/library/core/tests/slice.rs
index 7e198631cc7..3a98cd9d2ee 100644
--- a/library/core/tests/slice.rs
+++ b/library/core/tests/slice.rs
@@ -1280,6 +1280,9 @@ mod slice_index {
             }
         )*) => {$(
             mod $case_name {
+                #[allow(unused_imports)]
+                use core::ops::Bound;
+
                 #[test]
                 fn pass() {
                     let mut v = $data;
@@ -1376,6 +1379,24 @@ mod slice_index {
             bad: data[7..=6];
             message: "out of range";
         }
+
+        in mod boundpair_len {
+            data: [0, 1, 2, 3, 4, 5];
+
+            good: data[(Bound::Included(6), Bound::Unbounded)] == [];
+            good: data[(Bound::Unbounded, Bound::Included(5))] == [0, 1, 2, 3, 4, 5];
+            good: data[(Bound::Unbounded, Bound::Excluded(6))] == [0, 1, 2, 3, 4, 5];
+            good: data[(Bound::Included(0), Bound::Included(5))] == [0, 1, 2, 3, 4, 5];
+            good: data[(Bound::Included(0), Bound::Excluded(6))] == [0, 1, 2, 3, 4, 5];
+            good: data[(Bound::Included(2), Bound::Excluded(4))] == [2, 3];
+            good: data[(Bound::Excluded(1), Bound::Included(4))] == [2, 3, 4];
+            good: data[(Bound::Excluded(5), Bound::Excluded(6))] == [];
+            good: data[(Bound::Included(6), Bound::Excluded(6))] == [];
+            good: data[(Bound::Excluded(5), Bound::Included(5))] == [];
+            good: data[(Bound::Included(6), Bound::Included(5))] == [];
+            bad: data[(Bound::Unbounded, Bound::Included(6))];
+            message: "out of range";
+        }
     }
 
     panic_cases! {
@@ -1416,6 +1437,14 @@ mod slice_index {
             bad: data[4..=2];
             message: "but ends at";
         }
+
+        in mod boundpair_neg_width {
+            data: [0, 1, 2, 3, 4, 5];
+
+            good: data[(Bound::Included(4), Bound::Excluded(4))] == [];
+            bad: data[(Bound::Included(4), Bound::Excluded(3))];
+            message: "but ends at";
+        }
     }
 
     panic_cases! {
@@ -1434,6 +1463,20 @@ mod slice_index {
             bad: data[..= usize::MAX];
             message: "maximum usize";
         }
+
+        in mod boundpair_overflow_end {
+            data: [0; 1];
+
+            bad: data[(Bound::Unbounded, Bound::Included(usize::MAX))];
+            message: "maximum usize";
+        }
+
+        in mod boundpair_overflow_start {
+            data: [0; 1];
+
+            bad: data[(Bound::Excluded(usize::MAX), Bound::Unbounded)];
+            message: "maximum usize";
+        }
     } // panic_cases!
 }