#![allow(dead_code)]
use std::collections::HashMap;
#[derive(Debug, Clone)]
#[allow(dead_code)]
pub struct KeyCacheEntry<V> {
pub value: V,
pub expiry: u64,
pub hits: u32,
}
#[derive(Debug)]
#[allow(dead_code)]
pub struct KeyCache<V> {
entries: HashMap<String, KeyCacheEntry<V>>,
frame: u64,
capacity: usize,
}
#[allow(dead_code)]
pub fn new_key_cache<V>(capacity: usize) -> KeyCache<V> {
KeyCache {
entries: HashMap::new(),
frame: 0,
capacity,
}
}
#[allow(dead_code)]
pub fn kc_insert<V>(cache: &mut KeyCache<V>, key: &str, value: V, ttl: u64) {
if cache.entries.len() >= cache.capacity && !cache.entries.contains_key(key) {
if let Some(oldest) = cache
.entries
.iter()
.min_by_key(|(_, e)| e.expiry)
.map(|(k, _)| k.clone())
{
cache.entries.remove(&oldest);
}
}
cache.entries.insert(
key.to_string(),
KeyCacheEntry {
value,
expiry: cache.frame + ttl,
hits: 0,
},
);
}
#[allow(dead_code)]
pub fn kc_get<'a, V>(cache: &'a mut KeyCache<V>, key: &str) -> Option<&'a V> {
let frame = cache.frame;
if let Some(e) = cache.entries.get_mut(key) {
if e.expiry >= frame {
e.hits += 1;
return Some(&e.value);
}
}
None
}
#[allow(dead_code)]
pub fn kc_advance(cache: &mut KeyCache<impl std::fmt::Debug>, frames: u64) {
cache.frame += frames;
let frame = cache.frame;
cache.entries.retain(|_, e| e.expiry >= frame);
}
#[allow(dead_code)]
pub fn kc_remove<V>(cache: &mut KeyCache<V>, key: &str) -> bool {
cache.entries.remove(key).is_some()
}
#[allow(dead_code)]
pub fn kc_contains<V>(cache: &mut KeyCache<V>, key: &str) -> bool {
kc_get(cache, key).is_some()
}
#[allow(dead_code)]
pub fn kc_len<V>(cache: &KeyCache<V>) -> usize {
cache.entries.len()
}
#[allow(dead_code)]
pub fn kc_frame<V>(cache: &KeyCache<V>) -> u64 {
cache.frame
}
#[allow(dead_code)]
pub fn kc_hits<V>(cache: &KeyCache<V>, key: &str) -> u32 {
cache.entries.get(key).map(|e| e.hits).unwrap_or(0)
}
#[allow(dead_code)]
pub fn kc_clear<V>(cache: &mut KeyCache<V>) {
cache.entries.clear();
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_insert_get() {
let mut c: KeyCache<u32> = new_key_cache(10);
kc_insert(&mut c, "a", 42, 5);
assert_eq!(kc_get(&mut c, "a"), Some(&42));
}
#[test]
fn test_expired() {
let mut c: KeyCache<u32> = new_key_cache(10);
kc_insert(&mut c, "x", 1, 2);
kc_advance(&mut c, 3);
assert_eq!(kc_get(&mut c, "x"), None);
}
#[test]
fn test_remove() {
let mut c: KeyCache<u32> = new_key_cache(10);
kc_insert(&mut c, "k", 5, 100);
assert!(kc_remove(&mut c, "k"));
assert_eq!(kc_get(&mut c, "k"), None);
}
#[test]
fn test_contains() {
let mut c: KeyCache<i32> = new_key_cache(10);
kc_insert(&mut c, "z", -1, 10);
assert!(kc_contains(&mut c, "z"));
}
#[test]
fn test_capacity_eviction() {
let mut c: KeyCache<u32> = new_key_cache(2);
kc_insert(&mut c, "a", 1, 100);
kc_insert(&mut c, "b", 2, 50);
kc_insert(&mut c, "c", 3, 200);
assert_eq!(kc_len(&c), 2);
}
#[test]
fn test_advance_evicts() {
let mut c: KeyCache<u32> = new_key_cache(10);
kc_insert(&mut c, "a", 1, 1);
kc_advance(&mut c, 2);
assert_eq!(kc_len(&c), 0);
}
#[test]
fn test_hits() {
let mut c: KeyCache<u32> = new_key_cache(10);
kc_insert(&mut c, "h", 7, 10);
kc_get(&mut c, "h");
kc_get(&mut c, "h");
assert_eq!(kc_hits(&c, "h"), 2);
}
#[test]
fn test_clear() {
let mut c: KeyCache<u32> = new_key_cache(10);
kc_insert(&mut c, "a", 1, 10);
kc_clear(&mut c);
assert_eq!(kc_len(&c), 0);
}
#[test]
fn test_frame_advances() {
let mut c: KeyCache<u32> = new_key_cache(10);
kc_advance(&mut c, 5);
assert_eq!(kc_frame(&c), 5);
}
}