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.
427 lines
14 KiB
427 lines
14 KiB
#pragma once
|
|
#include "basic.h"
|
|
#include "encode.h"
|
|
|
|
// there are 3 types of pool:
|
|
// (1). case-insensitive string pool
|
|
// (2). case-sensitive string pool
|
|
// (3). GUID pool
|
|
|
|
#define MAX_DWORD 0xFFFF
|
|
typedef enum _sxs_pool_type_{
|
|
SXS_POOL_TYPE_UNIDENTIFIED,
|
|
SXS_POOL_TYPE_GUID,
|
|
SXS_POOL_TYPE_CASE_INSENSITIVE_STRING,
|
|
SXS_POOL_TYPE_CASE_SENSITIVE_STRING
|
|
}SXS_POOL_TYPE;
|
|
|
|
// default action is error if already exist in the pool
|
|
#define SXS_POOL_ADD_IF_ALREADY_IN_POOL_IGNORE 0x0001
|
|
|
|
#define SXS_STRING_POOL_DEFAULT_SIZE_IN_BYTE 1024
|
|
#define SXS_GUID_POOL_DEFAULT_SIZE_IN_BYTE 1024
|
|
|
|
#define SXS_POOL_DEFAULT_INDEX_TABLE_SIZE 32
|
|
|
|
#define SXS_POOL_INDEXTABLE_PROBE_ONLY 0x0000
|
|
#define SXS_POOL_INDEXTABLE_PROBE_IF_NOT_EXIST_INSERT_DATA 0x0001
|
|
|
|
#define SXS_POOL_ADD_CONVERTED_DATA_INTO_POOL 0x0001
|
|
#define SXS_POOL_ADD_DATA_INTOP_OOL_CONVERT_FIRST 0x0002
|
|
/*
|
|
class SXS_POOL_INDEX_ENTRY{
|
|
private:
|
|
inline VOID SetOffset(DWORD offset) {m_offset = offset;};
|
|
virtual VOID SetLength(DWORD length) = 0;
|
|
|
|
inline DWORD GetOffset() const {return m_offset; }
|
|
virtual DWORD GetLength() const = 0;
|
|
|
|
DWORD m_offset;
|
|
}
|
|
*/
|
|
class SXS_POOL_INDEX_ENTRY{
|
|
public:
|
|
SXS_POOL_INDEX_ENTRY(){}
|
|
|
|
virtual VOID SetIndexEntry(DWORD offset, DWORD length) = 0;
|
|
virtual VOID GetIndexEntry(DWORD & offset, DWORD * length) = 0;
|
|
inline DWORD GetOffset() const {return m_offset;}
|
|
inline VOID SetOffset(DWORD offset) {m_offset = offset;}
|
|
|
|
protected:
|
|
DWORD m_offset;
|
|
};
|
|
|
|
class SXS_STRING_POOL_INDEX_ENTRY : public SXS_POOL_INDEX_ENTRY{
|
|
public:
|
|
inline VOID SetIndexEntry(DWORD offset, DWORD length) { m_offset = offset; m_length = length;}
|
|
inline VOID GetIndexEntry(DWORD & offset, DWORD * length){ offset = m_offset; ASSERT(length != NULL); *length = m_length;}
|
|
inline DWORD GetLength() const { return m_length; }
|
|
private:
|
|
DWORD m_length;
|
|
};
|
|
|
|
class SXS_GUID_POOL_INDEX_ENTRY : public SXS_POOL_INDEX_ENTRY{
|
|
public:
|
|
inline VOID SetIndexEntry(DWORD offset, DWORD length = 0){ m_offset = offset; }
|
|
inline VOID GetIndexEntry(DWORD & offset, DWORD * length = NULL) {offset = m_offset; }
|
|
};
|
|
|
|
class SXS_POOL_DATA{
|
|
public:
|
|
SXS_POOL_DATA(DWORD dwUnitSize):m_dwCount(1), m_dwUnitSize(dwUnitSize){}
|
|
SXS_POOL_DATA(DWORD dwUnitSize, DWORD c):m_dwCount(c), m_dwUnitSize(dwUnitSize){}
|
|
|
|
inline BOOL IsContentEqual(SXS_POOL_DATA & b)
|
|
{
|
|
if (this->GetSizeInByte() == b.GetSizeInByte())
|
|
return (memcmp(this->GetPtr(), b.GetPtr(), GetSizeInByte()) == 0);
|
|
return FALSE;
|
|
}
|
|
inline DWORD GetSizeInByte() const { return m_dwCount * m_dwUnitSize; }
|
|
virtual PBYTE GetPtr() const = 0;
|
|
|
|
protected:
|
|
DWORD m_dwCount;
|
|
DWORD m_dwUnitSize;
|
|
};
|
|
|
|
template <typename TSimpleDataType>
|
|
class SXS_SIMPLEDATA_POOL_DATA : public SXS_POOL_DATA
|
|
{
|
|
public:
|
|
SXS_SIMPLEDATA_POOL_DATA(TSimpleDataType & d): m_data(d) {SXS_SIMPLEDATA_POOL_DATA(); }
|
|
SXS_SIMPLEDATA_POOL_DATA(TSimpleDataType d): m_data(d) {SXS_SIMPLEDATA_POOL_DATA(); }
|
|
inline VOID SetValue(const TSimpleDataType & data) {m_data = data;}
|
|
inline VOID GetValue(TSimpleDataType & data) {data = m_data;}
|
|
inline PBYTE GetPtr() const { return (PBYTE)&m_data; }; // since it is already a ref
|
|
|
|
protected:
|
|
TSimpleDataType & m_data;
|
|
SXS_SIMPLEDATA_POOL_DATA():SXS_POOL_DATA(sizeof(TSimpleDataType)){}
|
|
};
|
|
//
|
|
// this class is never been reassigned, that is, once it is assigned value, it will keep this value until it is deconstructed
|
|
//
|
|
template <typename TCHAR>
|
|
class SXS_STRING_DATA : public SXS_POOL_DATA{
|
|
public:
|
|
SXS_STRING_DATA():
|
|
m_pstrData(NULL), m_fMemoryAllocatedInside(false), m_fValueAssigned(false), SXS_POOL_DATA(sizeof(TCHAR), 0), m_Cch(m_dwCount){}
|
|
|
|
~SXS_STRING_DATA()
|
|
{
|
|
if (m_fMemoryAllocatedInside) {
|
|
FUSION_FREE_ARRAY(m_pstrData);
|
|
}
|
|
}
|
|
NTSTATUS NtAssign(PCWSTR Str, DWORD Cch);
|
|
NTSTATUS NtAssign(PBYTE Str, DWORD Ccb);
|
|
|
|
NTSTATUS NtResize(DWORD cch)
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
FN_TRACE_NTSTATUS(Status);
|
|
|
|
INTERNAL_ERROR_CHECK(m_fValueAssigned == false);
|
|
INTERNAL_ERROR_CHECK(m_pstrData == NULL);
|
|
|
|
IFALLOCFAILED_EXIT(m_pstrData = FUSION_NEW_ARRAY(TCHAR, cch));
|
|
m_Cch = cch;
|
|
|
|
FN_EPILOG;
|
|
}
|
|
|
|
inline VOID Clean() const {ASSERT(m_pstrData != NULL); m_pstrData[0] = 0;}
|
|
|
|
|
|
inline DWORD GetCch() const { return m_Cch; }
|
|
inline TCHAR* GetStr() const { return m_pstrData;}
|
|
inline TCHAR* GetBuffer() { return m_pstrData;}
|
|
inline PBYTE GetPtr() const { return (PBYTE)m_pstrData; }; // since it is already a ref
|
|
|
|
private:
|
|
TCHAR* m_pstrData; // offset in the pool
|
|
DWORD &m_Cch; // length of TCHAR
|
|
bool m_fValueAssigned;
|
|
bool m_fMemoryAllocatedInside;
|
|
};
|
|
|
|
|
|
template <typename TInputData, typename TIndexTableData, typename TPoolData, DWORD dwPoolSize = 0, DWORD dwIndexTableSize = 0>
|
|
class SxsPool{
|
|
public:
|
|
SxsPool():m_fInitialized(false), m_ePooltype(SXS_POOL_TYPE_UNIDENTIFIED), m_dwPoolSizeInByte(0),
|
|
m_pbPool(NULL), m_cursor(NULL), m_IndexData(NULL), m_IndexTableSize(0), m_cConflict(0) {}
|
|
|
|
~SxsPool(){
|
|
switch(m_ePooltype)
|
|
{
|
|
default:
|
|
ASSERT(FALSE); // never happen
|
|
break;
|
|
case SXS_POOL_TYPE_UNIDENTIFIED:
|
|
{
|
|
ASSERT(m_pbPool == NULL);
|
|
ASSERT(m_dwPoolSizeInByte == 0);
|
|
ASSERT(m_cursor == NULL);
|
|
ASSERT(m_IndexData == NULL);
|
|
ASSERT(m_IndexTableSize == 0);
|
|
|
|
break;
|
|
}
|
|
case SXS_POOL_TYPE_GUID:
|
|
case SXS_POOL_TYPE_CASE_INSENSITIVE_STRING:
|
|
case SXS_POOL_TYPE_CASE_SENSITIVE_STRING:
|
|
{
|
|
FUSION_DELETE_BLOB(m_pbPool);
|
|
FUSION_FREE_ARRAY(m_IndexData);
|
|
|
|
break;
|
|
}
|
|
}// end of switch
|
|
}
|
|
|
|
NTSTATUS Initialize(IN SXS_POOL_TYPE ePoolType)
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
FN_TRACE_NTSTATUS(Status);
|
|
|
|
PARAMETER_CHECK((ePooltype == SXS_GUID_POOL) ||
|
|
(ePooltype == SXS_CASE_INSENSITIVE_STRING_POOL) ||
|
|
(ePooltype == SXS_CASE_SENSITIVE_STRING_POOL));
|
|
|
|
INTERNAL_ERROR_CHECK(m_fInitialized == false);
|
|
m_fInitialized = true;
|
|
m_ePooltype = ePooltype;
|
|
|
|
m_dwPoolSizeInByte = dwPoolSize;
|
|
if (m_dwPoolSizeInByte == 0)
|
|
m_dwPoolSizeInByte = (ePooltype != SXS_GUID_POOL ? SXS_STRING_POOL_DEFAULT_SIZE_IN_BYTE : SXS_GUID_POOL_DEFAULT_SIZE_IN_BYTE);
|
|
IFALLOCFAILED_EXIT(m_pbPool = FUSION_NEW_BLOB(m_dwPoolSizeInByte));
|
|
m_cursor = NULL;
|
|
|
|
m_IndexTableSize = dwIndexTableSize > 0 ? dwIndexTableSize : SXS_POOL_DEFAULT_INDEX_TABLE_SIZE;
|
|
IFALLOCFAILED_EXIT(m_IndexData = FUSION_NEW_ARRAY(TIndexTableData, m_IndexTableSize));
|
|
ZeroMemory(m_IndexData, sizeof(TIndexTableData) * m_IndexTableSize);
|
|
|
|
FN_EPILOG;
|
|
}
|
|
|
|
// Function:
|
|
//
|
|
// adding a string/guid into pool, adding entry to index table
|
|
// return the index in the index table
|
|
// this function is used mostly
|
|
//
|
|
NTSTATUS Add(
|
|
IN DWORD dwFlags,
|
|
IN const TInputData& data,
|
|
OUT BOOL& fAlreadyExist,
|
|
OUT DWORD& dwIndexInTable
|
|
)
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
FN_TRACE_NTSTATUS(Status);
|
|
DWORD dwIndex;
|
|
TIndexTableData dataInIndexTable;
|
|
TPoolData poolData;
|
|
ULONG ulHash;
|
|
|
|
PARAMETER_CHECK((dwFlags & ~SXS_POOL_ADD_IF_ALREADY_IN_POOL_IGNORE) == 0);
|
|
|
|
INTERNAL_ERROR_CHECK(m_fInitialized == true);
|
|
|
|
IF_NOT_NTSTATUS_SUCCESS_EXIT(ConverInputDataIntoPoolData(0, data, poolData));
|
|
IF_NOT_NTSTATUS_SUCCESS_EXIT(HashData(0, data, ulHash));
|
|
// check whether the data has already in the pool and whether this is allowed
|
|
IF_NOT_NTSTATUS_SUCCESS_EXIT(LocateEntryInIndexTable(0, poolData, ulHash, fAlreadyExist, dwIndex));
|
|
|
|
if ((fAlreadyExist) && (!(dwFlags & SXS_POOL_ADD_IF_ALREADY_IN_POOL_IGNORE)))
|
|
{
|
|
Status = STATUS_DUPLICATE_NAME;
|
|
goto Exit;
|
|
}
|
|
|
|
IF_NOT_NTSTATUS_SUCCESS_EXIT(SaveDataIntoPool(0, dwIndex, poolData));
|
|
dwIndexInTable = dwIndex;
|
|
|
|
FN_EPILOG;
|
|
}
|
|
|
|
|
|
// Function:
|
|
//
|
|
// fetch data (string or a guid) from pool
|
|
// if data == NULL, length will be returned about the required bytes
|
|
//
|
|
NTSTATUS FetchDataFromPool(
|
|
IN DWORD dwFlags,
|
|
IN DWORD dwIndex,
|
|
OUT TInputData & data
|
|
)
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
FN_TRACE_NTSTATUS(Status);
|
|
TPoolData PoolData;
|
|
|
|
PARAMETER_CHECK(dwFlags == 0);
|
|
|
|
IF_NOT_NTSTATUS_SUCCESS_EXIT(GetDataFromPoolBasedOnIndexTable(m_IndexData[dwIndex], PoolData));
|
|
IF_NOT_NTSTATUS_SUCCESS_EXIT(ConvertPoolDataToOutputData(0, PoolData, data));
|
|
|
|
FN_EPILOG;
|
|
}
|
|
|
|
private:
|
|
// private functions
|
|
|
|
// functions must be instantiated
|
|
inline VOID SetIndexTableEntry(TIndexTableData & entry, DWORD offset, DWORD length =0);
|
|
|
|
NTSTATUS GetDataFromPoolBasedOnIndexTable(
|
|
IN TIndexTableData & indexData,
|
|
OUT TPoolData & pooldata
|
|
);
|
|
|
|
NTSTATUS HashData(
|
|
IN DWORD dwFlags,
|
|
IN const TInputData& data,
|
|
OUT ULONG& ulHashValue
|
|
);
|
|
|
|
NTSTATUS ConvertPoolDataToOutputData(
|
|
IN DWORD dwFlags,
|
|
IN TPoolData & dataInPool,
|
|
OUT TInputData & dataOut
|
|
);
|
|
|
|
NTSTATUS ConverInputDataIntoPoolData(
|
|
IN DWORD dwFlags,
|
|
IN const TInputData & dataOut,
|
|
OUT TPoolData & dataInPool
|
|
);
|
|
|
|
// "real" template functions
|
|
NTSTATUS ExtendPool(DWORD dwMiniRequirement)
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
FN_TRACE_NTSTATUS(Status);
|
|
BYTE * tpool = NULL;
|
|
|
|
DWORD dwExtend = MAX(dwMiniRequirement, m_dwPoolSizeInByte / 2);
|
|
if ( dwExtend + m_dwPoolSizeInByte > MAX_DWORD)
|
|
{
|
|
Status = STATUS_INTEGER_OVERFLOW;
|
|
goto Exit;
|
|
}
|
|
|
|
IFALLOCFAILED_EXIT(tpool = FUSION_NEW_BLOB(dwExtend + m_dwPoolSizeInByte));
|
|
DWORD dwOccupied = m_cursor - m_pbPool;
|
|
memcpy(tpool, m_pbPool, dwOccupied);
|
|
FUSION_DELETE_BLOB(m_pbPool);
|
|
m_pbPool = tpool;
|
|
tpool = NULL;
|
|
m_dwPoolSizeInByte = dwExtend + m_dwPoolSizeInByte;
|
|
m_cursor = m_pbPool + dwOccupied;
|
|
|
|
Exit:
|
|
if (tpool != NULL)
|
|
FUSION_DELETE_BLOB(tpool);
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS LocateEntryInIndexTable(
|
|
IN DWORD dwFlags,
|
|
IN const TPoolData& poolData,
|
|
IN ULONG ulHash,
|
|
OUT BOOL& fAlreadyExist,
|
|
OUT DWORD& dwIndexOut
|
|
)
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
FN_TRACE_NTSTATUS(Status);
|
|
|
|
ULONG ulHash2;
|
|
ULONG ulIndex;
|
|
TPoolData StoredData;
|
|
DWORD dwSizeMask = m_IndexTableSize - 1;
|
|
BOOL fExist = false;
|
|
|
|
PARAMETER_CHECK(dwFlags == 0);
|
|
fAlreadyExist = FALSE;
|
|
|
|
ulIndex = ulHash & dwSizeMask;
|
|
ulHash2 = ((ulHash * 17) & dwSizeMask) | 1;
|
|
while(1) {
|
|
if (m_IndexData[ulIndex].GetOffset() == 0) // index empty
|
|
break;
|
|
|
|
IF_NOT_NTSTATUS_SUCCESS_EXIT(GetDataFromPoolBasedOnIndexTable(m_IndexData[ulIndex], StoredData));
|
|
if (StoredData.IsContentEqual(const_cast<TPoolData &>(poolData)))
|
|
{
|
|
fExist = TRUE;
|
|
break;
|
|
}
|
|
|
|
// rehash
|
|
ulIndex = (ulIndex + ulHash2) & dwSizeMask;
|
|
m_cConflict ++;
|
|
}
|
|
|
|
fAlreadyExist = fExist;
|
|
dwIndexOut = (DWORD)ulIndex;
|
|
|
|
FN_EPILOG;
|
|
}
|
|
|
|
NTSTATUS SaveDataIntoPool(
|
|
IN DWORD dwFlags,
|
|
IN DWORD dwEntryIndexTable,
|
|
IN const TPoolData & poolData
|
|
)
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
FN_TRACE_NTSTATUS(Status);
|
|
|
|
PARAMETER_CHECK(dwFlags == 0);
|
|
|
|
DWORD dwRequiredDataSizeInByte = poolData.GetSizeInByte();
|
|
|
|
if ( dwRequiredDataSizeInByte > m_dwPoolSizeInByte - (m_cursor - m_pbPool))
|
|
{
|
|
IF_NOT_NTSTATUS_SUCCESS_EXIT(ExtendPool(dwRequiredDataSizeInByte));
|
|
}
|
|
|
|
memcpy(m_cursor, poolData.GetPtr(), dwRequiredDataSizeInByte);
|
|
m_IndexData[dwEntryIndexTable].SetIndexEntry(m_cursor - m_pbPool, poolData.GetCch());
|
|
m_cursor += dwRequiredDataSizeInByte;
|
|
|
|
FN_EPILOG;
|
|
}
|
|
|
|
// private data member
|
|
bool m_fInitialized;
|
|
SXS_POOL_TYPE m_ePooltype;
|
|
PBYTE m_pbPool;
|
|
DWORD m_dwPoolSizeInByte;
|
|
PBYTE m_cursor;
|
|
|
|
TIndexTableData* m_IndexData; // only needed for string pool
|
|
DWORD m_IndexTableSize;
|
|
|
|
// for statistics purpose
|
|
DWORD m_cConflict;
|
|
DWORD m_cSearch;
|
|
|
|
};
|
|
|
|
typedef SXS_STRING_DATA<BYTE> SXS_STRING_POOL_DATA;
|
|
typedef SXS_STRING_DATA<WCHAR> SXS_STRING_POOL_INPUT_DATA;
|
|
typedef SXS_SIMPLEDATA_POOL_DATA<GUID> SXS_GUID_POOL_DATA;
|
|
|
|
typedef SxsPool<SXS_STRING_POOL_INPUT_DATA, SXS_STRING_POOL_INDEX_ENTRY, SXS_STRING_POOL_DATA> SXS_STRING_POOL;
|
|
typedef SxsPool<GUID, SXS_GUID_POOL_INDEX_ENTRY, SXS_GUID_POOL_DATA> SXS_GUID_POOL;
|
|
|