Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PlainDB for multi-trie multi-hash #2

Merged
merged 6 commits into from
Feb 6, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion hash-db/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "hash-db"
version = "0.9.0"
version = "0.11.0"
authors = ["Parity Technologies <admin@parity.io>"]
description = "Trait for hash-keyed databases."
license = "Apache-2.0"
Expand Down
89 changes: 85 additions & 4 deletions hash-db/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,56 @@ pub trait Hasher: Sync + Send {
fn hash(x: &[u8]) -> Self::Out;
}

/// Trait modelling a plain datastore whose key is a fixed type.
/// The caller should ensure that a key only corresponds to
/// one value.
#[cfg(feature = "std")]
pub trait PlainDB<K, V>: Send + Sync + AsPlainDB<K, V> {
/// Look up a given hash into the bytes that hash to it, returning None if the
/// hash is not known.
fn get(&self, key: &K) -> Option<V>;

/// Check for the existance of a hash-key.
fn contains(&self, key: &K) -> bool;

/// Insert a datum item into the DB. Insertions are counted and the equivalent
/// number of `remove()`s must be performed before the data is considered dead.
/// The caller should ensure that a key only corresponds to one value.
fn emplace(&mut self, key: K, value: V);

/// Remove a datum previously inserted. Insertions can be "owed" such that the
/// same number of `insert()`s may happen without the data being eventually
/// being inserted into the DB. It can be "owed" more than once.
/// The caller should ensure that a key only corresponds to one value.
fn remove(&mut self, key: &K);
}

/// Trait for immutable reference of PlainDB.
#[cfg(feature = "std")]
pub trait PlainDBRef<K, V> {
/// Look up a given hash into the bytes that hash to it, returning None if the
/// hash is not known.
fn get(&self, key: &K) -> Option<V>;

/// Check for the existance of a hash-key.
fn contains(&self, key: &K) -> bool;
}

#[cfg(feature = "std")]
impl<'a, K, V> PlainDBRef<K, V> for &'a PlainDB<K, V> {
fn get(&self, key: &K) -> Option<V> { PlainDB::get(*self, key) }
fn contains(&self, key: &K) -> bool { PlainDB::contains(*self, key) }
}

#[cfg(feature = "std")]
impl<'a, K, V> PlainDBRef<K, V> for &'a mut PlainDB<K, V> {
fn get(&self, key: &K) -> Option<V> { PlainDB::get(*self, key) }
fn contains(&self, key: &K) -> bool { PlainDB::contains(*self, key) }
}

/// Trait modelling datastore keyed by a hash defined by the `Hasher`.
#[cfg(feature = "std")]
pub trait HashDB<H: Hasher, T>: Send + Sync + AsHashDB<H, T> {
/// Get the keys in the database together with number of underlying references.
fn keys(&self) -> HashMap<H::Out, i32>;

/// Look up a given hash into the bytes that hash to it, returning None if the
/// hash is not known.
fn get(&self, key: &H::Out) -> Option<T>;
Expand All @@ -76,7 +120,30 @@ pub trait HashDB<H: Hasher, T>: Send + Sync + AsHashDB<H, T> {
fn remove(&mut self, key: &H::Out);
}

/// Upcast trait.
/// Trait for immutable reference of HashDB.
#[cfg(feature = "std")]
pub trait HashDBRef<H: Hasher, T> {
/// Look up a given hash into the bytes that hash to it, returning None if the
/// hash is not known.
fn get(&self, key: &H::Out) -> Option<T>;

/// Check for the existance of a hash-key.
fn contains(&self, key: &H::Out) -> bool;
}

#[cfg(feature = "std")]
impl<'a, H: Hasher, T> HashDBRef<H, T> for &'a HashDB<H, T> {
fn get(&self, key: &H::Out) -> Option<T> { HashDB::get(*self, key) }
fn contains(&self, key: &H::Out) -> bool { HashDB::contains(*self, key) }
}

#[cfg(feature = "std")]
impl<'a, H: Hasher, T> HashDBRef<H, T> for &'a mut HashDB<H, T> {
fn get(&self, key: &H::Out) -> Option<T> { HashDB::get(*self, key) }
fn contains(&self, key: &H::Out) -> bool { HashDB::contains(*self, key) }
}

/// Upcast trait for HashDB.
#[cfg(feature = "std")]
pub trait AsHashDB<H: Hasher, T> {
/// Perform upcast to HashDB for anything that derives from HashDB.
Expand All @@ -85,6 +152,15 @@ pub trait AsHashDB<H: Hasher, T> {
fn as_hash_db_mut<'a>(&'a mut self) -> &'a mut (HashDB<H, T> + 'a);
}

/// Upcast trait for PlainDB.
#[cfg(feature = "std")]
pub trait AsPlainDB<K, V> {
/// Perform upcast to PlainDB for anything that derives from PlainDB.
fn as_plain_db(&self) -> &PlainDB<K, V>;
/// Perform mutable upcast to PlainDB for anything that derives from PlainDB.
fn as_plain_db_mut<'a>(&'a mut self) -> &'a mut (PlainDB<K, V> + 'a);
}

