1 | // Copyright 2025 Brian Smith. |
2 | // |
3 | // Permission to use, copy, modify, and/or distribute this software for any |
4 | // purpose with or without fee is hereby granted, provided that the above |
5 | // copyright notice and this permission notice appear in all copies. |
6 | // |
7 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES |
8 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
9 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY |
10 | // SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
11 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION |
12 | // OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN |
13 | // CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
14 | |
15 | use super::AsChunksMut; |
16 | use core::ops; |
17 | |
18 | #[inline (always)] |
19 | pub fn as_chunks<T, const N: usize>(slice: &[T]) -> (AsChunks<T, N>, &[T]) { |
20 | assert!(N != 0, "chunk size must be non-zero" ); |
21 | let len: usize = slice.len() / N; |
22 | let (multiple_of_n: &[T], remainder: &[T]) = slice.split_at(mid:len * N); |
23 | (AsChunks(multiple_of_n), remainder) |
24 | } |
25 | |
26 | #[derive (Clone, Copy)] |
27 | pub struct AsChunks<'a, T, const N: usize>(&'a [T]); |
28 | |
29 | impl<'a, T, const N: usize> AsChunks<'a, T, N> { |
30 | #[inline (always)] |
31 | pub fn from_ref(value: &'a [T; N]) -> Self { |
32 | Self(value) |
33 | } |
34 | |
35 | #[inline (always)] |
36 | pub fn as_flattened(&self) -> &[T] { |
37 | self.0 |
38 | } |
39 | |
40 | #[cfg (any(target_arch = "aarch64" , target_arch = "arm" , target_arch = "x86_64" ))] |
41 | #[inline (always)] |
42 | pub fn as_ptr(&self) -> *const [T; N] { |
43 | self.0.as_ptr().cast() |
44 | } |
45 | |
46 | #[inline (always)] |
47 | pub fn is_empty(&self) -> bool { |
48 | self.0.is_empty() |
49 | } |
50 | |
51 | #[inline (always)] |
52 | pub fn len(&self) -> usize { |
53 | self.0.len() / N |
54 | } |
55 | } |
56 | |
57 | impl<T, const N: usize> ops::Index<usize> for AsChunks<'_, T, N> |
58 | where |
59 | [T]: ops::Index<ops::Range<usize>, Output = [T]>, |
60 | { |
61 | type Output = [T; N]; |
62 | |
63 | #[inline (always)] |
64 | fn index(&self, index: usize) -> &Self::Output { |
65 | let start: usize = N * index; |
66 | let slice: &[T] = &self.0[start..(start + N)]; |
67 | slice.try_into().unwrap() |
68 | } |
69 | } |
70 | |
71 | impl<'a, T, const N: usize> IntoIterator for AsChunks<'a, T, N> { |
72 | type IntoIter = AsChunksIter<'a, T, N>; |
73 | type Item = &'a [T; N]; |
74 | |
75 | #[inline (always)] |
76 | fn into_iter(self) -> Self::IntoIter { |
77 | AsChunksIter(self.0.chunks_exact(N)) |
78 | } |
79 | } |
80 | |
81 | pub struct AsChunksIter<'a, T, const N: usize>(core::slice::ChunksExact<'a, T>); |
82 | |
83 | impl<'a, T, const N: usize> Iterator for AsChunksIter<'a, T, N> { |
84 | type Item = &'a [T; N]; |
85 | |
86 | #[inline (always)] |
87 | fn next(&mut self) -> Option<Self::Item> { |
88 | self.0.next().map(|x: &'a [T]| x.try_into().unwrap()) |
89 | } |
90 | } |
91 | |
92 | // `&mut [[T; N]]` is implicitly convertable to `&[[T; N]]` but our types can't |
93 | // do that. |
94 | impl<'a, T, const N: usize> From<&'a AsChunksMut<'_, T, N>> for AsChunks<'a, T, N> { |
95 | #[inline (always)] |
96 | fn from(as_mut: &'a AsChunksMut<'_, T, N>) -> Self { |
97 | Self(as_mut.as_flattened()) |
98 | } |
99 | } |
100 | |
101 | impl<'a, T, const N: usize> From<&'a [T; N]> for AsChunks<'a, T, N> { |
102 | #[inline (always)] |
103 | fn from(array: &'a [T; N]) -> Self { |
104 | Self(array) |
105 | } |
106 | } |
107 | |
108 | // TODO: `impl From<AsChunks<'a, T, {2*N}> for AsChunks<'a, T, N>`. |
109 | impl<'a, T> From<AsChunks<'a, T, 8>> for AsChunks<'a, T, 4> { |
110 | #[inline (always)] |
111 | fn from(as_2x: AsChunks<'a, T, 8>) -> Self { |
112 | Self(as_2x.0) |
113 | } |
114 | } |
115 | |