aboutsummaryrefslogtreecommitdiff
path: root/src/Ryujinx.Graphics.Gpu/Shader/HashTable/HashState.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/Ryujinx.Graphics.Gpu/Shader/HashTable/HashState.cs')
-rw-r--r--src/Ryujinx.Graphics.Gpu/Shader/HashTable/HashState.cs113
1 files changed, 113 insertions, 0 deletions
diff --git a/src/Ryujinx.Graphics.Gpu/Shader/HashTable/HashState.cs b/src/Ryujinx.Graphics.Gpu/Shader/HashTable/HashState.cs
new file mode 100644
index 00000000..584eefdc
--- /dev/null
+++ b/src/Ryujinx.Graphics.Gpu/Shader/HashTable/HashState.cs
@@ -0,0 +1,113 @@
+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;
+ }
+ }
+}