On Tue, 1 Oct 2024 16:59:50 +0200 Danilo Krummrich <dakr@xxxxxxxxxx> wrote: > From: Benno Lossin <benno.lossin@xxxxxxxxx> > > 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. > > Signed-off-by: Benno Lossin <benno.lossin@xxxxxxxxx> > Signed-off-by: Danilo Krummrich <dakr@xxxxxxxxxx> Reviewed-by: Gary Guo <gary@xxxxxxxxxxx> > --- > rust/kernel/alloc.rs | 1 + > rust/kernel/alloc/layout.rs | 91 +++++++++++++++++++++++++++++++++++++ > 2 files changed, 92 insertions(+) > create mode 100644 rust/kernel/alloc/layout.rs > > diff --git a/rust/kernel/alloc.rs b/rust/kernel/alloc.rs > index ebe58247504f..bf143a71d53d 100644 > --- a/rust/kernel/alloc.rs > +++ b/rust/kernel/alloc.rs > @@ -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))] > diff --git a/rust/kernel/alloc/layout.rs b/rust/kernel/alloc/layout.rs > new file mode 100644 > index 000000000000..a9c987aad8fb > --- /dev/null > +++ b/rust/kernel/alloc/layout.rs > @@ -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(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 { nit: this can also be const, although this can be added when need arise (or one may use `new(...).unwrap()` in const context, since it doesn't actually generate a call to `BUG`). > + // 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() } > + } > +}