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}