/* -*- 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 */ #include "nsVoidBTree.h" #ifdef DEBUG #include #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 = 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; } 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; 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; } 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; } 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; 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; } 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; } void nsVoidBTree::SizeOf(nsISizeOfHandler* aHandler, PRUint32* aResult) const { if (! aResult) return; *aResult = sizeof(*this); if (IsSingleElement()) return; Path path; path.Push(NS_REINTERPRET_CAST(Node*, mRoot & kRoot_PointerMask), 0); while (path.Length()) { Node* current; PRInt32 index; path.Pop(¤t, &index); if (current->GetType() == Node::eType_Data) { *aResult += sizeof(Node) + (sizeof(void*) * (kDataCapacity - 1)); } else { *aResult += sizeof(Node) + (sizeof(void*) * (kIndexCapacity - 1)); // If we're in an index node, and there are still kids to // traverse, well, traverse 'em. if (index < current->GetCount()) { path.Push(current, index + 1); path.Push(NS_STATIC_CAST(Node*, current->GetElementAt(index)), 0); } } } } //---------------------------------------------------------------------- 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(); } 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 and Iterator methods // void* nsVoidBTree::kDummyLast; void nsVoidBTree::ConstIterator::Next() { if (mIsSingleton) { mIsExhausted = PR_TRUE; 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 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) { mIsExhausted = PR_FALSE; 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()); } } const nsVoidBTree::Path nsVoidBTree::LeftMostPath() const { 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 path; } const nsVoidBTree::Path nsVoidBTree::RightMostPath() const { 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; } path.Push(current, count - 1); current = NS_STATIC_CAST(Node*, current->GetElementAt(count - 1)); } return path; }