2 * Copyright 2000, International Business Machines Corporation and others.
5 * This software has been released under the terms of the IBM Public
6 * License. For details, see the LICENSE file in the top-level source
7 * directory or online at http://www.openafs.org/dl/license10.html
11 #include <afs/param.h>
15 #include <WINNT/TaLocale.h>
19 * HashList - super-quick list manager
24 #include <WINNT/hashlist.h>
28 * DEFINITIONS ________________________________________________________________
32 #define iINVALID ((size_t)-1)
34 // We over-allocate the m_aObjects[] arrays whenever one fills up,
35 // in order to perform fewer allocations. The allocation size
36 // is defined by the macro below:
38 #define cREALLOC_OBJECTS 8192
40 // We also over-allocate the m_aKeys[] array in a hashlist, but
41 // nothing so dramatic here. It's simply always allocated to a
44 #define cREALLOC_KEYS 4
46 // The hashtables within each HASHLISTKEY also automatically resize
47 // themselves whenever they're too small for the number of objects
48 // they're supporting. There are two algorithms: a slow progression
49 // for user-defined keys, and a rapid progression for the index key.
50 // The difference is utilized because the index key (the key used
51 // internally by each hashlist to provide object-address-to-index
52 // lookups for fast Remove() calls) responds well to huge hash table
53 // sizes (because it hashes on addresses, which are evenly
54 // distributed). User-defined keys don't always use hashing indexes
55 // that respond so well to additional hashtable size, so they generally
56 // end up with smaller hash tables (so as not to waste memory).
57 // The algorithms below cause the following progression (presuming
58 // the default starting size of 1000 buckets):
60 // Buckets in normal keys: Buckets in index key:
61 // 1000 (up to 30000 objects) 1000 (up to 23000 objects)
62 // 3000 (up to 90000 objects) 4600 (up to 105800 objects)
63 // 9000 (up to 270000 objects) 21160 (up to 486680 objects)
64 // 27000 (up to 810000 objects) 97336 (up to 2238728 objects)
66 #define MIN_TABLE_SIZE(_cObjects) ((_cObjects) / 30)
67 #define TARGET_TABLE_SIZE(_cObjects) ((_cObjects) / 10)
69 #define MIN_TABLE_SIZE_FOR_KEY(_cObjects) ((_cObjects) / 23)
70 #define TARGET_TABLE_SIZE_FOR_KEY(_cObjects) ((_cObjects) / 5)
74 * REALLOC ____________________________________________________________________
81 #define REALLOC(_a,_c,_r,_i) HashList_ReallocFunction ((LPVOID*)&_a,sizeof(*_a),&_c,_r,_i)
82 BOOL HashList_ReallocFunction (LPVOID *ppTarget, size_t cbElement, size_t *pcTarget, size_t cReq, size_t cInc)
87 if (cReq <= *pcTarget)
90 if ((cNew = cInc * ((cReq + cInc-1) / cInc)) <= 0)
93 if ((pNew = (LPVOID)GlobalAlloc (GMEM_FIXED, cbElement * cNew)) == NULL)
97 memset (pNew, 0x00, cbElement * cNew);
100 memcpy (pNew, *ppTarget, cbElement * (*pcTarget));
101 memset (&((char*)pNew)[ cbElement * (*pcTarget) ], 0x00, cbElement * (cNew - *pcTarget));
102 GlobalFree ((HGLOBAL)*ppTarget);
112 * EXPANDARRAY CLASS __________________________________________________________
116 #define cEXPANDARRAYHEAPELEMENTS 1024
117 #define cREALLOC_EXPANDARRAYHEAPS 16
119 EXPANDARRAY::EXPANDARRAY (size_t cbElement, size_t cElementsPerHeap)
121 if ((m_cbElement = cbElement) == 0)
122 m_cbElement = sizeof(DWORD);
123 if ((m_cElementsPerHeap = cElementsPerHeap) == 0)
124 m_cElementsPerHeap = cEXPANDARRAYHEAPELEMENTS;
130 EXPANDARRAY::~EXPANDARRAY (void)
134 for (size_t ii = 0; ii < m_cHeaps; ++ii)
137 GlobalFree ((HGLOBAL)(m_aHeaps[ ii ]));
139 GlobalFree ((HGLOBAL)m_aHeaps);
143 PVOID EXPANDARRAY::GetAt (size_t iElement)
145 size_t iHeap = iElement / m_cElementsPerHeap;
146 size_t iIndex = iElement % m_cElementsPerHeap;
147 if ((iHeap >= m_cHeaps) || (!m_aHeaps[iHeap]))
151 pElement = &((PBYTE)m_aHeaps[ iHeap ]->aElements)[ iIndex * m_cbElement ];
155 PVOID EXPANDARRAY::SetAt (size_t iElement, PVOID pData)
157 size_t iHeap = iElement / m_cElementsPerHeap;
158 size_t iIndex = iElement % m_cElementsPerHeap;
160 if (!REALLOC (m_aHeaps, m_cHeaps, 1+iHeap, cREALLOC_EXPANDARRAYHEAPS))
163 if (!m_aHeaps[ iHeap ])
165 size_t cbHeap = sizeof(EXPANDARRAYHEAP) + (m_cElementsPerHeap * m_cbElement);
166 if ((m_aHeaps[ iHeap ] = (LPEXPANDARRAYHEAP)GlobalAlloc (GMEM_FIXED, cbHeap)) == NULL)
168 memset (m_aHeaps[ iHeap ], 0x00, cbHeap);
169 m_aHeaps[ iHeap ]->aElements = ((PBYTE)m_aHeaps[ iHeap ]) + sizeof(EXPANDARRAYHEAP);
173 pElement = &((PBYTE)m_aHeaps[ iHeap ]->aElements)[ iIndex * m_cbElement ];
175 memcpy (pElement, pData, m_cbElement);
181 * HASHLIST CLASS _____________________________________________________________
185 #define GetEntry(_pArray,_iEntry) ((LPHASHLISTENTRY)((_pArray)->GetAt(_iEntry)))
187 HASHLIST::HASHLIST (void)
189 InitializeCriticalSection (&m_cs);
196 m_aObjects = new EXPANDARRAY (sizeof(HASHLISTENTRY), cREALLOC_OBJECTS);
202 m_pKeyIndex = new HASHLISTKEY (this, TEXT("Index"), HASHLIST::KeyIndex_CompareObjectData, HASHLIST::KeyIndex_HashObject, HASHLIST::KeyIndex_HashData, cTABLESIZE_DEFAULT);
208 HASHLIST::~HASHLIST (void)
212 if (m_apKeys != NULL)
214 for (size_t iKey = 0; iKey < m_cpKeys; ++iKey)
216 if (m_apKeys[ iKey ] != NULL)
217 delete m_apKeys[ iKey ];
219 GlobalFree ((HGLOBAL)m_apKeys);
227 if (m_aObjects != NULL)
233 DeleteCriticalSection (&m_cs);
237 void HASHLIST::Enter (void)
239 EnterCriticalSection (m_pcs);
243 void HASHLIST::Leave (void)
245 LeaveCriticalSection (m_pcs);
249 void HASHLIST::SetCriticalSection (CRITICAL_SECTION *pcs)
251 m_pcs = (pcs == NULL) ? &m_cs : pcs;
255 BOOL HASHLIST::AddUnique (PVOID pEntryToAdd, LPHASHLISTKEY pKey)
257 if ( ((pKey == NULL) && (fIsInList (pEntryToAdd))) ||
258 ((pKey != NULL) && (pKey->fIsInList (pEntryToAdd))) )
263 return Add (pEntryToAdd);
267 BOOL HASHLIST::Add (PVOID pEntryToAdd)
274 size_t iObject = m_iNextFree;
276 if ((GetEntry(m_aObjects,iObject) && (GetEntry(m_aObjects,iObject)->pObject)))
278 for (iObject = 0; iObject < m_cObjectsMax; ++iObject)
280 if ( (!GetEntry(m_aObjects,iObject)) || (!GetEntry(m_aObjects,iObject)->pObject) )
285 m_iNextFree = 1+iObject;
288 Entry.pObject = pEntryToAdd;
289 Entry.iNext = iINVALID;
290 Entry.iPrevious = m_iLast;
291 m_aObjects->SetAt (iObject, &Entry);
293 if (m_iLast != iINVALID)
295 LPHASHLISTENTRY pEntry;
296 if ((pEntry = GetEntry(m_aObjects,m_iLast)) != NULL)
297 pEntry->iNext = iObject;
301 if (m_iFirst == iINVALID)
304 m_pKeyIndex->Add (iObject, (PVOID)(iObject+1));
306 for (size_t iKey = 0; iKey < m_cpKeys; ++iKey)
308 if (m_apKeys[ iKey ] == NULL)
310 m_apKeys[ iKey ]->Add (iObject, pEntryToAdd);
314 m_cObjectsMax = max (m_cObjectsMax, m_cObjects);
323 void HASHLIST::Remove (PVOID pEntryToRemove)
329 // The first step is to find which m_aObjects entry corresponds
330 // with this object. Ordinarily that'd be a brute-force search;
331 // however, to speed things up, we have a special key placed on
332 // the address of the object for determining its index. It lets
333 // us replace this code:
335 // for (size_t iObject = 0; iObject < m_cObjectsMax; ++iObject)
337 // if (m_aObjects[ iObject ].pObject == pEntryToRemove)
341 // Because this index key uses a hashtable that's resized very
342 // aggressively, performance tests of up to 100,000 objects
343 // indicate that removing an object now clocks in as O(1), rather
344 // than the O(N) that it would be if we used the brute-force approach.
345 // At 100,000 objects it does use up some 70k of memory, but wow
348 size_t iObject = iINVALID;
351 if ((pFind = m_pKeyIndex->GetFirstObject (pEntryToRemove)) != NULL)
352 iObject = *(size_t *)&pFind -1;
354 // Now remove the object.
356 if (iObject != iINVALID)
358 LPHASHLISTENTRY pEntry;
359 if ((pEntry = GetEntry(m_aObjects,iObject)) != NULL)
361 pEntry->pObject = NULL;
363 if (pEntry->iPrevious != iINVALID)
365 LPHASHLISTENTRY pPrevious;
366 if ((pPrevious = GetEntry(m_aObjects,pEntry->iPrevious)) != NULL)
367 pPrevious->iNext = pEntry->iNext;
369 if (pEntry->iNext != iINVALID)
371 LPHASHLISTENTRY pNext;
372 if ((pNext = GetEntry(m_aObjects,pEntry->iNext)) != NULL)
373 pNext->iPrevious = pEntry->iPrevious;
376 if (m_iLast == iObject)
377 m_iLast = pEntry->iPrevious;
378 if (m_iFirst == iObject)
379 m_iFirst = pEntry->iNext;
381 for (size_t iKey = 0; iKey < m_cpKeys; ++iKey)
383 if (m_apKeys[ iKey ] == NULL)
385 m_apKeys[ iKey ]->Remove (iObject);
388 m_pKeyIndex->Remove (iObject);
398 BOOL HASHLIST::Update (PVOID pEntryToUpdate)
404 if ((pFind = m_pKeyIndex->GetFirstObject (pEntryToUpdate)) == NULL)
408 size_t iObject = *(size_t *)&pFind -1;
410 for (size_t iKey = 0; iKey < m_cpKeys; ++iKey)
412 if (m_apKeys[ iKey ] == NULL)
415 m_apKeys[ iKey ]->Remove (iObject);
416 m_apKeys[ iKey ]->Add (iObject, pEntryToUpdate);
425 BOOL HASHLIST::fIsInList (PVOID pEntry)
428 if ((pFind = m_pKeyIndex->GetFirstObject (pEntry)) == NULL)
434 LPHASHLISTKEY HASHLIST::CreateKey (LPCTSTR pszKeyName, LPHASHFUNC_COMPAREOBJECTDATA fnCompareObjectData, LPHASHFUNC_HASHOBJECT fnHashObject, LPHASHFUNC_HASHDATA fnHashData, size_t cTableSize)
436 LPHASHLISTKEY pKey = NULL;
439 for (size_t iKey = 0; iKey < m_cpKeys; ++iKey)
441 if (m_apKeys[ iKey ] == NULL)
444 if (REALLOC (m_apKeys, m_cpKeys, 1+iKey, cREALLOC_KEYS))
446 m_apKeys[ iKey ] = new HASHLISTKEY (this, pszKeyName, fnCompareObjectData, fnHashObject, fnHashData, cTableSize);
447 pKey = m_apKeys[ iKey ];
449 if (MIN_TABLE_SIZE(m_cObjectsMax) > pKey->m_cBuckets)
453 else for (size_t iObject = 0; iObject < m_cObjectsMax; ++iObject)
455 LPHASHLISTENTRY pEntry;
456 if ((pEntry = GetEntry(m_aObjects,iObject)) == NULL)
458 if (pEntry->pObject != NULL)
459 m_apKeys[ iKey ]->Add (iObject, pEntry->pObject);
468 LPHASHLISTKEY HASHLIST::FindKey (LPCTSTR pszKeyName)
470 LPHASHLISTKEY pKey = NULL;
473 for (size_t iKey = 0; !pKey && (iKey < m_cpKeys); ++iKey)
475 if (m_apKeys[ iKey ] == NULL)
477 if (!lstrcmpi (m_apKeys[ iKey ]->m_szKeyName, pszKeyName))
478 pKey = m_apKeys[ iKey ];
486 void HASHLIST::RemoveKey (LPHASHLISTKEY pKey)
490 for (size_t iKey = 0; iKey < m_cpKeys; ++iKey)
492 if (m_apKeys[ iKey ] == pKey)
494 delete m_apKeys[ iKey ];
495 m_apKeys[ iKey ] = NULL;
504 LPENUM HASHLIST::FindFirst (void)
509 if (m_iFirst != iINVALID)
510 pEnum = New2 (ENUMERATION,(this, m_iFirst));
517 LPENUM HASHLIST::FindLast (void)
522 if (m_iLast != iINVALID)
523 pEnum = New2 (ENUMERATION,(this, m_iLast));
530 PVOID HASHLIST::GetFirstObject (void)
532 PVOID pObject = NULL;
535 if (m_iFirst != iINVALID)
536 pObject = GetEntry(m_aObjects,m_iFirst)->pObject;
543 PVOID HASHLIST::GetLastObject (void)
545 PVOID pObject = NULL;
548 if (m_iLast != iINVALID)
549 pObject = GetEntry(m_aObjects,m_iLast)->pObject;
556 size_t HASHLIST::GetCount (void)
562 BOOL CALLBACK HASHLIST::KeyIndex_CompareObjectData (LPHASHLISTKEY pKey, PVOID pObject, PVOID pData)
564 size_t iIndex = (size_t)pObject -1;
565 LPHASHLIST pList = pKey->GetHashList();
566 return (GetEntry(pList->m_aObjects,iIndex)->pObject == pData) ? TRUE : FALSE;
570 HASHVALUE CALLBACK HASHLIST::KeyIndex_HashObject (LPHASHLISTKEY pKey, PVOID pObject)
572 size_t iIndex = (size_t)pObject -1;
573 LPHASHLIST pList = pKey->GetHashList();
574 return KeyIndex_HashData (pKey, GetEntry(pList->m_aObjects,iIndex)->pObject);
578 HASHVALUE CALLBACK HASHLIST::KeyIndex_HashData (LPHASHLISTKEY pKey, PVOID pData)
580 return ((DWORD)pData) >> 4; // The "data" is the object's address.
585 * HASHLISTKEY CLASS _____________________________________________________________
589 HASHLISTKEY::HASHLISTKEY (LPHASHLIST pList, LPCTSTR pszKeyName, LPHASHFUNC_COMPAREOBJECTDATA fnCompareObjectData, LPHASHFUNC_HASHOBJECT fnHashObject, LPHASHFUNC_HASHDATA fnHashData, size_t cTableSize)
591 m_aObjects = new EXPANDARRAY (sizeof(HASHLISTENTRY), cREALLOC_OBJECTS);
593 m_cBuckets = (cTableSize == 0) ? cTABLESIZE_DEFAULT : cTableSize;
594 m_aBuckets = (struct HASHBUCKET *)GlobalAlloc (GMEM_FIXED, m_cBuckets * sizeof(struct HASHBUCKET));
595 for (size_t iBucket = 0; iBucket < m_cBuckets; ++iBucket)
597 m_aBuckets[ iBucket ].iFirst = iINVALID;
598 m_aBuckets[ iBucket ].iLast = iINVALID;
602 lstrcpy (m_szKeyName, pszKeyName);
603 m_fnCompareObjectData = fnCompareObjectData;
604 m_fnHashObject = fnHashObject;
605 m_fnHashData = fnHashData;
609 HASHLISTKEY::~HASHLISTKEY (void)
613 for (size_t iObject = 0; iObject < m_pList->m_cObjectsMax; ++iObject)
615 if (!GetEntry(m_aObjects,iObject))
617 if (!GetEntry(m_aObjects,iObject)->pObject)
625 GlobalFree ((HGLOBAL)m_aBuckets);
631 LPHASHLIST HASHLISTKEY::GetHashList (void)
637 BOOL HASHLISTKEY::CompareObjectData (PVOID pObject, PVOID pData)
639 return (*m_fnCompareObjectData)(this, pObject, pData);
642 HASHVALUE HASHLISTKEY::HashObject (PVOID pObject)
644 return ((*m_fnHashObject)(this, pObject)) % m_cBuckets;
647 HASHVALUE HASHLISTKEY::HashData (PVOID pData)
649 return ((*m_fnHashData)(this, pData)) % m_cBuckets;
653 LPENUM HASHLISTKEY::FindFirst (PVOID pData)
658 HASHVALUE hvSearch = HashData (pData);
661 if ((iObject = m_aBuckets[ hvSearch ].iFirst) != iINVALID)
663 LPHASHLISTENTRY pEntry;
665 if ((pEntry = GetEntry(m_aObjects,iObject)) == NULL)
667 if (CompareObjectData (pEntry->pObject, pData))
669 pEnum = New2 (ENUMERATION,(m_pList, iObject, this, pData));
672 } while ((iObject = pEntry->iNext) != iINVALID);
680 LPENUM HASHLISTKEY::FindLast (PVOID pData)
685 HASHVALUE hvSearch = HashData (pData);
688 if ((iObject = m_aBuckets[ hvSearch ].iLast) != iINVALID)
690 LPHASHLISTENTRY pEntry;
692 if ((pEntry = GetEntry(m_aObjects,iObject)) == NULL)
694 if (CompareObjectData (pEntry->pObject, pData))
696 pEnum = New2 (ENUMERATION,(m_pList, iObject, this, pData));
699 } while ((iObject = pEntry->iPrevious) != iINVALID);
707 PVOID HASHLISTKEY::GetFirstObject (PVOID pData)
709 PVOID pObject = NULL;
712 HASHVALUE hvSearch = HashData (pData);
715 if ((iObject = m_aBuckets[ hvSearch ].iFirst) != iINVALID)
717 LPHASHLISTENTRY pEntry;
719 if ((pEntry = GetEntry(m_aObjects,iObject)) == NULL)
721 if (CompareObjectData (pEntry->pObject, pData))
723 pObject = pEntry->pObject;
726 } while ((iObject = pEntry->iNext) != iINVALID);
734 PVOID HASHLISTKEY::GetLastObject (PVOID pData)
736 PVOID pObject = NULL;
739 HASHVALUE hvSearch = HashData (pData);
742 if ((iObject = m_aBuckets[ hvSearch ].iLast) != iINVALID)
744 LPHASHLISTENTRY pEntry;
746 if ((pEntry = GetEntry(m_aObjects,iObject)) == NULL)
748 if (CompareObjectData (pEntry->pObject, pData))
750 pObject = pEntry->pObject;
753 } while ((iObject = pEntry->iPrevious) != iINVALID);
761 BOOL HASHLISTKEY::fIsInList (PVOID pEntry)
764 if ((pFind = GetFirstObject (pEntry)) == NULL)
770 void HASHLISTKEY::Add (size_t iObject, PVOID pObject)
774 if ( ((this == m_pList->m_pKeyIndex) && (MIN_TABLE_SIZE_FOR_KEY(m_pList->m_cObjectsMax) > m_cBuckets)) ||
775 ((this != m_pList->m_pKeyIndex) && (MIN_TABLE_SIZE(m_pList->m_cObjectsMax) > m_cBuckets)) )
781 HASHVALUE hv = HashObject (pObject);
782 struct HASHBUCKET *pBucket = &m_aBuckets[ hv ];
785 Entry.pObject = pObject;
787 Entry.iNext = iINVALID;
788 Entry.iPrevious = pBucket->iLast;
789 m_aObjects->SetAt (iObject, &Entry);
791 pBucket->iLast = iObject;
793 if (Entry.iPrevious != iINVALID)
795 LPHASHLISTENTRY pPrevious;
796 if ((pPrevious = GetEntry(m_aObjects,Entry.iPrevious)) != NULL)
797 pPrevious->iNext = iObject;
800 if (pBucket->iFirst == iINVALID)
801 pBucket->iFirst = iObject;
808 void HASHLISTKEY::Remove (size_t iObject)
812 LPHASHLISTENTRY pEntry;
813 if ((pEntry = GetEntry(m_aObjects,iObject)) != NULL)
815 pEntry->pObject = NULL;
817 if (pEntry->iPrevious != iINVALID)
819 LPHASHLISTENTRY pPrevious;
820 if ((pPrevious = GetEntry(m_aObjects,pEntry->iPrevious)) != NULL)
821 pPrevious->iNext = pEntry->iNext;
824 if (pEntry->iNext != iINVALID)
826 LPHASHLISTENTRY pNext;
827 if ((pNext = GetEntry(m_aObjects,pEntry->iNext)) != NULL)
828 pNext->iPrevious = pEntry->iPrevious;
831 if (m_aBuckets[ pEntry->hv ].iLast == iObject)
832 m_aBuckets[ pEntry->hv ].iLast = pEntry->iPrevious;
833 if (m_aBuckets[ pEntry->hv ].iFirst == iObject)
834 m_aBuckets[ pEntry->hv ].iFirst = pEntry->iNext;
841 void HASHLISTKEY::Resize (void)
843 if (this == m_pList->m_pKeyIndex)
845 REALLOC (m_aBuckets, m_cBuckets, TARGET_TABLE_SIZE_FOR_KEY(m_pList->m_cObjectsMax), 1);
849 REALLOC (m_aBuckets, m_cBuckets, TARGET_TABLE_SIZE(m_pList->m_cObjectsMax), 1);
852 for (size_t iBucket = 0; iBucket < m_cBuckets; ++iBucket)
854 m_aBuckets[ iBucket ].iFirst = iINVALID;
855 m_aBuckets[ iBucket ].iLast = iINVALID;
858 for (size_t iObject = 0; ; ++iObject)
860 LPHASHLISTENTRY pEntry;
861 if ((pEntry = GetEntry(m_aObjects,iObject)) == NULL)
863 pEntry->pObject = NULL;
866 // Re-add the objects to this key. One caveat: if this is the
867 // hashlist's index key, then the format of the items is different--
868 // retain that difference.
870 if (this == m_pList->m_pKeyIndex)
872 for (iObject = 0; ; ++iObject)
874 LPHASHLISTENTRY pEntry;
875 if ((pEntry = GetEntry(m_pList->m_aObjects,iObject)) == NULL)
877 if (pEntry->pObject != NULL)
878 Add (iObject, (PVOID)(iObject+1));
881 else // normal, user-defined key
883 for (iObject = 0; ; ++iObject)
885 LPHASHLISTENTRY pEntry;
886 if ((pEntry = GetEntry(m_pList->m_aObjects,iObject)) == NULL)
888 if (pEntry->pObject != NULL)
889 Add (iObject, pEntry->pObject);
895 LPHASHLISTKEYDEBUGINFO HASHLISTKEY::GetDebugInfo (void)
899 LPHASHLISTKEYDEBUGINFO pInfo = new HASHLISTKEYDEBUGINFO;
900 memset (pInfo, 0x00, sizeof(HASHLISTKEYDEBUGINFO));
902 // Find out how full each bucket is.
904 REALLOC (pInfo->aBuckets, pInfo->cBuckets, m_cBuckets, 1);
906 for (size_t iBucket = 0; iBucket < pInfo->cBuckets; ++iBucket)
908 for (size_t iObject = m_aBuckets[ iBucket ].iFirst;
910 iObject = GetEntry(m_aObjects,iObject)->iNext)
912 (pInfo->aBuckets[ iBucket ])++;
916 // Calculate a "percent effectiveness". This is a pretty fuzzy
917 // calculation; we want 100% if all buckets are the same size
918 // (plus or minus one element), and 0% if all buckets except one are
919 // empty but that one bucket has more than cBuckets elements. In
920 // between, we'll try to create a roughly linear gradient. The
921 // calculation is effectively the proportion of the number of
922 // objects which are evenly distributed to the number of objects
925 if (pInfo->cBuckets == 0)
927 pInfo->perEffective = 100;
931 // We want an accurate count of objects, not the over-allocated
932 // count given by m_cObjectsMax.
935 for (iBucket = 0; iBucket < pInfo->cBuckets; ++iBucket)
936 cObjects += pInfo->aBuckets[ iBucket ];
940 pInfo->perEffective = 100;
944 // Determine what "even distribution" means--that's pretty easy.
945 // We increase the count by one to indicate that slight unevenness
946 // will occur unless the number of objects is a multiple of the
947 // number of buckets.
949 size_t cPerfectLength = (cObjects / pInfo->cBuckets) + 1;
951 size_t cObjectsInPlace = 0;
952 for (iBucket = 0; iBucket < pInfo->cBuckets; ++iBucket)
953 cObjectsInPlace += min( pInfo->aBuckets[ iBucket ], cPerfectLength );
955 // Now calculating that percent effectiveness is easy. If everything
956 // is evenly distributed, cObjectsInPlace will == cObjects--and
957 // we want to call it 100%. If eveything is on one chain, then
958 // cObjectsInPlace will be really small compared to cObjects.
960 pInfo->perEffective = (WORD)(cObjectsInPlace * 100 / cObjects);
969 void HASHLISTKEY::FreeDebugInfo (LPHASHLISTKEYDEBUGINFO pInfo)
972 GlobalFree ((HGLOBAL)(pInfo->aBuckets));
977 * ENUMERATION CLASS _____________________________________________________________
981 ENUMERATION::ENUMERATION (LPHASHLIST pList, size_t iObject, LPHASHLISTKEY pKey, PVOID pData)
988 PrepareWalk(); // finds m_iPrevious and m_iNext
994 ENUMERATION::~ENUMERATION (void)
1000 PVOID ENUMERATION::GetObject (void)
1002 return GetEntry(m_pList->m_aObjects,m_iObject)->pObject;
1006 LPENUMERATION ENUMERATION::FindNext (void)
1010 m_iObject = m_iNext;
1013 if (m_iObject == iINVALID)
1019 if (m_pKey->CompareObjectData (GetEntry(m_pList->m_aObjects,m_iObject)->pObject, m_pData))
1028 LPENUMERATION ENUMERATION::FindPrevious (void)
1032 m_iObject = m_iPrevious;
1035 if (m_iObject == iINVALID)
1041 if (m_pKey->CompareObjectData (GetEntry(m_pList->m_aObjects,m_iObject)->pObject, m_pData))
1050 void ENUMERATION::PrepareWalk (void)
1052 if (m_iObject == iINVALID)
1054 m_iPrevious = iINVALID;
1057 else if (m_pKey == NULL)
1059 m_iPrevious = GetEntry(m_pList->m_aObjects,m_iObject)->iPrevious;
1060 m_iNext = GetEntry(m_pList->m_aObjects,m_iObject)->iNext;
1064 m_iPrevious = GetEntry(m_pKey->m_aObjects,m_iObject)->iPrevious;
1065 m_iNext = GetEntry(m_pKey->m_aObjects,m_iObject)->iNext;
1071 * GENERAL-PURPOSE ____________________________________________________________
1075 HASHVALUE HashString (LPCTSTR pszString)
1078 return HashUnicodeString (pszString);
1080 return HashAnsiString (pszString);
1084 HASHVALUE HashAnsiString (LPCSTR pszStringA)
1088 for (size_t cch = lstrlenA(pszStringA); cch >= 4; pszStringA += 4, cch -= 4)
1089 hv += *(DWORD *)pszStringA;
1091 for (; cch; pszStringA++, cch--)
1097 HASHVALUE HashUnicodeString (LPWSTR pszStringW)
1101 for (size_t cch = lstrlenW(pszStringW); cch >= 2; pszStringW += 2, cch -= 2)
1103 hv += *(DWORD *)pszStringW; // since HIBYTE(*psz) is usually zero,
1104 hv = (hv >> 24) | (hv << 8); // rotate {hv} high-ward by 8 bits
1107 for (; cch; pszStringW++, cch--)