1use core::hash::BuildHasher;
2use core::hash::Hash;
3use core::hash::Hasher;
4
5#[cfg(not(feature = "std"))]
6extern crate alloc;
7#[cfg(feature = "std")]
8extern crate std as alloc;
9
10#[cfg(feature = "specialize")]
11use crate::BuildHasherExt;
12#[cfg(feature = "specialize")]
13use alloc::string::String;
14#[cfg(feature = "specialize")]
15use 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.
20pub(crate) trait CallHasher {
21 fn get_hash<H: Hash + ?Sized, B: BuildHasher>(value: &H, build_hasher: &B) -> u64;
22}
23
24#[cfg(not(feature = "specialize"))]
25impl<T> CallHasher for T
26where
27 T: Hash + ?Sized,
28{
29 #[inline]
30 fn get_hash<H: Hash + ?Sized, B: BuildHasher>(value: &H, build_hasher: &B) -> u64 {
31 let mut hasher: ::Hasher = build_hasher.build_hasher();
32 value.hash(&mut hasher);
33 hasher.finish()
34 }
35}
36
37#[cfg(feature = "specialize")]
38impl<T> CallHasher for T
39where
40 T: Hash + ?Sized,
41{
42 #[inline]
43 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
50macro_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}
61call_hasher_impl!(u8);
62call_hasher_impl!(u16);
63call_hasher_impl!(u32);
64call_hasher_impl!(u64);
65call_hasher_impl!(i8);
66call_hasher_impl!(i16);
67call_hasher_impl!(i32);
68call_hasher_impl!(i64);
69
70#[cfg(feature = "specialize")]
71impl CallHasher for u128 {
72 #[inline]
73 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")]
79impl CallHasher for i128 {
80 #[inline]
81 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")]
87impl CallHasher for usize {
88 #[inline]
89 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")]
95impl CallHasher for isize {
96 #[inline]
97 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")]
103impl CallHasher for [u8] {
104 #[inline]
105 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")]
111impl CallHasher for Vec<u8> {
112 #[inline]
113 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")]
119impl CallHasher for str {
120 #[inline]
121 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"))]
127impl CallHasher for String {
128 #[inline]
129 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)]
135mod test {
136 use super::*;
137 use crate::*;
138
139 #[test]
140 #[cfg(feature = "specialize")]
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]
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]
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