Skip to content

Commit 345c6b4

Browse files
niklasfbluss
authored andcommitted
Mirror optimization of std::Vec::retain (rust-lang/rust#81126)
1 parent 198a403 commit 345c6b4

File tree

1 file changed

+48
-12
lines changed

1 file changed

+48
-12
lines changed

src/arrayvec.rs

+48-12
Original file line numberDiff line numberDiff line change
@@ -443,22 +443,58 @@ impl<T, const CAP: usize> ArrayVec<T, CAP> {
443443
pub fn retain<F>(&mut self, mut f: F)
444444
where F: FnMut(&mut T) -> bool
445445
{
446-
let len = self.len();
447-
let mut del = 0;
448-
{
449-
let v = &mut **self;
450-
451-
for i in 0..len {
452-
if !f(&mut v[i]) {
453-
del += 1;
454-
} else if del > 0 {
455-
v.swap(i - del, i);
446+
// Check the implementation of
447+
// https://doc.rust-lang.org/std/vec/struct.Vec.html#method.retain
448+
// for safety arguments (especially regarding panics in f and when
449+
// dropping elements). Implementation closely mirrored here.
450+
451+
let original_len = self.len();
452+
unsafe { self.set_len(0) };
453+
454+
struct BackshiftOnDrop<'a, A: Array> {
455+
v: &'a mut ArrayVec<A>,
456+
processed_len: usize,
457+
deleted_cnt: usize,
458+
original_len: usize,
459+
}
460+
461+
impl<A: Array> Drop for BackshiftOnDrop<'_, A> {
462+
fn drop(&mut self) {
463+
if self.deleted_cnt > 0 {
464+
unsafe {
465+
ptr::copy(
466+
self.v.as_ptr().add(self.processed_len),
467+
self.v.as_mut_ptr().add(self.processed_len - self.deleted_cnt),
468+
self.original_len - self.processed_len
469+
);
470+
}
471+
}
472+
unsafe {
473+
self.v.set_len(self.original_len - self.deleted_cnt);
456474
}
457475
}
458476
}
459-
if del > 0 {
460-
self.drain(len - del..);
477+
478+
let mut g = BackshiftOnDrop { v: self, processed_len: 0, deleted_cnt: 0, original_len };
479+
480+
while g.processed_len < original_len {
481+
let cur = unsafe { &mut *g.v.as_mut_ptr().add(g.processed_len) };
482+
if !f(cur) {
483+
g.processed_len += 1;
484+
g.deleted_cnt += 1;
485+
unsafe { ptr::drop_in_place(cur) };
486+
continue;
487+
}
488+
if g.deleted_cnt > 0 {
489+
unsafe {
490+
let hole_slot = g.v.as_mut_ptr().add(g.processed_len - g.deleted_cnt);
491+
ptr::copy_nonoverlapping(cur, hole_slot, 1);
492+
}
493+
}
494+
g.processed_len += 1;
461495
}
496+
497+
drop(g);
462498
}
463499

464500
/// Set the vector’s length without dropping or moving out elements

0 commit comments

Comments
 (0)