diff --git a/src/libraries/Common/src/System/IO/Hashing/XxHash3.Common.cs b/src/libraries/Common/src/System/IO/Hashing/XxHash3.Common.cs
new file mode 100644
index 00000000000000..7ccc9a8a5a00eb
--- /dev/null
+++ b/src/libraries/Common/src/System/IO/Hashing/XxHash3.Common.cs
@@ -0,0 +1,285 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+// Based on the XXH3 implementation from https://github.com/Cyan4973/xxHash.
+
+using System.Buffers.Binary;
+using System.Diagnostics;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using static System.IO.Hashing.XxHashShared;
+
+namespace System.IO.Hashing
+{
+ /// Provides an implementation of the XXH3 hash algorithm for generating a 64-bit hash.
+ ///
+ /// For methods that persist the computed numerical hash value as bytes,
+ /// the value is written in the Big Endian byte order.
+ ///
+#if NET
+ [SkipLocalsInit]
+#endif
+#if SYSTEM_PRIVATE_CORELIB
+ internal
+#else
+ public
+#endif
+ sealed unsafe partial class XxHash3
+ {
+ /// Computes the XXH3 hash of the provided data.
+ /// The data to hash.
+ /// The seed value for this hash computation.
+ /// The computed XXH3 hash.
+#if !SYSTEM_PRIVATE_CORELIB
+ [CLSCompliant(false)]
+#endif
+ public static ulong HashToUInt64(ReadOnlySpan source, long seed = 0)
+ {
+ uint length = (uint)source.Length;
+ fixed (byte* sourcePtr = &MemoryMarshal.GetReference(source))
+ {
+ if (length <= 16)
+ {
+ return HashLength0To16(sourcePtr, length, (ulong)seed);
+ }
+
+ if (length <= 128)
+ {
+ return HashLength17To128(sourcePtr, length, (ulong)seed);
+ }
+
+ if (length <= MidSizeMaxBytes)
+ {
+ return HashLength129To240(sourcePtr, length, (ulong)seed);
+ }
+
+ return HashLengthOver240(sourcePtr, length, (ulong)seed);
+ }
+ }
+
+
+#if SYSTEM_PRIVATE_CORELIB
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int NonRandomizedHashToInt32(byte* sourcePtr, uint length)
+ {
+ switch (length)
+ {
+ case 0:
+ return (int)Avalanche(DefaultSecretUInt64_7 ^ DefaultSecretUInt64_8);
+
+ case 1:
+ case 2:
+ case 3:
+ return (int)HashLength1To3(sourcePtr, length, 0UL);
+
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ return (int)HashLength4To8(sourcePtr, length, 0UL);
+
+ case 9:
+ case 10:
+ case 11:
+ case 12:
+ case 13:
+ case 14:
+ case 15:
+ case 16:
+ return (int)HashLength9To16(sourcePtr, length, 0UL);
+
+ default:
+ {
+ if (length <= 128)
+ {
+ return (int)HashLength17To128(sourcePtr, length, 0UL);
+ }
+ if (length <= MidSizeMaxBytes)
+ {
+ return (int)HashLength129To240(sourcePtr, length, 0UL);
+ }
+ return (int)HashLengthOver240(sourcePtr, length, 0UL);
+ }
+ }
+ }
+#endif
+
+ private static ulong HashLength0To16(byte* source, uint length, ulong seed)
+ {
+ if (length > 8)
+ {
+ return HashLength9To16(source, length, seed);
+ }
+
+ if (length >= 4)
+ {
+ return HashLength4To8(source, length, seed);
+ }
+
+ if (length != 0)
+ {
+ return HashLength1To3(source, length, seed);
+ }
+
+ const ulong SecretXor = DefaultSecretUInt64_7 ^ DefaultSecretUInt64_8;
+ return Avalanche(seed ^ SecretXor);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static ulong HashLength1To3(byte* source, uint length, ulong seed)
+ {
+ Debug.Assert(length >= 1 && length <= 3);
+
+ // When source.Length == 1, c1 == source[0], c2 == source[0], c3 == source[0]
+ // When source.Length == 2, c1 == source[0], c2 == source[1], c3 == source[1]
+ // When source.Length == 3, c1 == source[0], c2 == source[1], c3 == source[2]
+ byte c1 = *source;
+ byte c2 = source[length >> 1];
+ byte c3 = source[length - 1];
+
+ uint combined = ((uint)c1 << 16) | ((uint)c2 << 24) | c3 | (length << 8);
+
+ const uint SecretXor = unchecked((uint)DefaultSecretUInt64_0) ^ (uint)(DefaultSecretUInt64_0 >> 32);
+ return Avalanche(combined ^ (SecretXor + seed));
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static ulong HashLength4To8(byte* source, uint length, ulong seed)
+ {
+ Debug.Assert(length >= 4 && length <= 8);
+
+ seed ^= (ulong)BinaryPrimitives.ReverseEndianness((uint)seed) << 32;
+
+ uint inputLow = ReadUInt32LE(source);
+ uint inputHigh = ReadUInt32LE(source + length - sizeof(uint));
+
+ const ulong SecretXor = DefaultSecretUInt64_1 ^ DefaultSecretUInt64_2;
+ ulong bitflip = SecretXor - seed;
+ ulong input64 = inputHigh + (((ulong)inputLow) << 32);
+
+ return Rrmxmx(input64 ^ bitflip, length);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static ulong HashLength9To16(byte* source, uint length, ulong seed)
+ {
+ Debug.Assert(length >= 9 && length <= 16);
+
+ const ulong SecretXorL = DefaultSecretUInt64_3 ^ DefaultSecretUInt64_4;
+ const ulong SecretXorR = DefaultSecretUInt64_5 ^ DefaultSecretUInt64_6;
+ ulong bitflipLow = SecretXorL + seed;
+ ulong bitflipHigh = SecretXorR - seed;
+
+ ulong inputLow = ReadUInt64LE(source) ^ bitflipLow;
+ ulong inputHigh = ReadUInt64LE(source + length - sizeof(ulong)) ^ bitflipHigh;
+
+ return FastAvalanche(
+ length +
+ BinaryPrimitives.ReverseEndianness(inputLow) +
+ inputHigh +
+ Multiply64To128ThenFold(inputLow, inputHigh));
+ }
+
+ private static ulong HashLength17To128(byte* source, uint length, ulong seed)
+ {
+ Debug.Assert(length >= 17 && length <= 128);
+
+ ulong hash = length * Prime64_1;
+
+ switch ((length - 1) / 32)
+ {
+ default: // case 3
+ hash += Mix16Bytes(source + 48, DefaultSecretUInt64_12, DefaultSecretUInt64_13, seed);
+ hash += Mix16Bytes(source + length - 64, DefaultSecretUInt64_14, DefaultSecretUInt64_15, seed);
+ goto case 2;
+ case 2:
+ hash += Mix16Bytes(source + 32, DefaultSecretUInt64_8, DefaultSecretUInt64_9, seed);
+ hash += Mix16Bytes(source + length - 48, DefaultSecretUInt64_10, DefaultSecretUInt64_11, seed);
+ goto case 1;
+ case 1:
+ hash += Mix16Bytes(source + 16, DefaultSecretUInt64_4, DefaultSecretUInt64_5, seed);
+ hash += Mix16Bytes(source + length - 32, DefaultSecretUInt64_6, DefaultSecretUInt64_7, seed);
+ goto case 0;
+ case 0:
+ hash += Mix16Bytes(source, DefaultSecretUInt64_0, DefaultSecretUInt64_1, seed);
+ hash += Mix16Bytes(source + length - 16, DefaultSecretUInt64_2, DefaultSecretUInt64_3, seed);
+ break;
+ }
+
+ return FastAvalanche(hash);
+ }
+
+ private static ulong HashLength129To240(byte* source, uint length, ulong seed)
+ {
+ Debug.Assert(length >= 129 && length <= 240);
+
+ ulong hash = length * Prime64_1;
+
+ hash += Mix16Bytes(source + (16 * 0), DefaultSecretUInt64_0, DefaultSecretUInt64_1, seed);
+ hash += Mix16Bytes(source + (16 * 1), DefaultSecretUInt64_2, DefaultSecretUInt64_3, seed);
+ hash += Mix16Bytes(source + (16 * 2), DefaultSecretUInt64_4, DefaultSecretUInt64_5, seed);
+ hash += Mix16Bytes(source + (16 * 3), DefaultSecretUInt64_6, DefaultSecretUInt64_7, seed);
+ hash += Mix16Bytes(source + (16 * 4), DefaultSecretUInt64_8, DefaultSecretUInt64_9, seed);
+ hash += Mix16Bytes(source + (16 * 5), DefaultSecretUInt64_10, DefaultSecretUInt64_11, seed);
+ hash += Mix16Bytes(source + (16 * 6), DefaultSecretUInt64_12, DefaultSecretUInt64_13, seed);
+ hash += Mix16Bytes(source + (16 * 7), DefaultSecretUInt64_14, DefaultSecretUInt64_15, seed);
+
+ hash = FastAvalanche(hash);
+
+ switch ((length - (16 * 8)) / 16)
+ {
+ default: // case 7
+ Debug.Assert((length - 16 * 8) / 16 == 7);
+ hash += Mix16Bytes(source + (16 * 14), DefaultSecret3UInt64_12, DefaultSecret3UInt64_13, seed);
+ goto case 6;
+ case 6:
+ hash += Mix16Bytes(source + (16 * 13), DefaultSecret3UInt64_10, DefaultSecret3UInt64_11, seed);
+ goto case 5;
+ case 5:
+ hash += Mix16Bytes(source + (16 * 12), DefaultSecret3UInt64_8, DefaultSecret3UInt64_9, seed);
+ goto case 4;
+ case 4:
+ hash += Mix16Bytes(source + (16 * 11), DefaultSecret3UInt64_6, DefaultSecret3UInt64_7, seed);
+ goto case 3;
+ case 3:
+ hash += Mix16Bytes(source + (16 * 10), DefaultSecret3UInt64_4, DefaultSecret3UInt64_5, seed);
+ goto case 2;
+ case 2:
+ hash += Mix16Bytes(source + (16 * 9), DefaultSecret3UInt64_2, DefaultSecret3UInt64_3, seed);
+ goto case 1;
+ case 1:
+ hash += Mix16Bytes(source + (16 * 8), DefaultSecret3UInt64_0, DefaultSecret3UInt64_1, seed);
+ goto case 0;
+ case 0:
+ hash += Mix16Bytes(source + length - 16, 0x7378D9C97E9FC831, 0xEBD33483ACC5EA64, seed); // DefaultSecret[119], DefaultSecret[127]
+ break;
+ }
+
+ return FastAvalanche(hash);
+ }
+
+ private static ulong HashLengthOver240(byte* source, uint length, ulong seed)
+ {
+ Debug.Assert(length > 240);
+
+ fixed (byte* defaultSecret = &MemoryMarshal.GetReference(DefaultSecret))
+ {
+ byte* secret = defaultSecret;
+ if (seed != 0)
+ {
+ byte* customSecret = stackalloc byte[SecretLengthBytes];
+ DeriveSecretFromSeed(customSecret, seed);
+ secret = customSecret;
+ }
+
+ ulong* accumulators = stackalloc ulong[AccumulatorCount];
+ InitializeAccumulators(accumulators);
+
+ HashInternalLoop(accumulators, source, length, secret);
+
+ return MergeAccumulators(accumulators, secret + 11, length * Prime64_1);
+ }
+ }
+ }
+}
diff --git a/src/libraries/System.IO.Hashing/src/System/IO/Hashing/XxHashShared.cs b/src/libraries/Common/src/System/IO/Hashing/XxHashShared.cs
similarity index 98%
rename from src/libraries/System.IO.Hashing/src/System/IO/Hashing/XxHashShared.cs
rename to src/libraries/Common/src/System/IO/Hashing/XxHashShared.cs
index cafa80dbbf112e..1d9ad2cc155112 100644
--- a/src/libraries/System.IO.Hashing/src/System/IO/Hashing/XxHashShared.cs
+++ b/src/libraries/Common/src/System/IO/Hashing/XxHashShared.cs
@@ -14,7 +14,9 @@
namespace System.IO.Hashing
{
+#if !SYSTEM_PRIVATE_CORELIB
/// Shared implementation of the XXH3 hash algorithm for 64-bit in and version.
+#endif
#if NET
[SkipLocalsInit]
#endif
@@ -426,7 +428,7 @@ public static ulong MergeAccumulators(ulong* accumulators, byte* secret, ulong s
result64 += Multiply64To128ThenFold(accumulators[4] ^ ReadUInt64LE(secret + 32), accumulators[5] ^ ReadUInt64LE(secret + 40));
result64 += Multiply64To128ThenFold(accumulators[6] ^ ReadUInt64LE(secret + 48), accumulators[7] ^ ReadUInt64LE(secret + 56));
- return Avalanche(result64);
+ return FastAvalanche(result64);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -439,9 +441,20 @@ public static ulong Mix16Bytes(byte* source, ulong secretLow, ulong secretHigh,
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ulong Multiply32To64(uint v1, uint v2) => (ulong)v1 * v2;
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal static ulong Avalanche(ulong hash)
+ {
+ hash ^= hash >> 33;
+ hash *= Prime64_2;
+ hash ^= hash >> 29;
+ hash *= Prime64_3;
+ hash ^= hash >> 32;
+ return hash;
+ }
+
/// "This is a fast avalanche stage, suitable when input bits are already partially mixed."
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static ulong Avalanche(ulong hash)
+ public static ulong FastAvalanche(ulong hash)
{
hash = XorShift(hash, 37);
hash *= 0x165667919E3779F9;
diff --git a/src/libraries/System.IO.Hashing/src/System.IO.Hashing.csproj b/src/libraries/System.IO.Hashing/src/System.IO.Hashing.csproj
index d41f622de28498..6739eaab0d1c7f 100644
--- a/src/libraries/System.IO.Hashing/src/System.IO.Hashing.csproj
+++ b/src/libraries/System.IO.Hashing/src/System.IO.Hashing.csproj
@@ -19,12 +19,17 @@ System.IO.Hashing.XxHash32
+
+ Common\System\IO\Hashing\XxHash3.Common.cs
+
-
+
+ Common\System\IO\Hashing\XxHashShared.cs
+
diff --git a/src/libraries/System.IO.Hashing/src/System/IO/Hashing/XxHash128.cs b/src/libraries/System.IO.Hashing/src/System/IO/Hashing/XxHash128.cs
index 23ff9fce02a671..dad458ff48874c 100644
--- a/src/libraries/System.IO.Hashing/src/System/IO/Hashing/XxHash128.cs
+++ b/src/libraries/System.IO.Hashing/src/System/IO/Hashing/XxHash128.cs
@@ -246,7 +246,7 @@ private static Hash128 HashLength0To16(byte* source, uint length, ulong seed)
const ulong BitFlipL = DefaultSecretUInt64_8 ^ DefaultSecretUInt64_9;
const ulong BitFlipH = DefaultSecretUInt64_10 ^ DefaultSecretUInt64_11;
- return new Hash128(XxHash64.Avalanche(seed ^ BitFlipL), XxHash64.Avalanche(seed ^ BitFlipH));
+ return new Hash128(Avalanche(seed ^ BitFlipL), Avalanche(seed ^ BitFlipH));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -271,7 +271,7 @@ private static Hash128 HashLength1To3(byte* source, uint length, ulong seed)
ulong keyedLo = combinedl ^ bitflipl;
ulong keyedHi = combinedh ^ bitfliph;
- return new Hash128(XxHash64.Avalanche(keyedLo), XxHash64.Avalanche(keyedHi));
+ return new Hash128(Avalanche(keyedLo), Avalanche(keyedHi));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -295,7 +295,7 @@ private static Hash128 HashLength4To8(byte* source, uint length, ulong seed)
m128Low = XorShift(m128Low, 35);
m128Low *= 0x9FB21C651E98DF25UL;
m128Low = XorShift(m128Low, 28);
- m128High = Avalanche(m128High);
+ m128High = FastAvalanche(m128High);
return new Hash128(m128Low, m128High);
}
@@ -322,8 +322,8 @@ private static Hash128 HashLength9To16(byte* source, uint length, ulong seed)
ulong h128High = Multiply64To128(m128Low, Prime64_2, out ulong h128Low);
h128High += m128High * (ulong)Prime64_2;
- h128Low = Avalanche(h128Low);
- h128High = Avalanche(h128High);
+ h128Low = FastAvalanche(h128Low);
+ h128High = FastAvalanche(h128High);
return new Hash128(h128Low, h128High);
}
@@ -365,8 +365,8 @@ private static Hash128 HashLength129To240(byte* source, uint length, ulong seed)
Mix32Bytes(ref accLow, ref accHigh, source + (32 * 2), source + (32 * 2) + 16, DefaultSecretUInt64_8, DefaultSecretUInt64_9, DefaultSecretUInt64_10, DefaultSecretUInt64_11, seed);
Mix32Bytes(ref accLow, ref accHigh, source + (32 * 3), source + (32 * 3) + 16, DefaultSecretUInt64_12, DefaultSecretUInt64_13, DefaultSecretUInt64_14, DefaultSecretUInt64_15, seed);
- accLow = Avalanche(accLow);
- accHigh = Avalanche(accHigh);
+ accLow = FastAvalanche(accLow);
+ accHigh = FastAvalanche(accHigh);
uint bound = ((length - (32 * 4)) / 32);
if (bound != 0)
@@ -418,8 +418,8 @@ private static Hash128 AvalancheHash(ulong accLow, ulong accHigh, uint length, u
ulong h128High = (accLow * Prime64_1)
+ (accHigh * Prime64_4)
+ ((length - seed) * Prime64_2);
- h128Low = Avalanche(h128Low);
- h128High = 0ul - Avalanche(h128High);
+ h128Low = FastAvalanche(h128Low);
+ h128High = 0ul - FastAvalanche(h128High);
return new Hash128(h128Low, h128High);
}
diff --git a/src/libraries/System.IO.Hashing/src/System/IO/Hashing/XxHash3.cs b/src/libraries/System.IO.Hashing/src/System/IO/Hashing/XxHash3.cs
index c7985efc0e66b7..07e591a845df29 100644
--- a/src/libraries/System.IO.Hashing/src/System/IO/Hashing/XxHash3.cs
+++ b/src/libraries/System.IO.Hashing/src/System/IO/Hashing/XxHash3.cs
@@ -16,10 +16,7 @@ namespace System.IO.Hashing
/// For methods that persist the computed numerical hash value as bytes,
/// the value is written in the Big Endian byte order.
///
-#if NET
- [SkipLocalsInit]
-#endif
- public sealed unsafe class XxHash3 : NonCryptographicHashAlgorithm
+ public sealed unsafe partial class XxHash3 : NonCryptographicHashAlgorithm
{
/// XXH3 produces 8-byte hashes.
private new const int HashLengthInBytes = 8;
@@ -119,35 +116,6 @@ public static bool TryHash(ReadOnlySpan source, Span destination, ou
return false;
}
- /// Computes the XXH3 hash of the provided data.
- /// The data to hash.
- /// The seed value for this hash computation.
- /// The computed XXH3 hash.
- [CLSCompliant(false)]
- public static ulong HashToUInt64(ReadOnlySpan source, long seed = 0)
- {
- uint length = (uint)source.Length;
- fixed (byte* sourcePtr = &MemoryMarshal.GetReference(source))
- {
- if (length <= 16)
- {
- return HashLength0To16(sourcePtr, length, (ulong)seed);
- }
-
- if (length <= 128)
- {
- return HashLength17To128(sourcePtr, length, (ulong)seed);
- }
-
- if (length <= MidSizeMaxBytes)
- {
- return HashLength129To240(sourcePtr, length, (ulong)seed);
- }
-
- return HashLengthOver240(sourcePtr, length, (ulong)seed);
- }
- }
-
/// Resets the hash computation to the initial state.
public override void Reset()
{
@@ -198,182 +166,5 @@ public ulong GetCurrentHashAsUInt64()
return current;
}
-
- private static ulong HashLength0To16(byte* source, uint length, ulong seed)
- {
- if (length > 8)
- {
- return HashLength9To16(source, length, seed);
- }
-
- if (length >= 4)
- {
- return HashLength4To8(source, length, seed);
- }
-
- if (length != 0)
- {
- return HashLength1To3(source, length, seed);
- }
-
- const ulong SecretXor = DefaultSecretUInt64_7 ^ DefaultSecretUInt64_8;
- return XxHash64.Avalanche(seed ^ SecretXor);
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static ulong HashLength1To3(byte* source, uint length, ulong seed)
- {
- Debug.Assert(length >= 1 && length <= 3);
-
- // When source.Length == 1, c1 == source[0], c2 == source[0], c3 == source[0]
- // When source.Length == 2, c1 == source[0], c2 == source[1], c3 == source[1]
- // When source.Length == 3, c1 == source[0], c2 == source[1], c3 == source[2]
- byte c1 = *source;
- byte c2 = source[length >> 1];
- byte c3 = source[length - 1];
-
- uint combined = ((uint)c1 << 16) | ((uint)c2 << 24) | c3 | (length << 8);
-
- const uint SecretXor = unchecked((uint)DefaultSecretUInt64_0) ^ (uint)(DefaultSecretUInt64_0 >> 32);
- return XxHash64.Avalanche(combined ^ (SecretXor + seed));
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static ulong HashLength4To8(byte* source, uint length, ulong seed)
- {
- Debug.Assert(length >= 4 && length <= 8);
-
- seed ^= (ulong)BinaryPrimitives.ReverseEndianness((uint)seed) << 32;
-
- uint inputLow = ReadUInt32LE(source);
- uint inputHigh = ReadUInt32LE(source + length - sizeof(uint));
-
- const ulong SecretXor = DefaultSecretUInt64_1 ^ DefaultSecretUInt64_2;
- ulong bitflip = SecretXor - seed;
- ulong input64 = inputHigh + (((ulong)inputLow) << 32);
-
- return Rrmxmx(input64 ^ bitflip, length);
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static ulong HashLength9To16(byte* source, uint length, ulong seed)
- {
- Debug.Assert(length >= 9 && length <= 16);
-
- const ulong SecretXorL = DefaultSecretUInt64_3 ^ DefaultSecretUInt64_4;
- const ulong SecretXorR = DefaultSecretUInt64_5 ^ DefaultSecretUInt64_6;
- ulong bitflipLow = SecretXorL + seed;
- ulong bitflipHigh = SecretXorR - seed;
-
- ulong inputLow = ReadUInt64LE(source) ^ bitflipLow;
- ulong inputHigh = ReadUInt64LE(source + length - sizeof(ulong)) ^ bitflipHigh;
-
- return Avalanche(
- length +
- BinaryPrimitives.ReverseEndianness(inputLow) +
- inputHigh +
- Multiply64To128ThenFold(inputLow, inputHigh));
- }
-
- private static ulong HashLength17To128(byte* source, uint length, ulong seed)
- {
- Debug.Assert(length >= 17 && length <= 128);
-
- ulong hash = length * Prime64_1;
-
- switch ((length - 1) / 32)
- {
- default: // case 3
- hash += Mix16Bytes(source + 48, DefaultSecretUInt64_12, DefaultSecretUInt64_13, seed);
- hash += Mix16Bytes(source + length - 64, DefaultSecretUInt64_14, DefaultSecretUInt64_15, seed);
- goto case 2;
- case 2:
- hash += Mix16Bytes(source + 32, DefaultSecretUInt64_8, DefaultSecretUInt64_9, seed);
- hash += Mix16Bytes(source + length - 48, DefaultSecretUInt64_10, DefaultSecretUInt64_11, seed);
- goto case 1;
- case 1:
- hash += Mix16Bytes(source + 16, DefaultSecretUInt64_4, DefaultSecretUInt64_5, seed);
- hash += Mix16Bytes(source + length - 32, DefaultSecretUInt64_6, DefaultSecretUInt64_7, seed);
- goto case 0;
- case 0:
- hash += Mix16Bytes(source, DefaultSecretUInt64_0, DefaultSecretUInt64_1, seed);
- hash += Mix16Bytes(source + length - 16, DefaultSecretUInt64_2, DefaultSecretUInt64_3, seed);
- break;
- }
-
- return Avalanche(hash);
- }
-
- private static ulong HashLength129To240(byte* source, uint length, ulong seed)
- {
- Debug.Assert(length >= 129 && length <= 240);
-
- ulong hash = length * Prime64_1;
-
- hash += Mix16Bytes(source + (16 * 0), DefaultSecretUInt64_0, DefaultSecretUInt64_1, seed);
- hash += Mix16Bytes(source + (16 * 1), DefaultSecretUInt64_2, DefaultSecretUInt64_3, seed);
- hash += Mix16Bytes(source + (16 * 2), DefaultSecretUInt64_4, DefaultSecretUInt64_5, seed);
- hash += Mix16Bytes(source + (16 * 3), DefaultSecretUInt64_6, DefaultSecretUInt64_7, seed);
- hash += Mix16Bytes(source + (16 * 4), DefaultSecretUInt64_8, DefaultSecretUInt64_9, seed);
- hash += Mix16Bytes(source + (16 * 5), DefaultSecretUInt64_10, DefaultSecretUInt64_11, seed);
- hash += Mix16Bytes(source + (16 * 6), DefaultSecretUInt64_12, DefaultSecretUInt64_13, seed);
- hash += Mix16Bytes(source + (16 * 7), DefaultSecretUInt64_14, DefaultSecretUInt64_15, seed);
-
- hash = Avalanche(hash);
-
- switch ((length - (16 * 8)) / 16)
- {
- default: // case 7
- Debug.Assert((length - 16 * 8) / 16 == 7);
- hash += Mix16Bytes(source + (16 * 14), DefaultSecret3UInt64_12, DefaultSecret3UInt64_13, seed);
- goto case 6;
- case 6:
- hash += Mix16Bytes(source + (16 * 13), DefaultSecret3UInt64_10, DefaultSecret3UInt64_11, seed);
- goto case 5;
- case 5:
- hash += Mix16Bytes(source + (16 * 12), DefaultSecret3UInt64_8, DefaultSecret3UInt64_9, seed);
- goto case 4;
- case 4:
- hash += Mix16Bytes(source + (16 * 11), DefaultSecret3UInt64_6, DefaultSecret3UInt64_7, seed);
- goto case 3;
- case 3:
- hash += Mix16Bytes(source + (16 * 10), DefaultSecret3UInt64_4, DefaultSecret3UInt64_5, seed);
- goto case 2;
- case 2:
- hash += Mix16Bytes(source + (16 * 9), DefaultSecret3UInt64_2, DefaultSecret3UInt64_3, seed);
- goto case 1;
- case 1:
- hash += Mix16Bytes(source + (16 * 8), DefaultSecret3UInt64_0, DefaultSecret3UInt64_1, seed);
- goto case 0;
- case 0:
- hash += Mix16Bytes(source + length - 16, 0x7378D9C97E9FC831, 0xEBD33483ACC5EA64, seed); // DefaultSecret[119], DefaultSecret[127]
- break;
- }
-
- return Avalanche(hash);
- }
-
- private static ulong HashLengthOver240(byte* source, uint length, ulong seed)
- {
- Debug.Assert(length > 240);
-
- fixed (byte* defaultSecret = &MemoryMarshal.GetReference(DefaultSecret))
- {
- byte* secret = defaultSecret;
- if (seed != 0)
- {
- byte* customSecret = stackalloc byte[SecretLengthBytes];
- DeriveSecretFromSeed(customSecret, seed);
- secret = customSecret;
- }
-
- ulong* accumulators = stackalloc ulong[AccumulatorCount];
- InitializeAccumulators(accumulators);
-
- HashInternalLoop(accumulators, source, length, secret);
-
- return MergeAccumulators(accumulators, secret + 11, length * Prime64_1);
- }
- }
}
}
diff --git a/src/libraries/System.IO.Hashing/src/System/IO/Hashing/XxHash64.State.cs b/src/libraries/System.IO.Hashing/src/System/IO/Hashing/XxHash64.State.cs
index 9462928e1bbc1c..cf6c6d2e9a275c 100644
--- a/src/libraries/System.IO.Hashing/src/System/IO/Hashing/XxHash64.State.cs
+++ b/src/libraries/System.IO.Hashing/src/System/IO/Hashing/XxHash64.State.cs
@@ -14,17 +14,6 @@ namespace System.IO.Hashing
{
public sealed partial class XxHash64
{
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- internal static ulong Avalanche(ulong hash)
- {
- hash ^= hash >> 33;
- hash *= Prime64_2;
- hash ^= hash >> 29;
- hash *= Prime64_3;
- hash ^= hash >> 32;
- return hash;
- }
-
private struct State
{
private ulong _acc1;
diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems
index cea0562929595a..d4eee49d31b81a 100644
--- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems
+++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems
@@ -1531,6 +1531,12 @@
Common\System\Threading\Tasks\TaskToAsyncResult.cs
+
+ Common\System\IO\Hashing\XxHash3.Common.cs
+
+
+ Common\System\IO\Hashing\XxHashShared.cs
+
diff --git a/src/libraries/System.Private.CoreLib/src/System/String.Comparison.cs b/src/libraries/System.Private.CoreLib/src/System/String.Comparison.cs
index f9261106b34c73..a101373d18f9ca 100644
--- a/src/libraries/System.Private.CoreLib/src/System/String.Comparison.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/String.Comparison.cs
@@ -827,36 +827,23 @@ internal static int GetHashCodeOrdinalIgnoreCase(ReadOnlySpan value)
// restructure the comparison so that for odd-length spans, we simulate the null terminator and include
// it in the hash computation exactly as does str.GetNonRandomizedHashCode().
- internal unsafe int GetNonRandomizedHashCode()
+ internal int GetNonRandomizedHashCode()
{
- fixed (char* src = &_firstChar)
- {
- Debug.Assert(src[Length] == '\0', "src[Length] == '\\0'");
- Debug.Assert(((int)src) % 4 == 0, "Managed string should start at 4 bytes boundary");
-
- uint hash1 = (5381 << 16) + 5381;
- uint hash2 = hash1;
-
- uint* ptr = (uint*)src;
- int length = Length;
-
- while (length > 2)
- {
- length -= 4;
- hash1 = (BitOperations.RotateLeft(hash1, 5) + hash1) ^ ptr[0];
- hash2 = (BitOperations.RotateLeft(hash2, 5) + hash2) ^ ptr[1];
- ptr += 2;
- }
-
- if (length > 0)
- {
- hash2 = (BitOperations.RotateLeft(hash2, 5) + hash2) ^ ptr[0];
- }
+ return GetNonRandomizedHashCode(new ReadOnlySpan(ref _firstChar, _stringLength));
+ }
- return (int)(hash1 + (hash2 * 1566083941));
+ // Use XxHash3 on platforms that accelerate it, such as AMD64 and ARM64.
+#if !MONO && (TARGET_AMD64 || TARGET_ARM64)
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ internal static unsafe int GetNonRandomizedHashCode(ReadOnlySpan span)
+ {
+ fixed (char* src = &MemoryMarshal.GetReference(span))
+ {
+ uint byteLength = (uint)span.Length * 2; // never overflows
+ return System.IO.Hashing.XxHash3.NonRandomizedHashToInt32((byte*)src, byteLength);
}
}
-
+#else
internal static unsafe int GetNonRandomizedHashCode(ReadOnlySpan span)
{
uint hash1 = (5381 << 16) + 5381;
@@ -913,6 +900,7 @@ internal static unsafe int GetNonRandomizedHashCode(ReadOnlySpan span)
return (int)(hash1 + (hash2 * 1_566_083_941));
}
+#endif
// We "normalize to lowercase" every char by ORing with 0x0020. This casts
// a very wide net because it will change, e.g., '^' to '~'. But that should