Coverage Report

Created: 2025-06-22 07:07

/rust/registry/src/index.crates.io-6f17d22bba15001f/ahash-0.8.12/src/lib.rs
Line
Count
Source (jump to first uncovered line)
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 randomness 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(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
150
#[cfg(feature = "std")]
151
/// A convenience trait that can be used together with the type aliases defined to
152
/// get access to the `new()` and `with_capacity()` methods for the HashMap type alias.
153
pub trait HashMapExt {
154
    /// Constructs a new HashMap
155
    fn new() -> Self;
156
    /// Constructs a new HashMap with a given initial capacity
157
    fn with_capacity(capacity: usize) -> Self;
158
}
159
160
#[cfg(feature = "std")]
161
/// A convenience trait that can be used together with the type aliases defined to
162
/// get access to the `new()` and `with_capacity()` methods for the HashSet type aliases.
163
pub trait HashSetExt {
164
    /// Constructs a new HashSet
165
    fn new() -> Self;
166
    /// Constructs a new HashSet with a given initial capacity
167
    fn with_capacity(capacity: usize) -> Self;
168
}
169
170
#[cfg(feature = "std")]
171
impl<K, V, S> HashMapExt for std::collections::HashMap<K, V, S>
172
where
173
    S: BuildHasher + Default,
174
{
175
0
    fn new() -> Self {
176
0
        std::collections::HashMap::with_hasher(S::default())
177
0
    }
178
179
0
    fn with_capacity(capacity: usize) -> Self {
180
0
        std::collections::HashMap::with_capacity_and_hasher(capacity, S::default())
181
0
    }
182
}
183
184
#[cfg(feature = "std")]
185
impl<K, S> HashSetExt for std::collections::HashSet<K, S>
186
where
187
    S: BuildHasher + Default,
188
{
189
0
    fn new() -> Self {
190
0
        std::collections::HashSet::with_hasher(S::default())
191
0
    }
192
193
0
    fn with_capacity(capacity: usize) -> Self {
194
0
        std::collections::HashSet::with_capacity_and_hasher(capacity, S::default())
195
0
    }
196
}
197
198
/// Provides a default [Hasher] with fixed keys.
199
/// This is typically used in conjunction with [BuildHasherDefault] to create
200
/// [AHasher]s in order to hash the keys of the map.
201
///
202
/// Generally it is preferable to use [RandomState] instead, so that different
203
/// hashmaps will have different keys. However if fixed keys are desirable this
204
/// may be used instead.
205
///
206
/// # Example
207
/// ```
208
/// use std::hash::BuildHasherDefault;
209
/// use ahash::{AHasher, RandomState};
210
/// use std::collections::HashMap;
211
///
212
/// let mut map: HashMap<i32, i32, BuildHasherDefault<AHasher>> = HashMap::default();
213
/// map.insert(12, 34);
214
/// ```
215
///
216
/// [BuildHasherDefault]: std::hash::BuildHasherDefault
217
/// [Hasher]: std::hash::Hasher
218
/// [HashMap]: std::collections::HashMap
219
impl Default for AHasher {
220
    /// Constructs a new [AHasher] with fixed keys.
221
    /// If `std` is enabled these will be generated upon first invocation.
222
    /// Otherwise if the `compile-time-rng`feature is enabled these will be generated at compile time.
223
    /// If neither of these features are available, hardcoded constants will be used.
224
    ///
225
    /// Because the values are fixed, different hashers will all hash elements the same way.
226
    /// This could make hash values predictable, if DOS attacks are a concern. If this behaviour is
227
    /// not required, it may be preferable to use [RandomState] instead.
228
    ///
229
    /// # Examples
230
    ///
231
    /// ```
232
    /// use ahash::AHasher;
233
    /// use std::hash::Hasher;
234
    ///
235
    /// let mut hasher_1 = AHasher::default();
236
    /// let mut hasher_2 = AHasher::default();
237
    ///
238
    /// hasher_1.write_u32(1234);
239
    /// hasher_2.write_u32(1234);
240
    ///
241
    /// assert_eq!(hasher_1.finish(), hasher_2.finish());
242
    /// ```
243
    #[inline]
244
0
    fn default() -> AHasher {
245
0
        RandomState::with_fixed_keys().build_hasher()
246
0
    }
247
}
248
249
// #[inline(never)]
250
// #[doc(hidden)]
251
// pub fn hash_test(input: &[u8]) -> u64 {
252
//     let a = RandomState::with_seeds(11, 22, 33, 44);
253
//     <[u8]>::get_hash(input, &a)
254
// }
255
256
#[cfg(feature = "std")]
257
#[cfg(test)]
258
mod test {
259
    use crate::convert::Convert;
260
    use crate::specialize::CallHasher;
261
    use crate::*;
262
    use core::hash::Hash;
263
    use core::hash::Hasher;
264
    use std::collections::HashMap;
265
266
    #[test]
267
    fn test_ahash_alias_map_construction() {
268
        let mut map = super::HashMap::with_capacity(1234);
269
        map.insert(1, "test");
270
    }
271
272
    #[test]
273
    fn test_ahash_alias_set_construction() {
274
        let mut set = super::HashSet::with_capacity(1234);
275
        set.insert(1);
276
    }
277
278
    #[test]
279
    fn test_default_builder() {
280
        use core::hash::BuildHasherDefault;
281
282
        let mut map = HashMap::<u32, u64, BuildHasherDefault<AHasher>>::default();
283
        map.insert(1, 3);
284
    }
285
286
    #[test]
287
    fn test_builder() {
288
        let mut map = HashMap::<u32, u64, RandomState>::default();
289
        map.insert(1, 3);
290
    }
291
292
    #[test]
293
    fn test_conversion() {
294
        let input: &[u8] = b"dddddddd";
295
        let bytes: u64 = as_array!(input, 8).convert();
296
        assert_eq!(bytes, 0x6464646464646464);
297
    }
298
299
    #[test]
300
    fn test_non_zero() {
301
        let mut hasher1 = AHasher::new_with_keys(0, 0);
302
        let mut hasher2 = AHasher::new_with_keys(0, 0);
303
        "foo".hash(&mut hasher1);
304
        "bar".hash(&mut hasher2);
305
        assert_ne!(hasher1.finish(), 0);
306
        assert_ne!(hasher2.finish(), 0);
307
        assert_ne!(hasher1.finish(), hasher2.finish());
308
309
        let mut hasher1 = AHasher::new_with_keys(0, 0);
310
        let mut hasher2 = AHasher::new_with_keys(0, 0);
311
        3_u64.hash(&mut hasher1);
312
        4_u64.hash(&mut hasher2);
313
        assert_ne!(hasher1.finish(), 0);
314
        assert_ne!(hasher2.finish(), 0);
315
        assert_ne!(hasher1.finish(), hasher2.finish());
316
    }
317
318
    #[test]
319
    fn test_non_zero_specialized() {
320
        let hasher_build = RandomState::with_seeds(0, 0, 0, 0);
321
322
        let h1 = str::get_hash("foo", &hasher_build);
323
        let h2 = str::get_hash("bar", &hasher_build);
324
        assert_ne!(h1, 0);
325
        assert_ne!(h2, 0);
326
        assert_ne!(h1, h2);
327
328
        let h1 = u64::get_hash(&3_u64, &hasher_build);
329
        let h2 = u64::get_hash(&4_u64, &hasher_build);
330
        assert_ne!(h1, 0);
331
        assert_ne!(h2, 0);
332
        assert_ne!(h1, h2);
333
    }
334
335
    #[test]
336
    fn test_ahasher_construction() {
337
        let _ = AHasher::new_with_keys(1234, 5678);
338
    }
339
340
    #[test]
341
    fn test_specialize_reference_hash() {
342
        let hasher_build = RandomState::with_seeds(0, 0, 0, 0);
343
        let h1 = hasher_build.hash_one(1u64);
344
        let h2 = hasher_build.hash_one(&1u64);
345
346
        assert_eq!(h1, h2);
347
348
        let h1 = u64::get_hash(&1_u64, &hasher_build);
349
        let h2 = <&u64>::get_hash(&&1_u64, &hasher_build);
350
351
        assert_eq!(h1, h2);
352
353
        let h1 = hasher_build.hash_one(1u128);
354
        let h2 = hasher_build.hash_one(&1u128);
355
356
        assert_eq!(h1, h2);
357
    }
358
}