From 183b007f2c83b885dd6472298aa5467c372c904a Mon Sep 17 00:00:00 2001 From: Cheng XU Date: Fri, 27 Aug 2021 18:09:54 -0700 Subject: [PATCH 1/4] feature gate random state This is especially useful for no_std development, where we don't want to introduce the getrandom dependency. After this change, one can choose to disable random-state feature and implement their own version of RandomState using whatever random source they choose. This also somewhat addresses #94 and avoids the need of using compile-time-rng in restricted platforms. --- Cargo.toml | 7 +++++-- src/aes_hash.rs | 10 ++++++---- src/fallback_hash.rs | 4 +++- src/lib.rs | 24 +++++++++++++++++++++++- src/random_state.rs | 15 +-------------- src/specialize.rs | 8 +++++++- tests/bench.rs | 2 ++ tests/map_tests.rs | 5 +++-- tests/nopanic.rs | 5 ++++- 9 files changed, 54 insertions(+), 26 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b849387..1a6850a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,7 +26,10 @@ doc = true default = ["std"] # Enabling this will enable `AHashMap` and `AHashSet`. -std = [] +std = ["random-state"] + +# Enable this will enable `RandomState`. +random-state = ["getrandom"] # 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.) @@ -66,7 +69,7 @@ codegen-units = 1 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" } +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/src/aes_hash.rs b/src/aes_hash.rs index 07e2ad1..1828907 100644 --- a/src/aes_hash.rs +++ b/src/aes_hash.rs @@ -2,9 +2,10 @@ use crate::convert::*; #[cfg(feature = "specialize")] use crate::fallback_hash::MULTIPLE; use crate::operations::*; +use crate::PI; +#[cfg(feature = "random-state")] use crate::RandomState; use core::hash::Hasher; -use crate::random_state::PI; /// A `Hasher` for hashing an arbitrary stream of bytes. /// @@ -70,7 +71,7 @@ impl AHasher { } } - + #[cfg(feature = "random-state")] #[inline] pub(crate) fn from_random_state(rand_state: &RandomState) -> Self { let key1 = [rand_state.k0, rand_state.k1].convert(); @@ -369,11 +370,12 @@ mod tests { use super::*; use crate::convert::Convert; use crate::operations::aesenc; - use crate::RandomState; use std::hash::{BuildHasher, Hasher}; + + #[cfg(feature = "random-state")] #[test] fn test_sanity() { - let mut hasher = RandomState::with_seeds(1, 2, 3, 4).build_hasher(); + let mut hasher = crate::RandomState::with_seeds(1, 2, 3, 4).build_hasher(); hasher.write_u64(0); let h1 = hasher.finish(); hasher.write(&[1, 0, 0, 0, 0, 0, 0, 0]); diff --git a/src/fallback_hash.rs b/src/fallback_hash.rs index 1665286..0be7cc9 100644 --- a/src/fallback_hash.rs +++ b/src/fallback_hash.rs @@ -1,7 +1,8 @@ use crate::convert::*; use crate::operations::folded_multiply; use crate::operations::read_small; -use crate::random_state::PI; +use crate::PI; +#[cfg(feature = "random-state")] use crate::RandomState; use core::hash::Hasher; @@ -53,6 +54,7 @@ impl AHasher { } } + #[cfg(feature = "random-state")] #[inline] #[allow(dead_code)] // Is not called if non-fallback hash is used. pub(crate) fn from_random_state(rand_state: &RandomState) -> AHasher { diff --git a/src/lib.rs b/src/lib.rs index 9964a7c..abfda56 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,11 +11,14 @@ //! //! # Example //! ``` +//! # #[cfg(feature = "random-state")] +//! # { //! 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. @@ -49,6 +52,7 @@ mod hash_map; #[cfg(feature = "std")] mod hash_set; mod operations; +#[cfg(feature = "random-state")] mod random_state; mod specialize; @@ -63,6 +67,8 @@ pub use crate::aes_hash::AHasher; all(any(target_arch = "arm", target_arch = "aarch64"), target_feature = "crypto", not(miri), feature = "stdsimd") )))] pub use crate::fallback_hash::AHasher; + +#[cfg(feature = "random-state")] pub use crate::random_state::RandomState; pub use crate::specialize::CallHasher; @@ -75,6 +81,22 @@ use core::hash::BuildHasher; use core::hash::Hash; use core::hash::Hasher; +pub(crate) const PI: [u64; 4] = [ + 0x243f_6a88_85a3_08d3, + 0x1319_8a2e_0370_7344, + 0xa409_3822_299f_31d0, + 0x082e_fa98_ec4e_6c89, +]; + +#[cfg(feature = "random-state")] +pub(crate) const PI2: [u64; 4] = [ + 0x4528_21e6_38d0_1377, + 0xbe54_66cf_34e9_0c6c, + 0xc0ac_29b7_c97c_50dd, + 0x3f84_d5b5_b547_0917, +]; + +#[cfg(feature = "random-state")] /// Provides a default [Hasher] with fixed keys. /// This is typically used in conjunction with [BuildHasherDefault] to create /// [AHasher]s in order to hash the keys of the map. @@ -86,7 +108,7 @@ use core::hash::Hasher; /// # Example /// ``` /// use std::hash::BuildHasherDefault; -/// use ahash::{AHasher, RandomState}; +/// use ahash::AHasher; /// use std::collections::HashMap; /// /// let mut map: HashMap> = HashMap::default(); diff --git a/src/random_state.rs b/src/random_state.rs index c3628bf..621aeba 100644 --- a/src/random_state.rs +++ b/src/random_state.rs @@ -2,6 +2,7 @@ use crate::convert::Convert; #[cfg(feature = "specialize")] use crate::BuildHasherExt; +use crate::{PI, PI2}; #[cfg(any( all(any(target_arch = "x86", target_arch = "x86_64"), target_feature = "aes", not(miri)), @@ -58,20 +59,6 @@ pub trait RandomSource { } -pub(crate) const PI: [u64; 4] = [ - 0x243f_6a88_85a3_08d3, - 0x1319_8a2e_0370_7344, - 0xa409_3822_299f_31d0, - 0x082e_fa98_ec4e_6c89, -]; - -pub(crate) const PI2: [u64; 4] = [ - 0x4528_21e6_38d0_1377, - 0xbe54_66cf_34e9_0c6c, - 0xc0ac_29b7_c97c_50dd, - 0x3f84_d5b5_b547_0917, -]; - struct DefaultRandomSource { counter: AtomicUsize, } diff --git a/src/specialize.rs b/src/specialize.rs index d94a4ee..3628323 100644 --- a/src/specialize.rs +++ b/src/specialize.rs @@ -19,6 +19,8 @@ use alloc::vec::Vec; /// for a specific type. So this may be faster for primitive types. /// # Example /// ``` +/// # #[cfg(feature = "random-state")] +/// # { /// use std::hash::BuildHasher; /// use ahash::RandomState; /// use ahash::CallHasher; @@ -27,16 +29,20 @@ use alloc::vec::Vec; /// //... /// 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: /// ``` +/// # #[cfg(feature = "random-state")] +/// # { /// /// use std::hash::BuildHasher; /// # use ahash::RandomState; /// # use ahash::CallHasher; /// # let hash_builder = RandomState::new(); /// let bytes: [u8; 4] = [1, 2, 3, 4]; /// let hash = <[u8]>::get_hash(&bytes, &hash_builder); +/// # } /// ``` pub trait CallHasher { fn get_hash(value: &H, build_hasher: &B) -> u64; @@ -152,7 +158,7 @@ impl CallHasher for String { } } -#[cfg(test)] +#[cfg(all(test, feature = "random-state"))] mod test { use super::*; use crate::*; diff --git a/tests/bench.rs b/tests/bench.rs index 9e6dccc..1a3a87c 100644 --- a/tests/bench.rs +++ b/tests/bench.rs @@ -1,3 +1,5 @@ +#![cfg(feature = "random-state")] + use ahash::{CallHasher, RandomState}; use criterion::*; use fxhash::FxHasher; diff --git a/tests/map_tests.rs b/tests/map_tests.rs index be617a2..4c1ae65 100644 --- a/tests/map_tests.rs +++ b/tests/map_tests.rs @@ -3,7 +3,7 @@ use std::hash::{BuildHasher, Hash, Hasher}; use criterion::*; use fxhash::FxHasher; -use ahash::{AHasher, CallHasher, RandomState}; +use ahash::{AHasher, CallHasher}; fn gen_word_pairs() -> Vec { let words: Vec<_> = r#" @@ -155,9 +155,10 @@ fn hash(b: &H, build_hasher: &B) -> u64 { H::get_hash(b, build_hasher) } +#[cfg(feature = "random-state")] #[test] fn test_bucket_distribution() { - let build_hasher = RandomState::with_seeds(1, 2, 3, 4); + let build_hasher = ahash::RandomState::with_seeds(1, 2, 3, 4); test_hash_common_words(&build_hasher); let sequence: Vec<_> = (0..320000).collect(); check_for_collisions(&build_hasher, &sequence, 32); diff --git a/tests/nopanic.rs b/tests/nopanic.rs index d48ff55..6598ba5 100644 --- a/tests/nopanic.rs +++ b/tests/nopanic.rs @@ -1,4 +1,4 @@ -use ahash::{AHasher, CallHasher, RandomState}; +use ahash::{AHasher, CallHasher}; use std::hash::BuildHasher; #[macro_use] @@ -48,9 +48,12 @@ fn hash_test_random_wrapper(num: i32, string: &str) { hash_test_specialize(num, string); } +#[cfg(feature = "random-state")] #[inline(never)] #[no_panic] fn hash_test_random(num: i32, string: &str) -> (u64, u64) { + use ahash::RandomState; + let build_hasher1 = RandomState::with_seeds(1, 2, 3, 4); let build_hasher2 = RandomState::with_seeds(1, 2, 3, 4); ( From e2aa06bd245794bc5a5496dede769e0d9cea929e Mon Sep 17 00:00:00 2001 From: Cheng XU Date: Fri, 27 Aug 2021 18:14:19 -0700 Subject: [PATCH 2/4] enable random-state feature in compare and ahash-cbindings --- compare/Cargo.toml | 2 +- smhasher/ahash-cbindings/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/compare/Cargo.toml b/compare/Cargo.toml index d2ba3b5..9c8cf5b 100644 --- a/compare/Cargo.toml +++ b/compare/Cargo.toml @@ -29,7 +29,7 @@ debug-assertions = false codegen-units = 1 [dependencies] -ahash = { path = "../", default-features = false } +ahash = { path = "../", default-features = false, features = ["random-state"] } [dev-dependencies] criterion = "0.3.3" diff --git a/smhasher/ahash-cbindings/Cargo.toml b/smhasher/ahash-cbindings/Cargo.toml index 49513b3..b872111 100644 --- a/smhasher/ahash-cbindings/Cargo.toml +++ b/smhasher/ahash-cbindings/Cargo.toml @@ -17,4 +17,4 @@ lto = 'fat' debug-assertions = false [dependencies] -ahash = { path = "../../", default-features = false } \ No newline at end of file +ahash = { path = "../../", default-features = false, features = ["random-state"] } From 6c08360a8cd8446df2bfed95211054218a37e132 Mon Sep 17 00:00:00 2001 From: Cheng XU Date: Fri, 27 Aug 2021 18:19:31 -0700 Subject: [PATCH 3/4] lib.rs: enable test for doc example that requiring std --- src/lib.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index abfda56..b3e488e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -22,12 +22,15 @@ //! ``` //! For convinence wrappers called `AHashMap` and `AHashSet` are also provided. //! These to the same thing with slightly less typing. -//! ```ignore +//! ``` +//! # #[cfg(feature = "std")] +//! # { //! 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)] From 09bcae3efb71e341cc16f5bd0bd79ec3a3b8c5f6 Mon Sep 17 00:00:00 2001 From: Cheng XU Date: Sat, 28 Aug 2021 23:33:50 -0700 Subject: [PATCH 4/4] Cargo.toml: only enable once_cell when random-state is enabled --- Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 1a6850a..811983a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,7 +29,7 @@ default = ["std"] std = ["random-state"] # Enable this will enable `RandomState`. -random-state = ["getrandom"] +random-state = ["getrandom", "once_cell"] # 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.) @@ -78,7 +78,7 @@ const-random = { version = "0.1.12", optional = true } serde = { version = "1.0.117", optional = true } [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 = ["alloc"], optional = true } [dev-dependencies] no-panic = "0.1.10"