use sketch_oxide::HeavyKeeper;
use xxhash_rust::xxh3::xxh3_64;
pub struct HotFunctionTracker {
keeper: HeavyKeeper,
}
impl HotFunctionTracker {
pub fn new(top_k: usize) -> Self {
let keeper = HeavyKeeper::new(top_k, 0.001, 0.01)
.expect("HeavyKeeper initialization should not fail");
Self { keeper }
}
pub fn record_call(&mut self, callee_name: &str) {
self.keeper.update(callee_name.as_bytes());
}
pub fn top_functions(&self) -> Vec<(u64, u32)> {
self.keeper.top_k()
}
pub fn tracked_count(&self) -> usize {
self.keeper.top_k().len()
}
pub fn hash_name(name: &str) -> u64 {
xxh3_64(name.as_bytes())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_new_tracker_is_empty() {
let tracker = HotFunctionTracker::new(10);
assert!(
tracker.top_functions().is_empty(),
"Fresh tracker should have no top functions"
);
}
#[test]
fn test_record_single_function() {
let mut tracker = HotFunctionTracker::new(10);
for _ in 0..100 {
tracker.record_call("process_data");
}
let top = tracker.top_functions();
assert!(
!top.is_empty(),
"Top functions should contain at least one entry after recording"
);
let max_count = top.iter().map(|&(_, c)| c).max().unwrap_or(0);
assert!(
max_count >= 50,
"Expected dominant function count >= 50, got {max_count}"
);
}
#[test]
fn test_multiple_functions_ranking() {
let mut tracker = HotFunctionTracker::new(10);
for _ in 0..500 {
tracker.record_call("hot");
}
for _ in 0..50 {
tracker.record_call("warm");
}
for _ in 0..5 {
tracker.record_call("cold");
}
let top = tracker.top_functions();
assert!(
top.len() >= 2,
"Should track at least the two most frequent functions"
);
let highest = top.iter().max_by_key(|&&(_, c)| c).unwrap();
assert!(
highest.1 >= 100,
"Hottest function count should be >= 100, got {}",
highest.1
);
}
#[test]
fn test_hash_name_deterministic() {
let h1 = HotFunctionTracker::hash_name("my_func");
let h2 = HotFunctionTracker::hash_name("my_func");
assert_eq!(h1, h2, "hash_name should be deterministic");
let h3 = HotFunctionTracker::hash_name("other_func");
assert_ne!(
h1, h3,
"Different names should (very likely) hash differently"
);
}
#[test]
fn test_tracked_count() {
let mut tracker = HotFunctionTracker::new(5);
tracker.record_call("alpha");
tracker.record_call("beta");
tracker.record_call("gamma");
let count = tracker.tracked_count();
assert!(
count <= 5,
"Tracked count should not exceed top_k, got {count}"
);
}
}