#ifndef HASHFN_UNSTABLE_H
#define HASHFN_UNSTABLE_H
typedef struct fasthash_state
{
uint64 accum;
uint64 hash;
} fasthash_state;
#define FH_SIZEOF_ACCUM sizeof(uint64)
static inline void
fasthash_init(fasthash_state *hs, uint64 seed)
{
memset(hs, 0, sizeof(fasthash_state));
hs->hash = seed ^ 0x880355f21e6d1965;
}
static inline uint64
fasthash_mix(uint64 h, uint64 tweak)
{
h ^= (h >> 23) + tweak;
h *= 0x2127599bf4325c37;
h ^= h >> 47;
return h;
}
static inline void
fasthash_combine(fasthash_state *hs)
{
hs->hash ^= fasthash_mix(hs->accum, 0);
hs->hash *= 0x880355f21e6d1965;
}
static inline void
fasthash_accum(fasthash_state *hs, const char *k, size_t len)
{
uint32 lower_four;
Assert(len <= FH_SIZEOF_ACCUM);
hs->accum = 0;
#ifdef WORDS_BIGENDIAN
switch (len)
{
case 8:
memcpy(&hs->accum, k, 8);
break;
case 7:
hs->accum |= (uint64) k[6] << 8;
case 6:
hs->accum |= (uint64) k[5] << 16;
case 5:
hs->accum |= (uint64) k[4] << 24;
case 4:
memcpy(&lower_four, k, sizeof(lower_four));
hs->accum |= (uint64) lower_four << 32;
break;
case 3:
hs->accum |= (uint64) k[2] << 40;
case 2:
hs->accum |= (uint64) k[1] << 48;
case 1:
hs->accum |= (uint64) k[0] << 56;
break;
case 0:
return;
}
#else
switch (len)
{
case 8:
memcpy(&hs->accum, k, 8);
break;
case 7:
hs->accum |= (uint64) k[6] << 48;
case 6:
hs->accum |= (uint64) k[5] << 40;
case 5:
hs->accum |= (uint64) k[4] << 32;
case 4:
memcpy(&lower_four, k, sizeof(lower_four));
hs->accum |= lower_four;
break;
case 3:
hs->accum |= (uint64) k[2] << 16;
case 2:
hs->accum |= (uint64) k[1] << 8;
case 1:
hs->accum |= (uint64) k[0];
break;
case 0:
return;
}
#endif
fasthash_combine(hs);
}
#define haszero64(v) \
(((v) - 0x0101010101010101) & ~(v) & 0x8080808080808080)
static inline size_t
fasthash_accum_cstring_unaligned(fasthash_state *hs, const char *str)
{
const char *const start = str;
while (*str)
{
size_t chunk_len = 0;
while (chunk_len < FH_SIZEOF_ACCUM && str[chunk_len] != '\0')
chunk_len++;
fasthash_accum(hs, str, chunk_len);
str += chunk_len;
}
return str - start;
}
pg_attribute_no_sanitize_address()
static inline size_t
fasthash_accum_cstring_aligned(fasthash_state *hs, const char *str)
{
const char *const start = str;
size_t remainder;
uint64 zero_byte_low;
Assert(PointerIsAligned(start, uint64));
for (;;)
{
uint64 chunk = *(uint64 *) str;
zero_byte_low = haszero64(chunk);
if (zero_byte_low)
break;
hs->accum = chunk;
fasthash_combine(hs);
str += FH_SIZEOF_ACCUM;
}
remainder = fasthash_accum_cstring_unaligned(hs, str);
str += remainder;
return str - start;
}
static inline size_t
fasthash_accum_cstring(fasthash_state *hs, const char *str)
{
#if SIZEOF_VOID_P >= 8
size_t len;
#ifdef USE_ASSERT_CHECKING
size_t len_check;
fasthash_state hs_check;
memcpy(&hs_check, hs, sizeof(fasthash_state));
len_check = fasthash_accum_cstring_unaligned(&hs_check, str);
#endif
if (PointerIsAligned(str, uint64))
{
len = fasthash_accum_cstring_aligned(hs, str);
Assert(len_check == len);
Assert(hs_check.hash == hs->hash);
return len;
}
#endif
return fasthash_accum_cstring_unaligned(hs, str);
}
static inline uint64
fasthash_final64(fasthash_state *hs, uint64 tweak)
{
return fasthash_mix(hs->hash, tweak);
}
static inline uint32
fasthash_reduce32(uint64 h)
{
return h - (h >> 32);
}
static inline uint32
fasthash_final32(fasthash_state *hs, uint64 tweak)
{
return fasthash_reduce32(fasthash_final64(hs, tweak));
}
static inline uint64
fasthash64(const char *k, size_t len, uint64 seed)
{
fasthash_state hs;
fasthash_init(&hs, 0);
hs.hash = seed ^ (len * 0x880355f21e6d1965);
while (len >= FH_SIZEOF_ACCUM)
{
fasthash_accum(&hs, k, FH_SIZEOF_ACCUM);
k += FH_SIZEOF_ACCUM;
len -= FH_SIZEOF_ACCUM;
}
fasthash_accum(&hs, k, len);
return fasthash_final64(&hs, 0);
}
static inline uint32
fasthash32(const char *k, size_t len, uint64 seed)
{
return fasthash_reduce32(fasthash64(k, len, seed));
}
static inline uint32
hash_string(const char *s)
{
fasthash_state hs;
size_t s_len;
fasthash_init(&hs, 0);
s_len = fasthash_accum_cstring(&hs, s);
return fasthash_final32(&hs, s_len);
}
#endif