diff --git a/benches/rlp.rs b/benches/rlp.rs new file mode 100644 index 0000000..3f8166e --- /dev/null +++ b/benches/rlp.rs @@ -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 . + +//! 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 = rlp.at(0).as_list(); + let _v1: Vec = rlp.at(1).at(0).as_list(); + let nested_rlp = rlp.at(2); + let _v2a: Vec = nested_rlp.at(0).as_list(); + let _v2b: Vec = 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(); + }); +} diff --git a/src/bytes.rs b/src/bytes.rs deleted file mode 100644 index e5f266f..0000000 --- a/src/bytes.rs +++ /dev/null @@ -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 . - -//! 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 = Result; - -/// 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; -} - -impl FromBytes for String { - fn from_bytes(bytes: &[u8]) -> FromBytesResult { - ::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 { - 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); - diff --git a/src/compression.rs b/src/compression.rs index b7cf72a..7a9c5a9 100644 --- a/src/compression.rs +++ b/src/compression.rs @@ -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 { fn map_rlp(rlp: &UntrustedRlp, f: F) -> Option> where F: Fn(&UntrustedRlp) -> Option> { 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 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() { diff --git a/src/error.rs b/src/error.rs index f3b43f8..4ad754d 100644 --- a/src/error.rs +++ b/src/error.rs @@ -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 for DecoderError { - fn from(err: FromBytesError) -> DecoderError { - DecoderError::FromBytesError(err) - } -} diff --git a/src/impls.rs b/src/impls.rs index affac1d..909f3bd 100644 --- a/src/impls.rs +++ b/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 { + match bytes.len() { + l if l <= mem::size_of::() => { + 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 { + 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 { } } +impl Decodable for Vec { + fn decode(rlp: &UntrustedRlp) -> Result { + rlp.decoder().decode_value(|bytes| { + Ok(bytes.to_vec()) + }) + } +} + impl Encodable for Option where T: Encodable { fn rlp_append(&self, s: &mut RlpStream) { match *self { @@ -39,6 +78,17 @@ impl Encodable for Option where T: Encodable { } } +impl Decodable for Option where T: Decodable { + fn decode(rlp: &UntrustedRlp) -> Result { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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), + } + }) + } +} diff --git a/src/lib.rs b/src/lib.rs index 406501b..4b01691 100644 --- a/src/lib.rs +++ b/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 = 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(bytes: &[u8]) -> T where T: RlpDecodable { +pub fn decode(bytes: &[u8]) -> T where T: Decodable { let rlp = Rlp::new(bytes); rlp.as_val() } +pub fn decode_list(bytes: &[u8]) -> Vec where T: Decodable { + let rlp = Rlp::new(bytes); + rlp.as_list() +} + /// Shortcut function to encode structure into rlp. /// /// ```rust diff --git a/src/rlpin.rs b/src/rlpin.rs index 7ae7156..c7c054a 100644 --- a/src/rlpin.rs +++ b/src/rlpin.rs @@ -15,8 +15,7 @@ // along with Parity. If not, see . use std::fmt; -use rustc_serialize::hex::ToHex; -use {View, DecoderError, UntrustedRlp, PayloadInfo, Prototype, RlpDecodable}; +use {UntrustedRlp, PayloadInfo, Prototype, Decodable}; impl<'a> From> 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 = rlp.iter().map(| i | i.as_val()).collect(); + /// } + /// ``` + pub fn iter(&'view self) -> RlpIterator<'a, 'view> { self.into_iter() } - fn as_val(&self) -> Result where T: RlpDecodable { - self.rlp.as_val() + /// Decode data into an object + pub fn as_val(&self) -> T where T: Decodable { + self.rlp.as_val().expect("Unexpected rlp error") } - fn val_at(&self, index: usize) -> Result where T: RlpDecodable { - self.at(index).rlp.as_val() - } -} - -impl <'a, 'view> Rlp<'a> where 'a: 'view { - fn view_as_val(r: &'view R) -> T where R: View<'a, 'view>, T: RlpDecodable { - let res: Result = r.as_val(); - res.unwrap_or_else(|e| panic!("DecodeError: {}, {}", e, r.as_raw().to_hex())) + pub fn as_list(&self) -> Vec where T: Decodable { + self.iter().map(|rlp| rlp.as_val()).collect() } - /// Decode into an object - pub fn as_val(&self) -> T where T: RlpDecodable { - Self::view_as_val(self) + /// Decode data at given list index into an object + pub fn val_at(&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(&self, index: usize) -> T where T: RlpDecodable { - Self::view_as_val(&self.at(index)) + pub fn list_at(&self, index: usize) -> Vec where T: Decodable { + self.at(index).as_list() } } diff --git a/src/traits.rs b/src/traits.rs index 3f79e9c..33c5ae5 100644 --- a/src/traits.rs +++ b/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(&self, f: &F) -> Result - where F: Fn(&[u8]) -> Result; - - /// 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(decoder: &D) -> Result where D: Decoder; -} - -/// Internal helper trait. Implement `Decodable` for custom types. -pub trait RlpDecodable: Sized { - /// Decode a value from RLP bytes - fn decode(decoder: &D) -> Result 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 = rlp.iter().map(| i | i.as_val()).collect(); - /// } - /// ``` - fn iter(&'view self) -> Self::Iter; - - /// Decode data into an object - fn as_val(&self) -> Result where T: RlpDecodable; - - /// Decode data at given list index into an object - fn val_at(&self, index: usize) -> Result where T: RlpDecodable; + fn decode(rlp: &UntrustedRlp) -> Result; } /// Structure encodable to RLP diff --git a/src/untrusted_rlp.rs b/src/untrusted_rlp.rs index e111fde..8f46bb6 100644 --- a/src/untrusted_rlp.rs +++ b/src/untrusted_rlp.rs @@ -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 (), } 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; - type PayloadInfo = Result; - type Data = Result<&'a [u8], DecoderError>; - type Item = Result, 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 { // 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 { 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 { 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, 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(&self) -> Result 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(&self) -> Result where T: Decodable { + T::decode(self) } - fn val_at(&self, index: usize) -> Result where T: RlpDecodable { + pub fn as_list(&self) -> Result, DecoderError> where T: Decodable { + self.iter().map(|rlp| rlp.as_val()).collect() + } + + pub fn val_at(&self, index: usize) -> Result where T: Decodable { self.at(index)?.as_val() } -} -impl<'a> UntrustedRlp<'a> { + pub fn list_at(&self, index: usize) -> Result, 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(&self, f: &F) -> Result + pub fn decode_value(&self, f: F) -> Result where F: Fn(&[u8]) -> Result { 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 Decodable for T where T: FromBytes { - fn decode(decoder: &D) -> Result where D: Decoder { - decoder.read_value(&|bytes: &[u8]| Ok(T::from_bytes(bytes)?)) - } -} - -impl Decodable for Vec where T: Decodable { - fn decode(decoder: &D) -> Result where D: Decoder { - decoder.as_rlp().iter().map(|d| T::decode(&BasicDecoder::new(d))).collect() - } -} - -impl Decodable for Option where T: Decodable { - fn decode(decoder: &D) -> Result where D: Decoder { - decoder.as_rlp().iter().map(|d| T::decode(&BasicDecoder::new(d))).collect::, DecoderError>>().map(|mut a| a.pop()) - } -} - -impl Decodable for Vec { - fn decode(decoder: &D) -> Result where D: Decoder { - decoder.read_value(&|bytes: &[u8]| Ok(bytes.to_vec())) - } -} - -macro_rules! impl_array_decodable { - ($index_type:ty, $len:expr ) => ( - impl Decodable for [T; $len] where T: Decodable { - fn decode(decoder: &D) -> Result 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 RlpDecodable for T where T: Decodable { - fn decode(decoder: &D) -> Result where D: Decoder { - Decodable::decode(decoder) - } -} - -struct DecodableU8 (u8); - -impl FromBytes for DecodableU8 { - fn from_bytes(bytes: &[u8]) -> FromBytesResult { - 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(decoder: &D) -> Result 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; diff --git a/src/tests.rs b/tests/tests.rs similarity index 90% rename from src/tests.rs rename to tests/tests.rs index bd3abfe..1c996ca 100644 --- a/src/tests.rs +++ b/tests/tests.rs @@ -14,9 +14,12 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . +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 = rlp.as_val().unwrap(); + let animals: Vec = 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(tests: Vec>) 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(tests: Vec>) 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, Vec) where T: RlpDecodable + fmt::Debug + cmp::Eq; +struct DTestPair(T, Vec) where T: Decodable + fmt::Debug + cmp::Eq; -fn run_decode_tests(tests: Vec>) where T: RlpDecodable + fmt::Debug + cmp::Eq { +struct VDTestPair(Vec, Vec) where T: Decodable + fmt::Debug + cmp::Eq; + +fn run_decode_tests(tests: Vec>) 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(tests: Vec>) where T: Decodable + fmt::Debug + cmp::Eq { + for t in &tests { + let res: Vec = 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]