#[cfg(feature = "metrics")]
use crate::metrics::metrics_impl::CoreOnlyMetrics;
#[cfg(feature = "metrics")]
use crate::metrics::snapshot::CoreOnlyMetricsSnapshot;
#[cfg(feature = "metrics")]
use crate::metrics::traits::{CoreMetricsRecorder, MetricsSnapshotProvider};
use crate::prelude::ReadOnlyCache;
use crate::traits::CoreCache;
use rustc_hash::FxHashMap;
use std::hash::Hash;
use std::marker::PhantomData;
use std::ptr::NonNull;
#[repr(C)]
struct Node<K, V> {
prev: Option<NonNull<Node<K, V>>>,
next: Option<NonNull<Node<K, V>>>,
key: K,
value: V,
}
pub struct Iter<'a, K, V> {
current: Option<NonNull<Node<K, V>>>,
remaining: usize,
_marker: PhantomData<&'a (K, V)>,
}
unsafe impl<K: Sync, V: Sync> Send for Iter<'_, K, V> {}
unsafe impl<K: Sync, V: Sync> Sync for Iter<'_, K, V> {}
impl<K, V> std::fmt::Debug for Iter<'_, K, V> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Iter")
.field("remaining", &self.remaining)
.finish()
}
}
pub struct IntoIter<K, V> {
cache: MruCore<K, V>,
remaining: usize,
}
impl<K, V> std::fmt::Debug for IntoIter<K, V> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("IntoIter")
.field("remaining", &self.remaining)
.finish()
}
}
pub struct MruCore<K, V> {
map: FxHashMap<K, NonNull<Node<K, V>>>,
head: Option<NonNull<Node<K, V>>>,
tail: Option<NonNull<Node<K, V>>>,
capacity: usize,
#[cfg(feature = "metrics")]
metrics: CoreOnlyMetrics,
}
unsafe impl<K: Send, V: Send> Send for MruCore<K, V> {}
unsafe impl<K: Sync, V: Sync> Sync for MruCore<K, V> {}
impl<K, V> MruCore<K, V> {
#[inline(always)]
fn detach(&mut self, node_ptr: NonNull<Node<K, V>>) {
unsafe {
let node = node_ptr.as_ref();
let prev = node.prev;
let next = node.next;
match prev {
Some(mut p) => p.as_mut().next = next,
None => self.head = next,
}
match next {
Some(mut n) => n.as_mut().prev = prev,
None => self.tail = prev,
}
}
}
#[inline(always)]
fn attach_head(&mut self, mut node_ptr: NonNull<Node<K, V>>) {
unsafe {
let node = node_ptr.as_mut();
node.prev = None;
node.next = self.head;
match self.head {
Some(mut h) => h.as_mut().prev = Some(node_ptr),
None => self.tail = Some(node_ptr),
}
self.head = Some(node_ptr);
}
}
#[inline(always)]
fn pop_head(&mut self) -> Option<Box<Node<K, V>>> {
self.head.map(|head_ptr| unsafe {
let node = Box::from_raw(head_ptr.as_ptr());
self.head = node.next;
match self.head {
Some(mut h) => h.as_mut().prev = None,
None => self.tail = None,
}
node
})
}
pub fn iter(&self) -> Iter<'_, K, V> {
Iter {
current: self.head,
remaining: self.map.len(),
_marker: PhantomData,
}
}
}
impl<K, V> MruCore<K, V>
where
K: Clone + Eq + Hash,
{
#[inline]
pub fn new(capacity: usize) -> Self {
Self {
map: FxHashMap::with_capacity_and_hasher(capacity, Default::default()),
head: None,
tail: None,
capacity,
#[cfg(feature = "metrics")]
metrics: CoreOnlyMetrics::default(),
}
}
#[inline]
pub fn get(&mut self, key: &K) -> Option<&V> {
let node_ptr = match self.map.get(key) {
Some(&ptr) => {
#[cfg(feature = "metrics")]
self.metrics.record_get_hit();
ptr
},
None => {
#[cfg(feature = "metrics")]
self.metrics.record_get_miss();
return None;
},
};
self.detach(node_ptr);
self.attach_head(node_ptr);
unsafe { Some(&node_ptr.as_ref().value) }
}
#[inline]
pub fn insert(&mut self, key: K, value: V) -> Option<V> {
#[cfg(feature = "metrics")]
self.metrics.record_insert_call();
if self.capacity == 0 {
return None;
}
if let Some(&node_ptr) = self.map.get(&key) {
#[cfg(feature = "metrics")]
self.metrics.record_insert_update();
let old = unsafe { std::mem::replace(&mut (*node_ptr.as_ptr()).value, value) };
return Some(old);
}
#[cfg(feature = "metrics")]
self.metrics.record_insert_new();
self.evict_if_needed();
let node = Box::new(Node {
prev: None,
next: None,
key: key.clone(),
value,
});
let node_ptr = NonNull::new(Box::into_raw(node)).unwrap();
self.map.insert(key, node_ptr);
self.attach_head(node_ptr);
#[cfg(debug_assertions)]
self.validate_invariants();
None
}
#[inline]
fn evict_if_needed(&mut self) {
#[cfg(feature = "metrics")]
if self.len() >= self.capacity && self.head.is_some() {
self.metrics.record_evict_call();
}
while self.len() >= self.capacity {
if let Some(node) = self.pop_head() {
self.map.remove(&node.key);
#[cfg(feature = "metrics")]
self.metrics.record_evicted_entry();
} else {
break;
}
}
}
#[inline]
pub fn len(&self) -> usize {
self.map.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.map.is_empty()
}
#[inline]
pub fn capacity(&self) -> usize {
self.capacity
}
#[inline]
pub fn contains(&self, key: &K) -> bool {
self.map.contains_key(key)
}
pub fn clear(&mut self) {
#[cfg(feature = "metrics")]
self.metrics.record_clear();
while self.pop_head().is_some() {}
self.map.clear();
#[cfg(debug_assertions)]
self.validate_invariants();
}
#[cfg(debug_assertions)]
fn validate_invariants(&self) {
if self.map.is_empty() {
debug_assert!(self.head.is_none(), "Empty cache should have no head");
debug_assert!(self.tail.is_none(), "Empty cache should have no tail");
return;
}
let mut count = 0;
let mut current = self.head;
let mut visited = std::collections::HashSet::new();
while let Some(ptr) = current {
count += 1;
assert!(visited.insert(ptr), "Cycle detected in list");
assert!(count <= self.map.len(), "Count exceeds map size");
unsafe {
let node = ptr.as_ref();
debug_assert!(
self.map.contains_key(&node.key),
"Node key not found in map"
);
current = node.next;
}
}
debug_assert_eq!(count, self.map.len(), "List count doesn't match map size");
}
}
impl<K, V> Drop for MruCore<K, V> {
fn drop(&mut self) {
while self.pop_head().is_some() {}
}
}
impl<K, V> std::fmt::Debug for MruCore<K, V> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("MruCore")
.field("capacity", &self.capacity)
.field("len", &self.map.len())
.finish_non_exhaustive()
}
}
impl<K, V> ReadOnlyCache<K, V> for MruCore<K, V>
where
K: Clone + Eq + Hash,
{
#[inline]
fn contains(&self, key: &K) -> bool {
self.map.contains_key(key)
}
#[inline]
fn len(&self) -> usize {
self.map.len()
}
#[inline]
fn capacity(&self) -> usize {
self.capacity
}
}
impl<K, V> CoreCache<K, V> for MruCore<K, V>
where
K: Clone + Eq + Hash,
{
#[inline]
fn insert(&mut self, key: K, value: V) -> Option<V> {
MruCore::insert(self, key, value)
}
#[inline]
fn get(&mut self, key: &K) -> Option<&V> {
MruCore::get(self, key)
}
fn clear(&mut self) {
MruCore::clear(self);
}
}
impl<'a, K, V> Iterator for Iter<'a, K, V> {
type Item = (&'a K, &'a V);
fn next(&mut self) -> Option<Self::Item> {
self.current.map(|ptr| {
self.remaining -= 1;
unsafe {
let node = ptr.as_ref();
self.current = node.next;
(&node.key, &node.value)
}
})
}
fn size_hint(&self) -> (usize, Option<usize>) {
(self.remaining, Some(self.remaining))
}
}
impl<K, V> ExactSizeIterator for Iter<'_, K, V> {}
impl<K, V> std::iter::FusedIterator for Iter<'_, K, V> {}
impl<K, V> Iterator for IntoIter<K, V> {
type Item = (K, V);
fn next(&mut self) -> Option<Self::Item> {
self.cache.pop_head().map(|node| {
self.remaining -= 1;
(node.key, node.value)
})
}
fn size_hint(&self) -> (usize, Option<usize>) {
(self.remaining, Some(self.remaining))
}
}
impl<K, V> ExactSizeIterator for IntoIter<K, V> {}
impl<K, V> std::iter::FusedIterator for IntoIter<K, V> {}
impl<K, V> IntoIterator for MruCore<K, V> {
type Item = (K, V);
type IntoIter = IntoIter<K, V>;
fn into_iter(self) -> Self::IntoIter {
let remaining = self.map.len();
IntoIter {
cache: self,
remaining,
}
}
}
impl<'a, K, V> IntoIterator for &'a MruCore<K, V> {
type Item = (&'a K, &'a V);
type IntoIter = Iter<'a, K, V>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl<K, V> Clone for MruCore<K, V>
where
K: Clone + Eq + Hash,
V: Clone,
{
fn clone(&self) -> Self {
let mut map = FxHashMap::with_capacity_and_hasher(self.capacity, Default::default());
let mut new_head: Option<NonNull<Node<K, V>>> = None;
let mut new_tail: Option<NonNull<Node<K, V>>> = None;
let mut prev_new: Option<NonNull<Node<K, V>>> = None;
let mut current = self.head;
while let Some(ptr) = current {
let node = unsafe { ptr.as_ref() };
let new_node = Box::new(Node {
prev: prev_new,
next: None,
key: node.key.clone(),
value: node.value.clone(),
});
let new_ptr = NonNull::new(Box::into_raw(new_node)).unwrap();
if let Some(mut prev) = prev_new {
unsafe {
prev.as_mut().next = Some(new_ptr);
}
} else {
new_head = Some(new_ptr);
}
map.insert(node.key.clone(), new_ptr);
prev_new = Some(new_ptr);
new_tail = Some(new_ptr);
current = node.next;
}
MruCore {
map,
head: new_head,
tail: new_tail,
capacity: self.capacity,
#[cfg(feature = "metrics")]
metrics: self.metrics,
}
}
}
#[cfg(feature = "metrics")]
impl<K, V> MruCore<K, V>
where
K: Clone + Eq + Hash,
{
pub fn metrics_snapshot(&self) -> CoreOnlyMetricsSnapshot {
CoreOnlyMetricsSnapshot {
get_calls: self.metrics.get_calls,
get_hits: self.metrics.get_hits,
get_misses: self.metrics.get_misses,
insert_calls: self.metrics.insert_calls,
insert_updates: self.metrics.insert_updates,
insert_new: self.metrics.insert_new,
evict_calls: self.metrics.evict_calls,
evicted_entries: self.metrics.evicted_entries,
cache_len: self.len(),
capacity: self.capacity,
}
}
}
#[cfg(feature = "metrics")]
impl<K, V> MetricsSnapshotProvider<CoreOnlyMetricsSnapshot> for MruCore<K, V>
where
K: Clone + Eq + Hash,
{
fn snapshot(&self) -> CoreOnlyMetricsSnapshot {
self.metrics_snapshot()
}
}
#[cfg(test)]
mod tests {
use super::*;
mod basic_operations {
use super::*;
#[test]
fn new_cache_is_empty() {
let cache: MruCore<&str, i32> = MruCore::new(100);
assert!(cache.is_empty());
assert_eq!(cache.len(), 0);
assert_eq!(cache.capacity(), 100);
}
#[test]
fn insert_and_get() {
let mut cache = MruCore::new(100);
cache.insert("key1", "value1");
assert_eq!(cache.len(), 1);
assert_eq!(cache.get(&"key1"), Some(&"value1"));
}
#[test]
fn insert_multiple_items() {
let mut cache = MruCore::new(100);
cache.insert("a", 1);
cache.insert("b", 2);
cache.insert("c", 3);
assert_eq!(cache.len(), 3);
assert_eq!(cache.get(&"a"), Some(&1));
assert_eq!(cache.get(&"b"), Some(&2));
assert_eq!(cache.get(&"c"), Some(&3));
}
#[test]
fn get_missing_key_returns_none() {
let mut cache: MruCore<&str, i32> = MruCore::new(100);
cache.insert("exists", 42);
assert_eq!(cache.get(&"missing"), None);
}
#[test]
fn update_existing_key() {
let mut cache = MruCore::new(100);
cache.insert("key", "initial");
cache.insert("key", "updated");
assert_eq!(cache.len(), 1);
assert_eq!(cache.get(&"key"), Some(&"updated"));
}
#[test]
fn contains_returns_correct_result() {
let mut cache = MruCore::new(100);
cache.insert("exists", 1);
assert!(cache.contains(&"exists"));
assert!(!cache.contains(&"missing"));
}
#[test]
fn clear_removes_all_entries() {
let mut cache = MruCore::new(100);
cache.insert("a", 1);
cache.insert("b", 2);
cache.clear();
assert!(cache.is_empty());
assert_eq!(cache.len(), 0);
assert!(!cache.contains(&"a"));
assert!(!cache.contains(&"b"));
}
#[test]
fn capacity_returns_correct_value() {
let cache: MruCore<i32, i32> = MruCore::new(500);
assert_eq!(cache.capacity(), 500);
}
}
mod mru_behavior {
use super::*;
#[test]
fn evicts_most_recently_used() {
let mut cache = MruCore::new(3);
cache.insert("a", 1);
cache.insert("b", 2);
cache.insert("c", 3);
cache.get(&"a");
cache.insert("d", 4);
assert!(!cache.contains(&"a"), "MRU 'a' should be evicted");
assert!(cache.contains(&"b"));
assert!(cache.contains(&"c"));
assert!(cache.contains(&"d"));
}
#[test]
fn most_recent_insert_evicted_first() {
let mut cache = MruCore::new(3);
cache.insert("a", 1);
cache.insert("b", 2);
cache.insert("c", 3);
cache.insert("d", 4);
assert!(cache.contains(&"a"));
assert!(cache.contains(&"b"));
assert!(!cache.contains(&"c"), "Most recent 'c' should be evicted");
assert!(cache.contains(&"d"));
}
#[test]
fn opposite_of_lru_behavior() {
let mut cache = MruCore::new(3);
cache.insert("first", 1);
cache.insert("middle", 2);
cache.insert("last", 3);
cache.insert("new", 4);
assert!(cache.contains(&"first"), "Oldest should stay in MRU");
assert!(cache.contains(&"middle"));
assert!(!cache.contains(&"last"), "Newest should be evicted in MRU");
assert!(cache.contains(&"new"));
}
#[test]
fn cyclic_pattern_simulation() {
let mut cache = MruCore::new(5);
for cycle in 0..3 {
for i in 1..=5 {
let key = format!("cycle{}_{}", cycle, i);
cache.insert(key, i);
}
}
assert!(cache.contains(&"cycle0_1".to_string())); assert!(cache.contains(&"cycle0_4".to_string())); assert!(!cache.contains(&"cycle1_3".to_string())); assert!(!cache.contains(&"cycle2_1".to_string())); assert!(cache.contains(&"cycle2_5".to_string())); }
}
mod eviction_behavior {
use super::*;
#[test]
fn eviction_occurs_when_over_capacity() {
let mut cache = MruCore::new(5);
for i in 0..10 {
cache.insert(i, i * 10);
}
assert_eq!(cache.len(), 5);
}
#[test]
fn eviction_removes_from_index() {
let mut cache = MruCore::new(3);
cache.insert("a", 1);
cache.insert("b", 2);
cache.insert("c", 3);
assert!(cache.contains(&"c"));
cache.insert("d", 4);
assert!(!cache.contains(&"c"));
assert_eq!(cache.get(&"c"), None);
}
#[test]
fn continuous_insertions_evict_correctly() {
let mut cache = MruCore::new(3);
cache.insert(1, 10);
cache.insert(2, 20);
cache.insert(3, 30);
assert_eq!(cache.len(), 3);
cache.insert(4, 40);
assert_eq!(cache.len(), 3);
assert!(!cache.contains(&3));
cache.insert(5, 50);
assert_eq!(cache.len(), 3);
assert!(!cache.contains(&4)); }
}
mod edge_cases {
use super::*;
#[test]
fn single_capacity_cache() {
let mut cache = MruCore::new(1);
cache.insert("a", 1);
assert_eq!(cache.get(&"a"), Some(&1));
cache.insert("b", 2);
assert!(!cache.contains(&"a"));
assert_eq!(cache.get(&"b"), Some(&2));
}
#[test]
fn get_after_update() {
let mut cache = MruCore::new(100);
cache.insert("key", "v1");
assert_eq!(cache.get(&"key"), Some(&"v1"));
cache.insert("key", "v2");
assert_eq!(cache.get(&"key"), Some(&"v2"));
cache.insert("key", "v3");
cache.insert("key", "v4");
assert_eq!(cache.get(&"key"), Some(&"v4"));
}
#[test]
fn large_capacity() {
let mut cache = MruCore::new(10000);
for i in 0..10000 {
cache.insert(i, i * 2);
}
assert_eq!(cache.len(), 10000);
assert_eq!(cache.get(&5000), Some(&10000));
assert_eq!(cache.get(&9999), Some(&19998));
}
#[test]
fn empty_cache_operations() {
let mut cache: MruCore<i32, i32> = MruCore::new(100);
assert!(cache.is_empty());
assert_eq!(cache.get(&1), None);
assert!(!cache.contains(&1));
cache.clear();
assert!(cache.is_empty());
}
#[test]
fn string_keys_and_values() {
let mut cache = MruCore::new(100);
cache.insert(String::from("hello"), String::from("world"));
cache.insert(String::from("foo"), String::from("bar"));
assert_eq!(
cache.get(&String::from("hello")),
Some(&String::from("world"))
);
assert_eq!(cache.get(&String::from("foo")), Some(&String::from("bar")));
}
}
#[test]
#[cfg(debug_assertions)]
fn validate_invariants_after_operations() {
let mut cache = MruCore::new(10);
for i in 1..=10 {
cache.insert(i, i * 100);
}
cache.validate_invariants();
for _ in 0..3 {
cache.get(&5);
}
cache.validate_invariants();
cache.insert(11, 1100);
cache.validate_invariants();
cache.insert(12, 1200);
cache.validate_invariants();
cache.clear();
cache.validate_invariants();
assert_eq!(cache.len(), 0);
}
#[test]
#[cfg(debug_assertions)]
fn validate_invariants_with_mru_evictions() {
let mut cache = MruCore::new(3);
cache.insert(1, 100);
cache.insert(2, 200);
cache.insert(3, 300);
cache.validate_invariants();
cache.get(&1);
cache.validate_invariants();
cache.insert(4, 400);
cache.validate_invariants();
assert!(!cache.contains(&1)); assert!(cache.contains(&2));
assert!(cache.contains(&3));
assert!(cache.contains(&4));
}
#[test]
fn zero_capacity_rejects_inserts() {
let mut cache: MruCore<&str, i32> = MruCore::new(0);
assert_eq!(cache.capacity(), 0);
cache.insert("key", 42);
assert_eq!(
cache.len(),
0,
"MruCore with capacity=0 should reject inserts"
);
}
#[test]
fn trait_insert_returns_old_value() {
let mut cache: MruCore<&str, i32> = MruCore::new(10);
let first = CoreCache::insert(&mut cache, "key", 1);
assert_eq!(first, None);
let second = CoreCache::insert(&mut cache, "key", 2);
assert_eq!(
second,
Some(1),
"Second insert via trait should return old value"
);
}
#[test]
fn inherent_insert_updates_value() {
let mut cache: MruCore<&str, i32> = MruCore::new(10);
cache.insert("key", 1);
cache.insert("key", 2);
assert_eq!(cache.get(&"key"), Some(&2));
}
mod iterator_tests {
use super::*;
#[test]
fn iter_mru_to_lru_order() {
let mut cache = MruCore::new(10);
cache.insert("a", 1);
cache.insert("b", 2);
cache.insert("c", 3);
let pairs: Vec<_> = cache.iter().collect();
assert_eq!(pairs, vec![(&"c", &3), (&"b", &2), (&"a", &1)]);
}
#[test]
fn iter_empty_cache() {
let cache: MruCore<&str, i32> = MruCore::new(10);
assert_eq!(cache.iter().count(), 0);
}
#[test]
fn iter_reflects_access_order() {
let mut cache = MruCore::new(10);
cache.insert("a", 1);
cache.insert("b", 2);
cache.insert("c", 3);
cache.get(&"a");
let keys: Vec<_> = cache.iter().map(|(k, _)| *k).collect();
assert_eq!(keys, vec!["a", "c", "b"]);
}
#[test]
fn iter_exact_size() {
let mut cache = MruCore::new(10);
cache.insert(1, 10);
cache.insert(2, 20);
cache.insert(3, 30);
let iter = cache.iter();
assert_eq!(iter.len(), 3);
}
#[test]
fn into_iter_consumes_cache() {
let mut cache = MruCore::new(10);
cache.insert("a", 1);
cache.insert("b", 2);
cache.insert("c", 3);
let pairs: Vec<_> = cache.into_iter().collect();
assert_eq!(pairs, vec![("c", 3), ("b", 2), ("a", 1)]);
}
#[test]
fn into_iter_ref() {
let mut cache = MruCore::new(10);
cache.insert("a", 1);
cache.insert("b", 2);
let pairs: Vec<_> = (&cache).into_iter().collect();
assert_eq!(pairs, vec![(&"b", &2), (&"a", &1)]);
}
}
mod clone_tests {
use super::*;
#[test]
fn clone_preserves_entries() {
let mut cache = MruCore::new(10);
cache.insert("a", 1);
cache.insert("b", 2);
cache.insert("c", 3);
let cloned = cache.clone();
assert_eq!(cloned.len(), 3);
assert_eq!(cloned.capacity(), 10);
}
#[test]
fn clone_is_independent() {
let mut cache = MruCore::new(10);
cache.insert("a", 1);
cache.insert("b", 2);
let mut cloned = cache.clone();
cloned.insert("c", 3);
assert_eq!(cache.len(), 2);
assert_eq!(cloned.len(), 3);
assert!(!cache.contains(&"c"));
}
#[test]
fn clone_preserves_order() {
let mut cache = MruCore::new(10);
cache.insert("a", 1);
cache.insert("b", 2);
cache.insert("c", 3);
cache.get(&"a");
let cloned = cache.clone();
let original_order: Vec<_> = cache.iter().map(|(k, _)| *k).collect();
let cloned_order: Vec<_> = cloned.iter().map(|(k, _)| *k).collect();
assert_eq!(original_order, cloned_order);
}
#[test]
fn clone_empty_cache() {
let cache: MruCore<&str, i32> = MruCore::new(100);
let cloned = cache.clone();
assert!(cloned.is_empty());
assert_eq!(cloned.capacity(), 100);
}
}
}