#[cfg(feature = "specialize")] use crate::HasherExt; use core::hash::Hash; use core::hash::Hasher; #[cfg(not(feature = "std"))] extern crate alloc; #[cfg(feature = "std")] extern crate std as alloc; #[cfg(feature = "specialize")] use alloc::string::String; #[cfg(feature = "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. It does however consume the hasher in the process. /// # Example /// ``` /// use std::hash::BuildHasher; /// use ahash::RandomState; /// use ahash::CallHasher; /// /// let hash_builder = RandomState::new(); /// //... /// let value = 17; /// let hash = u32::get_hash(&value, hash_builder.build_hasher()); /// ``` pub trait CallHasher { fn get_hash(value: &Self, hasher: H) -> u64; } #[cfg(not(feature = "specialize"))] impl CallHasher for T where T: Hash + ?Sized, { #[inline] fn get_hash(value: &T, mut hasher: H) -> u64 { value.hash(&mut hasher); hasher.finish() } } #[cfg(feature = "specialize")] impl CallHasher for T where T: Hash + ?Sized, { #[inline] default fn get_hash(value: &T, mut hasher: H) -> u64 { value.hash(&mut hasher); hasher.finish() } } macro_rules! call_hasher_impl { ($typ:ty) => { #[cfg(feature = "specialize")] impl CallHasher for $typ { #[inline] fn get_hash(value: &$typ, hasher: H) -> u64 { hasher.hash_u64(*value as u64) } } #[cfg(feature = "specialize")] impl CallHasher for &$typ { #[inline] fn get_hash(value: &&$typ, hasher: H) -> u64 { hasher.hash_u64(**value as u64) } } #[cfg(feature = "specialize")] impl CallHasher for &&$typ { #[inline] fn get_hash(value: &&&$typ, hasher: H) -> u64 { hasher.hash_u64(***value as u64) } } }; } 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: &u128, mut hasher: H) -> u64 { hasher.write_u128(*value); hasher.short_finish() } } #[cfg(feature = "specialize")] impl CallHasher for &u128 { #[inline] fn get_hash(value: &&u128, mut hasher: H) -> u64 { hasher.write_u128(**value); hasher.short_finish() } } #[cfg(feature = "specialize")] impl CallHasher for &&u128 { #[inline] fn get_hash(value: &&&u128, mut hasher: H) -> u64 { hasher.write_u128(***value); hasher.short_finish() } } #[cfg(feature = "specialize")] impl CallHasher for i128 { #[inline] fn get_hash(value: &i128, mut hasher: H) -> u64 { hasher.write_u128(*value as u128); hasher.short_finish() } } #[cfg(feature = "specialize")] impl CallHasher for &i128 { #[inline] fn get_hash(value: &&i128, mut hasher: H) -> u64 { hasher.write_u128(**value as u128); hasher.short_finish() } } #[cfg(feature = "specialize")] impl CallHasher for &&i128 { #[inline] fn get_hash(value: &&&i128, mut hasher: H) -> u64 { hasher.write_u128(***value as u128); hasher.short_finish() } } #[cfg(feature = "specialize")] impl CallHasher for [u8] { #[inline] fn get_hash(value: &Self, mut hasher: H) -> u64 { hasher.write(value); hasher.finish() } } #[cfg(feature = "specialize")] impl CallHasher for &[u8] { #[inline] fn get_hash(value: &Self, mut hasher: H) -> u64 { hasher.write(*value); hasher.finish() } } #[cfg(feature = "specialize")] impl CallHasher for &&[u8] { #[inline] fn get_hash(value: &Self, mut hasher: H) -> u64 { hasher.write(**value); hasher.finish() } } #[cfg(feature = "specialize")] impl CallHasher for Vec { #[inline] fn get_hash(value: &Self, mut hasher: H) -> u64 { hasher.write(value); hasher.finish() } } #[cfg(feature = "specialize")] impl CallHasher for &Vec { #[inline] fn get_hash(value: &Self, mut hasher: H) -> u64 { hasher.write(*value); hasher.finish() } } #[cfg(feature = "specialize")] impl CallHasher for &&Vec { #[inline] fn get_hash(value: &Self, mut hasher: H) -> u64 { hasher.write(**value); hasher.finish() } } #[cfg(feature = "specialize")] impl CallHasher for str { #[inline] fn get_hash(value: &Self, mut hasher: H) -> u64 { hasher.write(value.as_bytes()); hasher.finish() } } #[cfg(feature = "specialize")] impl CallHasher for &str { #[inline] fn get_hash(value: &Self, mut hasher: H) -> u64 { hasher.write(value.as_bytes()); hasher.finish() } } #[cfg(feature = "specialize")] impl CallHasher for &&str { #[inline] fn get_hash(value: &Self, mut hasher: H) -> u64 { hasher.write(value.as_bytes()); hasher.finish() } } #[cfg(feature = "specialize")] impl CallHasher for &&&str { #[inline] fn get_hash(value: &Self, mut hasher: H) -> u64 { hasher.write(value.as_bytes()); hasher.finish() } } #[cfg(all(feature = "specialize"))] impl CallHasher for String { #[inline] fn get_hash(value: &Self, mut hasher: H) -> u64 { hasher.write(value.as_bytes()); hasher.finish() } } #[cfg(all(feature = "specialize"))] impl CallHasher for &String { #[inline] fn get_hash(value: &Self, mut hasher: H) -> u64 { hasher.write(value.as_bytes()); hasher.finish() } } #[cfg(all(feature = "specialize"))] impl CallHasher for &&String { #[inline] fn get_hash(value: &Self, mut hasher: H) -> u64 { hasher.write(value.as_bytes()); hasher.finish() } } #[cfg(all(feature = "specialize"))] impl CallHasher for &&&String { #[inline] fn get_hash(value: &Self, mut hasher: H) -> u64 { hasher.write(value.as_bytes()); hasher.finish() } } #[cfg(test)] mod test { use super::*; use crate::*; #[test] #[cfg(feature = "specialize")] pub fn test_specialized_invoked() { let shortened = u64::get_hash(&0, AHasher::new_with_keys(1, 2)); let mut hasher = AHasher::new_with_keys(1, 2); 0_u64.hash(&mut hasher); assert_ne!(hasher.finish(), shortened); } /// Tests that some non-trivial transformation takes place. #[test] pub fn test_input_processed() { let hasher = || AHasher::new_with_keys(3, 2); assert_ne!(0, u64::get_hash(&0, hasher())); assert_ne!(1, u64::get_hash(&0, hasher())); assert_ne!(2, u64::get_hash(&0, hasher())); assert_ne!(3, u64::get_hash(&0, hasher())); assert_ne!(4, u64::get_hash(&0, hasher())); assert_ne!(5, u64::get_hash(&0, hasher())); assert_ne!(0, u64::get_hash(&1, hasher())); assert_ne!(1, u64::get_hash(&1, hasher())); assert_ne!(2, u64::get_hash(&1, hasher())); assert_ne!(3, u64::get_hash(&1, hasher())); assert_ne!(4, u64::get_hash(&1, hasher())); assert_ne!(5, u64::get_hash(&1, hasher())); let xored = u64::get_hash(&0, hasher()) ^ u64::get_hash(&1, hasher()); assert_ne!(0, xored); assert_ne!(1, xored); assert_ne!(2, xored); assert_ne!(3, xored); assert_ne!(4, xored); assert_ne!(5, xored); } #[test] pub fn test_ref_independent() { let hasher = || AHasher::new_with_keys(3, 2); assert_eq!(<&u8>::get_hash(&&1, hasher()), u8::get_hash(&1, hasher())); assert_eq!(<&u16>::get_hash(&&2, hasher()), u16::get_hash(&2, hasher())); assert_eq!(<&u32>::get_hash(&&3, hasher()), u32::get_hash(&3, hasher())); assert_eq!(<&u64>::get_hash(&&4, hasher()), u64::get_hash(&4, hasher())); assert_eq!(<&u128>::get_hash(&&5, hasher()), u128::get_hash(&5, hasher())); assert_eq!(<&str>::get_hash(&"test", hasher()), str::get_hash("test", hasher())); assert_eq!(<&str>::get_hash(&"test", hasher()), String::get_hash(&"test".to_string(), hasher())); #[cfg(feature = "specialize")] assert_eq!(<&str>::get_hash(&"test", hasher()), <[u8]>::get_hash("test".as_bytes(), hasher())); let hasher = || AHasher::new_with_keys(3, 2); assert_eq!(<&&u8>::get_hash(&&&1, hasher()), u8::get_hash(&1, hasher())); assert_eq!(<&&u16>::get_hash(&&&2, hasher()), u16::get_hash(&2, hasher())); assert_eq!(<&&u32>::get_hash(&&&3, hasher()), u32::get_hash(&3, hasher())); assert_eq!(<&&u64>::get_hash(&&&4, hasher()), u64::get_hash(&4, hasher())); assert_eq!(<&&u128>::get_hash(&&&5, hasher()), u128::get_hash(&5, hasher())); assert_eq!(<&&str>::get_hash(&&"test", hasher()), str::get_hash("test",hasher())); assert_eq!(<&&str>::get_hash(&&"test", hasher()), String::get_hash(&"test".to_string(), hasher())); #[cfg(feature = "specialize")] assert_eq!(<&&str>::get_hash(&&"test", hasher()), <[u8]>::get_hash(&"test".to_string().into_bytes(), hasher())); } }