rlp deserialization refactor, 30% faster (#4901)

* fixed naming of rlp modules

* RlpStream cleanup

* appending short rlp lists (0...55 bytes) is 25% faster

* RlpStream does not use bytes module, nor trait Stream

* removed unused code from rlp module

* compiling ethcore-util with new rlp serialization

* compiling parity with new rlp serialization

* fixed compiling ethcore-light with new rlp serialization

* fixed compiling ethsync with new rlp serialization

* moved rlp benches and rlp tests

* rlp deserialization refactor, 30% faster

* removed redundant comment, print

* fixed compiling parity with new rlp deserialization

* removed redundant double-space

* fixed failing test

* updated rlp docs, removed unused traits

* fixed rlp benchmarks

* replace usage of WriteBytesExt with ByteOrder

* removed unused, commented out code

* fixed merge conflict
This commit is contained in:
Marek Kotewicz 2017-03-22 14:41:46 +01:00 committed by GitHub
parent ddb4e348e4
commit e79518362e
10 changed files with 515 additions and 585 deletions

111
benches/rlp.rs Normal file
View File

@ -0,0 +1,111 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
//! benchmarking for rlp
//! should be started with:
//! ```bash
//! multirust run nightly cargo bench
//! ```
#![feature(test)]
extern crate test;
extern crate ethcore_bigint as bigint;
extern crate rlp;
use test::Bencher;
use bigint::prelude::U256;
use rlp::{RlpStream, Rlp};
#[bench]
fn bench_stream_u64_value(b: &mut Bencher) {
b.iter(|| {
// u64
let mut stream = RlpStream::new();
stream.append(&0x1023456789abcdefu64);
let _ = stream.out();
});
}
#[bench]
fn bench_decode_u64_value(b: &mut Bencher) {
b.iter(|| {
// u64
let data = vec![0x88, 0x10, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef];
let rlp = Rlp::new(&data);
let _: u64 = rlp.as_val();
});
}
#[bench]
fn bench_stream_u256_value(b: &mut Bencher) {
b.iter(|| {
// u256
let mut stream = RlpStream::new();
let uint: U256 = "8090a0b0c0d0e0f00910203040506077000000000000000100000000000012f0".into();
stream.append(&uint);
let _ = stream.out();
});
}
#[bench]
fn bench_decode_u256_value(b: &mut Bencher) {
b.iter(|| {
// u256
let data = vec![0xa0, 0x80, 0x90, 0xa0, 0xb0, 0xc0, 0xd0, 0xe0, 0xf0, 0x09, 0x10, 0x20,
0x30, 0x40, 0x50, 0x60, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0xf0];
let rlp = Rlp::new(&data);
let _ : U256 = rlp.as_val();
});
}
#[bench]
fn bench_stream_nested_empty_lists(b: &mut Bencher) {
b.iter(|| {
// [ [], [[]], [ [], [[]] ] ]
let mut stream = RlpStream::new_list(3);
stream.begin_list(0);
stream.begin_list(1).begin_list(0);
stream.begin_list(2).begin_list(0).begin_list(1).begin_list(0);
let _ = stream.out();
});
}
#[bench]
fn bench_decode_nested_empty_lists(b: &mut Bencher) {
b.iter(|| {
// [ [], [[]], [ [], [[]] ] ]
let data = vec![0xc7, 0xc0, 0xc1, 0xc0, 0xc3, 0xc0, 0xc1, 0xc0];
let rlp = Rlp::new(&data);
let _v0: Vec<u16> = rlp.at(0).as_list();
let _v1: Vec<u16> = rlp.at(1).at(0).as_list();
let nested_rlp = rlp.at(2);
let _v2a: Vec<u16> = nested_rlp.at(0).as_list();
let _v2b: Vec<u16> = nested_rlp.at(1).at(0).as_list();
});
}
#[bench]
fn bench_stream_1000_empty_lists(b: &mut Bencher) {
b.iter(|| {
let mut stream = RlpStream::new_list(1000);
for _ in 0..1000 {
stream.begin_list(0);
}
let _ = stream.out();
});
}

View File

@ -1,148 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
//! Unified interfaces for RLP bytes operations on basic types
//!
use std::{mem, fmt, cmp};
use std::error::Error as StdError;
use bigint::prelude::{U128, U256, H64, H128, H160, H256, H512, H520, H2048};
/// Error returned when `FromBytes` conversation goes wrong
#[derive(Debug, PartialEq, Eq)]
pub enum FromBytesError {
/// Expected more RLP data
DataIsTooShort,
/// Extra bytes after the end of the last item
DataIsTooLong,
/// Integer-representation is non-canonically prefixed with zero byte(s).
ZeroPrefixedInt,
/// String representation is not utf-8
InvalidUtf8,
}
impl StdError for FromBytesError {
fn description(&self) -> &str { "from_bytes error" }
}
impl fmt::Display for FromBytesError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Debug::fmt(&self, f)
}
}
/// Alias for the result of `FromBytes` trait
pub type FromBytesResult<T> = Result<T, FromBytesError>;
/// Converts to given type from its bytes representation
///
/// TODO: check size of bytes before conversation and return appropriate error
pub trait FromBytes: Sized {
/// Create a value from bytes
fn from_bytes(bytes: &[u8]) -> FromBytesResult<Self>;
}
impl FromBytes for String {
fn from_bytes(bytes: &[u8]) -> FromBytesResult<String> {
::std::str::from_utf8(bytes).map(|s| s.to_owned()).map_err(|_| FromBytesError::InvalidUtf8)
}
}
macro_rules! impl_uint_from_bytes {
($to: ident) => {
impl FromBytes for $to {
fn from_bytes(bytes: &[u8]) -> FromBytesResult<$to> {
match bytes.len() {
0 => Ok(0),
l if l <= mem::size_of::<$to>() => {
if bytes[0] == 0 {
return Err(FromBytesError::ZeroPrefixedInt)
}
let mut res = 0 as $to;
for i in 0..l {
let shift = (l - 1 - i) * 8;
res = res + ((bytes[i] as $to) << shift);
}
Ok(res)
}
_ => Err(FromBytesError::DataIsTooLong)
}
}
}
}
}
impl FromBytes for bool {
fn from_bytes(bytes: &[u8]) -> FromBytesResult<bool> {
match bytes.len() {
0 => Ok(false),
1 => Ok(bytes[0] != 0),
_ => Err(FromBytesError::DataIsTooLong),
}
}
}
//impl_uint_from_bytes!(u8);
impl_uint_from_bytes!(u16);
impl_uint_from_bytes!(u32);
impl_uint_from_bytes!(u64);
impl_uint_from_bytes!(usize);
macro_rules! impl_uint_from_bytes {
($name: ident, $size: expr) => {
impl FromBytes for $name {
fn from_bytes(bytes: &[u8]) -> FromBytesResult<$name> {
if !bytes.is_empty() && bytes[0] == 0 {
Err(FromBytesError::ZeroPrefixedInt)
} else if bytes.len() <= $size {
Ok($name::from(bytes))
} else {
Err(FromBytesError::DataIsTooLong)
}
}
}
}
}
impl_uint_from_bytes!(U256, 32);
impl_uint_from_bytes!(U128, 16);
macro_rules! impl_hash_from_bytes {
($name: ident, $size: expr) => {
impl FromBytes for $name {
fn from_bytes(bytes: &[u8]) -> FromBytesResult<$name> {
match bytes.len().cmp(&$size) {
cmp::Ordering::Less => Err(FromBytesError::DataIsTooShort),
cmp::Ordering::Greater => Err(FromBytesError::DataIsTooLong),
cmp::Ordering::Equal => {
let mut t = [0u8; $size];
t.copy_from_slice(bytes);
Ok($name(t))
}
}
}
}
}
}
impl_hash_from_bytes!(H64, 8);
impl_hash_from_bytes!(H128, 16);
impl_hash_from_bytes!(H160, 20);
impl_hash_from_bytes!(H256, 32);
impl_hash_from_bytes!(H512, 64);
impl_hash_from_bytes!(H520, 65);
impl_hash_from_bytes!(H2048, 256);

