475 lines
11 KiB
C++
475 lines
11 KiB
C++
#include "stdinc.h"
|
|
#include "stringpool.h"
|
|
#include "debmacro.h"
|
|
#include "fusiontrace.h"
|
|
|
|
//
|
|
// Implementation of the CStringPoolHeapSegment class:
|
|
//
|
|
|
|
BOOL
|
|
CStringPoolHeapSegment::Initialize()
|
|
{
|
|
// Nothing to do today but a good placeholder
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
CStringPoolHeapSegment::Initialize(
|
|
const WCHAR *StringIn,
|
|
ULONG Cch,
|
|
const WCHAR *&rpStringOut
|
|
)
|
|
{
|
|
BOOL fSuccess = FALSE;
|
|
FN_TRACE_WIN32(fSuccess);
|
|
|
|
// You shouldn't have gotten here if this was a "big" allocation
|
|
INTERNAL_ERROR_CHECK(Cch < NUMBER_OF(m_rgchBuffer));
|
|
INTERNAL_ERROR_CHECK(m_cchUsed == 0);
|
|
|
|
memcpy(m_rgchBuffer, StringIn, Cch * sizeof(WCHAR));
|
|
m_cchUsed = Cch;
|
|
rpStringOut = m_rgchBuffer;
|
|
|
|
fSuccess = TRUE;
|
|
Exit:
|
|
return fSuccess;
|
|
}
|
|
|
|
BOOL
|
|
CStringPoolHeapSegment::TryAllocString(
|
|
const WCHAR *StringIn,
|
|
ULONG Cch,
|
|
const WCHAR *&rpStringOut
|
|
)
|
|
{
|
|
BOOL fSuccess = FALSE;
|
|
FN_TRACE_WIN32(fSuccess);
|
|
|
|
// You shouldn't have gotten here if this was a "big" allocation
|
|
INTERNAL_ERROR_CHECK(Cch < NUMBER_OF(m_rgchBuffer));
|
|
|
|
rpStringOut = NULL;
|
|
|
|
if ((NUMBER_OF(m_rgchBuffer) - m_cchUsed) >= Cch)
|
|
{
|
|
WCHAR *pwch = &m_rgchBuffer[m_cchUsed];
|
|
memcpy(pwch, StringIn, Cch * sizeof(WCHAR));
|
|
rpStringOut = pwch;
|
|
m_cchUsed += Cch;
|
|
}
|
|
|
|
fSuccess = TRUE;
|
|
Exit:
|
|
return fSuccess;
|
|
}
|
|
|
|
//
|
|
// Implementation of the CStringPoolSingletonString class:
|
|
//
|
|
|
|
BOOL
|
|
CStringPoolSingletonString::Initialize(
|
|
const WCHAR *StringIn,
|
|
ULONG Cch,
|
|
const WCHAR *&rpStringOut
|
|
)
|
|
{
|
|
BOOL fSuccess = FALSE;
|
|
FN_TRACE_WIN32(fSuccess);
|
|
WCHAR *prgwch = NULL;
|
|
|
|
INTERNAL_ERROR_CHECK(m_prgwch == NULL);
|
|
|
|
if (Cch != 0)
|
|
{
|
|
prgwch = FUSION_NEW_ARRAY(WCHAR, Cch);
|
|
memcpy(prgwch, StringIn, Cch * sizeof(WCHAR));
|
|
m_prgwch = prgwch;
|
|
}
|
|
|
|
rpStringOut = prgwch;
|
|
prgwch = NULL;
|
|
|
|
fSuccess = TRUE;
|
|
Exit:
|
|
if (prgwch != NULL)
|
|
FUSION_DELETE_ARRAY(prgwch);
|
|
|
|
return fSuccess;
|
|
}
|
|
|
|
//
|
|
// Implementation of the CStringPoolHeap class:
|
|
//
|
|
|
|
BOOL
|
|
CStringPoolHeap::Initialize()
|
|
{
|
|
BOOL fSuccess = FALSE;
|
|
FN_TRACE_WIN32(fSuccess);
|
|
|
|
fSuccess = TRUE;
|
|
// Exit:
|
|
return fSuccess;
|
|
}
|
|
|
|
BOOL
|
|
CStringPoolHeap::DupString(
|
|
const WCHAR *StringIn,
|
|
ULONG Cch,
|
|
const WCHAR *&rpStringOut
|
|
)
|
|
{
|
|
BOOL fSuccess = FALSE;
|
|
FN_TRACE_WIN32(fSuccess);
|
|
CStringPoolSingletonString *pSingleton = NULL;
|
|
CStringPoolHeapSegment *pSegment = NULL;
|
|
|
|
rpStringOut = NULL;
|
|
|
|
if (Cch > CStringPoolHeapSegment::CchMax())
|
|
{
|
|
// It's too big to ever fit into a segment. Just allocate a singleton for it.
|
|
IFALLOCFAILED_EXIT(pSingleton = new CStringPoolSingletonString);
|
|
IFW32FALSE_EXIT(pSingleton->Initialize(StringIn, Cch, rpStringOut));
|
|
m_dequeSingletons.AddToTail(pSingleton);
|
|
pSingleton = NULL;
|
|
}
|
|
else
|
|
{
|
|
CDequeIterator<CStringPoolHeapSegment, FIELD_OFFSET(CStringPoolHeapSegment, m_leLinks)> iter(&m_dequeSegments);
|
|
|
|
for (iter.Reset(); iter.More(); iter.Next())
|
|
{
|
|
IFW32FALSE_EXIT(iter->TryAllocString(StringIn, Cch, rpStringOut));
|
|
if (rpStringOut != NULL)
|
|
break;
|
|
}
|
|
|
|
if (rpStringOut == NULL)
|
|
{
|
|
// I guess we need a new heap segment...
|
|
IFALLOCFAILED_EXIT(pSegment = new CStringPoolHeapSegment);
|
|
IFW32FALSE_EXIT(pSegment->Initialize(StringIn, Cch, rpStringOut));
|
|
INTERNAL_ERROR_CHECK(rpStringOut != NULL);
|
|
m_dequeSegments.AddToHead(pSegment);
|
|
pSegment = NULL;
|
|
}
|
|
}
|
|
|
|
fSuccess = TRUE;
|
|
Exit:
|
|
if (pSingleton != NULL)
|
|
FUSION_DELETE_SINGLETON(pSingleton);
|
|
|
|
if (pSegment != NULL)
|
|
FUSION_DELETE_SINGLETON(pSegment);
|
|
|
|
return fSuccess;
|
|
}
|
|
|
|
|
|
//
|
|
// Implementation of the CStringPoolEntry class:
|
|
//
|
|
|
|
BOOL
|
|
CStringPoolEntry::Initialize(
|
|
const WCHAR *StringIn,
|
|
ULONG CchIn,
|
|
ULONG ulPseudoKey,
|
|
CStringPoolHeap &rStringHeap
|
|
)
|
|
{
|
|
BOOL fSuccess = FALSE;
|
|
FN_TRACE_WIN32(fSuccess);
|
|
|
|
// Catch double initializations; a zero PseudoKey is pretty unlikely and we'll just miss the error in that
|
|
// one case.
|
|
INTERNAL_ERROR_CHECK(this->PseudoKey == 0);
|
|
|
|
IFW32FALSE_EXIT(rStringHeap.DupString(StringIn, CchIn, this->Buffer));
|
|
this->PseudoKey = ulPseudoKey;
|
|
this->Length = CchIn * sizeof(WCHAR);
|
|
|
|
fSuccess = TRUE;
|
|
Exit:
|
|
return fSuccess;
|
|
}
|
|
|
|
//
|
|
// Implementation of the CStringPoolEntryClump class:
|
|
//
|
|
|
|
CStringPoolEntryClump::~CStringPoolEntryClump()
|
|
{
|
|
if (m_prgEntries != NULL)
|
|
FUSION_DELETE_ARRAY(m_prgEntries);
|
|
|
|
m_prgEntries = NULL;
|
|
m_cEntriesAllocated = 0;
|
|
m_cEntriesUsed = 0;
|
|
}
|
|
|
|
BOOL
|
|
CStringPoolEntryClump::Initialize(
|
|
ULONG cStringsToAllocate
|
|
)
|
|
{
|
|
BOOL fSuccess = FALSE;
|
|
FN_TRACE_WIN32(fSuccess);
|
|
CStringPoolEntry *prgEntries = NULL;
|
|
|
|
ASSERT(cStringsToAllocate != 0);
|
|
|
|
PARAMETER_CHECK(cStringsToAllocate != 0);
|
|
|
|
IFALLOCFAILED_EXIT(prgEntries = FUSION_NEW_ARRAY(CStringPoolEntry, cStringsToAllocate));
|
|
|
|
m_prgEntries = prgEntries;
|
|
prgEntries = NULL;
|
|
m_cEntriesAllocated = cStringsToAllocate;
|
|
m_cEntriesUsed = 0;
|
|
|
|
fSuccess = TRUE;
|
|
Exit:
|
|
if (prgEntries != NULL)
|
|
FUSION_DELETE_ARRAY(prgEntries);
|
|
|
|
return fSuccess;
|
|
}
|
|
|
|
BOOL
|
|
CStringPoolEntryClump::FindOrAddEntry(
|
|
const WCHAR *StringIn,
|
|
ULONG CchIn,
|
|
ULONG ulPseudoKey,
|
|
CStringPoolHeap &rStringHeap,
|
|
ULONG &rulPosition,
|
|
CStringPoolEntryClump::FindOrAddDisposition &rdisposition,
|
|
const CStringPoolEntry *&rpEntry
|
|
)
|
|
{
|
|
BOOL fSuccess = FALSE;
|
|
FN_TRACE_WIN32(fSuccess);
|
|
ULONG i;
|
|
FindOrAddDisposition disposition = CStringPoolEntryClump::eInvalid;
|
|
const ULONG BytesIn = CchIn * sizeof(WCHAR);
|
|
|
|
rdisposition = CStringPoolEntryClump::eInvalid;
|
|
rpEntry = NULL;
|
|
|
|
for (i=0; i<m_cEntriesUsed; i++)
|
|
{
|
|
if (m_prgEntries[i].PseudoKey == ulPseudoKey)
|
|
{
|
|
if (m_prgEntries[i].Length == BytesIn)
|
|
{
|
|
if (memcmp(StringIn, m_prgEntries[i].Buffer, BytesIn) == 0)
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (i != m_cEntriesUsed)
|
|
{
|
|
// If we bailed out of the loop early, we must have found it.
|
|
disposition = CStringPoolEntryClump::eFound;
|
|
rulPosition = i;
|
|
rpEntry = &m_prgEntries[i];
|
|
}
|
|
else
|
|
{
|
|
// It's not already there; is there space for it?
|
|
if (m_cEntriesUsed != m_cEntriesAllocated)
|
|
{
|
|
IFW32FALSE_EXIT(m_prgEntries[m_cEntriesUsed].Initialize(StringIn, CchIn, ulPseudoKey, rStringHeap));
|
|
disposition = CStringPoolEntryClump::eAdded;
|
|
rpEntry = &m_prgEntries[m_cEntriesUsed];
|
|
rulPosition = m_cEntriesUsed++;
|
|
}
|
|
else
|
|
{
|
|
// Nope, keep going...
|
|
disposition = CStringPoolEntryClump::eNoRoom;
|
|
}
|
|
}
|
|
|
|
rdisposition = disposition;
|
|
|
|
fSuccess = TRUE;
|
|
Exit:
|
|
return fSuccess;
|
|
}
|
|
|
|
BOOL
|
|
CStringPoolEntryClump::FillInStringArray(
|
|
ULONG nArraySize,
|
|
SXS_XML_STRING *prgStrings,
|
|
ULONG iCurrent,
|
|
ULONG &rcWritten
|
|
)
|
|
{
|
|
BOOL fSuccess = FALSE;
|
|
FN_TRACE_WIN32(fSuccess);
|
|
ULONG i;
|
|
|
|
rcWritten = 0;
|
|
|
|
PARAMETER_CHECK(iCurrent < nArraySize);
|
|
PARAMETER_CHECK((iCurrent + m_cEntriesUsed) <= nArraySize);
|
|
|
|
for (i=0; i<m_cEntriesUsed; i++)
|
|
prgStrings[iCurrent++] = m_prgEntries[i];
|
|
|
|
rcWritten = m_cEntriesUsed;
|
|
fSuccess = TRUE;
|
|
Exit:
|
|
return fSuccess;
|
|
}
|
|
|
|
CStringPool::~CStringPool()
|
|
{
|
|
m_dequeEntryClumps.Clear<CStringPool>(this, CStringPool::ClearDequeEntry);
|
|
}
|
|
|
|
BOOL
|
|
CStringPool::Initialize()
|
|
{
|
|
BOOL fSuccess = FALSE;
|
|
FN_TRACE_WIN32(fSuccess);
|
|
|
|
INTERNAL_ERROR_CHECK(!m_fInitialized);
|
|
|
|
IFW32FALSE_EXIT(m_StringHeap.Initialize());
|
|
|
|
m_fInitialized = true;
|
|
m_cEntries = 0;
|
|
|
|
fSuccess = TRUE;
|
|
Exit:
|
|
return fSuccess;
|
|
}
|
|
|
|
BOOL
|
|
CStringPool::Canonicalize(
|
|
const WCHAR *StringIn,
|
|
ULONG CchIn,
|
|
ULONG ulPseudoKey,
|
|
ULONG &rulPosition,
|
|
const WCHAR *&rStringOut
|
|
)
|
|
{
|
|
BOOL fSuccess = FALSE;
|
|
FN_TRACE_WIN32(fSuccess);
|
|
ULONG ulPosition = 1;
|
|
CDequeIterator<CStringPoolEntryClump, FIELD_OFFSET(CStringPoolEntryClump, m_leEntryChain)> iter;
|
|
CStringPoolEntryClump::FindOrAddDisposition disposition = CStringPoolEntryClump::eNoRoom;
|
|
CStringPoolEntryClump *pNewClump = NULL;
|
|
const CStringPoolEntry *pEntry = NULL;
|
|
|
|
rulPosition = 0;
|
|
rStringOut = NULL;
|
|
|
|
iter.Rebind(&m_dequeEntryClumps);
|
|
|
|
for (iter.Reset(); iter.More(); iter.Next())
|
|
{
|
|
ULONG i;
|
|
|
|
IFW32FALSE_EXIT(iter->FindOrAddEntry(StringIn, CchIn, ulPseudoKey, m_StringHeap, i, disposition, pEntry));
|
|
|
|
if ((disposition == CStringPoolEntryClump::eFound) || (disposition == CStringPoolEntryClump::eAdded))
|
|
{
|
|
if (disposition == CStringPoolEntryClump::eAdded)
|
|
m_cEntries++;
|
|
|
|
ulPosition += i;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
// No room is equivalent to not found...
|
|
ASSERT(disposition == CStringPoolEntryClump::eNoRoom);
|
|
ulPosition += iter->EntriesUsed();
|
|
disposition = CStringPoolEntryClump::eNoRoom;
|
|
}
|
|
}
|
|
|
|
// If we get here with disposition == eNoRoom, we neither found it nor had room in an existing clump, so we need
|
|
// to allocate a new clump...
|
|
if (disposition == CStringPoolEntryClump::eNoRoom)
|
|
{
|
|
ULONG i;
|
|
|
|
IFALLOCFAILED_EXIT(pNewClump = new CStringPoolEntryClump);
|
|
IFW32FALSE_EXIT(pNewClump->Initialize());
|
|
IFW32FALSE_EXIT(pNewClump->FindOrAddEntry(StringIn, CchIn, ulPseudoKey, m_StringHeap, i, disposition, pEntry));
|
|
m_dequeEntryClumps.AddToTail(pNewClump);
|
|
pNewClump = NULL;
|
|
ASSERT(disposition == CStringPoolEntryClump::eAdded);
|
|
ASSERT(i == 0);
|
|
m_cEntries++;
|
|
}
|
|
|
|
INTERNAL_ERROR_CHECK(pEntry != NULL);
|
|
|
|
rulPosition = ulPosition;
|
|
rStringOut = pEntry->Buffer;
|
|
|
|
fSuccess = TRUE;
|
|
|
|
Exit:
|
|
if (pNewClump != NULL)
|
|
FUSION_DELETE_SINGLETON(pNewClump);
|
|
|
|
return fSuccess;
|
|
}
|
|
|
|
BOOL
|
|
CStringPool::FillInStringArray(
|
|
ULONG nArraySize,
|
|
SXS_XML_STRING *prgStrings,
|
|
ULONG &rcWritten
|
|
)
|
|
{
|
|
BOOL fSuccess = FALSE;
|
|
FN_TRACE_WIN32(fSuccess);
|
|
ULONG i;
|
|
CDequeIterator<CStringPoolEntryClump, FIELD_OFFSET(CStringPoolEntryClump, m_leEntryChain)> iter;
|
|
|
|
if (nArraySize < (m_cEntries + 1))
|
|
{
|
|
::SetLastError(ERROR_INSUFFICIENT_BUFFER);
|
|
goto Exit;
|
|
}
|
|
|
|
iter.Rebind(&m_dequeEntryClumps);
|
|
|
|
// We start filling in at index 1, not zero. We'll manually set zero to be invalid.
|
|
prgStrings[0].Flags = SXS_XML_STRING_FLAG_INVALID;
|
|
prgStrings[0].PseudoKey = 0;
|
|
prgStrings[0].Length = 0;
|
|
prgStrings[0].Buffer = NULL;
|
|
|
|
i = 1;
|
|
|
|
for (iter.Reset(); iter.More(); iter.Next())
|
|
{
|
|
ULONG cWrittenThisClump = 0;
|
|
IFW32FALSE_EXIT(iter->FillInStringArray(nArraySize, prgStrings, i, cWrittenThisClump));
|
|
INTERNAL_ERROR_CHECK((i + cWrittenThisClump) <= nArraySize);
|
|
i += cWrittenThisClump;
|
|
}
|
|
|
|
rcWritten = i;
|
|
|
|
fSuccess = TRUE;
|
|
Exit:
|
|
return fSuccess;
|
|
|
|
}
|
|
|