|
11 | 11 | //===----------------------------------------------------------------------===// |
12 | 12 |
|
13 | 13 | // |
14 | | -// This file implements helpers for constructing non-cryptographic hash |
15 | | -// functions. |
| 14 | +// This file implements helpers for hashing collections. |
16 | 15 | // |
17 | | -// This code was ported from LLVM's ADT/Hashing.h. |
18 | | -// |
19 | | -// Currently the algorithm is based on CityHash, but this is an implementation |
20 | | -// detail. Even more, there are facilities to mix in a per-execution seed to |
21 | | -// ensure that hash values differ between executions. |
22 | | -// |
23 | | - |
24 | | -@_frozen // FIXME(sil-serialize-all) |
25 | | -public // @testable |
26 | | -enum _HashingDetail { |
27 | | - |
28 | | - // FIXME(hasher): Remove |
29 | | - @inlinable // FIXME(sil-serialize-all) |
30 | | - @_transparent |
31 | | - internal static func getExecutionSeed() -> UInt64 { |
32 | | - // FIXME: This needs to be a per-execution seed. This is just a placeholder |
33 | | - // implementation. |
34 | | - return 0xff51afd7ed558ccd |
35 | | - } |
36 | | - |
37 | | - // FIXME(hasher): Remove |
38 | | - @inlinable // FIXME(sil-serialize-all) |
39 | | - @_transparent |
40 | | - internal static func hash16Bytes(_ low: UInt64, _ high: UInt64) -> UInt64 { |
41 | | - // Murmur-inspired hashing. |
42 | | - let mul: UInt64 = 0x9ddfea08eb382d69 |
43 | | - var a: UInt64 = (low ^ high) &* mul |
44 | | - a ^= (a >> 47) |
45 | | - var b: UInt64 = (high ^ a) &* mul |
46 | | - b ^= (b >> 47) |
47 | | - b = b &* mul |
48 | | - return b |
49 | | - } |
50 | | -} |
51 | | - |
52 | | -// |
53 | | -// API functions. |
54 | | -// |
55 | | - |
56 | | -// |
57 | | -// _mix*() functions all have type (T) -> T. These functions don't compress |
58 | | -// their inputs and just exhibit avalanche effect. |
59 | | -// |
60 | | - |
61 | | -// FIXME(hasher): Remove |
62 | | -@inlinable // FIXME(sil-serialize-all) |
63 | | -@_transparent |
64 | | -public // @testable |
65 | | -func _mixUInt32(_ value: UInt32) -> UInt32 { |
66 | | - // Zero-extend to 64 bits, hash, select 32 bits from the hash. |
67 | | - // |
68 | | - // NOTE: this differs from LLVM's implementation, which selects the lower |
69 | | - // 32 bits. According to the statistical tests, the 3 lowest bits have |
70 | | - // weaker avalanche properties. |
71 | | - let extendedValue = UInt64(value) |
72 | | - let extendedResult = _mixUInt64(extendedValue) |
73 | | - return UInt32((extendedResult >> 3) & 0xffff_ffff) |
74 | | -} |
75 | | - |
76 | | -// FIXME(hasher): Remove |
77 | | -@inlinable // FIXME(sil-serialize-all) |
78 | | -@_transparent |
79 | | -public // @testable |
80 | | -func _mixInt32(_ value: Int32) -> Int32 { |
81 | | - return Int32(bitPattern: _mixUInt32(UInt32(bitPattern: value))) |
82 | | -} |
83 | | - |
84 | | -// FIXME(hasher): Remove |
85 | | -@inlinable // FIXME(sil-serialize-all) |
86 | | -@_transparent |
87 | | -public // @testable |
88 | | -func _mixUInt64(_ value: UInt64) -> UInt64 { |
89 | | - // Similar to hash_4to8_bytes but using a seed instead of length. |
90 | | - let seed: UInt64 = _HashingDetail.getExecutionSeed() |
91 | | - let low: UInt64 = value & 0xffff_ffff |
92 | | - let high: UInt64 = value >> 32 |
93 | | - return _HashingDetail.hash16Bytes(seed &+ (low << 3), high) |
94 | | -} |
95 | | - |
96 | | -// FIXME(hasher): Remove |
97 | | -@inlinable // FIXME(sil-serialize-all) |
98 | | -@_transparent |
99 | | -public // @testable |
100 | | -func _mixInt64(_ value: Int64) -> Int64 { |
101 | | - return Int64(bitPattern: _mixUInt64(UInt64(bitPattern: value))) |
102 | | -} |
103 | | - |
104 | | -// FIXME(hasher): Remove |
105 | | -@inlinable // FIXME(sil-serialize-all) |
106 | | -@_transparent |
107 | | -public // @testable |
108 | | -func _mixUInt(_ value: UInt) -> UInt { |
109 | | -#if arch(i386) || arch(arm) |
110 | | - return UInt(_mixUInt32(UInt32(value))) |
111 | | -#elseif arch(x86_64) || arch(arm64) || arch(powerpc64) || arch(powerpc64le) || arch(s390x) |
112 | | - return UInt(_mixUInt64(UInt64(value))) |
113 | | -#endif |
114 | | -} |
115 | | - |
116 | | -// FIXME(hasher): Remove |
117 | | -@inlinable // FIXME(sil-serialize-all) |
118 | | -@_transparent |
119 | | -public // @testable |
120 | | -func _mixInt(_ value: Int) -> Int { |
121 | | -#if arch(i386) || arch(arm) |
122 | | - return Int(_mixInt32(Int32(value))) |
123 | | -#elseif arch(x86_64) || arch(arm64) || arch(powerpc64) || arch(powerpc64le) || arch(s390x) |
124 | | - return Int(_mixInt64(Int64(value))) |
125 | | -#endif |
126 | | -} |
127 | | - |
128 | | -/// Returns a new value that combines the two given hash values. |
129 | | -/// |
130 | | -/// Combining is performed using [a hash function][ref] described by T.C. Hoad |
131 | | -/// and J. Zobel, which is also adopted in the Boost C++ libraries. |
132 | | -/// |
133 | | -/// This function is used by synthesized implementations of `hashValue` to |
134 | | -/// combine the hash values of individual `struct` fields and associated values |
135 | | -/// of `enum`s. It is factored out into a standard library function so that the |
136 | | -/// specific hashing logic can be refined without requiring major changes to the |
137 | | -/// code that creates the synthesized AST nodes. |
138 | | -/// |
139 | | -/// [ref]: https://pdfs.semanticscholar.org/03bf/7be88e88ba047c6ab28036d0f28510299226.pdf |
140 | | -@_transparent |
141 | | -public // @testable |
142 | | -func _combineHashValues(_ firstValue: Int, _ secondValue: Int) -> Int { |
143 | | - // Use a magic number based on the golden ratio |
144 | | - // (0x1.9e3779b97f4a7c15f39cc0605cedc8341082276bf3a27251f86c6a11d0c18e95p0). |
145 | | -#if arch(i386) || arch(arm) |
146 | | - let magic = 0x9e3779b9 as UInt |
147 | | -#elseif arch(x86_64) || arch(arm64) || arch(powerpc64) || arch(powerpc64le) || arch(s390x) |
148 | | - let magic = 0x9e3779b97f4a7c15 as UInt |
149 | | -#endif |
150 | | - var x = UInt(bitPattern: firstValue) |
151 | | - x ^= UInt(bitPattern: secondValue) &+ magic &+ (x &<< 6) &+ (x &>> 2) |
152 | | - return Int(bitPattern: x) |
153 | | -} |
154 | | - |
155 | | -// FIXME(hasher): This hasher emulates Swift 4.1 hashValues. It is purely for |
156 | | -// benchmarking; to be removed. |
157 | | -internal struct _LegacyHasherCore: _HasherCore { |
158 | | - internal var _hash: Int |
159 | | - |
160 | | - @inline(__always) |
161 | | - internal init(seed: (UInt64, UInt64) = (0, 0)) { // seed is ignored |
162 | | - _hash = 0 |
163 | | - } |
164 | | - |
165 | | - @inline(__always) |
166 | | - internal mutating func compress(_ value: UInt64) { |
167 | | - let value = (UInt64.bitWidth > Int.bitWidth |
168 | | - ? Int(truncatingIfNeeded: value ^ (value &>> 32)) |
169 | | - : Int(truncatingIfNeeded: value)) |
170 | | - _hash = (_hash == 0 ? value : _combineHashValues(_hash, value)) |
171 | | - } |
172 | | - |
173 | | - @inline(__always) |
174 | | - internal mutating func finalize(tailAndByteCount: UInt64) -> UInt64 { |
175 | | - let count = (tailAndByteCount &>> 56) & 7 |
176 | | - if count > 0 { |
177 | | - compress(tailAndByteCount & ((1 &<< (count &<< 3)) - 1)) |
178 | | - } |
179 | | - return UInt64( |
180 | | - _truncatingBits: UInt(bitPattern: _mixInt(_hash))._lowWord) |
181 | | - } |
182 | | - |
183 | | - @inline(__always) |
184 | | - func _generateSeed() -> (UInt64, UInt64) { |
185 | | - return (0, 0) |
186 | | - } |
187 | | -} |
188 | | - |
189 | 16 |
|
190 | 17 | /// This protocol is only used for compile-time checks that |
191 | 18 | /// every buffer type implements all required operations. |
|
0 commit comments