diff --git a/libs/storage/Tsavorite/cs/src/core/Utilities/Utility.cs b/libs/storage/Tsavorite/cs/src/core/Utilities/Utility.cs
index a639355487..8583f67e67 100644
--- a/libs/storage/Tsavorite/cs/src/core/Utilities/Utility.cs
+++ b/libs/storage/Tsavorite/cs/src/core/Utilities/Utility.cs
@@ -2,6 +2,7 @@
// Licensed under the MIT license.
using System;
+using System.Buffers.Binary;
using System.Diagnostics;
using System.Numerics;
using System.Runtime.CompilerServices;
@@ -11,6 +12,18 @@
namespace Tsavorite.core
{
+ internal struct local_UInt128
+ {
+ public ulong Low { get; set; }
+ public ulong High { get; set; }
+
+ public local_UInt128(ulong low, ulong high)
+ {
+ Low = low;
+ High = high;
+ }
+ }
+
///
/// Empty type
///
@@ -178,21 +191,204 @@ public static long GetHashCode(long input)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe long HashBytes(byte* pbString, int len)
{
- const long magicno = 40343;
- char* pwString = (char*)pbString;
- int cbBuf = len / 2;
- ulong hashState = (ulong)len;
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ ulong RotateRight(ulong operand, int shiftCount)
+ {
+ shiftCount &= 0x3f;
+
+ return
+ (operand >> shiftCount) |
+ (operand << (64 - shiftCount));
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ ulong ReverseByteOrder(ulong operand)
+ {
+ return
+ (operand >> 56) |
+ ((operand & 0x00ff000000000000) >> 40) |
+ ((operand & 0x0000ff0000000000) >> 24) |
+ ((operand & 0x000000ff00000000) >> 8) |
+ ((operand & 0x00000000ff000000) << 8) |
+ ((operand & 0x0000000000ff0000) << 24) |
+ ((operand & 0x000000000000ff00) << 40) |
+ (operand << 56);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ ulong Mix(ulong value) =>
+ value ^ (value >> 47);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ long Hash64Len16(ulong u, ulong v, ulong mul)
+ {
+ ulong a = (u ^ v) * mul;
+ a ^= (a >> 47);
+
+ ulong b = (v ^ a) * mul;
+ b ^= (b >> 47);
+ b *= mul;
+
+ return (long)b;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ ulong Hash128to64(local_UInt128 x)
+ {
+ const ulong kMul = 0x9ddfea08eb382d69;
+
+ ulong a = (x.Low ^ x.High) * kMul;
+ a ^= (a >> 47);
+
+ ulong b = (x.High ^ a) * kMul;
+ b ^= (b >> 47);
+ b *= kMul;
+
+ return b;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ ulong _Hash64Len16(ulong u, ulong v)
+ {
+ return Hash128to64(
+ new local_UInt128(u, v));
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ local_UInt128 _WeakHashLen32WithSeeds(ulong w, ulong x, ulong y, ulong z, ulong a, ulong b)
+ {
+ a += w;
+ b = RotateRight(b + a + z, 21);
- for (int i = 0; i < cbBuf; i++, pwString++)
- hashState = magicno * hashState + *pwString;
+ ulong c = a;
+ a += x;
+ a += y;
- if ((len & 1) > 0)
+ b += RotateRight(a, 44);
+
+ return new local_UInt128(a + z, b + c);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ local_UInt128 WeakHashLen32WithSeeds(byte* data, int startIndex, UInt64 a, UInt64 b)
{
- byte* pC = (byte*)pwString;
- hashState = magicno * hashState + *pC;
+ return _WeakHashLen32WithSeeds(
+ Unsafe.Read(data + startIndex),
+ Unsafe.Read(data + startIndex + 8),
+ Unsafe.Read(data + startIndex + 16),
+ Unsafe.Read(data + startIndex + 24),
+ a,
+ b);
}
- return (long)Rotr64(magicno * hashState, 4);
+ const ulong K0 = 0xc3a5c85c97cb3127;
+ const ulong K1 = 0xb492b66fbe98f273;
+ const ulong K2 = 0x9ae16a3b2f90404f;
+
+ unchecked
+ {
+ if (len > 64)
+ {
+ ulong x = Unsafe.Read(pbString + len - 40);
+ ulong y = Unsafe.Read(pbString + len - 16) + Unsafe.Read(pbString + len - 56);
+ ulong z = _Hash64Len16(
+ Unsafe.Read(pbString + len - 48) + (ulong)len,
+ Unsafe.Read(pbString + len - 24));
+
+ local_UInt128 v = WeakHashLen32WithSeeds(pbString, len - 64, (ulong)len, z);
+ local_UInt128 w = WeakHashLen32WithSeeds(pbString, len - 32, y + K1, x);
+
+ x = x * K1 + Unsafe.Read(pbString);
+
+ // For each 64-byte chunk
+ var grouplen = 0 + (len - (len % 64));
+
+ for (var currentOffset = 0; currentOffset < grouplen; currentOffset += 64)
+ {
+ x = RotateRight(x + y + v.Low + Unsafe.Read(pbString + currentOffset + 8), 37) * K1;
+ y = RotateRight(y + v.High + Unsafe.Read(pbString + currentOffset + 48), 42) * K1;
+ x ^= w.High;
+ y += v.Low + Unsafe.Read(pbString + currentOffset + 40);
+ z = RotateRight(z + w.Low, 33) * K1;
+ v = WeakHashLen32WithSeeds(pbString, currentOffset, v.High * K1, x + w.Low);
+ w = WeakHashLen32WithSeeds(pbString, currentOffset + 32, z + w.High, y +
+ Unsafe.Read(pbString + currentOffset + 16));
+
+ ulong temp = x;
+ x = z;
+ z = temp;
+ }
+
+ return (long)_Hash64Len16(_Hash64Len16(v.Low, w.Low) + Mix(y) * K1 + z,
+ _Hash64Len16(v.High, w.High) + x);
+ }
+ else if (len > 32)
+ {
+ ulong mul = K2 + (ulong)len * 2;
+ ulong a = Unsafe.Read(pbString) * K2;
+ ulong b = Unsafe.Read(pbString + 8);
+ ulong c = Unsafe.Read(pbString + len - 24);
+ ulong d = Unsafe.Read(pbString + len - 32);
+ ulong e = Unsafe.Read(pbString + 16) * K2;
+ ulong f = Unsafe.Read(pbString + 24) * 9;
+ ulong g = Unsafe.Read(pbString + len - 8);
+ ulong h = Unsafe.Read(pbString + len - 16) * mul;
+
+ ulong u = RotateRight(a + g, 43) + (RotateRight(b, 30) + c) * 9;
+ ulong v = ((a + g) ^ d) + f + 1;
+ ulong w = ReverseByteOrder((u + v) * mul) + h;
+ ulong x = RotateRight(e + f, 42) + c;
+ ulong y = (ReverseByteOrder((v + w) * mul) + g) * mul;
+ ulong z = e + f + c;
+
+ a = ReverseByteOrder((x + z) * mul + y) + b;
+ b = Mix((z + a) * mul + d + h) * mul;
+ return (long)(b + x);
+ }
+ else if (len > 16)
+ {
+ ulong mul = K2 + (ulong)len * 2;
+ ulong a = Unsafe.Read(pbString) * K1;
+ ulong b = Unsafe.Read(pbString + 8);
+ ulong c = Unsafe.Read(pbString + len - 8) * mul;
+ ulong d = Unsafe.Read(pbString + len - 16) * K2;
+
+ return (long)Hash64Len16(
+ RotateRight(a + b, 43) +
+ RotateRight(c, 30) + d,
+ a + RotateRight(b + K2, 18) + c,
+ mul);
+ }
+ else if (len >= 8)
+ {
+ ulong mul = K2 + (ulong)len * 2;
+ ulong a = Unsafe.Read(pbString) + K2;
+ ulong b = Unsafe.Read(pbString + len - 8);
+ ulong c = RotateRight(b, 37) * mul + a;
+ ulong d = (RotateRight(a, 25) + b) * mul;
+
+ return Hash64Len16(c, d, mul);
+ }
+ else if (len >= 4)
+ {
+ ulong mul = K2 + (ulong)len * 2;
+ ulong a = Unsafe.Read(pbString);
+ return Hash64Len16((ulong)len + (a << 3), Unsafe.Read(pbString + len - 4), mul);
+ }
+ else if (len > 0)
+ {
+ byte a = pbString[0];
+ byte b = pbString[0 + (len >> 1)];
+ byte c = pbString[len - 1];
+
+ uint y = (uint)a + ((uint)b << 8);
+ uint z = (uint)len + ((uint)c << 2);
+
+ return (long)(Mix((ulong)(y * K2 ^ z * K0)) * K2);
+ }
+
+ return (long)K2;
+ }
}
///