/rust/registry/src/index.crates.io-1949cf8c6b5b557f/rustc-hash-1.1.0/src/lib.rs
Line | Count | Source |
1 | | // Copyright 2015 The Rust Project Developers. See the COPYRIGHT |
2 | | // file at the top-level directory of this distribution and at |
3 | | // http://rust-lang.org/COPYRIGHT. |
4 | | // |
5 | | // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or |
6 | | // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license |
7 | | // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your |
8 | | // option. This file may not be copied, modified, or distributed |
9 | | // except according to those terms. |
10 | | |
11 | | //! Fast, non-cryptographic hash used by rustc and Firefox. |
12 | | //! |
13 | | //! # Example |
14 | | //! |
15 | | //! ```rust |
16 | | //! # #[cfg(feature = "std")] |
17 | | //! # fn main() { |
18 | | //! use rustc_hash::FxHashMap; |
19 | | //! let mut map: FxHashMap<u32, u32> = FxHashMap::default(); |
20 | | //! map.insert(22, 44); |
21 | | //! # } |
22 | | //! # #[cfg(not(feature = "std"))] |
23 | | //! # fn main() { } |
24 | | //! ``` |
25 | | |
26 | | #![no_std] |
27 | | |
28 | | #[cfg(feature = "std")] |
29 | | extern crate std; |
30 | | |
31 | | use core::convert::TryInto; |
32 | | use core::default::Default; |
33 | | #[cfg(feature = "std")] |
34 | | use core::hash::BuildHasherDefault; |
35 | | use core::hash::Hasher; |
36 | | use core::mem::size_of; |
37 | | use core::ops::BitXor; |
38 | | #[cfg(feature = "std")] |
39 | | use std::collections::{HashMap, HashSet}; |
40 | | |
41 | | /// Type alias for a hashmap using the `fx` hash algorithm. |
42 | | #[cfg(feature = "std")] |
43 | | pub type FxHashMap<K, V> = HashMap<K, V, BuildHasherDefault<FxHasher>>; |
44 | | |
45 | | /// Type alias for a hashmap using the `fx` hash algorithm. |
46 | | #[cfg(feature = "std")] |
47 | | pub type FxHashSet<V> = HashSet<V, BuildHasherDefault<FxHasher>>; |
48 | | |
49 | | /// A speedy hash algorithm for use within rustc. The hashmap in liballoc |
50 | | /// by default uses SipHash which isn't quite as speedy as we want. In the |
51 | | /// compiler we're not really worried about DOS attempts, so we use a fast |
52 | | /// non-cryptographic hash. |
53 | | /// |
54 | | /// This is the same as the algorithm used by Firefox -- which is a homespun |
55 | | /// one not based on any widely-known algorithm -- though modified to produce |
56 | | /// 64-bit hash values instead of 32-bit hash values. It consistently |
57 | | /// out-performs an FNV-based hash within rustc itself -- the collision rate is |
58 | | /// similar or slightly worse than FNV, but the speed of the hash function |
59 | | /// itself is much higher because it works on up to 8 bytes at a time. |
60 | | pub struct FxHasher { |
61 | | hash: usize, |
62 | | } |
63 | | |
64 | | #[cfg(target_pointer_width = "32")] |
65 | | const K: usize = 0x9e3779b9; |
66 | | #[cfg(target_pointer_width = "64")] |
67 | | const K: usize = 0x517cc1b727220a95; |
68 | | |
69 | | impl Default for FxHasher { |
70 | | #[inline] |
71 | 2.99M | fn default() -> FxHasher { |
72 | 2.99M | FxHasher { hash: 0 } |
73 | 2.99M | } |
74 | | } |
75 | | |
76 | | impl FxHasher { |
77 | | #[inline] |
78 | 6.11M | fn add_to_hash(&mut self, i: usize) { |
79 | 6.11M | self.hash = self.hash.rotate_left(5).bitxor(i).wrapping_mul(K); |
80 | 6.11M | } |
81 | | } |
82 | | |
83 | | impl Hasher for FxHasher { |
84 | | #[inline] |
85 | 582k | fn write(&mut self, mut bytes: &[u8]) { |
86 | | #[cfg(target_pointer_width = "32")] |
87 | | let read_usize = |bytes: &[u8]| u32::from_ne_bytes(bytes[..4].try_into().unwrap()); |
88 | | #[cfg(target_pointer_width = "64")] |
89 | 1.40M | let read_usize = |bytes: &[u8]| u64::from_ne_bytes(bytes[..8].try_into().unwrap()); |
90 | | |
91 | 582k | let mut hash = FxHasher { hash: self.hash }; |
92 | 582k | assert!(size_of::<usize>() <= 8); |
93 | 1.98M | while bytes.len() >= size_of::<usize>() { |
94 | 1.40M | hash.add_to_hash(read_usize(bytes) as usize); |
95 | 1.40M | bytes = &bytes[size_of::<usize>()..]; |
96 | 1.40M | } |
97 | 582k | if (size_of::<usize>() > 4) && (bytes.len() >= 4) { |
98 | 16.5k | hash.add_to_hash(u32::from_ne_bytes(bytes[..4].try_into().unwrap()) as usize); |
99 | 16.5k | bytes = &bytes[4..]; |
100 | 565k | } |
101 | 582k | if (size_of::<usize>() > 2) && bytes.len() >= 2 { |
102 | 16.0k | hash.add_to_hash(u16::from_ne_bytes(bytes[..2].try_into().unwrap()) as usize); |
103 | 16.0k | bytes = &bytes[2..]; |
104 | 566k | } |
105 | 582k | if (size_of::<usize>() > 1) && bytes.len() >= 1 { |
106 | 566k | hash.add_to_hash(bytes[0] as usize); |
107 | 566k | } |
108 | 582k | self.hash = hash.hash; |
109 | 582k | } |
110 | | |
111 | | #[inline] |
112 | 937k | fn write_u8(&mut self, i: u8) { |
113 | 937k | self.add_to_hash(i as usize); |
114 | 937k | } |
115 | | |
116 | | #[inline] |
117 | 0 | fn write_u16(&mut self, i: u16) { |
118 | 0 | self.add_to_hash(i as usize); |
119 | 0 | } |
120 | | |
121 | | #[inline] |
122 | 2.62M | fn write_u32(&mut self, i: u32) { |
123 | 2.62M | self.add_to_hash(i as usize); |
124 | 2.62M | } |
125 | | |
126 | | #[cfg(target_pointer_width = "32")] |
127 | | #[inline] |
128 | | fn write_u64(&mut self, i: u64) { |
129 | | self.add_to_hash(i as usize); |
130 | | self.add_to_hash((i >> 32) as usize); |
131 | | } |
132 | | |
133 | | #[cfg(target_pointer_width = "64")] |
134 | | #[inline] |
135 | | fn write_u64(&mut self, i: u64) { |
136 | | self.add_to_hash(i as usize); |
137 | | } |
138 | | |
139 | | #[inline] |
140 | 543k | fn write_usize(&mut self, i: usize) { |
141 | 543k | self.add_to_hash(i); |
142 | 543k | } |
143 | | |
144 | | #[inline] |
145 | 2.99M | fn finish(&self) -> u64 { |
146 | 2.99M | self.hash as u64 |
147 | 2.99M | } |
148 | | } |