Simple macro to remove some of the verbosity in deserialization.

This commit is contained in:
Ignotus Peverell 2016-10-24 17:43:14 -07:00
parent c8aa8d7c18
commit b50e1ab038
No known key found for this signature in database
GPG key ID: 99CD25F39F8F8211
4 changed files with 56 additions and 19 deletions

View file

@ -123,23 +123,18 @@ impl Writeable for Block {
/// from a binary stream. /// from a binary stream.
impl Readable<Block> for Block { impl Readable<Block> for Block {
fn read(reader: &mut Reader) -> Result<Block, ser::Error> { fn read(reader: &mut Reader) -> Result<Block, ser::Error> {
let height = try!(reader.read_u64()); let (height, previous, timestamp, utxo_merkle, tx_merkle, total_fees, nonce) =
let previous = try!(reader.read_fixed_bytes(32)); ser_multiread!(reader, read_u64, read_32_bytes, read_i64, read_32_bytes, read_32_bytes, read_u64, read_u64);
let timestamp = try!(reader.read_i64());
let utxo_merkle = try!(reader.read_fixed_bytes(32));
let tx_merkle = try!(reader.read_fixed_bytes(32));
let total_fees = try!(reader.read_u64());
let nonce = try!(reader.read_u64());
// cuckoo cycle of 42 nodes // cuckoo cycle of 42 nodes
let mut pow = [0; PROOFSIZE]; let mut pow = [0; PROOFSIZE];
for n in 0..PROOFSIZE { for n in 0..PROOFSIZE {
pow[n] = try!(reader.read_u32()); pow[n] = try!(reader.read_u32());
} }
let td = try!(reader.read_u64());
let input_len = try!(reader.read_u64()); let (td, input_len, output_len, proof_len) =
let output_len = try!(reader.read_u64()); ser_multiread!(reader, read_u64, read_u64, read_u64, read_u64);
let proof_len = try!(reader.read_u64());
if input_len > MAX_IN_OUT_LEN || output_len > MAX_IN_OUT_LEN || proof_len > MAX_IN_OUT_LEN { if input_len > MAX_IN_OUT_LEN || output_len > MAX_IN_OUT_LEN || proof_len > MAX_IN_OUT_LEN {
return Err(ser::Error::TooLargeReadErr("Too many inputs, outputs or proofs.".to_string())); return Err(ser::Error::TooLargeReadErr("Too many inputs, outputs or proofs.".to_string()));
} }
@ -147,6 +142,7 @@ impl Readable<Block> for Block {
let inputs = try!((0..input_len).map(|_| Input::read(reader)).collect()); let inputs = try!((0..input_len).map(|_| Input::read(reader)).collect());
let outputs = try!((0..output_len).map(|_| Output::read(reader)).collect()); let outputs = try!((0..output_len).map(|_| Output::read(reader)).collect());
let proofs = try!((0..proof_len).map(|_| TxProof::read(reader)).collect()); let proofs = try!((0..proof_len).map(|_| TxProof::read(reader)).collect());
Ok(Block { Ok(Block {
header: BlockHeader { header: BlockHeader {
height: height, height: height,

View file

@ -47,8 +47,7 @@ impl Writeable for TxProof {
impl Readable<TxProof> for TxProof { impl Readable<TxProof> for TxProof {
fn read(reader: &mut Reader) -> Result<TxProof, ser::Error> { fn read(reader: &mut Reader) -> Result<TxProof, ser::Error> {
let remainder = try!(reader.read_fixed_bytes(33)); let (remainder, sig) = ser_multiread!(reader, read_33_bytes, read_vec);
let sig = try!(reader.read_vec());
Ok(TxProof { Ok(TxProof {
remainder: Commitment::from_vec(remainder), remainder: Commitment::from_vec(remainder),
sig: sig, sig: sig,
@ -88,10 +87,8 @@ impl Writeable for Transaction {
/// transaction from a binary stream. /// transaction from a binary stream.
impl Readable<Transaction> for Transaction { impl Readable<Transaction> for Transaction {
fn read(reader: &mut Reader) -> Result<Transaction, ser::Error> { fn read(reader: &mut Reader) -> Result<Transaction, ser::Error> {
let fee = try!(reader.read_u64()); let (fee, zerosig, input_len, output_len) =
let zerosig = try!(reader.read_vec()); ser_multiread!(reader, read_u64, read_vec, read_u64, read_u64);
let input_len = try!(reader.read_u64());
let output_len = try!(reader.read_u64());
// in case a facetious miner sends us more than what we can allocate // in case a facetious miner sends us more than what we can allocate
if input_len > MAX_IN_OUT_LEN || output_len > MAX_IN_OUT_LEN { if input_len > MAX_IN_OUT_LEN || output_len > MAX_IN_OUT_LEN {
@ -314,8 +311,7 @@ impl Writeable for Output {
/// an Output from a binary stream. /// an Output from a binary stream.
impl Readable<Output> for Output { impl Readable<Output> for Output {
fn read(reader: &mut Reader) -> Result<Output, ser::Error> { fn read(reader: &mut Reader) -> Result<Output, ser::Error> {
let commit = try!(reader.read_fixed_bytes(33)); let (commit, proof) = ser_multiread!(reader, read_33_bytes, read_vec);
let proof = try!(reader.read_vec());
Ok(Output::BlindOutput { Ok(Output::BlindOutput {
commit: Commitment::from_vec(commit), commit: Commitment::from_vec(commit),
proof: RangeProof::from_vec(proof), proof: RangeProof::from_vec(proof),

View file

@ -71,3 +71,17 @@ macro_rules! try_m {
} }
} }
} }
/// Eliminate some of the boilerplate of deserialization (package ser) by passing just the list of reader function.
/// Example before:
/// let foo = try!(reader.read_u64());
/// let bar = try!(reader.read_u32());
/// Example after:
/// let (foo, bar) = ser_multiread!(reader, read_u64, read_u32);
#[macro_export]
macro_rules! ser_multiread {
($rdr:ident, $($read_call:ident),*) => {
( $(try!($rdr.$read_call())),* )
}
}

View file

@ -28,6 +28,11 @@ use byteorder::{ReadBytesExt, WriteBytesExt, BigEndian};
pub enum Error { pub enum Error {
/// Wraps an io error produced when reading or writing /// Wraps an io error produced when reading or writing
IOErr(io::Error), IOErr(io::Error),
/// Expected a given value that wasn't found
UnexpectedData{
expected: Vec<u8>,
received: Vec<u8>,
},
/// When asked to read too much data /// When asked to read too much data
TooLargeReadErr(String), TooLargeReadErr(String),
} }
@ -42,6 +47,8 @@ pub trait AsFixedBytes {
/// Implementations defined how different numbers and binary structures are /// Implementations defined how different numbers and binary structures are
/// written to an underlying stream or container (depending on implementation). /// written to an underlying stream or container (depending on implementation).
pub trait Writer { pub trait Writer {
/// Writes a u8 as bytes
fn write_u8(&mut self, n: u8) -> Option<Error>;
/// Writes a u32 as bytes /// Writes a u32 as bytes
fn write_u32(&mut self, n: u32) -> Option<Error>; fn write_u32(&mut self, n: u32) -> Option<Error>;
/// Writes a u64 as bytes /// Writes a u64 as bytes
@ -59,6 +66,8 @@ pub trait Writer {
/// Implementations defined how different numbers and binary structures are /// Implementations defined how different numbers and binary structures are
/// read from an underlying stream or container (depending on implementation). /// read from an underlying stream or container (depending on implementation).
pub trait Reader { pub trait Reader {
/// Read a u8 from the underlying Read
fn read_u8(&mut self) -> Result<u8, Error>;
/// Read a u32 from the underlying Read /// Read a u32 from the underlying Read
fn read_u32(&mut self) -> Result<u32, Error>; fn read_u32(&mut self) -> Result<u32, Error>;
/// Read a u64 from the underlying Read /// Read a u64 from the underlying Read
@ -69,6 +78,12 @@ pub trait Reader {
fn read_vec(&mut self) -> Result<Vec<u8>, Error>; fn read_vec(&mut self) -> Result<Vec<u8>, Error>;
/// Read a fixed number of bytes from the underlying reader. /// Read a fixed number of bytes from the underlying reader.
fn read_fixed_bytes(&mut self, length: usize) -> Result<Vec<u8>, Error>; fn read_fixed_bytes(&mut self, length: usize) -> Result<Vec<u8>, Error>;
/// Convenience function to read 32 fixed bytes
fn read_32_bytes(&mut self) -> Result<Vec<u8>, Error>;
/// Convenience function to read 33 fixed bytes
fn read_33_bytes(&mut self) -> Result<Vec<u8>, Error>;
/// Consumes a byte from the reader, producing an error if it doesn't have the expected value
fn expect_u8(&mut self, val: u8) -> Result<u8, Error>;
} }
/// Trait that every type that can be serialized as binary must implement. /// Trait that every type that can be serialized as binary must implement.
@ -116,6 +131,9 @@ struct BinReader<'a> {
/// Utility wrapper for an underlying byte Reader. Defines higher level methods /// Utility wrapper for an underlying byte Reader. Defines higher level methods
/// to read numbers, byte vectors, hashes, etc. /// to read numbers, byte vectors, hashes, etc.
impl<'a> Reader for BinReader<'a> { impl<'a> Reader for BinReader<'a> {
fn read_u8(&mut self) -> Result<u8, Error> {
self.source.read_u8().map_err(Error::IOErr)
}
fn read_u32(&mut self) -> Result<u32, Error> { fn read_u32(&mut self) -> Result<u32, Error> {
self.source.read_u32::<BigEndian>().map_err(Error::IOErr) self.source.read_u32::<BigEndian>().map_err(Error::IOErr)
} }
@ -138,6 +156,16 @@ impl<'a> Reader for BinReader<'a> {
let mut buf = vec![0; length]; let mut buf = vec![0; length];
self.source.read_exact(&mut buf).map(move |_| buf).map_err(Error::IOErr) self.source.read_exact(&mut buf).map(move |_| buf).map_err(Error::IOErr)
} }
fn read_32_bytes(&mut self) -> Result<Vec<u8>, Error> {
self.read_fixed_bytes(32)
}
fn read_33_bytes(&mut self) -> Result<Vec<u8>, Error> {
self.read_fixed_bytes(33)
}
fn expect_u8(&mut self, val: u8) -> Result<u8, Error> {
let b = try!(self.read_u8());
if b == val { Ok(b) } else { Err(Error::UnexpectedData{expected: vec![val], received: vec![b]}) }
}
} }
/// Utility wrapper for an underlying byte Writer. Defines higher level methods /// Utility wrapper for an underlying byte Writer. Defines higher level methods
@ -147,6 +175,9 @@ struct BinWriter<'a> {
} }
impl<'a> Writer for BinWriter<'a> { impl<'a> Writer for BinWriter<'a> {
fn write_u8(&mut self, n: u8) -> Option<Error> {
self.sink.write_u8(n).err().map(Error::IOErr)
}
fn write_u32(&mut self, n: u32) -> Option<Error> { fn write_u32(&mut self, n: u32) -> Option<Error> {
self.sink.write_u32::<BigEndian>(n).err().map(Error::IOErr) self.sink.write_u32::<BigEndian>(n).err().map(Error::IOErr)
} }