Skip to content

Commit 064deca

Browse files
committed
Implement VecDeque::extract_if
Signed-off-by: tison <[email protected]>
1 parent 402ce0e commit 064deca

File tree

2 files changed

+226
-0
lines changed

2 files changed

+226
-0
lines changed
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
use core::ops::{Range, RangeBounds};
2+
use core::{fmt, ptr, slice};
3+
4+
use super::VecDeque;
5+
use crate::alloc::{Allocator, Global};
6+
7+
/// An iterator which uses a closure to determine if an element should be removed.
8+
///
9+
/// This struct is created by [`VecDeque::extract_if`].
10+
/// See its documentation for more.
11+
///
12+
/// # Example
13+
///
14+
/// ```
15+
/// use std::collections::vec_deque::ExtractIf;
16+
/// use std::collections::vec_deque::VecDeque;
17+
///
18+
/// let mut v = VecDeque::from([0, 1, 2]);
19+
/// let iter: ExtractIf<'_, _, _> = v.extract_if(.., |x| *x % 2 == 0);
20+
/// ```
21+
#[unstable(feature = "vec_deque_extract_if", issue = "147750")]
22+
#[must_use = "iterators are lazy and do nothing unless consumed"]
23+
pub struct ExtractIf<
24+
'a,
25+
T,
26+
F,
27+
#[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global,
28+
> {
29+
vec: &'a mut VecDeque<T, A>,
30+
/// The index of the item that will be inspected by the next call to `next`.
31+
idx: usize,
32+
/// Elements at and beyond this point will be retained. Must be equal or smaller than `old_len`.
33+
end: usize,
34+
/// The number of items that have been drained (removed) thus far.
35+
del: usize,
36+
/// The original length of `vec` prior to draining.
37+
old_len: usize,
38+
/// The filter test predicate.
39+
pred: F,
40+
}
41+
42+
impl<'a, T, F, A: Allocator> ExtractIf<'a, T, F, A> {
43+
pub(super) fn new<R: RangeBounds<usize>>(vec: &'a mut Vec<T, A>, pred: F, range: R) -> Self {
44+
let old_len = vec.len();
45+
let Range { start, end } = slice::range(range, ..old_len);
46+
47+
// Guard against the deque getting leaked (leak amplification)
48+
unsafe {
49+
vec.len = len;
50+
}
51+
ExtractIf { vec, idx: start, del: 0, end, old_len, pred }
52+
}
53+
54+
/// Returns a reference to the underlying allocator.
55+
#[unstable(feature = "allocator_api", issue = "32838")]
56+
#[inline]
57+
pub fn allocator(&self) -> &A {
58+
self.vec.allocator()
59+
}
60+
}
61+
62+
#[unstable(feature = "vec_deque_extract_if", issue = "147750")]
63+
impl<T, F, A: Allocator> Iterator for ExtractIf<'_, T, F, A>
64+
where
65+
F: FnMut(&mut T) -> bool,
66+
{
67+
type Item = T;
68+
69+
fn next(&mut self) -> Option<T> {
70+
while self.idx < self.end {
71+
let i = self.idx;
72+
// SAFETY:
73+
// We know that `i < self.end` from the if guard and that `self.end <= self.old_len` from
74+
// the validity of `Self`. Therefore `i` points to an element within `vec`.
75+
//
76+
// Additionally, the i-th element is valid because each element is visited at most once
77+
// and it is the first time we access vec[i].
78+
//
79+
// Note: we can't use `vec.get_mut(i).unwrap()` here since the precondition for that
80+
// function is that i < vec.len, but we've set vec's length to zero.
81+
let idx = self.to_physical_idx(index);
82+
let cur = unsafe { &mut *self.ptr().add(idx) };
83+
let drained = (self.pred)(cur);
84+
// Update the index *after* the predicate is called. If the index
85+
// is updated prior and the predicate panics, the element at this
86+
// index would be leaked.
87+
self.idx += 1;
88+
if drained {
89+
self.del += 1;
90+
// SAFETY: We never touch this element again after returning it.
91+
return Some(unsafe { ptr::read(cur) });
92+
} else if self.del > 0 {
93+
// SAFETY: `self.del` > 0, so the hole slot must not overlap with current element.
94+
// We use copy for move, and never touch this element again.
95+
unsafe {
96+
let hole_slot = self.vec.ptr().add(idx - self.del);
97+
ptr::copy_nonoverlapping(cur, hole_slot, 1);
98+
}
99+
}
100+
}
101+
None
102+
}
103+
104+
fn size_hint(&self) -> (usize, Option<usize>) {
105+
(0, Some(self.end - self.idx))
106+
}
107+
}
108+
109+
#[unstable(feature = "vec_deque_extract_if", issue = "147750")]
110+
impl<T, F, A: Allocator> Drop for ExtractIf<'_, T, F, A> {
111+
fn drop(&mut self) {
112+
if self.del > 0 {
113+
let idx = self.to_physical_idx(self.idx);
114+
// SAFETY: Trailing unchecked items must be valid since we never touch them.
115+
unsafe {
116+
ptr::copy(
117+
self.vec.ptr().add(idx),
118+
self.vec.ptr().add(idx - self.del),
119+
self.old_len - self.idx,
120+
);
121+
}
122+
}
123+
// SAFETY: After filling holes, all items are in contiguous memory.
124+
unsafe {
125+
self.vec.set_len(self.old_len - self.del);
126+
}
127+
}
128+
}
129+
130+
#[unstable(feature = "vec_deque_extract_if", issue = "147750")]
131+
impl<T, F, A> fmt::Debug for ExtractIf<'_, T, F, A>
132+
where
133+
T: fmt::Debug,
134+
A: Allocator,
135+
{
136+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
137+
let peek = if self.idx < self.end { self.vec.get(self.idx) } else { None };
138+
f.debug_struct("ExtractIf").field("peek", &peek).finish_non_exhaustive()
139+
}
140+
}

