unc_stdx/
lib.rs

1//! `stdx` crate contains polyfills which should really be in std,
2//! but currently aren't for one reason or another.
3#![deny(clippy::arithmetic_side_effects)]
4
5// TODO(mina86): Replace usage of the split functions by split_array_ref et al
6// methods of array and slice types once those are stabilised.
7
8/// Splits `&[u8; L + R]` into `(&[u8; L], &[u8; R])`.
9pub 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
19/// Splits `&mut [u8; L + R]` into `(&mut [u8; L], &mut [u8; R])`.
20pub 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
36/// Joins `[u8; L]` and `[u8; R]` into `[u8; L + R]`.
37pub 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
56/// Splits a slice into a slice of N-element arrays.
57// TODO(mina86): Replace with [T]::as_chunks once that’s stabilised.
58pub 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    // SAFETY: We cast a slice of `len * N` elements into a slice of `len` many
67    // `N` elements chunks.
68    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
88/// Like `as_chunks` but returns an error if there’s a remainder.
89pub 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
108/// Asserts, at compile time, that `S == A + B`.
109struct 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
114/// Asserts, at compile time, that `N` is non-zero.
115struct AssertNonZero<const N: usize>;
116impl<const N: usize> AssertNonZero<N> {
117    const OK: () = assert!(N != 0);
118}