bitcoin_internals/
slice.rs1pub trait SliceExt {
5 type Item;
7
8 fn bitcoin_as_chunks<const N: usize>(&self) -> (&[[Self::Item; N]], &[Self::Item]);
18
19 fn bitcoin_as_chunks_mut<const N: usize>(
29 &mut self,
30 ) -> (&mut [[Self::Item; N]], &mut [Self::Item]);
31
32 fn get_array<const ARRAY_LEN: usize>(&self, offset: usize) -> Option<&[Self::Item; ARRAY_LEN]>;
36
37 #[allow(clippy::type_complexity)] fn split_first_chunk<const ARRAY_LEN: usize>(
43 &self,
44 ) -> Option<(&[Self::Item; ARRAY_LEN], &[Self::Item])>;
45
46 #[allow(clippy::type_complexity)] fn split_last_chunk<const ARRAY_LEN: usize>(
52 &self,
53 ) -> Option<(&[Self::Item], &[Self::Item; ARRAY_LEN])>;
54}
55
56impl<T> SliceExt for [T] {
57 type Item = T;
58
59 fn bitcoin_as_chunks<const N: usize>(&self) -> (&[[Self::Item; N]], &[Self::Item]) {
60 #[allow(clippy::let_unit_value)]
61 let _ = Hack::<N>::IS_NONZERO;
62
63 let chunks_count = self.len() / N;
64 let total_left_len = chunks_count * N;
65 let (left, right) = self.split_at(total_left_len);
66 let left = unsafe {
71 core::slice::from_raw_parts(left.as_ptr().cast::<[Self::Item; N]>(), chunks_count)
72 };
73 (left, right)
74 }
75
76 fn bitcoin_as_chunks_mut<const N: usize>(
77 &mut self,
78 ) -> (&mut [[Self::Item; N]], &mut [Self::Item]) {
79 #[allow(clippy::let_unit_value)]
80 let _ = Hack::<N>::IS_NONZERO;
81
82 let chunks_count = self.len() / N;
83 let total_left_len = chunks_count * N;
84 let (left, right) = self.split_at_mut(total_left_len);
85 let left = unsafe {
90 core::slice::from_raw_parts_mut(
91 left.as_mut_ptr().cast::<[Self::Item; N]>(),
92 chunks_count,
93 )
94 };
95 (left, right)
96 }
97
98 fn get_array<const ARRAY_LEN: usize>(&self, offset: usize) -> Option<&[Self::Item; ARRAY_LEN]> {
99 self.get(offset..(offset + ARRAY_LEN)).map(|slice| {
100 slice
101 .try_into()
102 .expect("the arguments to `get` evaluate to the same length the return type uses")
103 })
104 }
105
106 fn split_first_chunk<const ARRAY_LEN: usize>(
107 &self,
108 ) -> Option<(&[Self::Item; ARRAY_LEN], &[Self::Item])> {
109 if self.len() < ARRAY_LEN {
110 return None;
111 }
112 let (first, remainder) = self.split_at(ARRAY_LEN);
113 Some((first.try_into().expect("we're passing `ARRAY_LEN` to `split_at` above"), remainder))
114 }
115
116 fn split_last_chunk<const ARRAY_LEN: usize>(
117 &self,
118 ) -> Option<(&[Self::Item], &[Self::Item; ARRAY_LEN])> {
119 if self.len() < ARRAY_LEN {
120 return None;
121 }
122 let (remainder, last) = self.split_at(self.len() - ARRAY_LEN);
123 Some((
124 remainder,
125 last.try_into().expect("we're passing `self.len() - ARRAY_LEN` to `split_at` above"),
126 ))
127 }
128}
129
130struct Hack<const N: usize>;
131
132impl<const N: usize> Hack<N> {
133 const IS_NONZERO: () = {
134 assert!(N != 0);
135 };
136}
137
138#[cfg(test)]
139mod tests {
140 use super::SliceExt;
141
142 const EMPTY: &[i32] = &[];
144
145 #[test]
146 fn one_to_one() {
147 let slice = [1];
148 let (left, right) = slice.bitcoin_as_chunks::<1>();
149 assert_eq!(left, &[[1]]);
150 assert_eq!(right, EMPTY);
151 }
152
153 #[test]
154 fn one_to_two() {
155 const EMPTY_LEFT: &[[i32; 2]] = &[];
156
157 let slice = [1i32];
158 let (left, right) = slice.bitcoin_as_chunks::<2>();
159 assert_eq!(left, EMPTY_LEFT);
160 assert_eq!(right, &[1]);
161 }
162
163 #[test]
164 fn two_to_one() {
165 let slice = [1, 2];
166 let (left, right) = slice.bitcoin_as_chunks::<1>();
167 assert_eq!(left, &[[1], [2]]);
168 assert_eq!(right, EMPTY);
169 }
170
171 #[test]
172 fn two_to_two() {
173 let slice = [1, 2];
174 let (left, right) = slice.bitcoin_as_chunks::<2>();
175 assert_eq!(left, &[[1, 2]]);
176 assert_eq!(right, EMPTY);
177 }
178
179 #[test]
180 fn three_to_two() {
181 let slice = [1, 2, 3];
182 let (left, right) = slice.bitcoin_as_chunks::<2>();
183 assert_eq!(left, &[[1, 2]]);
184 assert_eq!(right, &[3]);
185 }
186}