/ src / Ryujinx.Graphics.Gpu / Shader / HashTable / SmartDataAccessor.cs
SmartDataAccessor.cs
 1  using System;
 2  using System.Collections.Generic;
 3  
 4  namespace Ryujinx.Graphics.Gpu.Shader.HashTable
 5  {
 6      /// <summary>
 7      /// Smart data accessor that can cache data and hashes to avoid reading and re-hashing the same memory regions.
 8      /// </summary>
 9      ref struct SmartDataAccessor
10      {
11          private readonly IDataAccessor _dataAccessor;
12          private ReadOnlySpan<byte> _data;
13          private readonly SortedList<int, HashState> _cachedHashes;
14  
15          /// <summary>
16          /// Creates a new smart data accessor.
17          /// </summary>
18          /// <param name="dataAccessor">Data accessor</param>
19          public SmartDataAccessor(IDataAccessor dataAccessor)
20          {
21              _dataAccessor = dataAccessor;
22              _data = ReadOnlySpan<byte>.Empty;
23              _cachedHashes = new SortedList<int, HashState>();
24          }
25  
26          /// <summary>
27          /// Get a spans of a given size.
28          /// </summary>
29          /// <remarks>
30          /// The actual length of the span returned depends on the <see cref="IDataAccessor"/>
31          /// and might be less than requested.
32          /// </remarks>
33          /// <param name="length">Size in bytes</param>
34          /// <returns>Span with the requested size</returns>
35          public ReadOnlySpan<byte> GetSpan(int length)
36          {
37              if (_data.Length < length)
38              {
39                  _data = _dataAccessor.GetSpan(0, length);
40              }
41              else if (_data.Length > length)
42              {
43                  return _data[..length];
44              }
45  
46              return _data;
47          }
48  
49          /// <summary>
50          /// Gets a span of the requested size, and a hash of its data.
51          /// </summary>
52          /// <param name="length">Length of the span</param>
53          /// <param name="hash">Hash of the span data</param>
54          /// <returns>Span of data</returns>
55          public ReadOnlySpan<byte> GetSpanAndHash(int length, out uint hash)
56          {
57              ReadOnlySpan<byte> data = GetSpan(length);
58              hash = data.Length == length ? CalcHashCached(data) : 0;
59              return data;
60          }
61  
62          /// <summary>
63          /// Calculates the hash for a requested span.
64          /// This will try to use a cached hash if the data was already accessed before, to avoid re-hashing.
65          /// </summary>
66          /// <param name="data">Data to be hashed</param>
67          /// <returns>Hash of the data</returns>
68          private readonly uint CalcHashCached(ReadOnlySpan<byte> data)
69          {
70              HashState state = default;
71              bool found = false;
72  
73              for (int i = _cachedHashes.Count - 1; i >= 0; i--)
74              {
75                  int cachedHashSize = _cachedHashes.Keys[i];
76  
77                  if (cachedHashSize < data.Length)
78                  {
79                      state = _cachedHashes.Values[i];
80                      found = true;
81                      break;
82                  }
83              }
84  
85              if (!found)
86              {
87                  state = new HashState();
88                  state.Initialize();
89              }
90  
91              state.Continue(data);
92              _cachedHashes[data.Length & ~7] = state;
93              return state.Finalize(data);
94          }
95      }
96  }