use core::array::IntoIter;
use core::iter::{FusedIterator, Iterator, Take};
use core::mem::MaybeUninit;
#[derive(Debug, Clone)]
#[must_use = "iterators are lazy and do nothing unless consumed"]
pub struct ArrayChunks<I: Iterator, const N: usize> {
iter: I,
remainder: Option<Take<IntoIter<I::Item, N>>>,
}
impl<I, const N: usize> ArrayChunks<I, N>
where
I: Iterator,
{
pub fn new(iter: I) -> Self {
assert_ne!(N, 0, "chunk size must be non-zero");
Self {
iter,
remainder: None,
}
}
#[inline]
pub fn into_remainder(self) -> Option<Take<IntoIter<I::Item, N>>> {
self.remainder
}
}
impl<I, const N: usize> Iterator for ArrayChunks<I, N>
where
I: Iterator,
{
type Item = [I::Item; N];
#[inline]
fn next(&mut self) -> Option<Self::Item> {
match next_chunk(&mut self.iter) {
Ok(chunk) => Some(chunk),
Err(remainder) => {
self.remainder.get_or_insert(remainder);
None
}
}
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
let (lower, upper) = self.iter.size_hint();
(lower / N, upper.map(|n| n / N))
}
#[inline]
fn count(self) -> usize {
self.iter.count() / N
}
}
#[inline]
fn next_chunk<I: Iterator + Sized, const N: usize>(
iter: &mut I,
) -> Result<[I::Item; N], Take<IntoIter<I::Item, N>>> {
iter_next_chunk(iter)
}
impl<I, const N: usize> FusedIterator for ArrayChunks<I, N> where I: FusedIterator {}
impl<I, const N: usize> ExactSizeIterator for ArrayChunks<I, N>
where
I: ExactSizeIterator,
{
#[inline]
fn len(&self) -> usize {
self.iter.len() / N
}
}
#[inline]
fn iter_next_chunk<T, const N: usize>(
iter: &mut impl Iterator<Item = T>,
) -> Result<[T; N], Take<IntoIter<T, N>>> {
let mut array = uninit_array::<T, N>();
let r = iter_next_chunk_erased(&mut array, iter);
match r {
Ok(()) => {
Ok(unsafe { array_assume_init(array) })
}
Err(initialized) => {
let array = unsafe { array_assume_init(array) };
Err(array.into_iter().take(initialized))
}
}
}
#[inline(always)]
const fn uninit_array<T, const N: usize>() -> [MaybeUninit<T>; N] {
unsafe { MaybeUninit::<[MaybeUninit<T>; N]>::uninit().assume_init() }
}
#[inline(always)]
unsafe fn array_assume_init<T, const N: usize>(array: [MaybeUninit<T>; N]) -> [T; N] {
let ret = unsafe {
(&array as *const _ as *const [T; N]).read()
};
core::mem::forget(array);
ret
}
#[inline]
fn iter_next_chunk_erased<T>(
buffer: &mut [MaybeUninit<T>],
iter: &mut impl Iterator<Item = T>,
) -> Result<(), usize> {
let mut guard = Guard {
array_mut: buffer,
initialized: 0,
};
while guard.initialized < guard.array_mut.len() {
let Some(item) = iter.next() else {
let initialized = guard.initialized;
core::mem::forget(guard);
return Err(initialized);
};
unsafe { guard.push_unchecked(item) };
}
core::mem::forget(guard);
Ok(())
}
struct Guard<'a, T> {
pub array_mut: &'a mut [MaybeUninit<T>],
pub initialized: usize,
}
impl<T> Guard<'_, T> {
#[inline]
pub unsafe fn push_unchecked(&mut self, item: T) {
unsafe {
self.array_mut
.get_unchecked_mut(self.initialized)
.write(item);
self.initialized += 1;
}
}
}
impl<T> Drop for Guard<'_, T> {
fn drop(&mut self) {
debug_assert!(self.initialized <= self.array_mut.len());
unsafe {
core::ptr::drop_in_place(slice_assume_init_mut(
self.array_mut.get_unchecked_mut(..self.initialized),
));
}
}
}
#[inline(always)]
unsafe fn slice_assume_init_mut<T>(slice: &mut [MaybeUninit<T>]) -> &mut [T] {
unsafe { &mut *(slice as *mut [MaybeUninit<T>] as *mut [T]) }
}