/* * hash.cpp * * Purpose: * implementation of a string hash table * * Owner: * EricAn * * History: * Mar 97: Created. * * Copyright (C) Microsoft Corp. 1997 */ #include #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; }