All Downloads are FREE. Search and download functionalities are using the official Maven repository.

zmq.Trie Maven / Gradle / Ivy

There is a newer version: 0.6.0
Show newest version
/*
    Copyright (c) 2009-2011 250bpm s.r.o.
    Copyright (c) 2007-2009 iMatix Corporation
    Copyright (c) 2011-2012 Spotify AB
    Copyright (c) 2007-2011 Other 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 .
*/

package zmq;


public class Trie {
    private int refcnt;

    private byte min;
    private int count;
    private int live_nodes;
    
    public static interface ITrieHandler {
        void added(byte[] data, int size, Object arg);
    }
    Trie[] next;
    
    public Trie() {
        min = 0;
        count = 0;
        live_nodes = 0;
        
        refcnt = 0;
        next = null;
    }
    
    //  Add key to the trie. Returns true if this is a new item in the trie
    //  rather than a duplicate.
    public boolean add (byte[] prefix_)
    {
        return add (prefix_, 0);
    }
    
    public boolean add (byte[] prefix_, int start_)
    {
        //  We are at the node corresponding to the prefix. We are done.
        if (prefix_ == null || prefix_.length == start_) {
            ++refcnt;
            return refcnt == 1;
        }

        byte c = prefix_[start_];
        if (c < min || c >= min + count) {

            //  The character is out of range of currently handled
            //  charcters. We have to extend the table.
            if (count == 0) {
                min = c;
                count = 1;
                next = null;
            }
            else if (count == 1) {
                byte oldc = min;
                Trie oldp = next[0];
                count = (min < c ? c - min : min - c) + 1;
                next = new Trie[count];
                min = (byte)Math.min ((byte)min, (byte)c);
                next[oldc - min] = oldp;
            }
            else if (min < c) {

                //  The new character is above the current character range.
                count = c - min + 1;
                next = realloc(next, count, true);
            }
            else {

                //  The new character is below the current character range.
                count = (min + count) - c;
                next = realloc(next, count, false);
                min = c;
            }
        }

        //  If next node does not exist, create one.
        if (count == 1) {
            if (next == null) {
                next = new Trie[1];
                next[0] = new Trie();
                ++live_nodes;
                //alloc_assert (next.node);
            }
            return next[0].add (prefix_, start_ + 1);
        }
        else {
            if (next[c - min] == null) {
                next[c - min] = new Trie();
                ++live_nodes;
                //alloc_assert (next.table [c - min]);
            }
            return next[c - min].add (prefix_ , start_ + 1);
        }
    }
    
    private Trie[] realloc(Trie[] table, int size, boolean ended) {
        return Utils.realloc(Trie.class, table, size, ended);
    }
    
    //  Remove key from the trie. Returns true if the item is actually
    //  removed from the trie.
    public boolean rm (byte[] prefix_, int start_)
    {
        if (prefix_ == null || prefix_.length == start_) {
            if (refcnt == 0)
                return false;
            refcnt--;
            return refcnt == 0;
        }

        byte c = prefix_[ start_ ];
        if (count == 0 || c < min || c >= min + count)
            return false;

        Trie next_node =
            count == 1 ? next[0] : next[c - min];

        if (next_node == null)
            return false;

        boolean ret = next_node.rm (prefix_ , start_ + 1);
        if (next_node.is_redundant ()) {
            //delete next_node;
            assert (count > 0);

            if (count == 1) {
                next = null;
                count = 0;
                --live_nodes;
                assert (live_nodes == 0);
            }
            else {
                next[c - min] = null ; 
                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
                    Trie node = null;
                    for (int i = 0; i < count; ++i) {
                        if (next[i] != null) {
                            node = next[i];
                            min = (byte)(i + min);
                            break;
                        }
                    }

                    assert (node != null);
                    //free (next.table);
                    next = null;
                    next = new Trie[]{node};
                    count = 1;
                }
                else if (c == min) {
                    //  We can compact the table "from the left"
                    byte new_min = min;
                    for (int i = 1; i < count; ++i) {
                        if (next[i] != null) {
                            new_min = (byte) (i + min);
                            break;
                        }
                    }
                    assert (new_min != min);

                    assert (new_min > min);
                    assert (count > new_min - min);
                    count = count - (new_min - min);
                    
                    next = realloc(next, count, true);

                    min = new_min;
                }
                else if (c == min + count - 1) {
                    //  We can compact the table "from the right"
                    int new_count = count;
                    for (int i = 1; i < count; ++i) {
                        if (next[count - 1 - i] != null) {
                            new_count = count - i;
                            break;
                        }
                    }
                    assert (new_count != count);
                    count = new_count;

                    next = realloc(next, count, false);
                }
            }
        }

        return ret;
    }
    
    //  Check whether particular key is in the trie.
    public boolean check (byte[] data_)
    {
        //  This function is on critical path. It deliberately doesn't use
        //  recursion to get a bit better performance.
        Trie current = this;
        int start = 0;
        while (true) {

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

            //  We've checked all the data and haven't found matching subscription.
            if (data_.length == start)
                return false;

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

            //  Move to the next character.
            if (current.count == 1)
                current = current.next[0];
            else {
                current = current.next[c - current.min];
                if (current == null)
                    return false;
            }
            start++;
        }
    }
    
    //  Apply the function supplied to each subscription in the trie.
    public void apply(ITrieHandler func, Object arg_) {
        apply_helper(null, 0, 0, func, arg_ );
    }

    private void apply_helper(byte[] buff_, int buffsize_, int maxbuffsize_, ITrieHandler func_,
            Object arg_) {
        //  If this node is a subscription, apply the function.
        if (refcnt > 0)
            func_.added (buff_, buffsize_, arg_);

        //  Adjust the buffer.
        if (buffsize_  >= maxbuffsize_) {
            maxbuffsize_ = buffsize_  + 256;
            buff_ = Utils.realloc (buff_, maxbuffsize_);
            assert (buff_!=null);
        }

        //  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[0].apply_helper (buff_, buffsize_, maxbuffsize_, func_, arg_);
            return;
        }
        
        //  If there are multiple subnodes.
        for (int c = 0; c != count; c++) {
            buff_ [buffsize_] = (byte) (min + c);
            if (next[c] != null)
                next[c].apply_helper (buff_, buffsize_ + 1, maxbuffsize_,
                    func_, arg_);
        }
    }


    private boolean is_redundant ()
    {
        return refcnt == 0 && live_nodes == 0;
    }




}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy