Remove nsDST. b=110911 r=attinasi sr=brendan

This commit is contained in:
dbaron%fas.harvard.edu 2001-12-07 22:06:40 +00:00
parent 6bc2fdb948
commit 62e1e9537b
2 changed files with 0 additions and 1052 deletions

View File

@ -1,817 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: NPL 1.1/GPL 2.0/LGPL 2.1
*
* 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 the Initial Developer are Copyright (C) 1998
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the NPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the NPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#define PL_ARENA_CONST_ALIGN_MASK 3
#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");
// 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

View File

@ -1,235 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: NPL 1.1/GPL 2.0/LGPL 2.1
*
* 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 the Initial Developer are Copyright (C) 1998
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the NPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the NPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef nsDST_h___
#define nsDST_h___
#ifdef HAVE_MEMORY_H
#include <memory.h>
#endif
#include "nsError.h"
#ifdef NS_DEBUG
#include <stdio.h>
#endif
#include "plarena.h"
/**
* Function-like object used when enumerating the nodes of the DST
*/
class nsDSTNodeFunctor {
public:
virtual void operator() (void* aKey, void* aValue) = 0; // call operator
};
// Option flags for Search() member function
#define NS_DST_REMOVE_KEY_VALUE 0x0001
// nsresult error codes
#define NS_DST_KEY_NOT_THERE \
NS_ERROR_GENERATE_SUCCESS(NS_ERROR_MODULE_LAYOUT, 1)
#define NS_DST_VALUE_OVERWRITTEN \
NS_ERROR_GENERATE_SUCCESS(NS_ERROR_MODULE_LAYOUT, 2)
/**
* Digital search tree for doing a radix-search of pointer-based keys
*/
class nsDST {
public:
class NodeArena;
friend class NodeArena;
private:
class LeafNode;
class TwoNode;
friend class LeafNode;
friend class TwoNode;
public:
typedef unsigned long PtrBits;
// Memory arena pool used for fast allocation and deallocation of DST nodes.
// Maintains a free-list of freed objects.
// Node arenas can be shared across DST objects (they don't lock when allocating
// and freeing memory, so don't share them across threads). The DST object(s)
// own the node arena, and you just hold a weak reference.
class NodeArena {
public:
NodeArena(PRUint32 aArenaSize);
~NodeArena();
// Memory management functions
void* AllocLeafNode();
void* AllocTwoNode();
void FreeNode(LeafNode*);
void FreeNode(TwoNode*);
void FreeArenaPool();
// Lifetime management functions
void AddRef() {mRefCnt++;}
void Release();
PRBool IsShared() const {return mRefCnt > 1;}
#ifdef NS_DEBUG
int NumArenas() const;
PRUint32 ArenaSize() const {return mPool.arenasize;}
#endif
private:
PLArenaPool mPool;
LeafNode* mLeafNodeFreeList;
TwoNode* mTwoNodeFreeList;
PRUint32 mRefCnt;
};
// Create a DST. You specify the node arena to use; this allows the arena to
// be shared.
// By ignoring low-order pointer bits that are always 0, the tree height can
// be reduced. Because pointer memory should be at least 32-bit aligned, the
// default is for level 0 of the tree to start with bit 0x04 (i.e., we ignore
// the two low-order bits)
nsDST(NodeArena* aArena, PtrBits aLevelZeroBit = 0x04);
~nsDST();
nsresult Search(void* aKey, unsigned aOptions, void** aValue);
nsresult Insert(void* aKey, void* aValue, void** aOldValue);
nsresult Remove(void* aKey);
nsresult Clear();
nsresult Enumerate(nsDSTNodeFunctor& aFunctor) const;
#ifdef NS_DEBUG
void Dump(FILE*) const;
#endif
// Create a memory arena pool. You can specify the size of the underlying arenas
static NodeArena* NewMemoryArena(PRUint32 aArenaSize = 512);
private:
/////////////////////////////////////////////////////////////////////////////
// Classes that represents nodes in the DST
// To reduce the amount of memory we use there are two types of nodes:
// - leaf nodes
// - two nodes (left and right child)
//
// We distinguish the two types of nodes by looking at the low-order
// bit of the key. If it is 0, then the node is a leaf node. If it is
// 1, then the node is a two node. Use function Key() when retrieving
// the key, and function IsLeaf() to tell what type of node it is
//
// It's an invariant of the tree that a two node can not have both child links
// NULL. In that case it must be converted back to a leaf node
//
// NOTE: This code was originally put into nsDST.cpp to reduce size
// and to hide the implementation - See Troy's 1.10 revision.
// However the AIX xlC 3.6.4.0 compiler does not allow this.
class LeafNode {
public:
void* mKey;
void* mValue;
// Constructors
LeafNode(void* aKey, void* aValue);
LeafNode(const LeafNode& aLeafNode);
// Accessor for getting the key. Clears the bit used to tell if the
// node is a leaf. This function can be safely used on any node without
// knowing whether it's a leaf or a two node
void* Key() const {return (void*)(PtrBits(mKey) & ~0x1);}
// Helper function that returns TRUE if the node is a leaf
int IsLeaf() const {return 0 == (PtrBits(mKey) & 0x1);}
// Overloaded placement operator for allocating from an arena
void* operator new(size_t aSize, NodeArena* aArena) {return aArena->AllocLeafNode();}
};
// Definition of TwoNode class
class TwoNode : public LeafNode {
public:
LeafNode* mLeft; // left subtree
LeafNode* mRight; // right subtree
TwoNode(const LeafNode& aLeafNode);
void SetKeyAndValue(void* aKey, void* aValue) {
mKey = (void*)(PtrBits(aKey) | 0x01);
mValue = aValue;
}
// Overloaded placement operator for allocating from an arena
void* operator new(size_t aSize, NodeArena* aArena) {return aArena->AllocTwoNode();}
private:
TwoNode(const TwoNode&); // no implementation
void operator=(const TwoNode&); // no implementation
};
LeafNode* mRoot; // root node of the tree
NodeArena* mArena;
PtrBits mLevelZeroBit;
private:
// Helper functions
LeafNode* RemoveLeftMostLeafNode(TwoNode** aTwoNode);
void DestroyNode(LeafNode* aLeafNode);
void DestroyNode(TwoNode* aTwoNode);
LeafNode* ConvertToLeafNode(TwoNode** aTwoNode);
TwoNode* ConvertToTwoNode(LeafNode** aLeafNode);
void EnumTree(LeafNode* aNode, nsDSTNodeFunctor& aFunctor) const;
void FreeTree(LeafNode* aNode);
nsresult SearchTree(void* aKey, unsigned aOptions, void** aValue);
#ifdef NS_DEBUG
// Diagnostic functions
void VerifyTree(LeafNode* aNode, int aLevel = 0, PtrBits aLevelKeyBits = 0) const;
LeafNode* DepthFirstSearch(LeafNode* aNode, void* aKey) const;
void GatherStatistics(LeafNode* aNode,
int aLevel,
int& aNumLeafNodes,
int& aNumTwoNodes,
int aNodesPerLevel[]) const;
#endif
nsDST(const nsDST&); // no implementation
void operator=(const nsDST&); // no implementation
};
#endif /* nsDST_h___ */