Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 97 additions & 0 deletions src/iterators/extractif.rs
Comment thread
Kxrma47 marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
use crate::{
Allocator,
SmallVec
};

/// An iterator which uses a closure to determine if an element should be
/// removed.
///
/// Returned from [`SmallVec::extract_if`][1].
///
/// [1]: struct.SmallVec.html#method.extract_if
pub struct ExtractIf<'a, T, const N: usize, A: Allocator, F>
where F: FnMut(&mut T) -> bool
{
pub(crate) vec: &'a mut SmallVec<T, N, A>,
/// The index of the item that will be inspected by the next call to `next`.
pub(crate) idx: usize,
/// Elements at and beyond this point will be retained. Must be equal or
/// smaller than `old_len`.
pub(crate) end: usize,
/// The number of items that have been drained (removed) thus far.
pub(crate) del: usize,
/// The original length of `vec` prior to draining.
pub(crate) old_len: usize,
/// The filter test predicate.
pub(crate) pred: F
}
Comment thread
alejandro-vaz marked this conversation as resolved.

impl<T, const N: usize, A: Allocator, F> core::fmt::Debug for ExtractIf<'_, T, N, A, F>
where
F: FnMut(&mut T) -> bool,
T: core::fmt::Debug
{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_tuple("ExtractIf")
.field(&self.vec.as_slice())
.finish()
}
}

impl<T, F, const N: usize, A: Allocator> Iterator for ExtractIf<'_, T, N, A, F>
where F: FnMut(&mut T) -> bool
{
type Item = T;

fn next(&mut self) -> Option<T> {
unsafe {
while self.idx < self.end {
let i = self.idx;
// SAFETY: `i < self.end <= self.old_len`
let cur = self.vec.as_mut_ptr().add(i);
let drained = (self.pred)(&mut *cur);
// Update the index *after* the predicate is called. If the
// index is updated prior and the predicate
// panics, the element at this index would be
// leaked.
self.idx += 1;
if drained {
self.del += 1;
return Some(core::ptr::read(cur));
} else if self.del > 0 {
// SAFETY: `self.del <= i` therefore `i - self.del` is valid
core::ptr::copy_nonoverlapping(cur, cur.sub(self.del), 1);
}
}
None
}
}

fn size_hint(&self) -> (usize, Option<usize>) {
(0, Some(self.end - self.idx))
}
}

impl<T, F, const N: usize, A: Allocator> Drop for ExtractIf<'_, T, N, A, F>
where F: FnMut(&mut T) -> bool
{
fn drop(&mut self) {
unsafe {
if self.idx < self.old_len && self.del > 0 {
// This is a pretty messed up state, and there isn't really an
// obviously right thing to do. We don't want to keep trying
// to execute `pred`, so we just backshift all the unprocessed
// elements and tell the vec that they still exist. The
// backshift is required to prevent a
// double-drop of the last successfully
// drained item prior to a panic in the predicate.
let ptr = self.vec.as_mut_ptr();
let src = ptr.add(self.idx);
let dst = src.sub(self.del);
let tail_len = self.old_len - self.idx;
src.copy_to(dst, tail_len);
}
self.vec.set_len(self.old_len - self.del);
}
}
}
1 change: 1 addition & 0 deletions src/iterators/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
pub mod drain;
pub mod extractif;

#[cfg(feature = "rayon")]
mod rayon;
98 changes: 4 additions & 94 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@ mod comparisons;
mod conversions;
mod errors;
mod iterators;
pub use iterators::drain::Drain;
pub use iterators::{
drain::Drain,
extractif::ExtractIf
};
mod macros;
#[cfg(feature = "malloc_size_of")]
mod mallocsizeof;
Expand Down Expand Up @@ -164,99 +167,6 @@ impl<T, const N: usize> Default for SmallVec<T, N> {
}
}

/// An iterator which uses a closure to determine if an element should be
/// removed.
///
/// Returned from [`SmallVec::extract_if`][1].
///
/// [1]: struct.SmallVec.html#method.extract_if
pub struct ExtractIf<'a, T, const N: usize, A: Allocator, F>
where F: FnMut(&mut T) -> bool
{
vec: &'a mut SmallVec<T, N, A>,
/// The index of the item that will be inspected by the next call to `next`.
idx: usize,
/// Elements at and beyond this point will be retained. Must be equal or
/// smaller than `old_len`.
end: usize,
/// The number of items that have been drained (removed) thus far.
del: usize,
/// The original length of `vec` prior to draining.
old_len: usize,
/// The filter test predicate.
pred: F
}

impl<T, const N: usize, A: Allocator, F> core::fmt::Debug for ExtractIf<'_, T, N, A, F>
where
F: FnMut(&mut T) -> bool,
T: core::fmt::Debug
{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_tuple("ExtractIf")
.field(&self.vec.as_slice())
.finish()
}
}

impl<T, F, const N: usize, A: Allocator> Iterator for ExtractIf<'_, T, N, A, F>
where F: FnMut(&mut T) -> bool
{
type Item = T;

fn next(&mut self) -> Option<T> {
unsafe {
while self.idx < self.end {
let i = self.idx;
// SAFETY: `i < self.end <= self.old_len`
let cur = self.vec.as_mut_ptr().add(i);
let drained = (self.pred)(&mut *cur);
// Update the index *after* the predicate is called. If the
// index is updated prior and the predicate
// panics, the element at this index would be
// leaked.
self.idx += 1;
if drained {
self.del += 1;
return Some(core::ptr::read(cur));
} else if self.del > 0 {
// SAFETY: `self.del <= i` therefore `i - self.del` is valid
core::ptr::copy_nonoverlapping(cur, cur.sub(self.del), 1);
}
}
None
}
}

fn size_hint(&self) -> (usize, Option<usize>) {
(0, Some(self.end - self.idx))
}
}

impl<T, F, const N: usize, A: Allocator> Drop for ExtractIf<'_, T, N, A, F>
where F: FnMut(&mut T) -> bool
{
fn drop(&mut self) {
unsafe {
if self.idx < self.old_len && self.del > 0 {
// This is a pretty messed up state, and there isn't really an
// obviously right thing to do. We don't want to keep trying
// to execute `pred`, so we just backshift all the unprocessed
// elements and tell the vec that they still exist. The
// backshift is required to prevent a
// double-drop of the last successfully
// drained item prior to a panic in the predicate.
let ptr = self.vec.as_mut_ptr();
let src = ptr.add(self.idx);
let dst = src.sub(self.del);
let tail_len = self.old_len - self.idx;
src.copy_to(dst, tail_len);
}
self.vec.set_len(self.old_len - self.del);
}
}
}

pub struct Splice<'a, I: Iterator + 'a, const N: usize> {
drain: Drain<'a, I::Item, N, Global>,
replace_with: I
Expand Down
Loading