Skip to content

Commit 1961911

Browse files
authored
Rollup merge of rust-lang#77704 - AnthonyMikh:slice_index_with_ops_bound_pair, r=m-ou-se
Implement indexing slices with pairs of core::ops::Bound<usize> Closes rust-lang#49976. I am not sure about code duplication between `check_range` and `into_maybe_range`. Should be former implemented in terms of the latter? Also this PR doesn't address code duplication between `impl SliceIndex for Range*`.
2 parents 134b79c + 6763a40 commit 1961911

File tree

2 files changed

+155
-0
lines changed

2 files changed

+155
-0
lines changed

library/core/src/slice/index.rs

+112
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@ mod private_slice_index {
8181
impl Sealed for ops::RangeInclusive<usize> {}
8282
#[stable(feature = "slice_get_slice", since = "1.28.0")]
8383
impl Sealed for ops::RangeToInclusive<usize> {}
84+
#[stable(feature = "slice_index_with_ops_bound_pair", since = "1.53.0")]
85+
impl Sealed for (ops::Bound<usize>, ops::Bound<usize>) {}
8486
}
8587

8688
/// A helper trait used for indexing operations.
@@ -546,3 +548,113 @@ where
546548

547549
ops::Range { start, end }
548550
}
551+
552+
/// Convert pair of `ops::Bound`s into `ops::Range` without performing any bounds checking and (in debug) overflow checking
553+
fn into_range_unchecked(
554+
len: usize,
555+
(start, end): (ops::Bound<usize>, ops::Bound<usize>),
556+
) -> ops::Range<usize> {
557+
use ops::Bound;
558+
let start = match start {
559+
Bound::Included(i) => i,
560+
Bound::Excluded(i) => i + 1,
561+
Bound::Unbounded => 0,
562+
};
563+
let end = match end {
564+
Bound::Included(i) => i + 1,
565+
Bound::Excluded(i) => i,
566+
Bound::Unbounded => len,
567+
};
568+
start..end
569+
}
570+
571+
/// Convert pair of `ops::Bound`s into `ops::Range`.
572+
/// Returns `None` on overflowing indices.
573+
fn into_range(
574+
len: usize,
575+
(start, end): (ops::Bound<usize>, ops::Bound<usize>),
576+
) -> Option<ops::Range<usize>> {
577+
use ops::Bound;
578+
let start = match start {
579+
Bound::Included(start) => start,
580+
Bound::Excluded(start) => start.checked_add(1)?,
581+
Bound::Unbounded => 0,
582+
};
583+
584+
let end = match end {
585+
Bound::Included(end) => end.checked_add(1)?,
586+
Bound::Excluded(end) => end,
587+
Bound::Unbounded => len,
588+
};
589+
590+
// Don't bother with checking `start < end` and `end <= len`
591+
// since these checks are handled by `Range` impls
592+
593+
Some(start..end)
594+
}
595+
596+
/// Convert pair of `ops::Bound`s into `ops::Range`.
597+
/// Panics on overflowing indices.
598+
fn into_slice_range(
599+
len: usize,
600+
(start, end): (ops::Bound<usize>, ops::Bound<usize>),
601+
) -> ops::Range<usize> {
602+
use ops::Bound;
603+
let start = match start {
604+
Bound::Included(start) => start,
605+
Bound::Excluded(start) => {
606+
start.checked_add(1).unwrap_or_else(|| slice_start_index_overflow_fail())
607+
}
608+
Bound::Unbounded => 0,
609+
};
610+
611+
let end = match end {
612+
Bound::Included(end) => {
613+
end.checked_add(1).unwrap_or_else(|| slice_end_index_overflow_fail())
614+
}
615+
Bound::Excluded(end) => end,
616+
Bound::Unbounded => len,
617+
};
618+
619+
// Don't bother with checking `start < end` and `end <= len`
620+
// since these checks are handled by `Range` impls
621+
622+
start..end
623+
}
624+
625+
#[stable(feature = "slice_index_with_ops_bound_pair", since = "1.53.0")]
626+
unsafe impl<T> SliceIndex<[T]> for (ops::Bound<usize>, ops::Bound<usize>) {
627+
type Output = [T];
628+
629+
#[inline]
630+
fn get(self, slice: &[T]) -> Option<&Self::Output> {
631+
into_range(slice.len(), self)?.get(slice)
632+
}
633+
634+
#[inline]
635+
fn get_mut(self, slice: &mut [T]) -> Option<&mut Self::Output> {
636+
into_range(slice.len(), self)?.get_mut(slice)
637+
}
638+
639+
#[inline]
640+
unsafe fn get_unchecked(self, slice: *const [T]) -> *const Self::Output {
641+
// SAFETY: the caller has to uphold the safety contract for `get_unchecked`.
642+
unsafe { into_range_unchecked(slice.len(), self).get_unchecked(slice) }
643+
}
644+
645+
#[inline]
646+
unsafe fn get_unchecked_mut(self, slice: *mut [T]) -> *mut Self::Output {
647+
// SAFETY: the caller has to uphold the safety contract for `get_unchecked_mut`.
648+
unsafe { into_range_unchecked(slice.len(), self).get_unchecked_mut(slice) }
649+
}
650+
651+
#[inline]
652+
fn index(self, slice: &[T]) -> &Self::Output {
653+
into_slice_range(slice.len(), self).index(slice)
654+
}
655+
656+
#[inline]
657+
fn index_mut(self, slice: &mut [T]) -> &mut Self::Output {
658+
into_slice_range(slice.len(), self).index_mut(slice)
659+
}
660+
}

