1use crate::snapshot::ActiveAllocation;
4use std::collections::HashSet;
5
6use super::MemoryView;
7
8#[derive(Debug, Clone)]
10pub enum ViewFilter {
11 ByThread(u64),
13 ByType(String),
15 BySizeRange(usize, usize),
17 ByAddressRange(u64, u64),
19 ByMinSize(usize),
21 ByMaxSize(usize),
23}
24
25pub struct FilterBuilder<'a> {
27 view: &'a MemoryView,
28 filters: Vec<ViewFilter>,
29}
30
31impl<'a> FilterBuilder<'a> {
32 pub fn new(view: &'a MemoryView) -> Self {
34 Self {
35 view,
36 filters: Vec::new(),
37 }
38 }
39
40 pub fn by_thread(mut self, thread_id: u64) -> Self {
42 self.filters.push(ViewFilter::ByThread(thread_id));
43 self
44 }
45
46 pub fn by_type(mut self, type_name: &str) -> Self {
48 self.filters.push(ViewFilter::ByType(type_name.to_string()));
49 self
50 }
51
52 pub fn by_size_range(mut self, min: usize, max: usize) -> Self {
54 self.filters.push(ViewFilter::BySizeRange(min, max));
55 self
56 }
57
58 pub fn by_address_range(mut self, min: u64, max: u64) -> Self {
60 self.filters.push(ViewFilter::ByAddressRange(min, max));
61 self
62 }
63
64 pub fn by_min_size(mut self, min: usize) -> Self {
66 self.filters.push(ViewFilter::ByMinSize(min));
67 self
68 }
69
70 pub fn by_max_size(mut self, max: usize) -> Self {
72 self.filters.push(ViewFilter::ByMaxSize(max));
73 self
74 }
75
76 pub fn push(mut self, filter: ViewFilter) -> Self {
78 self.filters.push(filter);
79 self
80 }
81
82 pub fn apply(&self) -> Vec<&ActiveAllocation> {
84 let mut result: Vec<_> = self.view.allocations();
85
86 for filter in &self.filters {
87 result = match filter {
88 ViewFilter::ByThread(tid) => {
89 result.into_iter().filter(|a| a.thread_id == *tid).collect()
90 }
91 ViewFilter::ByType(t) => result
92 .into_iter()
93 .filter(|a| a.type_name.as_ref().map(|n| n.contains(t)).unwrap_or(false))
94 .collect(),
95 ViewFilter::BySizeRange(min, max) => result
96 .into_iter()
97 .filter(|a| a.size >= *min && a.size <= *max)
98 .collect(),
99 ViewFilter::ByAddressRange(min, max) => result
100 .into_iter()
101 .filter(|a| {
102 a.ptr
103 .map(|p| p as u64 >= *min && p as u64 <= *max)
104 .unwrap_or(false)
105 })
106 .collect(),
107 ViewFilter::ByMinSize(min) => {
108 result.into_iter().filter(|a| a.size >= *min).collect()
109 }
110 ViewFilter::ByMaxSize(max) => {
111 result.into_iter().filter(|a| a.size <= *max).collect()
112 }
113 };
114 }
115
116 result
117 }
118
119 pub fn count(&self) -> usize {
121 self.apply().len()
122 }
123
124 pub fn total_size(&self) -> usize {
126 self.apply().iter().map(|a| a.size).sum()
127 }
128
129 pub fn thread_ids(&self) -> Vec<u64> {
131 let allocs = self.apply();
132 let ids: HashSet<u64> = allocs.iter().map(|a| a.thread_id).collect();
133 ids.into_iter().collect()
134 }
135
136 pub fn type_names(&self) -> Vec<String> {
138 let allocs = self.apply();
139 let types: HashSet<String> = allocs.iter().filter_map(|a| a.type_name.clone()).collect();
140 types.into_iter().collect()
141 }
142}
143
144#[cfg(test)]
145mod tests {
146 use super::*;
147 use crate::event_store::MemoryEvent;
148
149 #[test]
150 fn test_filter_by_thread() {
151 let events = vec![
152 MemoryEvent::allocate(0x1000, 64, 1),
153 MemoryEvent::allocate(0x2000, 128, 2),
154 ];
155 let view = MemoryView::from_events(events);
156 let builder = view.filter().by_thread(1);
157 let filtered = builder.apply();
158 assert_eq!(filtered.len(), 1);
159 }
160
161 #[test]
162 fn test_filter_by_size() {
163 let events = vec![
164 MemoryEvent::allocate(0x1000, 64, 1),
165 MemoryEvent::allocate(0x2000, 128, 1),
166 MemoryEvent::allocate(0x3000, 256, 1),
167 ];
168 let view = MemoryView::from_events(events);
169 let builder = view.filter().by_size_range(100, 200);
170 let filtered = builder.apply();
171 assert_eq!(filtered.len(), 1);
172 }
173
174 #[test]
175 fn test_filter_chain() {
176 let events = vec![
177 MemoryEvent::allocate(0x1000, 64, 1).with_type_name("Vec<i32>".to_string()),
178 MemoryEvent::allocate(0x2000, 128, 1).with_type_name("String".to_string()),
179 MemoryEvent::allocate(0x3000, 64, 2).with_type_name("Vec<i32>".to_string()),
180 ];
181 let view = MemoryView::from_events(events);
182 let builder = view.filter().by_thread(1).by_type("Vec");
183 let filtered = builder.apply();
184 assert_eq!(filtered.len(), 1);
185 }
186
187 #[test]
188 fn test_filter_by_type_no_match() {
189 let events =
190 vec![MemoryEvent::allocate(0x1000, 64, 1).with_type_name("Vec<i32>".to_string())];
191 let view = MemoryView::from_events(events);
192 let builder = view.filter().by_type("HashMap");
193 let filtered = builder.apply();
194 assert_eq!(filtered.len(), 0);
195 }
196
197 #[test]
198 fn test_filter_by_type_none_type_name() {
199 let events = vec![MemoryEvent::allocate(0x1000, 64, 1)];
200 let view = MemoryView::from_events(events);
201 let builder = view.filter().by_type("Vec");
202 let filtered = builder.apply();
203 assert_eq!(filtered.len(), 0);
204 }
205
206 #[test]
207 fn test_filter_by_address_range() {
208 let events = vec![
209 MemoryEvent::allocate(0x1000, 64, 1),
210 MemoryEvent::allocate(0x2000, 128, 1),
211 MemoryEvent::allocate(0x3000, 256, 1),
212 ];
213 let view = MemoryView::from_events(events);
214 let builder = view.filter().by_address_range(0x1500, 0x2500);
215 let filtered = builder.apply();
216 assert_eq!(filtered.len(), 1);
217 }
218
219 #[test]
220 fn test_filter_by_min_size() {
221 let events = vec![
222 MemoryEvent::allocate(0x1000, 64, 1),
223 MemoryEvent::allocate(0x2000, 128, 1),
224 MemoryEvent::allocate(0x3000, 256, 1),
225 ];
226 let view = MemoryView::from_events(events);
227 let builder = view.filter().by_min_size(100);
228 let filtered = builder.apply();
229 assert_eq!(filtered.len(), 2);
230 }
231
232 #[test]
233 fn test_filter_by_max_size() {
234 let events = vec![
235 MemoryEvent::allocate(0x1000, 64, 1),
236 MemoryEvent::allocate(0x2000, 128, 1),
237 MemoryEvent::allocate(0x3000, 256, 1),
238 ];
239 let view = MemoryView::from_events(events);
240 let builder = view.filter().by_max_size(100);
241 let filtered = builder.apply();
242 assert_eq!(filtered.len(), 1);
243 }
244
245 #[test]
246 fn test_filter_push() {
247 let events = vec![
248 MemoryEvent::allocate(0x1000, 64, 1),
249 MemoryEvent::allocate(0x2000, 128, 2),
250 ];
251 let view = MemoryView::from_events(events);
252 let builder = view.filter().push(ViewFilter::ByThread(1));
253 let filtered = builder.apply();
254 assert_eq!(filtered.len(), 1);
255 }
256
257 #[test]
258 fn test_filter_count() {
259 let events = vec![
260 MemoryEvent::allocate(0x1000, 64, 1),
261 MemoryEvent::allocate(0x2000, 128, 2),
262 ];
263 let view = MemoryView::from_events(events);
264 let count = view.filter().by_thread(1).count();
265 assert_eq!(count, 1);
266 }
267
268 #[test]
269 fn test_filter_total_size() {
270 let events = vec![
271 MemoryEvent::allocate(0x1000, 64, 1),
272 MemoryEvent::allocate(0x2000, 128, 1),
273 ];
274 let view = MemoryView::from_events(events);
275 let total = view.filter().by_thread(1).total_size();
276 assert_eq!(total, 192);
277 }
278
279 #[test]
280 fn test_filter_thread_ids() {
281 let events = vec![
282 MemoryEvent::allocate(0x1000, 64, 1),
283 MemoryEvent::allocate(0x2000, 128, 2),
284 MemoryEvent::allocate(0x3000, 256, 1),
285 ];
286 let view = MemoryView::from_events(events);
287 let ids = view.filter().thread_ids();
288 assert_eq!(ids.len(), 2);
289 assert!(ids.contains(&1));
290 assert!(ids.contains(&2));
291 }
292
293 #[test]
294 fn test_filter_type_names() {
295 let events = vec![
296 MemoryEvent::allocate(0x1000, 64, 1).with_type_name("Vec<i32>".to_string()),
297 MemoryEvent::allocate(0x2000, 128, 2).with_type_name("String".to_string()),
298 ];
299 let view = MemoryView::from_events(events);
300 let types = view.filter().type_names();
301 assert_eq!(types.len(), 2);
302 assert!(types.contains(&"Vec<i32>".to_string()));
303 assert!(types.contains(&"String".to_string()));
304 }
305
306 #[test]
307 fn test_filter_empty_view() {
308 let events: Vec<MemoryEvent> = vec![];
309 let view = MemoryView::from_events(events);
310 let binding = view.filter().by_thread(1);
311 let filtered = binding.apply();
312 assert_eq!(filtered.len(), 0);
313 }
314
315 #[test]
316 fn test_filter_no_filters() {
317 let events = vec![
318 MemoryEvent::allocate(0x1000, 64, 1),
319 MemoryEvent::allocate(0x2000, 128, 2),
320 ];
321 let view = MemoryView::from_events(events);
322 let binding = view.filter();
323 let filtered = binding.apply();
324 assert_eq!(filtered.len(), 2);
325 }
326
327 #[test]
328 fn test_filter_multiple_chained() {
329 let events = vec![
330 MemoryEvent::allocate(0x1000, 64, 1).with_type_name("Vec<i32>".to_string()),
331 MemoryEvent::allocate(0x2000, 128, 1).with_type_name("String".to_string()),
332 MemoryEvent::allocate(0x3000, 256, 1).with_type_name("Vec<i32>".to_string()),
333 MemoryEvent::allocate(0x4000, 64, 2).with_type_name("Vec<i32>".to_string()),
334 ];
335 let view = MemoryView::from_events(events);
336 let binding = view.filter().by_thread(1).by_type("Vec").by_max_size(100);
337 let filtered = binding.apply();
338 assert_eq!(filtered.len(), 1);
339 }
340}