From 122144c00a69740fac44055702785e3c5c2fdd42 Mon Sep 17 00:00:00 2001 From: debris Date: Mon, 30 Nov 2015 02:55:03 +0100 Subject: [PATCH] docs for rlp --- src/rlp.rs | 358 +++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 248 insertions(+), 110 deletions(-) diff --git a/src/rlp.rs b/src/rlp.rs index 8e3553252..a24c63b46 100644 --- a/src/rlp.rs +++ b/src/rlp.rs @@ -1,85 +1,34 @@ -//! Rlp serialization module -//! -//! Types implementing `Endocable` and `Decodable` traits -//! can be easily coverted to and from rlp. -//! Trusted rlp should be decoded with `Rlp`, untrusted with `UntrustedRlp`. -//! -//! # Examples: -//! -//! ```rust -//! extern crate ethcore_util; -//! use ethcore_util::rlp::{Rlp, UntrustedRlp, RlpStream, Decodable}; -//! -//! fn encode_value() { -//! // 1029 -//! let mut stream = RlpStream::new(); -//! stream.append(&1029u32); -//! let out = stream.out().unwrap(); -//! assert_eq!(out, vec![0x82, 0x04, 0x05]); -//! } -//! -//! fn encode_list() { -//! // [ "cat", "dog" ] -//! let mut stream = RlpStream::new_list(2); -//! stream.append(&"cat").append(&"dog"); -//! let out = stream.out().unwrap(); -//! assert_eq!(out, vec![0xc8, 0x83, b'c', b'a', b't', 0x83, b'd', b'o', b'g']); -//! } -//! -//! fn encode_list2() { -//! // [ [], [[]], [ [], [[]] ] ] -//! let mut stream = RlpStream::new_list(3); -//! stream.append_list(0); -//! stream.append_list(1).append_list(0); -//! stream.append_list(2).append_list(0).append_list(1).append_list(0); -//! let out = stream.out().unwrap(); -//! assert_eq!(out, vec![0xc7, 0xc0, 0xc1, 0xc0, 0xc3, 0xc0, 0xc1, 0xc0]); -//! } -//! -//! fn decode_value() { -//! // 0x102456789abcdef -//! let data = vec![0x88, 0x10, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef]; -//! let rlp = Rlp::new(&data); -//! let _ = u64::decode(&rlp); -//! } +//! Rlp serialization module //! -//! fn decode_untrusted_string() { -//! // "cat" -//! let data = vec![0x83, b'c', b'a', b't']; -//! let rlp = UntrustedRlp::new(&data); -//! let _ = String::decode_untrusted(&rlp).unwrap(); -//! } -//! -//! fn decode_list() { -//! // ["cat", "dog"] -//! let data = vec![0xc8, 0x83, b'c', b'a', b't', 0x83, b'd', b'o', b'g']; -//! let rlp = Rlp::new(&data); -//! let _ : Vec = Decodable::decode(&rlp); -//! } -//! -//! fn decode_list2() { -//! // [ [], [[]], [ [], [[]] ] ] -//! let data = vec![0xc7, 0xc0, 0xc1, 0xc0, 0xc3, 0xc0, 0xc1, 0xc0]; -//! let rlp = Rlp::new(&data); -//! let _v0: Vec = Decodable::decode(&rlp.at(0)); -//! let _v1: Vec> = Decodable::decode(&rlp.at(1)); -//! let nested_rlp = rlp.at(2); -//! let _v2a: Vec = Decodable::decode(&nested_rlp.at(0)); -//! let _v2b: Vec> = Decodable::decode(&nested_rlp.at(1)); -//! } +//! Allows encoding, decoding, and view onto rlp-slice //! -//! fn main() { -//! encode_value(); -//! encode_list(); -//! encode_list2(); +//!# When should you use what? //! -//! decode_value(); -//! decode_untrusted_string(); -//! decode_list(); -//! decode_list2(); -//! } -//! ``` +//!### Use `encode` function when: +//! * You want to encode something inline. +//! * You do not work on big set of data. +//! * You want to encode whole data structure at once. //! +//!### Use `decode` function when: +//! * You want to decode something inline. +//! * You do not work on big set of data. +//! * You want to decode whole rlp at once. +//! +//!### Use `RlpStream` when: +//! * You want to encode something in portions. +//! * You encode a big set of data. +//! +//!### Use `Rlp` when: +//! * You are working on trusted data (not corrupted). +//! * You want to get view onto rlp-slice. +//! * You don't want to decode whole rlp at once. +//! +//!### Use `UntrustedRlp` when: +//! * You are working on untrusted data (~corrupted). +//! * You need to handle data corruption errors. +//! * You are working on input data. +//! * You want to get view onto rlp-slice. +//! * You don't want to decode whole rlp at once. use std::fmt; use std::cell::Cell; @@ -88,7 +37,12 @@ use std::error::Error as StdError; use bytes::{ToBytes, FromBytes, FromBytesError}; use vector::InsertSlice; -/// rlp container +/// Data-oriented view onto rlp-slice. +/// +/// This is immutable structere. No operations change it. +/// +/// Should be used in places where, error handling is required, +/// eg. on input #[derive(Debug)] pub struct UntrustedRlp<'a> { bytes: &'a [u8], @@ -152,10 +106,10 @@ impl From for DecoderError { } } -/// Unsafe wrapper for rlp decode_untrustedr. +/// Data-oriented view onto trusted rlp-slice. /// -/// It assumes that you know what you are doing. Doesn't bother -/// you with error handling. +/// Unlikely to `UntrustedRlp` doesn't bother you with error +/// handling. It assumes that you know what you are doing. pub struct Rlp<'a> { rlp: UntrustedRlp<'a> } @@ -173,30 +127,79 @@ impl<'a> From> for UntrustedRlp<'a> { } impl<'a> Rlp<'a> { - /// returns new instance of `Rlp` + /// Create a new instance of `Rlp` pub fn new(bytes: &'a [u8]) -> Rlp<'a> { Rlp { rlp: UntrustedRlp::new(bytes) } } + /// Get view onto rlp-slice at index. + /// + /// Caches offset to given index, so access to successive + /// slices is faster. + /// + /// ```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 rlp = Rlp::new(&data); + /// let dog = String::decode(&rlp.at(1)); + /// assert_eq!(dog, "dog".to_string()); + /// } + /// ``` pub fn at(&self, index: usize) -> Rlp<'a> { From::from(self.rlp.at(index).unwrap()) } - /// returns true if rlp is a list + /// List value + /// + /// ```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 rlp = Rlp::new(&data); + /// assert!(rlp.is_list()); + /// } + /// ``` pub fn is_list(&self) -> bool { self.rlp.is_list() } - /// returns true if rlp is a value + /// String value + /// + /// ```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 rlp = Rlp::new(&data); + /// assert!(rlp.at(1).is_data()); + /// } + /// ``` pub fn is_data(&self) -> bool { self.rlp.is_data() } - /// returns rlp iterator - pub fn iter(&'a self) -> UntrustedRlpIterator<'a> { - self.rlp.into_iter() + /// Get iterator over rlp-slices + /// + /// ```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 rlp = Rlp::new(&data); + /// let strings: Vec = rlp.iter().map(| i | String::decode(&i)).collect(); + /// } + /// ``` + pub fn iter(&'a self) -> RlpIterator<'a> { + self.into_iter() } } @@ -209,9 +212,22 @@ impl<'a> UntrustedRlp<'a> { } } - /// get container subset at given index - /// - /// paren container caches searched position + /// Get view onto rlp-slice at index + /// + /// Caches offset to given index, so access to successive + /// slices is faster. + /// + /// ```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 rlp = UntrustedRlp::new(&data); + /// let dog = String::decode_untrusted(&rlp.at(1).unwrap()).unwrap(); + /// assert_eq!(dog, "dog".to_string()); + /// } + /// ``` pub fn at(&self, index: usize) -> Result, DecoderError> { if !self.is_list() { return Err(DecoderError::UntrustedRlpExpectedToBeList); @@ -236,17 +252,53 @@ impl<'a> UntrustedRlp<'a> { Ok(UntrustedRlp::new(&bytes[0..found.prefix_len + found.value_len])) } - /// returns true if rlp is a list + /// List value + /// + /// ```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 rlp = UntrustedRlp::new(&data); + /// assert!(rlp.is_list()); + /// } + /// ``` pub fn is_list(&self) -> bool { self.bytes.len() > 0 && self.bytes[0] >= 0xc0 } - /// returns true if rlp is a value + /// String value + /// + /// ```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 rlp = UntrustedRlp::new(&data); + /// assert!(rlp.at(1).unwrap().is_data()); + /// } + /// ``` pub fn is_data(&self) -> bool { self.bytes.len() > 0 && self.bytes[0] <= 0xbf } - /// returns rlp iterator + /// Get iterator over rlp-slices + /// + /// ```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 rlp = UntrustedRlp::new(&data); + /// let strings: Vec = rlp.iter() + /// .map(| i | String::decode_untrusted(&i)) + /// .map(| s | s.unwrap()) + /// .collect(); + /// } + /// ``` pub fn iter(&'a self) -> UntrustedRlpIterator<'a> { self.into_iter() } @@ -307,7 +359,7 @@ impl<'a> UntrustedRlp<'a> { } } -/// non-consuming rlp iterator +/// Iterator over rlp-slice list elements. pub struct UntrustedRlpIterator<'a> { rlp: &'a UntrustedRlp<'a>, index: usize, @@ -336,18 +388,53 @@ impl<'a> Iterator for UntrustedRlpIterator<'a> { } } -/// shortcut function to decoded Trusted Rlp `&[u8]` into an object + +/// Iterator over trusted rlp-slice list elements. +pub struct RlpIterator<'a> { + rlp: &'a Rlp<'a>, + index: usize +} + +impl<'a> IntoIterator for &'a Rlp<'a> { + type Item = Rlp<'a>; + type IntoIter = RlpIterator<'a>; + + fn into_iter(self) -> Self::IntoIter { + RlpIterator { + rlp: self, + index: 0, + } + } +} + +impl<'a> Iterator for RlpIterator<'a> { + type Item = Rlp<'a>; + + fn next(&mut self) -> Option> { + let index = self.index; + let result = self.rlp.rlp.at(index).ok().map(| iter | { From::from(iter) }); + self.index += 1; + result + } +} + +/// 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); T::decode(&rlp) } -/// shortcut function to decode UntrustedRlp `&[u8]` into an object -pub fn decode_untrusted(bytes: &[u8]) -> Result where T: Decodable { - let rlp = UntrustedRlp::new(bytes); - T::decode_untrusted(&rlp) -} - pub trait Decodable: Sized { fn decode_untrusted(rlp: &UntrustedRlp) -> Result; fn decode(rlp: &Rlp) -> Self { @@ -432,15 +519,14 @@ impl ListInfo { } } -/// container that should be used to encode rlp +/// Appendable rlp encoder. pub struct RlpStream { unfinished_lists: LinkedList, encoder: BasicEncoder, } impl RlpStream { - /// create new container for values appended one after another, - /// but not being part of the same list + /// Initializes instance of empty `RlpStream`. pub fn new() -> RlpStream { RlpStream { unfinished_lists: LinkedList::new(), @@ -448,7 +534,7 @@ impl RlpStream { } } - /// create new container for list of size `max_len` + /// Initializes the `RLPStream` as a list. pub fn new_list(len: usize) -> RlpStream { let mut stream = RlpStream::new(); stream.append_list(len); @@ -456,6 +542,18 @@ impl RlpStream { } /// 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().unwrap(); + /// assert_eq!(out, vec![0xc8, 0x83, b'c', b'a', b't', 0x83, b'd', b'o', b'g']); + /// } + /// ``` pub fn append<'a, E>(&'a mut self, object: &E) -> &'a mut RlpStream where E: Encodable + fmt::Debug { //println!("append: {:?}", object); // encode given value and add it at the end of the stream @@ -469,6 +567,19 @@ impl RlpStream { } /// 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().unwrap(); + /// assert_eq!(out, vec![0xca, 0xc8, 0x83, b'c', b'a', b't', 0x83, b'd', b'o', b'g', 0x80]); + /// } + /// ``` pub fn append_list<'a>(&'a mut self, len: usize) -> &'a mut RlpStream { //println!("append_list: {}", len); // push new list @@ -499,12 +610,28 @@ impl RlpStream { self } - /// return true if stream is ready + /// 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().unwrap(); + /// assert_eq!(out, vec![0xc8, 0x83, b'c', b'a', b't', 0x83, b'd', b'o', b'g']); + /// } pub fn is_finished(&self) -> bool { self.unfinished_lists.back().is_none() } - /// streams out encoded bytes + /// Streams out encoded bytes. + /// + /// Returns an error if stream is not finished. pub fn out(self) -> Result, EncoderError> { match self.is_finished() { true => Ok(self.encoder.out()), @@ -534,7 +661,18 @@ impl RlpStream { } } -/// shortcut function to encode a `T: Encodable` into a UntrustedRlp `Vec` +/// 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 encoder = BasicEncoder::new(); @@ -922,7 +1060,7 @@ mod tests { fn run_decode_tests(tests: Vec>) where T: rlp::Decodable + fmt::Debug + cmp::Eq { for t in &tests { - let res: T = rlp::decode_untrusted(&t.1).unwrap(); + let res: T = rlp::decode(&t.1); assert_eq!(res, t.0); } }