Created
January 17, 2021 09:07
-
-
Save mtwilliams/eff668bf16621b862e2f8f7be07603a6 to your computer and use it in GitHub Desktop.
Compile-time hashing of string literals with xxHash32.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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