use lru::LruCache;
use rustc_hash::FxHasher;
use std::hash::{BuildHasherDefault, Hash, Hasher};
use std::num::NonZeroUsize;
use std::sync::Mutex;
const SHARDS: usize = 64;
type Shard = Mutex<LruCache<Vec<u8>, Vec<u32>, BuildHasherDefault<FxHasher>>>;
pub(crate) struct ChunkCache {
shards: Box<[Shard]>,
}
impl ChunkCache {
pub(crate) fn new(capacity: usize) -> Self {
let per_shard = NonZeroUsize::new(capacity / SHARDS).unwrap_or(NonZeroUsize::MIN);
Self {
shards: (0..SHARDS)
.map(|_| {
Mutex::new(LruCache::with_hasher(
per_shard,
BuildHasherDefault::default(),
))
})
.collect(),
}
}
fn shard(&self, key: &[u8]) -> &Shard {
let mut hasher = FxHasher::default();
key.hash(&mut hasher);
&self.shards[(hasher.finish() >> 32) as usize % SHARDS]
}
pub(crate) fn extend_into(&self, key: &[u8], out: &mut Vec<u32>) -> bool {
let Ok(mut shard) = self.shard(key).lock() else {
return false;
};
match shard.get(key) {
Some(ids) => {
out.extend_from_slice(ids);
true
}
None => false,
}
}
pub(crate) fn put(&self, key: &[u8], ids: &[u32]) {
if let Ok(mut shard) = self.shard(key).lock() {
shard.put(key.to_vec(), ids.to_vec());
}
}
pub(crate) fn clear(&self) {
for shard in &self.shards {
if let Ok(mut shard) = shard.lock() {
shard.clear();
}
}
}
pub(crate) fn len(&self) -> usize {
self.shards
.iter()
.map(|shard| shard.lock().map(|s| s.len()).unwrap_or(0))
.sum()
}
}
impl std::fmt::Debug for ChunkCache {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ChunkCache")
.field("shards", &SHARDS)
.field("len", &self.len())
.finish()
}
}