diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2022-04-28 16:01:47 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2022-04-28 16:01:47 +0000 |
commit | e9882f673704a27b8e53b727a718b9dbf396f35c (patch) | |
tree | 0b929457b4bd5c335b5c028b8956fe95b8a251f1 | |
parent | 0d2efbfe9640b299e76b909a6d0af35ca13761ed (diff) | |
parent | 1319627187e18db41095e680e6d8eaba75753fdd (diff) | |
download | ahash-android13-frc-adbd-release.tar.gz |
Snap for 8512216 from 1319627187e18db41095e680e6d8eaba75753fdd to tm-frc-adbd-releaset_frc_adb_330444000android13-frc-adbd-release
Change-Id: I35e7f8c77a48f533ca9d8dbec5e1d57d67886223
-rw-r--r-- | .cargo_vcs_info.json | 2 | ||||
-rw-r--r-- | Android.bp | 2 | ||||
-rw-r--r-- | Cargo.toml | 15 | ||||
-rw-r--r-- | Cargo.toml.orig | 10 | ||||
-rw-r--r-- | METADATA | 10 | ||||
-rw-r--r-- | build.rs | 1 | ||||
-rw-r--r-- | patches/0001-Use-dev-urandom-instead-of-getrandom.patch | 31 | ||||
-rw-r--r-- | src/aes_hash.rs | 8 | ||||
-rw-r--r-- | src/convert.rs | 16 | ||||
-rw-r--r-- | src/fallback_hash.rs | 8 | ||||
-rw-r--r-- | src/hash_quality_test.rs | 25 | ||||
-rw-r--r-- | src/lib.rs | 16 | ||||
-rw-r--r-- | src/operations.rs | 31 | ||||
-rw-r--r-- | src/random_state.rs | 219 | ||||
-rw-r--r-- | src/specialize.rs | 12 | ||||
-rw-r--r-- | tests/bench.rs | 20 |
16 files changed, 276 insertions, 150 deletions
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json index a2c6e68..832d476 100644 --- a/.cargo_vcs_info.json +++ b/.cargo_vcs_info.json @@ -1,5 +1,5 @@ { "git": { - "sha1": "ffa04fcb81f39755f636c75c9b7aa06533c0ae75" + "sha1": "e77cab8c1e15bfc9f54dfd28bd8820c2a7bb27c4" } } @@ -43,7 +43,7 @@ rust_library { host_supported: true, crate_name: "ahash", cargo_env_compat: true, - cargo_pkg_version: "0.7.4", + cargo_pkg_version: "0.7.6", srcs: ["src/lib.rs"], edition: "2018", arch: { @@ -13,10 +13,10 @@ [package] edition = "2018" name = "ahash" -version = "0.7.4" +version = "0.7.6" authors = ["Tom Kaitchuck <Tom.Kaitchuck@gmail.com>"] build = "./build.rs" -exclude = ["/smhasher"] +exclude = ["/smhasher", "/benchmark_tools"] description = "A non-cryptographic hash function using AES-NI for high performance" documentation = "https://docs.rs/ahash" readme = "README.md" @@ -98,16 +98,15 @@ version = "0.1.12" optional = true [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.0" - -[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.once_cell] -version = "1.5.2" -features = ["unstable", "alloc"] -default-features = false +version = "0.2.3" [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.serde] version = "1.0.117" optional = true +[target."cfg(not(all(target_arch = \"arm\", target_os = \"none\")))".dependencies.once_cell] +version = "1.8" +features = ["alloc"] +default-features = false [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.const-random] version = "0.1.12" optional = true diff --git a/Cargo.toml.orig b/Cargo.toml.orig index 1b3e831..4b56472 100644 --- a/Cargo.toml.orig +++ b/Cargo.toml.orig @@ -1,6 +1,6 @@ [package] name = "ahash" -version = "0.7.4" +version = "0.7.6" authors = ["Tom Kaitchuck <Tom.Kaitchuck@gmail.com>"] license = "MIT OR Apache-2.0" description = "A non-cryptographic hash function using AES-NI for high performance" @@ -11,7 +11,7 @@ categories = ["algorithms", "data-structures", "no-std"] edition = "2018" readme = "README.md" build = "./build.rs" -exclude = ["/smhasher"] +exclude = ["/smhasher", "/benchmark_tools"] [lib] name = "ahash" @@ -65,8 +65,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] -once_cell = { version = "1.5.2", default-features = false, features = ["unstable", "alloc"] } -getrandom = { version = "0.2.0" } +getrandom = { version = "0.2.3" } const-random = { version = "0.1.12", optional = true } serde = { version = "1.0.117", optional = true } @@ -74,6 +73,9 @@ serde = { version = "1.0.117", optional = true } 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"] } + [dev-dependencies] no-panic = "0.1.10" criterion = {version = "0.3.2"} @@ -7,13 +7,13 @@ third_party { } url { type: ARCHIVE - value: "https://static.crates.io/crates/ahash/ahash-0.7.4.crate" + value: "https://static.crates.io/crates/ahash/ahash-0.7.6.crate" } - version: "0.7.4" + version: "0.7.6" license_type: NOTICE last_upgrade_date { - year: 2021 - month: 6 - day: 14 + year: 2022 + month: 2 + day: 28 } } @@ -7,6 +7,7 @@ fn main() { if let Some(channel) = version_check::Channel::read() { if channel.supports_features() { println!("cargo:rustc-cfg=feature=\"specialize\""); + println!("cargo:rustc-cfg=feature=\"stdsimd\""); } } let os = env::var("CARGO_CFG_TARGET_OS").expect("CARGO_CFG_TARGET_OS was not set"); diff --git a/patches/0001-Use-dev-urandom-instead-of-getrandom.patch b/patches/0001-Use-dev-urandom-instead-of-getrandom.patch index fba802c..c5f4df3 100644 --- a/patches/0001-Use-dev-urandom-instead-of-getrandom.patch +++ b/patches/0001-Use-dev-urandom-instead-of-getrandom.patch @@ -17,12 +17,12 @@ Bug: 185934601 Change-Id: Ie81a1f3a893d578348db11aee114d1a8f2d9fac5 --- diff --git a/src/random_state.rs b/src/random_state.rs -index f394cd0..d8280b7 100644 +index c3628bf..835467c 100644 --- a/src/random_state.rs +++ b/src/random_state.rs -@@ -34,6 +34,15 @@ use crate::aes_hash::*; - #[cfg(not(all(any(target_arch = "x86", target_arch = "x86_64"), target_feature = "aes", not(miri))))] - use crate::fallback_hash::*; +@@ -48,6 +48,15 @@ use crate::fallback_hash::*; + #[cfg(not(all(target_arch = "arm", target_os = "none")))] + static RAND_SOURCE: OnceBox<Box<dyn RandomSource + Send + Sync>> = OnceBox::new(); +#[cfg(all(feature = "runtime-rng", not(all(feature = "compile-time-rng", test))))] +fn read_urandom(dest: &mut [u8]) -> Result<(), std::io::Error> { @@ -33,28 +33,17 @@ index f394cd0..d8280b7 100644 + f.read_exact(dest) +} + - #[cfg(all(feature = "runtime-rng", not(all(feature = "compile-time-rng", test))))] - static SEEDS: OnceBox<[[u64; 4]; 2]> = OnceBox::new(); + /// A supplier of Randomness used for different hashers. + /// See [RandomState.set_random_source]. + pub trait RandomSource { +@@ -98,7 +107,9 @@ impl RandomSource for DefaultRandomSource { -@@ -59,7 +68,9 @@ pub(crate) fn seeds() -> [u64; 4] { - { SEEDS.get_or_init(|| { let mut result: [u8; 64] = [0; 64]; - getrandom::getrandom(&mut result).expect("getrandom::getrandom() failed."); + if read_urandom(&mut result).is_err() { -+ getrandom::getrandom(&mut result).expect("getrandom::getrandom() failed.") ++ getrandom::getrandom(&mut result).expect("getrandom::getrandom() failed."); + } Box::new(result.convert()) - })[1] + }) } -@@ -107,7 +118,9 @@ impl RandomState { - { - let seeds = SEEDS.get_or_init(|| { - let mut result: [u8; 64] = [0; 64]; -- getrandom::getrandom(&mut result).expect("getrandom::getrandom() failed."); -+ if read_urandom(&mut result).is_err() { -+ getrandom::getrandom(&mut result).expect("getrandom::getrandom() failed.") -+ } - Box::new(result.convert()) - }); - RandomState::from_keys(seeds[0], seeds[1]) diff --git a/src/aes_hash.rs b/src/aes_hash.rs index 3698c4e..1c98582 100644 --- a/src/aes_hash.rs +++ b/src/aes_hash.rs @@ -230,13 +230,13 @@ pub(crate) struct AHasherU64 { impl Hasher for AHasherU64 { #[inline] fn finish(&self) -> u64 { - let rot = (self.pad & 64) as u32; + let rot = (self.pad & 63) as u32; self.buffer.rotate_left(rot) } #[inline] fn write(&mut self, _bytes: &[u8]) { - unreachable!("This should never be called") + unreachable!("Specialized hasher was called with a different type of object") } #[inline] @@ -261,12 +261,12 @@ impl Hasher for AHasherU64 { #[inline] fn write_u128(&mut self, _i: u128) { - unreachable!("This should never be called") + unreachable!("Specialized hasher was called with a different type of object") } #[inline] fn write_usize(&mut self, _i: usize) { - unimplemented!() + unreachable!("Specialized hasher was called with a different type of object") } } diff --git a/src/convert.rs b/src/convert.rs index 1bacb82..4c0a00e 100644 --- a/src/convert.rs +++ b/src/convert.rs @@ -8,13 +8,7 @@ macro_rules! convert { #[inline(always)] fn convert(self) -> $b { unsafe { - let mut result: $b = core::mem::zeroed(); - core::ptr::copy_nonoverlapping( - &self as *const $a as *const u8, - &mut result as *mut $b as *mut u8, - core::mem::size_of::<$b>(), - ); - return result; + core::mem::transmute::<$a, $b>(self) } } } @@ -22,13 +16,7 @@ macro_rules! convert { #[inline(always)] fn convert(self) -> $a { unsafe { - let mut result: $a = core::mem::zeroed(); - core::ptr::copy_nonoverlapping( - &self as *const $b as *const u8, - &mut result as *mut $a as *mut u8, - core::mem::size_of::<$a>(), - ); - return result; + core::mem::transmute::<$b, $a>(self) } } } diff --git a/src/fallback_hash.rs b/src/fallback_hash.rs index 372debc..aad9efc 100644 --- a/src/fallback_hash.rs +++ b/src/fallback_hash.rs @@ -233,13 +233,13 @@ pub(crate) struct AHasherU64 { impl Hasher for AHasherU64 { #[inline] fn finish(&self) -> u64 { - let rot = (self.pad & 64) as u32; + let rot = (self.pad & 63) as u32; self.buffer.rotate_left(rot) } #[inline] fn write(&mut self, _bytes: &[u8]) { - unreachable!("This should never be called") + unreachable!("Specialized hasher was called with a different type of object") } #[inline] @@ -264,12 +264,12 @@ impl Hasher for AHasherU64 { #[inline] fn write_u128(&mut self, _i: u128) { - unreachable!("This should never be called") + unreachable!("Specialized hasher was called with a different type of object") } #[inline] fn write_usize(&mut self, _i: usize) { - unimplemented!() + unreachable!("Specialized hasher was called with a different type of object") } } diff --git a/src/hash_quality_test.rs b/src/hash_quality_test.rs index 837924d..4cd3156 100644 --- a/src/hash_quality_test.rs +++ b/src/hash_quality_test.rs @@ -316,6 +316,16 @@ fn test_padding_doesnot_collide<T: Hasher>(hasher: impl Fn() -> T) { } } +fn test_length_extension<T: Hasher>(hasher: impl Fn(u128, u128) -> T) { + for key in 0..256 { + let h1 = hasher(key, key); + let v1 = hash_with(&[0_u8, 0, 0, 0, 0, 0, 0, 0], h1); + let h2 = hasher(key, key); + let v2 = hash_with(&[1_u8, 0, 0, 0, 0, 0, 0, 0, 0], h2); + assert_ne!(v1, v2); + } +} + #[cfg(test)] mod fallback_tests { use crate::fallback_hash::*; @@ -377,10 +387,18 @@ mod fallback_tests { test_padding_doesnot_collide(|| AHasher::new_with_keys(2, 0)); test_padding_doesnot_collide(|| AHasher::new_with_keys(2, 2)); } + + #[test] + fn fallback_length_extension() { + test_length_extension(|a, b| AHasher::new_with_keys(a, b)); + } } ///Basic sanity tests of the cypto properties of aHash. -#[cfg(all(any(target_arch = "x86", target_arch = "x86_64"), target_feature = "aes", not(miri)))] +#[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") +))] #[cfg(test)] mod aes_tests { use crate::aes_hash::*; @@ -457,4 +475,9 @@ mod aes_tests { test_padding_doesnot_collide(|| AHasher::test_with_keys(BAD_KEY, BAD_KEY)); test_padding_doesnot_collide(|| AHasher::test_with_keys(BAD_KEY2, BAD_KEY2)); } + + #[test] + fn aes_length_extension() { + test_length_extension(|a, b| AHasher::test_with_keys(a, b)); + } } @@ -30,11 +30,15 @@ #![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 = "stdsimd", feature(stdsimd))] #[macro_use] mod convert; -#[cfg(all(any(target_arch = "x86", target_arch = "x86_64"), target_feature = "aes", not(miri)))] +#[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)] @@ -48,10 +52,16 @@ mod operations; mod random_state; mod specialize; -#[cfg(all(any(target_arch = "x86", target_arch = "x86_64"), target_feature = "aes", not(miri)))] +#[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(all(any(target_arch = "x86", target_arch = "x86_64"), target_feature = "aes", not(miri))))] +#[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; diff --git a/src/operations.rs b/src/operations.rs index 2071d6b..b71fd5a 100644 --- a/src/operations.rs +++ b/src/operations.rs @@ -100,6 +100,22 @@ pub(crate) fn aesenc(value: u128, xor: u128) -> u128 { transmute(_mm_aesenc_si128(value, transmute(xor))) } } + +#[cfg(all(any(target_arch = "arm", target_arch = "aarch64"), 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::*; + use core::mem::transmute; + unsafe { + let value = transmute(value); + transmute(vaesmcq_u8(vaeseq_u8(value, transmute(xor)))) + } +} + #[cfg(all(any(target_arch = "x86", target_arch = "x86_64"), target_feature = "aes", not(miri)))] #[allow(unused)] #[inline(always)] @@ -115,6 +131,21 @@ pub(crate) fn aesdec(value: u128, xor: u128) -> u128 { } } +#[cfg(all(any(target_arch = "arm", target_arch = "aarch64"), 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::*; + use core::mem::transmute; + unsafe { + let value = transmute(value); + transmute(vaesimcq_u8(vaesdq_u8(value, transmute(xor)))) + } +} + #[cfg(test)] mod test { use super::*; diff --git a/src/random_state.rs b/src/random_state.rs index d8280b7..835467c 100644 --- a/src/random_state.rs +++ b/src/random_state.rs @@ -3,14 +3,21 @@ use crate::convert::Convert; #[cfg(feature = "specialize")] use crate::BuildHasherExt; -#[cfg(all(any(target_arch = "x86", target_arch = "x86_64"), target_feature = "aes", not(miri)))] +#[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(all(any(target_arch = "x86", target_arch = "x86_64"), target_feature = "aes", not(miri))))] +#[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; +use core::any::{Any, TypeId}; use core::fmt; use core::hash::BuildHasher; #[cfg(feature = "specialize")] @@ -22,18 +29,25 @@ extern crate alloc; #[cfg(feature = "std")] extern crate std as alloc; - -#[cfg(all(feature = "runtime-rng", not(all(feature = "compile-time-rng", test))))] use alloc::boxed::Box; use core::sync::atomic::{AtomicUsize, Ordering}; -#[cfg(all(feature = "runtime-rng", not(all(feature = "compile-time-rng", test))))] +#[cfg(not(all(target_arch = "arm", target_os = "none")))] use once_cell::race::OnceBox; -#[cfg(all(any(target_arch = "x86", target_arch = "x86_64"), target_feature = "aes", not(miri)))] +#[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(all(any(target_arch = "x86", target_arch = "x86_64"), target_feature = "aes", not(miri))))] +#[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<Box<dyn RandomSource + Send + Sync>> = OnceBox::new(); + #[cfg(all(feature = "runtime-rng", not(all(feature = "compile-time-rng", test))))] fn read_urandom(dest: &mut [u8]) -> Result<(), std::io::Error> { use std::fs::File; @@ -43,10 +57,15 @@ fn read_urandom(dest: &mut [u8]) -> Result<(), std::io::Error> { f.read_exact(dest) } -#[cfg(all(feature = "runtime-rng", not(all(feature = "compile-time-rng", test))))] -static SEEDS: OnceBox<[[u64; 4]; 2]> = 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]; -static COUNTER: AtomicUsize = AtomicUsize::new(0); + fn gen_hasher_seed(&self) -> usize; + +} pub(crate) const PI: [u64; 4] = [ 0x243f_6a88_85a3_08d3, @@ -62,30 +81,75 @@ pub(crate) const PI2: [u64; 4] = [ 0x3f84_d5b5_b547_0917, ]; -#[inline] -pub(crate) fn seeds() -> [u64; 4] { +struct DefaultRandomSource { + counter: AtomicUsize, +} + +impl DefaultRandomSource { + fn new() -> DefaultRandomSource { + 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 { + #[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]; if read_urandom(&mut result).is_err() { - getrandom::getrandom(&mut result).expect("getrandom::getrandom() failed.") + getrandom::getrandom(&mut result).expect("getrandom::getrandom() failed."); } Box::new(result.convert()) - })[1] + }) } + #[cfg(all(feature = "compile-time-rng", any(not(feature = "runtime-rng"), test)))] - { - [ - const_random!(u64), - const_random!(u64), - const_random!(u64), - const_random!(u64), - ] + 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")))] - { - PI + 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; + 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 } } @@ -111,74 +175,59 @@ 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 + } + /// Use randomly generated keys #[inline] pub fn new() -> RandomState { - #[cfg(all(feature = "runtime-rng", not(all(feature = "compile-time-rng", test))))] - { - let seeds = SEEDS.get_or_init(|| { - let mut result: [u8; 64] = [0; 64]; - if read_urandom(&mut result).is_err() { - getrandom::getrandom(&mut result).expect("getrandom::getrandom() failed.") - } - Box::new(result.convert()) - }); - RandomState::from_keys(seeds[0], seeds[1]) - } - #[cfg(all(feature = "compile-time-rng", any(not(feature = "runtime-rng"), test)))] - { - RandomState::from_keys( - [ - const_random!(u64), - const_random!(u64), - const_random!(u64), - const_random!(u64), - ], - [ - const_random!(u64), - const_random!(u64), - const_random!(u64), - const_random!(u64), - ], - ) - } - #[cfg(all(not(feature = "runtime-rng"), not(feature = "compile-time-rng")))] - { - RandomState::from_keys(PI, PI2) - } + let src = Self::get_src(); + let fixed = src.get_fixed_seeds(); + Self::from_keys(&fixed[0], &fixed[1], src.gen_hasher_seed()) } /// Allows for supplying seeds, but each time it is called the resulting state will be different. /// 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 { - RandomState::from_keys(seeds(), [k0, k1, k2, k3]) + let src = Self::get_src(); + let fixed = src.get_fixed_seeds(); + RandomState::from_keys(&fixed[0], &[k0, k1, k2, k3], src.gen_hasher_seed()) } - fn from_keys(a: [u64; 4], b: [u64; 4]) -> RandomState { - let [k0, k1, k2, k3] = a; + fn from_keys(a: &[u64; 4], b: &[u64; 4], c: usize) -> RandomState { + let &[k0, k1, k2, k3] = a; let mut hasher = AHasher::from_random_state(&RandomState { k0, k1, k2, k3 }); - - let stack_mem_loc = &hasher as *const _ as usize; - #[cfg(not(all(target_arch = "arm", target_os = "none")))] - { - hasher.write_usize(COUNTER.fetch_add(stack_mem_loc, Ordering::Relaxed)); - } - #[cfg(all(target_arch = "arm", target_os = "none"))] - { - let previous = COUNTER.load(Ordering::Relaxed); - let new = previous.wrapping_add(stack_mem_loc); - COUNTER.store(new, Ordering::Relaxed); - hasher.write_usize(new); - } - #[cfg(all(not(feature = "runtime-rng"), not(feature = "compile-time-rng")))] - hasher.write_usize(&PI as *const _ as usize); + hasher.write_usize(c); let mix = |k: u64| { let mut h = hasher.clone(); h.write_u64(k); h.finish() }; - RandomState { k0: mix(b[0]), k1: mix(b[1]), @@ -190,11 +239,23 @@ impl RandomState { /// Internal. Used by Default. #[inline] pub(crate) fn with_fixed_keys() -> RandomState { - let [k0, k1, k2, k3] = seeds(); + let [k0, k1, k2, k3] = Self::get_src().get_fixed_seeds()[0]; RandomState { k0, k1, k2, k3 } } + /// Allows for explicitly setting a seed to used. + /// + /// 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(); + RandomState::from_keys(&fixed[0], &fixed[1], key) + } + /// Allows for explicitly setting the seeds to used. + /// + /// Note: This method is robust against 0s being passed for one or more of the parameters + /// 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] } @@ -286,19 +347,19 @@ mod test { #[cfg(all(feature = "runtime-rng", not(all(feature = "compile-time-rng", test))))] #[test] fn test_not_pi() { - assert_ne!(PI, seeds()); + assert_ne!(PI, RandomState::get_src().get_fixed_seeds()[0]); } #[cfg(all(feature = "compile-time-rng", any(not(feature = "runtime-rng"), test)))] #[test] fn test_not_pi_const() { - assert_ne!(PI, seeds()); + assert_ne!(PI, RandomState::get_src().get_fixed_seeds()[0]); } #[cfg(all(not(feature = "runtime-rng"), not(feature = "compile-time-rng")))] #[test] fn test_pi() { - assert_eq!(PI, seeds()); + assert_eq!(PI, RandomState::get_src().get_fixed_seeds()[0]); } #[test] diff --git a/src/specialize.rs b/src/specialize.rs index 7dddb9a..d94a4ee 100644 --- a/src/specialize.rs +++ b/src/specialize.rs @@ -25,9 +25,19 @@ use alloc::vec::Vec; /// /// let hash_builder = RandomState::new(); /// //... -/// let value = 17; +/// 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::new(); +/// let bytes: [u8; 4] = [1, 2, 3, 4]; +/// let hash = <[u8]>::get_hash(&bytes, &hash_builder); +/// ``` pub trait CallHasher { fn get_hash<H: Hash + ?Sized, B: BuildHasher>(value: &H, build_hasher: &B) -> u64; } diff --git a/tests/bench.rs b/tests/bench.rs index 16904c0..9e6dccc 100644 --- a/tests/bench.rs +++ b/tests/bench.rs @@ -4,22 +4,34 @@ use fxhash::FxHasher; use std::collections::hash_map::DefaultHasher; use std::hash::{Hash, Hasher}; -#[cfg(all(any(target_arch = "x86", target_arch = "x86_64"), target_feature = "aes"))] +#[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") +))] fn aeshash<H: Hash>(b: &H) -> u64 { let build_hasher = RandomState::with_seeds(1, 2, 3, 4); H::get_hash(b, &build_hasher) } -#[cfg(not(all(any(target_arch = "x86", target_arch = "x86_64"), target_feature = "aes")))] +#[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") +)))] fn aeshash<H: Hash>(_b: &H) -> u64 { panic!("aes must be enabled") } -#[cfg(not(all(any(target_arch = "x86", target_arch = "x86_64"), target_feature = "aes")))] +#[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") +)))] fn fallbackhash<H: Hash>(b: &H) -> u64 { let build_hasher = RandomState::with_seeds(1, 2, 3, 4); H::get_hash(b, &build_hasher) } -#[cfg(all(any(target_arch = "x86", target_arch = "x86_64"), target_feature = "aes"))] +#[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") +))] fn fallbackhash<H: Hash>(_b: &H) -> u64 { panic!("aes must be disabled") } |