summaryrefslogtreecommitdiff
path: root/src/common/cityhash.cpp
diff options
context:
space:
mode:
authorGravatar ReinUsesLisp2021-02-18 00:23:53 -0300
committerGravatar ReinUsesLisp2021-02-18 00:45:17 -0300
commitf3805376f726e10ff2fe26e99b8702f20eee3eea (patch)
treea8079d5a1026e3a408e6a72c4c32ac9f7ff852f6 /src/common/cityhash.cpp
parenttests: Add tests for CityHash (diff)
downloadyuzu-f3805376f726e10ff2fe26e99b8702f20eee3eea.tar.gz
yuzu-f3805376f726e10ff2fe26e99b8702f20eee3eea.tar.xz
yuzu-f3805376f726e10ff2fe26e99b8702f20eee3eea.zip
common/cityhash: Use common types
Allow sharing return types with the rest of the code base. For example, we use 'u128 = std::array<u64, 2>', meanwhile Google's code uses 'uint128 = std::pair<u64, u64>'. While we are at it, use size_t instead of std::size_t.
Diffstat (limited to 'src/common/cityhash.cpp')
-rw-r--r--src/common/cityhash.cpp178
1 files changed, 87 insertions, 91 deletions
diff --git a/src/common/cityhash.cpp b/src/common/cityhash.cpp
index 4e1d874b5..66218fc21 100644
--- a/src/common/cityhash.cpp
+++ b/src/common/cityhash.cpp
@@ -28,8 +28,10 @@
28// compromising on hash quality. 28// compromising on hash quality.
29 29
30#include <algorithm> 30#include <algorithm>
31#include <string.h> // for memcpy and memset 31#include <cstring>
32#include "cityhash.h" 32#include <utility>
33
34#include "common/cityhash.h"
33#include "common/swap.h" 35#include "common/swap.h"
34 36
35// #include "config.h" 37// #include "config.h"
@@ -42,21 +44,17 @@
42 44
43using namespace std; 45using namespace std;
44 46
45typedef uint8_t uint8;
46typedef uint32_t uint32;
47typedef uint64_t uint64;
48
49namespace Common { 47namespace Common {
50 48
51static uint64 UNALIGNED_LOAD64(const char* p) { 49static u64 unaligned_load64(const char* p) {
52 uint64 result; 50 u64 result;
53 memcpy(&result, p, sizeof(result)); 51 std::memcpy(&result, p, sizeof(result));
54 return result; 52 return result;
55} 53}
56 54
57static uint32 UNALIGNED_LOAD32(const char* p) { 55static u32 unaligned_load32(const char* p) {
58 uint32 result; 56 u32 result;
59 memcpy(&result, p, sizeof(result)); 57 std::memcpy(&result, p, sizeof(result));
60 return result; 58 return result;
61} 59}
62 60
@@ -76,64 +74,64 @@ static uint32 UNALIGNED_LOAD32(const char* p) {
76#endif 74#endif
77#endif 75#endif
78 76
79static uint64 Fetch64(const char* p) { 77static u64 Fetch64(const char* p) {
80 return uint64_in_expected_order(UNALIGNED_LOAD64(p)); 78 return uint64_in_expected_order(unaligned_load64(p));
81} 79}
82 80
83static uint32 Fetch32(const char* p) { 81static u32 Fetch32(const char* p) {
84 return uint32_in_expected_order(UNALIGNED_LOAD32(p)); 82 return uint32_in_expected_order(unaligned_load32(p));
85} 83}
86 84
87// Some primes between 2^63 and 2^64 for various uses. 85// Some primes between 2^63 and 2^64 for various uses.
88static const uint64 k0 = 0xc3a5c85c97cb3127ULL; 86static constexpr u64 k0 = 0xc3a5c85c97cb3127ULL;
89static const uint64 k1 = 0xb492b66fbe98f273ULL; 87static constexpr u64 k1 = 0xb492b66fbe98f273ULL;
90static const uint64 k2 = 0x9ae16a3b2f90404fULL; 88static constexpr u64 k2 = 0x9ae16a3b2f90404fULL;
91 89
92// Bitwise right rotate. Normally this will compile to a single 90// Bitwise right rotate. Normally this will compile to a single
93// instruction, especially if the shift is a manifest constant. 91// instruction, especially if the shift is a manifest constant.
94static uint64 Rotate(uint64 val, int shift) { 92static u64 Rotate(u64 val, int shift) {
95 // Avoid shifting by 64: doing so yields an undefined result. 93 // Avoid shifting by 64: doing so yields an undefined result.
96 return shift == 0 ? val : ((val >> shift) | (val << (64 - shift))); 94 return shift == 0 ? val : ((val >> shift) | (val << (64 - shift)));
97} 95}
98 96
99static uint64 ShiftMix(uint64 val) { 97static u64 ShiftMix(u64 val) {
100 return val ^ (val >> 47); 98 return val ^ (val >> 47);
101} 99}
102 100
103static uint64 HashLen16(uint64 u, uint64 v) { 101static u64 HashLen16(u64 u, u64 v) {
104 return Hash128to64(uint128(u, v)); 102 return Hash128to64(u128{u, v});
105} 103}
106 104
107static uint64 HashLen16(uint64 u, uint64 v, uint64 mul) { 105static u64 HashLen16(u64 u, u64 v, u64 mul) {
108 // Murmur-inspired hashing. 106 // Murmur-inspired hashing.
109 uint64 a = (u ^ v) * mul; 107 u64 a = (u ^ v) * mul;
110 a ^= (a >> 47); 108 a ^= (a >> 47);
111 uint64 b = (v ^ a) * mul; 109 u64 b = (v ^ a) * mul;
112 b ^= (b >> 47); 110 b ^= (b >> 47);
113 b *= mul; 111 b *= mul;
114 return b; 112 return b;
115} 113}
116 114
117static uint64 HashLen0to16(const char* s, std::size_t len) { 115static u64 HashLen0to16(const char* s, size_t len) {
118 if (len >= 8) { 116 if (len >= 8) {
119 uint64 mul = k2 + len * 2; 117 u64 mul = k2 + len * 2;
120 uint64 a = Fetch64(s) + k2; 118 u64 a = Fetch64(s) + k2;
121 uint64 b = Fetch64(s + len - 8); 119 u64 b = Fetch64(s + len - 8);
122 uint64 c = Rotate(b, 37) * mul + a; 120 u64 c = Rotate(b, 37) * mul + a;
123 uint64 d = (Rotate(a, 25) + b) * mul; 121 u64 d = (Rotate(a, 25) + b) * mul;
124 return HashLen16(c, d, mul); 122 return HashLen16(c, d, mul);
125 } 123 }
126 if (len >= 4) { 124 if (len >= 4) {
127 uint64 mul = k2 + len * 2; 125 u64 mul = k2 + len * 2;
128 uint64 a = Fetch32(s); 126 u64 a = Fetch32(s);
129 return HashLen16(len + (a << 3), Fetch32(s + len - 4), mul); 127 return HashLen16(len + (a << 3), Fetch32(s + len - 4), mul);
130 } 128 }
131 if (len > 0) { 129 if (len > 0) {
132 uint8 a = s[0]; 130 u8 a = s[0];
133 uint8 b = s[len >> 1]; 131 u8 b = s[len >> 1];
134 uint8 c = s[len - 1]; 132 u8 c = s[len - 1];
135 uint32 y = static_cast<uint32>(a) + (static_cast<uint32>(b) << 8); 133 u32 y = static_cast<u32>(a) + (static_cast<u32>(b) << 8);
136 uint32 z = static_cast<uint32>(len) + (static_cast<uint32>(c) << 2); 134 u32 z = static_cast<u32>(len) + (static_cast<u32>(c) << 2);
137 return ShiftMix(y * k2 ^ z * k0) * k2; 135 return ShiftMix(y * k2 ^ z * k0) * k2;
138 } 136 }
139 return k2; 137 return k2;
@@ -141,22 +139,21 @@ static uint64 HashLen0to16(const char* s, std::size_t len) {
141 139
142// This probably works well for 16-byte strings as well, but it may be overkill 140// This probably works well for 16-byte strings as well, but it may be overkill
143// in that case. 141// in that case.
144static uint64 HashLen17to32(const char* s, std::size_t len) { 142static u64 HashLen17to32(const char* s, size_t len) {
145 uint64 mul = k2 + len * 2; 143 u64 mul = k2 + len * 2;
146 uint64 a = Fetch64(s) * k1; 144 u64 a = Fetch64(s) * k1;
147 uint64 b = Fetch64(s + 8); 145 u64 b = Fetch64(s + 8);
148 uint64 c = Fetch64(s + len - 8) * mul; 146 u64 c = Fetch64(s + len - 8) * mul;
149 uint64 d = Fetch64(s + len - 16) * k2; 147 u64 d = Fetch64(s + len - 16) * k2;
150 return HashLen16(Rotate(a + b, 43) + Rotate(c, 30) + d, a + Rotate(b + k2, 18) + c, mul); 148 return HashLen16(Rotate(a + b, 43) + Rotate(c, 30) + d, a + Rotate(b + k2, 18) + c, mul);
151} 149}
152 150
153// Return a 16-byte hash for 48 bytes. Quick and dirty. 151// Return a 16-byte hash for 48 bytes. Quick and dirty.
154// Callers do best to use "random-looking" values for a and b. 152// Callers do best to use "random-looking" values for a and b.
155static pair<uint64, uint64> WeakHashLen32WithSeeds(uint64 w, uint64 x, uint64 y, uint64 z, uint64 a, 153static pair<u64, u64> WeakHashLen32WithSeeds(u64 w, u64 x, u64 y, u64 z, u64 a, u64 b) {
156 uint64 b) {
157 a += w; 154 a += w;
158 b = Rotate(b + a + z, 21); 155 b = Rotate(b + a + z, 21);
159 uint64 c = a; 156 u64 c = a;
160 a += x; 157 a += x;
161 a += y; 158 a += y;
162 b += Rotate(a, 44); 159 b += Rotate(a, 44);
@@ -164,34 +161,34 @@ static pair<uint64, uint64> WeakHashLen32WithSeeds(uint64 w, uint64 x, uint64 y,
164} 161}
165 162
166// Return a 16-byte hash for s[0] ... s[31], a, and b. Quick and dirty. 163// Return a 16-byte hash for s[0] ... s[31], a, and b. Quick and dirty.
167static pair<uint64, uint64> WeakHashLen32WithSeeds(const char* s, uint64 a, uint64 b) { 164static pair<u64, u64> WeakHashLen32WithSeeds(const char* s, u64 a, u64 b) {
168 return WeakHashLen32WithSeeds(Fetch64(s), Fetch64(s + 8), Fetch64(s + 16), Fetch64(s + 24), a, 165 return WeakHashLen32WithSeeds(Fetch64(s), Fetch64(s + 8), Fetch64(s + 16), Fetch64(s + 24), a,
169 b); 166 b);
170} 167}
171 168
172// Return an 8-byte hash for 33 to 64 bytes. 169// Return an 8-byte hash for 33 to 64 bytes.
173static uint64 HashLen33to64(const char* s, std::size_t len) { 170static u64 HashLen33to64(const char* s, size_t len) {
174 uint64 mul = k2 + len * 2; 171 u64 mul = k2 + len * 2;
175 uint64 a = Fetch64(s) * k2; 172 u64 a = Fetch64(s) * k2;
176 uint64 b = Fetch64(s + 8); 173 u64 b = Fetch64(s + 8);
177 uint64 c = Fetch64(s + len - 24); 174 u64 c = Fetch64(s + len - 24);
178 uint64 d = Fetch64(s + len - 32); 175 u64 d = Fetch64(s + len - 32);
179 uint64 e = Fetch64(s + 16) * k2; 176 u64 e = Fetch64(s + 16) * k2;
180 uint64 f = Fetch64(s + 24) * 9; 177 u64 f = Fetch64(s + 24) * 9;
181 uint64 g = Fetch64(s + len - 8); 178 u64 g = Fetch64(s + len - 8);
182 uint64 h = Fetch64(s + len - 16) * mul; 179 u64 h = Fetch64(s + len - 16) * mul;
183 uint64 u = Rotate(a + g, 43) + (Rotate(b, 30) + c) * 9; 180 u64 u = Rotate(a + g, 43) + (Rotate(b, 30) + c) * 9;
184 uint64 v = ((a + g) ^ d) + f + 1; 181 u64 v = ((a + g) ^ d) + f + 1;
185 uint64 w = swap64((u + v) * mul) + h; 182 u64 w = swap64((u + v) * mul) + h;
186 uint64 x = Rotate(e + f, 42) + c; 183 u64 x = Rotate(e + f, 42) + c;
187 uint64 y = (swap64((v + w) * mul) + g) * mul; 184 u64 y = (swap64((v + w) * mul) + g) * mul;
188 uint64 z = e + f + c; 185 u64 z = e + f + c;
189 a = swap64((x + z) * mul + y) + b; 186 a = swap64((x + z) * mul + y) + b;
190 b = ShiftMix((z + a) * mul + d + h) * mul; 187 b = ShiftMix((z + a) * mul + d + h) * mul;
191 return b + x; 188 return b + x;
192} 189}
193 190
194uint64 CityHash64(const char* s, std::size_t len) { 191u64 CityHash64(const char* s, size_t len) {
195 if (len <= 32) { 192 if (len <= 32) {
196 if (len <= 16) { 193 if (len <= 16) {
197 return HashLen0to16(s, len); 194 return HashLen0to16(s, len);
@@ -204,15 +201,15 @@ uint64 CityHash64(const char* s, std::size_t len) {
204 201
205 // For strings over 64 bytes we hash the end first, and then as we 202 // For strings over 64 bytes we hash the end first, and then as we
206 // loop we keep 56 bytes of state: v, w, x, y, and z. 203 // loop we keep 56 bytes of state: v, w, x, y, and z.
207 uint64 x = Fetch64(s + len - 40); 204 u64 x = Fetch64(s + len - 40);
208 uint64 y = Fetch64(s + len - 16) + Fetch64(s + len - 56); 205 u64 y = Fetch64(s + len - 16) + Fetch64(s + len - 56);
209 uint64 z = HashLen16(Fetch64(s + len - 48) + len, Fetch64(s + len - 24)); 206 u64 z = HashLen16(Fetch64(s + len - 48) + len, Fetch64(s + len - 24));
210 pair<uint64, uint64> v = WeakHashLen32WithSeeds(s + len - 64, len, z); 207 pair<u64, u64> v = WeakHashLen32WithSeeds(s + len - 64, len, z);
211 pair<uint64, uint64> w = WeakHashLen32WithSeeds(s + len - 32, y + k1, x); 208 pair<u64, u64> w = WeakHashLen32WithSeeds(s + len - 32, y + k1, x);
212 x = x * k1 + Fetch64(s); 209 x = x * k1 + Fetch64(s);
213 210
214 // Decrease len to the nearest multiple of 64, and operate on 64-byte chunks. 211 // Decrease len to the nearest multiple of 64, and operate on 64-byte chunks.
215 len = (len - 1) & ~static_cast<std::size_t>(63); 212 len = (len - 1) & ~static_cast<size_t>(63);
216 do { 213 do {
217 x = Rotate(x + y + v.first + Fetch64(s + 8), 37) * k1; 214 x = Rotate(x + y + v.first + Fetch64(s + 8), 37) * k1;
218 y = Rotate(y + v.second + Fetch64(s + 48), 42) * k1; 215 y = Rotate(y + v.second + Fetch64(s + 48), 42) * k1;
@@ -229,21 +226,21 @@ uint64 CityHash64(const char* s, std::size_t len) {
229 HashLen16(v.second, w.second) + x); 226 HashLen16(v.second, w.second) + x);
230} 227}
231 228
232uint64 CityHash64WithSeed(const char* s, std::size_t len, uint64 seed) { 229u64 CityHash64WithSeed(const char* s, size_t len, u64 seed) {
233 return CityHash64WithSeeds(s, len, k2, seed); 230 return CityHash64WithSeeds(s, len, k2, seed);
234} 231}
235 232
236uint64 CityHash64WithSeeds(const char* s, std::size_t len, uint64 seed0, uint64 seed1) { 233u64 CityHash64WithSeeds(const char* s, size_t len, u64 seed0, u64 seed1) {
237 return HashLen16(CityHash64(s, len) - seed0, seed1); 234 return HashLen16(CityHash64(s, len) - seed0, seed1);
238} 235}
239 236
240// A subroutine for CityHash128(). Returns a decent 128-bit hash for strings 237// A subroutine for CityHash128(). Returns a decent 128-bit hash for strings
241// of any length representable in signed long. Based on City and Murmur. 238// of any length representable in signed long. Based on City and Murmur.
242static uint128 CityMurmur(const char* s, std::size_t len, uint128 seed) { 239static u128 CityMurmur(const char* s, size_t len, u128 seed) {
243 uint64 a = Uint128Low64(seed); 240 u64 a = seed[0];
244 uint64 b = Uint128High64(seed); 241 u64 b = seed[1];
245 uint64 c = 0; 242 u64 c = 0;
246 uint64 d = 0; 243 u64 d = 0;
247 signed long l = static_cast<long>(len) - 16; 244 signed long l = static_cast<long>(len) - 16;
248 if (l <= 0) { // len <= 16 245 if (l <= 0) { // len <= 16
249 a = ShiftMix(a * k1) * k1; 246 a = ShiftMix(a * k1) * k1;
@@ -266,20 +263,20 @@ static uint128 CityMurmur(const char* s, std::size_t len, uint128 seed) {
266 } 263 }
267 a = HashLen16(a, c); 264 a = HashLen16(a, c);
268 b = HashLen16(d, b); 265 b = HashLen16(d, b);
269 return uint128(a ^ b, HashLen16(b, a)); 266 return u128{a ^ b, HashLen16(b, a)};
270} 267}
271 268
272uint128 CityHash128WithSeed(const char* s, std::size_t len, uint128 seed) { 269u128 CityHash128WithSeed(const char* s, size_t len, u128 seed) {
273 if (len < 128) { 270 if (len < 128) {
274 return CityMurmur(s, len, seed); 271 return CityMurmur(s, len, seed);
275 } 272 }
276 273
277 // We expect len >= 128 to be the common case. Keep 56 bytes of state: 274 // We expect len >= 128 to be the common case. Keep 56 bytes of state:
278 // v, w, x, y, and z. 275 // v, w, x, y, and z.
279 pair<uint64, uint64> v, w; 276 pair<u64, u64> v, w;
280 uint64 x = Uint128Low64(seed); 277 u64 x = seed[0];
281 uint64 y = Uint128High64(seed); 278 u64 y = seed[1];
282 uint64 z = len * k1; 279 u64 z = len * k1;
283 v.first = Rotate(y ^ k1, 49) * k1 + Fetch64(s); 280 v.first = Rotate(y ^ k1, 49) * k1 + Fetch64(s);
284 v.second = Rotate(v.first, 42) * k1 + Fetch64(s + 8); 281 v.second = Rotate(v.first, 42) * k1 + Fetch64(s + 8);
285 w.first = Rotate(y + z, 35) * k1 + x; 282 w.first = Rotate(y + z, 35) * k1 + x;
@@ -313,7 +310,7 @@ uint128 CityHash128WithSeed(const char* s, std::size_t len, uint128 seed) {
313 w.first *= 9; 310 w.first *= 9;
314 v.first *= k0; 311 v.first *= k0;
315 // If 0 < len < 128, hash up to 4 chunks of 32 bytes each from the end of s. 312 // If 0 < len < 128, hash up to 4 chunks of 32 bytes each from the end of s.
316 for (std::size_t tail_done = 0; tail_done < len;) { 313 for (size_t tail_done = 0; tail_done < len;) {
317 tail_done += 32; 314 tail_done += 32;
318 y = Rotate(x + y, 42) * k0 + v.second; 315 y = Rotate(x + y, 42) * k0 + v.second;
319 w.first += Fetch64(s + len - tail_done + 16); 316 w.first += Fetch64(s + len - tail_done + 16);
@@ -328,13 +325,12 @@ uint128 CityHash128WithSeed(const char* s, std::size_t len, uint128 seed) {
328 // different 56-byte-to-8-byte hashes to get a 16-byte final result. 325 // different 56-byte-to-8-byte hashes to get a 16-byte final result.
329 x = HashLen16(x, v.first); 326 x = HashLen16(x, v.first);
330 y = HashLen16(y + z, w.first); 327 y = HashLen16(y + z, w.first);
331 return uint128(HashLen16(x + v.second, w.second) + y, HashLen16(x + w.second, y + v.second)); 328 return u128{HashLen16(x + v.second, w.second) + y, HashLen16(x + w.second, y + v.second)};
332} 329}
333 330
334uint128 CityHash128(const char* s, std::size_t len) { 331u128 CityHash128(const char* s, size_t len) {
335 return len >= 16 332 return len >= 16 ? CityHash128WithSeed(s + 16, len - 16, u128{Fetch64(s), Fetch64(s + 8) + k0})
336 ? CityHash128WithSeed(s + 16, len - 16, uint128(Fetch64(s), Fetch64(s + 8) + k0)) 333 : CityHash128WithSeed(s, len, u128{k0, k1});
337 : CityHash128WithSeed(s, len, uint128(k0, k1));
338} 334}
339 335
340} // namespace Common 336} // namespace Common