<PackageReference Include="System.IO.Hashing" Version="10.0.0-preview.1.25080.5" />

XxHash128

Provides an implementation of the XXH128 hash algorithm for generating a 128-bit hash.
using System.Buffers.Binary; using System.Diagnostics; using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace System.IO.Hashing { public sealed class XxHash128 : NonCryptographicHashAlgorithm { [DebuggerDisplay("Low64 = {Low64}, High64 = {High64}")] private readonly struct Hash128 { public readonly ulong Low64; public readonly ulong High64; public Hash128(ulong low64, ulong high64) { Low64 = low64; High64 = high64; } } private new const int HashLengthInBytes = 16; private XxHashShared.State _state; public XxHash128() : this(0) { } public XxHash128(long seed) : base(16) { 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[] array = new byte[16]; Hash(source, array, seed); return array; } 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 >= 16) { Hash128 hash = HashToHash128(source, seed); WriteBigEndian128(ref hash, destination); bytesWritten = 16; return true; } bytesWritten = 0; return false; } private unsafe static Hash128 HashToHash128(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) { Hash128 hash = GetCurrentHashAsHash128(); WriteBigEndian128(ref hash, destination); } private unsafe Hash128 GetCurrentHashAsHash128() { if (_state.TotalLength <= 240) { fixed (byte* pointer = _state.Buffer) { return HashToHash128(new ReadOnlySpan<byte>(pointer, (int)_state.TotalLength), (long)_state.Seed); } } ulong* accumulators = stackalloc ulong[8]; XxHashShared.CopyAccumulators(ref _state, accumulators); Hash128 result; fixed (byte* ptr = _state.Secret) { XxHashShared.DigestLong(ref _state, accumulators, ptr); result = new Hash128(XxHashShared.MergeAccumulators(accumulators, ptr + 11, (ulong)((long)_state.TotalLength * -7046029288634856825)), XxHashShared.MergeAccumulators(accumulators, ptr + 192 - 64 - 11, (ulong)(~((long)_state.TotalLength * -4417276706812531889)))); } return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static void WriteBigEndian128([In] [System.Runtime.CompilerServices.IsReadOnly] ref Hash128 hash, Span<byte> destination) { ulong value = hash.Low64; ulong value2 = hash.High64; if (BitConverter.IsLittleEndian) { value = BinaryPrimitives.ReverseEndianness(value); value2 = BinaryPrimitives.ReverseEndianness(value2); } ref byte reference = ref MemoryMarshal.GetReference(destination); Unsafe.WriteUnaligned(ref reference, value2); Unsafe.WriteUnaligned(ref Unsafe.AddByteOffset(ref reference, new IntPtr(8)), value); } private unsafe static Hash128 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 new Hash128(XxHash64.Avalanche(seed ^ 7507096552062056628), XxHash64.Avalanche((ulong)((long)seed ^ -7613947547284439735))); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private unsafe static Hash128 HashLength1To3(byte* source, uint length, ulong seed) { byte num = *source; byte b = source[length >> 1]; byte b2 = source[length - 1]; int num2 = (num << 16) | (b << 24) | b2 | (int)(length << 8); uint num3 = System.Numerics.BitOperations.RotateLeft(BinaryPrimitives.ReverseEndianness((uint)num2), 13); ulong num4 = 2267503259 + seed; ulong num5 = 808198283 - seed; ulong hash = (uint)num2 ^ num4; ulong hash2 = num3 ^ num5; return new Hash128(XxHash64.Avalanche(hash), XxHash64.Avalanche(hash2)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private unsafe static Hash128 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 = num + ((ulong)num2 << 32); ulong num4 = (ulong)(-4255862940314790740 + (long)seed); ulong num5 = XxHashShared.Multiply64To128(num3 ^ num4, (ulong)(-7046029288634856825 + (length << 2)), out ulong lower); num5 += lower << 1; lower ^= num5 >> 3; lower = XxHashShared.XorShift(lower, 35); lower = (ulong)((long)lower * -6939452855193903323); lower = XxHashShared.XorShift(lower, 28); num5 = XxHashShared.Avalanche(num5); return new Hash128(lower, num5); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private unsafe static Hash128 HashLength9To16(byte* source, uint length, ulong seed) { ulong num = 6455697860950631241 - seed; ulong num2 = (ulong)(-4466874330221494952 + (long)seed); ulong num3 = XxHashShared.ReadUInt64LE(source); ulong num4 = XxHashShared.ReadUInt64LE(source + length - 8); ulong num5 = XxHashShared.Multiply64To128(num3 ^ num4 ^ num, 11400714785074694791, out ulong lower); lower += (ulong)(length - 1) << 54; num4 ^= num2; num5 = (ulong)((long)num5 + ((sizeof(void*) < 8) ? (((long)num4 & -4294967296) + (long)XxHashShared.Multiply32To64((uint)num4, 2246822519)) : ((long)(num4 + XxHashShared.Multiply32To64((uint)num4, 2246822518))))); lower ^= BinaryPrimitives.ReverseEndianness(num5); ulong num6 = XxHashShared.Multiply64To128(lower, 14029467366897019727, out ulong lower2); num6 = (ulong)((long)num6 + (long)num5 * -4417276706812531889); lower2 = XxHashShared.Avalanche(lower2); num6 = XxHashShared.Avalanche(num6); return new Hash128(lower2, num6); } private unsafe static Hash128 HashLength17To128(byte* source, uint length, ulong seed) { ulong accLow = (ulong)(length * -7046029288634856825); ulong accHigh = 0; switch ((length - 1) / 32) { default: Mix32Bytes(ref accLow, ref accHigh, source + 48, source + length - 64, 4554437623014685352, 2111919702937427193, 3556072174620004746, 7238261902898274248, seed); goto case 2; case 2: Mix32Bytes(ref accLow, ref accHigh, source + 32, source + length - 48, 14627906620379768892, 11758427054878871688, 5690594596133299313, 15613098826807580984, seed); goto case 1; case 1: Mix32Bytes(ref accLow, ref accHigh, source + 16, source + length - 32, 8711581037947681227, 2410270004345854594, 10242386182634080440, 5487137525590930912, seed); break; case 0: break; } Mix32Bytes(ref accLow, ref accHigh, source, source + length - 16, 13712233961653862072, 2066345149520216444, 15823274712020931806, 2262974939099578482, seed); return AvalancheHash(accLow, accHigh, length, seed); } private unsafe static Hash128 HashLength129To240(byte* source, uint length, ulong seed) { ulong accLow = (ulong)(length * -7046029288634856825); ulong accHigh = 0; Mix32Bytes(ref accLow, ref accHigh, source, source + 16, 13712233961653862072, 2066345149520216444, 15823274712020931806, 2262974939099578482, seed); Mix32Bytes(ref accLow, ref accHigh, source + 32, source + 32 + 16, 8711581037947681227, 2410270004345854594, 10242386182634080440, 5487137525590930912, seed); Mix32Bytes(ref accLow, ref accHigh, source + 64, source + 64 + 16, 14627906620379768892, 11758427054878871688, 5690594596133299313, 15613098826807580984, seed); Mix32Bytes(ref accLow, ref accHigh, source + 96, source + 96 + 16, 4554437623014685352, 2111919702937427193, 3556072174620004746, 7238261902898274248, seed); accLow = XxHashShared.Avalanche(accLow); accHigh = XxHashShared.Avalanche(accHigh); uint num = (length - 128) / 32; if (num != 0) { Mix32Bytes(ref accLow, ref accHigh, source + 128, source + 128 + 16, 9295848262624092985, 7914194659941938988, 11835586108195898345, 16607528436649670564, seed); if (num >= 2) { Mix32Bytes(ref accLow, ref accHigh, source + 160, source + 160 + 16, 15013455763555273806, 5046485836271438973, 10391458616325699444, 5920048007935066598, seed); if (num == 3) Mix32Bytes(ref accLow, ref accHigh, source + 192, source + 192 + 16, 7336514198459093435, 5216419214072683403, 17228863761319568023, 8573350489219836230, seed); } } Mix32Bytes(ref accLow, ref accHigh, source + length - 16, source + length - 32, 5695865814404364607, 6464017090953185821, 8320639771003045937, 16992983559143025252, 0 - seed); return AvalancheHash(accLow, accHigh, length, seed); } private unsafe static Hash128 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; } ulong* accumulators = stackalloc ulong[8]; XxHashShared.InitializeAccumulators(accumulators); XxHashShared.HashInternalLoop(accumulators, source, length, ptr2); return new Hash128(XxHashShared.MergeAccumulators(accumulators, ptr2 + 11, (ulong)(length * -7046029288634856825)), XxHashShared.MergeAccumulators(accumulators, ptr2 + 192 - 64 - 11, (ulong)(~(length * -4417276706812531889)))); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static Hash128 AvalancheHash(ulong accLow, ulong accHigh, uint length, ulong seed) { ulong hash = accLow + accHigh; ulong hash2 = (ulong)((long)accLow * -7046029288634856825 + (long)accHigh * -8796714831421723037 + (long)(length - seed) * -4417276706812531889); ulong low = XxHashShared.Avalanche(hash); hash2 = 0 - XxHashShared.Avalanche(hash2); return new Hash128(low, hash2); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private unsafe static void Mix32Bytes(ref ulong accLow, ref ulong accHigh, byte* input1, byte* input2, ulong secret1, ulong secret2, ulong secret3, ulong secret4, ulong seed) { accLow += XxHashShared.Mix16Bytes(input1, secret1, secret2, seed); accLow ^= XxHashShared.ReadUInt64LE(input2) + XxHashShared.ReadUInt64LE(input2 + 8); accHigh += XxHashShared.Mix16Bytes(input2, secret3, secret4, seed); accHigh ^= XxHashShared.ReadUInt64LE(input1) + XxHashShared.ReadUInt64LE(input1 + 8); } } }