Skip to content

Commit 00bc9e8

Browse files
committed
fix(iter::skip): Optimize next and nth implementations of Skip
1 parent 801821d commit 00bc9e8

File tree

2 files changed

+50
-8
lines changed

2 files changed

+50
-8
lines changed

library/core/src/iter/adapters/skip.rs

+19-8
Original file line numberDiff line numberDiff line change
@@ -33,21 +33,32 @@ where
3333
#[inline]
3434
fn next(&mut self) -> Option<I::Item> {
3535
if unlikely(self.n > 0) {
36-
self.iter.nth(crate::mem::take(&mut self.n) - 1)?;
36+
self.iter.nth(crate::mem::take(&mut self.n))
37+
} else {
38+
self.iter.next()
3739
}
38-
self.iter.next()
3940
}
4041

4142
#[inline]
4243
fn nth(&mut self, n: usize) -> Option<I::Item> {
43-
// Can't just add n + self.n due to overflow.
4444
if self.n > 0 {
45-
let to_skip = self.n;
46-
self.n = 0;
47-
// nth(n) skips n+1
48-
self.iter.nth(to_skip - 1)?;
45+
let skip: usize = crate::mem::take(&mut self.n);
46+
// Checked add to handle overflow case.
47+
let n = match skip.checked_add(n) {
48+
Some(nth) => nth,
49+
None => {
50+
// In case of overflow, load skip value, before loading `n`.
51+
// Because the amount of elements to iterate is beyond `usize::MAX`, this
52+
// is split into two `nth` calls where the `skip` `nth` call is discarded.
53+
self.iter.nth(skip - 1)?;
54+
n
55+
}
56+
};
57+
// Load nth element including skip.
58+
self.iter.nth(n)
59+
} else {
60+
self.iter.nth(n)
4961
}
50-
self.iter.nth(n)
5162
}
5263

5364
#[inline]

library/core/tests/iter/adapters/skip.rs

+31
Original file line numberDiff line numberDiff line change
@@ -201,3 +201,34 @@ fn test_skip_non_fused() {
201201
// advance it further. `Unfuse` tests that this doesn't happen by panicking in that scenario.
202202
let _ = non_fused.skip(20).next();
203203
}
204+
205+
#[test]
206+
fn test_skip_non_fused_nth_overflow() {
207+
let non_fused = Unfuse::new(0..10);
208+
209+
// Ensures that calling skip and `nth` where the sum would overflow does not fail for non-fused
210+
// iterators.
211+
let _ = non_fused.skip(20).nth(usize::MAX);
212+
}
213+
214+
#[test]
215+
fn test_skip_overflow_wrapping() {
216+
// Test to ensure even on overflowing on `skip+nth` the correct amount of elements are yielded.
217+
struct WrappingIterator(usize);
218+
219+
impl Iterator for WrappingIterator {
220+
type Item = usize;
221+
222+
fn next(&mut self) -> core::option::Option<Self::Item> {
223+
<Self as Iterator>::nth(self, 0)
224+
}
225+
226+
fn nth(&mut self, nth: usize) -> core::option::Option<Self::Item> {
227+
self.0 = self.0.wrapping_add(nth.wrapping_add(1));
228+
Some(self.0)
229+
}
230+
}
231+
232+
let wrap = WrappingIterator(0);
233+
assert_eq!(wrap.skip(20).nth(usize::MAX), Some(20));
234+
}

0 commit comments

Comments
 (0)