SmartDataAccessor.cs 3.1 KB

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