mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-12-29 09:16:33 +00:00
rust: alloc: introduce ArrayLayout
When allocating memory for arrays using allocators, the `Layout::array` function is typically used. It returns a result, since the given size might be too big. However, `Vec` and its iterators store their allocated capacity and thus they already did check that the size is not too big. The `ArrayLayout` type provides this exact behavior, as it can be infallibly converted into a `Layout`. Instead of a `usize` capacity, `Vec` and other similar array-storing types can use `ArrayLayout` instead. Reviewed-by: Gary Guo <gary@garyguo.net> Signed-off-by: Benno Lossin <benno.lossin@proton.me> Signed-off-by: Danilo Krummrich <dakr@kernel.org> Link: https://lore.kernel.org/r/20241004154149.93856-16-dakr@kernel.org [ Formatted a few comments. - Miguel ] Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
This commit is contained in:
parent
e1044c2238
commit
9e7bbfa182
@ -5,6 +5,7 @@
|
||||
#[cfg(not(any(test, testlib)))]
|
||||
pub mod allocator;
|
||||
pub mod kbox;
|
||||
pub mod layout;
|
||||
pub mod vec_ext;
|
||||
|
||||
#[cfg(any(test, testlib))]
|
||||
|
91
rust/kernel/alloc/layout.rs
Normal file
91
rust/kernel/alloc/layout.rs
Normal file
@ -0,0 +1,91 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
//! Memory layout.
|
||||
//!
|
||||
//! Custom layout types extending or improving [`Layout`].
|
||||
|
||||
use core::{alloc::Layout, marker::PhantomData};
|
||||
|
||||
/// Error when constructing an [`ArrayLayout`].
|
||||
pub struct LayoutError;
|
||||
|
||||
/// A layout for an array `[T; n]`.
|
||||
///
|
||||
/// # Invariants
|
||||
///
|
||||
/// - `len * size_of::<T>() <= isize::MAX`.
|
||||
pub struct ArrayLayout<T> {
|
||||
len: usize,
|
||||
_phantom: PhantomData<fn() -> T>,
|
||||
}
|
||||
|
||||
impl<T> Clone for ArrayLayout<T> {
|
||||
fn clone(&self) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
impl<T> Copy for ArrayLayout<T> {}
|
||||
|
||||
const ISIZE_MAX: usize = isize::MAX as usize;
|
||||
|
||||
impl<T> ArrayLayout<T> {
|
||||
/// Creates a new layout for `[T; 0]`.
|
||||
pub const fn empty() -> Self {
|
||||
// INVARIANT: `0 * size_of::<T>() <= isize::MAX`.
|
||||
Self {
|
||||
len: 0,
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new layout for `[T; len]`.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// When `len * size_of::<T>()` overflows or when `len * size_of::<T>() > isize::MAX`.
|
||||
pub const fn new(len: usize) -> Result<Self, LayoutError> {
|
||||
match len.checked_mul(core::mem::size_of::<T>()) {
|
||||
Some(len) if len <= ISIZE_MAX => {
|
||||
// INVARIANT: We checked above that `len * size_of::<T>() <= isize::MAX`.
|
||||
Ok(Self {
|
||||
len,
|
||||
_phantom: PhantomData,
|
||||
})
|
||||
}
|
||||
_ => Err(LayoutError),
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new layout for `[T; len]`.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// `len` must be a value, for which `len * size_of::<T>() <= isize::MAX` is true.
|
||||
pub unsafe fn new_unchecked(len: usize) -> Self {
|
||||
// INVARIANT: By the safety requirements of this function
|
||||
// `len * size_of::<T>() <= isize::MAX`.
|
||||
Self {
|
||||
len,
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the number of array elements represented by this layout.
|
||||
pub const fn len(&self) -> usize {
|
||||
self.len
|
||||
}
|
||||
|
||||
/// Returns `true` when no array elements are represented by this layout.
|
||||
pub const fn is_empty(&self) -> bool {
|
||||
self.len == 0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<ArrayLayout<T>> for Layout {
|
||||
fn from(value: ArrayLayout<T>) -> Self {
|
||||
let res = Layout::array::<T>(value.len);
|
||||
// SAFETY: By the type invariant of `ArrayLayout` we have
|
||||
// `len * size_of::<T>() <= isize::MAX` and thus the result must be `Ok`.
|
||||
unsafe { res.unwrap_unchecked() }
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user