incident.h 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. #pragma once
  2. #include <array>
  3. #include <cstddef>
  4. #include <cstdint>
  5. #include <span>
  6. #include "../../encoding/bit_writer.h"
  7. namespace sunrise::middleware::bap::activity_message::incident {
  8. /** Activity message type 19 carries one incident. Both sides can send it. */
  9. inline constexpr std::uint32_t kMessageType = 19;
  10. /** Target indices are 13 bits and resolve through the 7,763-record global table. */
  11. inline constexpr std::uint8_t kTargetWidth = 13;
  12. /** The highest valid target index. Above it the Client indexes handler tables unbounded. */
  13. inline constexpr std::uint32_t kTargetMaximum = 7'762;
  14. /** These three rows carry type code -1 and are a crash risk, so they never pass. */
  15. inline constexpr std::array<std::uint32_t, 3> kPoisonTargets{795, 4'690, 5'375};
  16. /** The extra-target count is 5 bits, so the wire can ask for more than the limit allows. */
  17. inline constexpr std::uint8_t kExtraCountWidth = 5;
  18. /** At most 25 extra targets follow the primary one. */
  19. inline constexpr std::uint32_t kExtraTargetMaximum = 25;
  20. /** One bit says whether a compressed target selector follows. */
  21. inline constexpr std::uint8_t kSelectorPresenceWidth = 1;
  22. /** The selector byte length is 9 bits, so the wire can ask for more than the limit allows. */
  23. inline constexpr std::uint8_t kSelectorLengthWidth = 9;
  24. /** At most 260 selector bytes follow. Their meaning stays opaque. */
  25. inline constexpr std::uint32_t kSelectorMaximum = 260;
  26. /** One bit says whether optional field K follows. */
  27. inline constexpr std::uint8_t kOptionalPresenceWidth = 1;
  28. /** Optional field K is two 32-bit words. */
  29. inline constexpr std::uint8_t kOptionalFieldWidth = 64;
  30. /** Each optional word is a full 32-bit field. */
  31. inline constexpr std::uint8_t kOptionalWordWidth = 32;
  32. /** The payload byte length is 9 bits, so the wire can ask for more than the limit allows. */
  33. inline constexpr std::uint8_t kPayloadLengthWidth = 9;
  34. /** At most 500 payload bytes follow. */
  35. inline constexpr std::uint32_t kPayloadMaximum = 500;
  36. /** The smallest body is the five fixed fields with every count zero. */
  37. inline constexpr std::size_t kMinimumBodyBits = kTargetWidth + kExtraCountWidth
  38. + kSelectorPresenceWidth + kOptionalPresenceWidth
  39. + kPayloadLengthWidth;
  40. /** Why one incident did not pass validation. */
  41. enum class Verdict : std::uint8_t {
  42. accepted,
  43. /** The body is shorter than the fields it declares. */
  44. truncated,
  45. /** A target index is above 7,762. */
  46. targetOutOfRange,
  47. /** A target index is one of the three type-code -1 rows. */
  48. targetPoisoned,
  49. /** More than 25 extra targets were declared. */
  50. tooManyTargets,
  51. /** More than 500 payload bytes were declared. */
  52. payloadTooLong,
  53. /** More than 260 selector bytes were declared. */
  54. selectorTooLong,
  55. };
  56. /** One validated incident, framed to the end of its payload. */
  57. struct Incident {
  58. std::array<std::byte, kSelectorMaximum> selector{};
  59. std::array<std::byte, kPayloadMaximum> payload{};
  60. std::uint32_t primaryTarget{};
  61. std::uint32_t extraTargets[kExtraTargetMaximum]{};
  62. std::uint32_t extraTargetCount{};
  63. std::uint32_t selectorLength{};
  64. std::uint32_t payloadLength{};
  65. /** Both optional words, read only when the optional block is present. */
  66. std::uint32_t optionalWordA{};
  67. std::uint32_t optionalWordB{};
  68. /** Bits the body used. Below the payload's own bit count means trailing padding. */
  69. std::uint32_t consumedBits{};
  70. /** Set when a compressed selector is present. Its bytes stay opaque. */
  71. bool hasCompressedSelector{};
  72. /** Set when the two optional words are present. */
  73. bool hasOptionalBlock{};
  74. /** Set when the payload length and its bytes were reached and checked. */
  75. bool hasPayload{};
  76. };
  77. /** @return A short stable name for one verdict, for the log line. */
  78. [[nodiscard]] const char* verdict_name(Verdict verdict) noexcept;
  79. /**
  80. * Validates one incident body from its first target to the end of its payload.
  81. * Every target index is range and poison checked before anything else, because an out-of-range
  82. * index is a crash in the consumer rather than a decode error.
  83. * @param payload Activity message payload after the envelope.
  84. * @param parsed Cleared first. Receives every field reached before the verdict.
  85. * @return accepted, or the first rule the body broke.
  86. */
  87. [[nodiscard]] Verdict validate(std::span<const std::byte> payload, Incident& parsed) noexcept;
  88. /**
  89. * Writes one bounded incident body after a complete semantic preflight.
  90. * TODO: no sender yet. The outbound gameplay-event route has to open before this is called.
  91. */
  92. [[nodiscard]] bool write(encoding::bits::Writer& writer, const Incident& incident) noexcept;
  93. } // namespace sunrise::middleware::bap::activity_message::incident