using System; using System.Collections.Generic; namespace Ryujinx.Graphics.Gpu.Shader.HashTable { /// /// Smart data accessor that can cache data and hashes to avoid reading and re-hashing the same memory regions. /// ref struct SmartDataAccessor { private readonly IDataAccessor _dataAccessor; private ReadOnlySpan _data; private readonly SortedList _cachedHashes; /// /// Creates a new smart data accessor. /// /// Data accessor public SmartDataAccessor(IDataAccessor dataAccessor) { _dataAccessor = dataAccessor; _data = ReadOnlySpan.Empty; _cachedHashes = new SortedList(); } /// /// Get a spans of a given size. /// /// /// The actual length of the span returned depends on the /// and might be less than requested. /// /// Size in bytes /// Span with the requested size public ReadOnlySpan GetSpan(int length) { if (_data.Length < length) { _data = _dataAccessor.GetSpan(0, length); } else if (_data.Length > length) { return _data.Slice(0, length); } return _data; } /// /// Gets a span of the requested size, and a hash of its data. /// /// Length of the span /// Hash of the span data /// Span of data public ReadOnlySpan GetSpanAndHash(int length, out uint hash) { ReadOnlySpan data = GetSpan(length); hash = data.Length == length ? CalcHashCached(data) : 0; return data; } /// /// 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. /// /// Data to be hashed /// Hash of the data private uint CalcHashCached(ReadOnlySpan 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); } } }