use std::convert::AsRef;
use std::hash::Hash;
use std::collections::HashMap;
use parking_lot::RwLock;
use tetsy_kvdb::{DBTransaction, KeyValueDB};
use tetsy_rlp;
pub const COL_STATE: u32 = 0;
pub const COL_HEADERS: u32 = 1;
pub const COL_BODIES: u32 = 2;
pub const COL_EXTRA: u32 = 3;
pub const COL_TRACE: u32 = 4;
pub const COL_ACCOUNT_BLOOM: u32 = 5;
pub const COL_NODE_INFO: u32 = 6;
pub const COL_LIGHT_CHAIN: u32 = 7;
pub const COL_PRIVATE_TRANSACTIONS_STATE: u32 = 8;
pub const NUM_COLUMNS: u32 = 9;
#[derive(Clone, Copy)]
pub enum CacheUpdatePolicy {
Overwrite,
Remove,
}
pub trait Cache<K, V> {
fn insert(&mut self, k: K, v: V) -> Option<V>;
fn remove(&mut self, k: &K) -> Option<V>;
fn get(&self, k: &K) -> Option<&V>;
}
impl<K, V> Cache<K, V> for HashMap<K, V> where K: Hash + Eq {
fn insert(&mut self, k: K, v: V) -> Option<V> {
HashMap::insert(self, k, v)
}
fn remove(&mut self, k: &K) -> Option<V> {
HashMap::remove(self, k)
}
fn get(&self, k: &K) -> Option<&V> {
HashMap::get(self, k)
}
}
pub trait Key<T> {
type Target: AsRef<[u8]>;
fn key(&self) -> Self::Target;
}
pub trait Writable {
fn write<T, R>(&mut self, col: u32, key: &dyn Key<T, Target = R>, value: &T) where T: tetsy_rlp::Encodable, R: AsRef<[u8]>;
fn delete<T, R>(&mut self, col: u32, key: &dyn Key<T, Target = R>) where T: tetsy_rlp::Encodable, R: AsRef<[u8]>;
fn write_with_cache<K, T, R>(
&mut self,
col: u32,
cache: &mut dyn Cache<K, T>,
key: K,
value: T,
policy: CacheUpdatePolicy
)
where
K: Key<T, Target = R> + Hash + Eq,
T: tetsy_rlp::Encodable,
R: AsRef<[u8]>
{
self.write(col, &key, &value);
match policy {
CacheUpdatePolicy::Overwrite => {
cache.insert(key, value);
},
CacheUpdatePolicy::Remove => {
cache.remove(&key);
}
}
}
fn extend_with_cache<K, T, R>(
&mut self,
col: u32,
cache: &mut dyn Cache<K, T>,
values: HashMap<K, T>,
policy: CacheUpdatePolicy
)
where
K: Key<T, Target = R> + Hash + Eq,
T: tetsy_rlp::Encodable,
R: AsRef<[u8]>
{
match policy {
CacheUpdatePolicy::Overwrite => {
for (key, value) in values {
self.write(col, &key, &value);
cache.insert(key, value);
}
},
CacheUpdatePolicy::Remove => {
for (key, value) in &values {
self.write(col, key, value);
cache.remove(key);
}
},
}
}
fn extend_with_option_cache<K, T, R>(
&mut self,
col: u32,
cache: &mut dyn Cache<K, Option<T>>,
values: HashMap<K, Option<T>>,
policy: CacheUpdatePolicy
)
where
K: Key<T, Target = R> + Hash + Eq,
T: tetsy_rlp::Encodable,
R: AsRef<[u8]>
{
match policy {
CacheUpdatePolicy::Overwrite => {
for (key, value) in values {
match value {
Some(ref v) => self.write(col, &key, v),
None => self.delete(col, &key),
}
cache.insert(key, value);
}
},
CacheUpdatePolicy::Remove => {
for (key, value) in values {
match value {
Some(v) => self.write(col, &key, &v),
None => self.delete(col, &key),
}
cache.remove(&key);
}
},
}
}
}
pub trait Readable {
fn read<T, R>(&self, col: u32, key: &dyn Key<T, Target = R>) -> Option<T> where
T: tetsy_rlp::Decodable,
R: AsRef<[u8]>;
fn read_with_cache<K, T, C>(&self, col: u32, cache: &RwLock<C>, key: &K) -> Option<T> where
K: Key<T> + Eq + Hash + Clone,
T: Clone + tetsy_rlp::Decodable,
C: Cache<K, T> {
{
let read = cache.read();
if let Some(v) = read.get(key) {
return Some(v.clone());
}
}
self.read(col, key).map(|value: T|{
let mut write = cache.write();
write.insert(key.clone(), value.clone());
value
})
}
fn read_with_two_layer_cache<K, T, C>(
&self,
col: u32,
l1_cache: &RwLock<C>,
l2_cache: &RwLock<C>,
key: &K
) -> Option<T>
where
K: Key<T> + Eq + Hash + Clone,
T: Clone + tetsy_rlp::Decodable,
C: Cache<K, T>
{
{
let read = l1_cache.read();
if let Some(v) = read.get(key) {
return Some(v.clone());
}
}
self.read_with_cache(col, l2_cache, key)
}
fn exists<T, R>(&self, col: u32, key: &dyn Key<T, Target = R>) -> bool where R: AsRef<[u8]>;
fn exists_with_cache<K, T, R, C>(&self, col: u32, cache: &RwLock<C>, key: &K) -> bool where
K: Eq + Hash + Key<T, Target = R>,
R: AsRef<[u8]>,
C: Cache<K, T> {
{
let read = cache.read();
if read.get(key).is_some() {
return true;
}
}
self.exists::<T, R>(col, key)
}
}
impl Writable for DBTransaction {
fn write<T, R>(&mut self, col: u32, key: &dyn Key<T, Target = R>, value: &T) where T: tetsy_rlp::Encodable, R: AsRef<[u8]> {
self.put(col, key.key().as_ref(), &tetsy_rlp::encode(value));
}
fn delete<T, R>(&mut self, col: u32, key: &dyn Key<T, Target = R>) where T: tetsy_rlp::Encodable, R: AsRef<[u8]> {
self.delete(col, key.key().as_ref());
}
}
impl<KVDB: KeyValueDB + ?Sized> Readable for KVDB {
fn read<T, R>(&self, col: u32, key: &dyn Key<T, Target = R>) -> Option<T>
where T: tetsy_rlp::Decodable, R: AsRef<[u8]> {
self.get(col, key.key().as_ref())
.expect(&format!("db get failed, key: {:?}", key.key().as_ref()))
.map(|v| tetsy_rlp::decode(&v).expect("decode db value failed") )
}
fn exists<T, R>(&self, col: u32, key: &dyn Key<T, Target = R>) -> bool where R: AsRef<[u8]> {
let result = self.get(col, key.key().as_ref());
match result {
Ok(v) => v.is_some(),
Err(err) => {
panic!("db get failed, key: {:?}, err: {:?}", key.key().as_ref(), err);
}
}
}
}