/* -*- 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 */ #ifndef nsVoidBTree_h__ #define nsVoidBTree_h__ #include "nscore.h" #include "nsDebug.h" #include "nsError.h" class nsISizeOfHandler; /** * 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; void SizeOf(nsISizeOfHandler* aHandler, PRUint32* aResult) 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 = 8 }; //---------------------------------------- /** * A node in the b-tree, either data or index */ class Node { public: enum Type { eType_Data = 0, eType_Index = 1 }; enum { kTypeBit = 0x80000000, kCountShift = 24, kCountMask = 0x7f000000, kMaxCapacity = kCountMask, kSubTreeSizeMask = 0x00ffffff, kMaxSubTreeSize = kSubTreeSizeMask }; protected: /** * High bit is the type (data or index), next 7 bits are the * number of elements in the node, low 24 bits is the subtree * size. */ PRUint32 mBits; /** * 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() const { return (mBits & kTypeBit) ? eType_Index : eType_Data; } void SetType(Type aType) { if (aType == eType_Data) mBits &= ~kTypeBit; else mBits |= kTypeBit; } PRInt32 GetSubTreeSize() const { return PRInt32(mBits & kSubTreeSizeMask); } void SetSubTreeSize(PRInt32 aSubTreeSize) { mBits &= ~kSubTreeSizeMask; mBits |= PRUint32(aSubTreeSize) & kSubTreeSizeMask; } PRInt32 GetCount() const { return PRInt32((mBits & kCountMask) >> kCountShift); } void SetCount(PRInt32 aCount) { NS_PRECONDITION(aCount < PRInt32(kMaxCapacity), "overflow"); mBits &= ~kCountMask; mBits |= (PRUint32(aCount) << kCountShift) & kCountMask; } void* GetElementAt(PRInt32 aIndex) const { NS_PRECONDITION(aIndex >= 0 && aIndex < GetCount(), "bad index"); return mData[aIndex]; } 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); protected: // XXX Not to be implemented Node(); ~Node(); }; //---------------------------------------- class Path; friend class Path; struct Link; friend struct Link; // 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. This isn't // contained inside nsVoidBTree::Path because doing so breaks some // compilers. 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); } }; /** * A path through the b-tree, used to avoid recursion and * maintain state in iterators. */ class NS_COM Path { public: 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 { // This is tuned based on distribution data from nsVoidArray // that indicated that, during a "normal run" of mozilla, we'd // be able to fit about 90% of the nsVoidArray's contents into // an eight element array. // // If you decide to change these values, update kMaxDepth // appropriately so that we can fit kMaxSubTreeSize elements // in here. kDataCapacity = 8, kIndexCapacity = 8 }; 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; class Iterator; friend class Iterator; /** * A "const" bidirectional iterator over the nsVoidBTree. Supports * the usual iteration interface. */ class NS_COM ConstIterator { protected: friend class Iterator; // XXXwaterson broken PRPackedBool mIsSingleton; PRPackedBool mIsExhausted; PRWord mElement; Path mPath; void Next(); void Prev(); public: ConstIterator() : mIsSingleton(PR_TRUE), mElement(nsnull) {} ConstIterator(const ConstIterator& aOther) : mIsSingleton(aOther.mIsSingleton) { if (mIsSingleton) { mElement = aOther.mElement; mIsExhausted = aOther.mIsExhausted; } else { mPath = aOther.mPath; } } ConstIterator& operator=(const ConstIterator& aOther) { mIsSingleton = aOther.mIsSingleton; if (mIsSingleton) { mElement = aOther.mElement; mIsExhausted = aOther.mIsExhausted; } else { mPath = aOther.mPath; } return *this; } void* operator*() const { return mIsSingleton ? NS_REINTERPRET_CAST(void*, !mIsExhausted ? mElement : 0) : 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 temp; } PRBool operator==(const ConstIterator& aOther) const { return mIsSingleton ? (mElement == aOther.mElement && mIsExhausted == aOther.mIsExhausted) : mPath == aOther.mPath; } PRBool operator!=(const ConstIterator& aOther) const { return !aOther.operator==(*this); } protected: friend class nsVoidBTree; ConstIterator(PRWord aElement, PRBool aIsExhausted) : mIsSingleton(PR_TRUE), mIsExhausted(aElement ? aIsExhausted : PR_TRUE), mElement(aElement) {} ConstIterator(const Path& aPath) : mIsSingleton(PR_FALSE), mPath(aPath) {} }; /** * The first element in the nsVoidBTree */ ConstIterator First() const { return IsSingleElement() ? ConstIterator(mRoot, PR_FALSE) : ConstIterator(LeftMostPath()); } /** * "One past" the last element in the nsVoidBTree */ ConstIterator Last() const { return IsSingleElement() ? ConstIterator(mRoot, PR_TRUE) : ConstIterator(RightMostPath()); } class Iterator : public ConstIterator { protected: void** mElementRef; public: Iterator() {} Iterator(const Iterator& aOther) : ConstIterator(aOther), mElementRef(aOther.mElementRef) {} Iterator& operator=(const Iterator& aOther) { ConstIterator::operator=(aOther); mElementRef = aOther.mElementRef; return *this; } Iterator& operator++() { Next(); return *this; } Iterator operator++(int) { Iterator temp(*this); Next(); return temp; } Iterator& operator--() { Prev(); return *this; } Iterator operator--(int) { Iterator temp(*this); Prev(); return temp; } void*& operator*() const { return mIsSingleton ? (!mIsExhausted ? *mElementRef : kDummyLast) : mPath.TopNode()->GetElementAt(mPath.TopIndex()); } PRBool operator==(const Iterator& aOther) const { return mIsSingleton ? (mElement == aOther.mElement && mIsExhausted == aOther.mIsExhausted) : mPath == aOther.mPath; } PRBool operator!=(const Iterator& aOther) const { return !aOther.operator==(*this); } protected: Iterator(const Path& aPath) : ConstIterator(aPath) {} Iterator(PRWord* aElementRef, PRBool aIsExhausted) : ConstIterator(*aElementRef, aIsExhausted), mElementRef(NS_REINTERPRET_CAST(void**, aElementRef)) {} friend class nsVoidBTree; }; Iterator First() { return IsSingleElement() ? Iterator(&mRoot, PR_FALSE) : Iterator(LeftMostPath()); } Iterator Last() { return IsSingleElement() ? Iterator(&mRoot, PR_TRUE) : Iterator(RightMostPath()); } protected: const Path LeftMostPath() const; const Path RightMostPath() const; static void* kDummyLast; }; #endif // nsVoidBTree_h__