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