94 lines
2.4 KiB
C
94 lines
2.4 KiB
C
/*
|
|
Fash64: Douglas Crockford (2017-02-02)
|
|
64-bit hash that uses the high 64 bits of a 128-bit product for feedback.
|
|
|
|
Notes:
|
|
- Requires a way to get the high half of a 64x64->128 multiply.
|
|
- Uses __uint128_t when available; otherwise uses MSVC _umul128.
|
|
*/
|
|
|
|
#include <stdint.h>
|
|
#include <stddef.h>
|
|
|
|
typedef struct fash64_state {
|
|
uint64_t result;
|
|
uint64_t sum;
|
|
} fash64_state;
|
|
|
|
enum {
|
|
FASH64_PRIME_11 = 11111111111111111027ull,
|
|
FASH64_PRIME_8 = 8888888888888888881ull,
|
|
FASH64_PRIME_3 = 3333333333333333271ull
|
|
};
|
|
|
|
static inline void fash64_mul_hi_lo(uint64_t a, uint64_t b, uint64_t *hi, uint64_t *lo)
|
|
{
|
|
#if defined(__SIZEOF_INT128__)
|
|
__uint128_t p = (__uint128_t)a * (__uint128_t)b;
|
|
*lo = (uint64_t)p;
|
|
*hi = (uint64_t)(p >> 64);
|
|
#elif defined(_MSC_VER) && defined(_M_X64)
|
|
*lo = _umul128(a, b, hi);
|
|
#else
|
|
/* Portable fallback (no 128-bit type, no _umul128). */
|
|
uint64_t a0 = (uint32_t)a;
|
|
uint64_t a1 = a >> 32;
|
|
uint64_t b0 = (uint32_t)b;
|
|
uint64_t b1 = b >> 32;
|
|
|
|
uint64_t p00 = a0 * b0;
|
|
uint64_t p01 = a0 * b1;
|
|
uint64_t p10 = a1 * b0;
|
|
uint64_t p11 = a1 * b1;
|
|
|
|
uint64_t mid = (p00 >> 32) + (uint32_t)p01 + (uint32_t)p10;
|
|
*lo = (p00 & 0xffffffffull) | (mid << 32);
|
|
*hi = p11 + (p01 >> 32) + (p10 >> 32) + (mid >> 32);
|
|
#endif
|
|
}
|
|
|
|
static inline void fash64_begin(fash64_state *s)
|
|
{
|
|
s->result = (uint64_t)FASH64_PRIME_8;
|
|
s->sum = (uint64_t)FASH64_PRIME_3;
|
|
}
|
|
|
|
static inline void fash64_word(fash64_state *s, uint64_t word)
|
|
{
|
|
uint64_t high, low;
|
|
uint64_t mixed = s->result ^ word;
|
|
|
|
fash64_mul_hi_lo(mixed, (uint64_t)FASH64_PRIME_11, &high, &low);
|
|
|
|
s->sum += high;
|
|
s->result = low ^ s->sum;
|
|
}
|
|
|
|
static inline void fash64_block(fash64_state *s, const uint64_t *block, size_t word_count)
|
|
{
|
|
for (size_t i = 0; i < word_count; i++) fash64_word(s, block[i]);
|
|
}
|
|
|
|
static inline uint64_t fash64_end(const fash64_state *s)
|
|
{
|
|
return s->result;
|
|
}
|
|
|
|
/* Convenience one-shot helper */
|
|
static inline uint64_t fash64_hash_words(const uint64_t *words, size_t word_count, uint64_t extra_word)
|
|
{
|
|
fash64_state s;
|
|
fash64_begin(&s);
|
|
fash64_block(&s, words, word_count);
|
|
fash64_word(&s, extra_word);
|
|
return fash64_end(&s);
|
|
}
|
|
|
|
static inline uint64_t fash64_hash_one(uint64_t word)
|
|
{
|
|
uint64_t high, low;
|
|
uint64_t mixed = (uint64_t)FASH64_PRIME_8 ^ word;
|
|
fash64_mul_hi_lo(mixed, (uint64_t)FASH64_PRIME_11, &high, &low);
|
|
return low ^ ((uint64_t)FASH64_PRIME_3 + high);
|
|
}
|