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