struct_audit/analysis/
padding.rs1use crate::types::{LayoutMetrics, PaddingHole, StructLayout};
2
3pub fn analyze_layout(layout: &mut StructLayout, cache_line_size: u32) {
4 #[derive(Clone)]
5 struct Span {
6 start: u64,
7 end: u64,
8 member_name: String,
9 }
10
11 let mut spans = Vec::new();
12 let mut partial = false;
13
14 for member in &layout.members {
15 let Some(member_offset) = member.offset else {
16 partial = true;
17 continue;
18 };
19 let Some(member_size) = member.size else {
20 partial = true;
21 continue;
22 };
23 if member_size == 0 {
24 continue;
25 }
26
27 spans.push(Span {
28 start: member_offset,
29 end: member_offset.saturating_add(member_size),
30 member_name: member.name.clone(),
31 });
32 }
33
34 spans.sort_by_key(|s| (s.start, s.end));
35
36 let mut padding_holes = Vec::new();
37 let mut useful_size: u64 = 0;
38
39 if spans.is_empty() {
41 let cache_line_size_u64 = cache_line_size as u64;
42 let cache_lines_spanned =
43 if layout.size > 0 { layout.size.div_ceil(cache_line_size_u64) as u32 } else { 0 };
44
45 layout.metrics = LayoutMetrics {
46 total_size: layout.size,
47 useful_size: 0,
48 padding_bytes: 0,
49 padding_percentage: 0.0,
50 cache_lines_spanned,
51 cache_line_density: 0.0,
52 padding_holes,
53 partial,
54 };
55 return;
56 }
57
58 let mut current_start = spans[0].start;
61 let mut current_end = spans[0].end;
62 let mut current_end_member: Option<String> = Some(spans[0].member_name.clone());
63
64 for span in spans.into_iter().skip(1) {
65 if span.start > current_end {
66 useful_size = useful_size.saturating_add(current_end.saturating_sub(current_start));
67
68 if !partial {
69 padding_holes.push(PaddingHole {
70 offset: current_end,
71 size: span.start - current_end,
72 after_member: current_end_member.clone(),
73 });
74 }
75
76 current_start = span.start;
77 current_end = span.end;
78 current_end_member = Some(span.member_name);
79 continue;
80 }
81
82 if span.end >= current_end {
83 current_end = span.end;
84 current_end_member = Some(span.member_name);
85 }
86 }
87
88 useful_size = useful_size.saturating_add(current_end.saturating_sub(current_start));
89
90 if !partial && current_end < layout.size {
91 padding_holes.push(PaddingHole {
92 offset: current_end,
93 size: layout.size - current_end,
94 after_member: current_end_member,
95 });
96 }
97
98 let padding_bytes: u64 = padding_holes.iter().map(|h| h.size).sum();
99 let padding_percentage =
100 if layout.size > 0 { (padding_bytes as f64 / layout.size as f64) * 100.0 } else { 0.0 };
101
102 let cache_line_size_u64 = cache_line_size as u64;
103 let cache_lines_spanned =
104 if layout.size > 0 { layout.size.div_ceil(cache_line_size_u64) as u32 } else { 0 };
105
106 let total_cache_bytes = cache_lines_spanned as u64 * cache_line_size_u64;
107 let cache_line_density = if total_cache_bytes > 0 {
108 (useful_size as f64 / total_cache_bytes as f64) * 100.0
109 } else {
110 0.0
111 };
112
113 layout.metrics = LayoutMetrics {
114 total_size: layout.size,
115 useful_size,
116 padding_bytes,
117 padding_percentage,
118 cache_lines_spanned,
119 cache_line_density,
120 padding_holes,
121 partial,
122 };
123}