Skip to content

Commit 6f6637e

Browse files
authored
Port feature drain_filter (#292) to v2 (#333)
* port feature drain_filter (#292) to v2 * add Debug implementation for DrainFilter
1 parent a410c67 commit 6f6637e

File tree

4 files changed

+278
-0
lines changed

4 files changed

+278
-0
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
target
22
Cargo.lock
33
/fuzz/hfuzz_target
4+
/.vscode

Cargo.toml

+2
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ documentation = "https://docs.rs/smallvec/"
1616
write = []
1717
specialization = []
1818
may_dangle = []
19+
drain_filter = []
20+
drain_keep_rest = ["drain_filter"]
1921

2022
[dependencies]
2123
serde = { version = "1", optional = true, default-features = false }

src/lib.rs

+250
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,191 @@ impl<'a, T: 'a, const N: usize> Drop for Drain<'a, T, N> {
380380
}
381381
}
382382

383+
#[cfg(feature = "drain_filter")]
384+
/// An iterator which uses a closure to determine if an element should be removed.
385+
///
386+
/// Returned from [`SmallVec::drain_filter`][1].
387+
///
388+
/// [1]: struct.SmallVec.html#method.drain_filter
389+
pub struct DrainFilter<'a, T, const N: usize, F>
390+
where
391+
F: FnMut(&mut T) -> bool,
392+
{
393+
vec: &'a mut SmallVec<T, N>,
394+
/// The index of the item that will be inspected by the next call to `next`.
395+
idx: usize,
396+
/// The number of items that have been drained (removed) thus far.
397+
del: usize,
398+
/// The original length of `vec` prior to draining.
399+
old_len: usize,
400+
/// The filter test predicate.
401+
pred: F,
402+
/// A flag that indicates a panic has occurred in the filter test predicate.
403+
/// This is used as a hint in the drop implementation to prevent consumption
404+
/// of the remainder of the `DrainFilter`. Any unprocessed items will be
405+
/// backshifted in the `vec`, but no further items will be dropped or
406+
/// tested by the filter predicate.
407+
panic_flag: bool,
408+
}
409+
410+
#[cfg(feature = "drain_filter")]
411+
impl<T, const N: usize, F> core::fmt::Debug for DrainFilter<'_, T, N, F>
412+
where
413+
F: FnMut(&mut T) -> bool,
414+
T: core::fmt::Debug,
415+
{
416+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
417+
f.debug_tuple("DrainFilter")
418+
.field(&self.vec.as_slice())
419+
.finish()
420+
}
421+
}
422+
423+
#[cfg(feature = "drain_filter")]
424+
impl<T, F, const N: usize> Iterator for DrainFilter<'_, T, N, F>
425+
where
426+
F: FnMut(&mut T) -> bool,
427+
{
428+
type Item = T;
429+
430+
fn next(&mut self) -> Option<T> {
431+
unsafe {
432+
while self.idx < self.old_len {
433+
let i = self.idx;
434+
let v = core::slice::from_raw_parts_mut(self.vec.as_mut_ptr(), self.old_len);
435+
self.panic_flag = true;
436+
let drained = (self.pred)(&mut v[i]);
437+
self.panic_flag = false;
438+
// Update the index *after* the predicate is called. If the index
439+
// is updated prior and the predicate panics, the element at this
440+
// index would be leaked.
441+
self.idx += 1;
442+
if drained {
443+
self.del += 1;
444+
return Some(core::ptr::read(&v[i]));
445+
} else if self.del > 0 {
446+
let del = self.del;
447+
let src: *const Self::Item = &v[i];
448+
let dst: *mut Self::Item = &mut v[i - del];
449+
core::ptr::copy_nonoverlapping(src, dst, 1);
450+
}
451+
}
452+
None
453+
}
454+
}
455+
456+
fn size_hint(&self) -> (usize, Option<usize>) {
457+
(0, Some(self.old_len - self.idx))
458+
}
459+
}
460+
461+
#[cfg(feature = "drain_filter")]
462+
impl<T, F, const N: usize> Drop for DrainFilter<'_, T, N, F>
463+
where
464+
F: FnMut(&mut T) -> bool,
465+
{
466+
fn drop(&mut self) {
467+
struct BackshiftOnDrop<'a, 'b, T, const N: usize, F>
468+
where
469+
F: FnMut(&mut T) -> bool,
470+
{
471+
drain: &'b mut DrainFilter<'a, T, N, F>,
472+
}
473+
474+
impl<'a, 'b, T, const N: usize, F> Drop for BackshiftOnDrop<'a, 'b, T, N, F>
475+
where
476+
F: FnMut(&mut T) -> bool,
477+
{
478+
fn drop(&mut self) {
479+
unsafe {
480+
if self.drain.idx < self.drain.old_len && self.drain.del > 0 {
481+
// This is a pretty messed up state, and there isn't really an
482+
// obviously right thing to do. We don't want to keep trying
483+
// to execute `pred`, so we just backshift all the unprocessed
484+
// elements and tell the vec that they still exist. The backshift
485+
// is required to prevent a double-drop of the last successfully
486+
// drained item prior to a panic in the predicate.
487+
let ptr = self.drain.vec.as_mut_ptr();
488+
let src = ptr.add(self.drain.idx);
489+
let dst = src.sub(self.drain.del);
490+
let tail_len = self.drain.old_len - self.drain.idx;
491+
src.copy_to(dst, tail_len);
492+
}
493+
self.drain.vec.set_len(self.drain.old_len - self.drain.del);
494+
}
495+
}
496+
}
497+
498+
let backshift = BackshiftOnDrop { drain: self };
499+
500+
// Attempt to consume any remaining elements if the filter predicate
501+
// has not yet panicked. We'll backshift any remaining elements
502+
// whether we've already panicked or if the consumption here panics.
503+
if !backshift.drain.panic_flag {
504+
backshift.drain.for_each(drop);
505+
}
506+
}
507+
}
508+
509+
#[cfg(feature = "drain_keep_rest")]
510+
impl<T, F, const N: usize> DrainFilter<'_, T, N, F>
511+
where
512+
F: FnMut(&mut T) -> bool,
513+
{
514+
/// Keep unyielded elements in the source `Vec`.
515+
///
516+
/// # Examples
517+
///
518+
/// ```
519+
/// # use smallvec::{smallvec, SmallVec};
520+
///
521+
/// let mut vec: SmallVec<char, 2> = smallvec!['a', 'b', 'c'];
522+
/// let mut drain = vec.drain_filter(|_| true);
523+
///
524+
/// assert_eq!(drain.next().unwrap(), 'a');
525+
///
526+
/// // This call keeps 'b' and 'c' in the vec.
527+
/// drain.keep_rest();
528+
///
529+
/// // If we wouldn't call `keep_rest()`,
530+
/// // `vec` would be empty.
531+
/// assert_eq!(vec, SmallVec::<char, 2>::from_slice(&['b', 'c']));
532+
/// ```
533+
pub fn keep_rest(self) {
534+
// At this moment layout looks like this:
535+
//
536+
// _____________________/-- old_len
537+
// / \
538+
// [kept] [yielded] [tail]
539+
// \_______/ ^-- idx
540+
// \-- del
541+
//
542+
// Normally `Drop` impl would drop [tail] (via .for_each(drop), ie still calling `pred`)
543+
//
544+
// 1. Move [tail] after [kept]
545+
// 2. Update length of the original vec to `old_len - del`
546+
// a. In case of ZST, this is the only thing we want to do
547+
// 3. Do *not* drop self, as everything is put in a consistent state already, there is nothing to do
548+
let mut this = ManuallyDrop::new(self);
549+
550+
unsafe {
551+
// ZSTs have no identity, so we don't need to move them around.
552+
let needs_move = core::mem::size_of::<T>() != 0;
553+
554+
if needs_move && this.idx < this.old_len && this.del > 0 {
555+
let ptr = this.vec.as_mut_ptr();
556+
let src = ptr.add(this.idx);
557+
let dst = src.sub(this.del);
558+
let tail_len = this.old_len - this.idx;
559+
src.copy_to(dst, tail_len);
560+
}
561+
562+
let new_len = this.old_len - this.del;
563+
this.vec.set_len(new_len);
564+
}
565+
}
566+
}
567+
383568
/// An iterator that consumes a `SmallVec` and yields its items by value.
384569
///
385570
/// Returned from [`SmallVec::into_iter`][1].
@@ -732,6 +917,71 @@ impl<T, const N: usize> SmallVec<T, N> {
732917
}
733918
}
734919

