1309 lines
48 KiB
Diff
1309 lines
48 KiB
Diff
|
Index: zeromq-4.2.3/src/generic_mtrie.hpp
|
||
|
===================================================================
|
||
|
--- /dev/null
|
||
|
+++ zeromq-4.2.3/src/generic_mtrie.hpp
|
||
|
@@ -0,0 +1,117 @@
|
||
|
+/*
|
||
|
+Copyright (c) 2018 Contributors as noted in the AUTHORS file
|
||
|
+
|
||
|
+This file is part of libzmq, the ZeroMQ core engine in C++.
|
||
|
+
|
||
|
+libzmq is free software; you can redistribute it and/or modify it under
|
||
|
+the terms of the GNU Lesser General Public License (LGPL) as published
|
||
|
+by the Free Software Foundation; either version 3 of the License, or
|
||
|
+(at your option) any later version.
|
||
|
+
|
||
|
+As a special exception, the Contributors give you permission to link
|
||
|
+this library with independent modules to produce an executable,
|
||
|
+regardless of the license terms of these independent modules, and to
|
||
|
+copy and distribute the resulting executable under terms of your choice,
|
||
|
+provided that you also meet, for each linked independent module, the
|
||
|
+terms and conditions of the license of that module. An independent
|
||
|
+module is a module which is not derived from or based on this library.
|
||
|
+If you modify this library, you must extend this exception to your
|
||
|
+version of the library.
|
||
|
+
|
||
|
+libzmq is distributed in the hope that it will be useful, but WITHOUT
|
||
|
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||
|
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
||
|
+License for more details.
|
||
|
+
|
||
|
+You should have received a copy of the GNU Lesser General Public License
|
||
|
+along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||
|
+*/
|
||
|
+
|
||
|
+#ifndef __ZMQ_GENERIC_MTRIE_HPP_INCLUDED__
|
||
|
+#define __ZMQ_GENERIC_MTRIE_HPP_INCLUDED__
|
||
|
+
|
||
|
+#include <stddef.h>
|
||
|
+#include <set>
|
||
|
+
|
||
|
+#include "macros.hpp"
|
||
|
+#include "stdint.hpp"
|
||
|
+
|
||
|
+namespace zmq
|
||
|
+{
|
||
|
+// Multi-trie (prefix tree). Each node in the trie is a set of pointers.
|
||
|
+template <typename T> class generic_mtrie_t
|
||
|
+{
|
||
|
+ public:
|
||
|
+ typedef T value_t;
|
||
|
+ typedef const unsigned char *prefix_t;
|
||
|
+
|
||
|
+ enum rm_result
|
||
|
+ {
|
||
|
+ not_found,
|
||
|
+ last_value_removed,
|
||
|
+ values_remain
|
||
|
+ };
|
||
|
+
|
||
|
+ generic_mtrie_t ();
|
||
|
+ ~generic_mtrie_t ();
|
||
|
+
|
||
|
+ // Add key to the trie. Returns true iff no entry with the same prefix_
|
||
|
+ // and size_ existed before.
|
||
|
+ bool add (prefix_t prefix_, size_t size_, value_t *value_);
|
||
|
+
|
||
|
+ // Remove all entries with a specific value from the trie.
|
||
|
+ // The call_on_uniq_ flag controls if the callback is invoked
|
||
|
+ // when there are no entries left on a prefix only (true)
|
||
|
+ // or on every removal (false). The arg_ argument is passed
|
||
|
+ // through to the callback function.
|
||
|
+ template <typename Arg>
|
||
|
+ void rm (value_t *value_,
|
||
|
+ void (*func_) (const unsigned char *data_, size_t size_, Arg arg_),
|
||
|
+ Arg arg_,
|
||
|
+ bool call_on_uniq_);
|
||
|
+
|
||
|
+ // Removes a specific entry from the trie.
|
||
|
+ // Returns the result of the operation.
|
||
|
+ rm_result rm (prefix_t prefix_, size_t size_, value_t *value_);
|
||
|
+
|
||
|
+ // Calls a callback function for all matching entries, i.e. any node
|
||
|
+ // corresponding to data_ or a prefix of it. The arg_ argument
|
||
|
+ // is passed through to the callback function.
|
||
|
+ template <typename Arg>
|
||
|
+ void match (prefix_t data_,
|
||
|
+ size_t size_,
|
||
|
+ void (*func_) (value_t *value_, Arg arg_),
|
||
|
+ Arg arg_);
|
||
|
+
|
||
|
+ private:
|
||
|
+ bool is_redundant () const;
|
||
|
+
|
||
|
+ typedef std::set<value_t *> pipes_t;
|
||
|
+ pipes_t *_pipes;
|
||
|
+
|
||
|
+ unsigned char _min;
|
||
|
+ unsigned short _count;
|
||
|
+ unsigned short _live_nodes;
|
||
|
+ union _next_t
|
||
|
+ {
|
||
|
+ class generic_mtrie_t<value_t> *node;
|
||
|
+ class generic_mtrie_t<value_t> **table;
|
||
|
+ } _next;
|
||
|
+
|
||
|
+ struct iter
|
||
|
+ {
|
||
|
+ generic_mtrie_t<value_t> *node;
|
||
|
+ generic_mtrie_t<value_t> *next_node;
|
||
|
+ prefix_t prefix;
|
||
|
+ size_t size;
|
||
|
+ unsigned short current_child;
|
||
|
+ unsigned char new_min;
|
||
|
+ unsigned char new_max;
|
||
|
+ bool processed_for_removal;
|
||
|
+ };
|
||
|
+
|
||
|
+ ZMQ_NON_COPYABLE_NOR_MOVABLE (generic_mtrie_t)
|
||
|
+};
|
||
|
+}
|
||
|
+
|
||
|
+#endif
|
||
|
Index: zeromq-4.2.3/src/generic_mtrie_impl.hpp
|
||
|
===================================================================
|
||
|
--- /dev/null
|
||
|
+++ zeromq-4.2.3/src/generic_mtrie_impl.hpp
|
||
|
@@ -0,0 +1,586 @@
|
||
|
+/*
|
||
|
+Copyright (c) 2018 Contributors as noted in the AUTHORS file
|
||
|
+
|
||
|
+This file is part of libzmq, the ZeroMQ core engine in C++.
|
||
|
+
|
||
|
+libzmq is free software; you can redistribute it and/or modify it under
|
||
|
+the terms of the GNU Lesser General Public License (LGPL) as published
|
||
|
+by the Free Software Foundation; either version 3 of the License, or
|
||
|
+(at your option) any later version.
|
||
|
+
|
||
|
+As a special exception, the Contributors give you permission to link
|
||
|
+this library with independent modules to produce an executable,
|
||
|
+regardless of the license terms of these independent modules, and to
|
||
|
+copy and distribute the resulting executable under terms of your choice,
|
||
|
+provided that you also meet, for each linked independent module, the
|
||
|
+terms and conditions of the license of that module. An independent
|
||
|
+module is a module which is not derived from or based on this library.
|
||
|
+If you modify this library, you must extend this exception to your
|
||
|
+version of the library.
|
||
|
+
|
||
|
+libzmq is distributed in the hope that it will be useful, but WITHOUT
|
||
|
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||
|
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
||
|
+License for more details.
|
||
|
+
|
||
|
+You should have received a copy of the GNU Lesser General Public License
|
||
|
+along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||
|
+*/
|
||
|
+
|
||
|
+#ifndef __ZMQ_GENERIC_MTRIE_IMPL_HPP_INCLUDED__
|
||
|
+#define __ZMQ_GENERIC_MTRIE_IMPL_HPP_INCLUDED__
|
||
|
+
|
||
|
+
|
||
|
+#include <stdlib.h>
|
||
|
+
|
||
|
+#include <new>
|
||
|
+#include <algorithm>
|
||
|
+#include <list>
|
||
|
+
|
||
|
+#include "err.hpp"
|
||
|
+#include "macros.hpp"
|
||
|
+#include "generic_mtrie.hpp"
|
||
|
+
|
||
|
+namespace zmq
|
||
|
+{
|
||
|
+template <typename T>
|
||
|
+generic_mtrie_t<T>::generic_mtrie_t () :
|
||
|
+ _pipes (0),
|
||
|
+ _min (0),
|
||
|
+ _count (0),
|
||
|
+ _live_nodes (0)
|
||
|
+{
|
||
|
+}
|
||
|
+
|
||
|
+template <typename T> generic_mtrie_t<T>::~generic_mtrie_t ()
|
||
|
+{
|
||
|
+ LIBZMQ_DELETE (_pipes);
|
||
|
+
|
||
|
+ if (_count == 1) {
|
||
|
+ zmq_assert (_next.node);
|
||
|
+ LIBZMQ_DELETE (_next.node);
|
||
|
+ } else if (_count > 1) {
|
||
|
+ for (unsigned short i = 0; i != _count; ++i) {
|
||
|
+ LIBZMQ_DELETE (_next.table[i]);
|
||
|
+ }
|
||
|
+ free (_next.table);
|
||
|
+ }
|
||
|
+}
|
||
|
+
|
||
|
+template <typename T>
|
||
|
+bool generic_mtrie_t<T>::add (prefix_t prefix_, size_t size_, value_t *pipe_)
|
||
|
+{
|
||
|
+ generic_mtrie_t<value_t> *it = this;
|
||
|
+
|
||
|
+ while (size_) {
|
||
|
+ const unsigned char c = *prefix_;
|
||
|
+
|
||
|
+ if (c < it->_min || c >= it->_min + it->_count) {
|
||
|
+ // The character is out of range of currently handled
|
||
|
+ // characters. We have to extend the table.
|
||
|
+ if (!it->_count) {
|
||
|
+ it->_min = c;
|
||
|
+ it->_count = 1;
|
||
|
+ it->_next.node = NULL;
|
||
|
+ } else if (it->_count == 1) {
|
||
|
+ const unsigned char oldc = it->_min;
|
||
|
+ generic_mtrie_t *oldp = it->_next.node;
|
||
|
+ it->_count = (it->_min < c ? c - it->_min : it->_min - c) + 1;
|
||
|
+ it->_next.table = static_cast<generic_mtrie_t **> (
|
||
|
+ malloc (sizeof (generic_mtrie_t *) * it->_count));
|
||
|
+ alloc_assert (it->_next.table);
|
||
|
+ for (unsigned short i = 0; i != it->_count; ++i)
|
||
|
+ it->_next.table[i] = 0;
|
||
|
+ it->_min = std::min (it->_min, c);
|
||
|
+ it->_next.table[oldc - it->_min] = oldp;
|
||
|
+ } else if (it->_min < c) {
|
||
|
+ // The new character is above the current character range.
|
||
|
+ const unsigned short old_count = it->_count;
|
||
|
+ it->_count = c - it->_min + 1;
|
||
|
+ it->_next.table = static_cast<generic_mtrie_t **> (realloc (
|
||
|
+ it->_next.table, sizeof (generic_mtrie_t *) * it->_count));
|
||
|
+ alloc_assert (it->_next.table);
|
||
|
+ for (unsigned short i = old_count; i != it->_count; i++)
|
||
|
+ it->_next.table[i] = NULL;
|
||
|
+ } else {
|
||
|
+ // The new character is below the current character range.
|
||
|
+ const unsigned short old_count = it->_count;
|
||
|
+ it->_count = (it->_min + old_count) - c;
|
||
|
+ it->_next.table = static_cast<generic_mtrie_t **> (realloc (
|
||
|
+ it->_next.table, sizeof (generic_mtrie_t *) * it->_count));
|
||
|
+ alloc_assert (it->_next.table);
|
||
|
+ memmove (it->_next.table + it->_min - c, it->_next.table,
|
||
|
+ old_count * sizeof (generic_mtrie_t *));
|
||
|
+ for (unsigned short i = 0; i != it->_min - c; i++)
|
||
|
+ it->_next.table[i] = NULL;
|
||
|
+ it->_min = c;
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ // If next node does not exist, create one.
|
||
|
+ if (it->_count == 1) {
|
||
|
+ if (!it->_next.node) {
|
||
|
+ it->_next.node = new (std::nothrow) generic_mtrie_t;
|
||
|
+ alloc_assert (it->_next.node);
|
||
|
+ ++(it->_live_nodes);
|
||
|
+ }
|
||
|
+
|
||
|
+ ++prefix_;
|
||
|
+ --size_;
|
||
|
+ it = it->_next.node;
|
||
|
+ } else {
|
||
|
+ if (!it->_next.table[c - it->_min]) {
|
||
|
+ it->_next.table[c - it->_min] =
|
||
|
+ new (std::nothrow) generic_mtrie_t;
|
||
|
+ alloc_assert (it->_next.table[c - it->_min]);
|
||
|
+ ++(it->_live_nodes);
|
||
|
+ }
|
||
|
+
|
||
|
+ ++prefix_;
|
||
|
+ --size_;
|
||
|
+ it = it->_next.table[c - it->_min];
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ // We are at the node corresponding to the prefix. We are done.
|
||
|
+ const bool result = !it->_pipes;
|
||
|
+ if (!it->_pipes) {
|
||
|
+ it->_pipes = new (std::nothrow) pipes_t;
|
||
|
+ alloc_assert (it->_pipes);
|
||
|
+ }
|
||
|
+ it->_pipes->insert (pipe_);
|
||
|
+
|
||
|
+ return result;
|
||
|
+}
|
||
|
+
|
||
|
+template <typename T>
|
||
|
+template <typename Arg>
|
||
|
+void generic_mtrie_t<T>::rm (value_t *pipe_,
|
||
|
+ void (*func_) (prefix_t data_,
|
||
|
+ size_t size_,
|
||
|
+ Arg arg_),
|
||
|
+ Arg arg_,
|
||
|
+ bool call_on_uniq_)
|
||
|
+{
|
||
|
+ // This used to be implemented as a non-tail recursive travesal of the trie,
|
||
|
+ // which means remote clients controlled the depth of the recursion and the
|
||
|
+ // stack size.
|
||
|
+ // To simulate the non-tail recursion, with post-recursion changes depending on
|
||
|
+ // the result of the recursive call, a stack is used to re-visit the same node
|
||
|
+ // and operate on it again after children have been visisted.
|
||
|
+ // A boolean is used to record whether the node had already been visited and to
|
||
|
+ // determine if the pre- or post- children visit actions have to be taken.
|
||
|
+ // In the case of a node with (N > 1) children, the node has to be re-visited
|
||
|
+ // N times, in the correct order after each child visit.
|
||
|
+ std::list<struct iter> stack;
|
||
|
+ unsigned char *buff = NULL;
|
||
|
+ size_t maxbuffsize = 0;
|
||
|
+ struct iter it = {this, NULL, NULL, 0, 0, 0, false};
|
||
|
+ stack.push_back (it);
|
||
|
+
|
||
|
+ while (!stack.empty ()) {
|
||
|
+ it = stack.back ();
|
||
|
+ stack.pop_back ();
|
||
|
+
|
||
|
+ if (!it.processed_for_removal) {
|
||
|
+ // Remove the subscription from this node.
|
||
|
+ if (it.node->_pipes && it.node->_pipes->erase (pipe_)) {
|
||
|
+ if (!call_on_uniq_ || it.node->_pipes->empty ()) {
|
||
|
+ func_ (buff, it.size, arg_);
|
||
|
+ }
|
||
|
+
|
||
|
+ if (it.node->_pipes->empty ()) {
|
||
|
+ LIBZMQ_DELETE (it.node->_pipes);
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ // Adjust the buffer.
|
||
|
+ if (it.size >= maxbuffsize) {
|
||
|
+ maxbuffsize = it.size + 256;
|
||
|
+ buff =
|
||
|
+ static_cast<unsigned char *> (realloc (buff, maxbuffsize));
|
||
|
+ alloc_assert (buff);
|
||
|
+ }
|
||
|
+
|
||
|
+ switch (it.node->_count) {
|
||
|
+ case 0:
|
||
|
+ // If there are no subnodes in the trie, we are done with this node
|
||
|
+ // pre-processing.
|
||
|
+ break;
|
||
|
+ case 1: {
|
||
|
+ // If there's one subnode (optimisation).
|
||
|
+
|
||
|
+ buff[it.size] = it.node->_min;
|
||
|
+ // Mark this node as pre-processed and push it, so that the next
|
||
|
+ // visit after the operation on the child can do the removals.
|
||
|
+ it.processed_for_removal = true;
|
||
|
+ stack.push_back (it);
|
||
|
+ struct iter next = {
|
||
|
+ it.node->_next.node, NULL, NULL, ++it.size, 0, 0, false};
|
||
|
+ stack.push_back (next);
|
||
|
+ break;
|
||
|
+ }
|
||
|
+ default: {
|
||
|
+ // If there are multiple subnodes.
|
||
|
+ // When first visiting this node, initialize the new_min/max parameters
|
||
|
+ // which will then be used after each child has been processed, on the
|
||
|
+ // post-children iterations.
|
||
|
+ if (it.current_child == 0) {
|
||
|
+ // New min non-null character in the node table after the removal
|
||
|
+ it.new_min = it.node->_min + it.node->_count - 1;
|
||
|
+ // New max non-null character in the node table after the removal
|
||
|
+ it.new_max = it.node->_min;
|
||
|
+ }
|
||
|
+
|
||
|
+ // Mark this node as pre-processed and push it, so that the next
|
||
|
+ // visit after the operation on the child can do the removals.
|
||
|
+ buff[it.size] = it.node->_min + it.current_child;
|
||
|
+ it.processed_for_removal = true;
|
||
|
+ stack.push_back (it);
|
||
|
+ if (it.node->_next.table[it.current_child]) {
|
||
|
+ struct iter next = {
|
||
|
+ it.node->_next.table[it.current_child],
|
||
|
+ NULL,
|
||
|
+ NULL,
|
||
|
+ it.size + 1,
|
||
|
+ 0,
|
||
|
+ 0,
|
||
|
+ false};
|
||
|
+ stack.push_back (next);
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+ } else {
|
||
|
+ // Reset back for the next time, in case this node doesn't get deleted.
|
||
|
+ // This is done unconditionally, unlike when setting this variable to true.
|
||
|
+ it.processed_for_removal = false;
|
||
|
+
|
||
|
+ switch (it.node->_count) {
|
||
|
+ case 0:
|
||
|
+ // If there are no subnodes in the trie, we are done with this node
|
||
|
+ // post-processing.
|
||
|
+ break;
|
||
|
+ case 1:
|
||
|
+ // If there's one subnode (optimisation).
|
||
|
+
|
||
|
+ // Prune the node if it was made redundant by the removal
|
||
|
+ if (it.node->_next.node->is_redundant ()) {
|
||
|
+ LIBZMQ_DELETE (it.node->_next.node);
|
||
|
+ it.node->_count = 0;
|
||
|
+ --it.node->_live_nodes;
|
||
|
+ zmq_assert (it.node->_live_nodes == 0);
|
||
|
+ }
|
||
|
+ break;
|
||
|
+ default:
|
||
|
+ // If there are multiple subnodes.
|
||
|
+ {
|
||
|
+ if (it.node->_next.table[it.current_child]) {
|
||
|
+ // Prune redundant nodes from the mtrie
|
||
|
+ if (it.node->_next.table[it.current_child]
|
||
|
+ ->is_redundant ()) {
|
||
|
+ LIBZMQ_DELETE (
|
||
|
+ it.node->_next.table[it.current_child]);
|
||
|
+
|
||
|
+ zmq_assert (it.node->_live_nodes > 0);
|
||
|
+ --it.node->_live_nodes;
|
||
|
+ } else {
|
||
|
+ // The node is not redundant, so it's a candidate for being
|
||
|
+ // the new min/max node.
|
||
|
+ //
|
||
|
+ // We loop through the node array from left to right, so the
|
||
|
+ // first non-null, non-redundant node encountered is the new
|
||
|
+ // minimum index. Conversely, the last non-redundant, non-null
|
||
|
+ // node encountered is the new maximum index.
|
||
|
+ if (it.current_child + it.node->_min
|
||
|
+ < it.new_min)
|
||
|
+ it.new_min =
|
||
|
+ it.current_child + it.node->_min;
|
||
|
+ if (it.current_child + it.node->_min
|
||
|
+ > it.new_max)
|
||
|
+ it.new_max =
|
||
|
+ it.current_child + it.node->_min;
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ // If there are more children to visit, push again the current
|
||
|
+ // node, so that pre-processing can happen on the next child.
|
||
|
+ // If we are done, reset the child index so that the ::rm is
|
||
|
+ // fully idempotent.
|
||
|
+ ++it.current_child;
|
||
|
+ if (it.current_child >= it.node->_count)
|
||
|
+ it.current_child = 0;
|
||
|
+ else {
|
||
|
+ stack.push_back (it);
|
||
|
+ continue;
|
||
|
+ }
|
||
|
+
|
||
|
+ // All children have been visited and removed if needed, and
|
||
|
+ // all pre- and post-visit operations have been carried.
|
||
|
+ // Resize/free the node table if needed.
|
||
|
+ zmq_assert (it.node->_count > 1);
|
||
|
+
|
||
|
+ // Free the node table if it's no longer used.
|
||
|
+ switch (it.node->_live_nodes) {
|
||
|
+ case 0:
|
||
|
+ free (it.node->_next.table);
|
||
|
+ it.node->_next.table = NULL;
|
||
|
+ it.node->_count = 0;
|
||
|
+ break;
|
||
|
+ case 1:
|
||
|
+ // Compact the node table if possible
|
||
|
+
|
||
|
+ // If there's only one live node in the table we can
|
||
|
+ // switch to using the more compact single-node
|
||
|
+ // representation
|
||
|
+ zmq_assert (it.new_min == it.new_max);
|
||
|
+ zmq_assert (it.new_min >= it.node->_min);
|
||
|
+ zmq_assert (it.new_min
|
||
|
+ < it.node->_min + it.node->_count);
|
||
|
+ {
|
||
|
+ generic_mtrie_t *node =
|
||
|
+ it.node->_next
|
||
|
+ .table[it.new_min - it.node->_min];
|
||
|
+ zmq_assert (node);
|
||
|
+ free (it.node->_next.table);
|
||
|
+ it.node->_next.node = node;
|
||
|
+ }
|
||
|
+ it.node->_count = 1;
|
||
|
+ it.node->_min = it.new_min;
|
||
|
+ break;
|
||
|
+ default:
|
||
|
+ if (it.new_min > it.node->_min
|
||
|
+ || it.new_max < it.node->_min
|
||
|
+ + it.node->_count - 1) {
|
||
|
+ zmq_assert (it.new_max - it.new_min + 1
|
||
|
+ > 1);
|
||
|
+
|
||
|
+ generic_mtrie_t **old_table =
|
||
|
+ it.node->_next.table;
|
||
|
+ zmq_assert (it.new_min > it.node->_min
|
||
|
+ || it.new_max
|
||
|
+ < it.node->_min
|
||
|
+ + it.node->_count - 1);
|
||
|
+ zmq_assert (it.new_min >= it.node->_min);
|
||
|
+ zmq_assert (it.new_max
|
||
|
+ <= it.node->_min
|
||
|
+ + it.node->_count - 1);
|
||
|
+ zmq_assert (it.new_max - it.new_min + 1
|
||
|
+ < it.node->_count);
|
||
|
+
|
||
|
+ it.node->_count =
|
||
|
+ it.new_max - it.new_min + 1;
|
||
|
+ it.node->_next.table =
|
||
|
+ static_cast<generic_mtrie_t **> (
|
||
|
+ malloc (sizeof (generic_mtrie_t *)
|
||
|
+ * it.node->_count));
|
||
|
+ alloc_assert (it.node->_next.table);
|
||
|
+
|
||
|
+ memmove (it.node->_next.table,
|
||
|
+ old_table
|
||
|
+ + (it.new_min - it.node->_min),
|
||
|
+ sizeof (generic_mtrie_t *)
|
||
|
+ * it.node->_count);
|
||
|
+ free (old_table);
|
||
|
+
|
||
|
+ it.node->_min = it.new_min;
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ free (buff);
|
||
|
+}
|
||
|
+
|
||
|
+template <typename T>
|
||
|
+typename generic_mtrie_t<T>::rm_result
|
||
|
+generic_mtrie_t<T>::rm (prefix_t prefix_, size_t size_, value_t *pipe_)
|
||
|
+{
|
||
|
+ // This used to be implemented as a non-tail recursive travesal of the trie,
|
||
|
+ // which means remote clients controlled the depth of the recursion and the
|
||
|
+ // stack size.
|
||
|
+ // To simulate the non-tail recursion, with post-recursion changes depending on
|
||
|
+ // the result of the recursive call, a stack is used to re-visit the same node
|
||
|
+ // and operate on it again after children have been visisted.
|
||
|
+ // A boolean is used to record whether the node had already been visited and to
|
||
|
+ // determine if the pre- or post- children visit actions have to be taken.
|
||
|
+ rm_result ret = not_found;
|
||
|
+ std::list<struct iter> stack;
|
||
|
+ struct iter it = {this, NULL, prefix_, size_, 0, 0, 0, false};
|
||
|
+ stack.push_back (it);
|
||
|
+
|
||
|
+ while (!stack.empty ()) {
|
||
|
+ it = stack.back ();
|
||
|
+ stack.pop_back ();
|
||
|
+
|
||
|
+ if (!it.processed_for_removal) {
|
||
|
+ if (!it.size) {
|
||
|
+ if (!it.node->_pipes) {
|
||
|
+ ret = not_found;
|
||
|
+ continue;
|
||
|
+ }
|
||
|
+
|
||
|
+ typename pipes_t::size_type erased =
|
||
|
+ it.node->_pipes->erase (pipe_);
|
||
|
+ if (it.node->_pipes->empty ()) {
|
||
|
+ zmq_assert (erased == 1);
|
||
|
+ LIBZMQ_DELETE (it.node->_pipes);
|
||
|
+ ret = last_value_removed;
|
||
|
+ continue;
|
||
|
+ }
|
||
|
+
|
||
|
+ ret = (erased == 1) ? values_remain : not_found;
|
||
|
+ continue;
|
||
|
+ }
|
||
|
+
|
||
|
+ it.current_child = *it.prefix;
|
||
|
+ if (!it.node->_count || it.current_child < it.node->_min
|
||
|
+ || it.current_child >= it.node->_min + it.node->_count) {
|
||
|
+ ret = not_found;
|
||
|
+ continue;
|
||
|
+ }
|
||
|
+
|
||
|
+ it.next_node =
|
||
|
+ it.node->_count == 1
|
||
|
+ ? it.node->_next.node
|
||
|
+ : it.node->_next.table[it.current_child - it.node->_min];
|
||
|
+ if (!it.next_node) {
|
||
|
+ ret = not_found;
|
||
|
+ continue;
|
||
|
+ }
|
||
|
+
|
||
|
+ it.processed_for_removal = true;
|
||
|
+ stack.push_back (it);
|
||
|
+ struct iter next = {
|
||
|
+ it.next_node, NULL, it.prefix + 1, it.size - 1, 0, 0, 0, false};
|
||
|
+ stack.push_back (next);
|
||
|
+ } else {
|
||
|
+ it.processed_for_removal = false;
|
||
|
+
|
||
|
+ if (it.next_node->is_redundant ()) {
|
||
|
+ LIBZMQ_DELETE (it.next_node);
|
||
|
+ zmq_assert (it.node->_count > 0);
|
||
|
+
|
||
|
+ if (it.node->_count == 1) {
|
||
|
+ it.node->_next.node = NULL;
|
||
|
+ it.node->_count = 0;
|
||
|
+ --it.node->_live_nodes;
|
||
|
+ zmq_assert (it.node->_live_nodes == 0);
|
||
|
+ } else {
|
||
|
+ it.node->_next.table[it.current_child - it.node->_min] = 0;
|
||
|
+ zmq_assert (it.node->_live_nodes > 1);
|
||
|
+ --it.node->_live_nodes;
|
||
|
+
|
||
|
+ // Compact the table if possible
|
||
|
+ if (it.node->_live_nodes == 1) {
|
||
|
+ // If there's only one live node in the table we can
|
||
|
+ // switch to using the more compact single-node
|
||
|
+ // representation
|
||
|
+ unsigned short i;
|
||
|
+ for (i = 0; i < it.node->_count; ++i)
|
||
|
+ if (it.node->_next.table[i])
|
||
|
+ break;
|
||
|
+
|
||
|
+ zmq_assert (i < it.node->_count);
|
||
|
+ it.node->_min += i;
|
||
|
+ it.node->_count = 1;
|
||
|
+ generic_mtrie_t *oldp = it.node->_next.table[i];
|
||
|
+ free (it.node->_next.table);
|
||
|
+ it.node->_next.table = NULL;
|
||
|
+ it.node->_next.node = oldp;
|
||
|
+ } else if (it.current_child == it.node->_min) {
|
||
|
+ // We can compact the table "from the left"
|
||
|
+ unsigned short i;
|
||
|
+ for (i = 1; i < it.node->_count; ++i)
|
||
|
+ if (it.node->_next.table[i])
|
||
|
+ break;
|
||
|
+
|
||
|
+ zmq_assert (i < it.node->_count);
|
||
|
+ it.node->_min += i;
|
||
|
+ it.node->_count -= i;
|
||
|
+ generic_mtrie_t **old_table = it.node->_next.table;
|
||
|
+ it.node->_next.table =
|
||
|
+ static_cast<generic_mtrie_t **> (malloc (
|
||
|
+ sizeof (generic_mtrie_t *) * it.node->_count));
|
||
|
+ alloc_assert (it.node->_next.table);
|
||
|
+ memmove (it.node->_next.table, old_table + i,
|
||
|
+ sizeof (generic_mtrie_t *) * it.node->_count);
|
||
|
+ free (old_table);
|
||
|
+ } else if (it.current_child
|
||
|
+ == it.node->_min + it.node->_count - 1) {
|
||
|
+ // We can compact the table "from the right"
|
||
|
+ unsigned short i;
|
||
|
+ for (i = 1; i < it.node->_count; ++i)
|
||
|
+ if (it.node->_next.table[it.node->_count - 1 - i])
|
||
|
+ break;
|
||
|
+
|
||
|
+ zmq_assert (i < it.node->_count);
|
||
|
+ it.node->_count -= i;
|
||
|
+ generic_mtrie_t **old_table = it.node->_next.table;
|
||
|
+ it.node->_next.table =
|
||
|
+ static_cast<generic_mtrie_t **> (malloc (
|
||
|
+ sizeof (generic_mtrie_t *) * it.node->_count));
|
||
|
+ alloc_assert (it.node->_next.table);
|
||
|
+ memmove (it.node->_next.table, old_table,
|
||
|
+ sizeof (generic_mtrie_t *) * it.node->_count);
|
||
|
+ free (old_table);
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ return ret;
|
||
|
+}
|
||
|
+
|
||
|
+template <typename T>
|
||
|
+template <typename Arg>
|
||
|
+void generic_mtrie_t<T>::match (prefix_t data_,
|
||
|
+ size_t size_,
|
||
|
+ void (*func_) (value_t *pipe_, Arg arg_),
|
||
|
+ Arg arg_)
|
||
|
+{
|
||
|
+ for (generic_mtrie_t *current = this; current; data_++, size_--) {
|
||
|
+ // Signal the pipes attached to this node.
|
||
|
+ if (current->_pipes) {
|
||
|
+ for (typename pipes_t::iterator it = current->_pipes->begin (),
|
||
|
+ end = current->_pipes->end ();
|
||
|
+ it != end; ++it) {
|
||
|
+ func_ (*it, arg_);
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ // If we are at the end of the message, there's nothing more to match.
|
||
|
+ if (!size_)
|
||
|
+ break;
|
||
|
+
|
||
|
+ // If there are no subnodes in the trie, return.
|
||
|
+ if (current->_count == 0)
|
||
|
+ break;
|
||
|
+
|
||
|
+ if (current->_count == 1) {
|
||
|
+ // If there's one subnode (optimisation).
|
||
|
+ if (data_[0] != current->_min) {
|
||
|
+ break;
|
||
|
+ }
|
||
|
+ current = current->_next.node;
|
||
|
+ } else {
|
||
|
+ // If there are multiple subnodes.
|
||
|
+ if (data_[0] < current->_min
|
||
|
+ || data_[0] >= current->_min + current->_count) {
|
||
|
+ break;
|
||
|
+ }
|
||
|
+ current = current->_next.table[data_[0] - current->_min];
|
||
|
+ }
|
||
|
+ }
|
||
|
+}
|
||
|
+
|
||
|
+template <typename T> bool generic_mtrie_t<T>::is_redundant () const
|
||
|
+{
|
||
|
+ return !_pipes && _live_nodes == 0;
|
||
|
+}
|
||
|
+}
|
||
|
+
|
||
|
+
|
||
|
+#endif
|
||
|
Index: zeromq-4.2.3/src/mtrie.hpp
|
||
|
===================================================================
|
||
|
--- zeromq-4.2.3.orig/src/mtrie.hpp
|
||
|
+++ zeromq-4.2.3/src/mtrie.hpp
|
||
|
@@ -30,73 +30,23 @@
|
||
|
#ifndef __ZMQ_MTRIE_HPP_INCLUDED__
|
||
|
#define __ZMQ_MTRIE_HPP_INCLUDED__
|
||
|
|
||
|
-#include <stddef.h>
|
||
|
-#include <set>
|
||
|
+#include "generic_mtrie.hpp"
|
||
|
|
||
|
-#include "stdint.hpp"
|
||
|
+#if __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER > 1600)
|
||
|
+#define ZMQ_HAS_EXTERN_TEMPLATE 1
|
||
|
+#else
|
||
|
+#define ZMQ_HAS_EXTERN_TEMPLATE 0
|
||
|
+#endif
|
||
|
|
||
|
namespace zmq
|
||
|
{
|
||
|
+class pipe_t;
|
||
|
|
||
|
- class pipe_t;
|
||
|
-
|
||
|
- // Multi-trie. Each node in the trie is a set of pointers to pipes.
|
||
|
-
|
||
|
- class mtrie_t
|
||
|
- {
|
||
|
- public:
|
||
|
-
|
||
|
- mtrie_t ();
|
||
|
- ~mtrie_t ();
|
||
|
-
|
||
|
- // Add key to the trie. Returns true if it's a new subscription
|
||
|
- // rather than a duplicate.
|
||
|
- bool add (unsigned char *prefix_, size_t size_, zmq::pipe_t *pipe_);
|
||
|
-
|
||
|
- // Remove all subscriptions for a specific peer from the trie.
|
||
|
- // The call_on_uniq_ flag controls if the callback is invoked
|
||
|
- // when there are no subscriptions left on some topics or on
|
||
|
- // every removal.
|
||
|
- void rm (zmq::pipe_t *pipe_,
|
||
|
- void (*func_) (unsigned char *data_, size_t size_, void *arg_),
|
||
|
- void *arg_, bool call_on_uniq_);
|
||
|
-
|
||
|
- // Remove specific subscription from the trie. Return true is it was
|
||
|
- // actually removed rather than de-duplicated.
|
||
|
- bool rm (unsigned char *prefix_, size_t size_, zmq::pipe_t *pipe_);
|
||
|
-
|
||
|
- // Signal all the matching pipes.
|
||
|
- void match (unsigned char *data_, size_t size_,
|
||
|
- void (*func_) (zmq::pipe_t *pipe_, void *arg_), void *arg_);
|
||
|
-
|
||
|
- private:
|
||
|
-
|
||
|
- bool add_helper (unsigned char *prefix_, size_t size_,
|
||
|
- zmq::pipe_t *pipe_);
|
||
|
- void rm_helper (zmq::pipe_t *pipe_, unsigned char **buff_,
|
||
|
- size_t buffsize_, size_t maxbuffsize_,
|
||
|
- void (*func_) (unsigned char *data_, size_t size_, void *arg_),
|
||
|
- void *arg_, bool call_on_uniq_);
|
||
|
- bool rm_helper (unsigned char *prefix_, size_t size_,
|
||
|
- zmq::pipe_t *pipe_);
|
||
|
- bool is_redundant () const;
|
||
|
-
|
||
|
- typedef std::set <zmq::pipe_t*> pipes_t;
|
||
|
- pipes_t *pipes;
|
||
|
-
|
||
|
- unsigned char min;
|
||
|
- unsigned short count;
|
||
|
- unsigned short live_nodes;
|
||
|
- union {
|
||
|
- class mtrie_t *node;
|
||
|
- class mtrie_t **table;
|
||
|
- } next;
|
||
|
-
|
||
|
- mtrie_t (const mtrie_t&);
|
||
|
- const mtrie_t &operator = (const mtrie_t&);
|
||
|
- };
|
||
|
+#if ZMQ_HAS_EXTERN_TEMPLATE
|
||
|
+extern template class generic_mtrie_t<pipe_t>;
|
||
|
+#endif
|
||
|
|
||
|
+typedef generic_mtrie_t<pipe_t> mtrie_t;
|
||
|
}
|
||
|
|
||
|
#endif
|
||
|
-
|
||
|
Index: zeromq-4.2.3/src/macros.hpp
|
||
|
===================================================================
|
||
|
--- zeromq-4.2.3.orig/src/macros.hpp
|
||
|
+++ zeromq-4.2.3/src/macros.hpp
|
||
|
@@ -10,3 +10,12 @@
|
||
|
}
|
||
|
|
||
|
/******************************************************************************/
|
||
|
+
|
||
|
+
|
||
|
+#define ZMQ_NON_COPYABLE_NOR_MOVABLE(classname) \
|
||
|
+ public: \
|
||
|
+ classname (const classname &) = delete; \
|
||
|
+ classname &operator= (const classname &) = delete; \
|
||
|
+ classname (classname &&) = delete; \
|
||
|
+ classname &operator= (classname &&) = delete;
|
||
|
+
|
||
|
Index: zeromq-4.2.3/src/mtrie.cpp
|
||
|
===================================================================
|
||
|
--- zeromq-4.2.3.orig/src/mtrie.cpp
|
||
|
+++ zeromq-4.2.3/src/mtrie.cpp
|
||
|
@@ -28,407 +28,10 @@
|
||
|
*/
|
||
|
|
||
|
#include "precompiled.hpp"
|
||
|
-#include <stdlib.h>
|
||
|
-
|
||
|
-#include <new>
|
||
|
-#include <algorithm>
|
||
|
-
|
||
|
-#include "err.hpp"
|
||
|
-#include "pipe.hpp"
|
||
|
-#include "macros.hpp"
|
||
|
#include "mtrie.hpp"
|
||
|
+#include "generic_mtrie_impl.hpp"
|
||
|
|
||
|
-zmq::mtrie_t::mtrie_t () :
|
||
|
- pipes (0),
|
||
|
- min (0),
|
||
|
- count (0),
|
||
|
- live_nodes (0)
|
||
|
-{
|
||
|
-}
|
||
|
-
|
||
|
-zmq::mtrie_t::~mtrie_t ()
|
||
|
-{
|
||
|
- LIBZMQ_DELETE(pipes);
|
||
|
-
|
||
|
- if (count == 1) {
|
||
|
- zmq_assert (next.node);
|
||
|
- LIBZMQ_DELETE(next.node);
|
||
|
- }
|
||
|
- else if (count > 1) {
|
||
|
- for (unsigned short i = 0; i != count; ++i) {
|
||
|
- LIBZMQ_DELETE(next.table[i]);
|
||
|
- }
|
||
|
- free (next.table);
|
||
|
- }
|
||
|
-}
|
||
|
-
|
||
|
-bool zmq::mtrie_t::add (unsigned char *prefix_, size_t size_, pipe_t *pipe_)
|
||
|
-{
|
||
|
- return add_helper (prefix_, size_, pipe_);
|
||
|
-}
|
||
|
-
|
||
|
-bool zmq::mtrie_t::add_helper (unsigned char *prefix_, size_t size_,
|
||
|
- pipe_t *pipe_)
|
||
|
-{
|
||
|
- // We are at the node corresponding to the prefix. We are done.
|
||
|
- if (!size_) {
|
||
|
- bool result = !pipes;
|
||
|
- if (!pipes) {
|
||
|
- pipes = new (std::nothrow) pipes_t;
|
||
|
- alloc_assert (pipes);
|
||
|
- }
|
||
|
- pipes->insert (pipe_);
|
||
|
- return result;
|
||
|
- }
|
||
|
-
|
||
|
- unsigned char c = *prefix_;
|
||
|
- if (c < min || c >= min + count) {
|
||
|
-
|
||
|
- // The character is out of range of currently handled
|
||
|
- // characters. We have to extend the table.
|
||
|
- if (!count) {
|
||
|
- min = c;
|
||
|
- count = 1;
|
||
|
- next.node = NULL;
|
||
|
- }
|
||
|
- else
|
||
|
- if (count == 1) {
|
||
|
- unsigned char oldc = min;
|
||
|
- mtrie_t *oldp = next.node;
|
||
|
- count = (min < c ? c - min : min - c) + 1;
|
||
|
- next.table = (mtrie_t**)
|
||
|
- malloc (sizeof (mtrie_t*) * count);
|
||
|
- alloc_assert (next.table);
|
||
|
- for (unsigned short i = 0; i != count; ++i)
|
||
|
- next.table [i] = 0;
|
||
|
- min = std::min (min, c);
|
||
|
- next.table [oldc - min] = oldp;
|
||
|
- }
|
||
|
- else
|
||
|
- if (min < c) {
|
||
|
- // The new character is above the current character range.
|
||
|
- unsigned short old_count = count;
|
||
|
- count = c - min + 1;
|
||
|
- next.table = (mtrie_t**) realloc (next.table,
|
||
|
- sizeof (mtrie_t*) * count);
|
||
|
- alloc_assert (next.table);
|
||
|
- for (unsigned short i = old_count; i != count; i++)
|
||
|
- next.table [i] = NULL;
|
||
|
- }
|
||
|
- else {
|
||
|
- // The new character is below the current character range.
|
||
|
- unsigned short old_count = count;
|
||
|
- count = (min + old_count) - c;
|
||
|
- next.table = (mtrie_t**) realloc (next.table,
|
||
|
- sizeof (mtrie_t*) * count);
|
||
|
- alloc_assert (next.table);
|
||
|
- memmove (next.table + min - c, next.table,
|
||
|
- old_count * sizeof (mtrie_t*));
|
||
|
- for (unsigned short i = 0; i != min - c; i++)
|
||
|
- next.table [i] = NULL;
|
||
|
- min = c;
|
||
|
- }
|
||
|
- }
|
||
|
-
|
||
|
- // If next node does not exist, create one.
|
||
|
- if (count == 1) {
|
||
|
- if (!next.node) {
|
||
|
- next.node = new (std::nothrow) mtrie_t;
|
||
|
- alloc_assert (next.node);
|
||
|
- ++live_nodes;
|
||
|
- }
|
||
|
- return next.node->add_helper (prefix_ + 1, size_ - 1, pipe_);
|
||
|
- }
|
||
|
- else {
|
||
|
- if (!next.table [c - min]) {
|
||
|
- next.table [c - min] = new (std::nothrow) mtrie_t;
|
||
|
- alloc_assert (next.table [c - min]);
|
||
|
- ++live_nodes;
|
||
|
- }
|
||
|
- return next.table [c - min]->add_helper (prefix_ + 1, size_ - 1, pipe_);
|
||
|
- }
|
||
|
-}
|
||
|
-
|
||
|
-
|
||
|
-void zmq::mtrie_t::rm (pipe_t *pipe_,
|
||
|
- void (*func_) (unsigned char *data_, size_t size_, void *arg_),
|
||
|
- void *arg_, bool call_on_uniq_)
|
||
|
-{
|
||
|
- unsigned char *buff = NULL;
|
||
|
- rm_helper (pipe_, &buff, 0, 0, func_, arg_, call_on_uniq_);
|
||
|
- free (buff);
|
||
|
-}
|
||
|
-
|
||
|
-void zmq::mtrie_t::rm_helper (pipe_t *pipe_, unsigned char **buff_,
|
||
|
- size_t buffsize_, size_t maxbuffsize_,
|
||
|
- void (*func_) (unsigned char *data_, size_t size_, void *arg_),
|
||
|
- void *arg_, bool call_on_uniq_)
|
||
|
-{
|
||
|
- // Remove the subscription from this node.
|
||
|
- if (pipes && pipes->erase (pipe_)) {
|
||
|
- if (!call_on_uniq_ || pipes->empty ()) {
|
||
|
- func_ (*buff_, buffsize_, arg_);
|
||
|
- }
|
||
|
-
|
||
|
- if (pipes->empty ()) {
|
||
|
- LIBZMQ_DELETE(pipes);
|
||
|
- }
|
||
|
- }
|
||
|
-
|
||
|
- // Adjust the buffer.
|
||
|
- if (buffsize_ >= maxbuffsize_) {
|
||
|
- maxbuffsize_ = buffsize_ + 256;
|
||
|
- *buff_ = (unsigned char*) realloc (*buff_, maxbuffsize_);
|
||
|
- alloc_assert (*buff_);
|
||
|
- }
|
||
|
-
|
||
|
- // If there are no subnodes in the trie, return.
|
||
|
- if (count == 0)
|
||
|
- return;
|
||
|
-
|
||
|
- // If there's one subnode (optimisation).
|
||
|
- if (count == 1) {
|
||
|
- (*buff_) [buffsize_] = min;
|
||
|
- buffsize_++;
|
||
|
- next.node->rm_helper (pipe_, buff_, buffsize_, maxbuffsize_,
|
||
|
- func_, arg_, call_on_uniq_);
|
||
|
-
|
||
|
- // Prune the node if it was made redundant by the removal
|
||
|
- if (next.node->is_redundant ()) {
|
||
|
- LIBZMQ_DELETE(next.node);
|
||
|
- count = 0;
|
||
|
- --live_nodes;
|
||
|
- zmq_assert (live_nodes == 0);
|
||
|
- }
|
||
|
- return;
|
||
|
- }
|
||
|
-
|
||
|
- // If there are multiple subnodes.
|
||
|
- //
|
||
|
- // New min non-null character in the node table after the removal
|
||
|
- unsigned char new_min = min + count - 1;
|
||
|
- // New max non-null character in the node table after the removal
|
||
|
- unsigned char new_max = min;
|
||
|
- for (unsigned short c = 0; c != count; c++) {
|
||
|
- (*buff_) [buffsize_] = min + c;
|
||
|
- if (next.table [c]) {
|
||
|
- next.table [c]->rm_helper (pipe_, buff_, buffsize_ + 1,
|
||
|
- maxbuffsize_, func_, arg_, call_on_uniq_);
|
||
|
-
|
||
|
- // Prune redundant nodes from the mtrie
|
||
|
- if (next.table [c]->is_redundant ()) {
|
||
|
- LIBZMQ_DELETE(next.table[c]);
|
||
|
-
|
||
|
- zmq_assert (live_nodes > 0);
|
||
|
- --live_nodes;
|
||
|
- }
|
||
|
- else {
|
||
|
- // The node is not redundant, so it's a candidate for being
|
||
|
- // the new min/max node.
|
||
|
- //
|
||
|
- // We loop through the node array from left to right, so the
|
||
|
- // first non-null, non-redundant node encountered is the new
|
||
|
- // minimum index. Conversely, the last non-redundant, non-null
|
||
|
- // node encountered is the new maximum index.
|
||
|
- if (c + min < new_min)
|
||
|
- new_min = c + min;
|
||
|
- if (c + min > new_max)
|
||
|
- new_max = c + min;
|
||
|
- }
|
||
|
- }
|
||
|
- }
|
||
|
-
|
||
|
- zmq_assert (count > 1);
|
||
|
-
|
||
|
- // Free the node table if it's no longer used.
|
||
|
- if (live_nodes == 0) {
|
||
|
- free (next.table);
|
||
|
- next.table = NULL;
|
||
|
- count = 0;
|
||
|
- }
|
||
|
- // Compact the node table if possible
|
||
|
- else
|
||
|
- if (live_nodes == 1) {
|
||
|
- // If there's only one live node in the table we can
|
||
|
- // switch to using the more compact single-node
|
||
|
- // representation
|
||
|
- zmq_assert (new_min == new_max);
|
||
|
- zmq_assert (new_min >= min && new_min < min + count);
|
||
|
- mtrie_t *node = next.table [new_min - min];
|
||
|
- zmq_assert (node);
|
||
|
- free (next.table);
|
||
|
- next.node = node;
|
||
|
- count = 1;
|
||
|
- min = new_min;
|
||
|
- }
|
||
|
- else
|
||
|
- if (new_min > min || new_max < min + count - 1) {
|
||
|
- zmq_assert (new_max - new_min + 1 > 1);
|
||
|
-
|
||
|
- mtrie_t **old_table = next.table;
|
||
|
- zmq_assert (new_min > min || new_max < min + count - 1);
|
||
|
- zmq_assert (new_min >= min);
|
||
|
- zmq_assert (new_max <= min + count - 1);
|
||
|
- zmq_assert (new_max - new_min + 1 < count);
|
||
|
-
|
||
|
- count = new_max - new_min + 1;
|
||
|
- next.table = (mtrie_t**) malloc (sizeof (mtrie_t*) * count);
|
||
|
- alloc_assert (next.table);
|
||
|
-
|
||
|
- memmove (next.table, old_table + (new_min - min),
|
||
|
- sizeof (mtrie_t*) * count);
|
||
|
- free (old_table);
|
||
|
-
|
||
|
- min = new_min;
|
||
|
- }
|
||
|
-}
|
||
|
-
|
||
|
-bool zmq::mtrie_t::rm (unsigned char *prefix_, size_t size_, pipe_t *pipe_)
|
||
|
-{
|
||
|
- return rm_helper (prefix_, size_, pipe_);
|
||
|
-}
|
||
|
-
|
||
|
-bool zmq::mtrie_t::rm_helper (unsigned char *prefix_, size_t size_,
|
||
|
- pipe_t *pipe_)
|
||
|
-{
|
||
|
- if (!size_) {
|
||
|
- if (pipes) {
|
||
|
- pipes_t::size_type erased = pipes->erase (pipe_);
|
||
|
- zmq_assert (erased == 1);
|
||
|
- if (pipes->empty ()) {
|
||
|
- LIBZMQ_DELETE(pipes);
|
||
|
- }
|
||
|
- }
|
||
|
- return !pipes;
|
||
|
- }
|
||
|
-
|
||
|
- unsigned char c = *prefix_;
|
||
|
- if (!count || c < min || c >= min + count)
|
||
|
- return false;
|
||
|
-
|
||
|
- mtrie_t *next_node =
|
||
|
- count == 1 ? next.node : next.table [c - min];
|
||
|
-
|
||
|
- if (!next_node)
|
||
|
- return false;
|
||
|
-
|
||
|
- bool ret = next_node->rm_helper (prefix_ + 1, size_ - 1, pipe_);
|
||
|
-
|
||
|
- if (next_node->is_redundant ()) {
|
||
|
- LIBZMQ_DELETE(next_node);
|
||
|
- zmq_assert (count > 0);
|
||
|
-
|
||
|
- if (count == 1) {
|
||
|
- next.node = 0;
|
||
|
- count = 0;
|
||
|
- --live_nodes;
|
||
|
- zmq_assert (live_nodes == 0);
|
||
|
- }
|
||
|
- else {
|
||
|
- next.table [c - min] = 0;
|
||
|
- zmq_assert (live_nodes > 1);
|
||
|
- --live_nodes;
|
||
|
-
|
||
|
- // Compact the table if possible
|
||
|
- if (live_nodes == 1) {
|
||
|
- // If there's only one live node in the table we can
|
||
|
- // switch to using the more compact single-node
|
||
|
- // representation
|
||
|
- unsigned short i;
|
||
|
- for (i = 0; i < count; ++i)
|
||
|
- if (next.table [i])
|
||
|
- break;
|
||
|
-
|
||
|
- zmq_assert (i < count);
|
||
|
- min += i;
|
||
|
- count = 1;
|
||
|
- mtrie_t *oldp = next.table [i];
|
||
|
- free (next.table);
|
||
|
- next.node = oldp;
|
||
|
- }
|
||
|
- else
|
||
|
- if (c == min) {
|
||
|
- // We can compact the table "from the left"
|
||
|
- unsigned short i;
|
||
|
- for (i = 1; i < count; ++i)
|
||
|
- if (next.table [i])
|
||
|
- break;
|
||
|
-
|
||
|
- zmq_assert (i < count);
|
||
|
- min += i;
|
||
|
- count -= i;
|
||
|
- mtrie_t **old_table = next.table;
|
||
|
- next.table = (mtrie_t**) malloc (sizeof (mtrie_t*) * count);
|
||
|
- alloc_assert (next.table);
|
||
|
- memmove (next.table, old_table + i, sizeof (mtrie_t*) * count);
|
||
|
- free (old_table);
|
||
|
- }
|
||
|
- else
|
||
|
- if (c == min + count - 1) {
|
||
|
- // We can compact the table "from the right"
|
||
|
- unsigned short i;
|
||
|
- for (i = 1; i < count; ++i)
|
||
|
- if (next.table [count - 1 - i])
|
||
|
- break;
|
||
|
-
|
||
|
- zmq_assert (i < count);
|
||
|
- count -= i;
|
||
|
- mtrie_t **old_table = next.table;
|
||
|
- next.table = (mtrie_t**) malloc (sizeof (mtrie_t*) * count);
|
||
|
- alloc_assert (next.table);
|
||
|
- memmove (next.table, old_table, sizeof (mtrie_t*) * count);
|
||
|
- free (old_table);
|
||
|
- }
|
||
|
- }
|
||
|
- }
|
||
|
-
|
||
|
- return ret;
|
||
|
-}
|
||
|
-
|
||
|
-void zmq::mtrie_t::match (unsigned char *data_, size_t size_,
|
||
|
- void (*func_) (pipe_t *pipe_, void *arg_), void *arg_)
|
||
|
-{
|
||
|
- mtrie_t *current = this;
|
||
|
- while (true) {
|
||
|
-
|
||
|
- // Signal the pipes attached to this node.
|
||
|
- if (current->pipes) {
|
||
|
- for (pipes_t::iterator it = current->pipes->begin ();
|
||
|
- it != current->pipes->end (); ++it)
|
||
|
- func_ (*it, arg_);
|
||
|
- }
|
||
|
-
|
||
|
- // If we are at the end of the message, there's nothing more to match.
|
||
|
- if (!size_)
|
||
|
- break;
|
||
|
-
|
||
|
- // If there are no subnodes in the trie, return.
|
||
|
- if (current->count == 0)
|
||
|
- break;
|
||
|
-
|
||
|
- // If there's one subnode (optimisation).
|
||
|
- if (current->count == 1) {
|
||
|
- if (data_ [0] != current->min)
|
||
|
- break;
|
||
|
- current = current->next.node;
|
||
|
- data_++;
|
||
|
- size_--;
|
||
|
- continue;
|
||
|
- }
|
||
|
-
|
||
|
- // If there are multiple subnodes.
|
||
|
- if (data_ [0] < current->min || data_ [0] >=
|
||
|
- current->min + current->count)
|
||
|
- break;
|
||
|
- if (!current->next.table [data_ [0] - current->min])
|
||
|
- break;
|
||
|
- current = current->next.table [data_ [0] - current->min];
|
||
|
- data_++;
|
||
|
- size_--;
|
||
|
- }
|
||
|
-}
|
||
|
-
|
||
|
-bool zmq::mtrie_t::is_redundant () const
|
||
|
+namespace zmq
|
||
|
{
|
||
|
- return !pipes && live_nodes == 0;
|
||
|
+template class generic_mtrie_t<pipe_t>;
|
||
|
}
|
||
|
Index: zeromq-4.2.3/src/xpub.cpp
|
||
|
===================================================================
|
||
|
--- zeromq-4.2.3.orig/src/xpub.cpp
|
||
|
+++ zeromq-4.2.3/src/xpub.cpp
|
||
|
@@ -35,6 +35,7 @@
|
||
|
#include "err.hpp"
|
||
|
#include "msg.hpp"
|
||
|
#include "macros.hpp"
|
||
|
+#include "generic_mtrie_impl.hpp"
|
||
|
|
||
|
zmq::xpub_t::xpub_t (class ctx_t *parent_, uint32_t tid_, int sid_) :
|
||
|
socket_base_t (parent_, tid_, sid_),
|
||
|
@@ -205,7 +206,7 @@ int zmq::xpub_t::xsetsockopt (int option
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
-static void stub (unsigned char *data_, size_t size_, void *arg_)
|
||
|
+static void stub (zmq::mtrie_t::prefix_t data_, size_t size_, void *arg_)
|
||
|
{
|
||
|
LIBZMQ_UNUSED(data_);
|
||
|
LIBZMQ_UNUSED(size_);
|
||
|
@@ -222,7 +223,7 @@ void zmq::xpub_t::xpipe_terminated (pipe
|
||
|
// Remove pipe without actually sending the message as it was taken
|
||
|
// care of by the manual call above. subscriptions is the real mtrie,
|
||
|
// so the pipe must be removed from there or it will be left over.
|
||
|
- subscriptions.rm (pipe_, stub, NULL, false);
|
||
|
+ subscriptions.rm (pipe_, stub, static_cast<void*>(NULL), false);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
@@ -235,9 +236,8 @@ void zmq::xpub_t::xpipe_terminated (pipe
|
||
|
dist.pipe_terminated (pipe_);
|
||
|
}
|
||
|
|
||
|
-void zmq::xpub_t::mark_as_matching (pipe_t *pipe_, void *arg_)
|
||
|
+void zmq::xpub_t::mark_as_matching (pipe_t *pipe_, xpub_t *self)
|
||
|
{
|
||
|
- xpub_t *self = (xpub_t*) arg_;
|
||
|
self->dist.match (pipe_);
|
||
|
}
|
||
|
|
||
|
@@ -317,11 +317,9 @@ bool zmq::xpub_t::xhas_in ()
|
||
|
return !pending_data.empty ();
|
||
|
}
|
||
|
|
||
|
-void zmq::xpub_t::send_unsubscription (unsigned char *data_, size_t size_,
|
||
|
- void *arg_)
|
||
|
+void zmq::xpub_t::send_unsubscription (zmq::mtrie_t::prefix_t data_, size_t size_,
|
||
|
+ xpub_t *self)
|
||
|
{
|
||
|
- xpub_t *self = (xpub_t*) arg_;
|
||
|
-
|
||
|
if (self->options.type != ZMQ_PUB) {
|
||
|
// Place the unsubscription to the queue of pending (un)subscriptions
|
||
|
// to be retrieved by the user later on.
|
||
|
Index: zeromq-4.2.3/src/xpub.hpp
|
||
|
===================================================================
|
||
|
--- zeromq-4.2.3.orig/src/xpub.hpp
|
||
|
+++ zeromq-4.2.3/src/xpub.hpp
|
||
|
@@ -70,11 +70,11 @@ namespace zmq
|
||
|
|
||
|
// Function to be applied to the trie to send all the subscriptions
|
||
|
// upstream.
|
||
|
- static void send_unsubscription (unsigned char *data_, size_t size_,
|
||
|
- void *arg_);
|
||
|
+ static void send_unsubscription (zmq::mtrie_t::prefix_t data_, size_t size_,
|
||
|
+ xpub_t *self);
|
||
|
|
||
|
// Function to be applied to each matching pipes.
|
||
|
- static void mark_as_matching (zmq::pipe_t *pipe_, void *arg_);
|
||
|
+ static void mark_as_matching (zmq::pipe_t *pipe_, xpub_t *arg_);
|
||
|
|
||
|
// List of all subscriptions mapped to corresponding pipes.
|
||
|
mtrie_t subscriptions;
|