LinearDiskCache.h 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. // Copyright 2010 Dolphin Emulator Project
  2. // SPDX-License-Identifier: GPL-2.0-or-later
  3. #pragma once
  4. #include <algorithm>
  5. #include <cstring>
  6. #include <memory>
  7. #include <string>
  8. #include <type_traits>
  9. #include "Common/CommonTypes.h"
  10. #include "Common/IOFile.h"
  11. #include "Common/Version.h"
  12. // On disk format:
  13. // header{
  14. // u32 'DCAC';
  15. // u32 version; // svn_rev
  16. // u16 sizeof(key_type);
  17. // u16 sizeof(value_type);
  18. //}
  19. // key_value_pair{
  20. // u32 value_size;
  21. // key_type key;
  22. // value_type[value_size] value;
  23. //}
  24. namespace Common
  25. {
  26. template <typename K, typename V>
  27. class LinearDiskCacheReader
  28. {
  29. public:
  30. virtual void Read(const K& key, const V* value, u32 value_size) = 0;
  31. };
  32. // Dead simple unsorted key-value store with append functionality.
  33. // No random read functionality, all reading is done in OpenAndRead.
  34. // Keys and values can contain any characters, including \0.
  35. //
  36. // Suitable for caching generated shader bytecode between executions.
  37. // Not tuned for extreme performance but should be reasonably fast.
  38. // Does not support keys or values larger than 2GB, which should be reasonable.
  39. // Keys must have non-zero length; values can have zero length.
  40. // K and V are some POD type
  41. // K : the key type
  42. // V : value array type
  43. template <typename K, typename V>
  44. class LinearDiskCache
  45. {
  46. public:
  47. // return number of read entries
  48. u32 OpenAndRead(const std::string& filename, LinearDiskCacheReader<K, V>& reader)
  49. {
  50. // Since we're reading/writing directly to the storage of K instances,
  51. // K must be trivially copyable.
  52. static_assert(std::is_trivially_copyable<K>::value, "K must be a trivially copyable type");
  53. // close any currently opened file
  54. Close();
  55. m_num_entries = 0;
  56. // try opening for reading/writing
  57. m_file.Open(filename, "r+b");
  58. const u64 file_size = m_file.GetSize();
  59. m_header.Init();
  60. if (m_file.IsOpen() && ValidateHeader())
  61. {
  62. // good header, read some key/value pairs
  63. K key;
  64. std::unique_ptr<V[]> value = nullptr;
  65. u32 value_size = 0;
  66. u32 entry_number = 0;
  67. u64 last_valid_value_start = m_file.Tell();
  68. while (m_file.ReadArray(&value_size, 1))
  69. {
  70. const u64 next_extent = m_file.Tell() + sizeof(value_size) + value_size;
  71. if (next_extent > file_size)
  72. break;
  73. // TODO: use make_unique_for_overwrite in C++20
  74. value = std::unique_ptr<V[]>(new V[value_size]);
  75. // read key/value and pass to reader
  76. if (m_file.ReadArray(&key, 1) && m_file.ReadArray(value.get(), value_size) &&
  77. m_file.ReadArray(&entry_number, 1) && entry_number == m_num_entries + 1)
  78. {
  79. last_valid_value_start = m_file.Tell();
  80. reader.Read(key, value.get(), value_size);
  81. }
  82. else
  83. {
  84. break;
  85. }
  86. m_num_entries++;
  87. }
  88. m_file.ClearError();
  89. m_file.Seek(last_valid_value_start, File::SeekOrigin::Begin);
  90. return m_num_entries;
  91. }
  92. // failed to open file for reading or bad header
  93. // close and recreate file
  94. Close();
  95. m_file.Open(filename, "wb");
  96. WriteHeader();
  97. return 0;
  98. }
  99. void Sync() { m_file.Flush(); }
  100. void Close()
  101. {
  102. if (m_file.IsOpen())
  103. m_file.Close();
  104. }
  105. // Appends a key-value pair to the store.
  106. void Append(const K& key, const V* value, u32 value_size)
  107. {
  108. // TODO: Should do a check that we don't already have "key"? (I think each caller does that
  109. // already.)
  110. m_file.WriteArray(&value_size, 1);
  111. m_file.WriteArray(&key, 1);
  112. m_file.WriteArray(value, value_size);
  113. m_num_entries++;
  114. m_file.WriteArray(&m_num_entries, 1);
  115. }
  116. private:
  117. void WriteHeader() { m_file.WriteArray(&m_header, 1); }
  118. bool ValidateHeader()
  119. {
  120. char file_header[sizeof(Header)];
  121. return (m_file.ReadArray(file_header, sizeof(Header)) &&
  122. !memcmp((const char*)&m_header, file_header, sizeof(Header)));
  123. }
  124. struct Header
  125. {
  126. void Init()
  127. {
  128. // Null-terminator is intentionally not copied.
  129. std::memcpy(&id, "DCAC", sizeof(u32));
  130. std::memcpy(ver, Common::GetScmRevGitStr().c_str(),
  131. std::min(Common::GetScmRevGitStr().size(), sizeof(ver)));
  132. }
  133. u32 id = 0;
  134. const u16 key_t_size = sizeof(K);
  135. const u16 value_t_size = sizeof(V);
  136. char ver[40] = {};
  137. } m_header;
  138. File::IOFile m_file;
  139. u32 m_num_entries = 0;
  140. };
  141. } // namespace Common