use crate::CacheEntry;
use parking_lot::RwLockWriteGuard;
use std::collections::{HashMap, VecDeque};
pub fn move_key_to_end(order: &mut VecDeque<String>, key: &str) {
if let Some(pos) = order.iter().position(|k| k == key) {
order.remove(pos);
order.push_back(key.to_string());
}
}
pub fn find_min_frequency_key<R>(
map: &HashMap<String, CacheEntry<R>>,
order: &VecDeque<String>,
) -> Option<String> {
let mut min_freq_key: Option<String> = None;
let mut min_freq = u64::MAX;
for evict_key in order.iter() {
if let Some(entry) = map.get(evict_key) {
if entry.frequency < min_freq {
min_freq = entry.frequency;
min_freq_key = Some(evict_key.clone());
}
}
}
min_freq_key
}
pub fn remove_key_from_global_cache<R>(
map: &mut RwLockWriteGuard<HashMap<String, CacheEntry<R>>>,
order: &mut VecDeque<String>,
key: &str,
) -> bool {
let (removed_from_map, removed_from_order) = remove_from_maps(map, order, key);
removed_from_map || removed_from_order
}
pub fn remove_key_from_cache_local<R>(
map: &mut HashMap<String, CacheEntry<R>>,
order: &mut VecDeque<String>,
key: &str,
) -> bool {
let (removed_from_map, removed_from_order) = remove_from_maps(map, order, key);
removed_from_map || removed_from_order
}
fn remove_from_maps<R>(
map: &mut HashMap<String, CacheEntry<R>>,
order: &mut VecDeque<String>,
key: &str,
) -> (bool, bool) {
let removed_from_map = map.remove(key).is_some();
let removed_from_order = if let Some(pos) = order.iter().position(|k| k == key) {
order.remove(pos);
true
} else {
false
};
(removed_from_map, removed_from_order)
}
pub fn find_arc_eviction_key<'a, K, V, I>(
map: &HashMap<K, CacheEntry<V>>,
keys_iter: I,
) -> Option<K>
where
K: std::hash::Hash + Eq + Clone + 'a,
V: Clone,
I: Iterator<Item = (usize, &'a K)>,
{
let mut best_evict_key: Option<K> = None;
let mut best_score = f64::MAX;
let keys_vec: Vec<_> = keys_iter.collect();
let total_len = keys_vec.len();
for (idx, evict_key) in keys_vec {
if let Some(entry) = map.get(evict_key) {
let frequency = entry.frequency as f64;
let position_weight = (total_len - idx) as f64;
let score = frequency * position_weight;
if score < best_score {
best_score = score;
best_evict_key = Some(evict_key.clone());
}
}
}
best_evict_key
}
pub fn find_tlru_eviction_key<'a, K, V, I>(
map: &HashMap<K, CacheEntry<V>>,
keys_iter: I,
ttl: Option<u64>,
frequency_weight: Option<f64>,
) -> Option<K>
where
K: std::hash::Hash + Eq + Clone + 'a,
V: Clone,
I: Iterator<Item = (usize, &'a K)>,
{
let mut best_evict_key: Option<K> = None;
let mut best_score = f64::MAX;
let keys_vec: Vec<_> = keys_iter.collect();
let total_len = keys_vec.len();
for (idx, evict_key) in keys_vec {
if let Some(entry) = map.get(evict_key) {
let frequency = entry.frequency as f64;
let position_weight = (total_len - idx) as f64;
let age_factor = if let Some(ttl_secs) = ttl {
let elapsed = entry.inserted_at.elapsed().as_secs_f64();
let ttl_f64 = ttl_secs as f64;
(1.0 - (elapsed / ttl_f64).min(1.0)).max(0.0)
} else {
1.0 };
let frequency_component = match frequency_weight {
Some(weight) => frequency * weight,
None => frequency,
};
let score = frequency_component * position_weight * age_factor;
if score < best_score {
best_score = score;
best_evict_key = Some(evict_key.clone());
}
}
}
best_evict_key
}
#[cfg(test)]
mod tests {
use super::*;
use std::time::Instant;
fn create_cache_entry<R>(value: R, frequency: u64) -> CacheEntry<R> {
CacheEntry {
value,
inserted_at: Instant::now(),
frequency,
}
}
#[test]
fn test_move_key_to_end_existing_key() {
let mut order = VecDeque::from(vec![
"key1".to_string(),
"key2".to_string(),
"key3".to_string(),
]);
move_key_to_end(&mut order, "key2");
assert_eq!(order.len(), 3);
assert_eq!(order[0], "key1");
assert_eq!(order[1], "key3");
assert_eq!(order[2], "key2");
}
#[test]
fn test_move_key_to_end_first_key() {
let mut order = VecDeque::from(vec![
"key1".to_string(),
"key2".to_string(),
"key3".to_string(),
]);
move_key_to_end(&mut order, "key1");
assert_eq!(order.len(), 3);
assert_eq!(order[0], "key2");
assert_eq!(order[1], "key3");
assert_eq!(order[2], "key1");
}
#[test]
fn test_move_key_to_end_last_key() {
let mut order = VecDeque::from(vec![
"key1".to_string(),
"key2".to_string(),
"key3".to_string(),
]);
move_key_to_end(&mut order, "key3");
assert_eq!(order.len(), 3);
assert_eq!(order[0], "key1");
assert_eq!(order[1], "key2");
assert_eq!(order[2], "key3");
}
#[test]
fn test_move_key_to_end_nonexistent_key() {
let mut order = VecDeque::from(vec!["key1".to_string(), "key2".to_string()]);
move_key_to_end(&mut order, "key3");
assert_eq!(order.len(), 2);
assert_eq!(order[0], "key1");
assert_eq!(order[1], "key2");
}
#[test]
fn test_move_key_to_end_empty_queue() {
let mut order = VecDeque::new();
move_key_to_end(&mut order, "key1");
assert_eq!(order.len(), 0);
}
#[test]
fn test_move_key_to_end_single_key() {
let mut order = VecDeque::from(vec!["key1".to_string()]);
move_key_to_end(&mut order, "key1");
assert_eq!(order.len(), 1);
assert_eq!(order[0], "key1");
}
#[test]
fn test_find_min_frequency_key_basic() {
let mut map = HashMap::new();
map.insert("key1".to_string(), create_cache_entry(100, 5));
map.insert("key2".to_string(), create_cache_entry(200, 2)); map.insert("key3".to_string(), create_cache_entry(300, 8));
let order = VecDeque::from(vec![
"key1".to_string(),
"key2".to_string(),
"key3".to_string(),
]);
let min_key = find_min_frequency_key(&map, &order);
assert_eq!(min_key, Some("key2".to_string()));
}
#[test]
fn test_find_min_frequency_key_empty_queue() {
let map: HashMap<String, CacheEntry<i32>> = HashMap::new();
let order = VecDeque::new();
let min_key = find_min_frequency_key(&map, &order);
assert_eq!(min_key, None);
}
#[test]
fn test_find_min_frequency_key_empty_map() {
let map: HashMap<String, CacheEntry<i32>> = HashMap::new();
let order = VecDeque::from(vec!["key1".to_string(), "key2".to_string()]);
let min_key = find_min_frequency_key(&map, &order);
assert_eq!(min_key, None);
}
#[test]
fn test_find_min_frequency_key_single_entry() {
let mut map = HashMap::new();
map.insert("key1".to_string(), create_cache_entry(100, 10));
let order = VecDeque::from(vec!["key1".to_string()]);
let min_key = find_min_frequency_key(&map, &order);
assert_eq!(min_key, Some("key1".to_string()));
}
#[test]
fn test_find_min_frequency_key_tie_returns_first() {
let mut map = HashMap::new();
map.insert("key1".to_string(), create_cache_entry(100, 5));
map.insert("key2".to_string(), create_cache_entry(200, 3)); map.insert("key3".to_string(), create_cache_entry(300, 3));
let order = VecDeque::from(vec![
"key1".to_string(),
"key2".to_string(),
"key3".to_string(),
]);
let min_key = find_min_frequency_key(&map, &order);
assert_eq!(min_key, Some("key2".to_string()));
}
#[test]
fn test_find_min_frequency_key_orphaned_keys() {
let mut map = HashMap::new();
map.insert("key2".to_string(), create_cache_entry(200, 5));
map.insert("key3".to_string(), create_cache_entry(300, 2));
let order = VecDeque::from(vec![
"key1".to_string(), "key2".to_string(),
"key3".to_string(),
]);
let min_key = find_min_frequency_key(&map, &order);
assert_eq!(min_key, Some("key3".to_string()));
}
#[test]
fn test_find_min_frequency_key_all_orphaned() {
let mut map = HashMap::new();
map.insert("key4".to_string(), create_cache_entry(400, 1));
let order = VecDeque::from(vec![
"key1".to_string(),
"key2".to_string(),
"key3".to_string(),
]);
let min_key = find_min_frequency_key(&map, &order);
assert_eq!(min_key, None);
}
#[test]
fn test_find_min_frequency_key_zero_frequency() {
let mut map = HashMap::new();
map.insert("key1".to_string(), create_cache_entry(100, 10));
map.insert("key2".to_string(), create_cache_entry(200, 0)); map.insert("key3".to_string(), create_cache_entry(300, 5));
let order = VecDeque::from(vec![
"key1".to_string(),
"key2".to_string(),
"key3".to_string(),
]);
let min_key = find_min_frequency_key(&map, &order);
assert_eq!(min_key, Some("key2".to_string()));
}
#[test]
fn test_find_min_frequency_key_large_frequencies() {
let mut map = HashMap::new();
map.insert("key1".to_string(), create_cache_entry(100, u64::MAX - 1));
map.insert("key2".to_string(), create_cache_entry(200, u64::MAX)); map.insert("key3".to_string(), create_cache_entry(300, 1000));
let order = VecDeque::from(vec![
"key1".to_string(),
"key2".to_string(),
"key3".to_string(),
]);
let min_key = find_min_frequency_key(&map, &order);
assert_eq!(min_key, Some("key3".to_string()));
}
#[test]
fn test_find_min_frequency_key_different_types() {
let mut map = HashMap::new();
map.insert(
"key1".to_string(),
create_cache_entry("value1".to_string(), 5),
);
map.insert(
"key2".to_string(),
create_cache_entry("value2".to_string(), 2),
);
let order = VecDeque::from(vec!["key1".to_string(), "key2".to_string()]);
let min_key = find_min_frequency_key(&map, &order);
assert_eq!(min_key, Some("key2".to_string()));
}
#[test]
fn test_remove_key_from_cache_local_existing_key() {
let mut map = HashMap::new();
let mut order = VecDeque::new();
map.insert("key1".to_string(), create_cache_entry(100, 1));
order.push_back("key1".to_string());
let removed = remove_key_from_cache_local(&mut map, &mut order, "key1");
assert!(removed);
assert!(!map.contains_key("key1"));
assert!(order.is_empty());
}
#[test]
fn test_remove_key_from_cache_local_nonexistent_key() {
let mut map = HashMap::new();
let mut order = VecDeque::new();
map.insert("key1".to_string(), create_cache_entry(100, 1));
order.push_back("key1".to_string());
let removed = remove_key_from_cache_local(&mut map, &mut order, "key2");
assert!(!removed);
assert_eq!(map.len(), 1);
assert_eq!(order.len(), 1);
}
#[test]
fn test_remove_key_from_cache_local_multiple_entries() {
let mut map = HashMap::new();
let mut order = VecDeque::new();
map.insert("key1".to_string(), create_cache_entry(100, 1));
map.insert("key2".to_string(), create_cache_entry(200, 2));
map.insert("key3".to_string(), create_cache_entry(300, 3));
order.push_back("key1".to_string());
order.push_back("key2".to_string());
order.push_back("key3".to_string());
let removed = remove_key_from_cache_local(&mut map, &mut order, "key2");
assert!(removed);
assert!(!map.contains_key("key2"));
assert_eq!(map.len(), 2);
assert_eq!(order.len(), 2);
assert_eq!(order[0], "key1");
assert_eq!(order[1], "key3");
}
#[test]
fn test_remove_key_from_cache_local_only_in_map() {
let mut map = HashMap::new();
let mut order = VecDeque::new();
map.insert("key1".to_string(), create_cache_entry(100, 1));
let removed = remove_key_from_cache_local(&mut map, &mut order, "key1");
assert!(removed); assert!(!map.contains_key("key1"));
assert!(order.is_empty());
}
#[test]
fn test_remove_key_from_cache_local_only_in_order() {
let mut map: HashMap<String, CacheEntry<i32>> = HashMap::new();
let mut order = VecDeque::new();
order.push_back("key1".to_string());
let removed = remove_key_from_cache_local(&mut map, &mut order, "key1");
assert!(removed); assert!(map.is_empty());
assert!(order.is_empty());
}
#[test]
fn test_remove_key_from_cache_local_empty_structures() {
let mut map: HashMap<String, CacheEntry<i32>> = HashMap::new();
let mut order: VecDeque<String> = VecDeque::new();
let removed = remove_key_from_cache_local(&mut map, &mut order, "key1");
assert!(!removed);
assert!(map.is_empty());
assert!(order.is_empty());
}
#[test]
fn test_remove_key_from_cache_local_first_in_order() {
let mut map = HashMap::new();
let mut order = VecDeque::new();
map.insert("key1".to_string(), create_cache_entry(100, 1));
map.insert("key2".to_string(), create_cache_entry(200, 2));
order.push_back("key1".to_string());
order.push_back("key2".to_string());
let removed = remove_key_from_cache_local(&mut map, &mut order, "key1");
assert!(removed);
assert_eq!(map.len(), 1);
assert_eq!(order.len(), 1);
assert_eq!(order[0], "key2");
}
#[test]
fn test_remove_key_from_cache_local_last_in_order() {
let mut map = HashMap::new();
let mut order = VecDeque::new();
map.insert("key1".to_string(), create_cache_entry(100, 1));
map.insert("key2".to_string(), create_cache_entry(200, 2));
order.push_back("key1".to_string());
order.push_back("key2".to_string());
let removed = remove_key_from_cache_local(&mut map, &mut order, "key2");
assert!(removed);
assert_eq!(map.len(), 1);
assert_eq!(order.len(), 1);
assert_eq!(order[0], "key1");
}
#[test]
fn test_remove_key_from_cache_local_single_entry() {
let mut map = HashMap::new();
let mut order = VecDeque::new();
map.insert("key1".to_string(), create_cache_entry(100, 1));
order.push_back("key1".to_string());
let removed = remove_key_from_cache_local(&mut map, &mut order, "key1");
assert!(removed);
assert!(map.is_empty());
assert!(order.is_empty());
}
#[test]
fn test_remove_key_from_cache_local_different_value_types() {
let mut map = HashMap::new();
let mut order = VecDeque::new();
map.insert(
"key1".to_string(),
create_cache_entry("string_value".to_string(), 1),
);
order.push_back("key1".to_string());
let removed = remove_key_from_cache_local(&mut map, &mut order, "key1");
assert!(removed);
assert!(map.is_empty());
assert!(order.is_empty());
}
#[test]
fn test_remove_key_from_cache_existing_key() {
use parking_lot::RwLock;
let cache = RwLock::new(HashMap::new());
let mut order = VecDeque::new();
{
let mut map = cache.write();
map.insert("key1".to_string(), create_cache_entry(100, 1));
order.push_back("key1".to_string());
}
let mut map = cache.write();
let removed = remove_key_from_global_cache(&mut map, &mut order, "key1");
assert!(removed);
assert!(!map.contains_key("key1"));
assert!(order.is_empty());
}
#[test]
fn test_remove_key_from_cache_nonexistent_key() {
use parking_lot::RwLock;
let cache = RwLock::new(HashMap::new());
let mut order = VecDeque::new();
{
let mut map = cache.write();
map.insert("key1".to_string(), create_cache_entry(100, 1));
order.push_back("key1".to_string());
}
let mut map = cache.write();
let removed = remove_key_from_global_cache(&mut map, &mut order, "key2");
assert!(!removed);
assert_eq!(map.len(), 1);
assert_eq!(order.len(), 1);
}
#[test]
fn test_remove_key_from_cache_multiple_entries() {
use parking_lot::RwLock;
let cache = RwLock::new(HashMap::new());
let mut order = VecDeque::new();
{
let mut map = cache.write();
map.insert("key1".to_string(), create_cache_entry(100, 1));
map.insert("key2".to_string(), create_cache_entry(200, 2));
map.insert("key3".to_string(), create_cache_entry(300, 3));
order.push_back("key1".to_string());
order.push_back("key2".to_string());
order.push_back("key3".to_string());
}
let mut map = cache.write();
let removed = remove_key_from_global_cache(&mut map, &mut order, "key2");
assert!(removed);
assert!(!map.contains_key("key2"));
assert_eq!(map.len(), 2);
assert_eq!(order.len(), 2);
assert_eq!(order[0], "key1");
assert_eq!(order[1], "key3");
}
#[test]
fn test_remove_key_from_cache_only_in_map() {
use parking_lot::RwLock;
let cache = RwLock::new(HashMap::new());
let mut order = VecDeque::new();
{
let mut map = cache.write();
map.insert("key1".to_string(), create_cache_entry(100, 1));
}
let mut map = cache.write();
let removed = remove_key_from_global_cache(&mut map, &mut order, "key1");
assert!(removed);
assert!(!map.contains_key("key1"));
assert!(order.is_empty());
}
#[test]
fn test_remove_key_from_cache_only_in_order() {
use parking_lot::RwLock;
let cache: RwLock<HashMap<String, CacheEntry<i32>>> = RwLock::new(HashMap::new());
let mut order = VecDeque::new();
order.push_back("key1".to_string());
let mut map = cache.write();
let removed = remove_key_from_global_cache(&mut map, &mut order, "key1");
assert!(removed);
assert!(map.is_empty());
assert!(order.is_empty());
}
#[test]
fn test_remove_key_from_cache_empty_structures() {
use parking_lot::RwLock;
let cache: RwLock<HashMap<String, CacheEntry<i32>>> = RwLock::new(HashMap::new());
let mut order = VecDeque::new();
let mut map = cache.write();
let removed = remove_key_from_global_cache(&mut map, &mut order, "key1");
assert!(!removed);
assert!(map.is_empty());
assert!(order.is_empty());
}
#[test]
fn test_find_arc_eviction_key_empty_order() {
let map: HashMap<String, CacheEntry<i32>> = HashMap::new();
let order: Vec<String> = vec![];
let result = find_arc_eviction_key(&map, order.iter().enumerate());
assert_eq!(result, None);
}
#[test]
fn test_find_arc_eviction_key_single_entry() {
let mut map = HashMap::new();
map.insert("key1".to_string(), create_cache_entry(100, 5));
let order = vec!["key1".to_string()];
let result = find_arc_eviction_key(&map, order.iter().enumerate());
assert_eq!(result, Some("key1".to_string()));
}
#[test]
fn test_find_arc_eviction_key_low_frequency_wins() {
let mut map = HashMap::new();
map.insert("recent_freq".to_string(), create_cache_entry(200, 10));
map.insert("old_rare".to_string(), create_cache_entry(100, 1));
let order = vec!["recent_freq".to_string(), "old_rare".to_string()];
let result = find_arc_eviction_key(&map, order.iter().enumerate());
assert_eq!(result, Some("old_rare".to_string()));
}
#[test]
fn test_find_arc_eviction_key_recency_matters() {
let mut map = HashMap::new();
map.insert("recent".to_string(), create_cache_entry(200, 5));
map.insert("old".to_string(), create_cache_entry(100, 5));
let order = vec!["recent".to_string(), "old".to_string()];
let result = find_arc_eviction_key(&map, order.iter().enumerate());
assert_eq!(result, Some("old".to_string()));
}
#[test]
fn test_find_arc_eviction_key_multiple_entries() {
let mut map = HashMap::new();
map.insert("key1".to_string(), create_cache_entry(100, 10)); map.insert("key2".to_string(), create_cache_entry(200, 5)); map.insert("key3".to_string(), create_cache_entry(300, 3));
let order = vec![
"key1".to_string(), "key2".to_string(), "key3".to_string(), ];
let result = find_arc_eviction_key(&map, order.iter().enumerate());
assert_eq!(result, Some("key3".to_string()));
}
#[test]
fn test_find_arc_eviction_key_missing_entries() {
let mut map = HashMap::new();
map.insert("key1".to_string(), create_cache_entry(100, 5));
map.insert("key3".to_string(), create_cache_entry(300, 10));
let order = vec!["key3".to_string(), "key2".to_string(), "key1".to_string()];
let result = find_arc_eviction_key(&map, order.iter().enumerate());
assert_eq!(result, Some("key1".to_string()));
}
#[test]
fn test_find_arc_eviction_key_all_missing() {
let map: HashMap<String, CacheEntry<i32>> = HashMap::new();
let order = vec!["key1".to_string(), "key2".to_string()];
let result = find_arc_eviction_key(&map, order.iter().enumerate());
assert_eq!(result, None);
}
#[test]
fn test_find_arc_eviction_key_zero_frequency() {
let mut map = HashMap::new();
map.insert("high_freq".to_string(), create_cache_entry(200, 100));
map.insert("zero_freq".to_string(), create_cache_entry(100, 0));
let order = vec!["high_freq".to_string(), "zero_freq".to_string()];
let result = find_arc_eviction_key(&map, order.iter().enumerate());
assert_eq!(result, Some("zero_freq".to_string()));
}
#[test]
fn test_find_arc_eviction_key_complex_scenario() {
let mut map = HashMap::new();
map.insert("user:1".to_string(), create_cache_entry(1, 50)); map.insert("user:2".to_string(), create_cache_entry(2, 2)); map.insert("user:3".to_string(), create_cache_entry(3, 100));
let order = vec![
"user:1".to_string(), "user:2".to_string(), "user:3".to_string(), ];
let result = find_arc_eviction_key(&map, order.iter().enumerate());
assert_eq!(result, Some("user:2".to_string()));
}
#[test]
fn test_find_arc_eviction_key_with_integer_keys() {
let mut map = HashMap::new();
map.insert(1, create_cache_entry("a", 10));
map.insert(2, create_cache_entry("b", 5));
map.insert(3, create_cache_entry("c", 20));
let order = vec![1, 2, 3];
let result = find_arc_eviction_key(&map, order.iter().enumerate());
assert_eq!(result, Some(2));
}
#[test]
fn test_find_tlru_eviction_key_empty_order() {
let map: HashMap<String, CacheEntry<i32>> = HashMap::new();
let order: Vec<String> = vec![];
let result = find_tlru_eviction_key(&map, order.iter().enumerate(), Some(60), None);
assert_eq!(result, None);
}
#[test]
fn test_find_tlru_eviction_key_single_entry() {
let mut map = HashMap::new();
map.insert("key1".to_string(), create_cache_entry(100, 5));
let order = vec!["key1".to_string()];
let result = find_tlru_eviction_key(&map, order.iter().enumerate(), Some(60), None);
assert_eq!(result, Some("key1".to_string()));
}
#[test]
fn test_find_tlru_eviction_key_no_ttl() {
use std::thread;
use std::time::Duration;
let mut map = HashMap::new();
map.insert("old".to_string(), create_cache_entry(100, 5));
thread::sleep(Duration::from_millis(50));
map.insert("new".to_string(), create_cache_entry(200, 5));
let order = vec!["new".to_string(), "old".to_string()];
let result = find_tlru_eviction_key(&map, order.iter().enumerate(), None, None);
assert_eq!(result, Some("old".to_string()));
}
#[test]
fn test_find_tlru_eviction_key_age_matters() {
use std::thread;
use std::time::Duration;
let mut map = HashMap::new();
map.insert("old".to_string(), create_cache_entry(100, 10));
thread::sleep(Duration::from_millis(100));
map.insert("new".to_string(), create_cache_entry(200, 5));
let order = vec!["new".to_string(), "old".to_string()];
let result = find_tlru_eviction_key(&map, order.iter().enumerate(), Some(1), None);
assert_eq!(result, Some("old".to_string()));
}
#[test]
fn test_find_tlru_eviction_key_frequency_matters() {
let mut map = HashMap::new();
map.insert("high_freq".to_string(), create_cache_entry(200, 100));
map.insert("low_freq".to_string(), create_cache_entry(100, 1));
let order = vec!["high_freq".to_string(), "low_freq".to_string()];
let result = find_tlru_eviction_key(&map, order.iter().enumerate(), Some(60), None);
assert_eq!(result, Some("low_freq".to_string()));
}
#[test]
fn test_find_tlru_eviction_key_recency_matters() {
let mut map = HashMap::new();
map.insert("recent".to_string(), create_cache_entry(200, 5));
map.insert("old".to_string(), create_cache_entry(100, 5));
let order = vec!["recent".to_string(), "old".to_string()];
let result = find_tlru_eviction_key(&map, order.iter().enumerate(), Some(60), None);
assert_eq!(result, Some("old".to_string()));
}
#[test]
fn test_find_tlru_eviction_key_missing_entries() {
let mut map = HashMap::new();
map.insert("key2".to_string(), create_cache_entry(200, 5));
map.insert("key3".to_string(), create_cache_entry(300, 10));
let order = vec![
"key1".to_string(), "key2".to_string(),
"key3".to_string(),
];
let result = find_tlru_eviction_key(&map, order.iter().enumerate(), Some(60), None);
assert_eq!(result, Some("key2".to_string()));
}
#[test]
fn test_find_tlru_eviction_key_all_missing() {
let mut map = HashMap::new();
map.insert("key4".to_string(), create_cache_entry(400, 1));
let order = vec!["key1".to_string(), "key2".to_string(), "key3".to_string()];
let result = find_tlru_eviction_key(&map, order.iter().enumerate(), Some(60), None);
assert_eq!(result, None);
}
#[test]
fn test_find_tlru_eviction_key_zero_frequency() {
let mut map = HashMap::new();
map.insert("high_freq".to_string(), create_cache_entry(200, 10));
map.insert("zero_freq".to_string(), create_cache_entry(100, 0));
let order = vec!["high_freq".to_string(), "zero_freq".to_string()];
let result = find_tlru_eviction_key(&map, order.iter().enumerate(), Some(60), None);
assert_eq!(result, Some("zero_freq".to_string()));
}
#[test]
fn test_find_tlru_eviction_key_complex_scenario() {
use std::thread;
use std::time::Duration;
let mut map = HashMap::new();
map.insert("very_old".to_string(), create_cache_entry(100, 2));
thread::sleep(Duration::from_millis(50));
map.insert("medium".to_string(), create_cache_entry(200, 5));
thread::sleep(Duration::from_millis(50));
map.insert("recent".to_string(), create_cache_entry(300, 10));
let order = vec![
"recent".to_string(),
"medium".to_string(),
"very_old".to_string(),
];
let result = find_tlru_eviction_key(&map, order.iter().enumerate(), Some(1), None);
assert_eq!(result, Some("very_old".to_string()));
}
#[test]
fn test_find_tlru_eviction_key_with_integer_keys() {
let mut map = HashMap::new();
map.insert(1, create_cache_entry("a", 10));
map.insert(2, create_cache_entry("b", 5));
map.insert(3, create_cache_entry("c", 20));
let order = vec![1, 2, 3];
let result = find_tlru_eviction_key(&map, order.iter().enumerate(), Some(60), None);
assert_eq!(result, Some(2));
}
#[test]
fn test_find_tlru_eviction_key_approaching_expiration() {
use std::thread;
use std::time::Duration;
let mut map = HashMap::new();
map.insert("almost_expired".to_string(), create_cache_entry(100, 10));
thread::sleep(Duration::from_millis(900));
map.insert("fresh".to_string(), create_cache_entry(200, 5));
let order = vec!["fresh".to_string(), "almost_expired".to_string()];
let result = find_tlru_eviction_key(&map, order.iter().enumerate(), Some(1), None);
assert_eq!(result, Some("almost_expired".to_string()));
}
}
pub fn calculate_window_size(limit: usize, window_ratio: f64) -> usize {
let window_size = (limit as f64 * window_ratio) as usize;
window_size.max(1) }
pub fn should_admit(new_freq: u32, victim_freq: u32) -> bool {
new_freq >= victim_freq
}
pub fn find_w_tinylfu_victim<'a, K, R, I>(
map: &HashMap<K, CacheEntry<R>>,
protected_keys: I,
) -> Option<K>
where
K: Clone + Eq + std::hash::Hash + 'a,
I: Iterator<Item = &'a K>,
{
let mut min_freq = u64::MAX;
let mut victim_key: Option<K> = None;
for key in protected_keys {
if let Some(entry) = map.get(key) {
if entry.frequency < min_freq {
min_freq = entry.frequency;
victim_key = Some(key.clone());
}
}
}
victim_key
}
#[cfg(test)]
mod w_tinylfu_tests {
use super::*;
#[test]
fn test_calculate_window_size() {
assert_eq!(calculate_window_size(100, 0.2), 20);
assert_eq!(calculate_window_size(1000, 0.1), 100);
assert_eq!(calculate_window_size(50, 0.3), 15);
assert_eq!(calculate_window_size(5, 0.1), 1); assert_eq!(calculate_window_size(10, 0.0), 1); }
#[test]
fn test_should_admit() {
assert!(should_admit(10, 5));
assert!(should_admit(5, 5));
assert!(!should_admit(3, 10));
assert!(should_admit(0, 0));
assert!(!should_admit(0, 1));
}
#[test]
fn test_find_w_tinylfu_victim() {
let mut map = HashMap::new();
fn create_entry(val: i32, freq: u64) -> CacheEntry<i32> {
let mut entry = CacheEntry::new(val);
entry.frequency = freq;
entry
}
map.insert("key1".to_string(), create_entry(100, 10));
map.insert("key2".to_string(), create_entry(200, 5));
map.insert("key3".to_string(), create_entry(300, 15));
let protected_keys = vec!["key1".to_string(), "key2".to_string(), "key3".to_string()];
let victim = find_w_tinylfu_victim(&map, protected_keys.iter());
assert_eq!(victim, Some("key2".to_string()));
}
#[test]
fn test_find_w_tinylfu_victim_empty() {
let map: HashMap<String, CacheEntry<i32>> = HashMap::new();
let protected_keys: Vec<String> = vec![];
let victim = find_w_tinylfu_victim(&map, protected_keys.iter());
assert_eq!(victim, None);
}
#[test]
fn test_find_w_tinylfu_victim_single_entry() {
let mut map = HashMap::new();
fn create_entry(val: i32, freq: u64) -> CacheEntry<i32> {
let mut entry = CacheEntry::new(val);
entry.frequency = freq;
entry
}
map.insert("only_key".to_string(), create_entry(100, 7));
let protected_keys = vec!["only_key".to_string()];
let victim = find_w_tinylfu_victim(&map, protected_keys.iter());
assert_eq!(victim, Some("only_key".to_string()));
}
}