Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/corert.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'src/System.Private.CoreLib/shared/System/Marvin.cs')
-rw-r--r--src/System.Private.CoreLib/shared/System/Marvin.cs143
1 files changed, 143 insertions, 0 deletions
diff --git a/src/System.Private.CoreLib/shared/System/Marvin.cs b/src/System.Private.CoreLib/shared/System/Marvin.cs
new file mode 100644
index 000000000..d25a244c9
--- /dev/null
+++ b/src/System.Private.CoreLib/shared/System/Marvin.cs
@@ -0,0 +1,143 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Diagnostics;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using Internal.Runtime.CompilerServices;
+
+#if BIT64
+using nuint = System.UInt64;
+#else
+using nuint = System.UInt32;
+#endif
+
+namespace System
+{
+ internal static class Marvin
+ {
+ /// <summary>
+ /// Compute a Marvin hash and collapse it into a 32-bit hash.
+ /// </summary>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int ComputeHash32(ReadOnlySpan<byte> data, ulong seed) => ComputeHash32(ref MemoryMarshal.GetReference(data), data.Length, seed);
+
+ /// <summary>
+ /// Compute a Marvin hash and collapse it into a 32-bit hash.
+ /// </summary>
+ public static int ComputeHash32(ref byte data, int count, ulong seed)
+ {
+ nuint ucount = (nuint)count;
+ uint p0 = (uint)seed;
+ uint p1 = (uint)(seed >> 32);
+
+ nuint byteOffset = 0;
+
+ while (ucount >= 8)
+ {
+ p0 += Unsafe.ReadUnaligned<uint>(ref Unsafe.AddByteOffset(ref data, byteOffset));
+ Block(ref p0, ref p1);
+
+ p0 += Unsafe.ReadUnaligned<uint>(ref Unsafe.AddByteOffset(ref data, byteOffset + 4));
+ Block(ref p0, ref p1);
+
+ byteOffset += 8;
+ ucount -= 8;
+ }
+
+ switch (ucount)
+ {
+ case 4:
+ p0 += Unsafe.ReadUnaligned<uint>(ref Unsafe.AddByteOffset(ref data, byteOffset));
+ Block(ref p0, ref p1);
+ goto case 0;
+
+ case 0:
+ p0 += 0x80u;
+ break;
+
+ case 5:
+ p0 += Unsafe.ReadUnaligned<uint>(ref Unsafe.AddByteOffset(ref data, byteOffset));
+ byteOffset += 4;
+ Block(ref p0, ref p1);
+ goto case 1;
+
+ case 1:
+ p0 += 0x8000u | Unsafe.AddByteOffset(ref data, byteOffset);
+ break;
+
+ case 6:
+ p0 += Unsafe.ReadUnaligned<uint>(ref Unsafe.AddByteOffset(ref data, byteOffset));
+ byteOffset += 4;
+ Block(ref p0, ref p1);
+ goto case 2;
+
+ case 2:
+ p0 += 0x800000u | Unsafe.ReadUnaligned<ushort>(ref Unsafe.AddByteOffset(ref data, byteOffset));
+ break;
+
+ case 7:
+ p0 += Unsafe.ReadUnaligned<uint>(ref Unsafe.AddByteOffset(ref data, byteOffset));
+ byteOffset += 4;
+ Block(ref p0, ref p1);
+ goto case 3;
+
+ case 3:
+ p0 += 0x80000000u | (((uint)(Unsafe.AddByteOffset(ref data, byteOffset + 2))) << 16)| (uint)(Unsafe.ReadUnaligned<ushort>(ref Unsafe.AddByteOffset(ref data, byteOffset)));
+ break;
+
+ default:
+ Debug.Fail("Should not get here.");
+ break;
+ }
+
+ Block(ref p0, ref p1);
+ Block(ref p0, ref p1);
+
+ return (int)(p1 ^ p0);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static void Block(ref uint rp0, ref uint rp1)
+ {
+ uint p0 = rp0;
+ uint p1 = rp1;
+
+ p1 ^= p0;
+ p0 = _rotl(p0, 20);
+
+ p0 += p1;
+ p1 = _rotl(p1, 9);
+
+ p1 ^= p0;
+ p0 = _rotl(p0, 27);
+
+ p0 += p1;
+ p1 = _rotl(p1, 19);
+
+ rp0 = p0;
+ rp1 = p1;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static uint _rotl(uint value, int shift)
+ {
+ // This is expected to be optimized into a single rol (or ror with negated shift value) instruction
+ return (value << shift) | (value >> (32 - shift));
+ }
+
+ public static ulong DefaultSeed { get; } = GenerateSeed();
+
+ private static unsafe ulong GenerateSeed()
+ {
+#if MONO
+ return 839433921;
+#else
+ ulong seed;
+ Interop.GetRandomBytes((byte*)&seed, sizeof(ulong));
+ return seed;
+#endif
+ }
+ }
+}