mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
768 lines
17 KiB
768 lines
17 KiB
/***********************************************************************
|
|
* Microsoft (R) 32-Bit Incremental Linker
|
|
*
|
|
* Copyright (C) Microsoft Corp 1992-95. All rights reserved.
|
|
*
|
|
* File: hash.cpp
|
|
*
|
|
* File Comments:
|
|
*
|
|
* Generic dynamic hash tables implemented on top of dynamic arrays
|
|
*
|
|
***********************************************************************/
|
|
|
|
#include "link.h"
|
|
|
|
static VOID Expand_HT(IN PHT, IN PVOID);
|
|
|
|
VOID
|
|
SetStatus_HT(
|
|
PHT pht,
|
|
WORD flags)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Set the hash table status
|
|
|
|
Arguments:
|
|
|
|
pht - hash table structure
|
|
|
|
flags - hash table status flags.
|
|
|
|
Return Value:
|
|
|
|
new flags
|
|
|
|
--*/
|
|
|
|
{
|
|
assert(pht);
|
|
pht->flags = flags;
|
|
}
|
|
|
|
WORD
|
|
GetStatus_HT(
|
|
PHT pht)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Get the hash table status
|
|
|
|
Arguments:
|
|
|
|
pht - hash table structure
|
|
|
|
Return Value:
|
|
|
|
new flags
|
|
|
|
--*/
|
|
|
|
{
|
|
assert(pht);
|
|
return(pht->flags);
|
|
}
|
|
|
|
VOID Init_HT(
|
|
OUT PPHT ppht,
|
|
DWORD celementInChunk,
|
|
DWORD cchunkInDir,
|
|
const char *(*SzFromPv)(PVOID, PVOID),
|
|
WORD flags)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initialize the hash table.
|
|
|
|
Arguments:
|
|
|
|
ppht - pointer to a pointer to the hash table
|
|
|
|
celementsInChunk - number of elements in a dynamic array chunk
|
|
|
|
cchunksInDir - number of chunks in a dynamic array directory
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
|
|
{
|
|
assert(ppht);
|
|
*ppht = (PHT) Calloc(1, sizeof(HT));
|
|
|
|
// the initial number of buckets is arbitrary and can be tuned
|
|
(*ppht)->cbuckets = celementInChunk;
|
|
(*ppht)->iNextToSplitMax = celementInChunk;
|
|
(*ppht)->iNextToSplit = 0L;
|
|
(*ppht)->cExpands = 0L;
|
|
(*ppht)->pstateStack = NULL;
|
|
(*ppht)->celementInChunk = celementInChunk;
|
|
(*ppht)->cchunkInDir = cchunkInDir;
|
|
(*ppht)->SzFromPv = SzFromPv;
|
|
|
|
// set the hash table status to not full and inserts allowed
|
|
(*ppht)->flags = 0;
|
|
(*ppht)->flags |= flags;
|
|
|
|
// allocate a directory
|
|
(*ppht)->rgpchunk = (PCHUNK *) Calloc(cchunkInDir, sizeof(PCHUNK));
|
|
|
|
// allocate a chunk
|
|
(*ppht)->rgpchunk[0] = (PCHUNK) Calloc(1, sizeof(CHUNK));
|
|
|
|
// allocate elements for a chunk
|
|
(*ppht)->rgpchunk[0]->rgpelement =
|
|
(PELEMENT *) Calloc(celementInChunk, sizeof(PELEMENT));
|
|
}
|
|
|
|
VOID
|
|
Free_HT (
|
|
IN OUT PPHT ppht
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Free's up the hash table.
|
|
|
|
Arguments:
|
|
|
|
ppht - pointer to a pointer to the hash table
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
|
|
{
|
|
PELEMENT pelement, pelementNext;
|
|
DWORD ibucket;
|
|
DWORD iChunk, iChunkOld;
|
|
PHT pht;
|
|
|
|
assert(ppht);
|
|
assert(*ppht);
|
|
pht = *ppht;
|
|
|
|
// free all the elements & the array of ptrs to elements
|
|
iChunkOld = 0;
|
|
for (ibucket = 0; ibucket < pht->cbuckets; ibucket++) {
|
|
assert(ibucket / pht->celementInChunk < pht->cchunkInDir);
|
|
iChunk = ibucket / pht->celementInChunk;
|
|
pelement = pht->rgpchunk[iChunk]->
|
|
rgpelement[ibucket % pht->celementInChunk];
|
|
|
|
// free all elements in this bucket
|
|
while (pelement) {
|
|
pelementNext = pelement->pelementNext;
|
|
// TEMPORARY: elements are allocated from permanent heap (and not individually)
|
|
// free(pelement);
|
|
pelement = pelementNext;
|
|
}
|
|
|
|
// free the array of ptrs to elements of chunk (previous) & chunk itself
|
|
if (iChunk > iChunkOld) {
|
|
// UNDONE: This memory isn't safe to free. It is allocated by
|
|
// UNDONE: Calloc().
|
|
|
|
free(pht->rgpchunk[iChunkOld]->rgpelement);
|
|
free(pht->rgpchunk[iChunkOld]);
|
|
iChunkOld++;
|
|
}
|
|
}
|
|
|
|
// UNDONE: This memory isn't safe to free. It is allocated by
|
|
// UNDONE: Calloc().
|
|
|
|
// handle last chunk
|
|
free(pht->rgpchunk[iChunkOld]->rgpelement);
|
|
free(pht->rgpchunk[iChunkOld]);
|
|
|
|
// free the hash table directory
|
|
free(pht->rgpchunk);
|
|
|
|
// free the hash table struct itself
|
|
free(pht);
|
|
|
|
// done
|
|
*ppht = NULL;
|
|
}
|
|
|
|
|
|
__inline DWORD UlHash_HT(
|
|
const char *Name,
|
|
PHT pht)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Hash a name and return an unsigned long reflecting the name.
|
|
|
|
Arguments:
|
|
|
|
Name - pointer to symbol name to hash
|
|
|
|
ppht - pointer to a pointer to the hash table to hash into
|
|
|
|
Return Value:
|
|
|
|
hash value
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD ulHash;
|
|
DWORD ulK;
|
|
DWORD ulAddress;
|
|
CONST DWORD ulPrime = 1048583; // magic prime constant, see header
|
|
const BYTE *pb;
|
|
|
|
assert(Name);
|
|
assert(pht);
|
|
|
|
// hash function, this can be changed to tweak performance
|
|
for(pb = (BYTE *) Name, ulHash = 0; *pb;) {
|
|
ulHash = (ulHash << 2) + *pb++;
|
|
if ((ulK = ulHash & 0xc000) != 0) {
|
|
ulHash ^= (ulK >> 11);
|
|
ulHash ^= ulK;
|
|
}
|
|
|
|
ulHash ^= (ulHash << 5) + (ulHash >> 3);
|
|
}
|
|
ulHash %= ulPrime;
|
|
|
|
// account for possible grown hash table
|
|
ulAddress = ulHash % pht->iNextToSplitMax;
|
|
|
|
if (ulAddress < pht->iNextToSplit) {
|
|
ulAddress = ulHash % (pht->iNextToSplitMax * 2);
|
|
}
|
|
|
|
return (ulAddress);
|
|
}
|
|
|
|
PELEMENT PelementLookup_HT(
|
|
const char *Name,
|
|
HT *pht,
|
|
BOOL fAllocNew,
|
|
PVOID pvBlk,
|
|
PBOOL pfNew)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Lookup Name in the symbol table and return its record. If Name is not
|
|
found and fAllocNew == 1 allocate a new ELEMENT and blast it in, otherwise
|
|
return NULL;
|
|
|
|
Arguments:
|
|
|
|
Name - pointer to symbol name to hash
|
|
|
|
pht - pointer to a pointer to the hash table to hash into
|
|
|
|
fAllocNew - allocate a new element if fAllocNew == 1 and the element does
|
|
does not already exist
|
|
|
|
pfNew - *pfNew set to !0 iff Name was not found
|
|
- set on entry of desired
|
|
|
|
Return Value:
|
|
|
|
pointer to the generic contents of a hashtable element
|
|
|
|
--*/
|
|
|
|
{
|
|
ELEMENT **ppelementFirst;
|
|
ELEMENT *pelement;
|
|
DWORD ulAddress;
|
|
DWORD iDirectory;
|
|
DWORD iChunk;
|
|
DWORD ulLoad;
|
|
const char *sz;
|
|
|
|
assert(pht);
|
|
assert(Name);
|
|
|
|
// calculate the load or average chain length
|
|
assert(pht->cbuckets);
|
|
ulLoad = (pht->celements << 4) / pht->cbuckets;
|
|
|
|
// If the load is greater than an arbitrary threshold, grow the
|
|
// table. 1 is an arbitrary constant and can be adjusted.
|
|
// This MUST be done before the new element is put into the table since
|
|
// this would put an element without a pv pointer. If Expand_HT() compares
|
|
// an elements contents based on the method pht->SzFromPv we will assert.
|
|
|
|
if (ulLoad > 48) {
|
|
Expand_HT(pht, pvBlk);
|
|
}
|
|
|
|
// get bucket
|
|
ulAddress = UlHash_HT(Name, pht);
|
|
iDirectory = ulAddress / pht->celementInChunk;
|
|
iChunk = ulAddress % pht->celementInChunk;
|
|
assert(iDirectory < pht->cchunkInDir);
|
|
assert(iChunk < pht->celementInChunk);
|
|
ppelementFirst = &(pht->rgpchunk[iDirectory]->rgpelement[iChunk]);
|
|
assert(ppelementFirst);
|
|
|
|
// search the buckets
|
|
pelement = *ppelementFirst;
|
|
while (pelement) {
|
|
sz = pht->SzFromPv(pelement->pv, pvBlk);
|
|
assert(sz != NULL);
|
|
|
|
if (!strcmp(Name, sz)) {
|
|
// found it
|
|
return pelement;
|
|
}
|
|
|
|
pelement = pelement->pelementNext;
|
|
}
|
|
|
|
if (!fAllocNew) {
|
|
// return without allocating new element
|
|
return (NULL);
|
|
}
|
|
|
|
assert(!(GetStatus_HT(pht) & HT_InsertsNotAllowed));
|
|
|
|
// set to new element
|
|
*pfNew = 1;
|
|
|
|
// element doesn't exist, so blast it in
|
|
pelement = fINCR ? (ELEMENT *) Malloc(sizeof(ELEMENT)) :
|
|
(ELEMENT *) ALLOC_PERM(sizeof(ELEMENT));
|
|
assert(pelement);
|
|
memset(pelement, 0, sizeof(ELEMENT));
|
|
|
|
pelement->pelementNext = *ppelementFirst;
|
|
(*ppelementFirst) = pelement;
|
|
pht->celements++;
|
|
|
|
return (pelement);
|
|
}
|
|
|
|
static VOID
|
|
Expand_HT(
|
|
PHT pht,
|
|
PVOID pvBlk
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Expand the hash table if possible. The only thing that would hamper
|
|
the address space of the hash table from being expanded is if the
|
|
underlying dynamic array structure is full.
|
|
|
|
Arguments:
|
|
|
|
pht - pointer to the hash table to expand
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD iNewAddress;
|
|
DWORD iOldChunk;
|
|
DWORD iNewChunk;
|
|
ELEMENT *pelementCur;
|
|
ELEMENT *pelementPrev;
|
|
ELEMENT *pelementLastOfNew;
|
|
CHUNK *pchunkOld;
|
|
CHUNK *pchunkNew;
|
|
|
|
assert(pht);
|
|
assert(!(GetStatus_HT(pht) & HT_Full));
|
|
|
|
// see if we have reached the maximum size of the table
|
|
if (!((pht->iNextToSplit + pht->iNextToSplitMax) <
|
|
(pht->cchunkInDir * pht->celementInChunk))) {
|
|
SetStatus_HT(pht, (WORD)(GetStatus_HT(pht) | HT_Full));
|
|
return;
|
|
}
|
|
|
|
pht->cExpands++;
|
|
|
|
// locate the bucket to be split
|
|
assert(pht->cchunkInDir);
|
|
assert(pht->celementInChunk);
|
|
assert((pht->iNextToSplit / pht->celementInChunk) < pht->cchunkInDir);
|
|
pchunkOld = pht->rgpchunk[pht->iNextToSplit / pht->celementInChunk];
|
|
assert(pchunkOld);
|
|
iOldChunk = pht->iNextToSplit % pht->celementInChunk;
|
|
|
|
// expand the address space and if necessary allocate a new chunk
|
|
iNewAddress = pht->iNextToSplitMax + pht->iNextToSplit;
|
|
assert(pht->rgpchunk);
|
|
if (iNewAddress % pht->celementInChunk == 0) {
|
|
assert((iNewAddress / pht->celementInChunk) < pht->cchunkInDir);
|
|
pht->rgpchunk[iNewAddress / pht->celementInChunk] =
|
|
(PCHUNK) Calloc(1, sizeof(CHUNK));
|
|
|
|
pht->rgpchunk[iNewAddress / pht->celementInChunk]->rgpelement =
|
|
(PELEMENT *) Calloc(pht->celementInChunk, sizeof(PELEMENT));
|
|
}
|
|
|
|
assert((iNewAddress / pht->celementInChunk) < pht->cchunkInDir);
|
|
pchunkNew = pht->rgpchunk[iNewAddress / pht->celementInChunk];
|
|
assert(pchunkNew);
|
|
iNewChunk = iNewAddress % pht->celementInChunk;
|
|
|
|
// adjust the state variables
|
|
pht->iNextToSplit++;
|
|
if (pht->iNextToSplit == pht->iNextToSplitMax) {
|
|
pht->iNextToSplitMax *= 2;
|
|
pht->iNextToSplit = 0;
|
|
}
|
|
|
|
pht->cbuckets++;
|
|
|
|
// relocate records to the new bucket
|
|
assert(iOldChunk < pht->celementInChunk);
|
|
pelementCur = pchunkOld->rgpelement[iOldChunk];
|
|
pelementPrev = NULL;
|
|
pelementLastOfNew = NULL;
|
|
assert(pchunkNew->rgpelement);
|
|
assert(iNewChunk < pht->celementInChunk);
|
|
pchunkNew->rgpelement[iNewChunk] = NULL;
|
|
|
|
while (pelementCur) {
|
|
assert(pelementCur);
|
|
if (UlHash_HT(pht->SzFromPv(pelementCur->pv, pvBlk), pht) == iNewAddress) {
|
|
if (pelementLastOfNew == NULL) {
|
|
assert(iNewChunk < pht->celementInChunk);
|
|
assert(pchunkNew);
|
|
pchunkNew->rgpelement[iNewChunk] = pelementCur;
|
|
} else {
|
|
assert(pelementLastOfNew);
|
|
pelementLastOfNew->pelementNext = pelementCur;
|
|
}
|
|
|
|
if (pelementPrev == NULL) {
|
|
assert(iOldChunk < pht->celementInChunk);
|
|
assert(pchunkOld);
|
|
assert(pelementCur);
|
|
assert(pchunkOld->rgpelement);
|
|
pchunkOld->rgpelement[iOldChunk] = pelementCur->pelementNext;
|
|
} else {
|
|
assert(pelementPrev);
|
|
assert(pelementCur);
|
|
pelementPrev->pelementNext = pelementCur->pelementNext;
|
|
}
|
|
|
|
pelementLastOfNew = pelementCur;
|
|
pelementCur = pelementCur->pelementNext;
|
|
pelementLastOfNew->pelementNext = NULL;
|
|
} else {
|
|
pelementPrev = pelementCur;
|
|
pelementCur = pelementCur->pelementNext;
|
|
}
|
|
}
|
|
}
|
|
|
|
#if DBG
|
|
|
|
VOID
|
|
Statistics_HT(
|
|
HT *pht)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Dump statistics of the hashtable use to stdout. This is a debug
|
|
routine.
|
|
|
|
Arguments:
|
|
|
|
pht - pointer to the hash table to dump statistics on
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD rgulChainCounts[] = {0L, 0L, 0L, 0L, 0L, 0L, 0L};
|
|
DWORD ulChainMax = 0;
|
|
DWORD ulThis;
|
|
DWORD iChunk;
|
|
DWORD iDir;
|
|
DWORD i;
|
|
ELEMENT *pelement;
|
|
CHUNK *pchunk;
|
|
|
|
for (i = 0; i < pht->cbuckets; i++) {
|
|
iDir = i / pht->celementInChunk;
|
|
iChunk = i % pht->celementInChunk;
|
|
pchunk = pht->rgpchunk[0];
|
|
pelement = pchunk->rgpelement[iChunk];
|
|
ulThis = 0;
|
|
while (pelement) {
|
|
ulThis++;
|
|
pelement = pelement->pelementNext;
|
|
}
|
|
|
|
if (ulThis < 6) {
|
|
rgulChainCounts[ulThis]++;
|
|
} else {
|
|
rgulChainCounts[6]++;
|
|
}
|
|
|
|
if (ulThis > ulChainMax) {
|
|
ulChainMax = ulThis;
|
|
}
|
|
}
|
|
|
|
DBPRINT("\nHash Table Statistics\n");
|
|
DBPRINT("---------------------\n");
|
|
DBPRINT("celementsInChunk .... %lu\n", pht->celementInChunk);
|
|
DBPRINT("cchunksInDir ........ %lu\n", pht->cchunkInDir);
|
|
DBPRINT("# elements .......... %lu\n", pht->celements);
|
|
DBPRINT("# buckets ........... %lu\n", pht->cbuckets);
|
|
assert(pht->cbuckets);
|
|
DBPRINT("load ................ %f\n",
|
|
(float) pht->celements / (float) pht->cbuckets);
|
|
DBPRINT("# table expands ..... %lu\n", pht->cExpands);
|
|
DBPRINT("maximum bucket size . %lu\n", ulChainMax);
|
|
DBPRINT("flags................ %u\n", pht->flags);
|
|
DBPRINT("# buckets of size 0 . %lu\n", rgulChainCounts[0]);
|
|
DBPRINT("# buckets of size 1 . %lu\n", rgulChainCounts[1]);
|
|
DBPRINT("# buckets of size 2 . %lu\n", rgulChainCounts[2]);
|
|
DBPRINT("# buckets of size 3 . %lu\n", rgulChainCounts[3]);
|
|
DBPRINT("# buckets of size 4 . %lu\n", rgulChainCounts[4]);
|
|
DBPRINT("# buckets of size 5 . %lu\n", rgulChainCounts[5]);
|
|
DBPRINT("# buckets over 5 .... %lu\n\n", rgulChainCounts[6]);
|
|
fflush(stdout);
|
|
}
|
|
|
|
#endif // DBG
|
|
|
|
VOID
|
|
InitEnumeration_HT(
|
|
PHT pht)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initialize the enumeration of a hashtable.
|
|
|
|
Arguments:
|
|
|
|
pht - pointer to the hash table to enumerate
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
|
|
{
|
|
PSTATE pstate;
|
|
|
|
pstate = pht->pstateStack;
|
|
pht->pstateStack = (PSTATE) PvAllocZ(sizeof(STATE));
|
|
pht->pstateStack->pstateNext = pstate;
|
|
pht->pstateStack->iLast = 0L;
|
|
pht->pstateStack->cFound = 0L;
|
|
pht->pstateStack->pelementLast = NULL;
|
|
}
|
|
|
|
PELEMENT
|
|
PelementEnumerateNext_HT(
|
|
PHT pht)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Get the next element in the enumeration of a hash table.
|
|
|
|
Arguments:
|
|
|
|
pht - pointer to the hash table to enumerate
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
|
|
{
|
|
#define iLastS (pht->pstateStack->iLast)
|
|
#define pelementLastS (pht->pstateStack->pelementLast)
|
|
#define cFoundS (pht->pstateStack->cFound)
|
|
|
|
ELEMENT *pelement;
|
|
|
|
assert(pht);
|
|
assert(pht->pstateStack);
|
|
|
|
if (cFoundS >= pht->celements) {
|
|
// we completed the enumeration
|
|
return NULL;
|
|
}
|
|
|
|
// check the next element in the bucket
|
|
pelement = pelementLastS;
|
|
if (pelement) {
|
|
// there was someone in the bucket, got it
|
|
pelementLastS = pelement->pelementNext;
|
|
cFoundS++;
|
|
} else {
|
|
// there wasn't anyone in the bucket, find another bucket
|
|
for (;;) {
|
|
if (iLastS >= pht->cbuckets) {
|
|
// there are no more buckets to enumerate
|
|
return NULL;
|
|
}
|
|
|
|
// calculate the next bucket
|
|
assert(pht->rgpchunk[iLastS / pht->celementInChunk]);
|
|
pelement = pht->rgpchunk[iLastS / pht->celementInChunk]->
|
|
rgpelement[iLastS % pht->celementInChunk];
|
|
|
|
// increment to the next bucket
|
|
iLastS++;
|
|
|
|
if (pelement) {
|
|
// found a bucket with elements in it, got it
|
|
cFoundS++;
|
|
pelementLastS = pelement->pelementNext;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return (pelement);
|
|
|
|
#undef iLastS
|
|
#undef pelementLastS
|
|
#undef cFoundS
|
|
}
|
|
|
|
VOID
|
|
TerminateEnumerate_HT(
|
|
PHT pht)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Terminate and enumeration and free up a state.
|
|
|
|
Arguments:
|
|
|
|
pht - hast table
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
|
|
{
|
|
PSTATE pstate;
|
|
|
|
assert(pht);
|
|
pstate = pht->pstateStack;
|
|
assert(pstate);
|
|
pht->pstateStack = pstate->pstateNext;
|
|
FreePv(pstate);
|
|
}
|
|
|
|
DWORD
|
|
Celement_HT(
|
|
PHT pht)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Return the number of elements in a hash table.
|
|
|
|
Arguments:
|
|
|
|
pht - hast table
|
|
|
|
Return Value:
|
|
|
|
0 if hash table is non-empty, !0 otherwise
|
|
|
|
--*/
|
|
|
|
{
|
|
assert(pht);
|
|
return (pht->celements);
|
|
}
|
|
|
|
#if DBG
|
|
|
|
VOID
|
|
Dump_HT(
|
|
PHT pht,
|
|
PVOID pvBlk)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Dump a hash table to standard out. This is a debug routine.
|
|
|
|
Arguments:
|
|
|
|
pht - hast table
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PELEMENT pelement;
|
|
DWORD ibucket;
|
|
|
|
assert(pht);
|
|
|
|
DBPRINT("beginning dump of hash table\n");
|
|
DBPRINT("----------------------------\n");
|
|
for (ibucket = 0; ibucket < pht->cbuckets; ibucket++) {
|
|
assert(ibucket / pht->celementInChunk < pht->cchunkInDir);
|
|
pelement = pht->rgpchunk[ibucket / pht->celementInChunk]->
|
|
rgpelement[ibucket % pht->celementInChunk];
|
|
DBPRINT("bucket = %u\n", ibucket);
|
|
while (pelement) {
|
|
DBPRINT(" %s\n", pht->SzFromPv(pelement->pv, pvBlk));
|
|
pelement = pelement->pelementNext;
|
|
}
|
|
}
|
|
DBPRINT("-------------------------\n");
|
|
DBPRINT("ending dump of hash table\n\n");
|
|
}
|
|
|
|
#endif // DBG
|