#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FieldKind {
F64, I64, I32, I16, I8, U64, U32, U16, U8, Bool, Ptr, }
impl FieldKind {
pub fn size(&self) -> usize {
match self {
FieldKind::F64 | FieldKind::I64 | FieldKind::U64 | FieldKind::Ptr => 8,
FieldKind::I32 | FieldKind::U32 => 4,
FieldKind::I16 | FieldKind::U16 => 2,
FieldKind::I8 | FieldKind::U8 | FieldKind::Bool => 1,
}
}
pub fn alignment(&self) -> usize {
self.size()
}
}
#[derive(Debug, Clone)]
pub struct FieldInfo {
pub name: String,
pub kind: FieldKind,
pub offset: usize, pub size: usize,
}
#[derive(Debug)]
pub struct StructLayout {
pub fields: Vec<FieldInfo>,
pub total_size: usize, pub heap_field_mask: u64, }
impl StructLayout {
pub fn new(fields: &[(impl AsRef<str>, FieldKind)]) -> Self {
let mut current_offset = 8; let mut field_infos = Vec::new();
let mut heap_mask: u64 = 0;
for (i, (name, kind)) in fields.iter().enumerate() {
let align = kind.alignment();
let size = kind.size();
current_offset = (current_offset + align - 1) & !(align - 1);
field_infos.push(FieldInfo {
name: name.as_ref().to_string(),
kind: *kind,
offset: current_offset,
size,
});
if *kind == FieldKind::Ptr {
heap_mask |= 1u64 << i;
}
current_offset += size;
}
let total_size = (current_offset + 7) & !7;
StructLayout {
fields: field_infos,
total_size,
heap_field_mask: heap_mask,
}
}
pub fn field_offset(&self, idx: usize) -> usize {
self.fields[idx].offset
}
pub fn field_kind(&self, idx: usize) -> FieldKind {
self.fields[idx].kind
}
pub fn total_size(&self) -> usize {
self.total_size
}
pub fn field_count(&self) -> usize {
self.fields.len()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_point_f64_f64() {
let layout = StructLayout::new(&[
("x", FieldKind::F64),
("y", FieldKind::F64),
]);
assert_eq!(layout.field_count(), 2);
assert_eq!(layout.field_offset(0), 8); assert_eq!(layout.field_offset(1), 16); assert_eq!(layout.total_size(), 24);
assert_eq!(layout.heap_field_mask, 0);
}
#[test]
fn test_mixed_alignment_padding() {
let layout = StructLayout::new(&[
("a", FieldKind::I32),
("b", FieldKind::F64),
("c", FieldKind::I8),
]);
assert_eq!(layout.field_count(), 3);
assert_eq!(layout.field_offset(0), 8); assert_eq!(layout.field_kind(0), FieldKind::I32);
assert_eq!(layout.fields[0].size, 4);
assert_eq!(layout.field_offset(1), 16); assert_eq!(layout.field_kind(1), FieldKind::F64);
assert_eq!(layout.fields[1].size, 8);
assert_eq!(layout.field_offset(2), 24); assert_eq!(layout.field_kind(2), FieldKind::I8);
assert_eq!(layout.fields[2].size, 1);
assert_eq!(layout.total_size(), 32); }
#[test]
fn test_all_field_kinds() {
let layout = StructLayout::new(&[
("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), ]);
assert_eq!(layout.field_count(), 11);
assert_eq!(layout.field_offset(0), 8);
assert_eq!(layout.field_offset(1), 16);
assert_eq!(layout.field_offset(2), 24);
assert_eq!(layout.field_offset(3), 28);
assert_eq!(layout.field_offset(4), 30);
assert_eq!(layout.field_offset(5), 32);
assert_eq!(layout.field_offset(6), 40);
assert_eq!(layout.field_offset(7), 44);
assert_eq!(layout.field_offset(8), 46);
assert_eq!(layout.field_offset(9), 47);
assert_eq!(layout.field_offset(10), 48);
assert_eq!(layout.total_size(), 56); assert_eq!(layout.heap_field_mask, 1u64 << 10);
}
#[test]
fn test_heap_field_mask_positions_1_and_3() {
let layout = StructLayout::new(&[
("a", FieldKind::I32), ("b", FieldKind::Ptr), ("c", FieldKind::F64), ("d", FieldKind::Ptr), ]);
assert_eq!(layout.heap_field_mask, 0b1010);
}
#[test]
fn test_empty_struct() {
let layout = StructLayout::new(&[] as &[(&str, FieldKind)]);
assert_eq!(layout.field_count(), 0);
assert_eq!(layout.total_size(), 8); assert_eq!(layout.heap_field_mask, 0);
}
#[test]
fn test_single_bool_field() {
let layout = StructLayout::new(&[("flag", FieldKind::Bool)]);
assert_eq!(layout.field_count(), 1);
assert_eq!(layout.field_offset(0), 8);
assert_eq!(layout.fields[0].size, 1);
assert_eq!(layout.total_size(), 16);
assert_eq!(layout.heap_field_mask, 0);
}
#[test]
fn test_all_ptr_fields() {
let layout = StructLayout::new(&[
("a", FieldKind::Ptr),
("b", FieldKind::Ptr),
("c", FieldKind::Ptr),
]);
assert_eq!(layout.field_offset(0), 8);
assert_eq!(layout.field_offset(1), 16);
assert_eq!(layout.field_offset(2), 24);
assert_eq!(layout.total_size(), 32);
assert_eq!(layout.heap_field_mask, 0b111);
}
#[test]
fn test_small_fields_packing() {
let layout = StructLayout::new(&[
("a", FieldKind::I8), ("b", FieldKind::I8), ("c", FieldKind::I8), ("d", FieldKind::I8), ]);
assert_eq!(layout.field_offset(0), 8);
assert_eq!(layout.field_offset(1), 9);
assert_eq!(layout.field_offset(2), 10);
assert_eq!(layout.field_offset(3), 11);
assert_eq!(layout.total_size(), 16); }
#[test]
fn test_field_names_preserved() {
let layout = StructLayout::new(&[
("x_coord", FieldKind::F64),
("y_coord", FieldKind::F64),
]);
assert_eq!(layout.fields[0].name, "x_coord");
assert_eq!(layout.fields[1].name, "y_coord");
}
#[test]
fn test_field_kind_size_and_alignment() {
assert_eq!(FieldKind::F64.size(), 8);
assert_eq!(FieldKind::I64.size(), 8);
assert_eq!(FieldKind::I32.size(), 4);
assert_eq!(FieldKind::I16.size(), 2);
assert_eq!(FieldKind::I8.size(), 1);
assert_eq!(FieldKind::U64.size(), 8);
assert_eq!(FieldKind::U32.size(), 4);
assert_eq!(FieldKind::U16.size(), 2);
assert_eq!(FieldKind::U8.size(), 1);
assert_eq!(FieldKind::Bool.size(), 1);
assert_eq!(FieldKind::Ptr.size(), 8);
assert_eq!(FieldKind::F64.alignment(), 8);
assert_eq!(FieldKind::I32.alignment(), 4);
assert_eq!(FieldKind::I16.alignment(), 2);
assert_eq!(FieldKind::Bool.alignment(), 1);
assert_eq!(FieldKind::Ptr.alignment(), 8);
}
}