rlp init
This commit is contained in:
parent
526993d0c3
commit
3097a47a9f
62
Cargo.lock
generated
62
Cargo.lock
generated
@ -1,4 +1,66 @@
|
||||
[root]
|
||||
name = "ethcore-util"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"env_logger 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"memchr 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "env_logger"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"log 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.1.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"libc 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"libc 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "0.1.41"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"aho-corasick 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"memchr 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex-syntax 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
|
151
src/bytes.rs
Normal file
151
src/bytes.rs
Normal file
@ -0,0 +1,151 @@
|
||||
//! To/From Bytes conversation for basic types
|
||||
//!
|
||||
//! Types implementing `ToBytes` and `FromBytes` traits
|
||||
//! can be easily converted to and from bytes
|
||||
//!
|
||||
//! # Examples
|
||||
//!
|
||||
|
||||
use std::fmt;
|
||||
use std::error::Error as StdError;
|
||||
|
||||
/// TODO: optimise some conversations
|
||||
pub trait ToBytes {
|
||||
fn to_bytes(&self) -> Vec<u8>;
|
||||
fn to_bytes_len(&self) -> usize { self.to_bytes().len() }
|
||||
fn first_byte(&self) -> Option<u8> { self.to_bytes().first().map(|&x| { x })}
|
||||
}
|
||||
|
||||
impl <'a> ToBytes for &'a str {
|
||||
fn to_bytes(&self) -> Vec<u8> {
|
||||
From::from(*self)
|
||||
}
|
||||
|
||||
fn to_bytes_len(&self) -> usize { self.len() }
|
||||
}
|
||||
|
||||
impl ToBytes for String {
|
||||
fn to_bytes(&self) -> Vec<u8> {
|
||||
let s: &str = self.as_ref();
|
||||
From::from(s)
|
||||
}
|
||||
|
||||
fn to_bytes_len(&self) -> usize { self.len() }
|
||||
}
|
||||
|
||||
impl ToBytes for u8 {
|
||||
fn to_bytes(&self) -> Vec<u8> {
|
||||
match *self {
|
||||
0 => vec![],
|
||||
_ => vec![*self]
|
||||
}
|
||||
}
|
||||
|
||||
fn to_bytes_len(&self) -> usize {
|
||||
match *self {
|
||||
0 => 0,
|
||||
_ => 1
|
||||
}
|
||||
}
|
||||
fn first_byte(&self) -> Option<u8> {
|
||||
match *self {
|
||||
0 => None,
|
||||
_ => Some(*self)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToBytes for u64 {
|
||||
fn to_bytes(&self) -> Vec<u8> {
|
||||
let mut res= vec![];
|
||||
let count = self.to_bytes_len();
|
||||
for i in 0..count {
|
||||
let j = count - 1 - i;
|
||||
res.push((*self >> (j * 8)) as u8);
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
fn to_bytes_len(&self) -> usize { 8 - self.leading_zeros() as usize / 8 }
|
||||
}
|
||||
|
||||
macro_rules! impl_map_to_bytes {
|
||||
($from: ident, $to: ty) => {
|
||||
impl ToBytes for $from {
|
||||
fn to_bytes(&self) -> Vec<u8> { (*self as $to).to_bytes() }
|
||||
fn to_bytes_len(&self) -> usize { (*self as $to).to_bytes_len() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl_map_to_bytes!(usize, u64);
|
||||
impl_map_to_bytes!(u16, u64);
|
||||
impl_map_to_bytes!(u32, u64);
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum FromBytesError {
|
||||
UnexpectedEnd
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
pub type FromBytesResult<T> = Result<T, FromBytesError>;
|
||||
|
||||
/// implements "Sized", so the compiler can deducate the size
|
||||
/// of the return type
|
||||
pub trait FromBytes: Sized {
|
||||
fn from_bytes(bytes: &[u8]) -> FromBytesResult<Self>;
|
||||
}
|
||||
|
||||
impl FromBytes for String {
|
||||
fn from_bytes(bytes: &[u8]) -> FromBytesResult<String> {
|
||||
Ok(::std::str::from_utf8(bytes).unwrap().to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl FromBytes for u8 {
|
||||
fn from_bytes(bytes: &[u8]) -> FromBytesResult<u8> {
|
||||
match bytes.len() {
|
||||
0 => Ok(0),
|
||||
_ => Ok(bytes[0])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromBytes for u64 {
|
||||
fn from_bytes(bytes: &[u8]) -> FromBytesResult<u64> {
|
||||
match bytes.len() {
|
||||
0 => Ok(0),
|
||||
l => {
|
||||
let mut res = 0u64;
|
||||
for i in 0..l {
|
||||
let shift = (l - 1 - i) * 8;
|
||||
res = res + ((bytes[i] as u64) << shift);
|
||||
}
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_map_from_bytes {
|
||||
($from: ident, $to: ident) => {
|
||||
impl FromBytes for $from {
|
||||
fn from_bytes(bytes: &[u8]) -> FromBytesResult<$from> {
|
||||
$to::from_bytes(bytes).map(| x | { x as $from })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl_map_from_bytes!(usize, u64);
|
||||
impl_map_from_bytes!(u16, u64);
|
||||
impl_map_from_bytes!(u32, u64);
|
@ -1,3 +1,6 @@
|
||||
pub mod bytes;
|
||||
pub mod rlp;
|
||||
|
||||
#[test]
|
||||
fn it_works() {
|
||||
}
|
||||
|
254
src/rlp.rs
Normal file
254
src/rlp.rs
Normal file
@ -0,0 +1,254 @@
|
||||
//! Rlp serialization module
|
||||
|
||||
use std::fmt;
|
||||
use std::cell::Cell;
|
||||
use std::error::Error as StdError;
|
||||
use bytes::{FromBytes, FromBytesError};
|
||||
|
||||
/// rlp container
|
||||
#[derive(Debug)]
|
||||
pub struct Rlp<'a>{
|
||||
bytes: &'a [u8],
|
||||
cache: Cell<OffsetCache>
|
||||
}
|
||||
|
||||
/// rlp offset
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
struct OffsetCache {
|
||||
index: usize,
|
||||
offset: usize
|
||||
}
|
||||
|
||||
impl OffsetCache {
|
||||
fn new(index: usize, offset: usize) -> OffsetCache {
|
||||
OffsetCache { index: index, offset: offset }
|
||||
}
|
||||
}
|
||||
|
||||
/// stores basic information about item
|
||||
struct ItemInfo {
|
||||
prefix_len: usize,
|
||||
value_len: usize
|
||||
}
|
||||
|
||||
impl ItemInfo {
|
||||
fn new(prefix_len: usize, value_len: usize) -> ItemInfo {
|
||||
ItemInfo { prefix_len: prefix_len, value_len: value_len }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum DecoderError {
|
||||
FromBytesError(FromBytesError),
|
||||
RlpIsTooShort,
|
||||
RlpIsNotArray,
|
||||
BadRlp,
|
||||
}
|
||||
impl StdError for DecoderError {
|
||||
fn description(&self) -> &str { "builder error" }
|
||||
}
|
||||
|
||||
impl fmt::Display for DecoderError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
fmt::Debug::fmt(&self, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<FromBytesError> for DecoderError {
|
||||
fn from(err: FromBytesError) -> DecoderError { DecoderError::FromBytesError(err) }
|
||||
}
|
||||
|
||||
impl <'a>Rlp<'a> {
|
||||
/// returns new instance of `Rlp`
|
||||
pub fn new(bytes: &'a[u8]) -> Rlp<'a> {
|
||||
Rlp {
|
||||
bytes: bytes,
|
||||
cache: Cell::new(OffsetCache::new(usize::max_value(), 0))
|
||||
}
|
||||
}
|
||||
|
||||
/// get container subset at given index
|
||||
///
|
||||
/// paren container caches searched position
|
||||
pub fn at(&self, index: usize) -> Result<Rlp<'a>, DecoderError> {
|
||||
if !self.is_array() {
|
||||
return Err(DecoderError::RlpIsNotArray);
|
||||
}
|
||||
|
||||
// move to cached position if it's index is less or equal to
|
||||
// current search index, otherwise move to beginning of array
|
||||
let c = self.cache.get();
|
||||
let (mut bytes, to_skip) = match c.index <= index {
|
||||
true => (try!(Rlp::consume(self.bytes, c.offset)), index - c.index),
|
||||
false => (try!(self.consume_array_prefix()), index)
|
||||
};
|
||||
|
||||
// skip up to x items
|
||||
bytes = try!(Rlp::consume_items(bytes, to_skip));
|
||||
|
||||
// update the cache
|
||||
self.cache.set(OffsetCache::new(index, self.bytes.len() - bytes.len()));
|
||||
|
||||
// construct new rlp
|
||||
let found = try!(Rlp::item_info(bytes));
|
||||
Ok(Rlp::new(&bytes[0..found.prefix_len + found.value_len]))
|
||||
}
|
||||
|
||||
/// returns true if rlp is an array
|
||||
pub fn is_array(&self) -> bool {
|
||||
self.bytes.len() > 0 && self.bytes[0] >= 0xc0
|
||||
}
|
||||
|
||||
/// returns true if rlp is a value
|
||||
pub fn is_value(&self) -> bool {
|
||||
self.bytes.len() > 0 && self.bytes[0] <= 0xbf
|
||||
}
|
||||
|
||||
/// returns rlp iterator
|
||||
pub fn iter(&'a self) -> RlpIterator<'a> {
|
||||
self.into_iter()
|
||||
}
|
||||
|
||||
/// consumes first found prefix
|
||||
fn consume_array_prefix(&self) -> Result<&'a [u8], DecoderError> {
|
||||
let item = try!(Rlp::item_info(self.bytes));
|
||||
let bytes = try!(Rlp::consume(self.bytes, item.prefix_len));
|
||||
Ok(bytes)
|
||||
}
|
||||
|
||||
/// consumes fixed number of items
|
||||
fn consume_items(bytes: &'a [u8], items: usize) -> Result<&'a [u8], DecoderError> {
|
||||
let mut result = bytes;
|
||||
for _ in 0..items {
|
||||
let i = try!(Rlp::item_info(result));
|
||||
result = try!(Rlp::consume(result, (i.prefix_len + i.value_len)));
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
/// return first item info
|
||||
fn item_info(bytes: &[u8]) -> Result<ItemInfo, DecoderError> {
|
||||
let item = match bytes.first().map(|&x| x) {
|
||||
None => return Err(DecoderError::RlpIsTooShort),
|
||||
Some(0...0x7f) => ItemInfo::new(0, 1),
|
||||
Some(l @ 0x80...0xb7) => ItemInfo::new(1, l as usize - 0x80),
|
||||
Some(l @ 0xb8...0xbf) => {
|
||||
let len_of_len = l as usize - 0xb7;
|
||||
let prefix_len = 1 + len_of_len;
|
||||
let value_len = try!(usize::from_bytes(&bytes[1..prefix_len]));
|
||||
ItemInfo::new(prefix_len, value_len)
|
||||
}
|
||||
Some(l @ 0xc0...0xf7) => ItemInfo::new(1, l as usize - 0xc0),
|
||||
Some(l @ 0xf8...0xff) => {
|
||||
let len_of_len = l as usize - 0xf7;
|
||||
let prefix_len = 1 + len_of_len;
|
||||
let value_len = try!(usize::from_bytes(&bytes[1..prefix_len]));
|
||||
ItemInfo::new(prefix_len, value_len)
|
||||
},
|
||||
_ => return Err(DecoderError::BadRlp)
|
||||
};
|
||||
|
||||
match item.prefix_len + item.value_len <= bytes.len() {
|
||||
true => Ok(item),
|
||||
false => Err(DecoderError::RlpIsTooShort)
|
||||
}
|
||||
}
|
||||
|
||||
/// consumes slice prefix of length `len`
|
||||
fn consume(bytes: &'a [u8], len: usize) -> Result<&'a [u8], DecoderError> {
|
||||
match bytes.len() >= len {
|
||||
true => Ok(&bytes[len..]),
|
||||
false => Err(DecoderError::RlpIsTooShort)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// non-consuming rlp iterator
|
||||
pub struct RlpIterator<'a> {
|
||||
rlp: &'a Rlp<'a>,
|
||||
index: usize
|
||||
}
|
||||
|
||||
impl <'a> IntoIterator for &'a Rlp<'a> {
|
||||
type Item = Rlp<'a>;
|
||||
type IntoIter = RlpIterator<'a>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
RlpIterator { rlp: self, index: 0 }
|
||||
}
|
||||
}
|
||||
|
||||
impl <'a> Iterator for RlpIterator<'a> {
|
||||
type Item = Rlp<'a>;
|
||||
|
||||
fn next(&mut self) -> Option<Rlp<'a>> {
|
||||
let index = self.index;
|
||||
let result = self.rlp.at(index).ok();
|
||||
self.index += 1;
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use rlp;
|
||||
use rlp::Rlp;
|
||||
|
||||
#[test]
|
||||
fn rlp_at() {
|
||||
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_array());
|
||||
|
||||
let cat = rlp.at(0).unwrap();
|
||||
assert!(cat.is_value());
|
||||
assert_eq!(cat.bytes, &[0x83, b'c', b'a', b't']);
|
||||
|
||||
let dog = rlp.at(1).unwrap();
|
||||
assert!(dog.is_value());
|
||||
assert_eq!(dog.bytes, &[0x83, b'd', b'o', b'g']);
|
||||
|
||||
let cat_again = rlp.at(0).unwrap();
|
||||
assert!(cat_again.is_value());
|
||||
assert_eq!(cat_again.bytes, &[0x83, b'c', b'a', b't']);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rlp_at_err() {
|
||||
let data = vec![0xc8, 0x83, b'c', b'a', b't', 0x83, b'd', b'o'];
|
||||
{
|
||||
let rlp = Rlp::new(&data);
|
||||
assert!(rlp.is_array());
|
||||
|
||||
let cat_err = rlp.at(0).unwrap_err();
|
||||
assert_eq!(cat_err, rlp::DecoderError::RlpIsTooShort);
|
||||
|
||||
let dog_err = rlp.at(1).unwrap_err();
|
||||
assert_eq!(dog_err, rlp::DecoderError::RlpIsTooShort);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rlp_iter() {
|
||||
let data = vec![0xc8, 0x83, b'c', b'a', b't', 0x83, b'd', b'o', b'g'];
|
||||
{
|
||||
let rlp = Rlp::new(&data);
|
||||
let mut iter = rlp.iter();
|
||||
|
||||
let cat = iter.next().unwrap();
|
||||
assert!(cat.is_value());
|
||||
assert_eq!(cat.bytes, &[0x83, b'c', b'a', b't']);
|
||||
|
||||
let dog = iter.next().unwrap();
|
||||
assert!(dog.is_value());
|
||||
assert_eq!(dog.bytes, &[0x83, b'd', b'o', b'g']);
|
||||
|
||||
let cat_again = rlp.at(0).unwrap();
|
||||
assert!(cat_again.is_value());
|
||||
assert_eq!(cat_again.bytes, &[0x83, b'c', b'a', b't']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user