Skip to content

Commit a8f2e9b

Browse files
committed
specialize zip: Specialize .zip() for TrustedRandomAccess iterators
This allows common iterator compositions like a.zip(b) where a, b are slice::{Iter, IterMut} compile to *much* better code.
1 parent 592eaa5 commit a8f2e9b

File tree

2 files changed

+121
-21
lines changed

2 files changed

+121
-21
lines changed

src/libcore/iter/iterator.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ use super::{Chain, Cycle, Cloned, Enumerate, Filter, FilterMap, FlatMap, Fuse,
2323
use super::ChainState;
2424
use super::{DoubleEndedIterator, ExactSizeIterator, Extend, FromIterator,
2525
IntoIterator};
26+
use super::ZipImpl;
2627

2728
fn _assert_is_object_safe(_: &Iterator<Item=()>) {}
2829

@@ -383,7 +384,7 @@ pub trait Iterator {
383384
fn zip<U>(self, other: U) -> Zip<Self, U::IntoIter> where
384385
Self: Sized, U: IntoIterator
385386
{
386-
Zip{a: self, b: other.into_iter()}
387+
Zip::new(self, other.into_iter())
387388
}
388389

389390
/// Takes a closure and creates an iterator which calls that closure on each

src/libcore/iter/mod.rs

Lines changed: 119 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,7 @@
302302
use clone::Clone;
303303
use cmp;
304304
use fmt;
305+
use iter_private::TrustedRandomAccess;
305306
use ops::FnMut;
306307
use option::Option::{self, Some, None};
307308
use usize;
@@ -622,7 +623,9 @@ impl<A, B> DoubleEndedIterator for Chain<A, B> where
622623
#[stable(feature = "rust1", since = "1.0.0")]
623624
pub struct Zip<A, B> {
624625
a: A,
625-
b: B
626+
b: B,
627+
index: usize,
628+
len: usize,
626629
}
627630

628631
#[stable(feature = "rust1", since = "1.0.0")]
@@ -631,29 +634,13 @@ impl<A, B> Iterator for Zip<A, B> where A: Iterator, B: Iterator
631634
type Item = (A::Item, B::Item);
632635

633636
#[inline]
634-
fn next(&mut self) -> Option<(A::Item, B::Item)> {
635-
self.a.next().and_then(|x| {
636-
self.b.next().and_then(|y| {
637-
Some((x, y))
638-
})
639-
})
637+
fn next(&mut self) -> Option<Self::Item> {
638+
ZipImpl::next(self)
640639
}
641640

642641
#[inline]
643642
fn size_hint(&self) -> (usize, Option<usize>) {
644-
let (a_lower, a_upper) = self.a.size_hint();
645-
let (b_lower, b_upper) = self.b.size_hint();
646-
647-
let lower = cmp::min(a_lower, b_lower);
648-
649-
let upper = match (a_upper, b_upper) {
650-
(Some(x), Some(y)) => Some(cmp::min(x,y)),
651-
(Some(x), None) => Some(x),
652-
(None, Some(y)) => Some(y),
653-
(None, None) => None
654-
};
655-
656-
(lower, upper)
643+
ZipImpl::size_hint(self)
657644
}
658645
}
659646

@@ -664,6 +651,51 @@ impl<A, B> DoubleEndedIterator for Zip<A, B> where
664651
{
665652
#[inline]
666653
fn next_back(&mut self) -> Option<(A::Item, B::Item)> {
654+
ZipImpl::next_back(self)
655+
}
656+
}
657+
658+
// Zip specialization trait
659+
#[doc(hidden)]
660+
trait ZipImpl<A, B> {
661+
type Item;
662+
fn new(a: A, b: B) -> Self;
663+
fn next(&mut self) -> Option<Self::Item>;
664+
fn size_hint(&self) -> (usize, Option<usize>);
665+
fn next_back(&mut self) -> Option<Self::Item>
666+
where A: DoubleEndedIterator + ExactSizeIterator,
667+
B: DoubleEndedIterator + ExactSizeIterator;
668+
}
669+
670+
// General Zip impl
671+
#[doc(hidden)]
672+
impl<A, B> ZipImpl<A, B> for Zip<A, B>
673+
where A: Iterator, B: Iterator
674+
{
675+
type Item = (A::Item, B::Item);
676+
default fn new(a: A, b: B) -> Self {
677+
Zip {
678+
a: a,
679+
b: b,
680+
index: 0, // not used in general case
681+
len: 0,
682+
}
683+
}
684+
685+
#[inline]
686+
default fn next(&mut self) -> Option<(A::Item, B::Item)> {
687+
self.a.next().and_then(|x| {
688+
self.b.next().and_then(|y| {
689+
Some((x, y))
690+
})
691+
})
692+
}
693+
694+
#[inline]
695+
default fn next_back(&mut self) -> Option<(A::Item, B::Item)>
696+
where A: DoubleEndedIterator + ExactSizeIterator,
697+
B: DoubleEndedIterator + ExactSizeIterator
698+
{
667699
let a_sz = self.a.len();
668700
let b_sz = self.b.len();
669701
if a_sz != b_sz {
@@ -680,6 +712,73 @@ impl<A, B> DoubleEndedIterator for Zip<A, B> where
680712
_ => unreachable!(),
681713
}
682714
}
715+
716+
#[inline]
717+
default fn size_hint(&self) -> (usize, Option<usize>) {
718+
let (a_lower, a_upper) = self.a.size_hint();
719+
let (b_lower, b_upper) = self.b.size_hint();
720+
721+
let lower = cmp::min(a_lower, b_lower);
722+
723+
let upper = match (a_upper, b_upper) {
724+
(Some(x), Some(y)) => Some(cmp::min(x,y)),
725+
(Some(x), None) => Some(x),
726+
(None, Some(y)) => Some(y),
727+
(None, None) => None
728+
};
729+
730+
(lower, upper)
731+
}
732+
}
733+
734+
#[doc(hidden)]
735+
impl<A, B> ZipImpl<A, B> for Zip<A, B>
736+
where A: TrustedRandomAccess, B: TrustedRandomAccess
737+
{
738+
fn new(a: A, b: B) -> Self {
739+
let len = cmp::min(a.len(), b.len());
740+
Zip {
741+
a: a,
742+
b: b,
743+
index: 0,
744+
len: len,
745+
}
746+
}
747+
748+
#[inline]
749+
fn next(&mut self) -> Option<(A::Item, B::Item)> {
750+
if self.index < self.len {
751+
let i = self.index;
752+
self.index += 1;
753+
unsafe {
754+
Some((self.a.get_unchecked(i), self.b.get_unchecked(i)))
755+
}
756+
} else {
757+
None
758+
}
759+
}
760+
761+
#[inline]
762+
fn size_hint(&self) -> (usize, Option<usize>) {
763+
let len = self.len - self.index;
764+
(len, Some(len))
765+
}
766+
767+
#[inline]
768+
fn next_back(&mut self) -> Option<(A::Item, B::Item)>
769+
where A: DoubleEndedIterator + ExactSizeIterator,
770+
B: DoubleEndedIterator + ExactSizeIterator
771+
{
772+
if self.index < self.len {
773+
self.len -= 1;
774+
let i = self.len;
775+
unsafe {
776+
Some((self.a.get_unchecked(i), self.b.get_unchecked(i)))
777+
}
778+
} else {
779+
None
780+
}
781+
}
683782
}
684783

685784
#[stable(feature = "rust1", since = "1.0.0")]

0 commit comments

Comments
 (0)