diff --git a/Cargo.toml b/Cargo.toml index a63f6d5..b1c7d40 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ahash" -version = "0.8.11" +version = "0.8.12" authors = ["Tom Kaitchuck "] license = "MIT OR Apache-2.0" description = "A non-cryptographic hash function using AES-NI for high performance" @@ -42,7 +42,7 @@ compile-time-rng = ["const-random"] no-rng = [] # in case this is being used on an architecture lacking core::sync::atomic::AtomicUsize and friends -atomic-polyfill = [ "dep:atomic-polyfill", "once_cell/atomic-polyfill"] +atomic-polyfill = [ "dep:portable-atomic", "once_cell/critical-section"] # Nightly-only support for AES intrinsics on 32-bit ARM nightly-arm-aes = [] @@ -82,9 +82,9 @@ version_check = "0.9.4" const-random = { version = "0.1.17", optional = true } serde = { version = "1.0.117", optional = true } cfg-if = "1.0" -atomic-polyfill = { version="1.0.1", optional=true} -getrandom = { version = "0.2.7", optional = true } -zerocopy = { version = "0.7.31", default-features = false, features = ["simd"] } +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"] } [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 a136b36..f59538c 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=feature=\"specialize\""); + println!("cargo:rustc-cfg=specialize"); } 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") || arch.eq_ignore_ascii_case("aarch64") || arch.eq_ignore_ascii_case("mips64") @@ -15,6 +17,6 @@ fn main() { || arch.eq_ignore_ascii_case("riscv64gc") || arch.eq_ignore_ascii_case("s390x") { - println!("cargo:rustc-cfg=feature=\"folded_multiply\""); + println!("cargo:rustc-cfg=folded_multiply"); } } diff --git a/no_std_test/src/main.rs b/no_std_test/src/main.rs index d4bcced..f4b8748 100644 --- a/no_std_test/src/main.rs +++ b/no_std_test/src/main.rs @@ -1,8 +1,8 @@ //! This is a bare-bones `no-std` application that hashes a value and //! uses the hash value as the return value. - +#![no_main] #![no_std] -#![feature(alloc_error_handler, start, core_intrinsics, lang_items, link_cfg)] +#![feature(alloc_error_handler, core_intrinsics, lang_items)] #[global_allocator] static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT; @@ -15,7 +15,7 @@ use core::hash::{Hash, Hasher}; #[link(name = "libcmt")] extern "C" {} -#[start] +#[no_mangle] fn main(_argc: isize, _argv: *const *const u8) -> isize { let mut h: ahash::AHasher = Default::default(); 42_i32.hash(&mut h); diff --git a/src/aes_hash.rs b/src/aes_hash.rs index daf3ae4..dd6f925 100644 --- a/src/aes_hash.rs +++ b/src/aes_hash.rs @@ -34,7 +34,7 @@ impl AHasher { /// /// # Example /// - /// ``` + /// ```no_build /// use std::hash::Hasher; /// use ahash::AHasher; /// @@ -94,7 +94,7 @@ impl AHasher { } #[inline] - #[cfg(feature = "specialize")] + #[cfg(specialize)] fn short_finish(&self) -> u64 { let combined = aesenc(self.sum, self.enc); let result: [u64; 2] = aesdec(combined, combined).convert(); @@ -214,14 +214,14 @@ impl Hasher for AHasher { } } -#[cfg(feature = "specialize")] +#[cfg(specialize)] pub(crate) struct AHasherU64 { pub(crate) buffer: u64, pub(crate) pad: u64, } /// A specialized hasher for only primitives under 64 bits. -#[cfg(feature = "specialize")] +#[cfg(specialize)] impl Hasher for AHasherU64 { #[inline] fn finish(&self) -> u64 { @@ -264,11 +264,11 @@ impl Hasher for AHasherU64 { } } -#[cfg(feature = "specialize")] +#[cfg(specialize)] pub(crate) struct AHasherFixed(pub AHasher); /// A specialized hasher for fixed size primitives larger than 64 bits. -#[cfg(feature = "specialize")] +#[cfg(specialize)] impl Hasher for AHasherFixed { #[inline] fn finish(&self) -> u64 { @@ -311,12 +311,12 @@ impl Hasher for AHasherFixed { } } -#[cfg(feature = "specialize")] +#[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(feature = "specialize")] +#[cfg(specialize)] impl Hasher for AHasherStr { #[inline] fn finish(&self) -> u64 { @@ -423,11 +423,4 @@ mod tests { let result2: [u8; 16] = result2.convert(); assert_ne!(hex::encode(result), hex::encode(result2)); } - - #[test] - fn test_conversion() { - let input: &[u8] = "dddddddd".as_bytes(); - let bytes: u64 = as_array!(input, 8).convert(); - assert_eq!(bytes, 0x6464646464646464); - } } diff --git a/src/convert.rs b/src/convert.rs index 712eae1..4169f5f 100644 --- a/src/convert.rs +++ b/src/convert.rs @@ -19,47 +19,36 @@ macro_rules! convert { }; } -convert!([u128; 4], [u64; 8]); -convert!([u128; 4], [u32; 16]); -convert!([u128; 4], [u16; 32]); +macro_rules! convert_primitive_bytes { + ($a:ty, $b:ty) => { + impl Convert<$b> for $a { + #[inline(always)] + fn convert(self) -> $b { + self.to_ne_bytes() + } + } + impl Convert<$a> for $b { + #[inline(always)] + fn convert(self) -> $a { + <$a>::from_ne_bytes(self) + } + } + }; +} + convert!([u128; 4], [u8; 64]); convert!([u128; 2], [u64; 4]); -convert!([u128; 2], [u32; 8]); -convert!([u128; 2], [u16; 16]); convert!([u128; 2], [u8; 32]); convert!(u128, [u64; 2]); -convert!(u128, [u32; 4]); -convert!(u128, [u16; 8]); -convert!(u128, [u8; 16]); -convert!([u64; 8], [u32; 16]); -convert!([u64; 8], [u16; 32]); -convert!([u64; 8], [u8; 64]); -convert!([u64; 4], [u32; 8]); -convert!([u64; 4], [u16; 16]); -convert!([u64; 4], [u8; 32]); +convert_primitive_bytes!(u128, [u8; 16]); convert!([u64; 2], [u32; 4]); -convert!([u64; 2], [u16; 8]); +#[cfg(test)] convert!([u64; 2], [u8; 16]); -convert!([u32; 4], [u16; 8]); -convert!([u32; 4], [u8; 16]); -convert!([u16; 8], [u8; 16]); -convert!(u64, [u32; 2]); -convert!(u64, [u16; 4]); -convert!(u64, [u8; 8]); -convert!([u32; 2], [u16; 4]); -convert!([u32; 2], [u8; 8]); -convert!(u32, [u16; 2]); -convert!(u32, [u8; 4]); -convert!([u16; 2], [u8; 4]); -convert!(u16, [u8; 2]); +convert_primitive_bytes!(u64, [u8; 8]); +convert_primitive_bytes!(u32, [u8; 4]); +convert_primitive_bytes!(u16, [u8; 2]); convert!([[u64; 4]; 2], [u8; 64]); -convert!([f64; 2], [u8; 16]); -convert!([f32; 4], [u8; 16]); -convert!(f64, [u8; 8]); -convert!([f32; 2], [u8; 8]); -convert!(f32, [u8; 4]); - macro_rules! as_array { ($input:expr, $len:expr) => {{ { diff --git a/src/fallback_hash.rs b/src/fallback_hash.rs index bc5cbfe..9a2956d 100644 --- a/src/fallback_hash.rs +++ b/src/fallback_hash.rs @@ -115,7 +115,7 @@ impl AHasher { } #[inline] - #[cfg(feature = "specialize")] + #[cfg(specialize)] fn short_finish(&self) -> u64 { folded_multiply(self.buffer, self.pad) } @@ -199,14 +199,14 @@ impl Hasher for AHasher { } } -#[cfg(feature = "specialize")] +#[cfg(specialize)] pub(crate) struct AHasherU64 { pub(crate) buffer: u64, pub(crate) pad: u64, } /// A specialized hasher for only primitives under 64 bits. -#[cfg(feature = "specialize")] +#[cfg(specialize)] impl Hasher for AHasherU64 { #[inline] fn finish(&self) -> u64 { @@ -250,11 +250,11 @@ impl Hasher for AHasherU64 { } } -#[cfg(feature = "specialize")] +#[cfg(specialize)] pub(crate) struct AHasherFixed(pub AHasher); /// A specialized hasher for fixed size primitives larger than 64 bits. -#[cfg(feature = "specialize")] +#[cfg(specialize)] impl Hasher for AHasherFixed { #[inline] fn finish(&self) -> u64 { @@ -297,12 +297,12 @@ impl Hasher for AHasherFixed { } } -#[cfg(feature = "specialize")] +#[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(feature = "specialize")] +#[cfg(specialize)] impl Hasher for AHasherStr { #[inline] fn finish(&self) -> u64 { @@ -357,11 +357,4 @@ mod tests { let result2: [u8; 8] = result2.convert(); assert_ne!(hex::encode(result), hex::encode(result2)); } - - #[test] - fn test_conversion() { - let input: &[u8] = "dddddddd".as_bytes(); - let bytes: u64 = as_array!(input, 8).convert(); - assert_eq!(bytes, 0x6464646464646464); - } } diff --git a/src/hash_map.rs b/src/hash_map.rs index 2b6fbdc..ac30776 100644 --- a/src/hash_map.rs +++ b/src/hash_map.rs @@ -51,13 +51,13 @@ impl Into> for AHashMap { } impl AHashMap { - /// This crates a hashmap using [RandomState::new] which obtains its keys from [RandomSource]. + /// This creates a hashmap using [RandomState::new] which obtains its keys from [RandomSource]. /// See the documentation in [RandomSource] for notes about key strength. pub fn new() -> Self { AHashMap(HashMap::with_hasher(RandomState::new())) } - /// This crates a hashmap with the specified capacity using [RandomState::new]. + /// This creates a hashmap with the specified capacity using [RandomState::new]. /// See the documentation in [RandomSource] for notes about key strength. pub fn with_capacity(capacity: usize) -> Self { AHashMap(HashMap::with_capacity_and_hasher(capacity, RandomState::new())) @@ -348,7 +348,7 @@ impl FromIterator<(K, V)> for AHashMap where K: Eq + Hash, { - /// This crates a hashmap from the provided iterator using [RandomState::new]. + /// This creates a hashmap from the provided iterator using [RandomState::new]. /// See the documentation in [RandomSource] for notes about key strength. fn from_iter>(iter: T) -> Self { let mut inner = HashMap::with_hasher(RandomState::new()); @@ -404,7 +404,7 @@ where } } -/// NOTE: For safety this trait impl is only available available if either of the flags `runtime-rng` (on by default) or +/// NOTE: For safety this trait impl is only available if either of the flags `runtime-rng` (on by default) or /// `compile-time-rng` are enabled. This is to prevent weakly keyed maps from being accidentally created. Instead one of /// constructors for [RandomState] must be used. #[cfg(any(feature = "compile-time-rng", feature = "runtime-rng", feature = "no-rng"))] diff --git a/src/hash_quality_test.rs b/src/hash_quality_test.rs index f2fab16..a725380 100644 --- a/src/hash_quality_test.rs +++ b/src/hash_quality_test.rs @@ -70,8 +70,8 @@ fn test_no_full_collisions(gen_hash: impl Fn() -> T) { gen_combinations(&options, 7, Vec::new(), &mut combinations); let mut map: HashMap> = HashMap::new(); for combination in combinations { - use zerocopy::AsBytes; - let array = combination.as_slice().as_bytes().to_vec(); + use zerocopy::IntoBytes; + let array = combination.as_bytes().to_vec(); let mut hasher = gen_hash(); hasher.write(&array); let hash = hasher.finish(); @@ -407,7 +407,7 @@ mod fallback_tests { #[test] fn fallback_keys_affect_every_byte() { //For fallback second key is not used in every hash. - #[cfg(all(not(feature = "specialize"), feature = "folded_multiply"))] + #[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)); @@ -441,7 +441,7 @@ 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(target_arch = "aarch64", target_feature = "aes", not(miri)), + all(feature = "nightly-arm-aes", target_arch = "aarch64", target_feature = "aes", not(miri)), all(feature = "nightly-arm-aes", target_arch = "arm", target_feature = "aes", not(miri)), ))] #[cfg(test)] @@ -504,7 +504,7 @@ mod aes_tests { #[test] fn aes_keys_affect_every_byte() { - #[cfg(not(feature = "specialize"))] + #[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/hash_set.rs b/src/hash_set.rs index d03bef5..e12d8b0 100644 --- a/src/hash_set.rs +++ b/src/hash_set.rs @@ -47,13 +47,13 @@ impl Into> for AHashSet { } impl AHashSet { - /// This crates a hashset using [RandomState::new]. + /// This creates a hashset using [RandomState::new]. /// See the documentation in [RandomSource] for notes about key strength. pub fn new() -> Self { AHashSet(HashSet::with_hasher(RandomState::new())) } - /// This crates a hashset with the specified capacity using [RandomState::new]. + /// This craetes a hashset with the specified capacity using [RandomState::new]. /// See the documentation in [RandomSource] for notes about key strength. pub fn with_capacity(capacity: usize) -> Self { AHashSet(HashSet::with_capacity_and_hasher(capacity, RandomState::new())) @@ -245,7 +245,7 @@ impl FromIterator for AHashSet where T: Eq + Hash, { - /// This crates a hashset from the provided iterator using [RandomState::new]. + /// This creates a hashset from the provided iterator using [RandomState::new]. /// See the documentation in [RandomSource] for notes about key strength. #[inline] fn from_iter>(iter: I) -> AHashSet { diff --git a/src/lib.rs b/src/lib.rs index 69fb2ca..826806b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -34,7 +34,7 @@ map.insert(12, 34); The above requires a source of randomness to generate keys for the hashmap. By default this obtained from the OS. It is also possible to have randomness supplied via the `compile-time-rng` flag, or manually. -### If randomess is not available +### If randomness is not available [AHasher::default()] can be used to hash using fixed keys. This works with [BuildHasherDefault](std::hash::BuildHasherDefault). For example: @@ -97,7 +97,7 @@ 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(feature = "specialize", feature(min_specialization))] +#![cfg_attr(specialize, feature(min_specialization))] #![cfg_attr(feature = "nightly-arm-aes", feature(stdarch_arm_neon_intrinsics))] #[macro_use] @@ -146,8 +146,6 @@ mod specialize; pub use crate::random_state::RandomState; use core::hash::BuildHasher; -use core::hash::Hash; -use core::hash::Hasher; #[cfg(feature = "std")] /// A convenience trait that can be used together with the type aliases defined to @@ -248,63 +246,6 @@ impl Default for AHasher { } } -/// Used for specialization. (Sealed) -pub(crate) trait BuildHasherExt: BuildHasher { - #[doc(hidden)] - fn hash_as_u64(&self, value: &T) -> u64; - - #[doc(hidden)] - fn hash_as_fixed_length(&self, value: &T) -> u64; - - #[doc(hidden)] - fn hash_as_str(&self, value: &T) -> u64; -} - -impl BuildHasherExt for B { - #[inline] - #[cfg(feature = "specialize")] - default fn hash_as_u64(&self, value: &T) -> u64 { - let mut hasher = self.build_hasher(); - value.hash(&mut hasher); - hasher.finish() - } - #[inline] - #[cfg(not(feature = "specialize"))] - fn hash_as_u64(&self, value: &T) -> u64 { - let mut hasher = self.build_hasher(); - value.hash(&mut hasher); - hasher.finish() - } - #[inline] - #[cfg(feature = "specialize")] - default fn hash_as_fixed_length(&self, value: &T) -> u64 { - let mut hasher = self.build_hasher(); - value.hash(&mut hasher); - hasher.finish() - } - #[inline] - #[cfg(not(feature = "specialize"))] - fn hash_as_fixed_length(&self, value: &T) -> u64 { - let mut hasher = self.build_hasher(); - value.hash(&mut hasher); - hasher.finish() - } - #[inline] - #[cfg(feature = "specialize")] - default fn hash_as_str(&self, value: &T) -> u64 { - let mut hasher = self.build_hasher(); - value.hash(&mut hasher); - hasher.finish() - } - #[inline] - #[cfg(not(feature = "specialize"))] - fn hash_as_str(&self, value: &T) -> u64 { - let mut hasher = self.build_hasher(); - value.hash(&mut hasher); - hasher.finish() - } -} - // #[inline(never)] // #[doc(hidden)] // pub fn hash_test(input: &[u8]) -> u64 { @@ -318,6 +259,8 @@ mod test { use crate::convert::Convert; use crate::specialize::CallHasher; use crate::*; + use core::hash::Hash; + use core::hash::Hasher; use std::collections::HashMap; #[test] @@ -393,4 +336,23 @@ mod test { fn test_ahasher_construction() { let _ = AHasher::new_with_keys(1234, 5678); } + + #[test] + fn test_specialize_reference_hash() { + let hasher_build = RandomState::with_seeds(0, 0, 0, 0); + let h1 = hasher_build.hash_one(1u64); + let h2 = hasher_build.hash_one(&1u64); + + assert_eq!(h1, h2); + + let h1 = u64::get_hash(&1_u64, &hasher_build); + let h2 = <&u64>::get_hash(&&1_u64, &hasher_build); + + assert_eq!(h1, h2); + + let h1 = hasher_build.hash_one(1u128); + let h2 = hasher_build.hash_one(&1u128); + + assert_eq!(h1, h2); + } } diff --git a/src/operations.rs b/src/operations.rs index 8395007..c426036 100644 --- a/src/operations.rs +++ b/src/operations.rs @@ -13,14 +13,14 @@ const SHUFFLE_MASK: u128 = 0x020a0700_0c01030e_050f0d08_06090b04_u128; //const SHUFFLE_MASK: u128 = 0x040A0700_030E0106_0D050F08_020B0C09_u128; #[inline(always)] -#[cfg(feature = "folded_multiply")] +#[cfg(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"))] +#[cfg(not(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); @@ -364,9 +364,11 @@ mod test { #[test] fn test_add_length() { - let mut enc = (u64::MAX as u128) << 64 | 50; + let enc : [u64; 2] = [50, u64::MAX]; + let mut enc : u128 = enc.convert(); add_in_length(&mut enc, u64::MAX); - assert_eq!(enc >> 64, u64::MAX as u128); - assert_eq!(enc as u64, 49); + let enc : [u64; 2] = enc.convert(); + assert_eq!(enc[1], u64::MAX); + assert_eq!(enc[0], 49); } } diff --git a/src/random_state.rs b/src/random_state.rs index 46a39ab..46edc2d 100644 --- a/src/random_state.rs +++ b/src/random_state.rs @@ -10,11 +10,6 @@ cfg_if::cfg_if! { use crate::fallback_hash::*; } } -cfg_if::cfg_if! { - if #[cfg(feature = "specialize")]{ - use crate::BuildHasherExt; - } -} cfg_if::cfg_if! { if #[cfg(feature = "std")] { extern crate std as alloc; @@ -24,7 +19,7 @@ cfg_if::cfg_if! { } #[cfg(feature = "atomic-polyfill")] -use atomic_polyfill as atomic; +use portable_atomic as atomic; #[cfg(not(feature = "atomic-polyfill"))] use core::sync::atomic; @@ -79,7 +74,7 @@ cfg_if::cfg_if! { SEEDS.get_or_init(|| { let mut result: [u8; 64] = [0; 64]; - getrandom::getrandom(&mut result).expect("getrandom::getrandom() failed."); + getrandom::fill(&mut result).expect("getrandom::fill() failed."); Box::new(result.convert()) }) } @@ -394,16 +389,16 @@ impl BuildHasher for RandomState { ``` 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); @@ -458,17 +453,17 @@ 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(feature = "specialize")] + #[cfg(specialize)] #[inline] fn hash_one(&self, x: T) -> u64 { RandomState::hash_one(self, x) } } -#[cfg(feature = "specialize")] -impl BuildHasherExt for RandomState { +#[cfg(specialize)] +impl RandomState { #[inline] - fn hash_as_u64(&self, value: &T) -> u64 { + pub(crate) fn hash_as_u64(&self, value: &T) -> u64 { let mut hasher = AHasherU64 { buffer: self.k1, pad: self.k0, @@ -478,14 +473,14 @@ impl BuildHasherExt for RandomState { } #[inline] - fn hash_as_fixed_length(&self, value: &T) -> u64 { + 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] - fn hash_as_str(&self, value: &T) -> u64 { + pub(crate) fn hash_as_str(&self, value: &T) -> u64 { let mut hasher = AHasherStr(self.build_hasher()); value.hash(&mut hasher); hasher.finish() diff --git a/src/specialize.rs b/src/specialize.rs index 05d335b..0ba76da 100644 --- a/src/specialize.rs +++ b/src/specialize.rs @@ -1,3 +1,4 @@ +use crate::RandomState; use core::hash::BuildHasher; use core::hash::Hash; use core::hash::Hasher; @@ -7,127 +8,122 @@ extern crate alloc; #[cfg(feature = "std")] extern crate std as alloc; -#[cfg(feature = "specialize")] -use crate::BuildHasherExt; -#[cfg(feature = "specialize")] +#[cfg(specialize)] use alloc::string::String; -#[cfg(feature = "specialize")] +#[cfg(specialize)] 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. pub(crate) trait CallHasher { - fn get_hash(value: &H, build_hasher: &B) -> u64; + fn get_hash(value: &H, random_state: &RandomState) -> u64; } -#[cfg(not(feature = "specialize"))] +#[cfg(not(specialize))] impl CallHasher for T where T: Hash + ?Sized, { #[inline] - fn get_hash(value: &H, build_hasher: &B) -> u64 { - let mut hasher = build_hasher.build_hasher(); + fn get_hash(value: &H, random_state: &RandomState) -> u64 { + let mut hasher = random_state.build_hasher(); value.hash(&mut hasher); hasher.finish() } } -#[cfg(feature = "specialize")] +#[cfg(specialize)] impl CallHasher for T where T: Hash + ?Sized, { #[inline] - default fn get_hash(value: &H, build_hasher: &B) -> u64 { - let mut hasher = build_hasher.build_hasher(); + 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 { +macro_rules! call_hasher_impl_u64 { ($typ:ty) => { - #[cfg(feature = "specialize")] + #[cfg(specialize)] impl CallHasher for $typ { #[inline] - fn get_hash(value: &H, build_hasher: &B) -> u64 { - build_hasher.hash_as_u64(value) + fn get_hash(value: &H, random_state: &RandomState) -> u64 { + random_state.hash_as_u64(value) } } }; } -call_hasher_impl!(u8); -call_hasher_impl!(u16); -call_hasher_impl!(u32); -call_hasher_impl!(u64); -call_hasher_impl!(i8); -call_hasher_impl!(i16); -call_hasher_impl!(i32); -call_hasher_impl!(i64); - -#[cfg(feature = "specialize")] -impl CallHasher for u128 { - #[inline] - fn get_hash(value: &H, build_hasher: &B) -> u64 { - build_hasher.hash_as_fixed_length(value) - } -} - -#[cfg(feature = "specialize")] -impl CallHasher for i128 { - #[inline] - fn get_hash(value: &H, build_hasher: &B) -> u64 { - build_hasher.hash_as_fixed_length(value) - } -} - -#[cfg(feature = "specialize")] -impl CallHasher for usize { - #[inline] - fn get_hash(value: &H, build_hasher: &B) -> u64 { - build_hasher.hash_as_fixed_length(value) - } +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) + } + } + }; } -#[cfg(feature = "specialize")] -impl CallHasher for isize { - #[inline] - fn get_hash(value: &H, build_hasher: &B) -> u64 { - build_hasher.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(feature = "specialize")] +#[cfg(specialize)] impl CallHasher for [u8] { #[inline] - fn get_hash(value: &H, build_hasher: &B) -> u64 { - build_hasher.hash_as_str(value) + fn get_hash(value: &H, random_state: &RandomState) -> u64 { + random_state.hash_as_str(value) } } -#[cfg(feature = "specialize")] +#[cfg(specialize)] impl CallHasher for Vec { #[inline] - fn get_hash(value: &H, build_hasher: &B) -> u64 { - build_hasher.hash_as_str(value) + fn get_hash(value: &H, random_state: &RandomState) -> u64 { + random_state.hash_as_str(value) } } -#[cfg(feature = "specialize")] +#[cfg(specialize)] impl CallHasher for str { #[inline] - fn get_hash(value: &H, build_hasher: &B) -> u64 { - build_hasher.hash_as_str(value) + fn get_hash(value: &H, random_state: &RandomState) -> u64 { + random_state.hash_as_str(value) } } -#[cfg(all(feature = "specialize"))] +#[cfg(all(specialize))] impl CallHasher for String { #[inline] - fn get_hash(value: &H, build_hasher: &B) -> u64 { - build_hasher.hash_as_str(value) + fn get_hash(value: &H, random_state: &RandomState) -> u64 { + random_state.hash_as_str(value) } } @@ -137,7 +133,7 @@ mod test { use crate::*; #[test] - #[cfg(feature = "specialize")] + #[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); @@ -189,7 +185,7 @@ mod test { str::get_hash(&"test", &build_hasher), String::get_hash(&"test".to_string(), &build_hasher) ); - #[cfg(feature = "specialize")] + #[cfg(specialize)] assert_eq!( str::get_hash(&"test", &build_hasher), <[u8]>::get_hash("test".as_bytes(), &build_hasher) @@ -209,7 +205,7 @@ mod test { str::get_hash(&&"test", &build_hasher), String::get_hash(&"test".to_string(), &build_hasher) ); - #[cfg(feature = "specialize")] + #[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 2d000c0..f0cc027 100644 --- a/tests/bench.rs +++ b/tests/bench.rs @@ -1,4 +1,4 @@ -#![cfg_attr(feature = "specialize", feature(build_hasher_simple_hash_one))] +#![cfg_attr(specialize, feature(build_hasher_simple_hash_one))] use ahash::{AHasher, RandomState}; use criterion::*; diff --git a/tests/map_tests.rs b/tests/map_tests.rs index 97fdbee..42edb57 100644 --- a/tests/map_tests.rs +++ b/tests/map_tests.rs @@ -1,4 +1,4 @@ -#![cfg_attr(feature = "specialize", feature(build_hasher_simple_hash_one))] +#![cfg_attr(specialize, feature(build_hasher_simple_hash_one))] use std::hash::{BuildHasher, Hash, Hasher}; @@ -151,13 +151,13 @@ fn check_for_collisions(build_hasher: &B, items: &[H], ); } -#[cfg(feature = "specialize")] +#[cfg(specialize)] #[allow(unused)] // False positive fn hash(b: &H, build_hasher: &B) -> u64 { build_hasher.hash_one(b) } -#[cfg(not(feature = "specialize"))] +#[cfg(not(specialize))] #[allow(unused)] // False positive fn hash(b: &H, build_hasher: &B) -> u64 { let mut hasher = build_hasher.build_hasher();