[WIP] Partial Transition from Writeable/Readable to Codecs (#51)

* Sample Signatures for put_enc and get_dec
* Implement put_enc and get_dec
* Implement ChainCodec in grin_chain
* Truncate src only on complete Blocks
* Truncate src only on complete Tip + Check Len
* Move BlockHeader Encoding to BlockHeaderCodec
* Define put_enc for store::Batch
* Replace BlockCodec and BlockHeaderCodec with generic BlockCodec<T>
* Implement Default for BlockCodec Manually
* Replace get_ser/put_ser with get_enc/get_dec for chain::ChainKVStore
* Remove Writeable/Readable for chain::Tip
* Add Tokio-io and Bytes to grin_p2p
* Additional Setup for Message enum + Msg{Encode,Decode} traits
* base msg ping pong encoding and test
* fill out msg-codec tests
* Implement Hand Encoding/Decoding
* msg-encode shake
* msg-encode getpeeraddr
* codec peer-addrs message, SockAddr struct wierdness
* header message codec
* msg encoding finished prelim
* Implement PeerCodec Encoding/Decoding
* Set PeerStore to use PeerCodec for Encoding/Decoding
* Add a DecIterator
* Prune PeerStore
* Replace Decoding and Encoding in handle_payload
* Prune Writeable/Readable methods in store::Store
* Remove Incomplete Frame Testing ( Not Nessesary right now )
* separate block and tx codec tests
* Refactor {Tx,Block}Codec Tests
This commit is contained in:
Jacob Payne
2017-05-22 10:16:13 -07:00
committed by Ignotus Peverell
parent a402f39633
commit de4ebdde71
21 changed files with 1789 additions and 469 deletions
+64 -217
View File
@@ -19,6 +19,7 @@ use bytes::{BytesMut, BigEndian, BufMut, Buf, IntoBuf};
use num_bigint::BigUint;
use time::Timespec;
use time;
use std::marker::PhantomData;
use core::core::{Input, Output, Proof, TxKernel, Block, BlockHeader};
use core::core::hash::Hash;
@@ -38,34 +39,83 @@ macro_rules! try_opt_dec {
});
}
#[derive(Debug, Clone)]
pub struct BlockCodec;
/// Internal Convenience Trait
pub trait BlockEncode: Sized {
fn block_encode(&self, dst: &mut BytesMut) -> Result<(), io::Error>;
}
impl codec::Encoder for BlockCodec {
type Item = Block;
/// Internal Convenience Trait
pub trait BlockDecode: Sized {
fn block_decode(src: &mut BytesMut) -> Result<Option<Self>, io::Error>;
}
/// Decodes and encodes `Block`s and their subtypes
#[derive(Debug, Clone)]
pub struct BlockCodec<T: BlockDecode + BlockEncode> {
phantom: PhantomData<T>,
}
impl<T> Default for BlockCodec<T>
where T: BlockDecode + BlockEncode
{
fn default() -> Self {
BlockCodec { phantom: PhantomData }
}
}
impl<T> codec::Encoder for BlockCodec<T>
where T: BlockDecode + BlockEncode
{
type Item = T;
type Error = io::Error;
fn encode(&mut self, item: Self::Item, dst: &mut BytesMut) -> Result<(), Self::Error> {
T::block_encode(&item, dst)
}
}
impl<T> codec::Decoder for BlockCodec<T>
where T: BlockDecode + BlockEncode
{
type Item = T;
type Error = io::Error;
fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
// Create Temporary Buffer
let ref mut temp = src.clone();
let res = try_opt_dec!(T::block_decode(temp)?);
// If succesfull truncate src by bytes read from src;
let diff = src.len() - temp.len();
src.split_to(diff);
// Return Item
Ok(Some(res))
}
}
impl BlockEncode for Block {
fn block_encode(&self, dst: &mut BytesMut) -> Result<(), io::Error> {
// Put Header
item.header.block_encode(dst)?;
self.header.block_encode(dst)?;
// Put Lengths of Inputs, Outputs and Kernels in 3 u64's
dst.reserve(24);
dst.put_u64::<BigEndian>(item.inputs.len() as u64);
dst.put_u64::<BigEndian>(item.outputs.len() as u64);
dst.put_u64::<BigEndian>(item.kernels.len() as u64);
dst.put_u64::<BigEndian>(self.inputs.len() as u64);
dst.put_u64::<BigEndian>(self.outputs.len() as u64);
dst.put_u64::<BigEndian>(self.kernels.len() as u64);
// Put Inputs
for inp in &item.inputs {
for inp in &self.inputs {
inp.block_encode(dst)?;
}
// Put Outputs
for outp in &item.outputs {
for outp in &self.outputs {
outp.block_encode(dst)?;
}
// Put TxKernels
for proof in &item.kernels {
for proof in &self.kernels {
proof.block_encode(dst)?;
}
@@ -73,10 +123,9 @@ impl codec::Encoder for BlockCodec {
}
}
impl codec::Decoder for BlockCodec {
type Item = Block;
type Error = io::Error;
fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
impl BlockDecode for Block {
fn block_decode(src: &mut BytesMut) -> Result<Option<Self>, io::Error> {
// Get Header
let header = try_opt_dec!(BlockHeader::block_decode(src)?);
@@ -129,16 +178,6 @@ impl codec::Encoder for BlockHasher {
}
}
/// Internal Convenience Trait
trait BlockEncode: Sized {
fn block_encode(&self, dst: &mut BytesMut) -> Result<(), io::Error>;
}
/// Internal Convenience Trait
trait BlockDecode: Sized {
fn block_decode(src: &mut BytesMut) -> Result<Option<Self>, io::Error>;
}
impl BlockEncode for BlockHeader {
fn block_encode(&self, dst: &mut BytesMut) -> Result<(), io::Error> {
partial_block_encode(self, dst)?;
@@ -462,195 +501,3 @@ impl BlockDecode for Proof {
Ok(Some(Proof(proof_data)))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn should_have_block_codec_roundtrip() {
use tokio_io::codec::{Encoder, Decoder};
let input = Input(Commitment([1; PEDERSEN_COMMITMENT_SIZE]));
let output = Output {
features: OutputFeatures::empty(),
commit: Commitment([1; PEDERSEN_COMMITMENT_SIZE]),
proof: RangeProof {
proof: [1; 5134],
plen: 5134,
},
};
let kernel = TxKernel {
features: KernelFeatures::empty(),
excess: Commitment([1; PEDERSEN_COMMITMENT_SIZE]),
excess_sig: vec![1; 10],
fee: 100,
};
let block = Block {
header: BlockHeader::default(),
inputs: vec![input],
outputs: vec![output],
kernels: vec![kernel],
};
let mut buf = BytesMut::with_capacity(0);
let mut codec = BlockCodec {};
codec.encode(block.clone(), &mut buf).expect("Error During Block Encoding");
let d_block =
codec.decode(&mut buf).expect("Error During Block Decoding").expect("Unfinished Block");
assert_eq!(block.header.height, d_block.header.height);
assert_eq!(block.header.previous, d_block.header.previous);
assert_eq!(block.header.timestamp, d_block.header.timestamp);
assert_eq!(block.header.cuckoo_len, d_block.header.cuckoo_len);
assert_eq!(block.header.utxo_merkle, d_block.header.utxo_merkle);
assert_eq!(block.header.tx_merkle, d_block.header.tx_merkle);
assert_eq!(block.header.features, d_block.header.features);
assert_eq!(block.header.nonce, d_block.header.nonce);
assert_eq!(block.header.pow, d_block.header.pow);
assert_eq!(block.header.difficulty, d_block.header.difficulty);
assert_eq!(block.header.total_difficulty,
d_block.header.total_difficulty);
assert_eq!(block.inputs[0].commitment(), d_block.inputs[0].commitment());
assert_eq!(block.outputs[0].features, d_block.outputs[0].features);
assert_eq!(block.outputs[0].proof().as_ref(),
d_block.outputs[0].proof().as_ref());
assert_eq!(block.outputs[0].commitment(),
d_block.outputs[0].commitment());
assert_eq!(block.kernels[0].features, d_block.kernels[0].features);
assert_eq!(block.kernels[0].excess, d_block.kernels[0].excess);
assert_eq!(block.kernels[0].excess_sig, d_block.kernels[0].excess_sig);
assert_eq!(block.kernels[0].fee, d_block.kernels[0].fee);
}
#[test]
fn should_encode_and_decode_blockheader() {
let block_header = BlockHeader::default();
let mut buf = BytesMut::with_capacity(0);
block_header.block_encode(&mut buf);
let d_block_header = BlockHeader::block_decode(&mut buf).unwrap().unwrap();
assert_eq!(block_header.height, d_block_header.height);
assert_eq!(block_header.previous, d_block_header.previous);
assert_eq!(block_header.timestamp, d_block_header.timestamp);
assert_eq!(block_header.cuckoo_len, d_block_header.cuckoo_len);
assert_eq!(block_header.utxo_merkle, d_block_header.utxo_merkle);
assert_eq!(block_header.tx_merkle, d_block_header.tx_merkle);
assert_eq!(block_header.features, d_block_header.features);
assert_eq!(block_header.nonce, d_block_header.nonce);
assert_eq!(block_header.pow, d_block_header.pow);
assert_eq!(block_header.difficulty, d_block_header.difficulty);
assert_eq!(block_header.total_difficulty,
d_block_header.total_difficulty);
}
#[test]
fn should_encode_and_decode_input() {
let input = Input(Commitment([1; PEDERSEN_COMMITMENT_SIZE]));
let mut buf = BytesMut::with_capacity(0);
input.block_encode(&mut buf);
assert_eq!([1; PEDERSEN_COMMITMENT_SIZE].as_ref(), buf);
assert_eq!(input.commitment(),
Input::block_decode(&mut buf)
.unwrap()
.unwrap()
.commitment());
}
#[test]
fn should_encode_and_decode_output() {
let output = Output {
features: OutputFeatures::empty(),
commit: Commitment([1; PEDERSEN_COMMITMENT_SIZE]),
proof: RangeProof {
proof: [1; 5134],
plen: 5134,
},
};
let mut buf = BytesMut::with_capacity(0);
output.block_encode(&mut buf);
let d_output = Output::block_decode(&mut buf).unwrap().unwrap();
assert_eq!(output.features, d_output.features);
assert_eq!(output.proof().as_ref(), d_output.proof().as_ref());
assert_eq!(output.commitment(), d_output.commitment());
}
#[test]
fn should_encode_and_decode_txkernel() {
let kernel = TxKernel {
features: KernelFeatures::empty(),
excess: Commitment([1; PEDERSEN_COMMITMENT_SIZE]),
excess_sig: vec![1; 10],
fee: 100,
};
let mut buf = BytesMut::with_capacity(0);
kernel.block_encode(&mut buf);
let d_kernel = TxKernel::block_decode(&mut buf).unwrap().unwrap();
assert_eq!(kernel.features, d_kernel.features);
assert_eq!(kernel.excess, d_kernel.excess);
assert_eq!(kernel.excess_sig, d_kernel.excess_sig);
assert_eq!(kernel.fee, d_kernel.fee);
}
#[test]
fn should_encode_and_decode_difficulty() {
let difficulty = Difficulty::from_num(1000);
let mut buf = BytesMut::with_capacity(0);
difficulty.block_encode(&mut buf);
let d_difficulty = Difficulty::block_decode(&mut buf).unwrap().unwrap();
assert_eq!(difficulty, d_difficulty);
}
#[test]
fn should_encode_and_decode_hash() {
let hash = Hash([1u8; 32]);
let mut buf = BytesMut::with_capacity(0);
hash.block_encode(&mut buf);
let d_hash = Hash::block_decode(&mut buf).unwrap().unwrap();
assert_eq!(hash, d_hash);
}
#[test]
fn should_encode_and_decode_proof() {
let proof = Proof::zero();
let mut buf = BytesMut::with_capacity(0);
proof.block_encode(&mut buf);
let d_proof = Proof::block_decode(&mut buf).unwrap().unwrap();
assert_eq!(proof, d_proof);
}
}
+185
View File
@@ -0,0 +1,185 @@
// Copyright 2016 The Grin Developers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use std::io;
use tokio_io::*;
use bytes::{BytesMut, BigEndian, BufMut, Buf, IntoBuf};
use num_bigint::BigUint;
use time::Timespec;
use time;
use std::marker::PhantomData;
use core::core::{Input, Output, Proof, TxKernel, Block, BlockHeader};
use core::core::hash::Hash;
use core::core::target::Difficulty;
use core::core::transaction::{OutputFeatures, KernelFeatures};
use core::core::block::BlockFeatures;
use core::consensus::PROOFSIZE;
use secp::pedersen::{RangeProof, Commitment};
use secp::constants::PEDERSEN_COMMITMENT_SIZE;
use super::block::*;
#[test]
fn should_have_block_codec_roundtrip() {
use tokio_io::codec::{Encoder, Decoder};
let input = Input(Commitment([1; PEDERSEN_COMMITMENT_SIZE]));
let output = Output {
features: OutputFeatures::empty(),
commit: Commitment([1; PEDERSEN_COMMITMENT_SIZE]),
proof: RangeProof {
proof: [1; 5134],
plen: 5134,
},
};
let kernel = TxKernel {
features: KernelFeatures::empty(),
excess: Commitment([1; PEDERSEN_COMMITMENT_SIZE]),
excess_sig: vec![1; 10],
fee: 100,
};
let block = Block {
header: BlockHeader::default(),
inputs: vec![input],
outputs: vec![output],
kernels: vec![kernel],
};
let mut buf = BytesMut::with_capacity(0);
let mut codec = BlockCodec::default();
codec
.encode(block.clone(), &mut buf)
.expect("Error During Block Encoding");
let d_block = codec
.decode(&mut buf)
.expect("Error During Block Decoding")
.expect("Unfinished Block");
// Check if all bytes are read
assert_eq!(buf.len(), 0);
assert_eq!(block, d_block);
}
#[test]
fn should_have_block_header_codec_roundtrip() {
use tokio_io::codec::{Encoder, Decoder};
let mut codec = BlockCodec::default();
let block_header = BlockHeader::default();
let mut buf = BytesMut::with_capacity(0);
codec.encode(block_header.clone(), &mut buf);
let d_block_header = codec.decode(&mut buf).unwrap().unwrap();
assert_eq!(block_header, d_block_header);
}
#[test]
fn should_encode_and_decode_input() {
let input = Input(Commitment([1; PEDERSEN_COMMITMENT_SIZE]));
let mut buf = BytesMut::with_capacity(0);
input.block_encode(&mut buf);
let d_input = Input::block_decode(&mut buf).unwrap().unwrap();
assert_eq!(input,d_input)
}
#[test]
fn should_encode_and_decode_output() {
let output = Output {
features: OutputFeatures::empty(),
commit: Commitment([1; PEDERSEN_COMMITMENT_SIZE]),
proof: RangeProof {
proof: [1; 5134],
plen: 5134,
},
};
let mut buf = BytesMut::with_capacity(0);
output.block_encode(&mut buf);
let d_output = Output::block_decode(&mut buf).unwrap().unwrap();
assert_eq!(output, d_output);
}
#[test]
fn should_encode_and_decode_txkernel() {
let kernel = TxKernel {
features: KernelFeatures::empty(),
excess: Commitment([1; PEDERSEN_COMMITMENT_SIZE]),
excess_sig: vec![1; 10],
fee: 100,
};
let mut buf = BytesMut::with_capacity(0);
kernel.block_encode(&mut buf);
let d_kernel = TxKernel::block_decode(&mut buf).unwrap().unwrap();
assert_eq!(kernel, d_kernel);
}
#[test]
fn should_encode_and_decode_difficulty() {
let difficulty = Difficulty::from_num(1000);
let mut buf = BytesMut::with_capacity(0);
difficulty.block_encode(&mut buf);
let d_difficulty = Difficulty::block_decode(&mut buf).unwrap().unwrap();
assert_eq!(difficulty, d_difficulty);
}
#[test]
fn should_encode_and_decode_hash() {
let hash = Hash([1u8; 32]);
let mut buf = BytesMut::with_capacity(0);
hash.block_encode(&mut buf);
let d_hash = Hash::block_decode(&mut buf).unwrap().unwrap();
assert_eq!(hash, d_hash);
}
#[test]
fn should_encode_and_decode_proof() {
let proof = Proof::zero();
let mut buf = BytesMut::with_capacity(0);
proof.block_encode(&mut buf);
let d_proof = Proof::block_decode(&mut buf).unwrap().unwrap();
assert_eq!(proof, d_proof);
}
+8 -2
View File
@@ -12,9 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
//! Codecs for Blocks and Transactions
pub mod block;
pub mod tx;
pub use self::block::{BlockCodec, BlockHasher};
pub use self::tx::TxCodec;
#[cfg(test)]
mod block_test;
#[cfg(test)]
mod tx_test;
pub use self::block::{BlockCodec, BlockHasher };
pub use self::tx::TxCodec;
+5 -85
View File
@@ -31,7 +31,8 @@ macro_rules! try_opt_dec {
});
}
#[derive(Debug, Clone)]
/// Decodes and encodes `Transaction`s
#[derive(Debug, Clone, Default)]
pub struct TxCodec;
impl codec::Encoder for TxCodec {
@@ -121,12 +122,12 @@ impl codec::Decoder for TxCodec {
}
/// Internal Convenience Trait
trait TxEncode: Sized {
pub trait TxEncode: Sized {
fn tx_encode(&self, dst: &mut BytesMut) -> Result<(), io::Error>;
}
/// Internal Convenience Trait
trait TxDecode: Sized {
pub trait TxDecode: Sized {
fn tx_decode(src: &mut BytesMut) -> Result<Option<Self>, io::Error>;
}
@@ -187,85 +188,4 @@ impl TxDecode for Input {
Ok(Some(Input(Commitment(c))))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn should_have_tx_codec_roundtrip() {
use tokio_io::codec::{Encoder, Decoder};
let input = Input(Commitment([1; PEDERSEN_COMMITMENT_SIZE]));
let output = Output {
features: OutputFeatures::empty(),
commit: Commitment([1; PEDERSEN_COMMITMENT_SIZE]),
proof: RangeProof {
proof: [1; 5134],
plen: 5134,
},
};
let tx = Transaction {
inputs: vec![input],
outputs: vec![output],
fee: 0,
excess_sig: vec![0; 10],
};
let mut buf = BytesMut::with_capacity(0);
let mut codec = TxCodec {};
codec.encode(tx.clone(), &mut buf).expect("Error During Transaction Encoding");
let d_tx = codec.decode(&mut buf)
.expect("Error During Transaction Decoding")
.expect("Unfinished Transaction");
assert_eq!(tx.inputs[0].commitment(), d_tx.inputs[0].commitment());
assert_eq!(tx.outputs[0].features, d_tx.outputs[0].features);
assert_eq!(tx.fee, d_tx.fee);
assert_eq!(tx.excess_sig, d_tx.excess_sig);
}
#[test]
fn should_encode_and_decode_output() {
let output = Output {
features: OutputFeatures::empty(),
commit: Commitment([1; PEDERSEN_COMMITMENT_SIZE]),
proof: RangeProof {
proof: [1; 5134],
plen: 5134,
},
};
let mut buf = BytesMut::with_capacity(0);
output.tx_encode(&mut buf);
let d_output = Output::tx_decode(&mut buf).unwrap().unwrap();
assert_eq!(output.features, d_output.features);
assert_eq!(output.proof().as_ref(), d_output.proof().as_ref());
assert_eq!(output.commitment(), d_output.commitment());
}
#[test]
fn should_encode_and_decode_input() {
let input = Input(Commitment([1; PEDERSEN_COMMITMENT_SIZE]));
let mut buf = BytesMut::with_capacity(0);
input.tx_encode(&mut buf);
assert_eq!([1; PEDERSEN_COMMITMENT_SIZE].as_ref(), buf);
assert_eq!(input.commitment(),
Input::tx_decode(&mut buf)
.unwrap()
.unwrap()
.commitment());
}
}
}
+93
View File
@@ -0,0 +1,93 @@
// Copyright 2016 The Grin Developers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use std::io;
use tokio_io::*;
use bytes::{BytesMut, BigEndian, BufMut, Buf, IntoBuf};
use core::core::{Input, Output, Transaction};
use core::core::transaction::OutputFeatures;
use secp::pedersen::{RangeProof, Commitment};
use secp::constants::PEDERSEN_COMMITMENT_SIZE;
use super::tx::*;
#[test]
fn should_have_tx_codec_roundtrip() {
use tokio_io::codec::{Encoder, Decoder};
let input = Input(Commitment([1; PEDERSEN_COMMITMENT_SIZE]));
let output = Output {
features: OutputFeatures::empty(),
commit: Commitment([1; PEDERSEN_COMMITMENT_SIZE]),
proof: RangeProof {
proof: [1; 5134],
plen: 5134,
},
};
let tx = Transaction {
inputs: vec![input],
outputs: vec![output],
fee: 0,
excess_sig: vec![0; 10],
};
let mut buf = BytesMut::with_capacity(0);
let mut codec = TxCodec {};
codec
.encode(tx.clone(), &mut buf)
.expect("Error During Transaction Encoding");
let d_tx = codec
.decode(&mut buf)
.expect("Error During Transaction Decoding")
.expect("Unfinished Transaction");
assert_eq!(tx, d_tx);
}
#[test]
fn should_encode_and_decode_output() {
let output = Output {
features: OutputFeatures::empty(),
commit: Commitment([1; PEDERSEN_COMMITMENT_SIZE]),
proof: RangeProof {
proof: [1; 5134],
plen: 5134,
},
};
let mut buf = BytesMut::with_capacity(0);
output.tx_encode(&mut buf);
let d_output = Output::tx_decode(&mut buf).unwrap().unwrap();
assert_eq!(output, d_output);
}
#[test]
fn should_encode_and_decode_input() {
let input = Input(Commitment([1; PEDERSEN_COMMITMENT_SIZE]));
let mut buf = BytesMut::with_capacity(0);
input.tx_encode(&mut buf);
let d_input = Input::tx_decode(&mut buf).unwrap().unwrap();
assert_eq!(input, d_input);
}
+55 -66
View File
@@ -35,13 +35,16 @@ use std::fmt;
use std::iter::Iterator;
use std::marker::PhantomData;
use std::sync::RwLock;
use tokio_io::codec::{Encoder,Decoder};
use bytes::BytesMut;
use bytes::buf::{FromBuf, IntoBuf};
use byteorder::{WriteBytesExt, BigEndian};
use rocksdb::{DB, WriteBatch, DBCompactionStyle, DBIterator, IteratorMode, Direction};
use core::ser;
mod codec;
pub mod codec;
use codec::{BlockCodec, BlockHasher, TxCodec};
/// Main error type for this crate.
@@ -54,6 +57,8 @@ pub enum Error {
RocksDbErr(String),
/// Wraps a serialization error for Writeable or Readable
SerErr(ser::Error),
/// Wraps an Io Error
Io(std::io::Error),
}
impl fmt::Display for Error {
@@ -62,6 +67,7 @@ impl fmt::Display for Error {
&Error::NotFoundErr => write!(f, "Not Found"),
&Error::RocksDbErr(ref s) => write!(f, "RocksDb Error: {}", s),
&Error::SerErr(ref e) => write!(f, "Serialization Error: {}", e.to_string()),
&Error::Io(ref e) => write!(f, "Codec Error: {}", e)
}
}
}
@@ -72,6 +78,12 @@ impl From<rocksdb::Error> for Error {
}
}
impl From<std::io::Error> for Error {
fn from(e: std::io::Error) -> Error {
Error::Io(e)
}
}
/// Thread-safe rocksdb wrapper
pub struct Store {
rdb: RwLock<DB>,
@@ -98,13 +110,25 @@ impl Store {
db.put(key, &value[..]).map_err(&From::from)
}
/// Writes a single key and its `Writeable` value to the db. Encapsulates
/// serialization.
pub fn put_ser<W: ser::Writeable>(&self, key: &[u8], value: &W) -> Result<(), Error> {
let ser_value = ser::ser_vec(value);
match ser_value {
Ok(data) => self.put(key, data),
Err(err) => Err(Error::SerErr(err)),
/// Writes a single key and a value using a given encoder.
pub fn put_enc<E: Encoder>(&self, encoder: &mut E, key: &[u8], value: E::Item) -> Result<(), Error>
where Error: From<E::Error> {
let mut data = BytesMut::with_capacity(0);
encoder.encode(value, &mut data)?;
self.put(key, data.to_vec())
}
/// Gets a value from the db, provided its key and corresponding decoder
pub fn get_dec<D: Decoder>(&self, decoder: &mut D, key: &[u8]) -> Result<Option<D::Item>, Error>
where Error: From<D::Error> {
let data = self.get(key)?;
if let Some(buf) = data {
let mut buf = BytesMut::from_buf(buf);
decoder.decode(&mut buf).map_err(From::from)
} else {
Ok(None)
}
}
@@ -114,30 +138,6 @@ impl Store {
db.get(key).map(|r| r.map(|o| o.to_vec())).map_err(From::from)
}
/// Gets a `Readable` value from the db, provided its key. Encapsulates
/// serialization.
pub fn get_ser<T: ser::Readable>(&self, key: &[u8]) -> Result<Option<T>, Error> {
self.get_ser_limited(key, 0)
}
/// Gets a `Readable` value from the db, provided its key, allowing to
/// extract only partial data. The underlying Readable size must align
/// accordingly. Encapsulates serialization.
pub fn get_ser_limited<T: ser::Readable>(&self,
key: &[u8],
len: usize)
-> Result<Option<T>, Error> {
let data = try!(self.get(key));
match data {
Some(val) => {
let mut lval = if len > 0 { &val[..len] } else { &val[..] };
let r = try!(ser::deserialize(&mut lval).map_err(Error::SerErr));
Ok(Some(r))
}
None => Ok(None),
}
}
/// Whether the provided key exists
pub fn exists(&self, key: &[u8]) -> Result<bool, Error> {
let db = self.rdb.read().unwrap();
@@ -150,17 +150,15 @@ impl Store {
db.delete(key).map_err(From::from)
}
/// Produces an iterator of `Readable` types moving forward from the
/// provided
/// key.
pub fn iter<T: ser::Readable>(&self, from: &[u8]) -> SerIterator<T> {
/// Produces an iterator of items decoded by a decoder moving forward from the provided key.
pub fn iter_dec<D: Decoder>(&self, codec: D, from: &[u8]) -> DecIterator<D> {
let db = self.rdb.read().unwrap();
SerIterator {
DecIterator {
iter: db.iterator(IteratorMode::From(from, Direction::Forward)),
_marker: PhantomData,
codec: codec
}
}
/// Builds a new batch to be used with this store.
pub fn batch(&self) -> Batch {
Batch {
@@ -182,17 +180,13 @@ pub struct Batch<'a> {
}
impl<'a> Batch<'a> {
/// Writes a single key and its `Writeable` value to the batch. The write
/// function must be called to "commit" the batch to storage.
pub fn put_ser<W: ser::Writeable>(mut self, key: &[u8], value: &W) -> Result<Batch<'a>, Error> {
let ser_value = ser::ser_vec(value);
match ser_value {
Ok(data) => {
self.batch.put(key, &data[..])?;
Ok(self)
}
Err(err) => Err(Error::SerErr(err)),
}
/// Using a given encoder, Writes a single key and a value to the batch.
pub fn put_enc<E: Encoder>(mut self, encoder: &mut E, key: &[u8], value: E::Item) -> Result<Batch<'a>, Error> where Error: From<E::Error> {
let mut data = BytesMut::with_capacity(0);
encoder.encode(value, &mut data)?;
self.batch.put(key, &data)?;
Ok(self)
}
/// Writes the batch to RocksDb.
@@ -201,29 +195,24 @@ impl<'a> Batch<'a> {
}
}
/// An iterator thad produces Readable instances back. Wraps the lower level
/// DBIterator and deserializes the returned values.
pub struct SerIterator<T>
where T: ser::Readable
{
/// An iterator that produces items from a `DBIterator` instance with a given `Decoder`.
/// Iterates and decodes returned values
pub struct DecIterator<D> where D: Decoder {
iter: DBIterator,
_marker: PhantomData<T>,
codec: D
}
impl<T> Iterator for SerIterator<T>
where T: ser::Readable
{
type Item = T;
fn next(&mut self) -> Option<T> {
impl <D> Iterator for DecIterator<D> where D: Decoder {
type Item = D::Item;
fn next(&mut self) -> Option<Self::Item> {
let next = self.iter.next();
next.and_then(|r| {
let (_, v) = r;
ser::deserialize(&mut &v[..]).ok()
})
next.and_then(|(_, v)| {
self.codec.decode(&mut BytesMut::from(v.as_ref())).ok()
}).unwrap_or(None)
}
}
/// Build a db key from a prefix and a byte vector identifier.
pub fn to_key(prefix: u8, k: &mut Vec<u8>) -> Vec<u8> {
let mut res = Vec::with_capacity(k.len() + 2);