async_mqtt 5.0.0
Loading...
Searching...
No Matches
v5_suback.hpp
1// Copyright Takatoshi Kondo 2022
2//
3// Distributed under the Boost Software License, Version 1.0.
4// (See accompanying file LICENSE_1_0.txt or copy at
5// http://www.boost.org/LICENSE_1_0.txt)
6
7#if !defined(ASYNC_MQTT_PACKET_V5_SUBACK_HPP)
8#define ASYNC_MQTT_PACKET_V5_SUBACK_HPP
9
10#include <async_mqtt/exception.hpp>
11#include <async_mqtt/buffer.hpp>
12
13#include <async_mqtt/util/move.hpp>
14#include <async_mqtt/util/static_vector.hpp>
15#include <async_mqtt/util/endian_convert.hpp>
16
17#include <async_mqtt/packet/packet_id_type.hpp>
18#include <async_mqtt/packet/fixed_header.hpp>
19#include <async_mqtt/packet/topic_subopts.hpp>
20#include <async_mqtt/packet/reason_code.hpp>
21#include <async_mqtt/packet/property_variant.hpp>
22#include <async_mqtt/packet/copy_to_static_vector.hpp>
23
24namespace async_mqtt::v5 {
25
26namespace as = boost::asio;
27
35template <std::size_t PacketIdBytes>
37public:
38 using packet_id_t = typename packet_id_type<PacketIdBytes>::type;
39
48 packet_id_t packet_id,
49 std::vector<suback_reason_code> params,
50 properties props = {}
51 )
52 : fixed_header_{make_fixed_header(control_packet_type::suback, 0b0000)},
53 entries_{force_move(params)},
54 remaining_length_{PacketIdBytes + entries_.size()},
55 property_length_(async_mqtt::size(props)),
56 props_(force_move(props))
57 {
58 using namespace std::literals;
59 endian_store(packet_id, packet_id_.data());
60
61 auto pb = val_to_variable_bytes(boost::numeric_cast<std::uint32_t>(property_length_));
62 for (auto e : pb) {
63 property_length_buf_.push_back(e);
64 }
65
66 for (auto const& prop : props_) {
67 auto id = prop.id();
68 if (!validate_property(property_location::suback, id)) {
69 throw make_error(
70 errc::bad_message,
71 "v5::suback_packet property "s + id_to_str(id) + " is not allowed"
72 );
73 }
74 }
75
76 remaining_length_ += property_length_buf_.size() + property_length_;
77 remaining_length_buf_ = val_to_variable_bytes(boost::numeric_cast<std::uint32_t>(remaining_length_));
78 }
79
80 basic_suback_packet(buffer buf) {
81 // fixed_header
82 if (buf.empty()) {
83 throw make_error(
84 errc::bad_message,
85 "v5::suback_packet fixed_header doesn't exist"
86 );
87 }
88 fixed_header_ = static_cast<std::uint8_t>(buf.front());
89 buf.remove_prefix(1);
90 auto cpt_opt = get_control_packet_type_with_check(static_cast<std::uint8_t>(fixed_header_));
91 if (!cpt_opt || *cpt_opt != control_packet_type::suback) {
92 throw make_error(
93 errc::bad_message,
94 "v5::suback_packet fixed_header is invalid"
95 );
96 }
97
98 // remaining_length
99 if (auto vl_opt = insert_advance_variable_length(buf, remaining_length_buf_)) {
100 remaining_length_ = *vl_opt;
101 }
102 else {
103 throw make_error(errc::bad_message, "v5::suback_packet remaining length is invalid");
104 }
105 if (remaining_length_ != buf.size()) {
106 throw make_error(errc::bad_message, "v5::suback_packet remaining length doesn't match buf.size()");
107 }
108
109 // packet_id
110 if (!copy_advance(buf, packet_id_)) {
111 throw make_error(
112 errc::bad_message,
113 "v5::suback_packet packet_id doesn't exist"
114 );
115 }
116
117 // property
118 auto it = buf.begin();
119 if (auto pl_opt = variable_bytes_to_val(it, buf.end())) {
120 property_length_ = *pl_opt;
121 std::copy(buf.begin(), it, std::back_inserter(property_length_buf_));
122 buf.remove_prefix(std::size_t(std::distance(buf.begin(), it)));
123 if (buf.size() < property_length_) {
124 throw make_error(
125 errc::bad_message,
126 "v5::suback_packet properties_don't match its length"
127 );
128 }
129 auto prop_buf = buf.substr(0, property_length_);
130 props_ = make_properties(prop_buf, property_location::suback);
131 buf.remove_prefix(property_length_);
132 }
133 else {
134 throw make_error(
135 errc::bad_message,
136 "v5::suback_packet property_length is invalid"
137 );
138 }
139
140 if (remaining_length_ == 0) {
141 throw make_error(errc::bad_message, "v5::suback_packet doesn't have entries");
142 }
143
144 while (!buf.empty()) {
145 // reason_code
146 if (buf.empty()) {
147 throw make_error(
148 errc::bad_message,
149 "v5::suback_packet suback_reason_code doesn't exist"
150 );
151 }
152 auto rc = static_cast<suback_reason_code>(buf.front());
153 entries_.emplace_back(rc);
154 buf.remove_prefix(1);
155 }
156 }
157
158 constexpr control_packet_type type() const {
159 return control_packet_type::suback;
160 }
161
167 std::vector<as::const_buffer> const_buffer_sequence() const {
168 std::vector<as::const_buffer> ret;
170
171 ret.emplace_back(as::buffer(&fixed_header_, 1));
172
173 ret.emplace_back(as::buffer(remaining_length_buf_.data(), remaining_length_buf_.size()));
174
175 ret.emplace_back(as::buffer(packet_id_.data(), packet_id_.size()));
176
177 ret.emplace_back(as::buffer(property_length_buf_.data(), property_length_buf_.size()));
178 auto props_cbs = async_mqtt::const_buffer_sequence(props_);
179 std::move(props_cbs.begin(), props_cbs.end(), std::back_inserter(ret));
180
181 ret.emplace_back(as::buffer(entries_.data(), entries_.size()));
182
183 return ret;
184 }
185
190 std::size_t size() const {
191 return
192 1 + // fixed header
193 remaining_length_buf_.size() +
194 remaining_length_;
195 }
196
201 std::size_t num_of_const_buffer_sequence() const {
202 return
203 1 + // fixed header
204 1 + // remaining length
205 1 + // packet id
206 [&] () -> std::size_t {
207 if (property_length_buf_.size() == 0) return 0;
208 return
209 1 + // property length
210 async_mqtt::num_of_const_buffer_sequence(props_);
211 }() +
212 1; // suback_reason_code vector
213 }
214
219 packet_id_t packet_id() const {
220 return endian_load<packet_id_t>(packet_id_.data());
221 }
222
227 std::vector<suback_reason_code> const& entries() const {
228 return entries_;
229 }
230
235 properties const& props() const {
236 return props_;
237 }
238
239private:
240 std::uint8_t fixed_header_;
241 std::vector<suback_reason_code> entries_;
243 std::size_t remaining_length_;
244 static_vector<char, 4> remaining_length_buf_;
245
246 std::size_t property_length_ = 0;
247 static_vector<char, 4> property_length_buf_;
248 properties props_;
249};
250
251template <std::size_t PacketIdBytes>
252inline std::ostream& operator<<(std::ostream& o, basic_suback_packet<PacketIdBytes> const& v) {
253 o <<
254 "v5::suback{" <<
255 "pid:" << v.packet_id() << ",[";
256 auto b = v.entries().cbegin();
257 auto e = v.entries().cend();
258 if (b != e) {
259 o << *b++;
260 }
261 for (; b != e; ++b) {
262 o << "," << *b;
263 }
264 o << "]";
265 if (!v.props().empty()) {
266 o << ",ps:" << v.props();
267 }
268 o << "}";
269 return o;
270}
271
272using suback_packet = basic_suback_packet<2>;
273
274} // namespace async_mqtt::v5
275
276#endif // ASYNC_MQTT_PACKET_V5_SUBACK_HPP
Definition packet_variant.hpp:49
MQTT SUBACK packet (v5)
Definition v5_suback.hpp:36
std::vector< as::const_buffer > const_buffer_sequence() const
Create const buffer sequence it is for boost asio APIs.
Definition v5_suback.hpp:167
std::vector< suback_reason_code > const & entries() const
Get entries.
Definition v5_suback.hpp:227
std::size_t size() const
Get packet size.
Definition v5_suback.hpp:190
basic_suback_packet(packet_id_t packet_id, std::vector< suback_reason_code > params, properties props={})
constructor
Definition v5_suback.hpp:47
packet_id_t packet_id() const
Get packet_id.
Definition v5_suback.hpp:219
properties const & props() const
Definition v5_suback.hpp:235
std::size_t num_of_const_buffer_sequence() const
Get number of element of const_buffer_sequence.
Definition v5_suback.hpp:201