trie.cpp
Go to the documentation of this file.
1 /* SPDX-License-Identifier: MPL-2.0 */
2 
3 #include "precompiled.hpp"
4 #include "macros.hpp"
5 #include "err.hpp"
6 #include "trie.hpp"
7 
8 #include <stdlib.h>
9 
10 #include <new>
11 #include <algorithm>
12 
13 zmq::trie_t::trie_t () : _refcnt (0), _min (0), _count (0), _live_nodes (0)
14 {
15 }
16 
18 {
19  if (_count == 1) {
20  zmq_assert (_next.node);
21  LIBZMQ_DELETE (_next.node);
22  } else if (_count > 1) {
23  for (unsigned short i = 0; i != _count; ++i) {
24  LIBZMQ_DELETE (_next.table[i]);
25  }
26  free (_next.table);
27  }
28 }
29 
30 bool zmq::trie_t::add (unsigned char *prefix_, size_t size_)
31 {
32  // We are at the node corresponding to the prefix. We are done.
33  if (!size_) {
34  ++_refcnt;
35  return _refcnt == 1;
36  }
37 
38  const unsigned char c = *prefix_;
39  if (c < _min || c >= _min + _count) {
40  // The character is out of range of currently handled
41  // characters. We have to extend the table.
42  if (!_count) {
43  _min = c;
44  _count = 1;
45  _next.node = NULL;
46  } else if (_count == 1) {
47  const unsigned char oldc = _min;
48  trie_t *oldp = _next.node;
49  _count = (_min < c ? c - _min : _min - c) + 1;
50  _next.table =
51  static_cast<trie_t **> (malloc (sizeof (trie_t *) * _count));
52  alloc_assert (_next.table);
53  for (unsigned short i = 0; i != _count; ++i)
54  _next.table[i] = 0;
55  _min = std::min (_min, c);
56  _next.table[oldc - _min] = oldp;
57  } else if (_min < c) {
58  // The new character is above the current character range.
59  const unsigned short old_count = _count;
60  _count = c - _min + 1;
61  _next.table = static_cast<trie_t **> (
62  realloc (_next.table, sizeof (trie_t *) * _count));
63  zmq_assert (_next.table);
64  for (unsigned short i = old_count; i != _count; i++)
65  _next.table[i] = NULL;
66  } else {
67  // The new character is below the current character range.
68  const unsigned short old_count = _count;
69  _count = (_min + old_count) - c;
70  _next.table = static_cast<trie_t **> (
71  realloc (_next.table, sizeof (trie_t *) * _count));
72  zmq_assert (_next.table);
73  memmove (_next.table + _min - c, _next.table,
74  old_count * sizeof (trie_t *));
75  for (unsigned short i = 0; i != _min - c; i++)
76  _next.table[i] = NULL;
77  _min = c;
78  }
79  }
80 
81  // If next node does not exist, create one.
82  if (_count == 1) {
83  if (!_next.node) {
84  _next.node = new (std::nothrow) trie_t;
85  alloc_assert (_next.node);
86  ++_live_nodes;
87  zmq_assert (_live_nodes == 1);
88  }
89  return _next.node->add (prefix_ + 1, size_ - 1);
90  }
91  if (!_next.table[c - _min]) {
92  _next.table[c - _min] = new (std::nothrow) trie_t;
93  alloc_assert (_next.table[c - _min]);
94  ++_live_nodes;
95  zmq_assert (_live_nodes > 1);
96  }
97  return _next.table[c - _min]->add (prefix_ + 1, size_ - 1);
98 }
99 
100 bool zmq::trie_t::rm (unsigned char *prefix_, size_t size_)
101 {
102  // TODO: Shouldn't an error be reported if the key does not exist?
103  if (!size_) {
104  if (!_refcnt)
105  return false;
106  _refcnt--;
107  return _refcnt == 0;
108  }
109  const unsigned char c = *prefix_;
110  if (!_count || c < _min || c >= _min + _count)
111  return false;
112 
113  trie_t *next_node = _count == 1 ? _next.node : _next.table[c - _min];
114 
115  if (!next_node)
116  return false;
117 
118  const bool ret = next_node->rm (prefix_ + 1, size_ - 1);
119 
120  // Prune redundant nodes
121  if (next_node->is_redundant ()) {
122  LIBZMQ_DELETE (next_node);
123  zmq_assert (_count > 0);
124 
125  if (_count == 1) {
126  // The just pruned node is was the only live node
127  _next.node = 0;
128  _count = 0;
129  --_live_nodes;
130  zmq_assert (_live_nodes == 0);
131  } else {
132  _next.table[c - _min] = 0;
133  zmq_assert (_live_nodes > 1);
134  --_live_nodes;
135 
136  // Compact the table if possible
137  if (_live_nodes == 1) {
138  // We can switch to using the more compact single-node
139  // representation since the table only contains one live node
140  trie_t *node = 0;
141  // Since we always compact the table the pruned node must
142  // either be the left-most or right-most ptr in the node
143  // table
144  if (c == _min) {
145  // The pruned node is the left-most node ptr in the
146  // node table => keep the right-most node
147  node = _next.table[_count - 1];
148  _min += _count - 1;
149  } else if (c == _min + _count - 1) {
150  // The pruned node is the right-most node ptr in the
151  // node table => keep the left-most node
152  node = _next.table[0];
153  }
154  zmq_assert (node);
155  free (_next.table);
156  _next.node = node;
157  _count = 1;
158  } else if (c == _min) {
159  // We can compact the table "from the left".
160  // Find the left-most non-null node ptr, which we'll use as
161  // our new min
162  unsigned char new_min = _min;
163  for (unsigned short i = 1; i < _count; ++i) {
164  if (_next.table[i]) {
165  new_min = i + _min;
166  break;
167  }
168  }
169  zmq_assert (new_min != _min);
170 
171  trie_t **old_table = _next.table;
172  zmq_assert (new_min > _min);
173  zmq_assert (_count > new_min - _min);
174 
175  _count = _count - (new_min - _min);
176  _next.table =
177  static_cast<trie_t **> (malloc (sizeof (trie_t *) * _count));
178  alloc_assert (_next.table);
179 
180  memmove (_next.table, old_table + (new_min - _min),
181  sizeof (trie_t *) * _count);
182  free (old_table);
183 
184  _min = new_min;
185  } else if (c == _min + _count - 1) {
186  // We can compact the table "from the right".
187  // Find the right-most non-null node ptr, which we'll use to
188  // determine the new table size
189  unsigned short new_count = _count;
190  for (unsigned short i = 1; i < _count; ++i) {
191  if (_next.table[_count - 1 - i]) {
192  new_count = _count - i;
193  break;
194  }
195  }
196  zmq_assert (new_count != _count);
197  _count = new_count;
198 
199  trie_t **old_table = _next.table;
200  _next.table =
201  static_cast<trie_t **> (malloc (sizeof (trie_t *) * _count));
202  alloc_assert (_next.table);
203 
204  memmove (_next.table, old_table, sizeof (trie_t *) * _count);
205  free (old_table);
206  }
207  }
208  }
209  return ret;
210 }
211 
212 bool zmq::trie_t::check (const unsigned char *data_, size_t size_) const
213 {
214  // This function is on critical path. It deliberately doesn't use
215  // recursion to get a bit better performance.
216  const trie_t *current = this;
217  while (true) {
218  // We've found a corresponding subscription!
219  if (current->_refcnt)
220  return true;
221 
222  // We've checked all the data and haven't found matching subscription.
223  if (!size_)
224  return false;
225 
226  // If there's no corresponding slot for the first character
227  // of the prefix, the message does not match.
228  const unsigned char c = *data_;
229  if (c < current->_min || c >= current->_min + current->_count)
230  return false;
231 
232  // Move to the next character.
233  if (current->_count == 1)
234  current = current->_next.node;
235  else {
236  current = current->_next.table[c - current->_min];
237  if (!current)
238  return false;
239  }
240  data_++;
241  size_--;
242  }
243 }
244 
246  void (*func_) (unsigned char *data_, size_t size_, void *arg_), void *arg_)
247 {
248  unsigned char *buff = NULL;
249  apply_helper (&buff, 0, 0, func_, arg_);
250  free (buff);
251 }
252 
253 void zmq::trie_t::apply_helper (unsigned char **buff_,
254  size_t buffsize_,
255  size_t maxbuffsize_,
256  void (*func_) (unsigned char *data_,
257  size_t size_,
258  void *arg_),
259  void *arg_) const
260 {
261  // If this node is a subscription, apply the function.
262  if (_refcnt)
263  func_ (*buff_, buffsize_, arg_);
264 
265  // Adjust the buffer.
266  if (buffsize_ >= maxbuffsize_) {
267  maxbuffsize_ = buffsize_ + 256;
268  *buff_ = static_cast<unsigned char *> (realloc (*buff_, maxbuffsize_));
269  zmq_assert (*buff_);
270  }
271 
272  // If there are no subnodes in the trie, return.
273  if (_count == 0)
274  return;
275 
276  // If there's one subnode (optimisation).
277  if (_count == 1) {
278  (*buff_)[buffsize_] = _min;
279  buffsize_++;
280  _next.node->apply_helper (buff_, buffsize_, maxbuffsize_, func_, arg_);
281  return;
282  }
283 
284  // If there are multiple subnodes.
285  for (unsigned short c = 0; c != _count; c++) {
286  (*buff_)[buffsize_] = _min + c;
287  if (_next.table[c])
288  _next.table[c]->apply_helper (buff_, buffsize_ + 1, maxbuffsize_,
289  func_, arg_);
290  }
291 }
292 
294 {
295  return _refcnt == 0 && _live_nodes == 0;
296 }
LIBZMQ_DELETE
#define LIBZMQ_DELETE(p_object)
Definition: macros.hpp:7
zmq::trie_t::apply_helper
void apply_helper(unsigned char **buff_, size_t buffsize_, size_t maxbuffsize_, void(*func_)(unsigned char *data_, size_t size_, void *arg_), void *arg_) const
Definition: trie.cpp:253
data_
StringPiece data_
Definition: bytestream_unittest.cc:60
zmq::trie_t::check
bool check(const unsigned char *data_, size_t size_) const
Definition: trie.cpp:212
zmq::trie_t::_next
union zmq::trie_t::@71 _next
zmq::trie_t
Definition: trie.hpp:14
NULL
NULL
Definition: test_security_zap.cpp:405
precompiled.hpp
zmq_assert
#define zmq_assert(x)
Definition: err.hpp:102
zmq::trie_t::_refcnt
uint32_t _refcnt
Definition: trie.hpp:45
zmq::trie_t::rm
bool rm(unsigned char *prefix_, size_t size_)
Definition: trie.cpp:100
zmq::trie_t::trie_t
trie_t()
Definition: trie.cpp:13
zmq::trie_t::apply
void apply(void(*func_)(unsigned char *data_, size_t size_, void *arg_), void *arg_)
Definition: trie.cpp:245
trie.hpp
alloc_assert
#define alloc_assert(x)
Definition: err.hpp:146
macros.hpp
zmq::trie_t::~trie_t
~trie_t()
Definition: trie.cpp:17
zmq::trie_t::is_redundant
bool is_redundant() const
Definition: trie.cpp:293
i
int i
Definition: gmock-matchers_test.cc:764
err.hpp
zmq::trie_t::_count
unsigned short _count
Definition: trie.hpp:47
zmq::trie_t::add
bool add(unsigned char *prefix_, size_t size_)
Definition: trie.cpp:30
zmq::trie_t::node
class trie_t * node
Definition: trie.hpp:51
zmq::trie_t::_min
unsigned char _min
Definition: trie.hpp:46
zmq::trie_t::table
class trie_t ** table
Definition: trie.hpp:52
prefix_
std::string prefix_
Definition: src/google/protobuf/descriptor.cc:378


libaditof
Author(s):
autogenerated on Wed May 21 2025 02:07:00