radix_tree.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 "radix_tree.hpp"
7 
8 #include <stdlib.h>
9 #include <string.h>
10 #include <iterator>
11 #include <vector>
12 
13 node_t::node_t (unsigned char *data_) : _data (data_)
14 {
15 }
16 
17 uint32_t node_t::refcount ()
18 {
19  uint32_t u32;
20  memcpy (&u32, _data, sizeof (u32));
21  return u32;
22 }
23 
25 {
26  memcpy (_data, &value_, sizeof (value_));
27 }
28 
30 {
31  uint32_t u32;
32  memcpy (&u32, _data + sizeof (uint32_t), sizeof (u32));
33  return u32;
34 }
35 
37 {
38  memcpy (_data + sizeof (value_), &value_, sizeof (value_));
39 }
40 
41 uint32_t node_t::edgecount ()
42 {
43  uint32_t u32;
44  memcpy (&u32, _data + 2 * sizeof (uint32_t), sizeof (u32));
45  return u32;
46 }
47 
49 {
50  memcpy (_data + 2 * sizeof (value_), &value_, sizeof (value_));
51 }
52 
53 unsigned char *node_t::prefix ()
54 {
55  return _data + 3 * sizeof (uint32_t);
56 }
57 
58 void node_t::set_prefix (const unsigned char *bytes_)
59 {
60  memcpy (prefix (), bytes_, prefix_length ());
61 }
62 
63 unsigned char *node_t::first_bytes ()
64 {
65  return prefix () + prefix_length ();
66 }
67 
68 void node_t::set_first_bytes (const unsigned char *bytes_)
69 {
70  memcpy (first_bytes (), bytes_, edgecount ());
71 }
72 
73 unsigned char node_t::first_byte_at (size_t index_)
74 {
76  return first_bytes ()[index_];
77 }
78 
79 void node_t::set_first_byte_at (size_t index_, unsigned char byte_)
80 {
82  first_bytes ()[index_] = byte_;
83 }
84 
85 unsigned char *node_t::node_pointers ()
86 {
87  return prefix () + prefix_length () + edgecount ();
88 }
89 
90 void node_t::set_node_pointers (const unsigned char *pointers_)
91 {
92  memcpy (node_pointers (), pointers_, edgecount () * sizeof (void *));
93 }
94 
96 {
98 
99  unsigned char *data;
100  memcpy (&data, node_pointers () + index_ * sizeof (void *), sizeof (data));
101  return node_t (data);
102 }
103 
104 void node_t::set_node_at (size_t index_, node_t node_)
105 {
106  zmq_assert (index_ < edgecount ());
107  memcpy (node_pointers () + index_ * sizeof (void *), &node_._data,
108  sizeof (node_._data));
109 }
110 
112  unsigned char first_byte_,
113  node_t node_)
114 {
115  set_first_byte_at (index_, first_byte_);
116  set_node_at (index_, node_);
117 }
118 
119 bool node_t::operator== (node_t other_) const
120 {
121  return _data == other_._data;
122 }
123 
124 bool node_t::operator!= (node_t other_) const
125 {
126  return !(*this == other_);
127 }
128 
129 void node_t::resize (size_t prefix_length_, size_t edgecount_)
130 {
131  const size_t node_size = 3 * sizeof (uint32_t) + prefix_length_
132  + edgecount_ * (1 + sizeof (void *));
133  unsigned char *new_data =
134  static_cast<unsigned char *> (realloc (_data, node_size));
135  zmq_assert (new_data);
136  _data = new_data;
137  set_prefix_length (static_cast<uint32_t> (prefix_length_));
138  set_edgecount (static_cast<uint32_t> (edgecount_));
139 }
140 
141 node_t make_node (size_t refcount_, size_t prefix_length_, size_t edgecount_)
142 {
143  const size_t node_size = 3 * sizeof (uint32_t) + prefix_length_
144  + edgecount_ * (1 + sizeof (void *));
145 
146  unsigned char *data = static_cast<unsigned char *> (malloc (node_size));
147  zmq_assert (data);
148 
149  node_t node (data);
150  node.set_refcount (static_cast<uint32_t> (refcount_));
151  node.set_prefix_length (static_cast<uint32_t> (prefix_length_));
152  node.set_edgecount (static_cast<uint32_t> (edgecount_));
153  return node;
154 }
155 
156 // ----------------------------------------------------------------------
157 
158 zmq::radix_tree_t::radix_tree_t () : _root (make_node (0, 0, 0)), _size (0)
159 {
160 }
161 
162 static void free_nodes (node_t node_)
163 {
164  for (size_t i = 0, count = node_.edgecount (); i < count; ++i)
165  free_nodes (node_.node_at (i));
166  free (node_._data);
167 }
168 
170 {
171  free_nodes (_root);
172 }
173 
174 match_result_t::match_result_t (size_t key_bytes_matched_,
175  size_t prefix_bytes_matched_,
176  size_t edge_index_,
177  size_t parent_edge_index_,
178  node_t current_,
179  node_t parent_,
180  node_t grandparent_) :
181  _key_bytes_matched (key_bytes_matched_),
182  _prefix_bytes_matched (prefix_bytes_matched_),
183  _edge_index (edge_index_),
184  _parent_edge_index (parent_edge_index_),
185  _current_node (current_),
186  _parent_node (parent_),
187  _grandparent_node (grandparent_)
188 {
189 }
190 
191 match_result_t zmq::radix_tree_t::match (const unsigned char *key_,
192  size_t key_size_,
193  bool is_lookup_ = false) const
194 {
195  zmq_assert (key_);
196 
197  // Node we're currently at in the traversal and its predecessors.
198  node_t current_node = _root;
199  node_t parent_node = current_node;
200  node_t grandparent_node = current_node;
201  // Index of the next byte to match in the key.
202  size_t key_byte_index = 0;
203  // Index of the next byte to match in the current node's prefix.
204  size_t prefix_byte_index = 0;
205  // Index of the edge from parent to current node.
206  size_t edge_index = 0;
207  // Index of the edge from grandparent to parent.
208  size_t parent_edge_index = 0;
209 
210  while (current_node.prefix_length () > 0 || current_node.edgecount () > 0) {
211  const unsigned char *const prefix = current_node.prefix ();
212  const size_t prefix_length = current_node.prefix_length ();
213 
214  for (prefix_byte_index = 0;
215  prefix_byte_index < prefix_length && key_byte_index < key_size_;
216  ++prefix_byte_index, ++key_byte_index) {
217  if (prefix[prefix_byte_index] != key_[key_byte_index])
218  break;
219  }
220 
221  // Even if a prefix of the key matches and we're doing a
222  // lookup, this means we've found a matching subscription.
223  if (is_lookup_ && prefix_byte_index == prefix_length
224  && current_node.refcount () > 0) {
225  key_byte_index = key_size_;
226  break;
227  }
228 
229  // There was a mismatch or we've matched the whole key, so
230  // there's nothing more to do.
231  if (prefix_byte_index != prefix_length || key_byte_index == key_size_)
232  break;
233 
234  // We need to match the rest of the key. Check if there's an
235  // outgoing edge from this node.
236  node_t next_node = current_node;
237  for (size_t i = 0, edgecount = current_node.edgecount (); i < edgecount;
238  ++i) {
239  if (current_node.first_byte_at (i) == key_[key_byte_index]) {
240  parent_edge_index = edge_index;
241  edge_index = i;
242  next_node = current_node.node_at (i);
243  break;
244  }
245  }
246 
247  if (next_node == current_node)
248  break; // No outgoing edge.
249  grandparent_node = parent_node;
250  parent_node = current_node;
251  current_node = next_node;
252  }
253 
254  return match_result_t (key_byte_index, prefix_byte_index, edge_index,
255  parent_edge_index, current_node, parent_node,
256  grandparent_node);
257 }
258 
259 bool zmq::radix_tree_t::add (const unsigned char *key_, size_t key_size_)
260 {
261  const match_result_t match_result = match (key_, key_size_);
262  const size_t key_bytes_matched = match_result._key_bytes_matched;
263  const size_t prefix_bytes_matched = match_result._prefix_bytes_matched;
264  const size_t edge_index = match_result._edge_index;
265  node_t current_node = match_result._current_node;
266  node_t parent_node = match_result._parent_node;
267 
268  if (key_bytes_matched != key_size_) {
269  // Not all characters match, we might have to split the node.
270  if (prefix_bytes_matched == current_node.prefix_length ()) {
271  // The mismatch is at one of the outgoing edges, so we
272  // create an edge from the current node to a new leaf node
273  // that has the rest of the key as the prefix.
274  node_t key_node = make_node (1, key_size_ - key_bytes_matched, 0);
275  key_node.set_prefix (key_ + key_bytes_matched);
276 
277  // Reallocate for one more edge.
278  current_node.resize (current_node.prefix_length (),
279  current_node.edgecount () + 1);
280 
281  // Make room for the new edge. We need to shift the chunk
282  // of node pointers one byte to the right. Since resize()
283  // increments the edgecount by 1, node_pointers() tells us the
284  // destination address. The chunk of node pointers starts
285  // at one byte to the left of this destination.
286  //
287  // Since the regions can overlap, we use memmove.
288  memmove (current_node.node_pointers (),
289  current_node.node_pointers () - 1,
290  (current_node.edgecount () - 1) * sizeof (void *));
291 
292  // Add an edge to the new node.
293  current_node.set_edge_at (current_node.edgecount () - 1,
294  key_[key_bytes_matched], key_node);
295 
296  // We need to update all pointers to the current node
297  // after the call to resize().
298  if (current_node.prefix_length () == 0)
299  _root._data = current_node._data;
300  else
301  parent_node.set_node_at (edge_index, current_node);
302  _size.add (1);
303  return true;
304  }
305 
306  // There was a mismatch, so we need to split this node.
307  //
308  // Create two nodes that will be reachable from the parent.
309  // One node will have the rest of the characters from the key,
310  // and the other node will have the rest of the characters
311  // from the current node's prefix.
312  node_t key_node = make_node (1, key_size_ - key_bytes_matched, 0);
313  node_t split_node =
314  make_node (current_node.refcount (),
315  current_node.prefix_length () - prefix_bytes_matched,
316  current_node.edgecount ());
317 
318  // Copy the prefix chunks to the new nodes.
319  key_node.set_prefix (key_ + key_bytes_matched);
320  split_node.set_prefix (current_node.prefix () + prefix_bytes_matched);
321 
322  // Copy the current node's edges to the new node.
323  split_node.set_first_bytes (current_node.first_bytes ());
324  split_node.set_node_pointers (current_node.node_pointers ());
325 
326  // Resize the current node to accommodate a prefix comprising
327  // the matched characters and 2 outgoing edges to the above
328  // nodes. Set the refcount to 0 since this node doesn't hold a
329  // key.
330  current_node.resize (prefix_bytes_matched, 2);
331  current_node.set_refcount (0);
332 
333  // Add links to the new nodes. We don't need to copy the
334  // prefix since resize() retains it in the current node.
335  current_node.set_edge_at (0, key_node.prefix ()[0], key_node);
336  current_node.set_edge_at (1, split_node.prefix ()[0], split_node);
337 
338  _size.add (1);
339  parent_node.set_node_at (edge_index, current_node);
340  return true;
341  }
342 
343  // All characters in the key match, but we still might need to split.
344  if (prefix_bytes_matched != current_node.prefix_length ()) {
345  // All characters in the key match, but not all characters
346  // from the current node's prefix match.
347 
348  // Create a node that contains the rest of the characters from
349  // the current node's prefix and the outgoing edges from the
350  // current node.
351  node_t split_node =
352  make_node (current_node.refcount (),
353  current_node.prefix_length () - prefix_bytes_matched,
354  current_node.edgecount ());
355  split_node.set_prefix (current_node.prefix () + prefix_bytes_matched);
356  split_node.set_first_bytes (current_node.first_bytes ());
357  split_node.set_node_pointers (current_node.node_pointers ());
358 
359  // Resize the current node to hold only the matched characters
360  // from its prefix and one edge to the new node.
361  current_node.resize (prefix_bytes_matched, 1);
362 
363  // Add an edge to the split node and set the refcount to 1
364  // since this key wasn't inserted earlier. We don't need to
365  // set the prefix because the first `prefix_bytes_matched` bytes
366  // in the prefix are preserved by resize().
367  current_node.set_edge_at (0, split_node.prefix ()[0], split_node);
368  current_node.set_refcount (1);
369 
370  _size.add (1);
371  parent_node.set_node_at (edge_index, current_node);
372  return true;
373  }
374 
375  zmq_assert (key_bytes_matched == key_size_);
376  zmq_assert (prefix_bytes_matched == current_node.prefix_length ());
377 
378  _size.add (1);
379  current_node.set_refcount (current_node.refcount () + 1);
380  return current_node.refcount () == 1;
381 }
382 
383 bool zmq::radix_tree_t::rm (const unsigned char *key_, size_t key_size_)
384 {
385  const match_result_t match_result = match (key_, key_size_);
386  const size_t key_bytes_matched = match_result._key_bytes_matched;
387  const size_t prefix_bytes_matched = match_result._prefix_bytes_matched;
388  const size_t edge_index = match_result._edge_index;
389  const size_t parent_edge_index = match_result._parent_edge_index;
390  node_t current_node = match_result._current_node;
391  node_t parent_node = match_result._parent_node;
392  node_t grandparent_node = match_result._grandparent_node;
393 
394  if (key_bytes_matched != key_size_
395  || prefix_bytes_matched != current_node.prefix_length ()
396  || current_node.refcount () == 0)
397  return false;
398 
399  current_node.set_refcount (current_node.refcount () - 1);
400  _size.sub (1);
401  if (current_node.refcount () > 0)
402  return false;
403 
404  // Don't delete the root node.
405  if (current_node == _root)
406  return true;
407 
408  const size_t outgoing_edges = current_node.edgecount ();
409  if (outgoing_edges > 1)
410  // This node can't be merged with any other node, so there's
411  // nothing more to do.
412  return true;
413 
414  if (outgoing_edges == 1) {
415  // Merge this node with the single child node.
416  node_t child = current_node.node_at (0);
417 
418  // Make room for the child node's prefix and edges. We need to
419  // keep the old prefix length since resize() will overwrite
420  // it.
421  const uint32_t old_prefix_length = current_node.prefix_length ();
422  current_node.resize (old_prefix_length + child.prefix_length (),
423  child.edgecount ());
424 
425  // Append the child node's prefix to the current node.
426  memcpy (current_node.prefix () + old_prefix_length, child.prefix (),
427  child.prefix_length ());
428 
429  // Copy the rest of child node's data to the current node.
430  current_node.set_first_bytes (child.first_bytes ());
431  current_node.set_node_pointers (child.node_pointers ());
432  current_node.set_refcount (child.refcount ());
433 
434  free (child._data);
435  parent_node.set_node_at (edge_index, current_node);
436  return true;
437  }
438 
439  if (parent_node.edgecount () == 2 && parent_node.refcount () == 0
440  && parent_node != _root) {
441  // Removing this node leaves the parent with one child.
442  // If the parent doesn't hold a key or if it isn't the root,
443  // we can merge it with its single child node.
444  zmq_assert (edge_index < 2);
445  node_t other_child = parent_node.node_at (!edge_index);
446 
447  // Make room for the child node's prefix and edges. We need to
448  // keep the old prefix length since resize() will overwrite
449  // it.
450  const uint32_t old_prefix_length = parent_node.prefix_length ();
451  parent_node.resize (old_prefix_length + other_child.prefix_length (),
452  other_child.edgecount ());
453 
454  // Append the child node's prefix to the current node.
455  memcpy (parent_node.prefix () + old_prefix_length,
456  other_child.prefix (), other_child.prefix_length ());
457 
458  // Copy the rest of child node's data to the current node.
459  parent_node.set_first_bytes (other_child.first_bytes ());
460  parent_node.set_node_pointers (other_child.node_pointers ());
461  parent_node.set_refcount (other_child.refcount ());
462 
463  free (current_node._data);
464  free (other_child._data);
465  grandparent_node.set_node_at (parent_edge_index, parent_node);
466  return true;
467  }
468 
469  // This is a leaf node that doesn't leave its parent with one
470  // outgoing edge. Remove the outgoing edge to this node from the
471  // parent.
472  zmq_assert (outgoing_edges == 0);
473 
474  // Replace the edge to the current node with the last edge. An
475  // edge consists of a byte and a pointer to the next node. First
476  // replace the byte.
477  const size_t last_index = parent_node.edgecount () - 1;
478  const unsigned char last_byte = parent_node.first_byte_at (last_index);
479  const node_t last_node = parent_node.node_at (last_index);
480  parent_node.set_edge_at (edge_index, last_byte, last_node);
481 
482  // Move the chunk of pointers one byte to the left, effectively
483  // deleting the last byte in the region of first bytes by
484  // overwriting it.
485  memmove (parent_node.node_pointers () - 1, parent_node.node_pointers (),
486  parent_node.edgecount () * sizeof (void *));
487 
488  // Shrink the parent node to the new size, which "deletes" the
489  // last pointer in the chunk of node pointers.
490  parent_node.resize (parent_node.prefix_length (),
491  parent_node.edgecount () - 1);
492 
493  // Nothing points to this node now, so we can reclaim it.
494  free (current_node._data);
495 
496  if (parent_node.prefix_length () == 0)
497  _root._data = parent_node._data;
498  else
499  grandparent_node.set_node_at (parent_edge_index, parent_node);
500  return true;
501 }
502 
503 bool zmq::radix_tree_t::check (const unsigned char *key_, size_t key_size_)
504 {
505  if (_root.refcount () > 0)
506  return true;
507 
508  match_result_t match_result = match (key_, key_size_, true);
509  return match_result._key_bytes_matched == key_size_
510  && match_result._prefix_bytes_matched
511  == match_result._current_node.prefix_length ()
512  && match_result._current_node.refcount () > 0;
513 }
514 
515 static void
517  std::vector<unsigned char> &buffer_,
518  void (*func_) (unsigned char *data_, size_t size_, void *arg_),
519  void *arg_)
520 {
521  const size_t prefix_length = node_.prefix_length ();
522  buffer_.reserve (buffer_.size () + prefix_length);
523  std::copy (node_.prefix (), node_.prefix () + prefix_length,
524  std::back_inserter (buffer_));
525 
526  if (node_.refcount () > 0) {
527  zmq_assert (!buffer_.empty ());
528  func_ (&buffer_[0], buffer_.size (), arg_);
529  }
530 
531  for (size_t i = 0, edgecount = node_.edgecount (); i < edgecount; ++i) {
532  visit_keys (node_.node_at (i), buffer_, func_, arg_);
533  }
534  buffer_.resize (static_cast<uint32_t> (buffer_.size () - prefix_length));
535 }
536 
538  void (*func_) (unsigned char *data_, size_t size_, void *arg_), void *arg_)
539 {
540  if (_root.refcount () > 0)
541  func_ (NULL, 0, arg_); // Root node is always empty.
542 
543  std::vector<unsigned char> buffer;
544  for (size_t i = 0; i < _root.edgecount (); ++i)
545  visit_keys (_root.node_at (i), buffer, func_, arg_);
546 }
547 
548 size_t zmq::radix_tree_t::size () const
549 {
550  return _size.get ();
551 }
zmq::radix_tree_t::check
bool check(const unsigned char *key_, size_t key_size_)
Definition: radix_tree.cpp:503
node_t::set_node_at
void set_node_at(size_t index_, node_t node_)
Definition: radix_tree.cpp:104
match_result_t::_prefix_bytes_matched
size_t _prefix_bytes_matched
Definition: radix_tree.hpp:79
match_result_t::match_result_t
match_result_t(size_t key_bytes_matched_, size_t prefix_bytes_matched_, size_t edge_index_, size_t parent_edge_index_, node_t current_, node_t parent_, node_t grandparent)
Definition: radix_tree.cpp:174
data_
StringPiece data_
Definition: bytestream_unittest.cc:60
match_result_t::_grandparent_node
node_t _grandparent_node
Definition: radix_tree.hpp:84
zmq::radix_tree_t::apply
void apply(void(*func_)(unsigned char *data, size_t size, void *arg), void *arg_)
Definition: radix_tree.cpp:537
NULL
NULL
Definition: test_security_zap.cpp:405
node_t::set_first_bytes
void set_first_bytes(const unsigned char *bytes_)
Definition: radix_tree.cpp:68
node_t::first_bytes
unsigned char * first_bytes()
Definition: radix_tree.cpp:63
node_t::node_t
node_t(unsigned char *data_)
Definition: radix_tree.cpp:13
node_t::_data
unsigned char * _data
Definition: radix_tree.hpp:63
precompiled.hpp
node_t::prefix
unsigned char * prefix()
Definition: radix_tree.cpp:53
zmq_assert
#define zmq_assert(x)
Definition: err.hpp:102
node_t::operator!=
bool operator!=(node_t other_) const
Definition: radix_tree.cpp:124
zmq::radix_tree_t::match
match_result_t match(const unsigned char *key_, size_t key_size_, bool is_lookup_) const
Definition: radix_tree.cpp:191
node_t::set_node_pointers
void set_node_pointers(const unsigned char *pointers_)
Definition: radix_tree.cpp:90
radix_tree.hpp
zmq::radix_tree_t::radix_tree_t
radix_tree_t()
Definition: radix_tree.cpp:158
node_t::set_prefix
void set_prefix(const unsigned char *bytes_)
Definition: radix_tree.cpp:58
macros.hpp
node_t
Definition: radix_tree.hpp:37
prefix
static const char prefix[]
Definition: test_pair_ipc.cpp:26
node_t::set_edgecount
void set_edgecount(uint32_t value_)
Definition: radix_tree.cpp:48
buffer
GLuint buffer
Definition: glcorearb.h:2939
match_result_t
Definition: radix_tree.hpp:68
node_t::resize
void resize(size_t prefix_length_, size_t edgecount_)
Definition: radix_tree.cpp:129
node_t::set_prefix_length
void set_prefix_length(uint32_t value_)
Definition: radix_tree.cpp:36
googletest-filter-unittest.child
child
Definition: googletest-filter-unittest.py:62
zmq::radix_tree_t::_root
node_t _root
Definition: radix_tree.hpp:117
buffer
Definition: buffer_processor.h:43
index_
int index_
Definition: gmock-matchers_test.cc:6752
match_result_t::_edge_index
size_t _edge_index
Definition: radix_tree.hpp:80
zmq::radix_tree_t::add
bool add(const unsigned char *key_, size_t key_size_)
Definition: radix_tree.cpp:259
match_result_t::_key_bytes_matched
size_t _key_bytes_matched
Definition: radix_tree.hpp:78
zmq::radix_tree_t::size
size_t size() const
Definition: radix_tree.cpp:548
i
int i
Definition: gmock-matchers_test.cc:764
value_
int value_
Definition: gmock-matchers_test.cc:571
node_t::set_edge_at
void set_edge_at(size_t index_, unsigned char first_byte_, node_t node_)
Definition: radix_tree.cpp:111
visit_keys
static void visit_keys(node_t node_, std::vector< unsigned char > &buffer_, void(*func_)(unsigned char *data_, size_t size_, void *arg_), void *arg_)
Definition: radix_tree.cpp:516
make_node
node_t make_node(size_t refcount_, size_t prefix_length_, size_t edgecount_)
Definition: radix_tree.cpp:141
zmq::radix_tree_t::rm
bool rm(const unsigned char *key_, size_t key_size_)
Definition: radix_tree.cpp:383
node_t::operator==
bool operator==(node_t other_) const
Definition: radix_tree.cpp:119
node_t::first_byte_at
unsigned char first_byte_at(size_t index_)
Definition: radix_tree.cpp:73
node_t::node_at
node_t node_at(size_t index_)
Definition: radix_tree.cpp:95
err.hpp
data
GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const GLvoid * data
Definition: glcorearb.h:2879
buffer_
static uint8 buffer_[kBufferSize]
Definition: coded_stream_unittest.cc:136
node_t::edgecount
uint32_t edgecount()
Definition: radix_tree.cpp:41
match_result_t::_parent_node
node_t _parent_node
Definition: radix_tree.hpp:83
zmq::radix_tree_t::~radix_tree_t
~radix_tree_t()
Definition: radix_tree.cpp:169
match_result_t::_parent_edge_index
size_t _parent_edge_index
Definition: radix_tree.hpp:81
node_t::set_first_byte_at
void set_first_byte_at(size_t index_, unsigned char byte_)
Definition: radix_tree.cpp:79
count
GLint GLsizei count
Definition: glcorearb.h:2830
node_t::set_refcount
void set_refcount(uint32_t value_)
Definition: radix_tree.cpp:24
node_t::prefix_length
uint32_t prefix_length()
Definition: radix_tree.cpp:29
node_t::refcount
uint32_t refcount()
Definition: radix_tree.cpp:17
free_nodes
static void free_nodes(node_t node_)
Definition: radix_tree.cpp:162
match_result_t::_current_node
node_t _current_node
Definition: radix_tree.hpp:82
node_t::node_pointers
unsigned char * node_pointers()
Definition: radix_tree.cpp:85


libaditof
Author(s):
autogenerated on Wed May 21 2025 02:06:58