SPSP
Simple publish-subscribe protocol. Connects low power IoT clients to MQTT.
All Classes Files Functions Variables Typedefs Enumerations
wildcard_trie.hpp
Go to the documentation of this file.
1 
10 #pragma once
11 
12 #include <functional>
13 #include <memory>
14 #include <queue>
15 #include <string>
16 #include <unordered_map>
17 #include <vector>
18 
19 namespace SPSP
20 {
33  template <typename TValue>
35  {
40  struct Node
41  {
42  TValue value;
43  std::unordered_map<std::string, std::unique_ptr<Node>> childs;
44  size_t levelIndex = 0;
45  bool isLeaf = false;
46  };
47 
48  using BFSQueueT = std::queue<std::pair<std::string, const Node*>>;
49 
50  const std::string m_lSep;
51  const std::string m_lSingleWild;
52  const std::string m_lMultiWild;
53 
54  Node m_root;
55 
56  public:
64  WildcardTrie(const std::string& levelSeparator = "/",
65  const std::string& singleLevelWildcard = "+",
66  const std::string& multiLevelWildcard = "#") noexcept
67  : m_lSep{levelSeparator}, m_lSingleWild{singleLevelWildcard},
68  m_lMultiWild{multiLevelWildcard}
69  {}
70 
77  TValue& operator[](const std::string& key)
78  {
79  Node* cur = &m_root;
80  auto levels = this->splitToLevels(key);
81 
82  // Get or create child on each level
83  for (size_t i = 0; i < levels.size(); i++) {
84  auto& level = levels[i];
85 
86  // Create new child
87  if (cur->childs.find(level) == cur->childs.end()) {
88  cur->childs[level] = std::make_unique<Node>();
89  cur->childs[level]->levelIndex = i + 1;
90  }
91 
92  // Move to next level
93  cur = cur->childs.at(level).get();
94  }
95 
96  cur->isLeaf = true;
97 
98  return cur->value;
99  }
100 
107  void insert(const std::string& key, const TValue& value)
108  {
109  (*this)[key] = value;
110  }
111 
119  bool remove(const std::string& key)
120  {
121  Node* cur = &m_root;
122  auto levels = this->splitToLevels(key);
123 
124  std::vector<Node*> nodeStack;
125 
126  // Get node if exists
127  for (auto& level : levels) {
128  nodeStack.push_back(cur);
129 
130  if (cur->childs.find(level) == cur->childs.end()) {
131  return false;
132  }
133  cur = cur->childs.at(level).get();
134  }
135 
136  // Can't remove non-leaf node
137  if (!cur->isLeaf) {
138  return false;
139  }
140 
141  cur->isLeaf = false;
142 
143  if (cur->childs.empty()) {
144  // Delete all redundant ancestors
145  // There is `int` instead of `size_t`, because we need signed type.
146  for (int i = nodeStack.size() - 1; i >= 0; i--) {
147  Node* node = nodeStack.at(i);
148  if (node->isLeaf || node->childs.size() > 1 ||
149  node == &m_root) {
150  node->childs.erase(levels.at(i));
151 
152  // Previous ancestors are no longer redundant
153  break;
154  }
155  }
156  }
157 
158  return true;
159  }
160 
161  using FindReturnT = std::unordered_map<std::string, const TValue&>;
162 
169  const FindReturnT find(const std::string& key) const
170  {
171  auto levels = this->splitToLevels(key);
172 
173  FindReturnT values;
174 
175  // Queue for to-be-processed nodes
176  BFSQueueT nodeQueue;
177  nodeQueue.push({ "", &m_root });
178 
179  while (!nodeQueue.empty()) {
180  auto [nodeKey, node] = nodeQueue.front();
181 
182  if (node->levelIndex == levels.size() && node->isLeaf) {
183  // Match
184  values.insert({ nodeKey, node->value });
185  }
186  else if (node->levelIndex < levels.size()) {
187  // Enqueue relevant childs
188  for (auto& [childLevel, childNode] : node->childs) {
189  std::string childKey = nodeKey == ""
190  ? childLevel
191  : nodeKey + m_lSep + childLevel;
192 
193  if (childLevel == levels.at(node->levelIndex) ||
194  childLevel == m_lSingleWild) {
195  // Key matches or has single-level wildcard
196  nodeQueue.push({ childKey, childNode.get() });
197  }
198  else if (childLevel == m_lMultiWild &&
199  childNode->isLeaf) {
200  // Multi-level wildcard
201  values.insert({ childKey, childNode->value });
202  }
203  }
204  }
205 
206  nodeQueue.pop();
207  }
208 
209  return values;
210  }
211 
218  void forEach(std::function<void(const std::string& key, const TValue& value)> f)
219  {
220  // Queue for to-be-processed nodes
221  BFSQueueT nodeQueue;
222  nodeQueue.push({ "", &m_root });
223 
224  while (!nodeQueue.empty()) {
225  auto [nodeKey, node] = nodeQueue.front();
226 
227  // Call function
228  if (node->isLeaf) {
229  f(nodeKey, node->value);
230  }
231 
232  // Enqueue children
233  for (auto& [childLevel, childNode] : node->childs) {
234  std::string childKey = nodeKey == ""
235  ? childLevel
236  : nodeKey + m_lSep + childLevel;
237  nodeQueue.push({ childKey, childNode.get() });
238  }
239 
240  nodeQueue.pop();
241  }
242  }
243 
250  bool empty() const
251  {
252  return m_root.childs.empty();
253  }
254 
255  protected:
264  const std::vector<std::string> splitToLevels(const std::string& key) const
265  {
266  size_t curPos = 0, nextPos;
267  std::vector<std::string> levels;
268 
269  while ((nextPos = key.find(m_lSep, curPos)) != std::string::npos) {
270  levels.push_back(key.substr(curPos, nextPos - curPos));
271  curPos = nextPos + m_lSep.length();
272  }
273 
274  // Add the rest
275  levels.push_back(key.substr(curPos));
276 
277  return levels;
278  }
279  };
280 } // namespace SPSP
SPSP::WildcardTrie::find
const FindReturnT find(const std::string &key) const
Finds key in trie.
Definition: wildcard_trie.hpp:169
SPSP::WildcardTrie::splitToLevels
const std::vector< std::string > splitToLevels(const std::string &key) const
Splits key to levels.
Definition: wildcard_trie.hpp:264
SPSP::WildcardTrie::empty
bool empty() const
Empty predicate.
Definition: wildcard_trie.hpp:250
SPSP::WildcardTrie::operator[]
TValue & operator[](const std::string &key)
Gets/inserts current value of key
Definition: wildcard_trie.hpp:77
SPSP::WildcardTrie::remove
bool remove(const std::string &key)
Removes key from trie.
Definition: wildcard_trie.hpp:119
SPSP::WildcardTrie::WildcardTrie
WildcardTrie(const std::string &levelSeparator="/", const std::string &singleLevelWildcard="+", const std::string &multiLevelWildcard="#") noexcept
Constructs a new object.
Definition: wildcard_trie.hpp:64
SPSP::WildcardTrie::forEach
void forEach(std::function< void(const std::string &key, const TValue &value)> f)
Iterates through each item in trie and calls callback on each one.
Definition: wildcard_trie.hpp:218
SPSP::WildcardTrie
String-based trie with wildcard support.
Definition: wildcard_trie.hpp:34
SPSP::WildcardTrie::insert
void insert(const std::string &key, const TValue &value)
Inserts (or updates) key-value pair.
Definition: wildcard_trie.hpp:107