| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113 |
- 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;
- }
- }
- }
|