File indexing completed on 2025-09-17 08:38:19
0001
0002
0003
0004
0005
0006
0007
0008 #ifndef BOOST_MQTT5_CONTROL_PACKET_HPP
0009 #define BOOST_MQTT5_CONTROL_PACKET_HPP
0010
0011 #include <boost/mqtt5/types.hpp>
0012
0013 #include <boost/assert.hpp>
0014 #include <boost/smart_ptr/allocate_unique.hpp>
0015
0016 #include <algorithm>
0017 #include <cstdint>
0018 #include <memory>
0019 #include <string>
0020 #include <string_view>
0021 #include <vector>
0022
0023 namespace boost::mqtt5::detail {
0024
0025
0026 static constexpr int32_t default_max_send_size = 268'435'460;
0027
0028 enum class control_code_e : std::uint8_t {
0029 no_packet = 0b00000000,
0030
0031 connect = 0b00010000,
0032 connack = 0b00100000,
0033 publish = 0b00110000,
0034 puback = 0b01000000,
0035 pubrec = 0b01010000,
0036 pubrel = 0b01100000,
0037 pubcomp = 0b01110000,
0038 subscribe = 0b10000000,
0039 suback = 0b10010000,
0040 unsubscribe = 0b10100000,
0041 unsuback = 0b10110000,
0042 pingreq = 0b11000000,
0043 pingresp = 0b11010000,
0044 disconnect = 0b11100000,
0045 auth = 0b11110000,
0046 };
0047
0048 constexpr struct with_pid_ {} with_pid {};
0049 constexpr struct no_pid_ {} no_pid {};
0050
0051 template <typename Allocator>
0052 class control_packet {
0053 uint16_t _packet_id;
0054
0055 using alloc_type = Allocator;
0056 using deleter = boost::alloc_deleter<std::string, alloc_type>;
0057 std::unique_ptr<std::string, deleter> _packet;
0058
0059 control_packet(
0060 const Allocator& a,
0061 uint16_t packet_id, std::string packet
0062 ) :
0063 _packet_id(packet_id),
0064 _packet(boost::allocate_unique<std::string>(a, std::move(packet)))
0065 {}
0066
0067 public:
0068 control_packet(control_packet&&) noexcept = default;
0069 control_packet(const control_packet&) = delete;
0070
0071 control_packet& operator=(control_packet&&) noexcept = default;
0072 control_packet& operator=(const control_packet&) = delete;
0073
0074 template <
0075 typename EncodeFun,
0076 typename ...Args
0077 >
0078 static control_packet of(
0079 with_pid_, const Allocator& alloc,
0080 EncodeFun&& encode, uint16_t packet_id, Args&&... args
0081 ) {
0082 return control_packet {
0083 alloc, packet_id, encode(packet_id, std::forward<Args>(args)...)
0084 };
0085 }
0086
0087 template <
0088 typename EncodeFun,
0089 typename ...Args
0090 >
0091 static control_packet of(
0092 no_pid_, const Allocator& alloc,
0093 EncodeFun&& encode, Args&&... args
0094 ) {
0095 return control_packet {
0096 alloc, uint16_t(0), encode(std::forward<Args>(args)...)
0097 };
0098 }
0099
0100 size_t size() const {
0101 return _packet->size();
0102 }
0103
0104 control_code_e control_code() const {
0105 return control_code_e(uint8_t(*(_packet->data())) & 0b11110000);
0106 }
0107
0108 uint16_t packet_id() const {
0109 return _packet_id;
0110 }
0111
0112 qos_e qos() const {
0113 BOOST_ASSERT(control_code() == control_code_e::publish);
0114 auto byte = (uint8_t(*(_packet->data())) & 0b00000110) >> 1;
0115 return qos_e(byte);
0116 }
0117
0118 control_packet& set_dup() {
0119 BOOST_ASSERT(control_code() == control_code_e::publish);
0120 auto& byte = *(_packet->data());
0121 byte |= 0b00001000;
0122 return *this;
0123 }
0124
0125 std::string_view wire_data() const {
0126 return *_packet;
0127 }
0128 };
0129
0130 class packet_id_allocator {
0131 struct interval {
0132 uint16_t start, end;
0133
0134 interval(uint16_t start, uint16_t end) :
0135 start(start), end(end)
0136 {}
0137 };
0138
0139 std::vector<interval> _free_ids;
0140 static constexpr uint16_t MAX_PACKET_ID = 65535;
0141
0142 public:
0143 packet_id_allocator() {
0144 _free_ids.emplace_back(MAX_PACKET_ID, uint16_t(0));
0145 }
0146
0147 packet_id_allocator(packet_id_allocator&&) noexcept = default;
0148 packet_id_allocator(const packet_id_allocator&) = delete;
0149
0150 packet_id_allocator& operator=(packet_id_allocator&&) noexcept = default;
0151 packet_id_allocator& operator=(const packet_id_allocator&) = delete;
0152
0153 uint16_t allocate() {
0154 if (_free_ids.empty()) return 0;
0155 auto& last = _free_ids.back();
0156 if (last.start == ++last.end) {
0157 auto ret = last.end;
0158 _free_ids.pop_back();
0159 return ret;
0160 }
0161 return last.end;
0162 }
0163
0164 void free(uint16_t pid) {
0165 auto it = std::upper_bound(
0166 _free_ids.begin(), _free_ids.end(), pid,
0167 [](const uint16_t x, const interval& i) { return x > i.start; }
0168 );
0169 uint16_t* end_p = nullptr;
0170 if (it != _free_ids.begin()) {
0171 auto pit = std::prev(it);
0172 if (pit->end == pid)
0173 end_p = &pit->end;
0174 }
0175 if (it != _free_ids.end() && pid - 1 == it->start) {
0176 if (!end_p)
0177 it->start = pid;
0178 else {
0179 *end_p = it->end;
0180 _free_ids.erase(it);
0181 }
0182 }
0183 else {
0184 if (!end_p)
0185 _free_ids.insert(it, interval(pid, pid - 1));
0186 else
0187 *end_p = pid - 1;
0188 }
0189 }
0190 };
0191
0192 }
0193
0194 #endif