1#[cfg(creusot)]
4use creusot_std::prelude::*;
5
6pub trait SliceExt {
8 type Item;
10
11 #[cfg(creusot)]
14 #[logic]
15 fn view2(self) -> Seq<Self::Item>;
16
17 #[cfg_attr(creusot, requires(N@ != 0))]
27 #[cfg_attr(creusot, ensures(forall<i> i >= 0 && i < self.view2().len() - self.view2().len() % N@ ==> result.0@[i]@ == self.view2().subsequence(i * N@, i * N@ + N@)))]
28 #[cfg_attr(creusot, ensures(result.1@ == self.view2().subsequence(self.view2().len() - self.view2().len() % N@, self.view2().len())))]
29 fn bitcoin_as_chunks<const N: usize>(&self) -> (&[[Self::Item; N]], &[Self::Item]);
30
31 #[cfg_attr(creusot, requires(N@ != 0))]
41 #[cfg_attr(creusot, ensures(forall<i> i >= 0 && i < self.view2().len() - self.view2().len() % N@ ==> result.0@[i]@ == self.view2().subsequence(i * N@, i * N@ + N@)))]
42 #[cfg_attr(creusot, ensures(result.1@ == self.view2().subsequence(self.view2().len() - self.view2().len() % N@, self.view2().len())))]
43 fn bitcoin_as_chunks_mut<const N: usize>(
44 &mut self,
45 ) -> (&mut [[Self::Item; N]], &mut [Self::Item]);
46
47 #[cfg_attr(creusot, ensures(match result {
51 Some(arr) => arr@ == self.view2().subsequence(offset@, offset@ + ARRAY_LEN@),
52 None => offset@ + ARRAY_LEN@ > self.view2().len(),
53 }))]
54 fn get_array<const ARRAY_LEN: usize>(&self, offset: usize) -> Option<&[Self::Item; ARRAY_LEN]>;
55
56 #[allow(clippy::type_complexity)] #[cfg_attr(creusot, ensures(match result {
62 Some((arr, remainder)) => arr@ == self.view2().subsequence(0, ARRAY_LEN@) && remainder@ == self.view2().subsequence(ARRAY_LEN@, self.view2().len()),
63 None => ARRAY_LEN@ > self.view2().len(),
64 }))]
65 fn split_first_chunk<const ARRAY_LEN: usize>(
66 &self,
67 ) -> Option<(&[Self::Item; ARRAY_LEN], &[Self::Item])>;
68
69 #[allow(clippy::type_complexity)] #[cfg_attr(creusot, ensures(match result {
75 Some((remainder, arr)) => arr@ == self.view2().subsequence(self.view2().len() - ARRAY_LEN@, self.view2().len()) && remainder@ == self.view2().subsequence(0, self.view2().len() - ARRAY_LEN@),
76 None => ARRAY_LEN@ > self.view2().len(),
77 }))]
78 fn split_last_chunk<const ARRAY_LEN: usize>(
79 &self,
80 ) -> Option<(&[Self::Item], &[Self::Item; ARRAY_LEN])>;
81}
82
83impl<T> SliceExt for [T] {
84 type Item = T;
85
86 #[cfg(creusot)]
87 #[logic(open, inline)]
88 fn view2(self) -> Seq<Self::Item> { <Self as View>::view(self) }
89
90 #[cfg_attr(creusot, trusted)]
92 #[cfg_attr(creusot, requires(N@ != 0))]
93 #[cfg_attr(creusot, ensures(forall<i> i >= 0 && i < self.view2().len() - self.view2().len() % N@ ==> result.0@[i]@ == self.view2().subsequence(i * N@, i * N@ + N@)))]
94 #[cfg_attr(creusot, ensures(result.1@ == self.view2().subsequence(self.view2().len() - self.view2().len() % N@, self.view2().len())))]
95 fn bitcoin_as_chunks<const N: usize>(&self) -> (&[[Self::Item; N]], &[Self::Item]) {
96 #[allow(clippy::let_unit_value)]
97 let () = Hack::<N>::IS_NONZERO;
98
99 let chunks_count = self.len() / N;
100 let total_left_len = chunks_count * N;
101 let (left, right) = self.split_at(total_left_len);
102 let left = unsafe {
107 core::slice::from_raw_parts(left.as_ptr().cast::<[Self::Item; N]>(), chunks_count)
108 };
109 (left, right)
110 }
111
112 #[cfg_attr(creusot, trusted)]
114 #[cfg_attr(creusot, requires(N@ != 0))]
115 #[cfg_attr(creusot, ensures(forall<i> i >= 0 && i < self.view2().len() - self.view2().len() % N@ ==> result.0@[i]@ == self.view2().subsequence(i * N@, i * N@ + N@)))]
116 #[cfg_attr(creusot, ensures(result.1@ == self.view2().subsequence(self.view2().len() - self.view2().len() % N@, self.view2().len())))]
117 fn bitcoin_as_chunks_mut<const N: usize>(
118 &mut self,
119 ) -> (&mut [[Self::Item; N]], &mut [Self::Item]) {
120 #[allow(clippy::let_unit_value)]
121 let () = Hack::<N>::IS_NONZERO;
122
123 let chunks_count = self.len() / N;
124 let total_left_len = chunks_count * N;
125 let (left, right) = self.split_at_mut(total_left_len);
126 let left = unsafe {
131 core::slice::from_raw_parts_mut(
132 left.as_mut_ptr().cast::<[Self::Item; N]>(),
133 chunks_count,
134 )
135 };
136 (left, right)
137 }
138
139 #[cfg_attr(creusot, ensures(match result {
140 Some(arr) => arr@ == self.view2().subsequence(offset@, offset@ + ARRAY_LEN@),
141 None => offset@ + ARRAY_LEN@ > self.view2().len(),
142 }))]
143 fn get_array<const ARRAY_LEN: usize>(&self, offset: usize) -> Option<&[Self::Item; ARRAY_LEN]> {
144 let end = offset.checked_add(ARRAY_LEN)?;
145 self.get(offset..end).map(|slice| {
146 slice
147 .try_into()
148 .expect("the arguments to `get` evaluate to the same length the return type uses")
149 })
150 }
151
152 #[cfg_attr(creusot, ensures(match result {
153 Some((arr, remainder)) => arr@ == self.view2().subsequence(0, ARRAY_LEN@) && remainder@ == self.view2().subsequence(ARRAY_LEN@, self.view2().len()),
154 None => ARRAY_LEN@ > self.view2().len(),
155 }))]
156 fn split_first_chunk<const ARRAY_LEN: usize>(
157 &self,
158 ) -> Option<(&[Self::Item; ARRAY_LEN], &[Self::Item])> {
159 if self.len() < ARRAY_LEN {
160 return None;
161 }
162 let (first, remainder) = self.split_at(ARRAY_LEN);
163 Some((first.try_into().expect("we're passing `ARRAY_LEN` to `split_at` above"), remainder))
164 }
165
166 #[cfg_attr(creusot, ensures(match result {
167 Some((remainder, arr)) => arr@ == self.view2().subsequence(self.view2().len() - ARRAY_LEN@, self.view2().len()) && remainder@ == self.view2().subsequence(0, self.view2().len() - ARRAY_LEN@),
168 None => ARRAY_LEN@ > self.view2().len(),
169 }))]
170 fn split_last_chunk<const ARRAY_LEN: usize>(
171 &self,
172 ) -> Option<(&[Self::Item], &[Self::Item; ARRAY_LEN])> {
173 if self.len() < ARRAY_LEN {
174 return None;
175 }
176 let (remainder, last) = self.split_at(self.len() - ARRAY_LEN);
177 Some((
178 remainder,
179 last.try_into().expect("we're passing `self.len() - ARRAY_LEN` to `split_at` above"),
180 ))
181 }
182}
183
184struct Hack<const N: usize>;
185
186impl<const N: usize> Hack<N> {
187 const IS_NONZERO: () = {
188 assert!(N != 0);
189 };
190}
191
192#[cfg(test)]
193mod tests {
194 use super::SliceExt;
195
196 const EMPTY: &[i32] = &[];
198
199 #[test]
200 fn one_to_one() {
201 let slice = [1];
202 let (left, right) = slice.bitcoin_as_chunks::<1>();
203 assert_eq!(left, &[[1]]);
204 assert_eq!(right, EMPTY);
205 }
206
207 #[test]
208 fn one_to_two() {
209 const EMPTY_LEFT: &[[i32; 2]] = &[];
210
211 let slice = [1i32];
212 let (left, right) = slice.bitcoin_as_chunks::<2>();
213 assert_eq!(left, EMPTY_LEFT);
214 assert_eq!(right, &[1]);
215 }
216
217 #[test]
218 fn two_to_one() {
219 let slice = [1, 2];
220 let (left, right) = slice.bitcoin_as_chunks::<1>();
221 assert_eq!(left, &[[1], [2]]);
222 assert_eq!(right, EMPTY);
223 }
224
225 #[test]
226 fn two_to_two() {
227 let slice = [1, 2];
228 let (left, right) = slice.bitcoin_as_chunks::<2>();
229 assert_eq!(left, &[[1, 2]]);
230 assert_eq!(right, EMPTY);
231 }
232
233 #[test]
234 fn three_to_two() {
235 let slice = [1, 2, 3];
236 let (left, right) = slice.bitcoin_as_chunks::<2>();
237 assert_eq!(left, &[[1, 2]]);
238 assert_eq!(right, &[3]);
239 }
240}