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}