1use crate::schema::{FieldDef, StructMode, Type};
16
17const CLASS_SIZES: [u32; 4] = [8, 4, 2, 1];
20
21#[derive(Clone, Debug, PartialEq, Eq)]
22pub enum StructLayout {
23 Fixed(FixedLayout),
24 Packed(PackedLayout),
25}
26
27impl StructLayout {
28 pub fn align(&self) -> u32 {
29 match self {
30 StructLayout::Fixed(f) => f.align,
31 StructLayout::Packed(p) => p.align,
32 }
33 }
34
35 pub fn is_packed(&self) -> bool {
36 matches!(self, StructLayout::Packed(_))
37 }
38
39 pub fn as_fixed(&self) -> &FixedLayout {
43 match self {
44 StructLayout::Fixed(f) => f,
45 StructLayout::Packed(_) => panic!("expected a fixed-layout struct, found packed"),
46 }
47 }
48
49 pub fn as_packed(&self) -> &PackedLayout {
50 match self {
51 StructLayout::Packed(p) => p,
52 StructLayout::Fixed(_) => panic!("expected a packed struct, found fixed"),
53 }
54 }
55}
56
57#[derive(Clone, Debug, PartialEq, Eq)]
60pub struct FixedLayout {
61 pub bitmap_bytes: u32,
62 pub slots: Vec<u32>,
64 pub size: u32,
66 pub align: u32,
68 pub dense: bool,
69}
70
71#[derive(Clone, Debug, PartialEq, Eq)]
77pub struct PackedLayout {
78 pub bitmap_bytes: u32,
80 pub data_start: u32,
82 pub align: u32,
84 pub fields: Vec<PackedField>,
86 pub class_masks: [u64; 4],
89}
90
91#[derive(Clone, Debug, PartialEq, Eq)]
92pub struct PackedField {
93 pub size: u32,
95 pub align: u32,
96 pub class: usize,
98 pub same_low_mask: u64,
101}
102
103impl PackedLayout {
104 pub fn field_offset(&self, bitmap: u64, pos: usize) -> u32 {
107 let f = &self.fields[pos];
108 let mut off = self.data_start;
109 for (&mask, &size) in self
111 .class_masks
112 .iter()
113 .zip(CLASS_SIZES.iter())
114 .take(f.class)
115 {
116 off += (bitmap & mask).count_ones() * size;
117 }
118 off += (bitmap & f.same_low_mask).count_ones() * f.size;
120 off
121 }
122
123 pub fn block_size(&self, bitmap: u64) -> u32 {
125 let mut off = self.data_start;
126 for (&mask, &size) in self.class_masks.iter().zip(CLASS_SIZES.iter()) {
127 off += (bitmap & mask).count_ones() * size;
128 }
129 align_up(off, self.align)
130 }
131}
132
133pub fn align_up(x: u32, align: u32) -> u32 {
134 debug_assert!(align.is_power_of_two());
135 (x + align - 1) & !(align - 1)
136}
137
138pub fn slot_size_align(ty: &Type) -> (u32, u32) {
141 match ty {
142 Type::Bool | Type::U8 | Type::I8 => (1, 1),
143 Type::U16 | Type::I16 => (2, 2),
144 Type::U32 | Type::I32 | Type::F32 | Type::Enum(_) => (4, 4),
145 Type::U64 | Type::I64 | Type::F64 => (8, 8),
146 Type::String
147 | Type::Bytes
148 | Type::List(_)
149 | Type::Struct(_)
150 | Type::Map(_, _)
151 | Type::Union(_) => (4, 4),
152 }
153}
154
155pub fn union_payload_offset(variant: &Type) -> u32 {
159 let (_, align) = slot_size_align(variant);
160 align_up(4, align)
161}
162
163pub fn map_entry_layout(key: &Type, value: &Type) -> FixedLayout {
169 let fields = [
170 FieldDef {
171 id: 0,
172 name: String::new(),
173 ty: key.clone(),
174 default: None,
175 },
176 FieldDef {
177 id: 1,
178 name: String::new(),
179 ty: value.clone(),
180 default: None,
181 },
182 ];
183 compute_fixed(&fields, true)
184}
185
186fn class_index(size: u32) -> usize {
187 match size {
188 8 => 0,
189 4 => 1,
190 2 => 2,
191 _ => 3,
192 }
193}
194
195pub fn compute(fields: &[FieldDef], mode: StructMode) -> StructLayout {
198 match mode {
199 StructMode::Packed => StructLayout::Packed(compute_packed(fields)),
200 StructMode::Dense => StructLayout::Fixed(compute_fixed(fields, true)),
201 StructMode::Sparse => StructLayout::Fixed(compute_fixed(fields, false)),
202 }
203}
204
205fn compute_fixed(fields: &[FieldDef], dense: bool) -> FixedLayout {
208 let n = fields.len();
209 let bitmap_bytes = if dense { 0 } else { (n as u32).div_ceil(8) };
210 let mut order: Vec<usize> = (0..n).collect();
211 order.sort_by_key(|&i| {
212 (
213 std::cmp::Reverse(slot_size_align(&fields[i].ty).1),
214 fields[i].id,
215 )
216 });
217 let mut cursor = bitmap_bytes;
218 let mut slots = vec![0u32; n];
219 let mut max_align = 1u32;
220 for i in order {
221 let (size, align) = slot_size_align(&fields[i].ty);
222 max_align = max_align.max(align);
223 cursor = align_up(cursor, align);
224 slots[i] = cursor;
225 cursor += size;
226 }
227 FixedLayout {
228 bitmap_bytes,
229 slots,
230 size: align_up(cursor, max_align),
231 align: max_align,
232 dense,
233 }
234}
235
236fn compute_packed(fields: &[FieldDef]) -> PackedLayout {
237 let n = fields.len();
238 debug_assert!(n <= 64, "packed structs are limited to 64 fields");
239 let bitmap_bytes = (n as u32).div_ceil(8);
240 let mut class_masks = [0u64; 4];
241 let mut sizes_aligns = Vec::with_capacity(n);
242 let mut max_align = 1u32;
243 for (p, f) in fields.iter().enumerate() {
244 let (size, align) = slot_size_align(&f.ty);
245 max_align = max_align.max(align);
246 let class = class_index(size);
247 class_masks[class] |= 1u64 << p;
248 sizes_aligns.push((size, align, class));
249 }
250 let align = max_align;
251 let data_start = align_up(bitmap_bytes, align);
252 let mut pfields = Vec::with_capacity(n);
253 for (p, &(size, align, class)) in sizes_aligns.iter().enumerate() {
254 let low_bits = if p == 0 { 0 } else { (1u64 << p) - 1 };
255 pfields.push(PackedField {
256 size,
257 align,
258 class,
259 same_low_mask: class_masks[class] & low_bits,
260 });
261 }
262 PackedLayout {
263 bitmap_bytes,
264 data_start,
265 align,
266 fields: pfields,
267 class_masks,
268 }
269}