Ryujinx/Ryujinx.Graphics.Gpu/Shader/HashTable/HashState.cs

114 lines
3.6 KiB
C#

using System;
using System.Runtime.InteropServices;
namespace Ryujinx.Graphics.Gpu.Shader.HashTable
{
/// <summary>
/// State of a hash calculation.
/// </summary>
struct HashState
{
// This is using a slightly modified implementation of FastHash64.
// Reference: https://github.com/ztanml/fast-hash/blob/master/fasthash.c
private const ulong M = 0x880355f21e6d1965UL;
private ulong _hash;
private int _start;
/// <summary>
/// One shot hash calculation for a given data.
/// </summary>
/// <param name="data">Data to be hashed</param>
/// <returns>Hash of the given data</returns>
public static uint CalcHash(ReadOnlySpan<byte> data)
{
HashState state = new HashState();
state.Initialize();
state.Continue(data);
return state.Finalize(data);
}
/// <summary>
/// Initializes the hash state.
/// </summary>
public void Initialize()
{
_hash = 23;
}
/// <summary>
/// Calculates the hash of the given data.
/// </summary>
/// <remarks>
/// The full data must be passed on <paramref name="data"/>.
/// 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.
/// If a smaller slice of the data was already hashed before, only the additional data will be hashed.
/// This can be used for additive hashing of data in chuncks.
/// </remarks>
/// <param name="data">Data to be hashed</param>
public void Continue(ReadOnlySpan<byte> data)
{
ulong h = _hash;
ReadOnlySpan<ulong> dataAsUlong = MemoryMarshal.Cast<byte, ulong>(data.Slice(_start));
for (int i = 0; i < dataAsUlong.Length; i++)
{
ulong value = dataAsUlong[i];
h ^= Mix(value);
h *= M;
}
_hash = h;
_start = data.Length & ~7;
}
/// <summary>
/// Performs the hash finalization step, and returns the calculated hash.
/// </summary>
/// <remarks>
/// The full data must be passed on <paramref name="data"/>.
/// <paramref name="data"/> must start with the data passed on the last call to <see cref="Continue"/>.
/// No internal state is changed, so one can still continue hashing data with <see cref="Continue"/>
/// after calling this method.
/// </remarks>
/// <param name="data">Data to be hashed</param>
/// <returns>Hash of all the data hashed with this <see cref="HashState"/></returns>
public uint Finalize(ReadOnlySpan<byte> data)
{
ulong h = _hash;
int remainder = data.Length & 7;
if (remainder != 0)
{
ulong v = 0;
for (int i = data.Length - remainder; i < data.Length; i++)
{
v |= (ulong)data[i] << ((i - remainder) * 8);
}
h ^= Mix(v);
h *= M;
}
h = Mix(h);
return (uint)(h - (h >> 32));
}
/// <summary>
/// Hash mix function.
/// </summary>
/// <param name="h">Hash to mix</param>
/// <returns>Mixed hash</returns>
private static ulong Mix(ulong h)
{
h ^= h >> 23;
h *= 0x2127599bf4325c37UL;
h ^= h >> 47;
return h;
}
}
}