use std::hash::{BuildHasher, Hasher};
use quick_cache::sync::{Cache, DefaultLifecycle};
use quick_cache::Weighter;
use super::block::Block;
type Key = (u64, u64);
#[derive(Clone)]
struct BlockWeighter;
impl Weighter<Key, Block> for BlockWeighter {
fn weight(&self, _key: &Key, block: &Block) -> u64 {
block.size() as u64 + 64
}
}
#[derive(Clone, Copy, Default)]
struct BlockKeyHash;
#[derive(Default)]
struct BlockKeyHasher(u64);
impl BuildHasher for BlockKeyHash {
type Hasher = BlockKeyHasher;
fn build_hasher(&self) -> BlockKeyHasher {
BlockKeyHasher(0)
}
}
impl Hasher for BlockKeyHasher {
fn write(&mut self, bytes: &[u8]) {
for chunk in bytes.chunks(8) {
let mut word = [0u8; 8];
word[..chunk.len()].copy_from_slice(chunk);
self.write_u64(u64::from_le_bytes(word));
}
}
#[inline]
fn write_u64(&mut self, x: u64) {
self.0 = (self.0.rotate_left(29) ^ x).wrapping_mul(0x9e37_79b9_7f4a_7c15);
}
#[inline]
fn finish(&self) -> u64 {
let mut h = self.0;
h ^= h >> 33;
h = h.wrapping_mul(0xff51_afd7_ed55_8ccd);
h ^= h >> 33;
h = h.wrapping_mul(0xc4ce_b9fe_1a85_ec53);
h ^ (h >> 33)
}
}
pub struct BlockCache {
cache: Cache<Key, Block, BlockWeighter, BlockKeyHash>,
budget: u64,
pinned: std::sync::atomic::AtomicU64,
}
impl BlockCache {
pub fn new(capacity: u64) -> Self {
let items = (capacity / 4096).max(64) as usize;
BlockCache {
cache: Cache::with(
items,
capacity.max(1),
BlockWeighter,
BlockKeyHash,
DefaultLifecycle::default(),
),
budget: capacity.max(1),
pinned: std::sync::atomic::AtomicU64::new(0),
}
}
pub fn charge(&self, bytes: u64) {
let pinned = self
.pinned
.fetch_add(bytes, std::sync::atomic::Ordering::Relaxed)
+ bytes;
self.resize(pinned);
}
pub fn release(&self, bytes: u64) {
let pinned = self
.pinned
.fetch_sub(bytes, std::sync::atomic::Ordering::Relaxed)
- bytes;
self.resize(pinned);
}
fn resize(&self, pinned: u64) {
let capacity = self
.budget
.saturating_sub(pinned)
.max(self.budget / 8)
.max(1);
self.cache.set_capacity(capacity);
}
pub fn pinned(&self) -> u64 {
self.pinned.load(std::sync::atomic::Ordering::Relaxed)
}
pub fn get(&self, table: u64, offset: u64) -> Option<Block> {
self.cache.get(&(table, offset))
}
pub fn insert(&self, table: u64, offset: u64, block: Block) {
self.cache.insert((table, offset), block);
}
pub fn usage(&self) -> u64 {
self.cache.weight()
}
}