PcpHeap.java
| Index Score | ||
|---|---|---|
![]() |
![]() |
org.furthurnet.datastructures |
![]() |
![]() |
Furthurnet |
View: Reasons, Metrics, Source Code
These are the metrics that contribute to the Enerjy Score for this file, ranked by impact. So the metrics listed at the top influence the score to a greater extent that the metrics listed at the bottom.
/*
* FURTHUR - A distributed peer-to-peer file sharing system.
* Copyright (C) 2001 Jamie M. Addessi
* jaddessi@pcpnetworks.com
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package org.furthurnet.datastructures;
import java.util.Vector;
import org.furthurnet.datastructures.supporting.*;
public class PcpHeap
{
protected int currentSize = 0; // Number of elements in heap
protected HeapItem root = null; // The root of the heap
private HeapItem array[] = null; // Only used in debug mode
private long timeStamp = 0; // The current change timestamp
public long numInserts = 0;
public long numDeletes = 0;
public long numMerges = 0;
public long numRepositions = 0;
public long numFirewallDeletes = 0;
public long numFirewallRejections = 0;
public long numFirewallNodes = 0;
private boolean groupStatsNeedInitialized = false;
private int numNodesInGroup[] = new int[Constants.MAX_SPEEDGROUPS+1];
public PcpHeap()
{
if (groupStatsNeedInitialized)
{
groupStatsNeedInitialized = false;
for (int i=0; i<Constants.MAX_SPEEDGROUPS+1; i++)
numNodesInGroup[i] = 0;
}
}
public int getCurrentSize()
{
return currentSize;
}
public String insert(double speed, ClientData data)
{
if (speed < 0)
{
// a negative speed implies that the client does not know it's exact speed, just an assesed speed group
speed = Common.speedGroup(Math.abs(speed)) + Constants.UNKNOWN_INSERTION_SPEED +
(Math.random() * Constants.UNKNOWN_INSERTION_VARIANCE);
}
if (!data.isFirewall())
speed = adjustNodeSpeedsForInsert(speed); // We only adjust the distribution for non-firewall nodes
timeStamp++;
String changes = encodeChanges(insert(speed, data, false), data.getId());
return changes;
}
/**
* Insert into the heap, maintaining heap order.
* Duplicates are allowed.
* returns a reference to the parent of the highest modified node
*/
private HeapItem insert(double speed, ClientData data, boolean findShortestDistance)
{
double origSpeed = speed;
double incr = ((Math.floor(origSpeed) + 1) - origSpeed) / 5.0; // set the increment to be 1/5 of the way to the top of the speedgroup
if (incr < 0.1)
incr = 0.1;
HeapItem node = new HeapItem(speed, data);
HeapItem parent = null;
do
{
parent = insert(node, root, findShortestDistance);
if ((parent == null) && (node != root))
node.setSpeed(node.getSpeed() + incr); //if we couldn't find a spot, increment the speed and retry
}
while ((parent == null) && (node != root) &&
(node.getSpeed() < Math.floor(origSpeed) + 1.0));
if ((parent != null) || (node == root))
{
// only increment counters if the insert was successful
currentSize++;
numInserts++;
if (node.getData().isFirewall())
numFirewallNodes++;
else
numNodesInGroup[Common.speedGroup(speed)]++;
}
else
numFirewallRejections++;
return parent;
}
/**
* Insert the node into the subheap starting at 'start', maintaining heap order.
* The children of 'node' are both overwritten.
* Duplicates are allowed.
* returns a reference to the parent of the highest modified node
*/
private HeapItem insert(HeapItem node, HeapItem start, boolean findShortestDistance)
{
HeapPosition pos = null;
boolean allowFirewall = !node.getData().isFirewall(); // only allow a firewall insertion position if the new node is not a firewall node
if (findShortestDistance)
{
// if we are repositioning an existing node, we want to move it as short a distance as possible
HeapPosition leafPos = findAvailableLeafPosition(start, node.getSpeed(), true, allowFirewall);
HeapPosition internalPos = findInternalInsertionPosition(start, node.getSpeed(), true, true); // even if allowFirewall is false, insert it in a valid position and it will be resolved by checkFirewallConflict()
// use the position found with the shortest distance from pos.
if (leafPos == null)
pos = internalPos;
else if (internalPos == null)
pos = leafPos;
else
{
if (internalPos.depth < leafPos.depth)
pos = internalPos;
else
pos = leafPos;
}
}
else
{
// This is a new node, just find the position which repositions the least other nodes. (leaf is best)
pos = findAvailableLeafPosition(start, node.getSpeed(), false, allowFirewall);
if (pos == null) // no available leaf position
pos = findInternalInsertionPosition(start, node.getSpeed(), false, allowFirewall);
}
if (pos == null)
{
if ((root != null) || (!allowFirewall))
{
// this is a firewall node with no valid position, refuse it.
return null;
}
else
{
// this is the root
// Note: The first node interted must be the root. If any node is subsequently
// inserted with a speed faster than the root's, the algorithm will fail and the
// tree will be corrupted. So, make sure the root is inserted with the fastest possible speed.
root = node;
return null;
}
}
HeapItem oldChild = pos.node.child[pos.childNum];
pos.node.child[pos.childNum] = node;
node.child[Constants.LEFTCHILD] = oldChild;
node.child[Constants.RIGHTCHILD] = null;
// update timestamps of affected nodes
pos.node.timeStamp = timeStamp;
if (node.child[Constants.LEFTCHILD] != null)
node.timeStamp = timeStamp;
// if the inserted node is in a different speed group than its parent, insert a placeholder.
// if the root allows multiple children, don't give it a placeholder child.
if ((pos.node != root) || (Constants.MAX_ROOT_CHILDREN == 1))
if (Common.speedGroup(pos.node.getSpeed()) != Common.speedGroup(node.getSpeed()))
pos.node.child[siblingOf(pos.childNum)] = new HeapItem(Constants.PLACEHOLDER);
// if the inserted node is in a different speed group than its child, insert a placeholder
if (node.child[Constants.LEFTCHILD] != null)
if (Common.speedGroup(node.getSpeed()) != Common.speedGroup(node.child[Constants.LEFTCHILD].getSpeed()))
node.child[Constants.RIGHTCHILD] = new HeapItem(Constants.PLACEHOLDER);
// if the inserted node is in the same speed group as its parent, and its sibling
// is a placeholder, replace the placeholder with a null.
if (pos.node.child[siblingOf(pos.childNum)] != null)
if (Common.speedGroup(pos.node.getSpeed()) == Common.speedGroup(node.getSpeed()))
if (pos.node.child[siblingOf(pos.childNum)].isPlaceholder())
pos.node.child[siblingOf(pos.childNum)] = null;
return pos.node;
}
private HeapPosition findAvailableLeafPosition(HeapItem pos, double speed, boolean findShortestDistance)
{
return findAvailableLeafPosition(pos, speed, 0, findShortestDistance, true);
}
private HeapPosition findAvailableLeafPosition(HeapItem pos, double speed, boolean findShortestDistance, boolean allowFirewall)
{
return findAvailableLeafPosition(pos, speed, 0, findShortestDistance, allowFirewall);
}
private HeapPosition findAvailableLeafPosition(HeapItem pos, double speed, int depth, boolean findShortestDistance)
{
return findAvailableLeafPosition(pos, speed, depth, findShortestDistance, true);
}
private HeapPosition findAvailableLeafPosition(HeapItem pos, double speed, int depth, boolean findShortestDistance, boolean allowFirewall)
{
// finds a leaf position in the subtree starting at node 'pos' at which
// a node with speed 'speed' can be inserted. A leaf position with a sibling in
// a different speed group than 'speed' is invalid. If no valid position
// exists, null is returned.
// If multiple positions exist, we choose according to the following:
// If 'findShortestDistance' is true, return the one with the minimum depth
// If 'findShortestDistance' is false, find the one with the
// minimum speed difference between parent speed and inserted node speed.
// (If multiple positions exist in which this difference is less
// than TRIVIAL_SPEED_DIFFERENCE, the one with the minimum depth is returned.)
// Note: 'depth' here refers to the distance from the starting position in the
// heap to the found node. ** This varies by method
// If allowFirewall is false, then the algorithm will not return a leaf position
// under a firewall node.
if (pos == null)
return null;
if (pos.isPlaceholder())
return null;
// if the speed to be inserted is less than the speed of this node, then we
// have no valid positions in this subtree.
if (speed < pos.getSpeed()) return null;
HeapPosition found = null;
if ((pos.child[Constants.LEFTCHILD] == null) && (pos.child[Constants.RIGHTCHILD] == null))
{
// both children are null, this is a valid position
found = new HeapPosition(pos, Constants.LEFTCHILD, depth);
}
else if ((pos.child[Constants.LEFTCHILD] == null) && (pos.child[Constants.RIGHTCHILD] != null))
{
// left child is null. This is a valid position if the right child is in
// the same speed group as 'speed' or if parent is the root
if (!pos.child[Constants.RIGHTCHILD].isPlaceholder())
if ((pos == root) || (Common.speedGroup(pos.child[Constants.RIGHTCHILD].getSpeed()) == Common.speedGroup(speed)))
found = new HeapPosition(pos, Constants.LEFTCHILD, depth);
}
else if ((pos.child[Constants.LEFTCHILD] != null) && (pos.child[Constants.RIGHTCHILD] == null))
{
// right child is null. This is a valid position if the left child is in
// the same speed group as 'speed' or if parent is the root
if (!pos.child[Constants.LEFTCHILD].isPlaceholder())
if ((pos == root) || (Common.speedGroup(pos.child[Constants.LEFTCHILD].getSpeed()) == Common.speedGroup(speed)))
found = new HeapPosition(pos, Constants.RIGHTCHILD, depth);
}
// if allowFirewall is false, then we can't return a position which is a firewall node
if ((!allowFirewall) && (found != null))
if (found.node.getData().isFirewall())
found = null;
// if we're looking for the shortest distance, then a valid position is better than
// any position in its subtree.
if ((found != null) && (findShortestDistance))
return found;
// if the speeds of the position found and the node to be inserted are trivially close, then
// this position is better than any we could find in the subtrees.
if (found != null)
if (speed - found.node.getSpeed() < Constants.TRIVIAL_SPEED_DIFFERENCE)
return found;
HeapPosition foundInLeftSubtree = findAvailableLeafPosition(pos.child[Constants.LEFTCHILD], speed, depth + 1, findShortestDistance, allowFirewall);
HeapPosition foundInRightSubtree = findAvailableLeafPosition(pos.child[Constants.RIGHTCHILD], speed, depth + 1, findShortestDistance, allowFirewall);
// return the position found with the minimum speed difference or lesser depth, depending on 'findShortestDistance' boolean
if (found == null)
found = foundInLeftSubtree;
else if (foundInLeftSubtree != null)
if (foundInLeftSubtree.node.getSpeed() > found.node.getSpeed())
found = foundInLeftSubtree;
if (found == null)
found = foundInRightSubtree;
else if (foundInRightSubtree != null)
{
// if we're looking for the shortest distance, choose the one with the lesser depth
if (findShortestDistance)
{
if (foundInRightSubtree.depth < found.depth)
found = foundInRightSubtree;
}
else
{
if ((speed - foundInRightSubtree.node.getSpeed() < Constants.TRIVIAL_SPEED_DIFFERENCE) &&
(speed - found.node.getSpeed() < Constants.TRIVIAL_SPEED_DIFFERENCE))
{
// both the nodes found have speeds which are trivially close to the insertion node
// speed. We take the one with the lesser heap depth.
if (foundInRightSubtree.depth < found.depth)
found = foundInRightSubtree;
}
else if (foundInRightSubtree.node.getSpeed() > found.node.getSpeed())
found = foundInRightSubtree;
}
}
return found;
}
private HeapPosition findInternalInsertionPosition(HeapItem pos, double speed, boolean findShortestDistance)
{
return findInternalInsertionPosition(pos, speed, findShortestDistance, true);
}
private HeapPosition findInternalInsertionPosition(HeapItem pos, double speed, boolean findShortestDistance, boolean allowFirewall)
{
if (findShortestDistance)
return findClosestInternalInsertionPosition(pos, speed, 0, allowFirewall);
else
return findIdealInternalInsertionPosition(pos, speed, new IntegerPointer(0), allowFirewall);
}
private HeapPosition findIdealInternalInsertionPosition(HeapItem pos, double speed, IntegerPointer depth, boolean allowFirewall)
{
// finds an internal position in the subtree starting at node 'pos' below which
// a node with speed 'speed' can be inserted. To guarantee minimal rearangement
// of the heap, we return the qualifying node with the minimal depth.
// Note: 'depth' here refers to the length of the longest path from a found node to any
// of its leafs. ** This varies by method
// If allowFirewall is false, then the algorithm will not return a position
// adjacent to another firewall node.
depth.value = 0; // start at 0, as I recurse it will increase (trust me.)
if (pos == null) return null;
if (pos.isPlaceholder()) return null;
HeapPosition foundLeft = null;
HeapPosition foundRight = null;
IntegerPointer depthLeft = new IntegerPointer(0);
IntegerPointer depthRight = new IntegerPointer(0);
foundLeft = findIdealInternalInsertionPosition(pos.child[Constants.LEFTCHILD], speed, depthLeft, allowFirewall);
foundRight = findIdealInternalInsertionPosition(pos.child[Constants.RIGHTCHILD], speed, depthRight, allowFirewall);
// The height to the current node is the greater of the two.
if (depthLeft.value > depthRight.value)
depth.value = depthLeft.value;
else
depth.value = depthRight.value;
// if no valid insertion positions were found in my left subtree, check my left position
if (foundLeft == null)
if (pos.child[Constants.LEFTCHILD] != null)
{
if (!pos.child[Constants.LEFTCHILD].isPlaceholder())
if ((speed >= pos.getSpeed()) && (speed < pos.child[Constants.LEFTCHILD].getSpeed()))
foundLeft = new HeapPosition(pos, Constants.LEFTCHILD, depthLeft.value);
}
// if no valid insertion positions were found in my right subtree, check my right position
if (foundRight == null)
if (pos.child[Constants.RIGHTCHILD] != null)
{
if (!pos.child[Constants.RIGHTCHILD].isPlaceholder())
if ((speed >= pos.getSpeed()) && (speed < pos.child[Constants.RIGHTCHILD].getSpeed()))
foundRight = new HeapPosition(pos, Constants.RIGHTCHILD, depthRight.value);
}
HeapPosition found = null;
// if either of the found positions are firewalls, don't use them if allowFirewall is false
if (!allowFirewall)
{
if (pos.getData().isFirewall())
{
foundLeft = null;
foundRight = null;
}
else
{
if (foundLeft != null)
if (foundLeft.node.child[foundLeft.childNum] != null)
if (foundLeft.node.child[foundLeft.childNum].getData().isFirewall())
foundLeft = null;
if (foundRight != null)
if (foundRight.node.child[foundRight.childNum] != null)
if (foundRight.node.child[foundRight.childNum].getData().isFirewall())
foundRight = null;
}
}
// If we have one valid insertion position, return it. If we have two, return the
// one with the lesser depth.
if ((foundLeft == null) && (foundRight == null))
found = null;
else if (foundLeft == null)
found = foundRight;
else if (foundRight == null)
found = foundLeft;
else
{
if (foundLeft.depth == foundRight.depth)
{
// if the 2 positions found have the same subtree depths, we insert above the one with the slower root
if (foundLeft.node.child[foundLeft.childNum].getSpeed() > foundRight.node.child[foundRight.childNum].getSpeed())
found = foundLeft;
else
found = foundRight;
}
else if (foundLeft.depth < foundRight.depth)
found = foundLeft;
else
found = foundRight;
}
// before returning, increment depth
depth.value++;
return found;
}
private HeapPosition findClosestInternalInsertionPosition(HeapItem pos, double speed, int depth, boolean allowFirewall)
{
// finds the closest internal position in the subtree starting at node 'pos' below which
// a node with speed 'speed' can be inserted.
// Note: 'depth' here refers to the distance from the starting position in the
// heap to the found node. ** This varies by method
// If allowFirewall is false, then the algorithm will not return a position
// adjacent to another firewall node.
if (pos == null) return null;
if (pos.isPlaceholder()) return null;
HeapPosition foundLeft = null;
HeapPosition foundRight = null;
// Check my left position
if (pos.child[Constants.LEFTCHILD] != null)
{
if (!pos.child[Constants.LEFTCHILD].isPlaceholder())
if ((speed >= pos.getSpeed()) && (speed < pos.child[Constants.LEFTCHILD].getSpeed()))
foundLeft = new HeapPosition(pos, Constants.LEFTCHILD, depth);
}
// Check my right position
if (pos.child[Constants.RIGHTCHILD] != null)
{
if (!pos.child[Constants.RIGHTCHILD].isPlaceholder())
if ((speed >= pos.getSpeed()) && (speed < pos.child[Constants.RIGHTCHILD].getSpeed()))
foundRight = new HeapPosition(pos, Constants.RIGHTCHILD, depth);
}
// if either of the found positions are firewalls, don't use them if allowFirewall is false
if (!allowFirewall)
{
if (pos.getData().isFirewall())
{
foundLeft = null;
foundRight = null;
}
else
{
if (foundLeft != null)
if (foundLeft.node.child[foundLeft.childNum] != null)
if (foundLeft.node.child[foundLeft.childNum].getData().isFirewall())
foundLeft = null;
if (foundRight != null)
if (foundRight.node.child[foundRight.childNum] != null)
if (foundRight.node.child[foundRight.childNum].getData().isFirewall())
foundRight = null;
}
}
HeapPosition found = null;
if ((foundLeft == null) && (foundRight == null))
{
foundLeft = findClosestInternalInsertionPosition(pos.child[Constants.LEFTCHILD], speed, depth + 1, allowFirewall);
foundRight = findClosestInternalInsertionPosition(pos.child[Constants.RIGHTCHILD], speed, depth + 1, allowFirewall);
}
if (foundLeft == null)
found = foundRight;
else if (foundRight == null)
found = foundLeft;
else
{
// Choose the one with the lesser depth
if (foundLeft.depth == foundRight.depth)
{
// if the 2 positions found have the same subtree depths, we insert above the one with the slower root
if (foundLeft.node.child[foundLeft.childNum].getSpeed() > foundRight.node.child[foundRight.childNum].getSpeed())
found = foundLeft;
else
found = foundRight;
}
else if (foundLeft.depth < foundRight.depth)
found = foundLeft;
else
found = foundRight;
}
return found;
}
public String delete(String id, String callingNodeId)
{
HeapPosition pos = findNode(id, root);
if (pos != null)
{
timeStamp++;
double speed = pos.node.child[pos.childNum].getSpeed();
if (pos.node.child[pos.childNum].getData().isFirewall())
numFirewallNodes--;
else
{
adjustNodeSpeedsForDelete(speed); // We only adjust the distribution for non-firewall nodes
numNodesInGroup[Common.speedGroup(speed)]--;
}
delete(pos);
currentSize--;
numDeletes++;
checkFirewallConflict(pos.node);
return encodeChanges(pos.node, callingNodeId);
}
else
return null;
}
public FirewallParentDeletionInfo deleteFirewallParent(String id)
{
// if a parent of a firewall node is deleted,
HeapPosition pos = findNode(id, root);
if (pos != null)
{
HeapItem deleteNode = pos.node.child[pos.childNum];
String leftChildId = null;
if (deleteNode.child[Constants.LEFTCHILD] != null)
if (deleteNode.child[Constants.LEFTCHILD].getData() != null)
leftChildId = deleteNode.child[Constants.LEFTCHILD].getData().getId();
String rightChildId = null;
if (deleteNode.child[Constants.RIGHTCHILD] != null)
if (deleteNode.child[Constants.RIGHTCHILD].getData() != null)
rightChildId = deleteNode.child[Constants.RIGHTCHILD].getData().getId();
timeStamp++;
double speed = pos.node.child[pos.childNum].getSpeed();
adjustNodeSpeedsForDelete(speed); // We only adjust the distribution for non-firewall nodes
numNodesInGroup[Common.speedGroup(speed)]--;
delete(pos);
currentSize--;
numDeletes++;
checkFirewallConflict(pos.node);
FirewallParentDeletionInfo fpdInfo = new FirewallParentDeletionInfo();
fpdInfo.timeStamp = timeStamp;
fpdInfo.parentChanges = encodeChanges(pos.node, pos.node.timeStamp, false, true, null);
fpdInfo.leftChanges = firewallInfo(leftChildId, pos.node.timeStamp);
if (fpdInfo.leftChanges != null)
fpdInfo.leftId = leftChildId;
fpdInfo.rightChanges = firewallInfo(rightChildId, pos.node.timeStamp);
if (fpdInfo.rightChanges != null)
fpdInfo.rightId = rightChildId;
return fpdInfo;
}
else
return null;
}
public HeapItem findNode(String id)
{
HeapPosition pos = findNode(id, root);
if (pos == null)
return null;
else
return pos.node.child[pos.childNum];
}
public HeapItem findParent(String id)
{
HeapPosition pos = findNode(id, root);
if (pos == null)
return null;
else
return pos.node;
}
protected HeapPosition findNode(String id, HeapItem pos)
{
if (pos == null)
return null;
if (pos.child[Constants.LEFTCHILD] != null)
if (!pos.child[Constants.LEFTCHILD].isPlaceholder())
if (Common.equalStrings(pos.child[Constants.LEFTCHILD].getData().getId(), id))
return new HeapPosition(pos, Constants.LEFTCHILD);
if (pos.child[Constants.RIGHTCHILD] != null)
if (!pos.child[Constants.RIGHTCHILD].isPlaceholder())
if (Common.equalStrings(pos.child[Constants.RIGHTCHILD].getData().getId(), id))
return new HeapPosition(pos, Constants.RIGHTCHILD);
HeapPosition found = null;
found = findNode(id, pos.child[Constants.LEFTCHILD]);
if (found == null)
found = findNode(id, pos.child[Constants.RIGHTCHILD]);
return found;
}
private void delete(HeapPosition pos)
{
// deletes the node specified by 'pos'
HeapItem deleteNode = pos.node.child[pos.childNum];
if (deleteNode == null) return;
pos.node.timeStamp = timeStamp;
if ((deleteNode.child[Constants.LEFTCHILD] == null) && (deleteNode.child[Constants.RIGHTCHILD] == null))
{
// Node has no children, just delete it.
pos.node.child[pos.childNum] = null;
// if it had a sibling placeholder, delete that too.
if (pos.node.child[siblingOf(pos.childNum)] != null)
if (pos.node.child[siblingOf(pos.childNum)].isPlaceholder())
pos.node.child[siblingOf(pos.childNum)] = null;
}
else
{
int placeHolder = findPlaceholder(deleteNode);
if (placeHolder >= 0)
{
// The node to be deleted has a child placeholder.
if (pos.node == root)
{
// if the node to be deleted is directly under the root, then we can just move the child
// up and ditch the placeholder
pos.node.child[pos.childNum] = deleteNode.child[siblingOf(placeHolder)];
}
else if ((pos.node.child[siblingOf(pos.childNum)] == null) || (pos.node.child[siblingOf(pos.childNum)].isPlaceholder()))
{
// The node to be deleted's sibling is a placeholder or is null, set it
// to a placeholder, and move the delete node's only child and its subtree up.
pos.node.child[siblingOf(pos.childNum)] = new HeapItem(Constants.PLACEHOLDER);
pos.node.child[pos.childNum] = deleteNode.child[siblingOf(placeHolder)];
// If we just put a placeholder in a leftchild position, swap the children to
// keep all placeholders in rightchild positions.
if (siblingOf(pos.childNum) == Constants.LEFTCHILD)
pos.node.swapChildren();
}
else
{
// The node to be deleted has a child placeholder and a sibling.
HeapItem sibling = pos.node.child[siblingOf(pos.childNum)];
// copy its sibling over it
deleteNode.copyValues(sibling);
deleteNode.timeStamp = timeStamp;
int siblingPlaceholder = findPlaceholder(sibling);
if (siblingPlaceholder < 0)
{
// We've replaced the node with its sibling, now we can just delete the sibling normally
delete(new HeapPosition(pos.node, siblingOf(pos.childNum)));
// Through this algorithm, the sibling will always end up switching its child position. We'll swap it back now to minimize connection changes
pos.node.swapChildren();
}
else
{
// The sibling has a placeholder too.
// Copy the subtree of the sibling over the placeholder of the deletenode.
deleteNode.child[placeHolder] = sibling.child[siblingOf(siblingPlaceholder)];
// totally remove the position where the sibling was.
pos.node.child[siblingOf(pos.childNum)] = null;
// now we have two subheaps that are different speed groups than their parent,
// and may even be different speed groups than each other. mergeSubheaps will
// resolve this.
numMerges++;
mergeSubheaps(deleteNode);
// The parent of the deleted node will always end up with the child that it keeps
// on the wrong side. Swap it back to minimize conection changes.
pos.node.swapChildren();
}
}
}
else
{
// neither child is a placeholder. copy the faster child up, then delete it.
bubble(deleteNode);
}
}
}
private void bubble(HeapItem deleteNode)
{
// overwrite the 'deleteNode' with its faster child
if ((deleteNode.child[Constants.LEFTCHILD] == null) || (deleteNode.child[Constants.LEFTCHILD].isPlaceholder()))
{
deleteNode.copyValues(deleteNode.child[Constants.RIGHTCHILD]);
deleteNode.copyChildren(deleteNode.child[Constants.RIGHTCHILD]);
}
else if ((deleteNode.child[Constants.RIGHTCHILD] == null) || (deleteNode.child[Constants.RIGHTCHILD].isPlaceholder()))
{
deleteNode.copyValues(deleteNode.child[Constants.LEFTCHILD]);
deleteNode.copyChildren(deleteNode.child[Constants.LEFTCHILD]);
}
else
{
if (deleteNode.child[Constants.LEFTCHILD].getSpeed() <= deleteNode.child[Constants.RIGHTCHILD].getSpeed())
{
deleteNode.child[Constants.LEFTCHILD].timeStamp = timeStamp;
moveUp(deleteNode, Constants.LEFTCHILD);
}
else
{
deleteNode.child[Constants.RIGHTCHILD].timeStamp = timeStamp;
moveUp(deleteNode, Constants.RIGHTCHILD);
}
}
}
private void moveUp(HeapItem parent, int childNum)
{
// overwrites the node 'parent' with its child specified by childNum.
// then it calls delete on the child's position.
parent.copyValues(parent.child[childNum]);
delete(new HeapPosition(parent, childNum));
}
private void mergeSubheaps(HeapItem parent)
{
int slowerChild = -1;
int fasterChild = -1;
// Merge the two child subheaps of parent
if (parent.child[Constants.LEFTCHILD].getSpeed() > parent.child[Constants.RIGHTCHILD].getSpeed())
slowerChild = Constants.LEFTCHILD;
else
slowerChild = Constants.RIGHTCHILD;
fasterChild = siblingOf(slowerChild);
HeapItem slowerHeap = parent.child[slowerChild];
HeapItem fasterHeap = parent.child[fasterChild];
// remove the slower subheap, and replace it with a null or a placeholder
if (Common.speedGroup(parent.getSpeed()) == Common.speedGroup(parent.child[fasterChild].getSpeed()))
parent.child[slowerChild] = null;
else
parent.child[slowerChild] = new HeapItem(Constants.PLACEHOLDER);
// if the root of the faster heap has two null children, just move the slower subheap
// to its leftchild position, and insert a placeholder if necessary
if ((fasterHeap.child[Constants.LEFTCHILD] == null) && (fasterHeap.child[Constants.RIGHTCHILD] == null))
{
fasterHeap.child[Constants.LEFTCHILD] = slowerHeap;
if (Common.speedGroup(fasterHeap.getSpeed()) != Common.speedGroup(slowerHeap.getSpeed()))
fasterHeap.child[Constants.RIGHTCHILD] = new HeapItem(Constants.PLACEHOLDER);
fasterHeap.timeStamp = timeStamp;
}
else
{
boolean done = false;
do
{
done = false;
// pull the root of the slower subheap off and insert it into the faster subheap. We require
// an internal insertion so that the nodes are moved a minimal distance.
HeapItem slowerRoot = new HeapItem();
slowerRoot.copyValues(slowerHeap);
if ((slowerHeap.child[Constants.LEFTCHILD] == null) && (slowerHeap.child[Constants.RIGHTCHILD] == null))
{
slowerHeap = null;
}
else
{
bubble(slowerHeap);
}
HeapItem insertionParent = insert(slowerRoot, fasterHeap, true);
if (insertionParent == null)
{
// Could not insert it. Must be a firewall node.
findFirewallSpot(slowerRoot, fasterHeap);
}
else
{
stampNodes(fasterHeap, insertionParent); // Mark all nodes in between as changed
}
// if the slower root was inserted successfully, then it will have a null or placeholder right child.
// If the slower heap is gone, then we're done.
if (slowerHeap == null)
{
done = true;
}
// if the slower root was inserted ok, then the following steps will suffice. If not, then we must repeat the process.
else if (insertionParent != null)
{
if (slowerRoot.child[Constants.LEFTCHILD] == null)
{
// The slowerRoot was inserted at a leaf position. Move the rest of the heap to its's left subtree, and
// insert a placeholder if necessary. Then we're done.
slowerRoot.child[Constants.LEFTCHILD] = slowerHeap;
if (Common.speedGroup(slowerRoot.getSpeed()) != Common.speedGroup(slowerRoot.child[Constants.LEFTCHILD].getSpeed())) {
slowerRoot.child[Constants.RIGHTCHILD] = new HeapItem(Constants.PLACEHOLDER);
}
slowerRoot.timeStamp = timeStamp;
done = true;
}
else
{
// The slower root was inserted internally. Move the slower tree to it's right position temporarily,
// then merge it with its sibling. Then we're done.
slowerRoot.child[Constants.RIGHTCHILD] = slowerHeap; // it doesn't matter if we overwrite a rightchild placeholder with the
// slower heap because the merge will replace it if necessary.)
if ((Common.speedGroup(insertionParent.getSpeed()) != Common.speedGroup(slowerRoot.child[Constants.LEFTCHILD].getSpeed()))
|| (Common.speedGroup(insertionParent.getSpeed()) != Common.speedGroup(slowerRoot.child[Constants.RIGHTCHILD].getSpeed())))
mergeSubheaps(slowerRoot); // we have two children, at least one is not in the same speedgroup as the parent. merge them.
done = true;
}
}
else
{
// We still have nodes in the slower subheap, and the previous slowroot wasn't inserted successfully (firewall). Repeat.
done = false;
}
}
while (!done);
}
// We want to keep placeholders as right children only. If we end up with one on
// the right, swap it.
if (parent.child[Constants.LEFTCHILD] != null)
if (parent.child[Constants.LEFTCHILD].isPlaceholder())
parent.swapChildren();
}
private boolean stampNodes(HeapItem start, HeapItem finish)
{
// Apply the timestamp to all nodes on the path starting at start and ending at finish
if (start == null)
return false;
if (start.getSpeed() > finish.getSpeed())
return false;
if (start == finish)
return true;
if (stampNodes(start.child[Constants.LEFTCHILD], finish))
{
start.timeStamp = timeStamp;
return true;
}
else if (stampNodes(start.child[Constants.RIGHTCHILD], finish))
{
start.timeStamp = timeStamp;
return true;
}
else
return false;
}
public String repositionChildren(String clientId, String childId0, String childId1)
{
// clientId is the node that requested its children be repositioned.
if ((clientId == null) || (childId0 == null) || (childId1 == null))
return null;
HeapItem item = findNode(clientId);
if (item == null)
{
return null;
}
if ((item.child[0] == null) || (!item.child[0].getData().getId().equals(childId0)))
{
return null;
}
if ((item.child[1] == null) || (!item.child[1].getData().getId().equals(childId1)))
{
return null;
}
timeStamp++;
mergeSubheaps(item);
checkFirewallConflict(item);
return encodeChanges(item, clientId);
}
public String repositionParent(String clientId, String parentId)
{
// clientId is the node that requested its parent (parentId) be repositioned.
if ((clientId == null) || (parentId == null))
return null;
if (Common.equalStrings(parentId, root.getData().getId()))
return null; // can't reposition the root
HeapPosition pos = findNode(parentId, root);
if (pos == null)
return null;
HeapItem parent = pos.node.child[pos.childNum];
HeapItem client = null;
if (parent.child[Constants.LEFTCHILD].getData().getId().equals(clientId))
client = parent.child[Constants.LEFTCHILD];
else if (parent.child[Constants.RIGHTCHILD].getData().getId().equals(clientId))
client = parent.child[Constants.RIGHTCHILD];
if (client == null)
return null;
// Swap their positions and speeds.
String temp = parent.getData().getId();
parent.getData().setId(client.getData().getId());
client.getData().setId(temp);
timeStamp++;
pos.node.timeStamp = timeStamp;
parent.timeStamp = timeStamp;
client.timeStamp = timeStamp;
checkFirewallConflict(pos.node);
String ancestorInfo = encodeChanges(pos.node, clientId);
numRepositions++;
return ancestorInfo;
}
private int findPlaceholder(HeapItem hp)
{
// finds the index of a child placeholder of 'hp.' If 'hp' has no child placeholders returns -1
if (hp.child[Constants.LEFTCHILD] != null)
if (hp.child[Constants.LEFTCHILD].isPlaceholder()) return Constants.LEFTCHILD;
if (hp.child[Constants.RIGHTCHILD] != null)
if (hp.child[Constants.RIGHTCHILD].isPlaceholder()) return Constants.RIGHTCHILD;
return -1;
}
private int siblingOf(int childNum)
{
// if childNum is LEFT, return RIGHT
// if childNum is RIGHT, return LEFT
return 1 - childNum;
}
private double adjustNodeSpeedsForInsert(double speed)
{
// This method is called when a new node with speed 'speed' is about to be inserted. We adjust each of the other speeds to
// keep an even distribution. An adjusted speed is returned for the node to be inserted.
int speedGroup = Common.speedGroup(speed);
if ((numNodesInGroup[speedGroup] <= 0) && (root != null))
return (double)speedGroup + 0.5;
if ((numNodesInGroup[speedGroup] > 0) && (numNodesInGroup[speedGroup] < Constants.PARTITION_THRESHOLD))
{
double separation = 1.0 / (numNodesInGroup[speedGroup] + 2);
double previousSeparation = 1.0 / (numNodesInGroup[speedGroup] + 1);
double insertionPosition = (double)speedGroup + separation * (Math.floor((speed - (double)speedGroup) * numNodesInGroup[speedGroup]) + 2);
adjustNodeSpeedsForInsert(root, speedGroup, (separation/previousSeparation), insertionPosition, separation);
return insertionPosition;
}
else
return speed;
}
private void adjustNodeSpeedsForInsert(HeapItem node, int speedGroup, double multiplier, double pivot, double separation)
{
// because we're adding a node to this group, we need to adjust each speed down by multiplier. For values greater than pivot,
// add one separation to make room for the new node.
if (node != null)
{
double speed = node.getSpeed();
if (Common.speedGroup(speed) < speedGroup)
{
adjustNodeSpeedsForInsert(node.child[Constants.LEFTCHILD], speedGroup, multiplier, pivot, separation);
adjustNodeSpeedsForInsert(node.child[Constants.RIGHTCHILD], speedGroup, multiplier, pivot, separation);
}
else if (Common.speedGroup(speed) == speedGroup)
{
speed = (double)speedGroup + (speed - (double)speedGroup) * multiplier;
if (speed >= pivot - (separation / 2))
speed += separation;
if (Common.speedGroup(node.getSpeed()) == Common.speedGroup(speed))
node.setSpeed(speed);
adjustNodeSpeedsForInsert(node.child[Constants.LEFTCHILD], speedGroup, multiplier, pivot, separation);
adjustNodeSpeedsForInsert(node.child[Constants.RIGHTCHILD], speedGroup, multiplier, pivot, separation);
}
}
}
private void adjustNodeSpeedsForDelete(double speed)
{
// This method is called when the node with speed 'speed' is about to be deleted. We adjust each of the other speeds to
// keep an even distribution.
int speedGroup = Common.speedGroup(speed);
if ((numNodesInGroup[speedGroup] > 1) && (numNodesInGroup[speedGroup] < Constants.PARTITION_THRESHOLD))
{
double separation = 1.0 / (numNodesInGroup[speedGroup]);
double previousSeparation = 1.0 / (numNodesInGroup[speedGroup] + 1);
adjustNodeSpeedsForDelete(root, speedGroup, (separation/previousSeparation), speed, previousSeparation);
}
}
private void adjustNodeSpeedsForDelete(HeapItem node, int speedGroup, double multiplier, double pivot, double previousSeparation)
{
// because we're deleting a node from this group, we need to adjust each speed up by multiplier. For values greater than pivot,
// remove one separation to take the place of the leaving node.
if (node != null)
{
double speed = node.getSpeed();
if (Common.speedGroup(speed) < speedGroup)
{
adjustNodeSpeedsForDelete(node.child[Constants.LEFTCHILD], speedGroup, multiplier, pivot, previousSeparation);
adjustNodeSpeedsForDelete(node.child[Constants.RIGHTCHILD], speedGroup, multiplier, pivot, previousSeparation);
}
else if (Common.speedGroup(speed) == speedGroup)
{
if (speed >= pivot)
speed -= previousSeparation;
speed = (double)speedGroup + (speed - (double)speedGroup) * multiplier;
if (Common.speedGroup(node.getSpeed()) == Common.speedGroup(speed))
node.setSpeed(speed);
adjustNodeSpeedsForDelete(node.child[Constants.LEFTCHILD], speedGroup, multiplier, pivot, previousSeparation);
adjustNodeSpeedsForDelete(node.child[Constants.RIGHTCHILD], speedGroup, multiplier, pivot, previousSeparation);
}
}
}
public void echoHeap(Handler hnd, boolean show)
{
hnd.LogMessage(5, echoHeap(show));
}
public String echoHeap(boolean show)
{
// Echo the heap to a String.
// This method MUST be removed or synchronized for production.
if (!Constants.LOGGING_ENABLED)
return null;
String heap = "";
int treeHeight = findHeight(root);
heap += "--------------------------------------------------------------------------------\n";
heap += "Total Nodes :" + currentSize + "\n";
heap += "Tree Height :" + treeHeight + "\n";
heap += "Number of Inserts :" + numInserts + "\n";
heap += "Number of Deletes :" + numDeletes + "\n";
heap += "Number of Merges :" + numMerges + "\n";
heap += "Number of Repositions :" + numRepositions + "\n";
heap += "Number of Firewall Nodes :" + numFirewallNodes + "\n";
heap += "Number of Firewall Deletions :" + numFirewallDeletes + "\n";
heap += "Number of Firewall Rejections :" + numFirewallRejections + "\n";
for (int i=0; i<=Constants.MAX_SPEEDGROUPS; i++)
if (numNodesInGroup[i] > 0)
{
heap += "Group " + i + " Number of Non-Firewall Nodes: " + numNodesInGroup[i] + "\n";
}
if (!show) return null;
for (int i=0; i<treeHeight; i++)
{
String level = echoLevel(i);
int width = 500;
for (int j=0; j<(width - level.length())/2; j+=10)
heap += " ";
heap += level + "\n";
}
return heap;
}
private String echoLevel(int level)
{
return echoLevel(level, 0, root);
}
private String echoLevel(int targetLevel, int currentLevel, HeapItem pos)
{
if (targetLevel == currentLevel)
{
if (pos == null)
return " N ";
else if (pos.isPlaceholder())
return " PH";
else
{
String hasChildren = " ";
if ((pos.child[Constants.LEFTCHILD] != null) || (pos.child[Constants.RIGHTCHILD] != null))
hasChildren = hasChildren + "*";
if (pos.timeStamp == timeStamp)
hasChildren = hasChildren + "T";
return (pos.getData().getId() + "!" + Common.roundTo(pos.getSpeed(), 2) + hasChildren);
}
}
else if (pos == null)
return "";
else if (pos.isPlaceholder())
return "";
else if (targetLevel == currentLevel + 1)
{
if ((pos.child[Constants.LEFTCHILD] == null) && (pos.child[Constants.RIGHTCHILD] == null))
return " ";
else
return "(" + echoLevel(targetLevel, currentLevel + 1, pos.child[Constants.LEFTCHILD])
+ " " + echoLevel(targetLevel, currentLevel + 1, pos.child[Constants.RIGHTCHILD]) + ") ";
}
else if (targetLevel > currentLevel)
return echoLevel(targetLevel, currentLevel + 1, pos.child[Constants.LEFTCHILD]) +
echoLevel(targetLevel, currentLevel + 1, pos.child[Constants.RIGHTCHILD]);
return "";
}
private int findHeight(HeapItem pos)
{
int depth = 0; // start at 0, as I recurse it will increase (trust me.)
if (pos == null) return 0;
if (pos.isPlaceholder()) return 0;
int depthLeft = 0;
int depthRight = 0;
depthLeft = findHeight(pos.child[Constants.LEFTCHILD]);
depthRight = findHeight(pos.child[Constants.RIGHTCHILD]);
// The height to the current node is the greater of the two.
if (depthLeft > depthRight)
depth = depthLeft + 1;
else
depth = depthRight + 1;
return depth;
}
public String encodeChanges(HeapItem pos)
{
return encodeChanges(pos, null);
}
public String encodeChanges(HeapItem pos, String callerId)
{
if (pos == null)
return null;
else
{
String top = pos.getData().getId();
String buddy = new String("");
if (Common.isFirewall(top))
buddy = findParent(top).getData().getId(); // if the top node of the changing subtree is a firewall, we must send the changes to its parent instead.
return top + "/" + buddy + "/" + new Long(timeStamp).toString() + "/" + encodeChanges(pos, pos.timeStamp, false, true, callerId);
}
}
// encodes changes in a node's subtree into a string for transmission using an inorder traversal.
private String encodeChanges(HeapItem pos, long timeStamp, boolean isFirewallEncoding, boolean isTop, String callerId)
{
// isTop is true if this node is the top of the subtree that we're encoding. If it is true, we can encode it if it is a firewall, because it will get the info directly.
// also we set it to true if it is the child of the top, and it is the caller, since we know that it will get the info directly.
if (pos == null)
return new String("0/");
else if (pos.isPlaceholder())
return new String("0/");
else
{
if ((pos.timeStamp != timeStamp) || ((!isTop) && (pos.getData().isFirewall())))
{
if ((pos.child[Constants.LEFTCHILD] == null) && (pos.child[Constants.RIGHTCHILD] == null))
return new String(pos.getData().getId() + "!" + new Double(Common.roundTo(pos.getSpeed(), 2)).toString() + "/0/0/" + Constants.NO_FIREWALL_INFO + "|" + Constants.NO_FIREWALL_INFO + "|");
else if (pos.getData().isFirewall())
// if the node is a firewall node, we don't need to encode any more of the subtree. (The new parent won't be able to connect anyway). The old parent will send its new info.
return new String(pos.getData().getId() + "!" + new Double(Common.roundTo(pos.getSpeed(), 2)).toString() + "/*" + Constants.FIREWALL_TAG + "/*/" + Constants.NO_FIREWALL_INFO + "|" + Constants.NO_FIREWALL_INFO) + "|";
else
// if the timestamp hasn't changed, we don't need to encode any more of the subtree. We just give the timestamp of the most recent change to the node's subtree. This node will insure that it is up to date when it gets the timestamp.
return new String(pos.getData().getId() + "!" + new Double(Common.roundTo(pos.getSpeed(), 2)).toString() + "/*" + new Long(pos.timeStamp).toString() + "/*/" + Constants.NO_FIREWALL_INFO + "|" + Constants.NO_FIREWALL_INFO) + "|";
}
else
{
String previousLeft = pos.previousChild[Constants.LEFTCHILD];
String previousRight = pos.previousChild[Constants.RIGHTCHILD];
pos.previousChild[Constants.LEFTCHILD] = null;
pos.previousChild[Constants.RIGHTCHILD] = null;
boolean isLeftTop = (isTop) && (pos.child[Constants.LEFTCHILD] != null) && (pos.child[Constants.LEFTCHILD].getData() != null) && (Common.equalStrings(pos.child[Constants.LEFTCHILD].getData().getId(), callerId));
boolean isRightTop = (isTop) && (pos.child[Constants.RIGHTCHILD] != null) && (pos.child[Constants.RIGHTCHILD].getData() != null) && (Common.equalStrings(pos.child[Constants.RIGHTCHILD].getData().getId(), callerId));
String subtree = new String(pos.getData().getId() + "!" + new Double(Common.roundTo(pos.getSpeed(), 2)).toString() + "/") + encodeChanges(pos.child[Constants.LEFTCHILD], timeStamp, false, isLeftTop, callerId) + encodeChanges(pos.child[Constants.RIGHTCHILD], timeStamp, false, isRightTop, callerId) + firewallInfo(previousLeft, timeStamp) + "|" + firewallInfo(previousRight, timeStamp) + "|";
if (!isFirewallEncoding) // update the previousChild ID strings to reflect the new children since we are done with this node. If this is a firewall encoding, we don't do this for the current node because we're not done with this node.
for (int i=0; i<Constants.MAX_NUM_CHILDREN; i++)
{
pos.previousChild[i] = null;
if (pos.child[i] != null)
if (pos.child[i].getData() != null)
pos.previousChild[i] = pos.child[i].getData().getId();
}
return subtree;
}
}
}
private String firewallInfo(String id, long timeStamp)
{
// if the previous child was a firewall node, we include its new encoded subtree, so that the old parent may pass it down before disconnecting.
if (Common.isFirewall(id))
{
HeapPosition pos = findNode(id, root);
if (pos == null)
return Constants.FIREWALL_NODE_DELETED;
else
return "{" + pos.node.getData().getId() + "!" + new Double(Common.roundTo(pos.node.getSpeed(), 2)).toString() + "/" + pos.childNum + "/" + new Long(timeStamp).toString() + "/" + encodeChanges(pos.node.child[pos.childNum], timeStamp, true, true, null) + "}";
}
else
return Constants.NO_FIREWALL_INFO;
}
private void checkFirewallConflict(HeapItem pos)
{
// Two firewall nodes can NEVER be parent/child in the tree. (They will have no way to connect to each other.)
if (pos != null)
checkFirewallConflict(pos, pos.timeStamp);
}
private void checkFirewallConflict(HeapItem pos, long timeStamp)
{
// traverse the nodes with the current timestamp (nodes which were affected by the last operation) and
// see if we have two firewall nodes in a row.
for (int i=0; i<Constants.MAX_NUM_CHILDREN; i++)
{
if (pos.child[i] != null)
if (!pos.child[i].isPlaceholder())
{
if (pos.child[i].timeStamp == timeStamp)
checkFirewallConflict(pos.child[i], timeStamp);
boolean conflict = false;
do
{
conflict = false;
if (pos.child[i] != null)
if (!pos.child[i].isPlaceholder())
if ((pos.getData().isFirewall()) && (pos.child[i].getData().isFirewall()))
{
conflict = true;
resolveFirewallConflict(new HeapPosition(pos, i));
}
} while (conflict);
}
}
}
private void resolveFirewallConflict(HeapPosition pos)
{
// The node at pos and the node at pos.child[i] are both firewall nodes. Remove the child and attach it to the nearest valid non-firewall leaf.
HeapItem conflictNode = new HeapItem();
conflictNode.copyValues(pos.node.child[pos.childNum]);
delete(pos);
findFirewallSpot(conflictNode, pos.node);
}
private void findFirewallSpot(HeapItem conflictNode, HeapItem startPoint)
{
double origSpeed = conflictNode.getSpeed();
double incr = ((Math.floor(origSpeed) + 1) - origSpeed) / 5.0; // set the increment to be 1/5 of the way to the top of the speedgroup
if (incr < 0.1)
incr = 0.1;
try
{
HeapItem start = null;
HeapPosition leafPos = null;
do
{
start = startPoint;
do
{
// see if there is a valid non-firewall leaf position in the subtree
leafPos = findAvailableLeafPosition(start, conflictNode.getSpeed(), true, false);
if ((leafPos == null) && (start != root))
{
// if there isn't, move up a level and try again
start = findParent(start.getData().getId());
}
else {
break;
}
} // keep moving up until we reach the root or find a position
while (leafPos == null);
if (leafPos == null)
{
// if we can't find a position with the same speed, try with a slightly slower speed
conflictNode.setSpeed(conflictNode.getSpeed() + incr);
}
} // if we cant find a position within a reasonable speed difference, just delete it
while ((leafPos == null) && (conflictNode.getSpeed() < Math.floor(origSpeed) + 1.0));
// if we find a position, put the node there, if not just leave it deleted.
if (leafPos != null)
{
leafPos.node.child[leafPos.childNum] = conflictNode; // insert it
for (int i=0; i<Constants.MAX_NUM_CHILDREN; i++) // clear its children
{
conflictNode.child[i] = null;
}
// insert a placeholder if necessary
if (Common.speedGroup(leafPos.node.getSpeed()) != Common.speedGroup(conflictNode.getSpeed()))
{
leafPos.node.child[siblingOf(leafPos.childNum)] = new HeapItem(Constants.PLACEHOLDER);
}
// set the timestamp of the affected nodes
leafPos.node.timeStamp = timeStamp;
conflictNode.timeStamp = timeStamp;
return;
}
}
catch (Exception e)
{}
// If there is no valid place for the conflict node, treat this as a deletion
numFirewallDeletes++;
numFirewallNodes--;
currentSize--;
//numNodesInGroup[Common.speedGroup(origSpeed)]--;
return;
}
public HeapItem getRoot()
{
return root;
}
public boolean verifyHeapCount()
{
return (currentSize == countNodes(root));
}
private int countNodes(HeapItem h)
{
if (h == null)
return 0;
if (h.isPlaceholder())
return 0;
return 1 + countNodes(h.child[0]) + countNodes(h.child[1]);
}
public synchronized boolean reachedMaxFirewallConnections()
{
return (!verifyFirewallAcceptable(getCurrentSize()-1, numFirewallNodes));
}
public synchronized boolean reachedMaxConnections()
{
return (getCurrentSize()-1 >= Constants.MAX_CLIENT_CONNECTIONS);
}
private boolean verifyFirewallAcceptable(int size, long numFirewallNodes)
{
/* int numLimits = Constants.FIREWALL_LIMITS.length;
for (int i=0; i<numLimits; i++)
if (size <= Constants.FIREWALL_LIMITS[i][0])
{
if (numFirewallNodes >= Constants.FIREWALL_LIMITS[i][1])
return false;
else
return true;
}*/
return true;
}
public Vector getIDList()
{
Vector list = new Vector();
getIDList(root, list);
return list;
}
private void getIDList(HeapItem pos, Vector list)
{
if (pos != null)
{
if (pos != root)
{
ClientData data = pos.getData();
if (data != null)
list.add(data.getId());
}
getIDList(pos.child[Constants.LEFTCHILD], list);
getIDList(pos.child[Constants.RIGHTCHILD], list);
}
}
}
The table below shows all metrics for PcpHeap.java.




