jdunn%netscape.com 2318eebe99 Fix for AIX build bustage. Unfortunately the compiler requires that if
a class includes other classes, then the other classes must be defined
by the time the enclosing class is.  In this case nsDST includes
NodeArena, LeafNode & TwoNode and so therefore those classes must be
defined in nsDST.h.

r troy: Note, Troy didn't APPROVE of the fix (since it isn't pretty) but
        he has graciously allowed it in order to help AIX development.
        He did believe that it wouldn't hurt to put the change in.


git-svn-id: svn://10.0.0.236/trunk@53313 18797224-902f-48f8-a5cc-f745e15eee43
1999-11-12 03:52:50 +00:00

805 lines
22 KiB
C++

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
*/
#define PL_ARENA_CONST_ALIGN_MASK 3
#include "nslayout.h"
#include "nsDST.h"
#include "nsISupportsUtils.h"
#ifdef NS_DEBUG
#include <string.h>
#endif
// Constructors
inline nsDST::LeafNode::LeafNode(void* aKey, void* aValue)
: mKey(aKey), mValue(aValue)
{
}
inline nsDST::LeafNode::LeafNode(const LeafNode& aNode)
: mKey(aNode.Key()), // don't assume it's a leaf node
mValue(aNode.mValue)
{
}
// Constructor
inline nsDST::TwoNode::TwoNode(const LeafNode& aLeafNode)
: nsDST::LeafNode((void*)(PtrBits(aLeafNode.mKey) | 0x1), aLeafNode.mValue),
mLeft(0), mRight(0)
{
}
// If you know that the node is a leaf node, then you can quickly access
// the key without clearing the bit that indicates whether it's a leaf or
// a two node
#define DST_GET_LEAF_KEY(leaf) ((leaf)->mKey)
// Macros to check whether we branch left or branch right for a given
// key and bit mask
#define DST_BRANCHES_LEFT(key, mask) \
(0 == (PtrBits(key) & (mask)))
#define DST_BRANCHES_RIGHT(key, mask) \
((mask) == (PtrBits(key) & (mask)))
/////////////////////////////////////////////////////////////////////////////
// Arena used for fast allocation and deallocation of Node structures.
// Maintains free-list of freed objects
nsDST::NodeArena*
nsDST::NewMemoryArena(PRUint32 aArenaSize)
{
return new NodeArena(aArenaSize);
}
MOZ_DECL_CTOR_COUNTER(NodeArena);
// Constructor
nsDST::NodeArena::NodeArena(PRUint32 aArenaSize)
: mLeafNodeFreeList(0), mTwoNodeFreeList(0), mRefCnt(0)
{
MOZ_COUNT_CTOR(NodeArena);
PL_INIT_ARENA_POOL(&mPool, "DSTNodeArena", aArenaSize);
}
// Destructor
nsDST::NodeArena::~NodeArena()
{
MOZ_COUNT_DTOR(NodeArena);
// Free the arena in the pool and finish using it
PL_FinishArenaPool(&mPool);
}
void
nsDST::NodeArena::Release()
{
NS_PRECONDITION(mRefCnt > 0, "unexpected ref count");
if (--mRefCnt == 0) {
delete this;
}
}
// Called by the nsDST::LeafNode's overloaded placement operator when
// allocating a new node. First checks the free list. If the free list is
// empty, then it allocates memory from the arena
void*
nsDST::NodeArena::AllocLeafNode()
{
void* p;
if (mLeafNodeFreeList) {
// Remove the node at the head of the free-list
p = mLeafNodeFreeList;
mLeafNodeFreeList = (LeafNode*)mLeafNodeFreeList->mKey;
} else {
PL_ARENA_ALLOCATE(p, &mPool, sizeof(nsDST::LeafNode));
}
return p;
}
// Called by the nsDST::TwoNode's overloaded placement operator when
// allocating a new node. First checks the free list. If the free list is
// empty, then it allocates memory from the arena
void*
nsDST::NodeArena::AllocTwoNode()
{
void* p;
if (mTwoNodeFreeList) {
// Remove the node at the head of the free-list
p = mTwoNodeFreeList;
mTwoNodeFreeList = (TwoNode*)mTwoNodeFreeList->mKey;
} else {
PL_ARENA_ALLOCATE(p, &mPool, sizeof(nsDST::TwoNode));
}
return p;
}
// Called by the DST's DestroyNode() function. Adds the node to the head
// of the free list where it can be reused by AllocateNode()
void
nsDST::NodeArena::FreeNode(LeafNode* aLeafNode)
{
#ifdef NS_DEBUG
memset(aLeafNode, 0xde, sizeof(*aLeafNode));
#endif
// Add this node to the head of the free-list
aLeafNode->mKey = mLeafNodeFreeList;
mLeafNodeFreeList = aLeafNode;
}
// Called by the DST's DestroyNode() function. Adds the node to the head
// of the free list where it can be reused by AllocateNode()
void
nsDST::NodeArena::FreeNode(TwoNode* aTwoNode)
{
#ifdef NS_DEBUG
memset(aTwoNode, 0xde, sizeof(*aTwoNode));
#endif
// Add this node to the head of the free-list
aTwoNode->mKey = mTwoNodeFreeList;
mTwoNodeFreeList = aTwoNode;
}
// Called by the DST's Clear() function when we want to free the memory
// in the arena pool, but continue using the arena
void
nsDST::NodeArena::FreeArenaPool()
{
// Free the arena in the pool, but continue using it
PL_FreeArenaPool(&mPool);
// Clear the free lists
mLeafNodeFreeList = 0;
mTwoNodeFreeList = 0;
}
#ifdef NS_DEBUG
int
nsDST::NodeArena::NumArenas() const
{
// Calculate the number of arenas in use
int numArenas = 0;
for (PLArena* arena = mPool.first.next; arena; arena = arena->next) {
numArenas++;
}
return numArenas;
}
#endif
/////////////////////////////////////////////////////////////////////////////
// Digital search tree for doing a radix-search of pointer-based keys
MOZ_DECL_CTOR_COUNTER(nsDST);
// Constructor
nsDST::nsDST(NodeArena* aArena, PtrBits aLevelZeroBit)
: mRoot(0), mArena(aArena), mLevelZeroBit(aLevelZeroBit)
{
NS_PRECONDITION(aArena, "no node arena");
MOZ_COUNT_CTOR(nsDST);
// Add a reference to the node arena
mArena->AddRef();
}
// Destructor
nsDST::~nsDST()
{
MOZ_COUNT_DTOR(nsDST);
// If the node arena is shared, then we'll need to explicitly
// free each node in the tree
if (mArena->IsShared()) {
FreeTree(mRoot);
}
// Release our reference to the node arena
mArena->Release();
}
// Called by Remove() to destroy a node. Explicitly calls the destructor
// and then asks the memory arena to free the memory
inline void
nsDST::DestroyNode(LeafNode* aLeafNode)
{
aLeafNode->~LeafNode(); // call destructor
mArena->FreeNode(aLeafNode); // free memory
}
// Called by Remove() to destroy a node. Explicitly calls the destructor
// and then asks the memory arena to free the memory
inline void
nsDST::DestroyNode(TwoNode* aTwoNode)
{
aTwoNode->~TwoNode(); // call destructor
mArena->FreeNode(aTwoNode); // free memory
}
void
nsDST::FreeTree(LeafNode* aNode)
{
keepLooping:
if (!aNode) {
return;
}
if (aNode->IsLeaf()) {
DestroyNode(aNode);
} else {
LeafNode* left = ((TwoNode*)aNode)->mLeft;
LeafNode* right = ((TwoNode*)aNode)->mRight;
DestroyNode((TwoNode*)aNode);
FreeTree(left);
aNode = right;
goto keepLooping;
}
}
// Removes all nodes from the tree
nsresult
nsDST::Clear()
{
if (mArena->IsShared()) {
FreeTree(mRoot);
} else {
mArena->FreeArenaPool();
}
mRoot = 0;
return NS_OK;
}
// Enumerate all the nodes in the tree
nsresult
nsDST::Enumerate(nsDSTNodeFunctor& aFunctor) const
{
EnumTree(mRoot, aFunctor);
return NS_OK;
}
void
nsDST::EnumTree(LeafNode* aNode, nsDSTNodeFunctor& aFunctor) const
{
keepLooping:
if (!aNode) {
return;
}
// Call the function-like object
aFunctor(aNode->Key(), aNode->mValue);
if (!aNode->IsLeaf()) {
EnumTree(((TwoNode*)aNode)->mLeft, aFunctor);
aNode = ((TwoNode*)aNode)->mRight;
goto keepLooping;
}
}
nsDST::LeafNode*
nsDST::ConvertToLeafNode(TwoNode** aTwoNode)
{
LeafNode* leaf = new (mArena)LeafNode((*aTwoNode)->Key(), (*aTwoNode)->mValue);
DestroyNode(*aTwoNode);
*aTwoNode = (TwoNode*)leaf;
return leaf;
}
nsDST::TwoNode*
nsDST::ConvertToTwoNode(LeafNode** aLeafNode)
{
TwoNode* twoNode = new (mArena)TwoNode(**aLeafNode);
DestroyNode(*aLeafNode);
*aLeafNode = (LeafNode*)twoNode;
return twoNode;
}
// Searches the tree for a node with the specified key. Return the value
// or NULL if the key is not in the tree
nsresult
nsDST::Search(void* aKey, unsigned aOptions, void** aValue)
{
NS_ENSURE_ARG_POINTER(aValue);
*aValue = 0; // initialize OUT parameter
nsresult result = SearchTree(aKey, aOptions, aValue);
#ifdef DEBUG_troy
if (NS_DST_KEY_NOT_THERE == result) {
// Use an alternative algorithm to verify that there's really
// no node with a matching key
DepthFirstSearch(mRoot, aKey);
}
#endif
return result;
}
// Adds a new key to the tree. If the specified key is already in the
// tree, then the existing value is replaced by the new value. Returns
// NS_DST_VALUE_OVERWRITTEN if there is an existing value that is
// overwritten
nsresult
nsDST::Insert(void* aKey, void* aValue, void** aOldValue)
{
NS_PRECONDITION(0 == (PtrBits(aKey) & (mLevelZeroBit - 1)),
"ignored low-order bits are not zero");
NS_ENSURE_ARG_POINTER(aValue);
// Initialize OUT parameter
if (aOldValue) {
*aOldValue = 0;
}
// See if there's an existing node with a matching key
LeafNode** node = (LeafNode**)&mRoot;
TwoNode* branchReduction = 0;
nsresult result = NS_OK;
if (*node) {
PtrBits bitMask = mLevelZeroBit;
while (1) {
// See if the key matches
if ((*node)->Key() == aKey) {
// We found an existing node with a matching key. Replace the value and
// don't do a branch reduction
branchReduction = 0;
break;
}
// Is this a leaf node?
if ((*node)->IsLeaf()) {
if (!branchReduction) {
// Replace the leaf node with a two node and destroy the
// leaf node
TwoNode* twoNode = ConvertToTwoNode(node);
// Exit the loop and allocate a new leaf node and set its
// key and value
node = DST_BRANCHES_LEFT(aKey, bitMask) ? &twoNode->mLeft :
&twoNode->mRight;
}
break;
} else {
TwoNode*& twoNode = *(TwoNode**)node;
// Check whether we search the left branch or the right branch
if (DST_BRANCHES_LEFT(aKey, bitMask)) {
// If there's a left node and no right node, then see if we
// can reduce the one way braching in the tree
if (twoNode->mLeft && !twoNode->mRight) {
if (DST_BRANCHES_RIGHT(twoNode->mKey, bitMask)) {
// Yes, this node can become the right node of the tree. Remember
// this for later in case we don't find a matching node
branchReduction = twoNode;
}
}
node = &twoNode->mLeft;
} else {
// If there's a right node and no left node, then see if we
// can reduce the one way braching in the tree
if (twoNode->mRight && !twoNode->mLeft) {
if (DST_BRANCHES_LEFT(twoNode->mKey, bitMask)) {
// Yes, this node can become the left node of the tree. Remember
// this for later in case we don't find a matching node
branchReduction = twoNode;
}
}
node = &twoNode->mRight;
}
// Did we reach a null link?
if (!*node) {
break;
}
}
// Move to the next bit in the key
bitMask <<= 1;
}
}
if (branchReduction) {
// Reduce the one way branching by moving the existing key and
// value to either the right or left node
if (!branchReduction->mLeft) {
NS_ASSERTION(branchReduction->mRight, "bad state");
branchReduction->mLeft = new (mArena)LeafNode(*branchReduction);
} else {
NS_ASSERTION(!branchReduction->mRight, "bad state");
branchReduction->mRight = new (mArena)LeafNode(*branchReduction);
}
// Replace the existing key and value with the new key and value
branchReduction->SetKeyAndValue(aKey, aValue);
} else if (*node) {
// We found an existing node with a matching key. Replace the current
// value with the new value
if (aOldValue) {
*aOldValue = (*node)->mValue;
}
(*node)->mValue = aValue;
result = NS_DST_VALUE_OVERWRITTEN;
} else {
// Allocate a new leaf node and insert it into the tree
*node = new (mArena)LeafNode(aKey, aValue);
}
#ifdef DEBUG_troy
VerifyTree(mRoot);
// Verify that one and only one node in the tree have the specified key
DepthFirstSearch(mRoot, aKey);
#endif
return result;
}
// Helper function that removes and returns the left most leaf node
// of the specified subtree.
// Note: if the parent of the node that is removed is now a leaf,
// it will be converted to a leaf node...
nsDST::LeafNode*
nsDST::RemoveLeftMostLeafNode(TwoNode** aTwoNode)
{
NS_PRECONDITION(!(*aTwoNode)->IsLeaf(), "bad parameter");
keepLooking:
LeafNode** child;
if ((*aTwoNode)->mLeft) {
// Walk down the left branch
child = &(*aTwoNode)->mLeft;
// See if it's a leaf
if ((*child)->IsLeaf()) {
// Remove the child from its parent
LeafNode* result = *child;
*child = 0;
// If there's no right node then the parent is now a leaf so
// convert it to a leaf node
if (!(*aTwoNode)->mRight) {
ConvertToLeafNode(aTwoNode);
}
// Return the leaf node
return result;
}
} else if ((*aTwoNode)->mRight) {
// No left branch, so walk down the right branch
child = &(*aTwoNode)->mRight;
if ((*child)->IsLeaf()) {
// Remove the child from its parent
LeafNode* result = *child;
*child = 0;
// That was the parent's only child node so convert the parent
// node to a leaf node
ConvertToLeafNode(aTwoNode);
// Return the leaf node
return result;
}
} else {
// We should never encounter a two node with both links NULL. It should
// have been coverted to a leaf instead...
NS_ASSERTION(0, "bad node type");
return 0;
}
aTwoNode = (TwoNode**)child;
goto keepLooking;
}
// Returns NS_OK if there is a matching key and NS_DST_KEY_NOT_THERE
// otherwise
nsresult
nsDST::SearchTree(void* aKey, unsigned aOptions, void** aValue)
{
NS_PRECONDITION(0 == (PtrBits(aKey) & (mLevelZeroBit - 1)),
"ignored low-order bits are not zero");
if (mRoot) {
LeafNode** node;
TwoNode** parentNode = 0;
PtrBits bitMask = mLevelZeroBit;
if (mRoot->Key() == aKey) {
node = (LeafNode**)&mRoot;
} else if (mRoot->IsLeaf()) {
return NS_DST_KEY_NOT_THERE; // no node with a matching key
} else {
// Look for a node with a matching key
node = (LeafNode**)&mRoot;
while (1) {
NS_ASSERTION(!(*node)->IsLeaf(), "unexpected leaf mode");
parentNode = (TwoNode**)node;
// Check whether we search the left branch or the right branch
if (DST_BRANCHES_LEFT(aKey, bitMask)) {
node = &(*(TwoNode**)node)->mLeft;
} else {
node = &(*(TwoNode**)node)->mRight;
}
if (!*node) {
// We found a NULL link which means no node with a matching key
return NS_DST_KEY_NOT_THERE;
}
// Check if the key matches
if ((*node)->Key() == aKey) {
break;
}
// The key doesn't match. If this is a leaf node that means no
// node with a matching key
if ((*node)->IsLeaf()) {
return NS_DST_KEY_NOT_THERE;
}
// Move to the next bit in the key
bitMask <<= 1;
}
}
// We found a matching node
*aValue = (*node)->mValue;
// Should we remove the key/value pair?
if (aOptions & NS_DST_REMOVE_KEY_VALUE) {
if ((*node)->IsLeaf()) {
// Delete the leaf node
DestroyNode(*node);
// Disconnect the node from its parent node
*node = 0;
// If the parent now has no child nodes, then convert it to a
// leaf frame
if (parentNode && !(*parentNode)->mLeft && !(*parentNode)->mRight) {
ConvertToLeafNode(parentNode);
}
} else {
// We can't just move the left or right subtree up one level, because
// then we would have to re-sort the tree. Instead replace the node's
// key and value with that of its left most leaf node (any leaf frame
// would do)
LeafNode* leaf = RemoveLeftMostLeafNode((TwoNode**)node);
// Copy over the leaf's key and value
// Note: RemoveLeftMostLeafNode() may have converted "node" to a
// leaf node so don't make any assumptions here
if ((*node)->IsLeaf()) {
(*node)->mKey = DST_GET_LEAF_KEY(leaf);
} else {
(*node)->mKey = (void*)(PtrBits(DST_GET_LEAF_KEY(leaf)) | 0x01);
}
(*node)->mValue = leaf->mValue;
// Delete the leaf node
DestroyNode(leaf);
}
#ifdef DEBUG_troy
VerifyTree(mRoot);
#endif
}
return NS_OK;
}
return NS_DST_KEY_NOT_THERE;
}
// Removes a key from the tree. Returns NS_OK if successful and
// NS_DST_KEY_NOT_THERE if there is no node with a matching key
nsresult
nsDST::Remove(void* aKey)
{
void* value;
nsresult result = SearchTree(aKey, NS_DST_REMOVE_KEY_VALUE, &value);
#ifdef DEBUG_troy
if (NS_OK == result) {
// We found a node with a matching key and we removed it. Verify that
// we successfully removed the node
void* ignoreValue;
NS_POSTCONDITION(Search(aKey, 0, &ignoreValue) == NS_DST_KEY_NOT_THERE,
"remove operation failed");
}
#endif
return result;
}
#ifdef NS_DEBUG
// Helper function used to verify the integrity of the tree. Does a
// depth-first search of the tree looking for a node with the specified
// key. Also verifies that the key is not in the tree more than once
nsDST::LeafNode*
nsDST::DepthFirstSearch(LeafNode* aNode, void* aKey) const
{
if (!aNode) {
return 0;
} else if (aNode->IsLeaf()) {
return (aNode->Key() == aKey) ? aNode : 0;
} else {
// Search the left branch of the tree
LeafNode* result = DepthFirstSearch(((TwoNode*)aNode)->mLeft, aKey);
if (result) {
// Verify there's no matching node in the right branch
NS_ASSERTION(!DepthFirstSearch(((TwoNode*)aNode)->mRight, aKey),
"key in tree more than once");
} else {
// Search the right branch of the tree
result = DepthFirstSearch(((TwoNode*)aNode)->mRight, aKey);
}
// See if the node's key matches
if (aNode->Key() == aKey) {
NS_ASSERTION(!result, "key in tree more than once");
result = aNode;
}
return result;
}
}
// Helper function that verifies the integrity of the tree. Called
// by Insert() and Remove()
void
nsDST::VerifyTree(LeafNode* aNode, int aLevel, PtrBits aLevelKeyBits) const
{
if (aNode) {
// Verify that the first "aLevel" bits of this node's key agree with the
// accumulated key bits that apply to this branch of the tree, i.e., the
// path to this node as specified by the bits of the key
if (aLevel > 0) {
// When calculating the bit mask, take into consideration the low-order
// bits we ignore
PtrBits bitMask = (mLevelZeroBit << aLevel) - 1;
NS_ASSERTION(aLevelKeyBits == (PtrBits(aNode->Key()) & bitMask),
"key's bits don't match");
}
if (!aNode->IsLeaf()) {
const TwoNode* twoNode = (TwoNode*)aNode;
NS_ASSERTION(twoNode->mLeft || twoNode->mRight,
"two node with no child nodes");
// All node keys in the left subtree should have the next bit set to 0
VerifyTree(twoNode->mLeft, aLevel + 1, aLevelKeyBits);
// All node keys in the left subtree should have the next bit set to 1
VerifyTree(twoNode->mRight, aLevel + 1, aLevelKeyBits | (mLevelZeroBit << aLevel));
}
}
}
void
nsDST::GatherStatistics(LeafNode* aNode,
int aLevel,
int& aNumLeafNodes,
int& aNumTwoNodes,
int aNodesPerLevel[]) const
{
if (aNode) {
aNodesPerLevel[aLevel]++;
if (aNode->IsLeaf()) {
aNumLeafNodes++;
} else {
aNumTwoNodes++;
GatherStatistics(((TwoNode*)aNode)->mLeft, aLevel + 1, aNumLeafNodes,
aNumTwoNodes, aNodesPerLevel);
GatherStatistics(((TwoNode*)aNode)->mRight, aLevel + 1, aNumLeafNodes,
aNumTwoNodes, aNodesPerLevel);
}
}
}
void
nsDST::Dump(FILE* out) const
{
// Walk the tree gathering statistics about the number of nodes, the height
// of the tree (maximum node level), the average node level, and the median
// node level
static const int maxLevels = sizeof(void*) * 8;
int numLeafNodes = 0;
int numTwoNodes = 0;
int nodesPerLevel[maxLevels]; // count of the number of nodes at a given level
memset(&nodesPerLevel, 0, sizeof(int) * maxLevels);
// Walk each node in the tree recording its node level
GatherStatistics(mRoot, 0, numLeafNodes, numTwoNodes, nodesPerLevel);
// Calculate the height, average node level, and median node level
int height, medianLevel = 0, pathLength = 0;
for (height = 0; height < maxLevels; height++) {
int count = nodesPerLevel[height];
if (0 == count) {
break;
}
// Update the median node level
if (count > nodesPerLevel[medianLevel]) {
medianLevel = height;
}
// Update the path length
pathLength += height * count;
}
// Output the statistics
int numTotalNodes = numLeafNodes + numTwoNodes;
fputs("DST statistics\n", out);
fprintf(out, " Number of leaf nodes: %d\n", numLeafNodes);
fprintf(out, " Number of tree nodes: %d\n", numTwoNodes);
if (numTotalNodes > 0) {
fprintf(out, " Average node size: %.1f\n",
float(numLeafNodes * sizeof(LeafNode) + numTwoNodes * sizeof(TwoNode)) /
float(numTotalNodes));
}
fprintf(out, " Number of arenas: %d(%d)\n", mArena->NumArenas(), mArena->ArenaSize());
fprintf(out, " Height (maximum node level) of the tree: %d\n", height - 1);
if (numTotalNodes > 0) {
fprintf(out, " Average node level: %.1f\n", float(pathLength) / float(numTotalNodes));
}
fprintf(out, " Median node level: %d\n", medianLevel);
fprintf(out, " Path length: %d\n", pathLength);
// Output the number of nodes at each level of the tree
fputs(" Nodes per level: ", out);
fprintf(out, "%d", nodesPerLevel[0]);
for (int i = 1; i < height; i++) {
fprintf(out, ", %d", nodesPerLevel[i]);
}
fputs("\n", out);
}
#endif