using System;
using System.Runtime.InteropServices;
namespace Ryujinx.Graphics.Gpu.Shader.HashTable
{
///
/// State of a hash calculation.
///
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;
///
/// One shot hash calculation for a given data.
///
/// Data to be hashed
/// Hash of the given data
public static uint CalcHash(ReadOnlySpan data)
{
HashState state = new();
state.Initialize();
state.Continue(data);
return state.Finalize(data);
}
///
/// Initializes the hash state.
///
public void Initialize()
{
_hash = 23;
}
///
/// Calculates the hash of the given data.
///
///
/// The full data must be passed on .
/// If this is not the first time the method is called, then 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.
///
/// Data to be hashed
public void Continue(ReadOnlySpan data)
{
ulong h = _hash;
ReadOnlySpan dataAsUlong = MemoryMarshal.Cast(data[_start..]);
for (int i = 0; i < dataAsUlong.Length; i++)
{
ulong value = dataAsUlong[i];
h ^= Mix(value);
h *= M;
}
_hash = h;
_start = data.Length & ~7;
}
///
/// Performs the hash finalization step, and returns the calculated hash.
///
///
/// The full data must be passed on .
/// must start with the data passed on the last call to .
/// No internal state is changed, so one can still continue hashing data with
/// after calling this method.
///
/// Data to be hashed
/// Hash of all the data hashed with this
public readonly uint Finalize(ReadOnlySpan 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));
}
///
/// Hash mix function.
///
/// Hash to mix
/// Mixed hash
private static ulong Mix(ulong h)
{
h ^= h >> 23;
h *= 0x2127599bf4325c37UL;
h ^= h >> 47;
return h;
}
}
}