rune_alloc/slice/iter/macros.rs
1//! Macros used by iterators of slice.
2
3/// Convenience & performance macro for consuming the `end_or_len` field, by
4/// giving a `(&mut) usize` or `(&mut) NonNull<T>` depending whether `T` is
5/// or is not a ZST respectively.
6///
7/// Internally, this reads the `end` through a pointer-to-`NonNull` so that
8/// it'll get the appropriate non-null metadata in the backend without needing
9/// to call `assume` manually.
10macro_rules! if_zst {
11 (mut $this:ident, $len:ident => $zst_body:expr, $end:ident => $other_body:expr,) => {{
12 if T::IS_ZST {
13 // SAFETY: for ZSTs, the pointer is storing a provenance-free length,
14 // so consuming and updating it as a `usize` is fine.
15 let $len = unsafe { &mut *ptr::addr_of_mut!($this.end_or_len).cast::<usize>() };
16 $zst_body
17 } else {
18 // SAFETY: for non-ZSTs, the type invariant ensures it cannot be null
19 let $end = unsafe { &mut *ptr::addr_of_mut!($this.end_or_len).cast::<NonNull<T>>() };
20 $other_body
21 }
22 }};
23 ($this:ident, $len:ident => $zst_body:expr, $end:ident => $other_body:expr,) => {{
24 if T::IS_ZST {
25 let $len = ptr::addr($this.end_or_len);
26 $zst_body
27 } else {
28 // SAFETY: for non-ZSTs, the type invariant ensures it cannot be null
29 let $end = unsafe { *ptr::addr_of!($this.end_or_len).cast::<NonNull<T>>() };
30 $other_body
31 }
32 }};
33}
34
35// Inlining is_empty and len makes a huge performance difference
36macro_rules! is_empty {
37 ($self: ident) => {
38 if_zst!($self,
39 len => len == 0,
40 end => $self.ptr == end,
41 )
42 };
43}
44
45macro_rules! len {
46 ($self: ident) => {{
47 if_zst!($self,
48 len => len,
49 end => {
50 // To get rid of some bounds checks (see `position`), we use ptr_sub instead of
51 // offset_from (Tested by `codegen/slice-position-bounds-check`.)
52 // SAFETY: by the type invariant pointers are aligned and `start <= end`
53 unsafe { ptr::sub_ptr(end.as_ptr(), $self.ptr.as_ptr()) }
54 },
55 )
56 }};
57}
58
59// The shared definition of the `Iter` and `IterMut` iterators
60macro_rules! iterator {
61 (
62 struct $name:ident -> $ptr:ty,
63 $elem:ty,
64 $raw_mut:tt,
65 {$( $mut_:tt )?},
66 $into_ref:ident,
67 {$($extra:tt)*}
68 ) => {
69 // Returns the first element and moves the start of the iterator forwards by 1.
70 // Greatly improves performance compared to an inlined function. The iterator
71 // must not be empty.
72 macro_rules! next_unchecked {
73 ($self: ident) => { $self.post_inc_start(1).$into_ref() }
74 }
75
76 // Returns the last element and moves the end of the iterator backwards by 1.
77 // Greatly improves performance compared to an inlined function. The iterator
78 // must not be empty.
79 macro_rules! next_back_unchecked {
80 ($self: ident) => { $self.pre_dec_end(1).$into_ref() }
81 }
82
83 impl<T> $name<T> {
84 // Helper function for creating a slice from the iterator.
85 #[inline(always)]
86 unsafe fn make_slice<'a>(&self) -> &'a [T] {
87 // SAFETY: the iterator was created from a slice with pointer
88 // `self.ptr` and length `len!(self)`. This guarantees that all
89 // the prerequisites for `from_raw_parts` are fulfilled.
90 from_raw_parts(self.ptr.as_ptr(), len!(self))
91 }
92
93 // Helper function for moving the start of the iterator forwards by `offset` elements,
94 // returning the old start.
95 // Unsafe because the offset must not exceed `self.len()`.
96 #[inline(always)]
97 unsafe fn post_inc_start(&mut self, offset: usize) -> NonNull<T> {
98 let old = self.ptr;
99
100 // SAFETY: the caller guarantees that `offset` doesn't exceed `self.len()`,
101 // so this new pointer is inside `self` and thus guaranteed to be non-null.
102 unsafe {
103 if_zst!(mut self,
104 len => *len = len.wrapping_sub(offset),
105 _end => self.ptr = ptr::nonnull_add(self.ptr, offset),
106 );
107 }
108 old
109 }
110
111 // Helper function for moving the end of the iterator backwards by `offset` elements,
112 // returning the new end.
113 // Unsafe because the offset must not exceed `self.len()`.
114 #[inline(always)]
115 unsafe fn pre_dec_end(&mut self, offset: usize) -> NonNull<T> {
116 if_zst!(mut self,
117 // SAFETY: By our precondition, `offset` can be at most the
118 // current length, so the subtraction can never overflow.
119 len => unsafe {
120 *len = len.wrapping_sub(offset);
121 self.ptr
122 },
123 // SAFETY: the caller guarantees that `offset` doesn't exceed `self.len()`,
124 // which is guaranteed to not overflow an `isize`. Also, the resulting pointer
125 // is in bounds of `slice`, which fulfills the other requirements for `offset`.
126 end => unsafe {
127 *end = ptr::nonnull_sub(*end, offset);
128 *end
129 },
130 )
131 }
132 }
133
134 impl<T> ExactSizeIterator for $name<T> {
135 #[inline(always)]
136 fn len(&self) -> usize {
137 len!(self)
138 }
139 }
140
141 impl<T> Iterator for $name<T> {
142 type Item = $elem;
143
144 #[inline]
145 fn next(&mut self) -> Option<$elem> {
146 // could be implemented with slices, but this avoids bounds checks
147
148 // SAFETY: The call to `next_unchecked!` is
149 // safe since we check if the iterator is empty first.
150 unsafe {
151 if is_empty!(self) {
152 None
153 } else {
154 Some(next_unchecked!(self))
155 }
156 }
157 }
158
159 #[inline]
160 fn size_hint(&self) -> (usize, Option<usize>) {
161 let exact = len!(self);
162 (exact, Some(exact))
163 }
164
165 #[inline]
166 fn count(self) -> usize {
167 len!(self)
168 }
169
170 #[inline]
171 fn nth(&mut self, n: usize) -> Option<$elem> {
172 if n >= len!(self) {
173 // This iterator is now empty.
174 if_zst!(mut self,
175 len => *len = 0,
176 end => self.ptr = *end,
177 );
178 return None;
179 }
180 // SAFETY: We are in bounds. `post_inc_start` does the right thing even for ZSTs.
181 unsafe {
182 self.post_inc_start(n);
183 Some(next_unchecked!(self))
184 }
185 }
186
187 #[inline]
188 fn last(mut self) -> Option<$elem> {
189 self.next_back()
190 }
191
192 #[inline]
193 fn fold<B, F>(self, init: B, mut f: F) -> B
194 where
195 F: FnMut(B, Self::Item) -> B,
196 {
197 // this implementation consists of the following optimizations compared to the
198 // default implementation:
199 // - do-while loop, as is llvm's preferred loop shape,
200 // see https://releases.llvm.org/16.0.0/docs/LoopTerminology.html#more-canonical-loops
201 // - bumps an index instead of a pointer since the latter case inhibits
202 // some optimizations, see #111603
203 // - avoids Option wrapping/matching
204 if is_empty!(self) {
205 return init;
206 }
207 let mut acc = init;
208 let mut i = 0;
209 let len = len!(self);
210 loop {
211 // SAFETY: the loop iterates `i in 0..len`, which always is in bounds of
212 // the slice allocation
213 acc = f(acc, unsafe { & $( $mut_ )? *ptr::nonnull_add(self.ptr, i).as_ptr() });
214 // SAFETY: `i` can't overflow since it'll only reach usize::MAX if the
215 // slice had that length, in which case we'll break out of the loop
216 // after the increment
217 i = unsafe { i.wrapping_add(1) };
218 if i == len {
219 break;
220 }
221 }
222 acc
223 }
224
225 // We override the default implementation, which uses `try_fold`,
226 // because this simple implementation generates less LLVM IR and is
227 // faster to compile.
228 #[inline]
229 fn for_each<F>(mut self, mut f: F)
230 where
231 Self: Sized,
232 F: FnMut(Self::Item),
233 {
234 while let Some(x) = self.next() {
235 f(x);
236 }
237 }
238
239 // We override the default implementation, which uses `try_fold`,
240 // because this simple implementation generates less LLVM IR and is
241 // faster to compile.
242 #[inline]
243 fn all<F>(&mut self, mut f: F) -> bool
244 where
245 Self: Sized,
246 F: FnMut(Self::Item) -> bool,
247 {
248 while let Some(x) = self.next() {
249 if !f(x) {
250 return false;
251 }
252 }
253 true
254 }
255
256 // We override the default implementation, which uses `try_fold`,
257 // because this simple implementation generates less LLVM IR and is
258 // faster to compile.
259 #[inline]
260 fn any<F>(&mut self, mut f: F) -> bool
261 where
262 Self: Sized,
263 F: FnMut(Self::Item) -> bool,
264 {
265 while let Some(x) = self.next() {
266 if f(x) {
267 return true;
268 }
269 }
270 false
271 }
272
273 // We override the default implementation, which uses `try_fold`,
274 // because this simple implementation generates less LLVM IR and is
275 // faster to compile.
276 #[inline]
277 fn find<P>(&mut self, mut predicate: P) -> Option<Self::Item>
278 where
279 Self: Sized,
280 P: FnMut(&Self::Item) -> bool,
281 {
282 while let Some(x) = self.next() {
283 if predicate(&x) {
284 return Some(x);
285 }
286 }
287 None
288 }
289
290 // We override the default implementation, which uses `try_fold`,
291 // because this simple implementation generates less LLVM IR and is
292 // faster to compile.
293 #[inline]
294 fn find_map<B, F>(&mut self, mut f: F) -> Option<B>
295 where
296 Self: Sized,
297 F: FnMut(Self::Item) -> Option<B>,
298 {
299 while let Some(x) = self.next() {
300 if let Some(y) = f(x) {
301 return Some(y);
302 }
303 }
304 None
305 }
306
307 // We override the default implementation, which uses `try_fold`,
308 // because this simple implementation generates less LLVM IR and is
309 // faster to compile. Also, the `assume` avoids a bounds check.
310 #[inline]
311 fn position<P>(&mut self, mut predicate: P) -> Option<usize> where
312 Self: Sized,
313 P: FnMut(Self::Item) -> bool,
314 {
315 let n = len!(self);
316 let mut i = 0;
317 while let Some(x) = self.next() {
318 if predicate(x) {
319 // SAFETY: we are guaranteed to be in bounds by the loop invariant:
320 // when `i >= n`, `self.next()` returns `None` and the loop breaks.
321 unsafe { assume(i < n) };
322 return Some(i);
323 }
324 i += 1;
325 }
326 None
327 }
328
329 // We override the default implementation, which uses `try_fold`,
330 // because this simple implementation generates less LLVM IR and is
331 // faster to compile. Also, the `assume` avoids a bounds check.
332 #[inline]
333 fn rposition<P>(&mut self, mut predicate: P) -> Option<usize> where
334 P: FnMut(Self::Item) -> bool,
335 Self: Sized + ExactSizeIterator + DoubleEndedIterator
336 {
337 let n = len!(self);
338 let mut i = n;
339 while let Some(x) = self.next_back() {
340 i -= 1;
341 if predicate(x) {
342 // SAFETY: `i` must be lower than `n` since it starts at `n`
343 // and is only decreasing.
344 unsafe { assume(i < n) };
345 return Some(i);
346 }
347 }
348 None
349 }
350
351 $($extra)*
352 }
353
354 impl<T> DoubleEndedIterator for $name<T> {
355 #[inline]
356 fn next_back(&mut self) -> Option<$elem> {
357 // could be implemented with slices, but this avoids bounds checks
358
359 // SAFETY: The call to `next_back_unchecked!`
360 // is safe since we check if the iterator is empty first.
361 unsafe {
362 if is_empty!(self) {
363 None
364 } else {
365 Some(next_back_unchecked!(self))
366 }
367 }
368 }
369
370 #[inline]
371 fn nth_back(&mut self, n: usize) -> Option<$elem> {
372 if n >= len!(self) {
373 // This iterator is now empty.
374 if_zst!(mut self,
375 len => *len = 0,
376 end => *end = self.ptr,
377 );
378 return None;
379 }
380 // SAFETY: We are in bounds. `pre_dec_end` does the right thing even for ZSTs.
381 unsafe {
382 self.pre_dec_end(n);
383 Some(next_back_unchecked!(self))
384 }
385 }
386 }
387
388 impl<T> FusedIterator for $name<T> {}
389 }
390}