using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace ZeroLevel.Services.HashFunctions { public static class Murmur3 { // 128 bit output, 64 bit platform version public static ulong READ_SIZE = 16; private static ulong C1 = 0x87c37b91114253d5L; private static ulong C2 = 0x4cf5ad432745937fL; [MethodImpl(MethodImplOptions.AggressiveInlining)] private static ulong MixKey1(ulong k1) { k1 *= C1; k1 = k1.RotateLeft(31); k1 *= C2; return k1; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static ulong MixKey2(ulong k2) { k2 *= C2; k2 = k2.RotateLeft(33); k2 *= C1; return k2; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static ulong MixFinal(ulong k) { // avalanche bits k ^= k >> 33; k *= 0xff51afd7ed558ccdL; k ^= k >> 33; k *= 0xc4ceb9fe1a85ec53L; k ^= k >> 33; return k; } public static byte[] ComputeHash(byte[] bb, ulong seed = 0) { var h1 = seed; ulong h2 = 0; var length = 0UL; int pos = 0; ulong remaining = (ulong)bb.Length; // read 128 bits, 16 bytes, 2 longs in eacy cycle while (remaining >= READ_SIZE) { ulong k1 = bb.GetUInt64(pos); pos += 8; ulong k2 = bb.GetUInt64(pos); pos += 8; length += READ_SIZE; remaining -= READ_SIZE; h1 ^= MixKey1(k1); h1 = h1.RotateLeft(27); h1 += h2; h1 = h1 * 5 + 0x52dce729; h2 ^= MixKey2(k2); h2 = h2.RotateLeft(31); h2 += h1; h2 = h2 * 5 + 0x38495ab5; } // if the input MOD 16 != 0 if (remaining > 0) { ulong k1 = 0; ulong k2 = 0; length += remaining; // little endian (x86) processing switch (remaining) { case 15: k2 ^= (ulong)bb[pos + 14] << 48; // fall through goto case 14; case 14: k2 ^= (ulong)bb[pos + 13] << 40; // fall through goto case 13; case 13: k2 ^= (ulong)bb[pos + 12] << 32; // fall through goto case 12; case 12: k2 ^= (ulong)bb[pos + 11] << 24; // fall through goto case 11; case 11: k2 ^= (ulong)bb[pos + 10] << 16; // fall through goto case 10; case 10: k2 ^= (ulong)bb[pos + 9] << 8; // fall through goto case 9; case 9: k2 ^= (ulong)bb[pos + 8]; // fall through goto case 8; case 8: k1 ^= bb.GetUInt64(pos); break; case 7: k1 ^= (ulong)bb[pos + 6] << 48; // fall through goto case 6; case 6: k1 ^= (ulong)bb[pos + 5] << 40; // fall through goto case 5; case 5: k1 ^= (ulong)bb[pos + 4] << 32; // fall through goto case 4; case 4: k1 ^= (ulong)bb[pos + 3] << 24; // fall through goto case 3; case 3: k1 ^= (ulong)bb[pos + 2] << 16; // fall through goto case 2; case 2: k1 ^= (ulong)bb[pos + 1] << 8; // fall through goto case 1; case 1: k1 ^= (ulong)bb[pos]; // fall through break; default: throw new Exception("Something went wrong with remaining bytes calculation."); } h1 ^= MixKey1(k1); h2 ^= MixKey2(k2); } h1 ^= length; h2 ^= length; h1 += h2; h2 += h1; h1 = Murmur3.MixFinal(h1); h2 = Murmur3.MixFinal(h2); h1 += h2; h2 += h1; var hash = new byte[Murmur3.READ_SIZE]; Array.Copy(BitConverter.GetBytes(h1), 0, hash, 0, 8); Array.Copy(BitConverter.GetBytes(h2), 0, hash, 8, 8); return hash; } public static ulong ComputeULongHash(byte[] bb, ulong seed = 0) { var h1 = seed; ulong h2 = 0; var length = 0UL; int pos = 0; ulong remaining = (ulong)bb.Length; // read 128 bits, 16 bytes, 2 longs in eacy cycle while (remaining >= READ_SIZE) { ulong k1 = bb.GetUInt64(pos); pos += 8; ulong k2 = bb.GetUInt64(pos); pos += 8; length += READ_SIZE; remaining -= READ_SIZE; h1 ^= MixKey1(k1); h1 = h1.RotateLeft(27); h1 += h2; h1 = h1 * 5 + 0x52dce729; h2 ^= MixKey2(k2); h2 = h2.RotateLeft(31); h2 += h1; h2 = h2 * 5 + 0x38495ab5; } // if the input MOD 16 != 0 if (remaining > 0) { ulong k1 = 0; ulong k2 = 0; length += remaining; // little endian (x86) processing switch (remaining) { case 15: k2 ^= (ulong)bb[pos + 14] << 48; // fall through goto case 14; case 14: k2 ^= (ulong)bb[pos + 13] << 40; // fall through goto case 13; case 13: k2 ^= (ulong)bb[pos + 12] << 32; // fall through goto case 12; case 12: k2 ^= (ulong)bb[pos + 11] << 24; // fall through goto case 11; case 11: k2 ^= (ulong)bb[pos + 10] << 16; // fall through goto case 10; case 10: k2 ^= (ulong)bb[pos + 9] << 8; // fall through goto case 9; case 9: k2 ^= (ulong)bb[pos + 8]; // fall through goto case 8; case 8: k1 ^= bb.GetUInt64(pos); break; case 7: k1 ^= (ulong)bb[pos + 6] << 48; // fall through goto case 6; case 6: k1 ^= (ulong)bb[pos + 5] << 40; // fall through goto case 5; case 5: k1 ^= (ulong)bb[pos + 4] << 32; // fall through goto case 4; case 4: k1 ^= (ulong)bb[pos + 3] << 24; // fall through goto case 3; case 3: k1 ^= (ulong)bb[pos + 2] << 16; // fall through goto case 2; case 2: k1 ^= (ulong)bb[pos + 1] << 8; // fall through goto case 1; case 1: k1 ^= (ulong)bb[pos]; // fall through break; default: throw new Exception("Something went wrong with remaining bytes calculation."); } h1 ^= MixKey1(k1); h2 ^= MixKey2(k2); } h1 ^= length; h2 ^= length; h1 += h2; h2 += h1; h1 = Murmur3.MixFinal(h1); h2 = Murmur3.MixFinal(h2); h1 += h2; h2 += h1; return h2; } /// /// Hashes the into a MurmurHash3 as a . /// /// The span. /// The seed for this algorithm. /// The MurmurHash3 as a [MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint ComputeUIntHash(ref ReadOnlySpan bytes, uint seed) { ref byte bp = ref MemoryMarshal.GetReference(bytes); ref uint endPoint = ref Unsafe.Add(ref Unsafe.As(ref bp), bytes.Length >> 2); if (bytes.Length >= 4) { do { seed = RotateLeft(seed ^ RotateLeft(Unsafe.ReadUnaligned(ref bp) * 3432918353U, 15) * 461845907U, 13) * 5 - 430675100; bp = ref Unsafe.Add(ref bp, 4); } while (Unsafe.IsAddressLessThan(ref Unsafe.As(ref bp), ref endPoint)); } var remainder = bytes.Length & 3; if (remainder > 0) { uint num = 0; if (remainder > 2) num ^= Unsafe.Add(ref endPoint, 2) << 16; if (remainder > 1) num ^= Unsafe.Add(ref endPoint, 1) << 8; num ^= endPoint; seed ^= RotateLeft(num * 3432918353U, 15) * 461845907U; } seed ^= (uint)bytes.Length; seed = (uint)((seed ^ (seed >> 16)) * -2048144789); seed = (uint)((seed ^ (seed >> 13)) * -1028477387); return seed ^ seed >> 16; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ulong RotateLeft(this ulong original, int bits) => (original << bits) | (original >> (64 - bits)); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint RotateLeft(this uint original, int bits) => (original << bits) | (original >> (32 - bits)); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ulong RotateRight(this ulong original, int bits) => (original >> bits) | (original << (64 - bits)); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ulong GetUInt64(this byte[] bb, int pos) => (ulong)(bb[pos++] | bb[pos++] << 8 | bb[pos++] << 16 | bb[pos++] << 24); } }