windows-nt/Source/XPSP1/NT/base/win32/fusion/sxs/ssgenctx.cpp
2020-09-26 16:20:57 +08:00

665 lines
20 KiB
C++

/*++
Copyright (c) 2000 Microsoft Corporation
Module Name:
ssgenctx.cpp
Abstract:
String section generation context object implementation.
Author:
Michael J. Grier (MGrier) 23-Feb-2000
Revision History:
--*/
#include "stdinc.h"
#include <windows.h>
#include "sxsp.h"
#include "ssgenctx.h"
typedef struct _CALLBACKDATA
{
union
{
STRING_SECTION_GENERATION_CONTEXT_CBDATA_GETDATASIZE GetDataSize;
STRING_SECTION_GENERATION_CONTEXT_CBDATA_GETDATA GetData;
STRING_SECTION_GENERATION_CONTEXT_CBDATA_ENTRYDELETED EntryDeleted;
STRING_SECTION_GENERATION_CONTEXT_CBDATA_GETUSERDATASIZE GetUserDataSize;
STRING_SECTION_GENERATION_CONTEXT_CBDATA_GETUSERDATA GetUserData;
} u;
} CALLBACKDATA, *PCALLBACKDATA;
BOOL CSSGenCtx::Create(
PSTRING_SECTION_GENERATION_CONTEXT *SSGenContext,
ULONG DataFormatVersion,
BOOL CaseInSensitive,
STRING_SECTION_GENERATION_CONTEXT_CALLBACK_FUNCTION CallbackFunction,
PVOID CallbackContext
)
{
BOOL fSuccess = FALSE;
FN_TRACE_WIN32(fSuccess);
CSSGenCtx *pSSGenCtx;
IFALLOCFAILED_EXIT(pSSGenCtx = new CSSGenCtx);
pSSGenCtx->m_CallbackFunction = CallbackFunction;
pSSGenCtx->m_CallbackContext = CallbackContext;
pSSGenCtx->m_CaseInSensitive = (CaseInSensitive != FALSE);
pSSGenCtx->m_DataFormatVersion = DataFormatVersion;
*SSGenContext = (PSTRING_SECTION_GENERATION_CONTEXT) pSSGenCtx;
fSuccess = TRUE;
Exit:
return fSuccess;
}
CSSGenCtx::CSSGenCtx() : m_DoneAdding(false)
{
m_FirstEntry = NULL;
m_LastEntry = NULL;
m_EntryCount = 0;
m_HashTableSize = 0;
}
CSSGenCtx::~CSSGenCtx()
{
CSxsPreserveLastError ple;
CALLBACKDATA CBData;
Entry *pEntry = m_FirstEntry;
while (pEntry != NULL)
{
Entry *pNext = pEntry->m_Next;
CBData.u.EntryDeleted.DataContext = pEntry->m_DataContext;
(*m_CallbackFunction)(
m_CallbackContext,
STRING_SECTION_GENERATION_CONTEXT_CALLBACK_REASON_ENTRYDELETED,
&CBData);
FUSION_DELETE_SINGLETON(pEntry);
pEntry = pNext;
}
ple.Restore();
}
BOOL
CSSGenCtx::Add(
PCWSTR String,
SIZE_T Cch,
PVOID DataContext,
ULONG AssemblyRosterIndex,
DWORD DuplicateErrorCode
)
{
BOOL fSuccess = FALSE;
FN_TRACE_WIN32(fSuccess);
ULONG PseudoKey = 0;
Entry *pEntry = NULL;
PARAMETER_CHECK(DuplicateErrorCode != ERROR_SUCCESS);
INTERNAL_ERROR_CHECK(!m_DoneAdding);
if ((String != NULL) && (String[0] == L'\0'))
String = NULL;
IFW32FALSE_EXIT(::SxspHashString(String, Cch, &PseudoKey, m_CaseInSensitive));
for (pEntry = m_FirstEntry; pEntry != NULL; pEntry = pEntry->m_Next)
{
if ((pEntry->m_PseudoKey == PseudoKey) &&
(pEntry->m_StringBuffer.Cch() == Cch) &&
(::FusionpCompareStrings(
String,
Cch,
pEntry->m_StringBuffer,
Cch,
m_CaseInSensitive) == 0))
{
::FusionpSetLastWin32Error(DuplicateErrorCode);
pEntry = NULL;
goto Exit;
}
}
IFALLOCFAILED_EXIT(pEntry = new Entry);
if (!pEntry->Initialize(String, Cch, PseudoKey, DataContext, AssemblyRosterIndex))
goto Exit;
if (m_LastEntry == NULL)
m_FirstEntry = pEntry;
else
m_LastEntry->m_Next = pEntry;
m_LastEntry = pEntry;
pEntry = NULL;
m_EntryCount++;
fSuccess = TRUE;
Exit:
FUSION_DELETE_SINGLETON(pEntry);
return fSuccess;
}
BOOL
CSSGenCtx::Find(
PCWSTR String,
SIZE_T Cch,
PVOID *DataContext,
BOOL *Found
)
{
BOOL fSuccess = FALSE;
FN_TRACE_WIN32(fSuccess);
ULONG PseudoKey = 0;
Entry *pEntry = NULL;
if (DataContext != NULL)
*DataContext = NULL;
if (Found != NULL)
*Found = FALSE;
if ((String != NULL) && (String[0] == L'\0'))
String = NULL;
PARAMETER_CHECK(Found != NULL);
PARAMETER_CHECK((Cch == 0) || (String != NULL));
IFW32FALSE_EXIT(::SxspHashString(String, Cch, &PseudoKey, m_CaseInSensitive));
for (pEntry = m_FirstEntry; pEntry != NULL; pEntry = pEntry->m_Next)
{
if ((pEntry->m_PseudoKey == PseudoKey) &&
(pEntry->m_StringBuffer.Cch() == Cch) &&
(::FusionpCompareStrings(
String,
Cch,
pEntry->m_StringBuffer,
Cch,
m_CaseInSensitive) == 0))
break;
}
if (pEntry != NULL)
{
*Found = TRUE;
if (DataContext != NULL)
*DataContext = pEntry->m_DataContext;
}
fSuccess = TRUE;
Exit:
return fSuccess;
}
BOOL
CSSGenCtx::DoneAdding()
{
if (!m_DoneAdding)
{
// This is where to really figure out the optimal hash table size
// first level guess...
if (m_EntryCount < 3)
m_HashTableSize = 0;
else if (m_EntryCount < 15)
m_HashTableSize = 3;
else if (m_EntryCount < 100)
m_HashTableSize = 11;
else
m_HashTableSize = 101;
m_DoneAdding = true;
}
return TRUE;
}
BOOL
CSSGenCtx::GetSectionSize(
PSIZE_T SizeOut
)
{
BOOL fSuccess = FALSE;
FN_TRACE_WIN32(fSuccess);
SIZE_T UserDataSize = 0;
SIZE_T HeaderSize = 0;
SIZE_T EntryListSize = 0;
SIZE_T EntryDataSize = 0;
SIZE_T StringsSize = 0;
SIZE_T HashTableSize = 0;
CALLBACKDATA CBData;
Entry *pEntry = NULL;
if (SizeOut != NULL)
*SizeOut = 0;
PARAMETER_CHECK(SizeOut != NULL);
HeaderSize = sizeof(ACTIVATION_CONTEXT_STRING_SECTION_HEADER);
if (m_HashTableSize != 0)
{
//
// The data for the hash table includes:
//
// 1. a small fixed sized struct representing the metadata for the hash
// table (ACTIVATION_CONTEXT_STRING_SECTION_HASH_TABLE)
//
// 2. For each table bucket, a small struct pointing to the beginning of
// the collision chain and the length of said chain
// (ACTIVATION_CONTEXT_SECTION_STRING_HASH_BUCKET)
//
// 3. One entry in a collision chain per entry in the table. The entry
// is a LONG offset from the beginning of the section.
//
HashTableSize = sizeof(ACTIVATION_CONTEXT_STRING_SECTION_HASH_TABLE) +
(m_HashTableSize * sizeof(ACTIVATION_CONTEXT_STRING_SECTION_HASH_BUCKET)) +
(m_EntryCount * sizeof(LONG));
}
CBData.u.GetUserDataSize.DataSize = 0;
(*m_CallbackFunction)(m_CallbackContext, STRING_SECTION_GENERATION_CONTEXT_CALLBACK_REASON_GETUSERDATASIZE, &CBData);
UserDataSize = ROUND_ACTCTXDATA_SIZE(CBData.u.GetUserDataSize.DataSize);
EntryListSize = m_EntryCount * sizeof(ACTIVATION_CONTEXT_STRING_SECTION_ENTRY);
for (pEntry = m_FirstEntry; pEntry != NULL; pEntry = pEntry->m_Next)
{
CBData.u.GetDataSize.DataContext = pEntry->m_DataContext;
CBData.u.GetDataSize.DataSize = 0;
(*m_CallbackFunction)(m_CallbackContext, STRING_SECTION_GENERATION_CONTEXT_CALLBACK_REASON_GETDATASIZE, &CBData);
EntryDataSize += ROUND_ACTCTXDATA_SIZE(CBData.u.GetDataSize.DataSize);
// Only allocate space for non-null strings. If the null string is a key in the table,
// it takes up 0 bytes.
if (pEntry->m_StringBuffer.Cch() != 0)
StringsSize += ROUND_ACTCTXDATA_SIZE((pEntry->m_StringBuffer.Cch() + 1) * sizeof(WCHAR));
}
// If there's nothing to contain, don't even ask for space for the header.
if ((UserDataSize == 0) && (m_EntryCount == 0))
*SizeOut = 0;
else
*SizeOut = HeaderSize + UserDataSize + EntryListSize + EntryDataSize + StringsSize + HashTableSize;
fSuccess = TRUE;
Exit:
return fSuccess;
}
BOOL
CSSGenCtx::GetSectionData(
SIZE_T BufferSize,
PVOID Buffer,
PSIZE_T BytesWritten
)
{
BOOL fSuccess = FALSE;
FN_TRACE_WIN32(fSuccess);
SIZE_T BytesSoFar = 0;
SIZE_T BytesLeft = BufferSize;
PACTIVATION_CONTEXT_STRING_SECTION_HEADER Header;
PACTIVATION_CONTEXT_STRING_SECTION_HASH_TABLE HashTable = NULL;
PACTIVATION_CONTEXT_STRING_SECTION_HASH_BUCKET HashBucket = NULL;
PLONG HashCollisionChain = NULL;
CALLBACKDATA CBData;
PVOID Cursor = NULL;
SIZE_T RoundedSize;
if (BytesWritten != NULL)
*BytesWritten = 0;
if (BytesLeft < sizeof(ACTIVATION_CONTEXT_STRING_SECTION_HEADER))
{
::FusionpSetLastWin32Error(ERROR_INSUFFICIENT_BUFFER);
goto Exit;
}
Header = (PACTIVATION_CONTEXT_STRING_SECTION_HEADER) Buffer;
Cursor = (PVOID) (Header + 1);
Header->Magic = ACTIVATION_CONTEXT_STRING_SECTION_MAGIC;
Header->HeaderSize = sizeof(ACTIVATION_CONTEXT_STRING_SECTION_HEADER);
Header->FormatVersion = ACTIVATION_CONTEXT_STRING_SECTION_FORMAT_WHISTLER;
Header->DataFormatVersion = m_DataFormatVersion;
Header->Flags = 0;
if (m_CaseInSensitive)
Header->Flags |= ACTIVATION_CONTEXT_STRING_SECTION_CASE_INSENSITIVE;
Header->ElementCount = m_EntryCount;
Header->ElementListOffset = 0; // filled in after we figure out the user data area
Header->HashAlgorithm = SxspGetHashAlgorithm();
Header->SearchStructureOffset = 0;
Header->UserDataOffset = 0; // filled in below
Header->UserDataSize = 0;
BytesLeft -= sizeof(*Header);
BytesSoFar += sizeof(*Header);
CBData.u.GetUserDataSize.DataSize = 0;
(*m_CallbackFunction)(m_CallbackContext, STRING_SECTION_GENERATION_CONTEXT_CALLBACK_REASON_GETUSERDATASIZE, &CBData);
RoundedSize = ROUND_ACTCTXDATA_SIZE(CBData.u.GetUserDataSize.DataSize);
if (RoundedSize > BytesLeft)
{
::FusionpSetLastWin32Error(ERROR_INSUFFICIENT_BUFFER);
goto Exit;
}
if (RoundedSize != 0)
{
CBData.u.GetUserData.SectionHeader = Header;
CBData.u.GetUserData.BufferSize = RoundedSize;
CBData.u.GetUserData.Buffer = Cursor;
CBData.u.GetUserData.BytesWritten = 0;
(*m_CallbackFunction)(m_CallbackContext, STRING_SECTION_GENERATION_CONTEXT_CALLBACK_REASON_GETUSERDATA, &CBData);
ASSERT(CBData.u.GetUserData.BytesWritten <= RoundedSize);
RoundedSize = ROUND_ACTCTXDATA_SIZE(CBData.u.GetUserData.BytesWritten);
if (RoundedSize != 0)
{
BytesLeft -= RoundedSize;
BytesSoFar += RoundedSize;
Header->UserDataSize = static_cast<ULONG>(CBData.u.GetUserData.BytesWritten);
Header->UserDataOffset = static_cast<LONG>(((LONG_PTR) Cursor) - ((LONG_PTR) Header));
Cursor = (PVOID) (((ULONG_PTR) Cursor) + RoundedSize);
}
}
// Finally the array of entries...
if (m_EntryCount != 0)
{
PVOID DataCursor;
PACTIVATION_CONTEXT_STRING_SECTION_ENTRY EntryArray;
ULONG iEntry;
Entry *SrcEntry;
if (BytesLeft < (m_EntryCount * sizeof(ACTIVATION_CONTEXT_STRING_SECTION_ENTRY)))
{
::FusionpSetLastWin32Error(ERROR_INSUFFICIENT_BUFFER);
goto Exit;
}
BytesLeft -= (m_EntryCount * sizeof(ACTIVATION_CONTEXT_STRING_SECTION_ENTRY));
BytesSoFar += (m_EntryCount * sizeof(ACTIVATION_CONTEXT_STRING_SECTION_ENTRY));
EntryArray = (PACTIVATION_CONTEXT_STRING_SECTION_ENTRY) Cursor;
Header->ElementListOffset = static_cast<LONG>(((LONG_PTR) EntryArray) - ((LONG_PTR) Header));
DataCursor = (PVOID) (EntryArray + m_EntryCount);
SrcEntry = m_FirstEntry;
iEntry = 0;
while (SrcEntry != NULL)
{
// Record the offset to this entry; we use it later during hash table population
SrcEntry->m_EntryOffset = static_cast<LONG>(((LONG_PTR) &EntryArray[iEntry]) - ((LONG_PTR) Header));
EntryArray[iEntry].PseudoKey = SrcEntry->m_PseudoKey;
EntryArray[iEntry].AssemblyRosterIndex = SrcEntry->m_AssemblyRosterIndex;
if (SrcEntry->m_StringBuffer.Cch() != 0)
{
const USHORT Cb = static_cast<USHORT>((SrcEntry->m_StringBuffer.Cch() + 1) * sizeof(WCHAR));
RoundedSize = ROUND_ACTCTXDATA_SIZE(Cb);
if (BytesLeft < RoundedSize)
{
::FusionpSetLastWin32Error(ERROR_INSUFFICIENT_BUFFER);
goto Exit;
}
EntryArray[iEntry].KeyLength = Cb - sizeof(WCHAR);
EntryArray[iEntry].KeyOffset = static_cast<LONG>(((LONG_PTR) DataCursor) - ((LONG_PTR) Header));
memcpy(
DataCursor,
static_cast<PCWSTR>(SrcEntry->m_StringBuffer),
Cb);
DataCursor = (PVOID) (((ULONG_PTR) DataCursor) + RoundedSize);
BytesLeft -= Cb;
BytesSoFar += Cb;
}
else
{
EntryArray[iEntry].KeyLength = 0;
EntryArray[iEntry].KeyOffset = 0;
}
CBData.u.GetDataSize.DataContext = SrcEntry->m_DataContext;
CBData.u.GetDataSize.DataSize = 0;
(*m_CallbackFunction)(m_CallbackContext, STRING_SECTION_GENERATION_CONTEXT_CALLBACK_REASON_GETDATASIZE, &CBData);
if (CBData.u.GetDataSize.DataSize != 0)
{
RoundedSize = ROUND_ACTCTXDATA_SIZE(CBData.u.GetDataSize.DataSize);
if (BytesLeft < RoundedSize)
{
::FusionpSetLastWin32Error(ERROR_INSUFFICIENT_BUFFER);
goto Exit;
}
CBData.u.GetData.SectionHeader = Header;
CBData.u.GetData.DataContext = SrcEntry->m_DataContext;
CBData.u.GetData.BufferSize = RoundedSize;
CBData.u.GetData.Buffer = DataCursor;
CBData.u.GetData.BytesWritten = 0;
(*m_CallbackFunction)(m_CallbackContext, STRING_SECTION_GENERATION_CONTEXT_CALLBACK_REASON_GETDATA, &CBData);
if (CBData.u.GetData.BytesWritten != 0)
{
// If this assert fires, a contributor wrote past the bounds they
// were given.
ASSERT(CBData.u.GetData.BytesWritten <= RoundedSize);
if (CBData.u.GetData.BytesWritten > RoundedSize)
{
// Probably we have memory corruption, but at least we'll bail and
// avoid further scribbling on memory.
::FusionpDbgPrintEx(
FUSION_DBG_LEVEL_ERROR,
"SXS.DLL: String section data generation callback wrote more bytes than it should have. Bailing out and hoping memory isn't trashed.\n");
::FusionpSetLastWin32Error(ERROR_INTERNAL_ERROR);
goto Exit;
}
RoundedSize = ROUND_ACTCTXDATA_SIZE(CBData.u.GetData.BytesWritten);
BytesLeft -= RoundedSize;
BytesSoFar += RoundedSize;
EntryArray[iEntry].Offset = static_cast<LONG>(((LONG_PTR) DataCursor) - ((LONG_PTR) Header));
EntryArray[iEntry].Length = static_cast<ULONG>(CBData.u.GetData.BytesWritten);
DataCursor = (PVOID) (((ULONG_PTR) DataCursor) + RoundedSize);
}
else
{
EntryArray[iEntry].Offset = 0;
EntryArray[iEntry].Length = 0;
}
}
else
{
EntryArray[iEntry].Offset = 0;
EntryArray[iEntry].Length = 0;
}
SrcEntry = SrcEntry->m_Next;
iEntry++;
}
ASSERT(iEntry == m_EntryCount);
// If we're not generating a hash table, let's sort 'em.
if (m_HashTableSize == 0)
{
qsort(EntryArray, m_EntryCount, sizeof(ACTIVATION_CONTEXT_STRING_SECTION_ENTRY), &CSSGenCtx::CompareStringSectionEntries);
Header->Flags |= ACTIVATION_CONTEXT_STRING_SECTION_ENTRIES_IN_PSEUDOKEY_ORDER;
}
Cursor = (PVOID) DataCursor;
}
// Write the hash table at the end. We do it here so that the placement of everything else is
// already worked out.
if (m_HashTableSize != 0)
{
ULONG iBucket;
Entry *pEntry;
ULONG cCollisions;
if (BytesLeft < sizeof(ACTIVATION_CONTEXT_STRING_SECTION_HASH_TABLE))
{
::FusionpSetLastWin32Error(ERROR_INSUFFICIENT_BUFFER);
goto Exit;
}
HashTable = (PACTIVATION_CONTEXT_STRING_SECTION_HASH_TABLE) Cursor;
Cursor = (PVOID) (HashTable + 1);
BytesLeft -= sizeof(ACTIVATION_CONTEXT_STRING_SECTION_HASH_TABLE);
BytesSoFar += sizeof(ACTIVATION_CONTEXT_STRING_SECTION_HASH_TABLE);
if (BytesLeft < (m_HashTableSize * sizeof(ACTIVATION_CONTEXT_STRING_SECTION_HASH_BUCKET)))
{
::FusionpSetLastWin32Error(ERROR_INSUFFICIENT_BUFFER);
goto Exit;
}
HashBucket = (PACTIVATION_CONTEXT_STRING_SECTION_HASH_BUCKET) Cursor;
Cursor = (PVOID) (HashBucket + m_HashTableSize);
BytesLeft -= (m_HashTableSize * sizeof(ACTIVATION_CONTEXT_STRING_SECTION_HASH_BUCKET));
BytesSoFar += (m_HashTableSize * sizeof(ACTIVATION_CONTEXT_STRING_SECTION_HASH_BUCKET));
Header->SearchStructureOffset = static_cast<LONG>(((LONG_PTR) HashTable) - ((LONG_PTR) Header));
HashTable->BucketTableEntryCount = m_HashTableSize;
HashTable->BucketTableOffset = static_cast<LONG>(((LONG_PTR) HashBucket) - ((LONG_PTR) Header));
if (BytesLeft < (m_EntryCount * sizeof(LONG)))
{
::FusionpSetLastWin32Error(ERROR_INSUFFICIENT_BUFFER);
goto Exit;
}
HashCollisionChain = (PLONG) Cursor;
Cursor = (PVOID) (HashCollisionChain + m_EntryCount);
BytesLeft -= (m_EntryCount * sizeof(LONG));
BytesSoFar += (m_EntryCount * sizeof(LONG));
// In a disgusting move, we need to iterate over the hash buckets (not elements)
// finding which entries would go into the bucket so that we can build up the
// collision chain.
for (pEntry = m_FirstEntry; pEntry != NULL; pEntry = pEntry->m_Next)
pEntry->m_HashBucketIndex = pEntry->m_PseudoKey % m_HashTableSize;
cCollisions = 0;
for (iBucket=0; iBucket<m_HashTableSize; iBucket++)
{
bool fFirstForThisBucket = true;
HashBucket[iBucket].ChainCount = 0;
HashBucket[iBucket].ChainOffset = 0;
for (pEntry = m_FirstEntry; pEntry != NULL; pEntry = pEntry->m_Next)
{
if (pEntry->m_HashBucketIndex == iBucket)
{
if (fFirstForThisBucket)
{
HashBucket[iBucket].ChainOffset = static_cast<LONG>(((LONG_PTR) &HashCollisionChain[cCollisions]) - ((LONG_PTR) Header));
fFirstForThisBucket = false;
}
HashBucket[iBucket].ChainCount++;
HashCollisionChain[cCollisions++] = pEntry->m_EntryOffset;
}
}
}
}
if (BytesWritten != NULL)
*BytesWritten = BytesSoFar;
fSuccess = TRUE;
Exit:
return fSuccess;
}
int
__cdecl
CSSGenCtx::CompareStringSectionEntries(
const void *elem1,
const void *elem2
)
{
PCACTIVATION_CONTEXT_STRING_SECTION_ENTRY pEntry1 = reinterpret_cast<PCACTIVATION_CONTEXT_STRING_SECTION_ENTRY>(elem1);
PCACTIVATION_CONTEXT_STRING_SECTION_ENTRY pEntry2 = reinterpret_cast<PCACTIVATION_CONTEXT_STRING_SECTION_ENTRY>(elem2);
if (pEntry1->PseudoKey < pEntry2->PseudoKey)
return -1;
else if (pEntry1->PseudoKey == pEntry2->PseudoKey)
return 0;
return 1;
}
BOOL
CSSGenCtx::Entry::Initialize(
PCWSTR String,
SIZE_T Cch,
ULONG PseudoKey,
PVOID DataContext,
ULONG AssemblyRosterIndex
)
{
BOOL fSuccess = FALSE;
FN_TRACE_WIN32(fSuccess);
IFW32FALSE_EXIT(m_StringBuffer.Win32Assign(String, Cch));
m_DataContext = DataContext;
m_PseudoKey = PseudoKey;
m_AssemblyRosterIndex = AssemblyRosterIndex;
fSuccess = TRUE;
Exit:
return fSuccess;
}