Skip to content

Commit 1d82bfc

Browse files
author
sarah
committed
apply review suggestions, optimize trusted random access impl
1 parent ede4616 commit 1d82bfc

File tree

1 file changed

+166
-96
lines changed
  • library/core/src/iter/adapters

1 file changed

+166
-96
lines changed

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

Lines changed: 166 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,69 @@ trait ZipImpl<A, B> {
210210
Self: Iterator + TrustedRandomAccessNoCoerce;
211211
}
212212

213+
#[inline]
214+
fn ok<B, T>(mut f: impl FnMut(B, T) -> B) -> impl FnMut(B, T) -> Result<B, !> {
215+
move |acc, x| Ok(f(acc, x))
216+
}
217+
218+
#[inline]
219+
fn check_fold<AItem, B: Iterator, T, F: FnMut(T, (AItem, B::Item)) -> T>(
220+
mut b: B,
221+
mut f: F,
222+
) -> impl FnMut(T, AItem) -> T {
223+
move |acc, x| match b.next() {
224+
Some(y) => f(acc, (x, y)),
225+
None => panic!("Iterator expected Some(item), found None."),
226+
}
227+
}
228+
229+
#[inline]
230+
fn check_rfold<AItem, B: DoubleEndedIterator, T, F: FnMut(T, (AItem, B::Item)) -> T>(
231+
mut b: B,
232+
mut f: F,
233+
) -> impl FnMut(T, AItem) -> T {
234+
move |acc, x| match b.next_back() {
235+
Some(y) => f(acc, (x, y)),
236+
None => panic!("Iterator expected Some(item), found None."),
237+
}
238+
}
239+
240+
#[inline]
241+
fn check_try_fold<
242+
'b,
243+
AItem,
244+
B: Iterator,
245+
T,
246+
R: Try<Output = T>,
247+
F: 'b + FnMut(T, (AItem, B::Item)) -> R,
248+
>(
249+
b: &'b mut B,
250+
mut f: F,
251+
) -> impl 'b + FnMut(T, AItem) -> ControlFlow<R, T> {
252+
move |acc, x| match b.next() {
253+
Some(y) => ControlFlow::from_try(f(acc, (x, y))),
254+
None => ControlFlow::Break(R::from_output(acc)),
255+
}
256+
}
257+
258+
#[inline]
259+
fn check_try_rfold<
260+
'b,
261+
AItem,
262+
B: DoubleEndedIterator,
263+
T,
264+
R: Try<Output = T>,
265+
F: 'b + FnMut(T, (AItem, B::Item)) -> R,
266+
>(
267+
b: &'b mut B,
268+
mut f: F,
269+
) -> impl 'b + FnMut(T, AItem) -> ControlFlow<R, T> {
270+
move |acc, x| match b.next_back() {
271+
Some(y) => ControlFlow::from_try(f(acc, (x, y))),
272+
None => ControlFlow::Break(R::from_output(acc)),
273+
}
274+
}
275+
213276
// Work around limitations of specialization, requiring `default` impls to be repeated
214277
// in intermediary impls.
215278
macro_rules! zip_impl_general_defaults {
@@ -255,99 +318,48 @@ macro_rules! zip_impl_general_defaults {
255318
}
256319

257320
#[inline]
258-
default fn fold<T, F>(self, init: T, mut f: F) -> T
321+
default fn fold<T, F>(mut self, init: T, f: F) -> T
259322
where
260323
F: FnMut(T, Self::Item) -> T,
261324
{
262-
let mut a = self.a;
263-
let mut b = self.b;
264-
265-
let acc = a.try_fold(init, move |acc, x| match b.next() {
266-
Some(y) => Ok(f(acc, (x, y))),
267-
None => Err(acc),
268-
});
269-
270-
match acc {
271-
Ok(exhausted_a) => exhausted_a,
272-
Err(exhausted_b) => exhausted_b,
325+
let (lower, upper) = Iterator::size_hint(&self);
326+
if upper == Some(lower) {
327+
self.a.fold(init, check_fold(self.b, f))
328+
} else {
329+
ZipImpl::try_fold(&mut self, init, ok(f)).unwrap()
273330
}
274331
}
275332

276333
#[inline]
277-
default fn try_fold<T, F, R>(&mut self, init: T, mut f: F) -> R
334+
default fn try_fold<T, F, R>(&mut self, init: T, f: F) -> R
278335
where
279336
F: FnMut(T, Self::Item) -> R,
280337
R: Try<Output = T>,
281338
{
282-
let a = &mut self.a;
283-
let b = &mut self.b;
284-
285-
let acc = a.try_fold(init, move |acc, x| match b.next() {
286-
Some(y) => {
287-
let result = f(acc, (x, y));
288-
match result.branch() {
289-
ControlFlow::Continue(continue_) => Ok(continue_),
290-
ControlFlow::Break(break_) => Err(R::from_residual(break_)),
291-
}
292-
}
293-
None => Err(R::from_output(acc)),
294-
});
295-
296-
match acc {
297-
Ok(ok) => R::from_output(ok),
298-
Err(err) => err,
299-
}
339+
self.a.try_fold(init, check_try_fold(&mut self.b, f)).into_try()
300340
}
301341

302342
#[inline]
303-
default fn rfold<T, F>(mut self, init: T, mut f: F) -> T
343+
default fn rfold<T, F>(mut self, init: T, f: F) -> T
304344
where
305345
A: DoubleEndedIterator + ExactSizeIterator,
306346
B: DoubleEndedIterator + ExactSizeIterator,
307347
F: FnMut(T, Self::Item) -> T,
308348
{
309349
self.adjust_back();
310-
let mut a = self.a;
311-
let mut b = self.b;
312-
313-
let acc = a.try_rfold(init, move |acc, x| match b.next_back() {
314-
Some(y) => Ok(f(acc, (x, y))),
315-
None => Err(acc),
316-
});
317-
318-
match acc {
319-
Ok(exhausted_a) => exhausted_a,
320-
Err(exhausted_b) => exhausted_b,
321-
}
350+
self.a.rfold(init, check_rfold(self.b, f))
322351
}
323352

324353
#[inline]
325-
default fn try_rfold<T, F, R>(&mut self, init: T, mut f: F) -> R
354+
default fn try_rfold<T, F, R>(&mut self, init: T, f: F) -> R
326355
where
327356
A: DoubleEndedIterator + ExactSizeIterator,
328357
B: DoubleEndedIterator + ExactSizeIterator,
329358
F: FnMut(T, Self::Item) -> R,
330359
R: Try<Output = T>,
331360
{
332361
self.adjust_back();
333-
let a = &mut self.a;
334-
let b = &mut self.b;
335-
336-
let acc = a.try_rfold(init, move |acc, x| match b.next_back() {
337-
Some(y) => {
338-
let result = f(acc, (x, y));
339-
match result.branch() {
340-
ControlFlow::Continue(c) => ControlFlow::Continue(c),
341-
ControlFlow::Break(b) => ControlFlow::Break(R::from_residual(b)),
342-
}
343-
}
344-
None => ControlFlow::Break(R::from_output(acc)),
345-
});
346-
347-
match acc {
348-
ControlFlow::Continue(c) => R::from_output(c),
349-
ControlFlow::Break(b) => b,
350-
}
362+
self.a.try_rfold(init, check_try_rfold(&mut self.b, f)).into_try()
351363
}
352364
};
353365
}
@@ -411,6 +423,40 @@ where
411423
}
412424
}
413425

