/* * This is a part of the BugTrap package. * Copyright (c) 2005-2009 IntelleSoft. * All rights reserved. * * Description: Hash table. * Author: Maksim Pyatkovskiy. * * This source code is only intended as a supplement to the * BugTrap package reference and related electronic documentation * provided with the product. See these sources for detailed * information regarding the BugTrap package. */ #pragma once #include "ColHelper.h" /// Hash table. template < typename KEY_TYPE, typename DATA_TYPE, class KEY_INSTANCE_TRAITS = CObjectTraits, class DATA_INSTANCE_TRAITS = CObjectTraits, class COMPARE_TRAITS = CCompareTraits, class HASH_TRAITS = CHashTraits > class CHash { private: /// Hash item. class CHashItem { /// Hash table. friend class CHash; private: /// Contains key value. KEY_TYPE m_Key; /// Contains data value. DATA_TYPE m_Data; /// Pointer to the next hash item. CHashItem* m_pNext; }; /// Hash iterator. class CHashIterator { /// Hash table. friend class CHash; private: /// Pointer to current hash item. CHashItem* m_pItem; /// Index in the internal array containing current hash items. size_t m_nIndex; public: /// Return true if iterator points to valid value. operator bool(void) const { return (m_pItem != NULL); } /// Return false if iterator points to valid value. bool operator!(void) const { return (m_pItem == NULL); } }; /// Dynamic array of hash items arranged as list. CHashItem** m_pHash; /// List of deleted hash items. These items can be reused in future allocations. CHashItem* m_pGarbage; /// Size of dynamic array. size_t m_nHashSize; /// Number of items in collection. size_t m_nCount; public: /// Synonym of hash iterator type. typedef CHashIterator POSITION; enum { /// Default hash table size. DEFAULT_SIZE = 11 }; /// Initialize a hash table object with specified size. explicit CHash(size_t nHashSize = DEFAULT_SIZE); /// Makes a copy of a hash table. CHash(const CHash& rHash); /// Makes a copy of a hash table. const CHash& operator=(const CHash& rHash); /// Destroy a hash table object and free allocated memory. ~CHash(void); /// Return the size of dictionary. size_t GetSize(void) const; /// Return number of items in collection. size_t GetCount(void) const; /// Tests for the empty-hash condition (no elements). bool IsEmpty(void) const; /// Search for data mapped to a given key. Returns true if the key was found. bool Lookup(const KEY_TYPE& rKey, DATA_TYPE& rData); /// Search for data mapped to a given key. Returns pointer to founded data or NULL if a matching key is not found. DATA_TYPE* Lookup(const KEY_TYPE& rKey); /// Search for data mapped to a given key. Returns pointer to founded data or NULL if a matching key is not found. const DATA_TYPE* Lookup(const KEY_TYPE& rKey) const; /// Insert an element into the dictionary; replace an existing element if a matching key is found. void SetAt(const KEY_TYPE& rKey, const DATA_TYPE& rData); /// Insert an element into the dictionary - operator substitution for SetAt. DATA_TYPE& GetAt(const KEY_TYPE& rKey); /// Insert an element into the dictionary - operator substitution for SetAt. DATA_TYPE& operator[](const KEY_TYPE& rKey); /// Remove an element specified by a key. void Delete(const typename KEY_TYPE& rKey); /// Remove an element specified by a position. void DeleteAt(const typename POSITION& pos); /// Remove all the elements from this dictionary. void DeleteAll(bool bFree = false); /// Returns the position of the first element. POSITION GetStartPosition(void) const; /// Gets the next element for iterating. POSITION GetNextPosition(const typename POSITION& pos) const; /// Get the key at a given position. static const KEY_TYPE& GetKeyAt(const typename POSITION& pos); /// Get the data at a given position. static DATA_TYPE& GetDataAt(const typename POSITION& pos); private: /// Look for hash item by the specified key. CHashItem* InternalLookup(const typename KEY_TYPE& rKey, size_t& rIndex) const; /// Delete hash item from the hash table. void InternalDelete(CHashItem* pDelItem, size_t nIndex); /// Initialize hash table. void InitHashTable(size_t nHashSize); /// Throw an exception if position is not valid. static void ValidatePosition(const typename POSITION& pos); }; /** * @param nHashSize - hash table size. */ template void CHash::InitHashTable(size_t nHashSize) { m_pGarbage = NULL; m_nHashSize = nHashSize; m_nCount = 0; m_pHash = new CHashItem*[nHashSize]; if (! m_pHash) RaiseException(STATUS_NO_MEMORY, 0, 0, NULL); } /** * @param nHashSize - hash table size. */ template CHash::CHash(size_t nHashSize) { InitHashTable(nHashSize); for (size_t nIndex = 0; nIndex < nHashSize; ++nIndex) m_pHash[nIndex] = NULL; } /** * @param pos - validated position. */ template inline void CHash::ValidatePosition(const typename CHash::POSITION& pos) { _ASSERTE(pos); if (! pos) RaiseException(STATUS_ARRAY_BOUNDS_EXCEEDED, 0, 0, NULL); } /** * @param rHash - object to be copied. */ template CHash::CHash(const CHash& rHash) { InitHashTable(rHash.m_nHashSize); for (size_t nIndex = 0; nIndex < m_nHashSize; ++nIndex) { m_pHash[nIndex] = NULL; CHashItem* pItem = rHash.m_pHash[nIndex]; while (pItem) { SetAt(pItem->m_Key, pItem->m_Data); pItem = pItem->m_pNext; } } } /** * @param rHash - object to be copied. * @return reference to itself. */ template const CHash& CHash::operator=(const CHash& rHash) { if (this != &rHash) { DeleteAll(); for (size_t nIndex = 0; nIndex < rHash.m_nHashSize; ++nIndex) { CHashItem* pItem = rHash.m_pHash[nIndex]; while (pItem) { SetAt(pItem->m_Key, pItem->m_Data); pItem = pItem->m_pNext; } } } return *this; } template inline CHash::~CHash(void) { DeleteAll(true); delete[] m_pHash; } /** * @return size of hash table. */ template inline size_t CHash::GetSize(void) const { return m_nHashSize; } /** * @return the number of elements. */ template inline size_t CHash::GetCount(void) const { return m_nCount; } /** * @return true if this hash contains no elements; otherwise false. */ template inline bool CHash::IsEmpty(void) const { return (m_nCount == 0); } /** * @param rKey - specifies the key that identifies the element to be looked up. * @param rIndex - index in array of obtained item. * @return pointer to obtained hash item or NULL. */ template typename CHash::CHashItem* CHash::InternalLookup(const typename KEY_TYPE& rKey, size_t& rIndex) const { rIndex = HASH_TRAITS::HashKey(rKey) % m_nHashSize; CHashItem* pItem = m_pHash[rIndex]; while (pItem) { if (COMPARE_TRAITS::Compare(pItem->m_Key, rKey)) return pItem; pItem = pItem->m_pNext; } return NULL; } /** * @param rKey - specifies the key that identifies the element to be looked up. * @param rData - data variable that is being set to found value. * @return true if the key was found. */ template inline bool CHash::Lookup(const KEY_TYPE& rKey, DATA_TYPE& rData) { size_t nIndex; CHashItem* pItem = InternalLookup(rKey, nIndex); if (pItem) { rData = pItem->m_Data; return true; } return false; } /** * @param rKey - specifies the key that identifies the element to be looked up. * @return pointer to obtained hash item or NULL. */ template inline DATA_TYPE* CHash::Lookup(const KEY_TYPE& rKey) { size_t nIndex; CHashItem* pItem = InternalLookup(rKey, nIndex); return (pItem ? &pItem->m_Data : NULL); } /** * @param rKey - specifies the key that identifies the element to be looked up. * @return pointer to obtained hash item or NULL. */ template inline const DATA_TYPE* CHash::Lookup(const KEY_TYPE& rKey) const { size_t nIndex; CHashItem* pItem = InternalLookup(rKey, nIndex); return (pItem ? &pItem->m_Data : NULL); } /** * @param rKey - specifies the key of the new element. * @param rData - specifies the value of the new element. */ template inline void CHash::SetAt(const KEY_TYPE& rKey, const DATA_TYPE& rData) { DATA_INSTANCE_TRAITS::Assignment(GetAt(rKey), rData); } /** * @param rKey - the key used to retrieve the value from the hash. * @return reference to the item. */ template DATA_TYPE& CHash::GetAt(const KEY_TYPE& rKey) { size_t nIndex; CHashItem* pItem = InternalLookup(rKey, nIndex); if (pItem == NULL) { if (m_pGarbage) { pItem = m_pGarbage; m_pGarbage = m_pGarbage->m_pNext; } else { pItem = (CHashItem*)new BYTE[sizeof(CHashItem)]; if (! pItem) RaiseException(STATUS_NO_MEMORY, 0, 0, NULL); } KEY_INSTANCE_TRAITS::Constructor(pItem->m_Key); KEY_INSTANCE_TRAITS::Assignment(pItem->m_Key, rKey); DATA_INSTANCE_TRAITS::Constructor(pItem->m_Data); pItem->m_pNext = m_pHash[nIndex]; m_pHash[nIndex] = pItem; ++m_nCount; } return pItem->m_Data; } /** * @param rKey - the key used to retrieve the value from the hash. * @return reference to the item. */ template inline DATA_TYPE& CHash::operator[](const KEY_TYPE& rKey) { return GetAt(rKey); } /** * @param pDelItem - pointer to deleted hash item. * @param nIndex - index of array there deleted item can be found. */ template void CHash::InternalDelete(typename CHash::CHashItem* pDelItem, size_t nIndex) { CHashItem* pPrev = NULL; CHashItem* pItem = m_pHash[nIndex]; while (pItem) { if (pItem == pDelItem) { if (pPrev) pPrev->m_pNext = pItem->m_pNext; else m_pHash[nIndex] = pItem->m_pNext; KEY_INSTANCE_TRAITS::Destructor(pItem->m_Key); DATA_INSTANCE_TRAITS::Destructor(pItem->m_Data); pItem->m_pNext = m_pGarbage; m_pGarbage = pItem; --m_nCount; break; } else { pPrev = pItem; pItem = pItem->m_pNext; } } } /** * @param rKey - key for the element to be removed. */ template inline void CHash::Delete(const typename KEY_TYPE& rKey) { size_t nIndex; CHashItem* pItem = InternalLookup(rKey, nIndex); if (! pItem) RaiseException(STATUS_ARRAY_BOUNDS_EXCEEDED, 0, 0, NULL); InternalDelete(pItem, nIndex); } /** * @param bFree - pass true of release array memory. */ template void CHash::DeleteAll(bool bFree) { if (bFree) { CHashItem* pNext; while (m_pGarbage) { pNext = m_pGarbage->m_pNext; delete[] (PBYTE)m_pGarbage; m_pGarbage = pNext; } for (size_t nIndex = 0; nIndex < m_nHashSize; ++nIndex) { CHashItem* pItem = m_pHash[nIndex]; while (pItem) { pNext = pItem->m_pNext; KEY_INSTANCE_TRAITS::Destructor(pItem->m_Key); DATA_INSTANCE_TRAITS::Destructor(pItem->m_Data); delete[] (PBYTE)pItem; pItem = pNext; } m_pHash[nIndex] = NULL; } } else { for (size_t nIndex = 0; nIndex < m_nHashSize; ++nIndex) { CHashItem* pItem = m_pHash[nIndex]; while (pItem) { CHashItem* pNext = pItem->m_pNext; KEY_INSTANCE_TRAITS::Destructor(pItem->m_Key); DATA_INSTANCE_TRAITS::Destructor(pItem->m_Data); pItem->m_pNext = m_pGarbage; m_pGarbage = pItem; pItem = pNext; } m_pHash[nIndex] = NULL; } } m_nCount = 0; } /** * @return a @a POSITION value that indicates a starting position for iterating the hash; or NULL if the hash is empty. */ template typename CHash::POSITION CHash::GetStartPosition(void) const { POSITION posStart; size_t nIndex = 0; while (nIndex < m_nHashSize && m_pHash[nIndex] == NULL) ++nIndex; posStart.m_pItem = nIndex == m_nHashSize ? NULL : m_pHash[nIndex]; posStart.m_nIndex = nIndex; return posStart; } /** * @param pos - previous position. * @return a @a POSITION value that indicates the next position for iterating the hash; or NULL if it's a last position. */ template typename CHash::POSITION CHash::GetNextPosition(const typename CHash::POSITION& pos) const { ValidatePosition(pos); CHashItem* pItem = pos.m_pItem->m_pNext; size_t nIndex = pos.m_nIndex; while (pItem == NULL && ++nIndex < m_nHashSize) pItem = m_pHash[nIndex]; POSITION posNext; posNext.m_pItem = pItem; posNext.m_nIndex = nIndex; return posNext; } /** * @param pos - hash item position. * @return reference to key at specified position. */ template inline const KEY_TYPE& CHash::GetKeyAt(const typename CHash::POSITION& pos) { ValidatePosition(pos); return pos.m_pItem->m_Key; } /** * @param pos - hash item position. * @return reference to data at specified position. */ template inline DATA_TYPE& CHash::GetDataAt(const typename CHash::POSITION& pos) { ValidatePosition(pos); return pos.m_pItem->m_Data; } /** * @param pos - hash item position. */ template void CHash::DeleteAt(const typename CHash::POSITION& pos) { ValidatePosition(pos); InternalDelete(pos.m_pItem, pos.m_nIndex); }