const_util/
slice.rs

1//! Const variants of functions for dealing with slices
2
3use core::ops::{Bound, Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive};
4mod hidden {
5    use super::*;
6
7    #[repr(u8)]
8    pub enum IndexKind {
9        Range,
10        RangeInc,
11        RangeFull,
12        RangeFrom,
13        RangeTo,
14        RangeToInc,
15        BoundPair,
16    }
17    /// # Safety
18    /// `KIND` must be unique such that we can transmute back to `Self` based on it.
19    pub unsafe trait RangeIndex {
20        const KIND: IndexKind;
21    }
22    // SAFETY: `KIND` is unique
23    unsafe impl RangeIndex for Range<usize> {
24        const KIND: IndexKind = IndexKind::Range;
25    }
26    // SAFETY: `KIND` is unique
27    unsafe impl RangeIndex for RangeInclusive<usize> {
28        const KIND: IndexKind = IndexKind::RangeInc;
29    }
30    // SAFETY: `KIND` is unique
31    unsafe impl RangeIndex for RangeToInclusive<usize> {
32        const KIND: IndexKind = IndexKind::RangeToInc;
33    }
34    // SAFETY: `KIND` is unique
35    unsafe impl RangeIndex for RangeFrom<usize> {
36        const KIND: IndexKind = IndexKind::RangeFrom;
37    }
38    // SAFETY: `KIND` is unique
39    unsafe impl RangeIndex for RangeFull {
40        const KIND: IndexKind = IndexKind::RangeFull;
41    }
42    // SAFETY: `KIND` is unique
43    unsafe impl RangeIndex for RangeTo<usize> {
44        const KIND: IndexKind = IndexKind::RangeTo;
45    }
46    // SAFETY: `KIND` is unique
47    unsafe impl RangeIndex for (Bound<usize>, Bound<usize>) {
48        const KIND: IndexKind = IndexKind::BoundPair;
49    }
50}
51use hidden::*;
52
53use crate::mem::nonnull_from;
54use core::ptr::NonNull;
55const fn transmute_generic<Src: RangeIndex, Dst: RangeIndex>(src: Src) -> Dst {
56    assert!(const { Src::KIND as u8 == Dst::KIND as u8 });
57    let src = core::mem::ManuallyDrop::new(src);
58    // SAFETY: `KIND` uniquely identifies the implementor, meaning that `Src` and `Dst` are the
59    // same type, so this is a safe transmute from `ManuallyDrop<T>` to `T`
60    unsafe { core::mem::transmute_copy(&src) }
61}
62
63const fn into_range<R: RangeIndex>(range: R, len: usize) -> Option<Range<usize>> {
64    Some(match R::KIND {
65        IndexKind::Range => transmute_generic(range),
66        IndexKind::RangeInc => {
67            let range: RangeInclusive<usize> = transmute_generic(range);
68            let Some(end) = range.end().checked_add(1) else {
69                return None;
70            };
71            *range.start()..end
72        }
73        IndexKind::RangeFull => {
74            let _: RangeFull = transmute_generic(range);
75            0..len
76        }
77        IndexKind::RangeFrom => {
78            let r: RangeFrom<usize> = transmute_generic(range);
79            r.start..len
80        }
81        IndexKind::RangeTo => {
82            let r: RangeTo<usize> = transmute_generic(range);
83            0..r.end
84        }
85        IndexKind::RangeToInc => {
86            let range: RangeToInclusive<usize> = transmute_generic(range);
87            let Some(end) = range.end.checked_add(1) else {
88                return None;
89            };
90            0..end
91        }
92        IndexKind::BoundPair => {
93            let (start, end) = transmute_generic(range);
94            (match start {
95                Bound::Included(start) => start,
96                Bound::Excluded(start) => match start.checked_add(1) {
97                    Some(it) => it,
98                    None => return None,
99                },
100                Bound::Unbounded => 0,
101            })..match end {
102                Bound::Included(end) => match end.checked_add(1) {
103                    Some(it) => it,
104                    None => return None,
105                },
106                Bound::Excluded(end) => end,
107                Bound::Unbounded => len,
108            }
109        }
110    })
111}
112
113/// # Safety
114/// `slice` must come from a mutable or immutable reference. The returned pointer is valid for
115/// reborrowing as a subslice with the same mutability as the original reference.
116const unsafe fn slice_get_nonnull<T, R>(slice: NonNull<[T]>, range: R) -> Option<NonNull<[T]>>
117where
118    R: RangeIndex,
119{
120    let Some(Range { start, end }) = into_range(range, slice.len()) else {
121        return None;
122    };
123    let new_len = match usize::checked_sub(end, start) {
124        Some(it) if end <= slice.len() => it,
125        _ => return None,
126    };
127    // SAFETY: `slice` came from a reference and `start <= end < slice.len()`, so the pointer
128    // addition is in-bounds. The returned pointer can reborrowed as a valid subslice with the
129    // same mutability because `start + new_len = start + end - start = end < slice.len()`.
130    Some(unsafe { NonNull::slice_from_raw_parts(slice.cast::<T>().add(start), new_len) })
131}
132/// # Safety
133/// `slice` must come from a mutable or immutable reference. The returned pointer is valid for
134/// reborrowing as a subslice with the same mutability as the original reference.
135#[track_caller]
136const unsafe fn slice_index_nonnull<T, R>(slice: NonNull<[T]>, range: R) -> NonNull<[T]>
137where
138    R: RangeIndex,
139{
140    #[track_caller]
141    #[cold]
142    const fn build_msg_panic<const MSG_LEN: usize>(
143        msg_lhs: &str,
144        left_usize: usize,
145        msg_mid: &str,
146        right_usize: usize,
147    ) -> ! {
148        const fn write_str_usize(mut n: usize, mut to: &mut [u8]) -> &mut [u8] {
149            while let [slot, rem @ ..] = to {
150                let brk = n < 10;
151
152                *slot = (n % 10) as u8 + b'0';
153                n /= 10;
154                to = rem;
155
156                if brk {
157                    break;
158                }
159            }
160            to
161        }
162        let mut msg = [0; MSG_LEN];
163        let (lhs, rem) = msg.split_at_mut(msg_lhs.len());
164        lhs.copy_from_slice(msg_lhs.as_bytes());
165        let rem = write_str_usize(left_usize, rem);
166        let (mid, rem) = rem.split_at_mut(msg_mid.len());
167        mid.copy_from_slice(msg_mid.as_bytes());
168        let rem = write_str_usize(right_usize, rem);
169        let rem_len = rem.len();
170        match core::str::from_utf8(msg.split_at(MSG_LEN - rem_len).0) {
171            Ok(msg) => panic!("{}", msg),
172            Err(_) => unreachable!(),
173        }
174    }
175    const USIZE_STR_LEN: usize = {
176        let mut len = 1;
177        let mut n = usize::MAX;
178        while n >= 10 {
179            n /= 10;
180            len += 1;
181        }
182        len
183    };
184
185    let Some(Range { start, end }) = into_range(range, slice.len()) else {
186        const fn overflow_fail() -> ! {
187            panic!("attempted to index slice after maximum allowed usize")
188        }
189        overflow_fail()
190    };
191    let Some(new_len) = usize::checked_sub(end, start) else {
192        #[track_caller]
193        #[cold]
194        const fn bounds_order_fail(start: usize, end: usize) -> ! {
195            const LHS: &str = "slice index starts at ";
196            const MID: &str = " but ends at ";
197            const MSG_LEN: usize = LHS.len() + MID.len() + 2 * USIZE_STR_LEN;
198            build_msg_panic::<MSG_LEN>(LHS, start, MID, end)
199        }
200        bounds_order_fail(start, end)
201    };
202    if end > slice.len() {
203        #[track_caller]
204        #[cold]
205        const fn end_too_large_fail(index: usize, len: usize) -> ! {
206            const LHS: &str = "range end index ";
207            const MID: &str = " is out of range for slice of length ";
208            const MSG_LEN: usize = LHS.len() + MID.len() + 2 * USIZE_STR_LEN;
209            build_msg_panic::<MSG_LEN>(LHS, index, MID, len)
210        }
211        end_too_large_fail(end, slice.len());
212    }
213    // SAFETY: `slice` came from a reference and `start <= end < slice.len()`, so the pointer
214    // addition is in-bounds. The returned pointer can reborrowed as a valid subslice with the
215    // same mutability because `start + new_len = start + end - start = end < slice.len()`.
216    unsafe { NonNull::slice_from_raw_parts(slice.cast::<T>().add(start), new_len) }
217}
218
219/// Const equivalent of [`<[T]>::get`](slice::get) for ranges.
220pub const fn slice_get<T, I>(slice: &[T], index: I) -> Option<&[T]>
221where
222    I: RangeIndex,
223{
224    // SAFETY: `slice` comes from a reference and the resulting pointer is a valid
225    // subslice with the same mutability
226    unsafe {
227        match slice_get_nonnull(nonnull_from(slice), index) {
228            Some(r) => Some(r.as_ref()),
229            None => None,
230        }
231    }
232}
233/// Const equivalent of [`<[T]>::index`](slice::index) for ranges.
234pub const fn slice_index<T, I>(slice: &[T], index: I) -> &[T]
235where
236    I: RangeIndex,
237{
238    // SAFETY: `slice` comes from a reference and the resulting pointer is a valid
239    // subslice with the same mutability
240    unsafe { slice_index_nonnull(nonnull_from(slice), index).as_ref() }
241}
242
243/// Const equivalent of [`<[T]>::get_mut`](slice::get_mut) for ranges.
244pub const fn slice_get_mut<T, I>(slice: &mut [T], index: I) -> Option<&mut [T]>
245where
246    I: RangeIndex,
247{
248    // SAFETY: `slice` comes from a reference and the resulting pointer is a valid
249    // subslice with the same mutability
250    unsafe {
251        match slice_get_nonnull(nonnull_from(slice), index) {
252            Some(mut r) => Some(r.as_mut()),
253            None => None,
254        }
255    }
256}
257
258/// Const equivalent of [`<[T]>::index_mut`](slice::index_mut) for ranges.
259pub const fn slice_index_mut<T, I>(slice: &mut [T], index: I) -> &mut [T]
260where
261    I: RangeIndex,
262{
263    // SAFETY: `slice` comes from a reference and the resulting pointer is a valid
264    // subslice with the same mutability
265    unsafe { slice_index_nonnull(nonnull_from(slice), index).as_mut() }
266}
267
268#[test]
269fn test() {
270    let mut example: Vec<i32> = (0..20).collect();
271    let mut example2: Vec<i32> = example.clone();
272    let example = &mut *example;
273    let example2 = &mut *example2;
274    for i in 0..20 {
275        for j in 0..20 {
276            if i <= j {
277                assert_eq!(slice_index_mut(example, i..j), &mut example2[i..j]);
278            } else {
279                std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
280                    slice_index_mut(example, i..j);
281                }))
282                .unwrap_err();
283            }
284            assert_eq!(slice_get_mut(example, i..j), example2.get_mut(i..j));
285        }
286    }
287}