roster_intersection.h 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. #pragma once
  2. #include <array>
  3. #include <cstddef>
  4. #include <cstdint>
  5. #include <span>
  6. #include "scenario_reader.h"
  7. namespace sunrise::middleware::content::packages::tables {
  8. /**
  9. * A destination reaches at most this many slice sets.
  10. * Slice-set indices are spaced by the slice-set factor across a 512-wide region space, so this
  11. * is the hard bound. The widest installed destination reaches 47.
  12. */
  13. inline constexpr std::size_t kSliceSetCapacity = 64;
  14. /**
  15. * Roster keys tracked for one destination.
  16. * No installed destination reaches more than 2 objects carrying a wire slot type. This leaves
  17. * room to spare without a heap allocation.
  18. */
  19. inline constexpr std::size_t kRosterKeyCapacity = 16;
  20. static_assert(kSliceSetCapacity * kSliceSetIndexFactor == 512);
  21. // One bit per slice set, so the mask must cover the whole capacity.
  22. static_assert(kSliceSetCapacity == 64);
  23. /**
  24. * Which slice sets each candidate key appears in.
  25. * A key is safe to send only when it appears in every slice set of the destination. The client
  26. * dereferences a miss unchecked, so a key missing from one crashes on the switch out of it.
  27. */
  28. struct RosterIntersection {
  29. std::array<std::uint32_t, kRosterKeyCapacity> keys{};
  30. std::array<std::uint64_t, kRosterKeyCapacity> masks{};
  31. std::size_t keyCount{};
  32. /** Every slice set observed, whichever key carried it. */
  33. std::uint64_t observedSets{};
  34. /** Set when a key or a slice set did not fit, which makes the result unusable. */
  35. bool overflowed{};
  36. /** Set when a state's slice set could not be read, which makes every key unsafe. */
  37. bool unresolvedSet{};
  38. };
  39. /**
  40. * Records a slice set whose entry could not be read.
  41. * The destination still moves into that slice set and no key can be proved present in it, so no
  42. * key of this destination is safe to send afterwards.
  43. * @param state Accumulator for one destination.
  44. */
  45. void observe_unresolved_slice_set(RosterIntersection& state) noexcept;
  46. /**
  47. * The slot types that make an object worth publishing as a roster group.
  48. * Only 56 installed objects declare any of them, and the key limit above holds only for that
  49. * filtered set. Feeding every placed object instead overflows most destinations.
  50. */
  51. inline constexpr std::array<std::uint16_t, 9> kRosterSlotTypes = {
  52. 8, 13, 16, 17, 21, 35, 37, 41, 67};
  53. /**
  54. * Tests whether one placed object is a roster candidate.
  55. * @param object Whole placed-object bytes.
  56. * @return True when it declares a slot of one of the wire types.
  57. */
  58. [[nodiscard]] bool carries_roster_slot(std::span<const std::byte> object) noexcept;
  59. /**
  60. * Records a slice set the destination reaches, whether or not it holds a roster object.
  61. * The intersection is over every slice set of the destination, so a slice set that holds no
  62. * candidate still has to count. Leaving it out makes an unsafe key look safe.
  63. * @param state Accumulator for one destination.
  64. * @param sliceSetIndex Slice-set index as the entry reports it, already scaled by the factor.
  65. * @return True when the index is inside the region space.
  66. */
  67. [[nodiscard]] bool observe_slice_set(RosterIntersection& state,
  68. std::uint32_t sliceSetIndex) noexcept;
  69. /**
  70. * Records that one key appears in one slice set.
  71. * @param state Accumulator for one destination.
  72. * @param sliceSetIndex Slice-set index as the entry reports it, already scaled by the factor.
  73. * @param objectKey Registry key of the placed object.
  74. * @return True when the observation was recorded.
  75. */
  76. [[nodiscard]] bool observe_roster_key(RosterIntersection& state,
  77. std::uint32_t sliceSetIndex,
  78. std::uint32_t objectKey) noexcept;
  79. /**
  80. * Reports the keys present in every observed slice set.
  81. * @param state Accumulator for one destination.
  82. * @param output Receives the safe keys.
  83. * @param count Receives how many were written.
  84. * @return True when nothing overflowed and every safe key fits the output.
  85. */
  86. [[nodiscard]] bool safe_roster_keys(const RosterIntersection& state,
  87. std::span<std::uint32_t> output,
  88. std::size_t& count) noexcept;
  89. /**
  90. * Reports the keys present in some observed slice sets and not all, with the bubbles holding them.
  91. * These belong in a per-bubble sub-block: the top-level list would make the teardown sweep deref a
  92. * key the current slice set cannot find. One recorded bit is one bubble.
  93. * @param state Accumulator for one destination.
  94. * @param keys Receives the partially present keys.
  95. * @param masks Receives each key's bubbles, one bit per bubble index, in the same order.
  96. * @param count Receives how many were written.
  97. * @return True when nothing overflowed and every partial key fits the output.
  98. */
  99. [[nodiscard]] bool partial_roster_keys(const RosterIntersection& state,
  100. std::span<std::uint32_t> keys,
  101. std::span<std::uint64_t> masks,
  102. std::size_t& count) noexcept;
  103. } // namespace sunrise::middleware::content::packages::tables