From 32d96e8d862be67956577b6f39352d3015be91d2 Mon Sep 17 00:00:00 2001 From: "troy%netscape.com" Date: Tue, 13 Jul 1999 03:53:48 +0000 Subject: [PATCH] Initial check-in --- layout/html/base/src/nsDST.cpp | 357 +++++++++++++++++++++++++++++++++ layout/html/base/src/nsDST.h | 83 ++++++++ 2 files changed, 440 insertions(+) create mode 100644 layout/html/base/src/nsDST.cpp create mode 100644 layout/html/base/src/nsDST.h diff --git a/layout/html/base/src/nsDST.cpp b/layout/html/base/src/nsDST.cpp new file mode 100644 index 000000000000..d3e5413733f1 --- /dev/null +++ b/layout/html/base/src/nsDST.cpp @@ -0,0 +1,357 @@ +/* -*- 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.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All Rights + * Reserved. + */ +#define PL_ARENA_CONST_ALIGN_MASK 7 +#include "nslayout.h" +#include "nsDST.h" + +///////////////////////////////////////////////////////////////////////////// +// Structure that represents a node in the DST + +struct nsDST::Node { + void* mKey; + void* mValue; + Node* mLeft; // left subtree + Node* mRight; // right subtree + + Node(void* aKey, void* aValue) + : mKey(aKey), mValue(aValue), mLeft(0), mRight(0) {} + + int IsLeaf() {return !mLeft && !mRight;} + + // Overloaded placement operator for allocating from an arena + void* operator new(size_t aSize, nsDST::NodeArena& aArena) { + return aArena.AllocNode(aSize); + } +}; + +///////////////////////////////////////////////////////////////////////////// +// Arena used for fast allocation and deallocation of Node structures. +// Maintains a free-list of freed objects + +#define NS_DST_ARENA_BLOCK_SIZE 1024 + +nsDST::NodeArena::NodeArena() + : mFreeList(0) +{ + PL_INIT_ARENA_POOL(&mPool, "DSTNodeArena", NS_DST_ARENA_BLOCK_SIZE); +} + +nsDST::NodeArena::~NodeArena() +{ + // Free the arena in the pool and finish using it + PL_FinishArenaPool(&mPool); +} + +void* +nsDST::NodeArena::AllocNode(size_t aSize) +{ + void* p; + + if (mFreeList) { + // Remove the node at the head of the free-list + p = mFreeList; + mFreeList = mFreeList->mLeft; + } else { + PL_ARENA_ALLOCATE(p, &mPool, aSize); + } + return p; +} + +void +nsDST::NodeArena::FreeNode(void* p) +{ +#ifdef NS_DEBUG + memset(p, 0xde, sizeof(Node)); +#endif + // Add this node to the head of the free-list + ((Node*)p)->mLeft = mFreeList; + mFreeList = (Node*)p; +} + +void +nsDST::NodeArena::FreeArenaPool() +{ + // Free the arena in the pool, but continue using it + PL_FreeArenaPool(&mPool); + mFreeList = 0; +} + +///////////////////////////////////////////////////////////////////////////// +// Digital search tree for doing a radix-search of pointer-based keys + +nsDST::nsDST(PtrBits aLevelZeroBit) + : mRoot(0), mLevelZeroBit(aLevelZeroBit) +{ +} + +nsDST::~nsDST() +{ +} + +void +nsDST::Clear() +{ + mArena.FreeArenaPool(); + mRoot = 0; +} + +void* +nsDST::Insert(void* aKey, void* aValue) +{ + // See if there's a matching key + Node** node = SearchTree(aKey); + void* previousValue; + + if (*node) { + previousValue = (*node)->mValue; + + // Replace the current value with the new value + (*node)->mValue = aValue; + + } else { + // Allocate a new node and insert it into the tree + *node = new (mArena)Node(aKey, aValue); + previousValue = 0; + } + +#ifdef NS_DEBUG + VerifyTree(mRoot, mLevelZeroBit); +#endif + return previousValue; +} + +// Helper function that returns one of the leaf nodes of the specified +// subtree +nsDST::Node** +nsDST::FindLeafNode(Node** aNode) const +{ +keepLooking: + // See if the node has none, one, or two child nodes + if ((*aNode)->mLeft) { + // Walk down the left branch + aNode = &(*aNode)->mLeft; + goto keepLooking; + + } else if ((*aNode)->mRight) { + // Walk down the right branch + aNode = &(*aNode)->mRight; + goto keepLooking; + + } else { + // We found a leaf node + return aNode; + } +} + +inline void +nsDST::DestroyNode(Node* aNode) +{ + aNode->~Node(); // call destructor + mArena.FreeNode(aNode); // free memory +} + +void* +nsDST::Remove(void* aKey) +{ + Node** node = SearchTree(aKey); + + if (*node) { + Node* tmp = *node; + void* value = (*node)->mValue; + + if ((*node)->IsLeaf()) { + // Just disconnect the node from its parent node + *node = 0; + } 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 + // with any leaf node below it in the tree + Node** leaf = FindLeafNode(node); + + // Copy over both the left and right subtree pointers + if ((*node)->mLeft != (*leaf)) { + (*leaf)->mLeft = (*node)->mLeft; + } + if ((*node)->mRight != (*leaf)) { + (*leaf)->mRight = (*node)->mRight; + } + + // Insert the leaf node in its new level, and disconnect it from its old + // parent node + *node = *leaf; + *leaf = 0; + } + + DestroyNode(tmp); +#ifdef NS_DEBUG + VerifyTree(mRoot, mLevelZeroBit); +#endif + return value; + } + + return 0; +} + +void* +nsDST::Search(void* aKey) const +{ + Node** result = SearchTree(aKey); + +#ifdef NS_DEBUG + if (!*result) { + // Use an alternative algorithm to verify that the key is not in + // the tree + NS_POSTCONDITION(!DepthFirstSearch(mRoot, aKey), "DST search failed"); + } +#endif + + return (*result) ? (*result)->mValue : 0; +} + +// Non-recursive search function. Returns a pointer to the pointer to the +// node +nsDST::Node** +nsDST::SearchTree(void* aKey) const +{ + NS_PRECONDITION(0 == (PtrBits(aKey) & (mLevelZeroBit - 1)), + "ignored low-order bits are not zero"); + + Node** result = (Node**)&mRoot; + PtrBits bitMask = mLevelZeroBit; + + while (*result) { + // Check if the node matches + if ((*result)->mKey == aKey) { + return result; + } + + // Check whether we search the left branch or the right branch + if (0 == (PtrBits(aKey) & bitMask)) { + result = &(*result)->mLeft; + } else { + result = &(*result)->mRight; + } + bitMask <<= 1; + } + + // We failed to find the key: return where the node would be inserted + return result; +} + +#ifdef NS_DEBUG +nsDST::Node* +nsDST::DepthFirstSearch(Node* aNode, void* aKey) const +{ + if (!aNode) { + return 0; + } else if (aNode->mKey == aKey) { + return aNode; + } else { + Node* result = DepthFirstSearch(aNode->mLeft, aKey); + + if (!result) { + result = DepthFirstSearch(aNode->mRight, aKey); + } + + return result; + } +} + +void +nsDST::VerifyTree(Node* aNode, PtrBits aBitMask) const +{ + if (aNode->mLeft) { + // Verify that the bit in the left child's pointer is 0 + NS_ASSERTION(0 == (PtrBits(aNode->mLeft->mKey) & aBitMask), + "child in left subtree is invalid"); + VerifyTree(aNode->mLeft, aBitMask << 1); + } + if (aNode->mRight) { + // Verify that the bit in the right child's pointer is 1 + NS_ASSERTION((PtrBits(aNode->mRight->mKey) & aBitMask), + "child in right subtree is invalid"); + VerifyTree(aNode->mRight, aBitMask << 1); + } +} + +void +nsDST::GatherStatistics(Node* aNode, + int aLevel, + int& aNumNodes, + int aNodesPerLevel[]) const +{ + aNumNodes++; + aNodesPerLevel[aLevel]++; + if (aNode->mLeft) { + GatherStatistics(aNode->mLeft, aLevel + 1, aNumNodes, aNodesPerLevel); + } + if (aNode->mRight) { + GatherStatistics(aNode->mRight, aLevel + 1, aNumNodes, 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 numNodes = 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, numNodes, 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 + fputs("DST statistics\n", out); + fprintf(out, " Number of nodes: %d\n", numNodes); + fprintf(out, " Height (maximum node level) of the tree: %d\n", height - 1); + fprintf(out, " Average node level: %.1f\n", float(pathLength) / float(numNodes)); + 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 + diff --git a/layout/html/base/src/nsDST.h b/layout/html/base/src/nsDST.h new file mode 100644 index 000000000000..b485f9e22914 --- /dev/null +++ b/layout/html/base/src/nsDST.h @@ -0,0 +1,83 @@ +/* -*- 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.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All Rights + * Reserved. + */ +#ifndef nsDST_h___ +#define nsDST_h___ + +#include +#include "plarena.h" +#ifdef NS_DEBUG +#include +#endif + +/** + * Digital search tree for doing a radix-search of pointer-based keys + */ +class nsDST { +public: + typedef unsigned long PtrBits; + + // 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(PtrBits aLevelZeroBit = 0x04); + ~nsDST(); + + void* Search(void* aKey) const; + void* Insert(void* aKey, void* aValue); // returns the previous value (or 0) + void* Remove(void* aKey); + void Clear(); + +#ifdef NS_DEBUG + void Dump(FILE*) const; +#endif + +private: + struct Node; + struct NodeArena { + PLArenaPool mPool; + Node* mFreeList; + + NodeArena(); + ~NodeArena(); + + void* AllocNode(size_t); + void FreeNode(void*); + void FreeArenaPool(); + }; + Node* mRoot; // root node of the tree + NodeArena mArena; + PtrBits mLevelZeroBit; + +private: + Node** SearchTree(void* aKey) const; + Node** FindLeafNode(Node** aNode) const; + void DestroyNode(Node* aNode); + +#ifdef NS_DEBUG + Node* DepthFirstSearch(Node* aNode, void* aKey) const; + void VerifyTree(Node* aNode, PtrBits aBitMask) const; + void GatherStatistics(Node* aNode, + int aLevel, + int& aNumNodes, + int aNodesPerLevel[]) const; +#endif +}; + +#endif /* nsDST_h___ */ +