// NOTE: There used to be a `impl<T> AsHashDB for T` but that does not work with generics. See https://stackoverflow.com/questions/48432842/implementing-a-trait-for-reference-and-non-reference-types-causes-conflicting-im
// This means we need concrete impls of AsHashDB in several places, which somewhat defeats the point of the trait.
#[cfg(feature = "std")]
Expand All @@ -93,3 +169,8 @@ impl<'a, H: Hasher, T> AsHashDB<H, T> for &'a mut HashDB<H, T> {
fn as_hash_db_mut<'b>(&'b mut self) -> &'b mut (HashDB<H, T> + 'b) { &mut **self }
}

#[cfg(feature = "std")]
impl<'a, K, V> AsPlainDB<K, V> for &'a mut PlainDB<K, V> {
fn as_plain_db(&self) -> &PlainDB<K, V> { &**self }
fn as_plain_db_mut<'b>(&'b mut self) -> &'b mut (PlainDB<K, V> + 'b) { &mut **self }
}
2 changes: 1 addition & 1 deletion hash256-std-hasher/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "hash256-std-hasher"
description = "Standard library hasher for 256-bit prehashed keys."
version = "0.9.1"
version = "0.11.0"
authors = ["Parity Technologies <admin@parity.io>"]
license = "Apache-2.0"
homepage = "https://github.com/paritytech/trie"
Expand Down
6 changes: 3 additions & 3 deletions memory-db/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
[package]
name = "memory-db"
version = "0.9.1"
version = "0.11.0"
authors = ["Parity Technologies <admin@parity.io>"]
description = "In-memory implementation of hash-db, useful for tests"
repository = "https://github.com/paritytech/parity-common"
license = "Apache-2.0"

[dependencies]
heapsize = "0.4"
hash-db = { path = "../hash-db", version = "0.9.0"}
hash-db = { path = "../hash-db", version = "0.11.0"}

[dev-dependencies]
keccak-hasher = { path = "../test-support/keccak-hasher", version = "0.2.1"}
keccak-hasher = { path = "../test-support/keccak-hasher", version = "0.11.0"}
criterion = "0.2.8"

[[bench]]
Expand Down
143 changes: 91 additions & 52 deletions memory-db/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ extern crate hash_db;
extern crate heapsize;
#[cfg(test)] extern crate keccak_hasher;

use hash_db::{HashDB, Hasher as KeyHasher, AsHashDB};
use hash_db::{HashDB, HashDBRef, PlainDB, PlainDBRef, Hasher as KeyHasher, AsHashDB, AsPlainDB};
use heapsize::HeapSizeOf;
use std::collections::hash_map::Entry;
use std::collections::HashMap;
Expand All @@ -42,9 +42,9 @@ type FastMap<H, T> = HashMap<<H as KeyHasher>::Out, T, hash::BuildHasherDefault<
/// extern crate keccak_hasher;
/// extern crate memory_db;
///
/// use hash_db::*;
/// use hash_db::{Hasher, HashDB};
/// use keccak_hasher::KeccakHasher;
/// use memory_db::*;
/// use memory_db::MemoryDB;
/// fn main() {
/// let mut m = MemoryDB::<KeccakHasher, Vec<u8>>::default();
/// let d = "Hello world!".as_bytes();
Expand Down Expand Up @@ -121,7 +121,6 @@ where
}

impl<'a, H: KeyHasher, T> MemoryDB<H, T> where T: From<&'a [u8]> {

/// Create a new `MemoryDB` from a given null key/data
pub fn from_null_node(null_key: &'a [u8], null_node_data: T) -> Self {
MemoryDB {
Expand All @@ -148,9 +147,9 @@ impl<'a, H: KeyHasher, T> MemoryDB<H, T> where T: From<&'a [u8]> {
/// extern crate keccak_hasher;
/// extern crate memory_db;
///
/// use hash_db::*;
/// use hash_db::{Hasher, HashDB};
/// use keccak_hasher::KeccakHasher;
/// use memory_db::*;
/// use memory_db::MemoryDB;
///
/// fn main() {
/// let mut m = MemoryDB::<KeccakHasher, Vec<u8>>::default();
Expand Down Expand Up @@ -204,6 +203,17 @@ impl<'a, H: KeyHasher, T> MemoryDB<H, T> where T: From<&'a [u8]> {
}
}
}

/// Get the keys in the database together with number of underlying references.
pub fn keys(&self) -> HashMap<H::Out, i32> {
self.data.iter()
.filter_map(|(k, v)| if v.1 != 0 {
Some((*k, v.1))
} else {
None
})
.collect()
}
}

impl<H, T> MemoryDB<H, T>
Expand All @@ -218,48 +228,26 @@ where
}
}

