Source code of Windows XP (NT5)
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.
|
|
/*++
Copyright (c) 1995 Microsoft Corporation
Module Name:
Table.cxx
Abstract:
Implementation of the CHashTable and CTableElement.
Author:
Mario Goertzel [MarioGo]
Revision History:
MarioGo 02-15-95 Bits 'n pieces MarioGo 12-18-95 Changed from UUID to generic object keys
--*/
#include<or.hxx>
CTableElement * CTableElement::RemoveMatching( IN CTableKey &tk, OUT CTableElement **ppRemoved) /*++
Routine Description:
Helper function used to remove an element from a bucket in the hash table.
Arguments:
tk - Key to find the element being removed.
ppRemoved - Contains the element removed or NULL upon return.
Return Value:
Pointer to the remaining elements in the list if any. Use to replace the current pointer in the bucket.
--*/ { CTableElement *pcurrent = this; CTableElement *psaved = 0; CTableElement *pfirst = this;
while(pcurrent) { if (pcurrent->Compare(tk)) { *ppRemoved = pcurrent; if (0 != psaved) { psaved->_pnext = pcurrent->_pnext; pcurrent->_pnext = 0; return(pfirst); } else { // removing the first element in the list
ASSERT(pcurrent == pfirst); psaved = pcurrent->_pnext; pcurrent->_pnext = 0; return(psaved); } }
psaved = pcurrent; pcurrent = pcurrent->_pnext; }
*ppRemoved = 0; return(pfirst); }
CTableElement * CTableElement::RemoveMatching( IN CTableElement *pte, OUT CTableElement **ppRemoved) /*++
Routine Description:
Helper function used to remove an element from a bucket in the hash table.
Arguments:
pte - Element to be removed, compared by pointer value.
ppRemoved - Contains the element removed or NULL upon return.
Return Value:
Pointer to the remaining elements in the list if any. Use to replace the current pointer in the bucket.
--*/ { CTableElement *pcurrent = this; CTableElement *psaved = 0; CTableElement *pfirst = this;
while(pcurrent) { if (pcurrent == pte) { *ppRemoved = pcurrent; if (0 != psaved) { psaved->_pnext = pcurrent->_pnext; pcurrent->_pnext = 0; return(pfirst); } else { // removing the first element in the list
ASSERT(pcurrent == pfirst); psaved = pcurrent->_pnext; pcurrent->_pnext = 0; return(psaved); } }
psaved = pcurrent; pcurrent = pcurrent->_pnext; }
*ppRemoved = 0; return(pfirst); }
CHashTable::CHashTable(ORSTATUS &status, UINT start_size) { _cBuckets = start_size; _cElements = 0; _last = 0;
_buckets = new CTableElement *[start_size];
if (0 == _buckets) { status = OR_NOMEM; return; }
for(UINT i = 0; i < _cBuckets; i++) { _buckets[i] = NULL; }
status = OR_OK; }
CHashTable::~CHashTable() { #if 0
#if DBG
for(UINT i = 0; i < _cBuckets; i++) ASSERT(_buckets[i] == 0); #endif
delete _buckets; #endif
ASSERT(0); // D'tor unused 12/95
}
CTableElement * CHashTable::Lookup( IN CTableKey &id ) { DWORD hash = id.Hash(); CTableElement *it;
it = _buckets[hash % _cBuckets];
while(it) { if (it->Compare(id)) { return(it); }
it = it->Next(); }
return(0); }
void CHashTable::Add( IN CTableElement *pElement ) { DWORD hash = pElement->Hash();
hash %= _cBuckets;
_buckets[hash] = _buckets[hash]->Insert(pElement);
_cElements++;
if (_cElements > _cBuckets) { // Try to grow the table. If the allocation fails no need to worry,
// everything still works but might be a bit slower.
CTableElement **ppte = new CTableElement *[_cBuckets * 2];
if (ppte) { UINT i, uiBucketsOld = _cBuckets; CTableElement *pteT1, *pteT2; CTableElement **ppteOld = _buckets;
KdPrintEx((DPFLTR_DCOMSS_ID, DPFLTR_INFO_LEVEL, "OR: Growing table: %p\n", this));
// Change to the larger array of buckets.
_cBuckets *= 2; for(i = 0; i < _cBuckets; i++) { ppte[i] = NULL; } _buckets = ppte;
// Move every element from the old table into the large table.
for(i = 0; i < uiBucketsOld; i++) { pteT1 = ppteOld[i];
while(pteT1) { pteT2 = pteT1->Next(); pteT1->Unlink();
// Same as calling Add() but don't update _cElements.
hash = pteT1->Hash(); hash %= _cBuckets; _buckets[hash] = _buckets[hash]->Insert(pteT1);
pteT1 = pteT2; } } } }
return; }
CTableElement * CHashTable::Remove( IN CTableKey &id ) /*++
Routine Description:
Looks up and removes an element from the table.
Arguments:
id - The key to match the element to be removed
Return Value:
NULL - The element was not in the table
non-NULL - A pointer to the element which was removed.
--*/
{ DWORD hash = id.Hash(); CTableElement *pte;
hash %= _cBuckets;
_buckets[hash] = (_buckets[hash])->RemoveMatching(id, &pte);
if (pte) { _cElements--;
if (_last == pte) { _last = _buckets[hash]; } }
return pte; }
void CHashTable::Remove( IN CTableElement *pElement ) /*++
Routine Description:
Used to remove an element from the table given a pointer to it.
Arguments:
pElement - the element to be removed. This pointer value, keys are not compared.
Return Value:
None
--*/
{ DWORD hash = pElement->Hash(); CTableElement *pte;
hash %= _cBuckets;
_buckets[hash] = (_buckets[hash])->RemoveMatching(pElement, &pte);
if (pte) { ASSERT(pte == pElement);
_cElements--;
if (_last == pElement) { _last = _buckets[hash]; } }
return; }
CTableElement * CHashTable::Another( ) /*++
Routine Description:
Returns an element from the table. Usually this will be element found after the element last returned from this function. It may, due to races not solved here, repeat an element or skip an element.
Races occur when accessing the "_last" memeber; this function is called while holding a shared lock. (More then one thread may call it at a time.)
This isn't as bad as it sounds. _last can only be set to null in Remove() which requires exclusive access.
Arguments:
None
Return Value:
NULL or a pointer to an element in the table.
--*/
{ CTableElement *panother; int i, end;
if (_cElements == 0) { return(0); }
if (_last) { if (panother = _last->Next()) { if (panother) { _last = panother; return(panother); } }
ASSERT(panother == 0);
// no next, start looking from just after last's hash.
i = _last->Hash();
// Exersise for the reader (x + y) mod n == ( x mod n + y mod n ) mod n
end = i = (i + 1) % _cBuckets; } else { // no last, start from the start.
i = 0; end = _cBuckets - 1; }
do { if (_buckets[i]) { panother = _buckets[i]; ASSERT(panother); _last = panother; return(panother); } i = (i + 1) % _cBuckets; } while (i != end);
// Doesn't mean the table is empty, just that we didn't find
// another element. These are not the same thing.
return(0); }
|