use core::ptr::NonNull;
use crate::{BumpBox, polyfill::non_null};
#[derive(Debug)]
#[must_use = "iterators are lazy and do nothing unless consumed"]
pub struct ExtractIf<'a, T, F>
where
F: FnMut(&mut T) -> bool,
{
ptr: &'a mut NonNull<[T]>,
index: usize,
drained_count: usize,
original_len: usize,
filter: F,
}
impl<'a, T, F> ExtractIf<'a, T, F>
where
F: FnMut(&mut T) -> bool,
{
pub(crate) fn new<'a2>(boxed: &'a mut BumpBox<'a2, [T]>, filter: F) -> Self {
let ptr = unsafe { boxed.mut_ptr() };
let len = ptr.len();
non_null::set_len(ptr, 0);
Self {
ptr,
index: 0,
drained_count: 0,
original_len: len,
filter,
}
}
}
impl<T, F> Iterator for ExtractIf<'_, T, F>
where
F: FnMut(&mut T) -> bool,
{
type Item = T;
fn next(&mut self) -> Option<T> {
unsafe {
while self.index < self.original_len {
let start_ptr = non_null::as_non_null_ptr(*self.ptr);
let mut value_ptr = start_ptr.add(self.index);
let drained = (self.filter)(value_ptr.as_mut());
self.index += 1;
if drained {
self.drained_count += 1;
return Some(value_ptr.read());
} else if self.drained_count > 0 {
let src = value_ptr;
let dst = value_ptr.sub(self.drained_count);
src.copy_to_nonoverlapping(dst, 1);
}
}
None
}
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
(0, Some(self.original_len - self.index))
}
}
impl<T, F> Drop for ExtractIf<'_, T, F>
where
F: FnMut(&mut T) -> bool,
{
fn drop(&mut self) {
unsafe {
if self.index < self.original_len && self.drained_count > 0 {
let ptr = non_null::as_non_null_ptr(*self.ptr);
let src = ptr.add(self.index);
let dst = src.sub(self.drained_count);
let tail_len = self.original_len - self.index;
src.copy_to(dst, tail_len);
}
non_null::set_len(self.ptr, self.original_len - self.drained_count);
}
}
}