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:
parent
ddb4e348e4
commit
e79518362e
|
@ -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();
|
||||
});
|
||||
}
|
148
src/bytes.rs
148
src/bytes.rs
|
@ -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);
|
||||
|
|
@ -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() {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
159
src/impls.rs
159
src/impls.rs
|
@ -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),
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
19
src/lib.rs
19
src/lib.rs
|
@ -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
|
||||
|
|
201
src/rlpin.rs
201
src/rlpin.rs
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
204
src/traits.rs
204
src/traits.rs
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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]
|
Loading…
Reference in New Issue