cache_payload_reader.cpp 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. #include "cache_payload_reader.h"
  2. #include <algorithm>
  3. #include <limits>
  4. #include "../records/codec.h"
  5. #include "../records/validation.h"
  6. namespace sunrise::state::build_data::cache::read {
  7. namespace {
  8. /**
  9. * Adds one record array to the running cache size without unsigned overflow.
  10. * @param count Number of records.
  11. * @param stride Packed record size in bytes.
  12. * @param size Running file size.
  13. * @return True when the multiply and the add both fit.
  14. */
  15. [[nodiscard]] bool
  16. add_records(std::size_t count, std::size_t stride, std::uint64_t& size) noexcept {
  17. /** Cache offsets use the full unsigned 64-bit Windows file-size range. */
  18. constexpr std::uint64_t kMaximum = (std::numeric_limits<std::uint64_t>::max)();
  19. if (count > kMaximum / stride) {
  20. return false;
  21. }
  22. const std::uint64_t bytes = count * stride;
  23. if (bytes > kMaximum - size) {
  24. return false;
  25. }
  26. size += bytes;
  27. return true;
  28. }
  29. /**
  30. * Reads and decodes one record array, and extends the shared checksum.
  31. * @tparam Record Packed disk record type.
  32. * @tparam Value Runtime row type the codec overload picks.
  33. * @param file Open sequential cache handle.
  34. * @param output Span of rows to fill.
  35. * @param checksum Running payload checksum.
  36. * @return True when every packed row is complete and in its standard form.
  37. */
  38. template <typename Record, typename Value>
  39. [[nodiscard]] bool
  40. read_domain(HANDLE file, std::span<Value> output, std::uint64_t& checksum) noexcept {
  41. for (Value& value : output) {
  42. Record record{};
  43. if (!read_value(file, record) || !records::decode(record, value)) {
  44. return false;
  45. }
  46. checksum = records::checksum_value(checksum, record);
  47. }
  48. return true;
  49. }
  50. } // namespace
  51. /** Clears every output span so a failed read cannot expose partial records. */
  52. void clear(records::MutableDomains output) noexcept {
  53. if (output.constants != nullptr) {
  54. *output.constants = {};
  55. }
  56. std::fill(output.named.begin(), output.named.end(), content::Definition{});
  57. std::fill(output.items.begin(), output.items.end(), items::Definition{});
  58. std::fill(output.collectibles.begin(), output.collectibles.end(), collectibles::Definition{});
  59. std::fill(output.materialRequirementSets.begin(),
  60. output.materialRequirementSets.end(),
  61. material_requirements::Definition{});
  62. std::fill(output.itemDetails.begin(), output.itemDetails.end(), items::details::Definition{});
  63. std::fill(
  64. output.socketPlugRules.begin(), output.socketPlugRules.end(), items::socket_plugs::Rule{});
  65. std::fill(
  66. output.socketPlugPools.begin(), output.socketPlugPools.end(), items::socket_plugs::Pool{});
  67. std::fill(output.socketPlugMembers.begin(),
  68. output.socketPlugMembers.end(),
  69. items::socket_plugs::Member{});
  70. std::fill(output.inventoryBuckets.begin(),
  71. output.inventoryBuckets.end(),
  72. inventory::buckets::Descriptor{});
  73. std::fill(output.socketEntryLists.begin(),
  74. output.socketEntryLists.end(),
  75. socket_entry_lists::Definition{});
  76. std::fill(output.socketEntryTables.begin(),
  77. output.socketEntryTables.end(),
  78. socket_entry_lists::EntryTable{});
  79. std::fill(output.abilityBuckets.begin(), output.abilityBuckets.end(), abilities::Definition{});
  80. std::fill(output.progressions.begin(), output.progressions.end(), progressions::Definition{});
  81. std::fill(output.scenarios.begin(), output.scenarios.end(), scenarios::Definition{});
  82. std::fill(output.rosterGroups.begin(), output.rosterGroups.end(), scenarios::RosterGroup{});
  83. std::fill(output.spawnStems.begin(), output.spawnStems.end(), spawn_sets::Stem{});
  84. std::fill(output.spawnNameHashes.begin(), output.spawnNameHashes.end(), spawn_sets::NameHash{});
  85. std::fill(output.hashNames.begin(), output.hashNames.end(), hash_names::Name{});
  86. }
  87. /** Computes the exact file size for every record array. */
  88. bool expected_size(const records::DomainCounts& counts, std::uint64_t& size) noexcept {
  89. size = sizeof(records::Header);
  90. return add_records(counts.named, sizeof(records::NamedRecord), size)
  91. && add_records(counts.items, sizeof(records::ItemRecord), size)
  92. && add_records(counts.collectibles, sizeof(records::CollectibleRecord), size)
  93. && add_records(
  94. counts.materialRequirementSets, sizeof(records::MaterialRequirementSetRecord), size)
  95. && add_records(counts.itemDetails, sizeof(records::ItemDetailRecord), size)
  96. && add_records(counts.socketPlugRules, sizeof(records::SocketPlugRuleRecord), size)
  97. && add_records(counts.socketPlugPools, sizeof(records::SocketPlugPoolRecord), size)
  98. && add_records(counts.socketPlugMembers, sizeof(records::SocketPlugMemberRecord), size)
  99. && add_records(counts.inventoryBuckets, sizeof(records::InventoryBucketRecord), size)
  100. && add_records(counts.socketEntryLists, sizeof(records::SocketEntryListRecord), size)
  101. && add_records(counts.socketEntryTables, sizeof(records::SocketEntryTableRecord), size)
  102. && add_records(counts.abilityBuckets, sizeof(records::AbilityBucketRecord), size)
  103. && add_records(counts.progressions, sizeof(records::ProgressionRecord), size)
  104. && add_records(counts.scenarios, sizeof(records::ScenarioRecord), size)
  105. && add_records(counts.rosterGroups, sizeof(records::RosterGroupRecord), size)
  106. && add_records(counts.spawnStems, sizeof(records::SpawnStemRecord), size)
  107. && add_records(counts.spawnNameHashes, sizeof(records::SpawnNameHashRecord), size)
  108. && add_records(counts.hashNames, sizeof(records::HashNameRecord), size);
  109. }
  110. /** Reads every payload array and checks the decoded domains as one transaction. */
  111. bool read_payload(HANDLE file,
  112. const records::InvestmentConstants& constants,
  113. const records::DomainCounts& counts,
  114. records::MutableDomains output,
  115. std::uint64_t& checksum) noexcept {
  116. checksum = records::checksum_value(records::kChecksumOffsetBasis, constants);
  117. bool valid =
  118. read_domain<records::NamedRecord>(file, output.named.first(counts.named), checksum);
  119. valid =
  120. valid && read_domain<records::ItemRecord>(file, output.items.first(counts.items), checksum);
  121. valid = valid
  122. && read_domain<records::CollectibleRecord>(
  123. file, output.collectibles.first(counts.collectibles), checksum);
  124. valid =
  125. valid
  126. && read_domain<records::MaterialRequirementSetRecord>(
  127. file, output.materialRequirementSets.first(counts.materialRequirementSets), checksum);
  128. valid = valid
  129. && read_domain<records::ItemDetailRecord>(
  130. file, output.itemDetails.first(counts.itemDetails), checksum);
  131. valid = valid
  132. && read_domain<records::SocketPlugRuleRecord>(
  133. file, output.socketPlugRules.first(counts.socketPlugRules), checksum);
  134. valid = valid
  135. && read_domain<records::SocketPlugPoolRecord>(
  136. file, output.socketPlugPools.first(counts.socketPlugPools), checksum);
  137. valid = valid
  138. && read_domain<records::SocketPlugMemberRecord>(
  139. file, output.socketPlugMembers.first(counts.socketPlugMembers), checksum);
  140. valid = valid
  141. && read_domain<records::InventoryBucketRecord>(
  142. file, output.inventoryBuckets.first(counts.inventoryBuckets), checksum);
  143. valid = valid
  144. && read_domain<records::SocketEntryListRecord>(
  145. file, output.socketEntryLists.first(counts.socketEntryLists), checksum);
  146. valid = valid
  147. && read_domain<records::SocketEntryTableRecord>(
  148. file, output.socketEntryTables.first(counts.socketEntryTables), checksum);
  149. valid = valid
  150. && read_domain<records::AbilityBucketRecord>(
  151. file, output.abilityBuckets.first(counts.abilityBuckets), checksum);
  152. valid = valid
  153. && read_domain<records::ProgressionRecord>(
  154. file, output.progressions.first(counts.progressions), checksum);
  155. valid = valid
  156. && read_domain<records::ScenarioRecord>(
  157. file, output.scenarios.first(counts.scenarios), checksum);
  158. valid = valid
  159. && read_domain<records::RosterGroupRecord>(
  160. file, output.rosterGroups.first(counts.rosterGroups), checksum);
  161. valid = valid
  162. && read_domain<records::SpawnStemRecord>(
  163. file, output.spawnStems.first(counts.spawnStems), checksum);
  164. valid = valid
  165. && read_domain<records::SpawnNameHashRecord>(
  166. file, output.spawnNameHashes.first(counts.spawnNameHashes), checksum);
  167. valid = valid
  168. && read_domain<records::HashNameRecord>(
  169. file, output.hashNames.first(counts.hashNames), checksum);
  170. if (!valid) {
  171. return false;
  172. }
  173. return records::valid_domains({
  174. constants,
  175. output.named.first(counts.named),
  176. output.items.first(counts.items),
  177. output.collectibles.first(counts.collectibles),
  178. output.materialRequirementSets.first(counts.materialRequirementSets),
  179. output.itemDetails.first(counts.itemDetails),
  180. output.socketPlugRules.first(counts.socketPlugRules),
  181. output.socketPlugPools.first(counts.socketPlugPools),
  182. output.socketPlugMembers.first(counts.socketPlugMembers),
  183. output.inventoryBuckets.first(counts.inventoryBuckets),
  184. output.socketEntryLists.first(counts.socketEntryLists),
  185. output.socketEntryTables.first(counts.socketEntryTables),
  186. output.abilityBuckets.first(counts.abilityBuckets),
  187. output.progressions.first(counts.progressions),
  188. output.scenarios.first(counts.scenarios),
  189. output.rosterGroups.first(counts.rosterGroups),
  190. output.spawnStems.first(counts.spawnStems),
  191. output.spawnNameHashes.first(counts.spawnNameHashes),
  192. output.hashNames.first(counts.hashNames),
  193. });
  194. }
  195. } // namespace sunrise::state::build_data::cache::read