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.
344 lines
8.0 KiB
344 lines
8.0 KiB
/*
|
|
* hash.cpp
|
|
*
|
|
* Purpose:
|
|
* implementation of a string hash table
|
|
*
|
|
* Owner:
|
|
* EricAn
|
|
*
|
|
* History:
|
|
* Mar 97: Created.
|
|
*
|
|
* Copyright (C) Microsoft Corp. 1997
|
|
*/
|
|
|
|
#include <pch.hxx>
|
|
#include "dllmain.h"
|
|
#include "privunk.h"
|
|
#include "hash.h"
|
|
#include "demand.h"
|
|
|
|
// possible hash-table sizes, chosen from primes not close to powers of 2
|
|
static const DWORD s_rgPrimes[] = { 29, 53, 97, 193, 389, 769, 1543, 3079, 6151, 12289, 24593 };
|
|
|
|
BOOL FastStrCmp(char *psz1, char *psz2)
|
|
{
|
|
if (psz1 == NULL || psz2 == NULL)
|
|
return FALSE;
|
|
|
|
while (*psz1 && *psz2 && (*psz1 == *psz2))
|
|
{
|
|
psz1++;
|
|
psz2++;
|
|
};
|
|
|
|
return *psz1 == *psz2;
|
|
}
|
|
|
|
//+---------------------------------------------------------------
|
|
//
|
|
// Member: Constructor
|
|
//
|
|
// Synopsis:
|
|
//
|
|
//---------------------------------------------------------------
|
|
CHash::CHash(IUnknown *pUnkOuter) : CPrivateUnknown(pUnkOuter)
|
|
{
|
|
m_cBins = 0;
|
|
m_rgBins = NULL;
|
|
m_fDupe = FALSE;
|
|
m_pLastEntryEnum = NULL;
|
|
m_iListBinEnum = 0;
|
|
|
|
DllAddRef();
|
|
}
|
|
|
|
|
|
CHash::~CHash()
|
|
{
|
|
PHASHENTRY phe, pheTemp;
|
|
|
|
for (DWORD dw = 0; dw < m_cBins; dw++)
|
|
{
|
|
if (m_rgBins[dw].pheNext)
|
|
{
|
|
phe = m_rgBins[dw].pheNext;
|
|
while (phe)
|
|
{
|
|
pheTemp = phe;
|
|
phe = phe->pheNext;
|
|
if (m_fDupe && pheTemp->pszKey)
|
|
MemFree(pheTemp->pszKey);
|
|
|
|
MemFree(pheTemp);
|
|
}
|
|
}
|
|
|
|
if (m_rgBins[dw].pszKey && m_fDupe)
|
|
MemFree(m_rgBins[dw].pszKey);
|
|
|
|
}
|
|
SafeMemFree(m_rgBins);
|
|
DllRelease();
|
|
}
|
|
|
|
HRESULT CHash::Init(DWORD dwSize, BOOL fDupeKeys)
|
|
{
|
|
int i = 0;
|
|
|
|
m_fDupe = fDupeKeys;
|
|
|
|
while (i < (ARRAYSIZE(s_rgPrimes) - 1) && s_rgPrimes[i] < dwSize)
|
|
i++;
|
|
Assert(s_rgPrimes[i] >= dwSize || i == (ARRAYSIZE(s_rgPrimes)-1));
|
|
m_cBins = s_rgPrimes[i];
|
|
|
|
if (!MemAlloc((LPVOID*)&m_rgBins, m_cBins * sizeof(HASHENTRY)))
|
|
return E_OUTOFMEMORY;
|
|
|
|
ZeroMemory(m_rgBins, m_cBins * sizeof(HASHENTRY));
|
|
|
|
return NOERROR;
|
|
}
|
|
|
|
DWORD CHash::Hash(LPSTR psz)
|
|
{
|
|
DWORD h = 0;
|
|
while (*psz)
|
|
h = ((h << 4) + *psz++ + (h >> 28));
|
|
return (h % m_cBins);
|
|
}
|
|
|
|
HRESULT CHash::Insert(LPSTR psz, LPVOID pv, DWORD dwFlags)
|
|
{
|
|
PHASHENTRY phe = &m_rgBins[Hash(psz)];
|
|
|
|
if (m_fDupe &&
|
|
(!(psz = PszDupA(psz))))
|
|
return E_OUTOFMEMORY;
|
|
|
|
if (HF_NO_DUPLICATES & dwFlags)
|
|
{
|
|
PHASHENTRY pheCurrent = phe;
|
|
|
|
// Check for duplicate entries: if found, do not insert this entry
|
|
do
|
|
{
|
|
if (pheCurrent->pszKey && FastStrCmp(pheCurrent->pszKey, psz))
|
|
{
|
|
// This is already in our hash table. Replace data value
|
|
pheCurrent->pv = pv;
|
|
if (m_fDupe)
|
|
MemFree(psz);
|
|
|
|
return NOERROR;
|
|
}
|
|
|
|
// Advance pointer
|
|
pheCurrent = pheCurrent->pheNext;
|
|
} while (NULL != pheCurrent);
|
|
}
|
|
|
|
if (phe->pszKey)
|
|
{
|
|
PHASHENTRY pheNew;
|
|
|
|
if (!MemAlloc((LPVOID*)&pheNew, sizeof(HASHENTRY)))
|
|
{
|
|
if (m_fDupe)
|
|
MemFree(psz);
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
pheNew->pheNext = phe->pheNext;
|
|
phe->pheNext = pheNew;
|
|
phe = pheNew;
|
|
}
|
|
|
|
phe->pszKey = psz;
|
|
phe->pv = pv;
|
|
return NOERROR;
|
|
}
|
|
|
|
HRESULT CHash::Find(LPSTR psz, BOOL fRemove, LPVOID * ppv)
|
|
{
|
|
PHASHENTRY phe = &m_rgBins[Hash(psz)],
|
|
phePrev = NULL,
|
|
pheTemp;
|
|
|
|
if (phe->pszKey)
|
|
{
|
|
do
|
|
{
|
|
if (FastStrCmp(phe->pszKey, psz))
|
|
{
|
|
*ppv = phe->pv;
|
|
if (fRemove)
|
|
{
|
|
if (m_fDupe)
|
|
SafeMemFree(phe->pszKey);
|
|
|
|
if (phePrev)
|
|
{
|
|
// mid-chain
|
|
phePrev->pheNext = phe->pheNext;
|
|
MemFree(phe);
|
|
}
|
|
else
|
|
{
|
|
// head of bucket
|
|
phe->pv = NULL;
|
|
phe->pszKey = NULL;
|
|
pheTemp = phe->pheNext;
|
|
if (pheTemp)
|
|
{
|
|
CopyMemory(phe, pheTemp, sizeof(HASHENTRY));
|
|
MemFree(pheTemp);
|
|
}
|
|
}
|
|
}
|
|
return NOERROR;
|
|
}
|
|
phePrev = phe;
|
|
phe = phe->pheNext;
|
|
}
|
|
while (phe);
|
|
}
|
|
return CO_E_NOMATCHINGNAMEFOUND;
|
|
}
|
|
|
|
HRESULT CHash::Replace(LPSTR psz, LPVOID pv)
|
|
{
|
|
PHASHENTRY phe = &m_rgBins[Hash(psz)];
|
|
|
|
if (phe->pszKey)
|
|
{
|
|
do
|
|
{
|
|
if (FastStrCmp(phe->pszKey, psz))
|
|
{
|
|
phe->pv = pv;
|
|
return NOERROR;
|
|
}
|
|
phe = phe->pheNext;
|
|
}
|
|
while (phe);
|
|
}
|
|
return CO_E_NOMATCHINGNAMEFOUND;
|
|
}
|
|
|
|
|
|
HRESULT CHash::Reset()
|
|
{
|
|
m_iListBinEnum = 0;
|
|
m_pLastEntryEnum = &m_rgBins[0];
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CHash::Next(ULONG cFetch, LPVOID **prgpv, ULONG *pcFetched)
|
|
{
|
|
LPVOID *rgpv;
|
|
ULONG cFound=0;
|
|
PHASHENTRY phe;
|
|
HRESULT hr;
|
|
|
|
if (!MemAlloc((LPVOID *)&rgpv, sizeof(LPVOID) * cFetch))
|
|
return E_OUTOFMEMORY;
|
|
|
|
ZeroMemory(rgpv, sizeof(LPVOID) * cFetch);
|
|
|
|
while (m_pLastEntryEnum)
|
|
{
|
|
if (m_pLastEntryEnum->pszKey)
|
|
rgpv[cFound++] = m_pLastEntryEnum->pv;
|
|
|
|
m_pLastEntryEnum = m_pLastEntryEnum->pheNext;
|
|
|
|
if (!m_pLastEntryEnum && m_iListBinEnum < m_cBins -1)
|
|
m_pLastEntryEnum = &m_rgBins[++m_iListBinEnum];
|
|
|
|
if (cFound == cFetch)
|
|
break;
|
|
}
|
|
|
|
hr = cFound ? (cFound == cFetch ? S_OK : S_FALSE) : E_FAIL;
|
|
if (FAILED(hr))
|
|
{
|
|
MemFree(rgpv);
|
|
rgpv = NULL;
|
|
}
|
|
|
|
*prgpv = rgpv;
|
|
*pcFetched = cFound;
|
|
return hr;
|
|
}
|
|
|
|
|
|
#ifdef DEBUG
|
|
void CHash::Stats()
|
|
{
|
|
DWORD dwLongest = 0;
|
|
DWORD dwCollisions = 0;
|
|
DWORD dwTotalCost = 0;
|
|
DWORD dwItems = 0;
|
|
DWORD dwLength;
|
|
DWORD dw;
|
|
PHASHENTRY phe;
|
|
DWORD rgLen[100];
|
|
|
|
TraceCall("CHash::Stats()");
|
|
|
|
ZeroMemory(rgLen, sizeof(rgLen));
|
|
|
|
for (dw = 0; dw < m_cBins; dw++)
|
|
{
|
|
dwLength = 0;
|
|
if (m_rgBins[dw].pszKey)
|
|
{
|
|
dwLength++;
|
|
phe = m_rgBins[dw].pheNext;
|
|
while (phe)
|
|
{
|
|
dwCollisions++;
|
|
dwLength++;
|
|
phe = phe->pheNext;
|
|
}
|
|
}
|
|
if (dwLength > dwLongest)
|
|
dwLongest = dwLength;
|
|
dwTotalCost += (dwLength * (dwLength + 1)) / 2;
|
|
dwItems += dwLength;
|
|
rgLen[dwLength]++;
|
|
}
|
|
|
|
TraceInfo(_MSG("\tdwCollisions = %ld\r\n\tdwLongest = %ld\r\n\tdwItems = %ld\r\n\tdwTotalCost = %ld",
|
|
dwCollisions, dwLongest, dwItems, dwTotalCost));
|
|
|
|
for (dw = 0; dw <= dwLongest; dw++)
|
|
TraceInfo(_MSG("Len %d: %d", dw, rgLen[dw]));
|
|
}
|
|
#endif
|
|
|
|
|
|
//+---------------------------------------------------------------
|
|
//
|
|
// Member: PrivateQueryInterface
|
|
//
|
|
// Synopsis:
|
|
//
|
|
//---------------------------------------------------------------
|
|
HRESULT CHash::PrivateQueryInterface(REFIID riid, LPVOID *lplpObj)
|
|
{
|
|
if(!lplpObj)
|
|
return E_INVALIDARG;
|
|
|
|
*lplpObj = NULL;
|
|
|
|
if (IsEqualIID(riid, IID_IHashTable))
|
|
*lplpObj = (LPVOID)(IHashTable *)this;
|
|
else
|
|
return E_NOINTERFACE;
|
|
|
|
AddRef();
|
|
return NOERROR;
|
|
}
|