Skip to content

Commit 2d953ef

Browse files
committed
Vec: add retain, retain_mut.
1 parent f32d5d9 commit 2d953ef

File tree

1 file changed

+156
-0
lines changed

1 file changed

+156
-0
lines changed

src/vec.rs

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -667,6 +667,162 @@ impl<T, const N: usize> Vec<T, N> {
667667
ret
668668
}
669669
}
670+
671+
/// Retains only the elements specified by the predicate.
672+
///
673+
/// In other words, remove all elements `e` for which `f(&e)` returns `false`.
674+
/// This method operates in place, visiting each element exactly once in the
675+
/// original order, and preserves the order of the retained elements.
676+
///
677+
/// # Examples
678+
///
679+
/// ```
680+
/// use heapless::Vec;
681+
///
682+
/// let mut vec: Vec<_, 8> = Vec::from_slice(&[1, 2, 3, 4]).unwrap();
683+
/// vec.retain(|&x| x % 2 == 0);
684+
/// assert_eq!(vec, [2, 4]);
685+
/// ```
686+
///
687+
/// Because the elements are visited exactly once in the original order,
688+
/// external state may be used to decide which elements to keep.
689+
///
690+
/// ```
691+
/// use heapless::Vec;
692+
///
693+
/// let mut vec: Vec<_, 8> = Vec::from_slice(&[1, 2, 3, 4, 5]).unwrap();
694+
/// let keep = [false, true, true, false, true];
695+
/// let mut iter = keep.iter();
696+
/// vec.retain(|_| *iter.next().unwrap());
697+
/// assert_eq!(vec, [2, 3, 5]);
698+
/// ```
699+
pub fn retain<F>(&mut self, mut f: F)
700+
where
701+
F: FnMut(&T) -> bool,
702+
{
703+
self.retain_mut(|elem| f(elem));
704+
}
705+
706+
/// Retains only the elements specified by the predicate, passing a mutable reference to it.
707+
///
708+
/// In other words, remove all elements `e` such that `f(&mut e)` returns `false`.
709+
/// This method operates in place, visiting each element exactly once in the
710+
/// original order, and preserves the order of the retained elements.
711+
///
712+
/// # Examples
713+
///
714+
/// ```
715+
/// use heapless::Vec;
716+
///
717+
/// let mut vec: Vec<_, 8> = Vec::from_slice(&[1, 2, 3, 4]).unwrap();
718+
/// vec.retain_mut(|x| if *x <= 3 {
719+
/// *x += 1;
720+
/// true
721+
/// } else {
722+
/// false
723+
/// });
724+
/// assert_eq!(vec, [2, 3, 4]);
725+
/// ```
726+
pub fn retain_mut<F>(&mut self, mut f: F)
727+
where
728+
F: FnMut(&mut T) -> bool,
729+
{
730+
let original_len = self.len();
731+
// Avoid double drop if the drop guard is not executed,
732+
// since we may make some holes during the process.
733+
unsafe { self.set_len(0) };
734+
735+
// Vec: [Kept, Kept, Hole, Hole, Hole, Hole, Unchecked, Unchecked]
736+
// |<- processed len ->| ^- next to check
737+
// |<- deleted cnt ->|
738+
// |<- original_len ->|
739+
// Kept: Elements which predicate returns true on.
740+
// Hole: Moved or dropped element slot.
741+
// Unchecked: Unchecked valid elements.
742+
//
743+
// This drop guard will be invoked when predicate or `drop` of element panicked.
744+
// It shifts unchecked elements to cover holes and `set_len` to the correct length.
745+
// In cases when predicate and `drop` never panick, it will be optimized out.
746+
struct BackshiftOnDrop<'a, T, const N: usize> {
747+
v: &'a mut Vec<T, N>,
748+
processed_len: usize,
749+
deleted_cnt: usize,
750+
original_len: usize,
751+
}
752+
753+
impl<T, const N: usize> Drop for BackshiftOnDrop<'_, T, N> {
754+
fn drop(&mut self) {
755+
if self.deleted_cnt > 0 {
756+
// SAFETY: Trailing unchecked items must be valid since we never touch them.
757+
unsafe {
758+
ptr::copy(
759+
self.v.as_ptr().add(self.processed_len),
760+
self.v
761+
.as_mut_ptr()
762+
.add(self.processed_len - self.deleted_cnt),
763+
self.original_len - self.processed_len,
764+
);
765+
}
766+
}
767+
// SAFETY: After filling holes, all items are in contiguous memory.
768+
unsafe {
769+
self.v.set_len(self.original_len - self.deleted_cnt);
770+
}
771+
}
772+
}
773+
774+
let mut g = BackshiftOnDrop {
775+
v: self,
776+
processed_len: 0,
777+
deleted_cnt: 0,
778+
original_len,
779+
};
780+
781+
fn process_loop<F, T, const N: usize, const DELETED: bool>(
782+
original_len: usize,
783+
f: &mut F,
784+
g: &mut BackshiftOnDrop<'_, T, N>,
785+
) where
786+
F: FnMut(&mut T) -> bool,
787+
{
788+
while g.processed_len != original_len {
789+
let p = g.v.as_mut_ptr();
790+
// SAFETY: Unchecked element must be valid.
791+
let cur = unsafe { &mut *p.add(g.processed_len) };
792+
if !f(cur) {
793+
// Advance early to avoid double drop if `drop_in_place` panicked.
794+
g.processed_len += 1;
795+
g.deleted_cnt += 1;
796+
// SAFETY: We never touch this element again after dropped.
797+
unsafe { ptr::drop_in_place(cur) };
798+
// We already advanced the counter.
799+
if DELETED {
800+
continue;
801+
} else {
802+
break;
803+
}
804+
}
805+
if DELETED {
806+
// SAFETY: `deleted_cnt` > 0, so the hole slot must not overlap with current element.
807+
// We use copy for move, and never touch this element again.
808+
unsafe {
809+
let hole_slot = p.add(g.processed_len - g.deleted_cnt);
810+
ptr::copy_nonoverlapping(cur, hole_slot, 1);
811+
}
812+
}
813+
g.processed_len += 1;
814+
}
815+
}
816+
817+
// Stage 1: Nothing was deleted.
818+
process_loop::<F, T, N, false>(original_len, &mut f, &mut g);
819+
820+
// Stage 2: Some elements were deleted.
821+
process_loop::<F, T, N, true>(original_len, &mut f, &mut g);
822+
823+
// All item are processed. This can be optimized to `set_len` by LLVM.
824+
drop(g);
825+
}
670826
}
671827

672828
// Trait implementations

0 commit comments

Comments
 (0)