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