1use serde::Serialize;
2
3#[derive(Debug, Clone, Serialize)]
4pub struct StructLayout {
5 pub name: String,
6 pub size: u64,
7 pub alignment: Option<u64>,
8 pub members: Vec<MemberLayout>,
9 pub metrics: LayoutMetrics,
10 #[serde(skip_serializing_if = "Option::is_none")]
11 pub source_location: Option<SourceLocation>,
12}
13
14#[derive(Debug, Clone, Serialize)]
15pub struct MemberLayout {
16 pub name: String,
17 pub type_name: String,
18 pub offset: Option<u64>,
19 pub size: Option<u64>,
20 #[serde(skip_serializing_if = "Option::is_none")]
21 pub bit_offset: Option<u64>,
22 #[serde(skip_serializing_if = "Option::is_none")]
23 pub bit_size: Option<u64>,
24 #[serde(skip_serializing_if = "std::ops::Not::not")]
27 pub is_atomic: bool,
28}
29
30#[derive(Debug, Clone, Serialize, Default)]
31pub struct LayoutMetrics {
32 pub total_size: u64,
33 pub useful_size: u64,
34 pub padding_bytes: u64,
35 pub padding_percentage: f64,
36 pub cache_lines_spanned: u32,
37 pub cache_line_density: f64,
38 pub padding_holes: Vec<PaddingHole>,
39 #[serde(skip_serializing_if = "std::ops::Not::not")]
40 pub partial: bool,
41 #[serde(skip_serializing_if = "Option::is_none")]
42 pub false_sharing: Option<FalseSharingAnalysis>,
43}
44
45#[derive(Debug, Clone, Serialize)]
46pub struct PaddingHole {
47 pub offset: u64,
48 pub size: u64,
49 pub after_member: Option<String>,
50}
51
52#[derive(Debug, Clone, Serialize)]
53pub struct SourceLocation {
54 pub file: String,
55 pub line: u64,
56}
57
58#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
59pub struct FalseSharingWarning {
60 pub member_a: String,
61 pub member_b: String,
62 pub cache_line: u64,
63 pub gap_bytes: i64,
66}
67
68#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
69pub struct CacheLineSpanningWarning {
70 pub member: String,
71 pub type_name: String,
72 pub offset: u64,
73 pub size: u64,
74 pub start_cache_line: u64,
75 pub end_cache_line: u64,
76 pub lines_spanned: u64,
77}
78
79#[derive(Debug, Clone, Serialize, Default, PartialEq, Eq)]
80pub struct FalseSharingAnalysis {
81 pub atomic_members: Vec<AtomicMember>,
82 pub warnings: Vec<FalseSharingWarning>,
83 #[serde(skip_serializing_if = "Vec::is_empty")]
84 pub spanning_warnings: Vec<CacheLineSpanningWarning>,
85}
86
87#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
88pub struct AtomicMember {
89 pub name: String,
90 pub type_name: String,
91 pub offset: u64,
92 pub size: u64,
93 pub cache_line: u64,
94 pub end_cache_line: u64,
95 pub spans_cache_lines: bool,
96}
97
98impl StructLayout {
99 pub fn new(name: String, size: u64, alignment: Option<u64>) -> Self {
100 Self {
101 name,
102 size,
103 alignment,
104 members: Vec::new(),
105 metrics: LayoutMetrics::default(),
106 source_location: None,
107 }
108 }
109}
110
111impl MemberLayout {
112 pub fn new(name: String, type_name: String, offset: Option<u64>, size: Option<u64>) -> Self {
113 Self { name, type_name, offset, size, bit_offset: None, bit_size: None, is_atomic: false }
114 }
115
116 pub fn with_atomic(mut self, is_atomic: bool) -> Self {
117 self.is_atomic = is_atomic;
118 self
119 }
120
121 pub fn end_offset(&self) -> Option<u64> {
122 match (self.offset, self.size) {
123 (Some(off), Some(sz)) => off.checked_add(sz),
125 _ => None,
126 }
127 }
128}
129
130#[cfg(test)]
131mod tests {
132 use super::*;
133
134 #[test]
135 fn member_end_offset_handles_overflow() {
136 let member = MemberLayout::new("a".to_string(), "u8".to_string(), Some(u64::MAX), Some(1));
137 assert_eq!(member.end_offset(), None);
138 }
139
140 #[test]
141 fn member_end_offset_ok() {
142 let member = MemberLayout::new("a".to_string(), "u8".to_string(), Some(4), Some(2));
143 assert_eq!(member.end_offset(), Some(6));
144 }
145
146 #[test]
147 fn member_with_atomic_flag() {
148 let member = MemberLayout::new("a".to_string(), "u32".to_string(), Some(0), Some(4))
149 .with_atomic(true);
150 assert!(member.is_atomic);
151 }
152
153 #[test]
154 fn struct_new_initializes_fields() {
155 let s = StructLayout::new("Foo".to_string(), 16, Some(8));
156 assert_eq!(s.name, "Foo");
157 assert_eq!(s.size, 16);
158 assert_eq!(s.alignment, Some(8));
159 assert!(s.members.is_empty());
160 }
161}