use crate::view::MemoryView;
use std::collections::HashMap;
pub struct ClassificationAnalysis {
view: MemoryView,
}
impl ClassificationAnalysis {
pub fn from_view(view: &MemoryView) -> Self {
Self { view: view.clone() }
}
pub fn by_type(&self) -> HashMap<String, TypeClassification> {
let mut types: HashMap<String, TypeClassification> = HashMap::new();
for alloc in self.view.allocations() {
let type_name = alloc
.type_name
.clone()
.unwrap_or_else(|| "unknown".to_string());
let entry = types.entry(type_name).or_default();
entry.count += 1;
entry.total_bytes += alloc.size;
entry.category = classify_type(&alloc.type_name.clone().unwrap_or_default());
}
types
}
pub fn summary(&self) -> ClassificationSummary {
let types = self.by_type();
let mut categories: HashMap<TypeCategory, usize> = HashMap::new();
for classification in types.values() {
*categories.entry(classification.category).or_default() += classification.count;
}
ClassificationSummary {
total_types: types.len(),
categories,
}
}
}
#[derive(Debug, Clone, Default)]
pub struct TypeClassification {
pub count: usize,
pub total_bytes: usize,
pub category: TypeCategory,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub enum TypeCategory {
#[default]
Unknown,
Collection,
String,
SmartPointer,
Primitive,
Custom,
}
#[derive(Debug, Clone)]
pub struct ClassificationSummary {
pub total_types: usize,
pub categories: HashMap<TypeCategory, usize>,
}
fn classify_type(type_name: &str) -> TypeCategory {
if type_name.contains("Vec<")
|| type_name.contains("HashMap<")
|| type_name.contains("HashSet<")
|| type_name.contains("BTreeMap<")
|| type_name.contains("BTreeSet<")
|| type_name.contains("LinkedList<")
|| type_name.contains("VecDeque<")
{
TypeCategory::Collection
} else if type_name.contains("String") || type_name.contains("str") {
TypeCategory::String
} else if type_name.contains("Arc<")
|| type_name.contains("Rc<")
|| type_name.contains("Box<")
|| type_name.contains("Weak<")
{
TypeCategory::SmartPointer
} else if type_name.contains("i32")
|| type_name.contains("u32")
|| type_name.contains("i64")
|| type_name.contains("u64")
|| type_name.contains("f32")
|| type_name.contains("f64")
|| type_name.contains("bool")
|| type_name.contains("char")
{
TypeCategory::Primitive
} else if !type_name.is_empty() && type_name != "unknown" {
TypeCategory::Custom
} else {
TypeCategory::Unknown
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::event_store::MemoryEvent;
#[test]
fn test_classification() {
let events = vec![
MemoryEvent::allocate(0x1000, 64, 1).with_type_name("Vec<i32>".to_string()),
MemoryEvent::allocate(0x2000, 32, 1).with_type_name("String".to_string()),
];
let view = MemoryView::from_events(events);
let analysis = ClassificationAnalysis::from_view(&view);
let types = analysis.by_type();
assert_eq!(types.len(), 2);
}
#[test]
fn test_type_category() {
assert_eq!(classify_type("Vec<i32>"), TypeCategory::Collection);
assert_eq!(classify_type("String"), TypeCategory::String);
assert_eq!(classify_type("Arc<Mutex<T>>"), TypeCategory::SmartPointer);
}
}