From 51eff239fa3c6416aaf61e297605744ae0b26d97 Mon Sep 17 00:00:00 2001 From: debris Date: Tue, 8 Dec 2015 12:33:05 +0100 Subject: [PATCH] rlpstream separated to its own submodule --- src/rlp/faces.rs | 92 ++++++++++++++- src/rlp/mod.rs | 36 +++++- src/rlp/rlpstream.rs | 247 +++++++++++++++++++++++++++++++++++++++ src/rlp/untrusted_rlp.rs | 4 +- src/triehash.rs | 2 +- 5 files changed, 375 insertions(+), 6 deletions(-) diff --git a/src/rlp/faces.rs b/src/rlp/faces.rs index 392908f16..f55b5584e 100644 --- a/src/rlp/faces.rs +++ b/src/rlp/faces.rs @@ -219,7 +219,97 @@ pub trait Encodable { } pub trait Stream: Sized { + + /// Initializes instance of empty `Stream`. fn new() -> Self; + + /// Initializes the `Stream` as a list. fn new_list(len: usize) -> Self; - fn append<'a, E>(&'a mut self, object: &E) -> &'a mut Self; + + /// Apends value to the end of stream, chainable. + /// + /// ```rust + /// extern crate ethcore_util as util; + /// use util::rlp::*; + /// + /// fn main () { + /// let mut stream = RlpStream::new_list(2); + /// stream.append(&"cat").append(&"dog"); + /// let out = stream.out(); + /// assert_eq!(out, vec![0xc8, 0x83, b'c', b'a', b't', 0x83, b'd', b'o', b'g']); + /// } + /// ``` + fn append<'a, E>(&'a mut self, object: &E) -> &'a mut Self where E: Encodable; + + /// Declare appending the list of given size, chainable. + /// + /// ```rust + /// extern crate ethcore_util as util; + /// use util::rlp::*; + /// + /// fn main () { + /// let mut stream = RlpStream::new_list(2); + /// stream.append_list(2).append(&"cat").append(&"dog"); + /// stream.append(&""); + /// let out = stream.out(); + /// assert_eq!(out, vec![0xca, 0xc8, 0x83, b'c', b'a', b't', 0x83, b'd', b'o', b'g', 0x80]); + /// } + /// ``` + fn append_list<'a>(&'a mut self, len: usize) -> &'a mut Self; + + /// Apends null to the end of stream, chainable. + /// + /// ```rust + /// extern crate ethcore_util as util; + /// use util::rlp::*; + /// + /// fn main () { + /// let mut stream = RlpStream::new_list(2); + /// stream.append_empty_data().append_empty_data(); + /// let out = stream.out(); + /// assert_eq!(out, vec![0xc2, 0x80, 0x80]); + /// } + /// ``` + fn append_empty_data<'a>(&'a mut self) -> &'a mut Self; + + /// Appends raw (pre-serialised) RLP data. Use with caution. Chainable. + fn append_raw<'a>(&'a mut self, bytes: &[u8], item_count: usize) -> &'a mut Self; + + /// Clear the output stream so far. + /// + /// ```rust + /// extern crate ethcore_util as util; + /// use util::rlp::*; + /// + /// fn main () { + /// let mut stream = RlpStream::new_list(3); + /// stream.append(&"cat"); + /// stream.clear(); + /// stream.append(&"dog"); + /// let out = stream.out(); + /// assert_eq!(out, vec![0x83, b'd', b'o', b'g']); + /// } + fn clear(&mut self); + + /// Returns true if stream doesnt expect any more items. + /// + /// ```rust + /// extern crate ethcore_util as util; + /// use util::rlp::*; + /// + /// fn main () { + /// let mut stream = RlpStream::new_list(2); + /// stream.append(&"cat"); + /// assert_eq!(stream.is_finished(), false); + /// stream.append(&"dog"); + /// assert_eq!(stream.is_finished(), true); + /// let out = stream.out(); + /// assert_eq!(out, vec![0xc8, 0x83, b'c', b'a', b't', 0x83, b'd', b'o', b'g']); + /// } + fn is_finished(&self) -> bool; + + /// Streams out encoded bytes. + /// + /// panic! if stream is not finished. + fn out(self) -> Vec; } diff --git a/src/rlp/mod.rs b/src/rlp/mod.rs index a790a480c..c7d657fa0 100644 --- a/src/rlp/mod.rs +++ b/src/rlp/mod.rs @@ -37,13 +37,45 @@ pub mod rlp; pub mod untrusted_rlp; pub mod rlpstream; -pub use self::faces::{DecoderError, Decoder, Decodable, View}; +pub use self::faces::{DecoderError, Decoder, Decodable, View, Stream, Encodable, Encoder}; pub use self::rlp::{Rlp, RlpIterator}; pub use self::untrusted_rlp::{UntrustedRlp, UntrustedRlpIterator, Prototype, PayloadInfo}; +pub use self::rlpstream::{RlpStream}; -pub use self::old::{encode, RlpStream, Encodable}; +//pub use self::old::{encode, RlpStream, Encodable}; +/// Shortcut function to decode trusted rlp +/// +/// ```rust +/// extern crate ethcore_util as util; +/// use util::rlp::*; +/// +/// fn main () { +/// let data = vec![0xc8, 0x83, b'c', b'a', b't', 0x83, b'd', b'o', b'g']; +/// let animals: Vec = decode(&data); +/// assert_eq!(animals, vec!["cat".to_string(), "dog".to_string()]); +/// } +/// ``` pub fn decode(bytes: &[u8]) -> T where T: Decodable { let rlp = Rlp::new(bytes); rlp.as_val() } + +/// Shortcut function to encode structure into rlp. +/// +/// ```rust +/// extern crate ethcore_util as util; +/// use util::rlp::*; +/// +/// fn main () { +/// let animals = vec!["cat", "dog"]; +/// let out = encode(&animals); +/// assert_eq!(out, vec![0xc8, 0x83, b'c', b'a', b't', 0x83, b'd', b'o', b'g']); +/// } +/// ``` +pub fn encode(object: &E) -> Vec where E: Encodable +{ + let mut stream = RlpStream::new(); + stream.append(object); + stream.out() +} diff --git a/src/rlp/rlpstream.rs b/src/rlp/rlpstream.rs index e69de29bb..379852083 100644 --- a/src/rlp/rlpstream.rs +++ b/src/rlp/rlpstream.rs @@ -0,0 +1,247 @@ +use elastic_array::*; +use bytes::ToBytes; +use super::faces::{Stream, Encoder, Encodable}; + +#[derive(Debug, Copy, Clone)] +struct ListInfo { + position: usize, + current: usize, + max: usize, +} + +impl ListInfo { + fn new(position: usize, max: usize) -> ListInfo { + ListInfo { + position: position, + current: 0, + max: max, + } + } +} + +/// Appendable rlp encoder. +pub struct RlpStream { + unfinished_lists: ElasticArray16, + encoder: BasicEncoder, +} + +impl Stream for RlpStream { + fn new() -> Self { + RlpStream { + unfinished_lists: ElasticArray16::new(), + encoder: BasicEncoder::new(), + } + } + + fn new_list(len: usize) -> Self { + let mut stream = RlpStream::new(); + stream.append_list(len); + stream + } + + fn append<'a, E>(&'a mut self, object: &E) -> &'a mut RlpStream where E: Encodable { + // encode given value and add it at the end of the stream + object.encode(&mut self.encoder); + + // if list is finished, prepend the length + self.note_appended(1); + + // return chainable self + self + } + + fn append_list<'a>(&'a mut self, len: usize) -> &'a mut RlpStream { + match len { + 0 => { + // we may finish, if the appended list len is equal 0 + self.encoder.bytes.push(0xc0u8); + self.note_appended(1); + }, + _ => { + let position = self.encoder.bytes.len(); + self.unfinished_lists.push(ListInfo::new(position, len)); + }, + } + + // return chainable self + self + } + + fn append_empty_data<'a>(&'a mut self) -> &'a mut RlpStream { + // self push raw item + self.encoder.bytes.push(0x80); + + // try to finish and prepend the length + self.note_appended(1); + + // return chainable self + self + } + + fn append_raw<'a>(&'a mut self, bytes: &[u8], item_count: usize) -> &'a mut RlpStream { + // push raw items + self.encoder.bytes.append_slice(bytes); + + // try to finish and prepend the length + self.note_appended(item_count); + + // return chainable self + self + } + + fn clear(&mut self) { + // clear bytes + self.encoder.bytes.clear(); + + // clear lists + self.unfinished_lists.clear(); + } + + fn is_finished(&self) -> bool { + self.unfinished_lists.len() == 0 + } + + fn out(self) -> Vec { + match self.is_finished() { + true => self.encoder.out().to_vec(), + false => panic!() + } + } +} + +impl RlpStream { + + /// Try to finish lists + fn note_appended(&mut self, inserted_items: usize) -> () { + if self.unfinished_lists.len() == 0 { + return; + } + + let back = self.unfinished_lists.len() - 1; + let should_finish = match self.unfinished_lists.get_mut(back) { + None => false, + Some(ref mut x) => { + x.current += inserted_items; + if x.current > x.max { + panic!("You cannot append more items then you expect!"); + } + x.current == x.max + } + }; + + if should_finish { + let x = self.unfinished_lists.pop().unwrap(); + let len = self.encoder.bytes.len() - x.position; + self.encoder.insert_list_len_at_pos(len, x.position); + self.note_appended(1); + } + } +} + +struct BasicEncoder { + bytes: ElasticArray1024, +} + +impl BasicEncoder { + fn new() -> BasicEncoder { + BasicEncoder { bytes: ElasticArray1024::new() } + } + + /// inserts list prefix at given position + /// TODO: optimise it further? + fn insert_list_len_at_pos(&mut self, len: usize, pos: usize) -> () { + let mut res = vec![]; + match len { + 0...55 => res.push(0xc0u8 + len as u8), + _ => { + res.push(0xf7u8 + len.to_bytes_len() as u8); + res.extend(len.to_bytes()); + } + }; + + self.bytes.insert_slice(pos, &res); + } + + /// get encoded value + fn out(self) -> ElasticArray1024 { + self.bytes + } +} + +impl Encoder for BasicEncoder { + fn emit_value(&mut self, bytes: &[u8]) -> () { + match bytes.len() { + // just 0 + 0 => self.bytes.push(0x80u8), + // byte is its own encoding + 1 if bytes[0] < 0x80 => self.bytes.append_slice(bytes), + // (prefix + length), followed by the string + len @ 1 ... 55 => { + self.bytes.push(0x80u8 + len as u8); + self.bytes.append_slice(bytes); + } + // (prefix + length of length), followed by the length, followd by the string + len => { + self.bytes.push(0xb7 + len.to_bytes_len() as u8); + self.bytes.append_slice(&len.to_bytes()); + self.bytes.append_slice(bytes); + } + } + } + + fn emit_list(&mut self, f: F) -> () where F: FnOnce(&mut Self) -> () + { + // get len before inserting a list + let before_len = self.bytes.len(); + + // insert all list elements + f(self); + + // get len after inserting a list + let after_len = self.bytes.len(); + + // diff is list len + let list_len = after_len - before_len; + self.insert_list_len_at_pos(list_len, before_len); + } +} + +impl Encodable for T where T: ToBytes { + fn encode(&self, encoder: &mut E) -> () where E: Encoder { + encoder.emit_value(&self.to_bytes()) + } +} + +impl<'a, T> Encodable for &'a [T] where T: Encodable + 'a { + fn encode(&self, encoder: &mut E) -> () where E: Encoder { + encoder.emit_list(|e| { + // insert all list elements + for el in self.iter() { + el.encode(e); + } + }) + } +} + +impl Encodable for Vec where T: Encodable { + fn encode(&self, encoder: &mut E) -> () where E: Encoder { + let r: &[T] = self.as_ref(); + r.encode(encoder) + } +} + +/// lets treat bytes differently than other lists +/// they are a single value +impl<'a> Encodable for &'a [u8] { + fn encode(&self, encoder: &mut E) -> () where E: Encoder { + encoder.emit_value(self) + } +} + +/// lets treat bytes differently than other lists +/// they are a single value +impl Encodable for Vec { + fn encode(&self, encoder: &mut E) -> () where E: Encoder { + encoder.emit_value(self) + } +} diff --git a/src/rlp/untrusted_rlp.rs b/src/rlp/untrusted_rlp.rs index fd385b99d..9e2e2f522 100644 --- a/src/rlp/untrusted_rlp.rs +++ b/src/rlp/untrusted_rlp.rs @@ -266,7 +266,7 @@ impl<'a> BasicDecoder<'a> { PayloadInfo::new(header_len, value_len) }, // we cant reach this place, but rust requires _ to be implemented - _ => { panic!(); } + _ => { unreachable!(); } }; match item.header_len + item.value_len <= bytes.len() { @@ -297,7 +297,7 @@ impl<'a> Decoder for BasicDecoder<'a> { Ok(try!(f(&bytes[begin_of_value..begin_of_value + len]))) } // we are reading value, not a list! - _ => { unreachable!(); } + _ => Err(DecoderError::RlpExpectedToBeData) } } diff --git a/src/triehash.rs b/src/triehash.rs index eea79e9d4..67ffcc666 100644 --- a/src/triehash.rs +++ b/src/triehash.rs @@ -7,7 +7,7 @@ use std::cmp; use hash::*; use sha3::*; use rlp; -use rlp::RlpStream; +use rlp::{RlpStream, Stream}; use vector::SharedPrefix; /// Generates a trie root hash for a vector of values