paho-mqtt-cpp
MQTT C++ Client for POSIX and Windows
Loading...
Searching...
No Matches
topic_matcher.h
Go to the documentation of this file.
1
7
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
35namespace mqtt {
36
38
76template <typename T>
78{
79public:
81 using mapped_type = T;
82 using value_type = std::pair<key_type, mapped_type>;
85
86private:
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
112public:
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:261
const value_type * operator->() const noexcept
Definition: topic_matcher.h:279
const_reference operator*() const noexcept
Definition: topic_matcher.h:272
Definition: topic_matcher.h:113
const value_type * operator->() const noexcept
Definition: topic_matcher.h:225
iterator operator++(int) noexcept
Definition: topic_matcher.h:232
reference operator*() noexcept
Definition: topic_matcher.h:204
const_reference operator*() const noexcept
Definition: topic_matcher.h:211
iterator & operator++() noexcept
Definition: topic_matcher.h:241
bool operator!=(const iterator &other) const noexcept
Definition: topic_matcher.h:252
value_type * operator->() noexcept
Definition: topic_matcher.h:218
Definition: topic_matcher.h:78
value_type reference
Definition: topic_matcher.h:83
void insert(const value_type &val)
Definition: topic_matcher.h:334
const_iterator cend() const noexcept
Definition: topic_matcher.h:400
string key_type
Definition: topic_matcher.h:80
void insert(value_type &&val)
Definition: topic_matcher.h:313
std::pair< key_type, mapped_type > value_type
Definition: topic_matcher.h:82
const_iterator end() const noexcept
Definition: topic_matcher.h:389
T mapped_type
Definition: topic_matcher.h:81
const_iterator find(const key_type &key) const
Definition: topic_matcher.h:362
topic_matcher(std::initializer_list< value_type > lst)
Definition: topic_matcher.h:303
const_iterator matches(const string &topic) const
Definition: topic_matcher.h:378
iterator matches(const string &topic)
Definition: topic_matcher.h:370
iterator find(const key_type &key)
Definition: topic_matcher.h:343
const value_type & const_reference
Definition: topic_matcher.h:84
topic_matcher()
Definition: topic_matcher.h:287
Definition: topic.h:44
static std::vector< std::string > split(const std::string &topic)
Definition: async_client.h:49
std::string string
Definition: types.h:40