blob: 0d9095a462110f0bb91bf5daa8b39150e3c56938 [file] [log] [blame]
#[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<H: Hasher>(value: &Self, hasher: H) -> u64;
}
#[cfg(not(feature = "specialize"))]
impl<T> CallHasher for T
where
T: Hash + ?Sized,
{
#[inline]
fn get_hash<H: Hasher>(value: &T, mut hasher: H) -> u64 {
value.hash(&mut hasher);
hasher.finish()
}
}
#[cfg(feature = "specialize")]
impl<T> CallHasher for T
where
T: Hash + ?Sized,
{
#[inline]
default fn get_hash<H: Hasher>(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<H: Hasher>(value: &$typ, hasher: H) -> u64 {
hasher.hash_u64(*value as u64)
}
}
#[cfg(feature = "specialize")]
impl CallHasher for &$typ {
#[inline]
fn get_hash<H: Hasher>(value: &&$typ, hasher: H) -> u64 {
hasher.hash_u64(**value as u64)
}
}
#[cfg(feature = "specialize")]
impl CallHasher for &&$typ {
#[inline]
fn get_hash<H: Hasher>(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<H: Hasher>(value: &u128, mut hasher: H) -> u64 {
hasher.write_u128(*value);
hasher.short_finish()
}
}
#[cfg(feature = "specialize")]
impl CallHasher for &u128 {
#[inline]
fn get_hash<H: Hasher>(value: &&u128, mut hasher: H) -> u64 {
hasher.write_u128(**value);
hasher.short_finish()
}
}
#[cfg(feature = "specialize")]
impl CallHasher for &&u128 {
#[inline]
fn get_hash<H: Hasher>(value: &&&u128, mut hasher: H) -> u64 {
hasher.write_u128(***value);
hasher.short_finish()
}
}
#[cfg(feature = "specialize")]
impl CallHasher for i128 {
#[inline]
fn get_hash<H: Hasher>(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<H: Hasher>(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<H: Hasher>(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<H: Hasher>(value: &Self, mut hasher: H) -> u64 {
hasher.write(value);
hasher.finish()
}
}
#[cfg(feature = "specialize")]
impl CallHasher for &[u8] {
#[inline]
fn get_hash<H: Hasher>(value: &Self, mut hasher: H) -> u64 {
hasher.write(*value);
hasher.finish()
}
}
#[cfg(feature = "specialize")]
impl CallHasher for &&[u8] {
#[inline]
fn get_hash<H: Hasher>(value: &Self, mut hasher: H) -> u64 {
hasher.write(**value);
hasher.finish()
}
}
#[cfg(feature = "specialize")]
impl CallHasher for Vec<u8> {
#[inline]
fn get_hash<H: Hasher>(value: &Self, mut hasher: H) -> u64 {
hasher.write(value);
hasher.finish()
}
}
#[cfg(feature = "specialize")]
impl CallHasher for &Vec<u8> {
#[inline]
fn get_hash<H: Hasher>(value: &Self, mut hasher: H) -> u64 {
hasher.write(*value);
hasher.finish()
}
}
#[cfg(feature = "specialize")]
impl CallHasher for &&Vec<u8> {
#[inline]
fn get_hash<H: Hasher>(value: &Self, mut hasher: H) -> u64 {
hasher.write(**value);
hasher.finish()
}
}
#[cfg(feature = "specialize")]
impl CallHasher for str {
#[inline]
fn get_hash<H: Hasher>(value: &Self, mut hasher: H) -> u64 {
hasher.write(value.as_bytes());
hasher.finish()
}
}
#[cfg(feature = "specialize")]
impl CallHasher for &str {
#[inline]
fn get_hash<H: Hasher>(value: &Self, mut hasher: H) -> u64 {
hasher.write(value.as_bytes());
hasher.finish()
}
}
#[cfg(feature = "specialize")]
impl CallHasher for &&str {
#[inline]
fn get_hash<H: Hasher>(value: &Self, mut hasher: H) -> u64 {
hasher.write(value.as_bytes());
hasher.finish()
}
}
#[cfg(feature = "specialize")]
impl CallHasher for &&&str {
#[inline]
fn get_hash<H: Hasher>(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<H: Hasher>(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<H: Hasher>(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<H: Hasher>(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<H: Hasher>(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()));
}
}