Skip to content

Commit 4655895

Browse files
authored
Rollup merge of rust-lang#47944 - oberien:unboundediterator-trustedlen, r=bluss
Implement TrustedLen for Take<Repeat> and Take<RangeFrom> This will allow optimization of simple `repeat(x).take(n).collect()` iterators, which are currently not vectorized and have capacity checks. This will only support a few aggregates on `Repeat` and `RangeFrom`, which might be enough for simple cases, but doesn't optimize more complex ones. Namely, Cycle, StepBy, Filter, FilterMap, Peekable, SkipWhile, Skip, FlatMap, Fuse and Inspect are not marked `TrustedLen` when the inner iterator is infinite. Previous discussion can be found in rust-lang#47082 r? @alexcrichton
2 parents eb3b870 + ee8b4ca commit 4655895

File tree

6 files changed

+121
-11
lines changed

6 files changed

+121
-11
lines changed

src/libcore/iter/mod.rs

+39-10
Original file line numberDiff line numberDiff line change
@@ -2286,16 +2286,7 @@ impl<I> Iterator for Take<I> where I: Iterator{
22862286

22872287
#[inline]
22882288
fn size_hint(&self) -> (usize, Option<usize>) {
2289-
let (lower, upper) = self.iter.size_hint();
2290-
2291-
let lower = cmp::min(lower, self.n);
2292-
2293-
let upper = match upper {
2294-
Some(x) if x < self.n => Some(x),
2295-
_ => Some(self.n)
2296-
};
2297-
2298-
(lower, upper)
2289+
TakeImpl::size_hint(self)
22992290
}
23002291

23012292
#[inline]
@@ -2316,12 +2307,50 @@ impl<I> Iterator for Take<I> where I: Iterator{
23162307
}
23172308
}
23182309

2310+
trait TakeImpl {
2311+
fn size_hint(&self) -> (usize, Option<usize>);
2312+
}
2313+
2314+
impl<I: Iterator> TakeImpl for Take<I> {
2315+
#[inline]
2316+
default fn size_hint(&self) -> (usize, Option<usize>) {
2317+
let (lower, upper) = self.iter.size_hint();
2318+
2319+
let lower = cmp::min(lower, self.n);
2320+
2321+
let upper = match upper {
2322+
Some(x) if x < self.n => Some(x),
2323+
_ => Some(self.n)
2324+
};
2325+
2326+
(lower, upper)
2327+
}
2328+
}
2329+
2330+
impl<I: TrustedLen> TakeImpl for Take<I> {
2331+
#[inline]
2332+
fn size_hint(&self) -> (usize, Option<usize>) {
2333+
let (lower, upper) = self.iter.size_hint();
2334+
match upper {
2335+
None => (self.n, Some(self.n)),
2336+
Some(x) => {
2337+
debug_assert_eq!(x, lower);
2338+
let count = cmp::min(lower, self.n);
2339+
(count, Some(count))
2340+
}
2341+
}
2342+
}
2343+
}
2344+
23192345
#[stable(feature = "rust1", since = "1.0.0")]
23202346
impl<I> ExactSizeIterator for Take<I> where I: ExactSizeIterator {}
23212347

23222348
#[unstable(feature = "fused", issue = "35602")]
23232349
impl<I> FusedIterator for Take<I> where I: FusedIterator {}
23242350

2351+
#[unstable(feature = "trusted_len", issue = "37572")]
2352+
unsafe impl<I: TrustedLen> TrustedLen for Take<I> {}
2353+
23252354
/// An iterator to maintain state while iterating another iterator.
23262355
///
23272356
/// This `struct` is created by the [`scan`] method on [`Iterator`]. See its

src/libcore/iter/range.rs

