Update NEWS for 1.6.22
[pkg-k5-afs_openafs.git] / src / WINNT / afsapplib / hashlist.cpp
blob5c17ffaa101067700339be6ca448a6fcec0d462c
1 /*
2 * Copyright 2000, International Business Machines Corporation and others.
3 * All Rights Reserved.
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
8 */
10 extern "C" {
11 #include <afs/param.h>
12 #include <afs/stds.h>
15 #include <WINNT/TaLocale.h>
19 * HashList - super-quick list manager
23 #include <windows.h>
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
42 // multiple of 4.
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 ____________________________________________________________________
78 #ifdef REALLOC
79 #undef REALLOC
80 #endif
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)
84 LPVOID pNew;
85 size_t cNew;
87 if (cReq <= *pcTarget)
88 return TRUE;
90 if ((cNew = cInc * ((cReq + cInc-1) / cInc)) <= 0)
91 return FALSE;
93 if ((pNew = (LPVOID)GlobalAlloc (GMEM_FIXED, cbElement * cNew)) == NULL)
94 return FALSE;
96 if (*pcTarget == 0)
97 memset (pNew, 0x00, cbElement * cNew);
98 else
100 memcpy (pNew, *ppTarget, cbElement * (*pcTarget));
101 memset (&((char*)pNew)[ cbElement * (*pcTarget) ], 0x00, cbElement * (cNew - *pcTarget));
102 GlobalFree ((HGLOBAL)*ppTarget);
105 *ppTarget = pNew;
106 *pcTarget = cNew;
107 return TRUE;
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;
126 m_cHeaps = 0;
127 m_aHeaps = 0;
130 EXPANDARRAY::~EXPANDARRAY (void)
132 if (m_aHeaps)
134 for (size_t ii = 0; ii < m_cHeaps; ++ii)
136 if (m_aHeaps[ 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]))
148 return NULL;
150 PVOID pElement;
151 pElement = &((PBYTE)m_aHeaps[ iHeap ]->aElements)[ iIndex * m_cbElement ];
152 return pElement;
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))
161 return NULL;
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)
167 return NULL;
168 memset (m_aHeaps[ iHeap ], 0x00, cbHeap);
169 m_aHeaps[ iHeap ]->aElements = ((PBYTE)m_aHeaps[ iHeap ]) + sizeof(EXPANDARRAYHEAP);
172 PVOID pElement;
173 pElement = &((PBYTE)m_aHeaps[ iHeap ]->aElements)[ iIndex * m_cbElement ];
174 if (pData)
175 memcpy (pElement, pData, m_cbElement);
176 return pElement;
181 * HASHLIST CLASS _____________________________________________________________
185 #define GetEntry(_pArray,_iEntry) ((LPHASHLISTENTRY)((_pArray)->GetAt(_iEntry)))
187 HASHLIST::HASHLIST (void)
189 InitializeCriticalSection (&m_cs);
190 m_pcs = &m_cs;
191 Enter();
193 m_iFirst = iINVALID;
194 m_iLast = iINVALID;
195 m_iNextFree = 0;
196 m_aObjects = new EXPANDARRAY (sizeof(HASHLISTENTRY), cREALLOC_OBJECTS);
197 m_cObjects = 0;
198 m_cObjectsMax = 0;
199 m_apKeys = NULL;
200 m_cpKeys = 0;
202 m_pKeyIndex = new HASHLISTKEY (this, TEXT("Index"), HASHLIST::KeyIndex_CompareObjectData, HASHLIST::KeyIndex_HashObject, HASHLIST::KeyIndex_HashData, cTABLESIZE_DEFAULT);
204 Leave();
208 HASHLIST::~HASHLIST (void)
210 Enter();
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);
222 if (m_pKeyIndex)
224 delete m_pKeyIndex;
227 if (m_aObjects != NULL)
229 delete m_aObjects;
232 Leave();
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))) )
260 return TRUE;
263 return Add (pEntryToAdd);
267 BOOL HASHLIST::Add (PVOID pEntryToAdd)
269 BOOL rc = FALSE;
270 Enter();
272 if (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) )
281 break;
285 m_iNextFree = 1+iObject;
287 HASHLISTENTRY Entry;
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;
300 m_iLast = iObject;
301 if (m_iFirst == iINVALID)
302 m_iFirst = iObject;
304 m_pKeyIndex->Add (iObject, (PVOID)(iObject+1));
306 for (size_t iKey = 0; iKey < m_cpKeys; ++iKey)
308 if (m_apKeys[ iKey ] == NULL)
309 continue;
310 m_apKeys[ iKey ]->Add (iObject, pEntryToAdd);
313 ++m_cObjects;
314 m_cObjectsMax = max (m_cObjectsMax, m_cObjects);
315 rc = TRUE;
318 Leave();
319 return rc;
323 void HASHLIST::Remove (PVOID pEntryToRemove)
325 Enter();
327 if (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)
336 // {
337 // if (m_aObjects[ iObject ].pObject == pEntryToRemove)
338 // break;
339 // }
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
346 // is it fast...
348 size_t iObject = iINVALID;
350 PVOID pFind;
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)
384 continue;
385 m_apKeys[ iKey ]->Remove (iObject);
388 m_pKeyIndex->Remove (iObject);
389 --m_cObjects;
394 Leave();
398 BOOL HASHLIST::Update (PVOID pEntryToUpdate)
400 BOOL rc = TRUE;
401 Enter();
403 PVOID pFind;
404 if ((pFind = m_pKeyIndex->GetFirstObject (pEntryToUpdate)) == NULL)
405 rc = FALSE;
406 else
408 size_t iObject = *(size_t *)&pFind -1;
410 for (size_t iKey = 0; iKey < m_cpKeys; ++iKey)
412 if (m_apKeys[ iKey ] == NULL)
413 continue;
415 m_apKeys[ iKey ]->Remove (iObject);
416 m_apKeys[ iKey ]->Add (iObject, pEntryToUpdate);
420 Leave();
421 return rc;
425 BOOL HASHLIST::fIsInList (PVOID pEntry)
427 PVOID pFind;
428 if ((pFind = m_pKeyIndex->GetFirstObject (pEntry)) == NULL)
429 return FALSE;
430 return TRUE;
434 LPHASHLISTKEY HASHLIST::CreateKey (LPCTSTR pszKeyName, LPHASHFUNC_COMPAREOBJECTDATA fnCompareObjectData, LPHASHFUNC_HASHOBJECT fnHashObject, LPHASHFUNC_HASHDATA fnHashData, size_t cTableSize)
436 LPHASHLISTKEY pKey = NULL;
437 Enter();
439 size_t iKey;
440 for (iKey = 0; iKey < m_cpKeys; ++iKey)
442 if (m_apKeys[ iKey ] == NULL)
443 break;
445 if (REALLOC (m_apKeys, m_cpKeys, 1+iKey, cREALLOC_KEYS))
447 m_apKeys[ iKey ] = new HASHLISTKEY (this, pszKeyName, fnCompareObjectData, fnHashObject, fnHashData, cTableSize);
448 pKey = m_apKeys[ iKey ];
450 if (MIN_TABLE_SIZE(m_cObjectsMax) > pKey->m_cBuckets)
452 pKey->Resize();
454 else for (size_t iObject = 0; iObject < m_cObjectsMax; ++iObject)
456 LPHASHLISTENTRY pEntry;
457 if ((pEntry = GetEntry(m_aObjects,iObject)) == NULL)
458 continue;
459 if (pEntry->pObject != NULL)
460 m_apKeys[ iKey ]->Add (iObject, pEntry->pObject);
464 Leave();
465 return pKey;
469 LPHASHLISTKEY HASHLIST::FindKey (LPCTSTR pszKeyName)
471 LPHASHLISTKEY pKey = NULL;
472 Enter();
474 for (size_t iKey = 0; !pKey && (iKey < m_cpKeys); ++iKey)
476 if (m_apKeys[ iKey ] == NULL)
477 continue;
478 if (!lstrcmpi (m_apKeys[ iKey ]->m_szKeyName, pszKeyName))
479 pKey = m_apKeys[ iKey ];
482 Leave();
483 return pKey;
487 void HASHLIST::RemoveKey (LPHASHLISTKEY pKey)
489 Enter();
491 for (size_t iKey = 0; iKey < m_cpKeys; ++iKey)
493 if (m_apKeys[ iKey ] == pKey)
495 delete m_apKeys[ iKey ];
496 m_apKeys[ iKey ] = NULL;
497 break;
501 Leave();
505 LPENUM HASHLIST::FindFirst (void)
507 LPENUM pEnum = NULL;
508 Enter();
510 if (m_iFirst != iINVALID)
511 pEnum = New2 (ENUMERATION,(this, m_iFirst));
513 Leave();
514 return pEnum;
518 LPENUM HASHLIST::FindLast (void)
520 LPENUM pEnum = NULL;
521 Enter();
523 if (m_iLast != iINVALID)
524 pEnum = New2 (ENUMERATION,(this, m_iLast));
526 Leave();
527 return pEnum;
531 PVOID HASHLIST::GetFirstObject (void)
533 PVOID pObject = NULL;
534 Enter();
536 if (m_iFirst != iINVALID)
537 pObject = GetEntry(m_aObjects,m_iFirst)->pObject;
539 Leave();
540 return pObject;
544 PVOID HASHLIST::GetLastObject (void)
546 PVOID pObject = NULL;
547 Enter();
549 if (m_iLast != iINVALID)
550 pObject = GetEntry(m_aObjects,m_iLast)->pObject;
552 Leave();
553 return pObject;
557 size_t HASHLIST::GetCount (void)
559 return m_cObjects;
563 BOOL CALLBACK HASHLIST::KeyIndex_CompareObjectData (LPHASHLISTKEY pKey, PVOID pObject, PVOID pData)
565 size_t iIndex = (size_t)pObject -1;
566 LPHASHLIST pList = pKey->GetHashList();
567 return (GetEntry(pList->m_aObjects,iIndex)->pObject == pData) ? TRUE : FALSE;
571 HASHVALUE CALLBACK HASHLIST::KeyIndex_HashObject (LPHASHLISTKEY pKey, PVOID pObject)
573 size_t iIndex = (size_t)pObject -1;
574 LPHASHLIST pList = pKey->GetHashList();
575 return KeyIndex_HashData (pKey, GetEntry(pList->m_aObjects,iIndex)->pObject);
579 HASHVALUE CALLBACK HASHLIST::KeyIndex_HashData (LPHASHLISTKEY pKey, PVOID pData)
581 return ((DWORD)pData) >> 4; // The "data" is the object's address.
586 * HASHLISTKEY CLASS _____________________________________________________________
590 HASHLISTKEY::HASHLISTKEY (LPHASHLIST pList, LPCTSTR pszKeyName, LPHASHFUNC_COMPAREOBJECTDATA fnCompareObjectData, LPHASHFUNC_HASHOBJECT fnHashObject, LPHASHFUNC_HASHDATA fnHashData, size_t cTableSize)
592 m_aObjects = new EXPANDARRAY (sizeof(HASHLISTENTRY), cREALLOC_OBJECTS);
594 m_cBuckets = (cTableSize == 0) ? cTABLESIZE_DEFAULT : cTableSize;
595 m_aBuckets = (struct HASHBUCKET *)GlobalAlloc (GMEM_FIXED, m_cBuckets * sizeof(struct HASHBUCKET));
596 for (size_t iBucket = 0; iBucket < m_cBuckets; ++iBucket)
598 m_aBuckets[ iBucket ].iFirst = iINVALID;
599 m_aBuckets[ iBucket ].iLast = iINVALID;
602 m_pList = pList;
603 lstrcpy (m_szKeyName, pszKeyName);
604 m_fnCompareObjectData = fnCompareObjectData;
605 m_fnHashObject = fnHashObject;
606 m_fnHashData = fnHashData;
610 HASHLISTKEY::~HASHLISTKEY (void)
612 m_pList->Enter();
614 for (size_t iObject = 0; iObject < m_pList->m_cObjectsMax; ++iObject)
616 if (!GetEntry(m_aObjects,iObject))
617 continue;
618 if (!GetEntry(m_aObjects,iObject)->pObject)
619 continue;
620 Remove (iObject);
623 if (m_aObjects)
624 delete m_aObjects;
625 if (m_aBuckets)
626 GlobalFree ((HGLOBAL)m_aBuckets);
628 m_pList->Leave();
632 LPHASHLIST HASHLISTKEY::GetHashList (void)
634 return m_pList;
638 BOOL HASHLISTKEY::CompareObjectData (PVOID pObject, PVOID pData)
640 return (*m_fnCompareObjectData)(this, pObject, pData);
643 HASHVALUE HASHLISTKEY::HashObject (PVOID pObject)
645 return ((*m_fnHashObject)(this, pObject)) % m_cBuckets;
648 HASHVALUE HASHLISTKEY::HashData (PVOID pData)
650 return ((*m_fnHashData)(this, pData)) % m_cBuckets;
654 LPENUM HASHLISTKEY::FindFirst (PVOID pData)
656 LPENUM pEnum = NULL;
657 m_pList->Enter();
659 HASHVALUE hvSearch = HashData (pData);
661 size_t iObject;
662 if ((iObject = m_aBuckets[ hvSearch ].iFirst) != iINVALID)
664 LPHASHLISTENTRY pEntry;
665 do {
666 if ((pEntry = GetEntry(m_aObjects,iObject)) == NULL)
667 break;
668 if (CompareObjectData (pEntry->pObject, pData))
670 pEnum = New2 (ENUMERATION,(m_pList, iObject, this, pData));
671 break;
673 } while ((iObject = pEntry->iNext) != iINVALID);
676 m_pList->Leave();
677 return pEnum;
681 LPENUM HASHLISTKEY::FindLast (PVOID pData)
683 LPENUM pEnum = NULL;
684 m_pList->Enter();
686 HASHVALUE hvSearch = HashData (pData);
688 size_t iObject;
689 if ((iObject = m_aBuckets[ hvSearch ].iLast) != iINVALID)
691 LPHASHLISTENTRY pEntry;
692 do {
693 if ((pEntry = GetEntry(m_aObjects,iObject)) == NULL)
694 break;
695 if (CompareObjectData (pEntry->pObject, pData))
697 pEnum = New2 (ENUMERATION,(m_pList, iObject, this, pData));
698 break;
700 } while ((iObject = pEntry->iPrevious) != iINVALID);
703 m_pList->Leave();
704 return pEnum;
708 PVOID HASHLISTKEY::GetFirstObject (PVOID pData)
710 PVOID pObject = NULL;
711 m_pList->Enter();
713 HASHVALUE hvSearch = HashData (pData);
715 size_t iObject;
716 if ((iObject = m_aBuckets[ hvSearch ].iFirst) != iINVALID)
718 LPHASHLISTENTRY pEntry;
719 do {
720 if ((pEntry = GetEntry(m_aObjects,iObject)) == NULL)
721 break;
722 if (CompareObjectData (pEntry->pObject, pData))
724 pObject = pEntry->pObject;
725 break;
727 } while ((iObject = pEntry->iNext) != iINVALID);
730 m_pList->Leave();
731 return pObject;
735 PVOID HASHLISTKEY::GetLastObject (PVOID pData)
737 PVOID pObject = NULL;
738 m_pList->Enter();
740 HASHVALUE hvSearch = HashData (pData);
742 size_t iObject;
743 if ((iObject = m_aBuckets[ hvSearch ].iLast) != iINVALID)
745 LPHASHLISTENTRY pEntry;
746 do {
747 if ((pEntry = GetEntry(m_aObjects,iObject)) == NULL)
748 break;
749 if (CompareObjectData (pEntry->pObject, pData))
751 pObject = pEntry->pObject;
752 break;
754 } while ((iObject = pEntry->iPrevious) != iINVALID);
757 m_pList->Leave();
758 return pObject;
762 BOOL HASHLISTKEY::fIsInList (PVOID pEntry)
764 PVOID pFind;
765 if ((pFind = GetFirstObject (pEntry)) == NULL)
766 return FALSE;
767 return TRUE;
771 void HASHLISTKEY::Add (size_t iObject, PVOID pObject)
773 m_pList->Enter();
775 if ( ((this == m_pList->m_pKeyIndex) && (MIN_TABLE_SIZE_FOR_KEY(m_pList->m_cObjectsMax) > m_cBuckets)) ||
776 ((this != m_pList->m_pKeyIndex) && (MIN_TABLE_SIZE(m_pList->m_cObjectsMax) > m_cBuckets)) )
778 Resize();
780 else
782 HASHVALUE hv = HashObject (pObject);
783 struct HASHBUCKET *pBucket = &m_aBuckets[ hv ];
785 HASHLISTENTRY Entry;
786 Entry.pObject = pObject;
787 Entry.hv = hv;
788 Entry.iNext = iINVALID;
789 Entry.iPrevious = pBucket->iLast;
790 m_aObjects->SetAt (iObject, &Entry);
792 pBucket->iLast = iObject;
794 if (Entry.iPrevious != iINVALID)
796 LPHASHLISTENTRY pPrevious;
797 if ((pPrevious = GetEntry(m_aObjects,Entry.iPrevious)) != NULL)
798 pPrevious->iNext = iObject;
801 if (pBucket->iFirst == iINVALID)
802 pBucket->iFirst = iObject;
805 m_pList->Leave();
809 void HASHLISTKEY::Remove (size_t iObject)
811 m_pList->Enter();
813 LPHASHLISTENTRY pEntry;
814 if ((pEntry = GetEntry(m_aObjects,iObject)) != NULL)
816 pEntry->pObject = NULL;
818 if (pEntry->iPrevious != iINVALID)
820 LPHASHLISTENTRY pPrevious;
821 if ((pPrevious = GetEntry(m_aObjects,pEntry->iPrevious)) != NULL)
822 pPrevious->iNext = pEntry->iNext;
825 if (pEntry->iNext != iINVALID)
827 LPHASHLISTENTRY pNext;
828 if ((pNext = GetEntry(m_aObjects,pEntry->iNext)) != NULL)
829 pNext->iPrevious = pEntry->iPrevious;
832 if (m_aBuckets[ pEntry->hv ].iLast == iObject)
833 m_aBuckets[ pEntry->hv ].iLast = pEntry->iPrevious;
834 if (m_aBuckets[ pEntry->hv ].iFirst == iObject)
835 m_aBuckets[ pEntry->hv ].iFirst = pEntry->iNext;
838 m_pList->Leave();
842 void HASHLISTKEY::Resize (void)
844 if (this == m_pList->m_pKeyIndex)
846 REALLOC (m_aBuckets, m_cBuckets, TARGET_TABLE_SIZE_FOR_KEY(m_pList->m_cObjectsMax), 1);
848 else
850 REALLOC (m_aBuckets, m_cBuckets, TARGET_TABLE_SIZE(m_pList->m_cObjectsMax), 1);
853 for (size_t iBucket = 0; iBucket < m_cBuckets; ++iBucket)
855 m_aBuckets[ iBucket ].iFirst = iINVALID;
856 m_aBuckets[ iBucket ].iLast = iINVALID;
859 size_t iObject;
860 for (iObject = 0; ; ++iObject)
862 LPHASHLISTENTRY pEntry;
863 if ((pEntry = GetEntry(m_aObjects,iObject)) == NULL)
864 break;
865 pEntry->pObject = NULL;
868 // Re-add the objects to this key. One caveat: if this is the
869 // hashlist's index key, then the format of the items is different--
870 // retain that difference.
872 if (this == m_pList->m_pKeyIndex)
874 for (iObject = 0; ; ++iObject)
876 LPHASHLISTENTRY pEntry;
877 if ((pEntry = GetEntry(m_pList->m_aObjects,iObject)) == NULL)
878 break;
879 if (pEntry->pObject != NULL)
880 Add (iObject, (PVOID)(iObject+1));
883 else // normal, user-defined key
885 for (iObject = 0; ; ++iObject)
887 LPHASHLISTENTRY pEntry;
888 if ((pEntry = GetEntry(m_pList->m_aObjects,iObject)) == NULL)
889 break;
890 if (pEntry->pObject != NULL)
891 Add (iObject, pEntry->pObject);
897 LPHASHLISTKEYDEBUGINFO HASHLISTKEY::GetDebugInfo (void)
899 m_pList->Enter();
901 LPHASHLISTKEYDEBUGINFO pInfo = new HASHLISTKEYDEBUGINFO;
902 memset (pInfo, 0x00, sizeof(HASHLISTKEYDEBUGINFO));
904 // Find out how full each bucket is.
906 REALLOC (pInfo->aBuckets, pInfo->cBuckets, m_cBuckets, 1);
908 size_t iBucket;
909 for (iBucket = 0; iBucket < pInfo->cBuckets; ++iBucket)
911 for (size_t iObject = m_aBuckets[ iBucket ].iFirst;
912 iObject != iINVALID;
913 iObject = GetEntry(m_aObjects,iObject)->iNext)
915 (pInfo->aBuckets[ iBucket ])++;
919 // Calculate a "percent effectiveness". This is a pretty fuzzy
920 // calculation; we want 100% if all buckets are the same size
921 // (plus or minus one element), and 0% if all buckets except one are
922 // empty but that one bucket has more than cBuckets elements. In
923 // between, we'll try to create a roughly linear gradient. The
924 // calculation is effectively the proportion of the number of
925 // objects which are evenly distributed to the number of objects
926 // overall.
928 if (pInfo->cBuckets == 0)
930 pInfo->perEffective = 100;
932 else
934 // We want an accurate count of objects, not the over-allocated
935 // count given by m_cObjectsMax.
937 size_t cObjects = 0;
938 for (iBucket = 0; iBucket < pInfo->cBuckets; ++iBucket)
939 cObjects += pInfo->aBuckets[ iBucket ];
941 if (cObjects == 0)
943 pInfo->perEffective = 100;
945 else
947 // Determine what "even distribution" means--that's pretty easy.
948 // We increase the count by one to indicate that slight unevenness
949 // will occur unless the number of objects is a multiple of the
950 // number of buckets.
952 size_t cPerfectLength = (cObjects / pInfo->cBuckets) + 1;
954 size_t cObjectsInPlace = 0;
955 for (iBucket = 0; iBucket < pInfo->cBuckets; ++iBucket)
956 cObjectsInPlace += min( pInfo->aBuckets[ iBucket ], cPerfectLength );
958 // Now calculating that percent effectiveness is easy. If everything
959 // is evenly distributed, cObjectsInPlace will == cObjects--and
960 // we want to call it 100%. If eveything is on one chain, then
961 // cObjectsInPlace will be really small compared to cObjects.
963 pInfo->perEffective = (WORD)(cObjectsInPlace * 100 / cObjects);
967 m_pList->Leave();
968 return pInfo;
972 void HASHLISTKEY::FreeDebugInfo (LPHASHLISTKEYDEBUGINFO pInfo)
974 if (pInfo->aBuckets)
975 GlobalFree ((HGLOBAL)(pInfo->aBuckets));
980 * ENUMERATION CLASS _____________________________________________________________
984 ENUMERATION::ENUMERATION (LPHASHLIST pList, size_t iObject, LPHASHLISTKEY pKey, PVOID pData)
986 m_pList = pList;
987 m_pKey = pKey;
988 m_pData = pData;
989 m_iObject = iObject;
991 PrepareWalk(); // finds m_iPrevious and m_iNext
993 m_pList->Enter();
997 ENUMERATION::~ENUMERATION (void)
999 m_pList->Leave();
1003 PVOID ENUMERATION::GetObject (void)
1005 return GetEntry(m_pList->m_aObjects,m_iObject)->pObject;
1009 LPENUMERATION ENUMERATION::FindNext (void)
1011 for (;;)
1013 m_iObject = m_iNext;
1014 PrepareWalk();
1016 if (m_iObject == iINVALID)
1017 break;
1019 if (m_pKey == NULL)
1020 return this;
1022 if (m_pKey->CompareObjectData (GetEntry(m_pList->m_aObjects,m_iObject)->pObject, m_pData))
1023 return this;
1026 Delete (this);
1027 return NULL;
1031 LPENUMERATION ENUMERATION::FindPrevious (void)
1033 for (;;)
1035 m_iObject = m_iPrevious;
1036 PrepareWalk();
1038 if (m_iObject == iINVALID)
1039 break;
1041 if (m_pKey == NULL)
1042 return this;
1044 if (m_pKey->CompareObjectData (GetEntry(m_pList->m_aObjects,m_iObject)->pObject, m_pData))
1045 return this;
1048 Delete (this);
1049 return NULL;
1053 void ENUMERATION::PrepareWalk (void)
1055 if (m_iObject == iINVALID)
1057 m_iPrevious = iINVALID;
1058 m_iNext = iINVALID;
1060 else if (m_pKey == NULL)
1062 m_iPrevious = GetEntry(m_pList->m_aObjects,m_iObject)->iPrevious;
1063 m_iNext = GetEntry(m_pList->m_aObjects,m_iObject)->iNext;
1065 else
1067 m_iPrevious = GetEntry(m_pKey->m_aObjects,m_iObject)->iPrevious;
1068 m_iNext = GetEntry(m_pKey->m_aObjects,m_iObject)->iNext;
1074 * GENERAL-PURPOSE ____________________________________________________________
1078 HASHVALUE HashString (LPCTSTR pszString)
1080 #ifdef UNICODE
1081 return HashUnicodeString (pszString);
1082 #else
1083 return HashAnsiString (pszString);
1084 #endif
1087 HASHVALUE HashAnsiString (LPCSTR pszStringA)
1089 HASHVALUE hv = 0;
1091 size_t cch;
1092 for (cch = lstrlenA(pszStringA); cch >= 4; pszStringA += 4, cch -= 4)
1093 hv += *(DWORD *)pszStringA;
1095 for (; cch; pszStringA++, cch--)
1096 hv += *pszStringA;
1098 return hv;
1101 HASHVALUE HashUnicodeString (LPWSTR pszStringW)
1103 HASHVALUE hv = 0;
1105 size_t cch;
1106 for (cch = lstrlenW(pszStringW); cch >= 2; pszStringW += 2, cch -= 2)
1108 hv += *(DWORD *)pszStringW; // since HIBYTE(*psz) is usually zero,
1109 hv = (hv >> 24) | (hv << 8); // rotate {hv} high-ward by 8 bits
1112 for (; cch; pszStringW++, cch--)
1113 hv += *pszStringW;
1115 return hv;