core/iter/adapters/map_windows.rs
1use crate::iter::FusedIterator;
2use crate::mem::{ManuallyDrop, MaybeUninit, SizedTypeProperties};
3use crate::{fmt, ptr};
4
5/// An iterator over the mapped windows of another iterator.
6///
7/// This `struct` is created by the [`Iterator::map_windows`]. See its
8/// documentation for more information.
9#[must_use = "iterators are lazy and do nothing unless consumed"]
10#[unstable(feature = "iter_map_windows", issue = "87155")]
11pub struct MapWindows<I: Iterator, F, const N: usize> {
12 f: F,
13 inner: MapWindowsInner<I, N>,
14}
15
16struct MapWindowsInner<I: Iterator, const N: usize> {
17 iter: I,
18 // Since iterators are assumed lazy, i.e. it only yields an item when
19 // `Iterator::next()` is called, and `MapWindows` is not an exception.
20 //
21 // Before the first iteration, we keep the buffer `None`. When the user
22 // first call `next` or other methods that makes the iterator advance,
23 // we collect the first `N` items yielded from the inner iterator and
24 // put it into the buffer.
25 //
26 // When the inner iterator has returned a `None`, we take
27 // away this `buffer` and leave it `None` to reclaim its resources.
28 //
29 // FIXME: should we shrink the size of `buffer` using niche optimization?
30 buffer: Option<Buffer<I::Item, N>>,
31}
32
33/// `Buffer<T, N>` is semantically `[MaybeUninit<T>; 2 * N]`. This helps
34/// reduce moves while iterating. However, due
35/// to limitations of const generics, we use this different type. Note that
36/// it has the same underlying memory layout.
37///
38/// # Safety invariant
39///
40/// `self.buffer[self.start..self.start + N]` must be initialized,
41/// with all other elements being uninitialized. This also
42/// implies that `self.start <= N`.
43struct Buffer<T, const N: usize> {
44 buffer: [[MaybeUninit<T>; N]; 2],
45 start: usize,
46}
47
48impl<I: Iterator, F, const N: usize> MapWindows<I, F, N> {
49 pub(in crate::iter) const fn new(iter: I, f: F) -> Self {
50 assert!(N != 0, "array in `Iterator::map_windows` must contain more than 0 elements");
51
52 // Only ZST arrays' length can be so large.
53 if I::Item::IS_ZST {
54 assert!(
55 N.checked_mul(2).is_some(),
56 "array size of `Iterator::map_windows` is too large"
57 );
58 }
59
60 Self { inner: MapWindowsInner::new(iter), f }
61 }
62}
63
64impl<I: Iterator, const N: usize> MapWindowsInner<I, N> {
65 #[inline]
66 const fn new(iter: I) -> Self {
67 Self { iter, buffer: None }
68 }
69
70 fn next_window(&mut self) -> Option<&[I::Item; N]> {
71 match self.buffer {
72 // It is the first time to advance. We collect
73 // the first `N` items from `self.iter` to initialize `self.buffer`.
74 None => self.buffer = Buffer::try_from_iter(&mut self.iter),
75 Some(ref mut buffer) => match self.iter.next() {
76 None => {
77 self.buffer.take();
78 }
79 // Advance the iterator. We first call `next` before changing our buffer
80 // at all. This means that if `next` panics, our invariant is upheld and
81 // our `Drop` impl drops the correct elements.
82 Some(item) => buffer.push(item),
83 },
84 }
85 self.buffer.as_ref().map(Buffer::as_array_ref)
86 }
87
88 fn size_hint(&self) -> (usize, Option<usize>) {
89 let (lo, hi) = self.iter.size_hint();
90 if self.buffer.is_some() {
91 // If the first `N` items are already yielded by the inner iterator,
92 // the size hint is then equal to the that of the inner iterator's.
93 (lo, hi)
94 } else {
95 // If the first `N` items are not yet yielded by the inner iterator,
96 // the first `N` elements should be counted as one window, so both bounds
97 // should subtract `N - 1`.
98 (lo.saturating_sub(N - 1), hi.map(|hi| hi.saturating_sub(N - 1)))
99 }
100 }
101}
102
103impl<T, const N: usize> Buffer<T, N> {
104 fn try_from_iter(iter: &mut impl Iterator<Item = T>) -> Option<Self> {
105 let first_half = crate::array::iter_next_chunk(iter).ok()?;
106 let buffer =
107 [MaybeUninit::new(first_half).transpose(), [const { MaybeUninit::uninit() }; N]];
108 Some(Self { buffer, start: 0 })
109 }
110
111 #[inline]
112 fn buffer_ptr(&self) -> *const MaybeUninit<T> {
113 self.buffer.as_ptr().cast()
114 }
115
116 #[inline]
117 fn buffer_mut_ptr(&mut self) -> *mut MaybeUninit<T> {
118 self.buffer.as_mut_ptr().cast()
119 }
120
121 #[inline]
122 fn as_array_ref(&self) -> &[T; N] {
123 debug_assert!(self.start + N <= 2 * N);
124
125 // SAFETY: our invariant guarantees these elements are initialized.
126 unsafe { &*self.buffer_ptr().add(self.start).cast() }
127 }
128
129 #[inline]
130 fn as_uninit_array_mut(&mut self) -> &mut MaybeUninit<[T; N]> {
131 debug_assert!(self.start + N <= 2 * N);
132
133 // SAFETY: our invariant guarantees these elements are in bounds.
134 unsafe { &mut *self.buffer_mut_ptr().add(self.start).cast() }
135 }
136
137 /// Pushes a new item `next` to the back, and pops the front-most one.
138 ///
139 /// All the elements will be shifted to the front end when pushing reaches
140 /// the back end.
141 fn push(&mut self, next: T) {
142 let buffer_mut_ptr = self.buffer_mut_ptr();
143 debug_assert!(self.start + N <= 2 * N);
144
145 let to_drop = if self.start == N {
146 // We have reached the end of our buffer and have to copy
147 // everything to the start. Example layout for N = 3.
148 //
149 // 0 1 2 3 4 5 0 1 2 3 4 5
150 // ┌───┬───┬───┬───┬───┬───┐ ┌───┬───┬───┬───┬───┬───┐
151 // │ - │ - │ - │ a │ b │ c │ -> │ b │ c │ n │ - │ - │ - │
152 // └───┴───┴───┴───┴───┴───┘ └───┴───┴───┴───┴───┴───┘
153 // ↑ ↑
154 // start start
155
156 // SAFETY: the two pointers are valid for reads/writes of N -1
157 // elements because our array's size is semantically 2 * N. The
158 // regions also don't overlap for the same reason.
159 //
160 // We leave the old elements in place. As soon as `start` is set
161 // to 0, we treat them as uninitialized and treat their copies
162 // as initialized.
163 let to_drop = unsafe {
164 ptr::copy_nonoverlapping(buffer_mut_ptr.add(self.start + 1), buffer_mut_ptr, N - 1);
165 (*buffer_mut_ptr.add(N - 1)).write(next);
166 buffer_mut_ptr.add(self.start)
167 };
168 self.start = 0;
169 to_drop
170 } else {
171 // SAFETY: `self.start` is < N as guaranteed by the invariant
172 // plus the check above. Even if the drop at the end panics,
173 // the invariant is upheld.
174 //
175 // Example layout for N = 3:
176 //
177 // 0 1 2 3 4 5 0 1 2 3 4 5
178 // ┌───┬───┬───┬───┬───┬───┐ ┌───┬───┬───┬───┬───┬───┐
179 // │ - │ a │ b │ c │ - │ - │ -> │ - │ - │ b │ c │ n │ - │
180 // └───┴───┴───┴───┴───┴───┘ └───┴───┴───┴───┴───┴───┘
181 // ↑ ↑
182 // start start
183 //
184 let to_drop = unsafe {
185 (*buffer_mut_ptr.add(self.start + N)).write(next);
186 buffer_mut_ptr.add(self.start)
187 };
188 self.start += 1;
189 to_drop
190 };
191
192 // SAFETY: the index is valid and this is element `a` in the
193 // diagram above and has not been dropped yet.
194 unsafe { ptr::drop_in_place(to_drop.cast_init()) };
195 }
196}
197
198impl<T: Clone, const N: usize> Clone for Buffer<T, N> {
199 fn clone(&self) -> Self {
200 // Use `ManuallyDrop` until buffer is fully written to avoid dropping uninitialized elements on panic.
201 // (See `Buffer` rustdoc for safety invariant)
202 let mut buffer = ManuallyDrop::new(Buffer {
203 buffer: [[const { MaybeUninit::uninit() }; N], [const { MaybeUninit::uninit() }; N]],
204 start: self.start,
205 });
206
207 // `clone()` could panic; `ManuallyDrop` guards against that.
208 buffer.as_uninit_array_mut().write(self.as_array_ref().clone());
209
210 // We initialized the buffer above, so we are good now
211 ManuallyDrop::into_inner(buffer)
212 }
213}
214
215impl<I, const N: usize> Clone for MapWindowsInner<I, N>
216where
217 I: Iterator + Clone,
218 I::Item: Clone,
219{
220 fn clone(&self) -> Self {
221 Self { iter: self.iter.clone(), buffer: self.buffer.clone() }
222 }
223}
224
225impl<T, const N: usize> Drop for Buffer<T, N> {
226 fn drop(&mut self) {
227 // SAFETY: our invariant guarantees that N elements starting from
228 // `self.start` are initialized. We drop them here.
229 unsafe {
230 let initialized_part: *mut [T] =
231 self.buffer_mut_ptr().add(self.start).cast::<T>().cast_slice(N);
232 ptr::drop_in_place(initialized_part);
233 }
234 }
235}
236
237#[unstable(feature = "iter_map_windows", issue = "87155")]
238impl<I, F, R, const N: usize> Iterator for MapWindows<I, F, N>
239where
240 I: Iterator,
241 F: FnMut(&[I::Item; N]) -> R,
242{
243 type Item = R;
244
245 fn next(&mut self) -> Option<Self::Item> {
246 let window = self.inner.next_window()?;
247 let out = (self.f)(window);
248 Some(out)
249 }
250
251 fn size_hint(&self) -> (usize, Option<usize>) {
252 self.inner.size_hint()
253 }
254}
255
256#[unstable(feature = "iter_map_windows", issue = "87155")]
257impl<I, F, R, const N: usize> FusedIterator for MapWindows<I, F, N>
258where
259 I: FusedIterator,
260 F: FnMut(&[I::Item; N]) -> R,
261{
262}
263
264#[unstable(feature = "iter_map_windows", issue = "87155")]
265impl<I, F, R, const N: usize> ExactSizeIterator for MapWindows<I, F, N>
266where
267 I: ExactSizeIterator,
268 F: FnMut(&[I::Item; N]) -> R,
269{
270}
271
272#[unstable(feature = "iter_map_windows", issue = "87155")]
273impl<I: Iterator + fmt::Debug, F, const N: usize> fmt::Debug for MapWindows<I, F, N> {
274 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
275 f.debug_struct("MapWindows").field("iter", &self.inner.iter).finish()
276 }
277}
278
279#[unstable(feature = "iter_map_windows", issue = "87155")]
280impl<I, F, const N: usize> Clone for MapWindows<I, F, N>
281where
282 I: Iterator + Clone,
283 F: Clone,
284 I::Item: Clone,
285{
286 fn clone(&self) -> Self {
287 Self { f: self.f.clone(), inner: self.inner.clone() }
288 }
289}