920+
#[cfg(feature = "drain_filter")]
921+
/// Creates an iterator which uses a closure to determine if an element should be removed.
922+
///
923+
/// If the closure returns true, the element is removed and yielded. If the closure returns
924+
/// false, the element will remain in the vector and will not be yielded by the iterator.
925+
///
926+
/// Using this method is equivalent to the following code:
927+
/// ```
928+
/// # use smallvec::SmallVec;
929+
/// # let some_predicate = |x: &mut i32| { *x == 2 || *x == 3 || *x == 6 };
930+
/// # let mut vec: SmallVec<i32, 8> = SmallVec::from_slice(&[1i32, 2, 3, 4, 5, 6]);
931+
/// let mut i = 0;
932+
/// while i < vec.len() {
933+
/// if some_predicate(&mut vec[i]) {
934+
/// let val = vec.remove(i);
935+
/// // your code here
936+
/// } else {
937+
/// i += 1;
938+
/// }
939+
/// }
940+
///
941+
/// # assert_eq!(vec, SmallVec::<i32, 8>::from_slice(&[1i32, 4, 5]));
942+
/// ```
943+
/// ///
944+
/// But `drain_filter` is easier to use. `drain_filter` is also more efficient,
945+
/// because it can backshift the elements of the array in bulk.
946+
///
947+
/// Note that `drain_filter` also lets you mutate every element in the filter closure,
948+
/// regardless of whether you choose to keep or remove it.
949+
///
950+
/// # Examples
951+
///
952+
/// Splitting an array into evens and odds, reusing the original allocation:
953+
///
954+
/// ```
955+
/// # use smallvec::SmallVec;
956+
/// let mut numbers: SmallVec<i32, 16> = SmallVec::from_slice(&[1i32, 2, 3, 4, 5, 6, 8, 9, 11, 13, 14, 15]);
957+
///
958+
/// let evens = numbers.drain_filter(|x| *x % 2 == 0).collect::<SmallVec<i32, 16>>();
959+
/// let odds = numbers;
960+
///
961+
/// assert_eq!(evens, SmallVec::<i32, 16>::from_slice(&[2i32, 4, 6, 8, 14]));
962+
/// assert_eq!(odds, SmallVec::<i32, 16>::from_slice(&[1i32, 3, 5, 9, 11, 13, 15]));
963+
/// ```
964+
pub fn drain_filter<F>(&mut self, filter: F) -> DrainFilter<'_, T, N, F>
965+
where
966+
F: FnMut(&mut T) -> bool,
967+
{
968+
let old_len = self.len();
969+
970+
// Guard against us getting leaked (leak amplification)
971+
unsafe {
972+
self.set_len(0);
973+
}
974+
975+
DrainFilter {
976+
vec: self,
977+
idx: 0,
978+
del: 0,
979+
old_len,
980+
pred: filter,
981+
panic_flag: false,
982+
}
983+
}
984+
735985
#[inline]
736986
pub fn push(&mut self, value: T) {
737987
let len = self.len();

src/tests.rs

+25
Original file line numberDiff line numberDiff line change
@@ -1003,3 +1003,28 @@ fn test_clone_from() {
10031003
b.clone_from(&c);
10041004
assert_eq!(&*b, &[20, 21, 22]);
10051005
}
1006+
1007+
#[cfg(feature = "drain_filter")]
1008+
#[test]
1009+
fn drain_filter() {
1010+
let mut a: SmallVec<u8, 2> = smallvec![1u8, 2, 3, 4, 5, 6, 7, 8];
1011+
1012+
let b: SmallVec<u8, 2> = a.drain_filter(|x| *x % 3 == 0).collect();
1013+
1014+
assert_eq!(a, SmallVec::<u8, 2>::from_slice(&[1u8, 2, 4, 5, 7, 8]));
1015+
assert_eq!(b, SmallVec::<u8, 2>::from_slice(&[3u8, 6]));
1016+
}
1017+
1018+
#[cfg(feature = "drain_keep_rest")]
1019+
#[test]
1020+
fn drain_keep_rest() {
1021+
let mut a: SmallVec<i32, 3> = smallvec![1i32, 2, 3, 4, 5, 6, 7, 8];
1022+
let mut df = a.drain_filter(|x| *x % 2 == 0);
1023+
1024+
assert_eq!(df.next().unwrap(), 2);
1025+
assert_eq!(df.next().unwrap(), 4);
1026+
1027+
df.keep_rest();
1028+
1029+
assert_eq!(a, SmallVec::<i32, 3>::from_slice(&[1i32, 3, 5, 6, 7, 8]));
1030+
}

0 commit comments

Comments
 (0)