Allow canonicalizing the array::map loop in trusted cases
This commit is contained in:
parent
52df0558ea
commit
5bc328fdef
14 changed files with 239 additions and 144 deletions
|
|
@ -1,6 +1,5 @@
|
|||
use crate::array;
|
||||
use crate::iter::{ByRefSized, FusedIterator, Iterator, TrustedRandomAccessNoCoerce};
|
||||
use crate::mem::{self, MaybeUninit};
|
||||
use crate::ops::{ControlFlow, NeverShortCircuit, Try};
|
||||
|
||||
/// An iterator over `N` elements of the iterator at a time.
|
||||
|
|
@ -212,19 +211,14 @@ where
|
|||
let mut i = 0;
|
||||
// Use a while loop because (0..len).step_by(N) doesn't optimize well.
|
||||
while inner_len - i >= N {
|
||||
let mut chunk = MaybeUninit::uninit_array();
|
||||
let mut guard = array::Guard { array_mut: &mut chunk, initialized: 0 };
|
||||
while guard.initialized < N {
|
||||
let chunk = crate::array::from_fn(|local| {
|
||||
// SAFETY: The method consumes the iterator and the loop condition ensures that
|
||||
// all accesses are in bounds and only happen once.
|
||||
unsafe {
|
||||
let idx = i + guard.initialized;
|
||||
guard.push_unchecked(self.iter.__iterator_get_unchecked(idx));
|
||||
let idx = i + local;
|
||||
self.iter.__iterator_get_unchecked(idx)
|
||||
}
|
||||
}
|
||||
mem::forget(guard);
|
||||
// SAFETY: The loop above initialized all elements
|
||||
let chunk = unsafe { MaybeUninit::array_assume_init(chunk) };
|
||||
});
|
||||
accum = f(accum, chunk);
|
||||
i += N;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use crate::iter::adapters::{
|
||||
zip::try_get_unchecked, TrustedRandomAccess, TrustedRandomAccessNoCoerce,
|
||||
};
|
||||
use crate::iter::{FusedIterator, TrustedLen};
|
||||
use crate::iter::{FusedIterator, TrustedLen, UncheckedIterator};
|
||||
use crate::ops::Try;
|
||||
|
||||
/// An iterator that clones the elements of an underlying iterator.
|
||||
|
|
@ -140,3 +140,16 @@ where
|
|||
T: Clone,
|
||||
{
|
||||
}
|
||||
|
||||
impl<'a, I, T: 'a> UncheckedIterator for Cloned<I>
|
||||
where
|
||||
I: UncheckedIterator<Item = &'a T>,
|
||||
T: Clone,
|
||||
{
|
||||
unsafe fn next_unchecked(&mut self) -> T {
|
||||
// SAFETY: `Cloned` is 1:1 with the inner iterator, so if the caller promised
|
||||
// that there's an element left, the inner iterator has one too.
|
||||
let item = unsafe { self.it.next_unchecked() };
|
||||
item.clone()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use crate::fmt;
|
|||
use crate::iter::adapters::{
|
||||
zip::try_get_unchecked, SourceIter, TrustedRandomAccess, TrustedRandomAccessNoCoerce,
|
||||
};
|
||||
use crate::iter::{FusedIterator, InPlaceIterable, TrustedLen};
|
||||
use crate::iter::{FusedIterator, InPlaceIterable, TrustedLen, UncheckedIterator};
|
||||
use crate::ops::Try;
|
||||
|
||||
/// An iterator that maps the values of `iter` with `f`.
|
||||
|
|
@ -187,6 +187,19 @@ where
|
|||
{
|
||||
}
|
||||
|
||||
impl<B, I, F> UncheckedIterator for Map<I, F>
|
||||
where
|
||||
I: UncheckedIterator,
|
||||
F: FnMut(I::Item) -> B,
|
||||
{
|
||||
unsafe fn next_unchecked(&mut self) -> B {
|
||||
// SAFETY: `Map` is 1:1 with the inner iterator, so if the caller promised
|
||||
// that there's an element left, the inner iterator has one too.
|
||||
let item = unsafe { self.iter.next_unchecked() };
|
||||
(self.f)(item)
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[unstable(feature = "trusted_random_access", issue = "none")]
|
||||
unsafe impl<I, F> TrustedRandomAccess for Map<I, F> where I: TrustedRandomAccess {}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use crate::cmp;
|
||||
use crate::fmt::{self, Debug};
|
||||
use crate::iter::{DoubleEndedIterator, ExactSizeIterator, FusedIterator, Iterator};
|
||||
use crate::iter::{InPlaceIterable, SourceIter, TrustedLen};
|
||||
use crate::iter::{InPlaceIterable, SourceIter, TrustedLen, UncheckedIterator};
|
||||
|
||||
/// An iterator that iterates two other iterators simultaneously.
|
||||
///
|
||||
|
|
@ -417,6 +417,13 @@ where
|
|||
{
|
||||
}
|
||||
|
||||
impl<A, B> UncheckedIterator for Zip<A, B>
|
||||
where
|
||||
A: UncheckedIterator,
|
||||
B: UncheckedIterator,
|
||||
{
|
||||
}
|
||||
|
||||
// Arbitrarily selects the left side of the zip iteration as extractable "source"
|
||||
// it would require negative trait bounds to be able to try both
|
||||
#[unstable(issue = "none", feature = "inplace_iteration")]
|
||||
|
|
|
|||
|
|
@ -450,6 +450,7 @@ pub use self::adapters::{
|
|||
pub use self::adapters::{Intersperse, IntersperseWith};
|
||||
|
||||
pub(crate) use self::adapters::try_process;
|
||||
pub(crate) use self::traits::UncheckedIterator;
|
||||
|
||||
mod adapters;
|
||||
mod range;
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ mod double_ended;
|
|||
mod exact_size;
|
||||
mod iterator;
|
||||
mod marker;
|
||||
mod unchecked_iterator;
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub use self::{
|
||||
|
|
@ -19,3 +20,5 @@ pub use self::{
|
|||
pub use self::marker::InPlaceIterable;
|
||||
#[unstable(feature = "trusted_step", issue = "85731")]
|
||||
pub use self::marker::TrustedStep;
|
||||
|
||||
pub(crate) use self::unchecked_iterator::UncheckedIterator;
|
||||
|
|
|
|||
36
library/core/src/iter/traits/unchecked_iterator.rs
Normal file
36
library/core/src/iter/traits/unchecked_iterator.rs
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
use crate::iter::TrustedLen;
|
||||
|
||||
/// [`TrustedLen`] cannot have methods, so this allows augmenting it.
|
||||
///
|
||||
/// It currently requires `TrustedLen` because it's unclear whether it's
|
||||
/// reasonably possible to depend on the `size_hint` of anything else.
|
||||
pub(crate) trait UncheckedIterator: TrustedLen {
|
||||
/// Gets the next item from a non-empty iterator.
|
||||
///
|
||||
/// Because there's always a value to return, that means it can return
|
||||
/// the `Item` type directly, without wrapping it in an `Option`.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This can only be called if `size_hint().0 != 0`, guaranteeing that
|
||||
/// there's at least one item available.
|
||||
///
|
||||
/// Otherwise (aka when `size_hint().1 == Some(0)`), this is UB.
|
||||
///
|
||||
/// # Note to Implementers
|
||||
///
|
||||
/// This has a default implementation using [`Option::unwrap_unchecked`].
|
||||
/// That's probably sufficient if your `next` *always* returns `Some`,
|
||||
/// such as for infinite iterators. In more complicated situations, however,
|
||||
/// sometimes there can still be `insertvalue`/`assume`/`extractvalue`
|
||||
/// instructions remaining in the IR from the `Option` handling, at which
|
||||
/// point you might want to implement this manually instead.
|
||||
#[unstable(feature = "trusted_len_next_unchecked", issue = "37572")]
|
||||
#[inline]
|
||||
unsafe fn next_unchecked(&mut self) -> Self::Item {
|
||||
let opt = self.next();
|
||||
// SAFETY: The caller promised that we're not empty, and
|
||||
// `Self: TrustedLen` so we can actually trust the `size_hint`.
|
||||
unsafe { opt.unwrap_unchecked() }
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue