package_locator_cache.cpp 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. #include <array>
  2. #include <cstddef>
  3. #include "locator_cache.h"
  4. namespace sunrise::middleware::content::packages::reader::locator_cache {
  5. namespace {
  6. /**
  7. * Entries for every installed package. 528 were measured.
  8. * One pass reads from hundreds of them. At 16 entries a package change missed, and a miss
  9. * re-read all 2,202 installed file names.
  10. */
  11. constexpr std::size_t kCapacity = 1024;
  12. /** Standard 64-bit FNV-1a offset basis starts the directory key. */
  13. constexpr std::uint64_t kHashBasis = 14695981039346656037ULL;
  14. /** Standard 64-bit FNV-1a prime mixes each directory character. */
  15. constexpr std::uint64_t kHashPrime = 1099511628211ULL;
  16. /** One remembered lookup. Package paths carry no protected data. */
  17. struct Entry {
  18. std::uint64_t directoryHash{};
  19. std::uint32_t patchIndex{};
  20. std::uint16_t packageId{};
  21. bool occupied{};
  22. Path stem{};
  23. };
  24. SRWLOCK g_lock{SRWLOCK_INIT};
  25. std::array<Entry, kCapacity> g_entries{};
  26. /** Next entry to replace, so a full cache evicts in insertion order. */
  27. std::size_t g_cursor = 0;
  28. /** Directory whose package files have all been recorded, or zero. */
  29. std::uint64_t g_completeDirectory = 0;
  30. } // namespace
  31. /** @param value Directory text. @return Its case-sensitive key. */
  32. std::uint64_t directory_hash(std::wstring_view value) noexcept {
  33. std::uint64_t hash = kHashBasis;
  34. for (const wchar_t character : value) {
  35. hash ^= static_cast<std::uint64_t>(character);
  36. hash *= kHashPrime;
  37. }
  38. return hash;
  39. }
  40. /** Reads one remembered lookup. */
  41. bool find(std::uint64_t directoryHash,
  42. std::uint16_t packageId,
  43. Path& stem,
  44. std::uint32_t& patchIndex) noexcept {
  45. bool found = false;
  46. AcquireSRWLockShared(&g_lock);
  47. for (const Entry& entry : g_entries) {
  48. if (entry.occupied && entry.packageId == packageId
  49. && entry.directoryHash == directoryHash) {
  50. stem = entry.stem;
  51. patchIndex = entry.patchIndex;
  52. found = true;
  53. break;
  54. }
  55. }
  56. ReleaseSRWLockShared(&g_lock);
  57. return found;
  58. }
  59. /** Remembers one lookup, keeping the highest patch of each package. */
  60. void store(std::uint64_t directoryHash,
  61. std::uint16_t packageId,
  62. const Path& stem,
  63. std::uint32_t patchIndex) noexcept {
  64. AcquireSRWLockExclusive(&g_lock);
  65. std::size_t slot = g_entries.size();
  66. for (std::size_t index = 0; index < g_entries.size() && slot == g_entries.size(); ++index) {
  67. if (!g_entries[index].occupied) {
  68. slot = index;
  69. break;
  70. }
  71. if (g_entries[index].packageId == packageId
  72. && g_entries[index].directoryHash == directoryHash) {
  73. // A package is recorded once at its newest patch, whatever order its files arrive in.
  74. if (g_entries[index].patchIndex >= patchIndex) {
  75. ReleaseSRWLockExclusive(&g_lock);
  76. return;
  77. }
  78. slot = index;
  79. }
  80. }
  81. if (slot == g_entries.size()) {
  82. slot = g_cursor;
  83. g_cursor = (g_cursor + 1U) % g_entries.size();
  84. }
  85. g_entries[slot] = Entry{directoryHash, patchIndex, packageId, true, stem};
  86. ReleaseSRWLockExclusive(&g_lock);
  87. }
  88. /** Reports whether one directory has been read in full. */
  89. bool complete(std::uint64_t directoryHash) noexcept {
  90. AcquireSRWLockShared(&g_lock);
  91. const bool read = directoryHash != 0 && g_completeDirectory == directoryHash;
  92. ReleaseSRWLockShared(&g_lock);
  93. return read;
  94. }
  95. /** @param directoryHash Key of the directory whose package files are now all recorded. */
  96. void mark_complete(std::uint64_t directoryHash) noexcept {
  97. AcquireSRWLockExclusive(&g_lock);
  98. g_completeDirectory = directoryHash;
  99. ReleaseSRWLockExclusive(&g_lock);
  100. }
  101. } // namespace sunrise::middleware::content::packages::reader::locator_cache