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