514 lines
13 KiB
C++
514 lines
13 KiB
C++
|
/********************************************************************
|
||
|
|
||
|
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);
|
||
|
}
|