From 4640d64965aa7220c8a097a2374a7f35b109be6c Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sat, 28 Nov 2015 18:29:50 +0100 Subject: [PATCH] Reference-counted rocksdb backed DB. Just need DB I/O now. --- src/error.rs | 29 ++++++++++++++++- src/memorydb.rs | 23 ++++++------- src/overlaydb.rs | 84 ++++++++++++++++++++++++++++++++++++++++-------- 3 files changed, 110 insertions(+), 26 deletions(-) diff --git a/src/error.rs b/src/error.rs index 9dc471f67..bb09e2e1b 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,9 +1,17 @@ +#![feature(concat_idents)] + use rustc_serialize::hex::*; +#[derive(Debug)] +pub enum BaseDataError { + NegativelyReferencedHash, +} + #[derive(Debug)] pub enum EthcoreError { FromHex(FromHexError), - BadSize + BaseData(BaseDataError), + BadSize, } impl From for EthcoreError { @@ -11,3 +19,22 @@ impl From for EthcoreError { EthcoreError::FromHex(err) } } + +impl From for EthcoreError { + fn from(err: BaseDataError) -> EthcoreError { + EthcoreError::BaseData(err) + } +} + +// TODO: uncomment below once https://github.com/rust-lang/rust/issues/27336 sorted. +/*macro_rules! assimilate { + ($name:ident) => ( + impl From for EthcoreError { + fn from(err: concat_idents!($name, Error)) -> EthcoreError { + EthcoreError:: $name (err) + } + } + ) +} +assimilate!(FromHex); +assimilate!(BaseData);*/ \ No newline at end of file diff --git a/src/memorydb.rs b/src/memorydb.rs index 2f54b4524..cbb9e426b 100644 --- a/src/memorydb.rs +++ b/src/memorydb.rs @@ -4,6 +4,7 @@ use hash::*; use bytes::*; use sha3::*; use hashdb::*; +use std::mem; use std::collections::HashMap; #[derive(Debug,Clone)] @@ -85,19 +86,19 @@ impl MemoryDB { for empty in empties { self.data.remove(&empty); } } - /// Grab the number of references a particular `key` has. Returns None if the key - /// doesn't exist. - fn refs(&self, key: &H256) -> Option { - self.data.get(key).map(|&(_, rc)| rc) - } - - /// Grab the value associated with a particular `key`. Returns None if the key + /// Grab the raw information associated with a key. Returns None if the key /// doesn't exist. /// - /// Even when Some is returned, this is only guaranteed to return something useful - /// when `refs(key) > 0`. - fn value(&self, key: &H256) -> Option<&Bytes> { - self.data.get(key).map(|&(ref d, _)| d) + /// 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)> { + self.data.get(key) + } + + pub fn drain(&mut self) -> HashMap { + let mut data = HashMap::new(); + mem::swap(&mut self.data, &mut data); + data } } diff --git a/src/overlaydb.rs b/src/overlaydb.rs index 99d66cd5f..cb6d396c4 100644 --- a/src/overlaydb.rs +++ b/src/overlaydb.rs @@ -1,59 +1,115 @@ //! Disk-backed HashDB implementation. +use error::*; use hash::*; use bytes::*; use sha3::*; use hashdb::*; use memorydb::*; use std::ops::*; +use std::sync::*; use rocksdb::{DB, Writable}; #[derive(Clone)] pub struct OverlayDB { overlay: MemoryDB, - backing: DB, + backing: Arc, } impl OverlayDB { /// Create a new instance of OverlayDB given a `backing` database. - fn new(backing: DB) { - self.backing = backing; - overlay = MemoryDB::new(); + fn new(backing: DB) -> OverlayDB { + OverlayDB{ overlay: MemoryDB::new(), backing: Arc::new(backing) } } - /// Commit all memory operations to the backing database. - fn commit(&mut self) { - unimplemented!(); + /// Commit all memory operations to the backing database. + fn commit(&mut self) -> Result { + let mut ret = 0u32; + for i in self.overlay.drain().into_iter() { + let (key, (value, rc)) = i; + if rc != 0 { + let new_entry = match self.payload(&key) { + Some(x) => { + let (back_value, back_rc) = x; + if back_rc + rc < 0 { + return Err(From::from(BaseDataError::NegativelyReferencedHash)); + } + self.put_payload(&key, (&back_value, rc + back_rc)); + } + None => { + self.put_payload(&key, (&value, rc)); + } + }; + ret += 1; + } + } + Ok(ret) } /// Get the refs and value of the given key. fn payload(&self, key: &H256) -> Option<(Bytes, i32)> { unimplemented!(); } + + /// Get the refs and value of the given key. + fn put_payload(&self, key: &H256, payload: (&Bytes, i32)) { + unimplemented!(); + } } impl HashDB for OverlayDB { fn lookup(&self, key: &H256) -> Option { - // TODO: return ok if positive; if negative, check backing - might be enough references there to make + // return ok if positive; if negative, check backing - might be enough references there to make // it positive again. - let k = self.overlay.data.get(key); + let k = self.overlay.raw(key); match k { Some(&(ref d, rc)) if rc > 0 => Some(d.clone()), _ => { let memrc = k.map(|&(_, rc)| rc).unwrap_or(0); match self.payload(key) { - Some((d, rc)) if rc + memrc > 0 => Some(d), + Some(x) => { + let (d, rc) = x; + if rc + memrc > 0 { + Some(d) + } + else { + None + } + } + // Replace above match arm with this once https://github.com/rust-lang/rust/issues/15287 is done. + //Some((d, rc)) if rc + memrc > 0 => Some(d), _ => None, } } } } fn exists(&self, key: &H256) -> bool { - // TODO: copy and adapt code above. - m_overlay.exists(key) + // return ok if positive; if negative, check backing - might be enough references there to make + // it positive again. + let k = self.overlay.raw(key); + match k { + Some(&(ref d, rc)) if rc > 0 => true, + _ => { + let memrc = k.map(|&(_, rc)| rc).unwrap_or(0); + match self.payload(key) { + Some(x) => { + let (d, rc) = x; + if rc + memrc > 0 { + true + } + else { + false + } + } + // Replace above match arm with this once https://github.com/rust-lang/rust/issues/15287 is done. + //Some((d, rc)) if rc + memrc > 0 => true, + _ => false, + } + } + } } - fn insert(&mut self, value: &[u8]) -> H256 { m_overlay.insert(value) } - fn kill(&mut self, key: &H256) { m_overlay.kill(key); } + fn insert(&mut self, value: &[u8]) -> H256 { self.overlay.insert(value) } + fn kill(&mut self, key: &H256) { self.overlay.kill(key); } } #[test]