1use 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 pub unsafe trait RangeIndex {
20 const KIND: IndexKind;
21 }
22 unsafe impl RangeIndex for Range<usize> {
24 const KIND: IndexKind = IndexKind::Range;
25 }
26 unsafe impl RangeIndex for RangeInclusive<usize> {
28 const KIND: IndexKind = IndexKind::RangeInc;
29 }
30 unsafe impl RangeIndex for RangeToInclusive<usize> {
32 const KIND: IndexKind = IndexKind::RangeToInc;
33 }
34 unsafe impl RangeIndex for RangeFrom<usize> {
36 const KIND: IndexKind = IndexKind::RangeFrom;
37 }
38 unsafe impl RangeIndex for RangeFull {
40 const KIND: IndexKind = IndexKind::RangeFull;
41 }
42 unsafe impl RangeIndex for RangeTo<usize> {
44 const KIND: IndexKind = IndexKind::RangeTo;
45 }
46 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 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
113const 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 Some(unsafe { NonNull::slice_from_raw_parts(slice.cast::<T>().add(start), new_len) })
131}
132#[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 unsafe { NonNull::slice_from_raw_parts(slice.cast::<T>().add(start), new_len) }
217}
218
219pub const fn slice_get<T, I>(slice: &[T], index: I) -> Option<&[T]>
221where
222 I: RangeIndex,
223{
224 unsafe {
227 match slice_get_nonnull(nonnull_from(slice), index) {
228 Some(r) => Some(r.as_ref()),
229 None => None,
230 }
231 }
232}
233pub const fn slice_index<T, I>(slice: &[T], index: I) -> &[T]
235where
236 I: RangeIndex,
237{
238 unsafe { slice_index_nonnull(nonnull_from(slice), index).as_ref() }
241}
242
243pub const fn slice_get_mut<T, I>(slice: &mut [T], index: I) -> Option<&mut [T]>
245where
246 I: RangeIndex,
247{
248 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
258pub const fn slice_index_mut<T, I>(slice: &mut [T], index: I) -> &mut [T]
260where
261 I: RangeIndex,
262{
263 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}