abi_definitions/memory/
layout.rs

1use core::{alloc::Layout, fmt::Debug};
2
3#[repr(transparent)]
4#[derive(Clone, Copy, PartialEq, Eq)]
5pub struct CompactLayout(usize);
6
7impl Debug for CompactLayout {
8    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
9        write!(
10            f,
11            "CompactLayout {{ size: {}, alignment: {} }}",
12            self.get_size(),
13            self.get_alignment()
14        )
15    }
16}
17
18impl CompactLayout {
19    /// Alignment is stored in the upper 4 bits in a log2 format
20    const ALIGNMENT_BITS: u8 = 4;
21    /// Size uses the remaining bits
22    const SIZE_BITS: u8 = (size_of::<usize>() as u8 * 8) - Self::ALIGNMENT_BITS;
23
24    const SIZE_SHIFT: usize = 0;
25    const ALIGNMENT_SHIFT: usize = Self::SIZE_BITS as usize;
26
27    const MAXIMUM_SIZE: usize = (1 << Self::SIZE_BITS) - 1;
28
29    const MINIMUM_ALIGNMENT: usize = 1;
30    const MAXIMUM_ALIGNMENT: usize = 1 << ((1 << Self::ALIGNMENT_BITS) - 1);
31
32    pub fn from_size_alignment(size: usize, alignment: usize) -> Option<Self> {
33        let layout = Layout::from_size_align(size, alignment).ok()?;
34        Self::from_layout(&layout)
35    }
36
37    pub const fn from_layout(layout: &Layout) -> Option<Self> {
38        let size = layout.size();
39        let alignment = layout.align();
40
41        if size > Self::MAXIMUM_SIZE
42            || alignment < Self::MINIMUM_ALIGNMENT
43            || alignment > Self::MAXIMUM_ALIGNMENT
44            || !alignment.is_power_of_two()
45        {
46            return None;
47        }
48
49        let alignment_log2 = alignment.ilog2() as usize;
50
51        let compacted = (size << Self::SIZE_SHIFT) | (alignment_log2 << Self::ALIGNMENT_SHIFT);
52        Some(Self(compacted))
53    }
54
55    pub const fn into_layout(&self) -> Option<Layout> {
56        let size = self.get_size();
57        let alignment = self.get_alignment();
58
59        match Layout::from_size_align(size, alignment) {
60            Ok(layout) => Some(layout),
61            Err(_) => None,
62        }
63    }
64
65    pub const fn get_size(&self) -> usize {
66        (self.0 >> Self::SIZE_SHIFT) & Self::MAXIMUM_SIZE
67    }
68
69    pub const fn get_alignment_log2(&self) -> usize {
70        (self.0 >> Self::ALIGNMENT_SHIFT) & ((1 << Self::ALIGNMENT_BITS) - 1)
71    }
72
73    pub const fn get_alignment(&self) -> usize {
74        1 << self.get_alignment_log2()
75    }
76
77    pub const fn to_le_bytes(&self) -> [u8; size_of::<Self>()] {
78        self.0.to_le_bytes()
79    }
80
81    pub fn from_le_bytes(bytes: [u8; size_of::<Self>()]) -> Option<Self> {
82        let layout = Self(usize::from_le_bytes(bytes));
83
84        if layout.get_size() > Self::MAXIMUM_SIZE
85            || layout.get_alignment() < Self::MINIMUM_ALIGNMENT
86            || layout.get_alignment() > Self::MAXIMUM_ALIGNMENT
87        {
88            return None;
89        }
90
91        Some(layout)
92    }
93}
94
95#[cfg(test)]
96mod tests {
97    use super::*;
98
99    #[test]
100    fn test_basic_layout_conversion() {
101        let layout = Layout::from_size_align(64, 8).unwrap();
102        let compact = CompactLayout::from_layout(&layout).unwrap();
103        let restored = compact.into_layout().unwrap();
104        assert_eq!(restored, layout);
105    }
106
107    #[test]
108    fn test_minimum_size() {
109        let layout = Layout::from_size_align(0, 1).unwrap();
110        let compact = CompactLayout::from_layout(&layout).unwrap();
111        assert_eq!(compact.get_size(), 0);
112    }
113
114    #[test]
115    fn test_maximum_size() {
116        let layout = Layout::from_size_align(CompactLayout::MAXIMUM_SIZE, 1).unwrap();
117        let compact = CompactLayout::from_layout(&layout).unwrap();
118        assert_eq!(compact.get_size(), CompactLayout::MAXIMUM_SIZE);
119    }
120
121    #[test]
122    fn test_size_overflow() {
123        let layout = Layout::from_size_align(CompactLayout::MAXIMUM_SIZE + 1, 1).unwrap();
124        assert!(CompactLayout::from_layout(&layout).is_none());
125    }
126
127    #[test]
128    fn test_minimum_alignment() {
129        let layout = Layout::from_size_align(8, CompactLayout::MINIMUM_ALIGNMENT).unwrap();
130        let compact = CompactLayout::from_layout(&layout).unwrap();
131        assert_eq!(compact.get_alignment(), CompactLayout::MINIMUM_ALIGNMENT);
132    }
133
134    #[test]
135    fn test_maximum_alignment() {
136        let layout = Layout::from_size_align(8, CompactLayout::MAXIMUM_ALIGNMENT).unwrap();
137        let compact = CompactLayout::from_layout(&layout).unwrap();
138        assert_eq!(compact.get_alignment(), CompactLayout::MAXIMUM_ALIGNMENT);
139    }
140
141    #[test]
142    fn test_alignment_overflow() {
143        let layout = Layout::from_size_align(8, CompactLayout::MAXIMUM_ALIGNMENT * 2).unwrap();
144        assert!(CompactLayout::from_layout(&layout).is_none());
145    }
146
147    #[test]
148    fn test_non_power_of_two_alignment() {
149        // Layout::from_size_align already validates power of two, so we can't directly test
150        // but we verify the check is in place
151        let layout = Layout::from_size_align(16, 8).unwrap();
152        assert!(CompactLayout::from_layout(&layout).is_some());
153    }
154
155    #[test]
156    fn test_various_alignments() {
157        for exp in 0..=(CompactLayout::ALIGNMENT_BITS as u32 * 2) {
158            let align = 1 << exp;
159            if align <= CompactLayout::MAXIMUM_ALIGNMENT {
160                let layout = Layout::from_size_align(16, align).unwrap();
161                let compact = CompactLayout::from_layout(&layout).unwrap();
162                assert_eq!(compact.get_alignment(), align);
163                assert_eq!(compact.get_alignment_log2(), exp as usize);
164            }
165        }
166    }
167
168    #[test]
169    fn test_serialization_roundtrip() {
170        let layout = Layout::from_size_align(128, 16).unwrap();
171        let compact = CompactLayout::from_layout(&layout).unwrap();
172        let bytes = compact.to_le_bytes();
173        let restored = CompactLayout::from_le_bytes(bytes).unwrap();
174        assert_eq!(compact, restored);
175        assert_eq!(restored.get_size(), 128);
176        assert_eq!(restored.get_alignment(), 16);
177    }
178
179    #[test]
180    fn test_clone_and_copy() {
181        let layout = Layout::from_size_align(32, 4).unwrap();
182        let compact = CompactLayout::from_layout(&layout).unwrap();
183        let cloned = compact.clone();
184        let copied = compact;
185        assert_eq!(compact, cloned);
186        assert_eq!(compact, copied);
187    }
188
189    #[test]
190    fn test_multiple_size_alignment_combinations() {
191        let sizes = [0, 1, 16, 256, 4096];
192        let alignments = [1, 2, 4, 8, 16, 32, 64];
193
194        for &size in &sizes {
195            for &align in &alignments {
196                if size <= CompactLayout::MAXIMUM_SIZE && align <= CompactLayout::MAXIMUM_ALIGNMENT
197                {
198                    let layout = Layout::from_size_align(size, align).unwrap();
199                    let compact = CompactLayout::from_layout(&layout).unwrap();
200                    assert_eq!(compact.get_size(), size);
201                    assert_eq!(compact.get_alignment(), align);
202                }
203            }
204        }
205    }
206}