/*==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 "pnUtMath.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 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 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:
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)
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 ((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);
}
/****************************************************************************
*
* 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_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