426+
#[inline]
427+
fn adjust_back_trusted_random_access<
428+
A: TrustedRandomAccess + DoubleEndedIterator + ExactSizeIterator,
429+
B: TrustedRandomAccess + DoubleEndedIterator + ExactSizeIterator,
430+
>(
431+
zipped: &mut Zip<A, B>,
432+
) {
433+
if A::MAY_HAVE_SIDE_EFFECT || B::MAY_HAVE_SIDE_EFFECT {
434+
let sz_a = zipped.a.size();
435+
let sz_b = zipped.b.size();
436+
// Adjust a, b to equal length, make sure that only the first call
437+
// of `next_back` does this, otherwise we will break the restriction
438+
// on calls to `zipped.next_back()` after calling `get_unchecked()`.
439+
if sz_a != sz_b {
440+
let sz_a = zipped.a.size();
441+
if A::MAY_HAVE_SIDE_EFFECT && sz_a > zipped.len {
442+
for _ in 0..sz_a - zipped.len {
443+
// since next_back() may panic we increment the counters beforehand
444+
// to keep Zip's state in sync with the underlying iterator source
445+
zipped.a_len -= 1;
446+
zipped.a.next_back();
447+
}
448+
debug_assert_eq!(zipped.a_len, zipped.len);
449+
}
450+
let sz_b = zipped.b.size();
451+
if B::MAY_HAVE_SIDE_EFFECT && sz_b > zipped.len {
452+
for _ in 0..sz_b - zipped.len {
453+
zipped.b.next_back();
454+
}
455+
}
456+
}
457+
}
458+
}
459+
414460
#[doc(hidden)]
415461
impl<A, B> ZipImpl<A, B> for Zip<A, B>
416462
where
@@ -490,31 +536,7 @@ where
490536
A: DoubleEndedIterator + ExactSizeIterator,
491537
B: DoubleEndedIterator + ExactSizeIterator,
492538
{
493-
if A::MAY_HAVE_SIDE_EFFECT || B::MAY_HAVE_SIDE_EFFECT {
494-
let sz_a = self.a.size();
495-
let sz_b = self.b.size();
496-
// Adjust a, b to equal length, make sure that only the first call
497-
// of `next_back` does this, otherwise we will break the restriction
498-
// on calls to `self.next_back()` after calling `get_unchecked()`.
499-
if sz_a != sz_b {
500-
let sz_a = self.a.size();
501-
if A::MAY_HAVE_SIDE_EFFECT && sz_a > self.len {
502-
for _ in 0..sz_a - self.len {
503-
// since next_back() may panic we increment the counters beforehand
504-
// to keep Zip's state in sync with the underlying iterator source
505-
self.a_len -= 1;
506-
self.a.next_back();
507-
}
508-
debug_assert_eq!(self.a_len, self.len);
509-
}
510-
let sz_b = self.b.size();
511-
if B::MAY_HAVE_SIDE_EFFECT && sz_b > self.len {
512-
for _ in 0..sz_b - self.len {
513-
self.b.next_back();
514-
}
515-
}
516-
}
517-
}
539+
adjust_back_trusted_random_access(self);
518540
if self.index < self.len {
519541
// since get_unchecked executes code which can panic we increment the counters beforehand
520542
// so that the same index won't be accessed twice, as required by TrustedRandomAccess
@@ -536,9 +558,19 @@ where
536558
where
537559
F: FnMut(T, Self::Item) -> T,
538560
{
561+
// we don't need to adjust `self.{index, len}` since we have ownership of the iterator
539562
let mut accum = init;
540-
while let Some(x) = <Self as ZipImpl<A, B>>::next(&mut self) {
541-
accum = f(accum, x);
563+
let index = self.index;
564+
let len = self.len;
565+
for i in index..len {
566+
// SAFETY: `i` is smaller than `self.len`, thus smaller than `self.a.len()` and `self.b.len()`
567+
accum = unsafe {
568+
f(accum, (self.a.__iterator_get_unchecked(i), self.b.__iterator_get_unchecked(i)))
569+
};
570+
}
571+
if A::MAY_HAVE_SIDE_EFFECT && len < self.a_len {
572+
// SAFETY: `i` is smaller than `self.a_len`, which is equal to `self.a.len()`
573+
unsafe { self.a.__iterator_get_unchecked(len) };
542574
}
543575
accum
544576
}
@@ -549,10 +581,27 @@ where
549581
F: FnMut(T, Self::Item) -> R,
550582
R: Try<Output = T>,
551583
{
584+
let index = self.index;
585+
let len = self.len;
586+
552587
let mut accum = init;
553-
while let Some(x) = <Self as ZipImpl<A, B>>::next(self) {
554-
accum = f(accum, x)?;
588+
589+
for i in index..len {
590+
// adjust `self.index` beforehand in case once of the iterators panics.
591+
self.index = i + 1;
592+
// SAFETY: `i` is smaller than `self.len`, thus smaller than `self.a.len()` and `self.b.len()`
593+
accum = unsafe {
594+
f(accum, (self.a.__iterator_get_unchecked(i), self.b.__iterator_get_unchecked(i)))?
595+
};
596+
}
597+
598+
if A::MAY_HAVE_SIDE_EFFECT && len < self.a_len {
599+
self.index = len + 1;
600+
self.len = len + 1;
601+
// SAFETY: `i` is smaller than `self.a_len`, which is equal to `self.a.len()`
602+
unsafe { self.a.__iterator_get_unchecked(len) };
555603
}
604+
556605
try { accum }
557606
}
558607

@@ -563,9 +612,18 @@ where
563612
B: DoubleEndedIterator + ExactSizeIterator,
564613
F: FnMut(T, Self::Item) -> T,
565614
{
615+
// we don't need to adjust `self.{len, a_len}` since we have ownership of the iterator
616+
adjust_back_trusted_random_access(&mut self);
617+
566618
let mut accum = init;
567-
while let Some(x) = <Self as ZipImpl<A, B>>::next_back(&mut self) {
568-
accum = f(accum, x);
619+
let index = self.index;
620+
let len = self.len;
621+
for i in 0..len - index {
622+
let i = len - i - 1;
623+
// SAFETY: `i` is smaller than `self.len`, thus smaller than `self.a.len()` and `self.b.len()`
624+
accum = unsafe {
625+
f(accum, (self.a.__iterator_get_unchecked(i), self.b.__iterator_get_unchecked(i)))
626+
};
569627
}
570628
accum
571629
}
@@ -578,9 +636,21 @@ where
578636
F: FnMut(T, Self::Item) -> R,
579637
R: Try<Output = T>,
580638
{
639+
adjust_back_trusted_random_access(self);
640+
581641
let mut accum = init;
582-
while let Some(x) = <Self as ZipImpl<A, B>>::next_back(self) {
583-
accum = f(accum, x)?;
642+
let index = self.index;
643+
let len = self.len;
644+
for i in 0..len - index {
645+
// inner `i` goes backwards from `len` (exclusive) to `index` (inclusive)
646+
let i = len - i - 1;
647+
// adjust `self.{len, a_len}` beforehand in case once of the iterators panics.
648+
self.len = i;
649+
self.a_len -= 1;
650+
// SAFETY: `i` is smaller than `self.len`, thus smaller than `self.a.len()` and `self.b.len()`
651+
accum = unsafe {
652+
f(accum, (self.a.__iterator_get_unchecked(i), self.b.__iterator_get_unchecked(i)))?
653+
};
584654
}
585655
try { accum }
586656
}

0 commit comments

Comments
 (0)