From c0efa326c900cdbc5a5d0639eb4b1b9edc8e1936 Mon Sep 17 00:00:00 2001 From: Tom Kaitchuck Date: Mon, 18 Oct 2021 23:35:44 -0700 Subject: [PATCH 01/24] Test example Signed-off-by: Tom Kaitchuck --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 9964a7c..52ce8fc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,7 +19,7 @@ //! ``` //! For convinence wrappers called `AHashMap` and `AHashSet` are also provided. //! These to the same thing with slightly less typing. -//! ```ignore +//! ``` //! use ahash::AHashMap; //! //! let mut map: AHashMap = AHashMap::with_capacity(4); From 3b9d761d7f23348593c9499472774cc43363a918 Mon Sep 17 00:00:00 2001 From: Amanieu d'Antras Date: Fri, 22 Oct 2021 06:07:21 +0100 Subject: [PATCH 02/24] Use a runtime-rng feature flag instead of using OS detection (#82) * Use a runtime-rng feature flag instead of using OS detection * Use --no-default-features on WASM * Update Cargo.toml Co-authored-by: bl-ue <54780737+bl-ue@users.noreply.github.com> Co-authored-by: Tom Kaitchuck Co-authored-by: bl-ue <54780737+bl-ue@users.noreply.github.com> --- .github/workflows/rust.yml | 2 +- Cargo.toml | 22 +++++++++++----------- build.rs | 21 --------------------- 3 files changed, 12 insertions(+), 33 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index ba596bf..e2c51ca 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -148,4 +148,4 @@ jobs: - uses: actions-rs/cargo@v1 with: command: check - args: --target wasm32-unknown-unknown + args: --target wasm32-unknown-unknown --no-default-features diff --git a/Cargo.toml b/Cargo.toml index 4b56472..673b159 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,15 +22,18 @@ bench = true doc = true [features] -default = ["std"] +default = ["std", "runtime-rng"] # Enabling this will enable `AHashMap` and `AHashSet`. std = [] -# This is an alternitive to runtime key generation which does compile time key generation if getrandom is not available. -# (If getrandom is available this does nothing.) -# If this is on (and getrandom is off) it implies the produced binary will not be identical. -# If this is disabled and gerrandom is unavailable constant keys are used. +# Runtime random key generation using getrandom. +runtime-rng = ["getrandom", "once_cell"] + +# This is an alternative to runtime key generation which does compile time key generation if runtime-rng is not available. +# (If runtime-rng is available this does nothing.) +# If this is on (and runtime-rng is off) it implies the produced binary will not be identical. +# If this is disabled and runtime-rng is unavailable constant keys are used. compile-time-rng = ["const-random"] [[bench]] @@ -64,12 +67,9 @@ codegen-units = 1 [build-dependencies] version_check = "0.9" -[target.'cfg(any(target_os = "linux", target_os = "android", target_os = "windows", target_os = "macos", target_os = "ios", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd", target_os = "dragonfly", target_os = "solaris", target_os = "illumos", target_os = "fuchsia", target_os = "redox", target_os = "cloudabi", target_os = "haiku", target_os = "vxworks", target_os = "emscripten", target_os = "wasi"))'.dependencies] -getrandom = { version = "0.2.3" } -const-random = { version = "0.1.12", optional = true } -serde = { version = "1.0.117", optional = true } - -[target.'cfg(not(any(target_os = "linux", target_os = "android", target_os = "windows", target_os = "macos", target_os = "ios", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd", target_os = "dragonfly", target_os = "solaris", target_os = "illumos", target_os = "fuchsia", target_os = "redox", target_os = "cloudabi", target_os = "haiku", target_os = "vxworks", target_os = "emscripten", target_os = "wasi")))'.dependencies] +[dependencies] +once_cell = { version = "1.8", default-features = false, features = ["unstable", "alloc"], optional = true } +getrandom = { version = "0.2.3", optional = true } const-random = { version = "0.1.12", optional = true } serde = { version = "1.0.117", optional = true } diff --git a/build.rs b/build.rs index 8be4964..c119ef7 100644 --- a/build.rs +++ b/build.rs @@ -10,27 +10,6 @@ fn main() { println!("cargo:rustc-cfg=feature=\"stdsimd\""); } } - let os = env::var("CARGO_CFG_TARGET_OS").expect("CARGO_CFG_TARGET_OS was not set"); - if os.eq_ignore_ascii_case("linux") - || os.eq_ignore_ascii_case("android") - || os.eq_ignore_ascii_case("windows") - || os.eq_ignore_ascii_case("macos") - || os.eq_ignore_ascii_case("ios") - || os.eq_ignore_ascii_case("freebsd") - || os.eq_ignore_ascii_case("openbsd") - || os.eq_ignore_ascii_case("dragonfly") - || os.eq_ignore_ascii_case("solaris") - || os.eq_ignore_ascii_case("illumos") - || os.eq_ignore_ascii_case("fuchsia") - || os.eq_ignore_ascii_case("redox") - || os.eq_ignore_ascii_case("cloudabi") - || os.eq_ignore_ascii_case("haiku") - || os.eq_ignore_ascii_case("vxworks") - || os.eq_ignore_ascii_case("emscripten") - || os.eq_ignore_ascii_case("wasi") - { - println!("cargo:rustc-cfg=feature=\"runtime-rng\""); - } let arch = env::var("CARGO_CFG_TARGET_ARCH").expect("CARGO_CFG_TARGET_ARCH was not set"); if arch.eq_ignore_ascii_case("x86_64") || arch.eq_ignore_ascii_case("aarch64") From 7a2ea9a8f1681a00b8887013bcb582bf57487bdf Mon Sep 17 00:00:00 2001 From: Tom Kaitchuck Date: Thu, 21 Oct 2021 22:38:24 -0700 Subject: [PATCH 03/24] Use cfg-if to simplify conditions Signed-off-by: Tom Kaitchuck --- Cargo.toml | 1 + src/lib.rs | 47 ++++----- src/random_state.rs | 248 +++++++++++++++++++++++--------------------- 3 files changed, 153 insertions(+), 143 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 673b159..5fb8ca3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -72,6 +72,7 @@ once_cell = { version = "1.8", default-features = false, features = ["unstable", getrandom = { version = "0.2.3", optional = true } const-random = { version = "0.1.12", optional = true } serde = { version = "1.0.117", optional = true } +cfg-if = "1.0" [target.'cfg(not(all(target_arch = "arm", target_os = "none")))'.dependencies] once_cell = { version = "1.8", default-features = false, features = ["alloc"] } diff --git a/src/lib.rs b/src/lib.rs index 52ce8fc..a4db4ea 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -34,43 +34,40 @@ #[macro_use] mod convert; +cfg_if::cfg_if! { + if #[cfg(any( + all(any(target_arch = "x86", target_arch = "x86_64"), target_feature = "aes", not(miri)), + all(any(target_arch = "arm", target_arch = "aarch64"), target_feature = "crypto", not(miri), feature = "stdsimd") + ))] { + mod aes_hash; + pub use crate::aes_hash::AHasher; + } else { + mod fallback_hash; + pub use crate::fallback_hash::AHasher; + } +} + +cfg_if::cfg_if! { + if #[cfg(feature = "std")] { + mod hash_map; + mod hash_set; + + pub use crate::hash_map::AHashMap; + pub use crate::hash_set::AHashSet; + } +} -#[cfg(any( - all(any(target_arch = "x86", target_arch = "x86_64"), target_feature = "aes", not(miri)), - all(any(target_arch = "arm", target_arch = "aarch64"), target_feature = "crypto", not(miri), feature = "stdsimd") -))] -mod aes_hash; -mod fallback_hash; #[cfg(test)] mod hash_quality_test; -#[cfg(feature = "std")] -mod hash_map; -#[cfg(feature = "std")] -mod hash_set; mod operations; mod random_state; mod specialize; -#[cfg(any( - all(any(target_arch = "x86", target_arch = "x86_64"), target_feature = "aes", not(miri)), - all(any(target_arch = "arm", target_arch = "aarch64"), target_feature = "crypto", not(miri), feature = "stdsimd") -))] -pub use crate::aes_hash::AHasher; - -#[cfg(not(any( - all(any(target_arch = "x86", target_arch = "x86_64"), target_feature = "aes", not(miri)), - all(any(target_arch = "arm", target_arch = "aarch64"), target_feature = "crypto", not(miri), feature = "stdsimd") -)))] -pub use crate::fallback_hash::AHasher; pub use crate::random_state::RandomState; pub use crate::specialize::CallHasher; -#[cfg(feature = "std")] -pub use crate::hash_map::AHashMap; -#[cfg(feature = "std")] -pub use crate::hash_set::AHashSet; use core::hash::BuildHasher; use core::hash::Hash; use core::hash::Hasher; diff --git a/src/random_state.rs b/src/random_state.rs index c3628bf..b9df223 100644 --- a/src/random_state.rs +++ b/src/random_state.rs @@ -1,62 +1,34 @@ -#[cfg(all(feature = "runtime-rng", not(all(feature = "compile-time-rng", test))))] -use crate::convert::Convert; -#[cfg(feature = "specialize")] -use crate::BuildHasherExt; - -#[cfg(any( - all(any(target_arch = "x86", target_arch = "x86_64"), target_feature = "aes", not(miri)), - all(any(target_arch = "arm", target_arch = "aarch64"), target_feature = "crypto", not(miri), feature = "stdsimd") -))] -pub use crate::aes_hash::*; - -#[cfg(not(any( - all(any(target_arch = "x86", target_arch = "x86_64"), target_feature = "aes", not(miri)), - all(any(target_arch = "arm", target_arch = "aarch64"), target_feature = "crypto", not(miri), feature = "stdsimd") -)))] -pub use crate::fallback_hash::*; - -#[cfg(all(feature = "compile-time-rng", any(not(feature = "runtime-rng"), test)))] -use const_random::const_random; +cfg_if::cfg_if! { + if #[cfg(any( + all(any(target_arch = "x86", target_arch = "x86_64"), target_feature = "aes", not(miri)), + all(any(target_arch = "arm", target_arch = "aarch64"), target_feature = "crypto", not(miri), feature = "stdsimd") + ))] { + pub use crate::aes_hash::*; + } else { + pub use crate::fallback_hash::*; + } +} +cfg_if::cfg_if! { + if #[cfg(feature = "specialize")]{ + use crate::BuildHasherExt; + use core::hash::Hash; + } +} +cfg_if::cfg_if! { + if #[cfg(feature = "std")] { + extern crate std as alloc; + } else { + extern crate alloc; + } +} + +use alloc::boxed::Box; +use core::sync::atomic::{AtomicUsize, Ordering}; use core::any::{Any, TypeId}; use core::fmt; use core::hash::BuildHasher; -#[cfg(feature = "specialize")] -use core::hash::Hash; use core::hash::Hasher; -#[cfg(not(feature = "std"))] -extern crate alloc; -#[cfg(feature = "std")] -extern crate std as alloc; - -use alloc::boxed::Box; -use core::sync::atomic::{AtomicUsize, Ordering}; -#[cfg(not(all(target_arch = "arm", target_os = "none")))] -use once_cell::race::OnceBox; - -#[cfg(any( - all(any(target_arch = "x86", target_arch = "x86_64"), target_feature = "aes", not(miri)), - all(any(target_arch = "arm", target_arch = "aarch64"), target_feature = "crypto", not(miri), feature = "stdsimd") -))] -use crate::aes_hash::*; -#[cfg(not(any( - all(any(target_arch = "x86", target_arch = "x86_64"), target_feature = "aes", not(miri)), - all(any(target_arch = "arm", target_arch = "aarch64"), target_feature = "crypto", not(miri), feature = "stdsimd") -)))] -use crate::fallback_hash::*; - -#[cfg(not(all(target_arch = "arm", target_os = "none")))] -static RAND_SOURCE: OnceBox> = OnceBox::new(); - -/// A supplier of Randomness used for different hashers. -/// See [RandomState.set_random_source]. -pub trait RandomSource { - - fn get_fixed_seeds(&self) -> &'static [[u64; 4]; 2]; - - fn gen_hasher_seed(&self) -> usize; - -} pub(crate) const PI: [u64; 4] = [ 0x243f_6a88_85a3_08d3, @@ -72,6 +44,79 @@ pub(crate) const PI2: [u64; 4] = [ 0x3f84_d5b5_b547_0917, ]; +cfg_if::cfg_if! { + if #[cfg(all(feature = "compile-time-rng", any(test, fuzzing)))] { + fn get_fixed_seeds() -> &'static [[u64; 4]; 2] { + use const_random::const_random; + + const RAND: [[u64; 4]; 2] = [ + [ + const_random!(u64), + const_random!(u64), + const_random!(u64), + const_random!(u64), + ], [ + const_random!(u64), + const_random!(u64), + const_random!(u64), + const_random!(u64), + ] + ]; + &RAND + } + } else if #[cfg(feature = "runtime-rng")] { + fn get_fixed_seeds() -> &'static [[u64; 4]; 2] { + use crate::convert::Convert; + + static SEEDS: OnceBox<[[u64; 4]; 2]> = OnceBox::new(); + + SEEDS.get_or_init(|| { + let mut result: [u8; 64] = [0; 64]; + getrandom::getrandom(&mut result).expect("getrandom::getrandom() failed."); + Box::new(result.convert()) + }) + } + } else if #[cfg(feature = "compile-time-rng")] { + fn get_fixed_seeds() -> &'static [[u64; 4]; 2] { + use const_random::const_random; + + const RAND: [[u64; 4]; 2] = [ + [ + const_random!(u64), + const_random!(u64), + const_random!(u64), + const_random!(u64), + ], [ + const_random!(u64), + const_random!(u64), + const_random!(u64), + const_random!(u64), + ] + ]; + &RAND + } + } else { + fn get_fixed_seeds() -> &'static [[u64; 4]; 2] { + &[PI, PI2] + } + } +} + +cfg_if::cfg_if! { + if #[cfg(not(all(target_arch = "arm", target_os = "none")))] { + use once_cell::race::OnceBox; + + static RAND_SOURCE: OnceBox> = OnceBox::new(); + } +} +/// A supplier of Randomness used for different hashers. +/// See [RandomState::set_random_source]. +pub trait RandomSource { + + fn gen_hasher_seed(&self) -> usize; + +} + struct DefaultRandomSource { counter: AtomicUsize, } @@ -92,40 +137,6 @@ impl DefaultRandomSource { impl RandomSource for DefaultRandomSource { - #[cfg(all(feature = "runtime-rng", not(all(feature = "compile-time-rng", test))))] - fn get_fixed_seeds(&self) -> &'static [[u64; 4]; 2] { - static SEEDS: OnceBox<[[u64; 4]; 2]> = OnceBox::new(); - - SEEDS.get_or_init(|| { - let mut result: [u8; 64] = [0; 64]; - getrandom::getrandom(&mut result).expect("getrandom::getrandom() failed."); - Box::new(result.convert()) - }) - } - - #[cfg(all(feature = "compile-time-rng", any(not(feature = "runtime-rng"), test)))] - fn get_fixed_seeds(&self) -> &'static [[u64; 4]; 2] { - const RAND: [[u64; 4]; 2] = [ - [ - const_random!(u64), - const_random!(u64), - const_random!(u64), - const_random!(u64), - ], [ - const_random!(u64), - const_random!(u64), - const_random!(u64), - const_random!(u64), - ] - ]; - &RAND - } - - #[cfg(all(not(feature = "runtime-rng"), not(feature = "compile-time-rng")))] - fn get_fixed_seeds(&self) -> &'static [[u64; 4]; 2] { - &[PI, PI2] - } - #[cfg(not(all(target_arch = "arm", target_os = "none")))] fn gen_hasher_seed(&self) -> usize { let stack = self as *const _ as usize; @@ -164,38 +175,39 @@ impl fmt::Debug for RandomState { } impl RandomState { - - /// Provides an optional way to manually supply a source of randomness for Hasher keys. - /// - /// The provided [RandomSource] will be used to be used as a source of randomness by [RandomState] to generate new states. - /// If this method is not invoked the standard source of randomness is used as described in the Readme. - /// - /// The source of randomness can only be set once, and must be set before the first RandomState is created. - /// If the source has already been specified `Err` is returned with a `bool` indicating if the set failed because - /// method was previously invoked (true) or if the default source is already being used (false). - #[cfg(not(all(target_arch = "arm", target_os = "none")))] - pub fn set_random_source(source: impl RandomSource + Send + Sync + 'static) -> Result<(), bool> { - RAND_SOURCE.set(Box::new(Box::new(source))).map_err(|s| s.as_ref().type_id() != TypeId::of::<&DefaultRandomSource>()) - } - - #[inline] - #[cfg(not(all(target_arch = "arm", target_os = "none")))] - fn get_src() -> &'static dyn RandomSource { - RAND_SOURCE.get_or_init(|| Box::new(Box::new(DefaultRandomSource::new()))).as_ref() - } - - #[inline] - #[cfg(all(target_arch = "arm", target_os = "none"))] - fn get_src() -> &'static dyn RandomSource { - static RAND_SOURCE: DefaultRandomSource = DefaultRandomSource::default(); - &RAND_SOURCE + cfg_if::cfg_if! { + if #[cfg(all(target_arch = "arm", target_os = "none"))] { + #[inline] + fn get_src() -> &'static dyn RandomSource { + static RAND_SOURCE: DefaultRandomSource = DefaultRandomSource::default(); + &RAND_SOURCE + } + } else { + /// Provides an optional way to manually supply a source of randomness for Hasher keys. + /// + /// The provided [RandomSource] will be used to be used as a source of randomness by [RandomState] to generate new states. + /// If this method is not invoked the standard source of randomness is used as described in the Readme. + /// + /// The source of randomness can only be set once, and must be set before the first RandomState is created. + /// If the source has already been specified `Err` is returned with a `bool` indicating if the set failed because + /// method was previously invoked (true) or if the default source is already being used (false). + #[cfg(not(all(target_arch = "arm", target_os = "none")))] + pub fn set_random_source(source: impl RandomSource + Send + Sync + 'static) -> Result<(), bool> { + RAND_SOURCE.set(Box::new(Box::new(source))).map_err(|s| s.as_ref().type_id() != TypeId::of::<&DefaultRandomSource>()) + } + + #[inline] + fn get_src() -> &'static dyn RandomSource { + RAND_SOURCE.get_or_init(|| Box::new(Box::new(DefaultRandomSource::new()))).as_ref() + } + } } /// Use randomly generated keys #[inline] pub fn new() -> RandomState { let src = Self::get_src(); - let fixed = src.get_fixed_seeds(); + let fixed = get_fixed_seeds(); Self::from_keys(&fixed[0], &fixed[1], src.gen_hasher_seed()) } @@ -204,7 +216,7 @@ impl RandomState { #[inline] pub fn generate_with(k0: u64, k1: u64, k2: u64, k3: u64) -> RandomState { let src = Self::get_src(); - let fixed = src.get_fixed_seeds(); + let fixed = get_fixed_seeds(); RandomState::from_keys(&fixed[0], &[k0, k1, k2, k3], src.gen_hasher_seed()) } @@ -228,7 +240,7 @@ impl RandomState { /// Internal. Used by Default. #[inline] pub(crate) fn with_fixed_keys() -> RandomState { - let [k0, k1, k2, k3] = Self::get_src().get_fixed_seeds()[0]; + let [k0, k1, k2, k3] = get_fixed_seeds()[0]; RandomState { k0, k1, k2, k3 } } @@ -237,7 +249,7 @@ impl RandomState { /// Note: This method does not require the provided seed to be strong. #[inline] pub fn with_seed(key: usize) -> RandomState { - let fixed = Self::get_src().get_fixed_seeds(); + let fixed = get_fixed_seeds(); RandomState::from_keys(&fixed[0], &fixed[1], key) } @@ -336,7 +348,7 @@ mod test { #[cfg(all(feature = "runtime-rng", not(all(feature = "compile-time-rng", test))))] #[test] fn test_not_pi() { - assert_ne!(PI, RandomState::get_src().get_fixed_seeds()[0]); + assert_ne!(PI, get_fixed_seeds()[0]); } #[cfg(all(feature = "compile-time-rng", any(not(feature = "runtime-rng"), test)))] From 0c484f4310663acad6d2644f33f18872c6a39163 Mon Sep 17 00:00:00 2001 From: Tom Kaitchuck Date: Fri, 22 Oct 2021 00:31:06 -0700 Subject: [PATCH 04/24] Require rng for RandomState::default and new. Signed-off-by: Tom Kaitchuck --- Cargo.toml | 7 +++--- src/lib.rs | 41 ++++++++++++++++++-------------- src/random_state.rs | 58 +++++++++++++++++++++------------------------ src/specialize.rs | 5 ++-- 4 files changed, 56 insertions(+), 55 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 5fb8ca3..3195373 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,10 +28,10 @@ default = ["std", "runtime-rng"] std = [] # Runtime random key generation using getrandom. -runtime-rng = ["getrandom", "once_cell"] +runtime-rng = ["getrandom"] # This is an alternative to runtime key generation which does compile time key generation if runtime-rng is not available. -# (If runtime-rng is available this does nothing.) +# (If runtime-rng is enabled this does nothing.) # If this is on (and runtime-rng is off) it implies the produced binary will not be identical. # If this is disabled and runtime-rng is unavailable constant keys are used. compile-time-rng = ["const-random"] @@ -68,14 +68,13 @@ codegen-units = 1 version_check = "0.9" [dependencies] -once_cell = { version = "1.8", default-features = false, features = ["unstable", "alloc"], optional = true } getrandom = { version = "0.2.3", optional = true } const-random = { version = "0.1.12", optional = true } serde = { version = "1.0.117", optional = true } cfg-if = "1.0" [target.'cfg(not(all(target_arch = "arm", target_os = "none")))'.dependencies] -once_cell = { version = "1.8", default-features = false, features = ["alloc"] } +once_cell = { version = "1.8", default-features = false, features = ["unstable", "alloc"] } [dev-dependencies] no-panic = "0.1.10" diff --git a/src/lib.rs b/src/lib.rs index a4db4ea..a6944cc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,24 +8,29 @@ //! //! aHash uses the hardware AES instruction on x86 processors to provide a keyed hash function. //! aHash is not a cryptographically secure hash. -//! -//! # Example -//! ``` -//! use ahash::{AHasher, RandomState}; -//! use std::collections::HashMap; -//! -//! let mut map: HashMap = HashMap::default(); -//! map.insert(12, 34); -//! ``` -//! For convinence wrappers called `AHashMap` and `AHashSet` are also provided. -//! These to the same thing with slightly less typing. -//! ``` -//! use ahash::AHashMap; -//! -//! let mut map: AHashMap = AHashMap::with_capacity(4); -//! map.insert(12, 34); -//! map.insert(56, 78); -//! ``` +//! +#![cfg_attr(any(feature = "compile-time-rng", feature = "runtime-rng"), doc = r##" +# Example +``` +use ahash::{AHasher, RandomState}; +use std::collections::HashMap; + +let mut map: HashMap = HashMap::default(); +map.insert(12, 34); +``` +"##)] +#![cfg_attr(feature = "std", doc = r##" +For convinence wrappers called `AHashMap` and `AHashSet` are also provided. +These to the same thing with slightly less typing. +``` +use ahash::AHashMap; + +let mut map: AHashMap = AHashMap::with_capacity(4); +map.insert(12, 34); +map.insert(56, 78); +``` +"##)] + #![deny(clippy::correctness, clippy::complexity, clippy::perf)] #![allow(clippy::pedantic, clippy::cast_lossless, clippy::unreadable_literal)] #![cfg_attr(all(not(test), not(feature = "std")), no_std)] diff --git a/src/random_state.rs b/src/random_state.rs index b9df223..dff9461 100644 --- a/src/random_state.rs +++ b/src/random_state.rs @@ -127,12 +127,6 @@ impl DefaultRandomSource { counter: AtomicUsize::new(&PI as *const _ as usize), } } - - const fn default() -> DefaultRandomSource { - DefaultRandomSource { - counter: AtomicUsize::new(PI[3] as usize), - } - } } impl RandomSource for DefaultRandomSource { @@ -179,7 +173,7 @@ impl RandomState { if #[cfg(all(target_arch = "arm", target_os = "none"))] { #[inline] fn get_src() -> &'static dyn RandomSource { - static RAND_SOURCE: DefaultRandomSource = DefaultRandomSource::default(); + static RAND_SOURCE: DefaultRandomSource = DefaultRandomSource::new(); &RAND_SOURCE } } else { @@ -205,6 +199,7 @@ impl RandomState { /// Use randomly generated keys #[inline] + #[cfg(any(feature = "compile-time-rng", feature = "runtime-rng"))] pub fn new() -> RandomState { let src = Self::get_src(); let fixed = get_fixed_seeds(); @@ -263,6 +258,7 @@ impl RandomState { } } +#[cfg(any(feature = "compile-time-rng", feature = "runtime-rng"))] impl Default for RandomState { #[inline] fn default() -> Self { @@ -278,26 +274,26 @@ impl BuildHasher for RandomState { /// [AHasher]s that will return different hashcodes, but [Hasher]s created from the same [BuildHasher] /// will generate the same hashes for the same input data. /// - /// # Examples - /// - /// ``` - /// use ahash::{AHasher, RandomState}; - /// use std::hash::{Hasher, BuildHasher}; - /// - /// let build_hasher = RandomState::new(); - /// let mut hasher_1 = build_hasher.build_hasher(); - /// let mut hasher_2 = build_hasher.build_hasher(); - /// - /// hasher_1.write_u32(1234); - /// hasher_2.write_u32(1234); - /// - /// assert_eq!(hasher_1.finish(), hasher_2.finish()); - /// - /// let other_build_hasher = RandomState::new(); - /// let mut different_hasher = other_build_hasher.build_hasher(); - /// different_hasher.write_u32(1234); - /// assert_ne!(different_hasher.finish(), hasher_1.finish()); - /// ``` + #[cfg_attr(any(feature = "compile-time-rng", feature = "runtime-rng"), doc = r##" # Examples +``` + use ahash::{AHasher, RandomState}; + use std::hash::{Hasher, BuildHasher}; + + let build_hasher = RandomState::new(); + let mut hasher_1 = build_hasher.build_hasher(); + let mut hasher_2 = build_hasher.build_hasher(); + + hasher_1.write_u32(1234); + hasher_2.write_u32(1234); + + assert_eq!(hasher_1.finish(), hasher_2.finish()); + + let other_build_hasher = RandomState::new(); + let mut different_hasher = other_build_hasher.build_hasher(); + different_hasher.write_u32(1234); + assert_ne!(different_hasher.finish(), hasher_1.finish()); +``` + "##)] /// [Hasher]: std::hash::Hasher /// [BuildHasher]: std::hash::BuildHasher /// [HashMap]: std::collections::HashMap @@ -340,8 +336,8 @@ mod test { #[test] fn test_unique() { - let a = RandomState::new(); - let b = RandomState::new(); + let a = RandomState::generate_with(1, 2, 3, 4); + let b = RandomState::generate_with(1, 2, 3, 4); assert_ne!(a.build_hasher().finish(), b.build_hasher().finish()); } @@ -354,13 +350,13 @@ mod test { #[cfg(all(feature = "compile-time-rng", any(not(feature = "runtime-rng"), test)))] #[test] fn test_not_pi_const() { - assert_ne!(PI, RandomState::get_src().get_fixed_seeds()[0]); + assert_ne!(PI, get_fixed_seeds()[0]); } #[cfg(all(not(feature = "runtime-rng"), not(feature = "compile-time-rng")))] #[test] fn test_pi() { - assert_eq!(PI, RandomState::get_src().get_fixed_seeds()[0]); + assert_eq!(PI, get_fixed_seeds()[0]); } #[test] diff --git a/src/specialize.rs b/src/specialize.rs index d94a4ee..7b48065 100644 --- a/src/specialize.rs +++ b/src/specialize.rs @@ -23,7 +23,8 @@ use alloc::vec::Vec; /// use ahash::RandomState; /// use ahash::CallHasher; /// -/// let hash_builder = RandomState::new(); +#[cfg_attr(any(feature = "compile-time-rng", feature = "runtime-rng"), doc = "let hash_builder = RandomState::new();")] +#[cfg_attr(not(any(feature = "compile-time-rng", feature = "runtime-rng")), doc = "let hash_builder = RandomState::generate_with(1, 2, 3, 4);")] /// //... /// let value: u32 = 17; /// let hash = u32::get_hash(&value, &hash_builder); @@ -34,7 +35,7 @@ use alloc::vec::Vec; /// /// use std::hash::BuildHasher; /// # use ahash::RandomState; /// # use ahash::CallHasher; -/// # let hash_builder = RandomState::new(); +/// # let hash_builder = RandomState::generate_with(1, 2, 3, 4); /// let bytes: [u8; 4] = [1, 2, 3, 4]; /// let hash = <[u8]>::get_hash(&bytes, &hash_builder); /// ``` From eb5ae12d66bebac2f641cac1bf250af654f708dd Mon Sep 17 00:00:00 2001 From: Tom Kaitchuck Date: Fri, 22 Oct 2021 00:41:47 -0700 Subject: [PATCH 05/24] Move multiple Signed-off-by: Tom Kaitchuck --- src/aes_hash.rs | 2 -- src/fallback_hash.rs | 5 +++-- src/operations.rs | 3 +++ 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/aes_hash.rs b/src/aes_hash.rs index 1c98582..fc3eeec 100644 --- a/src/aes_hash.rs +++ b/src/aes_hash.rs @@ -1,6 +1,4 @@ use crate::convert::*; -#[cfg(feature = "specialize")] -use crate::fallback_hash::MULTIPLE; use crate::operations::*; use crate::RandomState; use core::hash::Hasher; diff --git a/src/fallback_hash.rs b/src/fallback_hash.rs index aad9efc..d4004b6 100644 --- a/src/fallback_hash.rs +++ b/src/fallback_hash.rs @@ -1,12 +1,13 @@ use crate::convert::*; use crate::operations::folded_multiply; use crate::operations::read_small; +use crate::operations::MULTIPLE; use crate::random_state::PI; use crate::RandomState; use core::hash::Hasher; -///This constant come from Kunth's prng (Empirically it works better than those from splitmix32). -pub(crate) const MULTIPLE: u64 = 6364136223846793005; + + const ROT: u32 = 23; //17 /// A `Hasher` for hashing an arbitrary stream of bytes. diff --git a/src/operations.rs b/src/operations.rs index b71fd5a..409cf19 100644 --- a/src/operations.rs +++ b/src/operations.rs @@ -1,5 +1,8 @@ use crate::convert::*; +///This constant come from Kunth's prng (Empirically it works better than those from splitmix32). +pub(crate) const MULTIPLE: u64 = 6364136223846793005; + /// This is a constant with a lot of special properties found by automated search. /// See the unit tests below. (Below are alternative values) #[cfg(all(target_feature = "ssse3", not(miri)))] From e1c8594f180e7c0fb096d4961a5fd64224a40acf Mon Sep 17 00:00:00 2001 From: Tom Kaitchuck Date: Fri, 22 Oct 2021 00:42:51 -0700 Subject: [PATCH 06/24] Restore const defult for arm. Signed-off-by: Tom Kaitchuck --- src/random_state.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/random_state.rs b/src/random_state.rs index dff9461..b6fe5af 100644 --- a/src/random_state.rs +++ b/src/random_state.rs @@ -127,6 +127,13 @@ impl DefaultRandomSource { counter: AtomicUsize::new(&PI as *const _ as usize), } } + + #[cfg(all(target_arch = "arm", target_os = "none"))] + const fn default() -> DefaultRandomSource { + DefaultRandomSource { + counter: AtomicUsize::new(PI[3] as usize), + } + } } impl RandomSource for DefaultRandomSource { @@ -173,7 +180,7 @@ impl RandomState { if #[cfg(all(target_arch = "arm", target_os = "none"))] { #[inline] fn get_src() -> &'static dyn RandomSource { - static RAND_SOURCE: DefaultRandomSource = DefaultRandomSource::new(); + static RAND_SOURCE: DefaultRandomSource = DefaultRandomSource::default(); &RAND_SOURCE } } else { From 2613d0eaf7693015db6c82fd80fe0a77dd954fcc Mon Sep 17 00:00:00 2001 From: Tom Kaitchuck Date: Fri, 22 Oct 2021 00:49:55 -0700 Subject: [PATCH 07/24] Don't use runtime rng when fuzzing Signed-off-by: Tom Kaitchuck --- src/random_state.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/random_state.rs b/src/random_state.rs index b6fe5af..6908237 100644 --- a/src/random_state.rs +++ b/src/random_state.rs @@ -64,7 +64,7 @@ cfg_if::cfg_if! { ]; &RAND } - } else if #[cfg(feature = "runtime-rng")] { + } else if #[cfg(all(feature = "runtime-rng", not(fuzzing)))] { fn get_fixed_seeds() -> &'static [[u64; 4]; 2] { use crate::convert::Convert; From a4fd0f82106a66cdea0abcbb3ae64ab075602009 Mon Sep 17 00:00:00 2001 From: Tom Kaitchuck Date: Fri, 22 Oct 2021 00:50:28 -0700 Subject: [PATCH 08/24] Fix test imports on nightly Signed-off-by: Tom Kaitchuck --- src/lib.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index a6944cc..db310eb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -39,6 +39,9 @@ map.insert(56, 78); #[macro_use] mod convert; + +mod fallback_hash; + cfg_if::cfg_if! { if #[cfg(any( all(any(target_arch = "x86", target_arch = "x86_64"), target_feature = "aes", not(miri)), @@ -47,7 +50,6 @@ cfg_if::cfg_if! { mod aes_hash; pub use crate::aes_hash::AHasher; } else { - mod fallback_hash; pub use crate::fallback_hash::AHasher; } } From 8ab8118e26e04fd7318ae298d2dfc1f197d49d28 Mon Sep 17 00:00:00 2001 From: Tom Kaitchuck Date: Sat, 27 Nov 2021 19:46:04 -0800 Subject: [PATCH 09/24] Add support for hash_one Signed-off-by: Tom Kaitchuck --- src/lib.rs | 1 + src/random_state.rs | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index db310eb..88d6617 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -35,6 +35,7 @@ map.insert(56, 78); #![allow(clippy::pedantic, clippy::cast_lossless, clippy::unreadable_literal)] #![cfg_attr(all(not(test), not(feature = "std")), no_std)] #![cfg_attr(feature = "specialize", feature(min_specialization))] +#![cfg_attr(feature = "specialize", feature(build_hasher_simple_hash_one))] #![cfg_attr(feature = "stdsimd", feature(stdsimd))] #[macro_use] diff --git a/src/random_state.rs b/src/random_state.rs index 6908237..cf7aaea 100644 --- a/src/random_state.rs +++ b/src/random_state.rs @@ -308,6 +308,14 @@ impl BuildHasher for RandomState { fn build_hasher(&self) -> AHasher { AHasher::from_random_state(self) } + + #[cfg(feature = "specialize")] + #[inline] + fn hash_one(&self, x: T) -> u64 + where Self: Sized { + use crate::specialize::CallHasher; + T::get_hash(&x, self) + } } #[cfg(feature = "specialize")] From 5b86423e275de9184d127d7377662b1c38c9d1f5 Mon Sep 17 00:00:00 2001 From: Tom Kaitchuck Date: Sat, 27 Nov 2021 20:02:43 -0800 Subject: [PATCH 10/24] Move test to use hash_one Signed-off-by: Tom Kaitchuck --- src/lib.rs | 3 +-- src/specialize.rs | 24 +----------------------- tests/bench.rs | 10 ++++++---- tests/map_tests.rs | 6 ++++-- tests/nopanic.rs | 12 +++++++----- 5 files changed, 19 insertions(+), 36 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 88d6617..fe9fa98 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -74,8 +74,6 @@ mod specialize; pub use crate::random_state::RandomState; -pub use crate::specialize::CallHasher; - use core::hash::BuildHasher; use core::hash::Hash; use core::hash::Hasher; @@ -202,6 +200,7 @@ mod test { use crate::*; use std::collections::HashMap; use std::hash::Hash; + use crate::specialize::CallHasher; #[test] fn test_default_builder() { diff --git a/src/specialize.rs b/src/specialize.rs index 7b48065..05d335b 100644 --- a/src/specialize.rs +++ b/src/specialize.rs @@ -17,29 +17,7 @@ use alloc::vec::Vec; /// Provides a way to get an optimized hasher for a given data type. /// Rather than using a Hasher generically which can hash any value, this provides a way to get a specialized hash /// for a specific type. So this may be faster for primitive types. -/// # Example -/// ``` -/// use std::hash::BuildHasher; -/// use ahash::RandomState; -/// use ahash::CallHasher; -/// -#[cfg_attr(any(feature = "compile-time-rng", feature = "runtime-rng"), doc = "let hash_builder = RandomState::new();")] -#[cfg_attr(not(any(feature = "compile-time-rng", feature = "runtime-rng")), doc = "let hash_builder = RandomState::generate_with(1, 2, 3, 4);")] -/// //... -/// let value: u32 = 17; -/// let hash = u32::get_hash(&value, &hash_builder); -/// ``` -/// Note that the type used to invoke `get_hash` must be the same a the type of value passed. -/// For example get a hasher specialized on `[u8]` can invoke: -/// ``` -/// /// use std::hash::BuildHasher; -/// # use ahash::RandomState; -/// # use ahash::CallHasher; -/// # let hash_builder = RandomState::generate_with(1, 2, 3, 4); -/// let bytes: [u8; 4] = [1, 2, 3, 4]; -/// let hash = <[u8]>::get_hash(&bytes, &hash_builder); -/// ``` -pub trait CallHasher { +pub(crate) trait CallHasher { fn get_hash(value: &H, build_hasher: &B) -> u64; } diff --git a/tests/bench.rs b/tests/bench.rs index 9e6dccc..647af5a 100644 --- a/tests/bench.rs +++ b/tests/bench.rs @@ -1,8 +1,10 @@ -use ahash::{CallHasher, RandomState}; +#![feature(build_hasher_simple_hash_one)] + +use ahash::{RandomState}; use criterion::*; use fxhash::FxHasher; use std::collections::hash_map::DefaultHasher; -use std::hash::{Hash, Hasher}; +use std::hash::{BuildHasher, Hash, Hasher}; #[cfg(any( all(any(target_arch = "x86", target_arch = "x86_64"), target_feature = "aes", not(miri)), @@ -10,7 +12,7 @@ use std::hash::{Hash, Hasher}; ))] fn aeshash(b: &H) -> u64 { let build_hasher = RandomState::with_seeds(1, 2, 3, 4); - H::get_hash(b, &build_hasher) + build_hasher.hash_one(b) } #[cfg(not(any( all(any(target_arch = "x86", target_arch = "x86_64"), target_feature = "aes", not(miri)), @@ -26,7 +28,7 @@ fn aeshash(_b: &H) -> u64 { )))] fn fallbackhash(b: &H) -> u64 { let build_hasher = RandomState::with_seeds(1, 2, 3, 4); - H::get_hash(b, &build_hasher) + build_hasher.hash_one(b) } #[cfg(any( all(any(target_arch = "x86", target_arch = "x86_64"), target_feature = "aes", not(miri)), diff --git a/tests/map_tests.rs b/tests/map_tests.rs index be617a2..5e07894 100644 --- a/tests/map_tests.rs +++ b/tests/map_tests.rs @@ -1,9 +1,11 @@ +#![feature(build_hasher_simple_hash_one)] + use std::hash::{BuildHasher, Hash, Hasher}; use criterion::*; use fxhash::FxHasher; -use ahash::{AHasher, CallHasher, RandomState}; +use ahash::{AHasher, RandomState}; fn gen_word_pairs() -> Vec { let words: Vec<_> = r#" @@ -152,7 +154,7 @@ fn check_for_collisions(build_hasher: &B, items: &[H], #[allow(unused)] // False positive fn hash(b: &H, build_hasher: &B) -> u64 { - H::get_hash(b, build_hasher) + build_hasher.hash_one(b) } #[test] diff --git a/tests/nopanic.rs b/tests/nopanic.rs index d48ff55..8536c9f 100644 --- a/tests/nopanic.rs +++ b/tests/nopanic.rs @@ -1,4 +1,6 @@ -use ahash::{AHasher, CallHasher, RandomState}; +#![feature(build_hasher_simple_hash_one)] + +use ahash::{AHasher, RandomState}; use std::hash::BuildHasher; #[macro_use] @@ -38,8 +40,8 @@ fn hash_test_specialize(num: i32, string: &str) -> (u64, u64) { let hasher1 = AHasher::new_with_keys(1, 2); let hasher2 = AHasher::new_with_keys(1, 2); ( - i32::get_hash(&num, &SimpleBuildHasher { hasher: hasher1 }), - <[u8]>::get_hash(string.as_bytes(), &SimpleBuildHasher { hasher: hasher2 }), + SimpleBuildHasher { hasher: hasher1 }.hash_one(num), + SimpleBuildHasher { hasher: hasher2 }.hash_one(string.as_bytes()), ) } @@ -54,8 +56,8 @@ fn hash_test_random(num: i32, string: &str) -> (u64, u64) { let build_hasher1 = RandomState::with_seeds(1, 2, 3, 4); let build_hasher2 = RandomState::with_seeds(1, 2, 3, 4); ( - i32::get_hash(&num, &build_hasher1), - <[u8]>::get_hash(string.as_bytes(), &build_hasher2), + build_hasher1.hash_one(&num), + build_hasher2.hash_one(string.as_bytes()) ) } From d32f11c64f22426bb358d5a9aa4ff762d3026a9d Mon Sep 17 00:00:00 2001 From: Tom Kaitchuck Date: Sat, 26 Feb 2022 20:19:16 -0800 Subject: [PATCH 11/24] Clean up after merge Signed-off-by: Tom Kaitchuck --- src/lib.rs | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index d180516..73ba20e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -71,26 +71,20 @@ cfg_if::cfg_if! { pub use crate::hash_map::AHashMap; pub use crate::hash_set::AHashSet; + + /// [Hasher]: std::hash::Hasher + /// [HashMap]: std::collections::HashMap + /// Type alias for [HashMap] + pub type HashMap = std::collections::HashMap; + + /// Type alias for [HashSet] + pub type HashSet = std::collections::HashSet; } } #[cfg(test)] mod hash_quality_test; - -#[cfg(feature = "std")] -/// [Hasher]: std::hash::Hasher -/// [HashMap]: std::collections::HashMap -/// Type alias for [HashMap] -pub type HashMap = std::collections::HashMap; -#[cfg(feature = "std")] -/// Type alias for [HashSet] -pub type HashSet = std::collections::HashSet; - -#[cfg(feature = "std")] -mod hash_map; -#[cfg(feature = "std")] -mod hash_set; mod operations; mod random_state; mod specialize; From bf19b409304a49855a8c9eab588ca9be9251bacc Mon Sep 17 00:00:00 2001 From: Tom Kaitchuck Date: Sun, 27 Feb 2022 14:05:24 -0800 Subject: [PATCH 12/24] add map benchmark Signed-off-by: Tom Kaitchuck --- Cargo.toml | 3 ++- src/random_state.rs | 3 +++ tests/bench.rs | 65 ++++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 67 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3195373..846f8b8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -78,13 +78,14 @@ once_cell = { version = "1.8", default-features = false, features = ["unstable", [dev-dependencies] no-panic = "0.1.10" -criterion = {version = "0.3.2"} +criterion = {version = "0.3.2", features = ["html_reports"] } seahash = "4.0" fnv = "1.0.5" fxhash = "0.2.1" hex = "0.4.2" rand = "0.7.3" serde_json = "1.0.59" +hashbrown = { version = "0.12.0" } [package.metadata.docs.rs] rustc-args = ["-C", "target-feature=+aes"] diff --git a/src/random_state.rs b/src/random_state.rs index a5dddbe..7d0a670 100644 --- a/src/random_state.rs +++ b/src/random_state.rs @@ -46,6 +46,7 @@ pub(crate) const PI2: [u64; 4] = [ cfg_if::cfg_if! { if #[cfg(all(feature = "compile-time-rng", any(test, fuzzing)))] { + #[inline] fn get_fixed_seeds() -> &'static [[u64; 4]; 2] { use const_random::const_random; @@ -65,6 +66,7 @@ cfg_if::cfg_if! { &RAND } } else if #[cfg(all(feature = "runtime-rng", not(fuzzing)))] { + #[inline] fn get_fixed_seeds() -> &'static [[u64; 4]; 2] { use crate::convert::Convert; @@ -77,6 +79,7 @@ cfg_if::cfg_if! { }) } } else if #[cfg(feature = "compile-time-rng")] { + #[inline] fn get_fixed_seeds() -> &'static [[u64; 4]; 2] { use const_random::const_random; diff --git a/tests/bench.rs b/tests/bench.rs index 647af5a..160dee7 100644 --- a/tests/bench.rs +++ b/tests/bench.rs @@ -1,10 +1,10 @@ #![feature(build_hasher_simple_hash_one)] -use ahash::{RandomState}; +use ahash::{AHasher, RandomState}; use criterion::*; use fxhash::FxHasher; use std::collections::hash_map::DefaultHasher; -use std::hash::{BuildHasher, Hash, Hasher}; +use std::hash::{BuildHasher, BuildHasherDefault, Hash, Hasher}; #[cfg(any( all(any(target_arch = "x86", target_arch = "x86_64"), target_feature = "aes", not(miri)), @@ -144,6 +144,64 @@ fn bench_sip(c: &mut Criterion) { group.bench_with_input("string", &gen_strings(), |b, s| b.iter(|| black_box(siphash(s)))); } +fn bench_map(c: &mut Criterion) { + let mut group = c.benchmark_group("map"); + group.bench_function("aHash-alias", |b| b.iter(|| { + let hm: ahash::HashMap = (0..1_000_000).map(|i| (i, i)).collect(); + let mut sum = 0; + for i in 0..1_000_000 { + if let Some(x) = hm.get(&i) { + sum += x; + } + } + })); + group.bench_function("aHash-hashBrown", |b| b.iter(|| { + let hm: hashbrown::HashMap = (0..1_000_000).map(|i| (i, i)).collect(); + let mut sum = 0; + for i in 0..1_000_000 { + if let Some(x) = hm.get(&i) { + sum += x; + } + } + })); + group.bench_function("aHash-hashBrown-explicit", |b| b.iter(|| { + let hm: hashbrown::HashMap = (0..1_000_000).map(|i| (i, i)).collect(); + let mut sum = 0; + for i in 0..1_000_000 { + if let Some(x) = hm.get(&i) { + sum += x; + } + } + })); + group.bench_function("aHash-wrapper", |b| b.iter(|| { + let hm: ahash::AHashMap = (0..1_000_000).map(|i| (i, i)).collect(); + let mut sum = 0; + for i in 0..1_000_000 { + if let Some(x) = hm.get(&i) { + sum += x; + } + } + })); + group.bench_function("aHash-rand", |b| b.iter(|| { + let hm: std::collections::HashMap = (0..1_000_000).map(|i| (i, i)).collect(); + let mut sum = 0; + for i in 0..1_000_000 { + if let Some(x) = hm.get(&i) { + sum += x; + } + } + })); + group.bench_function("aHash-default", |b| b.iter(|| { + let hm: std::collections::HashMap> = (0..1_000_000).map(|i| (i, i)).collect(); + let mut sum = 0; + for i in 0..1_000_000 { + if let Some(x) = hm.get(&i) { + sum += x; + } + } + })); +} + criterion_main!(benches); criterion_group!( benches, @@ -152,5 +210,6 @@ criterion_group!( bench_fx, bench_fnv, bench_sea, - bench_sip + bench_sip, + bench_map, ); From 7f5f0d8604f47de36882e6876a897fc28c9c2145 Mon Sep 17 00:00:00 2001 From: Tom Kaitchuck Date: Thu, 26 May 2022 00:46:18 -0700 Subject: [PATCH 13/24] Move get_src to a static method Signed-off-by: Tom Kaitchuck --- src/random_state.rs | 79 +++++++++++++++++++++++---------------------- 1 file changed, 40 insertions(+), 39 deletions(-) diff --git a/src/random_state.rs b/src/random_state.rs index 7d0a670..2986121 100644 --- a/src/random_state.rs +++ b/src/random_state.rs @@ -140,46 +140,25 @@ impl DefaultRandomSource { } impl RandomSource for DefaultRandomSource { - - #[cfg(not(all(target_arch = "arm", target_os = "none")))] - fn gen_hasher_seed(&self) -> usize { - let stack = self as *const _ as usize; - self.counter.fetch_add(stack, Ordering::Relaxed) - } - - #[cfg(all(target_arch = "arm", target_os = "none"))] - fn gen_hasher_seed(&self) -> usize { - let stack = self as *const _ as usize; - let previous = self.counter.load(Ordering::Relaxed); - let new = previous.wrapping_add(stack); - self.counter.store(new, Ordering::Relaxed); - new - } -} - -/// Provides a [Hasher] factory. This is typically used (e.g. by [HashMap]) to create -/// [AHasher]s in order to hash the keys of the map. See `build_hasher` below. -/// -/// [build_hasher]: ahash:: -/// [Hasher]: std::hash::Hasher -/// [BuildHasher]: std::hash::BuildHasher -/// [HashMap]: std::collections::HashMap -#[derive(Clone)] -pub struct RandomState { - pub(crate) k0: u64, - pub(crate) k1: u64, - pub(crate) k2: u64, - pub(crate) k3: u64, -} - -impl fmt::Debug for RandomState { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.pad("RandomState { .. }") + cfg_if::cfg_if! { + if #[cfg(all(target_arch = "arm", target_os = "none"))] { + fn gen_hasher_seed(&self) -> usize { + let stack = self as *const _ as usize; + let previous = self.counter.load(Ordering::Relaxed); + let new = previous.wrapping_add(stack); + self.counter.store(new, Ordering::Relaxed); + new + } + } else { + fn gen_hasher_seed(&self) -> usize { + let stack = self as *const _ as usize; + self.counter.fetch_add(stack, Ordering::Relaxed) + } + } } } -impl RandomState { - cfg_if::cfg_if! { +cfg_if::cfg_if! { if #[cfg(all(target_arch = "arm", target_os = "none"))] { #[inline] fn get_src() -> &'static dyn RandomSource { @@ -205,13 +184,35 @@ impl RandomState { RAND_SOURCE.get_or_init(|| Box::new(Box::new(DefaultRandomSource::new()))).as_ref() } } +} + +/// Provides a [Hasher] factory. This is typically used (e.g. by [HashMap]) to create +/// [AHasher]s in order to hash the keys of the map. See `build_hasher` below. +/// +/// [build_hasher]: ahash:: +/// [Hasher]: std::hash::Hasher +/// [BuildHasher]: std::hash::BuildHasher +/// [HashMap]: std::collections::HashMap +#[derive(Clone)] +pub struct RandomState { + pub(crate) k0: u64, + pub(crate) k1: u64, + pub(crate) k2: u64, + pub(crate) k3: u64, +} + +impl fmt::Debug for RandomState { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.pad("RandomState { .. }") } +} +impl RandomState { /// Use randomly generated keys #[inline] #[cfg(any(feature = "compile-time-rng", feature = "runtime-rng"))] pub fn new() -> RandomState { - let src = Self::get_src(); + let src = get_src(); let fixed = get_fixed_seeds(); Self::from_keys(&fixed[0], &fixed[1], src.gen_hasher_seed()) } @@ -220,7 +221,7 @@ impl RandomState { /// This is done using a static counter, so it can safely be used with a fixed keys. #[inline] pub fn generate_with(k0: u64, k1: u64, k2: u64, k3: u64) -> RandomState { - let src = Self::get_src(); + let src = get_src(); let fixed = get_fixed_seeds(); RandomState::from_keys(&fixed[0], &[k0, k1, k2, k3], src.gen_hasher_seed()) } From 16db81c2f16f18622c61d8e958074c735e9af845 Mon Sep 17 00:00:00 2001 From: Tom Kaitchuck Date: Mon, 8 Aug 2022 21:35:41 -0700 Subject: [PATCH 14/24] Remove some warnings Signed-off-by: Tom Kaitchuck --- smhasher/ahash-cbindings/src/lib.rs | 8 +++++--- src/hash_map.rs | 2 -- src/lib.rs | 2 +- src/operations.rs | 3 ++- src/random_state.rs | 6 +++--- 5 files changed, 11 insertions(+), 10 deletions(-) diff --git a/smhasher/ahash-cbindings/src/lib.rs b/smhasher/ahash-cbindings/src/lib.rs index 571ae87..e53c03e 100644 --- a/smhasher/ahash-cbindings/src/lib.rs +++ b/smhasher/ahash-cbindings/src/lib.rs @@ -1,10 +1,12 @@ +#![feature(build_hasher_simple_hash_one)] + use ahash::*; use core::slice; -use std::hash::{BuildHasher, Hasher}; +use std::hash::{BuildHasher}; #[no_mangle] pub extern "C" fn ahash64(buf: *const (), len: usize, seed: u64) -> u64 { let buf: &[u8] = unsafe { slice::from_raw_parts(buf as *const u8, len) }; - let build_hasher = RandomState::with_seeds(seed, 0, 0, 0); - <[u8]>::get_hash(&buf, &build_hasher) + let build_hasher = RandomState::with_seed(seed as usize); + build_hasher.hash_one(&buf) } diff --git a/src/hash_map.rs b/src/hash_map.rs index 4cce377..693ae4b 100644 --- a/src/hash_map.rs +++ b/src/hash_map.rs @@ -163,8 +163,6 @@ where /// types that can be `==` without being identical. See the [module-level /// documentation] for more. /// - /// [module-level documentation]: crate::collections#insert-and-complex-keys - /// /// # Examples /// /// ``` diff --git a/src/lib.rs b/src/lib.rs index bf41bed..8c146bf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -89,7 +89,7 @@ cfg_if::cfg_if! { mod hash_quality_test; mod operations; -mod random_state; +pub mod random_state; mod specialize; pub use crate::random_state::RandomState; diff --git a/src/operations.rs b/src/operations.rs index 5d7548d..0ee98b9 100644 --- a/src/operations.rs +++ b/src/operations.rs @@ -63,7 +63,7 @@ pub(crate) fn add_and_shuffle(a: u128, b: u128) -> u128 { shuffle(sum.convert()) } -#[allow(unused)] //not used by fallbac +#[allow(unused)] //not used by fallback #[inline(always)] pub(crate) fn shuffle_and_add(base: u128, to_add: u128) -> u128 { let shuffled: [u64; 2] = shuffle(base).convert(); @@ -149,6 +149,7 @@ pub(crate) fn aesdec(value: u128, xor: u128) -> u128 { } } +#[allow(unused)] #[inline(always)] pub(crate) fn add_in_length(enc: &mut u128, len: u64) { #[cfg(all(target_arch = "x86_64", target_feature = "sse2", not(miri)))] diff --git a/src/random_state.rs b/src/random_state.rs index 9cce662..9f69745 100644 --- a/src/random_state.rs +++ b/src/random_state.rs @@ -4,9 +4,9 @@ cfg_if::cfg_if! { all(any(target_arch = "x86", target_arch = "x86_64"), target_feature = "aes", not(miri)), all(any(target_arch = "arm", target_arch = "aarch64"), any(target_feature = "aes", target_feature = "crypto"), not(miri), feature = "stdsimd") ))] { - pub use crate::aes_hash::*; + use crate::aes_hash::*; } else { - pub use crate::fallback_hash::*; + use crate::fallback_hash::*; } } cfg_if::cfg_if! { @@ -113,7 +113,7 @@ cfg_if::cfg_if! { } } /// A supplier of Randomness used for different hashers. -/// See [RandomState::set_random_source]. +/// See [set_random_source]. pub trait RandomSource { fn gen_hasher_seed(&self) -> usize; From 70f9a039f6d38a2ccbffb35620a4f0be3142c8a7 Mon Sep 17 00:00:00 2001 From: Tom Kaitchuck Date: Mon, 8 Aug 2022 23:10:21 -0700 Subject: [PATCH 15/24] Add hash_once for non-nightly Signed-off-by: Tom Kaitchuck --- src/random_state.rs | 15 ++++++++++----- tests/bench.rs | 4 ++-- tests/map_tests.rs | 11 ++++++++++- tests/nopanic.rs | 15 +++++++++++---- 4 files changed, 33 insertions(+), 12 deletions(-) diff --git a/src/random_state.rs b/src/random_state.rs index 9f69745..643d6cb 100644 --- a/src/random_state.rs +++ b/src/random_state.rs @@ -1,4 +1,5 @@ +use core::hash::Hash; cfg_if::cfg_if! { if #[cfg(any( all(any(target_arch = "x86", target_arch = "x86_64"), target_feature = "aes", not(miri)), @@ -12,7 +13,6 @@ cfg_if::cfg_if! { cfg_if::cfg_if! { if #[cfg(feature = "specialize")]{ use crate::BuildHasherExt; - use core::hash::Hash; } } cfg_if::cfg_if! { @@ -268,6 +268,13 @@ impl RandomState { pub const fn with_seeds(k0: u64, k1: u64, k2: u64, k3: u64) -> RandomState { RandomState { k0: k0 ^ PI2[0], k1: k1 ^ PI2[1], k2: k2 ^ PI2[2], k3: k3 ^ PI2[3] } } + + #[inline] + pub fn hash_one(&self, x: T) -> u64 + where Self: Sized { + use crate::specialize::CallHasher; + T::get_hash(&x, self) + } } #[cfg(any(feature = "compile-time-rng", feature = "runtime-rng"))] @@ -316,10 +323,8 @@ impl BuildHasher for RandomState { #[cfg(feature = "specialize")] #[inline] - fn hash_one(&self, x: T) -> u64 - where Self: Sized { - use crate::specialize::CallHasher; - T::get_hash(&x, self) + fn hash_one(&self, x: T) -> u64 { + RandomState::hash_one(self, x) } } diff --git a/tests/bench.rs b/tests/bench.rs index 5d998f4..d7f58f8 100644 --- a/tests/bench.rs +++ b/tests/bench.rs @@ -1,10 +1,10 @@ -#![feature(build_hasher_simple_hash_one)] +#![cfg_attr(feature = "specialize", feature(build_hasher_simple_hash_one))] use ahash::{AHasher, RandomState}; use criterion::*; use fxhash::FxHasher; use std::collections::hash_map::DefaultHasher; -use std::hash::{BuildHasher, BuildHasherDefault, Hash, Hasher}; +use std::hash::{BuildHasherDefault, Hash, Hasher}; #[cfg(any( all(any(target_arch = "x86", target_arch = "x86_64"), target_feature = "aes", not(miri)), diff --git a/tests/map_tests.rs b/tests/map_tests.rs index 9a95c73..6e4c61c 100644 --- a/tests/map_tests.rs +++ b/tests/map_tests.rs @@ -1,4 +1,4 @@ -#![feature(build_hasher_simple_hash_one)] +#![cfg_attr(feature = "specialize", feature(build_hasher_simple_hash_one))] use std::hash::{BuildHasher, Hash, Hasher}; @@ -152,11 +152,20 @@ fn check_for_collisions(build_hasher: &B, items: &[H], ); } +#[cfg(feature = "specialize")] #[allow(unused)] // False positive fn hash(b: &H, build_hasher: &B) -> u64 { build_hasher.hash_one(b) } +#[cfg(not(feature = "specialize"))] +#[allow(unused)] // False positive +fn hash(b: &H, build_hasher: &B) -> u64 { + let mut hasher = build_hasher.build_hasher(); + b.hash(&mut hasher); + hasher.finish() +} + #[test] fn test_bucket_distribution() { let build_hasher = RandomState::with_seeds(1, 2, 3, 4); diff --git a/tests/nopanic.rs b/tests/nopanic.rs index 8536c9f..c9d00c3 100644 --- a/tests/nopanic.rs +++ b/tests/nopanic.rs @@ -1,7 +1,5 @@ -#![feature(build_hasher_simple_hash_one)] - use ahash::{AHasher, RandomState}; -use std::hash::BuildHasher; +use std::hash::{BuildHasher, Hash, Hasher}; #[macro_use] extern crate no_panic; @@ -26,6 +24,14 @@ struct SimpleBuildHasher { hasher: AHasher, } +impl SimpleBuildHasher { + fn hash_one(&self, x: T) -> u64 where Self: Sized { + let mut hasher = self.build_hasher(); + x.hash(&mut hasher); + hasher.finish() + } +} + impl BuildHasher for SimpleBuildHasher { type Hasher = AHasher; @@ -70,5 +76,6 @@ fn hash_test_specialize_wrapper(num: i32, string: &str) { fn test_no_panic() { hash_test_final_wrapper(2, "Foo"); hash_test_specialize_wrapper(2, "Bar"); - hash_test_random_wrapper(2, "Baz"); + hash_test_random(2, "Baz"); + hash_test_random_wrapper(2, "Bat"); } From 76896624de2bf02461400d497f0858d985ea7df7 Mon Sep 17 00:00:00 2001 From: Tom Kaitchuck Date: Mon, 8 Aug 2022 23:20:43 -0700 Subject: [PATCH 16/24] Fix bench for no-std Signed-off-by: Tom Kaitchuck --- tests/bench.rs | 111 +++++++++++++++++++++++++------------------------ 1 file changed, 57 insertions(+), 54 deletions(-) diff --git a/tests/bench.rs b/tests/bench.rs index d7f58f8..251e134 100644 --- a/tests/bench.rs +++ b/tests/bench.rs @@ -147,61 +147,64 @@ fn bench_sip(c: &mut Criterion) { } fn bench_map(c: &mut Criterion) { - let mut group = c.benchmark_group("map"); - group.bench_function("aHash-alias", |b| b.iter(|| { - let hm: ahash::HashMap = (0..1_000_000).map(|i| (i, i)).collect(); - let mut sum = 0; - for i in 0..1_000_000 { - if let Some(x) = hm.get(&i) { - sum += x; - } - } - })); - group.bench_function("aHash-hashBrown", |b| b.iter(|| { - let hm: hashbrown::HashMap = (0..1_000_000).map(|i| (i, i)).collect(); - let mut sum = 0; - for i in 0..1_000_000 { - if let Some(x) = hm.get(&i) { - sum += x; - } - } - })); - group.bench_function("aHash-hashBrown-explicit", |b| b.iter(|| { - let hm: hashbrown::HashMap = (0..1_000_000).map(|i| (i, i)).collect(); - let mut sum = 0; - for i in 0..1_000_000 { - if let Some(x) = hm.get(&i) { - sum += x; - } - } - })); - group.bench_function("aHash-wrapper", |b| b.iter(|| { - let hm: ahash::AHashMap = (0..1_000_000).map(|i| (i, i)).collect(); - let mut sum = 0; - for i in 0..1_000_000 { - if let Some(x) = hm.get(&i) { - sum += x; - } - } - })); - group.bench_function("aHash-rand", |b| b.iter(|| { - let hm: std::collections::HashMap = (0..1_000_000).map(|i| (i, i)).collect(); - let mut sum = 0; - for i in 0..1_000_000 { - if let Some(x) = hm.get(&i) { - sum += x; - } - } - })); - group.bench_function("aHash-default", |b| b.iter(|| { - let hm: std::collections::HashMap> = (0..1_000_000).map(|i| (i, i)).collect(); - let mut sum = 0; - for i in 0..1_000_000 { - if let Some(x) = hm.get(&i) { - sum += x; - } + #[cfg(feature = "std")] + { + let mut group = c.benchmark_group("map"); + group.bench_function("aHash-alias", |b| b.iter(|| { + let hm: ahash::HashMap = (0..1_000_000).map(|i| (i, i)).collect(); + let mut sum = 0; + for i in 0..1_000_000 { + if let Some(x) = hm.get(&i) { + sum += x; + } + } + })); + group.bench_function("aHash-hashBrown", |b| b.iter(|| { + let hm: hashbrown::HashMap = (0..1_000_000).map(|i| (i, i)).collect(); + let mut sum = 0; + for i in 0..1_000_000 { + if let Some(x) = hm.get(&i) { + sum += x; + } + } + })); + group.bench_function("aHash-hashBrown-explicit", |b| b.iter(|| { + let hm: hashbrown::HashMap = (0..1_000_000).map(|i| (i, i)).collect(); + let mut sum = 0; + for i in 0..1_000_000 { + if let Some(x) = hm.get(&i) { + sum += x; + } + } + })); + group.bench_function("aHash-wrapper", |b| b.iter(|| { + let hm: ahash::AHashMap = (0..1_000_000).map(|i| (i, i)).collect(); + let mut sum = 0; + for i in 0..1_000_000 { + if let Some(x) = hm.get(&i) { + sum += x; + } + } + })); + group.bench_function("aHash-rand", |b| b.iter(|| { + let hm: std::collections::HashMap = (0..1_000_000).map(|i| (i, i)).collect(); + let mut sum = 0; + for i in 0..1_000_000 { + if let Some(x) = hm.get(&i) { + sum += x; + } + } + })); + group.bench_function("aHash-default", |b| b.iter(|| { + let hm: std::collections::HashMap> = (0..1_000_000).map(|i| (i, i)).collect(); + let mut sum = 0; + for i in 0..1_000_000 { + if let Some(x) = hm.get(&i) { + sum += x; + } + } + })); } - })); } criterion_main!(benches); From 7d7b4deacca8498e33145dff4e0e2d7466aa4dac Mon Sep 17 00:00:00 2001 From: Tom Kaitchuck Date: Tue, 9 Aug 2022 00:28:47 -0700 Subject: [PATCH 17/24] Remove direct hasher instantion Signed-off-by: Tom Kaitchuck --- src/aes_hash.rs | 2 +- src/fallback_hash.rs | 2 +- src/random_state.rs | 20 ++++++++++++++++++++ tests/map_tests.rs | 5 ++--- tests/nopanic.rs | 8 ++++---- 5 files changed, 28 insertions(+), 9 deletions(-) diff --git a/src/aes_hash.rs b/src/aes_hash.rs index a3630f5..df67bfd 100644 --- a/src/aes_hash.rs +++ b/src/aes_hash.rs @@ -48,7 +48,7 @@ impl AHasher { /// println!("Hash is {:x}!", hasher.finish()); /// ``` #[inline] - pub fn new_with_keys(key1: u128, key2: u128) -> Self { + pub(crate) fn new_with_keys(key1: u128, key2: u128) -> Self { let pi: [u128; 2] = PI.convert(); let key1 = key1 ^ pi[0]; let key2 = key2 ^ pi[1]; diff --git a/src/fallback_hash.rs b/src/fallback_hash.rs index d4004b6..efad74c 100644 --- a/src/fallback_hash.rs +++ b/src/fallback_hash.rs @@ -32,7 +32,7 @@ impl AHasher { /// Creates a new hasher keyed to the provided key. #[inline] #[allow(dead_code)] // Is not called if non-fallback hash is used. - pub fn new_with_keys(key1: u128, key2: u128) -> AHasher { + pub(crate) fn new_with_keys(key1: u128, key2: u128) -> AHasher { let pi: [u128; 2] = PI.convert(); let key1: [u64; 2] = (key1 ^ pi[0]).convert(); let key2: [u64; 2] = (key2 ^ pi[1]).convert(); diff --git a/src/random_state.rs b/src/random_state.rs index 643d6cb..b750bcd 100644 --- a/src/random_state.rs +++ b/src/random_state.rs @@ -269,6 +269,16 @@ impl RandomState { RandomState { k0: k0 ^ PI2[0], k1: k1 ^ PI2[1], k2: k2 ^ PI2[2], k3: k3 ^ PI2[3] } } + /// Calculates the hash of a single value. + /// + /// This is intended as a convenience for code which *consumes* hashes, such + /// as the implementation of a hash table or in unit tests that check + /// whether a custom [`Hash`] implementation behaves as expected. + /// + /// This must not be used in any code which *creates* hashes, such as in an + /// implementation of [`Hash`]. The way to create a combined hash of + /// multiple values is to call [`Hash::hash`] multiple times using the same + /// [`Hasher`], not to call this method repeatedly and combine the results. #[inline] pub fn hash_one(&self, x: T) -> u64 where Self: Sized { @@ -321,6 +331,16 @@ impl BuildHasher for RandomState { AHasher::from_random_state(self) } + /// Calculates the hash of a single value. + /// + /// This is intended as a convenience for code which *consumes* hashes, such + /// as the implementation of a hash table or in unit tests that check + /// whether a custom [`Hash`] implementation behaves as expected. + /// + /// This must not be used in any code which *creates* hashes, such as in an + /// implementation of [`Hash`]. The way to create a combined hash of + /// multiple values is to call [`Hash::hash`] multiple times using the same + /// [`Hasher`], not to call this method repeatedly and combine the results. #[cfg(feature = "specialize")] #[inline] fn hash_one(&self, x: T) -> u64 { diff --git a/tests/map_tests.rs b/tests/map_tests.rs index 6e4c61c..3f4a06a 100644 --- a/tests/map_tests.rs +++ b/tests/map_tests.rs @@ -4,8 +4,7 @@ use std::hash::{BuildHasher, Hash, Hasher}; use criterion::*; use fxhash::FxHasher; - -use ahash::{AHasher, RandomState}; +use ahash::RandomState; fn gen_word_pairs() -> Vec { let words: Vec<_> = r#" @@ -204,7 +203,7 @@ fn test_ahash_alias_set_construction() { fn ahash_vec(b: &Vec) -> u64 { let mut total: u64 = 0; for item in b { - let mut hasher = AHasher::new_with_keys(1234, 5678); + let mut hasher = RandomState::with_seeds(12, 34, 56, 78).build_hasher(); item.hash(&mut hasher); total = total.wrapping_add(hasher.finish()); } diff --git a/tests/nopanic.rs b/tests/nopanic.rs index c9d00c3..4ad13bd 100644 --- a/tests/nopanic.rs +++ b/tests/nopanic.rs @@ -8,8 +8,8 @@ extern crate no_panic; #[no_panic] fn hash_test_final(num: i32, string: &str) -> (u64, u64) { use core::hash::Hasher; - let mut hasher1 = AHasher::new_with_keys(1, 2); - let mut hasher2 = AHasher::new_with_keys(3, 4); + let mut hasher1 = RandomState::with_seeds(1, 2, 3, 4).build_hasher(); + let mut hasher2 = RandomState::with_seeds(3, 4, 5, 6).build_hasher(); hasher1.write_i32(num); hasher2.write(string.as_bytes()); (hasher1.finish(), hasher2.finish()) @@ -43,8 +43,8 @@ impl BuildHasher for SimpleBuildHasher { #[inline(never)] #[no_panic] fn hash_test_specialize(num: i32, string: &str) -> (u64, u64) { - let hasher1 = AHasher::new_with_keys(1, 2); - let hasher2 = AHasher::new_with_keys(1, 2); + let hasher1 = RandomState::with_seeds(1, 2, 3, 4).build_hasher(); + let hasher2 = RandomState::with_seeds(1, 2, 3, 4).build_hasher(); ( SimpleBuildHasher { hasher: hasher1 }.hash_one(num), SimpleBuildHasher { hasher: hasher2 }.hash_one(string.as_bytes()), From 678fa84eb2eea710c0dd5f4bbfffc9c62d2eebdd Mon Sep 17 00:00:00 2001 From: Tom Kaitchuck Date: Tue, 9 Aug 2022 00:46:07 -0700 Subject: [PATCH 18/24] Bump version Signed-off-by: Tom Kaitchuck --- Cargo.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c30f619..6a0093a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ahash" -version = "0.7.6" +version = "0.8.0" authors = ["Tom Kaitchuck "] license = "MIT OR Apache-2.0" description = "A non-cryptographic hash function using AES-NI for high performance" @@ -83,9 +83,9 @@ seahash = "4.0" fnv = "1.0.5" fxhash = "0.2.1" hex = "0.4.2" -rand = "0.7.3" +rand = "0.8.5" serde_json = "1.0.59" -hashbrown = { version = "0.12.0" } +hashbrown = "0.12.3" [package.metadata.docs.rs] rustc-args = ["-C", "target-feature=+aes"] From 4dc5de80138f6c1379c7ede5302a59b1292de7a4 Mon Sep 17 00:00:00 2001 From: Tom Kaitchuck Date: Mon, 15 Aug 2022 23:58:23 -0700 Subject: [PATCH 19/24] Add faster fallback for architectures without folded_multiply Signed-off-by: Tom Kaitchuck --- src/fallback_hash.rs | 18 +++++++++++------- src/operations.rs | 4 +++- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/fallback_hash.rs b/src/fallback_hash.rs index efad74c..722db8c 100644 --- a/src/fallback_hash.rs +++ b/src/fallback_hash.rs @@ -6,8 +6,6 @@ use crate::random_state::PI; use crate::RandomState; use core::hash::Hasher; - - const ROT: u32 = 23; //17 /// A `Hasher` for hashing an arbitrary stream of bytes. @@ -102,9 +100,11 @@ impl AHasher { #[inline(always)] #[cfg(not(feature = "folded_multiply"))] fn update(&mut self, new_data: u64) { - let d1 = (new_data ^ self.buffer).wrapping_mul(MULTIPLE); - self.pad = (self.pad ^ d1).rotate_left(8).wrapping_mul(MULTIPLE); - self.buffer = (self.buffer ^ self.pad).rotate_left(24); + use crate::operations::INCREMENT; + + self.buffer = (self.buffer ^ new_data).wrapping_mul(self.pad ^ new_data.swap_bytes()); //Changing rather than fixed multiple removes linearity + self.buffer ^= self.buffer >> 47; // xorshift some good bits to the bottom + self.pad = self.pad.wrapping_add(INCREMENT); } /// Similar to the above this function performs an update using a "folded multiply". @@ -129,9 +129,13 @@ impl AHasher { #[inline(always)] #[cfg(not(feature = "folded_multiply"))] fn large_update(&mut self, new_data: u128) { + use crate::operations::INCREMENT; + let block: [u64; 2] = new_data.convert(); - self.update(block[0] ^ self.extra_keys[0]); - self.update(block[1] ^ self.extra_keys[1]); + self.buffer = (self.buffer ^ block[0]).wrapping_mul(self.pad ^ block[1].swap_bytes()); //Changing rather than fixed multiple removes linearity + self.buffer = (self.buffer ^ block[1]).wrapping_mul(self.pad.wrapping_add(1013904223) ^ block[0].swap_bytes()); //Reversing bytes prevents low impact high order bits. + self.buffer ^= self.buffer >> 47; // xorshift some good bits to the bottom + self.pad = self.pad.wrapping_add(INCREMENT); } #[inline] diff --git a/src/operations.rs b/src/operations.rs index 0ee98b9..a4161da 100644 --- a/src/operations.rs +++ b/src/operations.rs @@ -1,7 +1,9 @@ use crate::convert::*; -///This constant come from Kunth's prng (Empirically it works better than those from splitmix32). +///These constants come from Kunth's prng (Empirically it works better than those from splitmix32). pub(crate) const MULTIPLE: u64 = 6364136223846793005; +#[allow(unused)] //only used by fallback +pub(crate) const INCREMENT: u64 = 1442695040888963407; /// This is a constant with a lot of special properties found by automated search. /// See the unit tests below. (Below are alternative values) From a8652bf7bcad25b1be02b29a496493c56d91f188 Mon Sep 17 00:00:00 2001 From: Tom Kaitchuck Date: Thu, 18 Aug 2022 19:18:05 -0700 Subject: [PATCH 20/24] Remove folded mul on non-folded-mul path Signed-off-by: Tom Kaitchuck --- src/fallback_hash.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/fallback_hash.rs b/src/fallback_hash.rs index 722db8c..6f8e6ee 100644 --- a/src/fallback_hash.rs +++ b/src/fallback_hash.rs @@ -338,6 +338,7 @@ impl Hasher for AHasherStr { } #[inline] + #[cfg(feature = "folded_multiply")] fn write(&mut self, bytes: &[u8]) { if bytes.len() > 8 { self.0.write(bytes) @@ -349,6 +350,12 @@ impl Hasher for AHasherStr { } } + #[inline] + #[cfg(not(feature = "folded_multiply"))] + fn write(&mut self, bytes: &[u8]) { + self.0.write(bytes) + } + #[inline] fn write_u8(&mut self, _i: u8) {} From fb9d18118801f287315502ddb748f78fd23caa33 Mon Sep 17 00:00:00 2001 From: Tom Kaitchuck Date: Sat, 20 Aug 2022 13:25:22 -0700 Subject: [PATCH 21/24] Use extra keys in non-folded fallback Signed-off-by: Tom Kaitchuck --- src/fallback_hash.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/fallback_hash.rs b/src/fallback_hash.rs index 6f8e6ee..8fd3859 100644 --- a/src/fallback_hash.rs +++ b/src/fallback_hash.rs @@ -132,8 +132,8 @@ impl AHasher { use crate::operations::INCREMENT; let block: [u64; 2] = new_data.convert(); - self.buffer = (self.buffer ^ block[0]).wrapping_mul(self.pad ^ block[1].swap_bytes()); //Changing rather than fixed multiple removes linearity - self.buffer = (self.buffer ^ block[1]).wrapping_mul(self.pad.wrapping_add(1013904223) ^ block[0].swap_bytes()); //Reversing bytes prevents low impact high order bits. + self.buffer = (self.buffer ^ block[0]).wrapping_mul(self.pad ^ self.extra_keys[0] ^ block[1].swap_bytes()); //Changing rather than fixed multiple removes linearity + self.buffer = (self.buffer ^ block[1]).wrapping_mul(self.pad ^ self.extra_keys[1] ^ block[0].swap_bytes()); //Reversing bytes prevents low impact high order bits. self.buffer ^= self.buffer >> 47; // xorshift some good bits to the bottom self.pad = self.pad.wrapping_add(INCREMENT); } From 07f364115b6c693244fb0a1814f317b6a9dd72ed Mon Sep 17 00:00:00 2001 From: Tom Kaitchuck Date: Sun, 23 Oct 2022 18:25:51 -0700 Subject: [PATCH 22/24] Use add by 64 rather than xor. Use swap bytes rather than shift. Signed-off-by: Tom Kaitchuck --- smhasher/fallbackNoFoldedOutput.txt | 1137 +++++++++++++++++++++++++++ src/fallback_hash.rs | 11 +- 2 files changed, 1142 insertions(+), 6 deletions(-) create mode 100644 smhasher/fallbackNoFoldedOutput.txt diff --git a/smhasher/fallbackNoFoldedOutput.txt b/smhasher/fallbackNoFoldedOutput.txt new file mode 100644 index 0000000..8f63d7d --- /dev/null +++ b/smhasher/fallbackNoFoldedOutput.txt @@ -0,0 +1,1137 @@ +------------------------------------------------------------------------------ +--- Testing ahash64 "ahash 64bit" GOOD + +[[[ Sanity Tests ]]] + +Verification value 0x4F80BA96 ....... SKIP (self- or unseeded) +Running sanity check 1 .......... PASS +Running AppendedZeroesTest .......... PASS + +[[[ Speed Tests ]]] + +Bulk speed test - 262144-byte keys +Alignment 7 - 18.981 bytes/cycle - 54306.12 MiB/sec @ 3 ghz +Alignment 6 - 19.062 bytes/cycle - 54536.99 MiB/sec @ 3 ghz +Alignment 5 - 18.974 bytes/cycle - 54284.96 MiB/sec @ 3 ghz +Alignment 4 - 19.039 bytes/cycle - 54470.49 MiB/sec @ 3 ghz +Alignment 3 - 19.129 bytes/cycle - 54729.57 MiB/sec @ 3 ghz +Alignment 2 - 19.126 bytes/cycle - 54720.48 MiB/sec @ 3 ghz +Alignment 1 - 18.990 bytes/cycle - 54332.12 MiB/sec @ 3 ghz +Alignment 0 - 19.801 bytes/cycle - 56652.53 MiB/sec @ 3 ghz +Average - 19.138 bytes/cycle - 54754.16 MiB/sec @ 3 ghz + +Small key speed test - 1-byte keys - 33.00 cycles/hash +Small key speed test - 2-byte keys - 33.00 cycles/hash +Small key speed test - 3-byte keys - 31.58 cycles/hash +Small key speed test - 4-byte keys - 31.46 cycles/hash +Small key speed test - 5-byte keys - 31.53 cycles/hash +Small key speed test - 6-byte keys - 32.89 cycles/hash +Small key speed test - 7-byte keys - 33.00 cycles/hash +Small key speed test - 8-byte keys - 33.00 cycles/hash +Small key speed test - 9-byte keys - 32.00 cycles/hash +Small key speed test - 10-byte keys - 32.00 cycles/hash +Small key speed test - 11-byte keys - 31.64 cycles/hash +Small key speed test - 12-byte keys - 31.31 cycles/hash +Small key speed test - 13-byte keys - 31.42 cycles/hash +Small key speed test - 14-byte keys - 31.65 cycles/hash +Small key speed test - 15-byte keys - 32.00 cycles/hash +Small key speed test - 16-byte keys - 32.00 cycles/hash +Small key speed test - 17-byte keys - 33.90 cycles/hash +Small key speed test - 18-byte keys - 33.66 cycles/hash +Small key speed test - 19-byte keys - 33.63 cycles/hash +Small key speed test - 20-byte keys - 34.84 cycles/hash +Small key speed test - 21-byte keys - 35.00 cycles/hash +Small key speed test - 22-byte keys - 34.55 cycles/hash +Small key speed test - 23-byte keys - 33.60 cycles/hash +Small key speed test - 24-byte keys - 33.68 cycles/hash +Small key speed test - 25-byte keys - 34.32 cycles/hash +Small key speed test - 26-byte keys - 35.00 cycles/hash +Small key speed test - 27-byte keys - 35.00 cycles/hash +Small key speed test - 28-byte keys - 33.68 cycles/hash +Small key speed test - 29-byte keys - 33.70 cycles/hash +Small key speed test - 30-byte keys - 33.89 cycles/hash +Small key speed test - 31-byte keys - 35.00 cycles/hash +Average 33.127 cycles/hash + +[[[ 'Hashmap' Speed Tests ]]] + +std::unordered_map +Init std HashMapTest: 201.204 cycles/op (102774 inserts, 1% deletions) +Running std HashMapTest: 130.402 cycles/op (2.4 stdv) + +greg7mdp/parallel-hashmap +Init fast HashMapTest: 125.038 cycles/op (102774 inserts, 1% deletions) +Running fast HashMapTest: 87.416 cycles/op (0.1 stdv) ....... PASS + +[[[ Avalanche Tests ]]] + +Testing 24-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.653333% +Testing 32-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.654667% +Testing 40-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.725333% +Testing 48-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.680667% +Testing 56-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.686000% +Testing 64-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.676000% +Testing 72-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.676000% +Testing 80-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.620667% +Testing 96-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.699333% +Testing 112-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.684667% +Testing 128-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.734000% +Testing 160-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.673333% +Testing 512-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.748000% +Testing 1024-bit keys -> 64-bit hashes, 300000 reps.......... worst bias is 0.780000% + +[[[ Keyset 'Sparse' Tests ]]] + +Keyset 'Sparse' - 16-bit keys with up to 9 bits set - 50643 keys +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 0.3, actual 0 (0.00x) +Testing collisions (high 19-25 bits) - Worst is 22 bits: 313/304 (1.03x) +Testing collisions (low 32-bit) - Expected 0.3, actual 0 (0.00x) +Testing collisions (low 19-25 bits) - Worst is 25 bits: 43/38 (1.13x) +Testing distribution - Worst bias is the 13-bit window at bit 9 - 0.766% + +Keyset 'Sparse' - 24-bit keys with up to 8 bits set - 1271626 keys +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 188.2, actual 179 (0.95x) +Testing collisions (high 24-35 bits) - Worst is 30 bits: 772/752 (1.03x) +Testing collisions (low 32-bit) - Expected 188.2, actual 180 (0.96x) +Testing collisions (low 24-35 bits) - Worst is 24 bits: 46572/46996 (0.99x) +Testing distribution - Worst bias is the 17-bit window at bit 52 - 0.099% + +Keyset 'Sparse' - 32-bit keys with up to 7 bits set - 4514873 keys +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 2372.2, actual 2386 (1.01x) (14) +Testing collisions (high 25-38 bits) - Worst is 33 bits: 1206/1186 (1.02x) +Testing collisions (low 32-bit) - Expected 2372.2, actual 2291 (0.97x) +Testing collisions (low 25-38 bits) - Worst is 35 bits: 299/296 (1.01x) +Testing distribution - Worst bias is the 19-bit window at bit 7 - 0.048% + +Keyset 'Sparse' - 40-bit keys with up to 6 bits set - 4598479 keys +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 2460.8, actual 2467 (1.00x) (7) +Testing collisions (high 25-38 bits) - Worst is 38 bits: 42/38 (1.09x) +Testing collisions (low 32-bit) - Expected 2460.8, actual 2431 (0.99x) (-29) +Testing collisions (low 25-38 bits) - Worst is 37 bits: 80/76 (1.04x) +Testing distribution - Worst bias is the 18-bit window at bit 32 - 0.044% + +Keyset 'Sparse' - 48-bit keys with up to 6 bits set - 14196869 keys +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 23437.8, actual 23394 (1.00x) (-43) +Testing collisions (high 27-42 bits) - Worst is 41 bits: 58/45 (1.27x) +Testing collisions (low 32-bit) - Expected 23437.8, actual 23042 (0.98x) (-395) +Testing collisions (low 27-42 bits) - Worst is 40 bits: 97/91 (1.06x) +Testing distribution - Worst bias is the 20-bit window at bit 27 - 0.026% + +Keyset 'Sparse' - 56-bit keys with up to 5 bits set - 4216423 keys +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 2069.0, actual 2131 (1.03x) (63) +Testing collisions (high 25-38 bits) - Worst is 38 bits: 44/32 (1.36x) +Testing collisions (low 32-bit) - Expected 2069.0, actual 2134 (1.03x) (66) +Testing collisions (low 25-38 bits) - Worst is 38 bits: 40/32 (1.24x) +Testing distribution - Worst bias is the 19-bit window at bit 28 - 0.078% + +Keyset 'Sparse' - 64-bit keys with up to 5 bits set - 8303633 keys +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 8021.7, actual 8004 (1.00x) (-17) +Testing collisions (high 26-40 bits) - Worst is 35 bits: 1003/1003 (1.00x) +Testing collisions (low 32-bit) - Expected 8021.7, actual 8045 (1.00x) (24) +Testing collisions (low 26-40 bits) - Worst is 40 bits: 39/31 (1.24x) +Testing distribution - Worst bias is the 20-bit window at bit 25 - 0.045% + +Keyset 'Sparse' - 72-bit keys with up to 5 bits set - 15082603 keys +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 26451.8, actual 26592 (1.01x) (141) +Testing collisions (high 27-42 bits) - Worst is 33 bits: 13368/13233 (1.01x) +Testing collisions (low 32-bit) - Expected 26451.8, actual 26315 (0.99x) (-136) +Testing collisions (low 27-42 bits) - Worst is 40 bits: 113/103 (1.09x) +Testing distribution - Worst bias is the 20-bit window at bit 59 - 0.018% + +Keyset 'Sparse' - 96-bit keys with up to 4 bits set - 3469497 keys +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 1401.0, actual 1354 (0.97x) +Testing collisions (high 25-38 bits) - Worst is 36 bits: 88/87 (1.00x) +Testing collisions (low 32-bit) - Expected 1401.0, actual 1422 (1.02x) (22) +Testing collisions (low 25-38 bits) - Worst is 36 bits: 96/87 (1.10x) +Testing distribution - Worst bias is the 18-bit window at bit 43 - 0.055% + +Keyset 'Sparse' - 160-bit keys with up to 4 bits set - 26977161 keys +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 84546.1, actual 84656 (1.00x) (110) +Testing collisions (high 28-44 bits) - Worst is 42 bits: 84/82 (1.02x) +Testing collisions (low 32-bit) - Expected 84546.1, actual 84598 (1.00x) (52) +Testing collisions (low 28-44 bits) - Worst is 41 bits: 199/165 (1.20x) +Testing distribution - Worst bias is the 20-bit window at bit 3 - 0.014% + +Keyset 'Sparse' - 256-bit keys with up to 3 bits set - 2796417 keys +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 910.2, actual 914 (1.00x) (4) +Testing collisions (high 25-37 bits) - Worst is 36 bits: 62/56 (1.09x) +Testing collisions (low 32-bit) - Expected 910.2, actual 887 (0.97x) +Testing collisions (low 25-37 bits) - Worst is 35 bits: 130/113 (1.14x) +Testing distribution - Worst bias is the 19-bit window at bit 47 - 0.068% + +Keyset 'Sparse' - 512-bit keys with up to 3 bits set - 22370049 keys +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 58155.4, actual 58437 (1.00x) (282) +Testing collisions (high 28-43 bits) - Worst is 33 bits: 29437/29102 (1.01x) +Testing collisions (low 32-bit) - Expected 58155.4, actual 58276 (1.00x) (121) +Testing collisions (low 28-43 bits) - Worst is 35 bits: 7362/7280 (1.01x) +Testing distribution - Worst bias is the 19-bit window at bit 11 - 0.011% + +Keyset 'Sparse' - 1024-bit keys with up to 2 bits set - 524801 keys +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 32.1, actual 39 (1.22x) (7) +Testing collisions (high 22-32 bits) - Worst is 32 bits: 39/32 (1.22x) +Testing collisions (low 32-bit) - Expected 32.1, actual 29 (0.90x) +Testing collisions (low 22-32 bits) - Worst is 30 bits: 136/128 (1.06x) +Testing distribution - Worst bias is the 16-bit window at bit 59 - 0.126% + +Keyset 'Sparse' - 2048-bit keys with up to 2 bits set - 2098177 keys +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 512.4, actual 513 (1.00x) (1) +Testing collisions (high 24-36 bits) - Worst is 36 bits: 38/32 (1.19x) +Testing collisions (low 32-bit) - Expected 512.4, actual 491 (0.96x) +Testing collisions (low 24-36 bits) - Worst is 34 bits: 134/128 (1.05x) +Testing distribution - Worst bias is the 18-bit window at bit 17 - 0.134% + + +[[[ Keyset 'Permutation' Tests ]]] + +Combination Lowbits Tests: +Keyset 'Combination' - up to 7 blocks from a set of 8 - 2396744 keys +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 668.6, actual 705 (1.05x) (37) +Testing collisions (high 24-37 bits) - Worst is 32 bits: 705/668 (1.05x) +Testing collisions (low 32-bit) - Expected 668.6, actual 619 (0.93x) +Testing collisions (low 24-37 bits) - Worst is 24 bits: 163754/163326 (1.00x) +Testing distribution - Worst bias is the 18-bit window at bit 19 - 0.061% + + +Combination Highbits Tests +Keyset 'Combination' - up to 7 blocks from a set of 8 - 2396744 keys +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 668.6, actual 662 (0.99x) (-6) +Testing collisions (high 24-37 bits) - Worst is 30 bits: 2701/2672 (1.01x) +Testing collisions (low 32-bit) - Expected 668.6, actual 689 (1.03x) (21) +Testing collisions (low 24-37 bits) - Worst is 36 bits: 48/41 (1.15x) +Testing distribution - Worst bias is the 18-bit window at bit 47 - 0.053% + + +Combination Hi-Lo Tests: +Keyset 'Combination' - up to 6 blocks from a set of 15 - 12204240 keys +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 17322.9, actual 17227 (0.99x) (-95) +Testing collisions (high 27-41 bits) - Worst is 27 bits: 538234/538415 (1.00x) +Testing collisions (low 32-bit) - Expected 17322.9, actual 17271 (1.00x) (-51) +Testing collisions (low 27-41 bits) - Worst is 41 bits: 43/33 (1.27x) +Testing distribution - Worst bias is the 20-bit window at bit 5 - 0.027% + + +Combination 0x8000000 Tests: +Keyset 'Combination' - up to 22 blocks from a set of 2 - 8388606 keys +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 8186.7, actual 8305 (1.01x) (119) +Testing collisions (high 26-40 bits) - Worst is 32 bits: 8305/8186 (1.01x) +Testing collisions (low 32-bit) - Expected 8186.7, actual 8198 (1.00x) (12) +Testing collisions (low 26-40 bits) - Worst is 34 bits: 2097/2047 (1.02x) +Testing distribution - Worst bias is the 20-bit window at bit 57 - 0.044% + + +Combination 0x0000001 Tests: +Keyset 'Combination' - up to 22 blocks from a set of 2 - 8388606 keys +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 8186.7, actual 8255 (1.01x) (69) +Testing collisions (high 26-40 bits) - Worst is 38 bits: 142/127 (1.11x) +Testing collisions (low 32-bit) - Expected 8186.7, actual 8105 (0.99x) (-81) +Testing collisions (low 26-40 bits) - Worst is 40 bits: 34/31 (1.06x) +Testing distribution - Worst bias is the 20-bit window at bit 48 - 0.036% + + +Combination 0x800000000000000 Tests: +Keyset 'Combination' - up to 22 blocks from a set of 2 - 8388606 keys +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 8186.7, actual 8071 (0.99x) (-115) +Testing collisions (high 26-40 bits) - Worst is 27 bits: 256884/256766 (1.00x) +Testing collisions (low 32-bit) - Expected 8186.7, actual 8209 (1.00x) (23) +Testing collisions (low 26-40 bits) - Worst is 40 bits: 38/31 (1.19x) +Testing distribution - Worst bias is the 20-bit window at bit 25 - 0.032% + + +Combination 0x000000000000001 Tests: +Keyset 'Combination' - up to 22 blocks from a set of 2 - 8388606 keys +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 8186.7, actual 8052 (0.98x) (-134) +Testing collisions (high 26-40 bits) - Worst is 35 bits: 1029/1023 (1.00x) +Testing collisions (low 32-bit) - Expected 8186.7, actual 8128 (0.99x) (-58) +Testing collisions (low 26-40 bits) - Worst is 40 bits: 40/31 (1.25x) +Testing distribution - Worst bias is the 20-bit window at bit 21 - 0.070% + + +Combination 16-bytes [0-1] Tests: +Keyset 'Combination' - up to 22 blocks from a set of 2 - 8388606 keys +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 8186.7, actual 8235 (1.01x) (49) +Testing collisions (high 26-40 bits) - Worst is 38 bits: 134/127 (1.05x) +Testing collisions (low 32-bit) - Expected 8186.7, actual 8072 (0.99x) (-114) +Testing collisions (low 26-40 bits) - Worst is 26 bits: 503674/503108 (1.00x) +Testing distribution - Worst bias is the 20-bit window at bit 54 - 0.046% + + +Combination 16-bytes [0-last] Tests: +Keyset 'Combination' - up to 22 blocks from a set of 2 - 8388606 keys +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 8186.7, actual 8207 (1.00x) (21) +Testing collisions (high 26-40 bits) - Worst is 34 bits: 2088/2047 (1.02x) +Testing collisions (low 32-bit) - Expected 8186.7, actual 8017 (0.98x) +Testing collisions (low 26-40 bits) - Worst is 40 bits: 37/31 (1.16x) +Testing distribution - Worst bias is the 20-bit window at bit 10 - 0.037% + + +Combination 32-bytes [0-1] Tests: +Keyset 'Combination' - up to 22 blocks from a set of 2 - 8388606 keys +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 8186.7, actual 8218 (1.00x) (32) +Testing collisions (high 26-40 bits) - Worst is 32 bits: 8218/8186 (1.00x) +Testing collisions (low 32-bit) - Expected 8186.7, actual 8233 (1.01x) (47) +Testing collisions (low 26-40 bits) - Worst is 38 bits: 152/127 (1.19x) +Testing distribution - Worst bias is the 20-bit window at bit 2 - 0.029% + + +Combination 32-bytes [0-last] Tests: +Keyset 'Combination' - up to 22 blocks from a set of 2 - 8388606 keys +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 8186.7, actual 8324 (1.02x) (138) +Testing collisions (high 26-40 bits) - Worst is 37 bits: 268/255 (1.05x) +Testing collisions (low 32-bit) - Expected 8186.7, actual 8099 (0.99x) (-87) +Testing collisions (low 26-40 bits) - Worst is 36 bits: 525/511 (1.03x) +Testing distribution - Worst bias is the 20-bit window at bit 13 - 0.044% + + +Combination 64-bytes [0-1] Tests: +Keyset 'Combination' - up to 22 blocks from a set of 2 - 8388606 keys +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 8186.7, actual 8033 (0.98x) (-153) +Testing collisions (high 26-40 bits) - Worst is 39 bits: 67/63 (1.05x) +Testing collisions (low 32-bit) - Expected 8186.7, actual 8293 (1.01x) (107) +Testing collisions (low 26-40 bits) - Worst is 37 bits: 274/255 (1.07x) +Testing distribution - Worst bias is the 20-bit window at bit 29 - 0.037% + + +Combination 64-bytes [0-last] Tests: +Keyset 'Combination' - up to 22 blocks from a set of 2 - 8388606 keys +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 8186.7, actual 8335 (1.02x) (149) +Testing collisions (high 26-40 bits) - Worst is 32 bits: 8335/8186 (1.02x) +Testing collisions (low 32-bit) - Expected 8186.7, actual 8163 (1.00x) (-23) +Testing collisions (low 26-40 bits) - Worst is 40 bits: 35/31 (1.09x) +Testing distribution - Worst bias is the 20-bit window at bit 20 - 0.033% + + +Combination 128-bytes [0-1] Tests: +Keyset 'Combination' - up to 22 blocks from a set of 2 - 8388606 keys +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 8186.7, actual 8238 (1.01x) (52) +Testing collisions (high 26-40 bits) - Worst is 31 bits: 16565/16362 (1.01x) +Testing collisions (low 32-bit) - Expected 8186.7, actual 8062 (0.98x) (-124) +Testing collisions (low 26-40 bits) - Worst is 37 bits: 262/255 (1.02x) +Testing distribution - Worst bias is the 20-bit window at bit 16 - 0.042% + + +Combination 128-bytes [0-last] Tests: +Keyset 'Combination' - up to 22 blocks from a set of 2 - 8388606 keys +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 8186.7, actual 8270 (1.01x) (84) +Testing collisions (high 26-40 bits) - Worst is 39 bits: 70/63 (1.09x) +Testing collisions (low 32-bit) - Expected 8186.7, actual 8144 (0.99x) (-42) +Testing collisions (low 26-40 bits) - Worst is 40 bits: 41/31 (1.28x) +Testing distribution - Worst bias is the 20-bit window at bit 29 - 0.042% + + +[[[ Keyset 'Window' Tests ]]] + +Keyset 'Window' - 32-bit key, 25-bit window - 32 tests, 33554432 keys per test +Window at 0 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Window at 1 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Window at 2 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Window at 3 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Window at 4 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Window at 5 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Window at 6 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Window at 7 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Window at 8 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Window at 9 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Window at 10 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Window at 11 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Window at 12 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Window at 13 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Window at 14 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Window at 15 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Window at 16 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Window at 17 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Window at 18 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Window at 19 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Window at 20 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Window at 21 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Window at 22 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Window at 23 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Window at 24 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Window at 25 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Window at 26 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Window at 27 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Window at 28 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Window at 29 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Window at 30 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Window at 31 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Window at 32 - Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) + +[[[ Keyset 'Cyclic' Tests ]]] + +Keyset 'Cyclic' - 8 cycles of 8 bytes - 1000000 keys +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 116.4, actual 129 (1.11x) (13) +Testing collisions (high 23-34 bits) - Worst is 34 bits: 35/29 (1.20x) +Testing collisions (low 32-bit) - Expected 116.4, actual 105 (0.90x) +Testing collisions (low 23-34 bits) - Worst is 26 bits: 7507/7413 (1.01x) +Testing distribution - Worst bias is the 17-bit window at bit 40 - 0.089% + +Keyset 'Cyclic' - 8 cycles of 9 bytes - 1000000 keys +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 116.4, actual 112 (0.96x) +Testing collisions (high 23-34 bits) - Worst is 28 bits: 1915/1860 (1.03x) +Testing collisions (low 32-bit) - Expected 116.4, actual 109 (0.94x) +Testing collisions (low 23-34 bits) - Worst is 30 bits: 472/465 (1.01x) +Testing distribution - Worst bias is the 17-bit window at bit 28 - 0.111% + +Keyset 'Cyclic' - 8 cycles of 10 bytes - 1000000 keys +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 116.4, actual 106 (0.91x) +Testing collisions (high 23-34 bits) - Worst is 25 bits: 14825/14754 (1.00x) +Testing collisions (low 32-bit) - Expected 116.4, actual 130 (1.12x) (14) +Testing collisions (low 23-34 bits) - Worst is 34 bits: 39/29 (1.34x) +Testing distribution - Worst bias is the 17-bit window at bit 51 - 0.114% + +Keyset 'Cyclic' - 8 cycles of 11 bytes - 1000000 keys +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 116.4, actual 110 (0.94x) +Testing collisions (high 23-34 bits) - Worst is 27 bits: 3703/3716 (1.00x) +Testing collisions (low 32-bit) - Expected 116.4, actual 105 (0.90x) +Testing collisions (low 23-34 bits) - Worst is 29 bits: 977/930 (1.05x) +Testing distribution - Worst bias is the 17-bit window at bit 32 - 0.120% + +Keyset 'Cyclic' - 8 cycles of 12 bytes - 1000000 keys +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 116.4, actual 107 (0.92x) +Testing collisions (high 23-34 bits) - Worst is 23 bits: 57386/57305 (1.00x) +Testing collisions (low 32-bit) - Expected 116.4, actual 132 (1.13x) (16) +Testing collisions (low 23-34 bits) - Worst is 34 bits: 40/29 (1.37x) +Testing distribution - Worst bias is the 17-bit window at bit 57 - 0.073% + +Keyset 'Cyclic' - 8 cycles of 16 bytes - 1000000 keys +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 116.4, actual 132 (1.13x) (16) +Testing collisions (high 23-34 bits) - Worst is 32 bits: 132/116 (1.13x) +Testing collisions (low 32-bit) - Expected 116.4, actual 118 (1.01x) (2) +Testing collisions (low 23-34 bits) - Worst is 32 bits: 118/116 (1.01x) +Testing distribution - Worst bias is the 17-bit window at bit 46 - 0.095% + + +[[[ Keyset 'TwoBytes' Tests ]]] + +Keyset 'TwoBytes' - up-to-4-byte keys, 652545 total keys +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 49.6, actual 53 (1.07x) (4) +Testing collisions (high 23-33 bits) - Worst is 32 bits: 53/49 (1.07x) +Testing collisions (low 32-bit) - Expected 49.6, actual 60 (1.21x) (11) +Testing collisions (low 23-33 bits) - Worst is 33 bits: 35/24 (1.41x) +Testing distribution - Worst bias is the 16-bit window at bit 21 - 0.145% + +Keyset 'TwoBytes' - up-to-8-byte keys, 5471025 total keys +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 3483.1, actual 3402 (0.98x) +Testing collisions (high 26-39 bits) - Worst is 29 bits: 27946/27781 (1.01x) +Testing collisions (low 32-bit) - Expected 3483.1, actual 3541 (1.02x) (58) +Testing collisions (low 26-39 bits) - Worst is 38 bits: 56/54 (1.03x) +Testing distribution - Worst bias is the 19-bit window at bit 30 - 0.034% + +Keyset 'TwoBytes' - up-to-12-byte keys, 18616785 total keys +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 40289.5, actual 39891 (0.99x) (-398) +Testing collisions (high 27-42 bits) - Worst is 27 bits: 1232174/1233446 (1.00x) +Testing collisions (low 32-bit) - Expected 40289.5, actual 40423 (1.00x) (134) +Testing collisions (low 27-42 bits) - Worst is 42 bits: 42/39 (1.07x) +Testing distribution - Worst bias is the 20-bit window at bit 6 - 0.016% + +Keyset 'TwoBytes' - up-to-16-byte keys, 44251425 total keys +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 227182.3, actual 227113 (1.00x) (-69) +Testing collisions (high 29-45 bits) - Worst is 45 bits: 32/27 (1.15x) +Testing collisions (low 32-bit) - Expected 227182.3, actual 227820 (1.00x) (638) +Testing collisions (low 29-45 bits) - Worst is 45 bits: 30/27 (1.08x) +Testing distribution - Worst bias is the 20-bit window at bit 11 - 0.006% + +Keyset 'TwoBytes' - up-to-20-byte keys, 86536545 total keys +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 865959.1, actual 866474 (1.00x) (515) +Testing collisions (high 30-47 bits) - Worst is 45 bits: 108/106 (1.01x) +Testing collisions (low 32-bit) - Expected 865959.1, actual 865864 (1.00x) (-95) +Testing collisions (low 30-47 bits) - Worst is 37 bits: 27468/27237 (1.01x) +Testing distribution - Worst bias is the 20-bit window at bit 18 - 0.005% + + +[[[ Keyset 'Text' Tests ]]] + +Keyset 'Text' - keys of form "FooXXXXBar" - 14776336 keys +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 25389.0, actual 25545 (1.01x) (156) +Testing collisions (high 27-42 bits) - Worst is 40 bits: 106/99 (1.07x) +Testing collisions (low 32-bit) - Expected 25389.0, actual 25253 (0.99x) (-136) +Testing collisions (low 27-42 bits) - Worst is 42 bits: 25/24 (1.01x) +Testing distribution - Worst bias is the 20-bit window at bit 51 - 0.027% + +Keyset 'Text' - keys of form "FooBarXXXX" - 14776336 keys +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 25389.0, actual 25219 (0.99x) (-170) +Testing collisions (high 27-42 bits) - Worst is 31 bits: 50616/50719 (1.00x) +Testing collisions (low 32-bit) - Expected 25389.0, actual 25549 (1.01x) (160) +Testing collisions (low 27-42 bits) - Worst is 40 bits: 106/99 (1.07x) +Testing distribution - Worst bias is the 20-bit window at bit 60 - 0.022% + +Keyset 'Text' - keys of form "XXXXFooBar" - 14776336 keys +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 25389.0, actual 25373 (1.00x) (-16) +Testing collisions (high 27-42 bits) - Worst is 42 bits: 29/24 (1.17x) +Testing collisions (low 32-bit) - Expected 25389.0, actual 25204 (0.99x) (-185) +Testing collisions (low 27-42 bits) - Worst is 39 bits: 221/198 (1.11x) +Testing distribution - Worst bias is the 20-bit window at bit 28 - 0.020% + +Keyset 'Words' - 4000000 random keys of len 6-16 from alnum charset +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 1862.1, actual 1800 (0.97x) +Testing collisions (high 25-38 bits) - Worst is 37 bits: 65/58 (1.12x) +Testing collisions (low 32-bit) - Expected 1862.1, actual 1819 (0.98x) +Testing collisions (low 25-38 bits) - Worst is 34 bits: 484/465 (1.04x) +Testing distribution - Worst bias is the 19-bit window at bit 63 - 0.041% + +Keyset 'Words' - 4000000 random keys of len 6-16 from password charset +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 1862.1, actual 1827 (0.98x) (-35) +Testing collisions (high 25-38 bits) - Worst is 35 bits: 237/232 (1.02x) +Testing collisions (low 32-bit) - Expected 1862.1, actual 1885 (1.01x) (23) +Testing collisions (low 25-38 bits) - Worst is 35 bits: 237/232 (1.02x) +Testing distribution - Worst bias is the 19-bit window at bit 60 - 0.059% + +Keyset 'Words' - 102774 dict words +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 1.2, actual 0 (0.00x) +Testing collisions (high 20-27 bits) - Worst is 27 bits: 46/39 (1.17x) +Testing collisions (low 32-bit) - Expected 1.2, actual 1 (0.81x) +Testing collisions (low 20-27 bits) - Worst is 25 bits: 173/157 (1.10x) +Testing distribution - Worst bias is the 14-bit window at bit 35 - 0.340% + + +[[[ Keyset 'Zeroes' Tests ]]] + +Keyset 'Zeroes' - 204800 keys +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 4.9, actual 6 (1.23x) (2) +Testing collisions (high 21-29 bits) - Worst is 25 bits: 651/623 (1.04x) +Testing collisions (low 32-bit) - Expected 4.9, actual 5 (1.02x) (1) +Testing collisions (low 21-29 bits) - Worst is 27 bits: 166/156 (1.06x) +Testing distribution - Worst bias is the 15-bit window at bit 55 - 0.298% + + +[[[ Keyset 'Seed' Tests ]]] + +Keyset 'Seed' - 5000000 keys +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 2909.3, actual 2790 (0.96x) +Testing collisions (high 26-39 bits) - Worst is 26 bits: 181311/181723 (1.00x) +Testing collisions (low 32-bit) - Expected 2909.3, actual 2885 (0.99x) (-24) +Testing collisions (low 26-39 bits) - Worst is 39 bits: 33/22 (1.45x) +Testing distribution - Worst bias is the 19-bit window at bit 55 - 0.051% + + +[[[ Keyset 'PerlinNoise' Tests ]]] + +Testing 16777216 coordinates (L2) : +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 32725.4, actual 32807 (1.00x) (82) +Testing collisions (high 27-42 bits) - Worst is 39 bits: 266/255 (1.04x) +Testing collisions (low 32-bit) - Expected 32725.4, actual 33071 (1.01x) (346) +Testing collisions (low 27-42 bits) - Worst is 42 bits: 44/31 (1.38x) + +Testing AV variant, 128 count with 4 spacing, 4-12: +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 1116.2, actual 1124 (1.01x) (8) +Testing collisions (high 25-37 bits) - Worst is 36 bits: 83/69 (1.19x) +Testing collisions (low 32-bit) - Expected 1116.2, actual 1026 (0.92x) +Testing collisions (low 25-37 bits) - Worst is 27 bits: 35507/35452 (1.00x) + + +[[[ Diff 'Differential' Tests ]]] + +Testing 8303632 up-to-5-bit differentials in 64-bit keys -> 64 bit hashes. +1000 reps, 8303632000 total tests, expecting 0.00 random collisions.......... +0 total collisions, of which 0 single collisions were ignored + +Testing 11017632 up-to-4-bit differentials in 128-bit keys -> 64 bit hashes. +1000 reps, 11017632000 total tests, expecting 0.00 random collisions.......... +0 total collisions, of which 0 single collisions were ignored + +Testing 2796416 up-to-3-bit differentials in 256-bit keys -> 64 bit hashes. +1000 reps, 2796416000 total tests, expecting 0.00 random collisions.......... +0 total collisions, of which 0 single collisions were ignored + + +[[[ DiffDist 'Differential Distribution' Tests ]]] + +Testing bit 0 +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 511.9, actual 530 (1.04x) (19) +Testing collisions (high 24-36 bits) - Worst is 33 bits: 273/255 (1.07x) +Testing collisions (low 32-bit) - Expected 511.9, actual 537 (1.05x) (26) +Testing collisions (low 24-36 bits) - Worst is 36 bits: 42/31 (1.31x) +Testing distribution - Worst bias is the 18-bit window at bit 10 - 0.083% + +Testing bit 1 +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 511.9, actual 518 (1.01x) (7) +Testing collisions (high 24-36 bits) - Worst is 32 bits: 518/511 (1.01x) +Testing collisions (low 32-bit) - Expected 511.9, actual 526 (1.03x) (15) +Testing collisions (low 24-36 bits) - Worst is 36 bits: 36/31 (1.13x) +Testing distribution - Worst bias is the 18-bit window at bit 63 - 0.070% + +Testing bit 2 +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 511.9, actual 524 (1.02x) (13) +Testing collisions (high 24-36 bits) - Worst is 31 bits: 1063/1023 (1.04x) +Testing collisions (low 32-bit) - Expected 511.9, actual 528 (1.03x) (17) +Testing collisions (low 24-36 bits) - Worst is 31 bits: 1062/1023 (1.04x) +Testing distribution - Worst bias is the 18-bit window at bit 10 - 0.078% + +Testing bit 3 +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 511.9, actual 531 (1.04x) (20) +Testing collisions (high 24-36 bits) - Worst is 35 bits: 68/63 (1.06x) +Testing collisions (low 32-bit) - Expected 511.9, actual 494 (0.97x) +Testing collisions (low 24-36 bits) - Worst is 36 bits: 38/31 (1.19x) +Testing distribution - Worst bias is the 18-bit window at bit 14 - 0.059% + +Testing bit 4 +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 511.9, actual 519 (1.01x) (8) +Testing collisions (high 24-36 bits) - Worst is 35 bits: 72/63 (1.13x) +Testing collisions (low 32-bit) - Expected 511.9, actual 492 (0.96x) +Testing collisions (low 24-36 bits) - Worst is 24 bits: 125969/125777 (1.00x) +Testing distribution - Worst bias is the 18-bit window at bit 6 - 0.068% + +Testing bit 5 +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 511.9, actual 498 (0.97x) +Testing collisions (high 24-36 bits) - Worst is 36 bits: 35/31 (1.09x) +Testing collisions (low 32-bit) - Expected 511.9, actual 504 (0.98x) (-7) +Testing collisions (low 24-36 bits) - Worst is 35 bits: 75/63 (1.17x) +Testing distribution - Worst bias is the 18-bit window at bit 39 - 0.077% + +Testing bit 6 +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 511.9, actual 477 (0.93x) +Testing collisions (high 24-36 bits) - Worst is 34 bits: 134/127 (1.05x) +Testing collisions (low 32-bit) - Expected 511.9, actual 465 (0.91x) +Testing collisions (low 24-36 bits) - Worst is 26 bits: 32805/32429 (1.01x) +Testing distribution - Worst bias is the 18-bit window at bit 34 - 0.086% + +Testing bit 7 +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 511.9, actual 514 (1.00x) (3) +Testing collisions (high 24-36 bits) - Worst is 36 bits: 34/31 (1.06x) +Testing collisions (low 32-bit) - Expected 511.9, actual 498 (0.97x) +Testing collisions (low 24-36 bits) - Worst is 36 bits: 48/31 (1.50x) +Testing distribution - Worst bias is the 18-bit window at bit 33 - 0.090% + +Testing bit 8 +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 511.9, actual 474 (0.93x) +Testing collisions (high 24-36 bits) - Worst is 29 bits: 4132/4090 (1.01x) +Testing collisions (low 32-bit) - Expected 511.9, actual 483 (0.94x) +Testing collisions (low 24-36 bits) - Worst is 28 bits: 8305/8170 (1.02x) +Testing distribution - Worst bias is the 16-bit window at bit 37 - 0.063% + +Testing bit 9 +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 511.9, actual 481 (0.94x) +Testing collisions (high 24-36 bits) - Worst is 31 bits: 1048/1023 (1.02x) +Testing collisions (low 32-bit) - Expected 511.9, actual 501 (0.98x) +Testing collisions (low 24-36 bits) - Worst is 35 bits: 68/63 (1.06x) +Testing distribution - Worst bias is the 18-bit window at bit 24 - 0.065% + +Testing bit 10 +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 511.9, actual 504 (0.98x) (-7) +Testing collisions (high 24-36 bits) - Worst is 36 bits: 37/31 (1.16x) +Testing collisions (low 32-bit) - Expected 511.9, actual 494 (0.97x) +Testing collisions (low 24-36 bits) - Worst is 36 bits: 36/31 (1.13x) +Testing distribution - Worst bias is the 18-bit window at bit 43 - 0.068% + +Testing bit 11 +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 511.9, actual 509 (0.99x) (-2) +Testing collisions (high 24-36 bits) - Worst is 34 bits: 140/127 (1.09x) +Testing collisions (low 32-bit) - Expected 511.9, actual 572 (1.12x) (61) +Testing collisions (low 24-36 bits) - Worst is 32 bits: 572/511 (1.12x) +Testing distribution - Worst bias is the 18-bit window at bit 51 - 0.091% + +Testing bit 12 +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 511.9, actual 486 (0.95x) +Testing collisions (high 24-36 bits) - Worst is 24 bits: 125213/125777 (1.00x) +Testing collisions (low 32-bit) - Expected 511.9, actual 520 (1.02x) (9) +Testing collisions (low 24-36 bits) - Worst is 31 bits: 1043/1023 (1.02x) +Testing distribution - Worst bias is the 18-bit window at bit 6 - 0.083% + +Testing bit 13 +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 511.9, actual 547 (1.07x) (36) +Testing collisions (high 24-36 bits) - Worst is 35 bits: 78/63 (1.22x) +Testing collisions (low 32-bit) - Expected 511.9, actual 515 (1.01x) (4) +Testing collisions (low 24-36 bits) - Worst is 36 bits: 35/31 (1.09x) +Testing distribution - Worst bias is the 18-bit window at bit 32 - 0.074% + +Testing bit 14 +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 511.9, actual 457 (0.89x) +Testing collisions (high 24-36 bits) - Worst is 35 bits: 72/63 (1.13x) +Testing collisions (low 32-bit) - Expected 511.9, actual 509 (0.99x) (-2) +Testing collisions (low 24-36 bits) - Worst is 36 bits: 35/31 (1.09x) +Testing distribution - Worst bias is the 18-bit window at bit 24 - 0.084% + +Testing bit 15 +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 511.9, actual 510 (1.00x) (-1) +Testing collisions (high 24-36 bits) - Worst is 30 bits: 2133/2046 (1.04x) +Testing collisions (low 32-bit) - Expected 511.9, actual 476 (0.93x) +Testing collisions (low 24-36 bits) - Worst is 36 bits: 37/31 (1.16x) +Testing distribution - Worst bias is the 18-bit window at bit 52 - 0.071% + +Testing bit 16 +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 511.9, actual 503 (0.98x) (-8) +Testing collisions (high 24-36 bits) - Worst is 28 bits: 8161/8170 (1.00x) +Testing collisions (low 32-bit) - Expected 511.9, actual 508 (0.99x) (-3) +Testing collisions (low 24-36 bits) - Worst is 36 bits: 38/31 (1.19x) +Testing distribution - Worst bias is the 18-bit window at bit 49 - 0.062% + +Testing bit 17 +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 511.9, actual 513 (1.00x) (2) +Testing collisions (high 24-36 bits) - Worst is 36 bits: 41/31 (1.28x) +Testing collisions (low 32-bit) - Expected 511.9, actual 520 (1.02x) (9) +Testing collisions (low 24-36 bits) - Worst is 33 bits: 279/255 (1.09x) +Testing distribution - Worst bias is the 18-bit window at bit 50 - 0.070% + +Testing bit 18 +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 511.9, actual 546 (1.07x) (35) +Testing collisions (high 24-36 bits) - Worst is 33 bits: 283/255 (1.11x) +Testing collisions (low 32-bit) - Expected 511.9, actual 487 (0.95x) +Testing collisions (low 24-36 bits) - Worst is 28 bits: 8185/8170 (1.00x) +Testing distribution - Worst bias is the 18-bit window at bit 22 - 0.058% + +Testing bit 19 +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 511.9, actual 514 (1.00x) (3) +Testing collisions (high 24-36 bits) - Worst is 36 bits: 37/31 (1.16x) +Testing collisions (low 32-bit) - Expected 511.9, actual 498 (0.97x) +Testing collisions (low 24-36 bits) - Worst is 24 bits: 125639/125777 (1.00x) +Testing distribution - Worst bias is the 18-bit window at bit 54 - 0.082% + +Testing bit 20 +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 511.9, actual 525 (1.03x) (14) +Testing collisions (high 24-36 bits) - Worst is 35 bits: 74/63 (1.16x) +Testing collisions (low 32-bit) - Expected 511.9, actual 541 (1.06x) (30) +Testing collisions (low 24-36 bits) - Worst is 30 bits: 2173/2046 (1.06x) +Testing distribution - Worst bias is the 18-bit window at bit 10 - 0.062% + +Testing bit 21 +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 511.9, actual 510 (1.00x) (-1) +Testing collisions (high 24-36 bits) - Worst is 33 bits: 275/255 (1.07x) +Testing collisions (low 32-bit) - Expected 511.9, actual 517 (1.01x) (6) +Testing collisions (low 24-36 bits) - Worst is 34 bits: 135/127 (1.05x) +Testing distribution - Worst bias is the 17-bit window at bit 29 - 0.059% + +Testing bit 22 +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 511.9, actual 505 (0.99x) (-6) +Testing collisions (high 24-36 bits) - Worst is 27 bits: 16472/16298 (1.01x) +Testing collisions (low 32-bit) - Expected 511.9, actual 529 (1.03x) (18) +Testing collisions (low 24-36 bits) - Worst is 36 bits: 39/31 (1.22x) +Testing distribution - Worst bias is the 18-bit window at bit 45 - 0.071% + +Testing bit 23 +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 511.9, actual 526 (1.03x) (15) +Testing collisions (high 24-36 bits) - Worst is 36 bits: 38/31 (1.19x) +Testing collisions (low 32-bit) - Expected 511.9, actual 542 (1.06x) (31) +Testing collisions (low 24-36 bits) - Worst is 36 bits: 43/31 (1.34x) +Testing distribution - Worst bias is the 18-bit window at bit 57 - 0.070% + +Testing bit 24 +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 511.9, actual 515 (1.01x) (4) +Testing collisions (high 24-36 bits) - Worst is 36 bits: 38/31 (1.19x) +Testing collisions (low 32-bit) - Expected 511.9, actual 532 (1.04x) (21) +Testing collisions (low 24-36 bits) - Worst is 36 bits: 38/31 (1.19x) +Testing distribution - Worst bias is the 18-bit window at bit 50 - 0.060% + +Testing bit 25 +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 511.9, actual 511 (1.00x) +Testing collisions (high 24-36 bits) - Worst is 28 bits: 8353/8170 (1.02x) +Testing collisions (low 32-bit) - Expected 511.9, actual 497 (0.97x) +Testing collisions (low 24-36 bits) - Worst is 36 bits: 42/31 (1.31x) +Testing distribution - Worst bias is the 18-bit window at bit 4 - 0.067% + +Testing bit 26 +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 511.9, actual 499 (0.97x) +Testing collisions (high 24-36 bits) - Worst is 34 bits: 132/127 (1.03x) +Testing collisions (low 32-bit) - Expected 511.9, actual 538 (1.05x) (27) +Testing collisions (low 24-36 bits) - Worst is 36 bits: 40/31 (1.25x) +Testing distribution - Worst bias is the 18-bit window at bit 24 - 0.073% + +Testing bit 27 +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 511.9, actual 533 (1.04x) (22) +Testing collisions (high 24-36 bits) - Worst is 32 bits: 533/511 (1.04x) +Testing collisions (low 32-bit) - Expected 511.9, actual 485 (0.95x) +Testing collisions (low 24-36 bits) - Worst is 36 bits: 36/31 (1.13x) +Testing distribution - Worst bias is the 18-bit window at bit 10 - 0.105% + +Testing bit 28 +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 511.9, actual 532 (1.04x) (21) +Testing collisions (high 24-36 bits) - Worst is 34 bits: 136/127 (1.06x) +Testing collisions (low 32-bit) - Expected 511.9, actual 506 (0.99x) (-5) +Testing collisions (low 24-36 bits) - Worst is 33 bits: 265/255 (1.04x) +Testing distribution - Worst bias is the 18-bit window at bit 63 - 0.073% + +Testing bit 29 +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 511.9, actual 494 (0.97x) +Testing collisions (high 24-36 bits) - Worst is 29 bits: 4145/4090 (1.01x) +Testing collisions (low 32-bit) - Expected 511.9, actual 492 (0.96x) +Testing collisions (low 24-36 bits) - Worst is 36 bits: 35/31 (1.09x) +Testing distribution - Worst bias is the 18-bit window at bit 51 - 0.081% + +Testing bit 30 +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 511.9, actual 505 (0.99x) (-6) +Testing collisions (high 24-36 bits) - Worst is 26 bits: 32853/32429 (1.01x) +Testing collisions (low 32-bit) - Expected 511.9, actual 500 (0.98x) +Testing collisions (low 24-36 bits) - Worst is 30 bits: 2081/2046 (1.02x) +Testing distribution - Worst bias is the 18-bit window at bit 4 - 0.073% + +Testing bit 31 +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 511.9, actual 503 (0.98x) (-8) +Testing collisions (high 24-36 bits) - Worst is 36 bits: 37/31 (1.16x) +Testing collisions (low 32-bit) - Expected 511.9, actual 503 (0.98x) (-8) +Testing collisions (low 24-36 bits) - Worst is 26 bits: 32504/32429 (1.00x) +Testing distribution - Worst bias is the 18-bit window at bit 48 - 0.068% + +Testing bit 32 +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 511.9, actual 536 (1.05x) (25) +Testing collisions (high 24-36 bits) - Worst is 35 bits: 69/63 (1.08x) +Testing collisions (low 32-bit) - Expected 511.9, actual 526 (1.03x) (15) +Testing collisions (low 24-36 bits) - Worst is 33 bits: 274/255 (1.07x) +Testing distribution - Worst bias is the 18-bit window at bit 29 - 0.072% + +Testing bit 33 +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 511.9, actual 500 (0.98x) +Testing collisions (high 24-36 bits) - Worst is 36 bits: 39/31 (1.22x) +Testing collisions (low 32-bit) - Expected 511.9, actual 562 (1.10x) (51) +Testing collisions (low 24-36 bits) - Worst is 33 bits: 292/255 (1.14x) +Testing distribution - Worst bias is the 18-bit window at bit 19 - 0.116% + +Testing bit 34 +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 511.9, actual 569 (1.11x) (58) +Testing collisions (high 24-36 bits) - Worst is 34 bits: 151/127 (1.18x) +Testing collisions (low 32-bit) - Expected 511.9, actual 529 (1.03x) (18) +Testing collisions (low 24-36 bits) - Worst is 31 bits: 1087/1023 (1.06x) +Testing distribution - Worst bias is the 18-bit window at bit 42 - 0.070% + +Testing bit 35 +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 511.9, actual 518 (1.01x) (7) +Testing collisions (high 24-36 bits) - Worst is 29 bits: 4155/4090 (1.02x) +Testing collisions (low 32-bit) - Expected 511.9, actual 546 (1.07x) (35) +Testing collisions (low 24-36 bits) - Worst is 34 bits: 149/127 (1.16x) +Testing distribution - Worst bias is the 18-bit window at bit 9 - 0.097% + +Testing bit 36 +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 511.9, actual 519 (1.01x) (8) +Testing collisions (high 24-36 bits) - Worst is 31 bits: 1084/1023 (1.06x) +Testing collisions (low 32-bit) - Expected 511.9, actual 502 (0.98x) (-9) +Testing collisions (low 24-36 bits) - Worst is 36 bits: 35/31 (1.09x) +Testing distribution - Worst bias is the 18-bit window at bit 59 - 0.069% + +Testing bit 37 +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 511.9, actual 542 (1.06x) (31) +Testing collisions (high 24-36 bits) - Worst is 35 bits: 81/63 (1.27x) +Testing collisions (low 32-bit) - Expected 511.9, actual 544 (1.06x) (33) +Testing collisions (low 24-36 bits) - Worst is 36 bits: 37/31 (1.16x) +Testing distribution - Worst bias is the 17-bit window at bit 36 - 0.069% + +Testing bit 38 +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 511.9, actual 516 (1.01x) (5) +Testing collisions (high 24-36 bits) - Worst is 35 bits: 67/63 (1.05x) +Testing collisions (low 32-bit) - Expected 511.9, actual 486 (0.95x) +Testing collisions (low 24-36 bits) - Worst is 27 bits: 16424/16298 (1.01x) +Testing distribution - Worst bias is the 18-bit window at bit 16 - 0.116% + +Testing bit 39 +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 511.9, actual 516 (1.01x) (5) +Testing collisions (high 24-36 bits) - Worst is 34 bits: 139/127 (1.09x) +Testing collisions (low 32-bit) - Expected 511.9, actual 536 (1.05x) (25) +Testing collisions (low 24-36 bits) - Worst is 35 bits: 70/63 (1.09x) +Testing distribution - Worst bias is the 18-bit window at bit 43 - 0.066% + +Testing bit 40 +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 511.9, actual 533 (1.04x) (22) +Testing collisions (high 24-36 bits) - Worst is 36 bits: 35/31 (1.09x) +Testing collisions (low 32-bit) - Expected 511.9, actual 540 (1.05x) (29) +Testing collisions (low 24-36 bits) - Worst is 36 bits: 40/31 (1.25x) +Testing distribution - Worst bias is the 18-bit window at bit 25 - 0.065% + +Testing bit 41 +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 511.9, actual 501 (0.98x) +Testing collisions (high 24-36 bits) - Worst is 36 bits: 33/31 (1.03x) +Testing collisions (low 32-bit) - Expected 511.9, actual 548 (1.07x) (37) +Testing collisions (low 24-36 bits) - Worst is 33 bits: 277/255 (1.08x) +Testing distribution - Worst bias is the 18-bit window at bit 60 - 0.091% + +Testing bit 42 +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 511.9, actual 516 (1.01x) (5) +Testing collisions (high 24-36 bits) - Worst is 36 bits: 39/31 (1.22x) +Testing collisions (low 32-bit) - Expected 511.9, actual 553 (1.08x) (42) +Testing collisions (low 24-36 bits) - Worst is 32 bits: 553/511 (1.08x) +Testing distribution - Worst bias is the 18-bit window at bit 23 - 0.057% + +Testing bit 43 +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 511.9, actual 545 (1.06x) (34) +Testing collisions (high 24-36 bits) - Worst is 32 bits: 545/511 (1.06x) +Testing collisions (low 32-bit) - Expected 511.9, actual 497 (0.97x) +Testing collisions (low 24-36 bits) - Worst is 28 bits: 8234/8170 (1.01x) +Testing distribution - Worst bias is the 18-bit window at bit 61 - 0.090% + +Testing bit 44 +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 511.9, actual 461 (0.90x) +Testing collisions (high 24-36 bits) - Worst is 24 bits: 125866/125777 (1.00x) +Testing collisions (low 32-bit) - Expected 511.9, actual 526 (1.03x) (15) +Testing collisions (low 24-36 bits) - Worst is 36 bits: 38/31 (1.19x) +Testing distribution - Worst bias is the 18-bit window at bit 10 - 0.043% + +Testing bit 45 +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 511.9, actual 551 (1.08x) (40) +Testing collisions (high 24-36 bits) - Worst is 32 bits: 551/511 (1.08x) +Testing collisions (low 32-bit) - Expected 511.9, actual 512 (1.00x) (1) +Testing collisions (low 24-36 bits) - Worst is 35 bits: 65/63 (1.02x) +Testing distribution - Worst bias is the 17-bit window at bit 52 - 0.060% + +Testing bit 46 +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 511.9, actual 506 (0.99x) (-5) +Testing collisions (high 24-36 bits) - Worst is 35 bits: 67/63 (1.05x) +Testing collisions (low 32-bit) - Expected 511.9, actual 490 (0.96x) +Testing collisions (low 24-36 bits) - Worst is 36 bits: 36/31 (1.13x) +Testing distribution - Worst bias is the 18-bit window at bit 21 - 0.079% + +Testing bit 47 +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 511.9, actual 536 (1.05x) (25) +Testing collisions (high 24-36 bits) - Worst is 33 bits: 282/255 (1.10x) +Testing collisions (low 32-bit) - Expected 511.9, actual 483 (0.94x) +Testing collisions (low 24-36 bits) - Worst is 35 bits: 70/63 (1.09x) +Testing distribution - Worst bias is the 18-bit window at bit 9 - 0.079% + +Testing bit 48 +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 511.9, actual 527 (1.03x) (16) +Testing collisions (high 24-36 bits) - Worst is 35 bits: 72/63 (1.13x) +Testing collisions (low 32-bit) - Expected 511.9, actual 503 (0.98x) (-8) +Testing collisions (low 24-36 bits) - Worst is 35 bits: 70/63 (1.09x) +Testing distribution - Worst bias is the 18-bit window at bit 56 - 0.081% + +Testing bit 49 +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 511.9, actual 560 (1.09x) (49) +Testing collisions (high 24-36 bits) - Worst is 35 bits: 74/63 (1.16x) +Testing collisions (low 32-bit) - Expected 511.9, actual 523 (1.02x) (12) +Testing collisions (low 24-36 bits) - Worst is 33 bits: 263/255 (1.03x) +Testing distribution - Worst bias is the 17-bit window at bit 41 - 0.058% + +Testing bit 50 +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 511.9, actual 522 (1.02x) (11) +Testing collisions (high 24-36 bits) - Worst is 33 bits: 271/255 (1.06x) +Testing collisions (low 32-bit) - Expected 511.9, actual 519 (1.01x) (8) +Testing collisions (low 24-36 bits) - Worst is 36 bits: 40/31 (1.25x) +Testing distribution - Worst bias is the 18-bit window at bit 39 - 0.079% + +Testing bit 51 +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 511.9, actual 531 (1.04x) (20) +Testing collisions (high 24-36 bits) - Worst is 35 bits: 76/63 (1.19x) +Testing collisions (low 32-bit) - Expected 511.9, actual 489 (0.96x) +Testing collisions (low 24-36 bits) - Worst is 29 bits: 4122/4090 (1.01x) +Testing distribution - Worst bias is the 18-bit window at bit 6 - 0.071% + +Testing bit 52 +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 511.9, actual 512 (1.00x) (1) +Testing collisions (high 24-36 bits) - Worst is 35 bits: 67/63 (1.05x) +Testing collisions (low 32-bit) - Expected 511.9, actual 562 (1.10x) (51) +Testing collisions (low 24-36 bits) - Worst is 36 bits: 46/31 (1.44x) +Testing distribution - Worst bias is the 18-bit window at bit 15 - 0.055% + +Testing bit 53 +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 511.9, actual 501 (0.98x) +Testing collisions (high 24-36 bits) - Worst is 29 bits: 4137/4090 (1.01x) +Testing collisions (low 32-bit) - Expected 511.9, actual 535 (1.05x) (24) +Testing collisions (low 24-36 bits) - Worst is 32 bits: 535/511 (1.05x) +Testing distribution - Worst bias is the 18-bit window at bit 62 - 0.093% + +Testing bit 54 +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 511.9, actual 465 (0.91x) +Testing collisions (high 24-36 bits) - Worst is 27 bits: 16437/16298 (1.01x) +Testing collisions (low 32-bit) - Expected 511.9, actual 515 (1.01x) (4) +Testing collisions (low 24-36 bits) - Worst is 34 bits: 139/127 (1.09x) +Testing distribution - Worst bias is the 18-bit window at bit 46 - 0.076% + +Testing bit 55 +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 511.9, actual 555 (1.08x) (44) +Testing collisions (high 24-36 bits) - Worst is 36 bits: 39/31 (1.22x) +Testing collisions (low 32-bit) - Expected 511.9, actual 491 (0.96x) +Testing collisions (low 24-36 bits) - Worst is 34 bits: 138/127 (1.08x) +Testing distribution - Worst bias is the 18-bit window at bit 52 - 0.074% + +Testing bit 56 +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 511.9, actual 539 (1.05x) (28) +Testing collisions (high 24-36 bits) - Worst is 36 bits: 36/31 (1.13x) +Testing collisions (low 32-bit) - Expected 511.9, actual 533 (1.04x) (22) +Testing collisions (low 24-36 bits) - Worst is 36 bits: 39/31 (1.22x) +Testing distribution - Worst bias is the 18-bit window at bit 31 - 0.108% + +Testing bit 57 +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 511.9, actual 513 (1.00x) (2) +Testing collisions (high 24-36 bits) - Worst is 33 bits: 272/255 (1.06x) +Testing collisions (low 32-bit) - Expected 511.9, actual 522 (1.02x) (11) +Testing collisions (low 24-36 bits) - Worst is 35 bits: 68/63 (1.06x) +Testing distribution - Worst bias is the 18-bit window at bit 8 - 0.114% + +Testing bit 58 +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 511.9, actual 514 (1.00x) (3) +Testing collisions (high 24-36 bits) - Worst is 28 bits: 8230/8170 (1.01x) +Testing collisions (low 32-bit) - Expected 511.9, actual 545 (1.06x) (34) +Testing collisions (low 24-36 bits) - Worst is 36 bits: 43/31 (1.34x) +Testing distribution - Worst bias is the 18-bit window at bit 7 - 0.092% + +Testing bit 59 +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 511.9, actual 500 (0.98x) +Testing collisions (high 24-36 bits) - Worst is 30 bits: 2120/2046 (1.04x) +Testing collisions (low 32-bit) - Expected 511.9, actual 503 (0.98x) (-8) +Testing collisions (low 24-36 bits) - Worst is 35 bits: 69/63 (1.08x) +Testing distribution - Worst bias is the 18-bit window at bit 31 - 0.065% + +Testing bit 60 +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 511.9, actual 496 (0.97x) +Testing collisions (high 24-36 bits) - Worst is 35 bits: 65/63 (1.02x) +Testing collisions (low 32-bit) - Expected 511.9, actual 486 (0.95x) +Testing collisions (low 24-36 bits) - Worst is 29 bits: 4218/4090 (1.03x) +Testing distribution - Worst bias is the 18-bit window at bit 22 - 0.081% + +Testing bit 61 +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 511.9, actual 520 (1.02x) (9) +Testing collisions (high 24-36 bits) - Worst is 35 bits: 67/63 (1.05x) +Testing collisions (low 32-bit) - Expected 511.9, actual 486 (0.95x) +Testing collisions (low 24-36 bits) - Worst is 36 bits: 39/31 (1.22x) +Testing distribution - Worst bias is the 18-bit window at bit 6 - 0.064% + +Testing bit 62 +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 511.9, actual 522 (1.02x) (11) +Testing collisions (high 24-36 bits) - Worst is 32 bits: 522/511 (1.02x) +Testing collisions (low 32-bit) - Expected 511.9, actual 533 (1.04x) (22) +Testing collisions (low 24-36 bits) - Worst is 36 bits: 35/31 (1.09x) +Testing distribution - Worst bias is the 18-bit window at bit 52 - 0.084% + +Testing bit 63 +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 511.9, actual 498 (0.97x) +Testing collisions (high 24-36 bits) - Worst is 36 bits: 34/31 (1.06x) +Testing collisions (low 32-bit) - Expected 511.9, actual 496 (0.97x) +Testing collisions (low 24-36 bits) - Worst is 30 bits: 2053/2046 (1.00x) +Testing distribution - Worst bias is the 18-bit window at bit 53 - 0.104% + + +[[[ MomentChi2 Tests ]]] + +Analyze hashes produced from a serie of linearly increasing numbers of 32-bit, using a step of 2 ... +Target values to approximate : 38918200.000000 - 273633.333333 +4 threads starting... done +Popcount 1 stats : 38918835.315783 - 273623.606930 +Popcount 0 stats : 38918226.200577 - 273605.413971 +MomentChi2 for bits 1 : 0.737544 +MomentChi2 for bits 0 : 0.00125443 + +Derivative stats (transition from 2 consecutive values) : +Popcount 1 stats : 38919083.803108 - 273655.817065 +Popcount 0 stats : 38919331.387736 - 273661.792772 +MomentChi2 for deriv b1 : 1.42723 +MomentChi2 for deriv b0 : 2.33884 + + Great + + +[[[ Prng Tests ]]] + +Generating 33554432 random numbers : +Testing collisions ( 64-bit) - Expected 0.0, actual 0 (0.00x) +Testing collisions (high 32-bit) - Expected 130731.3, actual 131908 (1.01x) (1177) +Testing collisions (high 28-44 bits) - Worst is 37 bits: 4198/4095 (1.02x) +Testing collisions (low 32-bit) - Expected 130731.3, actual 131430 (1.01x) (699) +Testing collisions (low 28-44 bits) - Worst is 42 bits: 144/127 (1.13x) + +[[[ BadSeeds Tests ]]] + +Testing 0 internal secrets: +0x0 PASS + + +Input vcode 0x00000001, Output vcode 0x00000001, Result vcode 0x00000001 +Verification value is 0x00000001 - Testing took 666.859625 seconds +------------------------------------------------------------------------------- diff --git a/src/fallback_hash.rs b/src/fallback_hash.rs index 8fd3859..4572383 100644 --- a/src/fallback_hash.rs +++ b/src/fallback_hash.rs @@ -1,5 +1,5 @@ use crate::convert::*; -use crate::operations::folded_multiply; +use crate::operations::{add_by_64s, folded_multiply}; use crate::operations::read_small; use crate::operations::MULTIPLE; use crate::random_state::PI; @@ -130,11 +130,10 @@ impl AHasher { #[cfg(not(feature = "folded_multiply"))] fn large_update(&mut self, new_data: u128) { use crate::operations::INCREMENT; - - let block: [u64; 2] = new_data.convert(); - self.buffer = (self.buffer ^ block[0]).wrapping_mul(self.pad ^ self.extra_keys[0] ^ block[1].swap_bytes()); //Changing rather than fixed multiple removes linearity - self.buffer = (self.buffer ^ block[1]).wrapping_mul(self.pad ^ self.extra_keys[1] ^ block[0].swap_bytes()); //Reversing bytes prevents low impact high order bits. - self.buffer ^= self.buffer >> 47; // xorshift some good bits to the bottom + let block = add_by_64s(new_data.convert(), self.extra_keys); + let b1 = (self.buffer ^ block[0]).wrapping_mul(self.pad ^ block[1].swap_bytes()); //Changing rather than fixed multiple removes linearity + let b2 = (self.buffer.swap_bytes() ^ block[1]).wrapping_mul(self.pad ^ block[0].swap_bytes()); //Reversing bytes prevents low impact high order bits. + self.buffer = b1 ^ b2; self.pad = self.pad.wrapping_add(INCREMENT); } From ed38252a15b011810ea42334c23faded2b24b237 Mon Sep 17 00:00:00 2001 From: Tom Kaitchuck Date: Mon, 24 Oct 2022 17:02:56 -0700 Subject: [PATCH 23/24] Use psudo-folded-multiply to simplify code Signed-off-by: Tom Kaitchuck --- smhasher/fallbackNoFoldedOutput.txt | 86 ++++++++++++++--------------- src/fallback_hash.rs | 39 +------------ src/operations.rs | 8 +++ 3 files changed, 52 insertions(+), 81 deletions(-) diff --git a/smhasher/fallbackNoFoldedOutput.txt b/smhasher/fallbackNoFoldedOutput.txt index 8f63d7d..8b6040b 100644 --- a/smhasher/fallbackNoFoldedOutput.txt +++ b/smhasher/fallbackNoFoldedOutput.txt @@ -1,4 +1,4 @@ ------------------------------------------------------------------------------- +------------------------------------------------------------------------------- --- Testing ahash64 "ahash 64bit" GOOD [[[ Sanity Tests ]]] @@ -10,58 +10,58 @@ Running AppendedZeroesTest .......... PASS [[[ Speed Tests ]]] Bulk speed test - 262144-byte keys -Alignment 7 - 18.981 bytes/cycle - 54306.12 MiB/sec @ 3 ghz -Alignment 6 - 19.062 bytes/cycle - 54536.99 MiB/sec @ 3 ghz -Alignment 5 - 18.974 bytes/cycle - 54284.96 MiB/sec @ 3 ghz -Alignment 4 - 19.039 bytes/cycle - 54470.49 MiB/sec @ 3 ghz -Alignment 3 - 19.129 bytes/cycle - 54729.57 MiB/sec @ 3 ghz -Alignment 2 - 19.126 bytes/cycle - 54720.48 MiB/sec @ 3 ghz -Alignment 1 - 18.990 bytes/cycle - 54332.12 MiB/sec @ 3 ghz -Alignment 0 - 19.801 bytes/cycle - 56652.53 MiB/sec @ 3 ghz -Average - 19.138 bytes/cycle - 54754.16 MiB/sec @ 3 ghz +Alignment 7 - 18.833 bytes/cycle - 53881.77 MiB/sec @ 3 ghz +Alignment 6 - 18.835 bytes/cycle - 53887.84 MiB/sec @ 3 ghz +Alignment 5 - 18.835 bytes/cycle - 53888.73 MiB/sec @ 3 ghz +Alignment 4 - 19.338 bytes/cycle - 55327.26 MiB/sec @ 3 ghz +Alignment 3 - 19.422 bytes/cycle - 55566.47 MiB/sec @ 3 ghz +Alignment 2 - 19.475 bytes/cycle - 55717.32 MiB/sec @ 3 ghz +Alignment 1 - 19.266 bytes/cycle - 55120.73 MiB/sec @ 3 ghz +Alignment 0 - 19.932 bytes/cycle - 57026.80 MiB/sec @ 3 ghz +Average - 19.242 bytes/cycle - 55052.12 MiB/sec @ 3 ghz Small key speed test - 1-byte keys - 33.00 cycles/hash Small key speed test - 2-byte keys - 33.00 cycles/hash -Small key speed test - 3-byte keys - 31.58 cycles/hash -Small key speed test - 4-byte keys - 31.46 cycles/hash -Small key speed test - 5-byte keys - 31.53 cycles/hash -Small key speed test - 6-byte keys - 32.89 cycles/hash +Small key speed test - 3-byte keys - 32.02 cycles/hash +Small key speed test - 4-byte keys - 30.73 cycles/hash +Small key speed test - 5-byte keys - 30.81 cycles/hash +Small key speed test - 6-byte keys - 31.95 cycles/hash Small key speed test - 7-byte keys - 33.00 cycles/hash Small key speed test - 8-byte keys - 33.00 cycles/hash -Small key speed test - 9-byte keys - 32.00 cycles/hash -Small key speed test - 10-byte keys - 32.00 cycles/hash -Small key speed test - 11-byte keys - 31.64 cycles/hash -Small key speed test - 12-byte keys - 31.31 cycles/hash -Small key speed test - 13-byte keys - 31.42 cycles/hash -Small key speed test - 14-byte keys - 31.65 cycles/hash -Small key speed test - 15-byte keys - 32.00 cycles/hash -Small key speed test - 16-byte keys - 32.00 cycles/hash -Small key speed test - 17-byte keys - 33.90 cycles/hash -Small key speed test - 18-byte keys - 33.66 cycles/hash -Small key speed test - 19-byte keys - 33.63 cycles/hash -Small key speed test - 20-byte keys - 34.84 cycles/hash -Small key speed test - 21-byte keys - 35.00 cycles/hash -Small key speed test - 22-byte keys - 34.55 cycles/hash -Small key speed test - 23-byte keys - 33.60 cycles/hash -Small key speed test - 24-byte keys - 33.68 cycles/hash -Small key speed test - 25-byte keys - 34.32 cycles/hash -Small key speed test - 26-byte keys - 35.00 cycles/hash -Small key speed test - 27-byte keys - 35.00 cycles/hash -Small key speed test - 28-byte keys - 33.68 cycles/hash -Small key speed test - 29-byte keys - 33.70 cycles/hash -Small key speed test - 30-byte keys - 33.89 cycles/hash -Small key speed test - 31-byte keys - 35.00 cycles/hash -Average 33.127 cycles/hash +Small key speed test - 9-byte keys - 30.83 cycles/hash +Small key speed test - 10-byte keys - 30.79 cycles/hash +Small key speed test - 11-byte keys - 30.86 cycles/hash +Small key speed test - 12-byte keys - 31.79 cycles/hash +Small key speed test - 13-byte keys - 32.00 cycles/hash +Small key speed test - 14-byte keys - 32.31 cycles/hash +Small key speed test - 15-byte keys - 30.51 cycles/hash +Small key speed test - 16-byte keys - 30.93 cycles/hash +Small key speed test - 17-byte keys - 33.07 cycles/hash +Small key speed test - 18-byte keys - 35.00 cycles/hash +Small key speed test - 19-byte keys - 35.00 cycles/hash +Small key speed test - 20-byte keys - 33.93 cycles/hash +Small key speed test - 21-byte keys - 32.86 cycles/hash +Small key speed test - 22-byte keys - 32.96 cycles/hash +Small key speed test - 23-byte keys - 34.30 cycles/hash +Small key speed test - 24-byte keys - 35.00 cycles/hash +Small key speed test - 25-byte keys - 35.00 cycles/hash +Small key speed test - 26-byte keys - 32.65 cycles/hash +Small key speed test - 27-byte keys - 33.00 cycles/hash +Small key speed test - 28-byte keys - 33.44 cycles/hash +Small key speed test - 29-byte keys - 35.00 cycles/hash +Small key speed test - 30-byte keys - 35.00 cycles/hash +Small key speed test - 31-byte keys - 34.00 cycles/hash +Average 32.830 cycles/hash [[[ 'Hashmap' Speed Tests ]]] std::unordered_map -Init std HashMapTest: 201.204 cycles/op (102774 inserts, 1% deletions) -Running std HashMapTest: 130.402 cycles/op (2.4 stdv) +Init std HashMapTest: 199.037 cycles/op (102774 inserts, 1% deletions) +Running std HashMapTest: 125.873 cycles/op (2.4 stdv) greg7mdp/parallel-hashmap -Init fast HashMapTest: 125.038 cycles/op (102774 inserts, 1% deletions) -Running fast HashMapTest: 87.416 cycles/op (0.1 stdv) ....... PASS +Init fast HashMapTest: 113.297 cycles/op (102774 inserts, 1% deletions) +Running fast HashMapTest: 85.337 cycles/op (2.7 stdv) ....... PASS [[[ Avalanche Tests ]]] @@ -1133,5 +1133,5 @@ Testing 0 internal secrets: Input vcode 0x00000001, Output vcode 0x00000001, Result vcode 0x00000001 -Verification value is 0x00000001 - Testing took 666.859625 seconds +Verification value is 0x00000001 - Testing took 631.594488 seconds ------------------------------------------------------------------------------- diff --git a/src/fallback_hash.rs b/src/fallback_hash.rs index 4572383..d52c341 100644 --- a/src/fallback_hash.rs +++ b/src/fallback_hash.rs @@ -1,5 +1,5 @@ use crate::convert::*; -use crate::operations::{add_by_64s, folded_multiply}; +use crate::operations::{folded_multiply}; use crate::operations::read_small; use crate::operations::MULTIPLE; use crate::random_state::PI; @@ -92,21 +92,10 @@ impl AHasher { /// attacker somehow knew part of (but not all) the contents of the buffer before hand, /// they would not be able to predict any of the bits in the buffer at the end. #[inline(always)] - #[cfg(feature = "folded_multiply")] fn update(&mut self, new_data: u64) { self.buffer = folded_multiply(new_data ^ self.buffer, MULTIPLE); } - #[inline(always)] - #[cfg(not(feature = "folded_multiply"))] - fn update(&mut self, new_data: u64) { - use crate::operations::INCREMENT; - - self.buffer = (self.buffer ^ new_data).wrapping_mul(self.pad ^ new_data.swap_bytes()); //Changing rather than fixed multiple removes linearity - self.buffer ^= self.buffer >> 47; // xorshift some good bits to the bottom - self.pad = self.pad.wrapping_add(INCREMENT); - } - /// Similar to the above this function performs an update using a "folded multiply". /// However it takes in 128 bits of data instead of 64. Both halves must be masked. /// @@ -119,24 +108,12 @@ impl AHasher { /// can't be changed by the same set of input bits. To cancel this sequence with subsequent input would require /// knowing the keys. #[inline(always)] - #[cfg(feature = "folded_multiply")] fn large_update(&mut self, new_data: u128) { let block: [u64; 2] = new_data.convert(); let combined = folded_multiply(block[0] ^ self.extra_keys[0], block[1] ^ self.extra_keys[1]); self.buffer = (self.buffer.wrapping_add(self.pad) ^ combined).rotate_left(ROT); } - #[inline(always)] - #[cfg(not(feature = "folded_multiply"))] - fn large_update(&mut self, new_data: u128) { - use crate::operations::INCREMENT; - let block = add_by_64s(new_data.convert(), self.extra_keys); - let b1 = (self.buffer ^ block[0]).wrapping_mul(self.pad ^ block[1].swap_bytes()); //Changing rather than fixed multiple removes linearity - let b2 = (self.buffer.swap_bytes() ^ block[1]).wrapping_mul(self.pad ^ block[0].swap_bytes()); //Reversing bytes prevents low impact high order bits. - self.buffer = b1 ^ b2; - self.pad = self.pad.wrapping_add(INCREMENT); - } - #[inline] #[cfg(feature = "specialize")] fn short_finish(&self) -> u64 { @@ -212,18 +189,11 @@ impl Hasher for AHasher { } #[inline] - #[cfg(feature = "folded_multiply")] fn finish(&self) -> u64 { let rot = (self.buffer & 63) as u32; folded_multiply(self.buffer, self.pad).rotate_left(rot) } - #[inline] - #[cfg(not(feature = "folded_multiply"))] - fn finish(&self) -> u64 { - let rot = (self.buffer & 63) as u32; - (self.buffer.wrapping_mul(MULTIPLE) ^ self.pad).rotate_left(rot) - } } #[cfg(feature = "specialize")] @@ -337,7 +307,6 @@ impl Hasher for AHasherStr { } #[inline] - #[cfg(feature = "folded_multiply")] fn write(&mut self, bytes: &[u8]) { if bytes.len() > 8 { self.0.write(bytes) @@ -349,12 +318,6 @@ impl Hasher for AHasherStr { } } - #[inline] - #[cfg(not(feature = "folded_multiply"))] - fn write(&mut self, bytes: &[u8]) { - self.0.write(bytes) - } - #[inline] fn write_u8(&mut self, _i: u8) {} diff --git a/src/operations.rs b/src/operations.rs index a4161da..1398581 100644 --- a/src/operations.rs +++ b/src/operations.rs @@ -13,11 +13,19 @@ const SHUFFLE_MASK: u128 = 0x020a0700_0c01030e_050f0d08_06090b04_u128; //const SHUFFLE_MASK: u128 = 0x040A0700_030E0106_0D050F08_020B0C09_u128; #[inline(always)] +#[cfg(feature = "folded_multiply")] pub(crate) const fn folded_multiply(s: u64, by: u64) -> u64 { let result = (s as u128).wrapping_mul(by as u128); ((result & 0xffff_ffff_ffff_ffff) as u64) ^ ((result >> 64) as u64) } +#[inline(always)] +#[cfg(not(feature = "folded_multiply"))] +pub(crate) const fn folded_multiply(s: u64, by: u64) -> u64 { + let b1 = s.wrapping_mul(by.swap_bytes()); + let b2 = s.swap_bytes().wrapping_mul(!by); + b1 ^ b2.swap_bytes() +} /// Given a small (less than 8 byte slice) returns the same data stored in two u32s. /// (order of and non-duplication of bytes is NOT guaranteed) From bd1aee77e984a0260ac28d5e383bc107fc2744cf Mon Sep 17 00:00:00 2001 From: Tom Kaitchuck Date: Mon, 24 Oct 2022 17:09:14 -0700 Subject: [PATCH 24/24] Cleanup (cargo fmt) Signed-off-by: Tom Kaitchuck --- build.rs | 1 - src/aes_hash.rs | 12 ++--- src/convert.rs | 8 +--- src/fallback_hash.rs | 12 ++--- src/hash_quality_test.rs | 31 ++++++++++--- src/lib.rs | 18 +++++--- src/operations.rs | 26 +++++++---- src/random_state.rs | 31 ++++++++----- tests/bench.rs | 96 +++++++++++++++++++++++++++------------- tests/map_tests.rs | 2 +- tests/nopanic.rs | 10 ++--- 11 files changed, 161 insertions(+), 86 deletions(-) diff --git a/build.rs b/build.rs index 58d71a9..0c5b769 100644 --- a/build.rs +++ b/build.rs @@ -20,5 +20,4 @@ fn main() { { println!("cargo:rustc-cfg=feature=\"folded_multiply\""); } - } diff --git a/src/aes_hash.rs b/src/aes_hash.rs index df67bfd..702044e 100644 --- a/src/aes_hash.rs +++ b/src/aes_hash.rs @@ -1,8 +1,8 @@ use crate::convert::*; use crate::operations::*; +use crate::random_state::PI; use crate::RandomState; use core::hash::Hasher; -use crate::random_state::PI; /// A `Hasher` for hashing an arbitrary stream of bytes. /// @@ -68,7 +68,6 @@ impl AHasher { } } - #[inline] pub(crate) fn from_random_state(rand_state: &RandomState) -> Self { let key1 = [rand_state.k0, rand_state.k1].convert(); @@ -128,7 +127,11 @@ impl Hasher for AHasher { } #[inline] - #[cfg(any(target_pointer_width = "64", target_pointer_width = "32", target_pointer_width = "16"))] + #[cfg(any( + target_pointer_width = "64", + target_pointer_width = "32", + target_pointer_width = "16" + ))] fn write_usize(&mut self, i: usize) { self.write_u64(i as u64); } @@ -317,7 +320,7 @@ pub(crate) struct AHasherStr(pub AHasher); impl Hasher for AHasherStr { #[inline] fn finish(&self) -> u64 { - let result : [u64; 2] = self.0.enc.convert(); + let result: [u64; 2] = self.0.enc.convert(); result[0] } @@ -428,4 +431,3 @@ mod tests { assert_eq!(bytes, 0x6464646464646464); } } - diff --git a/src/convert.rs b/src/convert.rs index 4c0a00e..fc47baa 100644 --- a/src/convert.rs +++ b/src/convert.rs @@ -7,17 +7,13 @@ macro_rules! convert { impl Convert<$b> for $a { #[inline(always)] fn convert(self) -> $b { - unsafe { - core::mem::transmute::<$a, $b>(self) - } + unsafe { core::mem::transmute::<$a, $b>(self) } } } impl Convert<$a> for $b { #[inline(always)] fn convert(self) -> $a { - unsafe { - core::mem::transmute::<$b, $a>(self) - } + unsafe { core::mem::transmute::<$b, $a>(self) } } } }; diff --git a/src/fallback_hash.rs b/src/fallback_hash.rs index d52c341..f78074d 100644 --- a/src/fallback_hash.rs +++ b/src/fallback_hash.rs @@ -1,5 +1,5 @@ use crate::convert::*; -use crate::operations::{folded_multiply}; +use crate::operations::folded_multiply; use crate::operations::read_small; use crate::operations::MULTIPLE; use crate::random_state::PI; @@ -151,7 +151,11 @@ impl Hasher for AHasher { } #[inline] - #[cfg(any(target_pointer_width = "64", target_pointer_width = "32", target_pointer_width = "16"))] + #[cfg(any( + target_pointer_width = "64", + target_pointer_width = "32", + target_pointer_width = "16" + ))] fn write_usize(&mut self, i: usize) { self.write_u64(i as u64); } @@ -193,7 +197,6 @@ impl Hasher for AHasher { let rot = (self.buffer & 63) as u32; folded_multiply(self.buffer, self.pad).rotate_left(rot) } - } #[cfg(feature = "specialize")] @@ -312,8 +315,7 @@ impl Hasher for AHasherStr { self.0.write(bytes) } else { let value = read_small(bytes); - self.0.buffer = folded_multiply(value[0] ^ self.0.buffer, - value[1] ^ self.0.extra_keys[1]); + self.0.buffer = folded_multiply(value[0] ^ self.0.buffer, value[1] ^ self.0.extra_keys[1]); self.0.pad = self.0.pad.wrapping_add(bytes.len() as u64); } } diff --git a/src/hash_quality_test.rs b/src/hash_quality_test.rs index 125c522..bd85edd 100644 --- a/src/hash_quality_test.rs +++ b/src/hash_quality_test.rs @@ -147,7 +147,13 @@ fn assert_each_byte_differs(num: u64, base: u64, alternitives: Vec) { for alternitive in alternitives { changed_bits |= base ^ alternitive } - assert_eq!(core::u64::MAX, changed_bits, "Bits changed: {:x} on num: {:?}", changed_bits, num); + assert_eq!( + core::u64::MAX, + changed_bits, + "Bits changed: {:x} on num: {:?}", + changed_bits, + num + ); } fn test_finish_is_consistent(constructor: impl Fn(u128, u128) -> T) { @@ -273,11 +279,19 @@ fn test_padding_doesnot_collide(hasher: impl Fn() -> T) { let (same_bytes, same_nibbles) = count_same_bytes_and_nibbles(value, long.finish()); assert!( same_bytes <= 3, - "{} bytes of {} -> {:x} vs {:x}", num, c, value, long.finish() + "{} bytes of {} -> {:x} vs {:x}", + num, + c, + value, + long.finish() ); assert!( same_nibbles <= 8, - "{} bytes of {} -> {:x} vs {:x}", num, c, value, long.finish() + "{} bytes of {} -> {:x} vs {:x}", + num, + c, + value, + long.finish() ); let flipped_bits = (value ^ long.finish()).count_ones(); assert!(flipped_bits > 10); @@ -370,7 +384,7 @@ mod fallback_tests { fn fallback_keys_affect_every_byte() { //For fallback second key is not used in every hash. #[cfg(all(not(feature = "specialize"), feature = "folded_multiply"))] - test_keys_affect_every_byte(0, |a, b| AHasher::new_with_keys(a ^ b, a)); + test_keys_affect_every_byte(0, |a, b| AHasher::new_with_keys(a ^ b, a)); test_keys_affect_every_byte("", |a, b| AHasher::new_with_keys(a ^ b, a)); test_keys_affect_every_byte((0, 0), |a, b| AHasher::new_with_keys(a ^ b, a)); } @@ -397,7 +411,12 @@ mod fallback_tests { ///Basic sanity tests of the cypto properties of aHash. #[cfg(any( all(any(target_arch = "x86", target_arch = "x86_64"), target_feature = "aes", not(miri)), - all(any(target_arch = "arm", target_arch = "aarch64"), any(target_feature = "aes", target_feature = "crypto"), not(miri), feature = "stdsimd") + all( + any(target_arch = "arm", target_arch = "aarch64"), + any(target_feature = "aes", target_feature = "crypto"), + not(miri), + feature = "stdsimd" + ) ))] #[cfg(test)] mod aes_tests { @@ -460,7 +479,7 @@ mod aes_tests { #[test] fn aes_keys_affect_every_byte() { #[cfg(not(feature = "specialize"))] - test_keys_affect_every_byte(0, AHasher::test_with_keys); + test_keys_affect_every_byte(0, AHasher::test_with_keys); test_keys_affect_every_byte("", AHasher::test_with_keys); test_keys_affect_every_byte((0, 0), AHasher::test_with_keys); } diff --git a/src/lib.rs b/src/lib.rs index 8c146bf..4566af0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,8 +8,10 @@ //! //! aHash uses the hardware AES instruction on x86 processors to provide a keyed hash function. //! aHash is not a cryptographically secure hash. -//! -#![cfg_attr(any(feature = "compile-time-rng", feature = "runtime-rng"), doc = r##" +//! +#![cfg_attr( + any(feature = "compile-time-rng", feature = "runtime-rng"), + doc = r##" # Example ``` use ahash::{AHasher, RandomState}; @@ -18,8 +20,11 @@ use std::collections::HashMap; let mut map: HashMap = HashMap::default(); map.insert(12, 34); ``` -"##)] -#![cfg_attr(feature = "std", doc = r##" +"## +)] +#![cfg_attr( + feature = "std", + doc = r##" For convenience, both new-type wrappers and type aliases are provided. The new type wrappers are called called `AHashMap` and `AHashSet`. These do the same thing with slightly less typing. The type aliases are called `ahash::HashMap`, `ahash::HashSet` are also provided and alias the std::[HashMap] and std::[HashSet]. Why are there two options? The wrappers are convenient but @@ -39,7 +44,8 @@ map.insert(12, 34); let mut set = ahash::HashSet::with_capacity(10); set.insert(10); ``` -"##)] +"## +)] #![deny(clippy::correctness, clippy::complexity, clippy::perf)] #![allow(clippy::pedantic, clippy::cast_lossless, clippy::unreadable_literal)] #![cfg_attr(all(not(test), not(feature = "std")), no_std)] @@ -265,10 +271,10 @@ impl BuildHasherExt for B { #[cfg(test)] mod test { use crate::convert::Convert; + use crate::specialize::CallHasher; use crate::*; use std::collections::HashMap; use std::hash::Hash; - use crate::specialize::CallHasher; #[test] fn test_ahash_alias_map_construction() { diff --git a/src/operations.rs b/src/operations.rs index 1398581..ffd3b1a 100644 --- a/src/operations.rs +++ b/src/operations.rs @@ -1,9 +1,7 @@ use crate::convert::*; -///These constants come from Kunth's prng (Empirically it works better than those from splitmix32). +///This constant comes from Kunth's prng (Empirically it works better than those from splitmix32). pub(crate) const MULTIPLE: u64 = 6364136223846793005; -#[allow(unused)] //only used by fallback -pub(crate) const INCREMENT: u64 = 1442695040888963407; /// This is a constant with a lot of special properties found by automated search. /// See the unit tests below. (Below are alternative values) @@ -114,14 +112,19 @@ pub(crate) fn aesenc(value: u128, xor: u128) -> u128 { } } -#[cfg(all(any(target_arch = "arm", target_arch = "aarch64"), any(target_feature = "aes", target_feature = "crypto"), not(miri), feature = "stdsimd"))] +#[cfg(all( + any(target_arch = "arm", target_arch = "aarch64"), + any(target_feature = "aes", target_feature = "crypto"), + not(miri), + feature = "stdsimd" +))] #[allow(unused)] #[inline(always)] pub(crate) fn aesenc(value: u128, xor: u128) -> u128 { - #[cfg(target_arch = "arm")] - use core::arch::arm::*; #[cfg(target_arch = "aarch64")] use core::arch::aarch64::*; + #[cfg(target_arch = "arm")] + use core::arch::arm::*; use core::mem::transmute; unsafe { let value = transmute(value); @@ -144,14 +147,19 @@ pub(crate) fn aesdec(value: u128, xor: u128) -> u128 { } } -#[cfg(all(any(target_arch = "arm", target_arch = "aarch64"), any(target_feature = "aes", target_feature = "crypto"), not(miri), feature = "stdsimd"))] +#[cfg(all( + any(target_arch = "arm", target_arch = "aarch64"), + any(target_feature = "aes", target_feature = "crypto"), + not(miri), + feature = "stdsimd" +))] #[allow(unused)] #[inline(always)] pub(crate) fn aesdec(value: u128, xor: u128) -> u128 { - #[cfg(target_arch = "arm")] - use core::arch::arm::*; #[cfg(target_arch = "aarch64")] use core::arch::aarch64::*; + #[cfg(target_arch = "arm")] + use core::arch::arm::*; use core::mem::transmute; unsafe { let value = transmute(value); diff --git a/src/random_state.rs b/src/random_state.rs index 95c66b5..60ba51f 100644 --- a/src/random_state.rs +++ b/src/random_state.rs @@ -29,7 +29,6 @@ use core::sync::atomic; use alloc::boxed::Box; use atomic::{AtomicUsize, Ordering}; - use core::any::{Any, TypeId}; use core::fmt; use core::hash::BuildHasher; @@ -54,7 +53,7 @@ cfg_if::cfg_if! { #[inline] fn get_fixed_seeds() -> &'static [[u64; 4]; 2] { use const_random::const_random; - + const RAND: [[u64; 4]; 2] = [ [ const_random!(u64), @@ -74,9 +73,9 @@ cfg_if::cfg_if! { #[inline] fn get_fixed_seeds() -> &'static [[u64; 4]; 2] { use crate::convert::Convert; - + static SEEDS: OnceBox<[[u64; 4]; 2]> = OnceBox::new(); - + SEEDS.get_or_init(|| { let mut result: [u8; 64] = [0; 64]; getrandom::getrandom(&mut result).expect("getrandom::getrandom() failed."); @@ -87,7 +86,7 @@ cfg_if::cfg_if! { #[inline] fn get_fixed_seeds() -> &'static [[u64; 4]; 2] { use const_random::const_random; - + const RAND: [[u64; 4]; 2] = [ [ const_random!(u64), @@ -106,7 +105,7 @@ cfg_if::cfg_if! { } else { fn get_fixed_seeds() -> &'static [[u64; 4]; 2] { &[PI, PI2] - } + } } } @@ -120,9 +119,7 @@ cfg_if::cfg_if! { /// A supplier of Randomness used for different hashers. /// See [set_random_source]. pub trait RandomSource { - fn gen_hasher_seed(&self) -> usize; - } struct DefaultRandomSource { @@ -271,7 +268,12 @@ impl RandomState { /// or the same value being passed for more than one parameter. #[inline] pub const fn with_seeds(k0: u64, k1: u64, k2: u64, k3: u64) -> RandomState { - RandomState { k0: k0 ^ PI2[0], k1: k1 ^ PI2[1], k2: k2 ^ PI2[2], k3: k3 ^ PI2[3] } + RandomState { + k0: k0 ^ PI2[0], + k1: k1 ^ PI2[1], + k2: k2 ^ PI2[2], + k3: k3 ^ PI2[3], + } } /// Calculates the hash of a single value. @@ -286,7 +288,9 @@ impl RandomState { /// [`Hasher`], not to call this method repeatedly and combine the results. #[inline] pub fn hash_one(&self, x: T) -> u64 - where Self: Sized { + where + Self: Sized, + { use crate::specialize::CallHasher; T::get_hash(&x, self) } @@ -308,7 +312,9 @@ impl BuildHasher for RandomState { /// [AHasher]s that will return different hashcodes, but [Hasher]s created from the same [BuildHasher] /// will generate the same hashes for the same input data. /// - #[cfg_attr(any(feature = "compile-time-rng", feature = "runtime-rng"), doc = r##" # Examples + #[cfg_attr( + any(feature = "compile-time-rng", feature = "runtime-rng"), + doc = r##" # Examples ``` use ahash::{AHasher, RandomState}; use std::hash::{Hasher, BuildHasher}; @@ -327,7 +333,8 @@ impl BuildHasher for RandomState { different_hasher.write_u32(1234); assert_ne!(different_hasher.finish(), hasher_1.finish()); ``` - "##)] + "## + )] /// [Hasher]: std::hash::Hasher /// [BuildHasher]: std::hash::BuildHasher /// [HashMap]: std::collections::HashMap diff --git a/tests/bench.rs b/tests/bench.rs index 251e134..84a3739 100644 --- a/tests/bench.rs +++ b/tests/bench.rs @@ -8,7 +8,12 @@ use std::hash::{BuildHasherDefault, Hash, Hasher}; #[cfg(any( all(any(target_arch = "x86", target_arch = "x86_64"), target_feature = "aes", not(miri)), - all(any(target_arch = "arm", target_arch = "aarch64"), any(target_feature = "aes", target_feature = "crypto"), not(miri), feature = "stdsimd") + all( + any(target_arch = "arm", target_arch = "aarch64"), + any(target_feature = "aes", target_feature = "crypto"), + not(miri), + feature = "stdsimd" + ) ))] fn aeshash(b: &H) -> u64 { let build_hasher = RandomState::with_seeds(1, 2, 3, 4); @@ -16,7 +21,12 @@ fn aeshash(b: &H) -> u64 { } #[cfg(not(any( all(any(target_arch = "x86", target_arch = "x86_64"), target_feature = "aes", not(miri)), - all(any(target_arch = "arm", target_arch = "aarch64"), any(target_feature = "aes", target_feature = "crypto"), not(miri), feature = "stdsimd") + all( + any(target_arch = "arm", target_arch = "aarch64"), + any(target_feature = "aes", target_feature = "crypto"), + not(miri), + feature = "stdsimd" + ) )))] fn aeshash(_b: &H) -> u64 { panic!("aes must be enabled") @@ -24,7 +34,12 @@ fn aeshash(_b: &H) -> u64 { #[cfg(not(any( all(any(target_arch = "x86", target_arch = "x86_64"), target_feature = "aes", not(miri)), - all(any(target_arch = "arm", target_arch = "aarch64"), any(target_feature = "aes", target_feature = "crypto"), not(miri), feature = "stdsimd") + all( + any(target_arch = "arm", target_arch = "aarch64"), + any(target_feature = "aes", target_feature = "crypto"), + not(miri), + feature = "stdsimd" + ) )))] fn fallbackhash(b: &H) -> u64 { let build_hasher = RandomState::with_seeds(1, 2, 3, 4); @@ -32,7 +47,12 @@ fn fallbackhash(b: &H) -> u64 { } #[cfg(any( all(any(target_arch = "x86", target_arch = "x86_64"), target_feature = "aes", not(miri)), - all(any(target_arch = "arm", target_arch = "aarch64"), any(target_feature = "aes", target_feature = "crypto"), not(miri), feature = "stdsimd") + all( + any(target_arch = "arm", target_arch = "aarch64"), + any(target_feature = "aes", target_feature = "crypto"), + not(miri), + feature = "stdsimd" + ) ))] fn fallbackhash(_b: &H) -> u64 { panic!("aes must be disabled") @@ -148,9 +168,10 @@ fn bench_sip(c: &mut Criterion) { fn bench_map(c: &mut Criterion) { #[cfg(feature = "std")] - { - let mut group = c.benchmark_group("map"); - group.bench_function("aHash-alias", |b| b.iter(|| { + { + let mut group = c.benchmark_group("map"); + group.bench_function("aHash-alias", |b| { + b.iter(|| { let hm: ahash::HashMap = (0..1_000_000).map(|i| (i, i)).collect(); let mut sum = 0; for i in 0..1_000_000 { @@ -158,8 +179,10 @@ fn bench_map(c: &mut Criterion) { sum += x; } } - })); - group.bench_function("aHash-hashBrown", |b| b.iter(|| { + }) + }); + group.bench_function("aHash-hashBrown", |b| { + b.iter(|| { let hm: hashbrown::HashMap = (0..1_000_000).map(|i| (i, i)).collect(); let mut sum = 0; for i in 0..1_000_000 { @@ -167,8 +190,10 @@ fn bench_map(c: &mut Criterion) { sum += x; } } - })); - group.bench_function("aHash-hashBrown-explicit", |b| b.iter(|| { + }) + }); + group.bench_function("aHash-hashBrown-explicit", |b| { + b.iter(|| { let hm: hashbrown::HashMap = (0..1_000_000).map(|i| (i, i)).collect(); let mut sum = 0; for i in 0..1_000_000 { @@ -176,8 +201,10 @@ fn bench_map(c: &mut Criterion) { sum += x; } } - })); - group.bench_function("aHash-wrapper", |b| b.iter(|| { + }) + }); + group.bench_function("aHash-wrapper", |b| { + b.iter(|| { let hm: ahash::AHashMap = (0..1_000_000).map(|i| (i, i)).collect(); let mut sum = 0; for i in 0..1_000_000 { @@ -185,8 +212,10 @@ fn bench_map(c: &mut Criterion) { sum += x; } } - })); - group.bench_function("aHash-rand", |b| b.iter(|| { + }) + }); + group.bench_function("aHash-rand", |b| { + b.iter(|| { let hm: std::collections::HashMap = (0..1_000_000).map(|i| (i, i)).collect(); let mut sum = 0; for i in 0..1_000_000 { @@ -194,37 +223,44 @@ fn bench_map(c: &mut Criterion) { sum += x; } } - })); - group.bench_function("aHash-default", |b| b.iter(|| { - let hm: std::collections::HashMap> = (0..1_000_000).map(|i| (i, i)).collect(); + }) + }); + group.bench_function("aHash-default", |b| { + b.iter(|| { + let hm: std::collections::HashMap> = + (0..1_000_000).map(|i| (i, i)).collect(); let mut sum = 0; for i in 0..1_000_000 { if let Some(x) = hm.get(&i) { sum += x; } } - })); - } + }) + }); + } } criterion_main!(benches); #[cfg(any( all(any(target_arch = "x86", target_arch = "x86_64"), target_feature = "aes", not(miri)), - all(any(target_arch = "arm", target_arch = "aarch64"), any(target_feature = "aes", target_feature = "crypto"), not(miri), feature = "stdsimd") + all( + any(target_arch = "arm", target_arch = "aarch64"), + any(target_feature = "aes", target_feature = "crypto"), + not(miri), + feature = "stdsimd" + ) ))] -criterion_group!( - benches, - bench_ahash, - bench_fx, - bench_fnv, - bench_sea, - bench_sip -); +criterion_group!(benches, bench_ahash, bench_fx, bench_fnv, bench_sea, bench_sip); #[cfg(not(any( all(any(target_arch = "x86", target_arch = "x86_64"), target_feature = "aes", not(miri)), - all(any(target_arch = "arm", target_arch = "aarch64"), any(target_feature = "aes", target_feature = "crypto"), not(miri), feature = "stdsimd") + all( + any(target_arch = "arm", target_arch = "aarch64"), + any(target_feature = "aes", target_feature = "crypto"), + not(miri), + feature = "stdsimd" + ) )))] criterion_group!( benches, diff --git a/tests/map_tests.rs b/tests/map_tests.rs index 3f4a06a..8d798a0 100644 --- a/tests/map_tests.rs +++ b/tests/map_tests.rs @@ -2,9 +2,9 @@ use std::hash::{BuildHasher, Hash, Hasher}; +use ahash::RandomState; use criterion::*; use fxhash::FxHasher; -use ahash::RandomState; fn gen_word_pairs() -> Vec { let words: Vec<_> = r#" diff --git a/tests/nopanic.rs b/tests/nopanic.rs index 4ad13bd..56f754c 100644 --- a/tests/nopanic.rs +++ b/tests/nopanic.rs @@ -25,7 +25,10 @@ struct SimpleBuildHasher { } impl SimpleBuildHasher { - fn hash_one(&self, x: T) -> u64 where Self: Sized { + fn hash_one(&self, x: T) -> u64 + where + Self: Sized, + { let mut hasher = self.build_hasher(); x.hash(&mut hasher); hasher.finish() @@ -61,10 +64,7 @@ fn hash_test_random_wrapper(num: i32, string: &str) { fn hash_test_random(num: i32, string: &str) -> (u64, u64) { let build_hasher1 = RandomState::with_seeds(1, 2, 3, 4); let build_hasher2 = RandomState::with_seeds(1, 2, 3, 4); - ( - build_hasher1.hash_one(&num), - build_hasher2.hash_one(string.as_bytes()) - ) + (build_hasher1.hash_one(&num), build_hasher2.hash_one(string.as_bytes())) } #[inline(never)]