windows-nt/Source/XPSP1/NT/shell/ext/webcheck/strlist.cpp
2020-09-26 16:20:57 +08:00

569 lines
14 KiB
C++

//----------------------------------------------------------------------------
//
//----------------------------------------------------------------------------
#include "private.h"
#include "strlist.h"
#define TF_THISMODULE TF_STRINGLIST
//----------------------------------------------------------------------------
// CWCStringList
CWCStringList::CWCStringList()
{
// We may be able to remove this Clear() call if we guarantee that
// 1) the new operator zero inits
// 2) we don't use this class on the stack
Clear();
}
CWCStringList::~CWCStringList()
{
#ifdef DEBUG
if (m_iNumStrings > 100)
SpewHashStats(TRUE);
#endif
CleanUp();
}
// Clean up any allocated memory
void CWCStringList::CleanUp()
{
// Must free buffers even if not m_fValid, because we may have
// gotten only partway through Init() before running into a problem.
if (m_pBuffer)
{
MemFree(m_pBuffer);
m_pBuffer = NULL;
}
if (m_psiStrings)
{
MemFree(m_psiStrings);
m_psiStrings = NULL;
}
m_fValid = FALSE;
}
// Clear our internal structures to prepare to be initialized. Assumes we
// have no allocated memory (call CleanUp())
void CWCStringList::Clear()
{
m_fValid = FALSE;
m_iBufEnd = m_iBufSize = m_iNumStrings = m_iMaxStrings = 0;
m_pBuffer = NULL;
m_psiStrings = NULL;
ZeroMemory(m_Hash, sizeof(m_Hash));
}
void CWCStringList::Reset()
{
if (m_fValid || m_pBuffer || m_psiStrings)
{
CleanUp();
Clear();
}
}
BOOL CWCStringList::Init(int iInitBufSize)
{
if (m_fValid)
{
DBG("WCStringList::Init called when already initialized");
Reset();
}
if (iInitBufSize <= 0)
{
iInitBufSize = DEFAULT_INIT_BUF_SIZE;
}
m_iMaxStrings = iInitBufSize >> 5; // this is relatively arbitrary but doesn't matter much
m_pBuffer = (LPSTR)MemAlloc(LMEM_FIXED, iInitBufSize);
m_psiStrings = (LPSTRING_INDEX)MemAlloc(LMEM_FIXED, m_iMaxStrings * sizeof(STRING_INDEX));
if ((NULL == m_psiStrings) ||
(NULL == m_pBuffer))
{
DBG_WARN("Init() memory allocation failed");
CleanUp();
return FALSE;
}
*m_pBuffer = 0;
m_iBufSize = iInitBufSize;
m_iBufEnd = 0;
m_fValid = TRUE;
return TRUE;
}
// Sets up our internal data structures (hash and string_index)
// Sets m_iBufEnd.
// We must already be Init()ialized and the data in m_pBuffer
BOOL CWCStringList::InitializeFromBuffer()
{
LPCWSTR pNext;
int iLen;
if (!m_fValid)
return FALSE;
pNext = (LPCWSTR)m_pBuffer;
while (((LPSTR)pNext-m_pBuffer) < m_iBufSize)
{
iLen = lstrlenW(pNext);
InsertToHash(pNext, iLen, FALSE);
pNext += iLen+1;
}
m_iBufEnd = (int)((LPSTR)pNext - m_pBuffer);
return TRUE;
}
//
// IPersistStream members
//
// We save
// DWORD containing total length in bytes that follows. Will be
// multiple of four; may have 0-4 extra pad bytes on the end.
// String data.
//
// Smallest data we store is 4 bytes of zeroes. We still end up taking
// memory when we get restored. Don't instantiate one of these objects
// until you're going to use it.
STDMETHODIMP CWCStringList::IsDirty(void)
{
DBG("CWCStringList::IsDirty returning S_OK (true) as always");
return S_OK;
}
STDMETHODIMP CWCStringList::Load(IStream *pStm)
{
HRESULT hr;
ULONG cbRead;
DWORD dwDataSize;
DBG("CWCStringList::Load");
if (NULL==pStm)
return E_POINTER;
// Clean up our object
Reset();
// Load our data
hr = pStm->Read(&dwDataSize, sizeof(DWORD), &cbRead);
if (FAILED(hr) || cbRead != sizeof(DWORD))
return STG_E_READFAULT;
if (0 == dwDataSize)
{
if (!Init(512)) // Start with small buffer since we're empty
return E_OUTOFMEMORY;
return S_OK;
}
if (!Init(dwDataSize))
return E_OUTOFMEMORY;
ASSERT(dwDataSize <= (DWORD)m_iBufSize);
// Read in the string data
hr = pStm->Read(m_pBuffer, dwDataSize, &cbRead);
if (FAILED(hr) || cbRead != dwDataSize)
return STG_E_READFAULT;
// Set up hash tables etc.
InitializeFromBuffer();
DBG("CWCStringList::Load success");
return NOERROR;
}
STDMETHODIMP CWCStringList::Save(IStream *pStm, BOOL fClearDirty)
{
HRESULT hr;
ULONG cbWritten;
DWORD dwDataSize, dwZero=0;
DWORD dwZeroPad;
DBG("CWCStringList::Save");
if (NULL==pStm)
return E_POINTER;
// First write our data
dwDataSize = (m_iBufEnd+3) & 0xFFFFFFFC; // multiple of four
if ((0 == m_iBufSize) || (0 == m_iNumStrings))
{
dwDataSize = 0;
}
hr = pStm->Write(&dwDataSize, sizeof(DWORD), &cbWritten);
if (FAILED(hr) || sizeof(DWORD) != cbWritten)
return STG_E_WRITEFAULT;
if (dwDataSize > 0)
{
hr = pStm->Write(m_pBuffer, m_iBufSize, &cbWritten);
if (FAILED(hr) || sizeof(DWORD) != cbWritten)
return STG_E_WRITEFAULT;
dwZeroPad = dwDataSize - m_iBufSize;
ASSERT(dwZeroPad<4);
if (dwZeroPad && dwZeroPad<4)
{
hr = pStm->Write(&dwZero, dwZeroPad, &cbWritten);
if (FAILED(hr) || (dwZeroPad != cbWritten))
return STG_E_WRITEFAULT;
}
}
DBG("CWCStringList::Save success");
return NOERROR;
}
STDMETHODIMP CWCStringList::GetSizeMax(ULARGE_INTEGER *pcbSize)
{
DBG("CWCStringList::GetSizeMax");
if (NULL==pcbSize)
return E_POINTER;
pcbSize->LowPart = 0;
pcbSize->HighPart = 0;
pcbSize->LowPart = m_iBufEnd + 8;
return NOERROR;
}
// Returns a BSTR
BSTR CWCStringList::GetBSTR(int iNum)
{
LPCWSTR lpStr = GetString(iNum);
return SysAllocStringLen(lpStr, GetStringLen(iNum));
}
// Returns FALSE if string is not found
// Places string index (for GetString()) in *piNum only if string is found.
BOOL CWCStringList::FindString(LPCWSTR lpwstr, int iLen, int *piNum/*=NULL*/)
{
int iHash;
LPSTRING_INDEX psi;
if (!lpwstr)
return FALSE;
if (iLen < 0)
iLen = lstrlenW(lpwstr);
iHash = Hash(lpwstr, iLen);
for (psi = m_Hash[iHash]; psi; psi = psi->psiNext)
{
if ((psi->iLen == iLen) && memcmp(psi->lpwstr, lpwstr, iLen * sizeof(WCHAR)) == 0)
{
if (piNum)
*piNum = (int) (psi-m_psiStrings);
return TRUE; // String is a duplicate
}
}
return FALSE;
}
// returns STRLST_FAIL on failure,
// STRLST_DUPLICATE if the string already existed, and
// STRLST_ADDED if it's new
int CWCStringList::AddString(LPCWSTR lpwstr, DWORD_PTR dwData /*=NULL*/, int *piNum /*=NULL*/)
{
int iSize, iLen;
if (!lpwstr)
return STRLST_FAIL;
iLen = lstrlenW(lpwstr);
if (!m_fValid || !m_pBuffer)
{
DBG_WARN("WCStringList: AddString() called with invalid instance");
return STRLST_FAIL;
}
if (dwData != 0)
DBG_WARN("Value for dwData passed into CWCStringList::AddString");
if (FindString(lpwstr, iLen, piNum))
return STRLST_DUPLICATE; // String is a duplicate
// iSize will be size in bytes including null term
iSize = (iLen+1)*sizeof(WCHAR);
// Append string to current buffer
if (iSize >= (m_iBufSize - m_iBufEnd))
{
int iOldBufSize = m_iBufSize;
// Grow buffer.
m_iBufSize *= 2; // This way the number of reallocs drops off logarithmically
if (m_iBufEnd + iSize > m_iBufSize)
{
DBG("StringList special growing size");
m_iBufSize = m_iBufEnd + iSize;
}
TraceMsg(TF_THISMODULE,"StringList growing to size %d",m_iBufSize);
LPSTR pBuf = (LPSTR)MemReAlloc((HLOCAL)m_pBuffer, m_iBufSize, LMEM_MOVEABLE);
if (!pBuf)
{
m_iBufSize = iOldBufSize;
DBG_WARN("WCStringList: ReAlloc() failure");
// Realloc failure: our old memory is still present
return 0;
}
// Let's be clever and fix all our pointers instead of getting faults
if (m_pBuffer != pBuf)
{
int i;
LPSTRING_INDEX psi;
for (i=0, psi=&m_psiStrings[0]; i<m_iNumStrings; i++, psi++)
{
psi->lpwstr = (LPWSTR)(((LPSTR)psi->lpwstr - m_pBuffer) + pBuf);
}
m_pBuffer = pBuf;
}
}
if (piNum)
*piNum = m_iNumStrings;
LPWSTR pBufEnd = (LPWSTR)(m_pBuffer + m_iBufEnd);
StrCpyW(pBufEnd, lpwstr);
if (!InsertToHash(pBufEnd, iLen, TRUE))
return 0;
m_iBufEnd += iSize;
return STRLST_ADDED; // indicate we added a new string
}
BOOL CWCStringList::InsertToHash(LPCWSTR lpwstr, int iLen, BOOL fAlreadyHashed)
{
int iHash = fAlreadyHashed ? m_iLastHash : Hash(lpwstr, iLen);
// grow psiStrings if needed
ASSERT(m_iNumStrings <= m_iMaxStrings);
if (m_iNumStrings >= m_iMaxStrings)
{
m_iMaxStrings *= 2;
TraceMsg(TF_THISMODULE, "StringList growing max strings to %d", m_iMaxStrings);
LPSTRING_INDEX psiBuf = (LPSTRING_INDEX)MemReAlloc((HLOCAL)m_psiStrings,
m_iMaxStrings * sizeof(STRING_INDEX), LMEM_MOVEABLE);
if (!psiBuf)
{
// Realloc failure: Old memory still present
DBG_WARN("WCStringList::InsertToHash() ReAlloc failure");
m_iMaxStrings /= 2;
return FALSE;
}
// More cleverness
if (m_psiStrings != psiBuf)
{
int i;
LPSTRING_INDEX psi, *ppsi;
for (i=0, psi=psiBuf; i<m_iNumStrings; i++, psi++)
{
if (psi->psiNext)
psi->psiNext = (psi->psiNext - m_psiStrings) + psiBuf;
}
for (i=0, ppsi=m_Hash; i<STRING_HASH_SIZE; i++, ppsi++)
{
if (*ppsi)
*ppsi = (*ppsi - m_psiStrings) + psiBuf;
}
m_psiStrings = psiBuf;
}
}
m_psiStrings[m_iNumStrings].lpwstr = lpwstr;
m_psiStrings[m_iNumStrings].iLen = iLen;
m_psiStrings[m_iNumStrings].psiNext = m_Hash[iHash];
m_Hash[iHash] = &m_psiStrings[m_iNumStrings];
m_iNumStrings++;
return TRUE;
}
#ifdef DEBUG
// WARNING: this clobbers the hash
void CWCStringList::SpewHashStats(BOOL fVerbose)
{
/*
int i;
for (i = 0; i < STRING_HASH_SIZE; ++i)
{
int c = 0;
for (tagStringIndex *p = m_Hash[i]; p; p = p->psiNext)
++c;
if (c)
TraceMsg(TF_THISMODULE,"%10d%12d", i, c);
}
*/
TraceMsg(TF_THISMODULE,"### Hash size: %d Num. entries:%7d", STRING_HASH_SIZE, m_iNumStrings);
int i,n;
if (fVerbose)
{
TraceMsg(TF_THISMODULE," # of entries # of keys with that many");
for (i=0,n=0; n<STRING_HASH_SIZE; i++)
{
int k=0;
for (int j=0; j<STRING_HASH_SIZE; j++)
{
int c=0;
for (tagStringIndex* p=m_Hash[j]; p; p=p->psiNext)
c++;
if (c == i)
k++;
}
if (k)
{
TraceMsg(TF_THISMODULE,"%10d%12d", i, k);
n += k;
}
}
}
/*
int total=0;
if (fVerbose)
{
TraceMsg(TF_THISMODULE," length # of strings with that length",1);
for (i=0,n=0; n<m_iNumStrings; i++)
{
int k=0;
for (int j=0; j<m_iNumStrings; j++)
{
if (m_psiStrings[j].iLen == i)
k++;
}
if (k)
{
if (fVerbose)
TraceMsg(TF_THISMODULE,"%5d%10d", i, k);
n += k;
total += k*(k+1)/2;
}
}
}
TraceMsg(TF_THISMODULE,"### Average compares without hash * 100:%5d", total*100/m_iNumStrings);
total=0;
for (i=0; i<STRING_HASH_SIZE; i++)
{
for (tagStringIndex* p=m_Hash[i]; p; p=p->psiNext)
{
if (p->iLen < 0) continue;
int n=1;
for (tagStringIndex* q=p->psiNext; q; q=q->psiNext)
{
if (p->iLen == q->iLen)
{
n++;
q->iLen = -1;
}
}
total += n*(n+1)/2;
}
}
TraceMsg(TF_THISMODULE,"### Average compares with hash * 100:%8d", total*100/m_iNumStrings);
*/
}
#endif
//----------------------------------------------------------------------------
// CWCDwordStringList
CWCDwordStringList::CWCDwordStringList() : CWCStringList()
{
}
CWCDwordStringList::~CWCDwordStringList()
{
if (m_pData)
MemFree((HLOCAL)m_pData);
}
BOOL CWCDwordStringList::Init(int iInitBufSize/*=-1*/)
{
if (!CWCStringList::Init(iInitBufSize))
return FALSE;
m_pData = (DWORD_PTR*)MemAlloc(LMEM_FIXED, m_iMaxStrings * sizeof(DWORD));
if (NULL == m_pData)
return FALSE;
return TRUE;
}
int CWCDwordStringList::AddString(LPCWSTR psz, DWORD_PTR dwData/*=0*/, int* piNum/*=NULL*/)
{
int iOldMaxStrings = m_iMaxStrings; // track changes in m_iMaxStrings by this call:
int iNum;
int iResult = CWCStringList::AddString(psz, 0, &iNum);
if (iResult == 0)
return 0;
if (iOldMaxStrings != m_iMaxStrings) // make sure we have enough data space
{
DWORD_PTR *pData;
// TraceMsg(TF_THISMODULE, "DwordStringList expanding dwords to %d", m_iMaxStrings);
pData = (DWORD_PTR*)MemReAlloc((HLOCAL)m_pData, m_iMaxStrings * sizeof(DWORD),
LMEM_MOVEABLE);
ASSERT(pData);
if (!pData)
{
DBG_WARN("Realloc failure in DwordStringList");
MemFree(m_pData);
m_pData = NULL; // This is bad
return 0;
}
m_pData = pData;
}
if (iResult == 2) // only set data value if this is a new string
{
m_pData[iNum] = dwData;
}
if (piNum)
*piNum = iNum;
return iResult;
}