dungeon_cell/
layout.rs

1//! Compile time layout description.
2
3mod alignment;
4mod size;
5
6use core::marker::PhantomData;
7use core::mem;
8
9use crate::compile_time::{
10    const_usize_assert, AlignmentSmallerOrEqualTo, SizeSmallerOrEqualTo,
11    UsizeAssert,
12};
13use crate::marker_traits::{IsAlignment, IsLayout, IsSize};
14
15pub use alignment::Alignment;
16pub use size::Size;
17
18/// Combination of size and alignment.
19///
20/// This is the type to use where a [`IsLayout`] generic is required.
21///
22/// Unlike the [`std::alloc::Layout`], this type is for layouts known at compile time.
23/// Additionally, this type doesn't require size to be a multiple of alignment.
24///
25/// The layout given to a [`dungeon_cell`][crate] type determines the types it can store.
26/// To find the layout for a given set of types the
27/// [`layout_for!()`][crate::layout_for] macro can be used.
28///
29/// # Examples
30///
31/// ```
32/// use dungeon_cell::layout::{Layout, Size, Alignment};
33///
34/// type Test = Layout<Size<4>, Alignment<2>>;
35///
36/// assert_eq!(Test::size(), 4);
37/// assert_eq!(Test::alignment(), 2);
38/// ```
39#[derive(Copy, Clone)]
40pub struct Layout<Size: IsSize, Alignment: IsAlignment> {
41    _phantom: PhantomData<(Size, Alignment)>,
42}
43
44impl<Size: IsSize, Alignment: IsAlignment> IsLayout
45    for Layout<Size, Alignment>
46{
47    type Size = Size;
48
49    type Alignment = Alignment;
50}
51
52impl<Size: IsSize, Alignment: IsAlignment> Layout<Size, Alignment> {
53    /// Check if this layout exactly matches the layout for `T`.
54    ///
55    /// # Examples
56    /// ```
57    /// use dungeon_cell::layout::{Layout, Size, Alignment};
58    ///
59    /// type TestLayout = Layout<Size<4>, Alignment<4>>;
60    ///
61    /// assert!(TestLayout::is_layout_of::<i32>());
62    /// assert!(!TestLayout::is_layout_of::<i16>());
63    /// ```
64    #[inline]
65    pub const fn is_layout_of<T>() -> bool {
66        (mem::size_of::<T>() == Size::VALUE)
67            && (mem::align_of::<T>() == Alignment::VALUE)
68    }
69
70    /// Size in bytes.
71    ///
72    /// This is the value of [`S::VALUE`][crate::marker_traits::IsSize::VALUE].
73    ///
74    /// # Examples
75    /// ```
76    /// use dungeon_cell::layout_for;
77    ///
78    /// assert_eq!(<layout_for!(String)>::size(), 24);
79    /// ```
80    #[inline]
81    pub const fn size() -> usize {
82        Size::VALUE
83    }
84
85    /// Alignment in bytes.
86    ///
87    /// This is the value of [`Alignment::VALUE`][crate::marker_traits::IsAlignment::VALUE].
88    ///
89    /// # Examples
90    /// ```
91    /// use dungeon_cell::layout_for;
92    ///
93    /// assert_eq!(<layout_for!(String)>::alignment(), 8);
94    /// ```
95    #[inline]
96    pub const fn alignment() -> usize {
97        Alignment::VALUE
98    }
99
100    /// Create a [`std::alloc::Layout`] with the size and alignment of this layout.
101    ///
102    /// This function is available on `no_std`.
103    ///
104    /// # Examples
105    /// ```
106    /// use dungeon_cell::layout_for;
107    /// use std::alloc::Layout;
108    ///
109    /// let l: Layout = <layout_for!(String)>::to_std_layout().unwrap();
110    ///
111    /// assert_eq!(l.size(), 24);
112    /// assert_eq!(l.align(), 8);
113    /// ```
114    #[inline]
115    pub const fn to_std_layout(
116    ) -> Result<core::alloc::Layout, core::alloc::LayoutError> {
117        core::alloc::Layout::from_size_align(Size::VALUE, Alignment::VALUE)
118    }
119
120    /// Check if memory with this layout would allow a `T` to be stored.
121    ///
122    /// This checks that the size of `T` is equal or less than [`Self::size()`],
123    /// and that the alignment of `T` is equal or less than [`Self::alignment()`].
124    ///
125    /// # Examples
126    /// ```
127    /// use dungeon_cell::layout_for;
128    ///
129    /// type LayoutI32 = layout_for!(i32);
130    ///
131    /// assert!(LayoutI32::can_store::<u8>());
132    /// assert!(LayoutI32::can_store::<u16>());
133    /// assert!(LayoutI32::can_store::<i32>());
134    /// assert!(!LayoutI32::can_store::<String>());
135    /// ```
136    #[inline]
137    pub const fn can_store<T>() -> bool {
138        (mem::size_of::<T>() <= Size::VALUE)
139            && (mem::align_of::<T>() <= Alignment::VALUE)
140    }
141
142    /// Assert memory with this layout can store a value of `T`.
143    ///
144    /// This checks that the size of `T` is equal or less than [`Self::size()`],
145    /// and that the alignment of `T` is equal or less than [`Self::alignment()`].
146    ///
147    /// Unsafe code can use the above if this function returns.
148    ///
149    /// This function uses the assert system in [`compile_time`][crate::compile_time]
150    /// which means by default it will cause a compile time error if the assertion fails.
151    ///
152    /// # Examples
153    /// ```
154    /// use dungeon_cell::layout_for;
155    ///
156    /// // a i16 is smaller than a i32
157    /// <layout_for!(i32)>::assert_can_store::<i16>();
158    /// ```
159    /// ```compile_fail
160    /// use dungeon_cell::layout_for;
161    ///
162    /// // a i32 is bigger than a i16
163    /// <layout_for!(i16)>::assert_can_store::<i32>();
164    /// ```
165    #[track_caller]
166    pub fn assert_can_store<T>() {
167        SizeSmallerOrEqualTo::<T, Size>::assert();
168        AlignmentSmallerOrEqualTo::<T, Alignment>::assert();
169    }
170
171    /// Const form of [`Self::assert_can_store()`].
172    ///
173    /// Prefer using [`Self::assert_can_store()`] if possible because it can give
174    /// better error messages.
175    ///
176    /// # Examples
177    /// ```
178    /// use dungeon_cell::layout_for;
179    ///
180    /// const fn test() {
181    ///     // a i16 is smaller than a i32
182    ///     <layout_for!(i32)>::const_assert_can_store::<i16>();
183    /// }
184    ///
185    /// test();
186    /// ```
187    /// ```compile_fail
188    /// use dungeon_cell::layout_for;
189    ///
190    /// const fn test() {
191    ///     // a i32 is bigger than a i16
192    ///     <layout_for!(i16)>::const_assert_can_store::<i32>();
193    /// }
194    ///
195    /// test();
196    /// ```
197    #[track_caller]
198    pub const fn const_assert_can_store<T>() {
199        const_usize_assert::<SizeSmallerOrEqualTo<T, Size>>();
200        const_usize_assert::<AlignmentSmallerOrEqualTo<T, Alignment>>();
201    }
202}
203
204/// Helper to get [`Layout`] from a generic bounded by [`IsLayout`].
205pub type LayoutType<L> =
206    Layout<<L as IsLayout>::Size, <L as IsLayout>::Alignment>;
207
208/// Calculate the [`Layout`] needed to store a set of types.
209///
210/// This macro expects input of the form `layout_for!(Type1, Type2, Type3)`.
211/// The created [`Layout`] type will have a size equal to the maximum size
212/// of the given types, and will have an alignment equal to the maximum alignment
213/// of the given types. If you want the layout for the combination of types `Type1`,
214/// `Type2`, `Type3` as one value then use `layout_for!((Type1, Type2, Type3))`.
215///
216/// # Examples
217/// ```
218/// use dungeon_cell::layout_for;
219///
220/// // sizes
221///
222/// assert_eq!(<layout_for!()>::size(), 0);
223/// assert_eq!(<layout_for!(())>::size(), 0);
224///
225/// assert_eq!(<layout_for!(u8)>::size(), 1);
226/// assert_eq!(<layout_for!(u8, i8)>::size(), 1);
227///
228/// assert_eq!(<layout_for!(u32)>::size(), 4);
229/// assert_eq!(<layout_for!(u32, i32, u8, i16, ())>::size(), 4);
230///
231/// assert_eq!(<layout_for!(u32, u8, String, &'static str, ())>::size(), 24);
232///
233/// assert_eq!(<layout_for!([u8; 100], [i32; 5], String)>::size(), 100);
234///
235///
236/// // alignments
237///
238/// assert_eq!(<layout_for!()>::alignment(), 1);
239/// assert_eq!(<layout_for!(u8)>::alignment(), 1);
240///
241/// assert_eq!(<layout_for!(i32)>::alignment(), 4);
242///
243/// assert_eq!(<layout_for!([u8; 100], [i32; 5], String)>::alignment(), 8);
244/// ```
245///
246/// # Example Expansion
247/// ```
248/// # use dungeon_cell::layout_for;
249/// # type __ =
250/// layout_for!(i32, u8)
251/// # ;
252/// ```
253/// expands to
254/// ```
255/// # type __ =
256/// ::dungeon_cell::layout::Layout<
257///     ::dungeon_cell::layout::Size<
258///         {
259///             let mut max = 0;
260///
261///             let size = ::core::mem::size_of::<i32>();
262///             if size > max {
263///                 max = size;
264///             }
265///
266///             let size = ::core::mem::size_of::<u8>();
267///             if size > max {
268///                 max = size;
269///             }
270///
271///             max
272///         },
273///     >,
274///     ::dungeon_cell::layout::Alignment<
275///         {
276///             let mut max = 1;
277///
278///             let align = ::core::mem::align_of::<i32>();
279///             if align > max {
280///                 max = align;
281///             }
282///
283///             let align = ::core::mem::align_of::<u8>();
284///             if align > max {
285///                 max = align;
286///             }
287///
288///             max
289///         },
290///     >,
291/// >
292/// # ;
293/// ```
294#[macro_export]
295macro_rules! layout_for {
296    ($($type:ty),* $(,)?) => {
297        $crate::layout::Layout::<$crate::layout::Size<{
298            let mut max = 0;
299            $(
300                let size = ::core::mem::size_of::<$type>();
301                if size > max {
302                    max = size;
303                }
304            )*
305            max
306        }>, $crate::layout::Alignment<{
307            let mut max = 1;
308            $(
309                let align = ::core::mem::align_of::<$type>();
310                if align > max {
311                    max = align;
312                }
313            )*
314            max
315        }>>
316    };
317    ($($t:tt)*) => {
318        compile_error!("Expected input of the form `layout_for!(Type1, Type2, Type3)`")
319    }
320}
321
322#[cfg(test)]
323mod test {
324    use crate::layout::Alignment;
325
326    use super::*;
327
328    #[test]
329    fn assert_can_store() {
330        <Layout<Size<1>, Alignment<2>>>::assert_can_store::<u8>();
331        <Layout<Size<1>, Alignment<2>>>::const_assert_can_store::<u8>();
332    }
333
334    #[test]
335    fn is_layout_of() {
336        assert!(Layout::<Size<2>, Alignment<2>>::is_layout_of::<i16>());
337        assert!(Layout::<Size<1>, Alignment<1>>::is_layout_of::<u8>());
338        assert!(Layout::<Size<0>, Alignment<4>>::is_layout_of::<[i32; 0]>());
339        assert!(!Layout::<Size<0>, Alignment<4>>::is_layout_of::<[u8; 4]>());
340        assert!(!Layout::<Size<1>, Alignment<2>>::is_layout_of::<i16>());
341        assert!(!Layout::<Size<2>, Alignment<1>>::is_layout_of::<i16>());
342    }
343
344    #[test]
345    fn size() {
346        assert_eq!(Layout::<Size<0>, Alignment<1>>::size(), 0);
347        assert_eq!(Layout::<Size<1>, Alignment<1>>::size(), 1);
348        assert_eq!(Layout::<Size<2>, Alignment<1>>::size(), 2);
349    }
350
351    #[test]
352    fn alignment() {
353        assert_eq!(Layout::<Size<0>, Alignment<1>>::alignment(), 1);
354        assert_eq!(Layout::<Size<0>, Alignment<2>>::alignment(), 2);
355        assert_eq!(Layout::<Size<0>, Alignment<4>>::alignment(), 4);
356    }
357
358    #[cfg(feature = "alloc")]
359    #[test]
360    fn to_std_layout() {
361        let l = Layout::<Size<3>, Alignment<8>>::to_std_layout().unwrap();
362        assert_eq!(l.size(), 3);
363        assert_eq!(l.align(), 8);
364    }
365
366    #[test]
367    fn can_store() {
368        assert!(!Layout::<Size<2>, Alignment<1>>::can_store::<u16>());
369        assert!(Layout::<Size<2>, Alignment<1>>::can_store::<i8>());
370
371        assert!(Layout::<Size<4>, Alignment<2>>::can_store::<i16>());
372        assert!(!Layout::<Size<4>, Alignment<2>>::can_store::<u32>());
373    }
374}