/*==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;