answered_interactions.cpp 3.1 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485
  1. #include "answered_interactions.h"
  2. #include <array>
  3. #include <atomic>
  4. #include "../build_data/vendors/definition.h"
  5. namespace sunrise::state::vendors {
  6. // The list is indexed by the installed vendor index, so it has to span the same range the index
  7. // itself does. One that fell short would simply stop answering for the vendors past its end, and
  8. // nothing else would say so.
  9. static_assert(kVendorCapacity >= build_data::vendors::kIndexCapacity);
  10. namespace {
  11. /** Answered interactions, packed as `vendorIndex << 16 | interactionIndex`. Append only. */
  12. std::array<std::atomic<std::uint32_t>, kAnsweredCapacity> g_answered{};
  13. std::atomic<std::size_t> g_answeredCount{0};
  14. /** Interaction each vendor is showing, so an answered one can be named afterwards. */
  15. std::array<std::atomic<std::uint16_t>, kVendorCapacity> g_shown{};
  16. /** @param vendorIndex Vendor row. @param interactionIndex Interaction row. @return Packed key. */
  17. [[nodiscard]] constexpr std::uint32_t pack(std::uint16_t vendorIndex,
  18. std::uint16_t interactionIndex) noexcept {
  19. return (static_cast<std::uint32_t>(vendorIndex) << 16) | interactionIndex;
  20. }
  21. /** @param key Packed pair. @return True while the pair is answered. */
  22. [[nodiscard]] bool contains(std::uint32_t key) noexcept {
  23. const std::size_t held = g_answeredCount.load(std::memory_order_acquire);
  24. for (std::size_t slot = 0; slot < held; ++slot) {
  25. if (g_answered[slot].load(std::memory_order_relaxed) == key) {
  26. return true;
  27. }
  28. }
  29. return false;
  30. }
  31. } // namespace
  32. /** Records the interaction one vendor is showing right now. */
  33. void record_shown(std::uint16_t vendorIndex, std::uint16_t interactionIndex) noexcept {
  34. if (vendorIndex < kVendorCapacity) {
  35. g_shown[vendorIndex].store(interactionIndex, std::memory_order_relaxed);
  36. }
  37. }
  38. /** Answers whether one interaction has been answered this session. */
  39. bool is_answered(std::uint16_t vendorIndex, std::uint16_t interactionIndex) noexcept {
  40. return vendorIndex < kVendorCapacity && contains(pack(vendorIndex, interactionIndex));
  41. }
  42. /** Marks the interaction one vendor is showing right now as answered. */
  43. bool answer_shown(std::uint16_t vendorIndex) noexcept {
  44. if (vendorIndex >= kVendorCapacity) {
  45. return false;
  46. }
  47. const std::uint16_t shown = g_shown[vendorIndex].load(std::memory_order_relaxed);
  48. if (shown == kAbsentIndex) {
  49. return false;
  50. }
  51. const std::uint32_t key = pack(vendorIndex, shown);
  52. if (contains(key)) {
  53. return true;
  54. }
  55. const std::size_t slot = g_answeredCount.load(std::memory_order_relaxed);
  56. if (slot >= kAnsweredCapacity) {
  57. return false;
  58. }
  59. g_answered[slot].store(key, std::memory_order_relaxed);
  60. g_answeredCount.store(slot + 1, std::memory_order_release);
  61. return true;
  62. }
  63. /** Forgets every answer and every shown interaction. */
  64. void clear() noexcept {
  65. g_answeredCount.store(0, std::memory_order_release);
  66. for (auto& shown : g_shown) {
  67. shown.store(kAbsentIndex, std::memory_order_relaxed);
  68. }
  69. }
  70. } // namespace sunrise::state::vendors