Skip to content

Commit 64b0f35

Browse files
committed
vec: add extract_if()
Adapts alloc::vec::Vec::extract_if()
1 parent ae8d602 commit 64b0f35

2 files changed

Lines changed: 375 additions & 0 deletions

File tree

src/vec/extract_if.rs

Lines changed: 287 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,287 @@
1+
use core::ops::{Range, RangeBounds};
2+
use core::{fmt, ptr, slice};
3+
4+
use crate::LenType;
5+
6+
use super::VecView;
7+
8+
/// An iterator which uses a closure to determine if an element should be removed.
9+
///
10+
/// This struct is created by [`Vec::extract_if`].
11+
/// See its documentation for more.
12+
///
13+
/// # Example
14+
///
15+
/// ```
16+
/// use heapless::Vec;
17+
///
18+
/// let mut v = Vec::<_, 4>::from_array([0, 1, 2]);
19+
/// let iter: heapless::vec::ExtractIf<'_, _, _, _> = v.extract_if(.., |x| *x % 2 == 0);
20+
/// ```
21+
#[must_use = "iterators are lazy and do nothing unless consumed; \
22+
use `retain_mut` or `extract_if().for_each(drop)` to remove and discard elements"]
23+
pub struct ExtractIf<'a, T, F, LenT: LenType> {
24+
vec: &'a mut VecView<T, LenT>,
25+
/// The index of the item that will be inspected by the next call to `next`.
26+
idx: usize,
27+
/// Elements at and beyond this point will be retained. Must be equal or smaller than `old_len`.
28+
end: usize,
29+
/// The number of items that have been drained (removed) thus far.
30+
del: usize,
31+
/// The original length of `vec` prior to draining.
32+
old_len: usize,
33+
/// The filter test predicate.
34+
pred: F,
35+
}
36+
37+
impl<'a, T, F, LenT: LenType> ExtractIf<'a, T, F, LenT> {
38+
pub(super) fn new<R: RangeBounds<usize>>(
39+
vec: &'a mut VecView<T, LenT>,
40+
pred: F,
41+
range: R,
42+
) -> Self {
43+
let old_len = vec.len();
44+
let Range { start, end } = crate::slice::range(range, ..old_len);
45+
46+
// Guard against the vec getting leaked (leak amplification)
47+
unsafe {
48+
vec.set_len(0);
49+
}
50+
ExtractIf {
51+
vec,
52+
idx: start,
53+
del: 0,
54+
end,
55+
old_len,
56+
pred,
57+
}
58+
}
59+
}
60+
61+
impl<T, F, LenT: LenType> Iterator for ExtractIf<'_, T, F, LenT>
62+
where
63+
F: FnMut(&mut T) -> bool,
64+
{
65+
type Item = T;
66+
67+
fn next(&mut self) -> Option<T> {
68+
while self.idx < self.end {
69+
let i = self.idx;
70+
// SAFETY:
71+
// We know that `i < self.end` from the if guard and that `self.end <= self.old_len` from
72+
// the validity of `Self`. Therefore `i` points to an element within `vec`.
73+
//
74+
// Additionally, the i-th element is valid because each element is visited at most once
75+
// and it is the first time we access vec[i].
76+
//
77+
// Note: we can't use `vec.get_unchecked_mut(i)` here since the precondition for that
78+
// function is that i < vec.len(), but we've set vec's length to zero.
79+
let cur = unsafe { &mut *self.vec.as_mut_ptr().add(i) };
80+
let drained = (self.pred)(cur);
81+
// Update the index *after* the predicate is called. If the index
82+
// is updated prior and the predicate panics, the element at this
83+
// index would be leaked.
84+
self.idx += 1;
85+
if drained {
86+
self.del += 1;
87+
// SAFETY: We never touch this element again after returning it.
88+
return Some(unsafe { ptr::read(cur) });
89+
} else if self.del > 0 {
90+
// SAFETY: `self.del` > 0, so the hole slot must not overlap with current element.
91+
// We use copy for move, and never touch this element again.
92+
unsafe {
93+
let hole_slot = self.vec.as_mut_ptr().add(i - self.del);
94+
ptr::copy_nonoverlapping(cur, hole_slot, 1);
95+
}
96+
}
97+
}
98+
None
99+
}
100+
101+
fn size_hint(&self) -> (usize, Option<usize>) {
102+
(0, Some(self.end - self.idx))
103+
}
104+
}
105+
106+
impl<T, F, LenT: LenType> Drop for ExtractIf<'_, T, F, LenT> {
107+
fn drop(&mut self) {
108+
if self.del > 0 {
109+
// SAFETY: Trailing unchecked items must be valid since we never touch them.
110+
unsafe {
111+
ptr::copy(
112+
self.vec.as_ptr().add(self.idx),
113+
self.vec.as_mut_ptr().add(self.idx - self.del),
114+
self.old_len - self.idx,
115+
);
116+
}
117+
}
118+
// SAFETY: After filling holes, all items are in contiguous memory.
119+
unsafe {
120+
self.vec.set_len(self.old_len - self.del);
121+
}
122+
}
123+
}
124+
125+
impl<T, F, LenT: LenType> fmt::Debug for ExtractIf<'_, T, F, LenT>
126+
where
127+
T: fmt::Debug,
128+
{
129+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
130+
// We have to use pointer arithmetics here,
131+
// because the length of `self.vec` is temporarily set to 0.
132+
let start = self.vec.as_ptr();
133+
134+
// SAFETY: we always keep first `self.idx - self.del` elements valid.
135+
let retained = unsafe { slice::from_raw_parts(start, self.idx - self.del) };
136+
137+
// SAFETY: we have not yet touched elements starting at `self.idx`.
138+
let valid_tail =
139+
unsafe { slice::from_raw_parts(start.add(self.idx), self.old_len - self.idx) };
140+
141+
// SAFETY: `end - idx <= old_len - idx`, because `end <= old_len`. Also `idx <= end` by invariant.
142+
let (remainder, skipped_tail) =
143+
unsafe { valid_tail.split_at_unchecked(self.end - self.idx) };
144+
145+
f.debug_struct("ExtractIf")
146+
.field("retained", &retained)
147+
.field("remainder", &remainder)
148+
.field("skipped_tail", &skipped_tail)
149+
.finish_non_exhaustive()
150+
}
151+
}
152+
153+
#[cfg(test)]
154+
mod tests {
155+
use super::super::Vec;
156+
157+
#[test]
158+
fn extract_if_empty() {
159+
let mut vec = Vec::<i32, 8>::new();
160+
161+
{
162+
let mut iter = vec.extract_if(.., |_| true);
163+
assert_eq!(iter.size_hint(), (0, Some(0)));
164+
assert_eq!(iter.next(), None);
165+
assert_eq!(iter.size_hint(), (0, Some(0)));
166+
assert_eq!(iter.next(), None);
167+
assert_eq!(iter.size_hint(), (0, Some(0)));
168+
}
169+
assert_eq!(vec.len(), 0);
170+
assert_eq!(vec, []);
171+
}
172+
173+
#[test]
174+
fn extract_if_zst() {
175+
let mut vec = Vec::<(), 8>::from_array([(), (), (), (), ()]);
176+
let initial_len = vec.len();
177+
let mut count = 0;
178+
{
179+
let mut iter = vec.extract_if(.., |_| true);
180+
assert_eq!(iter.size_hint(), (0, Some(initial_len)));
181+
while let Some(_) = iter.next() {
182+
count += 1;
183+
assert_eq!(iter.size_hint(), (0, Some(initial_len - count)));
184+
}
185+
assert_eq!(iter.size_hint(), (0, Some(0)));
186+
assert_eq!(iter.next(), None);
187+
assert_eq!(iter.size_hint(), (0, Some(0)));
188+
}
189+
190+
assert_eq!(count, initial_len);
191+
assert_eq!(vec.len(), 0);
192+
assert_eq!(vec, []);
193+
}
194+
195+
#[test]
196+
fn extract_if_false() {
197+
let mut vec = Vec::<_, 16>::from_array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
198+
199+
let initial_len = vec.len();
200+
let mut count = 0;
201+
{
202+
let mut iter = vec.extract_if(.., |_| false);
203+
assert_eq!(iter.size_hint(), (0, Some(initial_len)));
204+
for _ in iter.by_ref() {
205+
count += 1;
206+
}
207+
assert_eq!(iter.size_hint(), (0, Some(0)));
208+
assert_eq!(iter.next(), None);
209+
assert_eq!(iter.size_hint(), (0, Some(0)));
210+
}
211+
212+
assert_eq!(count, 0);
213+
assert_eq!(vec.len(), initial_len);
214+
assert_eq!(vec, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
215+
}
216+
217+
#[test]
218+
fn extract_if_true() {
219+
let mut vec = Vec::<_, 16>::from_array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
220+
221+
let initial_len = vec.len();
222+
let mut count = 0;
223+
{
224+
let mut iter = vec.extract_if(.., |_| true);
225+
assert_eq!(iter.size_hint(), (0, Some(initial_len)));
226+
while let Some(_) = iter.next() {
227+
count += 1;
228+
assert_eq!(iter.size_hint(), (0, Some(initial_len - count)));
229+
}
230+
assert_eq!(iter.size_hint(), (0, Some(0)));
231+
assert_eq!(iter.next(), None);
232+
assert_eq!(iter.size_hint(), (0, Some(0)));
233+
}
234+
235+
assert_eq!(count, initial_len);
236+
assert_eq!(vec.len(), 0);
237+
assert_eq!(vec, []);
238+
}
239+
240+
#[test]
241+
fn extract_if_ranges() {
242+
let mut vec = Vec::<_, 16>::from_array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
243+
244+
let mut count = 0;
245+
let it = vec.extract_if(1..=3, |_| {
246+
count += 1;
247+
true
248+
});
249+
assert_eq!(it.collect::<Vec<_, 8>>(), [1, 2, 3]);
250+
assert_eq!(vec, [0, 4, 5, 6, 7, 8, 9, 10]);
251+
assert_eq!(count, 3);
252+
253+
let it = vec.extract_if(1..=3, |_| false);
254+
assert_eq!(it.collect::<Vec<_, 8>>(), []);
255+
assert_eq!(vec, [0, 4, 5, 6, 7, 8, 9, 10]);
256+
}
257+
258+
#[test]
259+
#[should_panic]
260+
fn extract_if_out_of_bounds() {
261+
let mut vec = Vec::<_, 8>::from_array([0, 1]);
262+
let _ = vec.extract_if(5.., |_| true).for_each(drop);
263+
}
264+
265+
#[test]
266+
fn extract_if_unconsumed() {
267+
let mut vec = Vec::<_, 4>::from_array([1, 2, 3, 4]);
268+
let drain = vec.extract_if(.., |&mut x| x % 2 != 0);
269+
drop(drain);
270+
assert_eq!(vec, [1, 2, 3, 4]);
271+
}
272+
273+
#[test]
274+
fn extract_if_debug() {
275+
let mut vec = Vec::<_, 8>::from_array([1, 2, 3, 4, 5, 6, 7, 8]);
276+
let mut drain = vec.extract_if(1..5, |&mut x| x % 2 != 0);
277+
assert_eq!(
278+
format!("{drain:?}"),
279+
"ExtractIf { retained: [1], remainder: [2, 3, 4, 5], skipped_tail: [6, 7, 8], .. }"
280+
);
281+
drain.next().unwrap();
282+
assert_eq!(
283+
format!("{drain:?}"),
284+
"ExtractIf { retained: [1, 2], remainder: [4, 5], skipped_tail: [6, 7, 8], .. }"
285+
);
286+
}
287+
}

src/vec/mod.rs

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ use crate::{
2121
};
2222

2323
mod drain;
24+
mod extract_if;
2425

2526
mod storage {
2627
use core::mem::MaybeUninit;
@@ -218,6 +219,7 @@ pub use storage::{OwnedVecStorage, VecStorage, ViewVecStorage};
218219
pub(crate) use storage::VecStorageInner;
219220

220221
pub use drain::Drain;
222+
pub use extract_if::ExtractIf;
221223

222224
/// Base struct for [`Vec`] and [`VecView`], generic over the [`VecStorage`].
223225
///
@@ -511,6 +513,92 @@ impl<T, LenT: LenType, S: VecStorage<T> + ?Sized> VecInner<T, LenT, S> {
511513
}
512514
}
513515

516+
/// Creates an iterator which uses a closure to determine if an element in the range should be removed.
517+
///
518+
/// If the closure returns `true`, the element is removed from the vector
519+
/// and yielded. If the closure returns `false`, or panics, the element
520+
/// remains in the vector and will not be yielded.
521+
///
522+
/// Only elements that fall in the provided range are considered for extraction, but any elements
523+
/// after the range will still have to be moved if any element has been extracted.
524+
///
525+
/// If the returned `ExtractIf` is not exhausted, e.g. because it is dropped without iterating
526+
/// or the iteration short-circuits, then the remaining elements will be retained.
527+
/// Use `extract_if().for_each(drop)` if you do not need the returned iterator,
528+
/// or [`retain_mut`] with a negated predicate if you also do not need to restrict the range.
529+
///
530+
/// [`retain_mut`]: VecInner::retain_mut
531+
///
532+
/// Using this method is equivalent to the following code:
533+
///
534+
/// ```
535+
/// use heapless::Vec;
536+
///
537+
/// # let some_predicate = |x: &mut i32| { *x % 2 == 1 };
538+
/// # let mut vec = Vec::<_, 8>::from_array([0, 1, 2, 3, 4, 5, 6]);
539+
/// # let mut vec2 = vec.clone();
540+
/// # let range = 1..5;
541+
/// let mut i = range.start;
542+
/// let end_items = vec.len() - range.end;
543+
/// # let mut extracted = Vec::<_, 8>::new();
544+
///
545+
/// while i < vec.len() - end_items {
546+
/// if some_predicate(&mut vec[i]) {
547+
/// let val = vec.remove(i);
548+
/// // your code here
549+
/// # extracted.push(val);
550+
/// } else {
551+
/// i += 1;
552+
/// }
553+
/// }
554+
///
555+
/// # let extracted2: Vec<_, 8> = vec2.extract_if(range, some_predicate).collect();
556+
/// # assert_eq!(vec, vec2);
557+
/// # assert_eq!(extracted, extracted2);
558+
/// ```
559+
///
560+
/// But `extract_if` is easier to use. `extract_if` is also more efficient,
561+
/// because it can backshift the elements of the array in bulk.
562+
///
563+
/// The iterator also lets you mutate the value of each element in the
564+
/// closure, regardless of whether you choose to keep or remove it.
565+
///
566+
/// # Panics
567+
///
568+
/// If `range` is out of bounds.
569+
///
570+
/// # Examples
571+
///
572+
/// Splitting a vector into even and odd values, reusing the original vector:
573+
///
574+
/// ```
575+
/// # use heapless::Vec;
576+
/// let mut numbers = Vec::<_, 16>::from_array([1, 2, 3, 4, 5, 6, 8, 9, 11, 13, 14, 15]);
577+
///
578+
/// let evens = numbers.extract_if(.., |x| *x % 2 == 0).collect::<Vec<_, 16>>();
579+
/// let odds = numbers;
580+
///
581+
/// assert_eq!(evens, Vec::<_, 16>::from_array([2, 4, 6, 8, 14]));
582+
/// assert_eq!(odds, Vec::<_, 16>::from_array([1, 3, 5, 9, 11, 13, 15]));
583+
/// ```
584+
///
585+
/// Using the range argument to only process a part of the vector:
586+
///
587+
/// ```
588+
/// # use heapless::Vec;
589+
/// let mut items = Vec::<_, 16>::from_array([0, 0, 0, 0, 0, 0, 0, 1, 2, 1, 2, 1, 2]);
590+
/// let ones = items.extract_if(7.., |x| *x == 1).collect::<Vec<_, 16>>();
591+
/// assert_eq!(items, [0, 0, 0, 0, 0, 0, 0, 2, 2, 2]);
592+
/// assert_eq!(ones.len(), 3);
593+
/// ```
594+
pub fn extract_if<F, R>(&mut self, range: R, filter: F) -> ExtractIf<'_, T, F, LenT>
595+
where
596+
F: FnMut(&mut T) -> bool,
597+
R: RangeBounds<usize>,
598+
{
599+
ExtractIf::new(self.as_mut_view(), filter, range)
600+
}
601+
514602
/// Get a reference to the `Vec`, erasing the `N` const-generic.
515603
///
516604
///

0 commit comments

Comments
 (0)