Source code of Windows XP (NT5)
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.
 
 
 
 
 
 

692 lines
16 KiB

/*===================================================================
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;
}