diff --git a/benches/rlp.rs b/benches/rlp.rs index 73af76bbe..f6a13f260 100644 --- a/benches/rlp.rs +++ b/benches/rlp.rs @@ -1,7 +1,7 @@ //! benchmarking for rlp //! should be started with: //! ```bash -//! multirust run nightly cargo bench +//! multirust run nightly cargo bench //! ``` #![feature(test)] @@ -16,80 +16,81 @@ use ethcore_util::uint::U256; #[bench] fn bench_stream_u64_value(b: &mut Bencher) { - b.iter( || { - //1029 - let mut stream = RlpStream::new(); - stream.append(&0x1023456789abcdefu64); - let _ = stream.out().unwrap(); - }); + b.iter(|| { + // u64 + let mut stream = RlpStream::new(); + stream.append(&0x1023456789abcdefu64); + let _ = stream.out().unwrap(); + }); } #[bench] fn bench_decode_u64_value(b: &mut Bencher) { - b.iter( || { - // 1029 - let data = vec![0x88, 0x10, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef]; - let rlp = Rlp::new(&data); - let _ = u64::decode(&rlp).unwrap(); - }); + b.iter(|| { + // u64 + let data = vec![0x88, 0x10, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef]; + let rlp = Rlp::new(&data); + let _ = u64::decode(&rlp).unwrap(); + }); } #[bench] fn bench_stream_u256_value(b: &mut Bencher) { - b.iter( || { - //u256 - let mut stream = RlpStream::new(); - stream.append(&U256::from_str("8090a0b0c0d0e0f00910203040506077000000000000000100000000000012f0").unwrap()); - let _ = stream.out().unwrap(); - }); + b.iter(|| { + // u256 + let mut stream = RlpStream::new(); + stream.append(&U256::from_str("8090a0b0c0d0e0f009102030405060770000000000000001000000000\ + 00012f0") + .unwrap()); + let _ = stream.out().unwrap(); + }); } #[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::decode(&rlp).unwrap(); - }); + 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::decode(&rlp).unwrap(); + }); } #[bench] fn bench_stream_nested_empty_lists(b: &mut Bencher) { - b.iter( || { - // [ [], [[]], [ [], [[]] ] ] - let mut stream = RlpStream::new_list(3); - stream.append_list(0); - stream.append_list(1).append_list(0); - stream.append_list(2).append_list(0).append_list(1).append_list(0); - let _ = stream.out().unwrap(); - }); + b.iter(|| { + // [ [], [[]], [ [], [[]] ] ] + let mut stream = RlpStream::new_list(3); + stream.append_list(0); + stream.append_list(1).append_list(0); + stream.append_list(2).append_list(0).append_list(1).append_list(0); + let _ = stream.out().unwrap(); + }); } #[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 = Decodable::decode(&rlp.at(0).unwrap()).unwrap(); - let _v1: Vec> = Decodable::decode(&rlp.at(1).unwrap()).unwrap(); - let nested_rlp = rlp.at(2).unwrap(); - let _v2a: Vec = Decodable::decode(&nested_rlp.at(0).unwrap()).unwrap(); - let _v2b: Vec> = Decodable::decode(&nested_rlp.at(1).unwrap()).unwrap(); - }); + b.iter(|| { + // [ [], [[]], [ [], [[]] ] ] + let data = vec![0xc7, 0xc0, 0xc1, 0xc0, 0xc3, 0xc0, 0xc1, 0xc0]; + let rlp = Rlp::new(&data); + let _v0: Vec = Decodable::decode(&rlp.at(0).unwrap()).unwrap(); + let _v1: Vec> = Decodable::decode(&rlp.at(1).unwrap()).unwrap(); + let nested_rlp = rlp.at(2).unwrap(); + let _v2a: Vec = Decodable::decode(&nested_rlp.at(0).unwrap()).unwrap(); + let _v2b: Vec> = Decodable::decode(&nested_rlp.at(1).unwrap()).unwrap(); + }); } #[bench] fn bench_stream_1000_empty_lists(b: &mut Bencher) { - b.iter( || { - let mut stream = RlpStream::new_list(1000); - for _ in 0..1000 { - stream.append_list(0); - } - let _ = stream.out().unwrap(); - }); + b.iter(|| { + let mut stream = RlpStream::new_list(1000); + for _ in 0..1000 { + stream.append_list(0); + } + let _ = stream.out().unwrap(); + }); } diff --git a/src/bloom.rs b/src/bloom.rs deleted file mode 100644 index fe619fae6..000000000 --- a/src/bloom.rs +++ /dev/null @@ -1,7 +0,0 @@ -use bytes::BytesConvertable; -// use hash::FixedHash; - -pub trait Bloomable { - fn shift_bloom(&mut self, bytes: &T) where T: BytesConvertable; - fn contains_bloom(&self, bytes: &T) -> bool where T: BytesConvertable; -} diff --git a/src/bytes.rs b/src/bytes.rs index 9521abdbd..c08c4ee2a 100644 --- a/src/bytes.rs +++ b/src/bytes.rs @@ -1,17 +1,49 @@ -//! To/From Bytes conversation for basic types -//! -//! Types implementing `ToBytes` and `FromBytes` traits -//! can be easily converted to and from bytes +//! Unified interfaces for bytes operations on basic types //! //! # Examples +//! ```rust +//! extern crate ethcore_util as util; +//! +//! fn bytes_convertable() { +//! use util::bytes::BytesConvertable; //! +//! let arr = [0; 5]; +//! let slice: &[u8] = arr.bytes(); +//! } +//! +//! fn to_bytes() { +//! use util::bytes::ToBytes; +//! +//! let a: Vec = "hello_world".to_bytes(); +//! let b: Vec = 400u32.to_bytes(); +//! let c: Vec = 0xffffffffffffffffu64.to_bytes(); +//! } +//! +//! fn from_bytes() { +//! use util::bytes::FromBytes; +//! +//! let a = String::from_bytes(&[b'd', b'o', b'g']); +//! let b = u8::from_bytes(&[0xfa]); +//! let c = u64::from_bytes(&[0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]); +//! } +//! +//! fn main() { +//! bytes_convertable(); +//! to_bytes(); +//! from_bytes(); +//! } +//! ``` use std::fmt; +use std::cmp::Ordering; use std::error::Error as StdError; use uint::{U128, U256}; +use hash::FixedHash; +/// Vector of bytes pub type Bytes = Vec; +/// Slice of bytes to underlying memory pub trait BytesConvertable { fn bytes(&self) -> &[u8]; } @@ -46,6 +78,8 @@ fn bytes_convertable() { assert_eq!([0u8; 0].bytes(), &[]); } +/// Converts given type to its shortest representation in bytes +/// /// TODO: optimise some conversations pub trait ToBytes { fn to_bytes(&self) -> Vec; @@ -141,9 +175,26 @@ macro_rules! impl_uint_to_bytes { impl_uint_to_bytes!(U256); impl_uint_to_bytes!(U128); +impl ToBytes for T where T: FixedHash { + fn to_bytes(&self) -> Vec { + let mut res: Vec = vec![]; + res.reserve(T::size()); + + unsafe { + use std::ptr; + ptr::copy(self.bytes().as_ptr(), res.as_mut_ptr(), T::size()); + res.set_len(T::size()); + } + + res + } +} + +/// Error returned when FromBytes conversation goes wrong #[derive(Debug, PartialEq, Eq)] pub enum FromBytesError { - UnexpectedEnd + DataIsTooShort, + DataIsTooLong } impl StdError for FromBytesError { @@ -156,10 +207,11 @@ impl fmt::Display for FromBytesError { } } +/// Alias for the result of FromBytes trait pub type FromBytesResult = Result; -/// implements "Sized", so the compiler can deducate the size -/// of the return type +/// Converts to given type from its bytes representation +/// /// TODO: check size of bytes before conversation and return appropriate error pub trait FromBytes: Sized { fn from_bytes(bytes: &[u8]) -> FromBytesResult; @@ -222,3 +274,22 @@ macro_rules! impl_uint_from_bytes { impl_uint_from_bytes!(U256); impl_uint_from_bytes!(U128); + +impl FromBytes for T where T: FixedHash { + fn from_bytes(bytes: &[u8]) -> FromBytesResult { + match bytes.len().cmp(&T::size()) { + Ordering::Less => return Err(FromBytesError::DataIsTooShort), + Ordering::Greater => return Err(FromBytesError::DataIsTooLong), + Ordering::Equal => () + }; + + unsafe { + use std::{mem, ptr}; + + let mut res: T = mem::uninitialized(); + ptr::copy(bytes.as_ptr(), res.mut_bytes().as_mut_ptr(), T::size()); + + Ok(res) + } + } +} diff --git a/src/hash.rs b/src/hash.rs index 98a76ad87..a0493f2bb 100644 --- a/src/hash.rs +++ b/src/hash.rs @@ -1,31 +1,30 @@ use std::str::FromStr; use std::fmt; use std::hash::{Hash, Hasher}; -use std::ops::{Index, IndexMut, BitOr}; +use std::ops::{Index, IndexMut, BitOr, BitAnd}; use rustc_serialize::hex::*; use error::EthcoreError; use rand::Rng; use rand::os::OsRng; use bytes::BytesConvertable; +use math::log2; /// types implementing FixedHash must be also BytesConvertable -pub trait FixedHash: BytesConvertable { +pub trait FixedHash: Sized + BytesConvertable { + fn new() -> Self; fn random() -> Self; fn randomize(&mut self); + fn size() -> usize; fn mut_bytes(&mut self) -> &mut [u8]; + fn shift_bloom<'a, T>(&'a mut self, b: &T) -> &'a mut Self where T: FixedHash; + fn bloom_part(&self, m: usize) -> T where T: FixedHash; + fn contains_bloom(&self, b: &T) -> bool where T: FixedHash; } macro_rules! impl_hash { ($from: ident, $size: expr) => { #[derive(Eq)] - pub struct $from (pub [u8; $size]); - - - impl $from { - fn new() -> $from { - $from([0; $size]) - } - } + pub struct $from ([u8; $size]); impl BytesConvertable for $from { fn bytes(&self) -> &[u8] { @@ -34,6 +33,10 @@ macro_rules! impl_hash { } impl FixedHash for $from { + fn new() -> $from { + $from([0; $size]) + } + fn random() -> $from { let mut hash = $from::new(); hash.randomize(); @@ -45,9 +48,68 @@ macro_rules! impl_hash { rng.fill_bytes(&mut self.0); } + fn size() -> usize { + $size + } + fn mut_bytes(&mut self) -> &mut [u8] { &mut self.0 } + + fn shift_bloom<'a, T>(&'a mut self, b: &T) -> &'a mut Self where T: FixedHash { + let bp: Self = b.bloom_part($size); + let new_self = &bp | self; + + // impl |= instead + + unsafe { + use std::{mem, ptr}; + ptr::copy(new_self.0.as_ptr(), self.0.as_mut_ptr(), mem::size_of::()); + } + + self + } + + fn bloom_part(&self, m: usize) -> T where T: FixedHash { + // numbers of bits + // TODO: move it to some constant + let p = 3; + + let bloom_bits = m * 8; + let mask = bloom_bits - 1; + let bloom_bytes = (log2(bloom_bits) + 7) / 8; + //println!("bb: {}", bloom_bytes); + + // must be a power of 2 + assert_eq!(m & (m - 1), 0); + // out of range + assert!(p * bloom_bytes <= $size); + + // return type + let mut ret = T::new(); + + // 'ptr' to out slice + let mut ptr = 0; + + // set p number of bits, + // p is equal 3 according to yellowpaper + for _ in 0..p { + let mut index = 0 as usize; + for _ in 0..bloom_bytes { + index = (index << 8) | self.0[ptr] as usize; + ptr += 1; + } + index &= mask; + ret.mut_bytes()[m - 1 - index / 8] |= 1 << (index % 8); + } + + ret + } + + fn contains_bloom(&self, b: &T) -> bool where T: FixedHash { + let bp: Self = b.bloom_part($size); + (&bp & self) == bp + } } impl FromStr for $from { @@ -120,6 +182,7 @@ macro_rules! impl_hash { } } + /// BitOr on references impl<'a> BitOr for &'a $from { type Output = $from; @@ -135,6 +198,7 @@ macro_rules! impl_hash { } } + /// Moving BitOr impl BitOr for $from { type Output = $from; @@ -143,6 +207,31 @@ macro_rules! impl_hash { } } + /// BitAnd on references + impl <'a> BitAnd for &'a $from { + type Output = $from; + + fn bitand(self, rhs: Self) -> Self::Output { + unsafe { + use std::mem; + let mut ret: $from = mem::uninitialized(); + for i in 0..$size { + ret.0[i] = self.0[i] & rhs.0[i]; + } + ret + } + } + } + + /// Moving BitAnd + impl BitAnd for $from { + type Output = $from; + + fn bitand(self, rhs: Self) -> Self::Output { + &self & &rhs + } + } + } } @@ -155,26 +244,55 @@ impl_hash!(H520, 65); impl_hash!(H1024, 128); impl_hash!(H2048, 256); -#[test] -fn hash() { - let h = H64([0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef]); - assert_eq!(H64::from_str("0123456789abcdef").unwrap(), h); - assert_eq!(format!("{}", h), "0123456789abcdef"); - assert_eq!(format!("{:?}", h), "0123456789abcdef"); - assert!(h == h); - assert!(h != H64([0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xee])); - assert!(h != H64([0; 8])); +#[cfg(test)] +mod tests { + use hash::*; + use std::str::FromStr; + + #[test] + fn hash() { + let h = H64([0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef]); + assert_eq!(H64::from_str("0123456789abcdef").unwrap(), h); + assert_eq!(format!("{}", h), "0123456789abcdef"); + assert_eq!(format!("{:?}", h), "0123456789abcdef"); + assert!(h == h); + assert!(h != H64([0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xee])); + assert!(h != H64([0; 8])); + } + + #[test] + fn hash_bitor() { + let a = H64([1; 8]); + let b = H64([2; 8]); + let c = H64([3; 8]); + + // borrow + assert_eq!(&a | &b, c); + + // move + assert_eq!(a | b, c); + } + + #[test] + fn shift_bloom() { + use sha3::Hashable; + + let bloom = H2048::from_str("00000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002020000000000000000000000000000000000000000000008000000001000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").unwrap(); + let address = Address::from_str("ef2d6d194084c2de36e0dabfce45d046b37d1106").unwrap(); + let topic = H256::from_str("02c69be41d0b7e40352fc85be1cd65eb03d40ef8427a0ca4596b1ead9a00e9fc").unwrap(); + + let mut my_bloom = H2048::new(); + assert!(!my_bloom.contains_bloom(&address.sha3())); + assert!(!my_bloom.contains_bloom(&topic.sha3())); + + my_bloom.shift_bloom(&address.sha3()); + assert!(my_bloom.contains_bloom(&address.sha3())); + assert!(!my_bloom.contains_bloom(&topic.sha3())); + + my_bloom.shift_bloom(&topic.sha3()); + assert_eq!(my_bloom, bloom); + assert!(my_bloom.contains_bloom(&address.sha3())); + assert!(my_bloom.contains_bloom(&topic.sha3())); + } } -#[test] -fn hash_bitor() { - let a = H64([1; 8]); - let b = H64([2; 8]); - let c = H64([3; 8]); - - // borrow - assert_eq!(&a | &b, c); - - // move - assert_eq!(a | b, c); -} diff --git a/src/lib.rs b/src/lib.rs index cb03d00b7..1c8ad9ccc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,7 @@ +//! Ethcore-util library +//! +//! TODO: check reexports + extern crate rustc_serialize; extern crate mio; extern crate rand; @@ -18,9 +22,11 @@ pub mod hashdb; pub mod memorydb; pub mod overlaydb; pub mod bloom; +pub mod math; //pub mod network; +// reexports pub use std::str::FromStr; pub use hash::*; pub use sha3::*; diff --git a/src/math.rs b/src/math.rs new file mode 100644 index 000000000..ba039f3d0 --- /dev/null +++ b/src/math.rs @@ -0,0 +1,10 @@ +/// log2 +pub fn log2(x: usize) -> u32 { + if x <= 1 { + return 0; + } + + let n = x.leading_zeros(); + ::std::mem::size_of::() as u32 * 8 - n +} + diff --git a/src/rlp.rs b/src/rlp.rs index ee74c3a33..4b6df2c6e 100644 --- a/src/rlp.rs +++ b/src/rlp.rs @@ -7,38 +7,75 @@ //! //! ```rust //! extern crate ethcore_util; -//! use ethcore_util::rlp::{RlpStream}; +//! use ethcore_util::rlp::{Rlp, RlpStream, Decodable}; //! //! fn encode_value() { -//! // 1029 -//! let mut stream = RlpStream::new(); -//! stream.append(&1029u32); -//! let out = stream.out().unwrap(); -//! assert_eq!(out, vec![0x82, 0x04, 0x05]); +//! // 1029 +//! let mut stream = RlpStream::new(); +//! stream.append(&1029u32); +//! let out = stream.out().unwrap(); +//! assert_eq!(out, vec![0x82, 0x04, 0x05]); //! } //! //! fn encode_list() { -//! // [ "cat", "dog" ] -//! let mut stream = RlpStream::new_list(2); -//! stream.append(&"cat").append(&"dog"); -//! let out = stream.out().unwrap(); -//! assert_eq!(out, vec![0xc8, 0x83, b'c', b'a', b't', 0x83, b'd', b'o', b'g']); +//! // [ "cat", "dog" ] +//! let mut stream = RlpStream::new_list(2); +//! stream.append(&"cat").append(&"dog"); +//! let out = stream.out().unwrap(); +//! assert_eq!(out, vec![0xc8, 0x83, b'c', b'a', b't', 0x83, b'd', b'o', b'g']); //! } //! //! fn encode_list2() { -//! // [ [], [[]], [ [], [[]] ] ] -//! let mut stream = RlpStream::new_list(3); -//! stream.append_list(0); -//! 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]); +//! // [ [], [[]], [ [], [[]] ] ] +//! let mut stream = RlpStream::new_list(3); +//! stream.append_list(0); +//! 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]); +//! } +//! +//! fn decode_value() { +//! // 0x102456789abcdef +//! let data = vec![0x88, 0x10, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef]; +//! let rlp = Rlp::new(&data); +//! let _ = u64::decode(&rlp).unwrap(); +//! } +//! +//! fn decode_string() { +//! // "cat" +//! let data = vec![0x83, b'c', b'a', b't']; +//! let rlp = Rlp::new(&data); +//! let _ = String::decode(&rlp).unwrap(); +//! } +//! +//! fn decode_list() { +//! // ["cat", "dog"] +//! let data = vec![0xc8, 0x83, b'c', b'a', b't', 0x83, b'd', b'o', b'g']; +//! let rlp = Rlp::new(&data); +//! let _ : Vec = Decodable::decode(&rlp).unwrap(); +//! } +//! +//! fn decode_list2() { +//! // [ [], [[]], [ [], [[]] ] ] +//! let data = vec![0xc7, 0xc0, 0xc1, 0xc0, 0xc3, 0xc0, 0xc1, 0xc0]; +//! let rlp = Rlp::new(&data); +//! let _v0: Vec = Decodable::decode(&rlp.at(0).unwrap()).unwrap(); +//! let _v1: Vec> = Decodable::decode(&rlp.at(1).unwrap()).unwrap(); +//! let nested_rlp = rlp.at(2).unwrap(); +//! let _v2a: Vec = Decodable::decode(&nested_rlp.at(0).unwrap()).unwrap(); +//! let _v2b: Vec> = Decodable::decode(&nested_rlp.at(1).unwrap()).unwrap(); //! } //! //! fn main() { -//! encode_value(); -//! encode_list(); -//! encode_list2(); +//! encode_value(); +//! encode_list(); +//! encode_list2(); +//! +//! decode_value(); +//! decode_string(); +//! decode_list(); +//! decode_list2(); //! } //! ``` //! @@ -711,6 +748,19 @@ mod tests { run_encode_tests(tests); } + #[test] + fn encode_address() { + use hash::*; + + let tests = vec![ + ETestPair(Address::from_str("ef2d6d194084c2de36e0dabfce45d046b37d1106").unwrap(), + vec![0x94, 0xef, 0x2d, 0x6d, 0x19, 0x40, 0x84, 0xc2, 0xde, + 0x36, 0xe0, 0xda, 0xbf, 0xce, 0x45, 0xd0, 0x46, + 0xb3, 0x7d, 0x11, 0x06]) + ]; + run_encode_tests(tests); + } + #[test] fn encode_vector_u8() { let tests = vec![ @@ -854,6 +904,19 @@ mod tests { run_decode_tests(tests); } + #[test] + fn decode_address() { + use hash::*; + + let tests = vec![ + DTestPair(Address::from_str("ef2d6d194084c2de36e0dabfce45d046b37d1106").unwrap(), + vec![0x94, 0xef, 0x2d, 0x6d, 0x19, 0x40, 0x84, 0xc2, 0xde, + 0x36, 0xe0, 0xda, 0xbf, 0xce, 0x45, 0xd0, 0x46, + 0xb3, 0x7d, 0x11, 0x06]) + ]; + run_decode_tests(tests); + } + #[test] fn decode_vector_u8() { let tests = vec![