Skip to main content

kernel/alloc/
layout.rs

1// SPDX-License-Identifier: GPL-2.0
2
3//! Memory layout.
4//!
5//! Custom layout types extending or improving [`Layout`].
6
7use core::{
8    alloc::Layout,
9    marker::PhantomData, //
10};
11
12/// Error when constructing an [`ArrayLayout`].
13pub struct LayoutError;
14
15/// A layout for an array `[T; n]`.
16///
17/// # Invariants
18///
19/// - `len * size_of::<T>() <= isize::MAX`.
20pub struct ArrayLayout<T> {
21    len: usize,
22    _phantom: PhantomData<fn() -> T>,
23}
24
25impl<T> Clone for ArrayLayout<T> {
26    fn clone(&self) -> Self {
27        *self
28    }
29}
30impl<T> Copy for ArrayLayout<T> {}
31
32const ISIZE_MAX: usize = isize::MAX as usize;
33
34impl<T> ArrayLayout<T> {
35    /// Creates a new layout for `[T; 0]`.
36    pub const fn empty() -> Self {
37        // INVARIANT: `0 * size_of::<T>() <= isize::MAX`.
38        Self {
39            len: 0,
40            _phantom: PhantomData,
41        }
42    }
43
44    /// Creates a new layout for `[T; len]`.
45    ///
46    /// # Errors
47    ///
48    /// When `len * size_of::<T>()` overflows or when `len * size_of::<T>() > isize::MAX`.
49    ///
50    /// # Examples
51    ///
52    /// ```
53    /// # use kernel::alloc::layout::{
54    /// #     ArrayLayout,
55    /// #     LayoutError, //
56    /// # };
57    /// let layout = ArrayLayout::<i32>::new(15)?;
58    /// assert_eq!(layout.len(), 15);
59    ///
60    /// // Errors because `len * size_of::<T>()` overflows.
61    /// let layout = ArrayLayout::<i32>::new(isize::MAX as usize);
62    /// assert!(layout.is_err());
63    ///
64    /// // Errors because `len * size_of::<i32>() > isize::MAX`,
65    /// // even though `len < isize::MAX`.
66    /// let layout = ArrayLayout::<i32>::new(isize::MAX as usize / 2);
67    /// assert!(layout.is_err());
68    ///
69    /// # Ok::<(), Error>(())
70    /// ```
71    pub const fn new(len: usize) -> Result<Self, LayoutError> {
72        match len.checked_mul(core::mem::size_of::<T>()) {
73            Some(size) if size <= ISIZE_MAX => {
74                // INVARIANT: We checked above that `len * size_of::<T>() <= isize::MAX`.
75                Ok(Self {
76                    len,
77                    _phantom: PhantomData,
78                })
79            }
80            _ => Err(LayoutError),
81        }
82    }
83
84    /// Creates a new layout for `[T; len]`.
85    ///
86    /// # Safety
87    ///
88    /// `len` must be a value, for which `len * size_of::<T>() <= isize::MAX` is true.
89    pub const unsafe fn new_unchecked(len: usize) -> Self {
90        // INVARIANT: By the safety requirements of this function
91        // `len * size_of::<T>() <= isize::MAX`.
92        Self {
93            len,
94            _phantom: PhantomData,
95        }
96    }
97
98    /// Returns the number of array elements represented by this layout.
99    pub const fn len(&self) -> usize {
100        self.len
101    }
102
103    /// Returns `true` when no array elements are represented by this layout.
104    pub const fn is_empty(&self) -> bool {
105        self.len == 0
106    }
107
108    /// Returns the size of the [`ArrayLayout`] in bytes.
109    pub const fn size(&self) -> usize {
110        self.len() * core::mem::size_of::<T>()
111    }
112}
113
114impl<T> From<ArrayLayout<T>> for Layout {
115    fn from(value: ArrayLayout<T>) -> Self {
116        let res = Layout::array::<T>(value.len);
117        // SAFETY: By the type invariant of `ArrayLayout` we have
118        // `len * size_of::<T>() <= isize::MAX` and thus the result must be `Ok`.
119        unsafe { res.unwrap_unchecked() }
120    }
121}