View File

@ -17,7 +17,7 @@
use std::collections::HashMap;
use elastic_array::ElasticArray1024;
use common::{BLOCKS_RLP_SWAPPER, SNAPSHOT_RLP_SWAPPER};
use {UntrustedRlp, View, Compressible, encode, RlpStream};
use {UntrustedRlp, Compressible, encode, RlpStream};
/// Stores RLPs used for compression
pub struct InvalidRlpSwapper<'a> {
@ -69,7 +69,7 @@ fn to_elastic(slice: &[u8]) -> ElasticArray1024<u8> {
fn map_rlp<F>(rlp: &UntrustedRlp, f: F) -> Option<ElasticArray1024<u8>> where
F: Fn(&UntrustedRlp) -> Option<ElasticArray1024<u8>> {
match rlp.iter()
.fold((false, RlpStream::new_list(rlp.item_count())),
.fold((false, RlpStream::new_list(rlp.item_count().unwrap_or(0))),
|(is_some, mut acc), subrlp| {
let new = f(&subrlp);
if let Some(ref insert) = new {
@ -138,7 +138,7 @@ fn deep_decompress(rlp: &UntrustedRlp, swapper: &InvalidRlpSwapper) -> Option<El
swapper.get_valid(rlp.as_raw()).map(to_elastic);
// Simply decompress data.
if rlp.is_data() { return simple_swap(); }
match rlp.item_count() {
match rlp.item_count().unwrap_or(0) {
// Look for special compressed list, which contains nested data.
2 if rlp.at(0).map(|r| r.as_raw() == &[0x81, 0x7f]).unwrap_or(false) =>
rlp.at(1).ok().map_or(simple_swap(),
@ -169,7 +169,7 @@ impl<'a> Compressible for UntrustedRlp<'a> {
#[cfg(test)]
mod tests {
use compression::InvalidRlpSwapper;
use {UntrustedRlp, Compressible, View, RlpType};
use {UntrustedRlp, Compressible, RlpType};
#[test]
fn invalid_rlp_swapper() {

View File

@ -16,13 +16,10 @@
use std::fmt;
use std::error::Error as StdError;
use bytes::FromBytesError;
#[derive(Debug, PartialEq, Eq)]
/// Error concerning the RLP decoder.
pub enum DecoderError {
/// Couldn't convert given bytes to an instance of required type.
FromBytesError(FromBytesError),
/// Data has additional bytes at the end of the valid RLP fragment.
RlpIsTooBig,
/// Data has too few bytes for valid RLP.
@ -56,9 +53,3 @@ impl fmt::Display for DecoderError {
fmt::Debug::fmt(&self, f)
}
}
impl From<FromBytesError> for DecoderError {
fn from(err: FromBytesError) -> DecoderError {
DecoderError::FromBytesError(err)
}
}

View File

@ -1,7 +1,26 @@
use std::{cmp, mem, str};
use byteorder::{ByteOrder, BigEndian};
use bigint::prelude::{Uint, U128, U256, H64, H128, H160, H256, H512, H520, H2048};
use traits::Encodable;
use traits::{Encodable, Decodable};
use stream::RlpStream;
use {UntrustedRlp, DecoderError};
pub fn decode_usize(bytes: &[u8]) -> Result<usize, DecoderError> {
match bytes.len() {
l if l <= mem::size_of::<usize>() => {
if bytes[0] == 0 {
return Err(DecoderError::RlpInvalidIndirection);
}
let mut res = 0usize;
for i in 0..l {
let shift = (l - 1 - i) * 8;
res = res + ((bytes[i] as usize) << shift);
}
Ok(res)
}
_ => Err(DecoderError::RlpIsTooBig),
}
}
impl Encodable for bool {
fn rlp_append(&self, s: &mut RlpStream) {
@ -13,6 +32,18 @@ impl Encodable for bool {
}
}
impl Decodable for bool {
fn decode(rlp: &UntrustedRlp) -> Result<Self, DecoderError> {
rlp.decoder().decode_value(|bytes| {
match bytes.len() {
0 => Ok(false),
1 => Ok(bytes[0] != 0),
_ => Err(DecoderError::RlpIsTooBig),
}
})
}
}
impl<'a> Encodable for &'a [u8] {
fn rlp_append(&self, s: &mut RlpStream) {
s.encoder().encode_value(self);
@ -25,6 +56,14 @@ impl Encodable for Vec<u8> {
}
}
impl Decodable for Vec<u8> {
fn decode(rlp: &UntrustedRlp) -> Result<Self, DecoderError> {
rlp.decoder().decode_value(|bytes| {
Ok(bytes.to_vec())
})
}
}
impl<T> Encodable for Option<T> where T: Encodable {
fn rlp_append(&self, s: &mut RlpStream) {
match *self {
@ -39,6 +78,17 @@ impl<T> Encodable for Option<T> where T: Encodable {
}
}
impl<T> Decodable for Option<T> where T: Decodable {
fn decode(rlp: &UntrustedRlp) -> Result<Self, DecoderError> {
let items = rlp.item_count()?;
match items {
1 => rlp.val_at(0).map(Some),
0 => Ok(None),
_ => Err(DecoderError::RlpIncorrectListLen),
}
}
}
impl Encodable for u8 {
fn rlp_append(&self, s: &mut RlpStream) {
if *self != 0 {
@ -49,6 +99,19 @@ impl Encodable for u8 {
}
}
impl Decodable for u8 {
fn decode(rlp: &UntrustedRlp) -> Result<Self, DecoderError> {
rlp.decoder().decode_value(|bytes| {
match bytes.len() {
1 if bytes[0] != 0 => Ok(bytes[0]),
0 => Ok(0),
1 => Err(DecoderError::RlpInvalidIndirection),
_ => Err(DecoderError::RlpIsTooBig),
}
})
}
}
macro_rules! impl_encodable_for_u {
($name: ident, $func: ident, $size: expr) => {
impl Encodable for $name {
@ -62,16 +125,52 @@ macro_rules! impl_encodable_for_u {
}
}
macro_rules! impl_decodable_for_u {
($name: ident) => {
impl Decodable for $name {
fn decode(rlp: &UntrustedRlp) -> Result<Self, DecoderError> {
rlp.decoder().decode_value(|bytes| {
match bytes.len() {
0 | 1 => u8::decode(rlp).map(|v| v as $name),
l if l <= mem::size_of::<$name>() => {
if bytes[0] == 0 {
return Err(DecoderError::RlpInvalidIndirection);
}
let mut res = 0 as $name;
for i in 0..l {
let shift = (l - 1 - i) * 8;
res = res + ((bytes[i] as $name) << shift);
}
Ok(res)
}
_ => Err(DecoderError::RlpIsTooBig),
}
})
}
}
}
}
impl_encodable_for_u!(u16, write_u16, 2);
impl_encodable_for_u!(u32, write_u32, 4);
impl_encodable_for_u!(u64, write_u64, 8);
impl_decodable_for_u!(u16);
impl_decodable_for_u!(u32);
impl_decodable_for_u!(u64);
impl Encodable for usize {
fn rlp_append(&self, s: &mut RlpStream) {
(*self as u64).rlp_append(s);
}
}
impl Decodable for usize {
fn decode(rlp: &UntrustedRlp) -> Result<Self, DecoderError> {
u64::decode(rlp).map(|value| value as usize)
}
}
macro_rules! impl_encodable_for_hash {
($name: ident) => {
impl Encodable for $name {
@ -82,6 +181,24 @@ macro_rules! impl_encodable_for_hash {
}
}
macro_rules! impl_decodable_for_hash {
($name: ident, $size: expr) => {
impl Decodable for $name {
fn decode(rlp: &UntrustedRlp) -> Result<Self, DecoderError> {
rlp.decoder().decode_value(|bytes| match bytes.len().cmp(&$size) {
cmp::Ordering::Less => Err(DecoderError::RlpIsTooShort),
cmp::Ordering::Greater => Err(DecoderError::RlpIsTooBig),
cmp::Ordering::Equal => {
let mut t = [0u8; $size];
t.copy_from_slice(bytes);
Ok($name(t))
}
})
}
}
}
}
impl_encodable_for_hash!(H64);
impl_encodable_for_hash!(H128);
impl_encodable_for_hash!(H160);
@ -90,6 +207,14 @@ impl_encodable_for_hash!(H512);
impl_encodable_for_hash!(H520);
impl_encodable_for_hash!(H2048);
impl_decodable_for_hash!(H64, 8);
impl_decodable_for_hash!(H128, 16);
impl_decodable_for_hash!(H160, 20);
impl_decodable_for_hash!(H256, 32);
impl_decodable_for_hash!(H512, 64);
impl_decodable_for_hash!(H520, 65);
impl_decodable_for_hash!(H2048, 256);
macro_rules! impl_encodable_for_uint {
($name: ident, $size: expr) => {
impl Encodable for $name {
@ -103,9 +228,30 @@ macro_rules! impl_encodable_for_uint {
}
}
macro_rules! impl_decodable_for_uint {
($name: ident, $size: expr) => {
impl Decodable for $name {
fn decode(rlp: &UntrustedRlp) -> Result<Self, DecoderError> {
rlp.decoder().decode_value(|bytes| {
if !bytes.is_empty() && bytes[0] == 0 {
Err(DecoderError::RlpInvalidIndirection)
} else if bytes.len() <= $size {
Ok($name::from(bytes))
} else {
Err(DecoderError::RlpIsTooBig)
}
})
}
}
}
}
impl_encodable_for_uint!(U256, 32);
impl_encodable_for_uint!(U128, 16);
impl_decodable_for_uint!(U256, 32);
impl_decodable_for_uint!(U128, 16);
impl<'a> Encodable for &'a str {
fn rlp_append(&self, s: &mut RlpStream) {
s.encoder().encode_value(self.as_bytes());
@ -118,3 +264,14 @@ impl Encodable for String {
}
}
impl Decodable for String {
fn decode(rlp: &UntrustedRlp) -> Result<Self, DecoderError> {
rlp.decoder().decode_value(|bytes| {
match str::from_utf8(bytes) {
Ok(s) => Ok(s.to_owned()),
// consider better error type here
Err(_err) => Err(DecoderError::RlpExpectedToBeData),
}
})
}
}

View File

@ -61,17 +61,13 @@ mod untrusted_rlp;
mod stream;
mod compression;
mod common;
mod bytes;
mod impls;
#[cfg(test)]
mod tests;
use std::borrow::Borrow;
use elastic_array::ElasticArray1024;
pub use error::DecoderError;
pub use traits::{Decoder, Decodable, View, Encodable, RlpDecodable, Compressible};
pub use traits::{Decodable, Encodable, Compressible};
pub use untrusted_rlp::{UntrustedRlp, UntrustedRlpIterator, PayloadInfo, Prototype};
pub use rlpin::{Rlp, RlpIterator};
pub use stream::RlpStream;
@ -88,16 +84,21 @@ pub const EMPTY_LIST_RLP: [u8; 1] = [0xC0; 1];
/// extern crate rlp;
///
/// fn main () {
/// let data = vec![0xc8, 0x83, b'c', b'a', b't', 0x83, b'd', b'o', b'g'];
/// let animals: Vec<String> = rlp::decode(&data);
/// assert_eq!(animals, vec!["cat".to_string(), "dog".to_string()]);
/// let data = vec![0x83, b'c', b'a', b't'];
/// let animal: String = rlp::decode(&data);
/// assert_eq!(animal, "cat".to_owned());
/// }
/// ```
pub fn decode<T>(bytes: &[u8]) -> T where T: RlpDecodable {
pub fn decode<T>(bytes: &[u8]) -> T where T: Decodable {
let rlp = Rlp::new(bytes);
rlp.as_val()
}
pub fn decode_list<T>(bytes: &[u8]) -> Vec<T> where T: Decodable {
let rlp = Rlp::new(bytes);
rlp.as_list()
}
/// Shortcut function to encode structure into rlp.
///
/// ```rust

View File

@ -15,8 +15,7 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
use std::fmt;
use rustc_serialize::hex::ToHex;
use {View, DecoderError, UntrustedRlp, PayloadInfo, Prototype, RlpDecodable};
use {UntrustedRlp, PayloadInfo, Prototype, Decodable};
impl<'a> From<UntrustedRlp<'a>> for Rlp<'a> {
fn from(rlp: UntrustedRlp<'a>) -> Rlp<'a> {
@ -39,95 +38,215 @@ impl<'a> fmt::Display for Rlp<'a> {
}
}
impl<'a, 'view> View<'a, 'view> for Rlp<'a> where 'a: 'view {
type Prototype = Prototype;
type PayloadInfo = PayloadInfo;
type Data = &'a [u8];
type Item = Rlp<'a>;
type Iter = RlpIterator<'a, 'view>;
impl<'a, 'view> Rlp<'a> where 'a: 'view {
/// Create a new instance of `Rlp`
fn new(bytes: &'a [u8]) -> Rlp<'a> {
pub fn new(bytes: &'a [u8]) -> Rlp<'a> {
Rlp {
rlp: UntrustedRlp::new(bytes)
}
}
fn as_raw(&'view self) -> &'a [u8] {
/// The raw data of the RLP as slice.
///
/// ```rust
/// extern crate rlp;
/// use 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 = rlp.at(1).as_raw();
/// assert_eq!(dog, &[0x83, b'd', b'o', b'g']);
/// }
/// ```
pub fn as_raw(&'view self) -> &'a [u8] {
self.rlp.as_raw()
}
fn prototype(&self) -> Self::Prototype {
/// Get the prototype of the RLP.
pub fn prototype(&self) -> Prototype {
self.rlp.prototype().unwrap()
}
fn payload_info(&self) -> Self::PayloadInfo {
/// Get payload info.
pub fn payload_info(&self) -> PayloadInfo {
self.rlp.payload_info().unwrap()
}
fn data(&'view self) -> Self::Data {
/// Get underlieing data.
pub fn data(&'view self) -> &'a [u8] {
self.rlp.data().unwrap()
}
fn item_count(&self) -> usize {
self.rlp.item_count()
/// Returns number of RLP items.
///
/// ```rust
/// extern crate rlp;
/// use 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_eq!(rlp.item_count(), 2);
/// let view = rlp.at(1);
/// assert_eq!(view.item_count(), 0);
/// }
/// ```
pub fn item_count(&self) -> usize {
self.rlp.item_count().unwrap_or(0)
}
fn size(&self) -> usize {
/// Returns the number of bytes in the data, or zero if it isn't data.
///
/// ```rust
/// extern crate rlp;
/// use 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_eq!(rlp.size(), 0);
/// let view = rlp.at(1);
/// assert_eq!(view.size(), 3);
/// }
/// ```
pub fn size(&self) -> usize {
self.rlp.size()
}
fn at(&'view self, index: usize) -> Self::Item {
/// Get view onto RLP-slice at index.
///
/// Caches offset to given index, so access to successive
/// slices is faster.
///
/// ```rust
/// extern crate rlp;
/// use 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 = rlp.at(1).as_val();
/// assert_eq!(dog, "dog".to_string());
/// }
/// ```
pub fn at(&'view self, index: usize) -> Rlp<'a> {
From::from(self.rlp.at(index).unwrap())
}
fn is_null(&self) -> bool {
/// No value
///
/// ```rust
/// extern crate rlp;
/// use rlp::*;
///
/// fn main () {
/// let data = vec![];
/// let rlp = Rlp::new(&data);
/// assert!(rlp.is_null());
/// }
/// ```
pub fn is_null(&self) -> bool {
self.rlp.is_null()
}
fn is_empty(&self) -> bool {
/// Contains a zero-length string or zero-length list.
///
/// ```rust
/// extern crate rlp;
/// use rlp::*;
///
/// fn main () {
/// let data = vec![0xc0];
/// let rlp = Rlp::new(&data);
/// assert!(rlp.is_empty());
/// }
/// ```
pub fn is_empty(&self) -> bool {
self.rlp.is_empty()
}
fn is_list(&self) -> bool {
/// List value
///
/// ```rust
/// extern crate rlp;
/// use 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()
}
fn is_data(&self) -> bool {
/// String value
///
/// ```rust
/// extern crate rlp;
/// use 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()
}
fn is_int(&self) -> bool {
/// Int value
///
/// ```rust
/// extern crate rlp;
/// use rlp::*;
///
/// fn main () {
/// let data = vec![0xc1, 0x10];
/// let rlp = Rlp::new(&data);
/// assert_eq!(rlp.is_int(), false);
/// assert_eq!(rlp.at(0).is_int(), true);
/// }
/// ```
pub fn is_int(&self) -> bool {
self.rlp.is_int()
}
fn iter(&'view self) -> Self::Iter {
/// Get iterator over rlp-slices
///
/// ```rust
/// extern crate rlp;
/// use 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<String> = rlp.iter().map(| i | i.as_val()).collect();
/// }
/// ```
pub fn iter(&'view self) -> RlpIterator<'a, 'view> {
self.into_iter()
}
fn as_val<T>(&self) -> Result<T, DecoderError> where T: RlpDecodable {
self.rlp.as_val()
/// Decode data into an object
pub fn as_val<T>(&self) -> T where T: Decodable {
self.rlp.as_val().expect("Unexpected rlp error")
}
fn val_at<T>(&self, index: usize) -> Result<T, DecoderError> where T: RlpDecodable {
self.at(index).rlp.as_val()
}
}
impl <'a, 'view> Rlp<'a> where 'a: 'view {
fn view_as_val<T, R>(r: &'view R) -> T where R: View<'a, 'view>, T: RlpDecodable {
let res: Result<T, DecoderError> = r.as_val();
res.unwrap_or_else(|e| panic!("DecodeError: {}, {}", e, r.as_raw().to_hex()))
pub fn as_list<T>(&self) -> Vec<T> where T: Decodable {
self.iter().map(|rlp| rlp.as_val()).collect()
}
/// Decode into an object
pub fn as_val<T>(&self) -> T where T: RlpDecodable {
Self::view_as_val(self)
/// Decode data at given list index into an object
pub fn val_at<T>(&self, index: usize) -> T where T: Decodable {
self.at(index).as_val()
}
/// Decode list item at given index into an object
pub fn val_at<T>(&self, index: usize) -> T where T: RlpDecodable {
Self::view_as_val(&self.at(index))
pub fn list_at<T>(&self, index: usize) -> Vec<T> where T: Decodable {
self.at(index).as_list()
}
}

View File

@ -16,212 +16,12 @@
//! Common RLP traits
use elastic_array::ElasticArray1024;
use stream::RlpStream;
use {DecoderError, UntrustedRlp};
/// Type is able to decode RLP.
pub trait Decoder: Sized {
/// Read a value from the RLP into a given type.
fn read_value<T, F>(&self, f: &F) -> Result<T, DecoderError>
where F: Fn(&[u8]) -> Result<T, DecoderError>;
/// Get underlying `UntrustedRLP` object.
fn as_rlp(&self) -> &UntrustedRlp;
/// Get underlying raw bytes slice.
fn as_raw(&self) -> &[u8];
}
use {DecoderError, UntrustedRlp, RlpStream};
/// RLP decodable trait
pub trait Decodable: Sized {
/// Decode a value from RLP bytes
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder;
}
/// Internal helper trait. Implement `Decodable` for custom types.
pub trait RlpDecodable: Sized {
/// Decode a value from RLP bytes
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder;
}
/// A view into RLP encoded data
pub trait View<'a, 'view>: Sized {
/// RLP prototype type
type Prototype;
/// Payload info type
type PayloadInfo;
/// Data type
type Data;
/// Item type
type Item;
/// Iterator type
type Iter;
/// Creates a new instance of `Rlp` reader
fn new(bytes: &'a [u8]) -> Self;
/// The raw data of the RLP as slice.
///
/// ```rust
/// extern crate rlp;
/// use 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 = rlp.at(1).as_raw();
/// assert_eq!(dog, &[0x83, b'd', b'o', b'g']);
/// }
/// ```
fn as_raw(&'view self) -> &'a [u8];
/// Get the prototype of the RLP.
fn prototype(&self) -> Self::Prototype;
/// Get payload info.
fn payload_info(&self) -> Self::PayloadInfo;
/// Get underlieing data.
fn data(&'view self) -> Self::Data;
/// Returns number of RLP items.
///
/// ```rust
/// extern crate rlp;
/// use 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_eq!(rlp.item_count(), 2);
/// let view = rlp.at(1);
/// assert_eq!(view.item_count(), 0);
/// }
/// ```
fn item_count(&self) -> usize;
/// Returns the number of bytes in the data, or zero if it isn't data.
///
/// ```rust
/// extern crate rlp;
/// use 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_eq!(rlp.size(), 0);
/// let view = rlp.at(1);
/// assert_eq!(view.size(), 3);
/// }
/// ```
fn size(&self) -> usize;
/// Get view onto RLP-slice at index.
///
/// Caches offset to given index, so access to successive
/// slices is faster.
///
/// ```rust
/// extern crate rlp;
/// use 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 = rlp.at(1).as_val();
/// assert_eq!(dog, "dog".to_string());
/// }
fn at(&'view self, index: usize) -> Self::Item;
/// No value
///
/// ```rust
/// extern crate rlp;
/// use rlp::*;
///
/// fn main () {
/// let data = vec![];
/// let rlp = Rlp::new(&data);
/// assert!(rlp.is_null());
/// }
/// ```
fn is_null(&self) -> bool;
/// Contains a zero-length string or zero-length list.
///
/// ```rust
/// extern crate rlp;
/// use rlp::*;
///
/// fn main () {
/// let data = vec![0xc0];
/// let rlp = Rlp::new(&data);
/// assert!(rlp.is_empty());
/// }
/// ```
fn is_empty(&self) -> bool;
/// List value
///
/// ```rust
/// extern crate rlp;
/// use 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());
/// }
/// ```
fn is_list(&self) -> bool;
/// String value
///
/// ```rust
/// extern crate rlp;
/// use 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());
/// }
/// ```
fn is_data(&self) -> bool;
/// Int value
///
/// ```rust
/// extern crate rlp;
/// use rlp::*;
///
/// fn main () {
/// let data = vec![0xc1, 0x10];
/// let rlp = Rlp::new(&data);
/// assert_eq!(rlp.is_int(), false);
/// assert_eq!(rlp.at(0).is_int(), true);
/// }
/// ```
fn is_int(&self) -> bool;
/// Get iterator over rlp-slices
///
/// ```rust
/// extern crate rlp;
/// use 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<String> = rlp.iter().map(| i | i.as_val()).collect();
/// }
/// ```
fn iter(&'view self) -> Self::Iter;
/// Decode data into an object
fn as_val<T>(&self) -> Result<T, DecoderError> where T: RlpDecodable;
/// Decode data at given list index into an object
fn val_at<T>(&self, index: usize) -> Result<T, DecoderError> where T: RlpDecodable;
fn decode(rlp: &UntrustedRlp) -> Result<Self, DecoderError>;
}
/// Structure encodable to RLP

View File

@ -17,9 +17,8 @@
use std::cell::Cell;
use std::fmt;
use rustc_serialize::hex::ToHex;
use bytes::{FromBytes, FromBytesResult, FromBytesError};
use ::{View, Decoder, Decodable, DecoderError, RlpDecodable};
use impls::decode_usize;
use {Decodable, DecoderError};
/// rlp offset
#[derive(Copy, Clone, Debug)]
@ -64,7 +63,7 @@ fn calculate_payload_info(header_bytes: &[u8], len_of_len: usize) -> Result<Payl
_ => (),
}
if header_bytes.len() < header_len { return Err(DecoderError::RlpIsTooShort); }
let value_len = usize::from_bytes(&header_bytes[1..header_len])?;
let value_len = decode_usize(&header_bytes[1..header_len])?;
Ok(PayloadInfo::new(header_len, value_len))
}
@ -141,15 +140,8 @@ impl<'a> fmt::Display for UntrustedRlp<'a> {
}
}
impl<'a, 'view> View<'a, 'view> for UntrustedRlp<'a> where 'a: 'view {
type Prototype = Result<Prototype, DecoderError>;
type PayloadInfo = Result<PayloadInfo, DecoderError>;
type Data = Result<&'a [u8], DecoderError>;
type Item = Result<UntrustedRlp<'a>, DecoderError>;
type Iter = UntrustedRlpIterator<'a, 'view>;
//returns new instance of `UntrustedRlp`
fn new(bytes: &'a [u8]) -> UntrustedRlp<'a> {
impl<'a, 'view> UntrustedRlp<'a> where 'a: 'view {
pub fn new(bytes: &'a [u8]) -> UntrustedRlp<'a> {
UntrustedRlp {
bytes: bytes,
offset_cache: Cell::new(OffsetCache::new(usize::max_value(), 0)),
@ -157,45 +149,45 @@ impl<'a, 'view> View<'a, 'view> for UntrustedRlp<'a> where 'a: 'view {
}
}
fn as_raw(&'view self) -> &'a [u8] {
pub fn as_raw(&'view self) -> &'a [u8] {
self.bytes
}
fn prototype(&self) -> Self::Prototype {
pub fn prototype(&self) -> Result<Prototype, DecoderError> {
// optimize? && return appropriate errors
if self.is_data() {
Ok(Prototype::Data(self.size()))
} else if self.is_list() {
Ok(Prototype::List(self.item_count()))
self.item_count().map(Prototype::List)
} else {
Ok(Prototype::Null)
}
}
fn payload_info(&self) -> Self::PayloadInfo {
pub fn payload_info(&self) -> Result<PayloadInfo, DecoderError> {
BasicDecoder::payload_info(self.bytes)
}
fn data(&'view self) -> Self::Data {
pub fn data(&'view self) -> Result<&'a [u8], DecoderError> {
let pi = BasicDecoder::payload_info(self.bytes)?;
Ok(&self.bytes[pi.header_len..(pi.header_len + pi.value_len)])
}
fn item_count(&self) -> usize {
pub fn item_count(&self) -> Result<usize, DecoderError> {
match self.is_list() {
true => match self.count_cache.get() {
Some(c) => c,
Some(c) => Ok(c),
None => {
let c = self.iter().count();
self.count_cache.set(Some(c));
c
Ok(c)
}
},
false => 0
false => Err(DecoderError::RlpExpectedToBeList),
}
}
fn size(&self) -> usize {
pub fn size(&self) -> usize {
match self.is_data() {
// TODO: No panic on malformed data, but ideally would Err on no PayloadInfo.
true => BasicDecoder::payload_info(self.bytes).map(|b| b.value_len).unwrap_or(0),
@ -203,7 +195,7 @@ impl<'a, 'view> View<'a, 'view> for UntrustedRlp<'a> where 'a: 'view {
}
}
fn at(&'view self, index: usize) -> Self::Item {
pub fn at(&'view self, index: usize) -> Result<UntrustedRlp<'a>, DecoderError> {
if !self.is_list() {
return Err(DecoderError::RlpExpectedToBeList);
}
@ -213,7 +205,7 @@ impl<'a, 'view> View<'a, 'view> for UntrustedRlp<'a> where 'a: 'view {
let c = self.offset_cache.get();
let (mut bytes, to_skip) = match c.index <= index {
true => (UntrustedRlp::consume(self.bytes, c.offset)?, index - c.index),
false => (self.consume_list_prefix()?, index),
false => (self.consume_list_payload()?, index),
};
// skip up to x items
@ -227,23 +219,23 @@ impl<'a, 'view> View<'a, 'view> for UntrustedRlp<'a> where 'a: 'view {
Ok(UntrustedRlp::new(&bytes[0..found.header_len + found.value_len]))
}
fn is_null(&self) -> bool {
pub fn is_null(&self) -> bool {
self.bytes.len() == 0
}
fn is_empty(&self) -> bool {
pub fn is_empty(&self) -> bool {
!self.is_null() && (self.bytes[0] == 0xc0 || self.bytes[0] == 0x80)
}
fn is_list(&self) -> bool {
pub fn is_list(&self) -> bool {
!self.is_null() && self.bytes[0] >= 0xc0
}
fn is_data(&self) -> bool {
pub fn is_data(&self) -> bool {
!self.is_null() && self.bytes[0] < 0xc0
}
fn is_int(&self) -> bool {
pub fn is_int(&self) -> bool {
if self.is_null() {
return false;
}
@ -256,23 +248,32 @@ impl<'a, 'view> View<'a, 'view> for UntrustedRlp<'a> where 'a: 'view {
}
}
fn iter(&'view self) -> Self::Iter {
pub fn iter(&'view self) -> UntrustedRlpIterator<'a, 'view> {
self.into_iter()
}
fn as_val<T>(&self) -> Result<T, DecoderError> where T: RlpDecodable {
// optimize, so it doesn't use clone (although This clone is cheap)
T::decode(&BasicDecoder::new(self.clone()))
pub fn as_val<T>(&self) -> Result<T, DecoderError> where T: Decodable {
T::decode(self)
}
fn val_at<T>(&self, index: usize) -> Result<T, DecoderError> where T: RlpDecodable {
pub fn as_list<T>(&self) -> Result<Vec<T>, DecoderError> where T: Decodable {
self.iter().map(|rlp| rlp.as_val()).collect()
}
pub fn val_at<T>(&self, index: usize) -> Result<T, DecoderError> where T: Decodable {
self.at(index)?.as_val()
}
}
impl<'a> UntrustedRlp<'a> {
pub fn list_at<T>(&self, index: usize) -> Result<Vec<T>, DecoderError> where T: Decodable {
self.at(index)?.as_list()
}
pub fn decoder(&self) -> BasicDecoder {
BasicDecoder::new(self.clone())
}
/// consumes first found prefix
fn consume_list_prefix(&self) -> Result<&'a [u8], DecoderError> {
fn consume_list_payload(&self) -> Result<&'a [u8], DecoderError> {
let item = BasicDecoder::payload_info(self.bytes)?;
let bytes = UntrustedRlp::consume(self.bytes, item.header_len)?;
Ok(bytes)
@ -327,7 +328,7 @@ impl<'a, 'view> Iterator for UntrustedRlpIterator<'a, 'view> {
}
}
struct BasicDecoder<'a> {
pub struct BasicDecoder<'a> {
rlp: UntrustedRlp<'a>
}
@ -346,10 +347,8 @@ impl<'a> BasicDecoder<'a> {
_ => Err(DecoderError::RlpIsTooShort),
}
}
}
impl<'a> Decoder for BasicDecoder<'a> {
fn read_value<T, F>(&self, f: &F) -> Result<T, DecoderError>
pub fn decode_value<T, F>(&self, f: F) -> Result<T, DecoderError>
where F: Fn(&[u8]) -> Result<T, DecoderError> {
let bytes = self.rlp.as_raw();
@ -378,7 +377,7 @@ impl<'a> Decoder for BasicDecoder<'a> {
if bytes.len() < begin_of_value {
return Err(DecoderError::RlpInconsistentLengthAndData);
}
let len = usize::from_bytes(&bytes[1..begin_of_value])?;
let len = decode_usize(&bytes[1..begin_of_value])?;
let last_index_of_value = begin_of_value + len;
if bytes.len() < last_index_of_value {
@ -390,108 +389,12 @@ impl<'a> Decoder for BasicDecoder<'a> {
_ => Err(DecoderError::RlpExpectedToBeData)
}
}
fn as_raw(&self) -> &[u8] {
self.rlp.as_raw()
}
fn as_rlp(&self) -> &UntrustedRlp {
&self.rlp
}
}
impl<T> Decodable for T where T: FromBytes {
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
decoder.read_value(&|bytes: &[u8]| Ok(T::from_bytes(bytes)?))
}
}
impl<T> Decodable for Vec<T> where T: Decodable {
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
decoder.as_rlp().iter().map(|d| T::decode(&BasicDecoder::new(d))).collect()
}
}
impl<T> Decodable for Option<T> where T: Decodable {
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
decoder.as_rlp().iter().map(|d| T::decode(&BasicDecoder::new(d))).collect::<Result<Vec<_>, DecoderError>>().map(|mut a| a.pop())
}
}
impl Decodable for Vec<u8> {
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
decoder.read_value(&|bytes: &[u8]| Ok(bytes.to_vec()))
}
}
macro_rules! impl_array_decodable {
($index_type:ty, $len:expr ) => (
impl<T> Decodable for [T; $len] where T: Decodable {
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
let decoders = decoder.as_rlp();
let mut result: [T; $len] = unsafe { ::std::mem::uninitialized() };
if decoders.item_count() != $len {
return Err(DecoderError::RlpIncorrectListLen);
}
for i in 0..decoders.item_count() {
result[i] = T::decode(&BasicDecoder::new(decoders.at(i)?))?;
}
Ok(result)
}
}
)
}
macro_rules! impl_array_decodable_recursive {
($index_type:ty, ) => ();
($index_type:ty, $len:expr, $($more:expr,)*) => (
impl_array_decodable!($index_type, $len);
impl_array_decodable_recursive!($index_type, $($more,)*);
);
}
impl_array_decodable_recursive!(
u8, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
32, 40, 48, 56, 64, 72, 96, 128, 160, 192, 224,
);
impl<T> RlpDecodable for T where T: Decodable {
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
Decodable::decode(decoder)
}
}
struct DecodableU8 (u8);
impl FromBytes for DecodableU8 {
fn from_bytes(bytes: &[u8]) -> FromBytesResult<DecodableU8> {
match bytes.len() {
0 => Ok(DecodableU8(0u8)),
1 => {
if bytes[0] == 0 {
return Err(FromBytesError::ZeroPrefixedInt)
}
Ok(DecodableU8(bytes[0]))
}
_ => Err(FromBytesError::DataIsTooLong)
}
}
}
impl RlpDecodable for u8 {
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
let u: DecodableU8 = Decodable::decode(decoder)?;
Ok(u.0)
}
}
#[cfg(test)]
mod tests {
use ::{UntrustedRlp, View};
use UntrustedRlp;
#[test]
fn test_rlp_display() {
use rustc_serialize::hex::FromHex;

View File

@ -14,9 +14,12 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
extern crate ethcore_bigint as bigint;
extern crate rlp;
use std::{fmt, cmp};
use bigint::prelude::U256;
use {Encodable, RlpDecodable, UntrustedRlp, RlpStream, View, DecoderError};
use rlp::{Encodable, Decodable, UntrustedRlp, RlpStream, DecoderError};
#[test]
fn rlp_at() {
@ -24,7 +27,7 @@ fn rlp_at() {
{
let rlp = UntrustedRlp::new(&data);
assert!(rlp.is_list());
let animals: Vec<String> = rlp.as_val().unwrap();
let animals: Vec<String> = rlp.as_list().unwrap();
assert_eq!(animals, vec!["cat".to_owned(), "dog".to_owned()]);
let cat = rlp.at(0).unwrap();
@ -89,7 +92,7 @@ fn run_encode_tests<T>(tests: Vec<ETestPair<T>>)
where T: Encodable
{
for t in &tests {
let res = super::encode(&t.0);
let res = rlp::encode(&t.0);
assert_eq!(&res[..], &t.1[..]);
}
}
@ -100,7 +103,7 @@ fn run_encode_tests_list<T>(tests: Vec<VETestPair<T>>)
where T: Encodable
{
for t in &tests {
let res = super::encode_list(&t.0);
let res = rlp::encode_list(&t.0);
assert_eq!(&res[..], &t.1[..]);
}
}
@ -210,11 +213,20 @@ fn encode_vector_str() {
run_encode_tests_list(tests);
}
struct DTestPair<T>(T, Vec<u8>) where T: RlpDecodable + fmt::Debug + cmp::Eq;
struct DTestPair<T>(T, Vec<u8>) where T: Decodable + fmt::Debug + cmp::Eq;
fn run_decode_tests<T>(tests: Vec<DTestPair<T>>) where T: RlpDecodable + fmt::Debug + cmp::Eq {
struct VDTestPair<T>(Vec<T>, Vec<u8>) where T: Decodable + fmt::Debug + cmp::Eq;
fn run_decode_tests<T>(tests: Vec<DTestPair<T>>) where T: Decodable + fmt::Debug + cmp::Eq {
for t in &tests {
let res: T = super::decode(&t.1);
let res: T = rlp::decode(&t.1);
assert_eq!(res, t.0);
}
}
fn run_decode_tests_list<T>(tests: Vec<VDTestPair<T>>) where T: Decodable + fmt::Debug + cmp::Eq {
for t in &tests {
let res: Vec<T> = rlp::decode_list(&t.1);
assert_eq!(res, t.0);
}
}
@ -318,35 +330,19 @@ fn decode_untrusted_address() {
#[test]
fn decode_untrusted_vector_u64() {
let tests = vec![
DTestPair(vec![], vec![0xc0]),
DTestPair(vec![15u64], vec![0xc1, 0x0f]),
DTestPair(vec![1, 2, 3, 7, 0xff], vec![0xc6, 1, 2, 3, 7, 0x81, 0xff]),
DTestPair(vec![0xffffffff, 1, 2, 3, 7, 0xff], vec![0xcb, 0x84, 0xff, 0xff, 0xff, 0xff, 1, 2, 3, 7, 0x81, 0xff]),
VDTestPair(vec![], vec![0xc0]),
VDTestPair(vec![15u64], vec![0xc1, 0x0f]),
VDTestPair(vec![1, 2, 3, 7, 0xff], vec![0xc6, 1, 2, 3, 7, 0x81, 0xff]),
VDTestPair(vec![0xffffffff, 1, 2, 3, 7, 0xff], vec![0xcb, 0x84, 0xff, 0xff, 0xff, 0xff, 1, 2, 3, 7, 0x81, 0xff]),
];
run_decode_tests(tests);
run_decode_tests_list(tests);
}
#[test]
fn decode_untrusted_vector_str() {
let tests = vec![DTestPair(vec!["cat".to_owned(), "dog".to_owned()],
let tests = vec![VDTestPair(vec!["cat".to_owned(), "dog".to_owned()],
vec![0xc8, 0x83, b'c', b'a', b't', 0x83, b'd', b'o', b'g'])];
run_decode_tests(tests);
}
#[test]
fn decode_untrusted_vector_of_vectors_str() {
let tests = vec![DTestPair(vec![vec!["cat".to_owned()]],
vec![0xc5, 0xc4, 0x83, b'c', b'a', b't'])];
run_decode_tests(tests);
}
#[test]
fn test_decoding_array() {
let v = vec![5u16, 2u16];
let res = super::encode_list(&v);
let arr: [u16; 2] = super::decode(&res);
assert_eq!(arr[0], 5);
assert_eq!(arr[1], 2);
run_decode_tests_list(tests);
}
#[test]