mqtt_cpp
subscription_map.hpp
Go to the documentation of this file.
1 // Copyright Wouter van Kleunen 2019
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(MQTT_BROKER_SUBSCRIPTION_MAP_HPP)
8 #define MQTT_BROKER_SUBSCRIPTION_MAP_HPP
9 
10 #include <unordered_map>
11 
12 #include <boost/functional/hash.hpp>
13 #include <boost/range/adaptor/reversed.hpp>
14 
16 
17 #include <mqtt/string_view.hpp>
18 #include <mqtt/optional.hpp>
19 #include <mqtt/buffer.hpp>
20 
22 
24 
86 // Combined storage for count and flags
87 // we can have 32bit or 64bit version
88 
89 // Compile error on other platforms (not 32 or 64 bit)
90 template<std::size_t N>
91 struct count_storage {
92  static_assert(N == 4 || N == 8, "Subscription map count_storage only knows how to handle architectures with 32 or 64 bit size_t: please update to support your platform.");
93 };
94 
95 template<>
96 struct count_storage<4> {
97 public:
98  count_storage(std::uint32_t v = 1)
99  : value_(v & 0x3fffffffUL), has_hash_child_(false), has_plus_child_(false)
100  { }
101 
102  static constexpr std::size_t max() { return std::numeric_limits<uint32_t>::max() >> 2; }
103 
104  std::uint32_t value() const { return value_; }
105  void set_value(std::uint32_t v) {
106  value_ = v & 0x3fffffffUL;
107  }
109  ++value_;
110  }
112  --value_;
113  }
114 
115  bool has_hash_child() const { return has_hash_child_; }
116  void set_hash_child(bool v) {
117  has_hash_child_ = v;
118  }
119 
120  bool has_plus_child() const { return has_plus_child_; }
121  void set_plus_child(bool v) {
122  has_plus_child_ = v;
123  }
124 
125 private:
126  std::uint32_t value_ : 30;
127  std::uint32_t has_hash_child_ : 1;
128  std::uint32_t has_plus_child_ : 1;
129 
130 };
131 
132 template<>
133 struct count_storage<8> {
134 public:
135  count_storage(std::uint64_t v= 1)
136  : value_(v & 0x3fffffffffffffffULL), has_hash_child_(false), has_plus_child_(false)
137  { }
138 
139  static constexpr std::uint64_t max() { return std::numeric_limits<uint64_t>::max() >> 2; }
140 
141  std::uint64_t value() const { return value_; }
142  void set_value(std::uint64_t v) {
143  value_ = v & 0x3fffffffffffffffULL;
144  }
146  ++value_;
147  }
149  --value_;
150  }
151 
152  bool has_hash_child() const { return has_hash_child_; }
153  void set_hash_child(bool v) {
154  has_hash_child_ = v;
155  }
156 
157  bool has_plus_child() const { return has_plus_child_; }
158  void set_plus_child(bool v) {
159  has_plus_child_ = v;
160  }
161 
162 
163 private:
164  std::uint64_t value_ : 62;
165  std::uint64_t has_hash_child_ : 1;
166  std::uint64_t has_plus_child_ : 1;
167 };
168 
169 template<typename Value>
171 public:
172  using node_id_t = std::size_t;
173  using path_entry_key = std::pair<node_id_t, buffer>;
175 
176 private:
177 
178  // Generate a node id for a new node
179  node_id_t generate_node_id() {
180  if(next_node_id == std::numeric_limits<node_id_t>::max())
182  return ++next_node_id;
183  }
184 
185  using count_storage_t = count_storage<sizeof(void *)>;
186 
187  struct path_entry {
188  node_id_t id;
189  path_entry_key parent;
190 
191  count_storage_t count;
192 
193  Value value;
194 
195  path_entry(node_id_t id, path_entry_key parent)
196  : id(id), parent(parent)
197  {}
198  };
199 
200  // Increase the subscription count for a specific node
201  static void increase_count_storage(count_storage_t &count) {
202  if(count.value() == count_storage_t::max()) {
204  }
205 
206  count.increment_value();
207  }
208 
209  // Decrease the subscription count for a specific node
210  static void decrease_count_storage(count_storage_t& count) {
211  BOOST_ASSERT(count.value() > 0);
212  count.decrement_value();
213  }
214 
215  using this_type = subscription_map_base<Value>;
216 
217  // Use boost hash to hash pair in path_entry_key
218  using map_type = std::unordered_map< path_entry_key, path_entry, boost::hash< path_entry_key > >;
219 
220  map_type map;
221  using map_type_iterator = typename map_type::iterator;
222  using map_type_const_iterator = typename map_type::const_iterator;
223 
224  node_id_t next_node_id = 0;
225 
226 protected:
227  // Key and id of the root key
230 
231  // Return the iterator of the root
232  map_type_iterator get_root() { return map.find(root_key); };
233  map_type_const_iterator get_root() const { return map.find(root_key); };
234 
235 
236  // Map size tracks the total number of subscriptions within the map
237  size_t map_size = 0;
238 
239  map_type_iterator get_key(path_entry_key key) { return map.find(key); }
240  map_type_iterator begin() { return map.begin(); }
241  map_type_iterator end() { return map.end(); }
242  map_type const& get_map() const { return map; }
243 
244  handle path_to_handle(std::vector< map_type_iterator > const& path) const {
245  return path.back()->first;
246  }
247 
248  std::vector< map_type_iterator> find_topic_filter(string_view topic_filter) {
249  auto parent_id = get_root()->second.id;
250  std::vector< map_type_iterator > path;
251 
253  topic_filter,
254  [this, &path, &parent_id](string_view t) mutable {
255  auto entry = map.find(path_entry_key(parent_id, t));
256 
257  if (entry == map.end()) {
258  path.clear();
259  return false;
260  }
261 
262  path.push_back(entry);
263  parent_id = entry->second.id;
264  return true;
265  }
266  );
267 
268  return path;
269  }
270 
271  std::vector<map_type_iterator> create_topic_filter(string_view topic_filter) {
272  auto parent = get_root();
273 
274  std::vector<map_type_iterator> result;
275 
277  topic_filter,
278  [this, &parent, &result](string_view t) mutable {
279  auto entry = map.find(path_entry_key(parent->second.id, t));
280 
281  if (entry == map.end()) {
282  entry =
283  map.emplace(
284  path_entry_key(
285  parent->second.id,
286  allocate_buffer(t)
287  ),
288  path_entry(generate_node_id(), parent->first)
289  ).first;
290 
291  parent->second.count.set_plus_child(parent->second.count.has_plus_child() | (t == "+"));
292  parent->second.count.set_hash_child(parent->second.count.has_hash_child() | (t == "#"));
293  }
294  else {
295  increase_count_storage(entry->second.count);
296  }
297 
298  result.push_back(entry);
299  parent = entry;
300  return true;
301  }
302  );
303 
304  return result;
305  }
306 
307  // Remove a value at the specified path
308  void remove_topic_filter(std::vector< map_type_iterator > const& path) {
309  bool remove_plus_child_flag = false;
310  bool remove_hash_child_flag = false;
311 
312  // Go through entries to remove
313  for (auto& entry : boost::adaptors::reverse(path)) {
314  if (remove_plus_child_flag) {
315  entry->second.count.set_plus_child(false);
316  remove_plus_child_flag = false;
317  }
318 
319  if (remove_hash_child_flag) {
320  entry->second.count.set_hash_child(false);
321  remove_hash_child_flag = false;
322  }
323 
324  decrease_count_storage(entry->second.count);
325  if (entry->second.count.value() == 0) {
326  remove_plus_child_flag = (entry->first.second == "+");
327  remove_hash_child_flag = (entry->first.second == "#");
328 
329  // Erase in unordered map only invalidates erased iterator
330  // other iterators are unaffected
331  map.erase(entry->first);
332  }
333  }
334 
335  auto root = get_root();
336  if (remove_plus_child_flag) {
337  root->second.count.set_plus_child(false);
338  }
339 
340  if (remove_hash_child_flag) {
341  root->second.count.set_hash_child(false);
342  }
343  }
344 
345  template <typename ThisType, typename Output>
346  static void find_match_impl(ThisType& self, string_view topic, Output&& callback) {
347  using iterator_type = decltype(self.map.end()); // const_iterator or iterator depends on self
348 
349  std::vector<iterator_type> entries;
350  entries.push_back(self.get_root());
351 
353  topic,
354  [&self, &entries, &callback](string_view t) {
355  std::vector<iterator_type> new_entries;
356 
357  for (auto& entry : entries) {
358  auto parent = entry->second.id;
359  auto i = self.map.find(path_entry_key(parent, t));
360  if (i != self.map.end()) {
361  new_entries.push_back(i);
362  }
363 
364  if (entry->second.count .has_plus_child()) {
365  i = self.map.find(path_entry_key(parent, string_view("+")));
366  if (i != self.map.end()) {
367  if (parent != self.root_node_id || t.empty() || t[0] != '$') {
368  new_entries.push_back(i);
369  }
370  }
371  }
372 
373  if (entry->second.count.has_hash_child()) {
374  i = self.map.find(path_entry_key(parent, string_view("#")));
375  if (i != self.map.end()) {
376  if (parent != self.root_node_id || t.empty() || t[0] != '$'){
377  callback(i->second.value);
378  }
379  }
380  }
381  }
382 
383  std::swap(entries, new_entries);
384  return !entries.empty();
385  }
386  );
387 
388  for (auto& entry : entries) {
389  callback(entry->second.value);
390  }
391  }
392 
393  // Find all topic filters that match the specified topic
394  template<typename Output>
395  void find_match(string_view topic, Output&& callback) const {
396  find_match_impl(*this, topic, std::forward<Output>(callback));
397  }
398 
399  // Find all topic filters and allow modification
400  template<typename Output>
401  void modify_match(string_view topic, Output&& callback) {
402  find_match_impl(*this, topic, std::forward<Output>(callback));
403  }
404 
405  template<typename ThisType, typename Output>
406  static void handle_to_iterators(ThisType& self, handle const &h, Output&& output) {
407  auto i = h;
408  while(i != self.root_key) {
409  auto entry_iter = self.map.find(i);
410  if (entry_iter == self.map.end()) {
411  throw_invalid_handle();
412  }
413 
414  output(entry_iter);
415  i = entry_iter->second.parent;
416  }
417  }
418 
419  // Exceptions used
420  static void throw_invalid_topic_filter() { throw std::runtime_error("Subscription map invalid topic filter was specified"); }
421  static void throw_invalid_handle() { throw std::runtime_error("Subscription map invalid handle was specified"); }
422  static void throw_max_stored_topics() { throw std::overflow_error("Subscription map maximum number of stored topic filters reached"); }
423 
424  // Get the iterators of a handle
425  std::vector<map_type_iterator> handle_to_iterators(handle const &h) {
426  std::vector<map_type_iterator> result;
427  handle_to_iterators(*this, h, [&result](map_type_iterator i) { result.push_back(i); });
428  std::reverse(result.begin(), result.end());
429  return result;
430  }
431 
432  // Increase the number of subscriptions for this handle
434  handle_to_iterators(*this, h, [](map_type_iterator i) {
435  increase_count_storage(i->second.count);
436  });
437  }
438 
439  // Increase the map size (total number of subscriptions stored)
441  if(map_size == std::numeric_limits<decltype(map_size)>::max()) {
442  throw_max_stored_topics();
443  }
444 
445  ++map_size;
446  }
447 
448  // Decrease the map size (total number of subscriptions stored)
450  BOOST_ASSERT(map_size > 0);
451  --map_size;
452  }
453 
454  // Increase the number of subscriptions for this path
455  void increase_subscriptions(std::vector<map_type_iterator> const &path) {
456  for (auto i : path) {
457  increase_count_storage(i->second.count);
458  }
459  }
460 
462  {
463  // Create the root node
464  root_node_id = generate_node_id();
465  root_key = path_entry_key(generate_node_id(), buffer());
466  map.emplace(root_key, path_entry(root_node_id, path_entry_key()));
467  }
468 
469 public:
470  // Return the number of elements in the tree
471  std::size_t internal_size() const { return map.size(); }
472 
473  // Return the number of registered topic filters
474  std::size_t size() const { return this->map_size; }
475 
476  // Lookup a topic filter
477  optional<handle> lookup(string_view topic_filter) {
478  auto path = this->find_topic_filter(topic_filter);
479  if(path.empty())
480  return optional<handle>();
481  else
482  return this->path_to_handle(force_move(path));
483  }
484 
485  // Get path of topic_filter
486  std::string handle_to_topic_filter(handle const &h) const {
487  std::string result;
488 
489  handle_to_iterators(*this, h, [&result](map_type_const_iterator i) {
490  if (result.empty()) {
491  result = std::string(i->first.second);
492  }
493  else {
494  result = std::string(i->first.second) + "/" + result;
495  }
496  });
497 
498  return result;
499  }
500 };
501 
502 template<typename Value>
504  : public subscription_map_base< optional<Value> > {
505 
506 public:
507 
508  // Handle of an entry
510 
511  // Insert a value at the specified topic_filter
512  template <typename V>
513  std::pair<handle, bool> insert(string_view topic_filter, V&& value) {
514  auto existing_subscription = this->find_topic_filter(topic_filter);
515  if (!existing_subscription.empty()) {
516  if(existing_subscription.back()->second.value)
517  return std::make_pair(this->path_to_handle(force_move(existing_subscription)), false);
518 
519  existing_subscription.back()->second.value.emplace(std::forward<V>(value));
520  return std::make_pair(this->path_to_handle(force_move(existing_subscription)), true);
521  }
522 
523  auto new_topic_filter = this->create_topic_filter(topic_filter);
524  new_topic_filter.back()->second.value = value;
525  this->increase_map_size();
526  return std::make_pair(this->path_to_handle(force_move(new_topic_filter)), true);
527  }
528 
529  // Update a value at the specified topic filter
530  template <typename V>
531  void update(string_view topic_filter, V&& value) {
532  auto path = this->find_topic_filter(topic_filter);
533  if (path.empty()) {
535  }
536 
537  path.back()->second.value.emplace(std::forward<V>(value));
538  }
539 
540  template <typename V>
541  void update(handle const &h, V&& value) {
542  auto entry_iter = this->get_key(h);
543  if (entry_iter == this->end()) {
545  }
546  entry_iter->second.value.emplace(std::forward<V>(value));
547  }
548 
549  // Remove a value at the specified topic filter
550  std::size_t erase(string_view topic_filter) {
551  auto path = this->find_topic_filter(topic_filter);
552  if (path.empty() || !path.back()->second.value) {
553  return 0;
554  }
555 
556  this->remove_topic_filter(path);
557  this->decrease_map_size();
558  return 1;
559  }
560 
561  // Remove a value using a handle
562  std::size_t erase(handle const &h) {
563  auto path = this->handle_to_iterators(h);
564  if (path.empty() || !path.back()->second.value) {
565  return 0;
566  }
567 
568  this->remove_topic_filter(path);
569  this->decrease_map_size();
570  return 1;
571  }
572 
573  // Find all topic filters that match the specified topic
574  template<typename Output>
575  void find(string_view topic, Output&& callback) const {
576  this->find_match(
577  topic,
578  [&callback]( optional<Value> const& value ) {
579  if (value) {
580  callback(value.value());
581  }
582  }
583  );
584  }
585 
586 };
587 
588 template<typename Key, typename Value, class Hash = std::hash<Key>, class Pred = std::equal_to<Key>, class Cont = std::unordered_map<Key, Value, Hash, Pred, std::allocator< std::pair<const Key, Value> > > >
590  : public subscription_map_base< Cont >
591 {
592 
593 public:
594  using container_t = Cont;
595 
596  // Handle of an entry
598 
599  // Insert a key => value at the specified topic filter
600  // returns the handle and true if key was inserted, false if key was updated
601  template <typename K, typename V>
602  std::pair<handle, bool> insert_or_assign(string_view topic_filter, K&& key, V&& value) {
603  auto path = this->find_topic_filter(topic_filter);
604  if (path.empty()) {
605  auto new_topic_filter = this->create_topic_filter(topic_filter);
606  new_topic_filter.back()->second.value.emplace(std::forward<K>(key), std::forward<V>(value));
607  this->increase_map_size();
608  return std::make_pair(this->path_to_handle(force_move(new_topic_filter)), true);
609  }
610  else {
611  auto& subscription_set = path.back()->second.value;
612 
613 #if __cpp_lib_unordered_map_try_emplace >= 201411L
614  auto insert_result = subscription_set.insert_or_assign(std::forward<K>(key), std::forward<V>(value));
615  if(insert_result.second) {
616  this->increase_subscriptions(path);
617  this->increase_map_size();
618  }
619  return std::make_pair(this->path_to_handle(force_move(path)), insert_result.second);
620 #else
621  auto iter = subscription_set.find(key);
622  if(iter == subscription_set.end()) {
623  subscription_set.emplace(std::forward<K>(key), std::forward<V>(value));
624  this->increase_subscriptions(path);
625  this->increase_map_size();
626  } else {
627  iter->second = std::forward<V>(value);
628  }
629  return std::make_pair(this->path_to_handle(force_move(path)), iter == subscription_set.end());
630 
631 #endif
632  }
633  }
634 
635  // Insert a key => value with a handle to the topic filter
636  // returns the handle and true if key was inserted, false if key was updated
637  template <typename K, typename V>
638  std::pair<handle, bool> insert_or_assign(handle const &h, K&& key, V&& value) {
639  auto h_iter = this->get_key(h);
640  if (h_iter == this->end()) {
641  this->throw_invalid_handle();
642  }
643 
644  auto& subscription_set = h_iter->second.value;
645 
646 #if __cpp_lib_unordered_map_try_emplace >= 201411L
647  auto insert_result = subscription_set.insert_or_assign(std::forward<K>(key), std::forward<V>(value));
648  if(insert_result.second) {
649  this->increase_subscriptions(h);
650  this->increase_map_size();
651  }
652  return std::make_pair(h, insert_result.second);
653 #else
654  auto iter = subscription_set.find(key);
655  if(iter == subscription_set.end()) {
656  subscription_set.emplace(std::forward<K>(key), std::forward<V>(value));
657  this->increase_subscriptions(h);
658  this->increase_map_size();
659  } else {
660  iter->second = std::forward<V>(value);
661  }
662  return std::make_pair(h, iter == subscription_set.end());
663 #endif
664  }
665 
666  // Remove a value at the specified handle
667  // returns the number of removed elements
668  std::size_t erase(handle const &h, Key const& key) {
669  // Find the handle in the map
670  auto h_iter = this->get_key(h);
671  if (h_iter == this->end()) {
672  this->throw_invalid_handle();
673  }
674 
675  // Remove the specified value
676  auto result = h_iter->second.value.erase(key);
677  if (result) {
679  this->decrease_map_size();
680  }
681 
682  return result;
683  }
684 
685  // Remove a value at the specified topic filter
686  // returns the number of removed elements
687  std::size_t erase(string_view topic_filter, Key const& key) {
688  // Find the topic filter in the map
689  auto path = this->find_topic_filter(topic_filter);
690  if (path.empty()) {
691  return 0;
692  }
693 
694  // Remove the specified value
695  auto result = path.back()->second.value.erase(key);
696  if (result) {
697  this->decrease_map_size();
698  this->remove_topic_filter(path);
699  }
700 
701  return result;
702  }
703 
704  // Find all topic filters that match the specified topic
705  template<typename Output>
706  void find(string_view topic, Output&& callback) const {
707  this->find_match(
708  topic,
709  [&callback]( Cont const &values ) {
710  for (auto const& i : values) {
711  callback(i.first, i.second);
712  }
713  }
714  );
715  }
716 
717  // Find all topic filters that match and allow modification
718  template<typename Output>
719  void modify(string_view topic, Output&& callback) {
720  this->modify_match(
721  topic,
722  [&callback]( Cont &values ) {
723  for (auto& i : values) {
724  callback(i.first, i.second);
725  }
726  }
727  );
728  }
729 
730  template<typename Output>
731  void dump(Output &out) {
732  out << "Root node id: " << this->root_node_id << std::endl;
733  for (auto const& i: this->get_map()) {
734  out << "(" << i.first.first << ", " << i.first.second << "): id: " << i.second.id << ", size: " << i.second.value.size() << ", value: " << i.second.count.value << std::endl;
735  }
736  }
737 
738 };
739 
741 
742 #endif // MQTT_BROKER_SUBSCRIPTION_MAP_HPP
#define MQTT_BROKER_NS_END
Definition: broker_namespace.hpp:22
#define MQTT_BROKER_NS_BEGIN
Definition: broker_namespace.hpp:21
Definition: subscription_map.hpp:591
std::pair< handle, bool > insert_or_assign(handle const &h, K &&key, V &&value)
Definition: subscription_map.hpp:638
Cont container_t
Definition: subscription_map.hpp:594
void modify(string_view topic, Output &&callback)
Definition: subscription_map.hpp:719
std::size_t erase(string_view topic_filter, Key const &key)
Definition: subscription_map.hpp:687
std::size_t erase(handle const &h, Key const &key)
Definition: subscription_map.hpp:668
void dump(Output &out)
Definition: subscription_map.hpp:731
std::pair< handle, bool > insert_or_assign(string_view topic_filter, K &&key, V &&value)
Definition: subscription_map.hpp:602
typename subscription_map_base< Value >::handle handle
Definition: subscription_map.hpp:597
void find(string_view topic, Output &&callback) const
Definition: subscription_map.hpp:706
Definition: subscription_map.hpp:504
void update(handle const &h, V &&value)
Definition: subscription_map.hpp:541
typename subscription_map_base< Value >::handle handle
Definition: subscription_map.hpp:509
std::size_t erase(handle const &h)
Definition: subscription_map.hpp:562
std::size_t erase(string_view topic_filter)
Definition: subscription_map.hpp:550
void update(string_view topic_filter, V &&value)
Definition: subscription_map.hpp:531
std::pair< handle, bool > insert(string_view topic_filter, V &&value)
Definition: subscription_map.hpp:513
void find(string_view topic, Output &&callback) const
Definition: subscription_map.hpp:575
Definition: subscription_map.hpp:170
handle path_to_handle(std::vector< map_type_iterator > const &path) const
Definition: subscription_map.hpp:244
subscription_map_base()
Definition: subscription_map.hpp:461
std::pair< node_id_t, buffer > path_entry_key
Definition: subscription_map.hpp:173
std::size_t internal_size() const
Definition: subscription_map.hpp:471
void remove_topic_filter(std::vector< map_type_iterator > const &path)
Definition: subscription_map.hpp:308
std::vector< map_type_iterator > handle_to_iterators(handle const &h)
Definition: subscription_map.hpp:425
map_type_iterator get_root()
Definition: subscription_map.hpp:232
void decrease_map_size()
Definition: subscription_map.hpp:449
void increase_map_size()
Definition: subscription_map.hpp:440
optional< handle > lookup(string_view topic_filter)
Definition: subscription_map.hpp:477
void increase_subscriptions(std::vector< map_type_iterator > const &path)
Definition: subscription_map.hpp:455
map_type_iterator get_key(path_entry_key key)
Definition: subscription_map.hpp:239
map_type_iterator end()
Definition: subscription_map.hpp:241
path_entry_key root_key
Definition: subscription_map.hpp:228
std::size_t node_id_t
Definition: subscription_map.hpp:172
node_id_t root_node_id
Definition: subscription_map.hpp:229
void increase_subscriptions(handle const &h)
Definition: subscription_map.hpp:433
static void find_match_impl(ThisType &self, string_view topic, Output &&callback)
Definition: subscription_map.hpp:346
map_type_const_iterator get_root() const
Definition: subscription_map.hpp:233
void find_match(string_view topic, Output &&callback) const
Definition: subscription_map.hpp:395
path_entry_key handle
Definition: subscription_map.hpp:174
std::vector< map_type_iterator > find_topic_filter(string_view topic_filter)
Definition: subscription_map.hpp:248
static void throw_invalid_topic_filter()
Definition: subscription_map.hpp:420
size_t map_size
Definition: subscription_map.hpp:237
static void handle_to_iterators(ThisType &self, handle const &h, Output &&output)
Definition: subscription_map.hpp:406
std::vector< map_type_iterator > create_topic_filter(string_view topic_filter)
Definition: subscription_map.hpp:271
void modify_match(string_view topic, Output &&callback)
Definition: subscription_map.hpp:401
static void throw_invalid_handle()
Definition: subscription_map.hpp:421
static void throw_max_stored_topics()
Definition: subscription_map.hpp:422
map_type const & get_map() const
Definition: subscription_map.hpp:242
map_type_iterator begin()
Definition: subscription_map.hpp:240
std::string handle_to_topic_filter(handle const &h) const
Definition: subscription_map.hpp:486
std::size_t size() const
Definition: subscription_map.hpp:474
id
Definition: property_id.hpp:19
boost::string_ref string_view
Definition: string_view.hpp:64
constexpr std::remove_reference_t< T > && force_move(T &&t)
Definition: move.hpp:20
const_buffer buffer(MQTT_NS::buffer const &data)
create boost::asio::const_buffer from the MQTT_NS::buffer boost::asio::const_buffer is a kind of view...
Definition: buffer.hpp:253
void increment_value()
Definition: subscription_map.hpp:108
void set_hash_child(bool v)
Definition: subscription_map.hpp:116
void set_plus_child(bool v)
Definition: subscription_map.hpp:121
void decrement_value()
Definition: subscription_map.hpp:111
static constexpr std::size_t max()
Definition: subscription_map.hpp:102
count_storage(std::uint32_t v=1)
Definition: subscription_map.hpp:98
bool has_hash_child() const
Definition: subscription_map.hpp:115
std::uint32_t value() const
Definition: subscription_map.hpp:104
bool has_plus_child() const
Definition: subscription_map.hpp:120
void set_value(std::uint32_t v)
Definition: subscription_map.hpp:105
void decrement_value()
Definition: subscription_map.hpp:148
bool has_plus_child() const
Definition: subscription_map.hpp:157
static constexpr std::uint64_t max()
Definition: subscription_map.hpp:139
void set_plus_child(bool v)
Definition: subscription_map.hpp:158
void set_hash_child(bool v)
Definition: subscription_map.hpp:153
bool has_hash_child() const
Definition: subscription_map.hpp:152
std::uint64_t value() const
Definition: subscription_map.hpp:141
void increment_value()
Definition: subscription_map.hpp:145
void set_value(std::uint64_t v)
Definition: subscription_map.hpp:142
count_storage(std::uint64_t v=1)
Definition: subscription_map.hpp:135
Definition: subscription_map.hpp:91
void topic_filter_tokenizer(Iterator first, Iterator last, Output write)
Definition: topic_filter_tokenizer.hpp:20