libzmq  master
ZeroMQ C++ Core Engine (LIBZMQ)
trie.cpp
Go to the documentation of this file.
1 /*
2  Copyright (c) 2007-2016 Contributors as noted in the AUTHORS file
3 
4  This file is part of libzmq, the ZeroMQ core engine in C++.
5 
6  libzmq is free software; you can redistribute it and/or modify it under
7  the terms of the GNU Lesser General Public License (LGPL) as published
8  by the Free Software Foundation; either version 3 of the License, or
9  (at your option) any later version.
10 
11  As a special exception, the Contributors give you permission to link
12  this library with independent modules to produce an executable,
13  regardless of the license terms of these independent modules, and to
14  copy and distribute the resulting executable under terms of your choice,
15  provided that you also meet, for each linked independent module, the
16  terms and conditions of the license of that module. An independent
17  module is a module which is not derived from or based on this library.
18  If you modify this library, you must extend this exception to your
19  version of the library.
20 
21  libzmq is distributed in the hope that it will be useful, but WITHOUT
22  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
23  FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
24  License for more details.
25 
26  You should have received a copy of the GNU Lesser General Public License
27  along with this program. If not, see <http://www.gnu.org/licenses/>.
28 */
29 
30 #include "precompiled.hpp"
31 #include <stdlib.h>
32 
33 #include <new>
34 #include <algorithm>
35 
36 #include "macros.hpp"
37 #include "platform.hpp"
38 #if defined ZMQ_HAVE_WINDOWS
39 #include "windows.hpp"
40 #endif
41 
42 #include "err.hpp"
43 #include "trie.hpp"
44 
46  refcnt (0),
47  min (0),
48  count (0),
49  live_nodes (0)
50 {
51 }
52 
54 {
55  if (count == 1) {
56  zmq_assert (next.node);
57  LIBZMQ_DELETE(next.node);
58  }
59  else if (count > 1) {
60  for (unsigned short i = 0; i != count; ++i) {
61  LIBZMQ_DELETE(next.table[i]);
62  }
63  free (next.table);
64  }
65 }
66 
67 bool zmq::trie_t::add (unsigned char *prefix_, size_t size_)
68 {
69  // We are at the node corresponding to the prefix. We are done.
70  if (!size_) {
71  ++refcnt;
72  return refcnt == 1;
73  }
74 
75  unsigned char c = *prefix_;
76  if (c < min || c >= min + count) {
77 
78  // The character is out of range of currently handled
79  // characters. We have to extend the table.
80  if (!count) {
81  min = c;
82  count = 1;
83  next.node = NULL;
84  }
85  else
86  if (count == 1) {
87  unsigned char oldc = min;
88  trie_t *oldp = next.node;
89  count = (min < c ? c - min : min - c) + 1;
90  next.table = (trie_t**)
91  malloc (sizeof (trie_t*) * count);
92  alloc_assert (next.table);
93  for (unsigned short i = 0; i != count; ++i)
94  next.table [i] = 0;
95  min = std::min (min, c);
96  next.table [oldc - min] = oldp;
97  }
98  else
99  if (min < c) {
100  // The new character is above the current character range.
101  unsigned short old_count = count;
102  count = c - min + 1;
103  next.table = (trie_t**) realloc ((void*) next.table,
104  sizeof (trie_t*) * count);
105  zmq_assert (next.table);
106  for (unsigned short i = old_count; i != count; i++)
107  next.table [i] = NULL;
108  }
109  else {
110 
111  // The new character is below the current character range.
112  unsigned short old_count = count;
113  count = (min + old_count) - c;
114  next.table = (trie_t**) realloc ((void*) next.table,
115  sizeof (trie_t*) * count);
116  zmq_assert (next.table);
117  memmove (next.table + min - c, next.table,
118  old_count * sizeof (trie_t*));
119  for (unsigned short i = 0; i != min - c; i++)
120  next.table [i] = NULL;
121  min = c;
122  }
123  }
124 
125  // If next node does not exist, create one.
126  if (count == 1) {
127  if (!next.node) {
128  next.node = new (std::nothrow) trie_t;
129  alloc_assert (next.node);
130  ++live_nodes;
131  zmq_assert (live_nodes == 1);
132  }
133  return next.node->add (prefix_ + 1, size_ - 1);
134  }
135  else {
136  if (!next.table [c - min]) {
137  next.table [c - min] = new (std::nothrow) trie_t;
138  alloc_assert (next.table [c - min]);
139  ++live_nodes;
140  zmq_assert (live_nodes > 1);
141  }
142  return next.table [c - min]->add (prefix_ + 1, size_ - 1);
143  }
144 }
145 
146 bool zmq::trie_t::rm (unsigned char *prefix_, size_t size_)
147 {
148  // TODO: Shouldn't an error be reported if the key does not exist?
149  if (!size_) {
150  if (!refcnt)
151  return false;
152  refcnt--;
153  return refcnt == 0;
154  }
155  unsigned char c = *prefix_;
156  if (!count || c < min || c >= min + count)
157  return false;
158 
159  trie_t *next_node =
160  count == 1 ? next.node : next.table [c - min];
161 
162  if (!next_node)
163  return false;
164 
165  bool ret = next_node->rm (prefix_ + 1, size_ - 1);
166 
167  // Prune redundant nodes
168  if (next_node->is_redundant ()) {
169  LIBZMQ_DELETE(next_node);
170  zmq_assert (count > 0);
171 
172  if (count == 1) {
173  // The just pruned node is was the only live node
174  next.node = 0;
175  count = 0;
176  --live_nodes;
177  zmq_assert (live_nodes == 0);
178  }
179  else {
180  next.table [c - min] = 0;
181  zmq_assert (live_nodes > 1);
182  --live_nodes;
183 
184  // Compact the table if possible
185  if (live_nodes == 1) {
186  // We can switch to using the more compact single-node
187  // representation since the table only contains one live node
188  trie_t *node = 0;
189  // Since we always compact the table the pruned node must
190  // either be the left-most or right-most ptr in the node
191  // table
192  if (c == min) {
193  // The pruned node is the left-most node ptr in the
194  // node table => keep the right-most node
195  node = next.table [count - 1];
196  min += count - 1;
197  }
198  else
199  if (c == min + count - 1) {
200  // The pruned node is the right-most node ptr in the
201  // node table => keep the left-most node
202  node = next.table [0];
203  }
204  zmq_assert (node);
205  free (next.table);
206  next.node = node;
207  count = 1;
208  }
209  else
210  if (c == min) {
211  // We can compact the table "from the left".
212  // Find the left-most non-null node ptr, which we'll use as
213  // our new min
214  unsigned char new_min = min;
215  for (unsigned short i = 1; i < count; ++i) {
216  if (next.table [i]) {
217  new_min = i + min;
218  break;
219  }
220  }
221  zmq_assert (new_min != min);
222 
223  trie_t **old_table = next.table;
224  zmq_assert (new_min > min);
225  zmq_assert (count > new_min - min);
226 
227  count = count - (new_min - min);
228  next.table = (trie_t**) malloc (sizeof (trie_t*) * count);
229  alloc_assert (next.table);
230 
231  memmove (next.table, old_table + (new_min - min),
232  sizeof (trie_t*) * count);
233  free (old_table);
234 
235  min = new_min;
236  }
237  else
238  if (c == min + count - 1) {
239  // We can compact the table "from the right".
240  // Find the right-most non-null node ptr, which we'll use to
241  // determine the new table size
242  unsigned short new_count = count;
243  for (unsigned short i = 1; i < count; ++i) {
244  if (next.table [count - 1 - i]) {
245  new_count = count - i;
246  break;
247  }
248  }
249  zmq_assert (new_count != count);
250  count = new_count;
251 
252  trie_t **old_table = next.table;
253  next.table = (trie_t**) malloc (sizeof (trie_t*) * count);
254  alloc_assert (next.table);
255 
256  memmove (next.table, old_table, sizeof (trie_t*) * count);
257  free (old_table);
258  }
259  }
260  }
261  return ret;
262 }
263 
264 bool zmq::trie_t::check (unsigned char *data_, size_t size_)
265 {
266  // This function is on critical path. It deliberately doesn't use
267  // recursion to get a bit better performance.
268  trie_t *current = this;
269  while (true) {
270 
271  // We've found a corresponding subscription!
272  if (current->refcnt)
273  return true;
274 
275  // We've checked all the data and haven't found matching subscription.
276  if (!size_)
277  return false;
278 
279  // If there's no corresponding slot for the first character
280  // of the prefix, the message does not match.
281  unsigned char c = *data_;
282  if (c < current->min || c >= current->min + current->count)
283  return false;
284 
285  // Move to the next character.
286  if (current->count == 1)
287  current = current->next.node;
288  else {
289  current = current->next.table [c - current->min];
290  if (!current)
291  return false;
292  }
293  data_++;
294  size_--;
295  }
296 }
297 
298 void zmq::trie_t::apply (void (*func_) (unsigned char *data_, size_t size_,
299  void *arg_), void *arg_)
300 {
301  unsigned char *buff = NULL;
302  apply_helper (&buff, 0, 0, func_, arg_);
303  free (buff);
304 }
305 
307  unsigned char **buff_, size_t buffsize_, size_t maxbuffsize_,
308  void (*func_) (unsigned char *data_, size_t size_, void *arg_), void *arg_)
309 {
310  // If this node is a subscription, apply the function.
311  if (refcnt)
312  func_ (*buff_, buffsize_, arg_);
313 
314  // Adjust the buffer.
315  if (buffsize_ >= maxbuffsize_) {
316  maxbuffsize_ = buffsize_ + 256;
317  *buff_ = (unsigned char*) realloc (*buff_, maxbuffsize_);
318  zmq_assert (*buff_);
319  }
320 
321  // If there are no subnodes in the trie, return.
322  if (count == 0)
323  return;
324 
325  // If there's one subnode (optimisation).
326  if (count == 1) {
327  (*buff_) [buffsize_] = min;
328  buffsize_++;
329  next.node->apply_helper (buff_, buffsize_, maxbuffsize_, func_, arg_);
330  return;
331  }
332 
333  // If there are multiple subnodes.
334  for (unsigned short c = 0; c != count; c++) {
335  (*buff_) [buffsize_] = min + c;
336  if (next.table [c])
337  next.table [c]->apply_helper (buff_, buffsize_ + 1, maxbuffsize_,
338  func_, arg_);
339  }
340 }
341 
343 {
344  return refcnt == 0 && live_nodes == 0;
345 }
#define LIBZMQ_DELETE(p_object)
Definition: macros.hpp:7
unsigned short live_nodes
Definition: trie.hpp:73
#define zmq_assert(x)
Definition: err.hpp:119
bool is_redundant() const
Definition: trie.cpp:342
union zmq::trie_t::@62 next
void apply_helper(unsigned char **buff_, size_t buffsize_, size_t maxbuffsize_, void(*func_)(unsigned char *data_, size_t size_, void *arg_), void *arg_)
Definition: trie.cpp:306
bool add(unsigned char *prefix_, size_t size_)
Definition: trie.cpp:67
~trie_t()
Definition: trie.cpp:53
#define alloc_assert(x)
Definition: err.hpp:159
class trie_t * node
Definition: trie.hpp:75
trie_t()
Definition: trie.cpp:45
uint32_t refcnt
Definition: trie.hpp:70
class trie_t ** table
Definition: trie.hpp:76
unsigned short count
Definition: trie.hpp:72
bool check(unsigned char *data_, size_t size_)
Definition: trie.cpp:264
void apply(void(*func_)(unsigned char *data_, size_t size_, void *arg_), void *arg_)
Definition: trie.cpp:298
bool rm(unsigned char *prefix_, size_t size_)
Definition: trie.cpp:146
unsigned char min
Definition: trie.hpp:71