library/core/tests/slice.rs

+43
Original file line numberDiff line numberDiff line change
@@ -1280,6 +1280,9 @@ mod slice_index {
12801280
}
12811281
)*) => {$(
12821282
mod $case_name {
1283+
#[allow(unused_imports)]
1284+
use core::ops::Bound;
1285+
12831286
#[test]
12841287
fn pass() {
12851288
let mut v = $data;
@@ -1376,6 +1379,24 @@ mod slice_index {
13761379
bad: data[7..=6];
13771380
message: "out of range";
13781381
}
1382+
1383+
in mod boundpair_len {
1384+
data: [0, 1, 2, 3, 4, 5];
1385+
1386+
good: data[(Bound::Included(6), Bound::Unbounded)] == [];
1387+
good: data[(Bound::Unbounded, Bound::Included(5))] == [0, 1, 2, 3, 4, 5];
1388+
good: data[(Bound::Unbounded, Bound::Excluded(6))] == [0, 1, 2, 3, 4, 5];
1389+
good: data[(Bound::Included(0), Bound::Included(5))] == [0, 1, 2, 3, 4, 5];
1390+
good: data[(Bound::Included(0), Bound::Excluded(6))] == [0, 1, 2, 3, 4, 5];
1391+
good: data[(Bound::Included(2), Bound::Excluded(4))] == [2, 3];
1392+
good: data[(Bound::Excluded(1), Bound::Included(4))] == [2, 3, 4];
1393+
good: data[(Bound::Excluded(5), Bound::Excluded(6))] == [];
1394+
good: data[(Bound::Included(6), Bound::Excluded(6))] == [];
1395+
good: data[(Bound::Excluded(5), Bound::Included(5))] == [];
1396+
good: data[(Bound::Included(6), Bound::Included(5))] == [];
1397+
bad: data[(Bound::Unbounded, Bound::Included(6))];
1398+
message: "out of range";
1399+
}
13791400
}
13801401

13811402
panic_cases! {
@@ -1416,6 +1437,14 @@ mod slice_index {
14161437
bad: data[4..=2];
14171438
message: "but ends at";
14181439
}
1440+
1441+
in mod boundpair_neg_width {
1442+
data: [0, 1, 2, 3, 4, 5];
1443+
1444+
good: data[(Bound::Included(4), Bound::Excluded(4))] == [];
1445+
bad: data[(Bound::Included(4), Bound::Excluded(3))];
1446+
message: "but ends at";
1447+
}
14191448
}
14201449

14211450
panic_cases! {
@@ -1434,6 +1463,20 @@ mod slice_index {
14341463
bad: data[..= usize::MAX];
14351464
message: "maximum usize";
14361465
}
1466+
1467+
in mod boundpair_overflow_end {
1468+
data: [0; 1];
1469+
1470+
bad: data[(Bound::Unbounded, Bound::Included(usize::MAX))];
1471+
message: "maximum usize";
1472+
}
1473+
1474+
in mod boundpair_overflow_start {
1475+
data: [0; 1];
1476+
1477+
bad: data[(Bound::Excluded(usize::MAX), Bound::Unbounded)];
1478+
message: "maximum usize";
1479+
}
14371480
} // panic_cases!
14381481
}
14391482

0 commit comments

Comments
 (0)