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 { |
21 | 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 ] |
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" )] |
38 | impl<T> CallHasher for T |
39 | where |
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 | |
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 ] |
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" )] |
79 | impl 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" )] |
87 | impl 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" )] |
95 | impl 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" )] |
103 | impl 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" )] |
111 | impl 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" )] |
119 | impl 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" ))] |
127 | impl 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)] |
135 | mod 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 | |