use std::sync::atomic::{AtomicU32, Ordering};
use rspack_util::fx_hash::FxDashMap;
use super::{
CacheKey, CacheValue, Etag,
cache_value::{CacheEntry, CacheValueData},
};
#[derive(Debug)]
pub enum MemoryCacheGetResult<T> {
NotCached,
Miss,
Hit(CacheValue<T>),
}
impl<T> Clone for MemoryCacheGetResult<T> {
fn clone(&self) -> Self {
match self {
Self::NotCached => Self::NotCached,
Self::Miss => Self::Miss,
Self::Hit(value) => Self::Hit(value.clone()),
}
}
}
#[derive(Debug)]
enum MemoryCacheValue {
Miss,
Hit(CacheEntry),
}
#[derive(Debug)]
struct MemoryCacheEntry {
value: MemoryCacheValue,
ttl: AtomicU32,
}
impl MemoryCacheEntry {
fn new(value: MemoryCacheValue, ttl: u32) -> Self {
Self {
value,
ttl: AtomicU32::new(ttl),
}
}
fn refresh(&self, ttl: u32) {
self.ttl.store(ttl, Ordering::Relaxed);
}
fn start_next_generation(&mut self) -> bool {
let ttl = self.ttl.get_mut();
if *ttl == 0 {
false
} else {
*ttl -= 1;
true
}
}
}
#[derive(Debug)]
pub struct MemoryCache {
max_generations: u32,
entries: FxDashMap<CacheKey, MemoryCacheEntry>,
}
impl Default for MemoryCache {
fn default() -> Self {
Self::new(1)
}
}
impl MemoryCache {
pub fn new(max_generations: u32) -> Self {
Self {
max_generations,
entries: FxDashMap::default(),
}
}
pub fn get<T: CacheValueData>(
&self,
key: &CacheKey,
etag: Option<&Etag>,
) -> MemoryCacheGetResult<T> {
let Some(entry) = self.entries.get(key) else {
return MemoryCacheGetResult::NotCached;
};
entry.refresh(self.max_generations);
let entry = match &entry.value {
MemoryCacheValue::Miss => return MemoryCacheGetResult::Miss,
MemoryCacheValue::Hit(entry) => entry,
};
if entry.matches(etag) {
entry
.value()
.clone()
.downcast()
.map_or(MemoryCacheGetResult::Miss, MemoryCacheGetResult::Hit)
} else {
MemoryCacheGetResult::Miss
}
}
pub fn store<T: CacheValueData>(&self, key: CacheKey, etag: Option<Etag>, value: CacheValue<T>) {
self.entries.insert(
key,
MemoryCacheEntry::new(
MemoryCacheValue::Hit(CacheEntry::new(etag, value.erase())),
self.max_generations,
),
);
}
pub fn store_miss(&self, key: CacheKey) {
self.entries.insert(
key,
MemoryCacheEntry::new(MemoryCacheValue::Miss, self.max_generations),
);
}
pub fn start_next_generation(&self) {
self
.entries
.retain(|_, entry| entry.start_next_generation());
}
pub fn clear(&self) {
self.entries.clear();
}
}