Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 7f787e3

Browse files
authoredAug 13, 2023
Rollup merge of #94667 - frank-king:feature/iter_map_windows, r=Mark-Simulacrum
Add `Iterator::map_windows` Tracking issue: #87155. This is inherited from the old PR #82413. Unlike #82413, this PR implements the `MapWindows` to be lazy: only when pulling from the outer iterator, `.next()` of the inner iterator will be called. ## Implementaion Steps - [x] Implement `MapWindows` to keep the iterators' [*Laziness*](https://doc.rust-lang.org/std/iter/index.html#laziness) contract. - [x] Fix the known bug of memory access error. - [ ] Full specialization of iterator-related traits for `MapWindows`. - [x] `Iterator::size_hint`, - [x] ~`Iterator::count`~, - [x] `ExactSizeIterator` (when `I: ExactSizeIterator`), - [x] ~`TrustedLen` (when `I: TrustedLen`)~, - [x] `FusedIterator`, - [x] ~`Iterator::advance_by`~, - [x] ~`Iterator::nth`~, - [ ] ... - [ ] More tests and docs. ## Unresolved Questions: - [ ] Is there any more iterator-related traits should be specialized? - [ ] Is the double-space buffer worth? - [ ] Should there be `rmap_windows` or something else? - [ ] Taking GAT for consideration, should the mapper function be `FnMut(&[I::Item; N]) -> R` or something like `FnMut(ArrayView<'_, I::Item, N>) -> R`? Where `ArrayView` is mentioned in rust-lang/generic-associated-types-initiative#2. - It can save memory, only the same size as the array window is needed, - It is more efficient, which requires less data copies, - It is possibly compatible with the GATified version of `LendingIterator::windows`. - But it prevents the array pattern matching like `iter.map_windows(|_arr: [_; N]| ())`, unless we extend the array pattern to allow matching the `ArrayView`.
2 parents 644e806 + 97c953f commit 7f787e3

File tree

7 files changed

+743
-1
lines changed

7 files changed

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

‎library/core/src/iter/adapters/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ mod inspect;
1616
mod intersperse;
1717
mod map;
1818
mod map_while;
19+
mod map_windows;
1920
mod peekable;
2021
mod rev;
2122
mod scan;
@@ -57,6 +58,9 @@ pub use self::intersperse::{Intersperse, IntersperseWith};
5758
#[stable(feature = "iter_map_while", since = "1.57.0")]
5859
pub use self::map_while::MapWhile;
5960

61+
#[unstable(feature = "iter_map_windows", reason = "recently added", issue = "87155")]
62+
pub use self::map_windows::MapWindows;
63+
6064
#[unstable(feature = "trusted_random_access", issue = "none")]
6165
pub use self::zip::TrustedRandomAccess;
6266

‎library/core/src/iter/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -440,6 +440,8 @@ pub use self::adapters::Copied;
440440
pub use self::adapters::Flatten;
441441
#[stable(feature = "iter_map_while", since = "1.57.0")]
442442
pub use self::adapters::MapWhile;
443+
#[unstable(feature = "iter_map_windows", reason = "recently added", issue = "87155")]
444+
pub use self::adapters::MapWindows;
443445
#[unstable(feature = "inplace_iteration", issue = "none")]
444446
pub use self::adapters::SourceIter;
445447
#[stable(feature = "iterator_step_by", since = "1.28.0")]

‎library/core/src/iter/traits/iterator.rs

