1 //! AHash is a high performance keyed hash function.
2 //!
3 //! It quickly provides a high quality hash where the result is not predictable without knowing the Key.
4 //! AHash works with `HashMap` to hash keys, but without allowing for the possibility that an malicious user can
5 //! induce a collision.
6 //!
7 //! # How aHash works
8 //!
9 //! When it is available aHash uses the hardware AES instructions to provide a keyed hash function.
10 //! When it is not, aHash falls back on a slightly slower alternative algorithm.
11 //!
12 //! Because aHash does not have a fixed standard for its output, it is able to improve over time.
13 //! But this also means that different computers or computers using different versions of ahash may observe different
14 //! hash values for the same input.
15 #![cfg_attr(
16     all(
17         feature = "std",
18         any(feature = "compile-time-rng", feature = "runtime-rng", feature = "no-rng")
19     ),
20     doc = r##"
21 # Basic Usage
22 AHash provides an implementation of the [Hasher] trait.
23 To construct a HashMap using aHash as its hasher do the following:
24 ```
25 use ahash::{AHasher, RandomState};
26 use std::collections::HashMap;
27 
28 let mut map: HashMap<i32, i32, RandomState> = HashMap::default();
29 map.insert(12, 34);
30 ```
31 
32 ### Randomness
33 
34 The above requires a source of randomness to generate keys for the hashmap. By default this obtained from the OS.
35 It is also possible to have randomness supplied via the `compile-time-rng` flag, or manually.
36 
37 ### If randomess is not available
38 
39 [AHasher::default()] can be used to hash using fixed keys. This works with
40 [BuildHasherDefault](std::hash::BuildHasherDefault). For example:
41 
42 ```
43 use std::hash::BuildHasherDefault;
44 use std::collections::HashMap;
45 use ahash::AHasher;
46 
47 let mut m: HashMap<_, _, BuildHasherDefault<AHasher>> = HashMap::default();
48  # m.insert(12, 34);
49 ```
50 It is also possible to instantiate [RandomState] directly:
51 
52 ```
53 use ahash::HashMap;
54 use ahash::RandomState;
55 
56 let mut m = HashMap::with_hasher(RandomState::with_seed(42));
57  # m.insert(1, 2);
58 ```
59 Or for uses besides a hashhmap:
60 ```
61 use std::hash::BuildHasher;
62 use ahash::RandomState;
63 
64 let hash_builder = RandomState::with_seed(42);
65 let hash = hash_builder.hash_one("Some Data");
66 ```
67 There are several constructors for [RandomState] with different ways to supply seeds.
68 
69 # Convenience wrappers
70 
71 For convenience, both new-type wrappers and type aliases are provided.
72 
73 The new type wrappers are called called `AHashMap` and `AHashSet`.
74 ```
75 use ahash::AHashMap;
76 
77 let mut map: AHashMap<i32, i32> = AHashMap::new();
78 map.insert(12, 34);
79 ```
80 This avoids the need to type "RandomState". (For convenience `From`, `Into`, and `Deref` are provided).
81 
82 # Aliases
83 
84 For even less typing and better interop with existing libraries (such as rayon) which require a `std::collection::HashMap` ,
85 the type aliases [HashMap], [HashSet] are provided.
86 
87 ```
88 use ahash::{HashMap, HashMapExt};
89 
90 let mut map: HashMap<i32, i32> = HashMap::new();
91 map.insert(12, 34);
92 ```
93 Note the import of [HashMapExt]. This is needed for the constructor.
94 
95 "##
96 )]
97 #![deny(clippy::correctness, clippy::complexity, clippy::perf)]
98 #![allow(clippy::pedantic, clippy::cast_lossless, clippy::unreadable_literal)]
99 #![cfg_attr(all(not(test), not(feature = "std")), no_std)]
100 #![cfg_attr(feature = "specialize", feature(min_specialization))]
101 #![cfg_attr(feature = "nightly-arm-aes", feature(stdarch_arm_neon_intrinsics))]
102 
103 #[macro_use]
104 mod convert;
105 
106 mod fallback_hash;
107 
108 cfg_if::cfg_if! {
109     if #[cfg(any(
110             all(any(target_arch = "x86", target_arch = "x86_64"), target_feature = "aes", not(miri)),
111             all(feature = "nightly-arm-aes", target_arch = "aarch64", target_feature = "aes", not(miri)),
112             all(feature = "nightly-arm-aes", target_arch = "arm", target_feature = "aes", not(miri)),
113         ))] {
114         mod aes_hash;
115         pub use crate::aes_hash::AHasher;
116     } else {
117         pub use crate::fallback_hash::AHasher;
118     }
119 }
120 
121 cfg_if::cfg_if! {
122     if #[cfg(feature = "std")] {
123         mod hash_map;
124         mod hash_set;
125 
126         pub use crate::hash_map::AHashMap;
127         pub use crate::hash_set::AHashSet;
128 
129         /// [Hasher]: std::hash::Hasher
130         /// [HashMap]: std::collections::HashMap
131         /// Type alias for [HashMap]<K, V, ahash::RandomState>
132         pub type HashMap<K, V> = std::collections::HashMap<K, V, crate::RandomState>;
133 
134         /// Type alias for [HashSet]<K, ahash::RandomState>
135         pub type HashSet<K> = std::collections::HashSet<K, crate::RandomState>;
136     }
137 }
138 
139 #[cfg(test)]
140 mod hash_quality_test;
141 
142 mod operations;
143 pub mod random_state;
144 mod specialize;
145 
146 pub use crate::random_state::RandomState;
147 
148 use core::hash::BuildHasher;
149 use core::hash::Hash;
150 use core::hash::Hasher;
151 
152 #[cfg(feature = "std")]
153 /// A convenience trait that can be used together with the type aliases defined to
154 /// get access to the `new()` and `with_capacity()` methods for the HashMap type alias.
155 pub trait HashMapExt {
156     /// Constructs a new HashMap
new() -> Self157     fn new() -> Self;
158     /// Constructs a new HashMap with a given initial capacity
with_capacity(capacity: usize) -> Self159     fn with_capacity(capacity: usize) -> Self;
160 }
161 
162 #[cfg(feature = "std")]
163 /// A convenience trait that can be used together with the type aliases defined to
164 /// get access to the `new()` and `with_capacity()` methods for the HashSet type aliases.
165 pub trait HashSetExt {
166     /// Constructs a new HashSet
new() -> Self167     fn new() -> Self;
168     /// Constructs a new HashSet with a given initial capacity
with_capacity(capacity: usize) -> Self169     fn with_capacity(capacity: usize) -> Self;
170 }
171 
172 #[cfg(feature = "std")]
173 impl<K, V, S> HashMapExt for std::collections::HashMap<K, V, S>
174 where
175     S: BuildHasher + Default,
176 {
new() -> Self177     fn new() -> Self {
178         std::collections::HashMap::with_hasher(S::default())
179     }
180 
with_capacity(capacity: usize) -> Self181     fn with_capacity(capacity: usize) -> Self {
182         std::collections::HashMap::with_capacity_and_hasher(capacity, S::default())
183     }
184 }
185 
186 #[cfg(feature = "std")]
187 impl<K, S> HashSetExt for std::collections::HashSet<K, S>
188 where
189     S: BuildHasher + Default,
190 {
new() -> Self191     fn new() -> Self {
192         std::collections::HashSet::with_hasher(S::default())
193     }
194 
with_capacity(capacity: usize) -> Self195     fn with_capacity(capacity: usize) -> Self {
196         std::collections::HashSet::with_capacity_and_hasher(capacity, S::default())
197     }
198 }
199 
200 /// Provides a default [Hasher] with fixed keys.
201 /// This is typically used in conjunction with [BuildHasherDefault] to create
202 /// [AHasher]s in order to hash the keys of the map.
203 ///
204 /// Generally it is preferable to use [RandomState] instead, so that different
205 /// hashmaps will have different keys. However if fixed keys are desirable this
206 /// may be used instead.
207 ///
208 /// # Example
209 /// ```
210 /// use std::hash::BuildHasherDefault;
211 /// use ahash::{AHasher, RandomState};
212 /// use std::collections::HashMap;
213 ///
214 /// let mut map: HashMap<i32, i32, BuildHasherDefault<AHasher>> = HashMap::default();
215 /// map.insert(12, 34);
216 /// ```
217 ///
218 /// [BuildHasherDefault]: std::hash::BuildHasherDefault
219 /// [Hasher]: std::hash::Hasher
220 /// [HashMap]: std::collections::HashMap
221 impl Default for AHasher {
222     /// Constructs a new [AHasher] with fixed keys.
223     /// If `std` is enabled these will be generated upon first invocation.
224     /// Otherwise if the `compile-time-rng`feature is enabled these will be generated at compile time.
225     /// If neither of these features are available, hardcoded constants will be used.
226     ///
227     /// Because the values are fixed, different hashers will all hash elements the same way.
228     /// This could make hash values predictable, if DOS attacks are a concern. If this behaviour is
229     /// not required, it may be preferable to use [RandomState] instead.
230     ///
231     /// # Examples
232     ///
233     /// ```
234     /// use ahash::AHasher;
235     /// use std::hash::Hasher;
236     ///
237     /// let mut hasher_1 = AHasher::default();
238     /// let mut hasher_2 = AHasher::default();
239     ///
240     /// hasher_1.write_u32(1234);
241     /// hasher_2.write_u32(1234);
242     ///
243     /// assert_eq!(hasher_1.finish(), hasher_2.finish());
244     /// ```
245     #[inline]
default() -> AHasher246     fn default() -> AHasher {
247         RandomState::with_fixed_keys().build_hasher()
248     }
249 }
250 
251 /// Used for specialization. (Sealed)
252 pub(crate) trait BuildHasherExt: BuildHasher {
253     #[doc(hidden)]
hash_as_u64<T: Hash + ?Sized>(&self, value: &T) -> u64254     fn hash_as_u64<T: Hash + ?Sized>(&self, value: &T) -> u64;
255 
256     #[doc(hidden)]
hash_as_fixed_length<T: Hash + ?Sized>(&self, value: &T) -> u64257     fn hash_as_fixed_length<T: Hash + ?Sized>(&self, value: &T) -> u64;
258 
259     #[doc(hidden)]
hash_as_str<T: Hash + ?Sized>(&self, value: &T) -> u64260     fn hash_as_str<T: Hash + ?Sized>(&self, value: &T) -> u64;
261 }
262 
263 impl<B: BuildHasher> BuildHasherExt for B {
264     #[inline]
265     #[cfg(feature = "specialize")]
hash_as_u64<T: Hash + ?Sized>(&self, value: &T) -> u64266     default fn hash_as_u64<T: Hash + ?Sized>(&self, value: &T) -> u64 {
267         let mut hasher = self.build_hasher();
268         value.hash(&mut hasher);
269         hasher.finish()
270     }
271     #[inline]
272     #[cfg(not(feature = "specialize"))]
hash_as_u64<T: Hash + ?Sized>(&self, value: &T) -> u64273     fn hash_as_u64<T: Hash + ?Sized>(&self, value: &T) -> u64 {
274         let mut hasher = self.build_hasher();
275         value.hash(&mut hasher);
276         hasher.finish()
277     }
278     #[inline]
279     #[cfg(feature = "specialize")]
hash_as_fixed_length<T: Hash + ?Sized>(&self, value: &T) -> u64280     default fn hash_as_fixed_length<T: Hash + ?Sized>(&self, value: &T) -> u64 {
281         let mut hasher = self.build_hasher();
282         value.hash(&mut hasher);
283         hasher.finish()
284     }
285     #[inline]
286     #[cfg(not(feature = "specialize"))]
hash_as_fixed_length<T: Hash + ?Sized>(&self, value: &T) -> u64287     fn hash_as_fixed_length<T: Hash + ?Sized>(&self, value: &T) -> u64 {
288         let mut hasher = self.build_hasher();
289         value.hash(&mut hasher);
290         hasher.finish()
291     }
292     #[inline]
293     #[cfg(feature = "specialize")]
hash_as_str<T: Hash + ?Sized>(&self, value: &T) -> u64294     default fn hash_as_str<T: Hash + ?Sized>(&self, value: &T) -> u64 {
295         let mut hasher = self.build_hasher();
296         value.hash(&mut hasher);
297         hasher.finish()
298     }
299     #[inline]
300     #[cfg(not(feature = "specialize"))]
hash_as_str<T: Hash + ?Sized>(&self, value: &T) -> u64301     fn hash_as_str<T: Hash + ?Sized>(&self, value: &T) -> u64 {
302         let mut hasher = self.build_hasher();
303         value.hash(&mut hasher);
304         hasher.finish()
305     }
306 }
307 
308 // #[inline(never)]
309 // #[doc(hidden)]
310 // pub fn hash_test(input: &[u8]) -> u64 {
311 //     let a = RandomState::with_seeds(11, 22, 33, 44);
312 //     <[u8]>::get_hash(input, &a)
313 // }
314 
315 #[cfg(feature = "std")]
316 #[cfg(test)]
317 mod test {
318     use crate::convert::Convert;
319     use crate::specialize::CallHasher;
320     use crate::*;
321     use std::collections::HashMap;
322 
323     #[test]
test_ahash_alias_map_construction()324     fn test_ahash_alias_map_construction() {
325         let mut map = super::HashMap::with_capacity(1234);
326         map.insert(1, "test");
327     }
328 
329     #[test]
test_ahash_alias_set_construction()330     fn test_ahash_alias_set_construction() {
331         let mut set = super::HashSet::with_capacity(1234);
332         set.insert(1);
333     }
334 
335     #[test]
test_default_builder()336     fn test_default_builder() {
337         use core::hash::BuildHasherDefault;
338 
339         let mut map = HashMap::<u32, u64, BuildHasherDefault<AHasher>>::default();
340         map.insert(1, 3);
341     }
342 
343     #[test]
test_builder()344     fn test_builder() {
345         let mut map = HashMap::<u32, u64, RandomState>::default();
346         map.insert(1, 3);
347     }
348 
349     #[test]
test_conversion()350     fn test_conversion() {
351         let input: &[u8] = b"dddddddd";
352         let bytes: u64 = as_array!(input, 8).convert();
353         assert_eq!(bytes, 0x6464646464646464);
354     }
355 
356     #[test]
test_non_zero()357     fn test_non_zero() {
358         let mut hasher1 = AHasher::new_with_keys(0, 0);
359         let mut hasher2 = AHasher::new_with_keys(0, 0);
360         "foo".hash(&mut hasher1);
361         "bar".hash(&mut hasher2);
362         assert_ne!(hasher1.finish(), 0);
363         assert_ne!(hasher2.finish(), 0);
364         assert_ne!(hasher1.finish(), hasher2.finish());
365 
366         let mut hasher1 = AHasher::new_with_keys(0, 0);
367         let mut hasher2 = AHasher::new_with_keys(0, 0);
368         3_u64.hash(&mut hasher1);
369         4_u64.hash(&mut hasher2);
370         assert_ne!(hasher1.finish(), 0);
371         assert_ne!(hasher2.finish(), 0);
372         assert_ne!(hasher1.finish(), hasher2.finish());
373     }
374 
375     #[test]
test_non_zero_specialized()376     fn test_non_zero_specialized() {
377         let hasher_build = RandomState::with_seeds(0, 0, 0, 0);
378 
379         let h1 = str::get_hash("foo", &hasher_build);
380         let h2 = str::get_hash("bar", &hasher_build);
381         assert_ne!(h1, 0);
382         assert_ne!(h2, 0);
383         assert_ne!(h1, h2);
384 
385         let h1 = u64::get_hash(&3_u64, &hasher_build);
386         let h2 = u64::get_hash(&4_u64, &hasher_build);
387         assert_ne!(h1, 0);
388         assert_ne!(h2, 0);
389         assert_ne!(h1, h2);
390     }
391 
392     #[test]
test_ahasher_construction()393     fn test_ahasher_construction() {
394         let _ = AHasher::new_with_keys(1234, 5678);
395     }
396 }
397