cl_array_ext/
slice.rs

1/// A slice with at least N elements. Can be dereferenced back into a regular slice on demand.
2///
3/// ```
4/// use cl_array_ext::SliceN;
5/// let a: &mut [_] = &mut [1, 2, 3, 4, 5];
6/// let b: &mut SliceN<_, 3> = a.try_into().unwrap();
7///
8/// b.head = [3, 2, 1];
9/// b.tail.reverse();
10///
11/// assert_eq!(a, [3, 2, 1, 5, 4]);
12/// ```
13#[repr(C)]
14#[allow(clippy::module_name_repetitions)]
15pub struct SliceN<T, const N: usize> {
16    /// Head of the slice, where N items are guaranteed to exist
17    pub head: [T; N],
18    /// Tail of the slice, may be empty
19    pub tail: [T],
20}
21
22impl<T, const N: usize> SliceN<T, N> {
23    /// Increases the bounds of the slice into a new known length.
24    /// # Safety
25    /// There must be at least M elements in the tail available, otherwise this will result in UB
26    pub unsafe fn increase_unchecked<const M: usize>(&self) -> &SliceN<T, { N + M }> {
27        let (p, meta) = (self as *const SliceN<T, N>).to_raw_parts();
28        &*core::ptr::from_raw_parts(p, meta - M)
29    }
30
31    /// Increases the bounds of the slice into a new known length.
32    /// # Safety
33    /// There must be at least M elements in the tail available, otherwise this will result in UB
34    pub unsafe fn increase_unchecked_mut<const M: usize>(&mut self) -> &mut SliceN<T, { N + M }> {
35        let (p, meta) = (self as *mut SliceN<T, N>).to_raw_parts();
36        &mut *core::ptr::from_raw_parts_mut(p, meta - M)
37    }
38
39    /// Increases the bounds of the slice into a new known length.
40    /// # Errors
41    /// There should be at least M elements in the tail available, otherwise this will return an error
42    pub fn increase<const M: usize>(&self) -> Result<&SliceN<T, { N + M }>, NotEnoughEntries> {
43        if self.tail.len() < M {
44            Err(NotEnoughEntries)
45        } else {
46            unsafe { Ok(self.increase_unchecked::<M>()) }
47        }
48    }
49
50    /// Increases the bounds of the slice into a new known length.
51    /// # Errors
52    /// There should be at least M elements in the tail available, otherwise this will return an error
53    pub fn increase_mut<const M: usize>(
54        &mut self,
55    ) -> Result<&mut SliceN<T, { N + M }>, NotEnoughEntries> {
56        if self.tail.len() < M {
57            Err(NotEnoughEntries)
58        } else {
59            unsafe { Ok(self.increase_unchecked_mut::<M>()) }
60        }
61    }
62
63    /// Decreases the bounds of the slice to a smaller known length.
64    pub fn downsize<const M: usize>(&self) -> &SliceN<T, M>
65    where
66        [T; N - M]: Sized, // M <= N
67    {
68        unsafe { SliceN::<T, M>::from_unchecked(self) }
69    }
70
71    /// Decreases the bounds of the slice to a smaller known length.
72    pub fn downsize_mut<const M: usize>(&mut self) -> &mut SliceN<T, M>
73    where
74        [T; N - M]: Sized, // M <= N
75    {
76        unsafe { SliceN::<T, M>::from_unchecked_mut(self) }
77    }
78
79    /// Convert a slice into one that is guaranteed to have at least N elements
80    /// # Safety
81    /// The length of the slice must be >= N, otherwise this will result in UB
82    pub unsafe fn from_unchecked(slice: &[T]) -> &Self {
83        // extract the pointer metadata for the slice
84        let (p, meta) = (slice as *const [T]).to_raw_parts();
85        // convert the address and meta back into a ref
86        &*core::ptr::from_raw_parts(p, meta - N)
87    }
88
89    /// Convert a mut slice into one that is guaranteed to have at least N elements
90    /// # Safety
91    /// The length of the slice must be >= N, otherwise this will result in UB
92    pub unsafe fn from_unchecked_mut(slice: &mut [T]) -> &mut Self {
93        // extract the pointer metadata for the slice
94        let (p, meta) = (slice as *mut [T]).to_raw_parts();
95        // convert the address and meta back into a ref
96        &mut *core::ptr::from_raw_parts_mut(p, meta - N)
97    }
98}
99
100/// Error type returned by the [`TryFrom`] implementations for [`SliceN`]
101#[derive(Debug)]
102pub struct NotEnoughEntries;
103
104impl<'a, T, const N: usize> TryFrom<&'a [T]> for &'a SliceN<T, N> {
105    type Error = NotEnoughEntries;
106    fn try_from(value: &'a [T]) -> Result<Self, Self::Error> {
107        if value.len() < N {
108            Err(NotEnoughEntries)
109        } else {
110            unsafe { Ok(SliceN::<T, N>::from_unchecked(value)) }
111        }
112    }
113}
114
115impl<'a, T, const N: usize> TryFrom<&'a mut [T]> for &'a mut SliceN<T, N> {
116    type Error = NotEnoughEntries;
117    fn try_from(value: &'a mut [T]) -> Result<Self, Self::Error> {
118        if value.len() < N {
119            Err(NotEnoughEntries)
120        } else {
121            unsafe { Ok(SliceN::<T, N>::from_unchecked_mut(value)) }
122        }
123    }
124}
125
126use core::fmt;
127impl<T: fmt::Debug, const N: usize> fmt::Debug for SliceN<T, N> {
128    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
129        f.debug_list().entries(self.iter()).finish()
130    }
131}
132
133use core::ops::{Deref, DerefMut};
134
135impl<T, const N: usize> Deref for SliceN<T, N> {
136    type Target = [T];
137    fn deref(&self) -> &Self::Target {
138        // extract the pointer metadata for the slice
139        let (p, meta) = (self as *const SliceN<T, N>).to_raw_parts();
140        // convert the address and meta back into a ref
141        unsafe { &*core::ptr::from_raw_parts(p, meta + N) }
142    }
143}
144
145impl<T, const N: usize> DerefMut for SliceN<T, N> {
146    fn deref_mut(&mut self) -> &mut Self::Target {
147        // extract the pointer metadata for the slice
148        let (p, meta) = (self as *mut SliceN<T, N>).to_raw_parts();
149        // convert the address and meta back into a ref
150        unsafe { &mut *core::ptr::from_raw_parts_mut(p, meta + N) }
151    }
152}
153
154#[cfg(test)]
155mod tests {
156    use crate::SliceN;
157
158    #[test]
159    fn slice_n() {
160        let a: &[_] = &[1, 2, 3, 4, 5];
161        let b: &SliceN<_, 3> = a.try_into().unwrap();
162
163        assert_eq!(b.len(), 5);
164        assert_eq!(b.head, [1, 2, 3]);
165        assert_eq!(b.tail, [4, 5]);
166        assert_eq!(&**b, a);
167
168        let b = b.increase::<2>().unwrap();
169        assert_eq!(b.head, [1, 2, 3, 4, 5]);
170        assert_eq!(b.tail, []);
171        let _ = b.increase::<1>().unwrap_err();
172        let _ = <&SliceN<_, 6>>::try_from(a).unwrap_err();
173    }
174
175    #[test]
176    fn slice_n_mut() {
177        let a: &mut [_] = &mut [1, 2, 3, 4, 5];
178        let b: &mut SliceN<_, 3> = a.try_into().unwrap();
179
180        b.head = [3, 2, 1];
181        b.downsize_mut::<2>().head = [9, 8];
182
183        assert_eq!(a, [9, 8, 1, 4, 5]);
184    }
185}