1#![deny(clippy::arithmetic_side_effects)]
4
5pub fn split_array<const N: usize, const L: usize, const R: usize>(
10 xs: &[u8; N],
11) -> (&[u8; L], &[u8; R]) {
12 const {
13 if N != L + R {
14 panic!()
15 }
16 };
17 let (left, right) = xs.split_at(L);
18 (left.try_into().unwrap(), right.try_into().unwrap())
19}
20
21pub fn split_array_mut<const N: usize, const L: usize, const R: usize>(
23 xs: &mut [u8; N],
24) -> (&mut [u8; L], &mut [u8; R]) {
25 const {
26 if N != L + R {
27 panic!()
28 }
29 };
30
31 let (left, right) = xs.split_at_mut(L);
32 (left.try_into().unwrap(), right.try_into().unwrap())
33}
34
35#[test]
36fn test_split() {
37 assert_eq!((&[0, 1], &[2, 3, 4]), split_array(&[0, 1, 2, 3, 4]));
38 assert_eq!((&mut [0, 1], &mut [2, 3, 4]), split_array_mut(&mut [0, 1, 2, 3, 4]));
39}
40
41pub fn join_array<const N: usize, const L: usize, const R: usize>(
43 left: [u8; L],
44 right: [u8; R],
45) -> [u8; N] {
46 const {
47 if N != L + R {
48 panic!()
49 }
50 };
51
52 let mut res = [0; N];
53 let (l, r) = res.split_at_mut(L);
54 l.copy_from_slice(&left);
55 r.copy_from_slice(&right);
56 res
57}
58
59#[test]
60fn test_join() {
61 assert_eq!([0, 1, 2, 3], join_array([0, 1], [2, 3]));
62}
63
64pub fn as_chunks<const N: usize, T>(slice: &[T]) -> (&[[T; N]], &[T]) {
67 const {
68 if N == 0 {
69 panic!()
70 }
71 };
72
73 let len = slice.len().checked_div(N).expect("static assert above ensures N ≠ 0");
74 let (head, tail) = slice
75 .split_at(len.checked_mul(N).expect("len * N ≤ slice.len() hence can't overflow here"));
76
77 let head = unsafe { std::slice::from_raw_parts(head.as_ptr().cast(), len) };
80 (head, tail)
81}
82
83#[derive(Debug, Eq, PartialEq)]
84pub struct InexactChunkingError {
85 slice_len: usize,
86 chunk_size: usize,
87}
88impl std::error::Error for InexactChunkingError {}
89impl std::fmt::Display for InexactChunkingError {
90 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
91 write!(
92 f,
93 "slice of size {} cannot be precisely split into chunks of size {}",
94 self.slice_len, self.chunk_size
95 )
96 }
97}
98
99pub fn as_chunks_exact<const N: usize, T>(slice: &[T]) -> Result<&[[T; N]], InexactChunkingError> {
101 let (chunks, remainder) = as_chunks(slice);
102 if remainder.is_empty() {
103 Ok(chunks)
104 } else {
105 Err(InexactChunkingError { slice_len: slice.len(), chunk_size: N })
106 }
107}
108
109#[test]
110fn test_as_chunks() {
111 assert_eq!((&[[0, 1], [2, 3]][..], &[4][..]), as_chunks::<2, _>(&[0, 1, 2, 3, 4]));
112 assert_eq!(Ok(&[[0, 1], [2, 3]][..]), as_chunks_exact::<2, _>(&[0, 1, 2, 3]));
113 assert_eq!(
114 Err(InexactChunkingError { slice_len: 5, chunk_size: 2 }),
115 as_chunks_exact::<2, _>(&[0, 1, 2, 3, 4])
116 );
117}