You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

303 lines
10 KiB

////////////////////////////////////////////////////////////////////
// Hash.h
//
// Copyright 2007 cDc@seacave
// Distributed under the Boost Software License, Version 1.0
// (See http://www.boost.org/LICENSE_1_0.txt)
#ifndef __SEACAVE_HASH_H__
#define __SEACAVE_HASH_H__
// I N C L U D E S /////////////////////////////////////////////////
// D E F I N E S ///////////////////////////////////////////////////
namespace SEACAVE {
// S T R U C T S ///////////////////////////////////////////////////
/**************************************************************************************
* StringHash template
* ---------------
* compile-time string hash template
**************************************************************************************/
class StringHash
{
private:
uint32_t m_val;
template<size_t N> inline uint32_t _Hash(const char (&str)[N]) const
{
typedef const char (&truncated_str)[N-2];
return str[N-1] + 65599 * (str[N-2] + 65599 * _Hash((truncated_str)str));
}
inline uint32_t _Hash(const char (&str)[3]) const { return str[2] + 65599 * (str[1] + 65599 * str[0]); }
inline uint32_t _Hash(const char (&str)[2]) const { return str[1] + 65599 * str[0]; }
public:
template <size_t N> StringHash(const char (&str)[N]) { m_val = _Hash(str); }
inline operator uint32_t() const { return m_val; }
};
/**************************************************************************************
* cHashTable template
* ---------------
* simple hash template (performs relatively well for less than 300 elements)
**************************************************************************************/
#define KEY_NA 0xFFFF
#define KEY_MAXSIZE 0x1000000
template <typename Type>
class cHashTable
{
public:
typedef uint32_t Key;
typedef uint16_t Idx;
typedef uint32_t Size;
typedef cList<Type> Values;
typedef cList<Key, Key> Keys;
typedef cList<Idx, Idx> Indices;
protected:
Keys m_keys; // Array of unique keys that have been set
Values m_values; // Matching array of unique values that have been set
Indices m_indices; // 1-based index array referencing the two arrays above
public:
cHashTable() : m_indices(32) { m_indices.Memset(0); }
~cHashTable() { Release(); }
inline void Release()
{
m_keys.Release();
m_values.Release();
m_indices.Release();
}
inline bool IsEmpty() const { return m_indices.IsEmpty(); }
inline size_t GetSize() const { return m_keys.GetSize(); }
inline const Type* GetBegin() const { return m_values.GetData(); }
inline const Type* GetEnd() const { return m_values.GetData()+m_values.GetSize(); }
inline Type* GetBegin() { return m_values.GetData(); }
inline Type* GetEnd() { return m_values.GetData()+m_values.GetSize(); }
inline const Indices& GetArrIndices() const { return m_indices; }
inline const Keys& GetArrKeys() const { return m_keys; }
inline const Values& GetArrValues() const { return m_values; }
inline Values& GetArrValues() { return m_values; }
private:
inline Size _StaticKeyToID(Key key, Size size) const { return key & (size-1); }
inline Size _KeyToID(Key key) const { return _StaticKeyToID(key, (Size)m_indices.GetSize()); }
inline Idx _IDToIndex(Size id) const { return m_indices[id] - 1; }
inline Idx _KeyToIndex(Key key) const { return (m_indices.IsEmpty() ? KEY_NA : _IDToIndex(_KeyToID(key))); }
// Completely discards then rebuilds all indices based on the current set of keys
void _RebuildIndices()
{
// Clear all current memory
m_indices.Memset(0);
// Run through all keys and recreate the indices
for (Idx i=0; i<m_keys.GetSize(); )
{
const Size index = _KeyToID(m_keys[i]);
m_indices[index] = ++i;
}
}
public:
// Returns a pointer to the value of a key if it exists, 0 otherwise
inline const Type* Find(Key key) const
{
const Idx index = _KeyToIndex(key);
return (index < m_keys.GetSize() && m_keys[index] == key) ? &m_values[index] : NULL;
}
// Non-constant version of the function above
inline Type* Find(Key key)
{
const Idx index = _KeyToIndex(key);
return (index < m_keys.GetSize() && m_keys[index] == key) ? &m_values[index] : NULL;
}
// Checks whether the specified key exists is in the hash
inline bool Exists(Key key) const
{
const Idx index = _KeyToIndex(key);
return (index < m_keys.GetSize() && m_keys[index] == key);
}
// Removes a single entry from the list
void Delete(Key key)
{
const Size id = _KeyToID(key);
const Idx index = _IDToIndex(id);
if (index < m_keys.GetSize() && m_keys[index] == key)
{
m_keys.RemoveAt(index);
m_values.RemoveAt(index);
m_indices[id] = 0;
// Adjust all the indices that follow this one
for (Size i=m_indices.GetSize(); i>0; )
{
if (m_indices[--i] > index)
{
--m_indices[i];
}
}
}
}
// Retrieves a value from the hash -- but it will only be valid if it exists, so be careful. In most
// cases you will either want to use an Exists() check first, or simply use the Find function.
inline const Type& operator [] (Key key) const
{
const Idx index = _KeyToIndex(key);
return m_values[index];
}
// Retrieves a value from the hash, inserting a new one if necessary
Type& operator [] (Key key)
{
// Get the index for this key
const Idx index = _KeyToIndex(key);
if (index != KEY_NA)
{
// If we found a valid entry, we need to match the actual key
const Key oldKey = m_keys[index];
// If the key matches, return the value
if (oldKey == key)
{
return m_values[index];
}
else
{
// Setting the key was unsuccessful due to another entry colliding with our key;
// we must expand the indices until we find a set of keys that will match.
Size newSize = m_indices.GetSize();
while (newSize < (KEY_MAXSIZE>>1))
{
newSize = newSize << 1;
// Find the next best size for the hash that would make both keys unique
if (_StaticKeyToID(key, newSize) != _StaticKeyToID(oldKey, newSize))
{
m_indices.ResizeExact(newSize);
_RebuildIndices();
break;
}
}
// Critical error if we didn't find a working size
ASSERT(_StaticKeyToID(key, newSize) != _StaticKeyToID(oldKey, newSize));
}
}
// assert the total number of values stored is in Idx range
ASSERT(m_keys.GetSize() < (Size)((Idx)-1));
// Append the new key to the end
m_keys.Insert(key);
// Add this new entry to the index list using the current array size as the 1-based index
m_indices[_KeyToID(key)] = (Idx)m_keys.GetSize();
// Return the value
return m_values.AddEmpty();
}
public:
// Fowler / Noll / Vo (FNV) Hash
// magic numbers from http://www.isthe.com/chongo/tech/comp/fnv/
static inline Key HashKeyFNV(const uint8_t* data, Size size)
{
Key hash = 2166136261U; //InitialFNV
for (size_t i = 0; i < size; i++)
{
hash = hash ^ (data[i]); // xor the low 8 bits
hash = hash * 16777619; // multiply by the magic number (FNVMultiple)
}
return hash;
}
// Function that calculates a hash value from a given data
// tested for random binary data (3 <= size <= 33) and performs VERY bad
static inline Key HashKeyR5(const uint8_t* data, Size size)
{
Key key = 0;
Key offset = 1357980759;
for (Size i=0; i<size; ++i, ++data)
{
key += (*data & 31) ^ offset;
key += i & (offset >> 15);
offset = key ^ (((~offset) >> 7) | (offset << 25));
}
return key;
}
static inline Key HashKey(const uint8_t* data, Size size) { return HashKeyFNV(data, size); }
static inline Key HashKey(LPCTSTR sz) { return HashKey((const uint8_t*)sz, (Size)_tcslen(sz)); }
static inline Key HashKey(const String& str) { return HashKey((const uint8_t*)str.c_str(), (Size)str.size()); }
// Convenience functions
inline Type* Find (LPCTSTR key) { return Find ( HashKey(key) ); }
inline Type* Find (const String& key) { return Find ( HashKey(key) ); }
inline const Type* Find (LPCTSTR key) const { return Find ( HashKey(key) ); }
inline const Type* Find (const String& key) const { return Find ( HashKey(key) ); }
inline void Delete (LPCTSTR key) { Delete( HashKey(key) ); }
inline void Delete (const String& key) { Delete( HashKey(key.c_str()) ); }
inline bool Exists (LPCTSTR key) const { return Exists( HashKey(key) ); }
inline bool Exists (const String& key) const { return Exists( HashKey(key.c_str()) ); }
inline Type& operator [] (LPCTSTR key) { return (*this)[ HashKey(key) ]; }
inline Type& operator [] (const String& key) { return (*this)[ HashKey(key.c_str()) ]; }
inline const Type& operator [] (LPCTSTR key) const { return (*this)[ HashKey(key) ]; }
inline const Type& operator [] (const String& key) const { return (*this)[ HashKey(key.c_str()) ]; }
};
/**************************************************************************************
* cMapWrap template
* ---------------
* STL map wrapper
**************************************************************************************/
template <typename Key, typename Type>
class cMapWrap : public std::unordered_map<Key, Type>
{
public:
typedef typename std::unordered_map<Key, Type> Base;
typedef typename Base::const_iterator const_iterator;
typedef typename Base::iterator iterator;
public:
cMapWrap() {}
~cMapWrap() {}
inline void Release() { this->clear(); }
inline bool IsEmpty() const { return this->empty(); }
inline size_t GetSize() const { return this->size(); }
inline const_iterator GetBegin() const { return this->begin(); }
inline const_iterator GetEnd() const { return this->end(); }
inline iterator GetBegin() { return this->begin(); }
inline iterator GetEnd() { return this->end(); }
inline const_iterator Find(const Key& key) const { return this->find(key); }
inline iterator Find(const Key& key) { return this->find(key); }
inline iterator Insert(const Key& key, bool& bExisted) { return Insert(key, Type(), bExisted); }
inline iterator Insert(const Key& key, const Type& val, bool& bExisted) {
const std::pair<iterator,bool> ret(this->emplace(key, val));
bExisted = !ret.second;
return ret.first;
}
};
/*----------------------------------------------------------------*/
} // namespace SEACAVE
#endif // __SEACAVE_HASH_H__