Skip to content

Add Iterator::array_chunks method #87776

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 8 commits into from
Closed
50 changes: 50 additions & 0 deletions library/core/benches/iter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -367,3 +367,53 @@ fn bench_partial_cmp(b: &mut Bencher) {
fn bench_lt(b: &mut Bencher) {
b.iter(|| (0..100000).map(black_box).lt((0..100000).map(black_box)))
}

#[bench]
fn bench_iter_array_map(b: &mut Bencher) {
b.iter(|| {
let mut acc = 0;
let iter = (0i64..10000).map(black_box).map(|_| black_box([0i64; 100]));
for_each_fold(iter, |x: [i64; 100]| acc += x.iter().sum::<i64>());
acc
});
}

#[bench]
fn bench_iter_array_chunks_loop(b: &mut Bencher) {
b.iter(|| {
let mut acc = 0;
let iter = (0i64..1000000).array_chunks::<100>().map(black_box);
for_each_loop(iter, |x: [i64; 100]| acc += x.iter().sum::<i64>());
acc
});
}

#[bench]
fn bench_iter_array_chunks_fold(b: &mut Bencher) {
b.iter(|| {
let mut acc = 0;
let iter = (0i64..1000000).array_chunks::<100>().map(black_box);
for_each_fold(iter, |x: [i64; 100]| acc += x.iter().sum::<i64>());
acc
});
}

#[bench]
fn bench_iter_array_chunks_rev_loop(b: &mut Bencher) {
b.iter(|| {
let mut acc = 0;
let iter = (0i64..1000000).array_chunks::<100>().map(black_box);
for_each_loop(iter.rev(), |x: [i64; 100]| acc += x.iter().sum::<i64>());
acc
});
}

#[bench]
fn bench_iter_array_chunks_rfold(b: &mut Bencher) {
b.iter(|| {
let mut acc = 0;
let iter = (0i64..1000000).array_chunks::<100>().map(black_box);
for_each_fold(iter.rev(), |x: [i64; 100]| acc += x.iter().sum::<i64>());
acc
});
}
2 changes: 2 additions & 0 deletions library/core/benches/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// wasm32 does not support benches (no time).
#![cfg(not(target_arch = "wasm32"))]
#![feature(array_chunks)]
#![feature(flt2dec)]
#![feature(iter_array_chunks)]
#![feature(test)]

extern crate test;
Expand Down
66 changes: 41 additions & 25 deletions library/core/src/array/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -437,6 +437,38 @@ impl<T, const N: usize> [T; N] {
}
}

pub(crate) struct Guard<T, const N: usize> {
ptr: *mut T,
pub(crate) init: usize,
}

impl<T, const N: usize> Drop for Guard<T, N> {
fn drop(&mut self) {
debug_assert!(self.init <= N);

let init_part = crate::ptr::slice_from_raw_parts_mut(self.ptr, self.init);

// SAFETY: this raw slice will contain only initialized objects.
unsafe {
crate::ptr::drop_in_place(init_part);
}
}
}

impl<T, const N: usize> Guard<T, N> {
/// Creates a `Guard` object that will drop the initialized portion
/// of the provided array when dropped.
///
/// # Safety
///
/// When the returned guard is dropped, the array must still be valid,
/// `init` must not be greater `N`, and the first `init` elements of
/// the array must be properly initialized.
pub(crate) unsafe fn new(array: &mut [MaybeUninit<T>; N]) -> Self {
Self { ptr: MaybeUninit::slice_as_mut_ptr(array), init: 0 }
}
}

/// Pulls `N` items from `iter` and returns them as an array. If the iterator
/// yields fewer than `N` items, this function exhibits undefined behavior.
///
Expand Down Expand Up @@ -484,39 +516,23 @@ where
return unsafe { Some(mem::zeroed()) };
}

struct Guard<T, const N: usize> {
ptr: *mut T,
initialized: usize,
}

impl<T, const N: usize> Drop for Guard<T, N> {
fn drop(&mut self) {
debug_assert!(self.initialized <= N);

let initialized_part = crate::ptr::slice_from_raw_parts_mut(self.ptr, self.initialized);

// SAFETY: this raw slice will contain only initialized objects.
unsafe {
crate::ptr::drop_in_place(initialized_part);
}
}
}

let mut array = MaybeUninit::uninit_array::<N>();
let mut guard: Guard<_, N> =
Guard { ptr: MaybeUninit::slice_as_mut_ptr(&mut array), initialized: 0 };
// SAFETY: `guard` is always either forgotten or dropped before the array
// is moved/dropped and `guard.init` properly tracks the initialized
// members of the array.
let mut guard = unsafe { Guard::new(&mut array) };

while let Some(item) = iter.next() {
// SAFETY: `guard.initialized` starts at 0, is increased by one in the
// SAFETY: `guard.init` starts at 0, is increased by one in the
// loop and the loop is aborted once it reaches N (which is
// `array.len()`).
unsafe {
array.get_unchecked_mut(guard.initialized).write(item);
array.get_unchecked_mut(guard.init).write(item);
}
guard.initialized += 1;
guard.init += 1;

// Check if the whole array was initialized.
if guard.initialized == N {
if guard.init == N {
mem::forget(guard);

// SAFETY: the condition above asserts that all elements are
Expand All @@ -527,7 +543,7 @@ where
}

// This is only reached if the iterator is exhausted before
// `guard.initialized` reaches `N`. Also note that `guard` is dropped here,
// `guard.init` reaches `N`. Also note that `guard` is dropped here,
// dropping all already initialized elements.
None
}
Loading