1#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8pub enum FieldKind {
9 F64, I64, I32, I16, I8, U64, U32, U16, U8, Bool, Ptr, }
21
22impl FieldKind {
23 pub fn size(&self) -> usize {
24 match self {
25 FieldKind::F64 | FieldKind::I64 | FieldKind::U64 | FieldKind::Ptr => 8,
26 FieldKind::I32 | FieldKind::U32 => 4,
27 FieldKind::I16 | FieldKind::U16 => 2,
28 FieldKind::I8 | FieldKind::U8 | FieldKind::Bool => 1,
29 }
30 }
31
32 pub fn alignment(&self) -> usize {
33 self.size()
34 }
35}
36
37#[derive(Debug, Clone)]
39pub struct FieldInfo {
40 pub name: String,
41 pub kind: FieldKind,
42 pub offset: usize, pub size: usize,
44}
45
46#[derive(Debug)]
52pub struct StructLayout {
53 pub fields: Vec<FieldInfo>,
54 pub total_size: usize, pub heap_field_mask: u64, }
57
58impl StructLayout {
59 pub fn new(fields: &[(impl AsRef<str>, FieldKind)]) -> Self {
62 let mut current_offset = 8; let mut field_infos = Vec::new();
64 let mut heap_mask: u64 = 0;
65
66 for (i, (name, kind)) in fields.iter().enumerate() {
67 let align = kind.alignment();
68 let size = kind.size();
69 current_offset = (current_offset + align - 1) & !(align - 1);
71 field_infos.push(FieldInfo {
72 name: name.as_ref().to_string(),
73 kind: *kind,
74 offset: current_offset,
75 size,
76 });
77 if *kind == FieldKind::Ptr {
78 heap_mask |= 1u64 << i;
79 }
80 current_offset += size;
81 }
82 let total_size = (current_offset + 7) & !7;
84
85 StructLayout {
86 fields: field_infos,
87 total_size,
88 heap_field_mask: heap_mask,
89 }
90 }
91
92 pub fn field_offset(&self, idx: usize) -> usize {
93 self.fields[idx].offset
94 }
95
96 pub fn field_kind(&self, idx: usize) -> FieldKind {
97 self.fields[idx].kind
98 }
99
100 pub fn total_size(&self) -> usize {
101 self.total_size
102 }
103
104 pub fn field_count(&self) -> usize {
105 self.fields.len()
106 }
107}
108
109#[cfg(test)]
110mod tests {
111 use super::*;
112
113 #[test]
114 fn test_point_f64_f64() {
115 let layout = StructLayout::new(&[
117 ("x", FieldKind::F64),
118 ("y", FieldKind::F64),
119 ]);
120 assert_eq!(layout.field_count(), 2);
121 assert_eq!(layout.field_offset(0), 8); assert_eq!(layout.field_offset(1), 16); assert_eq!(layout.total_size(), 24);
124 assert_eq!(layout.heap_field_mask, 0);
125 }
126
127 #[test]
128 fn test_mixed_alignment_padding() {
129 let layout = StructLayout::new(&[
135 ("a", FieldKind::I32),
136 ("b", FieldKind::F64),
137 ("c", FieldKind::I8),
138 ]);
139 assert_eq!(layout.field_count(), 3);
140 assert_eq!(layout.field_offset(0), 8); assert_eq!(layout.field_kind(0), FieldKind::I32);
142 assert_eq!(layout.fields[0].size, 4);
143
144 assert_eq!(layout.field_offset(1), 16); assert_eq!(layout.field_kind(1), FieldKind::F64);
146 assert_eq!(layout.fields[1].size, 8);
147
148 assert_eq!(layout.field_offset(2), 24); assert_eq!(layout.field_kind(2), FieldKind::I8);
150 assert_eq!(layout.fields[2].size, 1);
151
152 assert_eq!(layout.total_size(), 32); }
154
155 #[test]
156 fn test_all_field_kinds() {
157 let layout = StructLayout::new(&[
158 ("f0", FieldKind::F64), ("f1", FieldKind::I64), ("f2", FieldKind::I32), ("f3", FieldKind::I16), ("f4", FieldKind::I8), ("f5", FieldKind::U64), ("f6", FieldKind::U32), ("f7", FieldKind::U16), ("f8", FieldKind::U8), ("f9", FieldKind::Bool), ("f10", FieldKind::Ptr), ]);
170 assert_eq!(layout.field_count(), 11);
171
172 assert_eq!(layout.field_offset(0), 8);
174 assert_eq!(layout.field_offset(1), 16);
176 assert_eq!(layout.field_offset(2), 24);
178 assert_eq!(layout.field_offset(3), 28);
180 assert_eq!(layout.field_offset(4), 30);
182 assert_eq!(layout.field_offset(5), 32);
184 assert_eq!(layout.field_offset(6), 40);
186 assert_eq!(layout.field_offset(7), 44);
188 assert_eq!(layout.field_offset(8), 46);
190 assert_eq!(layout.field_offset(9), 47);
192 assert_eq!(layout.field_offset(10), 48);
194
195 assert_eq!(layout.total_size(), 56); assert_eq!(layout.heap_field_mask, 1u64 << 10);
198 }
199
200 #[test]
201 fn test_heap_field_mask_positions_1_and_3() {
202 let layout = StructLayout::new(&[
204 ("a", FieldKind::I32), ("b", FieldKind::Ptr), ("c", FieldKind::F64), ("d", FieldKind::Ptr), ]);
209 assert_eq!(layout.heap_field_mask, 0b1010);
210 }
211
212 #[test]
213 fn test_empty_struct() {
214 let layout = StructLayout::new(&[] as &[(&str, FieldKind)]);
215 assert_eq!(layout.field_count(), 0);
216 assert_eq!(layout.total_size(), 8); assert_eq!(layout.heap_field_mask, 0);
218 }
219
220 #[test]
221 fn test_single_bool_field() {
222 let layout = StructLayout::new(&[("flag", FieldKind::Bool)]);
224 assert_eq!(layout.field_count(), 1);
225 assert_eq!(layout.field_offset(0), 8);
226 assert_eq!(layout.fields[0].size, 1);
227 assert_eq!(layout.total_size(), 16);
228 assert_eq!(layout.heap_field_mask, 0);
229 }
230
231 #[test]
232 fn test_all_ptr_fields() {
233 let layout = StructLayout::new(&[
234 ("a", FieldKind::Ptr),
235 ("b", FieldKind::Ptr),
236 ("c", FieldKind::Ptr),
237 ]);
238 assert_eq!(layout.field_offset(0), 8);
239 assert_eq!(layout.field_offset(1), 16);
240 assert_eq!(layout.field_offset(2), 24);
241 assert_eq!(layout.total_size(), 32);
242 assert_eq!(layout.heap_field_mask, 0b111);
243 }
244
245 #[test]
246 fn test_small_fields_packing() {
247 let layout = StructLayout::new(&[
249 ("a", FieldKind::I8), ("b", FieldKind::I8), ("c", FieldKind::I8), ("d", FieldKind::I8), ]);
254 assert_eq!(layout.field_offset(0), 8);
255 assert_eq!(layout.field_offset(1), 9);
256 assert_eq!(layout.field_offset(2), 10);
257 assert_eq!(layout.field_offset(3), 11);
258 assert_eq!(layout.total_size(), 16); }
260
261 #[test]
262 fn test_field_names_preserved() {
263 let layout = StructLayout::new(&[
264 ("x_coord", FieldKind::F64),
265 ("y_coord", FieldKind::F64),
266 ]);
267 assert_eq!(layout.fields[0].name, "x_coord");
268 assert_eq!(layout.fields[1].name, "y_coord");
269 }
270
271 #[test]
272 fn test_field_kind_size_and_alignment() {
273 assert_eq!(FieldKind::F64.size(), 8);
275 assert_eq!(FieldKind::I64.size(), 8);
276 assert_eq!(FieldKind::I32.size(), 4);
277 assert_eq!(FieldKind::I16.size(), 2);
278 assert_eq!(FieldKind::I8.size(), 1);
279 assert_eq!(FieldKind::U64.size(), 8);
280 assert_eq!(FieldKind::U32.size(), 4);
281 assert_eq!(FieldKind::U16.size(), 2);
282 assert_eq!(FieldKind::U8.size(), 1);
283 assert_eq!(FieldKind::Bool.size(), 1);
284 assert_eq!(FieldKind::Ptr.size(), 8);
285
286 assert_eq!(FieldKind::F64.alignment(), 8);
288 assert_eq!(FieldKind::I32.alignment(), 4);
289 assert_eq!(FieldKind::I16.alignment(), 2);
290 assert_eq!(FieldKind::Bool.alignment(), 1);
291 assert_eq!(FieldKind::Ptr.alignment(), 8);
292 }
293}