// vim:fileencoding=utf-8:foldmethod=marker #include "murmur3.h" #include "hasher.h" #include "../../../common/aliases/aliases.h" #include "../../../common/assert/assert.h" #include "../../../common/misc/misc_utils.h" #include "../../../prng/xorshift/xorshift.h" wp_intern inline void validateMur3HasherCtx(const WpHasherCtx *ctx); wp_intern inline u64 fmix64(u64 k); WpHasher wpX64Mur3Hasher(void) { WpXor256State state = wpPrngXorshiftInit(); u32 seed = (u32)wpPrngXorshift256(&state); return wpX64Mur3HasherWithSeed(seed); } WpHasher wpX64Mur3HasherWithSeed(u32 seed) { return (WpHasher){ .func = wpX64Mur3GetHash128, .ctx = (WpHasherCtx){ .type = WP_HASHER_CTX_MURMUR3, .murmur3 = seed, }, }; } WpU128 wpX64Mur3GetHash128(WpU8Stream *bytes, WpHasherCtx ctx) { validateMur3HasherCtx(&ctx); const i32 nblocks = (bytes->count * bytes->item_size) / 16; u64 h1 = ctx.murmur3; u64 h2 = ctx.murmur3; const u64 c1 = wpU64Const(0x87c37b91114253d5); const u64 c2 = wpU64Const(0x4cf5ad432745937f); //---------- // body for(int i = 0; i < nblocks; ++i) { u64 k1 = *((u64 *)wpStreamConsumeCount(u8, bytes, sizeof(u64))); u64 k2 = *((u64 *)wpStreamConsumeCount(u8, bytes, sizeof(u64))); k1 *= c1; k1 = wpMiscUtilsRotl64(k1, 31); k1 *= c2; h1 ^= k1; h1 = wpMiscUtilsRotl64(h1, 27); h1 += h2; h1 = h1 * 5 +0x52dce729; k2 *= c2; k2 = wpMiscUtilsRotl64(k2, 33); k2 *= c1; h2 ^= k2; h2 = wpMiscUtilsRotl64(h2, 31); h2 += h1; h2 = h2*5+0x38495ab5; } //---------- // tail const u8 *tail = wpStreamConsumeCount(u8, bytes, bytes->count - bytes->position); u64 k1 = 0; u64 k2 = 0; u64 len = bytes->count * bytes->item_size; switch(len & 15) { case 15: k2 ^= ((u64)tail[14]) << 48; case 14: k2 ^= ((u64)tail[13]) << 40; case 13: k2 ^= ((u64)tail[12]) << 32; case 12: k2 ^= ((u64)tail[11]) << 24; case 11: k2 ^= ((u64)tail[10]) << 16; case 10: k2 ^= ((u64)tail[ 9]) << 8; case 9: k2 ^= ((u64)tail[ 8]) << 0; k2 *= c2; k2 = wpMiscUtilsRotl64(k2,33); k2 *= c1; h2 ^= k2; case 8: k1 ^= ((u64)tail[ 7]) << 56; case 7: k1 ^= ((u64)tail[ 6]) << 48; case 6: k1 ^= ((u64)tail[ 5]) << 40; case 5: k1 ^= ((u64)tail[ 4]) << 32; case 4: k1 ^= ((u64)tail[ 3]) << 24; case 3: k1 ^= ((u64)tail[ 2]) << 16; case 2: k1 ^= ((u64)tail[ 1]) << 8; case 1: k1 ^= ((u64)tail[ 0]) << 0; k1 *= c1; k1 = wpMiscUtilsRotl64(k1,31); k1 *= c2; h1 ^= k1; }; //---------- // finalization h1 ^= len; h2 ^= len; h1 += h2; h2 += h1; h1 = fmix64(h1); h2 = fmix64(h2); h1 += h2; h2 += h1; return (WpU128){ .value = { [0] = h1, [1] = h2 } }; } wp_intern inline void validateMur3HasherCtx(const WpHasherCtx *ctx) { wpRuntimeAssert(ctx->type == WP_HASHER_CTX_MURMUR3, "Invalid Murmur3 hash context"); } wp_intern inline u64 fmix64(u64 k) { k ^= k >> 33; k *= wpU64Const(0xff51afd7ed558ccd); k ^= k >> 33; k *= wpU64Const(0xc4ceb9fe1a85ec53); k ^= k >> 33; return k; }