mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-26 06:11:37 +00:00
First checked in. Not part of the build.
This commit is contained in:
parent
93b663f94c
commit
a2bb9ecb12
874
xpcom/ds/nsVoidBTree.cpp
Normal file
874
xpcom/ds/nsVoidBTree.cpp
Normal file
@ -0,0 +1,874 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/*
|
||||
* The contents of this file are subject to the Mozilla Public License
|
||||
* Version 1.1 (the "MPL"); you may not use this file except in
|
||||
* compliance with the MPL. You may obtain a copy of the MPL at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the MPL is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the MPL
|
||||
* for the specific language governing rights and limitations under the
|
||||
* MPL.
|
||||
*
|
||||
* The Initial Developer of this code under the MPL is Netscape
|
||||
* Communications Corporation. Portions created by Netscape are
|
||||
* Copyright (C) 1999 Netscape Communications Corporation. All Rights
|
||||
* Reserved.
|
||||
*
|
||||
* Original Author:
|
||||
* Chris Waterson <waterson@netscape.com>
|
||||
*/
|
||||
|
||||
#include "nsVoidBTree.h"
|
||||
|
||||
#ifdef DEBUG
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
|
||||
// Set this to force the tree to be verified after every insertion and
|
||||
// removal.
|
||||
//#define PARANOID 1
|
||||
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// nsVoidBTree::Node
|
||||
//
|
||||
// Implementation methods
|
||||
//
|
||||
|
||||
nsresult
|
||||
nsVoidBTree::Node::Create(Type aType, PRInt32 aCapacity, Node** aResult)
|
||||
{
|
||||
// So we only ever have to do one allocation for a Node, we do a
|
||||
// "naked" heap allocation, computing the size of the node and
|
||||
// "padding" it out so that it can hold aCapacity slots.
|
||||
char* bytes = new char[sizeof(Node) + (aCapacity - 1) * sizeof(void*)];
|
||||
if (! bytes)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
Node* result = NS_REINTERPRET_CAST(Node*, bytes);
|
||||
result->mBits = result->mSubTreeSize = 0;
|
||||
result->SetType(aType);
|
||||
|
||||
*aResult = result;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsVoidBTree::Node::Destroy(Node* aNode)
|
||||
{
|
||||
char* bytes = NS_REINTERPRET_CAST(char*, aNode);
|
||||
delete[] bytes;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
nsVoidBTree::Node::InsertElementAt(void* aElement, PRInt32 aIndex)
|
||||
{
|
||||
NS_PRECONDITION(aIndex >= 0 && aIndex <= GetCount(), "bad index");
|
||||
|
||||
PRInt32 count = GetCount();
|
||||
SetCount(count + 1);
|
||||
|
||||
while (count > aIndex) {
|
||||
mData[count] = mData[count - 1];
|
||||
--count;
|
||||
}
|
||||
|
||||
mData[aIndex] = aElement;
|
||||
}
|
||||
|
||||
void
|
||||
nsVoidBTree::Node::RemoveElementAt(PRInt32 aIndex)
|
||||
{
|
||||
NS_PRECONDITION(aIndex >= 0 && aIndex < GetCount(), "bad index");
|
||||
|
||||
PRInt32 count = GetCount();
|
||||
SetCount(count - 1);
|
||||
|
||||
while (aIndex < count) {
|
||||
mData[aIndex] = mData[aIndex + 1];
|
||||
++aIndex;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
//
|
||||
// nsVoidBTree::Path
|
||||
//
|
||||
// Implementation methods
|
||||
//
|
||||
|
||||
nsVoidBTree::Path::Path(const Path& aOther)
|
||||
: mTop(aOther.mTop)
|
||||
{
|
||||
for (PRInt32 i = 0; i < mTop; ++i)
|
||||
mLink[i] = aOther.mLink[i];
|
||||
}
|
||||
|
||||
nsVoidBTree::Path&
|
||||
nsVoidBTree::Path::operator=(const Path& aOther)
|
||||
{
|
||||
mTop = aOther.mTop;
|
||||
for (PRInt32 i = 0; i < mTop; ++i)
|
||||
mLink[i] = aOther.mLink[i];
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline nsresult
|
||||
nsVoidBTree::Path::Push(Node* aNode, PRInt32 aIndex)
|
||||
{
|
||||
// XXX If you overflow this thing, think about making larger index
|
||||
// or data nodes. You can pack a _lot_ of data into a pretty flat
|
||||
// tree.
|
||||
NS_PRECONDITION(mTop <= kMaxDepth, "overflow");
|
||||
if (mTop > kMaxDepth)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
mLink[mTop].mNode = aNode;
|
||||
mLink[mTop].mIndex = aIndex;
|
||||
++mTop;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
inline void
|
||||
nsVoidBTree::Path::Pop(Node** aNode, PRInt32* aIndex)
|
||||
{
|
||||
--mTop;
|
||||
*aNode = mLink[mTop].mNode;
|
||||
*aIndex = mLink[mTop].mIndex;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
//
|
||||
// nsVoidBTree methods
|
||||
//
|
||||
|
||||
nsVoidBTree::nsVoidBTree(const nsVoidBTree& aOther)
|
||||
{
|
||||
ConstIterator last = aOther.Last();
|
||||
for (ConstIterator element = aOther.First(); element != last; ++element)
|
||||
AppendElement(*element);
|
||||
}
|
||||
|
||||
nsVoidBTree&
|
||||
nsVoidBTree::operator=(const nsVoidBTree& aOther)
|
||||
{
|
||||
Clear();
|
||||
ConstIterator last = aOther.Last();
|
||||
for (ConstIterator element = aOther.First(); element != last; ++element)
|
||||
AppendElement(*element);
|
||||
return *this;
|
||||
}
|
||||
|
||||
PRInt32
|
||||
nsVoidBTree::Count() const
|
||||
{
|
||||
if (IsEmpty())
|
||||
return 0;
|
||||
|
||||
if (IsSingleElement())
|
||||
return 1;
|
||||
|
||||
Node* root = NS_REINTERPRET_CAST(Node*, mRoot & kRoot_PointerMask);
|
||||
return root->GetSubTreeSize();
|
||||
}
|
||||
|
||||
void*
|
||||
nsVoidBTree::ElementAt(PRInt32 aIndex) const
|
||||
{
|
||||
if (aIndex < 0 || aIndex >= Count())
|
||||
return nsnull;
|
||||
|
||||
if (IsSingleElement())
|
||||
return NS_REINTERPRET_CAST(void*, mRoot & kRoot_PointerMask);
|
||||
|
||||
Node* current = NS_REINTERPRET_CAST(Node*, mRoot & kRoot_PointerMask);
|
||||
while (current->GetType() != Node::eType_Data) {
|
||||
// We're still in the index. Find the right leaf.
|
||||
Node* next = nsnull;
|
||||
|
||||
PRInt32 count = current->GetCount();
|
||||
for (PRInt32 i = 0; i < count; ++i) {
|
||||
Node* child = NS_REINTERPRET_CAST(Node*, current->GetElementAt(i));
|
||||
|
||||
PRInt32 childcount = child->GetSubTreeSize();
|
||||
if (PRInt32(aIndex) < childcount) {
|
||||
next = child;
|
||||
break;
|
||||
}
|
||||
else {
|
||||
aIndex -= childcount;
|
||||
}
|
||||
}
|
||||
|
||||
if (! next) {
|
||||
NS_ERROR("corrupted");
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
current = next;
|
||||
}
|
||||
|
||||
return current->GetElementAt(aIndex);
|
||||
}
|
||||
|
||||
|
||||
PRInt32
|
||||
nsVoidBTree::IndexOf(void* aPossibleElement) const
|
||||
{
|
||||
NS_PRECONDITION((PRWord(aPossibleElement) & ~kRoot_PointerMask) == 0,
|
||||
"uh oh, someone wants to use the pointer bits");
|
||||
|
||||
NS_PRECONDITION(aPossibleElement != nsnull, "nsVoidBTree can't handle null elements");
|
||||
if (aPossibleElement == nsnull)
|
||||
return -1;
|
||||
|
||||
PRInt32 result = 0;
|
||||
ConstIterator last = Last();
|
||||
for (ConstIterator element = First(); element != last; ++element, ++result) {
|
||||
if (aPossibleElement == *element)
|
||||
return result;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
PRBool
|
||||
nsVoidBTree::InsertElementAt(void* aElement, PRInt32 aIndex)
|
||||
{
|
||||
NS_PRECONDITION((PRWord(aElement) & ~kRoot_PointerMask) == 0,
|
||||
"uh oh, someone wants to use the pointer bits");
|
||||
|
||||
if ((PRWord(aElement) & ~kRoot_PointerMask) != 0)
|
||||
return PR_FALSE;
|
||||
|
||||
NS_PRECONDITION(aElement != nsnull, "nsVoidBTree can't handle null elements");
|
||||
if (aElement == nsnull)
|
||||
return PR_FALSE;
|
||||
|
||||
PRInt32 count = Count();
|
||||
|
||||
if (aIndex < 0 || aIndex > count)
|
||||
return PR_FALSE;
|
||||
|
||||
nsresult rv;
|
||||
|
||||
if (IsSingleElement()) {
|
||||
// We're only a single element holder, and haven't yet
|
||||
// "faulted" to create the btree.
|
||||
|
||||
if (count == 0) {
|
||||
// If we have *no* elements, then just set the root
|
||||
// pointer and we're done.
|
||||
mRoot = PRWord(aElement);
|
||||
return PR_TRUE;
|
||||
}
|
||||
|
||||
// If we already had an element, and now we're adding
|
||||
// another. Fault and start creating the btree.
|
||||
void* element = NS_REINTERPRET_CAST(void*, mRoot & kRoot_PointerMask);
|
||||
|
||||
Node* newroot;
|
||||
rv = Node::Create(Node::eType_Data, kDataCapacity, &newroot);
|
||||
if (NS_FAILED(rv)) return PR_FALSE;
|
||||
|
||||
newroot->InsertElementAt(element, 0);
|
||||
newroot->SetSubTreeSize(1);
|
||||
SetRoot(newroot);
|
||||
}
|
||||
|
||||
Path path;
|
||||
|
||||
Node* current = NS_REINTERPRET_CAST(Node*, mRoot & kRoot_PointerMask);
|
||||
while (current->GetType() != Node::eType_Data) {
|
||||
// We're still in the index. Find the right leaf.
|
||||
Node* next = nsnull;
|
||||
|
||||
PRInt32 count = current->GetCount();
|
||||
for (PRInt32 i = 0; i < count; ++i) {
|
||||
Node* child = NS_REINTERPRET_CAST(Node*, current->GetElementAt(i));
|
||||
|
||||
PRInt32 childcount = child->GetSubTreeSize();
|
||||
if (PRInt32(aIndex) <= childcount) {
|
||||
rv = path.Push(current, i + 1);
|
||||
if (NS_FAILED(rv)) return PR_FALSE;
|
||||
|
||||
next = child;
|
||||
break;
|
||||
}
|
||||
else {
|
||||
aIndex -= childcount;
|
||||
}
|
||||
}
|
||||
|
||||
if (! next) {
|
||||
NS_ERROR("corrupted");
|
||||
return PR_FALSE;
|
||||
}
|
||||
|
||||
current = next;
|
||||
}
|
||||
|
||||
if (current->GetCount() >= kDataCapacity) {
|
||||
// We just blew the data node's buffer. Create another
|
||||
// datanode and split.
|
||||
rv = Split(path, current, aElement, aIndex);
|
||||
if (NS_FAILED(rv)) return PR_FALSE;
|
||||
}
|
||||
else {
|
||||
current->InsertElementAt(aElement, aIndex);
|
||||
current->SetSubTreeSize(current->GetSubTreeSize() + 1);
|
||||
}
|
||||
|
||||
while (path.Length() > 0) {
|
||||
PRInt32 index;
|
||||
path.Pop(¤t, &index);
|
||||
current->SetSubTreeSize(current->GetSubTreeSize() + 1);
|
||||
}
|
||||
|
||||
#ifdef PARANOID
|
||||
Verify(NS_REINTERPRET_CAST(Node*, mRoot & kRoot_PointerMask));
|
||||
#endif
|
||||
|
||||
return PR_TRUE;
|
||||
}
|
||||
|
||||
PRBool
|
||||
nsVoidBTree::ReplaceElementAt(void* aElement, PRInt32 aIndex)
|
||||
{
|
||||
NS_PRECONDITION((PRWord(aElement) & ~kRoot_PointerMask) == 0,
|
||||
"uh oh, someone wants to use the pointer bits");
|
||||
|
||||
if ((PRWord(aElement) & ~kRoot_PointerMask) != 0)
|
||||
return PR_FALSE;
|
||||
|
||||
NS_PRECONDITION(aElement != nsnull, "nsVoidBTree can't handle null elements");
|
||||
if (aElement == nsnull)
|
||||
return PR_FALSE;
|
||||
|
||||
if (aIndex < 0 || aIndex >= Count())
|
||||
return PR_FALSE;
|
||||
|
||||
if (IsSingleElement()) {
|
||||
mRoot = PRWord(aElement);
|
||||
return PR_TRUE;
|
||||
}
|
||||
|
||||
Node* current = NS_REINTERPRET_CAST(Node*, mRoot & kRoot_PointerMask);
|
||||
while (current->GetType() != Node::eType_Data) {
|
||||
// We're still in the index. Find the right leaf.
|
||||
Node* next = nsnull;
|
||||
|
||||
PRInt32 count = current->GetCount();
|
||||
for (PRInt32 i = 0; i < count; ++i) {
|
||||
Node* child = NS_REINTERPRET_CAST(Node*, current->GetElementAt(i));
|
||||
|
||||
PRInt32 childcount = child->GetSubTreeSize();
|
||||
if (PRInt32(aIndex) < childcount) {
|
||||
next = child;
|
||||
break;
|
||||
}
|
||||
else {
|
||||
aIndex -= childcount;
|
||||
}
|
||||
}
|
||||
|
||||
if (! next) {
|
||||
NS_ERROR("corrupted");
|
||||
return PR_FALSE;
|
||||
}
|
||||
|
||||
current = next;
|
||||
}
|
||||
|
||||
current->SetElementAt(aElement, aIndex);
|
||||
return PR_TRUE;
|
||||
}
|
||||
|
||||
PRBool
|
||||
nsVoidBTree::RemoveElement(void* aElement)
|
||||
{
|
||||
PRInt32 index = IndexOf(aElement);
|
||||
return (index >= 0) ? RemoveElementAt(index) : PR_FALSE;
|
||||
}
|
||||
|
||||
PRBool
|
||||
nsVoidBTree::RemoveElementAt(PRInt32 aIndex)
|
||||
{
|
||||
PRInt32 count = Count();
|
||||
|
||||
if (aIndex < 0 || aIndex >= count)
|
||||
return PR_FALSE;
|
||||
|
||||
if (IsSingleElement()) {
|
||||
// We're removing the one and only element
|
||||
mRoot = 0;
|
||||
return PR_TRUE;
|
||||
}
|
||||
|
||||
// We've got more than one element, and we're removing it.
|
||||
nsresult rv;
|
||||
Path path;
|
||||
|
||||
Node* root = NS_REINTERPRET_CAST(Node*, mRoot & kRoot_PointerMask);
|
||||
|
||||
Node* current = root;
|
||||
while (current->GetType() != Node::eType_Data) {
|
||||
// We're still in the index. Find the right leaf.
|
||||
Node* next = nsnull;
|
||||
|
||||
PRInt32 count = current->GetCount();
|
||||
for (PRInt32 i = 0; i < count; ++i) {
|
||||
Node* child = NS_REINTERPRET_CAST(Node*, current->GetElementAt(i));
|
||||
|
||||
PRInt32 childcount = child->GetSubTreeSize();
|
||||
if (PRInt32(aIndex) < childcount) {
|
||||
rv = path.Push(current, i);
|
||||
if (NS_FAILED(rv)) return PR_FALSE;
|
||||
|
||||
next = child;
|
||||
break;
|
||||
}
|
||||
else {
|
||||
aIndex -= childcount;
|
||||
}
|
||||
}
|
||||
|
||||
if (! next) {
|
||||
NS_ERROR("corrupted");
|
||||
return PR_FALSE;
|
||||
}
|
||||
|
||||
current = next;
|
||||
}
|
||||
|
||||
current->RemoveElementAt(aIndex);
|
||||
|
||||
while ((current->GetCount() == 0) && (current != root)) {
|
||||
Node* doomed = current;
|
||||
|
||||
PRInt32 index;
|
||||
path.Pop(¤t, &index);
|
||||
current->RemoveElementAt(index);
|
||||
|
||||
Node::Destroy(doomed);
|
||||
}
|
||||
|
||||
current->SetSubTreeSize(current->GetSubTreeSize() - 1);
|
||||
|
||||
while (path.Length() > 0) {
|
||||
PRInt32 index;
|
||||
path.Pop(¤t, &index);
|
||||
current->SetSubTreeSize(current->GetSubTreeSize() - 1);
|
||||
}
|
||||
|
||||
while ((root->GetType() == Node::eType_Index) && (root->GetCount() == 1)) {
|
||||
Node* doomed = root;
|
||||
root = NS_REINTERPRET_CAST(Node*, root->GetElementAt(0));
|
||||
SetRoot(root);
|
||||
Node::Destroy(doomed);
|
||||
}
|
||||
|
||||
#ifdef PARANOID
|
||||
Verify(root);
|
||||
#endif
|
||||
|
||||
return PR_TRUE;
|
||||
}
|
||||
|
||||
void
|
||||
nsVoidBTree::Clear(void)
|
||||
{
|
||||
if (IsEmpty())
|
||||
return;
|
||||
|
||||
if (! IsSingleElement()) {
|
||||
Node* root = NS_REINTERPRET_CAST(Node*, mRoot & kRoot_PointerMask);
|
||||
|
||||
#ifdef PARANOID
|
||||
Dump(root, 0);
|
||||
#endif
|
||||
|
||||
DestroySubtree(root);
|
||||
}
|
||||
|
||||
mRoot = 0;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
nsVoidBTree::Compact(void)
|
||||
{
|
||||
// XXX We could go through and try to merge datanodes.
|
||||
}
|
||||
|
||||
PRBool
|
||||
nsVoidBTree::EnumerateForwards(EnumFunc aFunc, void* aData) const
|
||||
{
|
||||
PRBool running = PR_TRUE;
|
||||
|
||||
ConstIterator last = Last();
|
||||
for (ConstIterator element = First(); running && element != last; ++element)
|
||||
running = (*aFunc)(*element, aData);
|
||||
|
||||
return running;
|
||||
}
|
||||
|
||||
PRBool
|
||||
nsVoidBTree::EnumerateBackwards(EnumFunc aFunc, void* aData) const
|
||||
{
|
||||
PRBool running = PR_TRUE;
|
||||
|
||||
ConstIterator element = Last();
|
||||
ConstIterator first = First();
|
||||
|
||||
if (element != first) {
|
||||
do {
|
||||
running = (*aFunc)(*--element, aData);
|
||||
} while (running && element != first);
|
||||
}
|
||||
|
||||
return running;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
nsresult
|
||||
nsVoidBTree::Split(Path& path, Node* aOldNode, void* aElementToInsert, PRInt32 aSplitIndex)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
PRInt32 capacity = (aOldNode->GetType() == Node::eType_Data) ? kDataCapacity : kIndexCapacity;
|
||||
PRInt32 delta = 0;
|
||||
|
||||
|
||||
Node* newnode;
|
||||
rv = Node::Create(aOldNode->GetType(), capacity, &newnode);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
if (aSplitIndex == capacity) {
|
||||
// If aSplitIndex is the same as the capacity of the node,
|
||||
// then there'll be nothing to copy from the old node to the
|
||||
// new node, and the element is really meant to be inserted in
|
||||
// the newnode. In that case, do it _now_ so that newnode's
|
||||
// subtree size will be correct.
|
||||
newnode->InsertElementAt(aElementToInsert, 0);
|
||||
|
||||
if (newnode->GetType() == Node::eType_Data) {
|
||||
newnode->SetSubTreeSize(1);
|
||||
}
|
||||
else {
|
||||
Node* child = NS_REINTERPRET_CAST(Node*, aElementToInsert);
|
||||
newnode->SetSubTreeSize(child->GetSubTreeSize());
|
||||
}
|
||||
}
|
||||
else {
|
||||
// We're meant to insert the element into the oldnode at
|
||||
// aSplitIndex. Copy data from aOldNode to the newnode but
|
||||
// _don't_ insert newnode yet. We may need to recursively
|
||||
// split parents, an operation that allocs, and hence, may
|
||||
// fail. If it does fail, we wan't to not screw up the
|
||||
// existing datastructure.
|
||||
//
|
||||
// Note that it should be the case that count == capacity, but
|
||||
// who knows, we may decide at some point to prematurely split
|
||||
// nodes for some reason or another.
|
||||
PRInt32 count = aOldNode->GetCount();
|
||||
PRInt32 i = aSplitIndex;
|
||||
PRInt32 j = 0;
|
||||
|
||||
newnode->SetCount(count - aSplitIndex);
|
||||
while (i < count) {
|
||||
if (aOldNode->GetType() == Node::eType_Data) {
|
||||
++delta;
|
||||
}
|
||||
else {
|
||||
Node* migrating = NS_REINTERPRET_CAST(Node*, aOldNode->GetElementAt(i));
|
||||
delta += migrating->GetSubTreeSize();
|
||||
}
|
||||
|
||||
newnode->SetElementAt(aOldNode->GetElementAt(i), j);
|
||||
++i;
|
||||
++j;
|
||||
}
|
||||
newnode->SetSubTreeSize(delta);
|
||||
}
|
||||
|
||||
// Now we split the node.
|
||||
|
||||
if (path.Length() == 0) {
|
||||
// We made it all the way up to the root! Ok, so, create a new
|
||||
// root
|
||||
Node* newroot;
|
||||
rv = Node::Create(Node::eType_Index, kIndexCapacity, &newroot);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
newroot->SetCount(2);
|
||||
newroot->SetElementAt(aOldNode, 0);
|
||||
newroot->SetElementAt(newnode, 1);
|
||||
newroot->SetSubTreeSize(aOldNode->GetSubTreeSize() + 1);
|
||||
SetRoot(newroot);
|
||||
}
|
||||
else {
|
||||
// Otherwise, use the "path" to pop off the next thing above us.
|
||||
Node* parent;
|
||||
PRInt32 indx;
|
||||
path.Pop(&parent, &indx);
|
||||
|
||||
if (parent->GetCount() >= kIndexCapacity) {
|
||||
// Parent is full, too. Recursively split it.
|
||||
rv = Split(path, parent, newnode, indx);
|
||||
if (NS_FAILED(rv)) {
|
||||
Node::Destroy(newnode);
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Room in the parent, so just smack it on up there.
|
||||
parent->InsertElementAt(newnode, indx);
|
||||
parent->SetSubTreeSize(parent->GetSubTreeSize() + 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Now, since all our operations that might fail have finished, we
|
||||
// can go ahead and monkey with the old node.
|
||||
|
||||
if (aSplitIndex == capacity) {
|
||||
PRInt32 nodeslost = newnode->GetSubTreeSize() - 1;
|
||||
PRInt32 subtreesize = aOldNode->GetSubTreeSize() - nodeslost;
|
||||
aOldNode->SetSubTreeSize(subtreesize);
|
||||
}
|
||||
else {
|
||||
aOldNode->SetCount(aSplitIndex);
|
||||
aOldNode->InsertElementAt(aElementToInsert, aSplitIndex);
|
||||
PRInt32 subtreesize = aOldNode->GetSubTreeSize() - delta + 1;
|
||||
aOldNode->SetSubTreeSize(subtreesize);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
PRInt32
|
||||
nsVoidBTree::Verify(Node* aNode)
|
||||
{
|
||||
// Sanity check the tree by verifying that the subtree sizes all
|
||||
// add up correctly.
|
||||
if (aNode->GetType() == Node::eType_Data) {
|
||||
NS_ASSERTION(aNode->GetCount() == aNode->GetSubTreeSize(), "corrupted");
|
||||
return aNode->GetCount();
|
||||
}
|
||||
else {
|
||||
PRInt32 childcount = 0;
|
||||
for (PRInt32 i = 0; i < aNode->GetCount(); ++i) {
|
||||
Node* child = NS_REINTERPRET_CAST(Node*, aNode->GetElementAt(i));
|
||||
childcount += Verify(child);
|
||||
}
|
||||
|
||||
NS_ASSERTION(childcount == aNode->GetSubTreeSize(), "corrupted");
|
||||
return childcount;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
nsVoidBTree::DestroySubtree(Node* aNode)
|
||||
{
|
||||
PRInt32 count = aNode->GetCount() - 1;
|
||||
while (count >= 0) {
|
||||
if (aNode->GetType() == Node::eType_Index)
|
||||
DestroySubtree(NS_REINTERPRET_CAST(Node*, aNode->GetElementAt(count)));
|
||||
|
||||
--count;
|
||||
}
|
||||
|
||||
Node::Destroy(aNode);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
void
|
||||
nsVoidBTree::Dump(Node* aNode, PRInt32 aIndent)
|
||||
{
|
||||
for (PRInt32 i = 0; i < aIndent; ++i)
|
||||
printf(" ");
|
||||
|
||||
if (aNode->GetType() == Node::eType_Data) {
|
||||
printf("data(%d/%d)\n", aNode->GetCount(), aNode->GetSubTreeSize());
|
||||
}
|
||||
else {
|
||||
printf("index(%d/%d)\n", aNode->GetCount(), aNode->GetSubTreeSize());
|
||||
for (PRInt32 j = 0; j < aNode->GetCount(); ++j)
|
||||
Dump(NS_REINTERPRET_CAST(Node*, aNode->GetElementAt(j)), aIndent + 1);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
//
|
||||
// nsVoidBTree::ConstIterator methods
|
||||
//
|
||||
|
||||
void
|
||||
nsVoidBTree::ConstIterator::Next()
|
||||
{
|
||||
if (mIsSingleton) {
|
||||
++mElement;
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise we're a real b-tree iterator, and we need to pull and
|
||||
// pop our path stack appropriately to gyrate into the right
|
||||
// position.
|
||||
while (1) {
|
||||
Node* current;
|
||||
PRInt32 index;
|
||||
mPath.Pop(¤t, &index);
|
||||
|
||||
PRInt32 count = current->GetCount();
|
||||
|
||||
NS_ASSERTION(index < count, "ran off the end, pal");
|
||||
|
||||
if (++index >= count) {
|
||||
// XXXwaterson Oh, this is so ugly. I wish I was smart
|
||||
// enough to figure out a prettier way to do it.
|
||||
//
|
||||
// See if we've just iterated past the last element in the
|
||||
// b-tree, and now need to leave ourselves in the magical
|
||||
// state that is equal to nsVoidBTree::Last().
|
||||
if (current->GetType() == Node::eType_Data) {
|
||||
PRBool rightmost = PR_TRUE;
|
||||
for (PRInt32 slot = mPath.mTop - 1; slot >= 0; --slot) {
|
||||
const Path::Link& link = mPath.mLink[slot];
|
||||
if (link.mIndex != link.mNode->GetCount() - 1) {
|
||||
rightmost = PR_FALSE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (rightmost) {
|
||||
// It's the last one. Make the path look exactly
|
||||
// like nsVoidBTree::Last().
|
||||
mPath.Push(current, index);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, we just ran off the end of a "middling"
|
||||
// node. Loop around, to pop back up the b-tree to its
|
||||
// parent.
|
||||
continue;
|
||||
}
|
||||
|
||||
// We're somewhere in the middle. Push the new location onto
|
||||
// the stack.
|
||||
mPath.Push(current, index);
|
||||
|
||||
// If we're in a data node, we're done: break out of the loop
|
||||
// here leaving the top of the stack pointing to the next data
|
||||
// element in the b-tree.
|
||||
if (current->GetType() == Node::eType_Data)
|
||||
break;
|
||||
|
||||
// Otherwise, we're still in an index node. Push next node
|
||||
// down onto the stack, starting "one off" to the left, and
|
||||
// continue around.
|
||||
mPath.Push(NS_STATIC_CAST(Node*, current->GetElementAt(index)), -1);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsVoidBTree::ConstIterator::Prev()
|
||||
{
|
||||
if (mIsSingleton) {
|
||||
--mElement;
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise we're a real b-tree iterator, and we need to pull and
|
||||
// pop our path stack appropriately to gyrate into the right
|
||||
// position. This is just like nsVoidBTree::ConstIterator::Next(),
|
||||
// but in reverse.
|
||||
while (1) {
|
||||
Node* current;
|
||||
PRInt32 index;
|
||||
mPath.Pop(¤t, &index);
|
||||
|
||||
NS_ASSERTION(index >= 0, "ran off the front, pal");
|
||||
|
||||
if (--index < 0)
|
||||
continue;
|
||||
|
||||
mPath.Push(current, index);
|
||||
|
||||
if (current->GetType() == Node::eType_Data)
|
||||
break;
|
||||
|
||||
current = NS_STATIC_CAST(Node*, current->GetElementAt(index));
|
||||
mPath.Push(current, current->GetCount());
|
||||
}
|
||||
}
|
||||
|
||||
nsVoidBTree::ConstIterator
|
||||
nsVoidBTree::First() const
|
||||
{
|
||||
// A singleton nsVoidBTree::ConstIterator is special-cased, and
|
||||
// will just to integer arithmetic to the word value that's the
|
||||
// pointer.
|
||||
if (IsSingleElement()) {
|
||||
return ConstIterator(mRoot & kRoot_PointerMask);
|
||||
}
|
||||
|
||||
// Othewise, we'll need to build a path along the left-hand side
|
||||
// of the b-tree.
|
||||
Path path;
|
||||
Node* current = NS_REINTERPRET_CAST(Node*, mRoot & kRoot_PointerMask);
|
||||
|
||||
while (1) {
|
||||
path.Push(current, 0);
|
||||
|
||||
if (current->GetType() == Node::eType_Data)
|
||||
break;
|
||||
|
||||
current = NS_STATIC_CAST(Node*, current->GetElementAt(0));
|
||||
}
|
||||
|
||||
return ConstIterator(path);
|
||||
}
|
||||
|
||||
|
||||
nsVoidBTree::ConstIterator
|
||||
nsVoidBTree::Last() const
|
||||
{
|
||||
// A singleton nsVoidBTree::ConstIterator is special-cased, and
|
||||
// will just to integer arithmetic to the word value that's the
|
||||
// pointer. So we'll take the root (if we have one) and add one to
|
||||
// it.
|
||||
if (IsSingleElement()) {
|
||||
return ConstIterator(mRoot ? (mRoot & kRoot_PointerMask) + 1 : 0);
|
||||
}
|
||||
|
||||
// Otherwise, we'll need to build a path along the right-hand side
|
||||
// of the b-tree.
|
||||
Path path;
|
||||
Node* current = NS_REINTERPRET_CAST(Node*, mRoot & kRoot_PointerMask);
|
||||
|
||||
while (1) {
|
||||
PRInt32 count = current->GetCount();
|
||||
|
||||
if (current->GetType() == Node::eType_Data) {
|
||||
path.Push(current, count);
|
||||
break;
|
||||
}
|
||||
else {
|
||||
path.Push(current, count - 1);
|
||||
current = NS_STATIC_CAST(Node*, current->GetElementAt(count - 1));
|
||||
}
|
||||
}
|
||||
|
||||
return ConstIterator(path);
|
||||
}
|
338
xpcom/ds/nsVoidBTree.h
Normal file
338
xpcom/ds/nsVoidBTree.h
Normal file
@ -0,0 +1,338 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/*
|
||||
* The contents of this file are subject to the Mozilla Public License
|
||||
* Version 1.1 (the "MPL"); you may not use this file except in
|
||||
* compliance with the MPL. You may obtain a copy of the MPL at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the MPL is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the MPL
|
||||
* for the specific language governing rights and limitations under the
|
||||
* MPL.
|
||||
*
|
||||
* The Initial Developer of this code under the MPL is Netscape
|
||||
* Communications Corporation. Portions created by Netscape are
|
||||
* Copyright (C) 1999 Netscape Communications Corporation. All Rights
|
||||
* Reserved.
|
||||
*
|
||||
* Original Author:
|
||||
* Chris Waterson <waterson@netscape.com>
|
||||
*/
|
||||
|
||||
#ifndef nsVoidBTree_h__
|
||||
#define nsVoidBTree_h__
|
||||
|
||||
#include "nscore.h"
|
||||
#include "nsDebug.h"
|
||||
#include "nsError.h"
|
||||
|
||||
/**
|
||||
* An nsVoidArray-compatible class that is implemented as a B-tree
|
||||
* rather than an array.
|
||||
*/
|
||||
class NS_COM nsVoidBTree
|
||||
{
|
||||
public:
|
||||
nsVoidBTree() : mRoot(0) {}
|
||||
|
||||
virtual ~nsVoidBTree() { Clear(); }
|
||||
|
||||
nsVoidBTree(const nsVoidBTree& aOther);
|
||||
|
||||
nsVoidBTree& operator=(const nsVoidBTree& aOther);
|
||||
|
||||
PRInt32 Count() const;
|
||||
|
||||
void* ElementAt(PRInt32 aIndex) const;
|
||||
|
||||
void* operator[](PRInt32 aIndex) const {
|
||||
return ElementAt(aIndex); }
|
||||
|
||||
PRInt32 IndexOf(void* aPossibleElement) const;
|
||||
|
||||
PRBool InsertElementAt(void* aElement, PRInt32 aIndex);
|
||||
|
||||
PRBool ReplaceElementAt(void* aElement, PRInt32 aIndex);
|
||||
|
||||
PRBool AppendElement(void* aElement) {
|
||||
return InsertElementAt(aElement, Count()); }
|
||||
|
||||
PRBool RemoveElement(void* aElement);
|
||||
|
||||
PRBool RemoveElementAt(PRInt32 aIndex);
|
||||
|
||||
void Clear();
|
||||
|
||||
void Compact();
|
||||
|
||||
/**
|
||||
* Enumerator callback function. Return PR_FALSE to stop
|
||||
*/
|
||||
typedef PRBool (*PR_CALLBACK EnumFunc)(void* aElement, void *aData);
|
||||
|
||||
PRBool EnumerateForwards(EnumFunc aFunc, void* aData) const;
|
||||
PRBool EnumerateBackwards(EnumFunc aFunc, void* aData) const;
|
||||
|
||||
protected:
|
||||
// This is as deep as a tree can ever grow, mostly because we use an
|
||||
// automatic variable to keep track of the path we take through the
|
||||
// tree while inserting and removing stuff.
|
||||
enum { kMaxDepth = 7 };
|
||||
|
||||
//----------------------------------------
|
||||
|
||||
/**
|
||||
* A node in the b-tree, either data or index
|
||||
*/
|
||||
class Node {
|
||||
public:
|
||||
enum Type { eType_Data = 0, eType_Index = 1 };
|
||||
|
||||
enum {
|
||||
kTypeBit = 1 << 31,
|
||||
kCountMask = ~kTypeBit,
|
||||
kMaxCapacity = kCountMask
|
||||
};
|
||||
|
||||
protected:
|
||||
/**
|
||||
* High bit is the type (data or index), low bit is the
|
||||
* number of elements in the node
|
||||
*/
|
||||
PRInt32 mBits;
|
||||
|
||||
/**
|
||||
* Cached size of this node's subtree
|
||||
*/
|
||||
PRInt32 mSubTreeSize;
|
||||
|
||||
/**
|
||||
* This node's data; when a Node allocated, this is actually
|
||||
* filled in to contain kDataCapacity or kIndexCapacity slots.
|
||||
*/
|
||||
void* mData[1];
|
||||
|
||||
public:
|
||||
static nsresult Create(Type aType, PRInt32 aCapacity, Node** aResult);
|
||||
static nsresult Destroy(Node* aNode);
|
||||
|
||||
Type GetType() {
|
||||
return (mBits & kTypeBit) ? eType_Index : eType_Data; }
|
||||
|
||||
void SetType(Type aType) {
|
||||
if (aType == eType_Data)
|
||||
mBits &= ~kTypeBit;
|
||||
else
|
||||
mBits |= kTypeBit;
|
||||
}
|
||||
|
||||
PRInt32 GetSubTreeSize() { return mSubTreeSize; }
|
||||
void SetSubTreeSize(PRInt32 aSubTreeSize) { mSubTreeSize = aSubTreeSize; }
|
||||
|
||||
PRInt32 GetCount() { return PRInt32(mBits & kCountMask); }
|
||||
void SetCount(PRInt32 aCount) {
|
||||
NS_PRECONDITION(aCount < kMaxCapacity, "overflow");
|
||||
mBits &= ~kCountMask;
|
||||
mBits |= aCount & kCountMask; }
|
||||
|
||||
void* GetElementAt(PRInt32 aIndex) {
|
||||
NS_PRECONDITION(aIndex >= 0 && aIndex < GetCount(), "bad index");
|
||||
return mData[aIndex];
|
||||
}
|
||||
|
||||
void SetElementAt(void* aElement, PRInt32 aIndex) {
|
||||
NS_PRECONDITION(aIndex >= 0 && aIndex < GetCount(), "bad index");
|
||||
mData[aIndex] = aElement;
|
||||
}
|
||||
|
||||
void InsertElementAt(void* aElement, PRInt32 aIndex);
|
||||
void RemoveElementAt(PRInt32 aIndex);
|
||||
|
||||
private:
|
||||
// XXX Not to be implemented
|
||||
Node();
|
||||
~Node();
|
||||
};
|
||||
|
||||
//----------------------------------------
|
||||
class Path;
|
||||
friend class Path;
|
||||
|
||||
/**
|
||||
* A path through the b-tree, used to avoid recursion and
|
||||
* maintain state in iterators.
|
||||
*/
|
||||
class NS_COM Path {
|
||||
public:
|
||||
// XXXwaterson Kinda gross this is all public, but I couldn't
|
||||
// figure out a better way to detect termination in
|
||||
// ConstIterator::Next(). It's a s3kret class anyway...
|
||||
struct Link {
|
||||
Node* mNode;
|
||||
PRInt32 mIndex;
|
||||
|
||||
Link&
|
||||
operator=(const Link& aOther) {
|
||||
mNode = aOther.mNode;
|
||||
mIndex = aOther.mIndex;
|
||||
return *this; }
|
||||
|
||||
PRBool operator==(const Link& aOther) const {
|
||||
return mNode == aOther.mNode && mIndex == aOther.mIndex; }
|
||||
|
||||
PRBool operator!=(const Link& aOther) const {
|
||||
return !aOther.operator==(*this); }
|
||||
};
|
||||
|
||||
Link mLink[kMaxDepth];
|
||||
PRInt32 mTop;
|
||||
|
||||
Path() : mTop(0) {}
|
||||
|
||||
Path(const Path& aOther);
|
||||
Path& operator=(const Path& aOther);
|
||||
|
||||
PRBool operator==(const Path& aOther) const {
|
||||
if (mTop != aOther.mTop)
|
||||
return PR_FALSE;
|
||||
PRInt32 last = mTop - 1;
|
||||
if (last >= 0 && mLink[last] != aOther.mLink[last])
|
||||
return PR_FALSE;
|
||||
return PR_TRUE; }
|
||||
|
||||
PRBool operator!=(const Path& aOther) const {
|
||||
return !aOther.operator==(*this); }
|
||||
|
||||
inline nsresult Push(Node* aNode, PRInt32 aIndex);
|
||||
inline void Pop(Node** aNode, PRInt32* aIndex);
|
||||
|
||||
PRInt32 Length() const { return mTop; }
|
||||
|
||||
Node* TopNode() const { return mLink[mTop - 1].mNode; }
|
||||
PRInt32 TopIndex() const { return mLink[mTop - 1].mIndex; }
|
||||
};
|
||||
|
||||
/**
|
||||
* A tagged pointer: if it's null, the b-tree is empty. If it's
|
||||
* non-null, and the low-bit is clear, it points to a single
|
||||
* element. If it's non-null, and the low-bit is set, it points to
|
||||
* a Node object.
|
||||
*/
|
||||
PRWord mRoot;
|
||||
|
||||
enum {
|
||||
kRoot_TypeBit = 1,
|
||||
kRoot_SingleElement = 0,
|
||||
kRoot_Node = 1,
|
||||
kRoot_PointerMask = ~kRoot_TypeBit
|
||||
};
|
||||
|
||||
// Bit twiddlies
|
||||
PRBool IsEmpty() const { return mRoot == 0; }
|
||||
|
||||
PRBool IsSingleElement() const {
|
||||
return (mRoot & kRoot_TypeBit) == kRoot_SingleElement; }
|
||||
|
||||
void SetRoot(Node* aNode) {
|
||||
mRoot = PRWord(aNode) | kRoot_Node; }
|
||||
|
||||
enum {
|
||||
// XXX Tune? If changed, update kMaxDepth appropriately so
|
||||
// that we can fit 2^31 elements in here.
|
||||
kDataCapacity = 16,
|
||||
kIndexCapacity = 16
|
||||
};
|
||||
|
||||
nsresult Split(Path& path, Node* aOldNode, void* aElementToInsert, PRInt32 aSplitIndex);
|
||||
PRInt32 Verify(Node* aNode);
|
||||
void DestroySubtree(Node* aNode);
|
||||
|
||||
#ifdef DEBUG
|
||||
void Dump(Node* aNode, PRInt32 aIndent);
|
||||
#endif
|
||||
|
||||
public:
|
||||
|
||||
class ConstIterator;
|
||||
friend class ConstIterator;
|
||||
|
||||
/**
|
||||
* A "const" bidirectional iterator over the nsVoidBTree. Supports
|
||||
* the usual iteration interface.
|
||||
*/
|
||||
class NS_COM ConstIterator {
|
||||
protected:
|
||||
PRBool mIsSingleton;
|
||||
PRWord mElement;
|
||||
Path mPath;
|
||||
|
||||
void Next();
|
||||
void Prev();
|
||||
|
||||
public:
|
||||
ConstIterator() : mIsSingleton(PR_TRUE), mElement(nsnull) {}
|
||||
|
||||
ConstIterator(ConstIterator& aOther) : mIsSingleton(aOther.mIsSingleton) {
|
||||
if (mIsSingleton)
|
||||
mElement = aOther.mElement;
|
||||
else
|
||||
mPath = aOther.mPath; }
|
||||
|
||||
ConstIterator&
|
||||
operator=(ConstIterator& aOther) {
|
||||
mIsSingleton = aOther.mIsSingleton;
|
||||
if (mIsSingleton)
|
||||
mElement = aOther.mElement;
|
||||
else
|
||||
mPath = aOther.mPath;
|
||||
return *this; }
|
||||
|
||||
void* operator*() const {
|
||||
return mIsSingleton
|
||||
? NS_REINTERPRET_CAST(void*, mElement)
|
||||
: mPath.TopNode()->GetElementAt(mPath.TopIndex()); }
|
||||
|
||||
ConstIterator& operator++() {
|
||||
Next();
|
||||
return *this; }
|
||||
|
||||
ConstIterator operator++(int) {
|
||||
ConstIterator temp(*this);
|
||||
Next();
|
||||
return temp; }
|
||||
|
||||
ConstIterator& operator--() {
|
||||
Prev();
|
||||
return *this; }
|
||||
|
||||
ConstIterator operator--(int) {
|
||||
ConstIterator temp(*this);
|
||||
Prev();
|
||||
return *this; }
|
||||
|
||||
PRBool operator==(const ConstIterator& aOther) const {
|
||||
return mIsSingleton ? mElement == aOther.mElement
|
||||
: mPath == aOther.mPath; }
|
||||
|
||||
PRBool operator!=(const ConstIterator& aOther) const {
|
||||
return !aOther.operator==(*this); }
|
||||
|
||||
protected:
|
||||
friend class nsVoidBTree;
|
||||
ConstIterator(PRWord aElement) : mIsSingleton(PR_TRUE), mElement(aElement) {}
|
||||
ConstIterator(const Path& aPath) : mIsSingleton(PR_FALSE), mPath(aPath) {}
|
||||
};
|
||||
|
||||
/**
|
||||
* The first element in the nsVoidBTree
|
||||
*/
|
||||
ConstIterator First() const;
|
||||
|
||||
/**
|
||||
* "One past" the last element in the nsVoidBTree
|
||||
*/
|
||||
ConstIterator Last() const;
|
||||
};
|
||||
|
||||
|
||||
#endif // nsVoidBTree_h__
|
Loading…
Reference in New Issue
Block a user