Coverage Report

Created: 2026-10-17 06:29

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}