Skip to content

Commit 3ad6e1d

Browse files
Darksonnjannau
authored andcommitted
rust: alloc: add Vec::insert_within_capacity
This adds a variant of Vec::insert that does not allocate memory. This makes it safe to use this function while holding a spinlock. Rust Binder uses it for the range allocator fast path. 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 c7bdf13 commit 3ad6e1d

File tree

2 files changed

+73
-1
lines changed

2 files changed

+73
-1
lines changed

rust/kernel/alloc/kvec.rs

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ use core::{
2525
};
2626

2727
mod errors;
28-
pub use self::errors::{PushError, RemoveError};
28+
pub use self::errors::{InsertError, PushError, RemoveError};
2929

3030
/// Create a [`KVec`] containing the arguments.
3131
///
@@ -361,6 +361,55 @@ where
361361
unsafe { self.inc_len(1) };
362362
}
363363

364+
/// Inserts an element at the given index in the [`Vec`] instance.
365+
///
366+
/// Fails if the vector does not have capacity for the new element. Panics if the index is out
367+
/// of bounds.
368+
///
369+
/// # Examples
370+
///
371+
/// ```
372+
/// use kernel::alloc::kvec::InsertError;
373+
///
374+
/// let mut v = KVec::with_capacity(5, GFP_KERNEL)?;
375+
/// for i in 0..5 {
376+
/// v.insert_within_capacity(0, i)?;
377+
/// }
378+
///
379+
/// assert!(matches!(v.insert_within_capacity(0, 5), Err(InsertError::OutOfCapacity(_))));
380+
/// assert!(matches!(v.insert_within_capacity(1000, 5), Err(InsertError::IndexOutOfBounds(_))));
381+
/// assert_eq!(v, [4, 3, 2, 1, 0]);
382+
/// # Ok::<(), Error>(())
383+
/// ```
384+
pub fn insert_within_capacity(
385+
&mut self,
386+
index: usize,
387+
element: T,
388+
) -> Result<(), InsertError<T>> {
389+
let len = self.len();
390+
if index > len {
391+
return Err(InsertError::IndexOutOfBounds(element));
392+
}
393+
394+
if len >= self.capacity() {
395+
return Err(InsertError::OutOfCapacity(element));
396+
}
397+
398+
// SAFETY: This is in bounds since `index <= len < capacity`.
399+
let p = unsafe { self.as_mut_ptr().add(index) };
400+
// INVARIANT: This breaks the Vec invariants by making `index` contain an invalid element,
401+
// but we restore the invariants below.
402+
// SAFETY: Both the src and dst ranges end no later than one element after the length.
403+
// Since the length is less than the capacity, both ranges are in bounds of the allocation.
404+
unsafe { ptr::copy(p, p.add(1), len - index) };
405+
// INVARIANT: This restores the Vec invariants.
406+
// SAFETY: The pointer is in-bounds of the allocation.
407+
unsafe { ptr::write(p, element) };
408+
// SAFETY: Index `len` contains a valid element due to the above copy and write.
409+
unsafe { self.inc_len(1) };
410+
Ok(())
411+
}
412+
364413
/// Removes the last element from a vector and returns it, or `None` if it is empty.
365414
///
366415
/// # Examples

rust/kernel/alloc/kvec/errors.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,26 @@ impl From<RemoveError> for Error {
3636
EINVAL
3737
}
3838
}
39+
40+
/// Error type for [`Vec::insert_within_capacity`].
41+
pub enum InsertError<T> {
42+
/// The value could not be inserted because the index is out of bounds.
43+
IndexOutOfBounds(T),
44+
/// The value could not be inserted because the vector is out of capacity.
45+
OutOfCapacity(T),
46+
}
47+
48+
impl<T> Debug for InsertError<T> {
49+
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
50+
match self {
51+
InsertError::IndexOutOfBounds(_) => write!(f, "Index out of bounds"),
52+
InsertError::OutOfCapacity(_) => write!(f, "Not enough capacity"),
53+
}
54+
}
55+
}
56+
57+
impl<T> From<InsertError<T>> for Error {
58+
fn from(_: InsertError<T>) -> Error {
59+
EINVAL
60+
}
61+
}

0 commit comments

Comments
 (0)