windows-nt/Source/XPSP1/NT/enduser/netmeeting/as/cpi32/ch.cpp
2020-09-26 16:20:57 +08:00

2007 lines
51 KiB
C++

#include "precomp.h"
//
// CH.CPP
// Cache Handler
//
// Copyright(c) Microsoft 1997-
//
#define MLZ_FILE_ZONE ZONE_CORE
//
// CACHE HANDLER
//
// The Cache Handler is a generic cache manager that handles blocks of
// memory supplied by the calling component.
//
// Once a cache of a particular size has been created, blocks of memory can
// be added to it (CH_CacheData). The cache can then be searched
// (CH_SearchCache) to try and match the contents of a given block of
// memory with the blocks in the cache.
//
// When a block is added to the cache and the cache is full, one of the
// blocks currently in the cache is discarded on a Least-Recently Used
// (LRU) basis.
//
// The component that creates the cache specifies a callback function which
// is called every time a block is removed from the cache. This allows the
// caller to free up memory blocks when they are no longer in use.
//
//
// FUNCTION: CH_CreateCache
//
BOOL ASHost::CH_CreateCache
(
PCHCACHE * ppCache,
UINT cCacheEntries,
UINT cNumEvictionCategories,
UINT cbNotHashed,
PFNCACHEDEL pfnCacheDel
)
{
UINT cbCacheSize;
UINT i;
PCHCACHE pCache;
DebugEntry(ASHost::CH_CreateCache);
//
// Initialize return value
//
pCache = NULL;
//
// Do a few parameter validation checks.
//
ASSERT((cCacheEntries > 0));
ASSERT((cCacheEntries < CH_MAX_CACHE_ENTRIES));
ASSERT(cNumEvictionCategories > 0);
ASSERT(cNumEvictionCategories <= CH_NUM_EVICTION_CATEGORIES);
//
// Calculate the amount of memory required.
// NOTE that the CHCACHE definition includes one cache entry
//
cbCacheSize = sizeof(CHCACHE) + ((cCacheEntries-1) * sizeof(CHENTRY));
//
// Allocate memory for the cache.
//
pCache = (PCHCACHE)new BYTE[cbCacheSize];
if (pCache == NULL)
{
ERROR_OUT(("Failed to alloc cache"));
DC_QUIT;
}
SET_STAMP(pCache, CHCACHE);
pCache->pRoot = NULL;
pCache->pFirst = NULL;
pCache->pLast= NULL;
pCache->free = 0;
pCache->cEntries = cCacheEntries;
pCache->cNumEvictionCategories = cNumEvictionCategories;
pCache->cbNotHashed = cbNotHashed;
pCache->pfnCacheDel = pfnCacheDel;
//
// Initialize the cache entries
//
for (i = 0; i < cCacheEntries; i++)
{
CHInitEntry(&pCache->Entry[i]);
pCache->Entry[i].free = (WORD)(i+1);
}
pCache->Entry[cCacheEntries-1].free = CH_MAX_CACHE_ENTRIES;
//
// Set up the default eviction category limits. Default is to balance
// at 75% to the high category, 75% of the remainder to the next lower
// and so on
//
for (i = cNumEvictionCategories; i > 0; i--)
{
pCache->iMRUHead[i-1] = CH_MAX_CACHE_ENTRIES;
pCache->iMRUTail[i-1] = CH_MAX_CACHE_ENTRIES;
pCache->cEvictThreshold[i-1] = (WORD)((cCacheEntries*3)/4);
}
DC_EXIT_POINT:
*ppCache = pCache;
DebugExitBOOL(ASHost::CH_CreateCache, (pCache != NULL));
return(pCache != NULL);
}
//
// CH_DestroyCache
// Destroys a created cache, if it is valid.
//
void ASHost::CH_DestroyCache(PCHCACHE pCache)
{
DebugEntry(ASHost::CH_DestroyCache);
ASSERT(IsValidCache(pCache));
//
// Clear the entries in the cache
//
CH_ClearCache(pCache);
//
// Free the memory
//
delete pCache;
DebugExitVOID(ASHost::CH_DestroyCache);
}
//
// FUNCTION: CH_SearchCache
//
BOOL ASHost::CH_SearchCache
(
PCHCACHE pCache,
LPBYTE pData,
UINT cbDataSize,
UINT evictionCategory,
UINT * piCacheEntry
)
{
BOOL rc = FALSE;
UINT checkSum;
DebugEntry(ASHost::CH_SearchCache);
ASSERT(IsValidCache(pCache));
checkSum = CHCheckSum(pData + pCache->cbNotHashed,
cbDataSize - pCache->cbNotHashed);
*piCacheEntry = CHTreeSearch(pCache, checkSum, cbDataSize, pData);
if ( *piCacheEntry != CH_MAX_CACHE_ENTRIES )
{
//
// Found a match
//
CHUpdateMRUList(pCache, *piCacheEntry, evictionCategory);
rc = TRUE;
}
DebugExitBOOL(ASHost::CH_SearchCache, rc);
return(rc);
}
//
// FUNCTION: CH_CacheData
//
UINT ASHost::CH_CacheData
(
PCHCACHE pCache,
LPBYTE pData,
UINT cbDataSize,
UINT evictionCategory
)
{
UINT evictionCount;
UINT iEntry = CH_MAX_CACHE_ENTRIES;
PCHENTRY pEntry;
DebugEntry(ASHost::CH_CacheData);
ASSERT(IsValidCache(pCache));
ASSERT((evictionCategory < pCache->cNumEvictionCategories));
if (!CHFindFreeCacheEntry(pCache, &iEntry, &evictionCount))
{
iEntry = CHEvictLRUCacheEntry(pCache, evictionCategory, evictionCount);
//
// MNM1422: Ideally we would now call CHFindFreeCacheEntry again to
// get the entry freed up by the eviction process - but since we
// have just been returned that entry, we may as well use it to
// improve performance.
//
// However, the processing has left pTreeCacheData->tree.free
// pointing to the entry we have just evicted - which we are about
// to use. So we need to perform the same processing on the free
// list as CHFindFreeCacheEntry would have done, or next time
// through, the first 'free' entry will really be in use, and the
// insert code will assert!
//
ASSERT(pCache->free == iEntry);
pCache->free = pCache->Entry[iEntry].free;
}
pEntry = &pCache->Entry[iEntry];
pEntry->pData = pData;
pEntry->cbData = cbDataSize;
pEntry->checkSum = CHCheckSum(pData + pCache->cbNotHashed,
cbDataSize - pCache->cbNotHashed);
pEntry->evictionCategory = (WORD)evictionCategory;
CHAvlInsert(pCache, pEntry);
TRACE_OUT(( "Cache 0x%08x entry %d checksum 0x%08x data 0x%08x",
pCache, iEntry, pEntry->checkSum, pEntry->pData));
CHUpdateMRUList(pCache, iEntry, evictionCategory);
DebugExitDWORD(ASHost::CH_CacheData, iEntry);
return(iEntry);
}
//
// FUNCTION: CH_SearchAndCacheData
//
BOOL ASHost::CH_SearchAndCacheData
(
PCHCACHE pCache,
LPBYTE pData,
UINT cbDataSize,
UINT evictionCategory,
UINT * piCacheEntry
)
{
UINT checkSum;
UINT i;
BOOL preExisting;
UINT iEntry = CH_MAX_CACHE_ENTRIES;
UINT evictionCount = 0;
PCHENTRY pEntry;
DebugEntry(ASHost::CH_SearchAndCacheData);
ASSERT(IsValidCache(pCache));
ASSERT(evictionCategory < pCache->cNumEvictionCategories);
//
// Does this entry exist?
//
checkSum = CHCheckSum(pData + pCache->cbNotHashed,
cbDataSize - pCache->cbNotHashed);
iEntry = CHTreeSearch(pCache, checkSum, cbDataSize, pData);
if ( iEntry == CH_MAX_CACHE_ENTRIES)
{
preExisting = FALSE;
//
// We didn't find the entry--can we add it?
//
TRACE_OUT(("CACHE: entry not found in cache 0x%08x csum 0x%08x",
pCache, checkSum));
if (!CHFindFreeCacheEntry(pCache, &iEntry, &evictionCount))
{
//
// Nope. Evict an entry
//
iEntry = CHEvictLRUCacheEntry(pCache, evictionCategory, evictionCount);
ASSERT(iEntry != CH_MAX_CACHE_ENTRIES);
TRACE_OUT(("CACHE: no free entries so evicted cache 0x%08x entry %d",
pCache, iEntry));
//
// Ideally we would now call CHFindFreeCacheEntry again to
// get the entry freed up via the eviction process, but since
// we just returned that entry use to to improve perf.
//
// However, the processing has left pCache->free pointing
// to the entry we just evicted and are about to use. So
// we need to fix it up.
//
ASSERT(pCache->free == iEntry);
pCache->free = pCache->Entry[iEntry].free;
}
//
// Fill in this entry's data
//
pEntry = &pCache->Entry[iEntry];
pEntry->pData = pData;
pEntry->cbData = cbDataSize;
pEntry->checkSum = checkSum;
pEntry->evictionCategory = (WORD)evictionCategory;
CHAvlInsert(pCache, pEntry);
TRACE_OUT(( "CACHE: NEW ENTRY cache 0x%08x entry %d csum 0x%08x pdata 0x%08x",
pCache, iEntry, checkSum, pEntry->pData));
}
else
{
//
// We found the entry
//
preExisting = TRUE;
TRACE_OUT(( "CACHE: entry found in cache 0x%08x entry %d csum 0x%08x",
pCache, iEntry, checkSum));
}
CHUpdateMRUList(pCache, iEntry, evictionCategory);
*piCacheEntry = iEntry;
DebugExitBOOL(ASHost::CH_SearchAndCacheData, preExisting);
return(preExisting);
}
//
// FUNCTION: CH_RemoveCacheEntry
//
void ASHost::CH_RemoveCacheEntry
(
PCHCACHE pCache,
UINT iCacheEntry
)
{
DebugEntry(ASHost::CH_RemoveCacheEntry);
ASSERT(IsValidCache(pCache));
// ASSERT(IsValidCacheIndex(pCache, iCacheEntry)); Always True
CHEvictCacheEntry(pCache, iCacheEntry, pCache->Entry[iCacheEntry].evictionCategory);
DebugExitVOID(ASHost::CH_RemoveCacheEntry);
}
//
// FUNCTION: CH_ClearCache
//
void ASHost::CH_ClearCache
(
PCHCACHE pCache
)
{
UINT i;
DebugEntry(ASHost::CH_ClearCache);
ASSERT(IsValidCache(pCache));
//
// Remove the cache entries
//
for (i = 0; i < pCache->cEntries; i++)
{
if (pCache->Entry[i].pData != NULL)
{
CHRemoveEntry(pCache, i);
}
}
DebugExitVOID(ASHost::CH_ClearCache);
}
//
// CH_TouchCacheEntry() - see ch.h
//
void ASHost::CH_TouchCacheEntry
(
PCHCACHE pCache,
UINT iCacheEntry
)
{
DebugEntry(ASHost::CH_TouchCacheEntry);
ASSERT(IsValidCache(pCache));
// ASSERT(IsValidCacheIndex(pCache, iCacheEntry)); Always True
TRACE_OUT(( "Touching cache entry 0x%08x %d", pCache, iCacheEntry));
CHUpdateMRUList(pCache, iCacheEntry, 0);
DebugExitVOID(ASHost::CH_TouchCacheEntry);
}
//
// CHInitEntry
// Initializes a cache entry
//
//
void ASHost::CHInitEntry(PCHENTRY pEntry)
{
pEntry->pParent = NULL;
pEntry->pLeft = NULL;
pEntry->pRight = NULL;
pEntry->pData = NULL;
pEntry->checkSum = 0;
pEntry->lHeight = 0xFFFF;
pEntry->rHeight = 0xFFFF;
pEntry->chain.next = CH_MAX_CACHE_ENTRIES;
pEntry->chain.prev = CH_MAX_CACHE_ENTRIES;
pEntry->cbData = 0;
}
//
// FUNCTION: CHUpdateMRUList
//
void ASHost::CHUpdateMRUList
(
PCHCACHE pCache,
UINT iEntry,
UINT evictionCategory
)
{
WORD inext;
WORD iprev;
DebugEntry(ASHost::CHUpdateMRUList);
//
// Move the given entry to the head of the MRU if isn't there already
//
if (pCache->iMRUHead[evictionCategory] != iEntry)
{
//
// Remove the supplied entry from the MRU list, if it is currently
// chained. Since we never do this if the entry is already in the
// front, an iprev of CH_MAX_CACHE_ENTRIES indicates that we are
// updated an unchained entry.
//
iprev = pCache->Entry[iEntry].chain.prev;
inext = pCache->Entry[iEntry].chain.next;
TRACE_OUT(("Add/promote entry %u which was chained off %hu to %hu",
iEntry,iprev,inext));
if (iprev != CH_MAX_CACHE_ENTRIES)
{
pCache->Entry[iprev].chain.next = inext;
if (inext != CH_MAX_CACHE_ENTRIES)
{
pCache->Entry[inext].chain.prev = iprev;
}
else
{
TRACE_OUT(("Removing final entry(%u) from MRU chain leaving %hu at tail",
iEntry, iprev));
pCache->iMRUTail[evictionCategory] = iprev;
}
}
//
// Now add this entry to the head of the MRU list
//
inext = pCache->iMRUHead[evictionCategory];
pCache->Entry[iEntry].chain.next = inext;
pCache->Entry[iEntry].chain.prev = CH_MAX_CACHE_ENTRIES;
pCache->iMRUHead[evictionCategory] = (WORD)iEntry;
if (inext != CH_MAX_CACHE_ENTRIES)
{
pCache->Entry[inext].chain.prev = (WORD)iEntry;
}
else
{
//
// If the MRU chain is currently empty, then we must first add
// the entry to the tail of the chain.
//
pCache->iMRUTail[evictionCategory] = (WORD)iEntry;
TRACE_OUT(("Cache 0x%08x entry %u is first so add to MRU %u tail",
pCache, iEntry, evictionCategory));
}
TRACE_OUT(( "Cache 0x%08x entry %u to head of MRU category %u",
pCache, iEntry, evictionCategory));
}
else
{
TRACE_OUT(("Cache 0x%08x entry %u already at head of eviction category %u",
pCache, iEntry, evictionCategory));
}
DebugExitVOID(ASHost::CHUpateMRUList);
}
//
// FUNCTION: CHFindFreeCacheEntry
//
BOOL ASHost::CHFindFreeCacheEntry
(
PCHCACHE pCache,
UINT* piEntry,
UINT* pEvictionCount
)
{
UINT iEntry;
BOOL rc = FALSE;
DebugEntry(ASHost::CHFindFreeCacheEntry);
ASSERT(IsValidCache(pCache));
iEntry = pCache->free;
if (iEntry == CH_MAX_CACHE_ENTRIES)
{
TRACE_OUT(( "Cache 0x%08x is full", pCache));
*pEvictionCount = pCache->cEntries;
rc = FALSE;
}
else
{
TRACE_OUT(( "Free entry at %u",iEntry));
*piEntry = iEntry;
pCache->free = pCache->Entry[iEntry].free;
*pEvictionCount = 0;
rc = TRUE;
}
DebugExitBOOL(ASHost::CHFindFreeCacheEntry, rc);
return(rc);
}
//
// FUNCTION: CHEvictCacheEntry
//
UINT ASHost::CHEvictCacheEntry
(
PCHCACHE pCache,
UINT iEntry,
UINT evictionCategory
)
{
WORD inext;
WORD iprev;
DebugEntry(ASHost::CHEvictCacheEntry);
//
// Evict the specified entry by removing it from the MRU chain, and
// then resetting it. If it is in the cache, it must be in an MRU
// cache.
//
inext = pCache->Entry[iEntry].chain.next;
iprev = pCache->Entry[iEntry].chain.prev;
TRACE_OUT(( "Evicting entry %u which was chained off %hu to %hu",
iEntry, iprev, inext));
if (iprev < CH_MAX_CACHE_ENTRIES)
{
pCache->Entry[iprev].chain.next = inext;
}
else
{
TRACE_OUT(("Removing head entry(%u) from MRU chain leaving %hu at head",
iEntry, inext));
pCache->iMRUHead[evictionCategory] = inext;
}
if (inext < CH_MAX_CACHE_ENTRIES)
{
pCache->Entry[inext].chain.prev = iprev;
}
else
{
TRACE_OUT(("Removing tail entry(%u) from MRU chain leaving %hu at tail",
iEntry, iprev));
pCache->iMRUTail[evictionCategory] = iprev;
}
CHRemoveEntry(pCache, iEntry);
DebugExitDWORD(ASHost::CHEvictCacheEntry, iEntry);
return(iEntry);
}
//
// FUNCTION: CHEvictLRUCacheEntry
//
UINT ASHost::CHEvictLRUCacheEntry
(
PCHCACHE pCache,
UINT evictionCategory,
UINT evictionCount
)
{
UINT iEntry;
UINT i;
DebugEntry(ASHost::CHEvictLRUCacheEntry);
TRACE_OUT(("0x%08x LRU eviction requested, category %u, count %u",
pCache, evictionCategory, evictionCount));
//
// Evict from the same eviction category provided the number cached
// is above the threshold. Otherwise, take from the category one above.
// This will allow the system to eventually stabilize at the correct
// thresholds as all cache entries get used up.
//
if (evictionCount < pCache->cEvictThreshold[evictionCategory])
{
for (i = 0; i < pCache->cNumEvictionCategories; i++)
{
evictionCategory = (evictionCategory + 1) %
pCache->cNumEvictionCategories;
if (pCache->iMRUTail[evictionCategory] != CH_MAX_CACHE_ENTRIES)
break;
}
WARNING_OUT(( "Threshold %u, count %u so set eviction category to %u",
pCache->cEvictThreshold[evictionCategory],
evictionCount,
evictionCategory));
}
//
// Evict the lasat entry in the MRU chain
//
iEntry = pCache->iMRUTail[evictionCategory];
TRACE_OUT(( "Selected %u for eviction",iEntry));
ASSERT((iEntry != CH_MAX_CACHE_ENTRIES));
CHEvictCacheEntry(pCache, iEntry, evictionCategory);
DebugExitDWORD(ASHost::CHEvictLRUCacheEntry, iEntry);
return(iEntry);
}
//
// FUNCTION: CHRemoveEntry
//
void ASHost::CHRemoveEntry
(
PCHCACHE pCache,
UINT iCacheEntry
)
{
DebugEntry(ASHost::CHRemoveEntry);
ASSERT(IsValidCache(pCache));
// ASSERT(IsValidCacheIndex(pCache, iCacheEntry)); Always True
if (pCache->Entry[iCacheEntry].pData != NULL)
{
if (pCache->pfnCacheDel)
{
(pCache->pfnCacheDel)(this, pCache, iCacheEntry,
pCache->Entry[iCacheEntry].pData);
}
else
{
// Simple deletion -- just free memory
delete[] pCache->Entry[iCacheEntry].pData;
}
}
CHAvlDelete(pCache, &pCache->Entry[iCacheEntry], iCacheEntry);
DebugExitVOID(ASHost::CHRemoveEntry);
}
//
// FUNCTION: CHCheckSum
//
// For processing speed we calculate the checksum based on whole multiples
// of 4 bytes followed by a final addition of the last few bytes
//
UINT ASHost::CHCheckSum
(
LPBYTE pData,
UINT cbDataSize
)
{
UINT cSum = 0;
UINT * pCh;
UINT * pEnd;
LPBYTE pCh8;
DebugEntry(ASHost::CHCheckSum);
ASSERT(cbDataSize > 3);
pCh = (UINT *)pData;
pEnd = (UINT *)(pData + cbDataSize - 4);
//
// Get the DWORD-aligned checksum
//
while (pCh <= pEnd)
{
cSum = (cSum << 1) + *pCh++ + ((cSum & 0x80000000) != 0);
}
//
// Get the rest past the last DWORD boundaray
//
pEnd = (UINT *)(pData + cbDataSize);
for (pCh8 = (LPBYTE)pCh; pCh8 < (LPBYTE)pEnd; pCh8++)
{
cSum = cSum + *pCh8;
}
DebugExitDWORD(ASHost::CHCheckSum, cSum);
return(cSum);
}
//
// FUNCTION: CHTreeSearch
//
// Finds a node in the cache tree which matches size, checksum and data.
//
UINT ASHost::CHTreeSearch
(
PCHCACHE pCache,
UINT checkSum,
UINT cbDataSize,
LPBYTE pData
)
{
PCHENTRY pEntry;
UINT iCacheEntry = CH_MAX_CACHE_ENTRIES;
DebugEntry(ASHost::CHTreeSearch);
pEntry = CHAvlFind(pCache, checkSum, cbDataSize);
while (pEntry != NULL)
{
ASSERT(IsValidCacheEntry(pEntry));
//
// Found a match based on the checksum. Now see if the data
// also matches.
//
if (!memcmp(pEntry->pData + pCache->cbNotHashed,
pData + pCache->cbNotHashed,
cbDataSize - pCache->cbNotHashed))
{
//
// Data also matches. Get an index into the memory block
// of the cache.
//
iCacheEntry = (UINT)(pEntry - pCache->Entry);
TRACE_OUT(( "Cache 0x%08x entry %d match-csum 0x%08x",
pCache, iCacheEntry, checkSum));
break;
}
else
{
TRACE_OUT(( "Checksum 0x%08x size %u matched, data didn't",
checkSum, cbDataSize));
pEntry = CHAvlFindEqual(pCache, pEntry);
}
}
DebugExitDWORD(ASHost::CHTreeSearch, iCacheEntry);
return(iCacheEntry);
}
//
// Name: CHAvlInsert
//
// Purpose: Insert the supplied node into the specified AVL tree
//
// Returns: Nothing
//
// Params: IN pTree - a pointer to the AVL tree
// IN pEntry - a pointer to the node to insert
//
// Operation: Scan down the tree looking for the insert point, going left
// if the insert key is less than or equal to the key in the tree
// and right if it is greater. When the insert point is found
// insert the new node and rebalance the tree if necessary.
//
//
void ASHost::CHAvlInsert
(
PCHCACHE pCache,
PCHENTRY pEntry
)
{
PCHENTRY pParentEntry;
int result;
DebugEntry(ASHost::CHAvlInsert);
ASSERT(IsValidCacheEntry(pEntry));
ASSERT(!IsCacheEntryInTree(pEntry));
pEntry->rHeight = 0;
pEntry->lHeight = 0;
if (pCache->pRoot == NULL)
{
//
// tree is empty, so insert at root
//
TRACE_OUT(( "tree is empty, so insert at root" ));
pCache->pRoot = pEntry;
pCache->pFirst = pEntry;
pCache->pLast = pEntry;
DC_QUIT;
}
//
// scan down the tree looking for the appropriate insert point
//
TRACE_OUT(( "scan for insert point" ));
pParentEntry = pCache->pRoot;
while (pParentEntry != NULL)
{
//
// go left or right, depending on comparison
//
result = CHCompare(pEntry->checkSum, pEntry->cbData, pParentEntry);
if (result > 0)
{
//
// new key is greater than this node's key, so move down right
// subtree
//
TRACE_OUT(( "move down right subtree" ));
if (pParentEntry->pRight == NULL)
{
//
// right subtree is empty, so insert here
//
TRACE_OUT(( "right subtree empty, insert here" ));
pEntry->pParent = pParentEntry;
ASSERT((pParentEntry != pEntry));
pParentEntry->pRight = pEntry;
pParentEntry->rHeight = 1;
if (pParentEntry == pCache->pLast)
{
//
// parent was the right-most node in the tree, so new
// node is now right-most
//
TRACE_OUT(( "new last node" ));
pCache->pLast = pEntry;
}
break;
}
else
{
//
// right subtree is not empty
//
TRACE_OUT(( "right subtree not empty" ));
pParentEntry = pParentEntry->pRight;
}
}
else
{
//
// New key is less than or equal to this node's key, so move
// down left subtree. The new node could be inserted before
// the current node when equal, but this happens so rarely
// that it's not worth special casing.
//
TRACE_OUT(( "move down left subtree" ));
if (pParentEntry->pLeft == NULL)
{
//
// left subtree is empty, so insert here
//
TRACE_OUT(( "left subtree empty, insert here" ));
pEntry->pParent = pParentEntry;
ASSERT((pParentEntry != pEntry));
pParentEntry->pLeft = pEntry;
pParentEntry->lHeight = 1;
if (pParentEntry == pCache->pFirst)
{
//
// parent was the left-most node in the tree, so new
// node is now left-most
//
TRACE_OUT(( "new first node" ));
pCache->pFirst = pEntry;
}
break;
}
else
{
//
// left subtree is not empty
//
TRACE_OUT(( "left subtree not empty" ));
pParentEntry = pParentEntry->pLeft;
}
}
}
//
// now rebalance the tree if necessary
//
CHAvlBalanceTree(pCache, pParentEntry);
DC_EXIT_POINT:
DebugExitVOID(ASHost::CHAvlInsert);
}
//
// Name: CHAvlDelete
//
// Purpose: Delete the specified node from the specified AVL tree
//
// Returns: Nothing
//
// Params: IN pCache - a pointer to the AVL tree
// IN pEntry - a pointer to the node to delete
//
//
void ASHost::CHAvlDelete
(
PCHCACHE pCache,
PCHENTRY pEntry,
UINT iCacheEntry
)
{
PCHENTRY pReplaceEntry;
PCHENTRY pParentEntry;
WORD newHeight;
DebugEntry(ASHost::CHAvlDelete);
ASSERT(IsValidCacheEntry(pEntry));
ASSERT(IsCacheEntryInTree(pEntry));
if ((pEntry->pLeft == NULL) && (pEntry->pRight == NULL))
{
//
// Barren node (no children). Update all references to it with
// our parent.
//
TRACE_OUT(( "delete barren node" ));
pReplaceEntry = NULL;
if (pCache->pFirst == pEntry)
{
//
// We are the first in the b-tree
//
TRACE_OUT(( "replace first node in tree" ));
pCache->pFirst = pEntry->pParent;
}
if (pCache->pLast == pEntry)
{
//
// We are the last in the b-tree
//
TRACE_OUT(( "replace last node in tree" ));
pCache->pLast = pEntry->pParent;
}
}
else if (pEntry->pLeft == NULL)
{
//
// This node has no left child, so update references to it with
// pointer to right child.
//
TRACE_OUT(( "node has no left child, replace with right child" ));
pReplaceEntry = pEntry->pRight;
if (pCache->pFirst == pEntry)
{
//
// We are the first in the b-tree
//
TRACE_OUT(( "replace first node in tree" ));
pCache->pFirst = pReplaceEntry;
}
// WE CAN'T BE THE LAST IN THE B-TREE SINCE WE HAVE A RIGHT CHILD
ASSERT(pCache->pLast != pEntry);
}
else if (pEntry->pRight == NULL)
{
//
// This node has no right child, so update references to it with
// pointer to left child.
//
TRACE_OUT(( "node has no right son, replace with left son" ));
pReplaceEntry = pEntry->pLeft;
// WE CAN'T BE THE FIRST IN THE B-TREE SINCE WE HAVE A LEFT CHILD
ASSERT(pCache->pFirst != pEntry);
if (pCache->pLast == pEntry)
{
//
// We are the last in the b-tree
//
TRACE_OUT(( "replace last node in tree" ));
pCache->pLast = pReplaceEntry;
}
}
else
{
//
// HARDEST CASE. WE HAVE LEFT AND RIGHT CHILDREN
TRACE_OUT(( "node has two sons" ));
if (pEntry->rHeight > pEntry->lHeight)
{
//
// Right subtree is bigger than left subtree
//
TRACE_OUT(( "right subtree is higher" ));
if (pEntry->pRight->pLeft == NULL)
{
//
// Replace references to entry with right child since it
// has no left child (left grandchild of us)
//
TRACE_OUT(( "replace node with right son" ));
pReplaceEntry = pEntry->pRight;
pReplaceEntry->pLeft = pEntry->pLeft;
pReplaceEntry->pLeft->pParent = pReplaceEntry;
pReplaceEntry->lHeight = pEntry->lHeight;
}
else
{
//
// Swap with leftmost descendent of the right subtree
//
TRACE_OUT(( "swap with left-most right descendent" ));
CHAvlSwapLeftmost(pCache, pEntry->pRight, pEntry);
pReplaceEntry = pEntry->pRight;
}
}
else
{
//
// Left subtree is bigger than or equal to right subtree
//
TRACE_OUT(( "left subtree is higher" ));
TRACE_OUT(( "(or both subtrees are of equal height)" ));
if (pEntry->pLeft->pRight == NULL)
{
//
// Replace references to entry with left child since it
// no right child (right grandchild of us)
//
TRACE_OUT(( "replace node with left son" ));
pReplaceEntry = pEntry->pLeft;
pReplaceEntry->pRight = pEntry->pRight;
pReplaceEntry->pRight->pParent = pReplaceEntry;
pReplaceEntry->rHeight = pEntry->rHeight;
}
else
{
//
// Swap with rightmost descendent of the left subtree
//
TRACE_OUT(( "swap with right-most left descendent" ));
CHAvlSwapRightmost(pCache, pEntry->pLeft, pEntry);
pReplaceEntry = pEntry->pLeft;
}
}
}
//
// NOTE: We can not save parent entry above because some code might
// swap the tree around. In which case, our parenty entry will change
// out from underneath us.
//
pParentEntry = pEntry->pParent;
//
// Clear out the about-to-be-deleted cache entry
//
TRACE_OUT(( "reset deleted node" ));
CHInitEntry(pEntry);
if (pReplaceEntry != NULL)
{
//
// Fix up parent pointers, and calculate new heights of subtree
//
TRACE_OUT(( "fixup parent pointer of replacement node" ));
pReplaceEntry->pParent = pParentEntry;
newHeight = (WORD)(1 + max(pReplaceEntry->lHeight, pReplaceEntry->rHeight));
}
else
{
newHeight = 0;
}
TRACE_OUT(( "new height of parent is %d", newHeight ));
if (pParentEntry != NULL)
{
//
// Fixup parent entry pointers
//
TRACE_OUT(( "fix-up parent node" ));
if (pParentEntry->pRight == pEntry)
{
//
// Entry is right child of parent
//
TRACE_OUT(( "replacement node is right son" ));
pParentEntry->pRight = pReplaceEntry;
pParentEntry->rHeight = newHeight;
}
else
{
//
// Entry is left child of parent
//
TRACE_OUT(( "replacement node is left son" ));
pParentEntry->pLeft = pReplaceEntry;
pParentEntry->lHeight = newHeight;
}
//
// Now rebalance the tree if necessary
//
CHAvlBalanceTree(pCache, pParentEntry);
}
else
{
//
// Replacement is now root of tree
//
TRACE_OUT(( "replacement node is now root of tree" ));
pCache->pRoot = pReplaceEntry;
}
//
// Put entry back into free list.
//
pEntry->free = pCache->free;
pCache->free = (WORD)iCacheEntry;
DebugExitVOID(ASHost::CHAvlDelete);
}
//
// Name: CHAvlNext
//
// Purpose: Find next node in the AVL tree
//
// Returns: A pointer to the next node's data
//
// Params: IN pEntry - a pointer to the current node in
// the tree
//
// Operation: If the specified node has a right-son then return the left-
// most son of this. Otherwise search back up until we find a
// node of which we are in the left sub-tree and return that.
//
//
LPBYTE ASHost::CHAvlNext
(
PCHENTRY pEntry
)
{
//
// find next node in tree
//
DebugEntry(ASHost::CHAvlNext);
ASSERT(IsValidCacheEntry(pEntry));
ASSERT(IsCacheEntryInTree(pEntry));
if (pEntry->pRight != NULL)
{
//
// Next entry is the left-most in the right-subtree
//
TRACE_OUT(( "next node is left-most right descendent" ));
pEntry = pEntry->pRight;
ASSERT(IsValidCacheEntry(pEntry));
while (pEntry->pLeft != NULL)
{
ASSERT(IsValidCacheEntry(pEntry->pLeft));
pEntry = pEntry->pLeft;
}
}
else
{
//
// No right child. So find an entry for which we are in its left
// subtree.
//
TRACE_OUT(( "find node which this is in left subtree of" ));
while (pEntry != NULL)
{
ASSERT(IsValidCacheEntry(pEntry));
if ((pEntry->pParent == NULL) ||
(pEntry->pParent->pLeft == pEntry))
{
pEntry = pEntry->pParent;
break;
}
pEntry = pEntry->pParent;
}
}
DebugExitVOID(ASHost::CHAvlNext);
return((pEntry != NULL) ? pEntry->pData : NULL);
}
//
// Name: CHAvlPrev
//
// Purpose: Find previous node in the AVL tree
//
// Returns: A pointer to the previous node's data in the tree
//
// Params: IN PNode - a pointer to the current node in
// the tree
//
// Operation: If we have a left-son then the previous node is the right-most
// son of this. Otherwise, look for a node of whom we are in the
// left subtree and return that.
//
//
LPBYTE ASHost::CHAvlPrev(PCHENTRY pEntry)
{
//
// find previous node in tree
//
DebugEntry(ASHost::CHAvlPrev);
ASSERT(IsValidCacheEntry(pEntry));
ASSERT(IsCacheEntryInTree(pEntry));
if (pEntry->pLeft != NULL)
{
//
// Previous entry is right-most in left-subtree
//
TRACE_OUT(( "previous node is right-most left descendent" ));
pEntry = pEntry->pLeft;
ASSERT(IsValidCacheEntry(pEntry));
while (pEntry->pRight != NULL)
{
ASSERT(IsValidCacheEntry(pEntry->pRight));
pEntry = pEntry->pRight;
}
}
else
{
//
// No left child. So find an entry for which we are in the right
// subtree.
//
TRACE_OUT(( "find node which this is in right subtree of"));
while (pEntry != NULL)
{
ASSERT(IsValidCacheEntry(pEntry));
if ((pEntry->pParent == NULL) ||
(pEntry->pParent->pRight == pEntry))
{
pEntry = pEntry->pParent;
break;
}
pEntry = pEntry->pParent;
}
}
DebugExitVOID(ASHost::CHAvlPrev);
return((pEntry != NULL) ? pEntry->pData : NULL);
}
//
// Name: CHAvlFindEqual
//
// Purpose: Find the node in the AVL tree with the same key and size as
// the supplied node
//
// Returns: A pointer to the node
// NULL if no node is found with the specified key and size
//
// Params: IN pCache - a pointer to the AVL tree
// IN pEntry - a pointer to the node to test
//
// Operation: Check if the left node has the same key and size, returning
// a pointer to its data if it does.
//
//
PCHENTRY ASHost::CHAvlFindEqual
(
PCHCACHE pCache,
PCHENTRY pEntry
)
{
int result;
PCHENTRY pReturn = NULL;
DebugEntry(ASHost::CHAvlFindEqual);
ASSERT(IsValidCacheEntry(pEntry));
if (pEntry->pLeft)
{
ASSERT(IsValidCacheEntry(pEntry->pLeft));
result = CHCompare(pEntry->pLeft->checkSum, pEntry->cbData, pEntry);
if (result < 0)
{
//
// specified key is less than key of this node - this is what
// will normally occur
//
TRACE_OUT(( "left node size %u csum 0x%08x",
pEntry->pLeft->cbData,
pEntry->pLeft->checkSum));
}
else if (result == 0)
{
//
// Found a match on size and key.
//
TRACE_OUT(( "left node dups size and key" ));
pReturn = pEntry->pLeft;
}
else
{
//
// This is an error (left node should never be greater)
//
ERROR_OUT(( "left node csum %#lx, supplied %#lx",
pEntry->pLeft->checkSum,
pEntry->checkSum));
}
}
DebugExitPVOID(ASHost::CHAvlFindEqual, pReturn);
return(pReturn);
}
//
// Name: CHAvlFind
//
// Purpose: Find the node in the AVL tree with the supplied key and size
//
// Returns: A pointer to the node
// NULL if no node is found with the specified key and size
//
// Params: IN pCache - a pointer to the AVL tree
// IN checkSum - a pointer to the key
// IN cbSize - number of node data bytes
//
// Operation: Search down the tree going left if the search key is less than
// the node in the tree and right if the search key is greater.
// When we run out of tree to search through either we've found
// it or the node is not in the tree.
//
//
PCHENTRY ASHost::CHAvlFind
(
PCHCACHE pCache,
UINT checkSum,
UINT cbSize
)
{
PCHENTRY pEntry;
int result;
// DebugEntry(ASHost::CHAvlFind);
pEntry = pCache->pRoot;
while (pEntry != NULL)
{
ASSERT(IsValidCacheEntry(pEntry));
//
// Compare the supplied key (checksum) with that of the current node
//
result = CHCompare(checkSum, cbSize, pEntry);
if (result > 0)
{
//
// Supplied key is greater than that of this entry, so look in
// the right subtree
//
pEntry = pEntry->pRight;
TRACE_OUT(( "move down right subtree to node 0x%08x", pEntry));
}
else if (result < 0)
{
//
// Supplied key is lesser than that of this entry, so look in
// the left subtree
//
pEntry = pEntry->pLeft;
TRACE_OUT(( "move down left subtree to node 0x%08x", pEntry));
}
else
{
//
// We found the FIRST entry with an identical key (checksum).
//
TRACE_OUT(( "found requested node" ));
break;
}
}
// DebugExitPVOID(ASHost::CHAvlFind, pEntry);
return(pEntry);
}
//
// Name: CHAvlBalanceTree
//
// Purpose: Reblance the tree starting at the supplied node and ending at
// the root of the tree
//
// Returns: Nothing
//
// Params: IN pCache - a pointer to the AVL tree
// IN pEntry - a pointer to the node to start
// balancing from
//
//
void ASHost::CHAvlBalanceTree
(
PCHCACHE pCache,
PCHENTRY pEntry
)
{
//
// Balance the tree starting at the given entry, ending with the root
// of the tree
//
DebugEntry(ASHost::CHAvlBalanceTree);
ASSERT(IsValidCacheEntry(pEntry));
while (pEntry->pParent != NULL)
{
ASSERT(IsValidCacheEntry(pEntry->pParent));
//
// node has uneven balance, so may need to rebalance it
//
TRACE_OUT(( "check node balance" ));
TRACE_OUT(( " rHeight = %hd", pEntry->rHeight ));
TRACE_OUT(( " lHeight = %hd", pEntry->lHeight ));
if (pEntry->pParent->pRight == pEntry)
{
//
// node is right-son of its parent
//
TRACE_OUT(( "node is right-son" ));
pEntry = pEntry->pParent;
CHAvlRebalance(&pEntry->pRight);
//
// now update the right height of the parent
//
pEntry->rHeight = (WORD)
(1 + max(pEntry->pRight->rHeight, pEntry->pRight->lHeight));
TRACE_OUT(( "new rHeight = %d", pEntry->rHeight ));
}
else
{
//
// node is left-son of its parent
//
TRACE_OUT(( "node is left-son" ));
pEntry = pEntry->pParent;
CHAvlRebalance(&pEntry->pLeft);
//
// now update the left height of the parent
//
pEntry->lHeight = (WORD)
(1 + max(pEntry->pLeft->rHeight, pEntry->pLeft->lHeight));
TRACE_OUT(( "new lHeight = %d", pEntry->lHeight ));
}
ASSERT(IsValidCacheEntry(pEntry));
}
if (pEntry->lHeight != pEntry->rHeight)
{
//
// rebalance root node
//
TRACE_OUT(( "rebalance root node"));
CHAvlRebalance(&pCache->pRoot);
}
DebugExitVOID(ASHost::CHAvlBalanceTree);
}
//
// Name: CHAvlRebalance
//
// Purpose: Reblance a subtree of the AVL tree (if necessary)
//
// Returns: Nothing
//
// Params: IN/OUT ppSubtree - a pointer to the subtree to
// rebalance
//
//
void ASHost::CHAvlRebalance
(
PCHENTRY * ppSubtree
)
{
int moment;
DebugEntry(ASHost::CHAvlRebalance);
ASSERT(IsValidCacheEntry(*ppSubtree));
TRACE_OUT(( "rebalance subtree" ));
TRACE_OUT(( " rHeight = %hd", (*ppSubtree)->rHeight ));
TRACE_OUT(( " lHeight = %hd", (*ppSubtree)->lHeight ));
//
// How unbalanced - don't want to recalculate
//
moment = (*ppSubtree)->rHeight - (*ppSubtree)->lHeight;
if (moment > 1)
{
//
// subtree is heavy on the right side
//
TRACE_OUT(( "subtree is heavy on right side" ));
TRACE_OUT(( "right subtree" ));
TRACE_OUT(( " rHeight = %d", (*ppSubtree)->pRight->rHeight ));
TRACE_OUT(( " lHeight = %d", (*ppSubtree)->pRight->lHeight ));
if ((*ppSubtree)->pRight->lHeight > (*ppSubtree)->pRight->rHeight)
{
//
// right subtree is heavier on left side, so must perform right
// rotation on this subtree to make it heavier on the right
// side
//
TRACE_OUT(( "right subtree is heavier on left side ..." ));
TRACE_OUT(( "... so rotate it right" ));
CHAvlRotateRight(&(*ppSubtree)->pRight);
TRACE_OUT(( "right subtree" ));
TRACE_OUT(( " rHeight = %d", (*ppSubtree)->pRight->rHeight ));
TRACE_OUT(( " lHeight = %d", (*ppSubtree)->pRight->lHeight ));
}
//
// now rotate the subtree left
//
TRACE_OUT(( "rotate subtree left" ));
CHAvlRotateLeft(ppSubtree);
}
else if (moment < -1)
{
//
// subtree is heavy on the left side
//
TRACE_OUT(( "subtree is heavy on left side" ));
TRACE_OUT(( "left subtree" ));
TRACE_OUT(( " rHeight = %d", (*ppSubtree)->pLeft->rHeight ));
TRACE_OUT(( " lHeight = %d", (*ppSubtree)->pLeft->lHeight ));
if ((*ppSubtree)->pLeft->rHeight > (*ppSubtree)->pLeft->lHeight)
{
//
// left subtree is heavier on right side, so must perform left
// rotation on this subtree to make it heavier on the left side
//
TRACE_OUT(( "left subtree is heavier on right side ..." ));
TRACE_OUT(( "... so rotate it left" ));
CHAvlRotateLeft(&(*ppSubtree)->pLeft);
TRACE_OUT(( "left subtree" ));
TRACE_OUT(( " rHeight = %d", (*ppSubtree)->pLeft->rHeight ));
TRACE_OUT(( " lHeight = %d", (*ppSubtree)->pLeft->lHeight ));
}
//
// now rotate the subtree right
//
TRACE_OUT(( "rotate subtree right" ));
CHAvlRotateRight(ppSubtree);
}
TRACE_OUT(( "balanced subtree" ));
TRACE_OUT(( " rHeight = %d", (*ppSubtree)->rHeight ));
TRACE_OUT(( " lHeight = %d", (*ppSubtree)->lHeight ));
DebugExitVOID(ASHost::CHAvlRebalance);
}
//
// Name: CHAvlRotateRight
//
// Purpose: Rotate a subtree of the AVL tree right
//
// Returns: Nothing
//
// Params: IN/OUT ppSubtree - a pointer to the subtree to rotate
//
//
void ASHost::CHAvlRotateRight
(
PCHENTRY * ppSubtree
)
{
PCHENTRY pLeftSon;
DebugEntry(ASHost::CHAvlRotateRight);
ASSERT(IsValidCacheEntry(*ppSubtree));
pLeftSon = (*ppSubtree)->pLeft;
ASSERT(IsValidCacheEntry(pLeftSon));
(*ppSubtree)->pLeft = pLeftSon->pRight;
if ((*ppSubtree)->pLeft != NULL)
{
(*ppSubtree)->pLeft->pParent = (*ppSubtree);
}
(*ppSubtree)->lHeight = pLeftSon->rHeight;
pLeftSon->pParent = (*ppSubtree)->pParent;
pLeftSon->pRight = *ppSubtree;
pLeftSon->pRight->pParent = pLeftSon;
pLeftSon->rHeight = (WORD)
(1 + max((*ppSubtree)->rHeight, (*ppSubtree)->lHeight));
*ppSubtree = pLeftSon;
DebugExitVOID(ASHost::CHAvlRotateRight);
}
//
// Name: CHAvlRotateLeft
//
// Purpose: Rotate a subtree of the AVL tree left
//
// Returns: Nothing
//
// Params: IN/OUT ppSubtree - a pointer to the subtree to rotate
//
//
void ASHost::CHAvlRotateLeft
(
PCHENTRY * ppSubtree
)
{
PCHENTRY pRightSon;
DebugEntry(ASHost::CHAvlRotateLeft);
ASSERT(IsValidCacheEntry(*ppSubtree));
pRightSon = (*ppSubtree)->pRight;
ASSERT(IsValidCacheEntry(pRightSon));
(*ppSubtree)->pRight = pRightSon->pLeft;
if ((*ppSubtree)->pRight != NULL)
{
(*ppSubtree)->pRight->pParent = (*ppSubtree);
}
(*ppSubtree)->rHeight = pRightSon->lHeight;
pRightSon->pParent = (*ppSubtree)->pParent;
pRightSon->pLeft = *ppSubtree;
pRightSon->pLeft->pParent = pRightSon;
pRightSon->lHeight = (WORD)
(1 + max((*ppSubtree)->rHeight, (*ppSubtree)->lHeight));
*ppSubtree = pRightSon;
DebugExitVOID(ASHost::CHAvlRotateLeft);
}
//
// Name: CHAvlSwapRightmost
//
// Purpose: Swap node with right-most descendent of subtree
//
// Returns: Nothing
//
// Params: IN pCache - a pointer to the tree
// IN pSubtree - a pointer to the subtree
// IN pEntry - a pointer to the node to swap
//
//
void ASHost::CHAvlSwapRightmost
(
PCHCACHE pCache,
PCHENTRY pSubtree,
PCHENTRY pEntry
)
{
PCHENTRY pSwapEntry;
PCHENTRY pSwapParent;
PCHENTRY pSwapLeft;
DebugEntry(ASHost::CHAvlSwapRightmost);
ASSERT(IsValidCacheEntry(pEntry));
ASSERT((pEntry->pRight != NULL));
ASSERT((pEntry->pLeft != NULL));
//
// find right-most descendent of subtree
//
ASSERT(IsValidCacheEntry(pSubtree));
pSwapEntry = pSubtree;
while (pSwapEntry->pRight != NULL)
{
pSwapEntry = pSwapEntry->pRight;
ASSERT(IsValidCacheEntry(pSwapEntry));
}
ASSERT((pSwapEntry->rHeight == 0));
ASSERT((pSwapEntry->lHeight <= 1));
//
// save parent and left-son of right-most descendent
//
pSwapParent = pSwapEntry->pParent;
pSwapLeft = pSwapEntry->pLeft;
//
// move swap node to its new position
//
pSwapEntry->pParent = pEntry->pParent;
pSwapEntry->pRight = pEntry->pRight;
pSwapEntry->pLeft = pEntry->pLeft;
pSwapEntry->rHeight = pEntry->rHeight;
pSwapEntry->lHeight = pEntry->lHeight;
pSwapEntry->pRight->pParent = pSwapEntry;
pSwapEntry->pLeft->pParent = pSwapEntry;
if (pEntry->pParent == NULL)
{
//
// node is at root of tree
//
pCache->pRoot = pSwapEntry;
}
else if (pEntry->pParent->pRight == pEntry)
{
//
// node is right-son of parent
//
pSwapEntry->pParent->pRight = pSwapEntry;
}
else
{
//
// node is left-son of parent
//
pSwapEntry->pParent->pLeft = pSwapEntry;
}
//
// move node to its new position
//
pEntry->pParent = pSwapParent;
pEntry->pRight = NULL;
pEntry->pLeft = pSwapLeft;
if (pEntry->pLeft != NULL)
{
pEntry->pLeft->pParent = pEntry;
pEntry->lHeight = 1;
}
else
{
pEntry->lHeight = 0;
}
pEntry->rHeight = 0;
pEntry->pParent->pRight = pEntry;
DebugExitVOID(ASHost::CHAvlSwapRightmost);
}
//
// Name: CHAvlSwapLeftmost
//
// Purpose: Swap node with left-most descendent of subtree
//
// Returns: Nothing
//
// Params: IN pCache - a pointer to the tree
// IN pSubtree - a pointer to the subtree
// IN pEntry - a pointer to the node to swap
//
//
void ASHost::CHAvlSwapLeftmost
(
PCHCACHE pCache,
PCHENTRY pSubtree,
PCHENTRY pEntry
)
{
PCHENTRY pSwapEntry;
PCHENTRY pSwapParent;
PCHENTRY pSwapRight;
DebugEntry(ASHost::CHAvlSwapLeftmost);
ASSERT(IsValidCacheEntry(pEntry));
ASSERT((pEntry->pRight != NULL));
ASSERT((pEntry->pLeft != NULL));
//
// find left-most descendent of pSubtree
//
ASSERT(IsValidCacheEntry(pSubtree));
pSwapEntry = pSubtree;
while (pSwapEntry->pLeft != NULL)
{
pSwapEntry = pSwapEntry->pLeft;
ASSERT(IsValidCacheEntry(pSwapEntry));
}
ASSERT((pSwapEntry->lHeight == 0));
ASSERT((pSwapEntry->rHeight <= 1));
//
// save parent and right-son of left-most descendent
//
pSwapParent = pSwapEntry->pParent;
pSwapRight = pSwapEntry->pRight;
//
// move swap node to its new position
//
pSwapEntry->pParent = pEntry->pParent;
pSwapEntry->pRight = pEntry->pRight;
pSwapEntry->pLeft = pEntry->pLeft;
pSwapEntry->rHeight = pEntry->rHeight;
pSwapEntry->lHeight = pEntry->lHeight;
pSwapEntry->pRight->pParent = pSwapEntry;
pSwapEntry->pLeft->pParent = pSwapEntry;
if (pEntry->pParent == NULL)
{
//
// node is at root of tree
//
pCache->pRoot = pSwapEntry;
}
else if (pEntry->pParent->pRight == pEntry)
{
//
// node is right-son of parent
//
pSwapEntry->pParent->pRight = pSwapEntry;
}
else
{
//
// node is left-son of parent
//
pSwapEntry->pParent->pLeft = pSwapEntry;
}
//
// move node to its new position
//
pEntry->pParent = pSwapParent;
pEntry->pRight = pSwapRight;
pEntry->pLeft = NULL;
if (pEntry->pRight != NULL)
{
pEntry->pRight->pParent = pEntry;
pEntry->rHeight = 1;
}
else
{
pEntry->rHeight = 0;
}
pEntry->lHeight = 0;
pEntry->pParent->pLeft = pEntry;
DebugExitVOID(ASHost::CHAvlSwapLeftmost);
}
//
// Name: CHCompare
//
// Purpose: Standard function for comparing UINTs
//
// Returns: -1 if key < pEntry->checksum
// -1 if key = pEntry->checksum AND sizes do not match
// 0 if key = pEntry->checksum AND sizes match
// 1 if key > pEntry->checksum
//
// Params: IN key - a pointer to the comparison key
// IN cbSize - number of comparison data bytes
// IN pEntry - a pointer to the node to compare
//
//
int ASHost::CHCompare
(
UINT key,
UINT cbSize,
PCHENTRY pEntry
)
{
int ret_val;
DebugEntry(ASHost::CHCompare);
ASSERT(IsValidCacheEntry(pEntry));
if (key < pEntry->checkSum)
{
ret_val = -1;
TRACE_OUT(( "Key is less (-1)"));
}
else if (key > pEntry->checkSum)
{
ret_val = 1;
TRACE_OUT(( "Key is more (+1)"));
}
else
{
if (cbSize == pEntry->cbData)
{
ret_val = 0;
TRACE_OUT(( "Key and size match"));
}
else
{
ret_val = -1;
TRACE_OUT(( "Key match, size mismatch (-1)"));
}
}
DebugExitDWORD(ASHost::CHCompare, ret_val);
return(ret_val);
}