activity_sdk_dialogue_group_index.cpp 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. #include "activity_sdk_dialogue_group_index.h"
  2. #include <algorithm>
  3. #include <cstring>
  4. #include <limits>
  5. namespace sunrise::client::content::activity::sdk_generation::dialogue_group_index {
  6. namespace {
  7. // One dialogue group row is 16 bytes in the blob.
  8. constexpr std::size_t kGroupStride = 16U;
  9. template <typename Value>
  10. [[nodiscard]] bool
  11. read_value(std::span<const std::byte> bytes, std::size_t offset, Value& output) noexcept {
  12. output = {};
  13. if (offset > bytes.size() || sizeof output > bytes.size() - offset) {
  14. return false;
  15. }
  16. std::memcpy(&output, bytes.data() + offset, sizeof output);
  17. return true;
  18. }
  19. /** Applies one signed blob-relative offset. @return False when the result leaves the blob. */
  20. [[nodiscard]] bool
  21. add_relative(std::size_t member, std::int64_t relative, std::size_t& target) noexcept {
  22. if (relative >= 0) {
  23. const auto distance = static_cast<std::uint64_t>(relative);
  24. if (distance > (std::numeric_limits<std::size_t>::max)() - member) {
  25. return false;
  26. }
  27. target = member + static_cast<std::size_t>(distance);
  28. return true;
  29. }
  30. const auto distance = static_cast<std::uint64_t>(-(relative + 1)) + 1U;
  31. if (distance > member) {
  32. return false;
  33. }
  34. target = member - static_cast<std::size_t>(distance);
  35. return true;
  36. }
  37. } // namespace
  38. /** Builds the sorted group index over one dialogue blob. @return False when it is malformed. */
  39. bool build(std::span<const std::byte> bytes,
  40. std::size_t groupRows,
  41. std::size_t groupCount,
  42. std::vector<Span>& output) noexcept {
  43. output.clear();
  44. if (groupRows > bytes.size() || groupCount > (bytes.size() - groupRows) / kGroupStride) {
  45. return false;
  46. }
  47. try {
  48. output.reserve(groupCount);
  49. for (std::size_t index = 0; index < groupCount; ++index) {
  50. const std::size_t row = groupRows + index * kGroupStride;
  51. Span group{};
  52. std::int64_t relative = 0;
  53. if (!read_value(bytes, row, group.definitionHash)
  54. || !read_value(bytes, row + 8U, relative)
  55. || !add_relative(row + 8U, relative, group.begin) || group.begin > bytes.size()) {
  56. output.clear();
  57. return false;
  58. }
  59. output.push_back(group);
  60. }
  61. for (Span& group : output) {
  62. group.end = bytes.size();
  63. for (const Span& candidate : output) {
  64. if (candidate.begin > group.begin && candidate.begin < group.end) {
  65. group.end = candidate.begin;
  66. }
  67. }
  68. }
  69. std::sort(output.begin(), output.end(), [](const Span& first, const Span& second) {
  70. return first.definitionHash < second.definitionHash;
  71. });
  72. if (std::adjacent_find(output.begin(),
  73. output.end(),
  74. [](const Span& first, const Span& second) {
  75. return first.definitionHash == second.definitionHash;
  76. })
  77. != output.end()) {
  78. output.clear();
  79. return false;
  80. }
  81. return true;
  82. } catch (...) {
  83. output.clear();
  84. return false;
  85. }
  86. }
  87. /** Finds one group by definition hash in the sorted index. */
  88. bool find(std::span<const Span> groups, std::uint32_t definitionHash, Span& output) noexcept {
  89. output = {};
  90. const auto row = std::lower_bound(
  91. groups.begin(), groups.end(), definitionHash, [](const Span& group, std::uint32_t hash) {
  92. return group.definitionHash < hash;
  93. });
  94. if (row == groups.end() || row->definitionHash != definitionHash) {
  95. return false;
  96. }
  97. output = *row;
  98. return true;
  99. }
  100. } // namespace sunrise::client::content::activity::sdk_generation::dialogue_group_index