zmq.Mtrie Maven / Gradle / Ivy
/*
Copyright (c) 2011 250bpm s.r.o.
Copyright (c) 2011-2012 Spotify AB
Copyright (c) 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;
import java.util.HashSet;
import java.util.Set;
//Multi-trie. Each node in the trie is a set of pointers to pipes.
public class Mtrie {
private Set pipes;
private int min;
private int count;
private int live_nodes;
private Mtrie[] next;
public static interface IMtrieHandler {
void invoke(Pipe pipe, byte[] data, int size, Object arg);
}
public Mtrie() {
min = 0;
count = 0;
live_nodes = 0;
pipes = null;
next = null;
}
public boolean add (byte[] prefix_, Pipe pipe_)
{
return add_helper (prefix_, 0, pipe_);
}
// Add key to the trie. Returns true if it's a new subscription
// rather than a duplicate.
public boolean add (byte[] prefix_, int start_, Pipe pipe_)
{
return add_helper (prefix_, start_, pipe_);
}
private boolean add_helper (byte[] prefix_, int start_, Pipe pipe_)
{
// We are at the node corresponding to the prefix. We are done.
if (prefix_ == null || prefix_.length == start_) {
boolean result = pipes == null;
if (pipes == null)
pipes = new HashSet();
pipes.add (pipe_);
return result;
}
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) {
int oldc = min;
Mtrie oldp = next[0];
count = (min < c ? c - min : min - c) + 1;
next = new Mtrie[count];
min = Math.min (min, 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 Mtrie[1];
next[0] = new Mtrie();
++live_nodes;
//alloc_assert (next.node);
}
return next[0].add_helper (prefix_, start_ + 1, pipe_);
}
else {
if (next[c - min] == null) {
next[c - min] = new Mtrie();
++live_nodes;
//alloc_assert (next.table [c - min]);
}
return next[c - min].add_helper (prefix_ , start_ + 1, pipe_);
}
}
private Mtrie[] realloc (Mtrie[] table, int size, boolean ended)
{
return Utils.realloc(Mtrie.class, table, size, ended);
}
// Remove all subscriptions for a specific peer from the trie.
// If there are no subscriptions left on some topics, invoke the
// supplied callback function.
public boolean rm (Pipe pipe_, IMtrieHandler func, Object arg) {
return rm_helper(pipe_, new byte[0], 0, 0, func, arg );
}
private boolean rm_helper(Pipe pipe_, byte[] buff_, int buffsize_, int maxbuffsize_,
IMtrieHandler func_, Object arg_) {
// Remove the subscription from this node.
if (pipes != null && pipes.remove(pipe_) && pipes.isEmpty()) {
func_.invoke(null, buff_, buffsize_, arg_);
pipes = null;
}
// Adjust the buffer.
if (buffsize_ >= maxbuffsize_) {
maxbuffsize_ = buffsize_ + 256;
buff_ = Utils.realloc(buff_, maxbuffsize_);
}
// If there are no subnodes in the trie, return.
if (count == 0)
return true;
// If there's one subnode (optimisation).
if (count == 1) {
buff_[buffsize_] = (byte) min;
buffsize_ ++;
next[0].rm_helper (pipe_, buff_, buffsize_, maxbuffsize_,
func_, arg_);
// Prune the node if it was made redundant by the removal
if (next[0].is_redundant ()) {
next = null;
count = 0;
--live_nodes;
assert (live_nodes == 0);
}
return true;
}
// If there are multiple subnodes.
//
// New min non-null character in the node table after the removal
int new_min = min + count - 1;
// New max non-null character in the node table after the removal
int new_max = min;
for (int c = 0; c != count; c++) {
buff_[buffsize_] = (byte) (min + c);
if (next[c] != null) {
next[c].rm_helper (pipe_, buff_, buffsize_ + 1,
maxbuffsize_, func_, arg_);
// Prune redundant nodes from the mtrie
if (next[c].is_redundant ()) {
next[c] = null;
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;
}
}
}
assert (count > 1);
// Free the node table if it's no longer used.
if (live_nodes == 0) {
next = 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
assert (new_min == new_max);
assert (new_min >= min && new_min < min + count);
Mtrie node = next [new_min - min];
assert (node != null);
next = null;
next = new Mtrie[]{node};
count = 1;
min = new_min;
}
else if (new_min > min || new_max < min + count - 1) {
assert (new_max - new_min + 1 > 1);
Mtrie[] old_table = next;
assert (new_min > min || new_max < min + count - 1);
assert (new_min >= min);
assert (new_max <= min + count - 1);
assert (new_max - new_min + 1 < count);
count = new_max - new_min + 1;
next = new Mtrie[count];
System.arraycopy(old_table, (new_min - min), next, 0, count);
min = new_min;
}
return true;
}
// Remove specific subscription from the trie. Return true is it was
// actually removed rather than de-duplicated.
public boolean rm (byte[] prefix_, int start_, Pipe pipe_)
{
return rm_helper (prefix_, start_, pipe_);
}
private boolean rm_helper (byte[] prefix_, int start_, Pipe pipe_)
{
if (prefix_ == null || prefix_.length == start_) {
if (pipes != null) {
boolean erased = pipes.remove(pipe_);
assert (erased);
if (pipes.isEmpty ()) {
pipes = null;
}
}
return pipes == null;
}
byte c = prefix_[ start_ ];
if (count == 0 || c < min || c >= min + count)
return false;
Mtrie next_node =
count == 1 ? next[0] : next[c - min];
if (next_node == null)
return false;
boolean ret = next_node.rm_helper (prefix_ , start_ + 1, pipe_);
if (next_node.is_redundant ()) {
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
int i;
for (i = 0; i < count; ++i) {
if (next[i] != null) {
break;
}
}
assert (i < count);
min += i;
count = 1;
Mtrie old = next [i];
next = new Mtrie [] { old };
}
else if (c == min) {
// We can compact the table "from the left"
int i;
for (i = 1; i < count; ++i) {
if (next[i] != null) {
break;
}
}
assert (i < count);
min += i;
count -= i;
next = realloc (next, count, true);
}
else if (c == min + count - 1) {
// We can compact the table "from the right"
int i;
for (i = 1; i < count; ++i) {
if (next[count - 1 - i] != null) {
break;
}
}
assert (i < count);
count -= i;
next = realloc(next, count, false);
}
}
}
return ret;
}
// Signal all the matching pipes.
public void match(byte[] data_, int size_, IMtrieHandler func_, Object arg_) {
Mtrie current = this;
int idx = 0;
while (true) {
// Signal the pipes attached to this node.
if (current.pipes != null) {
for (Pipe it : current.pipes)
func_.invoke(it, null, 0, arg_);
}
// If we are at the end of the message, there's nothing more to match.
if (size_ == 0)
break;
// If there are no subnodes in the trie, return.
if (current.count == 0)
break;
byte c = data_[idx];
// If there's one subnode (optimisation).
if (current.count == 1) {
if (c != current.min)
break;
current = current.next[0];
idx++;
size_--;
continue;
}
// If there are multiple subnodes.
if (c < current.min || c >=
current.min + current.count)
break;
if (current.next [c - current.min] == null)
break;
current = current.next [c - current.min];
idx++;
size_--;
}
}
private boolean is_redundant ()
{
return pipes == null && live_nodes == 0;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy