mirror of
https://github.com/zeromq/libzmq.git
synced 2025-05-02 07:31:39 +02:00
Problem: no unittests for mtrie
Solution: made mtrie generic (to remove complex dependency on pipe_t), and added some unit tests
This commit is contained in:
parent
9fc3692e3f
commit
a9712c0bf2
@ -611,6 +611,8 @@ set (cxx-sources
|
|||||||
fd.hpp
|
fd.hpp
|
||||||
fq.hpp
|
fq.hpp
|
||||||
gather.hpp
|
gather.hpp
|
||||||
|
generic_mtrie.hpp
|
||||||
|
generic_mtrie_impl.hpp
|
||||||
gssapi_client.hpp
|
gssapi_client.hpp
|
||||||
gssapi_mechanism_base.hpp
|
gssapi_mechanism_base.hpp
|
||||||
gssapi_server.hpp
|
gssapi_server.hpp
|
||||||
|
11
Makefile.am
11
Makefile.am
@ -64,6 +64,8 @@ src_libzmq_la_SOURCES = \
|
|||||||
src/fq.hpp \
|
src/fq.hpp \
|
||||||
src/gather.cpp \
|
src/gather.cpp \
|
||||||
src/gather.hpp \
|
src/gather.hpp \
|
||||||
|
src/generic_mtrie.hpp \
|
||||||
|
src/generic_mtrie_impl.hpp \
|
||||||
src/gssapi_mechanism_base.cpp \
|
src/gssapi_mechanism_base.cpp \
|
||||||
src/gssapi_mechanism_base.hpp \
|
src/gssapi_mechanism_base.hpp \
|
||||||
src/gssapi_client.cpp \
|
src/gssapi_client.cpp \
|
||||||
@ -865,7 +867,8 @@ if ENABLE_STATIC
|
|||||||
# unit tests - these include individual source files and test the internal functions
|
# unit tests - these include individual source files and test the internal functions
|
||||||
test_apps += \
|
test_apps += \
|
||||||
unittests/unittest_poller \
|
unittests/unittest_poller \
|
||||||
unittests/unittest_ypipe
|
unittests/unittest_ypipe \
|
||||||
|
unittests/unittest_mtrie
|
||||||
|
|
||||||
unittests_unittest_poller_SOURCES = unittests/unittest_poller.cpp
|
unittests_unittest_poller_SOURCES = unittests/unittest_poller.cpp
|
||||||
unittests_unittest_poller_CPPFLAGS = -I$(top_srcdir)/src ${UNITY_CPPFLAGS}
|
unittests_unittest_poller_CPPFLAGS = -I$(top_srcdir)/src ${UNITY_CPPFLAGS}
|
||||||
@ -878,6 +881,12 @@ unittests_unittest_ypipe_CPPFLAGS = -I$(top_srcdir)/src ${UNITY_CPPFLAGS}
|
|||||||
unittests_unittest_ypipe_LDADD = $(top_builddir)/src/.libs/libzmq.a \
|
unittests_unittest_ypipe_LDADD = $(top_builddir)/src/.libs/libzmq.a \
|
||||||
${src_libzmq_la_LIBADD} \
|
${src_libzmq_la_LIBADD} \
|
||||||
${UNITY_LIBS}
|
${UNITY_LIBS}
|
||||||
|
|
||||||
|
unittests_unittest_mtrie_SOURCES = unittests/unittest_mtrie.cpp
|
||||||
|
unittests_unittest_mtrie_CPPFLAGS = -I$(top_srcdir)/src ${UNITY_CPPFLAGS}
|
||||||
|
unittests_unittest_mtrie_LDADD = $(top_builddir)/src/.libs/libzmq.a \
|
||||||
|
${src_libzmq_la_LIBADD} \
|
||||||
|
${UNITY_LIBS}
|
||||||
endif
|
endif
|
||||||
|
|
||||||
check_PROGRAMS = ${test_apps}
|
check_PROGRAMS = ${test_apps}
|
||||||
|
104
src/generic_mtrie.hpp
Normal file
104
src/generic_mtrie.hpp
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
/*
|
||||||
|
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 "stdint.hpp"
|
||||||
|
|
||||||
|
namespace zmq
|
||||||
|
{
|
||||||
|
// Multi-trie. Each node in the trie is a set of pointers to pipes.
|
||||||
|
template <typename T> class generic_mtrie_t
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef T value_t;
|
||||||
|
typedef const unsigned char *prefix_t;
|
||||||
|
|
||||||
|
generic_mtrie_t ();
|
||||||
|
~generic_mtrie_t ();
|
||||||
|
|
||||||
|
// Add key to the trie. Returns true if it's a new subscription
|
||||||
|
// rather than a duplicate.
|
||||||
|
bool add (prefix_t prefix_, size_t size_, value_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 (value_t *pipe_,
|
||||||
|
void (*func_) (const 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 (prefix_t prefix_, size_t size_, value_t *pipe_);
|
||||||
|
|
||||||
|
// Signal all the matching pipes.
|
||||||
|
void match (prefix_t data_,
|
||||||
|
size_t size_,
|
||||||
|
void (*func_) (value_t *pipe_, void *arg_),
|
||||||
|
void *arg_);
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool add_helper (prefix_t prefix_, size_t size_, value_t *pipe_);
|
||||||
|
void rm_helper (value_t *pipe_,
|
||||||
|
unsigned char **buff_,
|
||||||
|
size_t buffsize_,
|
||||||
|
size_t maxbuffsize_,
|
||||||
|
void (*func_) (prefix_t data_, size_t size_, void *arg_),
|
||||||
|
void *arg_,
|
||||||
|
bool call_on_uniq_);
|
||||||
|
bool rm_helper (prefix_t prefix_, size_t size_, value_t *pipe_);
|
||||||
|
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
|
||||||
|
{
|
||||||
|
class generic_mtrie_t<value_t> *node;
|
||||||
|
class generic_mtrie_t<value_t> **table;
|
||||||
|
} next;
|
||||||
|
|
||||||
|
generic_mtrie_t (const generic_mtrie_t<value_t> &);
|
||||||
|
const generic_mtrie_t<value_t> &
|
||||||
|
operator= (const generic_mtrie_t<value_t> &);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
450
src/generic_mtrie_impl.hpp
Normal file
450
src/generic_mtrie_impl.hpp
Normal file
@ -0,0 +1,450 @@
|
|||||||
|
/*
|
||||||
|
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 "err.hpp"
|
||||||
|
#include "pipe.hpp"
|
||||||
|
#include "macros.hpp"
|
||||||
|
#include "generic_mtrie.hpp"
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
zmq::generic_mtrie_t<T>::generic_mtrie_t () :
|
||||||
|
pipes (0),
|
||||||
|
min (0),
|
||||||
|
count (0),
|
||||||
|
live_nodes (0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T> zmq::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 zmq::generic_mtrie_t<T>::add (prefix_t prefix_,
|
||||||
|
size_t size_,
|
||||||
|
value_t *pipe_)
|
||||||
|
{
|
||||||
|
return add_helper (prefix_, size_, pipe_);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
bool zmq::generic_mtrie_t<T>::add_helper (prefix_t prefix_,
|
||||||
|
size_t size_,
|
||||||
|
value_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;
|
||||||
|
generic_mtrie_t *oldp = next.node;
|
||||||
|
count = (min < c ? c - min : min - c) + 1;
|
||||||
|
next.table =
|
||||||
|
(generic_mtrie_t **) malloc (sizeof (generic_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 = (generic_mtrie_t **) realloc (
|
||||||
|
next.table, sizeof (generic_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 = (generic_mtrie_t **) realloc (
|
||||||
|
next.table, sizeof (generic_mtrie_t *) * count);
|
||||||
|
alloc_assert (next.table);
|
||||||
|
memmove (next.table + min - c, next.table,
|
||||||
|
old_count * sizeof (generic_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) generic_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) generic_mtrie_t;
|
||||||
|
alloc_assert (next.table[c - min]);
|
||||||
|
++live_nodes;
|
||||||
|
}
|
||||||
|
return next.table[c - min]->add_helper (prefix_ + 1, size_ - 1, pipe_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void zmq::generic_mtrie_t<T>::rm (value_t *pipe_,
|
||||||
|
void (*func_) (prefix_t 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void zmq::generic_mtrie_t<T>::rm_helper (value_t *pipe_,
|
||||||
|
unsigned char **buff_,
|
||||||
|
size_t buffsize_,
|
||||||
|
size_t maxbuffsize_,
|
||||||
|
void (*func_) (prefix_t 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);
|
||||||
|
generic_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);
|
||||||
|
|
||||||
|
generic_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 =
|
||||||
|
(generic_mtrie_t **) malloc (sizeof (generic_mtrie_t *) * count);
|
||||||
|
alloc_assert (next.table);
|
||||||
|
|
||||||
|
memmove (next.table, old_table + (new_min - min),
|
||||||
|
sizeof (generic_mtrie_t *) * count);
|
||||||
|
free (old_table);
|
||||||
|
|
||||||
|
min = new_min;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
bool zmq::generic_mtrie_t<T>::rm (prefix_t prefix_,
|
||||||
|
size_t size_,
|
||||||
|
value_t *pipe_)
|
||||||
|
{
|
||||||
|
return rm_helper (prefix_, size_, pipe_);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
bool zmq::generic_mtrie_t<T>::rm_helper (prefix_t prefix_,
|
||||||
|
size_t size_,
|
||||||
|
value_t *pipe_)
|
||||||
|
{
|
||||||
|
if (!size_) {
|
||||||
|
if (pipes) {
|
||||||
|
typename 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;
|
||||||
|
|
||||||
|
generic_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;
|
||||||
|
generic_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;
|
||||||
|
generic_mtrie_t **old_table = next.table;
|
||||||
|
next.table = (generic_mtrie_t **) malloc (
|
||||||
|
sizeof (generic_mtrie_t *) * count);
|
||||||
|
alloc_assert (next.table);
|
||||||
|
memmove (next.table, old_table + i,
|
||||||
|
sizeof (generic_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;
|
||||||
|
generic_mtrie_t **old_table = next.table;
|
||||||
|
next.table = (generic_mtrie_t **) malloc (
|
||||||
|
sizeof (generic_mtrie_t *) * count);
|
||||||
|
alloc_assert (next.table);
|
||||||
|
memmove (next.table, old_table,
|
||||||
|
sizeof (generic_mtrie_t *) * count);
|
||||||
|
free (old_table);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void zmq::generic_mtrie_t<T>::match (prefix_t data_,
|
||||||
|
size_t size_,
|
||||||
|
void (*func_) (value_t *pipe_, void *arg_),
|
||||||
|
void *arg_)
|
||||||
|
{
|
||||||
|
generic_mtrie_t *current = this;
|
||||||
|
while (true) {
|
||||||
|
// Signal the pipes attached to this node.
|
||||||
|
if (current->pipes) {
|
||||||
|
for (typename 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_--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T> bool zmq::generic_mtrie_t<T>::is_redundant () const
|
||||||
|
{
|
||||||
|
return !pipes && live_nodes == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
385
src/mtrie.cpp
385
src/mtrie.cpp
@ -28,389 +28,10 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "precompiled.hpp"
|
#include "precompiled.hpp"
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
#include <new>
|
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
#include "err.hpp"
|
|
||||||
#include "pipe.hpp"
|
|
||||||
#include "macros.hpp"
|
|
||||||
#include "mtrie.hpp"
|
#include "mtrie.hpp"
|
||||||
|
#include "generic_mtrie_impl.hpp"
|
||||||
|
|
||||||
zmq::mtrie_t::mtrie_t () : pipes (0), min (0), count (0), live_nodes (0)
|
namespace zmq
|
||||||
{
|
{
|
||||||
}
|
template class generic_mtrie_t<pipe_t>;
|
||||||
|
|
||||||
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 (prefix_t prefix_, size_t size_, pipe_t *pipe_)
|
|
||||||
{
|
|
||||||
return add_helper (prefix_, size_, pipe_);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool zmq::mtrie_t::add_helper (prefix_t 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_) (prefix_t 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_) (prefix_t 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 (prefix_t prefix_, size_t size_, pipe_t *pipe_)
|
|
||||||
{
|
|
||||||
return rm_helper (prefix_, size_, pipe_);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool zmq::mtrie_t::rm_helper (prefix_t 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 (prefix_t 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
|
|
||||||
{
|
|
||||||
return !pipes && live_nodes == 0;
|
|
||||||
}
|
}
|
||||||
|
@ -30,76 +30,23 @@
|
|||||||
#ifndef __ZMQ_MTRIE_HPP_INCLUDED__
|
#ifndef __ZMQ_MTRIE_HPP_INCLUDED__
|
||||||
#define __ZMQ_MTRIE_HPP_INCLUDED__
|
#define __ZMQ_MTRIE_HPP_INCLUDED__
|
||||||
|
|
||||||
#include <stddef.h>
|
#include "generic_mtrie.hpp"
|
||||||
#include <set>
|
|
||||||
|
|
||||||
#include "stdint.hpp"
|
#if __cplusplus >= 201103L || defined(_MSC_VER)
|
||||||
|
#define ZMQ_HAS_EXTERN_TEMPLATE 1
|
||||||
|
#else
|
||||||
|
#define ZMQ_HAS_EXTERN_TEMPLATE 0
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace zmq
|
namespace zmq
|
||||||
{
|
{
|
||||||
class pipe_t;
|
class pipe_t;
|
||||||
|
|
||||||
// Multi-trie. Each node in the trie is a set of pointers to pipes.
|
#if ZMQ_HAS_EXTERN_TEMPLATE
|
||||||
|
extern template class generic_mtrie_t<pipe_t>;
|
||||||
|
#endif
|
||||||
|
|
||||||
class mtrie_t
|
typedef generic_mtrie_t<pipe_t> mtrie_t;
|
||||||
{
|
|
||||||
public:
|
|
||||||
typedef const unsigned char *prefix_t;
|
|
||||||
|
|
||||||
mtrie_t ();
|
|
||||||
~mtrie_t ();
|
|
||||||
|
|
||||||
// Add key to the trie. Returns true if it's a new subscription
|
|
||||||
// rather than a duplicate.
|
|
||||||
bool add (prefix_t 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_) (const 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 (prefix_t prefix_, size_t size_, zmq::pipe_t *pipe_);
|
|
||||||
|
|
||||||
// Signal all the matching pipes.
|
|
||||||
void match (prefix_t data_,
|
|
||||||
size_t size_,
|
|
||||||
void (*func_) (zmq::pipe_t *pipe_, void *arg_),
|
|
||||||
void *arg_);
|
|
||||||
|
|
||||||
private:
|
|
||||||
bool add_helper (prefix_t 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_) (prefix_t data_, size_t size_, void *arg_),
|
|
||||||
void *arg_,
|
|
||||||
bool call_on_uniq_);
|
|
||||||
bool rm_helper (prefix_t 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 &);
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -4,6 +4,7 @@ cmake_minimum_required(VERSION "2.8.1")
|
|||||||
set(unittests
|
set(unittests
|
||||||
unittest_ypipe
|
unittest_ypipe
|
||||||
unittest_poller
|
unittest_poller
|
||||||
|
unittest_mtrie
|
||||||
)
|
)
|
||||||
|
|
||||||
#IF (ENABLE_DRAFTS)
|
#IF (ENABLE_DRAFTS)
|
||||||
|
113
unittests/unittest_mtrie.cpp
Normal file
113
unittests/unittest_mtrie.cpp
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2018 Contributors as noted in the AUTHORS file
|
||||||
|
|
||||||
|
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 "../tests/testutil.hpp"
|
||||||
|
|
||||||
|
#if defined(min)
|
||||||
|
#undef min
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <generic_mtrie_impl.hpp>
|
||||||
|
|
||||||
|
#include <unity.h>
|
||||||
|
|
||||||
|
void setUp ()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
void tearDown ()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_create ()
|
||||||
|
{
|
||||||
|
zmq::generic_mtrie_t<int> mtrie;
|
||||||
|
}
|
||||||
|
|
||||||
|
void mtrie_count (int *pipe, void *arg)
|
||||||
|
{
|
||||||
|
LIBZMQ_UNUSED (pipe);
|
||||||
|
int *count = static_cast<int *> (arg);
|
||||||
|
++*count;
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_check_empty_match_nonempty_data ()
|
||||||
|
{
|
||||||
|
zmq::generic_mtrie_t<int> mtrie;
|
||||||
|
const zmq::generic_mtrie_t<int>::prefix_t test_name =
|
||||||
|
reinterpret_cast<zmq::generic_mtrie_t<int>::prefix_t> ("foo");
|
||||||
|
|
||||||
|
int count = 0;
|
||||||
|
mtrie.match (test_name, 3, mtrie_count, &count);
|
||||||
|
TEST_ASSERT_EQUAL_INT (0, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_check_empty_match_empty_data ()
|
||||||
|
{
|
||||||
|
zmq::generic_mtrie_t<int> mtrie;
|
||||||
|
|
||||||
|
int count = 0;
|
||||||
|
mtrie.match (NULL, 0, mtrie_count, &count);
|
||||||
|
TEST_ASSERT_EQUAL_INT (0, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_add_single_entry_match_exact ()
|
||||||
|
{
|
||||||
|
int pipe;
|
||||||
|
|
||||||
|
zmq::generic_mtrie_t<int> mtrie;
|
||||||
|
const zmq::generic_mtrie_t<int>::prefix_t test_name =
|
||||||
|
reinterpret_cast<zmq::generic_mtrie_t<int>::prefix_t> ("foo");
|
||||||
|
|
||||||
|
bool res = mtrie.add (test_name, 3, &pipe);
|
||||||
|
TEST_ASSERT_TRUE (res);
|
||||||
|
|
||||||
|
int count = 0;
|
||||||
|
mtrie.match (test_name, 3, mtrie_count, &count);
|
||||||
|
TEST_ASSERT_EQUAL_INT (1, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_add_rm_single_entry_match_exact ()
|
||||||
|
{
|
||||||
|
int pipe;
|
||||||
|
zmq::generic_mtrie_t<int> mtrie;
|
||||||
|
const zmq::generic_mtrie_t<int>::prefix_t test_name =
|
||||||
|
reinterpret_cast<zmq::generic_mtrie_t<int>::prefix_t> ("foo");
|
||||||
|
|
||||||
|
mtrie.add (test_name, 3, &pipe);
|
||||||
|
bool res = mtrie.rm (test_name, 3, &pipe);
|
||||||
|
TEST_ASSERT_TRUE (res);
|
||||||
|
|
||||||
|
int count = 0;
|
||||||
|
mtrie.match (test_name, 3, mtrie_count, &count);
|
||||||
|
TEST_ASSERT_EQUAL_INT (0, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main (void)
|
||||||
|
{
|
||||||
|
setup_test_environment ();
|
||||||
|
|
||||||
|
UNITY_BEGIN ();
|
||||||
|
RUN_TEST (test_create);
|
||||||
|
RUN_TEST (test_check_empty_match_nonempty_data);
|
||||||
|
RUN_TEST (test_check_empty_match_empty_data);
|
||||||
|
RUN_TEST (test_add_single_entry_match_exact);
|
||||||
|
RUN_TEST (test_add_rm_single_entry_match_exact);
|
||||||
|
|
||||||
|
return UNITY_END ();
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user