gecko-dev/xpcom/ds/nsVoidBTree.h
2000-06-14 03:27:46 +00:00

447 lines
13 KiB
C++

/* -*- 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"
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__