ahash/
lib.rs

1//! AHash is a hashing algorithm is intended to be a high performance, (hardware specific), keyed hash function.
2//! This can be seen as a DOS resistant alternative to `FxHash`, or a fast equivalent to `SipHash`.
3//! It provides a high speed hash algorithm, but where the result is not predictable without knowing a Key.
4//! This allows it to be used in a `HashMap` without allowing for the possibility that an malicious user can
5//! induce a collision.
6//!
7//! # How aHash works
8//!
9//! aHash uses the hardware AES instruction on x86 processors to provide a keyed hash function.
10//! aHash is not a cryptographically secure hash.
11//!
12//! # Example
13//! ```
14//! use ahash::{AHasher, RandomState};
15//! use std::collections::HashMap;
16//!
17//! let mut map: HashMap<i32, i32, RandomState> = HashMap::default();
18//! map.insert(12, 34);
19//! ```
20//! For convinence wrappers called `AHashMap` and `AHashSet` are also provided.
21//! These to the same thing with slightly less typing.
22//! ```ignore
23//! use ahash::AHashMap;
24//!
25//! let mut map: AHashMap<i32, i32> = AHashMap::with_capacity(4);
26//! map.insert(12, 34);
27//! map.insert(56, 78);
28//! ```
29#![deny(clippy::correctness, clippy::complexity, clippy::perf)]
30#![allow(clippy::pedantic, clippy::cast_lossless, clippy::unreadable_literal)]
31#![cfg_attr(all(not(test), not(feature = "std")), no_std)]
32#![cfg_attr(feature = "specialize", feature(min_specialization))]
33#![cfg_attr(feature = "stdsimd", feature(stdsimd))]
34
35#[macro_use]
36mod convert;
37
38#[cfg(any(
39    all(any(target_arch = "x86", target_arch = "x86_64"), target_feature = "aes", not(miri)),
40    all(any(target_arch = "arm", target_arch = "aarch64"), target_feature = "crypto", not(miri), feature = "stdsimd")
41))]
42mod aes_hash;
43mod fallback_hash;
44#[cfg(test)]
45mod hash_quality_test;
46
47#[cfg(feature = "std")]
48mod hash_map;
49#[cfg(feature = "std")]
50mod hash_set;
51mod operations;
52mod random_state;
53mod specialize;
54
55#[cfg(any(
56    all(any(target_arch = "x86", target_arch = "x86_64"), target_feature = "aes", not(miri)),
57    all(any(target_arch = "arm", target_arch = "aarch64"), target_feature = "crypto", not(miri), feature = "stdsimd")
58))]
59pub use crate::aes_hash::AHasher;
60
61#[cfg(not(any(
62    all(any(target_arch = "x86", target_arch = "x86_64"), target_feature = "aes", not(miri)),
63    all(any(target_arch = "arm", target_arch = "aarch64"), target_feature = "crypto", not(miri), feature = "stdsimd")
64)))]
65pub use crate::fallback_hash::AHasher;
66pub use crate::random_state::RandomState;
67
68pub use crate::specialize::CallHasher;
69
70#[cfg(feature = "std")]
71pub use crate::hash_map::AHashMap;
72#[cfg(feature = "std")]
73pub use crate::hash_set::AHashSet;
74use core::hash::BuildHasher;
75use core::hash::Hash;
76use core::hash::Hasher;
77
78/// Provides a default [Hasher] with fixed keys.
79/// This is typically used in conjunction with [BuildHasherDefault] to create
80/// [AHasher]s in order to hash the keys of the map.
81///
82/// Generally it is preferable to use [RandomState] instead, so that different
83/// hashmaps will have different keys. However if fixed keys are desireable this
84/// may be used instead.
85///
86/// # Example
87/// ```
88/// use std::hash::BuildHasherDefault;
89/// use ahash::{AHasher, RandomState};
90/// use std::collections::HashMap;
91///
92/// let mut map: HashMap<i32, i32, BuildHasherDefault<AHasher>> = HashMap::default();
93/// map.insert(12, 34);
94/// ```
95///
96/// [BuildHasherDefault]: std::hash::BuildHasherDefault
97/// [Hasher]: std::hash::Hasher
98/// [HashMap]: std::collections::HashMap
99impl Default for AHasher {
100    /// Constructs a new [AHasher] with fixed keys.
101    /// If `std` is enabled these will be generated upon first invocation.
102    /// Otherwise if the `compile-time-rng`feature is enabled these will be generated at compile time.
103    /// If neither of these features are available, hardcoded constants will be used.
104    ///
105    /// Because the values are fixed, different hashers will all hash elements the same way.
106    /// This could make hash values predictable, if DOS attacks are a concern. If this behaviour is
107    /// not required, it may be preferable to use [RandomState] instead.
108    ///
109    /// # Examples
110    ///
111    /// ```
112    /// use ahash::AHasher;
113    /// use std::hash::Hasher;
114    ///
115    /// let mut hasher_1 = AHasher::default();
116    /// let mut hasher_2 = AHasher::default();
117    ///
118    /// hasher_1.write_u32(1234);
119    /// hasher_2.write_u32(1234);
120    ///
121    /// assert_eq!(hasher_1.finish(), hasher_2.finish());
122    /// ```
123    #[inline]
124    fn default() -> AHasher {
125        RandomState::with_fixed_keys().build_hasher()
126    }
127}
128
129/// Used for specialization. (Sealed)
130pub(crate) trait BuildHasherExt: BuildHasher {
131    #[doc(hidden)]
132    fn hash_as_u64<T: Hash + ?Sized>(&self, value: &T) -> u64;
133
134    #[doc(hidden)]
135    fn hash_as_fixed_length<T: Hash + ?Sized>(&self, value: &T) -> u64;
136
137    #[doc(hidden)]
138    fn hash_as_str<T: Hash + ?Sized>(&self, value: &T) -> u64;
139}
140
141impl<B: BuildHasher> BuildHasherExt for B {
142    #[inline]
143    #[cfg(feature = "specialize")]
144    default fn hash_as_u64<T: Hash + ?Sized>(&self, value: &T) -> u64 {
145        let mut hasher = self.build_hasher();
146        value.hash(&mut hasher);
147        hasher.finish()
148    }
149    #[inline]
150    #[cfg(not(feature = "specialize"))]
151    fn hash_as_u64<T: Hash + ?Sized>(&self, value: &T) -> u64 {
152        let mut hasher = self.build_hasher();
153        value.hash(&mut hasher);
154        hasher.finish()
155    }
156    #[inline]
157    #[cfg(feature = "specialize")]
158    default fn hash_as_fixed_length<T: Hash + ?Sized>(&self, value: &T) -> u64 {
159        let mut hasher = self.build_hasher();
160        value.hash(&mut hasher);
161        hasher.finish()
162    }
163    #[inline]
164    #[cfg(not(feature = "specialize"))]
165    fn hash_as_fixed_length<T: Hash + ?Sized>(&self, value: &T) -> u64 {
166        let mut hasher = self.build_hasher();
167        value.hash(&mut hasher);
168        hasher.finish()
169    }
170    #[inline]
171    #[cfg(feature = "specialize")]
172    default fn hash_as_str<T: Hash + ?Sized>(&self, value: &T) -> u64 {
173        let mut hasher = self.build_hasher();
174        value.hash(&mut hasher);
175        hasher.finish()
176    }
177    #[inline]
178    #[cfg(not(feature = "specialize"))]
179    fn hash_as_str<T: Hash + ?Sized>(&self, value: &T) -> u64 {
180        let mut hasher = self.build_hasher();
181        value.hash(&mut hasher);
182        hasher.finish()
183    }
184}
185
186// #[inline(never)]
187// #[doc(hidden)]
188// pub fn hash_test(input: &[u8]) -> u64 {
189//     let a = RandomState::with_seeds(11, 22, 33, 44);
190//     <[u8]>::get_hash(input, &a)
191// }
192
193#[cfg(feature = "std")]
194#[cfg(test)]
195mod test {
196    use crate::convert::Convert;
197    use crate::*;
198    use std::collections::HashMap;
199    use std::hash::Hash;
200
201    #[test]
202    fn test_default_builder() {
203        use core::hash::BuildHasherDefault;
204
205        let mut map = HashMap::<u32, u64, BuildHasherDefault<AHasher>>::default();
206        map.insert(1, 3);
207    }
208
209    #[test]
210    fn test_builder() {
211        let mut map = HashMap::<u32, u64, RandomState>::default();
212        map.insert(1, 3);
213    }
214
215    #[test]
216    fn test_conversion() {
217        let input: &[u8] = b"dddddddd";
218        let bytes: u64 = as_array!(input, 8).convert();
219        assert_eq!(bytes, 0x6464646464646464);
220    }
221
222
223    #[test]
224    fn test_non_zero() {
225        let mut hasher1 = AHasher::new_with_keys(0, 0);
226        let mut hasher2 = AHasher::new_with_keys(0, 0);
227        "foo".hash(&mut hasher1);
228        "bar".hash(&mut hasher2);
229        assert_ne!(hasher1.finish(), 0);
230        assert_ne!(hasher2.finish(), 0);
231        assert_ne!(hasher1.finish(), hasher2.finish());
232
233        let mut hasher1 = AHasher::new_with_keys(0, 0);
234        let mut hasher2 = AHasher::new_with_keys(0, 0);
235        3_u64.hash(&mut hasher1);
236        4_u64.hash(&mut hasher2);
237        assert_ne!(hasher1.finish(), 0);
238        assert_ne!(hasher2.finish(), 0);
239        assert_ne!(hasher1.finish(), hasher2.finish());
240    }
241
242    #[test]
243    fn test_non_zero_specialized() {
244        let hasher_build = RandomState::with_seeds(0,0,0,0);
245
246        let h1 = str::get_hash("foo", &hasher_build);
247        let h2 = str::get_hash("bar", &hasher_build);
248        assert_ne!(h1, 0);
249        assert_ne!(h2, 0);
250        assert_ne!(h1, h2);
251
252        let h1 = u64::get_hash(&3_u64, &hasher_build);
253        let h2 = u64::get_hash(&4_u64, &hasher_build);
254        assert_ne!(h1, 0);
255        assert_ne!(h2, 0);
256        assert_ne!(h1, h2);
257    }
258
259    #[test]
260    fn test_ahasher_construction() {
261        let _ = AHasher::new_with_keys(1234, 5678);
262    }
263}