control_packet.hpp 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. //
  2. // Copyright (c) 2023-2025 Ivica Siladic, Bruno Iljazovic, Korina Simicevic
  3. //
  4. // Distributed under the Boost Software License, Version 1.0.
  5. // (See accompanying file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt)
  6. //
  7. #ifndef BOOST_MQTT5_CONTROL_PACKET_HPP
  8. #define BOOST_MQTT5_CONTROL_PACKET_HPP
  9. #include <boost/mqtt5/types.hpp>
  10. #include <boost/assert.hpp>
  11. #include <boost/smart_ptr/allocate_unique.hpp>
  12. #include <algorithm>
  13. #include <cstdint>
  14. #include <memory>
  15. #include <string>
  16. #include <string_view>
  17. #include <vector>
  18. namespace boost::mqtt5::detail {
  19. /* max varint number (268'435'455) + fixed header size (1 + 4) */
  20. static constexpr int32_t default_max_send_size = 268'435'460;
  21. static constexpr int32_t default_max_recv_size = 65'536;
  22. enum class control_code_e : std::uint8_t {
  23. no_packet = 0b00000000, // 0
  24. connect = 0b00010000, // 1
  25. connack = 0b00100000, // 2
  26. publish = 0b00110000, // 3
  27. puback = 0b01000000, // 4
  28. pubrec = 0b01010000, // 5
  29. pubrel = 0b01100000, // 6
  30. pubcomp = 0b01110000, // 7
  31. subscribe = 0b10000000, // 8
  32. suback = 0b10010000, // 9
  33. unsubscribe = 0b10100000, // 10
  34. unsuback = 0b10110000, // 11
  35. pingreq = 0b11000000, // 12
  36. pingresp = 0b11010000, // 13
  37. disconnect = 0b11100000, // 14
  38. auth = 0b11110000, // 15
  39. };
  40. constexpr struct with_pid_ {} with_pid {};
  41. constexpr struct no_pid_ {} no_pid {};
  42. template <typename Allocator>
  43. class control_packet {
  44. uint16_t _packet_id;
  45. using alloc_type = Allocator;
  46. using deleter = boost::alloc_deleter<std::string, alloc_type>;
  47. std::unique_ptr<std::string, deleter> _packet;
  48. control_packet(
  49. const Allocator& a,
  50. uint16_t packet_id, std::string packet
  51. ) :
  52. _packet_id(packet_id),
  53. _packet(boost::allocate_unique<std::string>(a, std::move(packet)))
  54. {}
  55. public:
  56. control_packet(control_packet&&) noexcept = default;
  57. control_packet(const control_packet&) = delete;
  58. control_packet& operator=(control_packet&&) noexcept = default;
  59. control_packet& operator=(const control_packet&) = delete;
  60. template <
  61. typename EncodeFun,
  62. typename ...Args
  63. >
  64. static control_packet of(
  65. with_pid_, const Allocator& alloc,
  66. EncodeFun&& encode, uint16_t packet_id, Args&&... args
  67. ) {
  68. return control_packet {
  69. alloc, packet_id, encode(packet_id, std::forward<Args>(args)...)
  70. };
  71. }
  72. template <
  73. typename EncodeFun,
  74. typename ...Args
  75. >
  76. static control_packet of(
  77. no_pid_, const Allocator& alloc,
  78. EncodeFun&& encode, Args&&... args
  79. ) {
  80. return control_packet {
  81. alloc, uint16_t(0), encode(std::forward<Args>(args)...)
  82. };
  83. }
  84. size_t size() const {
  85. return _packet->size();
  86. }
  87. control_code_e control_code() const {
  88. return control_code_e(uint8_t(*(_packet->data())) & 0b11110000);
  89. }
  90. uint16_t packet_id() const {
  91. return _packet_id;
  92. }
  93. qos_e qos() const {
  94. BOOST_ASSERT(control_code() == control_code_e::publish);
  95. auto byte = (uint8_t(*(_packet->data())) & 0b00000110) >> 1;
  96. return qos_e(byte);
  97. }
  98. control_packet& set_dup() {
  99. BOOST_ASSERT(control_code() == control_code_e::publish);
  100. auto& byte = *(_packet->data());
  101. byte |= 0b00001000;
  102. return *this;
  103. }
  104. std::string_view wire_data() const {
  105. return *_packet;
  106. }
  107. };
  108. class packet_id_allocator {
  109. struct interval {
  110. uint16_t start, end;
  111. interval(uint16_t start, uint16_t end) :
  112. start(start), end(end)
  113. {}
  114. };
  115. std::vector<interval> _free_ids;
  116. static constexpr uint16_t MAX_PACKET_ID = 65535;
  117. public:
  118. packet_id_allocator() {
  119. _free_ids.emplace_back(MAX_PACKET_ID, uint16_t(0));
  120. }
  121. packet_id_allocator(packet_id_allocator&&) noexcept = default;
  122. packet_id_allocator(const packet_id_allocator&) = delete;
  123. packet_id_allocator& operator=(packet_id_allocator&&) noexcept = default;
  124. packet_id_allocator& operator=(const packet_id_allocator&) = delete;
  125. uint16_t allocate() {
  126. if (_free_ids.empty()) return 0;
  127. auto& last = _free_ids.back();
  128. if (last.start == ++last.end) {
  129. auto ret = last.end;
  130. _free_ids.pop_back();
  131. return ret;
  132. }
  133. return last.end;
  134. }
  135. void free(uint16_t pid) {
  136. auto it = std::upper_bound(
  137. _free_ids.begin(), _free_ids.end(), pid,
  138. [](const uint16_t x, const interval& i) { return x > i.start; }
  139. );
  140. uint16_t* end_p = nullptr;
  141. if (it != _free_ids.begin()) {
  142. auto pit = std::prev(it);
  143. if (pit->end == pid)
  144. end_p = &pit->end;
  145. }
  146. if (it != _free_ids.end() && pid - 1 == it->start) {
  147. if (!end_p)
  148. it->start = pid;
  149. else {
  150. *end_p = it->end;
  151. _free_ids.erase(it);
  152. }
  153. }
  154. else {
  155. if (!end_p)
  156. _free_ids.insert(it, interval(pid, pid - 1));
  157. else
  158. *end_p = pid - 1;
  159. }
  160. }
  161. };
  162. } // end namespace boost::mqtt5::detail
  163. #endif // !BOOST_MQTT5_CONTROL_PACKET_HPP