Merge branch 'master' of github.com:ethcore/parity into move_hash

This commit is contained in:
debris
2016-08-04 08:41:30 +02:00
66 changed files with 1281 additions and 600 deletions

View File

@@ -28,14 +28,17 @@ use hash::H256;
pub enum BaseDataError {
/// An entry was removed more times than inserted.
NegativelyReferencedHash(H256),
/// A committed value was inserted more than once.
AlreadyExists(H256),
}
impl fmt::Display for BaseDataError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
BaseDataError::NegativelyReferencedHash(hash) =>
f.write_fmt(format_args!("Entry {} removed from database more times \
than it was added.", hash)),
write!(f, "Entry {} removed from database more times than it was added.", hash),
BaseDataError::AlreadyExists(hash) =>
write!(f, "Committed key already exists in database: {}", hash),
}
}
}

View File

@@ -20,7 +20,7 @@ use bytes::*;
use std::collections::HashMap;
/// Trait modelling datastore keyed by a 32-byte Keccak hash.
pub trait HashDB: AsHashDB {
pub trait HashDB: AsHashDB + Send + Sync {
/// Get the keys in the database together with number of underlying references.
fn keys(&self) -> HashMap<H256, i32>;

View File

@@ -88,10 +88,10 @@ impl HashDB for ArchiveDB {
fn get(&self, key: &H256) -> Option<&[u8]> {
let k = self.overlay.raw(key);
match k {
Some(&(ref d, rc)) if rc > 0 => Some(d),
Some((d, rc)) if rc > 0 => Some(d),
_ => {
if let Some(x) = self.payload(key) {
Some(&self.overlay.denote(key, x).0)
Some(self.overlay.denote(key, x).0)
}
else {
None
@@ -185,6 +185,38 @@ impl JournalDB for ArchiveDB {
Ok((inserts + deletes) as u32)
}
fn inject(&mut self, batch: &DBTransaction) -> Result<u32, UtilError> {
let mut inserts = 0usize;
let mut deletes = 0usize;
for i in self.overlay.drain().into_iter() {
let (key, (value, rc)) = i;
if rc > 0 {
assert!(rc == 1);
if try!(self.backing.get(self.column, &key)).is_some() {
return Err(BaseDataError::AlreadyExists(key).into());
}
try!(batch.put(self.column, &key, &value));
inserts += 1;
}
if rc < 0 {
assert!(rc == -1);
if try!(self.backing.get(self.column, &key)).is_none() {
return Err(BaseDataError::NegativelyReferencedHash(key).into());
}
try!(batch.delete(self.column, &key));
deletes += 1;
}
}
for (mut key, value) in self.overlay.drain_aux().into_iter() {
key.push(AUX_FLAG);
try!(batch.put(self.column, &key, &value));
}
Ok((inserts + deletes) as u32)
}
fn latest_era(&self) -> Option<u64> { self.latest_era }
fn state(&self, id: &H256) -> Option<Bytes> {
@@ -449,4 +481,19 @@ mod tests {
assert!(state.is_some());
}
}
#[test]
fn inject() {
let temp = ::devtools::RandomTempPath::new();
let mut jdb = new_db(temp.as_path().as_path());
let key = jdb.insert(b"dog");
jdb.inject_batch().unwrap();
assert_eq!(jdb.get(&key).unwrap(), b"dog");
jdb.remove(&key);
jdb.inject_batch().unwrap();
assert!(jdb.get(&key).is_none());
}
}

View File

@@ -277,10 +277,10 @@ impl HashDB for EarlyMergeDB {
fn get(&self, key: &H256) -> Option<&[u8]> {
let k = self.overlay.raw(key);
match k {
Some(&(ref d, rc)) if rc > 0 => Some(d),
Some((d, rc)) if rc > 0 => Some(d),
_ => {
if let Some(x) = self.payload(key) {
Some(&self.overlay.denote(key, x).0)
Some(self.overlay.denote(key, x).0)
}
else {
None
@@ -430,7 +430,7 @@ impl JournalDB for EarlyMergeDB {
r.begin_list(inserts.len());
inserts.iter().foreach(|&(k, _)| {r.append(&k);});
r.append(&removes);
Self::insert_keys(&inserts, &self.backing, self.column, &mut refs, &batch, trace);
Self::insert_keys(&inserts, &self.backing, self.column, &mut refs, batch, trace);
if trace {
let ins = inserts.iter().map(|&(k, _)| k).collect::<Vec<_>>();
trace!(target: "jdb.ops", " Inserts: {:?}", ins);
@@ -464,7 +464,7 @@ impl JournalDB for EarlyMergeDB {
if trace {
trace!(target: "jdb.ops", " Expunging: {:?}", deletes);
}
Self::remove_keys(&deletes, &mut refs, &batch, self.column, RemoveFrom::Archive, trace);
Self::remove_keys(&deletes, &mut refs, batch, self.column, RemoveFrom::Archive, trace);
if trace {
trace!(target: "jdb.ops", " Finalising: {:?}", inserts);
@@ -482,7 +482,7 @@ impl JournalDB for EarlyMergeDB {
}
Some( RefInfo{queue_refs: x, in_archive: false} ) => {
// must set already in; ,
Self::set_already_in(&batch, self.column, k);
Self::set_already_in(batch, self.column, k);
refs.insert(k.clone(), RefInfo{ queue_refs: x - 1, in_archive: true });
}
Some( RefInfo{in_archive: true, ..} ) => {
@@ -496,7 +496,7 @@ impl JournalDB for EarlyMergeDB {
if trace {
trace!(target: "jdb.ops", " Reverting: {:?}", inserts);
}
Self::remove_keys(&inserts, &mut refs, &batch, self.column, RemoveFrom::Queue, trace);
Self::remove_keys(&inserts, &mut refs, batch, self.column, RemoveFrom::Queue, trace);
}
try!(batch.delete(self.column, &last));
@@ -513,6 +513,32 @@ impl JournalDB for EarlyMergeDB {
Ok(0)
}
fn inject(&mut self, batch: &DBTransaction) -> Result<u32, UtilError> {
let mut ops = 0;
for (key, (value, rc)) in self.overlay.drain() {
if rc != 0 { ops += 1 }
match rc {
0 => {}
1 => {
if try!(self.backing.get(self.column, &key)).is_some() {
return Err(BaseDataError::AlreadyExists(key).into());
}
try!(batch.put(self.column, &key, &value))
}
-1 => {
if try!(self.backing.get(self.column, &key)).is_none() {
return Err(BaseDataError::NegativelyReferencedHash(key).into());
}
try!(batch.delete(self.column, &key))
}
_ => panic!("Attempted to inject invalid state."),
}
}
Ok(ops)
}
}
#[cfg(test)]
@@ -1045,4 +1071,19 @@ mod tests {
assert!(!jdb.contains(&bar));
}
}
#[test]
fn inject() {
let temp = ::devtools::RandomTempPath::new();
let mut jdb = new_db(temp.as_path().as_path());
let key = jdb.insert(b"dog");
jdb.inject_batch().unwrap();
assert_eq!(jdb.get(&key).unwrap(), b"dog");
jdb.remove(&key);
jdb.inject_batch().unwrap();
assert!(jdb.get(&key).is_none());
}
}

View File

@@ -46,7 +46,7 @@ use super::JournalDB;
///
/// Commit workflow:
/// 1. Create a new journal record from the transaction overlay.
/// 2. Inseart each node from the transaction overlay into the History overlay increasing reference
/// 2. Insert each node from the transaction overlay into the History overlay increasing reference
/// count if it is already there. Note that the reference counting is managed by `MemoryDB`
/// 3. Clear the transaction overlay.
/// 4. For a canonical journal record that becomes ancient inserts its insertions into the disk DB
@@ -126,7 +126,7 @@ impl OverlayRecentDB {
}
fn payload(&self, key: &H256) -> Option<Bytes> {
self.backing.get(self.column, key).expect("Low-level database error. Some issue with your hard disk?").map(|v| v.to_vec())
self.backing.get(self.column, key).expect("Low-level database error. Some issue with your hard disk?")
}
fn read_overlay(db: &Database, col: Option<u32>) -> JournalOverlay {
@@ -155,7 +155,7 @@ impl OverlayRecentDB {
for r in insertions.iter() {
let k: H256 = r.val_at(0);
let v: Bytes = r.val_at(1);
overlay.emplace(OverlayRecentDB::to_short_key(&k), v);
overlay.emplace(to_short_key(&k), v);
inserted_keys.push(k);
count += 1;
}
@@ -176,12 +176,13 @@ impl OverlayRecentDB {
JournalOverlay { backing_overlay: overlay, journal: journal, latest_era: latest_era }
}
#[inline]
fn to_short_key(key: &H256) -> H256 {
let mut k = H256::new();
k[0..DB_PREFIX_LEN].copy_from_slice(&key[0..DB_PREFIX_LEN]);
k
}
}
#[inline]
fn to_short_key(key: &H256) -> H256 {
let mut k = H256::new();
k[0..DB_PREFIX_LEN].copy_from_slice(&key[0..DB_PREFIX_LEN]);
k
}
impl JournalDB for OverlayRecentDB {
@@ -208,7 +209,7 @@ impl JournalDB for OverlayRecentDB {
fn latest_era(&self) -> Option<u64> { self.journal_overlay.read().latest_era }
fn state(&self, key: &H256) -> Option<Bytes> {
let v = self.journal_overlay.read().backing_overlay.get(&OverlayRecentDB::to_short_key(key)).map(|v| v.to_vec());
let v = self.journal_overlay.read().backing_overlay.get(&to_short_key(key)).map(|v| v.to_vec());
v.or_else(|| self.backing.get_by_prefix(self.column, &key[0..DB_PREFIX_LEN]).map(|b| b.to_vec()))
}
@@ -229,7 +230,7 @@ impl JournalDB for OverlayRecentDB {
r.begin_list(2);
r.append(&k);
r.append(&v);
journal_overlay.backing_overlay.emplace(OverlayRecentDB::to_short_key(&k), v);
journal_overlay.backing_overlay.emplace(to_short_key(&k), v);
}
r.append(&removed_keys);
@@ -238,15 +239,15 @@ impl JournalDB for OverlayRecentDB {
k.append(&now);
k.append(&index);
k.append(&&PADDING[..]);
try!(batch.put(self.column, &k.drain(), r.as_raw()));
try!(batch.put_vec(self.column, &k.drain(), r.out()));
if journal_overlay.latest_era.map_or(true, |e| now > e) {
try!(batch.put(self.column, &LATEST_ERA_KEY, &encode(&now)));
try!(batch.put_vec(self.column, &LATEST_ERA_KEY, encode(&now).to_vec()));
journal_overlay.latest_era = Some(now);
}
journal_overlay.journal.entry(now).or_insert_with(Vec::new).push(JournalEntry { id: id.clone(), insertions: inserted_keys, deletions: removed_keys });
}
let journal_overlay = journal_overlay.deref_mut();
let journal_overlay = &mut *journal_overlay;
// apply old commits' details
if let Some((end_era, canon_id)) = end {
if let Some(ref mut records) = journal_overlay.journal.get_mut(&end_era) {
@@ -265,9 +266,9 @@ impl JournalDB for OverlayRecentDB {
{
if canon_id == journal.id {
for h in &journal.insertions {
if let Some(&(ref d, rc)) = journal_overlay.backing_overlay.raw(&OverlayRecentDB::to_short_key(h)) {
if let Some((d, rc)) = journal_overlay.backing_overlay.raw(&to_short_key(h)) {
if rc > 0 {
canon_insertions.push((h.clone(), d.clone())); //TODO: optimize this to avoid data copy
canon_insertions.push((h.clone(), d.to_owned())); //TODO: optimize this to avoid data copy
}
}
}
@@ -279,15 +280,15 @@ impl JournalDB for OverlayRecentDB {
}
// apply canon inserts first
for (k, v) in canon_insertions {
try!(batch.put(self.column, &k, &v));
try!(batch.put_vec(self.column, &k, v));
}
// update the overlay
for k in overlay_deletions {
journal_overlay.backing_overlay.remove_and_purge(&OverlayRecentDB::to_short_key(&k));
journal_overlay.backing_overlay.remove_and_purge(&to_short_key(&k));
}
// apply canon deletions
for k in canon_deletions {
if !journal_overlay.backing_overlay.contains(&OverlayRecentDB::to_short_key(&k)) {
if !journal_overlay.backing_overlay.contains(&to_short_key(&k)) {
try!(batch.delete(self.column, &k));
}
}
@@ -297,6 +298,31 @@ impl JournalDB for OverlayRecentDB {
Ok(0)
}
fn inject(&mut self, batch: &DBTransaction) -> Result<u32, UtilError> {
let mut ops = 0;
for (key, (value, rc)) in self.transaction_overlay.drain() {
if rc != 0 { ops += 1 }
match rc {
0 => {}
1 => {
if try!(self.backing.get(self.column, &key)).is_some() {
return Err(BaseDataError::AlreadyExists(key).into());
}
try!(batch.put(self.column, &key, &value))
}
-1 => {
if try!(self.backing.get(self.column, &key)).is_none() {
return Err(BaseDataError::NegativelyReferencedHash(key).into());
}
try!(batch.delete(self.column, &key))
}
_ => panic!("Attempted to inject invalid state."),
}
}
Ok(ops)
}
}
impl HashDB for OverlayRecentDB {
@@ -317,9 +343,9 @@ impl HashDB for OverlayRecentDB {
fn get(&self, key: &H256) -> Option<&[u8]> {
let k = self.transaction_overlay.raw(key);
match k {
Some(&(ref d, rc)) if rc > 0 => Some(d),
Some((d, rc)) if rc > 0 => Some(d),
_ => {
let v = self.journal_overlay.read().backing_overlay.get(&OverlayRecentDB::to_short_key(key)).map(|v| v.to_vec());
let v = self.journal_overlay.read().backing_overlay.get(&to_short_key(key)).map(|v| v.to_vec());
match v {
Some(x) => {
Some(&self.transaction_overlay.denote(key, x).0)
@@ -879,4 +905,19 @@ mod tests {
assert!(jdb.contains(&foo));
assert!(jdb.contains(&bar));
}
}
#[test]
fn inject() {
let temp = ::devtools::RandomTempPath::new();
let mut jdb = new_db(temp.as_path().as_path());
let key = jdb.insert(b"dog");
jdb.inject_batch().unwrap();
assert_eq!(jdb.get(&key).unwrap(), b"dog");
jdb.remove(&key);
jdb.inject_batch().unwrap();
assert!(jdb.get(&key).is_none());
}
}

View File

@@ -184,6 +184,14 @@ impl JournalDB for RefCountedDB {
let r = try!(self.forward.commit_to_batch(&batch));
Ok(r)
}
fn inject(&mut self, batch: &DBTransaction) -> Result<u32, UtilError> {
self.inserts.clear();
for remove in self.removes.drain(..) {
self.forward.remove(&remove);
}
self.forward.commit_to_batch(&batch)
}
}
#[cfg(test)]
@@ -298,4 +306,17 @@ mod tests {
assert!(!jdb.contains(&baz));
assert!(!jdb.contains(&bar));
}
#[test]
fn inject() {
let mut jdb = RefCountedDB::new_temp();
let key = jdb.insert(b"dog");
jdb.inject_batch().unwrap();
assert_eq!(jdb.get(&key).unwrap(), b"dog");
jdb.remove(&key);
jdb.inject_batch().unwrap();
assert!(jdb.get(&key).is_none());
}
}

View File

@@ -22,7 +22,7 @@ use kvdb::{Database, DBTransaction};
/// A `HashDB` which can manage a short-term journal potentially containing many forks of mutually
/// exclusive actions.
pub trait JournalDB : HashDB + Send + Sync {
pub trait JournalDB: HashDB {
/// Return a copy of ourself, in a box.
fn boxed_clone(&self) -> Box<JournalDB>;
@@ -39,6 +39,15 @@ pub trait JournalDB : HashDB + Send + Sync {
/// old era to the backing database, reverting any non-canonical historical commit's inserts.
fn commit(&mut self, batch: &DBTransaction, now: u64, id: &H256, end: Option<(u64, H256)>) -> Result<u32, UtilError>;
/// Commit all queued insert and delete operations without affecting any journalling -- this requires that all insertions
/// and deletions are indeed canonical and will likely lead to an invalid database if that assumption is violated.
///
/// Any keys or values inserted or deleted must be completely independent of those affected
/// by any previous `commit` operations. Essentially, this means that `inject` can be used
/// either to restore a state to a fresh database, or to insert data which may only be journalled
/// from this point onwards.
fn inject(&mut self, batch: &DBTransaction) -> Result<u32, UtilError>;
/// State data query
fn state(&self, _id: &H256) -> Option<Bytes>;
@@ -48,11 +57,19 @@ pub trait JournalDB : HashDB + Send + Sync {
/// Get backing database.
fn backing(&self) -> &Arc<Database>;
#[cfg(test)]
/// Commit all changes in a single batch
#[cfg(test)]
fn commit_batch(&mut self, now: u64, id: &H256, end: Option<(u64, H256)>) -> Result<u32, UtilError> {
let batch = self.backing().transaction();
let res = try!(self.commit(&batch, now, id, end));
self.backing().write(batch).map(|_| res).map_err(Into::into)
}
/// Inject all changes in a single batch.
#[cfg(test)]
fn inject_batch(&mut self) -> Result<u32, UtilError> {
let batch = self.backing().transaction();
let res = try!(self.inject(&batch));
self.backing().write(batch).map(|_| res).map_err(Into::into)
}
}

View File

@@ -16,8 +16,11 @@
//! Key-Value store abstraction with `RocksDB` backend.
use common::*;
use elastic_array::*;
use std::default::Default;
use rocksdb::{DB, Writable, WriteBatch, WriteOptions, IteratorMode, DBVector, DBIterator,
use rlp::{UntrustedRlp, RlpType, View, Compressible};
use rocksdb::{DB, Writable, WriteBatch, WriteOptions, IteratorMode, DBIterator,
Options, DBCompactionStyle, BlockBasedOptions, Direction, Cache, Column};
const DB_BACKGROUND_FLUSHES: i32 = 2;
@@ -25,30 +28,89 @@ const DB_BACKGROUND_COMPACTIONS: i32 = 2;
/// Write transaction. Batches a sequence of put/delete operations for efficiency.
pub struct DBTransaction {
batch: WriteBatch,
cfs: Vec<Column>,
ops: RwLock<Vec<DBOp>>,
}
enum DBOp {
Insert {
col: Option<u32>,
key: ElasticArray32<u8>,
value: Bytes,
},
InsertCompressed {
col: Option<u32>,
key: ElasticArray32<u8>,
value: Bytes,
},
Delete {
col: Option<u32>,
key: ElasticArray32<u8>,
}
}
impl DBTransaction {
/// Create new transaction.
pub fn new(db: &Database) -> DBTransaction {
pub fn new(_db: &Database) -> DBTransaction {
DBTransaction {
batch: WriteBatch::new(),
cfs: db.cfs.clone(),
ops: RwLock::new(Vec::with_capacity(256)),
}
}
/// Insert a key-value pair in the transaction. Any existing value value will be overwritten upon write.
pub fn put(&self, col: Option<u32>, key: &[u8], value: &[u8]) -> Result<(), String> {
col.map_or_else(|| self.batch.put(key, value), |c| self.batch.put_cf(self.cfs[c as usize], key, value))
let mut ekey = ElasticArray32::new();
ekey.append_slice(key);
self.ops.write().push(DBOp::Insert {
col: col,
key: ekey,
value: value.to_vec(),
});
Ok(())
}
/// Insert a key-value pair in the transaction. Any existing value value will be overwritten upon write.
pub fn put_vec(&self, col: Option<u32>, key: &[u8], value: Bytes) -> Result<(), String> {
let mut ekey = ElasticArray32::new();
ekey.append_slice(key);
self.ops.write().push(DBOp::Insert {
col: col,
key: ekey,
value: value,
});
Ok(())
}
/// Insert a key-value pair in the transaction. Any existing value value will be overwritten upon write.
/// Value will be RLP-compressed on flush
pub fn put_compressed(&self, col: Option<u32>, key: &[u8], value: Bytes) -> Result<(), String> {
let mut ekey = ElasticArray32::new();
ekey.append_slice(key);
self.ops.write().push(DBOp::InsertCompressed {
col: col,
key: ekey,
value: value,
});
Ok(())
}
/// Delete value by key.
pub fn delete(&self, col: Option<u32>, key: &[u8]) -> Result<(), String> {
col.map_or_else(|| self.batch.delete(key), |c| self.batch.delete_cf(self.cfs[c as usize], key))
let mut ekey = ElasticArray32::new();
ekey.append_slice(key);
self.ops.write().push(DBOp::Delete {
col: col,
key: ekey,
});
Ok(())
}
}
struct DBColumnOverlay {
insertions: HashMap<ElasticArray32<u8>, Bytes>,
compressed_insertions: HashMap<ElasticArray32<u8>, Bytes>,
deletions: HashSet<ElasticArray32<u8>>,
}
/// Compaction profile for the database settings
#[derive(Clone, Copy)]
pub struct CompactionProfile {
@@ -118,7 +180,7 @@ impl Default for DatabaseConfig {
}
}
/// Database iterator
/// Database iterator for flushed data only
pub struct DatabaseIterator {
iter: DBIterator,
}
@@ -136,6 +198,7 @@ pub struct Database {
db: DB,
write_opts: WriteOptions,
cfs: Vec<Column>,
overlay: RwLock<Vec<DBColumnOverlay>>,
}
impl Database {
@@ -209,7 +272,16 @@ impl Database {
},
Err(s) => { return Err(s); }
};
Ok(Database { db: db, write_opts: write_opts, cfs: cfs })
Ok(Database {
db: db,
write_opts: write_opts,
overlay: RwLock::new((0..(cfs.len() + 1)).map(|_| DBColumnOverlay {
insertions: HashMap::new(),
compressed_insertions: HashMap::new(),
deletions: HashSet::new(),
}).collect()),
cfs: cfs,
})
}
/// Creates new transaction for this database.
@@ -217,14 +289,107 @@ impl Database {
DBTransaction::new(self)
}
fn to_overly_column(col: Option<u32>) -> usize {
col.map_or(0, |c| (c + 1) as usize)
}
/// Commit transaction to database.
pub fn write_buffered(&self, tr: DBTransaction) -> Result<(), String> {
let mut overlay = self.overlay.write();
let ops = mem::replace(&mut *tr.ops.write(), Vec::new());
for op in ops {
match op {
DBOp::Insert { col, key, value } => {
let c = Self::to_overly_column(col);
overlay[c].deletions.remove(&key);
overlay[c].compressed_insertions.remove(&key);
overlay[c].insertions.insert(key, value);
},
DBOp::InsertCompressed { col, key, value } => {
let c = Self::to_overly_column(col);
overlay[c].deletions.remove(&key);
overlay[c].insertions.remove(&key);
overlay[c].compressed_insertions.insert(key, value);
},
DBOp::Delete { col, key } => {
let c = Self::to_overly_column(col);
overlay[c].insertions.remove(&key);
overlay[c].compressed_insertions.remove(&key);
overlay[c].deletions.insert(key);
},
}
};
Ok(())
}
/// Commit buffered changes to database.
pub fn flush(&self) -> Result<(), String> {
let batch = WriteBatch::new();
let mut overlay = self.overlay.write();
let mut c = 0;
for column in overlay.iter_mut() {
let insertions = mem::replace(&mut column.insertions, HashMap::new());
let compressed_insertions = mem::replace(&mut column.compressed_insertions, HashMap::new());
let deletions = mem::replace(&mut column.deletions, HashSet::new());
for d in deletions.into_iter() {
if c > 0 {
try!(batch.delete_cf(self.cfs[c - 1], &d));
} else {
try!(batch.delete(&d));
}
}
for (key, value) in insertions.into_iter() {
if c > 0 {
try!(batch.put_cf(self.cfs[c - 1], &key, &value));
} else {
try!(batch.put(&key, &value));
}
}
for (key, value) in compressed_insertions.into_iter() {
let compressed = UntrustedRlp::new(&value).compress(RlpType::Blocks);
if c > 0 {
try!(batch.put_cf(self.cfs[c - 1], &key, &compressed));
} else {
try!(batch.put(&key, &compressed));
}
}
c += 1;
}
self.db.write_opt(batch, &self.write_opts)
}
/// Commit transaction to database.
pub fn write(&self, tr: DBTransaction) -> Result<(), String> {
self.db.write_opt(tr.batch, &self.write_opts)
let batch = WriteBatch::new();
let ops = mem::replace(&mut *tr.ops.write(), Vec::new());
for op in ops {
match op {
DBOp::Insert { col, key, value } => {
try!(col.map_or_else(|| batch.put(&key, &value), |c| batch.put_cf(self.cfs[c as usize], &key, &value)))
},
DBOp::InsertCompressed { col, key, value } => {
let compressed = UntrustedRlp::new(&value).compress(RlpType::Blocks);
try!(col.map_or_else(|| batch.put(&key, &compressed), |c| batch.put_cf(self.cfs[c as usize], &key, &compressed)))
},
DBOp::Delete { col, key } => {
try!(col.map_or_else(|| batch.delete(&key), |c| batch.delete_cf(self.cfs[c as usize], &key)))
},
}
}
self.db.write_opt(batch, &self.write_opts)
}
/// Get value by key.
pub fn get(&self, col: Option<u32>, key: &[u8]) -> Result<Option<DBVector>, String> {
col.map_or_else(|| self.db.get(key), |c| self.db.get_cf(self.cfs[c as usize], key))
pub fn get(&self, col: Option<u32>, key: &[u8]) -> Result<Option<Bytes>, String> {
let overlay = &self.overlay.read()[Self::to_overly_column(col)];
overlay.insertions.get(key).or_else(|| overlay.compressed_insertions.get(key)).map_or_else(||
col.map_or_else(
|| self.db.get(key).map(|r| r.map(|v| v.to_vec())),
|c| self.db.get_cf(self.cfs[c as usize], key).map(|r| r.map(|v| v.to_vec()))),
|value| Ok(Some(value.clone())))
}
/// Get value by partial key. Prefix size should match configured prefix size.

View File

@@ -158,7 +158,7 @@ pub use overlaydb::*;
pub use journaldb::JournalDB;
pub use crypto::*;
pub use triehash::*;
pub use trie::*;
pub use trie::{Trie, TrieMut, TrieDB, TrieDBMut, TrieFactory, TrieError, SecTrieDB, SecTrieDBMut};
pub use nibbleslice::*;
pub use semantic_version::*;
pub use network::*;

View File

@@ -24,10 +24,10 @@ use hashdb::*;
use heapsize::*;
use std::mem;
use std::collections::HashMap;
use std::collections::hash_map::Entry;
use std::default::Default;
#[derive(Debug,Clone)]
const STATIC_NULL_RLP: (&'static [u8], i32) = (&[0x80; 1], 1);
use std::collections::hash_map::Entry;
/// Reference-counted memory-based `HashDB` implementation.
///
/// Use `new()` to create a new database. Insert items with `insert()`, remove items
@@ -71,25 +71,17 @@ use std::default::Default;
/// assert!(!m.contains(&k));
/// }
/// ```
#[derive(PartialEq)]
#[derive(Default, Clone, PartialEq)]
pub struct MemoryDB {
data: HashMap<H256, (Bytes, i32)>,
static_null_rlp: (Bytes, i32),
data: H256FastMap<(Bytes, i32)>,
aux: HashMap<Bytes, Bytes>,
}
impl Default for MemoryDB {
fn default() -> Self {
MemoryDB::new()
}
}
impl MemoryDB {
/// Create a new instance of the memory DB.
pub fn new() -> MemoryDB {
MemoryDB {
data: HashMap::new(),
static_null_rlp: (vec![0x80u8; 1], 1),
data: H256FastMap::default(),
aux: HashMap::new(),
}
}
@@ -123,21 +115,9 @@ impl MemoryDB {
for empty in empties { self.data.remove(&empty); }
}
/// Grab the raw information associated with a key. Returns None if the key
/// doesn't exist.
///
/// Even when Some is returned, the data is only guaranteed to be useful
/// when the refs > 0.
pub fn raw(&self, key: &H256) -> Option<&(Bytes, i32)> {
if key == &SHA3_NULL_RLP {
return Some(&self.static_null_rlp);
}
self.data.get(key)
}
/// Return the internal map of hashes to data, clearing the current state.
pub fn drain(&mut self) -> HashMap<H256, (Bytes, i32)> {
mem::replace(&mut self.data, HashMap::new())
pub fn drain(&mut self) -> H256FastMap<(Bytes, i32)> {
mem::replace(&mut self.data, H256FastMap::default())
}
/// Return the internal map of auxiliary data, clearing the current state.
@@ -145,14 +125,26 @@ impl MemoryDB {
mem::replace(&mut self.aux, HashMap::new())
}
/// Grab the raw information associated with a key. Returns None if the key
/// doesn't exist.
///
/// Even when Some is returned, the data is only guaranteed to be useful
/// when the refs > 0.
pub fn raw(&self, key: &H256) -> Option<(&[u8], i32)> {
if key == &SHA3_NULL_RLP {
return Some(STATIC_NULL_RLP.clone());
}
self.data.get(key).map(|&(ref v, x)| (&v[..], x))
}
/// Denote than an existing value has the given key. Used when a key gets removed without
/// a prior insert and thus has a negative reference with no value.
///
/// May safely be called even if the key's value is known, in which case it will be a no-op.
pub fn denote(&self, key: &H256, value: Bytes) -> &(Bytes, i32) {
pub fn denote(&self, key: &H256, value: Bytes) -> (&[u8], i32) {
if self.raw(key) == None {
unsafe {
let p = &self.data as *const HashMap<H256, (Bytes, i32)> as *mut HashMap<H256, (Bytes, i32)>;
let p = &self.data as *const H256FastMap<(Bytes, i32)> as *mut H256FastMap<(Bytes, i32)>;
(*p).insert(key.clone(), (value, 0));
}
}
@@ -162,6 +154,7 @@ impl MemoryDB {
/// Returns the size of allocated heap memory
pub fn mem_used(&self) -> usize {
self.data.heap_size_of_children()
+ self.aux.heap_size_of_children()
}
/// Remove an element and delete it from storage if reference count reaches zero.
@@ -190,6 +183,7 @@ impl HashDB for MemoryDB {
if key == &SHA3_NULL_RLP {
return Some(&NULL_RLP_STATIC);
}
match self.data.get(key) {
Some(&(ref d, rc)) if rc > 0 => Some(d),
_ => None
@@ -204,6 +198,7 @@ impl HashDB for MemoryDB {
if key == &SHA3_NULL_RLP {
return true;
}
match self.data.get(key) {
Some(&(_, x)) if x > 0 => true,
_ => false
@@ -217,14 +212,14 @@ impl HashDB for MemoryDB {
let key = value.sha3();
if match self.data.get_mut(&key) {
Some(&mut (ref mut old_value, ref mut rc @ -0x80000000i32 ... 0)) => {
*old_value = From::from(value);
*old_value = value.into();
*rc += 1;
false
},
Some(&mut (_, ref mut x)) => { *x += 1; false } ,
None => true,
}{ // ... None falls through into...
self.data.insert(key.clone(), (From::from(value), 1));
self.data.insert(key.clone(), (value.into(), 1));
}
key
}
@@ -233,6 +228,7 @@ impl HashDB for MemoryDB {
if value == &NULL_RLP {
return;
}
match self.data.get_mut(&key) {
Some(&mut (ref mut old_value, ref mut rc @ -0x80000000i32 ... 0)) => {
*old_value = value;
@@ -250,6 +246,7 @@ impl HashDB for MemoryDB {
if key == &SHA3_NULL_RLP {
return;
}
if match self.data.get_mut(key) {
Some(&mut (_, ref mut x)) => { *x -= 1; false }
None => true
@@ -281,9 +278,9 @@ fn memorydb_denote() {
for _ in 0..1000 {
let r = H256::random();
let k = r.sha3();
let &(ref v, ref rc) = m.denote(&k, r.to_bytes());
assert_eq!(v.as_slice(), r.as_slice());
assert_eq!(*rc, 0);
let (v, rc) = m.denote(&k, r.to_bytes());
assert_eq!(v, r.as_slice());
assert_eq!(rc, 0);
}
assert_eq!(m.get(&hash).unwrap(), b"Hello world!");

View File

@@ -99,7 +99,7 @@ impl OverlayDB {
pub fn revert(&mut self) { self.overlay.clear(); }
/// Get the number of references that would be committed.
pub fn commit_refs(&self, key: &H256) -> i32 { self.overlay.raw(key).map_or(0, |&(_, refs)| refs) }
pub fn commit_refs(&self, key: &H256) -> i32 { self.overlay.raw(key).map_or(0, |(_, refs)| refs) }
/// Get the refs and value of the given key.
fn payload(&self, key: &H256) -> Option<(Bytes, u32)> {
@@ -146,14 +146,14 @@ impl HashDB for OverlayDB {
// it positive again.
let k = self.overlay.raw(key);
match k {
Some(&(ref d, rc)) if rc > 0 => Some(d),
Some((d, rc)) if rc > 0 => Some(d),
_ => {
let memrc = k.map_or(0, |&(_, rc)| rc);
let memrc = k.map_or(0, |(_, rc)| rc);
match self.payload(key) {
Some(x) => {
let (d, rc) = x;
if rc as i32 + memrc > 0 {
Some(&self.overlay.denote(key, d).0)
Some(self.overlay.denote(key, d).0)
}
else {
None
@@ -171,9 +171,9 @@ impl HashDB for OverlayDB {
// it positive again.
let k = self.overlay.raw(key);
match k {
Some(&(_, rc)) if rc > 0 => true,
Some((_, rc)) if rc > 0 => true,
_ => {
let memrc = k.map_or(0, |&(_, rc)| rc);
let memrc = k.map_or(0, |(_, rc)| rc);
match self.payload(key) {
Some(x) => {
let (_, rc) = x;
@@ -192,6 +192,7 @@ impl HashDB for OverlayDB {
}
#[test]
#[cfg_attr(feature="dev", allow(blacklisted_name))]
fn overlaydb_revert() {
let mut m = OverlayDB::new_temp();
let foo = m.insert(b"foo"); // insert foo.

View File

@@ -45,7 +45,7 @@ static COMMON_RLPS: &'static [&'static [u8]] = &[
&[148, 50, 190, 52, 59, 148, 248, 96, 18, 77, 196, 254, 226, 120, 253, 203, 211, 140, 16, 45, 136],
&[148, 82, 188, 68, 213, 55, 131, 9, 238, 42, 191, 21, 57, 191, 113, 222, 27, 125, 123, 227, 181],
&[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
];
];
static INVALID_RLPS: &'static [&'static [u8]] = &[&[0x81, 0x0], &[0x81, 0x1], &[0x81, 0x2], &[0x81, 0x3], &[0x81, 0x4], &[0x81, 0x5], &[0x81, 0x6], &[0x81, 0x7], &[0x81, 0x8], &[0x81, 0x9], &[0x81, 0xa], &[0x81, 0xb], &[0x81, 0xc], &[0x81, 0xd], &[0x81, 0xe], &[0x81, 0xf], &[0x81, 0x10], &[0x81, 0x11], &[0x81, 0x12], &[0x81, 0x13], &[0x81, 0x14], &[0x81, 0x15], &[0x81, 0x16], &[0x81, 0x17], &[0x81, 0x18], &[0x81, 0x19], &[0x81, 0x1a], &[0x81, 0x1b], &[0x81, 0x1c], &[0x81, 0x1d], &[0x81, 0x1e], &[0x81, 0x1f], &[0x81, 0x20], &[0x81, 0x21], &[0x81, 0x22], &[0x81, 0x23], &[0x81, 0x24], &[0x81, 0x25], &[0x81, 0x26], &[0x81, 0x27], &[0x81, 0x28], &[0x81, 0x29], &[0x81, 0x2a], &[0x81, 0x2b], &[0x81, 0x2c], &[0x81, 0x2d], &[0x81, 0x2e], &[0x81, 0x2f], &[0x81, 0x30], &[0x81, 0x31], &[0x81, 0x32], &[0x81, 0x33], &[0x81, 0x34], &[0x81, 0x35], &[0x81, 0x36], &[0x81, 0x37], &[0x81, 0x38], &[0x81, 0x39], &[0x81, 0x3a], &[0x81, 0x3b], &[0x81, 0x3c], &[0x81, 0x3d], &[0x81, 0x3e], &[0x81, 0x3f], &[0x81, 0x40], &[0x81, 0x41], &[0x81, 0x42], &[0x81, 0x43], &[0x81, 0x44], &[0x81, 0x45], &[0x81, 0x46], &[0x81, 0x47], &[0x81, 0x48], &[0x81, 0x49], &[0x81, 0x4a], &[0x81, 0x4b], &[0x81, 0x4c], &[0x81, 0x4d], &[0x81, 0x4e], &[0x81, 0x4f], &[0x81, 0x50], &[0x81, 0x51], &[0x81, 0x52], &[0x81, 0x53], &[0x81, 0x54], &[0x81, 0x55], &[0x81, 0x56], &[0x81, 0x57], &[0x81, 0x58], &[0x81, 0x59], &[0x81, 0x5a], &[0x81, 0x5b], &[0x81, 0x5c], &[0x81, 0x5d], &[0x81, 0x5e], &[0x81, 0x5f], &[0x81, 0x60], &[0x81, 0x61], &[0x81, 0x62], &[0x81, 0x63], &[0x81, 0x64], &[0x81, 0x65], &[0x81, 0x66], &[0x81, 0x67], &[0x81, 0x68], &[0x81, 0x69], &[0x81, 0x6a], &[0x81, 0x6b], &[0x81, 0x6c], &[0x81, 0x6d], &[0x81, 0x6e], &[0x81, 0x6f], &[0x81, 0x70], &[0x81, 0x71], &[0x81, 0x72], &[0x81, 0x73], &[0x81, 0x74], &[0x81, 0x75], &[0x81, 0x76], &[0x81, 0x77], &[0x81, 0x78], &[0x81, 0x79], &[0x81, 0x7a], &[0x81, 0x7b], &[0x81, 0x7c], &[0x81, 0x7d], &[0x81, 0x7e]];
@@ -85,11 +85,11 @@ mod tests {
}
}
for v in values.iter() {
let rlp = UntrustedRlp::new(&v);
for v in &values {
let rlp = UntrustedRlp::new(v);
let mut flat = Vec::new();
flat_rlp(&mut flat, rlp);
for r in flat.iter() {
for r in &flat {
*rlp_counts.entry(r.as_raw()).or_insert(0) += 1;
*rlp_sizes.entry(r.as_raw()).or_insert(0) += space_saving(r.as_raw());
}

View File

@@ -43,11 +43,11 @@ impl<'a> InvalidRlpSwapper<'a> {
}
/// Get a valid RLP corresponding to an invalid one
fn get_valid(&self, invalid_rlp: &[u8]) -> Option<&[u8]> {
self.invalid_to_valid.get(invalid_rlp).map(|r| r.clone())
self.invalid_to_valid.get(invalid_rlp).cloned()
}
/// Get an invalid RLP corresponding to a valid one
fn get_invalid(&self, valid_rlp: &[u8]) -> Option<&[u8]> {
self.valid_to_invalid.get(valid_rlp).map(|r| r.clone())
self.valid_to_invalid.get(valid_rlp).cloned()
}
}
@@ -86,18 +86,18 @@ fn map_rlp<F>(rlp: &UntrustedRlp, f: F) -> Option<ElasticArray1024<u8>> where
/// Replace common RLPs with invalid shorter ones.
fn simple_compress(rlp: &UntrustedRlp, swapper: &InvalidRlpSwapper) -> ElasticArray1024<u8> {
if rlp.is_data() {
to_elastic(swapper.get_invalid(rlp.as_raw()).unwrap_or(rlp.as_raw()))
to_elastic(swapper.get_invalid(rlp.as_raw()).unwrap_or_else(|| rlp.as_raw()))
} else {
map_rlp(rlp, |r| Some(simple_compress(r, swapper))).unwrap_or(to_elastic(rlp.as_raw()))
map_rlp(rlp, |r| Some(simple_compress(r, swapper))).unwrap_or_else(|| to_elastic(rlp.as_raw()))
}
}
/// Recover valid RLP from a compressed form.
fn simple_decompress(rlp: &UntrustedRlp, swapper: &InvalidRlpSwapper) -> ElasticArray1024<u8> {
if rlp.is_data() {
to_elastic(swapper.get_valid(rlp.as_raw()).unwrap_or(rlp.as_raw()))
to_elastic(swapper.get_valid(rlp.as_raw()).unwrap_or_else(|| rlp.as_raw()))
} else {
map_rlp(rlp, |r| Some(simple_decompress(r, swapper))).unwrap_or(to_elastic(rlp.as_raw()))
map_rlp(rlp, |r| Some(simple_decompress(r, swapper))).unwrap_or_else(|| to_elastic(rlp.as_raw()))
}
}
@@ -105,7 +105,7 @@ fn simple_decompress(rlp: &UntrustedRlp, swapper: &InvalidRlpSwapper) -> Elastic
/// Tries to compress data insides.
fn deep_compress(rlp: &UntrustedRlp, swapper: &InvalidRlpSwapper) -> Option<ElasticArray1024<u8>> {
let simple_swap = ||
swapper.get_invalid(rlp.as_raw()).map(|b| to_elastic(&b));
swapper.get_invalid(rlp.as_raw()).map(to_elastic);
if rlp.is_data() {
// Try to treat the inside as RLP.
return match rlp.payload_info() {
@@ -134,7 +134,7 @@ fn deep_compress(rlp: &UntrustedRlp, swapper: &InvalidRlpSwapper) -> Option<Elas
/// Tries to decompress compressed data insides.
fn deep_decompress(rlp: &UntrustedRlp, swapper: &InvalidRlpSwapper) -> Option<ElasticArray1024<u8>> {
let simple_swap = ||
swapper.get_valid(rlp.as_raw()).map(|b| to_elastic(&b));
swapper.get_valid(rlp.as_raw()).map(to_elastic);
// Simply decompress data.
if rlp.is_data() { return simple_swap(); }
match rlp.item_count() {
@@ -152,17 +152,17 @@ fn deep_decompress(rlp: &UntrustedRlp, swapper: &InvalidRlpSwapper) -> Option<El
impl<'a> Compressible for UntrustedRlp<'a> {
type DataType = RlpType;
fn compress(&self, t: RlpType) -> ElasticArray1024<u8> {
fn compress(&self, t: RlpType) -> ElasticArray1024<u8> {
match t {
RlpType::Snapshot => simple_compress(self, &SNAPSHOT_RLP_SWAPPER),
RlpType::Blocks => deep_compress(self, &BLOCKS_RLP_SWAPPER).unwrap_or(to_elastic(self.as_raw())),
RlpType::Blocks => deep_compress(self, &BLOCKS_RLP_SWAPPER).unwrap_or_else(|| to_elastic(self.as_raw())),
}
}
fn decompress(&self, t: RlpType) -> ElasticArray1024<u8> {
match t {
RlpType::Snapshot => simple_decompress(self, &SNAPSHOT_RLP_SWAPPER),
RlpType::Blocks => deep_decompress(self, &BLOCKS_RLP_SWAPPER).unwrap_or(to_elastic(self.as_raw())),
RlpType::Blocks => deep_decompress(self, &BLOCKS_RLP_SWAPPER).unwrap_or_else(|| to_elastic(self.as_raw())),
}
}
}
@@ -232,8 +232,8 @@ mod tests {
let mut decomp_size = 0;
let mut comp_size = 0;
for v in values.iter() {
let rlp = UntrustedRlp::new(&v);
for v in &values {
let rlp = UntrustedRlp::new(v);
let compressed = rlp.compress(RlpType::Blocks).to_vec();
comp_size += compressed.len();
let decompressed = rlp.decompress(RlpType::Blocks).to_vec();

View File

@@ -37,7 +37,6 @@ pub use std::error::Error as StdError;
pub use std::ops::*;
pub use std::cmp::*;
pub use std::sync::Arc;
pub use std::cell::*;
pub use std::collections::*;
pub use rustc_serialize::json::Json;

View File

@@ -17,8 +17,7 @@
use hash::H256;
use sha3::Hashable;
use hashdb::HashDB;
use super::{TrieDB, Trie, TrieDBIterator, TrieError};
use trie::trietraits::TrieItem;
use super::{TrieDB, Trie, TrieDBIterator, TrieItem};
/// A `Trie` implementation which hashes keys and uses a generic `HashDB` backing database.
/// Additionaly it stores inserted hash-key mappings for later retrieval.
@@ -32,7 +31,7 @@ impl<'db> FatDB<'db> {
/// Create a new trie with the backing database `db` and empty `root`
/// Initialise to the state entailed by the genesis block.
/// This guarantees the trie is built correctly.
pub fn new(db: &'db HashDB, root: &'db H256) -> Result<Self, TrieError> {
pub fn new(db: &'db HashDB, root: &'db H256) -> super::Result<Self> {
let fatdb = FatDB {
raw: try!(TrieDB::new(db, root))
};
@@ -60,11 +59,13 @@ impl<'db> Trie for FatDB<'db> {
self.raw.root()
}
fn contains(&self, key: &[u8]) -> bool {
fn contains(&self, key: &[u8]) -> super::Result<bool> {
self.raw.contains(&key.sha3())
}
fn get<'a, 'key>(&'a self, key: &'key [u8]) -> Option<&'a [u8]> where 'a: 'key {
fn get<'a, 'key>(&'a self, key: &'key [u8]) -> super::Result<Option<&'a [u8]>>
where 'a: 'key
{
self.raw.get(&key.sha3())
}
}
@@ -105,9 +106,9 @@ fn fatdb_to_trie() {
let mut root = H256::default();
{
let mut t = FatDBMut::new(&mut memdb, &mut root);
t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]);
t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]).unwrap();
}
let t = FatDB::new(&memdb, &root).unwrap();
assert_eq!(t.get(&[0x01u8, 0x23]).unwrap(), &[0x01u8, 0x23]);
assert_eq!(t.get(&[0x01u8, 0x23]).unwrap().unwrap(), &[0x01u8, 0x23]);
assert_eq!(t.iter().collect::<Vec<_>>(), vec![(vec![0x01u8, 0x23], &[0x01u8, 0x23] as &[u8])]);
}

View File

@@ -17,7 +17,7 @@
use hash::H256;
use sha3::Hashable;
use hashdb::HashDB;
use super::{TrieDBMut, TrieMut, TrieError};
use super::{TrieDBMut, TrieMut};
/// A mutable `Trie` implementation which hashes keys and uses a generic `HashDB` backing database.
/// Additionaly it stores inserted hash-key mappings for later retrieval.
@@ -38,7 +38,7 @@ impl<'db> FatDBMut<'db> {
/// Create a new trie with the backing database `db` and `root`.
///
/// Returns an error if root does not exist.
pub fn from_existing(db: &'db mut HashDB, root: &'db mut H256) -> Result<Self, TrieError> {
pub fn from_existing(db: &'db mut HashDB, root: &'db mut H256) -> super::Result<Self> {
Ok(FatDBMut { raw: try!(TrieDBMut::from_existing(db, root)) })
}
@@ -62,23 +62,26 @@ impl<'db> TrieMut for FatDBMut<'db> {
self.raw.is_empty()
}
fn contains(&self, key: &[u8]) -> bool {
fn contains(&self, key: &[u8]) -> super::Result<bool> {
self.raw.contains(&key.sha3())
}
fn get<'a, 'key>(&'a self, key: &'key [u8]) -> Option<&'a [u8]> where 'a: 'key {
fn get<'a, 'key>(&'a self, key: &'key [u8]) -> super::Result<Option<&'a [u8]>>
where 'a: 'key
{
self.raw.get(&key.sha3())
}
fn insert(&mut self, key: &[u8], value: &[u8]) {
fn insert(&mut self, key: &[u8], value: &[u8]) -> super::Result<()> {
let hash = key.sha3();
self.raw.insert(&hash, value);
try!(self.raw.insert(&hash, value));
let db = self.raw.db_mut();
db.insert_aux(hash.to_vec(), key.to_vec());
Ok(())
}
fn remove(&mut self, key: &[u8]) {
self.raw.remove(&key.sha3());
fn remove(&mut self, key: &[u8]) -> super::Result<()> {
self.raw.remove(&key.sha3())
}
}
@@ -92,8 +95,8 @@ fn fatdb_to_trie() {
let mut root = H256::default();
{
let mut t = FatDBMut::new(&mut memdb, &mut root);
t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]);
t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]).unwrap();
}
let t = TrieDB::new(&memdb, &root).unwrap();
assert_eq!(t.get(&(&[0x01u8, 0x23]).sha3()).unwrap(), &[0x01u8, 0x23]);
assert_eq!(t.get(&(&[0x01u8, 0x23]).sha3()).unwrap().unwrap(), &[0x01u8, 0x23]);
}

View File

@@ -20,8 +20,6 @@ use std::fmt;
use hash::H256;
use hashdb::HashDB;
/// Export the trietraits module.
pub mod trietraits;
/// Export the standardmap module.
pub mod standardmap;
/// Export the journal module.
@@ -40,7 +38,6 @@ pub mod sectriedbmut;
mod fatdb;
mod fatdbmut;
pub use self::trietraits::{Trie, TrieMut};
pub use self::standardmap::{Alphabet, StandardMap, ValueMode};
pub use self::triedbmut::TrieDBMut;
pub use self::triedb::{TrieDB, TrieDBIterator};
@@ -49,19 +46,80 @@ pub use self::sectriedb::SecTrieDB;
pub use self::fatdb::{FatDB, FatDBIterator};
pub use self::fatdbmut::FatDBMut;
/// Trie Errors
#[derive(Debug)]
/// Trie Errors.
///
/// These borrow the data within them to avoid excessive copying on every
/// trie operation.
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum TrieError {
/// Attempted to create a trie with a state root not in the DB.
InvalidStateRoot,
InvalidStateRoot(H256),
/// Trie item not found in the database,
IncompleteDatabase(H256),
}
impl fmt::Display for TrieError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Trie Error: Invalid state root.")
match *self {
TrieError::InvalidStateRoot(ref root) => write!(f, "Invalid state root: {}", root),
TrieError::IncompleteDatabase(ref missing) =>
write!(f, "Database missing expected key: {}", missing),
}
}
}
/// Trie-Item type.
pub type TrieItem<'a> = (Vec<u8>, &'a [u8]);
/// Trie result type. Boxed to avoid copying around extra space for `H256`s on successful queries.
pub type Result<T> = ::std::result::Result<T, Box<TrieError>>;
/// A key-value datastore implemented as a database-backed modified Merkle tree.
pub trait Trie {
/// Return the root of the trie.
fn root(&self) -> &H256;
/// Is the trie empty?
fn is_empty(&self) -> bool { *self.root() == ::rlp::SHA3_NULL_RLP }
/// Does the trie contain a given key?
fn contains(&self, key: &[u8]) -> Result<bool> {
self.get(key).map(|x| x.is_some())
}
/// What is the value of the given key in this trie?
fn get<'a, 'key>(&'a self, key: &'key [u8]) -> Result<Option<&'a [u8]>> where 'a: 'key;
/// Returns an iterator over elements of trie.
fn iter<'a>(&'a self) -> Box<Iterator<Item = TrieItem> + 'a>;
}
/// A key-value datastore implemented as a database-backed modified Merkle tree.
pub trait TrieMut {
/// Return the root of the trie.
fn root(&mut self) -> &H256;
/// Is the trie empty?
fn is_empty(&self) -> bool;
/// Does the trie contain a given key?
fn contains(&self, key: &[u8]) -> Result<bool> {
self.get(key).map(|x| x.is_some())
}
/// What is the value of the given key in this trie?
fn get<'a, 'key>(&'a self, key: &'key [u8]) -> Result<Option<&'a [u8]>> where 'a: 'key;
/// Insert a `key`/`value` pair into the trie. An `empty` value is equivalent to removing
/// `key` from the trie.
fn insert(&mut self, key: &[u8], value: &[u8]) -> Result<()>;
/// Remove a `key` from the trie. Equivalent to making it equal to the empty
/// value.
fn remove(&mut self, key: &[u8]) -> Result<()>;
}
/// Trie types
#[derive(Debug, PartialEq, Clone)]
pub enum TrieSpec {
@@ -95,7 +153,7 @@ impl TrieFactory {
}
/// Create new immutable instance of Trie.
pub fn readonly<'db>(&self, db: &'db HashDB, root: &'db H256) -> Result<Box<Trie + 'db>, TrieError> {
pub fn readonly<'db>(&self, db: &'db HashDB, root: &'db H256) -> Result<Box<Trie + 'db>> {
match self.spec {
TrieSpec::Generic => Ok(Box::new(try!(TrieDB::new(db, root)))),
TrieSpec::Secure => Ok(Box::new(try!(SecTrieDB::new(db, root)))),
@@ -113,7 +171,7 @@ impl TrieFactory {
}
/// Create new mutable instance of trie and check for errors.
pub fn from_existing<'db>(&self, db: &'db mut HashDB, root: &'db mut H256) -> Result<Box<TrieMut + 'db>, TrieError> {
pub fn from_existing<'db>(&self, db: &'db mut HashDB, root: &'db mut H256) -> Result<Box<TrieMut + 'db>> {
match self.spec {
TrieSpec::Generic => Ok(Box::new(try!(TrieDBMut::from_existing(db, root)))),
TrieSpec::Secure => Ok(Box::new(try!(SecTrieDBMut::from_existing(db, root)))),

View File

@@ -48,7 +48,7 @@ impl<'a> Node<'a> {
},
// branch - first 16 are nodes, 17th is a value (or empty).
Prototype::List(17) => {
let mut nodes: [&'a [u8]; 16] = unsafe { ::std::mem::uninitialized() };
let mut nodes: [&'a [u8]; 16] = [&[], &[], &[], &[], &[], &[], &[], &[], &[], &[], &[], &[], &[], &[], &[], &[]];
for i in 0..16 {
nodes[i] = r.at(i).as_raw();
}

View File

@@ -18,8 +18,7 @@ use hash::H256;
use sha3::Hashable;
use hashdb::HashDB;
use super::triedb::TrieDB;
use super::trietraits::{Trie, TrieItem};
use super::TrieError;
use super::{Trie, TrieItem};
/// A `Trie` implementation which hashes keys and uses a generic `HashDB` backing database.
///
@@ -34,7 +33,7 @@ impl<'db> SecTrieDB<'db> {
/// Initialise to the state entailed by the genesis block.
/// This guarantees the trie is built correctly.
/// Returns an error if root does not exist.
pub fn new(db: &'db HashDB, root: &'db H256) -> Result<Self, TrieError> {
pub fn new(db: &'db HashDB, root: &'db H256) -> super::Result<Self> {
Ok(SecTrieDB { raw: try!(TrieDB::new(db, root)) })
}
@@ -56,11 +55,13 @@ impl<'db> Trie for SecTrieDB<'db> {
fn root(&self) -> &H256 { self.raw.root() }
fn contains(&self, key: &[u8]) -> bool {
fn contains(&self, key: &[u8]) -> super::Result<bool> {
self.raw.contains(&key.sha3())
}
fn get<'a, 'key>(&'a self, key: &'key [u8]) -> Option<&'a [u8]> where 'a: 'key {
fn get<'a, 'key>(&'a self, key: &'key [u8]) -> super::Result<Option<&'a [u8]>>
where 'a: 'key
{
self.raw.get(&key.sha3())
}
}
@@ -69,14 +70,14 @@ impl<'db> Trie for SecTrieDB<'db> {
fn trie_to_sectrie() {
use memorydb::MemoryDB;
use super::triedbmut::TrieDBMut;
use super::trietraits::TrieMut;
use super::super::TrieMut;
let mut memdb = MemoryDB::new();
let mut root = H256::default();
{
let mut t = TrieDBMut::new(&mut memdb, &mut root);
t.insert(&(&[0x01u8, 0x23]).sha3(), &[0x01u8, 0x23]);
t.insert(&(&[0x01u8, 0x23]).sha3(), &[0x01u8, 0x23]).unwrap();
}
let t = SecTrieDB::new(&memdb, &root).unwrap();
assert_eq!(t.get(&[0x01u8, 0x23]).unwrap(), &[0x01u8, 0x23]);
assert_eq!(t.get(&[0x01u8, 0x23]).unwrap().unwrap(), &[0x01u8, 0x23]);
}

View File

@@ -18,8 +18,7 @@ use hash::H256;
use sha3::Hashable;
use hashdb::HashDB;
use super::triedbmut::TrieDBMut;
use super::trietraits::TrieMut;
use super::TrieError;
use super::TrieMut;
/// A mutable `Trie` implementation which hashes keys and uses a generic `HashDB` backing database.
///
@@ -39,7 +38,7 @@ impl<'db> SecTrieDBMut<'db> {
/// Create a new trie with the backing database `db` and `root`.
///
/// Returns an error if root does not exist.
pub fn from_existing(db: &'db mut HashDB, root: &'db mut H256) -> Result<Self, TrieError> {
pub fn from_existing(db: &'db mut HashDB, root: &'db mut H256) -> super::Result<Self> {
Ok(SecTrieDBMut { raw: try!(TrieDBMut::from_existing(db, root)) })
}
@@ -59,20 +58,22 @@ impl<'db> TrieMut for SecTrieDBMut<'db> {
self.raw.is_empty()
}
fn contains(&self, key: &[u8]) -> bool {
fn contains(&self, key: &[u8]) -> super::Result<bool> {
self.raw.contains(&key.sha3())
}
fn get<'a, 'key>(&'a self, key: &'key [u8]) -> Option<&'a [u8]> where 'a: 'key {
fn get<'a, 'key>(&'a self, key: &'key [u8]) -> super::Result<Option<&'a [u8]>>
where 'a: 'key
{
self.raw.get(&key.sha3())
}
fn insert(&mut self, key: &[u8], value: &[u8]) {
self.raw.insert(&key.sha3(), value);
fn insert(&mut self, key: &[u8], value: &[u8]) -> super::Result<()> {
self.raw.insert(&key.sha3(), value)
}
fn remove(&mut self, key: &[u8]) {
self.raw.remove(&key.sha3());
fn remove(&mut self, key: &[u8]) -> super::Result<()> {
self.raw.remove(&key.sha3())
}
}
@@ -86,8 +87,8 @@ fn sectrie_to_trie() {
let mut root = H256::default();
{
let mut t = SecTrieDBMut::new(&mut memdb, &mut root);
t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]);
t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]).unwrap();
}
let t = TrieDB::new(&memdb, &root).unwrap();
assert_eq!(t.get(&(&[0x01u8, 0x23]).sha3()).unwrap(), &[0x01u8, 0x23]);
assert_eq!(t.get(&(&[0x01u8, 0x23]).sha3()).unwrap().unwrap(), &[0x01u8, 0x23]);
}

View File

@@ -18,9 +18,8 @@ use common::*;
use hashdb::*;
use nibbleslice::*;
use rlp::*;
use super::trietraits::{Trie, TrieItem};
use super::node::Node;
use super::TrieError;
use super::{Trie, TrieItem, TrieError};
/// A `Trie` implementation using a generic `HashDB` backing database.
///
@@ -41,11 +40,11 @@ use super::TrieError;
/// fn main() {
/// let mut memdb = MemoryDB::new();
/// let mut root = H256::new();
/// TrieDBMut::new(&mut memdb, &mut root).insert(b"foo", b"bar");
/// TrieDBMut::new(&mut memdb, &mut root).insert(b"foo", b"bar").unwrap();
/// let t = TrieDB::new(&memdb, &root).unwrap();
/// assert!(t.contains(b"foo"));
/// assert_eq!(t.get(b"foo").unwrap(), b"bar");
/// assert!(t.db_items_remaining().is_empty());
/// assert!(t.contains(b"foo").unwrap());
/// assert_eq!(t.get(b"foo").unwrap().unwrap(), b"bar");
/// assert!(t.db_items_remaining().unwrap().is_empty());
/// }
/// ```
pub struct TrieDB<'db> {
@@ -59,9 +58,9 @@ pub struct TrieDB<'db> {
impl<'db> TrieDB<'db> {
/// Create a new trie with the backing database `db` and `root`
/// Returns an error if `root` does not exist
pub fn new(db: &'db HashDB, root: &'db H256) -> Result<Self, TrieError> {
pub fn new(db: &'db HashDB, root: &'db H256) -> super::Result<Self> {
if !db.contains(root) {
Err(TrieError::InvalidStateRoot)
Err(Box::new(TrieError::InvalidStateRoot(*root)))
} else {
Ok(TrieDB {
db: db,
@@ -77,11 +76,11 @@ impl<'db> TrieDB<'db> {
}
/// Determine all the keys in the backing database that belong to the trie.
pub fn keys(&self) -> Vec<H256> {
pub fn keys(&self) -> super::Result<Vec<H256>> {
let mut ret: Vec<H256> = Vec::new();
ret.push(self.root.clone());
self.accumulate_keys(self.root_node(), &mut ret);
ret
try!(self.accumulate_keys(try!(self.root_node()), &mut ret));
Ok(ret)
}
/// Convert a vector of hashes to a hashmap of hash to occurrences.
@@ -95,49 +94,51 @@ impl<'db> TrieDB<'db> {
/// Determine occurrences of items in the backing database which are not related to this
/// trie.
pub fn db_items_remaining(&self) -> HashMap<H256, i32> {
pub fn db_items_remaining(&self) -> super::Result<HashMap<H256, i32>> {
let mut ret = self.db.keys();
for (k, v) in Self::to_map(self.keys()).into_iter() {
for (k, v) in Self::to_map(try!(self.keys())).into_iter() {
let keycount = *ret.get(&k).unwrap_or(&0);
match keycount <= v as i32 {
true => ret.remove(&k),
_ => ret.insert(k, keycount - v as i32),
};
}
ret
Ok(ret)
}
/// Recursion helper for `keys`.
fn accumulate_keys(&self, node: Node, acc: &mut Vec<H256>) {
fn accumulate_keys(&self, node: Node, acc: &mut Vec<H256>) -> super::Result<()> {
let mut handle_payload = |payload| {
let p = Rlp::new(payload);
if p.is_data() && p.size() == 32 {
acc.push(p.as_val());
}
self.accumulate_keys(self.get_node(payload), acc);
self.accumulate_keys(try!(self.get_node(payload)), acc)
};
match node {
Node::Extension(_, payload) => handle_payload(payload),
Node::Branch(payloads, _) => for payload in &payloads { handle_payload(payload) },
Node::Extension(_, payload) => try!(handle_payload(payload)),
Node::Branch(payloads, _) => for payload in &payloads { try!(handle_payload(payload)) },
_ => {},
}
Ok(())
}
/// Get the root node's RLP.
fn root_node(&self) -> Node {
Node::decoded(self.root_data())
fn root_node(&self) -> super::Result<Node> {
self.root_data().map(Node::decoded)
}
/// Get the data of the root node.
fn root_data(&self) -> &[u8] {
self.db.get(self.root).expect("Trie root not found!")
fn root_data(&self) -> super::Result<&[u8]> {
self.db.get(self.root).ok_or_else(|| Box::new(TrieError::InvalidStateRoot(*self.root)))
}
/// Get the root node as a `Node`.
fn get_node<'a>(&'a self, node: &'a [u8]) -> Node {
Node::decoded(self.get_raw_or_lookup(node))
fn get_node(&'db self, node: &'db [u8]) -> super::Result<Node> {
self.get_raw_or_lookup(node).map(Node::decoded)
}
/// Indentation helper for `formal_all`.
@@ -154,7 +155,9 @@ impl<'db> TrieDB<'db> {
Node::Leaf(slice, value) => try!(writeln!(f, "'{:?}: {:?}.", slice, value.pretty())),
Node::Extension(ref slice, ref item) => {
try!(write!(f, "'{:?} ", slice));
try!(self.fmt_all(self.get_node(item), f, deepness));
if let Ok(node) = self.get_node(item) {
try!(self.fmt_all(node, f, deepness));
}
},
Node::Branch(ref nodes, ref value) => {
try!(writeln!(f, ""));
@@ -164,12 +167,15 @@ impl<'db> TrieDB<'db> {
}
for i in 0..16 {
match self.get_node(nodes[i]) {
Node::Empty => {},
n => {
Ok(Node::Empty) => {},
Ok(n) => {
try!(self.fmt_indent(f, deepness + 1));
try!(write!(f, "'{:x} ", i));
try!(self.fmt_all(n, f, deepness + 1));
}
Err(e) => {
try!(write!(f, "ERROR: {}", e));
}
}
}
},
@@ -182,38 +188,46 @@ impl<'db> TrieDB<'db> {
}
/// Return optional data for a key given as a `NibbleSlice`. Returns `None` if no data exists.
fn do_lookup<'a, 'key>(&'a self, key: &NibbleSlice<'key>) -> Option<&'a [u8]> where 'a: 'key {
let root_rlp = self.root_data();
self.get_from_node(root_rlp, key)
fn do_lookup<'key>(&'db self, key: &NibbleSlice<'key>) -> super::Result<Option<&'db [u8]>>
where 'db: 'key
{
let root_rlp = try!(self.root_data());
self.get_from_node(&root_rlp, key)
}
/// Recursible function to retrieve the value given a `node` and a partial `key`. `None` if no
/// value exists for the key.
///
/// Note: Not a public API; use Trie trait functions.
fn get_from_node<'a, 'key>(&'a self, node: &'a [u8], key: &NibbleSlice<'key>) -> Option<&'a [u8]> where 'a: 'key {
fn get_from_node<'key>(&'db self, node: &'db [u8], key: &NibbleSlice<'key>) -> super::Result<Option<&'db [u8]>>
where 'db: 'key
{
match Node::decoded(node) {
Node::Leaf(ref slice, ref value) if key == slice => Some(value),
Node::Leaf(ref slice, ref value) if key == slice => Ok(Some(value)),
Node::Extension(ref slice, ref item) if key.starts_with(slice) => {
self.get_from_node(self.get_raw_or_lookup(item), &key.mid(slice.len()))
let data = try!(self.get_raw_or_lookup(item));
self.get_from_node(data, &key.mid(slice.len()))
},
Node::Branch(ref nodes, value) => match key.is_empty() {
true => value,
false => self.get_from_node(self.get_raw_or_lookup(nodes[key.at(0) as usize]), &key.mid(1))
true => Ok(value),
false => self.get_from_node(try!(self.get_raw_or_lookup(nodes[key.at(0) as usize])), &key.mid(1))
},
_ => None
_ => Ok(None)
}
}
/// Given some node-describing data `node`, return the actual node RLP.
/// This could be a simple identity operation in the case that the node is sufficiently small, but
/// may require a database lookup.
fn get_raw_or_lookup<'a>(&'a self, node: &'a [u8]) -> &'a [u8] {
fn get_raw_or_lookup(&'db self, node: &'db [u8]) -> super::Result<&'db [u8]> {
// check if its sha3 + len
let r = Rlp::new(node);
match r.is_data() && r.size() == 32 {
true => self.db.get(&r.as_val::<H256>()).unwrap_or_else(|| panic!("Not found! {:?}", r.as_val::<H256>())),
false => node
true => {
let key = r.as_val::<H256>();
self.db.get(&key).ok_or_else(|| Box::new(TrieError::IncompleteDatabase(key)))
}
false => Ok(node)
}
}
}
@@ -229,7 +243,6 @@ enum Status {
#[derive(Clone, Eq, PartialEq)]
struct Crumb<'a> {
node: Node<'a>,
// key: &'a[u8],
status: Status,
}
@@ -262,7 +275,7 @@ impl<'a> TrieDBIterator<'a> {
trail: vec![],
key_nibbles: Vec::new(),
};
r.descend(db.root_data());
r.descend(db.root_data().unwrap());
r
}
@@ -270,7 +283,7 @@ impl<'a> TrieDBIterator<'a> {
fn descend(&mut self, d: &'a [u8]) {
self.trail.push(Crumb {
status: Status::Entering,
node: self.db.get_node(d)
node: self.db.get_node(d).unwrap(),
});
match self.trail.last().unwrap().node {
Node::Leaf(n, _) | Node::Extension(n, _) => { self.key_nibbles.extend(n.iter()); },
@@ -342,11 +355,9 @@ impl<'db> Trie for TrieDB<'db> {
fn root(&self) -> &H256 { self.root }
fn contains(&self, key: &[u8]) -> bool {
self.get(key).is_some()
}
fn get<'a, 'key>(&'a self, key: &'key [u8]) -> Option<&'a [u8]> where 'a: 'key {
fn get<'a, 'key>(&'a self, key: &'key [u8]) -> super::Result<Option<&'a [u8]>>
where 'a: 'key
{
self.do_lookup(&NibbleSlice::new(key))
}
}
@@ -362,8 +373,8 @@ impl<'db> fmt::Debug for TrieDB<'db> {
#[test]
fn iterator() {
use super::trietraits::TrieMut;
use memorydb::*;
use super::TrieMut;
use super::triedbmut::*;
let d = vec![ &b"A"[..], &b"AA"[..], &b"AB"[..], &b"B"[..] ];
@@ -373,7 +384,7 @@ fn iterator() {
{
let mut t = TrieDBMut::new(&mut memdb, &mut root);
for x in &d {
t.insert(x, x);
t.insert(x, x).unwrap();
}
}
assert_eq!(d.iter().map(|i|i.to_vec()).collect::<Vec<_>>(), TrieDB::new(&memdb, &root).unwrap().iter().map(|x|x.0).collect::<Vec<_>>());

View File

@@ -273,11 +273,11 @@ impl<'a> Index<&'a StorageHandle> for NodeStorage {
/// let mut t = TrieDBMut::new(&mut memdb, &mut root);
/// assert!(t.is_empty());
/// assert_eq!(*t.root(), SHA3_NULL_RLP);
/// t.insert(b"foo", b"bar");
/// assert!(t.contains(b"foo"));
/// assert_eq!(t.get(b"foo").unwrap(), b"bar");
/// t.remove(b"foo");
/// assert!(!t.contains(b"foo"));
/// t.insert(b"foo", b"bar").unwrap();
/// assert!(t.contains(b"foo").unwrap());
/// assert_eq!(t.get(b"foo").unwrap().unwrap(), b"bar");
/// t.remove(b"foo").unwrap();
/// assert!(!t.contains(b"foo").unwrap());
/// }
/// ```
pub struct TrieDBMut<'a> {
@@ -309,9 +309,9 @@ impl<'a> TrieDBMut<'a> {
/// Create a new trie with the backing database `db` and `root.
/// Returns an error if `root` does not exist.
pub fn from_existing(db: &'a mut HashDB, root: &'a mut H256) -> Result<Self, TrieError> {
pub fn from_existing(db: &'a mut HashDB, root: &'a mut H256) -> super::Result<Self> {
if !db.contains(root) {
return Err(TrieError::InvalidStateRoot);
return Err(Box::new(TrieError::InvalidStateRoot(*root)));
}
let root_handle = NodeHandle::Hash(*root);
@@ -335,23 +335,23 @@ impl<'a> TrieDBMut<'a> {
}
// cache a node by hash
fn cache(&mut self, hash: H256) -> StorageHandle {
let node_rlp = self.db.get(&hash).expect("Not found!");
fn cache(&mut self, hash: H256) -> super::Result<StorageHandle> {
let node_rlp = try!(self.db.get(&hash).ok_or_else(|| Box::new(TrieError::IncompleteDatabase(hash))));
let node = Node::from_rlp(node_rlp, &*self.db, &mut self.storage);
self.storage.alloc(Stored::Cached(node, hash))
Ok(self.storage.alloc(Stored::Cached(node, hash)))
}
// inspect a node, choosing either to replace, restore, or delete it.
// if restored or replaced, returns the new node along with a flag of whether it was changed.
fn inspect<F>(&mut self, stored: Stored, inspector: F) -> Option<(Stored, bool)>
where F: FnOnce(&mut Self, Node) -> Action {
match stored {
Stored::New(node) => match inspector(self, node) {
fn inspect<F>(&mut self, stored: Stored, inspector: F) -> super::Result<Option<(Stored, bool)>>
where F: FnOnce(&mut Self, Node) -> super::Result<Action> {
Ok(match stored {
Stored::New(node) => match try!(inspector(self, node)) {
Action::Restore(node) => Some((Stored::New(node), false)),
Action::Replace(node) => Some((Stored::New(node), true)),
Action::Delete => None,
},
Stored::Cached(node, hash) => match inspector(self, node) {
Stored::Cached(node, hash) => match try!(inspector(self, node)) {
Action::Restore(node) => Some((Stored::Cached(node, hash), false)),
Action::Replace(node) => {
self.death_row.insert(hash);
@@ -362,21 +362,22 @@ impl<'a> TrieDBMut<'a> {
None
}
},
}
})
}
// walk the trie, attempting to find the key's node.
fn lookup<'x, 'key>(&'x self, partial: NibbleSlice<'key>, handle: &NodeHandle) -> Option<&'x [u8]>
where 'x: 'key {
fn lookup<'x, 'key>(&'x self, partial: NibbleSlice<'key>, handle: &NodeHandle) -> super::Result<Option<&'x [u8]>>
where 'x: 'key
{
match *handle {
NodeHandle::Hash(ref hash) => self.do_db_lookup(hash, partial),
NodeHandle::InMemory(ref handle) => match self.storage[handle] {
Node::Empty => None,
Node::Empty => Ok(None),
Node::Leaf(ref key, ref value) => {
if NibbleSlice::from_encoded(key).0 == partial {
Some(value)
Ok(Some(value))
} else {
None
Ok(None)
}
}
Node::Extension(ref slice, ref child) => {
@@ -384,15 +385,18 @@ impl<'a> TrieDBMut<'a> {
if partial.starts_with(&slice) {
self.lookup(partial.mid(slice.len()), child)
} else {
None
Ok(None)
}
}
Node::Branch(ref children, ref value) => {
if partial.is_empty() {
value.as_ref().map(|v| &v[..])
Ok(value.as_ref().map(|v| &v[..]))
} else {
let idx = partial.at(0);
(&children[idx as usize]).as_ref().and_then(|child| self.lookup(partial.mid(1), child))
match children[idx as usize].as_ref() {
Some(child) => self.lookup(partial.mid(1), child),
None => Ok(None),
}
}
}
}
@@ -400,60 +404,68 @@ impl<'a> TrieDBMut<'a> {
}
/// Return optional data for a key given as a `NibbleSlice`. Returns `None` if no data exists.
fn do_db_lookup<'x, 'key>(&'x self, hash: &H256, key: NibbleSlice<'key>) -> Option<&'x [u8]> where 'x: 'key {
self.db.get(hash).and_then(|node_rlp| self.get_from_db_node(node_rlp, key))
fn do_db_lookup<'x, 'key>(&'x self, hash: &H256, key: NibbleSlice<'key>) -> super::Result<Option<&'x [u8]>>
where 'x: 'key
{
self.db.get(hash).ok_or_else(|| Box::new(TrieError::IncompleteDatabase(*hash)))
.and_then(|node_rlp| self.get_from_db_node(node_rlp, key))
}
/// Recursible function to retrieve the value given a `node` and a partial `key`. `None` if no
/// value exists for the key.
///
/// Note: Not a public API; use Trie trait functions.
fn get_from_db_node<'x, 'key>(&'x self, node: &'x [u8], key: NibbleSlice<'key>) -> Option<&'x [u8]> where 'x: 'key {
fn get_from_db_node<'x, 'key>(&'x self, node: &'x [u8], key: NibbleSlice<'key>) -> super::Result<Option<&'x [u8]>>
where 'x: 'key
{
match RlpNode::decoded(node) {
RlpNode::Leaf(ref slice, ref value) if &key == slice => Some(value),
RlpNode::Leaf(ref slice, ref value) if &key == slice => Ok(Some(value)),
RlpNode::Extension(ref slice, ref item) if key.starts_with(slice) => {
self.get_from_db_node(self.get_raw_or_lookup(item), key.mid(slice.len()))
self.get_from_db_node(try!(self.get_raw_or_lookup(item)), key.mid(slice.len()))
},
RlpNode::Branch(ref nodes, value) => match key.is_empty() {
true => value,
false => self.get_from_db_node(self.get_raw_or_lookup(nodes[key.at(0) as usize]), key.mid(1))
true => Ok(value),
false => self.get_from_db_node(try!(self.get_raw_or_lookup(nodes[key.at(0) as usize])), key.mid(1))
},
_ => None
_ => Ok(None),
}
}
/// Given some node-describing data `node`, return the actual node RLP.
/// This could be a simple identity operation in the case that the node is sufficiently small, but
/// may require a database lookup.
fn get_raw_or_lookup<'x>(&'x self, node: &'x [u8]) -> &'x [u8] {
fn get_raw_or_lookup<'x>(&'x self, node: &'x [u8]) -> super::Result<&'x [u8]> {
// check if its sha3 + len
let r = Rlp::new(node);
match r.is_data() && r.size() == 32 {
true => self.db.get(&r.as_val::<H256>()).expect("Not found!"),
false => node
true => {
let key = r.as_val::<H256>();
self.db.get(&key).ok_or_else(|| Box::new(TrieError::IncompleteDatabase(key)))
}
false => Ok(node)
}
}
/// insert a key, value pair into the trie, creating new nodes if necessary.
fn insert_at(&mut self, handle: NodeHandle, partial: NibbleSlice, value: Bytes) -> (StorageHandle, bool) {
fn insert_at(&mut self, handle: NodeHandle, partial: NibbleSlice, value: Bytes) -> super::Result<(StorageHandle, bool)> {
let h = match handle {
NodeHandle::InMemory(h) => h,
NodeHandle::Hash(h) => self.cache(h)
NodeHandle::Hash(h) => try!(self.cache(h)),
};
let stored = self.storage.destroy(h);
let (new_stored, changed) = self.inspect(stored, move |trie, stored| {
trie.insert_inspector(stored, partial, value).into_action()
}).expect("Insertion never deletes.");
let (new_stored, changed) = try!(self.inspect(stored, move |trie, stored| {
trie.insert_inspector(stored, partial, value).map(|a| a.into_action())
})).expect("Insertion never deletes.");
(self.storage.alloc(new_stored), changed)
Ok((self.storage.alloc(new_stored), changed))
}
/// the insertion inspector.
#[cfg_attr(feature = "dev", allow(cyclomatic_complexity))]
fn insert_inspector(&mut self, node: Node, partial: NibbleSlice, value: Bytes) -> InsertAction {
fn insert_inspector(&mut self, node: Node, partial: NibbleSlice, value: Bytes) -> super::Result<InsertAction> {
trace!(target: "trie", "augmented (partial: {:?}, value: {:?})", partial, value.pretty());
match node {
Ok(match node {
Node::Empty => {
trace!(target: "trie", "empty: COMPOSE");
InsertAction::Replace(Node::Leaf(partial.encoded(true), value))
@@ -473,11 +485,11 @@ impl<'a> TrieDBMut<'a> {
let partial = partial.mid(1);
if let Some(child) = children[idx].take() {
// original had something there. recurse down into it.
let (new_child, changed) = self.insert_at(child, partial, value);
let (new_child, changed) = try!(self.insert_at(child, partial, value));
children[idx] = Some(new_child.into());
if !changed {
// the new node we composed didn't change. that means our branch is untouched too.
return InsertAction::Restore(Node::Branch(children, stored_value));
return Ok(InsertAction::Restore(Node::Branch(children, stored_value)));
}
} else {
// original had nothing there. compose a leaf.
@@ -516,7 +528,8 @@ impl<'a> TrieDBMut<'a> {
};
// always replace because whatever we get out here is not the branch we started with.
InsertAction::Replace(self.insert_inspector(branch, partial, value).unwrap_node())
let branch_action = try!(self.insert_inspector(branch, partial, value)).unwrap_node();
InsertAction::Replace(branch_action)
} else if cp == existing_key.len() {
trace!(target: "trie", "complete-prefix (cp={:?}): AUGMENT-AT-END", cp);
@@ -524,7 +537,7 @@ impl<'a> TrieDBMut<'a> {
// make a stub branch and an extension.
let branch = Node::Branch(empty_children(), Some(stored_value));
// augment the new branch.
let branch = self.insert_inspector(branch, partial.mid(cp), value).unwrap_node();
let branch = try!(self.insert_inspector(branch, partial.mid(cp), value)).unwrap_node();
// always replace since we took a leaf and made an extension.
let branch_handle = self.storage.alloc(Stored::New(branch)).into();
@@ -537,7 +550,7 @@ impl<'a> TrieDBMut<'a> {
let low = Node::Leaf(existing_key.mid(cp).encoded(true), stored_value);
// augment it. this will result in the Leaf -> cp == 0 routine,
// which creates a branch.
let augmented_low = self.insert_inspector(low, partial.mid(cp), value).unwrap_node();
let augmented_low = try!(self.insert_inspector(low, partial.mid(cp), value)).unwrap_node();
// make an extension using it. this is a replacement.
InsertAction::Replace(Node::Extension(
@@ -568,14 +581,15 @@ impl<'a> TrieDBMut<'a> {
};
// continue inserting.
InsertAction::Replace(self.insert_inspector(Node::Branch(children, None), partial, value).unwrap_node())
let branch_action = try!(self.insert_inspector(Node::Branch(children, None), partial, value)).unwrap_node();
InsertAction::Replace(branch_action)
} else if cp == existing_key.len() {
trace!(target: "trie", "complete-prefix (cp={:?}): AUGMENT-AT-END", cp);
// fully-shared prefix.
// insert into the child node.
let (new_child, changed) = self.insert_at(child_branch, partial.mid(cp), value);
let (new_child, changed) = try!(self.insert_at(child_branch, partial.mid(cp), value));
let new_ext = Node::Extension(existing_key.encoded(false), new_child.into());
// if the child branch wasn't changed, meaning this extension remains the same.
@@ -589,7 +603,7 @@ impl<'a> TrieDBMut<'a> {
// partially-shared.
let low = Node::Extension(existing_key.mid(cp).encoded(false), child_branch);
// augment the extension. this will take the cp == 0 path, creating a branch.
let augmented_low = self.insert_inspector(low, partial.mid(cp), value).unwrap_node();
let augmented_low = try!(self.insert_inspector(low, partial.mid(cp), value)).unwrap_node();
// always replace, since this extension is not the one we started with.
// this is known because the partial key is only the common prefix.
@@ -599,37 +613,38 @@ impl<'a> TrieDBMut<'a> {
))
}
}
}
})
}
/// Remove a node from the trie based on key.
fn remove_at(&mut self, handle: NodeHandle, partial: NibbleSlice) -> Option<(StorageHandle, bool)> {
fn remove_at(&mut self, handle: NodeHandle, partial: NibbleSlice) -> super::Result<Option<(StorageHandle, bool)>> {
let stored = match handle {
NodeHandle::InMemory(h) => self.storage.destroy(h),
NodeHandle::Hash(h) => {
let handle = self.cache(h);
let handle = try!(self.cache(h));
self.storage.destroy(handle)
}
};
self.inspect(stored, move |trie, node| trie.remove_inspector(node, partial))
.map(|(new, changed)| (self.storage.alloc(new), changed))
let opt = try!(self.inspect(stored, move |trie, node| trie.remove_inspector(node, partial)));
Ok(opt.map(|(new, changed)| (self.storage.alloc(new), changed)))
}
/// the removal inspector
fn remove_inspector(&mut self, node: Node, partial: NibbleSlice) -> Action {
match (node, partial.is_empty()) {
fn remove_inspector(&mut self, node: Node, partial: NibbleSlice) -> super::Result<Action> {
Ok(match (node, partial.is_empty()) {
(Node::Empty, _) => Action::Delete,
(Node::Branch(c, None), true) => Action::Restore(Node::Branch(c, None)),
(Node::Branch(children, _), true) => {
// always replace since we took the value out.
Action::Replace(self.fix(Node::Branch(children, None)))
Action::Replace(try!(self.fix(Node::Branch(children, None))))
}
(Node::Branch(mut children, value), false) => {
let idx = partial.at(0) as usize;
if let Some(child) = children[idx].take() {
trace!(target: "trie", "removing value out of branch child, partial={:?}", partial);
match self.remove_at(child, partial.mid(1)) {
match try!(self.remove_at(child, partial.mid(1))) {
Some((new, changed)) => {
children[idx] = Some(new.into());
let branch = Node::Branch(children, value);
@@ -644,7 +659,7 @@ impl<'a> TrieDBMut<'a> {
// the child we took was deleted.
// the node may need fixing.
trace!(target: "trie", "branch child deleted, partial={:?}", partial);
Action::Replace(self.fix(Node::Branch(children, value)))
Action::Replace(try!(self.fix(Node::Branch(children, value))))
}
}
} else {
@@ -670,14 +685,14 @@ impl<'a> TrieDBMut<'a> {
if cp == existing_len {
// try to remove from the child branch.
trace!(target: "trie", "removing from extension child, partial={:?}", partial);
match self.remove_at(child_branch, partial.mid(cp)) {
match try!(self.remove_at(child_branch, partial.mid(cp))) {
Some((new_child, changed)) => {
let new_child = new_child.into();
// if the child branch was unchanged, then the extension is too.
// otherwise, this extension may need fixing.
match changed {
true => Action::Replace(self.fix(Node::Extension(encoded, new_child))),
true => Action::Replace(try!(self.fix(Node::Extension(encoded, new_child)))),
false => Action::Restore(Node::Extension(encoded, new_child)),
}
}
@@ -692,7 +707,7 @@ impl<'a> TrieDBMut<'a> {
Action::Restore(Node::Extension(encoded, child_branch))
}
}
}
})
}
/// Given a node which may be in an _invalid state_, fix it such that it is then in a valid
@@ -701,7 +716,7 @@ impl<'a> TrieDBMut<'a> {
/// _invalid state_ means:
/// - Branch node where there is only a single entry;
/// - Extension node followed by anything other than a Branch node.
fn fix(&mut self, node: Node) -> Node {
fn fix(&mut self, node: Node) -> super::Result<Node> {
match node {
Node::Branch(mut children, value) => {
// if only a single value, transmute to leaf/extension and feed through fixed.
@@ -734,12 +749,12 @@ impl<'a> TrieDBMut<'a> {
(UsedIndex::None, Some(value)) => {
// make a leaf.
trace!(target: "trie", "fixing: branch -> leaf");
Node::Leaf(NibbleSlice::new(&[]).encoded(true), value)
Ok(Node::Leaf(NibbleSlice::new(&[]).encoded(true), value))
}
(_, value) => {
// all is well.
trace!(target: "trie", "fixing: restoring branch");
Node::Branch(children, value)
Ok(Node::Branch(children, value))
}
}
}
@@ -747,7 +762,7 @@ impl<'a> TrieDBMut<'a> {
let stored = match child {
NodeHandle::InMemory(h) => self.storage.destroy(h),
NodeHandle::Hash(h) => {
let handle = self.cache(h);
let handle = try!(self.cache(h));
self.storage.destroy(handle)
}
};
@@ -782,7 +797,7 @@ impl<'a> TrieDBMut<'a> {
let new_partial = NibbleSlice::new_composed(&partial, &sub_partial);
trace!(target: "trie", "fixing: extension -> leaf. new_partial={:?}", new_partial);
Node::Leaf(new_partial.encoded(true), value)
Ok(Node::Leaf(new_partial.encoded(true), value))
}
child_node => {
trace!(target: "trie", "fixing: restoring extension");
@@ -794,11 +809,11 @@ impl<'a> TrieDBMut<'a> {
Stored::New(child_node)
};
Node::Extension(partial, self.storage.alloc(stored).into())
Ok(Node::Extension(partial, self.storage.alloc(stored).into()))
}
}
}
other => other, // only ext and branch need fixing.
other => Ok(other), // only ext and branch need fixing.
}
}
@@ -881,29 +896,27 @@ impl<'a> TrieMut for TrieDBMut<'a> {
}
}
fn get<'b, 'key>(&'b self, key: &'key [u8]) -> Option<&'b [u8]> where 'b: 'key {
fn get<'x, 'key>(&'x self, key: &'key [u8]) -> super::Result<Option<&'x [u8]>> where 'x: 'key {
self.lookup(NibbleSlice::new(key), &self.root_handle)
}
fn contains(&self, key: &[u8]) -> bool {
self.get(key).is_some()
}
fn insert(&mut self, key: &[u8], value: &[u8]) {
fn insert(&mut self, key: &[u8], value: &[u8]) -> super::Result<()> {
if value.is_empty() {
self.remove(key);
return;
return self.remove(key);
}
let root_handle = self.root_handle();
let (new_handle, _) = self.insert_at(root_handle, NibbleSlice::new(key), value.to_owned());
let (new_handle, _) = try!(self.insert_at(root_handle, NibbleSlice::new(key), value.to_owned()));
self.root_handle = NodeHandle::InMemory(new_handle);
Ok(())
}
fn remove(&mut self, key: &[u8]) {
fn remove(&mut self, key: &[u8]) -> super::Result<()> {
let root_handle = self.root_handle();
let key = NibbleSlice::new(key);
match self.remove_at(root_handle, key) {
match try!(self.remove_at(root_handle, key)) {
Some((handle, _)) => {
self.root_handle = NodeHandle::InMemory(handle);
}
@@ -912,6 +925,8 @@ impl<'a> TrieMut for TrieDBMut<'a> {
*self.root = SHA3_NULL_RLP;
}
};
Ok(())
}
}
@@ -930,7 +945,7 @@ mod tests {
use super::*;
use rlp::*;
use bytes::ToPretty;
use super::super::trietraits::*;
use super::super::TrieMut;
use super::super::standardmap::*;
fn populate_trie<'db>(db: &'db mut HashDB, root: &'db mut H256, v: &[(Vec<u8>, Vec<u8>)]) -> TrieDBMut<'db> {
@@ -938,7 +953,7 @@ mod tests {
for i in 0..v.len() {
let key: &[u8]= &v[i].0;
let val: &[u8] = &v[i].1;
t.insert(key, val);
t.insert(key, val).unwrap();
}
t
}
@@ -946,7 +961,7 @@ mod tests {
fn unpopulate_trie<'db>(t: &mut TrieDBMut<'db>, v: &[(Vec<u8>, Vec<u8>)]) {
for i in v {
let key: &[u8]= &i.0;
t.remove(key);
t.remove(key).unwrap();
}
}
@@ -1009,7 +1024,7 @@ mod tests {
let mut memdb = MemoryDB::new();
let mut root = H256::new();
let mut t = TrieDBMut::new(&mut memdb, &mut root);
t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]);
t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]).unwrap();
assert_eq!(*t.root(), trie_root(vec![ (vec![0x01u8, 0x23], vec![0x01u8, 0x23]) ]));
}
@@ -1020,15 +1035,15 @@ mod tests {
let mut memdb = MemoryDB::new();
let mut root = H256::new();
let mut t1 = TrieDBMut::new(&mut memdb, &mut root);
t1.insert(&[0x01, 0x23], &big_value.to_vec());
t1.insert(&[0x01, 0x34], &big_value.to_vec());
t1.insert(&[0x01, 0x23], &big_value.to_vec()).unwrap();
t1.insert(&[0x01, 0x34], &big_value.to_vec()).unwrap();
let mut memdb2 = MemoryDB::new();
let mut root2 = H256::new();
let mut t2 = TrieDBMut::new(&mut memdb2, &mut root2);
t2.insert(&[0x01], &big_value.to_vec());
t2.insert(&[0x01, 0x23], &big_value.to_vec());
t2.insert(&[0x01, 0x34], &big_value.to_vec());
t2.remove(&[0x01]);
t2.insert(&[0x01], &big_value.to_vec()).unwrap();
t2.insert(&[0x01, 0x23], &big_value.to_vec()).unwrap();
t2.insert(&[0x01, 0x34], &big_value.to_vec()).unwrap();
t2.remove(&[0x01]).unwrap();
}
#[test]
@@ -1036,8 +1051,8 @@ mod tests {
let mut memdb = MemoryDB::new();
let mut root = H256::new();
let mut t = TrieDBMut::new(&mut memdb, &mut root);
t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]);
t.insert(&[0x01u8, 0x23], &[0x23u8, 0x45]);
t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]).unwrap();
t.insert(&[0x01u8, 0x23], &[0x23u8, 0x45]).unwrap();
assert_eq!(*t.root(), trie_root(vec![ (vec![0x01u8, 0x23], vec![0x23u8, 0x45]) ]));
}
@@ -1046,8 +1061,8 @@ mod tests {
let mut memdb = MemoryDB::new();
let mut root = H256::new();
let mut t = TrieDBMut::new(&mut memdb, &mut root);
t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]);
t.insert(&[0x11u8, 0x23], &[0x11u8, 0x23]);
t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]).unwrap();
t.insert(&[0x11u8, 0x23], &[0x11u8, 0x23]).unwrap();
assert_eq!(*t.root(), trie_root(vec![
(vec![0x01u8, 0x23], vec![0x01u8, 0x23]),
(vec![0x11u8, 0x23], vec![0x11u8, 0x23])
@@ -1059,9 +1074,9 @@ mod tests {
let mut memdb = MemoryDB::new();
let mut root = H256::new();
let mut t = TrieDBMut::new(&mut memdb, &mut root);
t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]);
t.insert(&[0xf1u8, 0x23], &[0xf1u8, 0x23]);
t.insert(&[0x81u8, 0x23], &[0x81u8, 0x23]);
t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]).unwrap();
t.insert(&[0xf1u8, 0x23], &[0xf1u8, 0x23]).unwrap();
t.insert(&[0x81u8, 0x23], &[0x81u8, 0x23]).unwrap();
assert_eq!(*t.root(), trie_root(vec![
(vec![0x01u8, 0x23], vec![0x01u8, 0x23]),
(vec![0x81u8, 0x23], vec![0x81u8, 0x23]),
@@ -1074,8 +1089,8 @@ mod tests {
let mut memdb = MemoryDB::new();
let mut root = H256::new();
let mut t = TrieDBMut::new(&mut memdb, &mut root);
t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]);
t.insert(&[], &[0x0]);
t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]).unwrap();
t.insert(&[], &[0x0]).unwrap();
assert_eq!(*t.root(), trie_root(vec![
(vec![], vec![0x0]),
(vec![0x01u8, 0x23], vec![0x01u8, 0x23]),
@@ -1087,8 +1102,8 @@ mod tests {
let mut memdb = MemoryDB::new();
let mut root = H256::new();
let mut t = TrieDBMut::new(&mut memdb, &mut root);
t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]);
t.insert(&[0x01u8, 0x34], &[0x01u8, 0x34]);
t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]).unwrap();
t.insert(&[0x01u8, 0x34], &[0x01u8, 0x34]).unwrap();
assert_eq!(*t.root(), trie_root(vec![
(vec![0x01u8, 0x23], vec![0x01u8, 0x23]),
(vec![0x01u8, 0x34], vec![0x01u8, 0x34]),
@@ -1100,9 +1115,9 @@ mod tests {
let mut memdb = MemoryDB::new();
let mut root = H256::new();
let mut t = TrieDBMut::new(&mut memdb, &mut root);
t.insert(&[0x01, 0x23, 0x45], &[0x01]);
t.insert(&[0x01, 0xf3, 0x45], &[0x02]);
t.insert(&[0x01, 0xf3, 0xf5], &[0x03]);
t.insert(&[0x01, 0x23, 0x45], &[0x01]).unwrap();
t.insert(&[0x01, 0xf3, 0x45], &[0x02]).unwrap();
t.insert(&[0x01, 0xf3, 0xf5], &[0x03]).unwrap();
assert_eq!(*t.root(), trie_root(vec![
(vec![0x01, 0x23, 0x45], vec![0x01]),
(vec![0x01, 0xf3, 0x45], vec![0x02]),
@@ -1118,8 +1133,8 @@ mod tests {
let mut memdb = MemoryDB::new();
let mut root = H256::new();
let mut t = TrieDBMut::new(&mut memdb, &mut root);
t.insert(&[0x01u8, 0x23], big_value0);
t.insert(&[0x11u8, 0x23], big_value1);
t.insert(&[0x01u8, 0x23], big_value0).unwrap();
t.insert(&[0x11u8, 0x23], big_value1).unwrap();
assert_eq!(*t.root(), trie_root(vec![
(vec![0x01u8, 0x23], big_value0.to_vec()),
(vec![0x11u8, 0x23], big_value1.to_vec())
@@ -1133,8 +1148,8 @@ mod tests {
let mut memdb = MemoryDB::new();
let mut root = H256::new();
let mut t = TrieDBMut::new(&mut memdb, &mut root);
t.insert(&[0x01u8, 0x23], big_value);
t.insert(&[0x11u8, 0x23], big_value);
t.insert(&[0x01u8, 0x23], big_value).unwrap();
t.insert(&[0x11u8, 0x23], big_value).unwrap();
assert_eq!(*t.root(), trie_root(vec![
(vec![0x01u8, 0x23], big_value.to_vec()),
(vec![0x11u8, 0x23], big_value.to_vec())
@@ -1146,7 +1161,7 @@ mod tests {
let mut memdb = MemoryDB::new();
let mut root = H256::new();
let t = TrieDBMut::new(&mut memdb, &mut root);
assert_eq!(t.get(&[0x5]), None);
assert_eq!(t.get(&[0x5]), Ok(None));
}
#[test]
@@ -1154,10 +1169,10 @@ mod tests {
let mut memdb = MemoryDB::new();
let mut root = H256::new();
let mut t = TrieDBMut::new(&mut memdb, &mut root);
t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]);
assert_eq!(t.get(&[0x1, 0x23]).unwrap(), &[0x1u8, 0x23]);
t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]).unwrap();
assert_eq!(t.get(&[0x1, 0x23]).unwrap().unwrap(), &[0x1u8, 0x23]);
t.commit();
assert_eq!(t.get(&[0x1, 0x23]).unwrap(), &[0x1u8, 0x23]);
assert_eq!(t.get(&[0x1, 0x23]).unwrap().unwrap(), &[0x1u8, 0x23]);
}
#[test]
@@ -1165,18 +1180,18 @@ mod tests {
let mut memdb = MemoryDB::new();
let mut root = H256::new();
let mut t = TrieDBMut::new(&mut memdb, &mut root);
t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]);
t.insert(&[0xf1u8, 0x23], &[0xf1u8, 0x23]);
t.insert(&[0x81u8, 0x23], &[0x81u8, 0x23]);
assert_eq!(t.get(&[0x01, 0x23]).unwrap(), &[0x01u8, 0x23]);
assert_eq!(t.get(&[0xf1, 0x23]).unwrap(), &[0xf1u8, 0x23]);
assert_eq!(t.get(&[0x81, 0x23]).unwrap(), &[0x81u8, 0x23]);
assert_eq!(t.get(&[0x82, 0x23]), None);
t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]).unwrap();
t.insert(&[0xf1u8, 0x23], &[0xf1u8, 0x23]).unwrap();
t.insert(&[0x81u8, 0x23], &[0x81u8, 0x23]).unwrap();
assert_eq!(t.get(&[0x01, 0x23]).unwrap().unwrap(), &[0x01u8, 0x23]);
assert_eq!(t.get(&[0xf1, 0x23]).unwrap().unwrap(), &[0xf1u8, 0x23]);
assert_eq!(t.get(&[0x81, 0x23]).unwrap().unwrap(), &[0x81u8, 0x23]);
assert_eq!(t.get(&[0x82, 0x23]), Ok(None));
t.commit();
assert_eq!(t.get(&[0x01, 0x23]).unwrap(), &[0x01u8, 0x23]);
assert_eq!(t.get(&[0xf1, 0x23]).unwrap(), &[0xf1u8, 0x23]);
assert_eq!(t.get(&[0x81, 0x23]).unwrap(), &[0x81u8, 0x23]);
assert_eq!(t.get(&[0x82, 0x23]), None);
assert_eq!(t.get(&[0x01, 0x23]).unwrap().unwrap(), &[0x01u8, 0x23]);
assert_eq!(t.get(&[0xf1, 0x23]).unwrap().unwrap(), &[0xf1u8, 0x23]);
assert_eq!(t.get(&[0x81, 0x23]).unwrap().unwrap(), &[0x81u8, 0x23]);
assert_eq!(t.get(&[0x82, 0x23]), Ok(None));
}
#[test]
@@ -1223,7 +1238,7 @@ mod tests {
let mut db = MemoryDB::new();
{
let mut t = TrieDBMut::new(&mut db, &mut root);
t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]);
t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]).unwrap();
}
{
@@ -1246,13 +1261,13 @@ mod tests {
let mut root = H256::new();
let mut t = TrieDBMut::new(&mut db, &mut root);
for &(ref key, ref value) in &x {
t.insert(key, value);
t.insert(key, value).unwrap();
}
assert_eq!(*t.root(), trie_root(x.clone()));
for &(ref key, _) in &x {
t.insert(key, &[]);
t.insert(key, &[]).unwrap();
}
assert!(t.is_empty());

View File

@@ -1,62 +0,0 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
use hash::H256;
use rlp::SHA3_NULL_RLP;
/// Trie-Item type.
pub type TrieItem<'a> = (Vec<u8>, &'a [u8]);
/// A key-value datastore implemented as a database-backed modified Merkle tree.
pub trait Trie {
/// Return the root of the trie.
fn root(&self) -> &H256;
/// Is the trie empty?
fn is_empty(&self) -> bool { *self.root() == SHA3_NULL_RLP }
/// Does the trie contain a given key?
fn contains(&self, key: &[u8]) -> bool;
/// What is the value of the given key in this trie?
fn get<'a, 'key>(&'a self, key: &'key [u8]) -> Option<&'a [u8]> where 'a: 'key;
/// Returns an iterator over elements of trie.
fn iter<'a>(&'a self) -> Box<Iterator<Item = TrieItem> + 'a>;
}
/// A key-value datastore implemented as a database-backed modified Merkle tree.
pub trait TrieMut {
/// Return the root of the trie.
fn root(&mut self) -> &H256;
/// Is the trie empty?
fn is_empty(&self) -> bool;
/// Does the trie contain a given key?
fn contains(&self, key: &[u8]) -> bool;
/// What is the value of the given key in this trie?
fn get<'a, 'key>(&'a self, key: &'key [u8]) -> Option<&'a [u8]> where 'a: 'key;
/// Insert a `key`/`value` pair into the trie. An `empty` value is equivalent to removing
/// `key` from the trie.
fn insert(&mut self, key: &[u8], value: &[u8]);
/// Remove a `key` from the trie. Equivalent to making it equal to the empty
/// value.
fn remove(&mut self, key: &[u8]);
}