drgn/libdrgn/cityhash.h
Omar Sandoval c0d8709b45 Update copyright headers to Meta
Signed-off-by: Omar Sandoval <osandov@osandov.com>
2021-11-21 15:59:44 -08:00

340 lines
9.7 KiB
C

// Copyright (c) Meta Platforms, Inc. and affiliates.
// SPDX-License-Identifier: GPL-3.0-or-later
#ifndef DRGN_CITYHASH_H
#define DRGN_CITYHASH_H
#include <byteswap.h>
#include <stddef.h>
#include <stdint.h>
static inline uint32_t cityhash_fetch32(const uint8_t *p)
{
return ((uint32_t)p[0] |
((uint32_t)p[1] << 8) |
((uint32_t)p[2] << 16) |
((uint32_t)p[3] << 24));
}
static inline uint64_t cityhash_fetch64(const uint8_t *p)
{
return ((uint64_t)p[0] |
((uint64_t)p[1] << 8) |
((uint64_t)p[2] << 16) |
((uint64_t)p[3] << 24) |
((uint64_t)p[4] << 32) |
((uint64_t)p[5] << 40) |
((uint64_t)p[6] << 48) |
((uint64_t)p[7] << 56));
}
#define cityhash_c1 UINT32_C(0xcc9e2d51)
#define cityhash_c2 UINT32_C(0x1b873593)
#define cityhash_c3 UINT32_C(0xe6546b64)
static uint32_t cityhash_fmix(uint32_t h)
{
h ^= h >> 16;
h *= UINT32_C(0x85ebca6b);
h ^= h >> 13;
h *= UINT32_C(0xc2b2ae35);
h ^= h >> 16;
return h;
}
static uint32_t cityhash_rotate32(uint32_t x, int b) {
return b == 0 ? x : (x >> b) | (x << (32 - b));
}
static uint32_t cityhash_mur(uint32_t a, uint32_t h) {
a *= cityhash_c1;
a = cityhash_rotate32(a, 17);
a *= cityhash_c2;
h ^= a;
h = cityhash_rotate32(h, 19);
return h * 5 + cityhash_c3;
}
static uint32_t cityhash32_len_13to24(const uint8_t *s, size_t len) {
uint32_t a = cityhash_fetch32(s - 4 + (len >> 1));
uint32_t b = cityhash_fetch32(s + 4);
uint32_t c = cityhash_fetch32(s + len - 8);
uint32_t d = cityhash_fetch32(s + (len >> 1));
uint32_t e = cityhash_fetch32(s);
uint32_t f = cityhash_fetch32(s + len - 4);
uint32_t h = len;
return cityhash_fmix(cityhash_mur(f, cityhash_mur(e, cityhash_mur(d, cityhash_mur(c, cityhash_mur(b, cityhash_mur(a, h)))))));
}
static uint32_t cityhash32_len_0to4(const uint8_t *s, size_t len) {
uint32_t b = 0;
uint32_t c = 9;
for (size_t i = 0; i < len; i++) {
signed char v = s[i];
b = b * cityhash_c1 + v;
c ^= b;
}
return cityhash_fmix(cityhash_mur(b, cityhash_mur(len, c)));
}
static uint32_t cityhash32_len_5to12(const uint8_t *s, size_t len) {
uint32_t a = len, b = len * 5, c = 9, d = b;
a += cityhash_fetch32(s);
b += cityhash_fetch32(s + len - 4);
c += cityhash_fetch32(s + ((len >> 1) & 4));
return cityhash_fmix(cityhash_mur(c, cityhash_mur(b, cityhash_mur(a, d))));
}
__attribute__((__unused__))
static uint32_t cityhash32(const void *data, size_t len) {
const uint8_t *s = data;
if (len <= 4)
return cityhash32_len_0to4(s, len);
else if (len <= 12)
return cityhash32_len_5to12(s, len);
else if (len <= 24)
return cityhash32_len_13to24(s, len);
uint32_t h = len, g = cityhash_c1 * len, f = g;
uint32_t a0 = cityhash_rotate32(cityhash_fetch32(s + len - 4) * cityhash_c1, 17) * cityhash_c2;
uint32_t a1 = cityhash_rotate32(cityhash_fetch32(s + len - 8) * cityhash_c1, 17) * cityhash_c2;
uint32_t a2 = cityhash_rotate32(cityhash_fetch32(s + len - 16) * cityhash_c1, 17) * cityhash_c2;
uint32_t a3 = cityhash_rotate32(cityhash_fetch32(s + len - 12) * cityhash_c1, 17) * cityhash_c2;
uint32_t a4 = cityhash_rotate32(cityhash_fetch32(s + len - 20) * cityhash_c1, 17) * cityhash_c2;
h ^= a0;
h = cityhash_rotate32(h, 19);
h = h * 5 + cityhash_c3;
h ^= a2;
h = cityhash_rotate32(h, 19);
h = h * 5 + cityhash_c3;
g ^= a1;
g = cityhash_rotate32(g, 19);
g = g * 5 + cityhash_c3;
g ^= a3;
g = cityhash_rotate32(g, 19);
g = g * 5 + cityhash_c3;
f += a4;
f = cityhash_rotate32(f, 19);
f = f * 5 + cityhash_c3;
size_t iters = (len - 1) / 20;
do {
uint32_t a0 = cityhash_rotate32(cityhash_fetch32(s) * cityhash_c1, 17) * cityhash_c2;
uint32_t a1 = cityhash_fetch32(s + 4);
uint32_t a2 = cityhash_rotate32(cityhash_fetch32(s + 8) * cityhash_c1, 17) * cityhash_c2;
uint32_t a3 = cityhash_rotate32(cityhash_fetch32(s + 12) * cityhash_c1, 17) * cityhash_c2;
uint32_t a4 = cityhash_fetch32(s + 16);
h ^= a0;
h = cityhash_rotate32(h, 18);
h = h * 5 + cityhash_c3;
f += a1;
f = cityhash_rotate32(f, 19);
f = f * cityhash_c1;
g += a2;
g = cityhash_rotate32(g, 18);
g = g * 5 + cityhash_c3;
h ^= a3 + a1;
h = cityhash_rotate32(h, 19);
h = h * 5 + cityhash_c3;
g ^= a4;
g = bswap_32(g) * 5;
h += a4 * 5;
h = bswap_32(h);
f += a0;
uint32_t tmp;
tmp = f;
f = g;
g = h;
h = tmp;
s += 20;
} while (--iters != 0);
g = cityhash_rotate32(g, 11) * cityhash_c1;
g = cityhash_rotate32(g, 17) * cityhash_c1;
f = cityhash_rotate32(f, 11) * cityhash_c1;
f = cityhash_rotate32(f, 17) * cityhash_c1;
h = cityhash_rotate32(h + g, 19);
h = h * 5 + cityhash_c3;
h = cityhash_rotate32(h, 17) * cityhash_c1;
h = cityhash_rotate32(h + f, 19);
h = h * 5 + cityhash_c3;
h = cityhash_rotate32(h, 17) * cityhash_c1;
return h;
}
struct cityhash_pair {
uint64_t first;
uint64_t second;
};
#define cityhash_k0 UINT64_C(0xc3a5c85c97cb3127)
#define cityhash_k1 UINT64_C(0xb492b66fbe98f273)
#define cityhash_k2 UINT64_C(0x9ae16a3b2f90404f)
static inline uint64_t cityhash_rotate(uint64_t x, int b)
{
return b == 0 ? x : (x >> b) | (x << (64 - b));
}
static inline uint64_t cityhash_shiftmix(uint64_t val)
{
return val ^ (val >> 47);
}
static inline uint64_t cityhash_128_to_64(uint64_t lo, uint64_t hi) {
static const uint64_t mul = UINT64_C(0x9ddfea08eb382d69);
uint64_t a = (lo ^ hi) * mul;
a ^= (a >> 47);
uint64_t b = (hi ^ a) * mul;
b ^= (b >> 47);
b *= mul;
return b;
}
static inline uint64_t cityhash_len_16(uint64_t u, uint64_t v, uint64_t mul)
{
uint64_t a = (u ^ v) * mul;
a ^= (a >> 47);
uint64_t b = (v ^ a) * mul;
b ^= (b >> 47);
b *= mul;
return b;
}
static uint64_t cityhash_len_0to16(const uint8_t *s, size_t len)
{
if (len == 0) {
return cityhash_k2;
} else if (len < 4) {
uint8_t a = s[0];
uint8_t b = s[len >> 1];
uint8_t c = s[len - 1];
uint32_t y = (uint32_t)a + ((uint32_t)b << 8);
uint32_t z = len + ((uint32_t)c << 2);
return cityhash_shiftmix(y * cityhash_k2 ^ z * cityhash_k0) * cityhash_k2;
} else if (len < 8) {
uint64_t mul = cityhash_k2 + len * 2;
uint64_t a = cityhash_fetch32(s);
return cityhash_len_16(len + (a << 3),
cityhash_fetch32(s + len - 4), mul);
} else {
uint64_t mul = cityhash_k2 + len * 2;
uint64_t a = cityhash_fetch64(s) + cityhash_k2;
uint64_t b = cityhash_fetch64(s + len - 8);
uint64_t c = cityhash_rotate(b, 37) * mul + a;
uint64_t d = (cityhash_rotate(a, 25) + b) * mul;
return cityhash_len_16(c, d, mul);
}
}
static uint64_t cityhash_len_17to32(const uint8_t *s, size_t len)
{
uint64_t mul = cityhash_k2 + len * 2;
uint64_t a = cityhash_fetch64(s) * cityhash_k1;
uint64_t b = cityhash_fetch64(s + 8);
uint64_t c = cityhash_fetch64(s + len - 8) * mul;
uint64_t d = cityhash_fetch64(s + len - 16) * cityhash_k2;
return cityhash_len_16(cityhash_rotate(a + b, 43) +
cityhash_rotate(c, 30) + d,
a + cityhash_rotate(b + cityhash_k2, 18) + c,
mul);
}
static struct cityhash_pair cityhash_weak_len_32_with_seeds(const uint8_t *s,
uint64_t a,
uint64_t b)
{
uint64_t w = cityhash_fetch64(s);
uint64_t x = cityhash_fetch64(s + 8);
uint64_t y = cityhash_fetch64(s + 16);
uint64_t z = cityhash_fetch64(s + 24);
a += w;
b = cityhash_rotate(b + a + z, 21);
uint64_t c = a;
a += x;
a += y;
b += cityhash_rotate(a, 44);
return (struct cityhash_pair){a + z, b + c};
}
static uint64_t cityhash_len_33to64(const uint8_t *s, size_t len)
{
uint64_t mul = cityhash_k2 + len * 2;
uint64_t a = cityhash_fetch64(s) * cityhash_k2;
uint64_t b = cityhash_fetch64(s + 8);
uint64_t c = cityhash_fetch64(s + len - 24);
uint64_t d = cityhash_fetch64(s + len - 32);
uint64_t e = cityhash_fetch64(s + 16) * cityhash_k2;
uint64_t f = cityhash_fetch64(s + 24) * 9;
uint64_t g = cityhash_fetch64(s + len - 8);
uint64_t h = cityhash_fetch64(s + len - 16) * mul;
uint64_t u = cityhash_rotate(a + g, 43) + (cityhash_rotate(b, 30) + c) * 9;
uint64_t v = ((a + g) ^ d) + f + 1;
uint64_t w = bswap_64((u + v) * mul) + h;
uint64_t x = cityhash_rotate(e + f, 42) + c;
uint64_t y = (bswap_64((v + w) * mul) + g) * mul;
uint64_t z = e + f + c;
a = bswap_64((x + z) * mul + y) + b;
b = cityhash_shiftmix((z + a) * mul + d + h) * mul;
return b + x;
}
__attribute__((__unused__))
static uint64_t cityhash64(const void *data, size_t len)
{
const uint8_t *s = data;
if (len <= 16)
return cityhash_len_0to16(s, len);
else if (len <= 32)
return cityhash_len_17to32(s, len);
else if (len <= 64)
return cityhash_len_33to64(s, len);
uint64_t x = cityhash_fetch64(s + len - 40);
uint64_t y = (cityhash_fetch64(s + len - 16) +
cityhash_fetch64(s + len - 56));
uint64_t z = cityhash_128_to_64(cityhash_fetch64(s + len - 48) + len,
cityhash_fetch64(s + len - 24));
struct cityhash_pair v =
cityhash_weak_len_32_with_seeds(s + len - 64, len, z);
struct cityhash_pair w =
cityhash_weak_len_32_with_seeds(s + len - 32, y + cityhash_k1,
x);
x = x * cityhash_k1 + cityhash_fetch64(s);
len = (len - 1) & ~(size_t)63;
do {
x = (cityhash_rotate(x + y + v.first +
cityhash_fetch64(s + 8), 37) *
cityhash_k1);
y = (cityhash_rotate(y + v.second +
cityhash_fetch64(s + 48), 42) *
cityhash_k1);
x ^= w.second;
y += v.first + cityhash_fetch64(s + 40);
z = cityhash_rotate(z + w.first, 33) * cityhash_k1;
v = cityhash_weak_len_32_with_seeds(s, v.second * cityhash_k1,
x + w.first);
w = cityhash_weak_len_32_with_seeds(s + 32, z + w.second,
y + cityhash_fetch64(s + 16));
uint64_t tmp = z;
z = x;
x = tmp;
s += 64;
len -= 64;
} while (len != 0);
return cityhash_128_to_64(cityhash_128_to_64(v.first, w.first) + cityhash_shiftmix(y) * cityhash_k1 + z,
cityhash_128_to_64(v.second, w.second) + x);
}
static inline size_t cityhash_size_t(const void *data, size_t len)
{
if (sizeof(size_t) == sizeof(uint32_t))
return cityhash32(data, len);
else
return cityhash64(data, len);
}
#endif /* DRGN_CITYHASH_H */