use std::collections::HashMap;
use std::hash::Hash;
#[derive(Debug)]
struct NodeChirho<K, V> {
key_chirho: K,
value_chirho: V,
prev_chirho: Option<usize>,
next_chirho: Option<usize>,
}
#[derive(Debug, Clone, Default)]
pub struct CacheStatsChirho {
pub hits_chirho: u64,
pub misses_chirho: u64,
pub evictions_chirho: u64,
}
impl CacheStatsChirho {
pub fn hit_ratio_chirho(&self) -> f64 {
let total_chirho = self.hits_chirho + self.misses_chirho;
if total_chirho == 0 {
0.0
} else {
self.hits_chirho as f64 / total_chirho as f64
}
}
}
#[derive(Debug)]
pub struct LruCacheChirho<K, V> {
capacity_chirho: usize,
map_chirho: HashMap<K, usize>,
nodes_chirho: Vec<Option<NodeChirho<K, V>>>,
head_chirho: Option<usize>,
tail_chirho: Option<usize>,
free_list_chirho: Vec<usize>,
}
impl<K: Hash + Eq + Clone, V> LruCacheChirho<K, V> {
pub fn new_chirho(capacity_chirho: usize) -> Self {
Self {
capacity_chirho: capacity_chirho.max(1),
map_chirho: HashMap::with_capacity(capacity_chirho),
nodes_chirho: Vec::with_capacity(capacity_chirho),
head_chirho: None,
tail_chirho: None,
free_list_chirho: Vec::new(),
}
}
pub fn len_chirho(&self) -> usize {
self.map_chirho.len()
}
pub fn is_empty_chirho(&self) -> bool {
self.map_chirho.is_empty()
}
pub fn capacity_chirho(&self) -> usize {
self.capacity_chirho
}
pub fn get_chirho(&mut self, key_chirho: &K) -> Option<&V> {
if let Some(&idx_chirho) = self.map_chirho.get(key_chirho) {
self.move_to_head_chirho(idx_chirho);
self.nodes_chirho[idx_chirho].as_ref().map(|n| &n.value_chirho)
} else {
None
}
}
pub fn get_mut_chirho(&mut self, key_chirho: &K) -> Option<&mut V> {
if let Some(&idx_chirho) = self.map_chirho.get(key_chirho) {
self.move_to_head_chirho(idx_chirho);
self.nodes_chirho[idx_chirho].as_mut().map(|n| &mut n.value_chirho)
} else {
None
}
}
pub fn put_chirho(&mut self, key_chirho: K, value_chirho: V) -> Option<V> {
if let Some(&idx_chirho) = self.map_chirho.get(&key_chirho) {
let old_value_chirho = {
let node_chirho = self.nodes_chirho[idx_chirho].as_mut().unwrap();
std::mem::replace(&mut node_chirho.value_chirho, value_chirho)
};
self.move_to_head_chirho(idx_chirho);
return Some(old_value_chirho);
}
let evicted_chirho = if self.map_chirho.len() >= self.capacity_chirho {
self.evict_lru_chirho()
} else {
None
};
let new_idx_chirho = self.allocate_node_chirho(key_chirho.clone(), value_chirho);
self.map_chirho.insert(key_chirho, new_idx_chirho);
self.add_to_head_chirho(new_idx_chirho);
evicted_chirho
}
pub fn remove_chirho(&mut self, key_chirho: &K) -> Option<V> {
if let Some(idx_chirho) = self.map_chirho.remove(key_chirho) {
let node_chirho = self.remove_node_chirho(idx_chirho);
self.free_list_chirho.push(idx_chirho);
node_chirho.map(|n| n.value_chirho)
} else {
None
}
}
pub fn clear_chirho(&mut self) {
self.map_chirho.clear();
self.nodes_chirho.clear();
self.free_list_chirho.clear();
self.head_chirho = None;
self.tail_chirho = None;
}
pub fn contains_chirho(&self, key_chirho: &K) -> bool {
self.map_chirho.contains_key(key_chirho)
}
fn allocate_node_chirho(&mut self, key_chirho: K, value_chirho: V) -> usize {
let node_chirho = NodeChirho {
key_chirho,
value_chirho,
prev_chirho: None,
next_chirho: None,
};
if let Some(idx_chirho) = self.free_list_chirho.pop() {
self.nodes_chirho[idx_chirho] = Some(node_chirho);
idx_chirho
} else {
let idx_chirho = self.nodes_chirho.len();
self.nodes_chirho.push(Some(node_chirho));
idx_chirho
}
}
fn add_to_head_chirho(&mut self, idx_chirho: usize) {
if let Some(node_chirho) = self.nodes_chirho[idx_chirho].as_mut() {
node_chirho.prev_chirho = None;
node_chirho.next_chirho = self.head_chirho;
}
if let Some(old_head_chirho) = self.head_chirho {
if let Some(node_chirho) = self.nodes_chirho[old_head_chirho].as_mut() {
node_chirho.prev_chirho = Some(idx_chirho);
}
}
self.head_chirho = Some(idx_chirho);
if self.tail_chirho.is_none() {
self.tail_chirho = Some(idx_chirho);
}
}
fn remove_node_chirho(&mut self, idx_chirho: usize) -> Option<NodeChirho<K, V>> {
let node_chirho = self.nodes_chirho[idx_chirho].take()?;
if let Some(prev_idx_chirho) = node_chirho.prev_chirho {
if let Some(prev_node_chirho) = self.nodes_chirho[prev_idx_chirho].as_mut() {
prev_node_chirho.next_chirho = node_chirho.next_chirho;
}
} else {
self.head_chirho = node_chirho.next_chirho;
}
if let Some(next_idx_chirho) = node_chirho.next_chirho {
if let Some(next_node_chirho) = self.nodes_chirho[next_idx_chirho].as_mut() {
next_node_chirho.prev_chirho = node_chirho.prev_chirho;
}
} else {
self.tail_chirho = node_chirho.prev_chirho;
}
Some(node_chirho)
}
fn move_to_head_chirho(&mut self, idx_chirho: usize) {
if self.head_chirho == Some(idx_chirho) {
return; }
let (prev_chirho, next_chirho) = {
let node_chirho = self.nodes_chirho[idx_chirho].as_ref().unwrap();
(node_chirho.prev_chirho, node_chirho.next_chirho)
};
if let Some(prev_idx_chirho) = prev_chirho {
if let Some(prev_node_chirho) = self.nodes_chirho[prev_idx_chirho].as_mut() {
prev_node_chirho.next_chirho = next_chirho;
}
}
if let Some(next_idx_chirho) = next_chirho {
if let Some(next_node_chirho) = self.nodes_chirho[next_idx_chirho].as_mut() {
next_node_chirho.prev_chirho = prev_chirho;
}
} else {
self.tail_chirho = prev_chirho;
}
if let Some(node_chirho) = self.nodes_chirho[idx_chirho].as_mut() {
node_chirho.prev_chirho = None;
node_chirho.next_chirho = self.head_chirho;
}
if let Some(old_head_chirho) = self.head_chirho {
if let Some(head_node_chirho) = self.nodes_chirho[old_head_chirho].as_mut() {
head_node_chirho.prev_chirho = Some(idx_chirho);
}
}
self.head_chirho = Some(idx_chirho);
}
fn evict_lru_chirho(&mut self) -> Option<V> {
let tail_idx_chirho = self.tail_chirho?;
let key_chirho = self.nodes_chirho[tail_idx_chirho].as_ref()?.key_chirho.clone();
self.remove_chirho(&key_chirho)
}
}
#[cfg(test)]
mod tests_chirho {
use super::*;
#[test]
fn test_basic_operations_chirho() {
let mut cache_chirho = LruCacheChirho::new_chirho(3);
cache_chirho.put_chirho("a", 1);
cache_chirho.put_chirho("b", 2);
cache_chirho.put_chirho("c", 3);
assert_eq!(cache_chirho.get_chirho(&"a"), Some(&1));
assert_eq!(cache_chirho.get_chirho(&"b"), Some(&2));
assert_eq!(cache_chirho.get_chirho(&"c"), Some(&3));
}
#[test]
fn test_eviction_chirho() {
let mut cache_chirho = LruCacheChirho::new_chirho(2);
cache_chirho.put_chirho("a", 1);
cache_chirho.put_chirho("b", 2);
cache_chirho.put_chirho("c", 3);
assert_eq!(cache_chirho.get_chirho(&"a"), None);
assert_eq!(cache_chirho.get_chirho(&"b"), Some(&2));
assert_eq!(cache_chirho.get_chirho(&"c"), Some(&3));
}
#[test]
fn test_lru_order_chirho() {
let mut cache_chirho = LruCacheChirho::new_chirho(2);
cache_chirho.put_chirho("a", 1);
cache_chirho.put_chirho("b", 2);
cache_chirho.get_chirho(&"a");
cache_chirho.put_chirho("c", 3);
assert_eq!(cache_chirho.get_chirho(&"a"), Some(&1));
assert_eq!(cache_chirho.get_chirho(&"b"), None);
assert_eq!(cache_chirho.get_chirho(&"c"), Some(&3));
}
#[test]
fn test_update_existing_chirho() {
let mut cache_chirho = LruCacheChirho::new_chirho(2);
cache_chirho.put_chirho("a", 1);
let old_chirho = cache_chirho.put_chirho("a", 10);
assert_eq!(old_chirho, Some(1));
assert_eq!(cache_chirho.get_chirho(&"a"), Some(&10));
}
#[test]
fn test_remove_chirho() {
let mut cache_chirho = LruCacheChirho::new_chirho(2);
cache_chirho.put_chirho("a", 1);
cache_chirho.put_chirho("b", 2);
let removed_chirho = cache_chirho.remove_chirho(&"a");
assert_eq!(removed_chirho, Some(1));
assert_eq!(cache_chirho.get_chirho(&"a"), None);
assert_eq!(cache_chirho.len_chirho(), 1);
}
#[test]
fn test_clear_chirho() {
let mut cache_chirho = LruCacheChirho::new_chirho(3);
cache_chirho.put_chirho("a", 1);
cache_chirho.put_chirho("b", 2);
cache_chirho.clear_chirho();
assert!(cache_chirho.is_empty_chirho());
assert_eq!(cache_chirho.get_chirho(&"a"), None);
}
#[test]
fn test_capacity_chirho() {
let cache_chirho: LruCacheChirho<i32, i32> = LruCacheChirho::new_chirho(5);
assert_eq!(cache_chirho.capacity_chirho(), 5);
}
#[test]
fn test_stats_hit_ratio_chirho() {
let stats_chirho = CacheStatsChirho {
hits_chirho: 75,
misses_chirho: 25,
evictions_chirho: 10,
};
assert!((stats_chirho.hit_ratio_chirho() - 0.75).abs() < 0.001);
}
#[test]
fn test_get_mut_chirho() {
let mut cache_chirho = LruCacheChirho::new_chirho(2);
cache_chirho.put_chirho("a", vec![1, 2, 3]);
if let Some(v_chirho) = cache_chirho.get_mut_chirho(&"a") {
v_chirho.push(4);
}
assert_eq!(cache_chirho.get_chirho(&"a"), Some(&vec![1, 2, 3, 4]));
}
#[test]
fn test_contains_chirho() {
let mut cache_chirho = LruCacheChirho::new_chirho(2);
cache_chirho.put_chirho("a", 1);
assert!(cache_chirho.contains_chirho(&"a"));
assert!(!cache_chirho.contains_chirho(&"b"));
}
}