#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 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 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 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(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 SXS_STRING_POOL_DATA; typedef SXS_STRING_DATA SXS_STRING_POOL_INPUT_DATA; typedef SXS_SIMPLEDATA_POOL_DATA SXS_GUID_POOL_DATA; typedef SxsPool SXS_STRING_POOL; typedef SxsPool SXS_GUID_POOL;