use std::num::NonZeroUsize;
use std::sync::Mutex;
use lru::LruCache;
use xxhash_rust::xxh3::Xxh3;
use crate::eval::{CanvasInfo, TileId};
use crate::value::PortValue;
pub type Hash128 = u128;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct CacheKey(pub Hash128);
impl CacheKey {
pub fn build(
canvas: CanvasInfo,
tile: Option<TileId>,
params_hash: Hash128,
inputs: &[Hash128],
) -> Self {
let mut h = Xxh3::new();
h.update(&canvas.tile_size.to_le_bytes());
h.update(&canvas.pad.to_le_bytes());
if let Some(t) = tile {
h.update(&[t.z]);
h.update(&t.x.to_le_bytes());
h.update(&t.y.to_le_bytes());
}
h.update(¶ms_hash.to_le_bytes());
for i in inputs {
h.update(&i.to_le_bytes());
}
CacheKey(h.digest128())
}
}
pub const DEFAULT_CAPACITY: usize = 4096;
pub struct Cache {
inner: Mutex<LruCache<CacheKey, PortValue>>,
}
impl Default for Cache {
fn default() -> Self {
Self::new()
}
}
impl Cache {
pub fn new() -> Self {
Self::with_capacity(DEFAULT_CAPACITY)
}
pub fn with_capacity(cap: usize) -> Self {
let cap = NonZeroUsize::new(cap.max(1)).expect("cap.max(1) is non-zero");
Self {
inner: Mutex::new(LruCache::new(cap)),
}
}
pub fn get(&self, key: CacheKey) -> Option<PortValue> {
self.lock().get(&key).cloned()
}
pub fn insert(&self, key: CacheKey, value: PortValue) {
self.lock().put(key, value);
}
pub fn len(&self) -> usize {
self.lock().len()
}
pub fn is_empty(&self) -> bool {
self.lock().is_empty()
}
pub fn clear(&self) {
self.lock().clear();
}
pub fn capacity(&self) -> usize {
self.lock().cap().get()
}
fn lock(&self) -> std::sync::MutexGuard<'_, LruCache<CacheKey, PortValue>> {
self.inner.lock().unwrap_or_else(|e| e.into_inner())
}
}