Skip to content

Commit 756bd6e

Browse files
committed
Use next_chunk in ArrayChunks impl
1 parent 475e4ba commit 756bd6e

File tree

1 file changed

+37
-132
lines changed

1 file changed

+37
-132
lines changed

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

+37-132
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
use crate::array;
22
use crate::iter::{FusedIterator, Iterator};
3-
use crate::mem;
4-
use crate::mem::MaybeUninit;
53
use crate::ops::{ControlFlow, NeverShortCircuit, Try};
6-
use crate::ptr;
74

85
/// An iterator over `N` elements of the iterator at a time.
96
///
@@ -70,37 +67,18 @@ where
7067
F: FnMut(B, Self::Item) -> R,
7168
R: Try<Output = B>,
7269
{
73-
let mut array = MaybeUninit::uninit_array();
74-
// SAFETY: `array` will still be valid if `guard` is dropped.
75-
let mut guard = unsafe { FrontGuard::new(&mut array) };
76-
77-
let result = self.iter.try_fold(init, |mut acc, item| {
78-
// SAFETY: `init` starts at 0, increases by one each iteration and
79-
// is reset to 0 once it reaches N.
80-
unsafe { array.get_unchecked_mut(guard.init) }.write(item);
81-
guard.init += 1;
82-
if guard.init == N {
83-
guard.init = 0;
84-
let array = mem::replace(&mut array, MaybeUninit::uninit_array());
85-
// SAFETY: the condition above asserts that all elements are
86-
// initialized.
87-
let item = unsafe { MaybeUninit::array_assume_init(array) };
88-
acc = f(acc, item)?;
89-
}
90-
R::from_output(acc)
91-
});
92-
match result.branch() {
93-
ControlFlow::Continue(o) => {
94-
if guard.init > 0 {
95-
let init = guard.init;
96-
mem::forget(guard);
97-
// SAFETY: `array` was initialized with `init` elements.
98-
self.remainder =
99-
Some(unsafe { array::IntoIter::new_unchecked(array, 0..init) });
70+
let mut acc = init;
71+
loop {
72+
match self.iter.next_chunk() {
73+
Ok(chunk) => acc = f(acc, chunk)?,
74+
Err(remainder) => {
75+
// Make sure to not override `self.remainder` with an empty array
76+
// when `next` is called after `ArrayChunks` exhaustion.
77+
self.remainder.get_or_insert(remainder);
78+
79+
break try { acc };
10080
}
101-
R::from_output(o)
10281
}
103-
ControlFlow::Break(r) => R::from_residual(r),
10482
}
10583
}
10684

@@ -113,33 +91,6 @@ where
11391
}
11492
}
11593

116-
/// A guard for an array where elements are filled from the left.
117-
struct FrontGuard<T, const N: usize> {
118-
/// A pointer to the array that is being filled. We need to use a raw
119-
/// pointer here because of the lifetime issues in the fold implementations.
120-
ptr: *mut T,
121-
/// The number of *initialized* elements.
122-
init: usize,
123-
}
124-
125-
impl<T, const N: usize> FrontGuard<T, N> {
126-
unsafe fn new(array: &mut [MaybeUninit<T>; N]) -> Self {
127-
Self { ptr: MaybeUninit::slice_as_mut_ptr(array), init: 0 }
128-
}
129-
}
130-
131-
impl<T, const N: usize> Drop for FrontGuard<T, N> {
132-
fn drop(&mut self) {
133-
debug_assert!(self.init <= N);
134-
// SAFETY: This raw slice will only contain the initialized objects
135-
// within the buffer.
136-
unsafe {
137-
let slice = ptr::slice_from_raw_parts_mut(self.ptr, self.init);
138-
ptr::drop_in_place(slice);
139-
}
140-
}
141-
}
142-
14394
#[unstable(feature = "iter_array_chunks", reason = "recently added", issue = "none")]
14495
impl<I, const N: usize> DoubleEndedIterator for ArrayChunks<I, N>
14596
where
@@ -157,29 +108,20 @@ where
157108
R: Try<Output = B>,
158109
{
159110
// We are iterating from the back we need to first handle the remainder.
160-
if self.next_back_remainder().is_none() {
161-
return R::from_output(init);
162-
}
111+
self.next_back_remainder();
163112

164-
let mut array = MaybeUninit::uninit_array();
165-
// SAFETY: `array` will still be valid if `guard` is dropped.
166-
let mut guard = unsafe { BackGuard::new(&mut array) };
113+
let mut acc = init;
114+
let mut iter = self.iter.by_ref().rev();
167115

168-
self.iter.try_rfold(init, |mut acc, item| {
169-
guard.uninit -= 1;
170-
// SAFETY: `uninit` starts at N, decreases by one each iteration and
171-
// is reset to N once it reaches 0.
172-
unsafe { array.get_unchecked_mut(guard.uninit) }.write(item);
173-
if guard.uninit == 0 {
174-
guard.uninit = N;
175-
let array = mem::replace(&mut array, MaybeUninit::uninit_array());
176-
// SAFETY: the condition above asserts that all elements are
177-
// initialized.
178-
let item = unsafe { MaybeUninit::array_assume_init(array) };
179-
acc = f(acc, item)?;
180-
}
181-
R::from_output(acc)
182-
})
116+
// NB remainder is handled by `next_back_remainder`, so
117+
// `next_chunk` can't return `Err` with non-empty remainder
118+
// (assuming correct `I as ExactSizeIterator` impl).
119+
while let Ok(mut chunk) = iter.next_chunk() {
120+
chunk.reverse();
121+
acc = f(acc, chunk)?
122+
}
123+
124+
try { acc }
183125
}
184126

185127
fn rfold<B, F>(mut self, init: B, mut f: F) -> B
@@ -195,63 +137,26 @@ impl<I, const N: usize> ArrayChunks<I, N>
195137
where
196138
I: DoubleEndedIterator + ExactSizeIterator,
197139
{
198-
#[inline]
199-
fn next_back_remainder(&mut self) -> Option<()> {
140+
/// Updates `self.remainder` such that `self.iter.len` is divisible by `N`.
141+
fn next_back_remainder(&mut self) {
142+
// Make sure to not override `self.remainder` with an empty array
143+
// when `next_back` is called after `ArrayChunks` exhaustion.
144+
if self.remainder.is_some() {
145+
return;
146+
}
147+
200148
// We use the `ExactSizeIterator` implementation of the underlying
201149
// iterator to know how many remaining elements there are.
202150
let rem = self.iter.len() % N;
203-
if rem == 0 {
204-
return Some(());
205-
}
206-
207-
let mut array = MaybeUninit::uninit_array();
208151

209-
// SAFETY: The array will still be valid if `guard` is dropped and
210-
// it is forgotten otherwise.
211-
let mut guard = unsafe { FrontGuard::new(&mut array) };
152+
// Take the last `rem` elements out of `self.iter`.
153+
let mut remainder =
154+
// SAFETY: `unwrap_err` always succeeds because x % N < N for all x.
155+
unsafe { self.iter.by_ref().rev().take(rem).next_chunk().unwrap_err_unchecked() };
212156

213-
// SAFETY: `rem` is in the range 1..N based on how it is calculated.
214-
for slot in unsafe { array.get_unchecked_mut(..rem) }.iter_mut() {
215-
slot.write(self.iter.next_back()?);
216-
guard.init += 1;
217-
}
218-
219-
let init = guard.init;
220-
mem::forget(guard);
221-
// SAFETY: `array` was initialized with exactly `init` elements.
222-
self.remainder = unsafe {
223-
array.get_unchecked_mut(..init).reverse();
224-
Some(array::IntoIter::new_unchecked(array, 0..init))
225-
};
226-
Some(())
227-
}
228-
}
229-
230-
/// A guard for an array where elements are filled from the right.
231-
struct BackGuard<T, const N: usize> {
232-
/// A pointer to the array that is being filled. We need to use a raw
233-
/// pointer here because of the lifetime issues in the rfold implementations.
234-
ptr: *mut T,
235-
/// The number of *uninitialized* elements.
236-
uninit: usize,
237-
}
238-
239-
impl<T, const N: usize> BackGuard<T, N> {
240-
unsafe fn new(array: &mut [MaybeUninit<T>; N]) -> Self {
241-
Self { ptr: MaybeUninit::slice_as_mut_ptr(array), uninit: N }
242-
}
243-
}
244-
245-
impl<T, const N: usize> Drop for BackGuard<T, N> {
246-
fn drop(&mut self) {
247-
debug_assert!(self.uninit <= N);
248-
// SAFETY: This raw slice will only contain the initialized objects
249-
// within the buffer.
250-
unsafe {
251-
let ptr = self.ptr.offset(self.uninit as isize);
252-
let slice = ptr::slice_from_raw_parts_mut(ptr, N - self.uninit);
253-
ptr::drop_in_place(slice);
254-
}
157+
// We used `.rev()` above, so we need to re-reverse the reminder
158+
remainder.as_mut_slice().reverse();
159+
self.remainder = Some(remainder);
255160
}
256161
}
257162

0 commit comments

Comments
 (0)