#ifndef UTLHASH_H #define UTLHASH_H #pragma once #include #include #include "utlmemory.h" #include "utlvector.h" #include "utllinkedlist.h" #include "commonmacros.h" #include "generichash.h" typedef unsigned int UtlHashHandle_t; template 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 HashBucketList_t; CUtlVector m_Buckets; CompareFunc_t m_CompareFunc; KeyFunc_t m_KeyFunc; bool m_bPowerOfTwo; unsigned int m_ModMask; }; template CUtlHash::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 CUtlHash::~CUtlHash() { Purge(); } template inline bool CUtlHash::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 inline int CUtlHash::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 inline int CUtlHash::GetBucketIndex(UtlHashHandle_t handle) const { return (((handle >> 16) & 0x0000ffff)); } template inline int CUtlHash::GetKeyDataIndex(UtlHashHandle_t handle) const { return (handle & 0x0000ffff); } template inline UtlHashHandle_t CUtlHash::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 inline void CUtlHash::Purge(void) { int bucketCount = m_Buckets.Count(); for (int ndxBucket = 0; ndxBucket < bucketCount; ndxBucket++) { m_Buckets[ndxBucket].Purge(); } } template inline bool CUtlHash::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& 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 inline UtlHashHandle_t CUtlHash::Find(Data const& src) const { unsigned int ndxBucket; int ndxKeyData; if (DoFind(src, &ndxBucket, &ndxKeyData)) { return (BuildHandle(ndxBucket, ndxKeyData)); } return (InvalidHandle()); } template inline UtlHashHandle_t CUtlHash::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 inline UtlHashHandle_t CUtlHash::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 inline UtlHashHandle_t CUtlHash::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 inline void CUtlHash::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 inline void CUtlHash::RemoveAll() { int bucketCount = m_Buckets.Count(); for (int ndxBucket = 0; ndxBucket < bucketCount; ndxBucket++) { m_Buckets[ndxBucket].RemoveAll(); } } template inline Data& CUtlHash::Element(UtlHashHandle_t handle) { int ndxBucket = GetBucketIndex(handle); int ndxKeyData = GetKeyDataIndex(handle); return (m_Buckets[ndxBucket].Element(ndxKeyData)); } template inline Data const& CUtlHash::Element(UtlHashHandle_t handle) const { int ndxBucket = GetBucketIndex(handle); int ndxKeyData = GetKeyDataIndex(handle); return (m_Buckets[ndxBucket].Element(ndxKeyData)); } template inline Data& CUtlHash::operator[](UtlHashHandle_t handle) { int ndxBucket = GetBucketIndex(handle); int ndxKeyData = GetKeyDataIndex(handle); return (m_Buckets[ndxBucket].Element(ndxKeyData)); } template inline Data const& CUtlHash::operator[](UtlHashHandle_t handle) const { int ndxBucket = GetBucketIndex(handle); int ndxKeyData = GetKeyDataIndex(handle); return (m_Buckets[ndxBucket].Element(ndxKeyData)); } template inline UtlHashHandle_t CUtlHash::GetFirstHandle() const { return GetNextHandle((UtlHashHandle_t)-1); } template inline UtlHashHandle_t CUtlHash::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 inline void CUtlHash::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 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 struct HashFastData_t_ { unsigned int m_uiKey; HashData m_Data; }; typedef HashFastData_t_ HashFastData_t; unsigned int m_uiBucketMask; CUtlVector m_aBuckets; CUtlFixedLinkedList m_aDataPool; }; template CUtlHashFast::CUtlHashFast() { Purge(); } template CUtlHashFast::~CUtlHashFast() { Purge(); } template inline void CUtlHashFast::Purge(void) { m_aBuckets.Purge(); m_aDataPool.Purge(); } template bool CUtlHashFast::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 inline int CUtlHashFast::Count(void) { return m_aDataPool.Count(); } template inline UtlHashFastHandle_t CUtlHashFast::Insert(unsigned int uiKey, const Data& data) { UtlHashFastHandle_t hHash = Find(uiKey); if (hHash != InvalidHandle()) return hHash; return FastInsert(uiKey, data); } template inline UtlHashFastHandle_t CUtlHashFast::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 inline void CUtlHashFast::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 inline void CUtlHashFast::RemoveAll(void) { m_aBuckets.RemoveAll(); m_aDataPool.RemoveAll(); } template inline UtlHashFastHandle_t CUtlHashFast::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 inline Data& CUtlHashFast::Element(UtlHashFastHandle_t hHash) { return (m_aDataPool[hHash].m_Data); } template inline Data const& CUtlHashFast::Element(UtlHashFastHandle_t hHash) const { return (m_aDataPool[hHash].m_Data); } template inline Data& CUtlHashFast::operator[](UtlHashFastHandle_t hHash) { return (m_aDataPool[hHash].m_Data); } template inline Data const& CUtlHashFast::operator[](UtlHashFastHandle_t hHash) const { return (m_aDataPool[hHash].m_Data); } typedef int UtlHashFixedHandle_t; template 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 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 struct HashFixedData_t_ { unsigned int m_uiKey; Data_t m_Data; }; typedef HashFixedData_t_ HashFixedData_t; enum { BUCKET_MASK = NUM_BUCKETS - 1 }; CUtlPtrLinkedList m_aBuckets[NUM_BUCKETS]; int m_nElements; }; template CUtlHashFixed::CUtlHashFixed() { Purge(); } template CUtlHashFixed::~CUtlHashFixed() { Purge(); } template inline void CUtlHashFixed::Purge(void) { RemoveAll(); } template inline int CUtlHashFixed::Count(void) { return m_nElements; } template inline UtlHashFixedHandle_t CUtlHashFixed::Insert(unsigned int uiKey, const Data& data) { UtlHashFixedHandle_t hHash = Find(uiKey); if (hHash != InvalidHandle()) return hHash; return FastInsert(uiKey, data); } template inline UtlHashFixedHandle_t CUtlHashFixed::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 inline void CUtlHashFixed::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 inline void CUtlHashFixed::RemoveAll(void) { for (int i = 0; i < NUM_BUCKETS; i++) { m_aBuckets[i].RemoveAll(); } m_nElements = 0; } template inline UtlHashFixedHandle_t CUtlHashFixed::Find(unsigned int uiKey) { int iBucket = HashFuncs::Hash(uiKey, NUM_BUCKETS - 1); CUtlPtrLinkedList& 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 inline Data& CUtlHashFixed::Element(UtlHashFixedHandle_t hHash) { return ((HashFixedData_t*)hHash)->m_Data; } template inline Data const& CUtlHashFixed::Element(UtlHashFixedHandle_t hHash) const { return ((HashFixedData_t*)hHash)->m_Data; } template inline Data& CUtlHashFixed::operator[](UtlHashFixedHandle_t hHash) { return ((HashFixedData_t*)hHash)->m_Data; } template inline Data const& CUtlHashFixed::operator[](UtlHashFixedHandle_t hHash) const { return ((HashFixedData_t*)hHash)->m_Data; } #endif