/*==LICENSE==* CyanWorlds.com Engine - MMOG client, server and tools Copyright (C) 2011 Cyan Worlds, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . You can contact Cyan Worlds, Inc. by email legal@cyan.com or by snail mail at: Cyan Worlds, Inc. 14617 N Newport Hwy Mead, WA 99021 *==LICENSE==*/ /***************************************************************************** * * $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtHash.h * ***/ #ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNUTILS_PRIVATE_PNUTHASH_H #error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtHash.h included more than once" #endif #define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNUTILS_PRIVATE_PNUTHASH_H /**************************************************************************** * * Macros * ***/ // Define a field inside an object that is used to link it into a hash table #define HASHLINK(object) THashLink< object > // Define a POINTER to a hash table, not a hash table #define HASHTABLE(object,key) THashTable< object, key > // Define a hash table: // - starts with kSlotMinCount rows // - can grow to kDefaultSlotMaxCount rows // (hash table grows when a row contains more than kGrowOnListSize entries #define HASHTABLEDECL(object,key,link) THashTableDecl< object, key, offsetof(object,link), 0 > // Define a hash table in situations when a forward reference prevents use of HASHTABLEDECL // - Size characteristics are identical to HASHTABLEDECL #define HASHTABLEDYN(object,key) THashTableDyn< object, key > // Define a hash table with: // - starts with // - row table never grows #define HASHTABLEDECLSIZE(object,key,link,size) THashTableDecl #if defined(_MSC_VER) #define forceinline __forceinline #else #define forceinline inline #endif /**************************************************************************** * * Forward declarations * ***/ template class THashLink; template class TBaseHashTable; /**************************************************************************** * * CHashValue * ***/ class CHashValue { private: static const dword s_hashTable[]; dword m_result; inline void Construct () { m_result = 0x325d1eae; } public: static dword LookupHashBits (unsigned value) { ASSERT(value < 0x100); return s_hashTable[value]; } inline CHashValue () { Construct() ; } inline CHashValue (const CHashValue & source) { m_result = source.m_result; } inline CHashValue (const void * data, unsigned bytes) { Construct(); Hash(data, bytes); } inline CHashValue & operator= (const CHashValue & source) { m_result = source.m_result; return *this; } inline bool operator== (const CHashValue & source) const { return (m_result == source.m_result); } inline dword GetHash () const { return m_result; } forceinline void Hash (const void * data, unsigned bytes); forceinline void Hash8 (unsigned data); forceinline void Hash16 (unsigned data); forceinline void Hash32 (unsigned data); }; //=========================================================================== void CHashValue::Hash (const void * data, unsigned bytes) { for (const byte * curr = (const byte *)data, * term = curr + bytes; curr != term; ++curr) Hash8(*curr); } //=========================================================================== void CHashValue::Hash8 (unsigned data) { m_result += s_hashTable[m_result >> 24] ^ (m_result >> 6) ^ s_hashTable[data & 0xff]; } //=========================================================================== void CHashValue::Hash16 (unsigned data) { Hash8(data); Hash8(data >> 8); } //=========================================================================== void CHashValue::Hash32 (unsigned data) { Hash8(data); Hash8(data >> 8); Hash8(data >> 16); Hash8(data >> 24); } /**************************************************************************** * * THashLink * ***/ template class THashLink { friend class TBaseHashTable; private: unsigned m_hash; LINK(T) m_linkToFull; LINK(T) m_linkToSlot; public: inline bool IsLinked () const; inline T * Next (); inline const T * Next () const; inline T * Prev (); inline const T * Prev () const; inline void Unlink (); }; //=========================================================================== template bool THashLink::IsLinked () const { return m_linkToFull.IsLinked(); } //=========================================================================== template T * THashLink::Next () { return m_linkToFull.Next(); } //=========================================================================== template const T * THashLink::Next () const { return m_linkToFull.Next(); } //=========================================================================== template T * THashLink::Prev () { return m_linkToFull.Prev(); } //=========================================================================== template const T * THashLink::Prev () const { return m_linkToFull.Prev(); } //=========================================================================== template void THashLink::Unlink () { m_linkToFull.Unlink(); m_linkToSlot.Unlink(); } /**************************************************************************** * * TBaseHashTable * ***/ template class TBaseHashTable { private: enum { kSlotMinCount = 8 }; enum { kDefaultSlotMaxCount = 1024 }; enum { kGrowOnListSize = 5 }; LIST(T) m_fullList; int m_linkOffset; FARRAYOBJ(LIST(T)) m_slotListArray; unsigned m_slotMask; // always set to a power of two minus one unsigned m_slotMaxCount; inline bool CheckGrowTable (LIST(T) * slotList); inline const THashLink & GetLink (const T * object) const; inline THashLink & GetLink (T * object); inline void SetSlotCount (unsigned count); protected: inline unsigned GetHash (const T * object) const; inline unsigned & GetHash (T * object); inline const LIST(T) & GetSlotList (unsigned hash) const; inline LIST(T) & GetSlotList (unsigned hash); inline void SetLinkOffset (int linkOffset, unsigned maxSize); inline void SetSlotMaxCount (unsigned count); public: inline TBaseHashTable (); inline TBaseHashTable (const TBaseHashTable & source); inline TBaseHashTable & operator= (const TBaseHashTable & source); inline void Add (T * object, unsigned hash); inline void Clear (); inline void Delete (T * object); inline T * Head (); inline const T * Head () const; inline T * Next (const T * object); inline const T * Next (const T * object) const; inline void Order (T * linkedObject, ELinkType linkType, T * existingObject); inline T * Prev (const T * object); inline const T * Prev (const T * object) const; inline T * Tail (); inline const T * Tail () const; inline void Unlink (T * object); }; //=========================================================================== template TBaseHashTable::TBaseHashTable () { m_slotMask = 0; m_slotMaxCount = kDefaultSlotMaxCount; // more initialization done during call to SetLinkOffset() } //=========================================================================== template TBaseHashTable::TBaseHashTable (const TBaseHashTable & source) { #ifdef HS_DEBUGGING FATAL("No copy constructor"); #endif TBaseHashTable(); } //=========================================================================== template TBaseHashTable & TBaseHashTable::operator= (const TBaseHashTable & source) { #ifdef HS_DEBUGGING FATAL("No assignment operator"); #endif return *this; } //=========================================================================== template void TBaseHashTable::Add (T * object, unsigned hash) { GetHash(object) = hash; LIST(T) * list = &GetSlotList(hash); if (CheckGrowTable(list)) list = &GetSlotList(hash); m_fullList.Link(object); list->Link(object); } //=========================================================================== template bool TBaseHashTable::CheckGrowTable (LIST(T) * list) { unsigned nextCount = (m_slotMask + 1) * 2; if (nextCount > m_slotMaxCount) return false; unsigned listCount = 0; for (T * curr = list->Head(); curr; curr = list->Next(curr)) ++listCount; if (listCount + 1 < kGrowOnListSize) return false; SetSlotCount(nextCount); return true; } //=========================================================================== template void TBaseHashTable::Clear () { m_fullList.Clear(); } //=========================================================================== template void TBaseHashTable::Delete (T * object) { DEL(object); } //=========================================================================== template unsigned TBaseHashTable::GetHash (const T * object) const { return GetLink(object).m_hash; } //=========================================================================== template unsigned & TBaseHashTable::GetHash (T * object) { return GetLink(object).m_hash; } //=========================================================================== template const THashLink & TBaseHashTable::GetLink (const T * object) const { return *(const THashLink *)((const byte *)object + m_linkOffset); } //=========================================================================== template THashLink & TBaseHashTable::GetLink (T * object) { return *(THashLink *)((byte *)object + m_linkOffset); } //=========================================================================== template const LIST(T) & TBaseHashTable::GetSlotList (unsigned hash) const { return m_slotListArray[hash & m_slotMask]; } //=========================================================================== template LIST(T) & TBaseHashTable::GetSlotList (unsigned hash) { return m_slotListArray[hash & m_slotMask]; } //=========================================================================== template T * TBaseHashTable::Head () { return m_fullList.Head(); } //=========================================================================== template const T * TBaseHashTable::Head () const { return m_fullList.Head(); } //=========================================================================== template T * TBaseHashTable::Next (const T * object) { return m_fullList.Next(object); } //=========================================================================== template const T * TBaseHashTable::Next (const T * object) const { return m_fullList.Next(object); } //=========================================================================== template void TBaseHashTable::Order (T * linkedObject, ELinkType linkType, T * existingObject) { THashLink & link = GetLink(linkedObject); ASSERT(link.m_linkToFull.IsLinked()); m_fullList.Link(linkedObject, linkType, existingObject); } //=========================================================================== template T * TBaseHashTable::Prev (const T * object) { return m_fullList.Prev(object); } //=========================================================================== template const T * TBaseHashTable::Prev (const T * object) const { return m_fullList.Prev(object); } //=========================================================================== template void TBaseHashTable::SetLinkOffset (int linkOffset, unsigned maxSize) { ASSERT(!m_fullList.Head()); ASSERT(!m_slotListArray.Count()); ASSERT(!m_slotMask); m_linkOffset = linkOffset; m_fullList.SetLinkOffset(m_linkOffset + offsetof(THashLink, m_linkToFull)); if (!m_slotMask) SetSlotCount(max(kSlotMinCount, MathNextPow2(maxSize))); } //=========================================================================== template void TBaseHashTable::SetSlotCount (unsigned count) { ASSERT(!(count & (count - 1))); // power of two ASSERT(count >= 2); if (count == m_slotMask + 1) return; m_slotMask = count - 1; m_slotListArray.ZeroCount(); m_slotListArray.SetCount(count); for (unsigned loop = 0; loop < count; ++loop) m_slotListArray[loop].SetLinkOffset(m_linkOffset + offsetof(THashLink, m_linkToSlot)); for (T * curr = Head(); curr; curr = Next(curr)) GetSlotList(GetHash(curr)).Link(curr); } //=========================================================================== template void TBaseHashTable::SetSlotMaxCount (unsigned count) { if (count) m_slotMaxCount = max(kSlotMinCount, count); } //=========================================================================== template T * TBaseHashTable::Tail () { return m_fullList.Tail(); } //=========================================================================== template const T * TBaseHashTable::Tail () const { return m_fullList.Tail(); } //=========================================================================== template void TBaseHashTable::Unlink (T * object) { THashLink & link = GetLink(object); link.Unlink(); } /**************************************************************************** * * THashTable * ***/ template class THashTable : public TBaseHashTable { public: inline void Add (T * object); inline void Add (T * object, unsigned hash); inline T * Find (const K & key); inline T * FindNext (const K & key, T * object); inline const T * Find (const K & key) const; inline const T * FindNext (const K & key, const T * object) const; inline T * Unduplicate (T * object, const K & key); }; //=========================================================================== template inline void THashTable::Add (T * object) { TBaseHashTable::Add(object, object->GetHash()); } //=========================================================================== template inline void THashTable::Add (T * object, unsigned hash) { TBaseHashTable::Add(object, hash); } //=========================================================================== template T * THashTable::Find (const K & key) { return (T *)((const THashTable *)this)->Find(key); } //=========================================================================== template T * THashTable::FindNext (const K & key, T * object) { return (T *)((const THashTable *)this)->FindNext(key, object); } //=========================================================================== template const T * THashTable::Find (const K & key) const { unsigned hash = key.GetHash(); const LIST(T) & slotList = this->GetSlotList(hash); for (const T * curr = slotList.Head(); curr; curr = slotList.Next(curr)) if ((GetHash(curr) == hash) && (*curr == key)) return curr; return nil; } //=========================================================================== template const T * THashTable::FindNext (const K & key, const T * object) const { unsigned hash = key.GetHash(); const LIST(T) & slotList = this->GetSlotList(hash); for (const T * curr = slotList.Next(object); curr; curr = slotList.Next(curr)) if ((GetHash(curr) == hash) && (*curr == key)) return curr; return nil; } //=========================================================================== template T * THashTable::Unduplicate (T * object, const K & key) { T * existing = Find(key); if (existing) { DEL(object); return existing; } else { Add(object); return object; } } /**************************************************************************** * * THashTableDecl * ***/ template class THashTableDecl : public THashTable { public: inline THashTableDecl (); }; //=========================================================================== template THashTableDecl::THashTableDecl () { this->SetLinkOffset(linkOffset, maxSize); this->SetSlotMaxCount(maxSize); } /**************************************************************************** * * THashTableDyn * ***/ template class THashTableDyn : public THashTable { public: void Initialize (int linkOffset, unsigned maxSize = 0); }; //=========================================================================== template void THashTableDyn::Initialize (int linkOffset, unsigned maxSize) { this->SetLinkOffset(linkOffset, maxSize); this->SetSlotMaxCount(maxSize); } /**************************************************************************** * * THashKeyVal * ***/ template class THashKeyVal { public: THashKeyVal () : m_value(0) { } THashKeyVal (const T & value) : m_value(value) { } bool operator== (const THashKeyVal & rhs) const { return m_value == rhs.m_value; } unsigned GetHash () const { CHashValue hash(&m_value, sizeof(m_value)); return hash.GetHash(); } const T & GetValue () const { return m_value; } void SetValue (const T & value) { m_value = value; } protected: T m_value; }; /**************************************************************************** * * CHashKeyStrPtr / CHashKeyStrPtrI * ***/ //=========================================================================== template class THashKeyStrBase { public: const C * GetString () const { return m_str; } protected: THashKeyStrBase () : m_str(nil) { } THashKeyStrBase (const C str[]) : m_str(str) { } virtual ~THashKeyStrBase () { } const C * m_str; }; //=========================================================================== template class THashKeyStrCmp : public THashKeyStrBase { public: bool operator== (const THashKeyStrCmp & rhs) const { return StrCmp(this->m_str, rhs.m_str) == 0; } unsigned GetHash () const { return StrHash(this->m_str); } protected: THashKeyStrCmp () { } THashKeyStrCmp (const C str[]) : THashKeyStrBase(str) { } }; //=========================================================================== template class THashKeyStrCmpI : public THashKeyStrBase { public: bool operator== (const THashKeyStrCmpI & rhs) const { return StrCmpI(this->m_str, rhs.m_str) == 0; } unsigned GetHash () const { return StrHashI(this->m_str); } protected: THashKeyStrCmpI () { } THashKeyStrCmpI (const C str[]) : THashKeyStrBase(str) { } }; /**************************************************************************** * * THashKeyStrPtr * ***/ template class THashKeyStrPtr : public T { public: THashKeyStrPtr () { } THashKeyStrPtr (const C str[]) : T(str) { } void SetString (const C str[]) { this->m_str = str; } }; typedef THashKeyStrPtr< wchar, THashKeyStrCmp > CHashKeyStrPtr; typedef THashKeyStrPtr< wchar, THashKeyStrCmpI > CHashKeyStrPtrI; typedef THashKeyStrPtr< char, THashKeyStrCmp > CHashKeyStrPtrChar; typedef THashKeyStrPtr< char, THashKeyStrCmpI > CHashKeyStrPtrCharI; /**************************************************************************** * * THashKeyStr * ***/ template class THashKeyStr : public T { public: THashKeyStr () { } THashKeyStr (const C str[]) { SetString(str); } THashKeyStr (const THashKeyStr &); // intentionally unimplemented THashKeyStr & operator= (const THashKeyStr &); // intentionally unimplemented ~THashKeyStr () { SetString(nil); } void SetString (const C str[]) { // deprecated if (this->m_str) FREE(const_cast(this->m_str)); if (str) this->m_str = StrDup(str); else this->m_str = nil; } }; typedef THashKeyStr< wchar, THashKeyStrCmp > CHashKeyStr; typedef THashKeyStr< wchar, THashKeyStrCmpI > CHashKeyStrI; typedef THashKeyStr< char, THashKeyStrCmp > CHashKeyStrChar; typedef THashKeyStr< char, THashKeyStrCmpI > CHashKeyStrCharI;