Skip to main content

polars_utils/
range.rs

1use std::ops;
2
3// Copied from the stdlib.
4fn slice_index_fail(start: usize, end: usize, len: usize) -> ! {
5    if start > len {
6        panic!("range start index {start} out of range for slice of length {len}",)
7    }
8
9    if end > len {
10        panic!("range end index {end} out of range for slice of length {len}",)
11    }
12
13    if start > end {
14        panic!("slice index starts at {start} but ends at {end}",)
15    }
16
17    // Only reachable if the range was a `RangeInclusive` or a
18    // `RangeToInclusive`, with `end == len`.
19    panic!("range end index {end} out of range for slice of length {len}",)
20}
21
22#[must_use]
23pub fn check_range<R>(range: R, bounds: ops::RangeTo<usize>) -> ops::Range<usize>
24where
25    R: ops::RangeBounds<usize>,
26{
27    let len = bounds.end;
28    let end = match range.end_bound() {
29        ops::Bound::Included(&end) if end >= len => slice_index_fail(0, end, len),
30        // Cannot overflow because `end < len` implies `end < usize::MAX`.
31        ops::Bound::Included(&end) => end + 1,
32
33        ops::Bound::Excluded(&end) if end > len => slice_index_fail(0, end, len),
34        ops::Bound::Excluded(&end) => end,
35        ops::Bound::Unbounded => len,
36    };
37
38    let start = match range.start_bound() {
39        ops::Bound::Excluded(&start) if start >= end => slice_index_fail(start, end, len),
40        // Cannot overflow because `start < end` implies `start < usize::MAX`.
41        ops::Bound::Excluded(&start) => start + 1,
42
43        ops::Bound::Included(&start) if start > end => slice_index_fail(start, end, len),
44        ops::Bound::Included(&start) => start,
45
46        ops::Bound::Unbounded => 0,
47    };
48
49    ops::Range { start, end }
50}
51
52#[must_use]
53pub fn decode_range_unchecked<R>(range: R, bounds: ops::RangeTo<usize>) -> ops::Range<usize>
54where
55    R: ops::RangeBounds<usize>,
56{
57    let len = bounds.end;
58    let end = match range.end_bound() {
59        ops::Bound::Included(&end) => end + 1,
60        ops::Bound::Excluded(&end) => end,
61        ops::Bound::Unbounded => len,
62    };
63
64    let start = match range.start_bound() {
65        ops::Bound::Excluded(&start) => start + 1,
66        ops::Bound::Included(&start) => start,
67        ops::Bound::Unbounded => 0,
68    };
69
70    ops::Range { start, end }
71}