paho-mqtt-cpp
MQTT C++ Client for POSIX and Windows
topic_matcher.h
Go to the documentation of this file.
1 
8 /*******************************************************************************
9  * Copyright (c) 2022 Frank Pagliughi <fpagliughi@mindspring.com>
10  *
11  * All rights reserved. This program and the accompanying materials
12  * are made available under the terms of the Eclipse Public License v2.0
13  * and Eclipse Distribution License v1.0 which accompany this distribution.
14  *
15  * The Eclipse Public License is available at
16  * http://www.eclipse.org/legal/epl-v20.html
17  * and the Eclipse Distribution License is available at
18  * http://www.eclipse.org/org/documents/edl-v10.php.
19  *
20  * Contributors:
21  * Frank Pagliughi - initial implementation and documentation
22  *******************************************************************************/
23 
24 #ifndef __mqtt_topic_matcher_h
25 #define __mqtt_topic_matcher_h
26 
27 #include "mqtt/types.h"
28 #include "mqtt/topic.h"
29 #include <vector>
30 #include <map>
31 #include <forward_list>
32 #include <initializer_list>
33 #include <memory>
34 
35 namespace mqtt {
36 
38 
76 template <typename T>
78 {
79 public:
80  using key_type = string;
81  using mapped_type = T;
82  using value_type = std::pair<key_type, mapped_type>;
84  using const_reference = const value_type&;
85 
86 private:
90  struct node
91  {
92  using ptr_t = std::unique_ptr<node>;
93  using map_t = std::map<string, ptr_t>;
94 
96  std::unique_ptr<value_type> content;
98  map_t children;
99 
100  static ptr_t create() {
101  // TODO: When available (C++14,17) use
102  // std::make_unique<node>();
103  return std::unique_ptr<node>(new node{});
104  }
105  };
106  using node_ptr = typename node::ptr_t;
107  using node_map = typename node::map_t;
108 
110  node_ptr root_;
111 
112 public:
113  class iterator {
117  struct search_node {
119  node* node_;
121  std::forward_list<string> syms_;
122 
123  search_node() : node_(nullptr) {}
124  search_node(node* nd, const std::forward_list<string>& sy)
125  : node_(nd), syms_(sy) {}
126  search_node(node* nd, std::forward_list<string>&& sy)
127  : node_(nd), syms_(std::move(sy)) {}
128  };
129 
131  value_type* pval_;
133  search_node snode_;
135  std::vector<search_node> nodes_;
136 
144  void next() {
145  pval_ = nullptr;
146 
147  // Can't move if we're already at the end
148  if (!snode_.node_)
149  return;
150 
151  if (snode_.syms_.empty()) {
152  pval_ = snode_.node_->content.get();
153  }
154  else {
155  typename node_map::iterator child;
156  auto map_end = snode_.node_->children.end();
157  auto sym = snode_.syms_.front();
158 
159  if ((child = snode_.node_->children.find(sym)) != map_end) {
160  auto syms = snode_.syms_;
161  syms.pop_front();
162  nodes_.push_back({child->second.get(), std::move(syms)});
163  }
164  if ((child = snode_.node_->children.find("+")) != map_end) {
165  auto syms = snode_.syms_;
166  syms.pop_front();
167  nodes_.push_back({child->second.get(), std::move(syms)});
168  }
169  if ((child = snode_.node_->children.find("#")) != map_end) {
170  pval_ = child->second->content.get();
171  }
172  }
173 
174  if (!nodes_.empty()) {
175  // TODO: List pop_front()?
176  snode_ = nodes_.back();
177  nodes_.pop_back();
178  if (!pval_) {
179  // Recurse
180  return this->next();
181  }
182  }
183  else {
184  snode_ = search_node();
185  }
186  }
187 
188  friend class topic_matcher;
189 
190  iterator() : pval_(nullptr) {}
191  iterator(value_type* pval) : pval_(pval) {}
192  iterator(node* root, const string& topic) : pval_(nullptr) {
193  auto v = topic::split(topic);
194  std::forward_list<string> syms(v.begin(), v.end());
195  snode_ = search_node(root, std::move(syms));
196  next();
197  }
198 
199  public:
204  reference operator*() noexcept {
205  return *pval_;
206  }
211  const_reference operator*() const noexcept {
212  return *pval_;
213  }
218  value_type* operator->() noexcept {
219  return pval_;
220  }
225  const value_type* operator->() const noexcept {
226  return pval_;
227  }
232  iterator operator++(int) noexcept {
233  auto tmp = *this;
234  this->next();
235  return tmp;
236  }
241  iterator& operator++() noexcept {
242  this->next();
243  return *this;
244  }
252  bool operator!=(const iterator& other) const noexcept {
253  // TODO: Is this sufficient in the long run?
254  return pval_ != other.pval_ || snode_.node_ != other.snode_.node_;
255  }
256  };
257 
261  class const_iterator : public iterator {
262  using base = iterator;
263 
264  friend class topic_matcher;
265  const_iterator(iterator it) : base(it) {}
266 
267  public:
272  const_reference operator*() const noexcept {
273  return base::operator*();
274  }
279  const value_type* operator->() const noexcept {
280  return base::operator->();
281  }
282  };
283 
288  : root_(node::create()) {}
303  topic_matcher(std::initializer_list<value_type> lst)
304  : root_(node::create()) {
305  for (const auto& v : lst) {
306  insert(v);
307  }
308  }
313  void insert(value_type&& val) {
314  auto nd = root_.get();
315  auto fields = topic::split(val.first);
316 
317  for (auto& field : fields) {
318  auto it = nd->children.find(field);
319  if (it == nd->children.end()) {
320  nd->children[field] = node::create();
321  it = nd->children.find(field);
322  }
323  nd = it->second.get();
324  }
325  // TODO: When available (C++14,17) use:
326  // std::make_unique<value_type>(std::move(val));
327  nd->content = std::unique_ptr<value_type>(new value_type{std::move(val)});
328  }
334  void insert(const value_type& val) {
335  value_type v { val };
336  this->insert(std::move(v));
337  }
343  iterator find(const key_type& key) {
344  auto nd = root_.get();
345  auto fields = topic::split(key);
346 
347  for (auto& field : fields) {
348  auto it = nd->children.find(field);
349  if (it == nd->children.end())
350  return end();
351 
352  nd = it->second.get();
353  }
354  return iterator{ nd->content.get() };
355  }
362  const_iterator find(const key_type& key) const {
363  return const_cast<topic_matcher*>(this)->find(key);
364  }
370  iterator matches(const string& topic) {
371  return iterator(root_.get(), topic);
372  }
378  const_iterator matches(const string& topic) const {
379  return iterator(root_.get(), topic);
380  }
389  const_iterator end() const noexcept {
390  return iterator {};
391  }
400  const_iterator cend() const noexcept {
401  return iterator {};
402  }
403 };
404 
406 // end namespace mqtt
407 }
408 
409 #endif // __mqtt_topic_matcher_h
410 
Definition: topic_matcher.h:113
void insert(const value_type &val)
Definition: topic_matcher.h:334
std::string string
Definition: types.h:40
T mapped_type
Definition: topic_matcher.h:81
const_reference operator*() const noexcept
Definition: topic_matcher.h:211
reference operator*() noexcept
Definition: topic_matcher.h:204
value_type reference
Definition: topic_matcher.h:83
value_type * operator->() noexcept
Definition: topic_matcher.h:218
Definition: topic_matcher.h:77
Definition: topic.h:43
const value_type * operator->() const noexcept
Definition: topic_matcher.h:225
void insert(value_type &&val)
Definition: topic_matcher.h:313
const_reference operator*() const noexcept
Definition: topic_matcher.h:272
iterator & operator++() noexcept
Definition: topic_matcher.h:241
static std::vector< std::string > split(const std::string &topic)
iterator find(const key_type &key)
Definition: topic_matcher.h:343
const_iterator find(const key_type &key) const
Definition: topic_matcher.h:362
iterator matches(const string &topic)
Definition: topic_matcher.h:370
topic_matcher()
Definition: topic_matcher.h:287
const value_type & const_reference
Definition: topic_matcher.h:84
std::pair< key_type, mapped_type > value_type
Definition: topic_matcher.h:82
const_iterator matches(const string &topic) const
Definition: topic_matcher.h:378
const_iterator cend() const noexcept
Definition: topic_matcher.h:400
const_iterator end() const noexcept
Definition: topic_matcher.h:389
topic_matcher(std::initializer_list< value_type > lst)
Definition: topic_matcher.h:303
const value_type * operator->() const noexcept
Definition: topic_matcher.h:279
string key_type
Definition: topic_matcher.h:80
Definition: async_client.h:49
bool operator!=(const iterator &other) const noexcept
Definition: topic_matcher.h:252
iterator operator++(int) noexcept
Definition: topic_matcher.h:232
Definition: topic_matcher.h:261