Lines changed: 159 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ use super::super::{ArrayChunks, Chain, Cloned, Copied, Cycle, Enumerate, Filter,
1010
use super::super::{FlatMap, Flatten};
1111
use super::super::{FromIterator, Intersperse, IntersperseWith, Product, Sum, Zip};
1212
use super::super::{
13-
Inspect, Map, MapWhile, Peekable, Rev, Scan, Skip, SkipWhile, StepBy, Take, TakeWhile,
13+
Inspect, Map, MapWhile, MapWindows, Peekable, Rev, Scan, Skip, SkipWhile, StepBy, Take,
14+
TakeWhile,
1415
};
1516

1617
fn _assert_is_object_safe(_: &dyn Iterator<Item = ()>) {}
@@ -1591,6 +1592,163 @@ pub trait Iterator {
15911592
Flatten::new(self)
15921593
}
15931594

1595+
/// Calls the given function `f` for each contiguous window of size `N` over
1596+
/// `self` and returns an iterator over the outputs of `f`. Like [`slice::windows()`],
1597+
/// the windows during mapping overlap as well.
1598+
///
1599+
/// In the following example, the closure is called three times with the
1600+
/// arguments `&['a', 'b']`, `&['b', 'c']` and `&['c', 'd']` respectively.
1601+
///
1602+
/// ```
1603+
/// #![feature(iter_map_windows)]
1604+
///
1605+
/// let strings = "abcd".chars()
1606+
/// .map_windows(|[x, y]| format!("{}+{}", x, y))
1607+
/// .collect::<Vec<String>>();
1608+
///
1609+
/// assert_eq!(strings, vec!["a+b", "b+c", "c+d"]);
1610+
/// ```
1611+
///
1612+
/// Note that the const parameter `N` is usually inferred by the
1613+
/// destructured argument in the closure.
1614+
///
1615+
/// The returned iterator yields 𝑘 − `N` + 1 items (where 𝑘 is the number of
1616+
/// items yielded by `self`). If 𝑘 is less than `N`, this method yields an
1617+
/// empty iterator.
1618+
///
1619+
/// The returned iterator implements [`FusedIterator`], because once `self`
1620+
/// returns `None`, even if it returns a `Some(T)` again in the next iterations,
1621+
/// we cannot put it into a contigious array buffer, and thus the returned iterator
1622+
/// should be fused.
1623+
///
1624+
/// [`slice::windows()`]: slice::windows
1625+
/// [`FusedIterator`]: crate::iter::FusedIterator
1626+
///
1627+
/// # Panics
1628+
///
1629+
/// Panics if `N` is 0. This check will most probably get changed to a
1630+
/// compile time error before this method gets stabilized.
1631+
///
1632+
/// ```should_panic
1633+
/// #![feature(iter_map_windows)]
1634+
///
1635+
/// let iter = std::iter::repeat(0).map_windows(|&[]| ());
1636+
/// ```
1637+
///
1638+
/// # Examples
1639+
///
1640+
/// Building the sums of neighboring numbers.
1641+
///
1642+
/// ```
1643+
/// #![feature(iter_map_windows)]
1644+
///
1645+
/// let mut it = [1, 3, 8, 1].iter().map_windows(|&[a, b]| a + b);
1646+
/// assert_eq!(it.next(), Some(4)); // 1 + 3
1647+
/// assert_eq!(it.next(), Some(11)); // 3 + 8
1648+
/// assert_eq!(it.next(), Some(9)); // 8 + 1
1649+
/// assert_eq!(it.next(), None);
1650+
/// ```
1651+
///
1652+
/// Since the elements in the following example implement `Copy`, we can
1653+
/// just copy the array and get an iterator over the windows.
1654+
///
1655+
/// ```
1656+
/// #![feature(iter_map_windows)]
1657+
///
1658+
/// let mut it = "ferris".chars().map_windows(|w: &[_; 3]| *w);
1659+
/// assert_eq!(it.next(), Some(['f', 'e', 'r']));
1660+
/// assert_eq!(it.next(), Some(['e', 'r', 'r']));
1661+
/// assert_eq!(it.next(), Some(['r', 'r', 'i']));
1662+
/// assert_eq!(it.next(), Some(['r', 'i', 's']));
1663+
/// assert_eq!(it.next(), None);
1664+
/// ```
1665+
///
1666+
/// You can also use this function to check the sortedness of an iterator.
1667+
/// For the simple case, rather use [`Iterator::is_sorted`].
1668+
///
1669+
/// ```
1670+
/// #![feature(iter_map_windows)]
1671+
///
1672+
/// let mut it = [0.5, 1.0, 3.5, 3.0, 8.5, 8.5, f32::NAN].iter()
1673+
/// .map_windows(|[a, b]| a <= b);
1674+
///
1675+
/// assert_eq!(it.next(), Some(true)); // 0.5 <= 1.0
1676+
/// assert_eq!(it.next(), Some(true)); // 1.0 <= 3.5
1677+
/// assert_eq!(it.next(), Some(false)); // 3.5 <= 3.0
1678+
/// assert_eq!(it.next(), Some(true)); // 3.0 <= 8.5
1679+
/// assert_eq!(it.next(), Some(true)); // 8.5 <= 8.5
1680+
/// assert_eq!(it.next(), Some(false)); // 8.5 <= NAN
1681+
/// assert_eq!(it.next(), None);
1682+
/// ```
1683+
///
1684+
/// For non-fused iterators, they are fused after `map_windows`.
1685+
///
1686+
/// ```
1687+
/// #![feature(iter_map_windows)]
1688+
///
1689+
/// #[derive(Default)]
1690+
/// struct NonFusedIterator {
1691+
/// state: i32,
1692+
/// }
1693+
///
1694+
/// impl Iterator for NonFusedIterator {
1695+
/// type Item = i32;
1696+
///
1697+
/// fn next(&mut self) -> Option<i32> {
1698+
/// let val = self.state;
1699+
/// self.state = self.state + 1;
1700+
///
1701+
/// // yields `0..5` first, then only even numbers since `6..`.
1702+
/// if val < 5 || val % 2 == 0 {
1703+
/// Some(val)
1704+
/// } else {
1705+
/// None
1706+
/// }
1707+
/// }
1708+
/// }
1709+
///
1710+
///
1711+
/// let mut iter = NonFusedIterator::default();
1712+
///
1713+
/// // yields 0..5 first.
1714+
/// assert_eq!(iter.next(), Some(0));
1715+
/// assert_eq!(iter.next(), Some(1));
1716+
/// assert_eq!(iter.next(), Some(2));
1717+
/// assert_eq!(iter.next(), Some(3));
1718+
/// assert_eq!(iter.next(), Some(4));
1719+
/// // then we can see our iterator going back and forth
1720+
/// assert_eq!(iter.next(), None);
1721+
/// assert_eq!(iter.next(), Some(6));
1722+
/// assert_eq!(iter.next(), None);
1723+
/// assert_eq!(iter.next(), Some(8));
1724+
/// assert_eq!(iter.next(), None);
1725+
///
1726+
/// // however, with `.map_windows()`, it is fused.
1727+
/// let mut iter = NonFusedIterator::default()
1728+
/// .map_windows(|arr: &[_; 2]| *arr);
1729+
///
1730+
/// assert_eq!(iter.next(), Some([0, 1]));
1731+
/// assert_eq!(iter.next(), Some([1, 2]));
1732+
/// assert_eq!(iter.next(), Some([2, 3]));
1733+
/// assert_eq!(iter.next(), Some([3, 4]));
1734+
/// assert_eq!(iter.next(), None);
1735+
///
1736+
/// // it will always return `None` after the first time.
1737+
/// assert_eq!(iter.next(), None);
1738+
/// assert_eq!(iter.next(), None);
1739+
/// assert_eq!(iter.next(), None);
1740+
/// ```
1741+
#[inline]
1742+
#[unstable(feature = "iter_map_windows", reason = "recently added", issue = "87155")]
1743+
#[rustc_do_not_const_check]
1744+
fn map_windows<F, R, const N: usize>(self, f: F) -> MapWindows<Self, F, N>
1745+
where
1746+
Self: Sized,
1747+
F: FnMut(&[Self::Item; N]) -> R,
1748+
{
1749+
MapWindows::new(self, f)
1750+
}
1751+
15941752
/// Creates an iterator which ends after the first [`None`].
15951753
///
15961754
/// After an iterator returns [`None`], future calls may or may not yield
Lines changed: 283 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,283 @@
1+
use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};
2+
3+
#[cfg(not(panic = "abort"))]
4+
mod drop_checks {
5+
//! These tests mainly make sure the elements are correctly dropped.
6+
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering::SeqCst};
7+
8+
#[derive(Debug)]
9+
struct DropInfo {
10+
dropped_twice: AtomicBool,
11+
alive_count: AtomicUsize,
12+
}
13+
14+
impl DropInfo {
15+
const fn new() -> Self {
16+
Self { dropped_twice: AtomicBool::new(false), alive_count: AtomicUsize::new(0) }
17+
}
18+
19+
#[track_caller]
20+
fn check(&self) {
21+
assert!(!self.dropped_twice.load(SeqCst), "a value was dropped twice");
22+
assert_eq!(self.alive_count.load(SeqCst), 0);
23+
}
24+
}
25+
26+
#[derive(Debug)]
27+
struct DropCheck<'a> {
28+
info: &'a DropInfo,
29+
was_dropped: bool,
30+
}
31+
32+
impl<'a> DropCheck<'a> {
33+
fn new(info: &'a DropInfo) -> Self {
34+
info.alive_count.fetch_add(1, SeqCst);
35+
36+
Self { info, was_dropped: false }
37+
}
38+
}
39+
40+
impl Drop for DropCheck<'_> {
41+
fn drop(&mut self) {
42+
if self.was_dropped {
43+
self.info.dropped_twice.store(true, SeqCst);
44+
}
45+
self.was_dropped = true;
46+
47+
self.info.alive_count.fetch_sub(1, SeqCst);
48+
}
49+
}
50+
51+
fn iter(info: &DropInfo, len: usize, panic_at: usize) -> impl Iterator<Item = DropCheck<'_>> {
52+
(0..len).map(move |i| {
53+
if i == panic_at {
54+
panic!("intended panic");
55+
}
56+
DropCheck::new(info)
57+
})
58+
}
59+
60+
#[track_caller]
61+
fn check<const N: usize>(len: usize, panic_at: usize) {
62+
check_drops(|info| {
63+
iter(info, len, panic_at).map_windows(|_: &[_; N]| {}).last();
64+
});
65+
}
66+
67+
#[track_caller]
68+
fn check_drops(f: impl FnOnce(&DropInfo)) {
69+
let info = DropInfo::new();
70+
let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
71+
f(&info);
72+
}));
73+
info.check();
74+
}
75+
76+
#[test]
77+
fn no_iter_panic_n1() {
78+
check::<1>(0, 100);
79+
check::<1>(1, 100);
80+
check::<1>(2, 100);
81+
check::<1>(13, 100);
82+
}
83+
84+
#[test]
85+
fn no_iter_panic_n2() {
86+
check::<2>(0, 100);
87+
check::<2>(1, 100);
88+
check::<2>(2, 100);
89+
check::<2>(3, 100);
90+
check::<2>(13, 100);
91+
}
92+
93+
#[test]
94+
fn no_iter_panic_n5() {
95+
check::<5>(0, 100);
96+
check::<5>(1, 100);
97+
check::<5>(2, 100);
98+
check::<5>(13, 100);
99+
check::<5>(30, 100);
100+
}
101+
102+
#[test]
103+
fn panic_in_first_batch() {
104+
check::<1>(7, 0);
105+
106+
check::<2>(7, 0);
107+
check::<2>(7, 1);
108+
109+
check::<3>(7, 0);
110+
check::<3>(7, 1);
111+
check::<3>(7, 2);
112+
}
113+
114+
#[test]
115+
fn panic_in_middle() {
116+
check::<1>(7, 1);
117+
check::<1>(7, 5);
118+
check::<1>(7, 6);
119+
120+
check::<2>(7, 2);
121+
check::<2>(7, 5);
122+
check::<2>(7, 6);
123+
124+
check::<5>(13, 5);
125+
check::<5>(13, 8);
126+
check::<5>(13, 12);
127+
}
128+
129+
#[test]
130+
fn len_equals_n() {
131+
check::<1>(1, 100);
132+
check::<1>(1, 0);
133+
134+
check::<2>(2, 100);
135+
check::<2>(2, 0);
136+
check::<2>(2, 1);
137+
138+
check::<5>(5, 100);
139+
check::<5>(5, 0);
140+
check::<5>(5, 1);
141+
check::<5>(5, 4);
142+
}
143+
}
144+
145+
#[test]
146+
fn output_n1() {
147+
assert_eq!("".chars().map_windows(|[c]| *c).collect::<Vec<_>>(), vec![]);
148+
assert_eq!("x".chars().map_windows(|[c]| *c).collect::<Vec<_>>(), vec!['x']);
149+
assert_eq!("abcd".chars().map_windows(|[c]| *c).collect::<Vec<_>>(), vec!['a', 'b', 'c', 'd']);
150+
}
151+
152+
#[test]
153+
fn output_n2() {
154+
assert_eq!(
155+
"".chars().map_windows(|a: &[_; 2]| *a).collect::<Vec<_>>(),
156+
<Vec<[char; 2]>>::new(),
157+
);
158+
assert_eq!("ab".chars().map_windows(|a: &[_; 2]| *a).collect::<Vec<_>>(), vec![['a', 'b']]);
159+
assert_eq!(
160+
"abcd".chars().map_windows(|a: &[_; 2]| *a).collect::<Vec<_>>(),
161+
vec![['a', 'b'], ['b', 'c'], ['c', 'd']],
162+
);
163+
}
164+
165+
#[test]
166+
fn test_case_from_pr_82413_comment() {
167+
for () in std::iter::repeat("0".to_owned()).map_windows(|_: &[_; 3]| {}).take(4) {}
168+
}
169+
170+
#[test]
171+
#[should_panic = "array in `Iterator::map_windows` must contain more than 0 elements"]
172+
fn check_zero_window() {
173+
let _ = std::iter::repeat(0).map_windows(|_: &[_; 0]| ());
174+
}
175+
176+
#[test]
177+
fn test_zero_sized_type() {
178+
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
179+
struct Data;
180+
let data: Vec<_> =
181+
std::iter::repeat(Data).take(10).map_windows(|arr: &[Data; 5]| *arr).collect();
182+
assert_eq!(data, [[Data; 5]; 6]);
183+
}
184+
185+
#[test]
186+
#[should_panic = "array size of `Iterator::map_windows` is too large"]
187+
fn test_too_large_array_size() {
188+
let _ = std::iter::repeat(()).map_windows(|arr: &[(); usize::MAX]| *arr);
189+
}
190+
191+
#[test]
192+
fn test_laziness() {
193+
let counter = AtomicUsize::new(0);
194+
let mut iter = (0..5)
195+
.inspect(|_| {
196+
counter.fetch_add(1, SeqCst);
197+
})
198+
.map_windows(|arr: &[i32; 2]| *arr);
199+
assert_eq!(counter.load(SeqCst), 0);
200+
201+
assert_eq!(iter.next(), Some([0, 1]));
202+
// The first iteration consumes N items (N = 2).
203+
assert_eq!(counter.load(SeqCst), 2);
204+
205+
assert_eq!(iter.next(), Some([1, 2]));
206+
assert_eq!(counter.load(SeqCst), 3);
207+
208+
assert_eq!(iter.next(), Some([2, 3]));
209+
assert_eq!(counter.load(SeqCst), 4);
210+
211+
assert_eq!(iter.next(), Some([3, 4]));
212+
assert_eq!(counter.load(SeqCst), 5);
213+
214+
assert_eq!(iter.next(), None);
215+
assert_eq!(counter.load(SeqCst), 5);
216+
}
217+
218+
#[test]
219+
fn test_size_hint() {
220+
struct SizeHintCheckHelper((usize, Option<usize>));
221+
222+
impl Iterator for SizeHintCheckHelper {
223+
type Item = i32;
224+
225+
fn next(&mut self) -> Option<i32> {
226+
let (ref mut lo, ref mut hi) = self.0;
227+
let next = (*hi != Some(0)).then_some(0);
228+
*lo = lo.saturating_sub(1);
229+
if let Some(hi) = hi {
230+
*hi = hi.saturating_sub(1);
231+
}
232+
next
233+
}
234+
235+
fn size_hint(&self) -> (usize, Option<usize>) {
236+
self.0
237+
}
238+
}
239+
240+
fn check_size_hint<const N: usize>(
241+
size_hint: (usize, Option<usize>),
242+
mut mapped_size_hint: (usize, Option<usize>),
243+
) {
244+
let mut iter = SizeHintCheckHelper(size_hint);
245+
let mut mapped_iter = iter.by_ref().map_windows(|_: &[_; N]| ());
246+
while mapped_iter.size_hint().0 > 0 {
247+
assert_eq!(mapped_iter.size_hint(), mapped_size_hint);
248+
assert!(mapped_iter.next().is_some());
249+
mapped_size_hint.0 -= 1;
250+
mapped_size_hint.1 = mapped_size_hint.1.map(|hi| hi.saturating_sub(1));
251+
}
252+
}
253+
254+
check_size_hint::<1>((0, None), (0, None));
255+
check_size_hint::<1>((0, Some(0)), (0, Some(0)));
256+
check_size_hint::<1>((0, Some(2)), (0, Some(2)));
257+
check_size_hint::<1>((1, None), (1, None));
258+
check_size_hint::<1>((1, Some(1)), (1, Some(1)));
259+
check_size_hint::<1>((1, Some(4)), (1, Some(4)));
260+
check_size_hint::<1>((5, None), (5, None));
261+
check_size_hint::<1>((5, Some(5)), (5, Some(5)));
262+
check_size_hint::<1>((5, Some(10)), (5, Some(10)));
263+
264+
check_size_hint::<2>((0, None), (0, None));
265+
check_size_hint::<2>((0, Some(0)), (0, Some(0)));
266+
check_size_hint::<2>((0, Some(2)), (0, Some(1)));
267+
check_size_hint::<2>((1, None), (0, None));
268+
check_size_hint::<2>((1, Some(1)), (0, Some(0)));
269+
check_size_hint::<2>((1, Some(4)), (0, Some(3)));
270+
check_size_hint::<2>((5, None), (4, None));
271+
check_size_hint::<2>((5, Some(5)), (4, Some(4)));
272+
check_size_hint::<2>((5, Some(10)), (4, Some(9)));
273+
274+
check_size_hint::<5>((0, None), (0, None));
275+
check_size_hint::<5>((0, Some(0)), (0, Some(0)));
276+
check_size_hint::<5>((0, Some(2)), (0, Some(0)));
277+
check_size_hint::<5>((1, None), (0, None));
278+
check_size_hint::<5>((1, Some(1)), (0, Some(0)));
279+
check_size_hint::<5>((1, Some(4)), (0, Some(0)));
280+
check_size_hint::<5>((5, None), (1, None));
281+
check_size_hint::<5>((5, Some(5)), (1, Some(1)));
282+
check_size_hint::<5>((5, Some(10)), (1, Some(6)));
283+
}

‎library/core/tests/iter/adapters/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ mod fuse;
1313
mod inspect;
1414
mod intersperse;
1515
mod map;
16+
mod map_windows;
1617
mod peekable;
1718
mod scan;
1819
mod skip;

‎library/core/tests/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@
110110
#![feature(is_ascii_octdigit)]
111111
#![feature(get_many_mut)]
112112
#![feature(offset_of)]
113+
#![feature(iter_map_windows)]
113114
#![deny(unsafe_op_in_unsafe_fn)]
114115
#![deny(fuzzy_provenance_casts)]
115116

0 commit comments

Comments
 (0)
Please sign in to comment.