Skip to content

Instantly share code, notes, and snippets.

@mtwilliams
Created January 17, 2021 09:07
Show Gist options
  • Save mtwilliams/eff668bf16621b862e2f8f7be07603a6 to your computer and use it in GitHub Desktop.
Save mtwilliams/eff668bf16621b862e2f8f7be07603a6 to your computer and use it in GitHub Desktop.
Compile-time hashing of string literals with xxHash32.
#define XXHASH32_PRIME_1 0x9e3779b1ul
#define XXHASH32_PRIME_2 0x85ebca77ul
#define XXHASH32_PRIME_3 0xc2b2ae3dul
#define XXHASH32_PRIME_4 0x27d4eb2ful
#define XXHASH32_PRIME_5 0x165667b1ul
struct _compile_time_hasher {
static constexpr uint32_t hash(const char *const data, size_t len, uint32_t seed = 0) {
return avalanche(
apply4(
(
len >= 16
?
// Process in 16 byte blocks.
apply16(seed + XXHASH32_PRIME_1 + XXHASH32_PRIME_2, seed + XXHASH32_PRIME_2, seed + 0, seed - XXHASH32_PRIME_1, data, len) + static_cast<uint32_t>(len)
:
// Too small.
seed + XXHASH32_PRIME_5 + static_cast<uint32_t>(len)
),
// Process remaining bytes.
data + ((len / 16) * 16),
len % 16
)
);
}
private:
static constexpr uint32_t rotl(uint32_t x, unsigned r) {
return ((x << r) | (x >> (32 - r)));
}
static constexpr uint32_t mix(uint32_t h, uint32_t prime, unsigned shift) {
return (h ^ (h >> shift)) * prime;
}
static constexpr uint32_t round(uint32_t h, uint32_t x) {
return rotl(h + x * XXHASH32_PRIME_2, 13) * XXHASH32_PRIME_1;
}
static constexpr uint32_t avalanche(uint32_t h) {
return mix(mix(mix(h, XXHASH32_PRIME_2, 15), XXHASH32_PRIME_3, 13), 1, 16);
}
static constexpr uint8_t read1(const char *const p) {
return static_cast<uint8_t>(*p);
}
static constexpr uint32_t read4(const char *const p) {
#if ON_LITTLE_ENDIAN
return ((uint32_t)p[0] << 0) | ((uint32_t)p[1] << 8)
| ((uint32_t)p[2] << 16) | ((uint32_t)p[3] << 24);
#elif ON_BIG_ENDIAN
#endif
}
// Recursive function that processes 1-3 remaining bytes.
static constexpr uint32_t apply1(uint32_t h, const char *p, size_t n) {
return (
n > 0
?
apply1(rotl(h + read1(p) * XXHASH32_PRIME_5, 11) * XXHASH32_PRIME_1, p + 1, n - 1)
:
h
);
}
// Recursive function that processes last 1-3 four byte groups, then calls apply1 to handle remaining bytes.
static constexpr uint32_t apply4(uint32_t h, const char *p, size_t n) {
return (
n >= 4
?
apply4(rotl(h + read4(p) * XXHASH32_PRIME_3, 17) * XXHASH32_PRIME_4, p + 4, n - 4)
:
// Process remaining bytes individually.
apply1(h, p, n)
);
}
// Recursive function that processes 16 byte groups.
static constexpr uint32_t apply16(uint32_t a, uint32_t b, uint32_t c, uint32_t d, const char *p, size_t n) {
return (
n >= 16
?
apply16(round(a, read4(p + 0)), round(b, read4(p + 4)), round(c, read4(p + 8)), round(d, read4(p + 12)), p + 16, n - 16)
:
rotl(a, 1) + rotl(b, 7) + rotl(c, 12) + rotl(d, 18)
);
}
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment