forked from Mirror/Ryujinx
97 lines
3.1 KiB
C#
97 lines
3.1 KiB
C#
|
using System;
|
|||
|
using System.Collections.Generic;
|
|||
|
|
|||
|
namespace Ryujinx.Graphics.Gpu.Shader.HashTable
|
|||
|
{
|
|||
|
/// <summary>
|
|||
|
/// Smart data accessor that can cache data and hashes to avoid reading and re-hashing the same memory regions.
|
|||
|
/// </summary>
|
|||
|
ref struct SmartDataAccessor
|
|||
|
{
|
|||
|
private readonly IDataAccessor _dataAccessor;
|
|||
|
private ReadOnlySpan<byte> _data;
|
|||
|
private readonly SortedList<int, HashState> _cachedHashes;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Creates a new smart data accessor.
|
|||
|
/// </summary>
|
|||
|
/// <param name="dataAccessor">Data accessor</param>
|
|||
|
public SmartDataAccessor(IDataAccessor dataAccessor)
|
|||
|
{
|
|||
|
_dataAccessor = dataAccessor;
|
|||
|
_data = ReadOnlySpan<byte>.Empty;
|
|||
|
_cachedHashes = new SortedList<int, HashState>();
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Get a spans of a given size.
|
|||
|
/// </summary>
|
|||
|
/// <remarks>
|
|||
|
/// The actual length of the span returned depends on the <see cref="IDataAccessor"/>
|
|||
|
/// and might be less than requested.
|
|||
|
/// </remarks>
|
|||
|
/// <param name="length">Size in bytes</param>
|
|||
|
/// <returns>Span with the requested size</returns>
|
|||
|
public ReadOnlySpan<byte> GetSpan(int length)
|
|||
|
{
|
|||
|
if (_data.Length < length)
|
|||
|
{
|
|||
|
_data = _dataAccessor.GetSpan(0, length);
|
|||
|
}
|
|||
|
else if (_data.Length > length)
|
|||
|
{
|
|||
|
return _data.Slice(0, length);
|
|||
|
}
|
|||
|
|
|||
|
return _data;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Gets a span of the requested size, and a hash of its data.
|
|||
|
/// </summary>
|
|||
|
/// <param name="length">Length of the span</param>
|
|||
|
/// <param name="hash">Hash of the span data</param>
|
|||
|
/// <returns>Span of data</returns>
|
|||
|
public ReadOnlySpan<byte> GetSpanAndHash(int length, out uint hash)
|
|||
|
{
|
|||
|
ReadOnlySpan<byte> data = GetSpan(length);
|
|||
|
hash = data.Length == length ? CalcHashCached(data) : 0;
|
|||
|
return data;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Calculates the hash for a requested span.
|
|||
|
/// This will try to use a cached hash if the data was already accessed before, to avoid re-hashing.
|
|||
|
/// </summary>
|
|||
|
/// <param name="data">Data to be hashed</param>
|
|||
|
/// <returns>Hash of the data</returns>
|
|||
|
private uint CalcHashCached(ReadOnlySpan<byte> data)
|
|||
|
{
|
|||
|
HashState state = default;
|
|||
|
bool found = false;
|
|||
|
|
|||
|
for (int i = _cachedHashes.Count - 1; i >= 0; i--)
|
|||
|
{
|
|||
|
int cachedHashSize = _cachedHashes.Keys[i];
|
|||
|
|
|||
|
if (cachedHashSize < data.Length)
|
|||
|
{
|
|||
|
state = _cachedHashes.Values[i];
|
|||
|
found = true;
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (!found)
|
|||
|
{
|
|||
|
state = new HashState();
|
|||
|
state.Initialize();
|
|||
|
}
|
|||
|
|
|||
|
state.Continue(data);
|
|||
|
_cachedHashes[data.Length & ~7] = state;
|
|||
|
return state.Finalize(data);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|