async_mqtt 5.0.0
Loading...
Searching...
No Matches
v3_1_1_unsubscribe.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_V3_1_1_UNSUBSCRIBE_HPP)
8#define ASYNC_MQTT_PACKET_V3_1_1_UNSUBSCRIBE_HPP
9
10#include <boost/numeric/conversion/cast.hpp>
11
12#include <async_mqtt/exception.hpp>
13#include <async_mqtt/buffer.hpp>
14
15#include <async_mqtt/util/move.hpp>
16#include <async_mqtt/util/static_vector.hpp>
17#include <async_mqtt/util/endian_convert.hpp>
18#include <async_mqtt/util/utf8validate.hpp>
19
20#include <async_mqtt/packet/packet_id_type.hpp>
21#include <async_mqtt/packet/fixed_header.hpp>
22#include <async_mqtt/packet/topic_sharename.hpp>
23#include <async_mqtt/variable_bytes.hpp>
24#include <async_mqtt/packet/copy_to_static_vector.hpp>
25
26namespace async_mqtt::v3_1_1 {
27
28namespace as = boost::asio;
29
37template <std::size_t PacketIdBytes>
39public:
40 using packet_id_t = typename packet_id_type<PacketIdBytes>::type;
41
49 packet_id_t packet_id,
50 std::vector<topic_sharename> params
51 )
52 : fixed_header_{make_fixed_header(control_packet_type::unsubscribe, 0b0010)},
53 entries_{force_move(params)},
54 remaining_length_{PacketIdBytes}
55 {
56 topic_length_buf_entries_.reserve(entries_.size());
57 for (auto const& e : entries_) {
58 topic_length_buf_entries_.push_back(
59 endian_static_vector(
60 boost::numeric_cast<std::uint16_t>(e.all_topic().size())
61 )
62 );
63 }
64
65 endian_store(packet_id, packet_id_.data());
66
67 // Check for errors before allocating.
68 for (auto const& e : entries_) {
69 auto size = e.all_topic().size();
70 if (size > 0xffff) {
71 throw make_error(
72 errc::bad_message,
73 "v3_1_1::unsubscribe_packet length of topic is invalid"
74 );
75 }
76 remaining_length_ +=
77 2 + // topic filter length
78 size; // topic filter
79
80 if (!utf8string_check(e.all_topic())) {
81 throw make_error(
82 errc::bad_message,
83 "v3_1_1::unsubscribe_packet topic filter invalid utf8"
84 );
85 }
86 }
87
88 remaining_length_buf_ = val_to_variable_bytes(boost::numeric_cast<std::uint32_t>(remaining_length_));
89 }
90
92 // fixed_header
93 if (buf.empty()) {
94 throw make_error(
95 errc::bad_message,
96 "v3_1_1::unsubscribe_packet fixed_header doesn't exist"
97 );
98 }
99 fixed_header_ = static_cast<std::uint8_t>(buf.front());
100 buf.remove_prefix(1);
101 auto cpt_opt = get_control_packet_type_with_check(static_cast<std::uint8_t>(fixed_header_));
102 if (!cpt_opt || *cpt_opt != control_packet_type::unsubscribe) {
103 throw make_error(
104 errc::bad_message,
105 "v3_1_1::unsubscribe_packet fixed_header is invalid"
106 );
107 }
108
109 // remaining_length
110 if (auto vl_opt = insert_advance_variable_length(buf, remaining_length_buf_)) {
111 remaining_length_ = *vl_opt;
112 }
113 else {
114 throw make_error(errc::bad_message, "v3_1_1::unsubscribe_packet remaining length is invalid");
115 }
116 if (remaining_length_ != buf.size()) {
117 throw make_error(errc::bad_message, "v3_1_1::unsubscribe_packet remaining length doesn't match buf.size()");
118 }
119
120 // packet_id
121 if (!copy_advance(buf, packet_id_)) {
122 throw make_error(
123 errc::bad_message,
124 "v3_1_1::unsubscribe_packet packet_id doesn't exist"
125 );
126 }
127
128 if (remaining_length_ == 0) {
129 throw make_error(errc::bad_message, "v3_1_1::unsubscribe_packet doesn't have entries");
130 }
131
132 while (!buf.empty()) {
133 // topic_length
134 static_vector<char, 2> topic_length_buf;
135 if (!insert_advance(buf, topic_length_buf)) {
136 throw make_error(
137 errc::bad_message,
138 "v3_1_1::unsubscribe_packet length of topic is invalid"
139 );
140 }
141 auto topic_length = endian_load<std::uint16_t>(topic_length_buf.data());
142 topic_length_buf_entries_.push_back(topic_length_buf);
143
144 // topic
145 if (buf.size() < topic_length) {
146 throw make_error(
147 errc::bad_message,
148 "v3_1_1::unsubscribe_packet topic doesn't match its length"
149 );
150 }
151 auto topic = buf.substr(0, topic_length);
152 if (!utf8string_check(topic)) {
153 throw make_error(
154 errc::bad_message,
155 "v3_1_1::unsubscribe_packet topic filter invalid utf8"
156 );
157 }
158 entries_.emplace_back(force_move(topic));
159 buf.remove_prefix(topic_length);
160 }
161 }
162
163 constexpr control_packet_type type() const {
164 return control_packet_type::unsubscribe;
165 }
166
172 std::vector<as::const_buffer> const_buffer_sequence() const {
173 std::vector<as::const_buffer> ret;
175
176 ret.emplace_back(as::buffer(&fixed_header_, 1));
177
178 ret.emplace_back(as::buffer(remaining_length_buf_.data(), remaining_length_buf_.size()));
179
180 ret.emplace_back(as::buffer(packet_id_.data(), packet_id_.size()));
181
182 BOOST_ASSERT(entries_.size() == topic_length_buf_entries_.size());
183 auto it = topic_length_buf_entries_.begin();
184 for (auto const& e : entries_) {
185 ret.emplace_back(as::buffer(it->data(), it->size()));
186 ret.emplace_back(as::buffer(e.all_topic()));
187 ++it;
188 }
189
190 return ret;
191 }
192
197 std::size_t size() const {
198 return
199 1 + // fixed header
200 remaining_length_buf_.size() +
201 remaining_length_;
202 }
203
208 std::size_t num_of_const_buffer_sequence() const {
209 return
210 1 + // fixed header
211 1 + // remaining length
212 1 + // packet id
213 entries_.size() * 2; // topic name length, topic name
214 }
215
220 packet_id_t packet_id() const {
221 return endian_load<packet_id_t>(packet_id_.data());
222 }
223
228 std::vector<topic_sharename> const& entries() const {
229 return entries_;
230 }
231
232private:
233 std::uint8_t fixed_header_;
234 std::vector<static_vector<char, 2>> topic_length_buf_entries_;
235 std::vector<topic_sharename> entries_;
237 std::size_t remaining_length_;
238 static_vector<char, 4> remaining_length_buf_;
239};
240
241template <std::size_t PacketIdBytes>
242inline std::ostream& operator<<(std::ostream& o, basic_unsubscribe_packet<PacketIdBytes> const& v) {
243 o <<
244 "v3_1_1::unsubscribe{" <<
245 "pid:" << v.packet_id() << ",[";
246 auto b = v.entries().cbegin();
247 auto e = v.entries().cend();
248 if (b != e) {
249 o <<
250 "{topic:" <<
251 b->all_topic() << "}";
252 ++b;
253 }
254 for (; b != e; ++b) {
255 o << "," <<
256 "{topic:" <<
257 b->all_topic() << "}";
258 }
259 o << "]}";
260 return o;
261}
262
268
269} // namespace async_mqtt::v3_1_1
270
271#endif // ASYNC_MQTT_PACKET_V3_1_1_UNSUBSCRIBE_HPP
Definition packet_variant.hpp:49
buffer that has string_view interface This class provides string_view interface. This class hold stri...
Definition buffer.hpp:30
buffer substr(size_type pos=0, size_type count=npos) const &
get substring The returned buffer ragnge is the same as string_view::substr(). In addition the lifeti...
Definition buffer.hpp:201
MQTT UNSUBSCRIBE packet (v3.1.1)
Definition v3_1_1_unsubscribe.hpp:38
basic_unsubscribe_packet(packet_id_t packet_id, std::vector< topic_sharename > params)
constructor
Definition v3_1_1_unsubscribe.hpp:48
std::size_t num_of_const_buffer_sequence() const
Get number of element of const_buffer_sequence.
Definition v3_1_1_unsubscribe.hpp:208
packet_id_t packet_id() const
Get packet_id.
Definition v3_1_1_unsubscribe.hpp:220
std::vector< as::const_buffer > const_buffer_sequence() const
Create const buffer sequence it is for boost asio APIs.
Definition v3_1_1_unsubscribe.hpp:172
std::size_t size() const
Get packet size.
Definition v3_1_1_unsubscribe.hpp:197
std::vector< topic_sharename > const & entries() const
Get entries.
Definition v3_1_1_unsubscribe.hpp:228