//////////////////////////////////////////////////////////////////// // 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 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 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 class cHashTable { public: typedef uint32_t Key; typedef uint16_t Idx; typedef uint32_t Size; typedef cList Values; typedef cList Keys; typedef cList 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; i0; ) { 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> 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 class cMapWrap : public std::unordered_map { public: typedef typename std::unordered_map 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 ret(this->emplace(key, val)); bExisted = !ret.second; return ret.first; } }; /*----------------------------------------------------------------*/ } // namespace SEACAVE #endif // __SEACAVE_HASH_H__