Files
game-physics/Effects11/inc/d3dxGlobal.h
2017-10-11 15:01:05 +02:00

1285 lines
34 KiB
C++

//--------------------------------------------------------------------------------------
// File: D3DXGlobal.h
//
// Direct3D 11 Effects helper defines and data structures
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// http://go.microsoft.com/fwlink/p/?LinkId=271568
//--------------------------------------------------------------------------------------
#pragma once
#include <assert.h>
#include <string.h>
namespace D3DX11Debug
{
// Helper sets a D3D resource name string (used by PIX and debug layer leak reporting).
inline void SetDebugObjectName(_In_ ID3D11DeviceChild* resource, _In_z_ const char *name )
{
#if !defined(NO_D3D11_DEBUG_NAME) && ( defined(_DEBUG) || defined(PROFILE) )
resource->SetPrivateData( WKPDID_D3DDebugObjectName, static_cast<UINT>(strlen(name)), name );
#else
UNREFERENCED_PARAMETER(resource);
UNREFERENCED_PARAMETER(name);
#endif
}
template<UINT TNameLength>
inline void SetDebugObjectName(_In_ ID3D11DeviceChild* resource, _In_z_ const char (&name)[TNameLength])
{
#if !defined(NO_D3D11_DEBUG_NAME) && ( defined(_DEBUG) || defined(PROFILE) )
resource->SetPrivateData(WKPDID_D3DDebugObjectName, TNameLength - 1, name);
#else
UNREFERENCED_PARAMETER(resource);
UNREFERENCED_PARAMETER(name);
#endif
}
}
using namespace D3DX11Debug;
#define SAFE_RELEASE(p) { if (p) { (p)->Release(); (p) = nullptr; } }
#define SAFE_ADDREF(p) { if (p) { (p)->AddRef(); } }
#define SAFE_DELETE_ARRAY(p) { delete [](p); p = nullptr; }
#define SAFE_DELETE(p) { delete (p); p = nullptr; }
#if FXDEBUG
#define __BREAK_ON_FAIL { __debugbreak(); }
#else
#define __BREAK_ON_FAIL
#endif
#define VA(x, action) { hr = (x); if (FAILED(hr)) { action; __BREAK_ON_FAIL; return hr; } }
#define VNA(x,action) { if (!(x)) { action; __BREAK_ON_FAIL; hr = E_OUTOFMEMORY; goto lExit; } }
#define VBA(x,action) { if (!(x)) { action; __BREAK_ON_FAIL; hr = E_FAIL; goto lExit; } }
#define VHA(x,action) { hr = (x); if (FAILED(hr)) { action; __BREAK_ON_FAIL; goto lExit; } }
#define V(x) { VA (x, 0) }
#define VN(x) { VNA(x, 0) }
#define VB(x) { VBA(x, 0) }
#define VH(x) { VHA(x, 0) }
#define VBD(x,str) { VBA(x, DPF(1,str)) }
#define VHD(x,str) { VHA(x, DPF(1,str)) }
#define VEASSERT(x) { hr = (x); if (FAILED(hr)) { __BREAK_ON_FAIL; assert(!#x); goto lExit; } }
#define VNASSERT(x) { if (!(x)) { __BREAK_ON_FAIL; assert(!#x); hr = E_OUTOFMEMORY; goto lExit; } }
#define D3DX11FLTASSIGN(a,b) { *reinterpret_cast< UINT32* >(&(a)) = *reinterpret_cast< UINT32* >(&(b)); }
// Preferred data alignment -- must be a power of 2!
static const uint32_t c_DataAlignment = sizeof(UINT_PTR);
inline uint32_t AlignToPowerOf2(uint32_t Value, uint32_t Alignment)
{
assert((Alignment & (Alignment - 1)) == 0);
// to align to 2^N, add 2^N - 1 and AND with all but lowest N bits set
_Analysis_assume_(Alignment > 0 && Value < MAXDWORD - Alignment);
return (Value + Alignment - 1) & (~(Alignment - 1));
}
inline void * AlignToPowerOf2(void *pValue, UINT_PTR Alignment)
{
assert((Alignment & (Alignment - 1)) == 0);
// to align to 2^N, add 2^N - 1 and AND with all but lowest N bits set
return (void *)(((UINT_PTR)pValue + Alignment - 1) & (~((UINT_PTR)Alignment - 1)));
}
namespace D3DX11Core
{
//////////////////////////////////////////////////////////////////////////
// CMemoryStream - A class to simplify reading binary data
//////////////////////////////////////////////////////////////////////////
class CMemoryStream
{
uint8_t *m_pData;
size_t m_cbData;
size_t m_readPtr;
public:
HRESULT SetData(_In_reads_bytes_(size) const void *pData, _In_ size_t size);
HRESULT Read(_Out_ uint32_t *pUint);
HRESULT Read(_Outptr_result_buffer_(size) void **ppData, _In_ size_t size);
HRESULT Read(_Outptr_ LPCSTR *ppString);
HRESULT ReadAtOffset(_In_ size_t offset, _In_ size_t size, _Outptr_result_buffer_(size) void **ppData);
HRESULT ReadAtOffset(_In_ size_t offset, _Outptr_result_z_ LPCSTR *ppString);
size_t GetPosition();
HRESULT Seek(_In_ size_t offset);
CMemoryStream();
~CMemoryStream();
};
}
#if defined(_DEBUG) && !defined(_M_X64)
namespace D3DX11Debug
{
// This variable indicates how many ticks to go before rolling over
// all of the timer variables in the FX system.
// It is read from the system registry in debug builds; in retail the high bit is simply tested.
_declspec(selectany) unsigned int g_TimerRolloverCount = 0x80000000;
}
#endif // _DEBUG && !_M_X64
//////////////////////////////////////////////////////////////////////////
// CEffectVector - A vector implementation
//////////////////////////////////////////////////////////////////////////
template<class T> class CEffectVector
{
protected:
#if _DEBUG
T *m_pCastData; // makes debugging easier to have a casted version of the data
#endif // _DEBUG
uint8_t *m_pData;
uint32_t m_MaxSize;
uint32_t m_CurSize;
HRESULT Grow()
{
return Reserve(m_CurSize + 1);
}
HRESULT Reserve(_In_ uint32_t DesiredSize)
{
if (DesiredSize > m_MaxSize)
{
uint8_t *pNewData;
uint32_t newSize = std::max(m_MaxSize * 2, DesiredSize);
if (newSize < 16)
newSize = 16;
if ((newSize < m_MaxSize) || (newSize < m_CurSize) || (newSize >= UINT_MAX / sizeof(T)))
{
m_hLastError = E_OUTOFMEMORY;
return m_hLastError;
}
pNewData = new uint8_t[newSize * sizeof(T)];
if (pNewData == nullptr)
{
m_hLastError = E_OUTOFMEMORY;
return m_hLastError;
}
if (m_pData)
{
memcpy(pNewData, m_pData, m_CurSize * sizeof(T));
delete []m_pData;
}
m_pData = pNewData;
m_MaxSize = newSize;
}
#if _DEBUG
m_pCastData = (T*) m_pData;
#endif // _DEBUG
return S_OK;
}
public:
HRESULT m_hLastError;
CEffectVector<T>() : m_hLastError(S_OK), m_pData(nullptr), m_CurSize(0), m_MaxSize(0)
{
#if _DEBUG
m_pCastData = nullptr;
#endif // _DEBUG
}
~CEffectVector<T>()
{
Clear();
}
// cleanly swaps two vectors -- useful for when you want
// to reallocate a vector and copy data over, then swap them back
void SwapVector(_Out_ CEffectVector<T> &vOther)
{
uint8_t tempData[sizeof(*this)];
memcpy(tempData, this, sizeof(*this));
memcpy(this, &vOther, sizeof(*this));
memcpy(&vOther, tempData, sizeof(*this));
}
HRESULT CopyFrom(_In_ const CEffectVector<T> &vOther)
{
HRESULT hr = S_OK;
Clear();
VN( m_pData = new uint8_t[vOther.m_MaxSize * sizeof(T)] );
m_CurSize = vOther.m_CurSize;
m_MaxSize = vOther.m_MaxSize;
m_hLastError = vOther.m_hLastError;
for (size_t i = 0; i < m_CurSize; ++ i)
{
((T*)m_pData)[i] = ((T*)vOther.m_pData)[i];
}
lExit:
#if _DEBUG
m_pCastData = (T*) m_pData;
#endif // _DEBUG
return hr;
}
void Clear()
{
Empty();
SAFE_DELETE_ARRAY(m_pData);
m_MaxSize = 0;
#if _DEBUG
m_pCastData = nullptr;
#endif // _DEBUG
}
void ClearWithoutDestructor()
{
m_CurSize = 0;
m_hLastError = S_OK;
SAFE_DELETE_ARRAY(m_pData);
m_MaxSize = 0;
#if _DEBUG
m_pCastData = nullptr;
#endif // _DEBUG
}
void Empty()
{
// manually invoke destructor on all elements
for (size_t i = 0; i < m_CurSize; ++ i)
{
((T*)m_pData + i)->~T();
}
m_CurSize = 0;
m_hLastError = S_OK;
}
T* Add()
{
if (FAILED(Grow()))
return nullptr;
// placement new
return new((T*)m_pData + (m_CurSize ++)) T;
}
T* AddRange(_In_ uint32_t count)
{
if (m_CurSize + count < m_CurSize)
{
m_hLastError = E_OUTOFMEMORY;
return nullptr;
}
if (FAILED(Reserve(m_CurSize + count)))
return nullptr;
T *pData = (T*)m_pData + m_CurSize;
for (size_t i = 0; i < count; ++ i)
{
new(pData + i) T;
}
m_CurSize += count;
return pData;
}
HRESULT Add(_In_ const T& var)
{
if (FAILED(Grow()))
return m_hLastError;
memcpy((T*)m_pData + m_CurSize, &var, sizeof(T));
m_CurSize++;
return S_OK;
}
HRESULT AddRange(_In_reads_(count) const T *pVar, _In_ uint32_t count)
{
if (m_CurSize + count < m_CurSize)
{
m_hLastError = E_OUTOFMEMORY;
return m_hLastError;
}
if (FAILED(Reserve(m_CurSize + count)))
return m_hLastError;
memcpy((T*)m_pData + m_CurSize, pVar, count * sizeof(T));
m_CurSize += count;
return S_OK;
}
HRESULT Insert(_In_ const T& var, _In_ uint32_t index)
{
assert(index < m_CurSize);
if (FAILED(Grow()))
return m_hLastError;
memmove((T*)m_pData + index + 1, (T*)m_pData + index, (m_CurSize - index) * sizeof(T));
memcpy((T*)m_pData + index, &var, sizeof(T));
m_CurSize++;
return S_OK;
}
HRESULT InsertRange(_In_reads_(count) const T *pVar, _In_ uint32_t index, _In_ uint32_t count)
{
assert(index < m_CurSize);
if (m_CurSize + count < m_CurSize)
{
m_hLastError = E_OUTOFMEMORY;
return m_hLastError;
}
if (FAILED(Reserve(m_CurSize + count)))
return m_hLastError;
memmove((T*)m_pData + index + count, (T*)m_pData + index, (m_CurSize - index) * sizeof(T));
memcpy((T*)m_pData + index, pVar, count * sizeof(T));
m_CurSize += count;
return S_OK;
}
inline T& operator[](_In_ size_t index)
{
assert(index < m_CurSize);
return ((T*)m_pData)[index];
}
// Deletes element at index and shifts all other values down
void Delete(_In_ uint32_t index)
{
assert(index < m_CurSize);
-- m_CurSize;
memmove((T*)m_pData + index, (T*)m_pData + index + 1, (m_CurSize - index) * sizeof(T));
}
// Deletes element at index and moves the last element into its place
void QuickDelete(_In_ uint32_t index)
{
assert(index < m_CurSize);
-- m_CurSize;
memcpy((T*)m_pData + index, (T*)m_pData + m_CurSize, sizeof(T));
}
inline uint32_t GetSize() const
{
return m_CurSize;
}
inline T* GetData() const
{
return (T*)m_pData;
}
uint32_t FindIndexOf(_In_ const void *pEntry) const
{
for (size_t i = 0; i < m_CurSize; ++ i)
{
if (((T*)m_pData + i) == pEntry)
return i;
}
return -1;
}
void Sort(int (__cdecl *pfnCompare)(const void *pElem1, const void *pElem2))
{
qsort(m_pData, m_CurSize, sizeof(T), pfnCompare);
}
};
//////////////////////////////////////////////////////////////////////////
// CEffectVectorOwner - implements a vector of ptrs to objects. The vector owns the objects.
//////////////////////////////////////////////////////////////////////////
template<class T> class CEffectVectorOwner : public CEffectVector<T*>
{
public:
~CEffectVectorOwner<T>()
{
Clear();
for (size_t i=0; i<m_CurSize; i++)
SAFE_DELETE(((T**)m_pData)[i]);
SAFE_DELETE_ARRAY(m_pData);
}
void Clear()
{
Empty();
SAFE_DELETE_ARRAY(m_pData);
m_MaxSize = 0;
}
void Empty()
{
// manually invoke destructor on all elements
for (size_t i = 0; i < m_CurSize; ++ i)
{
SAFE_DELETE(((T**)m_pData)[i]);
}
m_CurSize = 0;
m_hLastError = S_OK;
}
void Delete(_In_ uint32_t index)
{
assert(index < m_CurSize);
SAFE_DELETE(((T**)m_pData)[index]);
CEffectVector<T*>::Delete(index);
}
};
//////////////////////////////////////////////////////////////////////////
// Checked uint32_t, uint64_t
// Use CheckedNumber only with uint32_t and uint64_t
//////////////////////////////////////////////////////////////////////////
template <class T, T MaxValue> class CheckedNumber
{
T m_Value;
bool m_bValid;
public:
CheckedNumber<T, MaxValue>() : m_Value(0), m_bValid(true)
{
}
CheckedNumber<T, MaxValue>(const T &value) : m_Value(value), m_bValid(true)
{
}
CheckedNumber<T, MaxValue>(const CheckedNumber<T, MaxValue> &value) : m_bValid(value.m_bValid), m_Value(value.m_Value)
{
}
CheckedNumber<T, MaxValue> &operator+(const CheckedNumber<T, MaxValue> &other)
{
CheckedNumber<T, MaxValue> Res(*this);
return Res+=other;
}
CheckedNumber<T, MaxValue> &operator+=(const CheckedNumber<T, MaxValue> &other)
{
if (!other.m_bValid)
{
m_bValid = false;
}
else
{
m_Value += other.m_Value;
if (m_Value < other.m_Value)
m_bValid = false;
}
return *this;
}
CheckedNumber<T, MaxValue> &operator*(const CheckedNumber<T, MaxValue> &other)
{
CheckedNumber<T, MaxValue> Res(*this);
return Res*=other;
}
CheckedNumber<T, MaxValue> &operator*=(const CheckedNumber<T, MaxValue> &other)
{
if (!other.m_bValid)
{
m_bValid = false;
}
else
{
if (other.m_Value != 0)
{
if (m_Value > MaxValue / other.m_Value)
{
m_bValid = false;
}
}
m_Value *= other.m_Value;
}
return *this;
}
HRESULT GetValue(_Out_ T *pValue)
{
if (!m_bValid)
{
*pValue = uint32_t(-1);
return E_FAIL;
}
*pValue = m_Value;
return S_OK;
}
};
typedef CheckedNumber<uint32_t, _UI32_MAX> CCheckedDword;
typedef CheckedNumber<uint64_t, _UI64_MAX> CCheckedDword64;
//////////////////////////////////////////////////////////////////////////
// Data Block Store - A linked list of allocations
//////////////////////////////////////////////////////////////////////////
class CDataBlock
{
protected:
uint32_t m_size;
uint32_t m_maxSize;
uint8_t *m_pData;
CDataBlock *m_pNext;
bool m_IsAligned; // Whether or not to align the data to c_DataAlignment
public:
// AddData appends an existing use buffer to the data block
HRESULT AddData(_In_reads_bytes_(bufferSize) const void *pNewData, _In_ uint32_t bufferSize, _Outptr_ CDataBlock **ppBlock);
// Allocate reserves bufferSize bytes of contiguous memory and returns a pointer to the user
_Success_(return != nullptr)
void* Allocate(_In_ uint32_t bufferSize, _Outptr_ CDataBlock **ppBlock);
void EnableAlignment();
CDataBlock();
~CDataBlock();
friend class CDataBlockStore;
};
class CDataBlockStore
{
protected:
CDataBlock *m_pFirst;
CDataBlock *m_pLast;
uint32_t m_Size;
uint32_t m_Offset; // m_Offset gets added to offsets returned from AddData & AddString. Use this to set a global for the entire string block
bool m_IsAligned; // Whether or not to align the data to c_DataAlignment
public:
#if _DEBUG
uint32_t m_cAllocations;
#endif
public:
HRESULT AddString(_In_z_ LPCSTR pString, _Inout_ uint32_t *pOffset);
// Writes a null-terminated string to buffer
HRESULT AddData(_In_reads_bytes_(bufferSize) const void *pNewData, _In_ uint32_t bufferSize, _Inout_ uint32_t *pOffset);
// Writes data block to buffer
// Memory allocator support
void* Allocate(_In_ uint32_t bufferSize);
uint32_t GetSize();
void EnableAlignment();
CDataBlockStore();
~CDataBlockStore();
};
// Custom allocator that uses CDataBlockStore
// The trick is that we never free, so we don't have to keep as much state around
// Use PRIVATENEW in CEffectLoader
inline void* __cdecl operator new(_In_ size_t s, _In_ CDataBlockStore &pAllocator)
{
#ifdef _M_X64
assert( s <= 0xffffffff );
#endif
return pAllocator.Allocate( (uint32_t)s );
}
inline void __cdecl operator delete(_In_opt_ void* p, _In_ CDataBlockStore &pAllocator)
{
UNREFERENCED_PARAMETER(p);
UNREFERENCED_PARAMETER(pAllocator);
}
//////////////////////////////////////////////////////////////////////////
// Hash table
//////////////////////////////////////////////////////////////////////////
#define HASH_MIX(a,b,c) \
{ \
a -= b; a -= c; a ^= (c>>13); \
b -= c; b -= a; b ^= (a<<8); \
c -= a; c -= b; c ^= (b>>13); \
a -= b; a -= c; a ^= (c>>12); \
b -= c; b -= a; b ^= (a<<16); \
c -= a; c -= b; c ^= (b>>5); \
a -= b; a -= c; a ^= (c>>3); \
b -= c; b -= a; b ^= (a<<10); \
c -= a; c -= b; c ^= (b>>15); \
}
static uint32_t ComputeHash(_In_reads_bytes_(cbToHash) const uint8_t *pb, _In_ uint32_t cbToHash)
{
uint32_t cbLeft = cbToHash;
uint32_t a;
uint32_t b;
a = b = 0x9e3779b9; // the golden ratio; an arbitrary value
uint32_t c = 0;
while (cbLeft >= 12)
{
const uint32_t *pdw = reinterpret_cast<const uint32_t *>(pb);
a += pdw[0];
b += pdw[1];
c += pdw[2];
HASH_MIX(a,b,c);
pb += 12;
cbLeft -= 12;
}
c += cbToHash;
switch(cbLeft) // all the case statements fall through
{
case 11: c+=((uint32_t) pb[10] << 24);
case 10: c+=((uint32_t) pb[9] << 16);
case 9 : c+=((uint32_t) pb[8] << 8);
// the first byte of c is reserved for the length
case 8 : b+=((uint32_t) pb[7] << 24);
case 7 : b+=((uint32_t) pb[6] << 16);
case 6 : b+=((uint32_t) pb[5] << 8);
case 5 : b+=pb[4];
case 4 : a+=((uint32_t) pb[3] << 24);
case 3 : a+=((uint32_t) pb[2] << 16);
case 2 : a+=((uint32_t) pb[1] << 8);
case 1 : a+=pb[0];
}
HASH_MIX(a,b,c);
return c;
}
static uint32_t ComputeHashLower(_In_reads_bytes_(cbToHash) const uint8_t *pb, _In_ uint32_t cbToHash)
{
uint32_t cbLeft = cbToHash;
uint32_t a;
uint32_t b;
a = b = 0x9e3779b9; // the golden ratio; an arbitrary value
uint32_t c = 0;
while (cbLeft >= 12)
{
uint8_t pbT[12];
for( size_t i = 0; i < 12; i++ )
pbT[i] = (uint8_t)tolower(pb[i]);
uint32_t *pdw = reinterpret_cast<uint32_t *>(pbT);
a += pdw[0];
b += pdw[1];
c += pdw[2];
HASH_MIX(a,b,c);
pb += 12;
cbLeft -= 12;
}
c += cbToHash;
uint8_t pbT[12];
for( size_t i = 0; i < cbLeft; i++ )
pbT[i] = (uint8_t)tolower(pb[i]);
switch(cbLeft) // all the case statements fall through
{
case 11: c+=((uint32_t) pbT[10] << 24);
case 10: c+=((uint32_t) pbT[9] << 16);
case 9 : c+=((uint32_t) pbT[8] << 8);
// the first byte of c is reserved for the length
case 8 : b+=((uint32_t) pbT[7] << 24);
case 7 : b+=((uint32_t) pbT[6] << 16);
case 6 : b+=((uint32_t) pbT[5] << 8);
case 5 : b+=pbT[4];
case 4 : a+=((uint32_t) pbT[3] << 24);
case 3 : a+=((uint32_t) pbT[2] << 16);
case 2 : a+=((uint32_t) pbT[1] << 8);
case 1 : a+=pbT[0];
}
HASH_MIX(a,b,c);
return c;
}
static uint32_t ComputeHash(_In_z_ LPCSTR pString)
{
return ComputeHash(reinterpret_cast<const uint8_t*>(pString), (uint32_t)strlen(pString));
}
// 1) these numbers are prime
// 2) each is slightly less than double the last
// 4) each is roughly in between two powers of 2;
// (2^n hash table sizes are VERY BAD; they effectively truncate your
// precision down to the n least significant bits of the hash)
static const uint32_t c_PrimeSizes[] =
{
11,
23,
53,
97,
193,
389,
769,
1543,
3079,
6151,
12289,
24593,
49157,
98317,
196613,
393241,
786433,
1572869,
3145739,
6291469,
12582917,
25165843,
50331653,
100663319,
201326611,
402653189,
805306457,
1610612741,
};
template<typename T, bool (*pfnIsEqual)(const T &Data1, const T &Data2)>
class CEffectHashTable
{
protected:
struct SHashEntry
{
uint32_t Hash;
T Data;
SHashEntry *pNext;
};
// Array of hash entries
SHashEntry **m_rgpHashEntries;
uint32_t m_NumHashSlots;
uint32_t m_NumEntries;
bool m_bOwnHashEntryArray;
public:
class CIterator
{
friend class CEffectHashTable;
protected:
SHashEntry **ppHashSlot;
SHashEntry *pHashEntry;
public:
T GetData()
{
assert(pHashEntry != 0);
_Analysis_assume_(pHashEntry != 0);
return pHashEntry->Data;
}
uint32_t GetHash()
{
assert(pHashEntry != 0);
_Analysis_assume_(pHashEntry != 0);
return pHashEntry->Hash;
}
};
CEffectHashTable() : m_rgpHashEntries(nullptr), m_NumHashSlots(0), m_NumEntries(0), m_bOwnHashEntryArray(false)
{
}
HRESULT Initialize(_In_ const CEffectHashTable *pOther)
{
HRESULT hr = S_OK;
SHashEntry **rgpNewHashEntries = nullptr;
uint32_t valuesMigrated = 0;
uint32_t actualSize;
Cleanup();
actualSize = pOther->m_NumHashSlots;
VN( rgpNewHashEntries = new SHashEntry*[actualSize] );
ZeroMemory(rgpNewHashEntries, sizeof(SHashEntry*) * actualSize);
// Expensive operation: rebuild the hash table
CIterator iter, nextIter;
pOther->GetFirstEntry(&iter);
while (!pOther->PastEnd(&iter))
{
uint32_t index = iter.GetHash() % actualSize;
// we need to advance to the next element
// before we seize control of this element and move
// it to the new table
nextIter = iter;
pOther->GetNextEntry(&nextIter);
// seize this hash entry, migrate it to the new table
SHashEntry *pNewEntry;
VN( pNewEntry = new SHashEntry );
pNewEntry->pNext = rgpNewHashEntries[index];
pNewEntry->Data = iter.pHashEntry->Data;
pNewEntry->Hash = iter.pHashEntry->Hash;
rgpNewHashEntries[index] = pNewEntry;
iter = nextIter;
++ valuesMigrated;
}
assert(valuesMigrated == pOther->m_NumEntries);
m_rgpHashEntries = rgpNewHashEntries;
m_NumHashSlots = actualSize;
m_NumEntries = pOther->m_NumEntries;
m_bOwnHashEntryArray = true;
rgpNewHashEntries = nullptr;
lExit:
SAFE_DELETE_ARRAY( rgpNewHashEntries );
return hr;
}
protected:
void CleanArray()
{
if (m_bOwnHashEntryArray)
{
SAFE_DELETE_ARRAY(m_rgpHashEntries);
m_bOwnHashEntryArray = false;
}
}
public:
void Cleanup()
{
for (size_t i = 0; i < m_NumHashSlots; ++ i)
{
SHashEntry *pCurrentEntry = m_rgpHashEntries[i];
SHashEntry *pTempEntry;
while (nullptr != pCurrentEntry)
{
pTempEntry = pCurrentEntry->pNext;
SAFE_DELETE(pCurrentEntry);
pCurrentEntry = pTempEntry;
-- m_NumEntries;
}
}
CleanArray();
m_NumHashSlots = 0;
assert(m_NumEntries == 0);
}
~CEffectHashTable()
{
Cleanup();
}
static uint32_t GetNextHashTableSize(_In_ uint32_t DesiredSize)
{
// figure out the next logical size to use
for (size_t i = 0; i < _countof(c_PrimeSizes); ++i )
{
if (c_PrimeSizes[i] >= DesiredSize)
{
return c_PrimeSizes[i];
}
}
return DesiredSize;
}
// O(n) function
// Grows to the next suitable size (based off of the prime number table)
// DesiredSize is merely a suggestion
HRESULT Grow(_In_ uint32_t DesiredSize,
_In_ uint32_t ProvidedArraySize = 0,
_In_reads_opt_(ProvidedArraySize) void** ProvidedArray = nullptr,
_In_ bool OwnProvidedArray = false)
{
HRESULT hr = S_OK;
SHashEntry **rgpNewHashEntries = nullptr;
uint32_t valuesMigrated = 0;
uint32_t actualSize;
VB( DesiredSize > m_NumHashSlots );
actualSize = GetNextHashTableSize(DesiredSize);
if (ProvidedArray &&
ProvidedArraySize >= actualSize)
{
rgpNewHashEntries = reinterpret_cast<SHashEntry**>(ProvidedArray);
}
else
{
OwnProvidedArray = true;
VN( rgpNewHashEntries = new SHashEntry*[actualSize] );
}
ZeroMemory(rgpNewHashEntries, sizeof(SHashEntry*) * actualSize);
// Expensive operation: rebuild the hash table
CIterator iter, nextIter;
GetFirstEntry(&iter);
while (!PastEnd(&iter))
{
uint32_t index = iter.GetHash() % actualSize;
// we need to advance to the next element
// before we seize control of this element and move
// it to the new table
nextIter = iter;
GetNextEntry(&nextIter);
// seize this hash entry, migrate it to the new table
iter.pHashEntry->pNext = rgpNewHashEntries[index];
rgpNewHashEntries[index] = iter.pHashEntry;
iter = nextIter;
++ valuesMigrated;
}
assert(valuesMigrated == m_NumEntries);
CleanArray();
m_rgpHashEntries = rgpNewHashEntries;
m_NumHashSlots = actualSize;
m_bOwnHashEntryArray = OwnProvidedArray;
lExit:
return hr;
}
HRESULT AutoGrow()
{
// arbitrary heuristic -- grow if 1:1
if (m_NumEntries >= m_NumHashSlots)
{
// grows this hash table so that it is roughly 50% full
return Grow(m_NumEntries * 2 + 1);
}
return S_OK;
}
#if _DEBUG
void PrintHashTableStats()
{
if (m_NumHashSlots == 0)
{
DPF(0, "Uninitialized hash table!");
return;
}
float variance = 0.0f;
float mean = (float)m_NumEntries / (float)m_NumHashSlots;
uint32_t unusedSlots = 0;
DPF(0, "Hash table slots: %d, Entries in table: %d", m_NumHashSlots, m_NumEntries);
for (size_t i = 0; i < m_NumHashSlots; ++ i)
{
uint32_t entries = 0;
SHashEntry *pCurrentEntry = m_rgpHashEntries[i];
while (nullptr != pCurrentEntry)
{
SHashEntry *pCurrentEntry2 = m_rgpHashEntries[i];
// check other hash entries in this slot for hash collisions or duplications
while (pCurrentEntry2 != pCurrentEntry)
{
if (pCurrentEntry->Hash == pCurrentEntry2->Hash)
{
if (pfnIsEqual(pCurrentEntry->Data, pCurrentEntry2->Data))
{
assert(0);
DPF(0, "Duplicate entry (identical hash, identical data) found!");
}
else
{
DPF(0, "Hash collision (hash: %d)", pCurrentEntry->Hash);
}
}
pCurrentEntry2 = pCurrentEntry2->pNext;
}
pCurrentEntry = pCurrentEntry->pNext;
++ entries;
}
if (0 == entries)
{
++ unusedSlots;
}
// mean must be greater than 0 at this point
variance += (float)entries * (float)entries / mean;
}
variance /= std::max(1.0f, (m_NumHashSlots - 1));
variance -= (mean * mean);
DPF(0, "Mean number of entries per slot: %f, Standard deviation: %f, Unused slots; %d", mean, variance, unusedSlots);
}
#endif // _DEBUG
// S_OK if element is found, E_FAIL otherwise
HRESULT FindValueWithHash(_In_ T Data, _In_ uint32_t Hash, _Out_ CIterator *pIterator)
{
assert(m_NumHashSlots > 0);
uint32_t index = Hash % m_NumHashSlots;
SHashEntry *pEntry = m_rgpHashEntries[index];
while (nullptr != pEntry)
{
if (Hash == pEntry->Hash && pfnIsEqual(pEntry->Data, Data))
{
pIterator->ppHashSlot = m_rgpHashEntries + index;
pIterator->pHashEntry = pEntry;
return S_OK;
}
pEntry = pEntry->pNext;
}
return E_FAIL;
}
// S_OK if element is found, E_FAIL otherwise
HRESULT FindFirstMatchingValue(_In_ uint32_t Hash, _Out_ CIterator *pIterator)
{
assert(m_NumHashSlots > 0);
uint32_t index = Hash % m_NumHashSlots;
SHashEntry *pEntry = m_rgpHashEntries[index];
while (nullptr != pEntry)
{
if (Hash == pEntry->Hash)
{
pIterator->ppHashSlot = m_rgpHashEntries + index;
pIterator->pHashEntry = pEntry;
return S_OK;
}
pEntry = pEntry->pNext;
}
return E_FAIL;
}
// Adds data at the specified hash slot without checking for existence
HRESULT AddValueWithHash(_In_ T Data, _In_ uint32_t Hash)
{
HRESULT hr = S_OK;
assert(m_NumHashSlots > 0);
SHashEntry *pHashEntry;
uint32_t index = Hash % m_NumHashSlots;
VN( pHashEntry = new SHashEntry );
pHashEntry->pNext = m_rgpHashEntries[index];
pHashEntry->Data = Data;
pHashEntry->Hash = Hash;
m_rgpHashEntries[index] = pHashEntry;
++ m_NumEntries;
lExit:
return hr;
}
// Iterator code:
//
// CMyHashTable::CIterator myIt;
// for (myTable.GetFirstEntry(&myIt); !myTable.PastEnd(&myIt); myTable.GetNextEntry(&myIt)
// { myTable.GetData(&myIt); }
void GetFirstEntry(_Out_ CIterator *pIterator)
{
SHashEntry **ppEnd = m_rgpHashEntries + m_NumHashSlots;
pIterator->ppHashSlot = m_rgpHashEntries;
while (pIterator->ppHashSlot < ppEnd)
{
if (nullptr != *(pIterator->ppHashSlot))
{
pIterator->pHashEntry = *(pIterator->ppHashSlot);
return;
}
++ pIterator->ppHashSlot;
}
}
bool PastEnd(_Inout_ CIterator *pIterator)
{
SHashEntry **ppEnd = m_rgpHashEntries + m_NumHashSlots;
assert(pIterator->ppHashSlot >= m_rgpHashEntries && pIterator->ppHashSlot <= ppEnd);
return (pIterator->ppHashSlot == ppEnd);
}
void GetNextEntry(_Inout_ CIterator *pIterator)
{
SHashEntry **ppEnd = m_rgpHashEntries + m_NumHashSlots;
assert(pIterator->ppHashSlot >= m_rgpHashEntries && pIterator->ppHashSlot <= ppEnd);
assert(pIterator->pHashEntry != 0);
_Analysis_assume_(pIterator->pHashEntry != 0);
pIterator->pHashEntry = pIterator->pHashEntry->pNext;
if (nullptr != pIterator->pHashEntry)
{
return;
}
++ pIterator->ppHashSlot;
while (pIterator->ppHashSlot < ppEnd)
{
pIterator->pHashEntry = *(pIterator->ppHashSlot);
if (nullptr != pIterator->pHashEntry)
{
return;
}
++ pIterator->ppHashSlot;
}
// hit the end of the list, ppHashSlot == ppEnd
}
void RemoveEntry(_Inout_ CIterator *pIterator)
{
SHashEntry *pTemp;
SHashEntry **ppPrev;
SHashEntry **ppEnd = m_rgpHashEntries + m_NumHashSlots;
assert(pIterator && !PastEnd(pIterator));
ppPrev = pIterator->ppHashSlot;
pTemp = *ppPrev;
while (pTemp)
{
if (pTemp == pIterator->pHashEntry)
{
*ppPrev = pTemp->pNext;
pIterator->ppHashSlot = ppEnd;
delete pTemp;
return;
}
ppPrev = &pTemp->pNext;
pTemp = pTemp->pNext;
}
// Should never get here
assert(0);
}
};
// Allocates the hash slots on the regular heap (since the
// hash table can grow), but all hash entries are allocated on
// a private heap
template<typename T, bool (*pfnIsEqual)(const T &Data1, const T &Data2)>
class CEffectHashTableWithPrivateHeap : public CEffectHashTable<T, pfnIsEqual>
{
protected:
CDataBlockStore *m_pPrivateHeap;
public:
CEffectHashTableWithPrivateHeap()
{
m_pPrivateHeap = nullptr;
}
void Cleanup()
{
CleanArray();
m_NumHashSlots = 0;
m_NumEntries = 0;
}
~CEffectHashTableWithPrivateHeap()
{
Cleanup();
}
// Call this only once
void SetPrivateHeap(_In_ CDataBlockStore *pPrivateHeap)
{
assert(nullptr == m_pPrivateHeap);
m_pPrivateHeap = pPrivateHeap;
}
// Adds data at the specified hash slot without checking for existence
HRESULT AddValueWithHash(_In_ T Data, _In_ uint32_t Hash)
{
HRESULT hr = S_OK;
assert(m_pPrivateHeap);
_Analysis_assume_(m_pPrivateHeap);
assert(m_NumHashSlots > 0);
SHashEntry *pHashEntry;
uint32_t index = Hash % m_NumHashSlots;
VN( pHashEntry = new(*m_pPrivateHeap) SHashEntry );
pHashEntry->pNext = m_rgpHashEntries[index];
pHashEntry->Data = Data;
pHashEntry->Hash = Hash;
m_rgpHashEntries[index] = pHashEntry;
++ m_NumEntries;
lExit:
return hr;
}
};