ahash/
specialize.rs

1use crate::RandomState;
2use core::hash::BuildHasher;
3use core::hash::Hash;
4use core::hash::Hasher;
5
6#[cfg(not(feature = "std"))]
7extern crate alloc;
8#[cfg(feature = "std")]
9extern crate std as alloc;
10
11#[cfg(specialize)]
12use alloc::string::String;
13#[cfg(specialize)]
14use alloc::vec::Vec;
15
16/// Provides a way to get an optimized hasher for a given data type.
17/// Rather than using a Hasher generically which can hash any value, this provides a way to get a specialized hash
18/// for a specific type. So this may be faster for primitive types.
19pub(crate) trait CallHasher {
20    fn get_hash<H: Hash + ?Sized>(value: &H, random_state: &RandomState) -> u64;
21}
22
23#[cfg(not(specialize))]
24impl<T> CallHasher for T
25where
26    T: Hash + ?Sized,
27{
28    #[inline]
29    fn get_hash<H: Hash + ?Sized>(value: &H, random_state: &RandomState) -> u64 {
30        let mut hasher = random_state.build_hasher();
31        value.hash(&mut hasher);
32        hasher.finish()
33    }
34}
35
36#[cfg(specialize)]
37impl<T> CallHasher for T
38where
39    T: Hash + ?Sized,
40{
41    #[inline]
42    default fn get_hash<H: Hash + ?Sized>(value: &H, random_state: &RandomState) -> u64 {
43        let mut hasher = random_state.build_hasher();
44        value.hash(&mut hasher);
45        hasher.finish()
46    }
47}
48
49macro_rules! call_hasher_impl_u64 {
50    ($typ:ty) => {
51        #[cfg(specialize)]
52        impl CallHasher for $typ {
53            #[inline]
54            fn get_hash<H: Hash + ?Sized>(value: &H, random_state: &RandomState) -> u64 {
55                random_state.hash_as_u64(value)
56            }
57        }
58    };
59}
60call_hasher_impl_u64!(u8);
61call_hasher_impl_u64!(u16);
62call_hasher_impl_u64!(u32);
63call_hasher_impl_u64!(u64);
64call_hasher_impl_u64!(i8);
65call_hasher_impl_u64!(i16);
66call_hasher_impl_u64!(i32);
67call_hasher_impl_u64!(i64);
68call_hasher_impl_u64!(&u8);
69call_hasher_impl_u64!(&u16);
70call_hasher_impl_u64!(&u32);
71call_hasher_impl_u64!(&u64);
72call_hasher_impl_u64!(&i8);
73call_hasher_impl_u64!(&i16);
74call_hasher_impl_u64!(&i32);
75call_hasher_impl_u64!(&i64);
76
77macro_rules! call_hasher_impl_fixed_length{
78    ($typ:ty) => {
79        #[cfg(specialize)]
80        impl CallHasher for $typ {
81            #[inline]
82            fn get_hash<H: Hash + ?Sized>(value: &H, random_state: &RandomState) -> u64 {
83                random_state.hash_as_fixed_length(value)
84            }
85        }
86    };
87}
88
89call_hasher_impl_fixed_length!(u128);
90call_hasher_impl_fixed_length!(i128);
91call_hasher_impl_fixed_length!(usize);
92call_hasher_impl_fixed_length!(isize);
93call_hasher_impl_fixed_length!(&u128);
94call_hasher_impl_fixed_length!(&i128);
95call_hasher_impl_fixed_length!(&usize);
96call_hasher_impl_fixed_length!(&isize);
97
98#[cfg(specialize)]
99impl CallHasher for [u8] {
100    #[inline]
101    fn get_hash<H: Hash + ?Sized>(value: &H, random_state: &RandomState) -> u64 {
102        random_state.hash_as_str(value)
103    }
104}
105
106#[cfg(specialize)]
107impl CallHasher for Vec<u8> {
108    #[inline]
109    fn get_hash<H: Hash + ?Sized>(value: &H, random_state: &RandomState) -> u64 {
110        random_state.hash_as_str(value)
111    }
112}
113
114#[cfg(specialize)]
115impl CallHasher for str {
116    #[inline]
117    fn get_hash<H: Hash + ?Sized>(value: &H, random_state: &RandomState) -> u64 {
118        random_state.hash_as_str(value)
119    }
120}
121
122#[cfg(all(specialize))]
123impl CallHasher for String {
124    #[inline]
125    fn get_hash<H: Hash + ?Sized>(value: &H, random_state: &RandomState) -> u64 {
126        random_state.hash_as_str(value)
127    }
128}
129
130#[cfg(test)]
131mod test {
132    use super::*;
133    use crate::*;
134
135    #[test]
136    #[cfg(specialize)]
137    pub fn test_specialized_invoked() {
138        let build_hasher = RandomState::with_seeds(1, 2, 3, 4);
139        let shortened = u64::get_hash(&0, &build_hasher);
140        let mut hasher = AHasher::new_with_keys(1, 2);
141        0_u64.hash(&mut hasher);
142        assert_ne!(hasher.finish(), shortened);
143    }
144
145    /// Tests that some non-trivial transformation takes place.
146    #[test]
147    pub fn test_input_processed() {
148        let build_hasher = RandomState::with_seeds(2, 2, 2, 2);
149        assert_ne!(0, u64::get_hash(&0, &build_hasher));
150        assert_ne!(1, u64::get_hash(&0, &build_hasher));
151        assert_ne!(2, u64::get_hash(&0, &build_hasher));
152        assert_ne!(3, u64::get_hash(&0, &build_hasher));
153        assert_ne!(4, u64::get_hash(&0, &build_hasher));
154        assert_ne!(5, u64::get_hash(&0, &build_hasher));
155
156        assert_ne!(0, u64::get_hash(&1, &build_hasher));
157        assert_ne!(1, u64::get_hash(&1, &build_hasher));
158        assert_ne!(2, u64::get_hash(&1, &build_hasher));
159        assert_ne!(3, u64::get_hash(&1, &build_hasher));
160        assert_ne!(4, u64::get_hash(&1, &build_hasher));
161        assert_ne!(5, u64::get_hash(&1, &build_hasher));
162
163        let xored = u64::get_hash(&0, &build_hasher) ^ u64::get_hash(&1, &build_hasher);
164        assert_ne!(0, xored);
165        assert_ne!(1, xored);
166        assert_ne!(2, xored);
167        assert_ne!(3, xored);
168        assert_ne!(4, xored);
169        assert_ne!(5, xored);
170    }
171
172    #[test]
173    pub fn test_ref_independent() {
174        let build_hasher = RandomState::with_seeds(1, 2, 3, 4);
175        assert_eq!(u8::get_hash(&&1, &build_hasher), u8::get_hash(&1, &build_hasher));
176        assert_eq!(u16::get_hash(&&2, &build_hasher), u16::get_hash(&2, &build_hasher));
177        assert_eq!(u32::get_hash(&&3, &build_hasher), u32::get_hash(&3, &build_hasher));
178        assert_eq!(u64::get_hash(&&4, &build_hasher), u64::get_hash(&4, &build_hasher));
179        assert_eq!(u128::get_hash(&&5, &build_hasher), u128::get_hash(&5, &build_hasher));
180        assert_eq!(
181            str::get_hash(&"test", &build_hasher),
182            str::get_hash("test", &build_hasher)
183        );
184        assert_eq!(
185            str::get_hash(&"test", &build_hasher),
186            String::get_hash(&"test".to_string(), &build_hasher)
187        );
188        #[cfg(specialize)]
189        assert_eq!(
190            str::get_hash(&"test", &build_hasher),
191            <[u8]>::get_hash("test".as_bytes(), &build_hasher)
192        );
193
194        let build_hasher = RandomState::with_seeds(10, 20, 30, 40);
195        assert_eq!(u8::get_hash(&&&1, &build_hasher), u8::get_hash(&1, &build_hasher));
196        assert_eq!(u16::get_hash(&&&2, &build_hasher), u16::get_hash(&2, &build_hasher));
197        assert_eq!(u32::get_hash(&&&3, &build_hasher), u32::get_hash(&3, &build_hasher));
198        assert_eq!(u64::get_hash(&&&4, &build_hasher), u64::get_hash(&4, &build_hasher));
199        assert_eq!(u128::get_hash(&&&5, &build_hasher), u128::get_hash(&5, &build_hasher));
200        assert_eq!(
201            str::get_hash(&&"test", &build_hasher),
202            str::get_hash("test", &build_hasher)
203        );
204        assert_eq!(
205            str::get_hash(&&"test", &build_hasher),
206            String::get_hash(&"test".to_string(), &build_hasher)
207        );
208        #[cfg(specialize)]
209        assert_eq!(
210            str::get_hash(&&"test", &build_hasher),
211            <[u8]>::get_hash(&"test".to_string().into_bytes(), &build_hasher)
212        );
213    }
214}