/*==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 . Additional permissions under GNU GPL version 3 section 7 If you modify this Program, or any covered work, by linking or combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK, NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK (or a modified version of those libraries), containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA, PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the licensors of this Program grant you additional permission to convey the resulting work. Corresponding Source for a non-source form of such a combination shall include the source code for the parts of OpenSSL and IJG JPEG Library used as well as that of the covered work. 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 * ***/ #ifndef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNUTILS_PRIVATE_PNUTHASH_H #define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNUTILS_PRIVATE_PNUTHASH_H #include "Pch.h" #include "pnUtList.h" #include "pnUtArray.h" #include "pnUtStr.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 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 > #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 uint32_t s_hashTable[]; uint32_t m_result; inline void Construct () { m_result = 0x325d1eae; } public: static uint32_t 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 uint32_t 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 uint8_t * curr = (const uint8_t *)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: THashLink() : m_hash(0) { } 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) { delete 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 uint8_t *)object + m_linkOffset); } //=========================================================================== template THashLink & TBaseHashTable::GetLink (T * object) { return *(THashLink *)((uint8_t *)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) { // http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 uint32_t v = maxSize - 1; v |= v >> 1; v |= v >> 2; v |= v >> 4; v |= v >> 8; v |= v >> 16; v++; SetSlotCount(max(kSlotMinCount, v)); } } //=========================================================================== 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 ((this->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 ((this->GetHash(curr) == hash) && (*curr == key)) return curr; return nil; } //=========================================================================== template T * THashTable::Unduplicate (T * object, const K & key) { T * existing = Find(key); if (existing) { delete 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); } /**************************************************************************** * * 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_t, THashKeyStrCmp > CHashKeyStrPtr; typedef THashKeyStrPtr< wchar_t, 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_t, THashKeyStrCmp > CHashKeyStr; typedef THashKeyStr< wchar_t, THashKeyStrCmpI > CHashKeyStrI; typedef THashKeyStr< char, THashKeyStrCmp > CHashKeyStrChar; typedef THashKeyStr< char, THashKeyStrCmpI > CHashKeyStrCharI; #endif