<PackageReference Include="System.IO.Hashing" Version="10.0.0-rc.2.25502.107" />

XxHash32

public sealed class XxHash32 : NonCryptographicHashAlgorithm
Provides an implementation of the XxHash32 algorithm.
using System.Buffers.Binary; using System.Numerics; using System.Runtime.CompilerServices; namespace System.IO.Hashing { public sealed class XxHash32 : NonCryptographicHashAlgorithm { private struct State { private uint _acc1; private uint _acc2; private uint _acc3; private uint _acc4; private readonly uint _smallAcc; private bool _hadFullStripe; internal State(uint seed) { _acc1 = seed + 606290984; _acc2 = (uint)((int)seed + -2048144777); _acc3 = seed; _acc4 = (uint)((int)seed - -1640531535); _smallAcc = seed + 374761393; _hadFullStripe = false; } internal void ProcessStripe(ReadOnlySpan<byte> source) { source = source.Slice(0, 16); _acc1 = ApplyRound(_acc1, source); _acc2 = ApplyRound(_acc2, source.Slice(4)); _acc3 = ApplyRound(_acc3, source.Slice(8)); _acc4 = ApplyRound(_acc4, source.Slice(12)); _hadFullStripe = true; } [MethodImpl(MethodImplOptions.AggressiveInlining)] [System.Runtime.CompilerServices.IsReadOnly] private uint Converge() { return System.Numerics.BitOperations.RotateLeft(_acc1, 1) + System.Numerics.BitOperations.RotateLeft(_acc2, 7) + System.Numerics.BitOperations.RotateLeft(_acc3, 12) + System.Numerics.BitOperations.RotateLeft(_acc4, 18); } private static uint ApplyRound(uint acc, ReadOnlySpan<byte> lane) { acc = (uint)((int)acc + (int)BinaryPrimitives.ReadUInt32LittleEndian(lane) * -2048144777); acc = System.Numerics.BitOperations.RotateLeft(acc, 13); acc = (uint)((int)acc * -1640531535); return acc; } [System.Runtime.CompilerServices.IsReadOnly] internal uint Complete(int length, ReadOnlySpan<byte> remaining) { uint num = _hadFullStripe ? Converge() : _smallAcc; num = (uint)((int)num + length); while (remaining.Length >= 4) { uint num2 = BinaryPrimitives.ReadUInt32LittleEndian(remaining); num = (uint)((int)num + (int)num2 * -1028477379); num = System.Numerics.BitOperations.RotateLeft(num, 17); num *= 668265263; remaining = remaining.Slice(4); } foreach (uint num3 in remaining) { num += num3 * 374761393; num = System.Numerics.BitOperations.RotateLeft(num, 11); num = (uint)((int)num * -1640531535); } num ^= num >> 15; num = (uint)((int)num * -2048144777); num ^= num >> 13; num = (uint)((int)num * -1028477379); return num ^ (num >> 16); } } private const int HashSize = 4; private const int StripeSize = 16; private readonly uint _seed; private State _state; private byte[] _holdback; private int _length; public XxHash32() : this(0) { } public XxHash32(int seed) : base(4) { _seed = (uint)seed; Reset(); } private XxHash32(uint seed, State state, byte[] holdback, int length) : base(4) { _seed = seed; _state = state; if ((length & 15) > 0) _holdback = (byte[])holdback?.Clone(); _length = length; } [System.Runtime.CompilerServices.NullableContext(1)] public XxHash32 Clone() { return new XxHash32(_seed, _state, _holdback, _length); } public override void Reset() { _state = new State(_seed); _length = 0; } public override void Append(ReadOnlySpan<byte> source) { int num = _length & 15; if (num != 0) { int num2 = 16 - num; if (source.Length < num2) { source.CopyTo(_holdback.AsSpan(num)); _length += source.Length; return; } source.Slice(0, num2).CopyTo(_holdback.AsSpan(num)); _state.ProcessStripe(_holdback); source = source.Slice(num2); _length += num2; } while (source.Length >= 16) { _state.ProcessStripe(source); source = source.Slice(16); _length += 16; } if (source.Length > 0) { if (_holdback == null) _holdback = new byte[16]; source.CopyTo(_holdback); _length += source.Length; } } protected override void GetCurrentHashCore(Span<byte> destination) { uint currentHashAsUInt = GetCurrentHashAsUInt32(); BinaryPrimitives.WriteUInt32BigEndian(destination, currentHashAsUInt); } [CLSCompliant(false)] public uint GetCurrentHashAsUInt32() { int num = _length & 15; ReadOnlySpan<byte> remaining = ReadOnlySpan<byte>.Empty; if (num > 0) remaining = new ReadOnlySpan<byte>(_holdback, 0, num); return _state.Complete(_length, remaining); } [System.Runtime.CompilerServices.NullableContext(1)] public static byte[] Hash(byte[] source) { ExceptionPolyfills.ThrowIfNull(source, "source"); return Hash(new ReadOnlySpan<byte>(source), 0); } [System.Runtime.CompilerServices.NullableContext(1)] public static byte[] Hash(byte[] source, int seed) { ExceptionPolyfills.ThrowIfNull(source, "source"); return Hash(new ReadOnlySpan<byte>(source), seed); } [return: System.Runtime.CompilerServices.Nullable(1)] public static byte[] Hash(ReadOnlySpan<byte> source, int seed = 0) { byte[] obj = new byte[4]; BinaryPrimitives.WriteUInt32BigEndian(value: HashToUInt32(source, seed), destination: obj); return obj; } public static bool TryHash(ReadOnlySpan<byte> source, Span<byte> destination, out int bytesWritten, int seed = 0) { if (destination.Length < 4) { bytesWritten = 0; return false; } uint value = HashToUInt32(source, seed); BinaryPrimitives.WriteUInt32BigEndian(destination, value); bytesWritten = 4; return true; } public static int Hash(ReadOnlySpan<byte> source, Span<byte> destination, int seed = 0) { if (destination.Length < 4) NonCryptographicHashAlgorithm.ThrowDestinationTooShort(); uint value = HashToUInt32(source, seed); BinaryPrimitives.WriteUInt32BigEndian(destination, value); return 4; } [CLSCompliant(false)] public static uint HashToUInt32(ReadOnlySpan<byte> source, int seed = 0) { int length = source.Length; State state = new State((uint)seed); while (source.Length >= 16) { state.ProcessStripe(source); source = source.Slice(16); } return state.Complete(length, source); } } }