async_mqtt 4.1.0
Loading...
Searching...
No Matches
property_variant.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_PROPERTY_VARIANT_HPP)
8#define ASYNC_MQTT_PACKET_PROPERTY_VARIANT_HPP
9
10#include <async_mqtt/util/variant.hpp>
12#include <async_mqtt/packet/validate_property.hpp>
13#include <async_mqtt/exception.hpp>
14
15namespace async_mqtt {
16
21public:
22
27 template <
28 typename Property,
29 std::enable_if_t<
30 !std::is_same_v<std::decay_t<Property>, property_variant>,
31 std::nullptr_t
32 >* = nullptr
33 >
34 property_variant(Property&& property):var_{std::forward<Property>(property)}
35 {}
36
41 template <typename Func>
42 auto visit(Func&& func) const& {
43 return
44 async_mqtt::visit(
45 std::forward<Func>(func),
46 var_
47 );
48 }
49
54 template <typename Func>
55 auto visit(Func&& func) & {
56 return
57 async_mqtt::visit(
58 std::forward<Func>(func),
59 var_
60 );
61 }
62
67 template <typename Func>
68 auto visit(Func&& func) && {
69 return
70 async_mqtt::visit(
71 std::forward<Func>(func),
72 force_move(var_)
73 );
74 }
75
80 property::id id() const {
81 return visit(
82 overload {
83 [] (auto const& p) {
84 return p.id();
85 },
86 [] (system_error const&) {
87 BOOST_ASSERT(false);
88 return property::id(0);
89 }
90 }
91 );
92 }
93
98 std::size_t num_of_const_buffer_sequence() const {
99 return visit(
100 overload {
101 [] (auto const& p) {
102 return p.num_of_const_buffer_sequence();
103 },
104 [] (system_error const&) {
105 BOOST_ASSERT(false);
106 return std::size_t(0);
107 }
108 }
109 );
110 }
111
117 std::vector<as::const_buffer> const_buffer_sequence() const {
118 return visit(
119 overload {
120 [] (auto const& p) {
121 return p.const_buffer_sequence();
122 },
123 [] (system_error const&) {
124 BOOST_ASSERT(false);
125 return std::vector<as::const_buffer>{};
126 }
127 }
128 );
129 }
130
135 std::size_t size() const {
136 return visit(
137 overload {
138 [] (auto const& p) {
139 return p.size();
140 },
141 [] (system_error const&) {
142 BOOST_ASSERT(false);
143 return std::size_t(0);
144 }
145 }
146 );
147 }
148
153 template <typename T>
154 decltype(auto) get() {
155 return std::get<T>(var_);
156 }
157
162 template <typename T>
163 decltype(auto) get() const {
164 return std::get<T>(var_);
165 }
166
171 template <typename T>
172 decltype(auto) get_if() {
173 return std::get_if<T>(&var_);
174 }
175
180 template <typename T>
181 decltype(auto) get_if() const {
182 return std::get_if<T>(&var_);
183 }
184
185 operator bool() {
186 return var_.index() != 0;
187 }
188
189 friend bool operator==(property_variant const& lhs, property_variant const& rhs) {
190 return lhs.var_ == rhs.var_;
191 }
192 friend bool operator<(property_variant const& lhs, property_variant const& rhs) {
193 return lhs.var_ < rhs.var_;
194 }
195 friend std::ostream& operator<<(std::ostream& o, property_variant const& v) {
196 v.visit(
197 [&] (auto const& p) { o << p; }
198 );
199 return o;
200 }
201
202private:
203 using variant_t = variant<
204 system_error,
205 property::payload_format_indicator,
206 property::message_expiry_interval,
207 property::content_type,
208 property::response_topic,
209 property::correlation_data,
210 property::subscription_identifier,
211 property::session_expiry_interval,
212 property::assigned_client_identifier,
213 property::server_keep_alive,
214 property::authentication_method,
215 property::authentication_data,
216 property::request_problem_information,
217 property::will_delay_interval,
218 property::request_response_information,
219 property::response_information,
220 property::server_reference,
221 property::reason_string,
222 property::receive_maximum,
223 property::topic_alias_maximum,
224 property::topic_alias,
225 property::maximum_qos,
226 property::retain_available,
227 property::user_property,
228 property::maximum_packet_size,
229 property::wildcard_subscription_available,
230 property::subscription_identifier_available,
231 property::shared_subscription_available
232 >;
233
234 variant_t var_;
235};
236
237using properties = std::vector<property_variant>;
238
239inline std::ostream& operator<<(std::ostream& o, properties const& props) {
240 o << "[";
241 auto it = props.cbegin();
242 auto end = props.cend();
243
244 if (it != end) {
245 o << *it++;
246 }
247 for (; it != end; ++it) {
248 o << "," << *it;
249 }
250 o << "]";
251 return o;
252}
253
254inline
255property_variant make_property_variant(buffer& buf, property_location loc) {
256 if (buf.empty()) {
257 return make_error(
258 errc::bad_message,
259 "property doesn't exist"
260 );
261 }
262
263 try {
264 using namespace std::literals;
265 auto id = static_cast<property::id>(buf.front());
266 if (!validate_property(loc, id)) {
267 return make_error(
268 errc::bad_message,
269 "property "s + property::id_to_str(id) + " is not allowed in " + property_location_to_str(loc)
270 );
271 }
272 buf.remove_prefix(1);
273 switch (id) {
274 case property::id::payload_format_indicator: {
275 if (buf.size() < 1) {
276 return make_error(
277 errc::bad_message,
278 "property::payload_format_indicator is invalid"
279 );
280 }
281 auto p = property::payload_format_indicator(buf.begin(), std::next(buf.begin(), 1));
282 buf.remove_prefix(1);
283 return property_variant(p);
284 } break;
285 case property::id::message_expiry_interval: {
286 if (buf.size() < 4) {
287 return make_error(
288 errc::bad_message,
289 "property::message_expiry_interval is invalid"
290 );
291 }
292 auto p = property::message_expiry_interval(buf.begin(), std::next(buf.begin(), 4));
293 buf.remove_prefix(4);
294 return property_variant(p);
295 } break;
296 case property::id::content_type: {
297 if (buf.size() < 2) {
298 return make_error(
299 errc::bad_message,
300 "property::content_type length is invalid"
301 );
302 }
303 auto len = endian_load<std::uint16_t>(buf.data());
304 if (buf.size() < 2U + len) {
305 return make_error(
306 errc::bad_message,
307 "property::content_type is invalid"
308 );
309 }
310 auto p = property::content_type(buf.substr(2, len));
311 buf.remove_prefix(2 + len);
312 return property_variant(p);
313 } break;
314 case property::id::response_topic: {
315 if (buf.size() < 2) {
316 return make_error(
317 errc::bad_message,
318 "property::response_topic length is invalid"
319 );
320 }
321 auto len = endian_load<std::uint16_t>(buf.data());
322 if (buf.size() < 2U + len) {
323 return make_error(
324 errc::bad_message,
325 "property::response_topic is invalid"
326 );
327 }
328 auto p = property::response_topic(buf.substr(2, len));
329 buf.remove_prefix(2 + len);
330 return property_variant(p);
331 } break;
332 case property::id::correlation_data: {
333 if (buf.size() < 2) {
334 return make_error(
335 errc::bad_message,
336 "property::correlation_data length is invalid"
337 );
338 }
339 auto len = endian_load<std::uint16_t>(buf.data());
340 if (buf.size() < 2U + len) {
341 return make_error(
342 errc::bad_message,
343 "property::correlation_data is invalid"
344 );
345 }
346 auto p = property::correlation_data(buf.substr(2, len));
347 buf.remove_prefix(2 + len);
348 return property_variant(p);
349 } break;
350 case property::id::subscription_identifier: {
351 auto it = buf.begin();
352 if (auto val_opt = variable_bytes_to_val(it, buf.end())) {
353 auto p = property::subscription_identifier(*val_opt);
354 buf.remove_prefix(std::size_t(std::distance(buf.begin(), it)));
355 return property_variant(p);
356 }
357 return make_error(
358 errc::bad_message,
359 "property::subscription_identifier is invalid"
360 );
361 } break;
362 case property::id::session_expiry_interval: {
363 if (buf.size() < 4) {
364 return make_error(
365 errc::bad_message,
366 "property::session_expiry_interval is invalid"
367 );
368 }
369 auto p = property::session_expiry_interval(buf.begin(), std::next(buf.begin(), 4));
370 buf.remove_prefix(4);
371 return property_variant(p);
372 } break;
373 case property::id::assigned_client_identifier: {
374 if (buf.size() < 2) {
375 return make_error(
376 errc::bad_message,
377 "property::assigned_client_identifier length is invalid"
378 );
379 }
380 auto len = endian_load<std::uint16_t>(buf.data());
381 if (buf.size() < 2U + len) {
382 return make_error(
383 errc::bad_message,
384 "property::assigned_client_identifier is invalid"
385 );
386 }
387 auto p = property::assigned_client_identifier(buf.substr(2, len));
388 buf.remove_prefix(2 + len);
389 return property_variant(p);
390 } break;
391 case property::id::server_keep_alive: {
392 if (buf.size() < 2) {
393 return make_error(
394 errc::bad_message,
395 "property::server_keep_alive is invalid"
396 );
397 }
398 auto p = property::server_keep_alive(buf.begin(), std::next(buf.begin(), 2));
399 buf.remove_prefix(2);
400 return property_variant(p);
401 } break;
402 case property::id::authentication_method: {
403 if (buf.size() < 2) {
404 return make_error(
405 errc::bad_message,
406 "property::authentication_method length is invalid"
407 );
408 }
409 auto len = endian_load<std::uint16_t>(buf.data());
410 if (buf.size() < 2U + len) {
411 return make_error(
412 errc::bad_message,
413 "property::authentication_method is invalid"
414 );
415 }
416 auto p = property::authentication_method(buf.substr(2, len));
417 buf.remove_prefix(2 + len);
418 return property_variant(p);
419 } break;
420 case property::id::authentication_data: {
421 if (buf.size() < 2) {
422 return make_error(
423 errc::bad_message,
424 "property::authentication_data length is invalid"
425 );
426 }
427 auto len = endian_load<std::uint16_t>(buf.data());
428 if (buf.size() < 2U + len) {
429 return make_error(
430 errc::bad_message,
431 "property::authentication_data is invalid"
432 );
433 }
434 auto p = property::authentication_data(buf.substr(2, len));
435 buf.remove_prefix(2 + len);
436 return property_variant(p);
437 } break;
438 case property::id::request_problem_information: {
439 if (buf.size() < 1) {
440 return make_error(
441 errc::bad_message,
442 "property::request_problem_information is invalid"
443 );
444 }
445 auto p = property::request_problem_information(buf.begin(), std::next(buf.begin(), 1));
446 buf.remove_prefix(1);
447 return property_variant(p);
448 } break;
449 case property::id::will_delay_interval: {
450 if (buf.size() < 4) {
451 return make_error(
452 errc::bad_message,
453 "property::will_delay_interval is invalid"
454 );
455 }
456 auto p = property::will_delay_interval(buf.begin(), std::next(buf.begin(), 4));
457 buf.remove_prefix(4);
458 return property_variant(p);
459 } break;
460 case property::id::request_response_information: {
461 if (buf.size() < 1) {
462 return make_error(
463 errc::bad_message,
464 "property::request_response_information is invalid"
465 );
466 }
467 auto p = property::request_response_information(buf.begin(), std::next(buf.begin(), 1));
468 buf.remove_prefix(1);
469 return property_variant(p);
470 } break;
471 case property::id::response_information: {
472 if (buf.size() < 2) {
473 return make_error(
474 errc::bad_message,
475 "property::response_information length is invalid"
476 );
477 }
478 auto len = endian_load<std::uint16_t>(buf.data());
479 if (buf.size() < 2U + len) {
480 return make_error(
481 errc::bad_message,
482 "property::response_information is invalid"
483 );
484 }
485 auto p = property::response_information(buf.substr(2, len));
486 buf.remove_prefix(2 + len);
487 return property_variant(p);
488 } break;
489 case property::id::server_reference: {
490 if (buf.size() < 2) {
491 return make_error(
492 errc::bad_message,
493 "property::server_reference length is invalid"
494 );
495 }
496 auto len = endian_load<std::uint16_t>(buf.data());
497 if (buf.size() < 2U + len) {
498 return make_error(
499 errc::bad_message,
500 "property::server_reference is invalid"
501 );
502 }
503 auto p = property::server_reference(buf.substr(2, len));
504 buf.remove_prefix(2 + len);
505 return property_variant(p);
506 } break;
507 case property::id::reason_string: {
508 if (buf.size() < 2) {
509 return make_error(
510 errc::bad_message,
511 "property::reason_string length is invalid"
512 );
513 }
514 auto len = endian_load<std::uint16_t>(buf.data());
515 if (buf.size() < 2U + len) {
516 return make_error(
517 errc::bad_message,
518 "property::reason_string is invalid"
519 );
520 }
521 auto p = property::reason_string(buf.substr(2, len));
522 buf.remove_prefix(2 + len);
523 return property_variant(p);
524 } break;
525 case property::id::receive_maximum: {
526 if (buf.size() < 2) {
527 return make_error(
528 errc::bad_message,
529 "property::receive_maximum is invalid"
530 );
531 }
532 auto p = property::receive_maximum(buf.begin(), std::next(buf.begin(), 2));
533 buf.remove_prefix(2);
534 return property_variant(p);
535 } break;
536 case property::id::topic_alias_maximum: {
537 if (buf.size() < 2) {
538 return make_error(
539 errc::bad_message,
540 "property::topic_alias_maximum is invalid"
541 );
542 }
543 auto p = property::topic_alias_maximum(buf.begin(), std::next(buf.begin(), 2));
544 buf.remove_prefix(2);
545 return property_variant(p);
546 } break;
547 case property::id::topic_alias: {
548 if (buf.size() < 2) {
549 return make_error(
550 errc::bad_message,
551 "property::topic_alias is invalid"
552 );
553 }
554 auto p = property::topic_alias(buf.begin(), std::next(buf.begin(), 2));
555 buf.remove_prefix(2);
556 return property_variant(p);
557 } break;
558 case property::id::maximum_qos: {
559 if (buf.size() < 1) {
560 return make_error(
561 errc::bad_message,
562 "property::maximum_qos is invalid"
563 );
564 }
565 auto p = property::maximum_qos(buf.begin(), std::next(buf.begin(), 1));
566 buf.remove_prefix(1);
567 return property_variant(p);
568 } break;
569 case property::id::retain_available: {
570 if (buf.size() < 1) {
571 return make_error(
572 errc::bad_message,
573 "property::reason_string length is invalid"
574 );
575 }
576 auto p = property::retain_available(buf.begin(), std::next(buf.begin(), 1));
577 buf.remove_prefix(1);
578 return property_variant(p);
579 } break;
580 case property::id::user_property: {
581 if (buf.size() < 2) {
582 return make_error(
583 errc::bad_message,
584 "property::user_property key length is invalid"
585 );
586 }
587 auto keylen = endian_load<std::uint16_t>(buf.data());
588 if (buf.size() < 2U + keylen) {
589 return make_error(
590 errc::bad_message,
591 "property::user_property key is invalid"
592 );
593 }
594 auto key = buf.substr(2, keylen);
595 buf.remove_prefix(2 + keylen);
596
597 if (buf.size() < 2) {
598 return make_error(
599 errc::bad_message,
600 "property::user_property val length is invalid"
601 );
602 }
603 auto vallen = endian_load<std::uint16_t>(buf.data());
604 if (buf.size() < 2U + vallen) {
605 return make_error(
606 errc::bad_message,
607 "property::user_property val is invalid"
608 );
609 }
610 auto val = buf.substr(2, vallen);
611 auto p = property::user_property(force_move(key), force_move(val));
612 buf.remove_prefix(2 + vallen);
613
614 return property_variant(p);
615 } break;
616 case property::id::maximum_packet_size: {
617 if (buf.size() < 4) {
618 return make_error(
619 errc::bad_message,
620 "property::maximum_packet_size is invalid"
621 );
622 }
623 auto p = property::maximum_packet_size(buf.begin(), std::next(buf.begin(), 4));
624 buf.remove_prefix(4);
625 return property_variant(p);
626 } break;
627 case property::id::wildcard_subscription_available: {
628 if (buf.size() < 1) {
629 return make_error(
630 errc::bad_message,
631 "property::wildcard_subscription_available is invalid"
632 );
633 }
634 auto p = property::wildcard_subscription_available(buf.begin(), std::next(buf.begin(), 1));
635 buf.remove_prefix(1);
636 return property_variant(p);
637 } break;
638 case property::id::subscription_identifier_available: {
639 if (buf.size() < 1) {
640 return make_error(
641 errc::bad_message,
642 "property::subscription_identifier_available is invalid"
643 );
644 }
645 auto p = property::subscription_identifier_available(buf.begin(), std::next(buf.begin(), 1));
646 buf.remove_prefix(1);
647 return property_variant(p);
648 } break;
649 case property::id::shared_subscription_available: {
650 if (buf.size() < 1) {
651 return make_error(
652 errc::bad_message,
653 "property::shared_subscription_available is invalid"
654 );
655 }
656 auto p = property::shared_subscription_available(buf.begin(), std::next(buf.begin(), 1));
657 buf.remove_prefix(1);
658 return property_variant(p);
659 } break;
660 }
661 }
662 catch (system_error const& e) {
663 return e;
664 }
665 return make_error(
666 errc::bad_message,
667 "unknown error"
668 );
669}
670
671inline
672properties make_properties(buffer buf, property_location loc) {
673 properties props;
674 while (!buf.empty()) {
675 if (auto pv = make_property_variant(buf, loc)) {
676 props.push_back(force_move(pv));
677 }
678 else {
679 throw pv;
680 }
681 }
682
683 return props;
684}
685
686inline
687std::vector<as::const_buffer> const_buffer_sequence(properties const& props) {
688 std::vector<as::const_buffer> v;
689 for (auto const& p : props) {
690 auto cbs = p.const_buffer_sequence();
691 std::move(cbs.begin(), cbs.end(), std::back_inserter(v));
692 }
693 return v;
694}
695
696inline
697std::size_t size(properties const& props) {
698 return
699 std::accumulate(
700 props.begin(),
701 props.end(),
702 std::size_t(0U),
703 [](std::size_t total, property_variant const& pv) {
704 return total + pv.size();
705 }
706 );
707}
708
709inline
710std::size_t num_of_const_buffer_sequence(properties const& props) {
711 return
712 std::accumulate(
713 props.begin(),
714 props.end(),
715 std::size_t(0U),
716 [](std::size_t total, property_variant const& pv) {
717 return total + pv.num_of_const_buffer_sequence();
718 }
719 );
720}
721
722} // namespace async_mqtt
723
724#endif // ASYNC_MQTT_PACKET_PROPERTY_VARIANT_HPP
Definition packet_variant.hpp:49
property variant
Definition property_variant.hpp:20
decltype(auto) get()
Get by type. If not match, then throw std::bad_variant_access exception.
Definition property_variant.hpp:154
std::size_t size() const
Get packet size.
Definition property_variant.hpp:135
property::id id() const
Get property_id.
Definition property_variant.hpp:80
std::vector< as::const_buffer > const_buffer_sequence() const
Create const buffer sequence. it is for boost asio APIs.
Definition property_variant.hpp:117
decltype(auto) get_if() const
Get by type pointer.
Definition property_variant.hpp:181
property_variant(Property &&property)
constructor
Definition property_variant.hpp:34
decltype(auto) get_if()
Get by type pointer.
Definition property_variant.hpp:172
auto visit(Func &&func) &
visit to variant
Definition property_variant.hpp:55
decltype(auto) get() const
Get by type. If not match, then throw std::bad_variant_access exception.
Definition property_variant.hpp:163
std::size_t num_of_const_buffer_sequence() const
Get number of element of const_buffer_sequence.
Definition property_variant.hpp:98
auto visit(Func &&func) const &
visit to variant
Definition property_variant.hpp:42
auto visit(Func &&func) &&
visit to variant
Definition property_variant.hpp:68
async_mqtt error class. It is used as CompletionToken parameter and exception.
Definition exception.hpp:29