diff --git a/src/rlp.rs b/src/rlp.rs index a93bb14d3..12d94f4a1 100644 --- a/src/rlp.rs +++ b/src/rlp.rs @@ -86,6 +86,7 @@ pub enum DecoderError { FromBytesError(FromBytesError), RlpIsTooShort, RlpExpectedToBeList, + RlpExpectedToBeValue, BadRlp, } impl StdError for DecoderError { @@ -233,6 +234,61 @@ impl <'a> Iterator for RlpIterator<'a> { } } +/// shortcut function to decode a Rlp `&[u8]` into an object +pub fn decode(bytes: &[u8]) -> Result where T: Decodable { + let rlp = Rlp::new(bytes); + T::decode(&rlp) +} + +pub trait Decodable: Sized { + fn decode(rlp: &Rlp) -> Result; +} + +impl Decodable for T where T: FromBytes { + fn decode(rlp: &Rlp) -> Result { + match rlp.is_value() { + true => BasicDecoder::read_value(rlp.bytes), + false => Err(DecoderError::RlpExpectedToBeValue) + } + } +} + +impl Decodable for Vec where T: Decodable { + fn decode(rlp: &Rlp) -> Result { + match rlp.is_list() { + true => rlp.iter().map(|rlp| T::decode(&rlp)).collect(), + false => Err(DecoderError::RlpExpectedToBeValue) + } + } +} + +trait Decoder { + fn read_value(bytes: &[u8]) -> Result where T: FromBytes; +} + +struct BasicDecoder; + +impl Decoder for BasicDecoder { + fn read_value(bytes: &[u8]) -> Result where T: FromBytes { + match bytes.first().map(|&x| x) { + // rlp is too short + None => Err(DecoderError::RlpIsTooShort), + // single byt value + Some(l @ 0...0x7f) => Ok(try!(T::from_bytes(&[l]))), + // 0-55 bytes + Some(l @ 0x80...0xb7) => Ok(try!(T::from_bytes(&bytes[1..(1 + l as usize - 0x80)]))), + // longer than 55 bytes + Some(l @ 0xb8...0xbf) => { + let len_of_len = l as usize - 0xb7; + let begin_of_value = 1 as usize + len_of_len; + let len = try!(usize::from_bytes(&bytes[1..begin_of_value])); + Ok(try!(T::from_bytes(&bytes[begin_of_value..begin_of_value + len]))) + }, + _ => Err(DecoderError::BadRlp) + } + } +} + #[derive(Debug)] struct ListInfo { position: usize, @@ -465,8 +521,9 @@ impl Encoder for BasicEncoder { #[cfg(test)] mod tests { + use std::{fmt, cmp}; use rlp; - use rlp::{Rlp, RlpStream}; + use rlp::{Rlp, RlpStream, Decodable}; #[test] fn rlp_at() { @@ -474,18 +531,23 @@ mod tests { { let rlp = Rlp::new(&data); assert!(rlp.is_list()); + let animals = as rlp::Decodable>::decode(&rlp).unwrap(); + assert_eq!(animals, vec!["cat".to_string(), "dog".to_string()]); let cat = rlp.at(0).unwrap(); assert!(cat.is_value()); assert_eq!(cat.bytes, &[0x83, b'c', b'a', b't']); + assert_eq!(String::decode(&cat).unwrap(), "cat".to_string()); let dog = rlp.at(1).unwrap(); assert!(dog.is_value()); assert_eq!(dog.bytes, &[0x83, b'd', b'o', b'g']); + assert_eq!(String::decode(&dog).unwrap(), "dog".to_string()); let cat_again = rlp.at(0).unwrap(); assert!(cat_again.is_value()); assert_eq!(cat_again.bytes, &[0x83, b'c', b'a', b't']); + assert_eq!(String::decode(&cat_again).unwrap(), "cat".to_string()); } } @@ -648,10 +710,118 @@ mod tests { fn rlp_stream_list() { let mut stream = RlpStream::new_list(3); stream.append_list(0); - stream.append_list(1).append(&vec![] as &Vec); + stream.append_list(1).append_list(0); stream.append_list(2).append_list(0).append_list(1).append_list(0); let out = stream.out().unwrap(); assert_eq!(out, vec![0xc7, 0xc0, 0xc1, 0xc0, 0xc3, 0xc0, 0xc1, 0xc0]); } + + struct DTestPair(T, Vec) where T: rlp::Decodable + fmt::Debug + cmp::Eq; + + fn run_decode_tests(tests: Vec>) where T: rlp::Decodable + fmt::Debug + cmp::Eq { + for t in &tests { + let res: T = rlp::decode(&t.1).unwrap(); + assert_eq!(res, t.0); + } + } + + #[test] + fn decode_u8() { + let tests = vec![ + DTestPair(0u8, vec![0u8]), + DTestPair(15, vec![15]), + DTestPair(55, vec![55]), + DTestPair(56, vec![56]), + DTestPair(0x7f, vec![0x7f]), + DTestPair(0x80, vec![0x81, 0x80]), + DTestPair(0xff, vec![0x81, 0xff]), + ]; + run_decode_tests(tests); + } + + #[test] + fn decode_u16() { + let tests = vec![ + DTestPair(0u16, vec![0u8]), + DTestPair(0x100, vec![0x82, 0x01, 0x00]), + DTestPair(0xffff, vec![0x82, 0xff, 0xff]), + ]; + run_decode_tests(tests); + } + + #[test] + fn decode_u32() { + let tests = vec![ + DTestPair(0u32, vec![0u8]), + DTestPair(0x10000, vec![0x83, 0x01, 0x00, 0x00]), + DTestPair(0xffffff, vec![0x83, 0xff, 0xff, 0xff]), + ]; + run_decode_tests(tests); + } + + #[test] + fn decode_u64() { + let tests = vec![ + DTestPair(0u64, vec![0u8]), + DTestPair(0x1000000, vec![0x84, 0x01, 0x00, 0x00, 0x00]), + DTestPair(0xFFFFFFFF, vec![0x84, 0xff, 0xff, 0xff, 0xff]), + ]; + run_decode_tests(tests); + } + + #[test] + fn decode_str() { + let tests = vec![ + DTestPair("cat".to_string(), vec![0x83, b'c', b'a', b't']), + DTestPair("dog".to_string(), vec![0x83, b'd', b'o', b'g']), + DTestPair("Marek".to_string(), vec![0x85, b'M', b'a', b'r', b'e', b'k']), + DTestPair("".to_string(), vec![0x80]), + DTestPair("Lorem ipsum dolor sit amet, consectetur adipisicing elit".to_string(), + vec![0xb8, 0x38, b'L', b'o', b'r', b'e', b'm', b' ', b'i', + b'p', b's', b'u', b'm', b' ', b'd', b'o', b'l', b'o', b'r', + b' ', b's', b'i', b't', b' ', b'a', b'm', b'e', b't', b',', + b' ', b'c', b'o', b'n', b's', b'e', b'c', b't', b'e', b't', + b'u', b'r', b' ', b'a', b'd', b'i', b'p', b'i', b's', b'i', + b'c', b'i', b'n', b'g', b' ', b'e', b'l', b'i', b't']) + ]; + run_decode_tests(tests); + } + + #[test] + fn decode_vector_u8() { + let tests = vec![ + DTestPair(vec![] as Vec, vec![0xc0]), + DTestPair(vec![15u8], vec![0xc1, 0x0f]), + DTestPair(vec![1u8, 2, 3, 7, 0xff], vec![0xc6, 1, 2, 3, 7, 0x81, 0xff]), + ]; + run_decode_tests(tests); + } + + #[test] + fn decode_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]), + ]; + run_decode_tests(tests); + } + + #[test] + fn decode_vector_str() { + let tests = vec![ + DTestPair(vec!["cat".to_string(), "dog".to_string()], vec![0xc8, 0x83, b'c', b'a', b't', 0x83, b'd', b'o', b'g']) + ]; + run_decode_tests(tests); + } + + #[test] + fn decode_vector_of_vectors_str() { + let tests = vec![ + DTestPair(vec![vec!["cat".to_string()]], vec![0xc5, 0xc4, 0x83, b'c', b'a', b't']) + ]; + run_decode_tests(tests); + } }