use quick_cache::Weighter;
use quick_cache::sync::Cache;
use crate::byte_view::ByteView;
use crate::config::CacheConfig;
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub struct ValueKey {
pub shard_id: u8,
pub file_id: u32,
pub offset: u32,
}
#[derive(Clone)]
struct ValueWeighter;
impl Weighter<ValueKey, ByteView> for ValueWeighter {
fn weight(&self, _key: &ValueKey, value: &ByteView) -> u64 {
value.len() as u64
}
}
pub struct ValueCache {
inner: Option<Cache<ValueKey, ByteView, ValueWeighter>>,
}
impl ValueCache {
pub fn new(config: &CacheConfig) -> Self {
if config.max_size == 0 {
return Self { inner: None };
}
let cache = Cache::with_weighter(config.estimated_items, config.max_size, ValueWeighter);
Self { inner: Some(cache) }
}
pub fn get(&self, key: &ValueKey) -> Option<ByteView> {
self.inner.as_ref()?.get(key)
}
pub fn insert(&self, key: ValueKey, value: ByteView) {
if let Some(cache) = &self.inner {
cache.insert(key, value);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::config::CacheConfig;
fn key(file_id: u32, offset: u32) -> ValueKey {
ValueKey {
shard_id: 0,
file_id,
offset,
}
}
#[test]
fn roundtrip_and_disambiguation() {
let cfg = CacheConfig {
max_size: 1 << 20,
estimated_items: 16,
};
let cache = ValueCache::new(&cfg);
cache.insert(key(1, 0), ByteView::new(b"alpha"));
cache.insert(key(1, 64), ByteView::new(b"beta"));
cache.insert(key(2, 0), ByteView::new(b"gamma"));
assert_eq!(cache.get(&key(1, 0)).unwrap().as_bytes(), b"alpha");
assert_eq!(cache.get(&key(1, 64)).unwrap().as_bytes(), b"beta");
assert_eq!(cache.get(&key(2, 0)).unwrap().as_bytes(), b"gamma");
assert!(cache.get(&key(9, 9)).is_none());
}
#[test]
fn disabled_when_max_size_zero() {
let cfg = CacheConfig {
max_size: 0,
estimated_items: 16,
};
let cache = ValueCache::new(&cfg);
cache.insert(key(1, 0), ByteView::new(b"x"));
assert!(cache.get(&key(1, 0)).is_none());
}
#[test]
fn capacity_pressure_evicts_something() {
let cfg = CacheConfig {
max_size: 1 << 20,
estimated_items: 64,
};
let cache = ValueCache::new(&cfg);
let blob = ByteView::from_vec(vec![0u8; 64 * 1024]);
for i in 0..64u32 {
cache.insert(key(1, i * 64 * 1024), blob.clone());
}
let present = (0..64u32)
.filter(|i| cache.get(&key(1, i * 64 * 1024)).is_some())
.count();
assert!(
present < 64,
"bounded cache must evict under pressure, got {present}/64"
);
}
}