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 std::collections::HashMap;
|
||||||
use elastic_array::ElasticArray1024;
|
use elastic_array::ElasticArray1024;
|
||||||
use common::{BLOCKS_RLP_SWAPPER, SNAPSHOT_RLP_SWAPPER};
|
use common::{BLOCKS_RLP_SWAPPER, SNAPSHOT_RLP_SWAPPER};
|
||||||
use {UntrustedRlp, View, Compressible, encode, RlpStream};
|
use {UntrustedRlp, Compressible, encode, RlpStream};
|
||||||
|
|
||||||
/// Stores RLPs used for compression
|
/// Stores RLPs used for compression
|
||||||
pub struct InvalidRlpSwapper<'a> {
|
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
|
fn map_rlp<F>(rlp: &UntrustedRlp, f: F) -> Option<ElasticArray1024<u8>> where
|
||||||
F: Fn(&UntrustedRlp) -> Option<ElasticArray1024<u8>> {
|
F: Fn(&UntrustedRlp) -> Option<ElasticArray1024<u8>> {
|
||||||
match rlp.iter()
|
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| {
|
|(is_some, mut acc), subrlp| {
|
||||||
let new = f(&subrlp);
|
let new = f(&subrlp);
|
||||||
if let Some(ref insert) = new {
|
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);
|
swapper.get_valid(rlp.as_raw()).map(to_elastic);
|
||||||
// Simply decompress data.
|
// Simply decompress data.
|
||||||
if rlp.is_data() { return simple_swap(); }
|
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.
|
// Look for special compressed list, which contains nested data.
|
||||||
2 if rlp.at(0).map(|r| r.as_raw() == &[0x81, 0x7f]).unwrap_or(false) =>
|
2 if rlp.at(0).map(|r| r.as_raw() == &[0x81, 0x7f]).unwrap_or(false) =>
|
||||||
rlp.at(1).ok().map_or(simple_swap(),
|
rlp.at(1).ok().map_or(simple_swap(),
|
||||||
|
@ -169,7 +169,7 @@ impl<'a> Compressible for UntrustedRlp<'a> {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use compression::InvalidRlpSwapper;
|
use compression::InvalidRlpSwapper;
|
||||||
use {UntrustedRlp, Compressible, View, RlpType};
|
use {UntrustedRlp, Compressible, RlpType};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn invalid_rlp_swapper() {
|
fn invalid_rlp_swapper() {
|
||||||
|
|
|
@ -16,13 +16,10 @@
|
||||||
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::error::Error as StdError;
|
use std::error::Error as StdError;
|
||||||
use bytes::FromBytesError;
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
/// Error concerning the RLP decoder.
|
/// Error concerning the RLP decoder.
|
||||||
pub enum DecoderError {
|
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.
|
/// Data has additional bytes at the end of the valid RLP fragment.
|
||||||
RlpIsTooBig,
|
RlpIsTooBig,
|
||||||
/// Data has too few bytes for valid RLP.
|
/// Data has too few bytes for valid RLP.
|
||||||
|
@ -56,9 +53,3 @@ impl fmt::Display for DecoderError {
|
||||||
fmt::Debug::fmt(&self, f)
|
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 byteorder::{ByteOrder, BigEndian};
|
||||||
use bigint::prelude::{Uint, U128, U256, H64, H128, H160, H256, H512, H520, H2048};
|
use bigint::prelude::{Uint, U128, U256, H64, H128, H160, H256, H512, H520, H2048};
|
||||||
use traits::Encodable;
|
use traits::{Encodable, Decodable};
|
||||||
use stream::RlpStream;
|
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 {
|
impl Encodable for bool {
|
||||||
fn rlp_append(&self, s: &mut RlpStream) {
|
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] {
|
impl<'a> Encodable for &'a [u8] {
|
||||||
fn rlp_append(&self, s: &mut RlpStream) {
|
fn rlp_append(&self, s: &mut RlpStream) {
|
||||||
s.encoder().encode_value(self);
|
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 {
|
impl<T> Encodable for Option<T> where T: Encodable {
|
||||||
fn rlp_append(&self, s: &mut RlpStream) {
|
fn rlp_append(&self, s: &mut RlpStream) {
|
||||||
match *self {
|
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 {
|
impl Encodable for u8 {
|
||||||
fn rlp_append(&self, s: &mut RlpStream) {
|
fn rlp_append(&self, s: &mut RlpStream) {
|
||||||
if *self != 0 {
|
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 {
|
macro_rules! impl_encodable_for_u {
|
||||||
($name: ident, $func: ident, $size: expr) => {
|
($name: ident, $func: ident, $size: expr) => {
|
||||||
impl Encodable for $name {
|
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!(u16, write_u16, 2);
|
||||||
impl_encodable_for_u!(u32, write_u32, 4);
|
impl_encodable_for_u!(u32, write_u32, 4);
|
||||||
impl_encodable_for_u!(u64, write_u64, 8);
|
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 {
|
impl Encodable for usize {
|
||||||
fn rlp_append(&self, s: &mut RlpStream) {
|
fn rlp_append(&self, s: &mut RlpStream) {
|
||||||
(*self as u64).rlp_append(s);
|
(*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 {
|
macro_rules! impl_encodable_for_hash {
|
||||||
($name: ident) => {
|
($name: ident) => {
|
||||||
impl Encodable for $name {
|
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!(H64);
|
||||||
impl_encodable_for_hash!(H128);
|
impl_encodable_for_hash!(H128);
|
||||||
impl_encodable_for_hash!(H160);
|
impl_encodable_for_hash!(H160);
|
||||||
|
@ -90,6 +207,14 @@ impl_encodable_for_hash!(H512);
|
||||||
impl_encodable_for_hash!(H520);
|
impl_encodable_for_hash!(H520);
|
||||||
impl_encodable_for_hash!(H2048);
|
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 {
|
macro_rules! impl_encodable_for_uint {
|
||||||
($name: ident, $size: expr) => {
|
($name: ident, $size: expr) => {
|
||||||
impl Encodable for $name {
|
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!(U256, 32);
|
||||||
impl_encodable_for_uint!(U128, 16);
|
impl_encodable_for_uint!(U128, 16);
|
||||||
|
|
||||||
|
impl_decodable_for_uint!(U256, 32);
|
||||||
|
impl_decodable_for_uint!(U128, 16);
|
||||||
|
|
||||||
impl<'a> Encodable for &'a str {
|
impl<'a> Encodable for &'a str {
|
||||||
fn rlp_append(&self, s: &mut RlpStream) {
|
fn rlp_append(&self, s: &mut RlpStream) {
|
||||||
s.encoder().encode_value(self.as_bytes());
|
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 stream;
|
||||||
mod compression;
|
mod compression;
|
||||||
mod common;
|
mod common;
|
||||||
mod bytes;
|
|
||||||
mod impls;
|
mod impls;
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests;
|
|
||||||
|
|
||||||
use std::borrow::Borrow;
|
use std::borrow::Borrow;
|
||||||
use elastic_array::ElasticArray1024;
|
use elastic_array::ElasticArray1024;
|
||||||
|
|
||||||
pub use error::DecoderError;
|
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 untrusted_rlp::{UntrustedRlp, UntrustedRlpIterator, PayloadInfo, Prototype};
|
||||||
pub use rlpin::{Rlp, RlpIterator};
|
pub use rlpin::{Rlp, RlpIterator};
|
||||||
pub use stream::RlpStream;
|
pub use stream::RlpStream;
|
||||||
|
@ -88,16 +84,21 @@ pub const EMPTY_LIST_RLP: [u8; 1] = [0xC0; 1];
|
||||||
/// extern crate rlp;
|
/// extern crate rlp;
|
||||||
///
|
///
|
||||||
/// fn main () {
|
/// fn main () {
|
||||||
/// let data = vec![0xc8, 0x83, b'c', b'a', b't', 0x83, b'd', b'o', b'g'];
|
/// let data = vec![0x83, b'c', b'a', b't'];
|
||||||
/// let animals: Vec<String> = rlp::decode(&data);
|
/// let animal: String = rlp::decode(&data);
|
||||||
/// assert_eq!(animals, vec!["cat".to_string(), "dog".to_string()]);
|
/// 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);
|
let rlp = Rlp::new(bytes);
|
||||||
rlp.as_val()
|
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.
|
/// Shortcut function to encode structure into rlp.
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
|
|
201
src/rlpin.rs
201
src/rlpin.rs
|
@ -15,8 +15,7 @@
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use rustc_serialize::hex::ToHex;
|
use {UntrustedRlp, PayloadInfo, Prototype, Decodable};
|
||||||
use {View, DecoderError, UntrustedRlp, PayloadInfo, Prototype, RlpDecodable};
|
|
||||||
|
|
||||||
impl<'a> From<UntrustedRlp<'a>> for Rlp<'a> {
|
impl<'a> From<UntrustedRlp<'a>> for Rlp<'a> {
|
||||||
fn from(rlp: UntrustedRlp<'a>) -> 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 {
|
impl<'a, 'view> Rlp<'a> where 'a: 'view {
|
||||||
type Prototype = Prototype;
|
|
||||||
type PayloadInfo = PayloadInfo;
|
|
||||||
type Data = &'a [u8];
|
|
||||||
type Item = Rlp<'a>;
|
|
||||||
type Iter = RlpIterator<'a, 'view>;
|
|
||||||
|
|
||||||
/// Create a new instance of `Rlp`
|
/// Create a new instance of `Rlp`
|
||||||
fn new(bytes: &'a [u8]) -> Rlp<'a> {
|
pub fn new(bytes: &'a [u8]) -> Rlp<'a> {
|
||||||
Rlp {
|
Rlp {
|
||||||
rlp: UntrustedRlp::new(bytes)
|
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()
|
self.rlp.as_raw()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn prototype(&self) -> Self::Prototype {
|
/// Get the prototype of the RLP.
|
||||||
|
pub fn prototype(&self) -> Prototype {
|
||||||
self.rlp.prototype().unwrap()
|
self.rlp.prototype().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn payload_info(&self) -> Self::PayloadInfo {
|
/// Get payload info.
|
||||||
|
pub fn payload_info(&self) -> PayloadInfo {
|
||||||
self.rlp.payload_info().unwrap()
|
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()
|
self.rlp.data().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn item_count(&self) -> usize {
|
/// Returns number of RLP items.
|
||||||
self.rlp.item_count()
|
///
|
||||||
|
/// ```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()
|
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())
|
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()
|
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()
|
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()
|
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()
|
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()
|
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()
|
self.into_iter()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn as_val<T>(&self) -> Result<T, DecoderError> where T: RlpDecodable {
|
/// Decode data into an object
|
||||||
self.rlp.as_val()
|
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 {
|
pub fn as_list<T>(&self) -> Vec<T> where T: Decodable {
|
||||||
self.at(index).rlp.as_val()
|
self.iter().map(|rlp| rlp.as_val()).collect()
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl <'a, 'view> Rlp<'a> where 'a: 'view {
|
/// Decode data at given list index into an object
|
||||||
fn view_as_val<T, R>(r: &'view R) -> T where R: View<'a, 'view>, T: RlpDecodable {
|
pub fn val_at<T>(&self, index: usize) -> T where T: Decodable {
|
||||||
let res: Result<T, DecoderError> = r.as_val();
|
self.at(index).as_val()
|
||||||
res.unwrap_or_else(|e| panic!("DecodeError: {}, {}", e, r.as_raw().to_hex()))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Decode into an object
|
pub fn list_at<T>(&self, index: usize) -> Vec<T> where T: Decodable {
|
||||||
pub fn as_val<T>(&self) -> T where T: RlpDecodable {
|
self.at(index).as_list()
|
||||||
Self::view_as_val(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
204
src/traits.rs
204
src/traits.rs
|
@ -16,212 +16,12 @@
|
||||||
|
|
||||||
//! Common RLP traits
|
//! Common RLP traits
|
||||||
use elastic_array::ElasticArray1024;
|
use elastic_array::ElasticArray1024;
|
||||||
use stream::RlpStream;
|
use {DecoderError, UntrustedRlp, 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];
|
|
||||||
}
|
|
||||||
|
|
||||||
/// RLP decodable trait
|
/// RLP decodable trait
|
||||||
pub trait Decodable: Sized {
|
pub trait Decodable: Sized {
|
||||||
/// Decode a value from RLP bytes
|
/// Decode a value from RLP bytes
|
||||||
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder;
|
fn decode(rlp: &UntrustedRlp) -> Result<Self, DecoderError>;
|
||||||
}
|
|
||||||
|
|
||||||
/// 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Structure encodable to RLP
|
/// Structure encodable to RLP
|
||||||
|
|
|
@ -17,9 +17,8 @@
|
||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use rustc_serialize::hex::ToHex;
|
use rustc_serialize::hex::ToHex;
|
||||||
|
use impls::decode_usize;
|
||||||
use bytes::{FromBytes, FromBytesResult, FromBytesError};
|
use {Decodable, DecoderError};
|
||||||
use ::{View, Decoder, Decodable, DecoderError, RlpDecodable};
|
|
||||||
|
|
||||||
/// rlp offset
|
/// rlp offset
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[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); }
|
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))
|
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 {
|
impl<'a, 'view> UntrustedRlp<'a> where 'a: 'view {
|
||||||
type Prototype = Result<Prototype, DecoderError>;
|
pub fn new(bytes: &'a [u8]) -> UntrustedRlp<'a> {
|
||||||
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> {
|
|
||||||
UntrustedRlp {
|
UntrustedRlp {
|
||||||
bytes: bytes,
|
bytes: bytes,
|
||||||
offset_cache: Cell::new(OffsetCache::new(usize::max_value(), 0)),
|
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
|
self.bytes
|
||||||
}
|
}
|
||||||
|
|
||||||
fn prototype(&self) -> Self::Prototype {
|
pub fn prototype(&self) -> Result<Prototype, DecoderError> {
|
||||||
// optimize? && return appropriate errors
|
// optimize? && return appropriate errors
|
||||||
if self.is_data() {
|
if self.is_data() {
|
||||||
Ok(Prototype::Data(self.size()))
|
Ok(Prototype::Data(self.size()))
|
||||||
} else if self.is_list() {
|
} else if self.is_list() {
|
||||||
Ok(Prototype::List(self.item_count()))
|
self.item_count().map(Prototype::List)
|
||||||
} else {
|
} else {
|
||||||
Ok(Prototype::Null)
|
Ok(Prototype::Null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn payload_info(&self) -> Self::PayloadInfo {
|
pub fn payload_info(&self) -> Result<PayloadInfo, DecoderError> {
|
||||||
BasicDecoder::payload_info(self.bytes)
|
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)?;
|
let pi = BasicDecoder::payload_info(self.bytes)?;
|
||||||
Ok(&self.bytes[pi.header_len..(pi.header_len + pi.value_len)])
|
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() {
|
match self.is_list() {
|
||||||
true => match self.count_cache.get() {
|
true => match self.count_cache.get() {
|
||||||
Some(c) => c,
|
Some(c) => Ok(c),
|
||||||
None => {
|
None => {
|
||||||
let c = self.iter().count();
|
let c = self.iter().count();
|
||||||
self.count_cache.set(Some(c));
|
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() {
|
match self.is_data() {
|
||||||
// TODO: No panic on malformed data, but ideally would Err on no PayloadInfo.
|
// 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),
|
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() {
|
if !self.is_list() {
|
||||||
return Err(DecoderError::RlpExpectedToBeList);
|
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 c = self.offset_cache.get();
|
||||||
let (mut bytes, to_skip) = match c.index <= index {
|
let (mut bytes, to_skip) = match c.index <= index {
|
||||||
true => (UntrustedRlp::consume(self.bytes, c.offset)?, index - c.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
|
// 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]))
|
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
|
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)
|
!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
|
!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
|
!self.is_null() && self.bytes[0] < 0xc0
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_int(&self) -> bool {
|
pub fn is_int(&self) -> bool {
|
||||||
if self.is_null() {
|
if self.is_null() {
|
||||||
return false;
|
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()
|
self.into_iter()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn as_val<T>(&self) -> Result<T, DecoderError> where T: RlpDecodable {
|
pub fn as_val<T>(&self) -> Result<T, DecoderError> where T: Decodable {
|
||||||
// optimize, so it doesn't use clone (although This clone is cheap)
|
T::decode(self)
|
||||||
T::decode(&BasicDecoder::new(self.clone()))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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()
|
self.at(index)?.as_val()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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())
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> UntrustedRlp<'a> {
|
|
||||||
/// consumes first found prefix
|
/// 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 item = BasicDecoder::payload_info(self.bytes)?;
|
||||||
let bytes = UntrustedRlp::consume(self.bytes, item.header_len)?;
|
let bytes = UntrustedRlp::consume(self.bytes, item.header_len)?;
|
||||||
Ok(bytes)
|
Ok(bytes)
|
||||||
|
@ -327,7 +328,7 @@ impl<'a, 'view> Iterator for UntrustedRlpIterator<'a, 'view> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct BasicDecoder<'a> {
|
pub struct BasicDecoder<'a> {
|
||||||
rlp: UntrustedRlp<'a>
|
rlp: UntrustedRlp<'a>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -346,10 +347,8 @@ impl<'a> BasicDecoder<'a> {
|
||||||
_ => Err(DecoderError::RlpIsTooShort),
|
_ => Err(DecoderError::RlpIsTooShort),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Decoder for BasicDecoder<'a> {
|
pub fn decode_value<T, F>(&self, f: F) -> Result<T, DecoderError>
|
||||||
fn read_value<T, F>(&self, f: &F) -> Result<T, DecoderError>
|
|
||||||
where F: Fn(&[u8]) -> Result<T, DecoderError> {
|
where F: Fn(&[u8]) -> Result<T, DecoderError> {
|
||||||
|
|
||||||
let bytes = self.rlp.as_raw();
|
let bytes = self.rlp.as_raw();
|
||||||
|
@ -378,7 +377,7 @@ impl<'a> Decoder for BasicDecoder<'a> {
|
||||||
if bytes.len() < begin_of_value {
|
if bytes.len() < begin_of_value {
|
||||||
return Err(DecoderError::RlpInconsistentLengthAndData);
|
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;
|
let last_index_of_value = begin_of_value + len;
|
||||||
if bytes.len() < last_index_of_value {
|
if bytes.len() < last_index_of_value {
|
||||||
|
@ -390,108 +389,12 @@ impl<'a> Decoder for BasicDecoder<'a> {
|
||||||
_ => Err(DecoderError::RlpExpectedToBeData)
|
_ => 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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use ::{UntrustedRlp, View};
|
use UntrustedRlp;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_rlp_display() {
|
fn test_rlp_display() {
|
||||||
use rustc_serialize::hex::FromHex;
|
use rustc_serialize::hex::FromHex;
|
||||||
|
|
|
@ -14,9 +14,12 @@
|
||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// 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 std::{fmt, cmp};
|
||||||
use bigint::prelude::U256;
|
use bigint::prelude::U256;
|
||||||
use {Encodable, RlpDecodable, UntrustedRlp, RlpStream, View, DecoderError};
|
use rlp::{Encodable, Decodable, UntrustedRlp, RlpStream, DecoderError};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn rlp_at() {
|
fn rlp_at() {
|
||||||
|
@ -24,7 +27,7 @@ fn rlp_at() {
|
||||||
{
|
{
|
||||||
let rlp = UntrustedRlp::new(&data);
|
let rlp = UntrustedRlp::new(&data);
|
||||||
assert!(rlp.is_list());
|
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()]);
|
assert_eq!(animals, vec!["cat".to_owned(), "dog".to_owned()]);
|
||||||
|
|
||||||
let cat = rlp.at(0).unwrap();
|
let cat = rlp.at(0).unwrap();
|
||||||
|
@ -89,7 +92,7 @@ fn run_encode_tests<T>(tests: Vec<ETestPair<T>>)
|
||||||
where T: Encodable
|
where T: Encodable
|
||||||
{
|
{
|
||||||
for t in &tests {
|
for t in &tests {
|
||||||
let res = super::encode(&t.0);
|
let res = rlp::encode(&t.0);
|
||||||
assert_eq!(&res[..], &t.1[..]);
|
assert_eq!(&res[..], &t.1[..]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -100,7 +103,7 @@ fn run_encode_tests_list<T>(tests: Vec<VETestPair<T>>)
|
||||||
where T: Encodable
|
where T: Encodable
|
||||||
{
|
{
|
||||||
for t in &tests {
|
for t in &tests {
|
||||||
let res = super::encode_list(&t.0);
|
let res = rlp::encode_list(&t.0);
|
||||||
assert_eq!(&res[..], &t.1[..]);
|
assert_eq!(&res[..], &t.1[..]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -210,11 +213,20 @@ fn encode_vector_str() {
|
||||||
run_encode_tests_list(tests);
|
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 {
|
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);
|
assert_eq!(res, t.0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -318,35 +330,19 @@ fn decode_untrusted_address() {
|
||||||
#[test]
|
#[test]
|
||||||
fn decode_untrusted_vector_u64() {
|
fn decode_untrusted_vector_u64() {
|
||||||
let tests = vec![
|
let tests = vec![
|
||||||
DTestPair(vec![], vec![0xc0]),
|
VDTestPair(vec![], vec![0xc0]),
|
||||||
DTestPair(vec![15u64], vec![0xc1, 0x0f]),
|
VDTestPair(vec![15u64], vec![0xc1, 0x0f]),
|
||||||
DTestPair(vec![1, 2, 3, 7, 0xff], vec![0xc6, 1, 2, 3, 7, 0x81, 0xff]),
|
VDTestPair(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![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]
|
#[test]
|
||||||
fn decode_untrusted_vector_str() {
|
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'])];
|
vec![0xc8, 0x83, b'c', b'a', b't', 0x83, b'd', b'o', b'g'])];
|
||||||
run_decode_tests(tests);
|
run_decode_tests_list(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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
Loading…
Reference in New Issue