ghostscope_protocol/
trace_context.rs1use crate::type_info::TypeInfo;
7use serde::{Deserialize, Serialize};
8
9#[derive(Debug, Clone, Serialize, Deserialize)]
11pub struct TraceContext {
12 pub strings: Vec<String>,
14
15 pub types: Vec<TypeInfo>,
17
18 pub variable_names: Vec<String>,
20}
21
22impl TraceContext {
23 pub fn new() -> Self {
25 Self {
26 strings: Vec::new(),
27 types: Vec::new(),
28 variable_names: Vec::new(),
29 }
30 }
31
32 pub fn add_string(&mut self, s: String) -> u16 {
34 if let Some(index) = self.strings.iter().position(|existing| existing == &s) {
36 return index as u16;
37 }
38
39 let index = self.strings.len();
40 if index > u16::MAX as usize {
41 panic!("String table overflow: too many strings");
42 }
43
44 self.strings.push(s);
45 index as u16
46 }
47
48 pub fn add_type(&mut self, type_info: TypeInfo) -> u16 {
50 let index = self.types.len();
53 if index > u16::MAX as usize {
54 panic!("Type table overflow: too many types");
55 }
56
57 tracing::debug!("Adding type at index {}: {:#?}", index, type_info);
59
60 self.types.push(type_info);
61 index as u16
62 }
63
64 pub fn add_variable_name(&mut self, name: String) -> u16 {
66 if let Some(index) = self
68 .variable_names
69 .iter()
70 .position(|existing| existing == &name)
71 {
72 return index as u16;
73 }
74
75 let index = self.variable_names.len();
76 if index > u16::MAX as usize {
77 panic!("Variable name table overflow: too many variable names");
78 }
79
80 self.variable_names.push(name);
81 index as u16
82 }
83
84 pub fn get_string(&self, index: u16) -> Option<&str> {
86 self.strings.get(index as usize).map(|s| s.as_str())
87 }
88
89 pub fn get_type(&self, index: u16) -> Option<&TypeInfo> {
91 self.types.get(index as usize)
92 }
93
94 pub fn get_variable_name(&self, index: u16) -> Option<&str> {
96 self.variable_names.get(index as usize).map(|s| s.as_str())
97 }
98
99 pub fn string_count(&self) -> usize {
101 self.strings.len()
102 }
103
104 pub fn type_count(&self) -> usize {
106 self.types.len()
107 }
108
109 pub fn variable_name_count(&self) -> usize {
111 self.variable_names.len()
112 }
113
114 pub fn clear(&mut self) {
116 self.strings.clear();
117 self.types.clear();
118 self.variable_names.clear();
119 }
120
121 pub fn estimated_memory_usage(&self) -> usize {
123 let strings_size: usize = self.strings.iter().map(|s| s.len()).sum();
124 let variable_names_size: usize = self.variable_names.iter().map(|s| s.len()).sum();
125
126 let types_size = self.types.len() * 100;
129
130 strings_size + variable_names_size + types_size
131 }
132}
133
134impl Default for TraceContext {
135 fn default() -> Self {
136 Self::new()
137 }
138}
139
140#[cfg(test)]
141mod tests {
142 use super::*;
143 use crate::type_info::{StructMember, TypeInfo};
144
145 #[test]
146 fn test_trace_context_strings() {
147 let mut ctx = TraceContext::new();
148
149 let idx1 = ctx.add_string("Hello".to_string());
150 let idx2 = ctx.add_string("World".to_string());
151 let idx3 = ctx.add_string("Hello".to_string()); assert_eq!(idx1, 0);
154 assert_eq!(idx2, 1);
155 assert_eq!(idx3, 0); assert_eq!(ctx.get_string(idx1), Some("Hello"));
158 assert_eq!(ctx.get_string(idx2), Some("World"));
159 assert_eq!(ctx.string_count(), 2);
160 }
161
162 #[test]
163 fn test_trace_context_variable_names() {
164 let mut ctx = TraceContext::new();
165
166 let idx1 = ctx.add_variable_name("person".to_string());
167 let idx2 = ctx.add_variable_name("name".to_string());
168 let idx3 = ctx.add_variable_name("person".to_string()); assert_eq!(idx1, 0);
171 assert_eq!(idx2, 1);
172 assert_eq!(idx3, 0); assert_eq!(ctx.get_variable_name(idx1), Some("person"));
175 assert_eq!(ctx.get_variable_name(idx2), Some("name"));
176 assert_eq!(ctx.variable_name_count(), 2);
177 }
178
179 #[test]
180 fn test_trace_context_types() {
181 let mut ctx = TraceContext::new();
182
183 let type1 = TypeInfo::BaseType {
184 name: "int".to_string(),
185 size: 4,
186 encoding: gimli::constants::DW_ATE_signed.0 as u16,
187 };
188
189 let type2 = TypeInfo::StructType {
190 name: "Person".to_string(),
191 size: 32,
192 members: vec![StructMember {
193 name: "name".to_string(),
194 member_type: TypeInfo::BaseType {
195 name: "char".to_string(),
196 size: 1,
197 encoding: gimli::constants::DW_ATE_signed_char.0 as u16,
198 },
199 offset: 0,
200 bit_offset: None,
201 bit_size: None,
202 }],
203 };
204
205 let idx1 = ctx.add_type(type1.clone());
206 let idx2 = ctx.add_type(type2.clone());
207
208 assert_eq!(idx1, 0);
209 assert_eq!(idx2, 1);
210
211 assert_eq!(ctx.get_type(idx1), Some(&type1));
212 assert_eq!(ctx.get_type(idx2), Some(&type2));
213 assert_eq!(ctx.type_count(), 2);
214 }
215
216 #[test]
217 fn test_trace_context_combined() {
218 let mut ctx = TraceContext::new();
219
220 let str_idx = ctx.add_string("format: {} = {}".to_string());
221 let var_idx = ctx.add_variable_name("person.name".to_string());
222 let type_idx = ctx.add_type(TypeInfo::BaseType {
223 name: "char[32]".to_string(),
224 size: 32,
225 encoding: gimli::constants::DW_ATE_signed_char.0 as u16,
226 });
227
228 assert_eq!(str_idx, 0);
229 assert_eq!(var_idx, 0);
230 assert_eq!(type_idx, 0);
231
232 assert_eq!(ctx.string_count(), 1);
234 assert_eq!(ctx.variable_name_count(), 1);
235 assert_eq!(ctx.type_count(), 1);
236 }
237
238 #[test]
239 fn test_trace_context_memory_usage() {
240 let mut ctx = TraceContext::new();
241
242 ctx.add_string("Hello World".to_string()); ctx.add_variable_name("variable".to_string()); ctx.add_type(TypeInfo::BaseType {
245 name: "int".to_string(),
246 size: 4,
247 encoding: gimli::constants::DW_ATE_signed.0 as u16,
248 }); let usage = ctx.estimated_memory_usage();
251 assert!(usage >= 119); }
253}