+3
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,9 @@ impl<A: Step> Iterator for ops::RangeFrom<A> {
325325
#[unstable(feature = "fused", issue = "35602")]
326326
impl<A: Step> FusedIterator for ops::RangeFrom<A> {}
327327

328+
#[unstable(feature = "trusted_len", issue = "37572")]
329+
unsafe impl<A: Step> TrustedLen for ops::RangeFrom<A> {}
330+
328331
#[unstable(feature = "inclusive_range", reason = "recently added, follows RFC", issue = "28237")]
329332
impl<A: Step> Iterator for ops::RangeInclusive<A> {
330333
type Item = A;

src/libcore/iter/sources.rs

+3
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ impl<A: Clone> DoubleEndedIterator for Repeat<A> {
4444
#[unstable(feature = "fused", issue = "35602")]
4545
impl<A: Clone> FusedIterator for Repeat<A> {}
4646

47+
#[unstable(feature = "trusted_len", issue = "37572")]
48+
unsafe impl<A: Clone> TrustedLen for Repeat<A> {}
49+
4750
/// Creates a new iterator that endlessly repeats a single element.
4851
///
4952
/// The `repeat()` function repeats a single value over and over and over and

src/libcore/iter/traits.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -972,7 +972,8 @@ impl<'a, I: FusedIterator + ?Sized> FusedIterator for &'a mut I {}
972972
/// The upper bound must only be [`None`] if the actual iterator length is
973973
/// larger than [`usize::MAX`].
974974
///
975-
/// The iterator must produce exactly the number of elements it reported.
975+
/// The iterator must produce exactly the number of elements it reported
976+
/// or diverge before reaching the end.
976977
///
977978
/// # Safety
978979
///

src/libcore/tests/iter.rs

+43
Original file line numberDiff line numberDiff line change
@@ -1371,6 +1371,29 @@ fn test_range_from_nth() {
13711371
assert_eq!(r, 16..);
13721372
assert_eq!(r.nth(10), Some(26));
13731373
assert_eq!(r, 27..);
1374+
1375+
assert_eq!((0..).size_hint(), (usize::MAX, None));
1376+
}
1377+
1378+
fn is_trusted_len<I: TrustedLen>(_: I) {}
1379+
1380+
#[test]
1381+
fn test_range_from_take() {
1382+
let mut it = (0..).take(3);
1383+
assert_eq!(it.next(), Some(0));
1384+
assert_eq!(it.next(), Some(1));
1385+
assert_eq!(it.next(), Some(2));
1386+
assert_eq!(it.next(), None);
1387+
is_trusted_len((0..).take(3));
1388+
assert_eq!((0..).take(3).size_hint(), (3, Some(3)));
1389+
assert_eq!((0..).take(0).size_hint(), (0, Some(0)));
1390+
assert_eq!((0..).take(usize::MAX).size_hint(), (usize::MAX, Some(usize::MAX)));
1391+
}
1392+
1393+
#[test]
1394+
fn test_range_from_take_collect() {
1395+
let v: Vec<_> = (0..).take(3).collect();
1396+
assert_eq!(v, vec![0, 1, 2]);
13741397
}
13751398

13761399
#[test]
@@ -1465,6 +1488,26 @@ fn test_repeat() {
14651488
assert_eq!(it.next(), Some(42));
14661489
assert_eq!(it.next(), Some(42));
14671490
assert_eq!(it.next(), Some(42));
1491+
assert_eq!(repeat(42).size_hint(), (usize::MAX, None));
1492+
}
1493+
1494+
#[test]
1495+
fn test_repeat_take() {
1496+
let mut it = repeat(42).take(3);
1497+
assert_eq!(it.next(), Some(42));
1498+
assert_eq!(it.next(), Some(42));
1499+
assert_eq!(it.next(), Some(42));
1500+
assert_eq!(it.next(), None);
1501+
is_trusted_len(repeat(42).take(3));
1502+
assert_eq!(repeat(42).take(3).size_hint(), (3, Some(3)));
1503+
assert_eq!(repeat(42).take(0).size_hint(), (0, Some(0)));
1504+
assert_eq!(repeat(42).take(usize::MAX).size_hint(), (usize::MAX, Some(usize::MAX)));
1505+
}
1506+
1507+
#[test]
1508+
fn test_repeat_take_collect() {
1509+
let v: Vec<_> = repeat(42).take(3).collect();
1510+
assert_eq!(v, vec![42, 42, 42]);
14681511
}
14691512

14701513
#[test]
+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// compile-flags: -O
12+
// ignore-tidy-linelength
13+
14+
#![crate_type = "lib"]
15+
16+
use std::iter;
17+
18+
// CHECK-LABEL: @repeat_take_collect
19+
#[no_mangle]
20+
pub fn repeat_take_collect() -> Vec<u8> {
21+
// CHECK: call void @llvm.memset.p0i8
22+
iter::repeat(42).take(100000).collect()
23+
}
24+
25+
// CHECK-LABEL: @range_from_take_collect
26+
#[no_mangle]
27+
pub fn range_from_take_collect() -> Vec<u8> {
28+
// CHECK: %[[SPLATINSERT:.*]] = insertelement <{{[0-9]+}} x i8> undef, i8 %{{.*}}, i32 0
29+
// CHECK-NEXT: %{{.*}} = shufflevector <[[WIDTH:[0-9]+]] x i8> %[[SPLATINSERT]], <[[WIDTH]] x i8> undef, <[[WIDTH]] x i32> zeroinitializer
30+
(0..).take(100000).collect()
31+
}

0 commit comments

Comments
 (0)