trie.cpp 11 KB

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