use crate::Database;
use parking_lot::RwLock;
use smallvec::SmallVec;
use std::{
cell::RefCell,
hash::{Hash, Hasher},
sync::Arc,
};
pub trait Cache: Clone + Default + std::fmt::Debug {
fn read_db(&self, name_hash: u64) -> Option<Database>;
fn write_db(&self, db: CachedDb);
fn remove_dbi(&self, dbi: ffi::MDBX_dbi);
}
#[derive(Debug, Clone, Copy)]
pub struct CachedDb {
name_hash: u64,
db: Database,
}
impl CachedDb {
pub(crate) fn new(name: Option<&str>, db: Database) -> Self {
let name_hash = Self::hash_name(name);
Self { name_hash, db }
}
#[inline]
pub(crate) fn hash_name(name: Option<&str>) -> u64 {
let mut hasher = std::hash::DefaultHasher::new();
name.hash(&mut hasher);
hasher.finish()
}
}
impl From<CachedDb> for Database {
fn from(value: CachedDb) -> Self {
value.db
}
}
#[derive(Debug, Default, Clone)]
#[repr(transparent)]
pub struct DbCache(SmallVec<[CachedDb; 16]>);
impl DbCache {
fn read_db(&self, name_hash: u64) -> Option<Database> {
for entry in self.0.iter() {
if entry.name_hash == name_hash {
return Some(entry.db);
}
}
None
}
fn write_db(&mut self, db: CachedDb) {
for entry in self.0.iter() {
if entry.name_hash == db.name_hash {
return; }
}
self.0.push(db);
}
fn remove_dbi(&mut self, dbi: ffi::MDBX_dbi) {
self.0.retain(|entry| entry.db.dbi() != dbi);
}
}
#[derive(Debug, Clone)]
pub struct SharedCache {
cache: Arc<RwLock<DbCache>>,
}
impl SharedCache {
fn new() -> Self {
Self { cache: Arc::new(RwLock::new(DbCache::default())) }
}
fn read(&self) -> parking_lot::RwLockReadGuard<'_, DbCache> {
self.cache.read()
}
fn write(&self) -> parking_lot::RwLockWriteGuard<'_, DbCache> {
self.cache.write()
}
}
impl Cache for SharedCache {
fn read_db(&self, name_hash: u64) -> Option<Database> {
let cache = self.read();
cache.read_db(name_hash)
}
fn write_db(&self, db: CachedDb) {
let mut cache = self.write();
cache.write_db(db);
}
fn remove_dbi(&self, dbi: ffi::MDBX_dbi) {
let mut cache = self.write();
cache.remove_dbi(dbi);
}
}
impl Default for SharedCache {
fn default() -> Self {
Self::new()
}
}
impl Cache for RefCell<DbCache> {
fn read_db(&self, name_hash: u64) -> Option<Database> {
let cache = self.borrow();
cache.read_db(name_hash)
}
fn write_db(&self, db: CachedDb) {
let mut cache = self.borrow_mut();
cache.write_db(db);
}
fn remove_dbi(&self, dbi: ffi::MDBX_dbi) {
let mut cache = self.borrow_mut();
cache.remove_dbi(dbi);
}
}