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
805 lines
22 KiB
C++
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
|
|
|