Skip to main content

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}