const_sub_array/
lib.rs

1//! Allows to extract a fixed size sub-array out of an array with complie-time length and offset
2//! checks.
3//!
4//! Based on [`sub-array`](https://crates.io/crates/sub-array) crate.
5//!
6//! # Example
7//!
8//! Getting a sub array:
9//!
10//! ```
11//! use const_sub_array::SubArray;
12//!
13//! let arr: [u8; 7] = [1, 2, 3, 4, 5, 6, 7];
14//!
15//! // Get a sub-array starting at offset 1 with 3 items.
16//! let sub: &[u8; 3] = arr.sub_array_ref::<1, 3>();
17//! assert_eq!(sub, &[2, 3, 4]);
18//! ```
19//!
20//! Initializing an `[u8; 10]` array with `(u16, u32, u32)`:
21//!
22//! ```
23//! use const_sub_array::SubArray;
24//!
25//! let foo: u16 = 42;
26//! let bar: u32 = 0x1234;
27//! let baz: u32 = 0x5678;
28//!
29//! let mut arr = [0u8; 10];
30//! *arr.sub_array_mut::<0, 2>() = foo.to_be_bytes();
31//! *arr.sub_array_mut::<2, 4>() = bar.to_be_bytes();
32//! *arr.sub_array_mut::<6, 4>() = baz.to_be_bytes();
33//!
34//! assert_eq!(
35//!     arr,
36//!     [
37//!         0, 42, // foo
38//!         0x0, 0x0, 0x12, 0x34, // bar
39//!         0x0, 0x0, 0x56, 0x78, // baz
40//!     ]
41//! );
42//! ```
43
44#![no_std]
45
46/// Apply a compile-time const generic assertion.
47///
48/// Based on <https://github.com/nvzqz/static-assertions/issues/40#issuecomment-846228355>.
49#[macro_export]
50macro_rules! const_assert {
51    ($($list:ident : $ty:ty),* => $expr:expr) => {{
52        struct Assert<$(const $list: usize,)*>;
53        impl<$(const $list: $ty,)*> Assert<$($list,)*> {
54            const OK: u8 = 0 - !($expr) as u8;
55        }
56        Assert::<$($list,)*>::OK
57    }};
58    ($expr:expr) => {
59        const OK: u8 = 0 - !($expr) as u8;
60    };
61}
62
63/// Array that can be slice into a smaller sub-array.
64///
65/// See the [crate] level reference.
66///
67/// # Safety
68///
69/// Implementation guarantees to generate *compile-time* errors when the requested sub-array
70/// `length + offset` combinations exceed the size of the array that the sub-array is being
71/// extracted from.
72pub unsafe trait SubArray {
73    /// The value type of this array.
74    ///
75    /// This is the `T` in `[T; N]` on regular arrays.
76    type Item;
77
78    /// Get a reference to a sub-array of length `M` starting at `OFFSET`.
79    ///
80    /// # Safety
81    ///
82    /// Implementation guarantees to generate a *compile-time* error
83    /// when `M + OFFSET` is greater than the size of the array
84    /// that sub-array is being extracted from.
85    ///
86    /// # Example
87    /// ```
88    /// use const_sub_array::SubArray;
89    ///
90    /// let arr: [u8; 5] = [9, 8, 7, 6, 5];
91    ///
92    /// // Get a sub-array starting at offset 3 and size 2.
93    /// let sub: &[u8; 2] = arr.sub_array_ref::<3, 2>();
94    /// assert_eq!(sub, &[6, 5]);
95    /// ```
96    fn sub_array_ref<const OFFSET: usize, const M: usize>(&self) -> &[Self::Item; M];
97
98    /// Get a mutable reference to a sub-array of length `M` starting at `OFFSET`.
99    ///
100    /// # Example
101    /// ```
102    /// use const_sub_array::SubArray;
103    ///
104    /// let mut arr: [u8; 5] = [9, 8, 7, 6, 5];
105    ///
106    /// // Get a mutable sub-array starting at offset 0 and size 2.
107    /// let sub: &mut [u8; 2] = arr.sub_array_mut::<0, 2>();
108    /// assert_eq!(sub, &mut [9, 8]);
109    /// ```
110    fn sub_array_mut<const OFFSET: usize, const M: usize>(&mut self) -> &mut [Self::Item; M];
111}
112
113/// Implementation on regular arrays.
114unsafe impl<T, const N: usize> SubArray for [T; N] {
115    type Item = T;
116
117    #[allow(unsafe_code)]
118    fn sub_array_ref<const OFFSET: usize, const M: usize>(&self) -> &[Self::Item; M] {
119        const_assert!(OFFSET: usize, M: usize, N: usize => OFFSET + M <= N);
120        unsafe { &*(self.as_ptr().add(OFFSET) as *const [T; M]) }
121    }
122
123    #[allow(unsafe_code)]
124    fn sub_array_mut<const OFFSET: usize, const M: usize>(&mut self) -> &mut [Self::Item; M] {
125        const_assert!(OFFSET: usize, M: usize, N: usize => OFFSET + M <= N);
126        unsafe { &mut *(self.as_ptr().add(OFFSET) as *mut [T; M]) }
127    }
128}
129
130/// Implementation on mutable references.
131unsafe impl<T> SubArray for &mut T
132where
133    T: SubArray,
134{
135    type Item = T::Item;
136
137    fn sub_array_ref<const OFFSET: usize, const N: usize>(&self) -> &[Self::Item; N] {
138        (**self).sub_array_ref::<OFFSET, N>()
139    }
140
141    fn sub_array_mut<const OFFSET: usize, const N: usize>(&mut self) -> &mut [Self::Item; N] {
142        (**self).sub_array_mut::<OFFSET, N>()
143    }
144}
145
146#[cfg(test)]
147mod tests {
148    extern crate alloc;
149
150    use alloc::string::String;
151    use alloc::string::ToString;
152
153    use super::*;
154
155    #[test]
156    fn empty_ref() {
157        let arr = [0_u8; 0];
158        assert_eq!(arr.sub_array_ref::<0, 0>(), &[]);
159    }
160
161    #[test]
162    fn empty_mut() {
163        let mut arr = [0_u8; 0];
164        assert_eq!(arr.sub_array_mut::<0, 0>(), &mut []);
165    }
166
167    #[test]
168    fn full_ref() {
169        let arr = [1, 2, 3_i8];
170        assert_eq!(arr.sub_array_ref::<0, 3>(), &[1, 2, 3]);
171    }
172
173    #[test]
174    fn full_mut() {
175        let mut arr = [1, 2, 3_i8];
176        assert_eq!(arr.sub_array_mut::<0, 3>(), &mut [1, 2, 3]);
177    }
178
179    #[test]
180    fn first_ref() {
181        let arr = [1, 2, 3_u16];
182        assert_eq!(arr.sub_array_ref::<0, 1>(), &[1]);
183    }
184
185    #[test]
186    fn first_mut() {
187        let mut arr = [1, 2, 3_u16];
188        assert_eq!(arr.sub_array_mut::<0, 1>(), &mut [1]);
189    }
190
191    #[test]
192    fn middle_ref() {
193        let arr = [1, 2, 3_i16];
194        assert_eq!(arr.sub_array_ref::<1, 1>(), &[2]);
195    }
196
197    #[test]
198    fn middle_mut() {
199        let mut arr = [1, 2, 3_i16];
200        assert_eq!(arr.sub_array_mut::<1, 1>(), &mut [2]);
201    }
202
203    #[test]
204    fn last_ref() {
205        let arr = [1, 2, 3_i16];
206        assert_eq!(arr.sub_array_ref::<2, 1>(), &[3]);
207    }
208
209    #[test]
210    fn last_mut() {
211        let mut arr = [1, 2, 3_i16];
212        assert_eq!(arr.sub_array_mut::<2, 1>(), &mut [3]);
213    }
214
215    #[derive(Debug, PartialEq, Eq)]
216    struct NotClone(&'static str);
217
218    const NOT_CLONE_ARRAY: [NotClone; 5] = [
219        NotClone("abc"),
220        NotClone("foo"),
221        NotClone("bar"),
222        NotClone("qux"),
223        NotClone("fox"),
224    ];
225
226    #[test]
227    fn not_clone_ref() {
228        let exp_arr = [NotClone("foo"), NotClone("bar"), NotClone("qux")];
229        let arr = NOT_CLONE_ARRAY;
230        assert_eq!(arr.sub_array_ref::<1, 3>(), &exp_arr);
231    }
232
233    #[test]
234    fn not_clone_mut() {
235        let mut exp_arr = [NotClone("foo"), NotClone("bar"), NotClone("qux")];
236        let mut arr = NOT_CLONE_ARRAY;
237        assert_eq!(arr.sub_array_mut::<1, 3>(), &mut exp_arr);
238    }
239
240    #[test]
241    fn some_strings() {
242        let arr: [String; 5] = NOT_CLONE_ARRAY.map(|s| s.0.to_string());
243        assert_eq!(
244            arr.sub_array_ref::<2, 2>(),
245            &[String::from("bar"), String::from("qux")]
246        );
247    }
248}