| // 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`. |
| /// |
| /// # Examples |
| /// |
| /// ``` |
| /// # use kernel::alloc::layout::{ArrayLayout, LayoutError}; |
| /// let layout = ArrayLayout::<i32>::new(15)?; |
| /// assert_eq!(layout.len(), 15); |
| /// |
| /// // Errors because `len * size_of::<T>()` overflows. |
| /// let layout = ArrayLayout::<i32>::new(isize::MAX as usize); |
| /// assert!(layout.is_err()); |
| /// |
| /// // Errors because `len * size_of::<i32>() > isize::MAX`, |
| /// // even though `len < isize::MAX`. |
| /// let layout = ArrayLayout::<i32>::new(isize::MAX as usize / 2); |
| /// assert!(layout.is_err()); |
| /// |
| /// # Ok::<(), Error>(()) |
| /// ``` |
| pub const fn new(len: usize) -> Result<Self, LayoutError> { |
| match len.checked_mul(core::mem::size_of::<T>()) { |
| Some(size) if size <= 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() } |
| } |
| } |