diff --git a/Cargo.toml b/Cargo.toml index 568554d..a986589 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -85,6 +85,7 @@ cfg-if = "1.0" portable-atomic = { version = "1.0.0", optional = true } getrandom = { version = "0.3.1", optional = true } zerocopy = { version = "0.8.24", default-features = false, features = ["simd"] } +typeid = "1.0" [target.'cfg(not(all(target_arch = "arm", target_os = "none")))'.dependencies] once_cell = { version = "1.18.0", default-features = false, features = ["alloc"] } diff --git a/build.rs b/build.rs index f59538c..ae7cf3e 100644 --- a/build.rs +++ b/build.rs @@ -4,10 +4,12 @@ use std::env; fn main() { println!("cargo:rerun-if-changed=build.rs"); - println!("cargo:rustc-check-cfg=cfg(specialize)"); - if let Some(true) = version_check::supports_feature("specialize") { - println!("cargo:rustc-cfg=specialize"); + + println!("cargo:rustc-check-cfg=cfg(build_hasher_hash_one)"); + if let Some(true) = version_check::is_min_version("1.71.0") { + println!("cargo:rustc-cfg=build_hasher_hash_one"); } + let arch = env::var("CARGO_CFG_TARGET_ARCH").expect("CARGO_CFG_TARGET_ARCH was not set"); println!("cargo:rustc-check-cfg=cfg(folded_multiply)"); if arch.eq_ignore_ascii_case("x86_64") diff --git a/src/aes_hash.rs b/src/aes_hash.rs index dd6f925..c0c5063 100644 --- a/src/aes_hash.rs +++ b/src/aes_hash.rs @@ -94,7 +94,6 @@ impl AHasher { } #[inline] - #[cfg(specialize)] fn short_finish(&self) -> u64 { let combined = aesenc(self.sum, self.enc); let result: [u64; 2] = aesdec(combined, combined).convert(); @@ -214,14 +213,12 @@ impl Hasher for AHasher { } } -#[cfg(specialize)] pub(crate) struct AHasherU64 { pub(crate) buffer: u64, pub(crate) pad: u64, } /// A specialized hasher for only primitives under 64 bits. -#[cfg(specialize)] impl Hasher for AHasherU64 { #[inline] fn finish(&self) -> u64 { @@ -264,11 +261,9 @@ impl Hasher for AHasherU64 { } } -#[cfg(specialize)] pub(crate) struct AHasherFixed(pub AHasher); /// A specialized hasher for fixed size primitives larger than 64 bits. -#[cfg(specialize)] impl Hasher for AHasherFixed { #[inline] fn finish(&self) -> u64 { @@ -311,12 +306,10 @@ impl Hasher for AHasherFixed { } } -#[cfg(specialize)] pub(crate) struct AHasherStr(pub AHasher); /// A specialized hasher for strings /// Note that the other types don't panic because the hash impl for String tacks on an unneeded call. (As does vec) -#[cfg(specialize)] impl Hasher for AHasherStr { #[inline] fn finish(&self) -> u64 { diff --git a/src/fallback_hash.rs b/src/fallback_hash.rs index 9a2956d..e2ca2a4 100644 --- a/src/fallback_hash.rs +++ b/src/fallback_hash.rs @@ -115,7 +115,6 @@ impl AHasher { } #[inline] - #[cfg(specialize)] fn short_finish(&self) -> u64 { folded_multiply(self.buffer, self.pad) } @@ -199,14 +198,12 @@ impl Hasher for AHasher { } } -#[cfg(specialize)] pub(crate) struct AHasherU64 { pub(crate) buffer: u64, pub(crate) pad: u64, } /// A specialized hasher for only primitives under 64 bits. -#[cfg(specialize)] impl Hasher for AHasherU64 { #[inline] fn finish(&self) -> u64 { @@ -250,11 +247,9 @@ impl Hasher for AHasherU64 { } } -#[cfg(specialize)] pub(crate) struct AHasherFixed(pub AHasher); /// A specialized hasher for fixed size primitives larger than 64 bits. -#[cfg(specialize)] impl Hasher for AHasherFixed { #[inline] fn finish(&self) -> u64 { @@ -297,12 +292,10 @@ impl Hasher for AHasherFixed { } } -#[cfg(specialize)] pub(crate) struct AHasherStr(pub AHasher); /// A specialized hasher for a single string /// Note that the other types don't panic because the hash impl for String tacks on an unneeded call. (As does vec) -#[cfg(specialize)] impl Hasher for AHasherStr { #[inline] fn finish(&self) -> u64 { diff --git a/src/hash_quality_test.rs b/src/hash_quality_test.rs index eaa0de2..9fa561b 100644 --- a/src/hash_quality_test.rs +++ b/src/hash_quality_test.rs @@ -406,9 +406,6 @@ mod fallback_tests { #[test] fn fallback_keys_affect_every_byte() { - //For fallback second key is not used in every hash. - #[cfg(all(not(specialize), folded_multiply))] - 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)); } @@ -504,8 +501,6 @@ mod aes_tests { #[test] fn aes_keys_affect_every_byte() { - #[cfg(not(specialize))] - 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 270df3c..db285e5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -97,7 +97,6 @@ Note the import of [HashMapExt]. This is needed for the constructor. #![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)] -#![cfg_attr(specialize, feature(min_specialization))] #![cfg_attr(feature = "nightly-arm-aes", feature(stdarch_arm_neon_intrinsics))] #[macro_use] diff --git a/src/random_state.rs b/src/random_state.rs index a8066dc..1b2b22e 100644 --- a/src/random_state.rs +++ b/src/random_state.rs @@ -356,6 +356,30 @@ impl RandomState { use crate::specialize::CallHasher; T::get_hash(&x, self) } + + #[inline] + pub(crate) fn hash_as_u64(&self, value: &T) -> u64 { + let mut hasher = AHasherU64 { + buffer: self.k1, + pad: self.k0, + }; + value.hash(&mut hasher); + hasher.finish() + } + + #[inline] + pub(crate) fn hash_as_fixed_length(&self, value: &T) -> u64 { + let mut hasher = AHasherFixed(self.build_hasher()); + value.hash(&mut hasher); + hasher.finish() + } + + #[inline] + pub(crate) fn hash_as_str(&self, value: &T) -> u64 { + let mut hasher = AHasherStr(self.build_hasher()); + value.hash(&mut hasher); + hasher.finish() + } } /// Creates an instance of RandomState using keys obtained from the random number generator. @@ -453,40 +477,13 @@ impl BuildHasher for RandomState { /// 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(specialize)] + #[cfg(build_hasher_hash_one)] #[inline] fn hash_one(&self, x: T) -> u64 { RandomState::hash_one(self, x) } } -#[cfg(specialize)] -impl RandomState { - #[inline] - pub(crate) fn hash_as_u64(&self, value: &T) -> u64 { - let mut hasher = AHasherU64 { - buffer: self.k1, - pad: self.k0, - }; - value.hash(&mut hasher); - hasher.finish() - } - - #[inline] - pub(crate) fn hash_as_fixed_length(&self, value: &T) -> u64 { - let mut hasher = AHasherFixed(self.build_hasher()); - value.hash(&mut hasher); - hasher.finish() - } - - #[inline] - pub(crate) fn hash_as_str(&self, value: &T) -> u64 { - let mut hasher = AHasherStr(self.build_hasher()); - value.hash(&mut hasher); - hasher.finish() - } -} - #[cfg(test)] mod test { use super::*; diff --git a/src/specialize.rs b/src/specialize.rs index 0ba76da..377c543 100644 --- a/src/specialize.rs +++ b/src/specialize.rs @@ -1,4 +1,5 @@ use crate::RandomState; +use core::any::TypeId; use core::hash::BuildHasher; use core::hash::Hash; use core::hash::Hasher; @@ -8,11 +9,18 @@ extern crate alloc; #[cfg(feature = "std")] extern crate std as alloc; -#[cfg(specialize)] use alloc::string::String; -#[cfg(specialize)] use alloc::vec::Vec; +#[inline] +fn is() -> bool +where + Generic: ?Sized, + Expected: ?Sized + 'static, +{ + typeid::of::() == TypeId::of::() +} + /// 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. @@ -20,110 +28,51 @@ pub(crate) trait CallHasher { fn get_hash(value: &H, random_state: &RandomState) -> u64; } -#[cfg(not(specialize))] impl CallHasher for T where T: Hash + ?Sized, { #[inline] fn get_hash(value: &H, random_state: &RandomState) -> u64 { - let mut hasher = random_state.build_hasher(); - value.hash(&mut hasher); - hasher.finish() - } -} - -#[cfg(specialize)] -impl CallHasher for T -where - T: Hash + ?Sized, -{ - #[inline] - default fn get_hash(value: &H, random_state: &RandomState) -> u64 { - let mut hasher = random_state.build_hasher(); - value.hash(&mut hasher); - hasher.finish() - } -} - -macro_rules! call_hasher_impl_u64 { - ($typ:ty) => { - #[cfg(specialize)] - impl CallHasher for $typ { - #[inline] - fn get_hash(value: &H, random_state: &RandomState) -> u64 { - random_state.hash_as_u64(value) - } + if is::() + || is::() + || is::() + || is::() + || is::() + || is::() + || is::() + || is::() + || is::() + || is::() + || is::() + || is::() + || is::() + || is::() + || is::() + || is::() + { + random_state.hash_as_u64(value) + } else if is::() + || is::() + || is::() + || is::() + || is::() + || is::() + || is::() + || is::() + { + random_state.hash_as_fixed_length(value) + } else if is::() + || is::>() + || is::() + || is::() + { + random_state.hash_as_str(value) + } else { + let mut hasher = random_state.build_hasher(); + value.hash(&mut hasher); + hasher.finish() } - }; -} -call_hasher_impl_u64!(u8); -call_hasher_impl_u64!(u16); -call_hasher_impl_u64!(u32); -call_hasher_impl_u64!(u64); -call_hasher_impl_u64!(i8); -call_hasher_impl_u64!(i16); -call_hasher_impl_u64!(i32); -call_hasher_impl_u64!(i64); -call_hasher_impl_u64!(&u8); -call_hasher_impl_u64!(&u16); -call_hasher_impl_u64!(&u32); -call_hasher_impl_u64!(&u64); -call_hasher_impl_u64!(&i8); -call_hasher_impl_u64!(&i16); -call_hasher_impl_u64!(&i32); -call_hasher_impl_u64!(&i64); - -macro_rules! call_hasher_impl_fixed_length{ - ($typ:ty) => { - #[cfg(specialize)] - impl CallHasher for $typ { - #[inline] - fn get_hash(value: &H, random_state: &RandomState) -> u64 { - random_state.hash_as_fixed_length(value) - } - } - }; -} - -call_hasher_impl_fixed_length!(u128); -call_hasher_impl_fixed_length!(i128); -call_hasher_impl_fixed_length!(usize); -call_hasher_impl_fixed_length!(isize); -call_hasher_impl_fixed_length!(&u128); -call_hasher_impl_fixed_length!(&i128); -call_hasher_impl_fixed_length!(&usize); -call_hasher_impl_fixed_length!(&isize); - -#[cfg(specialize)] -impl CallHasher for [u8] { - #[inline] - fn get_hash(value: &H, random_state: &RandomState) -> u64 { - random_state.hash_as_str(value) - } -} - -#[cfg(specialize)] -impl CallHasher for Vec { - #[inline] - fn get_hash(value: &H, random_state: &RandomState) -> u64 { - random_state.hash_as_str(value) - } -} - -#[cfg(specialize)] -impl CallHasher for str { - #[inline] - fn get_hash(value: &H, random_state: &RandomState) -> u64 { - random_state.hash_as_str(value) - } -} - -#[cfg(all(specialize))] -impl CallHasher for String { - #[inline] - fn get_hash(value: &H, random_state: &RandomState) -> u64 { - random_state.hash_as_str(value) } } @@ -133,7 +82,6 @@ mod test { use crate::*; #[test] - #[cfg(specialize)] pub fn test_specialized_invoked() { let build_hasher = RandomState::with_seeds(1, 2, 3, 4); let shortened = u64::get_hash(&0, &build_hasher); @@ -185,7 +133,6 @@ mod test { str::get_hash(&"test", &build_hasher), String::get_hash(&"test".to_string(), &build_hasher) ); - #[cfg(specialize)] assert_eq!( str::get_hash(&"test", &build_hasher), <[u8]>::get_hash("test".as_bytes(), &build_hasher) @@ -205,7 +152,6 @@ mod test { str::get_hash(&&"test", &build_hasher), String::get_hash(&"test".to_string(), &build_hasher) ); - #[cfg(specialize)] assert_eq!( str::get_hash(&&"test", &build_hasher), <[u8]>::get_hash(&"test".to_string().into_bytes(), &build_hasher) diff --git a/tests/bench.rs b/tests/bench.rs index 6432a2c..945a052 100644 --- a/tests/bench.rs +++ b/tests/bench.rs @@ -1,5 +1,3 @@ -#![cfg_attr(specialize, feature(build_hasher_simple_hash_one))] - use ahash::{AHasher, RandomState}; use criterion::*; use fxhash::FxHasher; diff --git a/tests/map_tests.rs b/tests/map_tests.rs index 42edb57..1ef6b31 100644 --- a/tests/map_tests.rs +++ b/tests/map_tests.rs @@ -1,5 +1,3 @@ -#![cfg_attr(specialize, feature(build_hasher_simple_hash_one))] - use std::hash::{BuildHasher, Hash, Hasher}; use ahash::RandomState; @@ -151,13 +149,13 @@ fn check_for_collisions(build_hasher: &B, items: &[H], ); } -#[cfg(specialize)] +#[cfg(build_hasher_hash_one)] #[allow(unused)] // False positive fn hash(b: &H, build_hasher: &B) -> u64 { build_hasher.hash_one(b) } -#[cfg(not(specialize))] +#[cfg(not(build_hasher_hash_one))] #[allow(unused)] // False positive fn hash(b: &H, build_hasher: &B) -> u64 { let mut hasher = build_hasher.build_hasher();