Skip to content

Commit d6a933e

Browse files
Darksonnjannau
authored andcommitted
rust: alloc: add Vec::retain
This adds a common Vec method called `retain` that removes all elements that don't match a certain condition. Rust Binder uses it to find all processes that match a given pid. The stdlib retain method takes &T rather than &mut T and has a separate retain_mut for the &mut T case. However, this is considered an API mistake that can't be fixed now due to backwards compatibility. There's no reason for us to repeat that mistake. Signed-off-by: Alice Ryhl <[email protected]> Reviewed-by: Greg Kroah-Hartman <[email protected]> Reviewed-by: Benno Lossin <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Danilo Krummrich <[email protected]>
1 parent c7bf5b9 commit d6a933e

File tree

1 file changed

+72
-0
lines changed

1 file changed

+72
-0
lines changed

rust/kernel/alloc/kvec.rs

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -613,6 +613,29 @@ where
613613
elements: elems.iter_mut(),
614614
}
615615
}
616+
617+
/// Removes all elements that don't match the provided closure.
618+
///
619+
/// # Examples
620+
///
621+
/// ```
622+
/// let mut v = kernel::kvec![1, 2, 3, 4]?;
623+
/// v.retain(|i| *i % 2 == 0);
624+
/// assert_eq!(v, [2, 4]);
625+
/// # Ok::<(), Error>(())
626+
/// ```
627+
pub fn retain(&mut self, mut f: impl FnMut(&mut T) -> bool) {
628+
let mut num_kept = 0;
629+
let mut next_to_check = 0;
630+
while let Some(to_check) = self.get_mut(next_to_check) {
631+
if f(to_check) {
632+
self.swap(num_kept, next_to_check);
633+
num_kept += 1;
634+
}
635+
next_to_check += 1;
636+
}
637+
self.truncate(num_kept);
638+
}
616639
}
617640

618641
impl<T: Clone, A: Allocator> Vec<T, A> {
@@ -1135,3 +1158,52 @@ impl<'vec, T> Drop for DrainAll<'vec, T> {
11351158
}
11361159
}
11371160
}
1161+
1162+
#[macros::kunit_tests(rust_kvec_kunit)]
1163+
mod tests {
1164+
use super::*;
1165+
use crate::prelude::*;
1166+
1167+
#[test]
1168+
fn test_kvec_retain() {
1169+
/// Verify correctness for one specific function.
1170+
#[expect(clippy::needless_range_loop)]
1171+
fn verify(c: &[bool]) {
1172+
let mut vec1: KVec<usize> = KVec::with_capacity(c.len(), GFP_KERNEL).unwrap();
1173+
let mut vec2: KVec<usize> = KVec::with_capacity(c.len(), GFP_KERNEL).unwrap();
1174+
1175+
for i in 0..c.len() {
1176+
vec1.push_within_capacity(i).unwrap();
1177+
if c[i] {
1178+
vec2.push_within_capacity(i).unwrap();
1179+
}
1180+
}
1181+
1182+
vec1.retain(|i| c[*i]);
1183+
1184+
assert_eq!(vec1, vec2);
1185+
}
1186+
1187+
/// Add one to a binary integer represented as a boolean array.
1188+
fn add(value: &mut [bool]) {
1189+
let mut carry = true;
1190+
for v in value {
1191+
let new_v = carry != *v;
1192+
carry = carry && *v;
1193+
*v = new_v;
1194+
}
1195+
}
1196+
1197+
// This boolean array represents a function from index to boolean. We check that `retain`
1198+
// behaves correctly for all possible boolean arrays of every possible length less than
1199+
// ten.
1200+
let mut func = KVec::with_capacity(10, GFP_KERNEL).unwrap();
1201+
for len in 0..10 {
1202+
for _ in 0u32..1u32 << len {
1203+
verify(&func);
1204+
add(&mut func);
1205+
}
1206+
func.push_within_capacity(false).unwrap();
1207+
}
1208+
}
1209+
}

0 commit comments

Comments
 (0)