|
|
/********************************************************************
Copyright (c) 1999-2000 Microsoft Corporation
Module Name: PFHash.cpp
Abstract: hash table implementation. This hash table is NOT thread safe.
Revision History: DerekM created 05/01/99 DerekM modified 03/14/00
********************************************************************/
#include "stdafx.h"
#include "PFHash.h"
/////////////////////////////////////////////////////////////////////////////
// tracing stuff
#ifdef THIS_FILE
#undef THIS_FILE
#endif
static char __szTraceSourceFile[] = __FILE__; #define THIS_FILE __szTraceSourceFile
/////////////////////////////////////////////////////////////////////////////
// CPFHashBase - construction
// ***************************************************************************
CPFHashBase::CPFHashBase(void) { m_pfnDelete = NULL; m_rgpMap = NULL; m_pEnumNext = NULL; m_cSlots = 0; m_cObjs = 0; m_iEnumSlot = (DWORD)-1; }
// ***************************************************************************
CPFHashBase::~CPFHashBase() { this->Cleanup(); if (m_rgpMap != NULL) MyFree(m_rgpMap); }
/////////////////////////////////////////////////////////////////////////////
// CPFHashBase - internal methods
// ***************************************************************************
void CPFHashBase::Cleanup(void) { USE_TRACING("CPFHashBase::Cleanup");
SPFHashObj *pObj = NULL, *pObjNext; DWORD i; // if the map is NULL, our work here is done. We can go lay on a beach
// somewhere...
if (m_rgpMap == NULL) return;
// delete everything from the map
for (i = 0; i < m_cSlots; i++) { pObj = m_rgpMap[i]; while(pObj != NULL) { pObjNext = pObj->pNext; if (pObj->pvTag != NULL) /*this->*/DeleteTag(pObj->pvTag); if (pObj->pvData != NULL && m_pfnDelete != NULL) (*this->m_pfnDelete)(pObj->pvData); MyFree(pObj); pObj = pObjNext; }
m_rgpMap[i] = NULL; }
m_cObjs = 0; }
// ***************************************************************************
SPFHashObj *CPFHashBase::FindInChain(LPVOID pvTag, DWORD iSlot, SPFHashObj ***pppObjStore) { USE_TRACING("CPFHashBase::FindInChain");
SPFHashObj *pObj = NULL, **ppObjStore = NULL; INT_PTR iResult;
// search thru the array. Since we insert in sorted order, we can
// optimize searching and stop if we get to an array larger than we are.
// So that this can be used to find locations for inserting & deleting, we
// also keep track of the next ptr of the previous object and return it if
// necessary...
pObj = m_rgpMap[iSlot]; ppObjStore = &m_rgpMap[iSlot]; while (pObj != NULL) { // if it's a string, do a strcmp. Otherwise do a subtraction
iResult = /*this->*/CompareTag(pObj->pvTag, pvTag); // if it's equal, we found it
if (iResult == 0) { break; }
// if it's greater, we can stop
else if (iResult > 0) { pObj = NULL; break; } // incrememnt ptrs and continue walkin' the chain gang...
ppObjStore = &(pObj->pNext); pObj = pObj->pNext; }
// return the next ptr.
if (pppObjStore != NULL) *pppObjStore = ppObjStore;
return pObj; }
////////////////////////////////////////////////////////////////////////////
// CPFHashBase - exposed methods
// ***************************************************************************
HRESULT CPFHashBase::Init(DWORD cSlots) { USE_TRACING("CPFHashBase::Init");
SPFHashObj **rgpMap = NULL; HRESULT hr = NOERROR; VALIDATEPARM(hr, (cSlots <= 1)); if (FAILED(hr)) goto done;
// if we already have an array, clear out the old contents. If the new
// size is different than the old, then nuke the array as well and
// reallocate.
if (m_rgpMap != NULL) { this->Cleanup(); if (cSlots == m_cSlots) goto done;
MyFree(m_rgpMap); m_rgpMap = NULL; m_cSlots = 0; }
// alloc the array
rgpMap = (SPFHashObj **)MyAlloc(cSlots * sizeof(SPFHashObj *)); VALIDATEEXPR(hr, (rgpMap == NULL), E_OUTOFMEMORY); if (FAILED(hr)) goto done;
ZeroMemory(rgpMap, cSlots * sizeof(SPFHashObj *));
// save off internal data
m_rgpMap = rgpMap; m_cSlots = cSlots;
rgpMap = NULL;
done: if (rgpMap != NULL) MyFree(rgpMap);
return hr; }
// ***************************************************************************
HRESULT CPFHashBase::AddToMap(LPVOID pvTag, LPVOID pvData, LPVOID *ppvOld) { USE_TRACING("CPFHashBase::AddToMap");
SPFHashObj *pObj = NULL, **ppObjStore = NULL, *pNewObj = NULL; HRESULT hr = NOERROR; DWORD iSlot;
VALIDATEPARM(hr, (pvTag == NULL)); if (FAILED(hr)) goto done;
// if we don't have a map already setup fail...
VALIDATEEXPR(hr, (m_rgpMap == NULL), E_FAIL) if (FAILED(hr)) goto done;
if (ppvOld != NULL) *ppvOld = NULL;
// compute the slot & try to find the object
iSlot = /*this->*/ComputeSlot(pvTag); pObj = this->FindInChain(pvTag, iSlot, &ppObjStore); // we're just updating an existing element
if (pObj != NULL) { // does the user want it back?
if (ppvOld != NULL) *ppvOld = pObj->pvData; // or should be just delete it?
else if (pObj->pvData != NULL && m_pfnDelete != NULL) (*this->m_pfnDelete)(pObj->pvData);
pObj->pvData = pvData; }
// ok folks, we got REAL work to do now.
else { if (ppvOld != NULL) *ppvOld = NULL;
// alloc new object
pNewObj = (SPFHashObj *)MyAlloc(sizeof(SPFHashObj)); VALIDATEEXPR(hr, (pNewObj == NULL), E_OUTOFMEMORY); if (FAILED(hr)) goto done;
TESTHR(hr, /*this->*/AllocTag(pvTag, &(pNewObj->pvTag))); if (FAILED(hr)) goto done;
pNewObj->pvData = pvData; pNewObj->pNext = *ppObjStore; *ppObjStore = pNewObj;
// increment the number of objects
m_cObjs++;
pNewObj = NULL; }
done: if (pNewObj != NULL) { if (pNewObj->pvTag != NULL) /*this->*/DeleteTag(pNewObj->pvTag); MyFree(pNewObj); }
return hr; }
// ***************************************************************************
HRESULT CPFHashBase::FindInMap(LPVOID pvTag, LPVOID *ppv) { USE_TRACING("CPFHashBase::FindInMap");
SPFHashObj *pObj = NULL; HRESULT hr = NOERROR; DWORD iSlot;
// validate params
VALIDATEPARM(hr, (ppv == NULL || pvTag == NULL)); if (FAILED(hr)) goto done;
// if we don't have a map already setup fail...
VALIDATEEXPR(hr, (m_rgpMap == NULL), E_FAIL); if (FAILED(hr)) goto done;
// compute the slot & try to find the object
iSlot = /*this->*/ComputeSlot(pvTag); pObj = this->FindInChain(pvTag, iSlot, NULL);
// if we didn't find it, signal S_FALSE
if (pObj == NULL) { hr = S_FALSE; goto done; }
// otherwise, return the data to the user
*ppv = pObj->pvData;
done: return hr; }
// ***************************************************************************
HRESULT CPFHashBase::RemoveFromMap(LPVOID pvTag, LPVOID *ppvOld) { USE_TRACING("CPFHashBase::RemoveFromMap");
SPFHashObj *pObj = NULL, **ppObjStore = NULL; HRESULT hr = NOERROR; DWORD iSlot;
// validate params
VALIDATEPARM(hr, (ppvOld == NULL || pvTag == NULL)); if (FAILED(hr)) goto done;
// if we don't have a map already setup fail...
VALIDATEEXPR(hr, (m_rgpMap == NULL), E_FAIL); if (FAILED(hr)) goto done;
// compute the slot & try to find the object
iSlot = /*this->*/ComputeSlot(pvTag); pObj = this->FindInChain(pvTag, iSlot, &ppObjStore); // if we didn't find one, just return NOERROR cuz not having one in the
// map is pretty much what the user wanted...
if (pObj != NULL) { // does the user want it back?
if (ppvOld != NULL) *ppvOld = pObj->pvData; // or should be just delete it?
else if (pObj->pvData != NULL && m_pfnDelete != NULL) (*this->m_pfnDelete)(pObj->pvData);
*ppObjStore = pObj->pNext; /*this->*/DeleteTag(pObj->pvTag); MyFree(pObj); }
done: return hr; }
// ***************************************************************************
HRESULT CPFHashBase::RemoveAll(void) { USE_TRACING("CPFHashBase::RemoveAll");
// if we don't have a map already just succeed cuz everything has been
// deleted. Otherwise, remove everything in life (as far as the map
// is concerned anyway).
if (m_rgpMap != NULL) this->Cleanup(); return NOERROR; }
// ***************************************************************************
HRESULT CPFHashBase::BeginEnum(void) { USE_TRACING("CPFHashBase::BeginEnum");
HRESULT hr = NOERROR;
// if we don't have a map already setup fail...
VALIDATEEXPR(hr, (m_rgpMap == NULL), E_FAIL); if (FAILED(hr)) goto done; m_iEnumSlot = 0; m_pEnumNext = m_rgpMap[0]; done: return hr; } // ***************************************************************************
HRESULT CPFHashBase::EnumNext(LPVOID *ppvTag, LPVOID *ppvData) { USE_TRACING("CPFHashBase::EnumNext");
HRESULT hr = NOERROR;
// if we don't have a map already setup fail...
VALIDATEEXPR(hr, (m_rgpMap == NULL || m_iEnumSlot == (DWORD)-1), E_FAIL); if (FAILED(hr)) goto done;
for(;;) { if (m_pEnumNext != NULL) { *ppvTag = m_pEnumNext->pvTag; *ppvData = m_pEnumNext->pvData; m_pEnumNext = m_pEnumNext->pNext; break; }
m_iEnumSlot++; if (m_iEnumSlot >= m_cSlots) { m_iEnumSlot = (DWORD)-1; hr = S_FALSE; break; }
m_pEnumNext = m_rgpMap[m_iEnumSlot]; }
done: return hr; } /*
#if defined(DEBUG) || defined(_DEBUG)
// ***************************************************************************
void CPFHashBase::DumpAll(FILE *pf) { USE_TRACING("CPFHashBASE::DumpAll");
SPFHashObj *pmo = NULL, *pmoNext; DWORD i; // if the map is NULL, our work here is done. We can go lay on a beach
// somewhere...
if (m_rgpMap == NULL) { fprintf(pf, "empty map\n\n"); return; }
// delete everything from the map
for (i = 0; i < m_cSlots; i++) { fprintf(pf, "Slot %2d: ", i);
pmo = m_rgpMap[i]; while(pmo != NULL) { pmoNext = pmo->pNext; this->PrintTag(pf, pmo->pvTag); pmo = pmoNext; }
fprintf(pf, "\n"); }
fprintf(pf, "\n"); }
// ***************************************************************************
void CPFHashBase::DumpCount(FILE *pf) { USE_TRACING("CPFHashBASE::DumpCount"); fprintf(pf, "count: %d\n", m_cObjs); }
#endif
*/
/////////////////////////////////////////////////////////////////////////////
// CPFHashWSTR virtual method implementation
// ***************************************************************************
HRESULT CPFHashWSTR::AllocTag(LPVOID pvTag, LPVOID *ppvTagCopy) { USE_TRACING("CPFHashWSTR::AllocTag"); HRESULT hr = NOERROR; LPWSTR pwsz = NULL;
pwsz = (LPWSTR)MyAlloc((wcslen((LPWSTR)pvTag) + 1) * sizeof(WCHAR)); VALIDATEEXPR(hr, (pwsz == NULL), E_OUTOFMEMORY); if (FAILED(hr)) goto done;
wcscpy(pwsz, (LPWSTR)pvTag); *ppvTagCopy = (LPVOID)pwsz;
done: return hr; }
// ***************************************************************************
DWORD CPFHashWSTR::ComputeSlot(LPVOID pvTag) { USE_TRACING("CPFHashWSTR::ComputeSlot");
WCHAR *pwch = NULL; DWORD dwHash;
dwHash = 0; for (pwch = (WCHAR *)pvTag; *pwch != '\0'; pwch++) dwHash = ((dwHash << 6) + towupper(*pwch)) % m_cSlots;
return dwHash;
}
// ***************************************************************************
void CPFHashWSTR::DeleteTag(LPVOID pvTag) { USE_TRACING("CPFHashWSTR::DeleteTag"); MyFree(pvTag); }
// ***************************************************************************
INT_PTR CPFHashWSTR::CompareTag(LPVOID pvTag1, LPVOID pvTag2) { USE_TRACING("CPFHashWSTR::CompareTag"); return (INT_PTR)_wcsicmp((LPWSTR)pvTag1, (LPWSTR)pvTag2); }
|