XxHash3
Provides an implementation of the XXH3 hash algorithm for generating a 64-bit hash.
using System.Buffers.Binary;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace System.IO.Hashing
{
public sealed class XxHash3 : NonCryptographicHashAlgorithm
{
private new const int HashLengthInBytes = 8;
private XxHashShared.State _state;
public XxHash3()
: this(0)
{
}
public XxHash3(long seed)
: base(8)
{
XxHashShared.Initialize(ref _state, (ulong)seed);
}
[System.Runtime.CompilerServices.NullableContext(1)]
public static byte[] Hash(byte[] source)
{
return Hash(source, 0);
}
[System.Runtime.CompilerServices.NullableContext(1)]
public static byte[] Hash(byte[] source, long seed)
{
if (source == null)
throw new ArgumentNullException("source");
return Hash(new ReadOnlySpan<byte>(source), seed);
}
[return: System.Runtime.CompilerServices.Nullable(1)]
public static byte[] Hash(ReadOnlySpan<byte> source, long seed = 0)
{
byte[] obj = new byte[8];
BinaryPrimitives.WriteUInt64BigEndian(value: HashToUInt64(source, seed), destination: obj);
return obj;
}
public static int Hash(ReadOnlySpan<byte> source, Span<byte> destination, long seed = 0)
{
if (!TryHash(source, destination, out int bytesWritten, seed))
NonCryptographicHashAlgorithm.ThrowDestinationTooShort();
return bytesWritten;
}
public static bool TryHash(ReadOnlySpan<byte> source, Span<byte> destination, out int bytesWritten, long seed = 0)
{
if (destination.Length >= 8) {
ulong value = HashToUInt64(source, seed);
if (BitConverter.IsLittleEndian)
value = BinaryPrimitives.ReverseEndianness(value);
Unsafe.WriteUnaligned(ref MemoryMarshal.GetReference(destination), value);
bytesWritten = 8;
return true;
}
bytesWritten = 0;
return false;
}
[CLSCompliant(false)]
public unsafe static ulong HashToUInt64(ReadOnlySpan<byte> source, long seed = 0)
{
uint length = (uint)source.Length;
fixed (byte* source2 = &MemoryMarshal.GetReference(source)) {
if (length <= 16)
return HashLength0To16(source2, length, (ulong)seed);
if (length <= 128)
return HashLength17To128(source2, length, (ulong)seed);
if (length <= 240)
return HashLength129To240(source2, length, (ulong)seed);
return HashLengthOver240(source2, length, (ulong)seed);
}
}
public override void Reset()
{
XxHashShared.Reset(ref _state);
}
public override void Append(ReadOnlySpan<byte> source)
{
XxHashShared.Append(ref _state, source);
}
protected override void GetCurrentHashCore(Span<byte> destination)
{
ulong currentHashAsUInt = GetCurrentHashAsUInt64();
BinaryPrimitives.WriteUInt64BigEndian(destination, currentHashAsUInt);
}
[CLSCompliant(false)]
public unsafe ulong GetCurrentHashAsUInt64()
{
if (_state.TotalLength <= 240) {
fixed (byte* pointer = _state.Buffer) {
return HashToUInt64(new ReadOnlySpan<byte>(pointer, (int)_state.TotalLength), (long)_state.Seed);
}
}
ulong* accumulators = stackalloc ulong[8];
XxHashShared.CopyAccumulators(ref _state, accumulators);
fixed (byte* ptr = _state.Secret) {
XxHashShared.DigestLong(ref _state, accumulators, ptr);
return XxHashShared.MergeAccumulators(accumulators, ptr + 11, (ulong)((long)_state.TotalLength * -7046029288634856825));
}
}
private unsafe 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);
return XxHash64.Avalanche((ulong)((long)seed ^ -8707998980786479652));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private unsafe static ulong HashLength1To3(byte* source, uint length, ulong seed)
{
byte num = *source;
byte b = source[length >> 1];
byte b2 = source[length - 1];
return XxHash64.Avalanche((uint)((num << 16) | (b << 24) | b2 | (int)(length << 8)) ^ (2267503259 + seed));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private unsafe static ulong HashLength4To8(byte* source, uint length, ulong seed)
{
seed ^= (ulong)BinaryPrimitives.ReverseEndianness((uint)seed) << 32;
uint num = XxHashShared.ReadUInt32LE(source);
uint num2 = XxHashShared.ReadUInt32LE(source + length - 4);
ulong num3 = (ulong)(-4090762196417718878 - (long)seed);
return XxHashShared.Rrmxmx((num2 + ((ulong)num << 32)) ^ num3, length);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private unsafe static ulong HashLength9To16(byte* source, uint length, ulong seed)
{
ulong num = 7458650908927343033 + seed;
ulong num2 = (ulong)(-5812251307325107654 - (long)seed);
ulong num3 = XxHashShared.ReadUInt64LE(source) ^ num;
ulong num4 = XxHashShared.ReadUInt64LE(source + length - 8) ^ num2;
return XxHashShared.Avalanche(length + BinaryPrimitives.ReverseEndianness(num3) + num4 + XxHashShared.Multiply64To128ThenFold(num3, num4));
}
private unsafe static ulong HashLength17To128(byte* source, uint length, ulong seed)
{
ulong num = (ulong)(length * -7046029288634856825);
switch ((length - 1) / 32) {
default:
num += XxHashShared.Mix16Bytes(source + 48, 4554437623014685352, 2111919702937427193, seed);
num += XxHashShared.Mix16Bytes(source + length - 64, 3556072174620004746, 7238261902898274248, seed);
goto case 2;
case 2:
num += XxHashShared.Mix16Bytes(source + 32, 14627906620379768892, 11758427054878871688, seed);
num += XxHashShared.Mix16Bytes(source + length - 48, 5690594596133299313, 15613098826807580984, seed);
goto case 1;
case 1:
num += XxHashShared.Mix16Bytes(source + 16, 8711581037947681227, 2410270004345854594, seed);
num += XxHashShared.Mix16Bytes(source + length - 32, 10242386182634080440, 5487137525590930912, seed);
break;
case 0:
break;
}
num += XxHashShared.Mix16Bytes(source, 13712233961653862072, 2066345149520216444, seed);
num += XxHashShared.Mix16Bytes(source + length - 16, 15823274712020931806, 2262974939099578482, seed);
return XxHashShared.Avalanche(num);
}
private unsafe static ulong HashLength129To240(byte* source, uint length, ulong seed)
{
ulong num = (ulong)(length * -7046029288634856825);
num += XxHashShared.Mix16Bytes(source, 13712233961653862072, 2066345149520216444, seed);
num += XxHashShared.Mix16Bytes(source + 16, 15823274712020931806, 2262974939099578482, seed);
num += XxHashShared.Mix16Bytes(source + 32, 8711581037947681227, 2410270004345854594, seed);
num += XxHashShared.Mix16Bytes(source + 48, 10242386182634080440, 5487137525590930912, seed);
num += XxHashShared.Mix16Bytes(source + 64, 14627906620379768892, 11758427054878871688, seed);
num += XxHashShared.Mix16Bytes(source + 80, 5690594596133299313, 15613098826807580984, seed);
num += XxHashShared.Mix16Bytes(source + 96, 4554437623014685352, 2111919702937427193, seed);
num += XxHashShared.Mix16Bytes(source + 112, 3556072174620004746, 7238261902898274248, seed);
num = XxHashShared.Avalanche(num);
switch ((length - 128) / 16) {
default:
num += XxHashShared.Mix16Bytes(source + 224, 13536968629829821247, 16163852396094277575, seed);
goto case 6;
case 6:
num += XxHashShared.Mix16Bytes(source + 208, 17228863761319568023, 8573350489219836230, seed);
goto case 5;
case 5:
num += XxHashShared.Mix16Bytes(source + 192, 7336514198459093435, 5216419214072683403, seed);
goto case 4;
case 4:
num += XxHashShared.Mix16Bytes(source + 176, 10391458616325699444, 5920048007935066598, seed);
goto case 3;
case 3:
num += XxHashShared.Mix16Bytes(source + 160, 15013455763555273806, 5046485836271438973, seed);
goto case 2;
case 2:
num += XxHashShared.Mix16Bytes(source + 144, 11835586108195898345, 16607528436649670564, seed);
goto case 1;
case 1:
num += XxHashShared.Mix16Bytes(source + 128, 9295848262624092985, 7914194659941938988, seed);
break;
case 0:
break;
}
num += XxHashShared.Mix16Bytes(source + length - 16, 8320639771003045937, 16992983559143025252, seed);
return XxHashShared.Avalanche(num);
}
private unsafe static ulong HashLengthOver240(byte* source, uint length, ulong seed)
{
fixed (byte* ptr = &MemoryMarshal.GetReference(XxHashShared.DefaultSecret)) {
byte* ptr2 = ptr;
if (seed != 0) {
byte* intPtr = stackalloc byte[192];
XxHashShared.DeriveSecretFromSeed(intPtr, seed);
ptr2 = intPtr;
}
byte* accumulators = stackalloc byte[64];
XxHashShared.InitializeAccumulators((ulong*)accumulators);
XxHashShared.HashInternalLoop((ulong*)accumulators, source, length, ptr2);
return XxHashShared.MergeAccumulators((ulong*)accumulators, ptr2 + 11, (ulong)(length * -7046029288634856825));
}
}
}
}