File: System\IO\Hashing\XxHash32.State.cs
Web Access
Project: src\src\libraries\System.IO.Hashing\src\System.IO.Hashing.csproj (System.IO.Hashing)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Buffers.Binary;
using System.Diagnostics;
using System.Numerics;
using System.Runtime.CompilerServices;
using static System.IO.Hashing.XxHashShared;
 
// Implemented from the specification at
// https://github.com/Cyan4973/xxHash/blob/f9155bd4c57e2270a4ffbb176485e5d713de1c9b/doc/xxhash_spec.md
 
namespace System.IO.Hashing
{
    public sealed partial class XxHash32
    {
        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 + unchecked(Prime32_1 + Prime32_2);
                _acc2 = seed + Prime32_2;
                _acc3 = seed;
                _acc4 = seed - Prime32_1;
 
                _smallAcc = seed + Prime32_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(uint)));
                _acc3 = ApplyRound(_acc3, source.Slice(2 * sizeof(uint)));
                _acc4 = ApplyRound(_acc4, source.Slice(3 * sizeof(uint)));
 
                _hadFullStripe = true;
            }
 
            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            private readonly uint Converge()
            {
                return
                    BitOperations.RotateLeft(_acc1, 1) +
                    BitOperations.RotateLeft(_acc2, 7) +
                    BitOperations.RotateLeft(_acc3, 12) +
                    BitOperations.RotateLeft(_acc4, 18);
            }
 
            private static uint ApplyRound(uint acc, ReadOnlySpan<byte> lane)
            {
                acc += BinaryPrimitives.ReadUInt32LittleEndian(lane) * Prime32_2;
                acc = BitOperations.RotateLeft(acc, 13);
                acc *= Prime32_1;
 
                return acc;
            }
 
            internal readonly uint Complete(int length, ReadOnlySpan<byte> remaining)
            {
                uint acc = _hadFullStripe ? Converge() : _smallAcc;
 
                acc += (uint)length;
 
                while (remaining.Length >= sizeof(uint))
                {
                    uint lane = BinaryPrimitives.ReadUInt32LittleEndian(remaining);
                    acc += lane * Prime32_3;
                    acc = BitOperations.RotateLeft(acc, 17);
                    acc *= Prime32_4;
 
                    remaining = remaining.Slice(sizeof(uint));
                }
 
                for (int i = 0; i < remaining.Length; i++)
                {
                    uint lane = remaining[i];
                    acc += lane * Prime32_5;
                    acc = BitOperations.RotateLeft(acc, 11);
                    acc *= Prime32_1;
                }
 
                acc ^= (acc >> 15);
                acc *= Prime32_2;
                acc ^= (acc >> 13);
                acc *= Prime32_3;
                acc ^= (acc >> 16);
 
                return acc;
            }
        }
    }
}