trie.cpp 5.13 KB
Newer Older
1
/*
2 3
    Copyright (c) 2007-2011 iMatix Corporation
    Copyright (c) 2007-2011 Other contributors as noted in the AUTHORS file
4 5 6 7

    This file is part of 0MQ.

    0MQ is free software; you can redistribute it and/or modify it under
8
    the terms of the GNU Lesser General Public License as published by
9 10 11 12 13 14
    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
15
    GNU Lesser General Public License for more details.
16

17
    You should have received a copy of the GNU Lesser General Public License
18 19 20 21 22 23 24 25
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <stdlib.h>

#include <new>
#include <algorithm>

26 27 28 29 30
#include "platform.hpp"
#if defined ZMQ_HAVE_WINDOWS
#include "windows.hpp"
#endif

31
#include "err.hpp"
32
#include "trie.hpp"
33

34
zmq::trie_t::trie_t () :
35 36 37 38 39 40
    refcnt (0),
    min (0),
    count (0)
{
}

41
zmq::trie_t::~trie_t ()
42 43 44 45
{
    if (count == 1)
        delete next.node;
    else if (count > 1) {
46
        for (unsigned short i = 0; i != count; ++i)
47 48 49 50 51 52
            if (next.table [i])
                delete next.table [i];
        free (next.table);
    }
}

53
void zmq::trie_t::add (unsigned char *prefix_, size_t size_)
54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
{
    //  We are at the node corresponding to the prefix. We are done.
    if (!size_) {
        ++refcnt;
        return;
    }

    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;
73
            trie_t *oldp = next.node;
74
            count = (min < c ? c - min : min - c) + 1;
75 76
            next.table = (trie_t**)
                malloc (sizeof (trie_t*) * count);
77
            alloc_assert (next.table);
78
            for (unsigned short i = 0; i != count; ++i)
79 80 81 82 83 84 85
                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.
86
            unsigned short old_count = count;
87
            count = c - min + 1;
88 89
            next.table = (trie_t**) realloc ((void*) next.table,
                sizeof (trie_t*) * count);
90
            zmq_assert (next.table);
91
            for (unsigned short i = old_count; i != count; i++)
92 93 94 95 96
                next.table [i] = NULL;
        }
        else {

            //  The new character is below the current character range.
97
            unsigned short old_count = count;
98
            count = (min + old_count) - c;
99 100
            next.table = (trie_t**) realloc ((void*) next.table,
                sizeof (trie_t*) * count);
101 102
            zmq_assert (next.table);
            memmove (next.table + min - c, next.table,
103
                old_count * sizeof (trie_t*));
104
            for (unsigned short i = 0; i != min - c; i++)
105 106 107 108 109 110 111 112
                next.table [i] = NULL;
            min = c;
        }
    }

    //  If next node does not exist, create one.
    if (count == 1) {
        if (!next.node) {
113
            next.node = new (std::nothrow) trie_t;
114
            alloc_assert (next.node);
115 116 117 118 119
        }
        next.node->add (prefix_ + 1, size_ - 1);
    }
    else {
        if (!next.table [c - min]) {
120
            next.table [c - min] = new (std::nothrow) trie_t;
121
            alloc_assert (next.table [c - min]);
122 123 124 125 126
        }
        next.table [c - min]->add (prefix_ + 1, size_ - 1);
    }
}

127
bool zmq::trie_t::rm (unsigned char *prefix_, size_t size_)
128 129 130 131 132 133 134 135 136 137 138 139
{
     if (!size_) {
         if (!refcnt)
             return false;
         refcnt--;
         return true;
     }

     unsigned char c = *prefix_;
     if (!count || c < min || c >= min + count)
         return false;

140
     trie_t *next_node =
141 142 143 144 145 146 147 148
         count == 1 ? next.node : next.table [c - min];

     if (!next_node)
         return false;

     return next_node->rm (prefix_ + 1, size_ - 1);
}

149
bool zmq::trie_t::check (unsigned char *data_, size_t size_)
150 151 152
{
    //  This function is on critical path. It deliberately doesn't use
    //  recursion to get a bit better performance.
153
    trie_t *current = this;
154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181
    while (true) {

        //  We've found a corresponding subscription!
        if (current->refcnt)
            return true;

        //  We've checked all the data and haven't found matching subscription.
        if (!size_)
            return false;

        //  If there's no corresponding slot for the first character
        //  of the prefix, the message does not match.
        unsigned char c = *data_;
        if (c < current->min || c >= current->min + current->count)
            return false;

        //  Move to the next character.
        if (current->count == 1)
            current = current->next.node;
        else {
            current = current->next.table [c - current->min];
            if (!current)
                return false;
        }
        data_++;
        size_--;
    }
}