impl<H, T> HashDB<H, T> for MemoryDB<H, T>
impl<H, T> PlainDB<H::Out, T> for MemoryDB<H, T>
where
H: KeyHasher,
T: Default + PartialEq<T> + for<'a> From<&'a [u8]> + Clone + Send + Sync,
{
fn keys(&self) -> HashMap<H::Out, i32> {
self.data.iter()
.filter_map(|(k, v)| if v.1 != 0 {
Some((*k, v.1))
} else {
None
})
.collect()
}

fn get(&self, key: &H::Out) -> Option<T> {
if key == &self.hashed_null_node {
return Some(self.null_node_data.clone());
}

match self.data.get(key) {
Some(&(ref d, rc)) if rc > 0 => Some(d.clone()),
_ => None
}
}

fn contains(&self, key: &H::Out) -> bool {
if key == &self.hashed_null_node {
return true;
}

match self.data.get(key) {
Some(&(_, x)) if x > 0 => true,
_ => false
}
}

fn emplace(&mut self, key:H::Out, value: T) {
if value == self.null_node_data {
return;
}

fn emplace(&mut self, key: H::Out, value: T) {
match self.data.entry(key) {
Entry::Occupied(mut entry) => {
let &mut (ref mut old_value, ref mut rc) = entry.get_mut();
Expand All @@ -274,23 +262,65 @@ where
}
}

fn insert(&mut self, value: &[u8]) -> H::Out {
if T::from(value) == self.null_node_data {
return self.hashed_null_node.clone();
}
let key = H::hash(value);
match self.data.entry(key) {
fn remove(&mut self, key: &H::Out) {
match self.data.entry(*key) {
Entry::Occupied(mut entry) => {
let &mut (ref mut old_value, ref mut rc) = entry.get_mut();
if *rc <= 0 {
*old_value = value.into();
}
*rc += 1;
let &mut (_, ref mut rc) = entry.get_mut();
*rc -= 1;
},
Entry::Vacant(entry) => {
entry.insert((value.into(), 1));
entry.insert((T::default(), -1));
},
}
}
}

impl<H, T> PlainDBRef<H::Out, T> for MemoryDB<H, T>
where
H: KeyHasher,
T: Default + PartialEq<T> + for<'a> From<&'a [u8]> + Clone + Send + Sync,
{
fn get(&self, key: &H::Out) -> Option<T> { PlainDB::get(self, key) }
fn contains(&self, key: &H::Out) -> bool { PlainDB::contains(self, key) }
}

impl<H, T> HashDB<H, T> for MemoryDB<H, T>
where
H: KeyHasher,
T: Default + PartialEq<T> + for<'a> From<&'a [u8]> + Clone + Send + Sync,
{
fn get(&self, key: &H::Out) -> Option<T> {
if key == &self.hashed_null_node {
return Some(self.null_node_data.clone());
}

PlainDB::get(self, key)
}

fn contains(&self, key: &H::Out) -> bool {
if key == &self.hashed_null_node {
return true;
}

PlainDB::contains(self, key)
}

fn emplace(&mut self, key: H::Out, value: T) {
if value == self.null_node_data {
return;
}

PlainDB::emplace(self, key, value)
}

fn insert(&mut self, value: &[u8]) -> H::Out {
if T::from(value) == self.null_node_data {
return self.hashed_null_node.clone();
}

let key = H::hash(value);
PlainDB::emplace(self, key.clone(), value.into());

key
}

Expand All @@ -299,17 +329,26 @@ where
return;
}

match self.data.entry(*key) {
Entry::Occupied(mut entry) => {
let &mut (_, ref mut rc) = entry.get_mut();
*rc -= 1;
},
Entry::Vacant(entry) => {
entry.insert((T::default(), -1));
},
}
PlainDB::remove(self, key)
}
}

impl<H, T> HashDBRef<H, T> for MemoryDB<H, T>
where
H: KeyHasher,
T: Default + PartialEq<T> + for<'a> From<&'a [u8]> + Clone + Send + Sync,
{
fn get(&self, key: &H::Out) -> Option<T> { HashDB::get(self, key) }
fn contains(&self, key: &H::Out) -> bool { HashDB::contains(self, key) }
}

impl<H, T> AsPlainDB<H::Out, T> for MemoryDB<H, T>
where
H: KeyHasher,
T: Default + PartialEq<T> + for<'a> From<&'a[u8]> + Clone + Send + Sync,
{
fn as_plain_db(&self) -> &PlainDB<H::Out, T> { self }
fn as_plain_db_mut(&mut self) -> &mut PlainDB<H::Out, T> { self }
}

impl<H, T> AsHashDB<H, T> for MemoryDB<H, T>
Expand All @@ -323,7 +362,7 @@ where

#[cfg(test)]
mod tests {
use super::*;
use super::{MemoryDB, HashDB, KeyHasher};
use keccak_hasher::KeccakHasher;

#[test]
Expand Down
Loading