|
|
/*===================================================================
Microsoft Denali
Microsoft Confidential. Copyright 1997 Microsoft Corporation. All Rights Reserved.
File: idhash.cpp
Owner: DmitryR
Source file for the new hashing stuff ===================================================================*/ #include "denpre.h"
#pragma hdrstop
#include "idhash.h"
#include "memcls.h"
#include "memchk.h"
/*===================================================================
C P t r A r r a y ===================================================================*/
HRESULT CPtrArray::Insert ( int iPos, void *pv ) { if (!m_rgpvPtrs) // empty?
{ m_rgpvPtrs = (void **)malloc(m_dwInc * sizeof(void *)); if (!m_rgpvPtrs) return E_OUTOFMEMORY; m_dwSize = m_dwInc; m_cPtrs = 0; } else if (m_cPtrs == m_dwSize) // full?
{ void **pNewPtrs = (void **)realloc ( m_rgpvPtrs, (m_dwSize + m_dwInc) * sizeof(void *) ); if (!pNewPtrs) return E_OUTOFMEMORY; m_rgpvPtrs = pNewPtrs; m_dwSize += m_dwInc; }
if (iPos < 0) iPos = 0; if ((DWORD)iPos >= m_cPtrs) // append?
{ m_rgpvPtrs[m_cPtrs++] = pv; return S_OK; }
memmove ( &m_rgpvPtrs[iPos+1], &m_rgpvPtrs[iPos], (m_cPtrs-iPos) * sizeof(void *) );
m_rgpvPtrs[iPos] = pv; m_cPtrs++; return S_OK; }
HRESULT CPtrArray::Find ( void *pv, int *piPos ) const { Assert(piPos);
for (DWORD i = 0; i < m_cPtrs; i++) { if (m_rgpvPtrs[i] == pv) { *piPos = i; return S_OK; } }
// not found
*piPos = -1; return S_FALSE; }
HRESULT CPtrArray::Remove ( void *pv ) { HRESULT hr = S_FALSE;
for (DWORD i = 0; i < m_cPtrs; i++) { if (m_rgpvPtrs[i] == pv) hr = Remove(i); }
return hr; }
HRESULT CPtrArray::Remove ( int iPos ) { Assert(iPos >= 0 && (DWORD)iPos < m_cPtrs); Assert(m_rgpvPtrs);
// remove the element
DWORD dwMoveSize = (m_cPtrs - iPos - 1) * sizeof(void *); if (dwMoveSize) memmove(&m_rgpvPtrs[iPos], &m_rgpvPtrs[iPos+1], dwMoveSize); m_cPtrs--;
if (m_dwSize > 4*m_dwInc && m_dwSize > 8*m_cPtrs) { // collapse to 1/4 if size > 4 x increment and less < 1/8 full
void **pNewPtrs = (void **)realloc ( m_rgpvPtrs, (m_dwSize / 4) * sizeof(void *) );
if (pNewPtrs) { m_rgpvPtrs = pNewPtrs; m_dwSize /= 4; } }
return S_OK; }
HRESULT CPtrArray::Clear() { if (m_rgpvPtrs) free(m_rgpvPtrs);
m_dwSize = 0; m_rgpvPtrs = NULL; m_cPtrs = 0; return S_OK; }
/*===================================================================
C I d H a s h U n i t ===================================================================*/
// Everything is inline for this structure. See the header file.
/*===================================================================
C I d H a s h A r r a y ===================================================================*/
/*===================================================================
For some hardcoded element counts (that relate to session hash), ACACHE is used for allocations This is wrapped in the two functions below. ===================================================================*/ ACACHE_FSA_EXTERN(MemBlock128) ACACHE_FSA_EXTERN(MemBlock256) static inline void *AcacheAllocIdHashArray(DWORD cElems) { /* removed because in 64bit land it doesn't work because of padding
void *pvMem; if (cElems == 13) { pvMem = ACACHE_FSA_ALLOC(MemBlock128); } else if (cElems == 31) { pvMem = ACACHE_FSA_ALLOC(MemBlock256); } else { pvMem = malloc(2*sizeof(USHORT) + cElems*sizeof(CIdHashElem)); } */
return malloc(offsetof(CIdHashArray, m_rgElems) + cElems*sizeof(CIdHashElem)); }
static inline void AcacheFreeIdHashArray(CIdHashArray *pArray) { /* removed because in 64bit land it doesn't work because of padding
if (pArray->m_cElems == 13) { ACACHE_FSA_FREE(MemBlock128, pArray); } else if (pArray->m_cElems == 31) { ACACHE_FSA_FREE(MemBlock256, pArray); } else { free(pArray); } */ free(pArray); }
/*===================================================================
CIdHashArray::Alloc
Static method. Allocates CIdHashArray as a memory block containing var length array.
Parameters: cElems # of elements in the array
Returns: Newly created CIdHashArray ===================================================================*/ CIdHashArray *CIdHashArray::Alloc ( DWORD cElems ) { CIdHashArray *pArray = (CIdHashArray *)AcacheAllocIdHashArray(cElems); if (!pArray) return NULL;
pArray->m_cElems = (USHORT)cElems; pArray->m_cNotNulls = 0; memset(&(pArray->m_rgElems[0]), 0, cElems * sizeof(CIdHashElem)); return pArray; }
/*===================================================================
CIdHashArray::Alloc
Static method. Frees allocated CIdHashArray as a memory block. Also frees any sub-arrays.
Parameters: pArray CIdHashArray object to be freed
Returns: ===================================================================*/ void CIdHashArray::Free ( CIdHashArray *pArray ) { if (pArray->m_cNotNulls > 0) { for (DWORD i = 0; i < pArray->m_cElems; i++) { if (pArray->m_rgElems[i].FIsArray()) Free(pArray->m_rgElems[i].PArray()); } }
AcacheFreeIdHashArray(pArray); }
/*===================================================================
CIdHashArray::Find
Searches this array and any sub-arrays for an objects with the given id.
Parameters: dwId id to look for ppvObj object found (if any)
Returns: S_OK = found, S_FALSE = not found ===================================================================*/ HRESULT CIdHashArray::Find ( DWORD_PTR dwId, void **ppvObj ) const { DWORD i = (DWORD)(dwId % m_cElems);
if (m_rgElems[i].DWId() == dwId) { if (ppvObj) *ppvObj = m_rgElems[i].PObject(); return S_OK; }
if (m_rgElems[i].FIsArray()) return m_rgElems[i].PArray()->Find(dwId, ppvObj);
// Not found
if (ppvObj) *ppvObj = NULL; return S_FALSE; }
/*===================================================================
CIdHashArray::Add
Adds an object to this (or sub-) array by Id. Creates new sub-arrays as needed.
Parameters: dwId object id pvObj object to add rgusSizes array of sizes (used when creating sub-arrays)
Returns: HRESULT (S_OK = added) ===================================================================*/ HRESULT CIdHashArray::Add ( DWORD_PTR dwId, void *pvObj, USHORT *rgusSizes ) { DWORD i = (DWORD)(dwId % m_cElems);
if (m_rgElems[i].FIsEmpty()) { m_rgElems[i].SetToObject(dwId, pvObj); m_cNotNulls++; return S_OK; }
// Advance array of sizes one level deeper
if (rgusSizes[0]) // not at the end
++rgusSizes;
if (m_rgElems[i].FIsObject()) {
// this array logic can't handle adding the same ID twice. It will
// loop endlessly. So, if an attempt is made to add the same
// ID a second, return an error
if (m_rgElems[i].DWId() == dwId) { return E_INVALIDARG; }
// Old object already taken the slot - need to create new array
// the size of first for three levels is predefined
// increment by 1 thereafter
CIdHashArray *pArray = Alloc (rgusSizes[0] ? rgusSizes[0] : m_cElems+1); if (!pArray) return E_OUTOFMEMORY;
// Push the old object down into the array
HRESULT hr = pArray->Add(m_rgElems[i].DWId(), m_rgElems[i].PObject(), rgusSizes); if (FAILED(hr)) return hr;
// Put array into slot
m_rgElems[i].SetToArray(pArray); }
Assert(m_rgElems[i].FIsArray()); return m_rgElems[i].PArray()->Add(dwId, pvObj, rgusSizes); }
/*===================================================================
CIdHashArray::Remove
Removes an object by id from this (or sub-) array. Removes empty sub-arrays.
Parameters: dwId object id ppvObj object removed (out, optional)
Returns: HRESULT (S_OK = removed, S_FALSE = not found) ===================================================================*/ HRESULT CIdHashArray::Remove ( DWORD_PTR dwId, void **ppvObj ) { DWORD i = (DWORD)(dwId % m_cElems);
if (m_rgElems[i].DWId() == dwId) { if (ppvObj) *ppvObj = m_rgElems[i].PObject(); m_rgElems[i].SetToEmpty(); m_cNotNulls--; return S_OK; }
if (m_rgElems[i].FIsArray()) { HRESULT hr = m_rgElems[i].PArray()->Remove(dwId, ppvObj); if (hr == S_OK && m_rgElems[i].PArray()->m_cNotNulls == 0) { Free(m_rgElems[i].PArray()); m_rgElems[i].SetToEmpty(); } return hr; }
// Not found
if (ppvObj) *ppvObj = NULL; return S_FALSE; }
/*===================================================================
CIdHashArray::Iterate
Calls a supplied callback for each object in the array and sub-arrays.
Parameters: pfnCB callback pvArg1, pvArg2 args to path to the callback
Returns: IteratorCallbackCode what to do next? ===================================================================*/ IteratorCallbackCode CIdHashArray::Iterate ( PFNIDHASHCB pfnCB, void *pvArg1, void *pvArg2 ) { IteratorCallbackCode rc = iccContinue;
for (DWORD i = 0; i < m_cElems; i++) { if (m_rgElems[i].FIsObject()) { rc = (*pfnCB)(m_rgElems[i].PObject(), pvArg1, pvArg2);
// remove if requested
if (rc & (iccRemoveAndContinue|iccRemoveAndStop)) { m_rgElems[i].SetToEmpty(); m_cNotNulls--; } } else if (m_rgElems[i].FIsArray()) { rc = m_rgElems[i].PArray()->Iterate(pfnCB, pvArg1, pvArg2);
// remove sub-array if empty
if (m_rgElems[i].PArray()->m_cNotNulls == 0) { Free(m_rgElems[i].PArray()); m_rgElems[i].SetToEmpty(); } } else { continue; }
// stop if requested
if (rc & (iccStop|iccRemoveAndStop)) { rc = iccStop; break; } }
return rc; }
#ifdef DBG
/*===================================================================
CIdHashTable::Dump
Dump hash table to a file (for debugging).
Parameters: szFile file name where to dump
Returns: ===================================================================*/ void CIdHashArray::DumpStats ( FILE *f, int nVerbose, DWORD iLevel, DWORD &cElems, DWORD &cSlots, DWORD &cArrays, DWORD &cDepth ) const { if (nVerbose > 0) { for (DWORD t = 0; t < iLevel; t++) fprintf(f, "\t"); fprintf(f, "Array (level=%d addr=%p) %d slots, %d not null:\n", iLevel, this, m_cElems, m_cNotNulls); }
cSlots += m_cElems; cArrays++;
if (iLevel > cDepth) cDepth = iLevel;
for (DWORD i = 0; i < m_cElems; i++) { if (nVerbose > 1) { for (DWORD t = 0; t < iLevel; t++) fprintf(f, "\t"); fprintf(f, "%[%08x:%p@%04d] ", m_rgElems[i].m_dw, m_rgElems[i].m_pv, i); }
if (m_rgElems[i].FIsEmpty()) { if (nVerbose > 1) fprintf(f, "NULL\n"); } else if (m_rgElems[i].FIsObject()) { if (nVerbose > 1) fprintf(f, "Object\n"); cElems++; } else if (m_rgElems[i].FIsArray()) { if (nVerbose > 1) fprintf(f, "Array:\n"); m_rgElems[i].PArray()->DumpStats(f, nVerbose, iLevel+1, cElems, cSlots, cArrays, cDepth); } else { if (nVerbose > 1) fprintf(f, "BAD\n"); } } } #endif
/*===================================================================
C I d H a s h T a b l e ===================================================================*/
/*===================================================================
CIdHashTable::Init
Initialize id hash table. Does not allocate anything.
Parameters: usSize1 size of the first level array usSize2 size of the 2nd level arrays (optional) usSize3 size of the 3rd level arrays (optional)
Returns: S_OK ===================================================================*/ HRESULT CIdHashTable::Init ( USHORT usSize1, USHORT usSize2, USHORT usSize3 ) { Assert(!FInited()); Assert(usSize1);
m_rgusSizes[0] = usSize1; // size of first level array
m_rgusSizes[1] = usSize2 ? usSize2 : 7; m_rgusSizes[2] = usSize3 ? usSize3 : 11; m_rgusSizes[3] = 0; // last one stays 0 to indicate
// the end of predefined sizes
m_pArray = NULL; return S_OK; }
/*===================================================================
CIdHashTable::UnInit
Uninitialize id hash table. Frees all arrays.
Parameters:
Returns: S_OK ===================================================================*/ HRESULT CIdHashTable::UnInit() { if (!FInited()) { Assert(!m_pArray); return S_OK; }
if (m_pArray) CIdHashArray::Free(m_pArray);
m_pArray = NULL; m_rgusSizes[0] = 0; return S_OK; }
#ifdef DBG
/*===================================================================
CIdHashTable::AssertValid
Validates id hash table.
Parameters:
Returns: ===================================================================*/ void CIdHashTable::AssertValid() const { Assert(FInited()); }
/*===================================================================
CIdHashTable::Dump
Dump hash table to a file (for debugging).
Parameters: szFile file name where to dump
Returns: ===================================================================*/ void CIdHashTable::Dump ( const char *szFile ) const { Assert(FInited()); Assert(szFile);
FILE *f = fopen(szFile, "a"); if (!f) return;
fprintf(f, "ID Hash Table Dump:\n");
DWORD cElems = 0; DWORD cSlots = 0; DWORD cArrays = 0; DWORD cDepth = 0;
if (m_pArray) m_pArray->DumpStats(f, 1, 1, cElems, cSlots, cArrays, cDepth);
fprintf(f, "Total %d Objects in %d Slots, %d Arrays, %d Max Depth\n\n", cElems, cSlots, cArrays, cDepth); fclose(f); } #endif
/*===================================================================
C H a s h L o c k ===================================================================*/
/*===================================================================
CHashLock::Init
Initialize the critical section.
Parameters:
Returns: S_OK ===================================================================*/ HRESULT CHashLock::Init() { Assert(!m_fInited);
HRESULT hr; ErrInitCriticalSection(&m_csLock, hr); if (FAILED(hr)) return hr;
m_fInited = TRUE; return S_OK; }
/*===================================================================
CHashLock::UnInit
Uninitialize the critical section.
Parameters:
Returns: S_OK ===================================================================*/ HRESULT CHashLock::UnInit() { if (m_fInited) { DeleteCriticalSection(&m_csLock); m_fInited = FALSE; } return S_OK; }
|