use std::hash::{BuildHasher, Hasher};
use cached::{Cached, LruCache, UnboundCache};
struct FnvHasher(u64);
impl Default for FnvHasher {
fn default() -> Self {
Self(0xcbf2_9ce4_8422_2325)
}
}
impl Hasher for FnvHasher {
fn write(&mut self, bytes: &[u8]) {
for &b in bytes {
self.0 ^= b as u64;
self.0 = self.0.wrapping_mul(0x0000_0100_0000_01b3);
}
}
fn finish(&self) -> u64 {
self.0
}
}
#[derive(Clone, Default)]
struct FnvBuildHasher;
impl BuildHasher for FnvBuildHasher {
type Hasher = FnvHasher;
fn build_hasher(&self) -> Self::Hasher {
FnvHasher::default()
}
}
#[test]
fn unbound_cache_custom_hasher_hit_and_miss() {
let mut cache = UnboundCache::<String, u64>::builder()
.hasher(FnvBuildHasher)
.build()
.expect("build UnboundCache with FnvBuildHasher");
assert_eq!(
cache.cache_get(&"absent".to_string()),
None,
"cache miss on absent key"
);
cache.cache_set("alpha".to_string(), 1);
cache.cache_set("beta".to_string(), 2);
cache.cache_set("gamma".to_string(), 3);
assert_eq!(cache.cache_get(&"alpha".to_string()), Some(&1));
assert_eq!(cache.cache_get(&"beta".to_string()), Some(&2));
assert_eq!(cache.cache_get(&"gamma".to_string()), Some(&3));
assert_eq!(cache.cache_get(&"delta".to_string()), None);
cache.cache_set("alpha".to_string(), 99);
assert_eq!(
cache.cache_get(&"alpha".to_string()),
Some(&99),
"overwritten value must be returned"
);
let _ = cache.cache_remove(&"beta".to_string());
assert_eq!(
cache.cache_get(&"beta".to_string()),
None,
"removed entry must return None"
);
}
#[test]
fn unbound_cache_custom_hasher_metrics() {
let mut cache = UnboundCache::<String, u64>::builder()
.hasher(FnvBuildHasher)
.build()
.expect("build UnboundCache with FnvBuildHasher");
let hits_before = cache.cache_hits().unwrap_or(0);
let misses_before = cache.cache_misses().unwrap_or(0);
cache.cache_set("x".to_string(), 42);
cache.cache_get(&"absent".to_string());
cache.cache_get(&"x".to_string());
let hits_after = cache.cache_hits().unwrap_or(0);
let misses_after = cache.cache_misses().unwrap_or(0);
assert!(
hits_after > hits_before,
"hit counter must have incremented"
);
assert!(
misses_after > misses_before,
"miss counter must have incremented"
);
}
#[test]
fn lru_cache_custom_hasher_hit_and_miss() {
let mut cache = LruCache::<String, u64>::builder()
.max_size(8)
.hasher(FnvBuildHasher)
.build()
.expect("build LruCache with FnvBuildHasher");
assert_eq!(cache.cache_get(&"absent".to_string()), None);
cache.cache_set("a".to_string(), 10);
cache.cache_set("b".to_string(), 20);
cache.cache_set("c".to_string(), 30);
assert_eq!(cache.cache_get(&"a".to_string()), Some(&10));
assert_eq!(cache.cache_get(&"b".to_string()), Some(&20));
assert_eq!(cache.cache_get(&"c".to_string()), Some(&30));
assert_eq!(cache.cache_get(&"z".to_string()), None);
}
#[test]
fn lru_cache_custom_hasher_eviction() {
let mut cache = LruCache::<String, u64>::builder()
.max_size(2)
.hasher(FnvBuildHasher)
.build()
.expect("build LruCache(2) with FnvBuildHasher");
cache.cache_set("first".to_string(), 1); cache.cache_set("second".to_string(), 2);
cache.cache_get(&"first".to_string());
cache.cache_set("third".to_string(), 3);
assert_eq!(
cache.cache_get(&"second".to_string()),
None,
"LRU entry (second) must have been evicted"
);
assert_eq!(cache.cache_get(&"first".to_string()), Some(&1));
assert_eq!(cache.cache_get(&"third".to_string()), Some(&3));
assert_eq!(
cache.cache_evictions(),
Some(1),
"eviction counter must be 1 after one LRU eviction"
);
}