| File: Utilities\Hashing\XxHash64.State.cs | Web Access |
| Project: src\roslyn\src\Razor\src\Shared\Microsoft.AspNetCore.Razor.Utilities.Shared\Microsoft.AspNetCore.Razor.Utilities.Shared.csproj (Microsoft.AspNetCore.Razor.Utilities.Shared) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // Copied from https://github.com/dotnet/runtime/blob/f1d463f46268382a8287d71aea929dadaa5dfef5/src/libraries/System.IO.Hashing/src/System/IO/Hashing/XxHash64.State.cs#L1C13-L1C13 // Remove once we can actually add a reference to System.IO.Hashing v8.0.0 // Implemented from the specification at // https://github.com/Cyan4973/xxHash/blob/f9155bd4c57e2270a4ffbb176485e5d713de1c9b/doc/xxhash_spec.md using System.Runtime.CompilerServices; using static System.IO.Hashing.XxHashShared; namespace System.IO.Hashing; internal 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; } #if false private struct State { private ulong _acc1; private ulong _acc2; private ulong _acc3; private ulong _acc4; private readonly ulong _smallAcc; private bool _hadFullStripe; internal State(ulong seed) { _acc1 = seed + unchecked(Prime64_1 + Prime64_2); _acc2 = seed + Prime64_2; _acc3 = seed; _acc4 = seed - Prime64_1; _smallAcc = seed + Prime64_5; _hadFullStripe = false; } internal void ProcessStripe(ReadOnlySpan<byte> source) { Debug.Assert(source.Length >= StripeSize); source = source.Slice(0, StripeSize); _acc1 = ApplyRound(_acc1, source); _acc2 = ApplyRound(_acc2, source.Slice(sizeof(ulong))); _acc3 = ApplyRound(_acc3, source.Slice(2 * sizeof(ulong))); _acc4 = ApplyRound(_acc4, source.Slice(3 * sizeof(ulong))); _hadFullStripe = true; } private static ulong MergeAccumulator(ulong acc, ulong accN) { acc ^= ApplyRound(0, accN); acc *= Prime64_1; acc += Prime64_4; return acc; } private readonly ulong Converge() { ulong acc = BitOperations.RotateLeft(_acc1, 1) + BitOperations.RotateLeft(_acc2, 7) + BitOperations.RotateLeft(_acc3, 12) + BitOperations.RotateLeft(_acc4, 18); acc = MergeAccumulator(acc, _acc1); acc = MergeAccumulator(acc, _acc2); acc = MergeAccumulator(acc, _acc3); acc = MergeAccumulator(acc, _acc4); return acc; } private static ulong ApplyRound(ulong acc, ReadOnlySpan<byte> lane) { return ApplyRound(acc, BinaryPrimitives.ReadUInt64LittleEndian(lane)); } private static ulong ApplyRound(ulong acc, ulong lane) { acc += lane * Prime64_2; acc = BitOperations.RotateLeft(acc, 31); acc *= Prime64_1; return acc; } // Inliner may decide to inline this method into HashToUInt64() with help of PGO and // can run out of "time budget" producing non-inlined simple calls such as Span.Slice. // TODO: Remove NoInlining when https://github.com/dotnet/runtime/issues/85531 is fixed. [MethodImpl(MethodImplOptions.NoInlining)] internal readonly ulong Complete(long length, ReadOnlySpan<byte> remaining) { ulong acc = _hadFullStripe ? Converge() : _smallAcc; acc += (ulong)length; while (remaining.Length >= sizeof(ulong)) { ulong lane = BinaryPrimitives.ReadUInt64LittleEndian(remaining); acc ^= ApplyRound(0, lane); acc = BitOperations.RotateLeft(acc, 27); acc *= Prime64_1; acc += Prime64_4; remaining = remaining.Slice(sizeof(ulong)); } // Doesn't need to be a while since it can occur at most once. if (remaining.Length >= sizeof(uint)) { ulong lane = BinaryPrimitives.ReadUInt32LittleEndian(remaining); acc ^= lane * Prime64_1; acc = BitOperations.RotateLeft(acc, 23); acc *= Prime64_2; acc += Prime64_3; remaining = remaining.Slice(sizeof(uint)); } for (int i = 0; i < remaining.Length; i++) { ulong lane = remaining[i]; acc ^= lane * Prime64_5; acc = BitOperations.RotateLeft(acc, 11); acc *= Prime64_1; } return Avalanche(acc); } } #endif }