HashState.cs
  1  using System;
  2  using System.Runtime.InteropServices;
  3  
  4  namespace Ryujinx.Graphics.Gpu.Shader.HashTable
  5  {
  6      /// <summary>
  7      /// State of a hash calculation.
  8      /// </summary>
  9      struct HashState
 10      {
 11          // This is using a slightly modified implementation of FastHash64.
 12          // Reference: https://github.com/ztanml/fast-hash/blob/master/fasthash.c
 13          private const ulong M = 0x880355f21e6d1965UL;
 14          private ulong _hash;
 15          private int _start;
 16  
 17          /// <summary>
 18          /// One shot hash calculation for a given data.
 19          /// </summary>
 20          /// <param name="data">Data to be hashed</param>
 21          /// <returns>Hash of the given data</returns>
 22          public static uint CalcHash(ReadOnlySpan<byte> data)
 23          {
 24              HashState state = new();
 25  
 26              state.Initialize();
 27              state.Continue(data);
 28              return state.Finalize(data);
 29          }
 30  
 31          /// <summary>
 32          /// Initializes the hash state.
 33          /// </summary>
 34          public void Initialize()
 35          {
 36              _hash = 23;
 37          }
 38  
 39          /// <summary>
 40          /// Calculates the hash of the given data.
 41          /// </summary>
 42          /// <remarks>
 43          /// The full data must be passed on <paramref name="data"/>.
 44          /// If this is not the first time the method is called, then <paramref name="data"/> must start with the data passed on the last call.
 45          /// If a smaller slice of the data was already hashed before, only the additional data will be hashed.
 46          /// This can be used for additive hashing of data in chuncks.
 47          /// </remarks>
 48          /// <param name="data">Data to be hashed</param>
 49          public void Continue(ReadOnlySpan<byte> data)
 50          {
 51              ulong h = _hash;
 52  
 53              ReadOnlySpan<ulong> dataAsUlong = MemoryMarshal.Cast<byte, ulong>(data[_start..]);
 54  
 55              for (int i = 0; i < dataAsUlong.Length; i++)
 56              {
 57                  ulong value = dataAsUlong[i];
 58  
 59                  h ^= Mix(value);
 60                  h *= M;
 61              }
 62  
 63              _hash = h;
 64              _start = data.Length & ~7;
 65          }
 66  
 67          /// <summary>
 68          /// Performs the hash finalization step, and returns the calculated hash.
 69          /// </summary>
 70          /// <remarks>
 71          /// The full data must be passed on <paramref name="data"/>.
 72          /// <paramref name="data"/> must start with the data passed on the last call to <see cref="Continue"/>.
 73          /// No internal state is changed, so one can still continue hashing data with <see cref="Continue"/>
 74          /// after calling this method.
 75          /// </remarks>
 76          /// <param name="data">Data to be hashed</param>
 77          /// <returns>Hash of all the data hashed with this <see cref="HashState"/></returns>
 78          public readonly uint Finalize(ReadOnlySpan<byte> data)
 79          {
 80              ulong h = _hash;
 81  
 82              int remainder = data.Length & 7;
 83              if (remainder != 0)
 84              {
 85                  ulong v = 0;
 86  
 87                  for (int i = data.Length - remainder; i < data.Length; i++)
 88                  {
 89                      v |= (ulong)data[i] << ((i - remainder) * 8);
 90                  }
 91  
 92                  h ^= Mix(v);
 93                  h *= M;
 94              }
 95  
 96              h = Mix(h);
 97              return (uint)(h - (h >> 32));
 98          }
 99  
100          /// <summary>
101          /// Hash mix function.
102          /// </summary>
103          /// <param name="h">Hash to mix</param>
104          /// <returns>Mixed hash</returns>
105          private static ulong Mix(ulong h)
106          {
107              h ^= h >> 23;
108              h *= 0x2127599bf4325c37UL;
109              h ^= h >> 47;
110              return h;
111          }
112      }
113  }