diff --git a/Cargo.lock b/Cargo.lock index 596c32060..50e847da2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -548,6 +548,7 @@ dependencies = [ "bigint 4.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "heapsize 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", + "plain_hasher 0.1.0", "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2214,6 +2215,13 @@ name = "pkg-config" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "plain_hasher" +version = "0.1.0" +dependencies = [ + "crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "podio" version = "0.1.5" diff --git a/util/bigint/Cargo.toml b/util/bigint/Cargo.toml index 36a272f8c..fcca56078 100644 --- a/util/bigint/Cargo.toml +++ b/util/bigint/Cargo.toml @@ -13,6 +13,7 @@ rustc-hex = "1.0" rand = "0.3.12" libc = "0.2" heapsize = { version = "0.4", optional = true } +plain_hasher = { path = "../plain_hasher" } [features] x64asm_arithmetic=[] diff --git a/util/bigint/src/hash.rs b/util/bigint/src/hash.rs index ca6adf0e7..316062bc4 100644 --- a/util/bigint/src/hash.rs +++ b/util/bigint/src/hash.rs @@ -16,6 +16,7 @@ use std::collections::{HashMap, HashSet}; use rand::{Rand, Rng}; use rand::os::OsRng; use rustc_hex::{FromHex, FromHexError}; +use plain_hasher::PlainHasher; use bigint::U256; use libc::{c_void, memcmp}; @@ -446,41 +447,6 @@ impl_hash!(H2048, 256); known_heap_size!(0, H32, H64, H128, H160, H256, H264, H512, H520, H1024, H2048); // Specialized HashMap and HashSet -/// Hasher that just takes 8 bytes of the provided value. -/// May only be used for keys which are 32 bytes. -pub struct PlainHasher { - prefix: [u8; 8], - _marker: [u64; 0], // for alignment -} - -impl Default for PlainHasher { - #[inline] - fn default() -> PlainHasher { - PlainHasher { - prefix: [0; 8], - _marker: [0; 0], - } - } -} - -impl Hasher for PlainHasher { - #[inline] - fn finish(&self) -> u64 { - unsafe { ::std::mem::transmute(self.prefix) } - } - - #[inline] - fn write(&mut self, bytes: &[u8]) { - debug_assert!(bytes.len() == 32); - - for quarter in bytes.chunks(8) { - for (x, y) in self.prefix.iter_mut().zip(quarter) { - *x ^= *y - } - } - } -} - /// Specialized version of `HashMap` with H256 keys and fast hashing function. pub type H256FastMap = HashMap>; /// Specialized version of `HashSet` with H256 keys and fast hashing function. diff --git a/util/bigint/src/lib.rs b/util/bigint/src/lib.rs index 7a85de6a1..1b1b5d488 100644 --- a/util/bigint/src/lib.rs +++ b/util/bigint/src/lib.rs @@ -14,6 +14,7 @@ extern crate rand; extern crate rustc_hex; extern crate bigint; extern crate libc; +extern crate plain_hasher; #[cfg(feature="heapsizeof")] #[macro_use] diff --git a/util/plain_hasher/Cargo.toml b/util/plain_hasher/Cargo.toml new file mode 100644 index 000000000..0ed45ba36 --- /dev/null +++ b/util/plain_hasher/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "plain_hasher" +description = "Hasher for 32-bit keys." +version = "0.1.0" +authors = ["Parity Technologies "] +license = "MIT" +keywords = ["hash", "hasher"] +categories = ["no-std"] +homepage = "https://github.com/paritytech/plain_hasher" + +[dependencies] +crunchy = "0.1.6" diff --git a/util/plain_hasher/benches/bench.rs b/util/plain_hasher/benches/bench.rs new file mode 100644 index 000000000..e7e8570ab --- /dev/null +++ b/util/plain_hasher/benches/bench.rs @@ -0,0 +1,33 @@ +#![feature(test)] + +extern crate test; +extern crate plain_hasher; + +use std::hash::Hasher; +use std::collections::hash_map::DefaultHasher; +use test::{Bencher, black_box}; +use plain_hasher::PlainHasher; + +#[bench] +fn write_plain_hasher(b: &mut Bencher) { + b.iter(|| { + let n: u8 = black_box(100); + (0..n).fold(PlainHasher::default(), |mut old, new| { + let bb = black_box([new; 32]); + old.write(&bb as &[u8]); + old + }); + }); +} + +#[bench] +fn write_default_hasher(b: &mut Bencher) { + b.iter(|| { + let n: u8 = black_box(100); + (0..n).fold(DefaultHasher::default(), |mut old, new| { + let bb = black_box([new; 32]); + old.write(&bb as &[u8]); + old + }); + }); +} diff --git a/util/plain_hasher/src/lib.rs b/util/plain_hasher/src/lib.rs new file mode 100644 index 000000000..b16596363 --- /dev/null +++ b/util/plain_hasher/src/lib.rs @@ -0,0 +1,55 @@ +#![no_std] +#[macro_use] +extern crate crunchy; + +use core::{hash, mem}; + +/// Hasher that just takes 8 bytes of the provided value. +/// May only be used for keys which are 32 bytes. +#[derive(Default)] +pub struct PlainHasher { + prefix: u64, +} + +impl hash::Hasher for PlainHasher { + #[inline] + fn finish(&self) -> u64 { + self.prefix + } + + #[inline] + #[allow(unused_assignments)] + fn write(&mut self, bytes: &[u8]) { + debug_assert!(bytes.len() == 32); + + unsafe { + let mut bytes_ptr = bytes.as_ptr(); + let prefix_u8: &mut [u8; 8] = mem::transmute(&mut self.prefix); + let mut prefix_ptr = prefix_u8.as_mut_ptr(); + + unroll! { + for _i in 0..8 { + *prefix_ptr ^= (*bytes_ptr ^ *bytes_ptr.offset(8)) ^ (*bytes_ptr.offset(16) ^ *bytes_ptr.offset(24)); + + bytes_ptr = bytes_ptr.offset(1); + prefix_ptr = prefix_ptr.offset(1); + } + } + } + } +} + +#[cfg(test)] +mod tests { + use core::hash::Hasher; + use super::PlainHasher; + + #[test] + fn it_works() { + let mut bytes = [32u8; 32]; + bytes[0] = 15; + let mut hasher = PlainHasher::default(); + hasher.write(&bytes); + assert_eq!(hasher.prefix, 47); + } +}