use crate::snapshot::ActiveAllocation;
use std::collections::HashSet;
use super::MemoryView;
#[derive(Debug, Clone)]
pub enum ViewFilter {
ByThread(u64),
ByType(String),
BySizeRange(usize, usize),
ByAddressRange(u64, u64),
ByMinSize(usize),
ByMaxSize(usize),
}
pub struct FilterBuilder<'a> {
view: &'a MemoryView,
filters: Vec<ViewFilter>,
}
impl<'a> FilterBuilder<'a> {
pub fn new(view: &'a MemoryView) -> Self {
Self {
view,
filters: Vec::new(),
}
}
pub fn by_thread(mut self, thread_id: u64) -> Self {
self.filters.push(ViewFilter::ByThread(thread_id));
self
}
pub fn by_type(mut self, type_name: &str) -> Self {
self.filters.push(ViewFilter::ByType(type_name.to_string()));
self
}
pub fn by_size_range(mut self, min: usize, max: usize) -> Self {
self.filters.push(ViewFilter::BySizeRange(min, max));
self
}
pub fn by_address_range(mut self, min: u64, max: u64) -> Self {
self.filters.push(ViewFilter::ByAddressRange(min, max));
self
}
pub fn by_min_size(mut self, min: usize) -> Self {
self.filters.push(ViewFilter::ByMinSize(min));
self
}
pub fn by_max_size(mut self, max: usize) -> Self {
self.filters.push(ViewFilter::ByMaxSize(max));
self
}
pub fn push(mut self, filter: ViewFilter) -> Self {
self.filters.push(filter);
self
}
pub fn apply(&self) -> Vec<&ActiveAllocation> {
let mut result: Vec<_> = self.view.allocations();
for filter in &self.filters {
result = match filter {
ViewFilter::ByThread(tid) => {
result.into_iter().filter(|a| a.thread_id == *tid).collect()
}
ViewFilter::ByType(t) => result
.into_iter()
.filter(|a| a.type_name.as_ref().map(|n| n.contains(t)).unwrap_or(false))
.collect(),
ViewFilter::BySizeRange(min, max) => result
.into_iter()
.filter(|a| a.size >= *min && a.size <= *max)
.collect(),
ViewFilter::ByAddressRange(min, max) => result
.into_iter()
.filter(|a| {
a.ptr
.map(|p| p as u64 >= *min && p as u64 <= *max)
.unwrap_or(false)
})
.collect(),
ViewFilter::ByMinSize(min) => {
result.into_iter().filter(|a| a.size >= *min).collect()
}
ViewFilter::ByMaxSize(max) => {
result.into_iter().filter(|a| a.size <= *max).collect()
}
};
}
result
}
pub fn count(&self) -> usize {
self.apply().len()
}
pub fn total_size(&self) -> usize {
self.apply().iter().map(|a| a.size).sum()
}
pub fn thread_ids(&self) -> Vec<u64> {
let allocs = self.apply();
let ids: HashSet<u64> = allocs.iter().map(|a| a.thread_id).collect();
ids.into_iter().collect()
}
pub fn type_names(&self) -> Vec<String> {
let allocs = self.apply();
let types: HashSet<String> = allocs.iter().filter_map(|a| a.type_name.clone()).collect();
types.into_iter().collect()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::event_store::MemoryEvent;
#[test]
fn test_filter_by_thread() {
let events = vec![
MemoryEvent::allocate(0x1000, 64, 1),
MemoryEvent::allocate(0x2000, 128, 2),
];
let view = MemoryView::from_events(events);
let builder = view.filter().by_thread(1);
let filtered = builder.apply();
assert_eq!(filtered.len(), 1);
}
#[test]
fn test_filter_by_size() {
let events = vec![
MemoryEvent::allocate(0x1000, 64, 1),
MemoryEvent::allocate(0x2000, 128, 1),
MemoryEvent::allocate(0x3000, 256, 1),
];
let view = MemoryView::from_events(events);
let builder = view.filter().by_size_range(100, 200);
let filtered = builder.apply();
assert_eq!(filtered.len(), 1);
}
#[test]
fn test_filter_chain() {
let events = vec![
MemoryEvent::allocate(0x1000, 64, 1).with_type_name("Vec<i32>".to_string()),
MemoryEvent::allocate(0x2000, 128, 1).with_type_name("String".to_string()),
MemoryEvent::allocate(0x3000, 64, 2).with_type_name("Vec<i32>".to_string()),
];
let view = MemoryView::from_events(events);
let builder = view.filter().by_thread(1).by_type("Vec");
let filtered = builder.apply();
assert_eq!(filtered.len(), 1);
}
#[test]
fn test_filter_by_type_no_match() {
let events =
vec![MemoryEvent::allocate(0x1000, 64, 1).with_type_name("Vec<i32>".to_string())];
let view = MemoryView::from_events(events);
let builder = view.filter().by_type("HashMap");
let filtered = builder.apply();
assert_eq!(filtered.len(), 0);
}
#[test]
fn test_filter_by_type_none_type_name() {
let events = vec![MemoryEvent::allocate(0x1000, 64, 1)];
let view = MemoryView::from_events(events);
let builder = view.filter().by_type("Vec");
let filtered = builder.apply();
assert_eq!(filtered.len(), 0);
}
#[test]
fn test_filter_by_address_range() {
let events = vec![
MemoryEvent::allocate(0x1000, 64, 1),
MemoryEvent::allocate(0x2000, 128, 1),
MemoryEvent::allocate(0x3000, 256, 1),
];
let view = MemoryView::from_events(events);
let builder = view.filter().by_address_range(0x1500, 0x2500);
let filtered = builder.apply();
assert_eq!(filtered.len(), 1);
}
#[test]
fn test_filter_by_min_size() {
let events = vec![
MemoryEvent::allocate(0x1000, 64, 1),
MemoryEvent::allocate(0x2000, 128, 1),
MemoryEvent::allocate(0x3000, 256, 1),
];
let view = MemoryView::from_events(events);
let builder = view.filter().by_min_size(100);
let filtered = builder.apply();
assert_eq!(filtered.len(), 2);
}
#[test]
fn test_filter_by_max_size() {
let events = vec![
MemoryEvent::allocate(0x1000, 64, 1),
MemoryEvent::allocate(0x2000, 128, 1),
MemoryEvent::allocate(0x3000, 256, 1),
];
let view = MemoryView::from_events(events);
let builder = view.filter().by_max_size(100);
let filtered = builder.apply();
assert_eq!(filtered.len(), 1);
}
#[test]
fn test_filter_push() {
let events = vec![
MemoryEvent::allocate(0x1000, 64, 1),
MemoryEvent::allocate(0x2000, 128, 2),
];
let view = MemoryView::from_events(events);
let builder = view.filter().push(ViewFilter::ByThread(1));
let filtered = builder.apply();
assert_eq!(filtered.len(), 1);
}
#[test]
fn test_filter_count() {
let events = vec![
MemoryEvent::allocate(0x1000, 64, 1),
MemoryEvent::allocate(0x2000, 128, 2),
];
let view = MemoryView::from_events(events);
let count = view.filter().by_thread(1).count();
assert_eq!(count, 1);
}
#[test]
fn test_filter_total_size() {
let events = vec![
MemoryEvent::allocate(0x1000, 64, 1),
MemoryEvent::allocate(0x2000, 128, 1),
];
let view = MemoryView::from_events(events);
let total = view.filter().by_thread(1).total_size();
assert_eq!(total, 192);
}
#[test]
fn test_filter_thread_ids() {
let events = vec![
MemoryEvent::allocate(0x1000, 64, 1),
MemoryEvent::allocate(0x2000, 128, 2),
MemoryEvent::allocate(0x3000, 256, 1),
];
let view = MemoryView::from_events(events);
let ids = view.filter().thread_ids();
assert_eq!(ids.len(), 2);
assert!(ids.contains(&1));
assert!(ids.contains(&2));
}
#[test]
fn test_filter_type_names() {
let events = vec![
MemoryEvent::allocate(0x1000, 64, 1).with_type_name("Vec<i32>".to_string()),
MemoryEvent::allocate(0x2000, 128, 2).with_type_name("String".to_string()),
];
let view = MemoryView::from_events(events);
let types = view.filter().type_names();
assert_eq!(types.len(), 2);
assert!(types.contains(&"Vec<i32>".to_string()));
assert!(types.contains(&"String".to_string()));
}
#[test]
fn test_filter_empty_view() {
let events: Vec<MemoryEvent> = vec![];
let view = MemoryView::from_events(events);
let binding = view.filter().by_thread(1);
let filtered = binding.apply();
assert_eq!(filtered.len(), 0);
}
#[test]
fn test_filter_no_filters() {
let events = vec![
MemoryEvent::allocate(0x1000, 64, 1),
MemoryEvent::allocate(0x2000, 128, 2),
];
let view = MemoryView::from_events(events);
let binding = view.filter();
let filtered = binding.apply();
assert_eq!(filtered.len(), 2);
}
#[test]
fn test_filter_multiple_chained() {
let events = vec![
MemoryEvent::allocate(0x1000, 64, 1).with_type_name("Vec<i32>".to_string()),
MemoryEvent::allocate(0x2000, 128, 1).with_type_name("String".to_string()),
MemoryEvent::allocate(0x3000, 256, 1).with_type_name("Vec<i32>".to_string()),
MemoryEvent::allocate(0x4000, 64, 2).with_type_name("Vec<i32>".to_string()),
];
let view = MemoryView::from_events(events);
let binding = view.filter().by_thread(1).by_type("Vec").by_max_size(100);
let filtered = binding.apply();
assert_eq!(filtered.len(), 1);
}
}