library/alloc/src/collections/vec_deque/mod.rs

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@ use crate::collections::{TryReserveError, TryReserveErrorKind};
2424
use crate::raw_vec::RawVec;
2525
use crate::vec::Vec;
2626

27+
#[unstable(feature = "vec_deque_extract_if", issue = "147750")]
28+
pub use self::extract_if::ExtractIf;
29+
30+
mod extract_if;
31+
2732
#[macro_use]
2833
mod macros;
2934

@@ -542,6 +547,87 @@ impl<T, A: Allocator> VecDeque<T, A> {
542547
}
543548
debug_assert!(self.head < self.capacity() || self.capacity() == 0);
544549
}
550+
551+
/// Creates an iterator which uses a closure to determine if an element in the range should be removed.
552+
///
553+
/// If the closure returns `true`, the element is removed from the deque and yielded. If the closure
554+
/// returns `false`, or panics, the element remains in the deque and will not be yielded.
555+
///
556+
/// Only elements that fall in the provided range are considered for extraction, but any elements
557+
/// after the range will still have to be moved if any element has been extracted.
558+
///
559+
/// If the returned `ExtractIf` is not exhausted, e.g. because it is dropped without iterating
560+
/// or the iteration short-circuits, then the remaining elements will be retained.
561+
/// Use [`retain_mut`] with a negated predicate if you do not need the returned iterator.
562+
///
563+
/// [`retain_mut`]: VecDeque::retain_mut
564+
///
565+
/// Using this method is equivalent to the following code:
566+
///
567+
/// ```
568+
/// # let some_predicate = |x: &mut i32| { *x % 2 == 1 };
569+
/// # let mut deq = (0..10).collect();
570+
/// # let mut deq2 = deq.clone();
571+
/// # let range = 1..5;
572+
/// let mut i = range.start;
573+
/// let end_items = deq.len() - range.end;
574+
/// # let mut extracted = vec![];
575+
///
576+
/// while i < deq.len() - end_items {
577+
/// if some_predicate(&mut deq[i]) {
578+
/// let val = deq.remove(i);
579+
/// // your code here
580+
/// # extracted.push(val);
581+
/// } else {
582+
/// i += 1;
583+
/// }
584+
/// }
585+
///
586+
/// # let extracted2: Vec<_> = deq2.extract_if(range, some_predicate).collect();
587+
/// # assert_eq!(deq, deq2);
588+
/// # assert_eq!(extracted, extracted2);
589+
/// ```
590+
///
591+
/// But `extract_if` is easier to use. `extract_if` is also more efficient,
592+
/// because it can backshift the elements of the array in bulk.
593+
///
594+
/// The iterator also lets you mutate the value of each element in the
595+
/// closure, regardless of whether you choose to keep or remove it.
596+
///
597+
/// # Panics
598+
///
599+
/// If `range` is out of bounds.
600+
///
601+
/// # Examples
602+
///
603+
/// Splitting a deque into even and odd values, reusing the original deque:
604+
///
605+
/// ```
606+
/// let mut numbers = VecDeque::from([1, 2, 3, 4, 5, 6, 8, 9, 11, 13, 14, 15]);
607+
///
608+
/// let evens = numbers.extract_if(.., |x| *x % 2 == 0).collect::<VecDeque<_>>();
609+
/// let odds = numbers;
610+
///
611+
/// assert_eq!(evens, VecDeque::from([2, 4, 6, 8, 14]));
612+
/// assert_eq!(odds, VecDeque::from([1, 3, 5, 9, 11, 13, 15]));
613+
/// ```
614+
///
615+
/// Using the range argument to only process a part of the deque:
616+
///
617+
/// ```
618+
/// let mut items = VecDeque::from([0, 0, 0, 0, 0, 0, 0, 1, 2, 1, 2, 1, 2]);
619+
/// let ones = items.extract_if(7.., |x| *x == 1).collect::<VecDeque<_>>();
620+
/// assert_eq!(items, VecDeque::from([0, 0, 0, 0, 0, 0, 0, 2, 2, 2]));
621+
/// assert_eq!(ones.len(), 3);
622+
/// ```
623+
#[unstable(feature = "vec_deque_extract_if", issue = "147750")]
624+
pub fn extract_if<F, R>(&mut self, range: R, filter: F) -> ExtractIf<'_, T, F, A>
625+
where
626+
F: FnMut(&mut T) -> bool,
627+
R: RangeBounds<usize>,
628+
{
629+
ExtractIf::new(self, filter, range)
630+
}
545631
}
546632

547633
impl<T> VecDeque<T> {

0 commit comments

Comments
 (0)