use crate::{CASDatabase, KVDatabase, MemDb};
use dashmap::DashSet;
use gear_core::utils;
use gprimitives::H256;
use std::{collections::HashSet, sync::Arc};
pub struct CASOverlay {
db: Box<dyn CASDatabase>,
mem: MemDb,
}
impl CASOverlay {
pub fn new(db: Box<dyn CASDatabase>) -> Self {
Self {
db,
mem: MemDb::default(),
}
}
}
impl CASDatabase for CASOverlay {
fn clone_boxed(&self) -> Box<dyn CASDatabase> {
Box::new(Self {
db: self.db.clone_boxed(),
mem: self.mem.clone(),
})
}
fn read(&self, hash: H256) -> Option<Vec<u8>> {
self.mem.read(hash).or_else(|| self.db.read(hash))
}
fn contains(&self, hash: H256) -> bool {
CASDatabase::contains(&self.mem, hash) || CASDatabase::contains(&*self.db, hash)
}
fn write(&self, data: &[u8]) -> H256 {
self.mem.write(data)
}
}
pub struct KVOverlay {
db: Box<dyn KVDatabase>,
mem: MemDb,
erased_keys: Arc<DashSet<Vec<u8>>>,
}
impl KVOverlay {
pub fn new(db: Box<dyn KVDatabase>) -> Self {
Self {
db,
mem: MemDb::default(),
erased_keys: Default::default(),
}
}
fn is_erased(&self, key: &[u8]) -> bool {
self.erased_keys.contains(key)
}
fn erase(&self, key: Vec<u8>) -> bool {
self.erased_keys.insert(key)
}
}
impl KVDatabase for KVOverlay {
fn clone_boxed(&self) -> Box<dyn KVDatabase> {
Box::new(Self {
db: self.db.clone_boxed(),
mem: self.mem.clone(),
erased_keys: self.erased_keys.clone(),
})
}
fn get(&self, key: &[u8]) -> Option<Vec<u8>> {
self.mem.get(key).or_else(|| {
if !self.is_erased(key) {
self.db.get(key)
} else {
None
}
})
}
unsafe fn take(&self, key: &[u8]) -> Option<Vec<u8>> {
if !self.is_erased(key) {
unsafe {
self.mem.take(key).or_else(|| {
self.db.get(key).inspect(|_| {
self.erase(key.to_vec());
})
})
}
} else {
None
}
}
unsafe fn delete(&self, key: &[u8]) {
unsafe { self.mem.delete(key) };
self.erase(key.to_vec());
}
fn contains(&self, key: &[u8]) -> bool {
KVDatabase::contains(&self.mem, key)
|| (!self.is_erased(key) && KVDatabase::contains(&*self.db, key))
}
fn put(&self, key: &[u8], value: Vec<u8>) {
self.erased_keys.remove(key);
self.mem.put(key, value)
}
fn iter_prefix<'a>(
&'a self,
prefix: &'a [u8],
) -> Box<dyn Iterator<Item = (Vec<u8>, Vec<u8>)> + 'a> {
let mem_iter = self.mem.iter_prefix(prefix);
let db_iter = self
.db
.iter_prefix(prefix)
.filter(|(key, _)| !self.is_erased(key));
let full_iter = mem_iter.chain(db_iter);
let mut known_keys = HashSet::new();
let filtered_iter = full_iter
.filter_map(move |(k, v)| known_keys.insert(utils::hash(&k)).then_some((k, v)));
Box::new(filtered_iter)
}
fn is_empty(&self) -> bool {
self.mem.is_empty()
&& match (self.db.is_empty(), self.erased_keys.is_empty()) {
(true, _) => true,
(false, true) => false,
(false, false) => {
unimplemented!()
}
}
}
}