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.
 
 
 
 
 
 

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;
}