Skip to content

Commit 7fe75c3

Browse files
committed
Add ExtractIf for SmallVec
1 parent a1eceec commit 7fe75c3

File tree

2 files changed

+102
-0
lines changed

2 files changed

+102
-0
lines changed

compiler/rustc_data_structures/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ pub mod packed;
6666
pub mod profiling;
6767
pub mod sharded;
6868
pub mod small_c_str;
69+
pub mod smallvec;
6970
pub mod snapshot_map;
7071
pub mod sorted_map;
7172
pub mod sso;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
//! A version of [`std::vec::ExtractIf`] for `SmallVec`.
2+
//!
3+
//! Remove once https://github.com/servo/rust-smallvec/issues/360 is solved.
4+
5+
use core::slice;
6+
use std::ptr;
7+
8+
use smallvec::{Array, SmallVec};
9+
10+
/// An iterator for [`SmallVec`] which uses a closure to determine if an element should be removed.
11+
#[must_use = "iterators are lazy and do nothing unless consumed"]
12+
pub struct ExtractIf<'a, A, F>
13+
where
14+
A: Array,
15+
{
16+
vec: &'a mut SmallVec<A>,
17+
/// The index of the item that will be inspected by the next call to `next`.
18+
idx: usize,
19+
/// The number of items that have been drained (removed) thus far.
20+
del: usize,
21+
/// The original length of `vec` prior to draining.
22+
old_len: usize,
23+
/// The filter test predicate.
24+
pred: F,
25+
}
26+
27+
impl<'a, A, F> ExtractIf<'a, A, F>
28+
where
29+
A: Array,
30+
{
31+
pub fn new(vec: &'a mut SmallVec<A>, filter: F) -> Self {
32+
let old_len = vec.len();
33+
34+
// Guard against us getting leaked (leak amplification)
35+
unsafe {
36+
vec.set_len(0);
37+
}
38+
39+
ExtractIf { vec, idx: 0, del: 0, old_len, pred: filter }
40+
}
41+
}
42+
43+
impl<A, F> Iterator for ExtractIf<'_, A, F>
44+
where
45+
A: Array,
46+
F: FnMut(&mut A::Item) -> bool,
47+
{
48+
type Item = A::Item;
49+
50+
fn next(&mut self) -> Option<Self::Item> {
51+
unsafe {
52+
while self.idx < self.old_len {
53+
let i = self.idx;
54+
let v = slice::from_raw_parts_mut(self.vec.as_mut_ptr(), self.old_len);
55+
let drained = (self.pred)(&mut v[i]);
56+
// Update the index *after* the predicate is called. If the index
57+
// is updated prior and the predicate panics, the element at this
58+
// index would be leaked.
59+
self.idx += 1;
60+
if drained {
61+
self.del += 1;
62+
return Some(ptr::read(&v[i]));
63+
} else if self.del > 0 {
64+
let del = self.del;
65+
let src: *const A::Item = &v[i];
66+
let dst: *mut A::Item = &mut v[i - del];
67+
ptr::copy_nonoverlapping(src, dst, 1);
68+
}
69+
}
70+
None
71+
}
72+
}
73+
74+
fn size_hint(&self) -> (usize, Option<usize>) {
75+
(0, Some(self.old_len - self.idx))
76+
}
77+
}
78+
79+
impl<A, F> Drop for ExtractIf<'_, A, F>
80+
where
81+
A: Array,
82+
{
83+
fn drop(&mut self) {
84+
unsafe {
85+
if self.idx < self.old_len && self.del > 0 {
86+
// This is a pretty messed up state, and there isn't really an
87+
// obviously right thing to do. We don't want to keep trying
88+
// to execute `pred`, so we just backshift all the unprocessed
89+
// elements and tell the vec that they still exist. The backshift
90+
// is required to prevent a double-drop of the last successfully
91+
// drained item prior to a panic in the predicate.
92+
let ptr = self.vec.as_mut_ptr();
93+
let src = ptr.add(self.idx);
94+
let dst = src.sub(self.del);
95+
let tail_len = self.old_len - self.idx;
96+
src.copy_to(dst, tail_len);
97+
}
98+
self.vec.set_len(self.old_len - self.del);
99+
}
100+
}
101+
}

0 commit comments

Comments
 (0)