Index: zeromq-4.0.4/src/generic_mtrie.hpp
===================================================================
--- /dev/null
+++ zeromq-4.0.4/src/generic_mtrie.hpp
@@ -0,0 +1,122 @@
+/*
+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;
+    };
+
+public:
+    generic_mtrie_t (const generic_mtrie_t &) = delete;
+    generic_mtrie_t &operator= (const generic_mtrie_t &) = delete;
+    generic_mtrie_t (generic_mtrie_t &&) = delete;
+    generic_mtrie_t &operator= (generic_mtrie_t &&) = delete;
+
+};
+}
+
+#endif
Index: zeromq-4.0.4/src/generic_mtrie_impl.hpp
===================================================================
--- /dev/null
+++ zeromq-4.0.4/src/generic_mtrie_impl.hpp
@@ -0,0 +1,592 @@
+/*
+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 "generic_mtrie.hpp"
+
+#define LIBZMQ_DELETE(p_object)                                                \
+    {                                                                          \
+        delete p_object;                                                       \
+        p_object = 0;                                                          \
+    }
+
+
+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.0.4/src/mtrie.hpp
===================================================================
--- zeromq-4.0.4.orig/src/mtrie.hpp
+++ zeromq-4.0.4/src/mtrie.hpp
@@ -20,71 +20,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.
-        //  If there are no subscriptions left on some topics, invoke the
-        //  supplied callback function.
-        void rm (zmq::pipe_t *pipe_,
-            void (*func_) (unsigned char *data_, size_t size_, void *arg_),
-            void *arg_);
-
-        //  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 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.0.4/src/mtrie.cpp
===================================================================
--- zeromq-4.0.4.orig/src/mtrie.cpp
+++ zeromq-4.0.4/src/mtrie.cpp
@@ -1,430 +1,8 @@
-/*
-    Copyright (c) 2007-2013 Contributors as noted in the AUTHORS file
+#include "generic_mtrie_impl.hpp"
 
-    This file is part of 0MQ.
-
-    0MQ is free software; you can redistribute it and/or modify it under
-    the terms of the GNU Lesser General Public License as published by
-    the Free Software Foundation; either version 3 of the License, or
-    (at your option) any later version.
-
-    0MQ 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/>.
-*/
-
-#include <stdlib.h>
-
-#include <new>
-#include <algorithm>
-
-#include "platform.hpp"
-#if defined ZMQ_HAVE_WINDOWS
-#include "windows.hpp"
-#endif
-
-#include "err.hpp"
-#include "pipe.hpp"
-#include "mtrie.hpp"
-
-zmq::mtrie_t::mtrie_t () :
-    pipes (0),
-    min (0),
-    count (0),
-    live_nodes (0)
-{
-}
-
-zmq::mtrie_t::~mtrie_t ()
-{
-    if (pipes) {
-        delete pipes;
-        pipes = 0;
-    }
-
-    if (count == 1) {
-        zmq_assert (next.node);
-        delete next.node;
-        next.node = 0;
-    }
-    else 
-    if (count > 1) {
-        for (unsigned short i = 0; i != count; ++i)
-            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
-        //  charcters. 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_)
-{
-    unsigned char *buff = NULL;
-    rm_helper (pipe_, &buff, 0, 0, func_, arg_);
-    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_)
-{
-    //  Remove the subscription from this node.
-    if (pipes && pipes->erase (pipe_) && pipes->empty ()) {
-        func_ (*buff_, buffsize_, arg_);
-        delete pipes;
-        pipes = 0;
-    }
-
-    //  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_);
-
-        //  Prune the node if it was made redundant by the removal
-        if (next.node->is_redundant ()) {
-            delete next.node;
-            next.node = 0;
-            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_);
-
-            //  Prune redundant nodes from the mtrie
-            if (next.table [c]->is_redundant ()) {
-                delete next.table [c];
-                next.table [c] = 0;
-
-                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 ()) {
-                delete pipes;
-                pipes = 0;
-            }
-        }
-        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 ()) {
-        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_)
+namespace zmq
 {
-    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_--;
-    }
+class pipe_t;
+template class generic_mtrie_t<pipe_t>;
 }
 
-bool zmq::mtrie_t::is_redundant () const
-{
-    return !pipes && live_nodes == 0;
-}
Index: zeromq-4.0.4/src/xpub.cpp
===================================================================
--- zeromq-4.0.4.orig/src/xpub.cpp
+++ zeromq-4.0.4/src/xpub.cpp
@@ -23,6 +23,7 @@
 #include "pipe.hpp"
 #include "err.hpp"
 #include "msg.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_),
@@ -107,14 +108,13 @@ void zmq::xpub_t::xpipe_terminated (pipe
     //  Remove the pipe from the trie. If there are topics that nobody
     //  is interested in anymore, send corresponding unsubscriptions
     //  upstream.
-    subscriptions.rm (pipe_, send_unsubscription, this);
+    subscriptions.rm (pipe_, send_unsubscription, this, false);
 
     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_);
 }
 
@@ -174,11 +174,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)sunscriptions
         //  to be retrived by the user later on.
Index: zeromq-4.0.4/src/xpub.hpp
===================================================================
--- zeromq-4.0.4.orig/src/xpub.hpp
+++ zeromq-4.0.4/src/xpub.hpp
@@ -60,11 +60,11 @@ namespace zmq
 
         //  Function to be applied to the trie to send all the subsciptions
         //  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;
