/src/libzmq/src/radix_tree.cpp
Line | Count | Source |
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 | 0 | node_t::node_t (unsigned char *data_) : _data (data_) |
14 | 0 | { |
15 | 0 | } |
16 | | |
17 | | uint32_t node_t::refcount () |
18 | 0 | { |
19 | 0 | uint32_t u32; |
20 | 0 | memcpy (&u32, _data, sizeof (u32)); |
21 | 0 | return u32; |
22 | 0 | } |
23 | | |
24 | | void node_t::set_refcount (uint32_t value_) |
25 | 0 | { |
26 | 0 | memcpy (_data, &value_, sizeof (value_)); |
27 | 0 | } |
28 | | |
29 | | uint32_t node_t::prefix_length () |
30 | 0 | { |
31 | 0 | uint32_t u32; |
32 | 0 | memcpy (&u32, _data + sizeof (uint32_t), sizeof (u32)); |
33 | 0 | return u32; |
34 | 0 | } |
35 | | |
36 | | void node_t::set_prefix_length (uint32_t value_) |
37 | 0 | { |
38 | 0 | memcpy (_data + sizeof (value_), &value_, sizeof (value_)); |
39 | 0 | } |
40 | | |
41 | | uint32_t node_t::edgecount () |
42 | 0 | { |
43 | 0 | uint32_t u32; |
44 | 0 | memcpy (&u32, _data + 2 * sizeof (uint32_t), sizeof (u32)); |
45 | 0 | return u32; |
46 | 0 | } |
47 | | |
48 | | void node_t::set_edgecount (uint32_t value_) |
49 | 0 | { |
50 | 0 | memcpy (_data + 2 * sizeof (value_), &value_, sizeof (value_)); |
51 | 0 | } |
52 | | |
53 | | unsigned char *node_t::prefix () |
54 | 0 | { |
55 | 0 | return _data + 3 * sizeof (uint32_t); |
56 | 0 | } |
57 | | |
58 | | void node_t::set_prefix (const unsigned char *bytes_) |
59 | 0 | { |
60 | 0 | memcpy (prefix (), bytes_, prefix_length ()); |
61 | 0 | } |
62 | | |
63 | | unsigned char *node_t::first_bytes () |
64 | 0 | { |
65 | 0 | return prefix () + prefix_length (); |
66 | 0 | } |
67 | | |
68 | | void node_t::set_first_bytes (const unsigned char *bytes_) |
69 | 0 | { |
70 | 0 | memcpy (first_bytes (), bytes_, edgecount ()); |
71 | 0 | } |
72 | | |
73 | | unsigned char node_t::first_byte_at (size_t index_) |
74 | 0 | { |
75 | 0 | zmq_assert (index_ < edgecount ()); |
76 | 0 | return first_bytes ()[index_]; |
77 | 0 | } |
78 | | |
79 | | void node_t::set_first_byte_at (size_t index_, unsigned char byte_) |
80 | 0 | { |
81 | 0 | zmq_assert (index_ < edgecount ()); |
82 | 0 | first_bytes ()[index_] = byte_; |
83 | 0 | } |
84 | | |
85 | | unsigned char *node_t::node_pointers () |
86 | 0 | { |
87 | 0 | return prefix () + prefix_length () + edgecount (); |
88 | 0 | } |
89 | | |
90 | | void node_t::set_node_pointers (const unsigned char *pointers_) |
91 | 0 | { |
92 | 0 | memcpy (node_pointers (), pointers_, edgecount () * sizeof (void *)); |
93 | 0 | } |
94 | | |
95 | | node_t node_t::node_at (size_t index_) |
96 | 0 | { |
97 | 0 | zmq_assert (index_ < edgecount ()); |
98 | |
|
99 | 0 | unsigned char *data; |
100 | 0 | memcpy (&data, node_pointers () + index_ * sizeof (void *), sizeof (data)); |
101 | 0 | return node_t (data); |
102 | 0 | } |
103 | | |
104 | | void node_t::set_node_at (size_t index_, node_t node_) |
105 | 0 | { |
106 | 0 | zmq_assert (index_ < edgecount ()); |
107 | 0 | memcpy (node_pointers () + index_ * sizeof (void *), &node_._data, |
108 | 0 | sizeof (node_._data)); |
109 | 0 | } |
110 | | |
111 | | void node_t::set_edge_at (size_t index_, |
112 | | unsigned char first_byte_, |
113 | | node_t node_) |
114 | 0 | { |
115 | 0 | set_first_byte_at (index_, first_byte_); |
116 | 0 | set_node_at (index_, node_); |
117 | 0 | } |
118 | | |
119 | | bool node_t::operator== (node_t other_) const |
120 | 0 | { |
121 | 0 | return _data == other_._data; |
122 | 0 | } |
123 | | |
124 | | bool node_t::operator!= (node_t other_) const |
125 | 0 | { |
126 | 0 | return !(*this == other_); |
127 | 0 | } |
128 | | |
129 | | void node_t::resize (size_t prefix_length_, size_t edgecount_) |
130 | 0 | { |
131 | 0 | const size_t node_size = 3 * sizeof (uint32_t) + prefix_length_ |
132 | 0 | + edgecount_ * (1 + sizeof (void *)); |
133 | 0 | unsigned char *new_data = |
134 | 0 | static_cast<unsigned char *> (realloc (_data, node_size)); |
135 | 0 | zmq_assert (new_data); |
136 | 0 | _data = new_data; |
137 | 0 | set_prefix_length (static_cast<uint32_t> (prefix_length_)); |
138 | 0 | set_edgecount (static_cast<uint32_t> (edgecount_)); |
139 | 0 | } |
140 | | |
141 | | node_t make_node (size_t refcount_, size_t prefix_length_, size_t edgecount_) |
142 | 0 | { |
143 | 0 | const size_t node_size = 3 * sizeof (uint32_t) + prefix_length_ |
144 | 0 | + edgecount_ * (1 + sizeof (void *)); |
145 | |
|
146 | 0 | unsigned char *data = static_cast<unsigned char *> (malloc (node_size)); |
147 | 0 | zmq_assert (data); |
148 | |
|
149 | 0 | node_t node (data); |
150 | 0 | node.set_refcount (static_cast<uint32_t> (refcount_)); |
151 | 0 | node.set_prefix_length (static_cast<uint32_t> (prefix_length_)); |
152 | 0 | node.set_edgecount (static_cast<uint32_t> (edgecount_)); |
153 | 0 | return node; |
154 | 0 | } |
155 | | |
156 | | // ---------------------------------------------------------------------- |
157 | | |
158 | 0 | zmq::radix_tree_t::radix_tree_t () : _root (make_node (0, 0, 0)), _size (0) |
159 | 0 | { |
160 | 0 | } |
161 | | |
162 | | static void free_nodes (node_t node_) |
163 | 0 | { |
164 | 0 | for (size_t i = 0, count = node_.edgecount (); i < count; ++i) |
165 | 0 | free_nodes (node_.node_at (i)); |
166 | 0 | free (node_._data); |
167 | 0 | } |
168 | | |
169 | | zmq::radix_tree_t::~radix_tree_t () |
170 | 0 | { |
171 | 0 | free_nodes (_root); |
172 | 0 | } |
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 | 0 | _key_bytes_matched (key_bytes_matched_), |
182 | 0 | _prefix_bytes_matched (prefix_bytes_matched_), |
183 | 0 | _edge_index (edge_index_), |
184 | 0 | _parent_edge_index (parent_edge_index_), |
185 | 0 | _current_node (current_), |
186 | 0 | _parent_node (parent_), |
187 | 0 | _grandparent_node (grandparent_) |
188 | 0 | { |
189 | 0 | } |
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 | 0 | { |
195 | 0 | zmq_assert (key_); |
196 | | |
197 | | // Node we're currently at in the traversal and its predecessors. |
198 | 0 | node_t current_node = _root; |
199 | 0 | node_t parent_node = current_node; |
200 | 0 | node_t grandparent_node = current_node; |
201 | | // Index of the next byte to match in the key. |
202 | 0 | size_t key_byte_index = 0; |
203 | | // Index of the next byte to match in the current node's prefix. |
204 | 0 | size_t prefix_byte_index = 0; |
205 | | // Index of the edge from parent to current node. |
206 | 0 | size_t edge_index = 0; |
207 | | // Index of the edge from grandparent to parent. |
208 | 0 | size_t parent_edge_index = 0; |
209 | |
|
210 | 0 | while (current_node.prefix_length () > 0 || current_node.edgecount () > 0) { |
211 | 0 | const unsigned char *const prefix = current_node.prefix (); |
212 | 0 | const size_t prefix_length = current_node.prefix_length (); |
213 | |
|
214 | 0 | for (prefix_byte_index = 0; |
215 | 0 | prefix_byte_index < prefix_length && key_byte_index < key_size_; |
216 | 0 | ++prefix_byte_index, ++key_byte_index) { |
217 | 0 | if (prefix[prefix_byte_index] != key_[key_byte_index]) |
218 | 0 | break; |
219 | 0 | } |
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 | 0 | if (is_lookup_ && prefix_byte_index == prefix_length |
224 | 0 | && current_node.refcount () > 0) { |
225 | 0 | key_byte_index = key_size_; |
226 | 0 | break; |
227 | 0 | } |
228 | | |
229 | | // There was a mismatch or we've matched the whole key, so |
230 | | // there's nothing more to do. |
231 | 0 | if (prefix_byte_index != prefix_length || key_byte_index == key_size_) |
232 | 0 | break; |
233 | | |
234 | | // We need to match the rest of the key. Check if there's an |
235 | | // outgoing edge from this node. |
236 | 0 | node_t next_node = current_node; |
237 | 0 | for (size_t i = 0, edgecount = current_node.edgecount (); i < edgecount; |
238 | 0 | ++i) { |
239 | 0 | if (current_node.first_byte_at (i) == key_[key_byte_index]) { |
240 | 0 | parent_edge_index = edge_index; |
241 | 0 | edge_index = i; |
242 | 0 | next_node = current_node.node_at (i); |
243 | 0 | break; |
244 | 0 | } |
245 | 0 | } |
246 | |
|
247 | 0 | if (next_node == current_node) |
248 | 0 | break; // No outgoing edge. |
249 | 0 | grandparent_node = parent_node; |
250 | 0 | parent_node = current_node; |
251 | 0 | current_node = next_node; |
252 | 0 | } |
253 | |
|
254 | 0 | return match_result_t (key_byte_index, prefix_byte_index, edge_index, |
255 | 0 | parent_edge_index, current_node, parent_node, |
256 | 0 | grandparent_node); |
257 | 0 | } |
258 | | |
259 | | bool zmq::radix_tree_t::add (const unsigned char *key_, size_t key_size_) |
260 | 0 | { |
261 | 0 | const match_result_t match_result = match (key_, key_size_); |
262 | 0 | const size_t key_bytes_matched = match_result._key_bytes_matched; |
263 | 0 | const size_t prefix_bytes_matched = match_result._prefix_bytes_matched; |
264 | 0 | const size_t edge_index = match_result._edge_index; |
265 | 0 | node_t current_node = match_result._current_node; |
266 | 0 | node_t parent_node = match_result._parent_node; |
267 | |
|
268 | 0 | if (key_bytes_matched != key_size_) { |
269 | | // Not all characters match, we might have to split the node. |
270 | 0 | 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 | 0 | node_t key_node = make_node (1, key_size_ - key_bytes_matched, 0); |
275 | 0 | key_node.set_prefix (key_ + key_bytes_matched); |
276 | | |
277 | | // Reallocate for one more edge. |
278 | 0 | current_node.resize (current_node.prefix_length (), |
279 | 0 | 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 | 0 | memmove (current_node.node_pointers (), |
289 | 0 | current_node.node_pointers () - 1, |
290 | 0 | (current_node.edgecount () - 1) * sizeof (void *)); |
291 | | |
292 | | // Add an edge to the new node. |
293 | 0 | current_node.set_edge_at (current_node.edgecount () - 1, |
294 | 0 | 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 | 0 | if (current_node.prefix_length () == 0) |
299 | 0 | _root._data = current_node._data; |
300 | 0 | else |
301 | 0 | parent_node.set_node_at (edge_index, current_node); |
302 | 0 | _size.add (1); |
303 | 0 | return true; |
304 | 0 | } |
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 | 0 | node_t key_node = make_node (1, key_size_ - key_bytes_matched, 0); |
313 | 0 | node_t split_node = |
314 | 0 | make_node (current_node.refcount (), |
315 | 0 | current_node.prefix_length () - prefix_bytes_matched, |
316 | 0 | current_node.edgecount ()); |
317 | | |
318 | | // Copy the prefix chunks to the new nodes. |
319 | 0 | key_node.set_prefix (key_ + key_bytes_matched); |
320 | 0 | split_node.set_prefix (current_node.prefix () + prefix_bytes_matched); |
321 | | |
322 | | // Copy the current node's edges to the new node. |
323 | 0 | split_node.set_first_bytes (current_node.first_bytes ()); |
324 | 0 | 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 | 0 | current_node.resize (prefix_bytes_matched, 2); |
331 | 0 | 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 | 0 | current_node.set_edge_at (0, key_node.prefix ()[0], key_node); |
336 | 0 | current_node.set_edge_at (1, split_node.prefix ()[0], split_node); |
337 | |
|
338 | 0 | _size.add (1); |
339 | 0 | parent_node.set_node_at (edge_index, current_node); |
340 | 0 | return true; |
341 | 0 | } |
342 | | |
343 | | // All characters in the key match, but we still might need to split. |
344 | 0 | 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 | 0 | node_t split_node = |
352 | 0 | make_node (current_node.refcount (), |
353 | 0 | current_node.prefix_length () - prefix_bytes_matched, |
354 | 0 | current_node.edgecount ()); |
355 | 0 | split_node.set_prefix (current_node.prefix () + prefix_bytes_matched); |
356 | 0 | split_node.set_first_bytes (current_node.first_bytes ()); |
357 | 0 | 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 | 0 | 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 | 0 | current_node.set_edge_at (0, split_node.prefix ()[0], split_node); |
368 | 0 | current_node.set_refcount (1); |
369 | |
|
370 | 0 | _size.add (1); |
371 | 0 | parent_node.set_node_at (edge_index, current_node); |
372 | 0 | return true; |
373 | 0 | } |
374 | | |
375 | 0 | zmq_assert (key_bytes_matched == key_size_); |
376 | 0 | zmq_assert (prefix_bytes_matched == current_node.prefix_length ()); |
377 | |
|
378 | 0 | _size.add (1); |
379 | 0 | current_node.set_refcount (current_node.refcount () + 1); |
380 | 0 | return current_node.refcount () == 1; |
381 | 0 | } |
382 | | |
383 | | bool zmq::radix_tree_t::rm (const unsigned char *key_, size_t key_size_) |
384 | 0 | { |
385 | 0 | const match_result_t match_result = match (key_, key_size_); |
386 | 0 | const size_t key_bytes_matched = match_result._key_bytes_matched; |
387 | 0 | const size_t prefix_bytes_matched = match_result._prefix_bytes_matched; |
388 | 0 | const size_t edge_index = match_result._edge_index; |
389 | 0 | const size_t parent_edge_index = match_result._parent_edge_index; |
390 | 0 | node_t current_node = match_result._current_node; |
391 | 0 | node_t parent_node = match_result._parent_node; |
392 | 0 | node_t grandparent_node = match_result._grandparent_node; |
393 | |
|
394 | 0 | if (key_bytes_matched != key_size_ |
395 | 0 | || prefix_bytes_matched != current_node.prefix_length () |
396 | 0 | || current_node.refcount () == 0) |
397 | 0 | return false; |
398 | | |
399 | 0 | current_node.set_refcount (current_node.refcount () - 1); |
400 | 0 | _size.sub (1); |
401 | 0 | if (current_node.refcount () > 0) |
402 | 0 | return false; |
403 | | |
404 | | // Don't delete the root node. |
405 | 0 | if (current_node == _root) |
406 | 0 | return true; |
407 | | |
408 | 0 | const size_t outgoing_edges = current_node.edgecount (); |
409 | 0 | if (outgoing_edges > 1) |
410 | | // This node can't be merged with any other node, so there's |
411 | | // nothing more to do. |
412 | 0 | return true; |
413 | | |
414 | 0 | if (outgoing_edges == 1) { |
415 | | // Merge this node with the single child node. |
416 | 0 | 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 | 0 | const uint32_t old_prefix_length = current_node.prefix_length (); |
422 | 0 | current_node.resize (old_prefix_length + child.prefix_length (), |
423 | 0 | child.edgecount ()); |
424 | | |
425 | | // Append the child node's prefix to the current node. |
426 | 0 | memcpy (current_node.prefix () + old_prefix_length, child.prefix (), |
427 | 0 | child.prefix_length ()); |
428 | | |
429 | | // Copy the rest of child node's data to the current node. |
430 | 0 | current_node.set_first_bytes (child.first_bytes ()); |
431 | 0 | current_node.set_node_pointers (child.node_pointers ()); |
432 | 0 | current_node.set_refcount (child.refcount ()); |
433 | |
|
434 | 0 | free (child._data); |
435 | 0 | parent_node.set_node_at (edge_index, current_node); |
436 | 0 | return true; |
437 | 0 | } |
438 | | |
439 | 0 | if (parent_node.edgecount () == 2 && parent_node.refcount () == 0 |
440 | 0 | && 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 | 0 | zmq_assert (edge_index < 2); |
445 | 0 | 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 | 0 | const uint32_t old_prefix_length = parent_node.prefix_length (); |
451 | 0 | parent_node.resize (old_prefix_length + other_child.prefix_length (), |
452 | 0 | other_child.edgecount ()); |
453 | | |
454 | | // Append the child node's prefix to the current node. |
455 | 0 | memcpy (parent_node.prefix () + old_prefix_length, |
456 | 0 | other_child.prefix (), other_child.prefix_length ()); |
457 | | |
458 | | // Copy the rest of child node's data to the current node. |
459 | 0 | parent_node.set_first_bytes (other_child.first_bytes ()); |
460 | 0 | parent_node.set_node_pointers (other_child.node_pointers ()); |
461 | 0 | parent_node.set_refcount (other_child.refcount ()); |
462 | |
|
463 | 0 | free (current_node._data); |
464 | 0 | free (other_child._data); |
465 | 0 | grandparent_node.set_node_at (parent_edge_index, parent_node); |
466 | 0 | return true; |
467 | 0 | } |
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 | 0 | 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 | 0 | const size_t last_index = parent_node.edgecount () - 1; |
478 | 0 | const unsigned char last_byte = parent_node.first_byte_at (last_index); |
479 | 0 | const node_t last_node = parent_node.node_at (last_index); |
480 | 0 | 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 | 0 | memmove (parent_node.node_pointers () - 1, parent_node.node_pointers (), |
486 | 0 | 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 | 0 | parent_node.resize (parent_node.prefix_length (), |
491 | 0 | parent_node.edgecount () - 1); |
492 | | |
493 | | // Nothing points to this node now, so we can reclaim it. |
494 | 0 | free (current_node._data); |
495 | |
|
496 | 0 | if (parent_node.prefix_length () == 0) |
497 | 0 | _root._data = parent_node._data; |
498 | 0 | else |
499 | 0 | grandparent_node.set_node_at (parent_edge_index, parent_node); |
500 | 0 | return true; |
501 | 0 | } |
502 | | |
503 | | bool zmq::radix_tree_t::check (const unsigned char *key_, size_t key_size_) |
504 | 0 | { |
505 | 0 | if (_root.refcount () > 0) |
506 | 0 | return true; |
507 | | |
508 | 0 | match_result_t match_result = match (key_, key_size_, true); |
509 | 0 | return match_result._key_bytes_matched == key_size_ |
510 | 0 | && match_result._prefix_bytes_matched |
511 | 0 | == match_result._current_node.prefix_length () |
512 | 0 | && match_result._current_node.refcount () > 0; |
513 | 0 | } |
514 | | |
515 | | static void |
516 | | visit_keys (node_t node_, |
517 | | std::vector<unsigned char> &buffer_, |
518 | | void (*func_) (unsigned char *data_, size_t size_, void *arg_), |
519 | | void *arg_) |
520 | 0 | { |
521 | 0 | const size_t prefix_length = node_.prefix_length (); |
522 | 0 | buffer_.reserve (buffer_.size () + prefix_length); |
523 | 0 | std::copy (node_.prefix (), node_.prefix () + prefix_length, |
524 | 0 | std::back_inserter (buffer_)); |
525 | |
|
526 | 0 | if (node_.refcount () > 0) { |
527 | 0 | zmq_assert (!buffer_.empty ()); |
528 | 0 | func_ (&buffer_[0], buffer_.size (), arg_); |
529 | 0 | } |
530 | |
|
531 | 0 | for (size_t i = 0, edgecount = node_.edgecount (); i < edgecount; ++i) { |
532 | 0 | visit_keys (node_.node_at (i), buffer_, func_, arg_); |
533 | 0 | } |
534 | 0 | buffer_.resize (static_cast<uint32_t> (buffer_.size () - prefix_length)); |
535 | 0 | } |
536 | | |
537 | | void zmq::radix_tree_t::apply ( |
538 | | void (*func_) (unsigned char *data_, size_t size_, void *arg_), void *arg_) |
539 | 0 | { |
540 | 0 | if (_root.refcount () > 0) |
541 | 0 | func_ (NULL, 0, arg_); // Root node is always empty. |
542 | |
|
543 | 0 | std::vector<unsigned char> buffer; |
544 | 0 | for (size_t i = 0; i < _root.edgecount (); ++i) |
545 | 0 | visit_keys (_root.node_at (i), buffer, func_, arg_); |
546 | 0 | } |
547 | | |
548 | | size_t zmq::radix_tree_t::size () const |
549 | 0 | { |
550 | 0 | return _size.get (); |
551 | 0 | } |