windows-nt/Source/XPSP1/NT/admin/admt/varset/varndx.cpp

534 lines
12 KiB
C++
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*---------------------------------------------------------------------------
File: VarMapIndex.cpp
Comments: Helper class for CMapStringToVar.
CIndexTree implements a sorted, balanced binary tree. This is used by CMapStringToVar
to provide enumeration in sorted order by key.
CIndexTree is currently implemented as a Red-Black tree.
(c) Copyright 1995-1998, Mission Critical Software, Inc., All Rights Reserved
Proprietary and confidential to Mission Critical Software, Inc.
REVISION LOG ENTRY
Revision By: Christy Boles
Revised on 11/19/98 18:17:47
---------------------------------------------------------------------------
*/
#include "stdafx.h"
#include "VarMap.h"
#include "VarNdx.h"
#ifdef STRIPPED_VARSET
#include "NoMcs.h"
#else
#pragma warning (push,3)
#include "McString.h"
#include "McLog.h"
#pragma warning (pop)
using namespace McString;
#endif
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
// Comparison functions used for sorting and searching
int CompareItems(CIndexItem* i1, CIndexItem* i2)
{
ASSERT(i1 && i2);
int result;
result = i1->GetKey().Compare(i2->GetKey());
return result;
}
int CompareStringToItem(CString s, CIndexItem *i)
{
ASSERT(i);
int result;
result = s.Compare(i->GetKey());
return result;
}
int CompareItemsNoCase(CIndexItem* i1, CIndexItem* i2)
{
ASSERT(i1 && i2);
int result;
result = i1->GetKey().CompareNoCase(i2->GetKey());
return result;
}
int CompareStringToItemNoCase(CString s, CIndexItem *i)
{
ASSERT(i);
int result;
// this assumes i->Data is not null
result = s.CompareNoCase(i->GetKey());
return result;
}
CVarData *
CIndexItem::GetValue()
{
if ( pData )
{
return pData->value;
}
else
{
return NULL;
}
}
CString
CIndexItem::GetKey()
{
if ( pData )
{
return pData->key;
}
else
{
return _T("");
}
}
/////////////////////////////////////////////////////////////////////////////
/// Implementation of Red-Black Tree
CIndexItem * // ret- pointer to node in index
CIndexTree::Insert(
CHashItem * data // in - item from hash table
)
{
CIndexItem * item = new CIndexItem(data);
CIndexItem * curr;
CIndexItem * parent;
int compResult=0;
if ( ! m_root )
{
m_root = item;
}
else
{
curr = m_root;
parent = NULL;
while ( curr )
{
parent = curr;
compResult = (*m_Compare)(item,curr);
if ( compResult < 0 )
{
curr = curr->Left();
}
else if ( compResult > 0 )
{
curr = curr->Right();
}
else
{
// The same key should not appear multiple times in the hash table
// this is a bug
ASSERT(FALSE);
delete item;
curr->Data(data);
}
}
if ( ! curr )
{
// The item was not in the tree
ASSERT(compResult!=0);
item->Parent(parent);
// Add the item in the appropriate place
if ( compResult < 0 )
{
parent->Left(item);
}
else
{
parent->Right(item);
}
// now rebalance the tree
CIndexItem * uncle;
BOOL uncleIsRight;
item->Black();
while ( item != m_root && parent->IsRed() )
{
// we don't have to worry about grandparent being null, since parent is red, and
// the root is always black.
// is the parent a left or right child? (algorithm is symmetric)
if ( parent == parent->Parent()->Left() )
{
uncle = parent->Parent()->Right();
uncleIsRight = TRUE;
}
else
{
uncle = parent->Parent()->Left();
uncleIsRight = FALSE;
}
if ( uncleIsRight )
{
if ( uncle && uncle->IsRed() )
{
parent->Black();
uncle->Black();
item = parent->Parent();
item->Red();
}
else if ( item == parent->Right() )
{
item = parent;
LeftRotate(item);
}
parent->Black();
parent->Parent()->Red();
RightRotate(parent->Parent());
}
else // same as above, except swap left and right
{
if ( uncle && uncle->IsRed() )
{
parent->Black();
uncle->Black();
item = parent->Parent();
item->Red();
}
else if ( item == parent->Left() )
{
item = parent;
RightRotate(item);
}
parent->Black();
parent->Parent()->Red();
LeftRotate(parent->Parent());
}
}
}
}
m_root->Black(); // see, the root is always black
return item;
}
void
CIndexTree::RightRotate(
CIndexItem * item // in - item to rotate from
)
{
CIndexItem * y = item->Right();
if ( y )
{
// turn y's left subtree into x's right subtree
item->Right(y->Left());
if ( y->Left() )
{
y->Left()->Parent(item);
}
y->Parent(item->Parent()); // link item's parent to y
if (! item->Parent() )
{
m_root = y;
}
else if ( item == item->Parent()->Left() )
{
item->Parent()->Left(y);
}
else
{
item->Parent()->Right(y);
}
// put item on y's left
y->Left(item);
item->Parent(y);
}
}
void
CIndexTree::LeftRotate(
CIndexItem * item // in - item to rotate from
)
{
CIndexItem * y = item->Left();
if ( y )
{
// turn y's right subtree into x's left subtree
item->Left(y->Right());
if ( y->Right() )
{
y->Right()->Parent(item);
}
// link item's parent to y
y->Parent(item->Parent());
if ( ! item->Parent() )
{
m_root = y;
}
else if ( item == item->Parent()->Right() )
{
item->Parent()->Right(y);
}
else
{
item->Parent()->Left(y);
}
// put item on y's right
y->Right(item);
item->Parent(y);
}
}
CIndexItem * // ret- the node immediately preceding the given node
CIndexTree::GetPrevItem(
CIndexItem * item // in - a node in the index tree
) const
{
CIndexItem * curr;
if ( item->Left() )
{
curr = item->Left();
while ( curr->Right() )
{
curr = curr->Right();
}
}
else
{
curr = item;
while ( curr->Parent() && curr->Parent()->Left() == curr )
{
curr = curr->Parent();
}
curr = curr->Parent();
}
return curr;
}
CIndexItem * // ret- the node immediately following the given node
CIndexTree::GetNextItem(
CIndexItem * item // in - a node in the index tree
) const
{
CIndexItem * curr;
if ( item->Right() )
{
curr = item->Right();
while ( curr->Left() )
{
curr = curr->Left();
}
}
else
{
curr = item;
while ( curr->Parent() && curr->Parent()->Right() == curr )
{
curr = curr->Parent();
}
curr = curr->Parent();
}
return curr;
}
void
CIndexTree::RemoveAll()
{
// do a post-order traversal, removing each node
if ( m_root )
{
RemoveHelper(m_root);
m_root = NULL;
}
}
// helper function for removing all items in the tree
void
CIndexTree::RemoveHelper(
CIndexItem * curr // in - current node
)
{
// our tree currently does not support removing a single item, so we'll use a brute force method
// recursively delete children, then delete the current node
if ( curr->Left() )
{
RemoveHelper(curr->Left());
}
if ( curr->Right() )
{
RemoveHelper(curr->Right());
}
delete curr;
}
void
CIndexItem::McLogInternalDiagnostics(CString keyName, int depth)
{
CString key;
CString strLeft;
CString strRight;
CString strParent;
if ( ! keyName.IsEmpty() )
{
key = keyName + ".";
}
if ( pData )
{
key = key + pData->key;
}
else
{
MC_LOG("data is NULL");
}
MC_LOG("address="<<makeStr(this,L"0x%lx") << " pData="<< makeStr(pData,L"0x%lx") << " pLeft="<<makeStr(pLeft,L"0x%lx")<<" pRight="<<makeStr(pRight,L"0x%lx")<< " pParent="<<makeStr(pParent,L"0x%lx") << " red="<<makeStr(red,L"0x%lx") << " depth="<<makeStr(depth));
if ( pLeft )
strLeft = pLeft->GetKey();
if ( pRight )
strRight = pRight->GetKey();
if ( pParent )
strParent = pParent->GetKey();
MC_LOG(" Key=" << String(key) << " Left=" << String(strLeft) << " Right=" << String(strRight) << " Parent="<< String(strParent) );
if ( pLeft )
pLeft->McLogInternalDiagnostics(keyName,depth+1);
if ( pRight )
pRight->McLogInternalDiagnostics(keyName,depth+1);
}
CIndexItem * // ret- smallest node in the index that is >= value
CIndexTree::GetFirstAfter(
CString value // in - string to compare keys to
) const
{
CIndexItem * item = m_root;
CIndexItem * result = NULL;
int cRes;
while ( item )
{
cRes = m_CompareKey(value,item);
if ( ! cRes )
{
break;
}
if ( cRes > 0 )
{
item = item->Left();
}
else
{
result = item;
item = item->Right();
}
}
return result;
}
void CIndexTree::McLogInternalDiagnostics(CString keyName)
{
CString blockname;
blockname = "Index of "+ keyName;
CString compareFn;
CString compareKey;
if ( m_Compare == &CompareItems )
{
compareFn = "CompareItems";
}
else if ( m_Compare == &CompareItemsNoCase )
{
compareFn = "CompareItemsNoCase";
}
else
{
compareFn.Format(_T("Unknown function, address=%lx"),m_Compare);
}
if ( m_CompareKey == &CompareStringToItem )
{
compareKey = "CompareStringToItem";
}
else if ( m_CompareKey == &CompareStringToItemNoCase )
{
compareKey = "CompareStringToItemNoCase";
}
else
{
compareKey.Format(_T("Unknown function, address=%lx"),m_CompareKey);
}
MC_LOG(String(blockname) << " CaseSensitive=" << makeStr(m_CaseSensitive) << " Compare Function="<<String(compareFn)<< "Compare Key Function=" << String(compareKey) );
if ( m_root )
{
MC_LOG("Beginning preorder dump of index");
m_root->McLogInternalDiagnostics(keyName,0);
}
else
{
MC_LOG("Root of index is NULL");
}
}
#ifdef _DEBUG
BOOL CIndexTree::AssertValid(int nItems) const
{
BOOL bValid = TRUE;
int i;
CIndexItem * curr = GetFirstItem();
CIndexItem * prev = NULL;
for ( i = 0 ; i < nItems ; i++ )
{
ASSERT(curr);
if ( prev && curr )
{
ASSERT(m_Compare(prev,curr) <= 0 );
}
prev = curr;
curr = GetNextItem(curr);
}
ASSERT(curr == NULL); // we should have reached the end
for ( i = 0 ; i < nItems -1 ; i++ )
{
prev = GetPrevItem(prev);
ASSERT(prev);
}
ASSERT(prev == GetFirstItem());
return bValid;
}
#endif