Seaside/SpyCustom/sdk/utlhash.h

732 lines
19 KiB
C
Raw Normal View History

2021-06-16 18:48:15 +03:00
#ifndef UTLHASH_H
#define UTLHASH_H
#pragma once
#include <assert.h>
#include <limits.h>
#include "utlmemory.h"
#include "utlvector.h"
#include "utllinkedlist.h"
#include "commonmacros.h"
#include "generichash.h"
typedef unsigned int UtlHashHandle_t;
template<class Data, typename C = bool (*)(Data const&, Data const&), typename K = unsigned int (*)(Data const&) >
class CUtlHash
{
public:
typedef C CompareFunc_t;
typedef K KeyFunc_t;
CUtlHash(int bucketCount = 0, int growCount = 0, int initCount = 0,
CompareFunc_t compareFunc = 0, KeyFunc_t keyFunc = 0);
~CUtlHash();
static UtlHashHandle_t InvalidHandle(void) { return (UtlHashHandle_t)~0; }
bool IsValidHandle(UtlHashHandle_t handle) const;
int Count(void) const;
void Purge(void);
UtlHashHandle_t Insert(Data const& src);
UtlHashHandle_t Insert(Data const& src, bool* pDidInsert);
UtlHashHandle_t AllocEntryFromKey(Data const& src);
void Remove(UtlHashHandle_t handle);
void RemoveAll();
UtlHashHandle_t Find(Data const& src) const;
Data& Element(UtlHashHandle_t handle);
Data const& Element(UtlHashHandle_t handle) const;
Data& operator[](UtlHashHandle_t handle);
Data const& operator[](UtlHashHandle_t handle) const;
UtlHashHandle_t GetFirstHandle() const;
UtlHashHandle_t GetNextHandle(UtlHashHandle_t h) const;
void Log(const char* filename);
protected:
int GetBucketIndex(UtlHashHandle_t handle) const;
int GetKeyDataIndex(UtlHashHandle_t handle) const;
UtlHashHandle_t BuildHandle(int ndxBucket, int ndxKeyData) const;
bool DoFind(Data const& src, unsigned int* pBucket, int* pIndex) const;
protected:
typedef CUtlVector<Data> HashBucketList_t;
CUtlVector<HashBucketList_t> m_Buckets;
CompareFunc_t m_CompareFunc;
KeyFunc_t m_KeyFunc;
bool m_bPowerOfTwo;
unsigned int m_ModMask;
};
template<class Data, typename C, typename K>
CUtlHash<Data, C, K>::CUtlHash(int bucketCount, int growCount, int initCount,
CompareFunc_t compareFunc, KeyFunc_t keyFunc) :
m_CompareFunc(compareFunc),
m_KeyFunc(keyFunc)
{
m_Buckets.SetSize(bucketCount);
for (int ndxBucket = 0; ndxBucket < bucketCount; ndxBucket++)
{
m_Buckets[ndxBucket].SetSize(initCount);
m_Buckets[ndxBucket].SetGrowSize(growCount);
}
m_bPowerOfTwo = IsPowerOfTwo(bucketCount);
m_ModMask = m_bPowerOfTwo ? (bucketCount - 1) : 0;
}
template<class Data, typename C, typename K>
CUtlHash<Data, C, K>::~CUtlHash()
{
Purge();
}
template<class Data, typename C, typename K>
inline bool CUtlHash<Data, C, K>::IsValidHandle(UtlHashHandle_t handle) const
{
int ndxBucket = GetBucketIndex(handle);
int ndxKeyData = GetKeyDataIndex(handle);
if ( (ndxBucket < m_Buckets.Count()))
{
if ( (ndxKeyData < m_Buckets[ndxBucket].Count()))
return true;
}
return false;
}
template<class Data, typename C, typename K>
inline int CUtlHash<Data, C, K>::Count(void) const
{
int count = 0;
int bucketCount = m_Buckets.Count();
for (int ndxBucket = 0; ndxBucket < bucketCount; ndxBucket++)
{
count += m_Buckets[ndxBucket].Count();
}
return count;
}
template<class Data, typename C, typename K>
inline int CUtlHash<Data, C, K>::GetBucketIndex(UtlHashHandle_t handle) const
{
return (((handle >> 16) & 0x0000ffff));
}
template<class Data, typename C, typename K>
inline int CUtlHash<Data, C, K>::GetKeyDataIndex(UtlHashHandle_t handle) const
{
return (handle & 0x0000ffff);
}
template<class Data, typename C, typename K>
inline UtlHashHandle_t CUtlHash<Data, C, K>::BuildHandle(int ndxBucket, int ndxKeyData) const
{
assert((ndxBucket >= 0) && (ndxBucket < 65536));
assert((ndxKeyData >= 0) && (ndxKeyData < 65536));
UtlHashHandle_t handle = ndxKeyData;
handle |= (ndxBucket << 16);
return handle;
}
template<class Data, typename C, typename K>
inline void CUtlHash<Data, C, K>::Purge(void)
{
int bucketCount = m_Buckets.Count();
for (int ndxBucket = 0; ndxBucket < bucketCount; ndxBucket++)
{
m_Buckets[ndxBucket].Purge();
}
}
template<class Data, typename C, typename K>
inline bool CUtlHash<Data, C, K>::DoFind(Data const& src, unsigned int* pBucket, int* pIndex) const
{
unsigned int key = m_KeyFunc(src);
unsigned int ndxBucket;
if (m_bPowerOfTwo)
{
*pBucket = ndxBucket = (key & m_ModMask);
}
else
{
int bucketCount = m_Buckets.Count();
*pBucket = ndxBucket = key % bucketCount;
}
int ndxKeyData;
const CUtlVector<Data>& bucket = m_Buckets[ndxBucket];
int keyDataCount = bucket.Count();
for (ndxKeyData = 0; ndxKeyData < keyDataCount; ndxKeyData++)
{
if (m_CompareFunc(bucket.Element(ndxKeyData), src))
break;
}
if (ndxKeyData == keyDataCount)
return false;
*pIndex = ndxKeyData;
return true;
}
template<class Data, typename C, typename K>
inline UtlHashHandle_t CUtlHash<Data, C, K>::Find(Data const& src) const
{
unsigned int ndxBucket;
int ndxKeyData;
if (DoFind(src, &ndxBucket, &ndxKeyData))
{
return (BuildHandle(ndxBucket, ndxKeyData));
}
return (InvalidHandle());
}
template<class Data, typename C, typename K>
inline UtlHashHandle_t CUtlHash<Data, C, K>::Insert(Data const& src)
{
unsigned int ndxBucket;
int ndxKeyData;
if (DoFind(src, &ndxBucket, &ndxKeyData))
{
return (BuildHandle(ndxBucket, ndxKeyData));
}
ndxKeyData = m_Buckets[ndxBucket].AddToTail(src);
return (BuildHandle(ndxBucket, ndxKeyData));
}
template<class Data, typename C, typename K>
inline UtlHashHandle_t CUtlHash<Data, C, K>::Insert(Data const& src, bool* pDidInsert)
{
unsigned int ndxBucket;
int ndxKeyData;
if (DoFind(src, &ndxBucket, &ndxKeyData))
{
*pDidInsert = false;
return (BuildHandle(ndxBucket, ndxKeyData));
}
*pDidInsert = true;
ndxKeyData = m_Buckets[ndxBucket].AddToTail(src);
return (BuildHandle(ndxBucket, ndxKeyData));
}
template<class Data, typename C, typename K>
inline UtlHashHandle_t CUtlHash<Data, C, K>::AllocEntryFromKey(Data const& src)
{
unsigned int ndxBucket;
int ndxKeyData;
if (DoFind(src, &ndxBucket, &ndxKeyData))
{
return (BuildHandle(ndxBucket, ndxKeyData));
}
ndxKeyData = m_Buckets[ndxBucket].AddToTail();
return (BuildHandle(ndxBucket, ndxKeyData));
}
template<class Data, typename C, typename K>
inline void CUtlHash<Data, C, K>::Remove(UtlHashHandle_t handle)
{
assert(IsValidHandle(handle));
int ndxBucket = GetBucketIndex(handle);
int ndxKeyData = GetKeyDataIndex(handle);
if (m_Buckets[ndxBucket].IsValidIndex(ndxKeyData))
{
m_Buckets[ndxBucket].FastRemove(ndxKeyData);
}
}
template<class Data, typename C, typename K>
inline void CUtlHash<Data, C, K>::RemoveAll()
{
int bucketCount = m_Buckets.Count();
for (int ndxBucket = 0; ndxBucket < bucketCount; ndxBucket++)
{
m_Buckets[ndxBucket].RemoveAll();
}
}
template<class Data, typename C, typename K>
inline Data& CUtlHash<Data, C, K>::Element(UtlHashHandle_t handle)
{
int ndxBucket = GetBucketIndex(handle);
int ndxKeyData = GetKeyDataIndex(handle);
return (m_Buckets[ndxBucket].Element(ndxKeyData));
}
template<class Data, typename C, typename K>
inline Data const& CUtlHash<Data, C, K>::Element(UtlHashHandle_t handle) const
{
int ndxBucket = GetBucketIndex(handle);
int ndxKeyData = GetKeyDataIndex(handle);
return (m_Buckets[ndxBucket].Element(ndxKeyData));
}
template<class Data, typename C, typename K>
inline Data& CUtlHash<Data, C, K>::operator[](UtlHashHandle_t handle)
{
int ndxBucket = GetBucketIndex(handle);
int ndxKeyData = GetKeyDataIndex(handle);
return (m_Buckets[ndxBucket].Element(ndxKeyData));
}
template<class Data, typename C, typename K>
inline Data const& CUtlHash<Data, C, K>::operator[](UtlHashHandle_t handle) const
{
int ndxBucket = GetBucketIndex(handle);
int ndxKeyData = GetKeyDataIndex(handle);
return (m_Buckets[ndxBucket].Element(ndxKeyData));
}
template<class Data, typename C, typename K>
inline UtlHashHandle_t CUtlHash<Data, C, K>::GetFirstHandle() const
{
return GetNextHandle((UtlHashHandle_t)-1);
}
template<class Data, typename C, typename K>
inline UtlHashHandle_t CUtlHash<Data, C, K>::GetNextHandle(UtlHashHandle_t handle) const
{
++handle;
int bi = GetBucketIndex(handle);
int ki = GetKeyDataIndex(handle);
int nBuckets = m_Buckets.Count();
for (; bi < nBuckets; ++bi)
{
if (ki < m_Buckets[bi].Count())
return BuildHandle(bi, ki);
ki = 0;
}
return InvalidHandle();
}
template<class Data, typename C, typename K>
inline void CUtlHash<Data, C, K>::Log(const char* filename)
{
FILE* pDebugFp;
pDebugFp = fopen(filename, "w");
if (!pDebugFp)
return;
int maxBucketSize = 0;
int numBucketsEmpty = 0;
int bucketCount = m_Buckets.Count();
fprintf(pDebugFp, "\n%d Buckets\n", bucketCount);
for (int ndxBucket = 0; ndxBucket < bucketCount; ndxBucket++)
{
int count = m_Buckets[ndxBucket].Count();
if (count > maxBucketSize) { maxBucketSize = count; }
if (count == 0)
numBucketsEmpty++;
fprintf(pDebugFp, "Bucket %d: %d\n", ndxBucket, count);
}
fprintf(pDebugFp, "\nBucketHeads Used: %d\n", bucketCount - numBucketsEmpty);
fprintf(pDebugFp, "Max Bucket Size: %d\n", maxBucketSize);
fclose(pDebugFp);
}
typedef int UtlHashFastHandle_t;
#define UTLHASH_POOL_SCALAR 2
class CUtlHashFastNoHash
{
public:
static int Hash(int key, int bucketMask)
{
return (key & bucketMask);
}
};
class CUtlHashFastGenericHash
{
public:
static int Hash(int key, int bucketMask)
{
return (HashIntConventional(key) & bucketMask);
}
};
template<class Data, class HashFuncs = CUtlHashFastNoHash >
class CUtlHashFast
{
public:
CUtlHashFast();
~CUtlHashFast();
void Purge(void);
static UtlHashFastHandle_t InvalidHandle(void) { return (UtlHashFastHandle_t)~0; }
bool Init(int nBucketCount);
int Count(void);
UtlHashFastHandle_t Insert(unsigned int uiKey, const Data& data);
UtlHashFastHandle_t FastInsert(unsigned int uiKey, const Data& data);
void Remove(UtlHashFastHandle_t hHash);
void RemoveAll(void);
UtlHashFastHandle_t Find(unsigned int uiKey);
Data& Element(UtlHashFastHandle_t hHash);
Data const& Element(UtlHashFastHandle_t hHash) const;
Data& operator[](UtlHashFastHandle_t hHash);
Data const& operator[](UtlHashFastHandle_t hHash) const;
template <typename HashData>
struct HashFastData_t_
{
unsigned int m_uiKey;
HashData m_Data;
};
typedef HashFastData_t_<Data> HashFastData_t;
unsigned int m_uiBucketMask;
CUtlVector<UtlHashFastHandle_t> m_aBuckets;
CUtlFixedLinkedList<HashFastData_t> m_aDataPool;
};
template<class Data, class HashFuncs> CUtlHashFast<Data, HashFuncs>::CUtlHashFast()
{
Purge();
}
template<class Data, class HashFuncs> CUtlHashFast<Data, HashFuncs>::~CUtlHashFast()
{
Purge();
}
template<class Data, class HashFuncs> inline void CUtlHashFast<Data, HashFuncs>::Purge(void)
{
m_aBuckets.Purge();
m_aDataPool.Purge();
}
template<class Data, class HashFuncs> bool CUtlHashFast<Data, HashFuncs>::Init(int nBucketCount)
{
if (!IsPowerOfTwo(nBucketCount))
return false;
m_aBuckets.SetSize(nBucketCount);
for (int iBucket = 0; iBucket < nBucketCount; ++iBucket)
{
m_aBuckets[iBucket] = m_aDataPool.InvalidIndex();
}
m_uiBucketMask = nBucketCount - 1;
int nGrowSize = UTLHASH_POOL_SCALAR * nBucketCount;
m_aDataPool.SetGrowSize(nGrowSize);
return true;
}
template<class Data, class HashFuncs> inline int CUtlHashFast<Data, HashFuncs>::Count(void)
{
return m_aDataPool.Count();
}
template<class Data, class HashFuncs> inline UtlHashFastHandle_t CUtlHashFast<Data, HashFuncs>::Insert(unsigned int uiKey, const Data& data)
{
UtlHashFastHandle_t hHash = Find(uiKey);
if (hHash != InvalidHandle())
return hHash;
return FastInsert(uiKey, data);
}
template<class Data, class HashFuncs> inline UtlHashFastHandle_t CUtlHashFast<Data, HashFuncs>::FastInsert(unsigned int uiKey, const Data& data)
{
int iHashData = m_aDataPool.Alloc(true);
HashFastData_t* pHashData = &m_aDataPool[iHashData];
if (!pHashData)
return InvalidHandle();
pHashData->m_uiKey = uiKey;
pHashData->m_Data = data;
int iBucket = HashFuncs::Hash(uiKey, m_uiBucketMask);
m_aDataPool.LinkBefore(m_aBuckets[iBucket], iHashData);
m_aBuckets[iBucket] = iHashData;
return iHashData;
}
template<class Data, class HashFuncs> inline void CUtlHashFast<Data, HashFuncs>::Remove(UtlHashFastHandle_t hHash)
{
int iBucket = HashFuncs::Hash(m_aDataPool[hHash].m_uiKey, m_uiBucketMask);
if (m_aBuckets[iBucket] == hHash)
{
m_aBuckets[iBucket] = m_aDataPool.Next(hHash);
}
else
{
m_aDataPool.Unlink(hHash);
}
m_aDataPool.Remove(hHash);
}
template<class Data, class HashFuncs> inline void CUtlHashFast<Data, HashFuncs>::RemoveAll(void)
{
m_aBuckets.RemoveAll();
m_aDataPool.RemoveAll();
}
template<class Data, class HashFuncs> inline UtlHashFastHandle_t CUtlHashFast<Data, HashFuncs>::Find(unsigned int uiKey)
{
int iBucket = HashFuncs::Hash(uiKey, m_uiBucketMask);
for (int iElement = m_aBuckets[iBucket]; iElement != m_aDataPool.InvalidIndex(); iElement = m_aDataPool.Next(iElement))
{
if (m_aDataPool[iElement].m_uiKey == uiKey)
return iElement;
}
return InvalidHandle();
}
template<class Data, class HashFuncs> inline Data& CUtlHashFast<Data, HashFuncs>::Element(UtlHashFastHandle_t hHash)
{
return (m_aDataPool[hHash].m_Data);
}
template<class Data, class HashFuncs> inline Data const& CUtlHashFast<Data, HashFuncs>::Element(UtlHashFastHandle_t hHash) const
{
return (m_aDataPool[hHash].m_Data);
}
template<class Data, class HashFuncs> inline Data& CUtlHashFast<Data, HashFuncs>::operator[](UtlHashFastHandle_t hHash)
{
return (m_aDataPool[hHash].m_Data);
}
template<class Data, class HashFuncs> inline Data const& CUtlHashFast<Data, HashFuncs>::operator[](UtlHashFastHandle_t hHash) const
{
return (m_aDataPool[hHash].m_Data);
}
typedef int UtlHashFixedHandle_t;
template <int NUM_BUCKETS>
class CUtlHashFixedGenericHash
{
public:
static int Hash(int key, int bucketMask)
{
int hash = HashIntConventional(key);
if (NUM_BUCKETS <= USHRT_MAX)
{
hash ^= (hash >> 16);
}
if (NUM_BUCKETS <= UCHAR_MAX)
{
hash ^= (hash >> 8);
}
return (hash & bucketMask);
}
};
template<class Data, int NUM_BUCKETS, class CHashFuncs = CUtlHashFastNoHash >
class CUtlHashFixed
{
public:
CUtlHashFixed();
~CUtlHashFixed();
void Purge(void);
static UtlHashFixedHandle_t InvalidHandle(void) { return (UtlHashFixedHandle_t)~0; }
int Count(void);
UtlHashFixedHandle_t Insert(unsigned int uiKey, const Data& data);
UtlHashFixedHandle_t FastInsert(unsigned int uiKey, const Data& data);
void Remove(UtlHashFixedHandle_t hHash);
void RemoveAll(void);
UtlHashFixedHandle_t Find(unsigned int uiKey);
Data& Element(UtlHashFixedHandle_t hHash);
Data const& Element(UtlHashFixedHandle_t hHash) const;
Data& operator[](UtlHashFixedHandle_t hHash);
Data const& operator[](UtlHashFixedHandle_t hHash) const;
template <typename Data_t>
struct HashFixedData_t_
{
unsigned int m_uiKey;
Data_t m_Data;
};
typedef HashFixedData_t_<Data> HashFixedData_t;
enum
{
BUCKET_MASK = NUM_BUCKETS - 1
};
CUtlPtrLinkedList<HashFixedData_t> m_aBuckets[NUM_BUCKETS];
int m_nElements;
};
template<class Data, int NUM_BUCKETS, class HashFuncs> CUtlHashFixed<Data, NUM_BUCKETS, HashFuncs>::CUtlHashFixed()
{
Purge();
}
template<class Data, int NUM_BUCKETS, class HashFuncs> CUtlHashFixed<Data, NUM_BUCKETS, HashFuncs>::~CUtlHashFixed()
{
Purge();
}
template<class Data, int NUM_BUCKETS, class HashFuncs> inline void CUtlHashFixed<Data, NUM_BUCKETS, HashFuncs>::Purge(void)
{
RemoveAll();
}
template<class Data, int NUM_BUCKETS, class HashFuncs> inline int CUtlHashFixed<Data, NUM_BUCKETS, HashFuncs>::Count(void)
{
return m_nElements;
}
template<class Data, int NUM_BUCKETS, class HashFuncs> inline UtlHashFixedHandle_t CUtlHashFixed<Data, NUM_BUCKETS, HashFuncs>::Insert(unsigned int uiKey, const Data& data)
{
UtlHashFixedHandle_t hHash = Find(uiKey);
if (hHash != InvalidHandle())
return hHash;
return FastInsert(uiKey, data);
}
template<class Data, int NUM_BUCKETS, class HashFuncs> inline UtlHashFixedHandle_t CUtlHashFixed<Data, NUM_BUCKETS, HashFuncs>::FastInsert(unsigned int uiKey, const Data& data)
{
int iBucket = HashFuncs::Hash(uiKey, NUM_BUCKETS - 1);
UtlPtrLinkedListIndex_t iElem = m_aBuckets[iBucket].AddToHead();
HashFixedData_t* pHashData = &m_aBuckets[iBucket][iElem];
Assert((UtlPtrLinkedListIndex_t)pHashData == iElem);
pHashData->m_uiKey = uiKey;
pHashData->m_Data = data;
m_nElements++;
return (UtlHashFixedHandle_t)pHashData;
}
template<class Data, int NUM_BUCKETS, class HashFuncs> inline void CUtlHashFixed<Data, NUM_BUCKETS, HashFuncs>::Remove(UtlHashFixedHandle_t hHash)
{
HashFixedData_t* pHashData = (HashFixedData_t*)hHash;
Assert(Find(pHashData->m_uiKey) != InvalidHandle());
int iBucket = HashFuncs::Hash(pHashData->m_uiKey, NUM_BUCKETS - 1);
m_aBuckets[iBucket].Remove((UtlPtrLinkedListIndex_t)pHashData);
m_nElements--;
}
template<class Data, int NUM_BUCKETS, class HashFuncs> inline void CUtlHashFixed<Data, NUM_BUCKETS, HashFuncs>::RemoveAll(void)
{
for (int i = 0; i < NUM_BUCKETS; i++)
{
m_aBuckets[i].RemoveAll();
}
m_nElements = 0;
}
template<class Data, int NUM_BUCKETS, class HashFuncs> inline UtlHashFixedHandle_t CUtlHashFixed<Data, NUM_BUCKETS, HashFuncs>::Find(unsigned int uiKey)
{
int iBucket = HashFuncs::Hash(uiKey, NUM_BUCKETS - 1);
CUtlPtrLinkedList<HashFixedData_t>& bucket = m_aBuckets[iBucket];
for (UtlPtrLinkedListIndex_t iElement = bucket.Head(); iElement != bucket.InvalidIndex(); iElement = bucket.Next(iElement))
{
if (bucket[iElement].m_uiKey == uiKey)
return (UtlHashFixedHandle_t)iElement;
}
return InvalidHandle();
}
template<class Data, int NUM_BUCKETS, class HashFuncs> inline Data& CUtlHashFixed<Data, NUM_BUCKETS, HashFuncs>::Element(UtlHashFixedHandle_t hHash)
{
return ((HashFixedData_t*)hHash)->m_Data;
}
template<class Data, int NUM_BUCKETS, class HashFuncs> inline Data const& CUtlHashFixed<Data, NUM_BUCKETS, HashFuncs>::Element(UtlHashFixedHandle_t hHash) const
{
return ((HashFixedData_t*)hHash)->m_Data;
}
template<class Data, int NUM_BUCKETS, class HashFuncs> inline Data& CUtlHashFixed<Data, NUM_BUCKETS, HashFuncs>::operator[](UtlHashFixedHandle_t hHash)
{
return ((HashFixedData_t*)hHash)->m_Data;
}
template<class Data, int NUM_BUCKETS, class HashFuncs> inline Data const& CUtlHashFixed<Data, NUM_BUCKETS, HashFuncs>::operator[](UtlHashFixedHandle_t hHash) const
{
return ((HashFixedData_t*)hHash)->m_Data;
}
#endif