use std::sync::Arc;
use quick_cache::Weighter;
use quick_cache::sync::Cache;
use crate::config::CacheConfig;
use crate::io::aligned_buf::AlignedBuf;
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub struct BlockKey {
pub shard_id: u8,
pub file_id: u32,
pub block_offset: u64,
}
#[derive(Clone)]
struct BlockWeighter;
impl Weighter<BlockKey, Arc<AlignedBuf>> for BlockWeighter {
fn weight(&self, _key: &BlockKey, _value: &Arc<AlignedBuf>) -> u64 {
4096
}
}
pub struct BlockCache {
inner: Option<Cache<BlockKey, Arc<AlignedBuf>, BlockWeighter>>,
}
impl BlockCache {
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, BlockWeighter);
Self { inner: Some(cache) }
}
pub fn get(&self, key: &BlockKey) -> Option<Arc<AlignedBuf>> {
self.inner.as_ref()?.get(key)
}
pub fn insert(&self, key: BlockKey, block: Arc<AlignedBuf>) {
if let Some(cache) = &self.inner {
cache.insert(key, block);
}
}
pub fn invalidate_file(&self, shard_id: u8, file_id: u32, total_bytes: u64) {
if let Some(cache) = &self.inner {
let num_blocks = total_bytes.div_ceil(4096);
for i in 0..num_blocks {
cache.remove(&BlockKey {
shard_id,
file_id,
block_offset: i * 4096,
});
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::config::CacheConfig;
use crate::io::aligned_buf::AlignedBuf;
fn make_marked_buf(marker: u8) -> Arc<AlignedBuf> {
let mut buf = AlignedBuf::zeroed(4096);
buf[0] = marker;
Arc::new(buf)
}
#[test]
fn block_cache_disambiguates_file_ids_across_u16_boundary() {
let cfg = CacheConfig {
max_size: 1 << 20,
estimated_items: 32,
};
let cache = BlockCache::new(&cfg);
let buf_a = make_marked_buf(0xAA);
let buf_b = make_marked_buf(0xBB);
let key_low = BlockKey {
shard_id: 0,
file_id: 65_535,
block_offset: 0,
};
let key_high = BlockKey {
shard_id: 0,
file_id: 65_536,
block_offset: 0,
};
cache.insert(key_low, buf_a.clone());
cache.insert(key_high, buf_b.clone());
let got_low = cache.get(&key_low).expect("low present");
let got_high = cache.get(&key_high).expect("high present");
assert_eq!(got_low[0], 0xAA);
assert_eq!(got_high[0], 0xBB);
assert!(!Arc::ptr_eq(&got_low, &got_high));
}
}