aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2022-04-28 16:01:47 +0000
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2022-04-28 16:01:47 +0000
commite9882f673704a27b8e53b727a718b9dbf396f35c (patch)
tree0b929457b4bd5c335b5c028b8956fe95b8a251f1
parent0d2efbfe9640b299e76b909a6d0af35ca13761ed (diff)
parent1319627187e18db41095e680e6d8eaba75753fdd (diff)
downloadahash-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.json2
-rw-r--r--Android.bp2
-rw-r--r--Cargo.toml15
-rw-r--r--Cargo.toml.orig10
-rw-r--r--METADATA10
-rw-r--r--build.rs1
-rw-r--r--patches/0001-Use-dev-urandom-instead-of-getrandom.patch31
-rw-r--r--src/aes_hash.rs8
-rw-r--r--src/convert.rs16
-rw-r--r--src/fallback_hash.rs8
-rw-r--r--src/hash_quality_test.rs25
-rw-r--r--src/lib.rs16
-rw-r--r--src/operations.rs31
-rw-r--r--src/random_state.rs219
-rw-r--r--src/specialize.rs12
-rw-r--r--tests/bench.rs20
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"
}
}
diff --git a/Android.bp b/Android.bp
index 27d6b4c..9e1253a 100644
--- a/Android.bp
+++ b/Android.bp
@@ -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: {
diff --git a/Cargo.toml b/Cargo.toml
index d39374c..b412f79 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -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"}
diff --git a/METADATA b/METADATA
index 7738498..7ee2368 100644
--- a/METADATA
+++ b/METADATA
@@ -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
}
}
diff --git a/build.rs b/build.rs
index 118c250..8be4964 100644
--- a/build.rs
+++ b/build.rs
@@ -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));
+ }
}
diff --git a/src/lib.rs b/src/lib.rs
index 4ee1ac6..9964a7c 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -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")
}