abi_definitions/memory/
layout.rs1use 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 const ALIGNMENT_BITS: u8 = 4;
21 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 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}