2000-06-10 09:55:07 +04:00
|
|
|
/* -*- 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"
|
2000-06-10 11:31:09 +04:00
|
|
|
class nsISizeOfHandler;
|
2000-06-10 09:55:07 +04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* 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;
|
|
|
|
|
2001-10-16 09:31:36 +04:00
|
|
|
#ifdef DEBUG
|
2000-06-10 11:31:09 +04:00
|
|
|
void SizeOf(nsISizeOfHandler* aHandler, PRUint32* aResult) const;
|
2001-10-16 09:31:36 +04:00
|
|
|
#endif
|
2000-06-10 11:31:09 +04:00
|
|
|
|
2000-06-10 09:55:07 +04:00
|
|
|
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.
|
2000-06-13 09:16:02 +04:00
|
|
|
enum { kMaxDepth = 8 };
|
2000-06-10 09:55:07 +04:00
|
|
|
|
|
|
|
//----------------------------------------
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A node in the b-tree, either data or index
|
|
|
|
*/
|
|
|
|
class Node {
|
|
|
|
public:
|
|
|
|
enum Type { eType_Data = 0, eType_Index = 1 };
|
|
|
|
|
|
|
|
enum {
|
2000-06-13 09:16:02 +04:00
|
|
|
kTypeBit = 0x80000000,
|
|
|
|
kCountShift = 24,
|
|
|
|
kCountMask = 0x7f000000,
|
|
|
|
kMaxCapacity = kCountMask,
|
|
|
|
kSubTreeSizeMask = 0x00ffffff,
|
|
|
|
kMaxSubTreeSize = kSubTreeSizeMask
|
2000-06-10 09:55:07 +04:00
|
|
|
};
|
|
|
|
|
|
|
|
protected:
|
|
|
|
/**
|
2000-06-13 09:16:02 +04:00
|
|
|
* 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.
|
2000-06-10 09:55:07 +04:00
|
|
|
*/
|
2000-06-13 09:16:02 +04:00
|
|
|
PRUint32 mBits;
|
2000-06-10 09:55:07 +04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* 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);
|
|
|
|
|
2000-06-13 09:16:02 +04:00
|
|
|
Type GetType() const {
|
2000-06-10 09:55:07 +04:00
|
|
|
return (mBits & kTypeBit) ? eType_Index : eType_Data; }
|
|
|
|
|
|
|
|
void SetType(Type aType) {
|
|
|
|
if (aType == eType_Data)
|
|
|
|
mBits &= ~kTypeBit;
|
|
|
|
else
|
|
|
|
mBits |= kTypeBit;
|
|
|
|
}
|
|
|
|
|
2000-06-13 09:16:02 +04:00
|
|
|
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); }
|
2000-06-10 09:55:07 +04:00
|
|
|
|
|
|
|
void SetCount(PRInt32 aCount) {
|
2000-06-13 09:16:02 +04:00
|
|
|
NS_PRECONDITION(aCount < PRInt32(kMaxCapacity), "overflow");
|
2000-06-10 09:55:07 +04:00
|
|
|
mBits &= ~kCountMask;
|
2000-06-13 09:16:02 +04:00
|
|
|
mBits |= (PRUint32(aCount) << kCountShift) & kCountMask; }
|
2000-06-10 09:55:07 +04:00
|
|
|
|
2000-06-13 09:16:02 +04:00
|
|
|
void* GetElementAt(PRInt32 aIndex) const {
|
2000-06-10 09:55:07 +04:00
|
|
|
NS_PRECONDITION(aIndex >= 0 && aIndex < GetCount(), "bad index");
|
2000-06-13 09:16:02 +04:00
|
|
|
return mData[aIndex]; }
|
|
|
|
|
|
|
|
void*& GetElementAt(PRInt32 aIndex) {
|
|
|
|
NS_PRECONDITION(aIndex >= 0 && aIndex < GetCount(), "bad index");
|
|
|
|
return mData[aIndex]; }
|
2000-06-10 09:55:07 +04:00
|
|
|
|
|
|
|
void SetElementAt(void* aElement, PRInt32 aIndex) {
|
|
|
|
NS_PRECONDITION(aIndex >= 0 && aIndex < GetCount(), "bad index");
|
2000-06-13 09:16:02 +04:00
|
|
|
mData[aIndex] = aElement; }
|
2000-06-10 09:55:07 +04:00
|
|
|
|
|
|
|
void InsertElementAt(void* aElement, PRInt32 aIndex);
|
|
|
|
void RemoveElementAt(PRInt32 aIndex);
|
|
|
|
|
2000-06-10 10:05:09 +04:00
|
|
|
protected:
|
2000-06-10 09:55:07 +04:00
|
|
|
// XXX Not to be implemented
|
|
|
|
Node();
|
|
|
|
~Node();
|
|
|
|
};
|
|
|
|
|
|
|
|
//----------------------------------------
|
|
|
|
class Path;
|
|
|
|
friend class Path;
|
|
|
|
|
2000-06-14 07:27:46 +04:00
|
|
|
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); }
|
|
|
|
};
|
|
|
|
|
2000-06-10 09:55:07 +04:00
|
|
|
/**
|
|
|
|
* 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 {
|
2000-06-13 09:16:02 +04:00
|
|
|
// 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
|
2000-06-10 09:55:07 +04:00
|
|
|
};
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
2000-06-13 09:16:02 +04:00
|
|
|
class Iterator;
|
|
|
|
friend class Iterator;
|
|
|
|
|
2000-06-10 09:55:07 +04:00
|
|
|
/**
|
|
|
|
* A "const" bidirectional iterator over the nsVoidBTree. Supports
|
|
|
|
* the usual iteration interface.
|
|
|
|
*/
|
|
|
|
class NS_COM ConstIterator {
|
|
|
|
protected:
|
2000-06-13 09:16:02 +04:00
|
|
|
friend class Iterator; // XXXwaterson broken
|
|
|
|
|
|
|
|
PRPackedBool mIsSingleton;
|
|
|
|
PRPackedBool mIsExhausted;
|
2000-06-10 09:55:07 +04:00
|
|
|
PRWord mElement;
|
|
|
|
Path mPath;
|
|
|
|
|
|
|
|
void Next();
|
|
|
|
void Prev();
|
|
|
|
|
|
|
|
public:
|
|
|
|
ConstIterator() : mIsSingleton(PR_TRUE), mElement(nsnull) {}
|
|
|
|
|
2000-06-10 10:05:09 +04:00
|
|
|
ConstIterator(const ConstIterator& aOther) : mIsSingleton(aOther.mIsSingleton) {
|
2000-06-13 09:16:02 +04:00
|
|
|
if (mIsSingleton) {
|
2000-06-10 09:55:07 +04:00
|
|
|
mElement = aOther.mElement;
|
2000-06-13 09:16:02 +04:00
|
|
|
mIsExhausted = aOther.mIsExhausted;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
mPath = aOther.mPath; } }
|
2000-06-10 09:55:07 +04:00
|
|
|
|
|
|
|
ConstIterator&
|
2000-06-10 10:05:09 +04:00
|
|
|
operator=(const ConstIterator& aOther) {
|
2000-06-10 09:55:07 +04:00
|
|
|
mIsSingleton = aOther.mIsSingleton;
|
2000-06-13 09:16:02 +04:00
|
|
|
if (mIsSingleton) {
|
2000-06-10 09:55:07 +04:00
|
|
|
mElement = aOther.mElement;
|
2000-06-13 09:16:02 +04:00
|
|
|
mIsExhausted = aOther.mIsExhausted;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
mPath = aOther.mPath;
|
|
|
|
}
|
2000-06-10 09:55:07 +04:00
|
|
|
return *this; }
|
|
|
|
|
|
|
|
void* operator*() const {
|
|
|
|
return mIsSingleton
|
2000-06-13 09:16:02 +04:00
|
|
|
? NS_REINTERPRET_CAST(void*, !mIsExhausted ? mElement : 0)
|
2000-06-10 09:55:07 +04:00
|
|
|
: 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();
|
2000-06-10 10:05:09 +04:00
|
|
|
return temp; }
|
2000-06-10 09:55:07 +04:00
|
|
|
|
|
|
|
PRBool operator==(const ConstIterator& aOther) const {
|
2000-06-13 09:16:02 +04:00
|
|
|
return mIsSingleton
|
|
|
|
? (mElement == aOther.mElement && mIsExhausted == aOther.mIsExhausted)
|
2000-06-10 09:55:07 +04:00
|
|
|
: mPath == aOther.mPath; }
|
|
|
|
|
|
|
|
PRBool operator!=(const ConstIterator& aOther) const {
|
|
|
|
return !aOther.operator==(*this); }
|
|
|
|
|
|
|
|
protected:
|
|
|
|
friend class nsVoidBTree;
|
2000-06-13 09:16:02 +04:00
|
|
|
ConstIterator(PRWord aElement, PRBool aIsExhausted)
|
|
|
|
: mIsSingleton(PR_TRUE), mIsExhausted(aElement ? aIsExhausted : PR_TRUE), mElement(aElement) {}
|
|
|
|
|
|
|
|
ConstIterator(const Path& aPath)
|
|
|
|
: mIsSingleton(PR_FALSE), mPath(aPath) {}
|
2000-06-10 09:55:07 +04:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The first element in the nsVoidBTree
|
|
|
|
*/
|
2000-06-13 09:16:02 +04:00
|
|
|
ConstIterator First() const {
|
|
|
|
return IsSingleElement() ? ConstIterator(mRoot, PR_FALSE) : ConstIterator(LeftMostPath()); }
|
2000-06-10 09:55:07 +04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* "One past" the last element in the nsVoidBTree
|
|
|
|
*/
|
2000-06-13 09:16:02 +04:00
|
|
|
ConstIterator Last() const {
|
|
|
|
return IsSingleElement() ? ConstIterator(mRoot, PR_TRUE) : ConstIterator(RightMostPath()); }
|
|
|
|
|
|
|
|
class Iterator : public ConstIterator {
|
|
|
|
protected:
|
2000-06-13 10:32:09 +04:00
|
|
|
void** mElementRef;
|
2000-06-13 09:16:02 +04:00
|
|
|
|
|
|
|
public:
|
2000-06-13 12:02:08 +04:00
|
|
|
Iterator() {}
|
|
|
|
|
|
|
|
Iterator(const Iterator& aOther)
|
|
|
|
: ConstIterator(aOther),
|
|
|
|
mElementRef(aOther.mElementRef) {}
|
|
|
|
|
|
|
|
Iterator&
|
|
|
|
operator=(const Iterator& aOther) {
|
|
|
|
ConstIterator::operator=(aOther);
|
|
|
|
mElementRef = aOther.mElementRef;
|
|
|
|
return *this; }
|
|
|
|
|
2000-06-13 09:16:02 +04:00
|
|
|
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 {
|
2000-06-13 12:02:08 +04:00
|
|
|
return mIsSingleton
|
|
|
|
? (!mIsExhausted ? *mElementRef : kDummyLast)
|
|
|
|
: mPath.TopNode()->GetElementAt(mPath.TopIndex()); }
|
2000-06-13 09:16:02 +04:00
|
|
|
|
|
|
|
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),
|
2000-06-13 10:32:09 +04:00
|
|
|
mElementRef(NS_REINTERPRET_CAST(void**, aElementRef)) {}
|
2000-06-13 09:16:02 +04:00
|
|
|
|
|
|
|
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;
|
|
|
|
|
2000-06-13 10:32:09 +04:00
|
|
|
static void* kDummyLast;
|
2000-06-10 09:55:07 +04:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
#endif // nsVoidBTree_h__
|