near_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 stabilized.
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    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
21/// Splits `&mut [u8; L + R]` into `(&mut [u8; L], &mut [u8; R])`.
22pub 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
41/// Joins `[u8; L]` and `[u8; R]` into `[u8; L + R]`.
42pub 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
64/// Splits a slice into a slice of N-element arrays.
65// TODO(mina86): Replace with [T]::as_chunks once that’s stabilized.
66pub 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    // SAFETY: We cast a slice of `len * N` elements into a slice of `len` many
78    // `N` elements chunks.
79    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
99/// Like `as_chunks` but returns an error if there’s a remainder.
100pub 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}