#pragma once #include #include #include namespace halfsiphash { namespace detail { constexpr uint32_t rotl(uint32_t x, int b) { return (x << b) | (x >> (32 - b)); } constexpr uint32_t load_le32(const uint8_t *p) { return static_cast(p[0]) | (static_cast(p[1]) << 8) | (static_cast(p[2]) << 16) | (static_cast(p[3]) << 24); } inline void store_le32(uint8_t *p, uint32_t v) { p[0] = static_cast(v); p[1] = static_cast(v >> 8); p[2] = static_cast(v >> 16); p[3] = static_cast(v >> 24); } inline void sipround(uint32_t &v0, uint32_t &v1, uint32_t &v2, uint32_t &v3) { v0 += v1; v1 = rotl(v1, 5); v1 ^= v0; v0 = rotl(v0, 16); v2 += v3; v3 = rotl(v3, 8); v3 ^= v2; v0 += v3; v3 = rotl(v3, 7); v3 ^= v0; v2 += v1; v1 = rotl(v1, 13); v1 ^= v2; v2 = rotl(v2, 16); } } // namespace detail // Compute HalfSipHash-2-4 with an 8-byte key, returning a 32-bit hash. inline uint32_t hash32(std::span data, const uint8_t key[8]) { using namespace detail; uint32_t k0 = load_le32(key); uint32_t k1 = load_le32(key + 4); uint32_t v0 = 0 ^ k0; uint32_t v1 = 0 ^ k1; uint32_t v2 = UINT32_C(0x6c796765) ^ k0; uint32_t v3 = UINT32_C(0x74656462) ^ k1; const uint8_t *end = data.data() + data.size() - (data.size() % 4); for (const uint8_t *p = data.data(); p != end; p += 4) { uint32_t m = load_le32(p); v3 ^= m; sipround(v0, v1, v2, v3); sipround(v0, v1, v2, v3); v0 ^= m; } uint32_t b = static_cast(data.size()) << 24; switch (data.size() & 3) { case 3: b |= static_cast(end[2]) << 16; [[fallthrough]]; case 2: b |= static_cast(end[1]) << 8; [[fallthrough]]; case 1: b |= static_cast(end[0]); break; case 0: break; } v3 ^= b; sipround(v0, v1, v2, v3); sipround(v0, v1, v2, v3); v0 ^= b; v2 ^= 0xff; sipround(v0, v1, v2, v3); sipround(v0, v1, v2, v3); sipround(v0, v1, v2, v3); sipround(v0, v1, v2, v3); return v1 ^ v3; } } // namespace halfsiphash