-
Notifications
You must be signed in to change notification settings - Fork 13.4k
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
Changes from 2 commits
563db25
4f507b9
45bfc7f
bd9d277
2289b77
6a15bc6
c14ba10
85edaca
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
use core::iter::FusedIterator; | ||
use core::mem::{self, MaybeUninit}; | ||
use core::ptr; | ||
|
||
/// An iterator that yields the elements of another iterator in | ||
/// chunks of size `N`. | ||
/// | ||
/// This `struct` is created by the [`array_chunks`] method on [`Iterator`]. See | ||
/// its documentation for more. | ||
/// | ||
/// [`array_chunks`]: Iterator::array_chunks | ||
#[unstable(feature = "iter_array_chunks", issue = "none")] | ||
#[derive(Debug)] | ||
pub struct ArrayChunks<I: Iterator, const N: usize> { | ||
iter: I, | ||
buffer: [MaybeUninit<I::Item>; N], | ||
init: usize, | ||
} | ||
|
||
#[unstable(feature = "iter_array_chunks", issue = "none")] | ||
impl<I: Iterator, const N: usize> Drop for ArrayChunks<I, N> { | ||
fn drop(&mut self) { | ||
// SAFETY: This is safe: `remainder_mut` returns exactly the sub-slice | ||
// of elements that were initialized but not yielded and so have yet | ||
// to be dropped. | ||
unsafe { | ||
ptr::drop_in_place(self.remainder_mut()); | ||
} | ||
} | ||
} | ||
|
||
impl<I: Iterator, const N: usize> ArrayChunks<I, N> { | ||
pub(in crate::iter) fn new(iter: I) -> Self { | ||
Self { iter, init: 0, buffer: MaybeUninit::uninit_array() } | ||
} | ||
|
||
/// Returns the remainder of the elements yielded by the original | ||
/// iterator that were insufficient to fill another chunk. The | ||
/// returned slice has at most `N-1` elements. | ||
#[unstable(feature = "iter_array_chunks", issue = "none")] | ||
pub fn remainder(&self) -> &[I::Item] { | ||
// SAFETY: We know that all elements before `init` are properly initialized. | ||
unsafe { MaybeUninit::slice_assume_init_ref(&self.buffer[..self.init]) } | ||
} | ||
|
||
/// Returns the remainder of the elements yielded by the original | ||
/// iterator that were insufficient to fill another chunk. The | ||
/// returned slice has at most `N-1` elements. | ||
#[unstable(feature = "iter_array_chunks", issue = "none")] | ||
pub fn remainder_mut(&mut self) -> &mut [I::Item] { | ||
// SAFETY: We know that all elements before `init` are properly initialized. | ||
unsafe { MaybeUninit::slice_assume_init_mut(&mut self.buffer[..self.init]) } | ||
} | ||
} | ||
|
||
#[unstable(feature = "iter_array_chunks", issue = "none")] | ||
impl<I: Iterator, const N: usize> Iterator for ArrayChunks<I, N> { | ||
the8472 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
type Item = [I::Item; N]; | ||
|
||
fn next(&mut self) -> Option<Self::Item> { | ||
while self.init < N { | ||
self.buffer[self.init] = MaybeUninit::new(self.iter.next()?); | ||
self.init += 1; | ||
} | ||
self.init = 0; | ||
// SAFETY: This is safe: `MaybeUninit<T>` is guaranteed to have the same layout | ||
// as `T` and the entire array has just been initialized. | ||
unsafe { Some(mem::transmute_copy(&self.buffer)) } | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Reusing memory (going through There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have added some benchmarks comparing it to looping over a mapped iterator producing arrays and the
This comment was marked as resolved.
Sorry, something went wrong. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I managed to improve the performance by quite a bit. Now it only takes twice as long as passing an array through an iterator pipeline. I also removed the
|
||
} | ||
} | ||
|
||
#[unstable(feature = "iter_array_chunks", issue = "none")] | ||
impl<I: FusedIterator, const N: usize> FusedIterator for ArrayChunks<I, N> {} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
use core::cell::Cell; | ||
use core::iter::*; | ||
|
||
#[derive(Debug)] | ||
struct DropBomb<'a> { | ||
dropped: bool, | ||
counter: &'a Cell<usize>, | ||
} | ||
|
||
impl Drop for DropBomb<'_> { | ||
fn drop(&mut self) { | ||
if self.dropped { | ||
panic!("double dropped!"); | ||
} | ||
self.dropped = true; | ||
self.counter.set(self.counter.get() + 1); | ||
} | ||
} | ||
|
||
#[test] | ||
fn test_iterator_array_chunks_remainder() { | ||
let mut iter = (0..=10).array_chunks::<4>(); | ||
assert_eq!(iter.remainder(), &[]); | ||
assert_eq!(iter.remainder_mut(), &[]); | ||
assert_eq!(iter.next(), Some([0, 1, 2, 3])); | ||
assert_eq!(iter.remainder(), &[]); | ||
assert_eq!(iter.remainder_mut(), &[]); | ||
assert_eq!(iter.next(), Some([4, 5, 6, 7])); | ||
assert_eq!(iter.remainder(), &[]); | ||
assert_eq!(iter.remainder_mut(), &[]); | ||
assert_eq!(iter.next(), None); | ||
assert_eq!(iter.remainder(), &[8, 9, 10]); | ||
assert_eq!(iter.remainder_mut(), &[8, 9, 10]); | ||
} | ||
|
||
#[test] | ||
fn test_iterator_array_chunks_drop() { | ||
let counter = Cell::new(0); | ||
let create = | ||
|n| (0..n).map(|_| DropBomb { dropped: false, counter: &counter }).array_chunks::<3>(); | ||
|
||
let iter = create(5); | ||
assert_eq!(counter.get(), 0); | ||
drop(iter); | ||
assert_eq!(counter.get(), 0); | ||
|
||
let mut iter = create(3); | ||
counter.set(0); | ||
iter.next(); | ||
assert_eq!(counter.get(), 3); | ||
assert!(iter.next().is_none()); | ||
assert_eq!(counter.get(), 3); | ||
assert_eq!(iter.remainder().len(), 0); | ||
drop(iter); | ||
assert_eq!(counter.get(), 3); | ||
|
||
let mut iter = create(5); | ||
counter.set(0); | ||
iter.next(); | ||
assert_eq!(counter.get(), 3); | ||
assert!(iter.next().is_none()); | ||
assert_eq!(counter.get(), 3); | ||
assert_eq!(iter.remainder().len(), 2); | ||
drop(iter); | ||
assert_eq!(counter.get(), 5); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
mod array_chunks; | ||
mod chain; | ||
mod cloned; | ||
mod copied; | ||
|
Uh oh!
There was an error while loading. Please reload this page.