parameter_messages.cpp 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. #include "parameter_messages.h"
  2. #include <array>
  3. #include <cstddef>
  4. #include "../../encoding/bit_raw.h"
  5. namespace sunrise::middleware::gameplay::group {
  6. namespace {
  7. namespace bits = encoding::bits;
  8. /** The mode flag is one bit. */
  9. constexpr std::uint8_t kFlagWidth = 1;
  10. /** Width of a tag-reflection root bit. Clear skips the whole field walk. */
  11. constexpr std::uint8_t kRootBitClear = 1;
  12. /** Width of the `activity-host` id, written most-significant-bit first. */
  13. constexpr std::uint8_t kHostIdWidth = 64;
  14. /** Width of the `activity-host` address. */
  15. constexpr std::uint8_t kAddressWidth = 32;
  16. /** Width of the `activity-host` port. */
  17. constexpr std::uint8_t kPortWidth = 16;
  18. /** Bits in one byte. */
  19. constexpr unsigned kByteBits = 8;
  20. /** Mask of one byte. */
  21. constexpr std::uint32_t kByteMask = 0xFF;
  22. /** Parameters one presence group covers. The encoder divides 25 by this and rounds up. */
  23. constexpr std::uint8_t kGroupSize = 16;
  24. /** Groups the 25 parameters fall into, and the width of the presence field that names them. */
  25. constexpr std::uint8_t kGroupCount = 2;
  26. /** Only the low 25 bits of either mask name a parameter. */
  27. constexpr std::uint64_t kParameterMaskBits = 0x1FFFFFF;
  28. /**
  29. * Reduces one parameter mask to the presence bits the encoder writes ahead of it.
  30. * @param mask Parameter mask.
  31. * @return Bit per group, set when that group holds any named parameter.
  32. */
  33. [[nodiscard]] std::uint64_t group_presence(std::uint64_t mask) noexcept {
  34. std::uint64_t groups = 0;
  35. for (std::uint8_t group = 0; group < kGroupCount; ++group) {
  36. const std::uint64_t span = (mask >> (group * kGroupSize)) & ((1ULL << kGroupSize) - 1);
  37. if ((span & kParameterMaskBits) != 0) {
  38. groups |= 1ULL << group;
  39. }
  40. }
  41. return groups;
  42. }
  43. /**
  44. * Writes the `activity-host` body.
  45. * @param writer Open writer.
  46. * @param body Field values.
  47. * @return True when every field fit.
  48. */
  49. [[nodiscard]] bool write_activity_host(bits::Writer& writer,
  50. const ActivityHostParameter& body) noexcept {
  51. std::array<std::byte, sizeof(std::uint32_t)> mask{};
  52. for (std::size_t index = 0; index < mask.size(); ++index) {
  53. const unsigned shift = static_cast<unsigned>(index) * kByteBits;
  54. mask[index] = static_cast<std::byte>((body.memberMask >> shift) & kByteMask);
  55. }
  56. return bits::write_raw_u64(writer, body.selectionId) && writer.write(body.hostId, kHostIdWidth)
  57. && bits::write_raw(writer, mask) && writer.write(body.address, kAddressWidth)
  58. && writer.write(body.port, kPortWidth);
  59. }
  60. /**
  61. * Writes one carried parameter body.
  62. * A clear root bit is a complete body for both tag-reflection parameters. It skips the field
  63. * walk and leaves the reader's own values in place.
  64. * @param writer Open writer.
  65. * @param parameter Registry index.
  66. * @param update Update being written, for the parameters that carry field values.
  67. * @return True when the parameter has an encoder here and its body fit.
  68. */
  69. [[nodiscard]] bool write_parameter_body(bits::Writer& writer,
  70. std::uint8_t parameter,
  71. const ParameterUpdate& update) noexcept {
  72. if (parameter == static_cast<std::uint8_t>(Parameter::publicSessionReservations)
  73. || parameter == static_cast<std::uint8_t>(Parameter::currentActivity)) {
  74. return writer.write(0U, kRootBitClear);
  75. }
  76. if (parameter == static_cast<std::uint8_t>(Parameter::activityHost)) {
  77. return write_activity_host(writer, update.activityHost);
  78. }
  79. return false;
  80. }
  81. /**
  82. * Writes the per-parameter bits of one mask, in ascending index order.
  83. * Only parameters in a present group get a bit.
  84. * @param writer Open writer.
  85. * @param mask Parameter mask, already reduced to its meaningful bits.
  86. * @param groups Presence bits for that mask.
  87. * @param carriesBodies True for the value mask, whose named parameters carry a body.
  88. * @param update Update being written, for the parameters that carry field values.
  89. * @return True when every bit and body fit.
  90. */
  91. [[nodiscard]] bool write_mask_bits(bits::Writer& writer,
  92. std::uint64_t mask,
  93. std::uint64_t groups,
  94. bool carriesBodies,
  95. const ParameterUpdate& update) noexcept {
  96. for (std::uint8_t parameter = 0; parameter < kParameterCount; ++parameter) {
  97. if ((groups & (1ULL << (parameter / kGroupSize))) == 0) {
  98. continue;
  99. }
  100. const bool named = ((mask >> parameter) & 1ULL) != 0;
  101. if (!writer.write(named ? 1U : 0U, kFlagWidth)) {
  102. return false;
  103. }
  104. // A body must follow its own bit. The consumer reads them interleaved, not in two runs.
  105. if (named && carriesBodies && !write_parameter_body(writer, parameter, update)) {
  106. return false;
  107. }
  108. }
  109. return true;
  110. }
  111. } // namespace
  112. /** Reads the header of a parameter request. */
  113. bool read_parameter_request(bits::Reader& reader, ParameterRequestHeader& output) noexcept {
  114. ParameterRequestHeader candidate{};
  115. std::uint64_t mode = 0;
  116. if (!bits::read_raw_u64(reader, candidate.sessionId) || !reader.read(kFlagWidth, mode)
  117. || !bits::read_raw_u64(reader, candidate.requestedMask)) {
  118. return false;
  119. }
  120. candidate.modeFlag = mode != 0;
  121. output = candidate;
  122. return true;
  123. }
  124. /** Writes a parameter update. */
  125. bool write_parameter_update(bits::Writer& writer, const ParameterUpdate& body) noexcept {
  126. const std::uint64_t released = body.releasedMask & kParameterMaskBits;
  127. const std::uint64_t carried = body.carriedMask & kParameterMaskBits;
  128. if ((carried & ~kEncodableParameters) != 0) {
  129. return false;
  130. }
  131. // Field order differs from the request. The flag leads here, and the session id follows it.
  132. return writer.write(body.resetFlag ? 1U : 0U, kFlagWidth)
  133. && bits::write_raw_u64(writer, body.sessionId)
  134. && writer.write(group_presence(released), kGroupCount)
  135. && writer.write(group_presence(carried), kGroupCount)
  136. && write_mask_bits(writer, released, group_presence(released), false, body)
  137. && write_mask_bits(writer, carried, group_presence(carried), true, body);
  138. }
  139. /** Reports whether one parameter was requested. */
  140. bool requests(std::uint64_t mask, std::uint8_t parameter) noexcept {
  141. if (parameter >= kParameterCount) {
  142. return false;
  143. }
  144. return ((mask >> parameter) & 1U) != 0;
  145. }
  146. } // namespace sunrise::middleware::gameplay::group