Leaked source code of windows server 2003
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.
|
|
//+---------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1992 - 1994.
//
// File: strhash.cxx
//
// Contents: A hash table for strings
//
// History: 7-Nov-94 BruceFo Created
//
//----------------------------------------------------------------------------
#include "headers.hxx"
#pragma hdrstop
#include "strhash.hxx"
const DWORD g_cMinElemsRehash = 3; const DWORD g_cMinBuckets = 13;
//////////////////////////////////////////////////////////////////////////////
// Methods for the CStrHashBucketElem class
//////////////////////////////////////////////////////////////////////////////
CStrHashBucketElem::CStrHashBucketElem( IN const WCHAR* pszKey ) : m_pszKey(pszKey), m_next(NULL) { INIT_SIG(CStrHashBucketElem); }
CStrHashBucketElem::~CStrHashBucketElem() { CHECK_SIG(CStrHashBucketElem); }
BOOL CStrHashBucketElem::IsEqual( IN const WCHAR* pszKey ) { CHECK_SIG(CStrHashBucketElem); appAssert(NULL != pszKey); appAssert(NULL != m_pszKey);
// NOTE: case-insensitive compare. This affects the hash function!
return (0 == _wcsicmp(pszKey, m_pszKey)); }
//////////////////////////////////////////////////////////////////////////////
// Methods for the CStrHashBucket class
//////////////////////////////////////////////////////////////////////////////
CStrHashBucket::CStrHashBucket( VOID ) : m_head(NULL) { INIT_SIG(CStrHashBucket); }
CStrHashBucket::~CStrHashBucket( VOID ) { CHECK_SIG(CStrHashBucket);
for (CStrHashBucketElem* x = m_head; NULL != x; ) { CStrHashBucketElem* tmp = x->m_next; delete x; x = tmp; } }
HRESULT CStrHashBucket::Insert( IN const WCHAR* pszKey ) { CHECK_SIG(CStrHashBucket); appAssert(NULL != pszKey);
CStrHashBucketElem* x = new CStrHashBucketElem(pszKey); if (NULL == x) { return E_OUTOFMEMORY; }
x->m_next = m_head; m_head = x; return S_OK; }
// return TRUE if it was found and removed, FALSE if it wasn't even found
BOOL CStrHashBucket::Remove( IN const WCHAR* pszKey ) { CHECK_SIG(CStrHashBucket); appAssert(NULL != pszKey);
CStrHashBucketElem** pPrev = &m_head;
for (CStrHashBucketElem* x = m_head; NULL != x; x = x->m_next) { if (x->IsEqual(pszKey)) // found it
{ *pPrev = x->m_next; delete x; return TRUE; }
pPrev = &x->m_next; }
return FALSE; // didn't find it
}
BOOL CStrHashBucket::IsMember( IN const WCHAR* pszKey ) { CHECK_SIG(CStrHashBucket); appAssert(NULL != pszKey);
for (CStrHashBucketElem* x = m_head; NULL != x; x = x->m_next) { if (x->IsEqual(pszKey)) { return TRUE; // found it
} } return FALSE; // didn't find it
}
//////////////////////////////////////////////////////////////////////////////
// Methods for the CStrHashTable class
//////////////////////////////////////////////////////////////////////////////
CStrHashTable::CStrHashTable( IN DWORD cNumBuckets ) : m_cElems(0), m_cMinNumBuckets(cNumBuckets) { INIT_SIG(CStrHashTable);
m_cNumBuckets = max(cNumBuckets, g_cMinBuckets); m_ht = new CStrHashBucket[m_cNumBuckets]; if (NULL == m_ht) { appDebugOut((DEB_ERROR, "Failed to allocate hash table with %d buckets\n", m_cNumBuckets));
m_cMinNumBuckets = 0; m_cNumBuckets = 0; } }
HRESULT CStrHashTable::QueryError( VOID ) { if (NULL == m_ht) { return E_OUTOFMEMORY; }
return S_OK; }
CStrHashTable::~CStrHashTable( VOID ) { CHECK_SIG(CStrHashTable);
delete[] m_ht; }
HRESULT CStrHashTable::Insert( IN const WCHAR* pszKey ) { CHECK_SIG(CStrHashTable);
if (NULL == pszKey) { return E_INVALIDARG; }
appAssert(NULL != m_ht);
DWORD key = HashFunction(pszKey); if (!(m_ht[key].IsMember(pszKey))) { // only insert if the key isn't already in the table.
HRESULT hr = m_ht[key].Insert(pszKey); CHECK_HRESULT(hr); if (FAILED(hr)) { return hr; } ++m_cElems; return CheckRehash(); }
return S_OK; }
HRESULT CStrHashTable::Remove( IN const WCHAR* pszKey ) { CHECK_SIG(CStrHashTable);
if (NULL == pszKey) { return E_INVALIDARG; }
appAssert(NULL != m_ht);
if (m_ht[HashFunction(pszKey)].Remove(pszKey)) { --m_cElems; return CheckRehash(); }
return S_OK; // key was not found and hence not deleted
}
BOOL CStrHashTable::IsMember( IN const WCHAR* pszKey ) { CHECK_SIG(CStrHashTable);
if (NULL == pszKey) { return FALSE; // invalid argument, really
}
appAssert(NULL != m_ht);
return m_ht[HashFunction(pszKey)].IsMember(pszKey); }
DWORD CStrHashTable::Count( VOID ) { CHECK_SIG(CStrHashTable);
return m_cElems; }
// This returns the iteration data, or NULL on failure
CIterateData* CStrHashTable::IterateStart( VOID ) { CHECK_SIG(CStrHashTable);
CIterateData* pData = new CIterateData; if (NULL != pData) { IterateGetNext(pData); } return pData; }
const WCHAR* CStrHashTable::IterateGetData( IN OUT CIterateData* pCurrent ) { CHECK_SIG(CStrHashTable); appAssert(NULL != pCurrent); appAssert(ITERATE_END != pCurrent->m_CurrentBucket);
return pCurrent->m_pCurrentElem->m_pszKey; }
BOOL CStrHashTable::IterateDone( IN CIterateData* pCurrent ) { CHECK_SIG(CStrHashTable); appAssert(NULL != pCurrent);
return (ITERATE_END == pCurrent->m_CurrentBucket); }
VOID CStrHashTable::IterateEnd( IN CIterateData* pCurrent ) { CHECK_SIG(CStrHashTable);
delete pCurrent; }
VOID CStrHashTable::IterateGetNext( IN OUT CIterateData* pCurrent ) { CHECK_SIG(CStrHashTable); appAssert(NULL != pCurrent); appAssert(ITERATE_END != pCurrent->m_CurrentBucket);
if (NULL != pCurrent->m_pCurrentElem) { if (NULL != pCurrent->m_pCurrentElem->m_next) { // just get next element in bucket
pCurrent->m_pCurrentElem = pCurrent->m_pCurrentElem->m_next; } else { // need to search to new bucket
++pCurrent->m_CurrentBucket; IterateGetBucket(pCurrent); } } else { IterateGetBucket(pCurrent); } }
VOID CStrHashTable::IterateGetBucket( IN OUT CIterateData* pCurrent ) { CHECK_SIG(CStrHashTable); appAssert(NULL != m_ht);
for (DWORD i = pCurrent->m_CurrentBucket; i < m_cNumBuckets; i++) { if (NULL != m_ht[i].m_head) { pCurrent->m_pCurrentElem = m_ht[i].m_head; pCurrent->m_CurrentBucket = i; return; } } pCurrent->m_CurrentBucket = ITERATE_END; // we've iterated through all!
}
DWORD CStrHashTable::HashFunction( IN const WCHAR* pszKey ) { CHECK_SIG(CStrHashTable);
appAssert(NULL != pszKey); appAssert(m_cNumBuckets > 0);
DWORD total = 0; for (const WCHAR* p = pszKey; L'\0' != *p; p++) { // lower case it, so case-insensitive IsEqual works
total += towlower(*p); } return total % m_cNumBuckets; }
HRESULT CStrHashTable::CheckRehash( VOID ) { CHECK_SIG(CStrHashTable);
if (m_cElems > g_cMinElemsRehash && m_cElems > m_cMinNumBuckets) { if ( (m_cElems > m_cNumBuckets) || (m_cElems < m_cNumBuckets / 4) ) { // add one to at least make it odd (for better hashing behavior)
return Rehash(m_cElems * 2 + 1); } } return S_OK; }
HRESULT CStrHashTable::Rehash( IN DWORD cNumBuckets ) { CHECK_SIG(CStrHashTable);
cNumBuckets = max(cNumBuckets, g_cMinBuckets); CStrHashBucket* ht = new CStrHashBucket[cNumBuckets]; if (NULL == ht) { // return error, but don't delete the existing table
return E_OUTOFMEMORY; }
appAssert(NULL != m_ht);
// now transfer all data from old to new
ULONG cOldNumBuckets = m_cNumBuckets; m_cNumBuckets = cNumBuckets; // set this so HashFunction() uses it
for (ULONG i=0; i < cOldNumBuckets; i++) { for (CStrHashBucketElem* x = m_ht[i].m_head; NULL != x; ) { CStrHashBucketElem* tmp = x->m_next;
// now, just put this bucket in the right place in the new
// hash table. Avoid performing new's and copying the keys.
DWORD bucket = HashFunction(x->m_pszKey); x->m_next = ht[bucket].m_head; ht[bucket].m_head = x;
x = tmp; }
m_ht[i].m_head = NULL; // there isn't anything left in the list!
}
// the data is transfered; complete the switchover
delete[] m_ht; m_ht = ht;
return S_OK; }
|