| #pragma once |
| |
| #ifndef TCG_HASH_H |
| #define TCG_HASH_H |
| |
| |
| #include "list.h" |
| |
| namespace tcg |
| { |
| |
| |
| |
| |
| |
| |
| |
| template <typename K, typename T, typename Hash_functor = size_t (*)(const K &)> |
| class hash |
| { |
| public: |
| typedef K key_type; |
| typedef T value_type; |
| typedef Hash_functor hash_type; |
| |
| struct BucketNode { |
| K m_key; |
| T m_val; |
| size_t m_next; |
| size_t m_prev; |
| |
| BucketNode(const K &key, const T &val) : m_key(key), m_val(val), m_next(-1), m_prev(-1) {} |
| BucketNode(const std::pair<K, T> &pair) : m_key(pair.first), m_val(pair.second), m_next(-1), m_prev(-1) {} |
| ~BucketNode() {} |
| }; |
| |
| typedef typename tcg::list<BucketNode>::size_t size_t; |
| |
| typedef typename tcg::list<BucketNode>::iterator iterator; |
| typedef typename tcg::list<BucketNode>::const_iterator const_iterator; |
| |
| private: |
| std::vector<size_t> m_bucketsIdx; |
| tcg::list<BucketNode> m_items; |
| Hash_functor m_hash; |
| |
| private: |
| bool createItem(const K &key, const T &val) |
| { |
| m_items.push_back(BucketNode(key, val)); |
| |
| size_t itemsCount = m_items.size(), bucketsCount = m_bucketsIdx.size(); |
| if (itemsCount > bucketsCount) { |
| do |
| bucketsCount = 2 * bucketsCount + 1; |
| while (itemsCount > bucketsCount); |
| |
| rehash(bucketsCount); |
| return true; |
| } |
| return false; |
| } |
| |
| size_t touchKey(const K &key, const T &val = T()) |
| { |
| size_t hashValue = m_hash(key) % m_bucketsIdx.size(); |
| size_t bucketIdx = m_bucketsIdx[hashValue]; |
| size_t oldIdx = _neg; |
| |
| if (bucketIdx == _neg) { |
| |
| bool rehashed = createItem(key, val); |
| bucketIdx = m_items.last().m_idx; |
| |
| if (!rehashed) |
| |
| m_bucketsIdx[hashValue] = bucketIdx; |
| |
| return bucketIdx; |
| } else { |
| |
| for (; |
| bucketIdx != _neg && m_items[bucketIdx].m_key != key; |
| oldIdx = bucketIdx, bucketIdx = m_items[bucketIdx].m_next) |
| ; |
| } |
| |
| if (bucketIdx == _neg) { |
| |
| bool rehashed = createItem(key, val); |
| bucketIdx = m_items.last().m_idx; |
| |
| if (!rehashed && oldIdx != _neg) { |
| |
| m_items[oldIdx].m_next = bucketIdx; |
| m_items[bucketIdx].m_prev = oldIdx; |
| } |
| } |
| |
| return bucketIdx; |
| } |
| |
| public: |
| |
| |
| |
| hash(const Hash_functor &func = Hash_functor(), size_t bucketsCount = 89) |
| : m_bucketsIdx(bucketsCount, _neg), m_hash(func) {} |
| |
| template <typename ForIt> |
| hash(ForIt begin, ForIt end, const Hash_functor &func = Hash_functor(), size_t bucketsCount = 89) |
| : m_items(begin, end), m_hash(func) |
| { |
| for (size_t nCount = m_items.nodesCount(); bucketsCount < nCount; |
| bucketsCount = 2 * bucketsCount + 1) |
| ; |
| rehash(bucketsCount); |
| } |
| |
| |
| template <typename BidIt> |
| hash(BidIt begin, BidIt end, size_t nodesCount, const Hash_functor &func = Hash_functor(), size_t bucketsCount = 89) |
| : m_items(begin, end, nodesCount), m_hash(func) |
| { |
| for (size_t nCount = m_items.nodesCount(); bucketsCount < nCount; |
| bucketsCount = 2 * bucketsCount + 1) |
| ; |
| rehash(bucketsCount); |
| } |
| |
| |
| |
| void rehash(size_t newSize) |
| { |
| m_bucketsIdx.clear(); |
| m_bucketsIdx.resize(newSize, _neg); |
| |
| size_t bucketIdx; |
| |
| iterator it, iEnd = end(); |
| for (it = begin(); it != iEnd; ++it) { |
| bucketIdx = m_hash(it->m_key) % newSize; |
| size_t &idx = m_bucketsIdx[bucketIdx]; |
| |
| it->m_next = idx; |
| it->m_prev = _neg; |
| |
| if (idx != _neg) |
| m_items[idx].m_prev = it.m_idx; |
| idx = it.m_idx; |
| } |
| } |
| |
| |
| |
| size_t size() const { return m_items.size(); } |
| bool empty() { return size() == 0; } |
| |
| void clear() |
| { |
| m_items.clear(); |
| m_bucketsIdx.clear(); |
| m_bucketsIdx.resize(1, _neg); |
| } |
| |
| |
| |
| iterator begin() { return m_items.begin(); } |
| iterator last() { return m_items.last(); } |
| iterator end() { return m_items.end(); } |
| |
| |
| |
| const_iterator begin() const { return m_items.begin(); } |
| const_iterator last() const { return m_items.last(); } |
| const_iterator end() const { return m_items.end(); } |
| |
| |
| |
| iterator find(const K &key) |
| { |
| size_t hashValue = m_hash(key) % m_bucketsIdx.size(); |
| size_t bucketIdx = m_bucketsIdx[hashValue]; |
| |
| if (bucketIdx == _neg) |
| return end(); |
| |
| for (; |
| bucketIdx != _neg && m_items[bucketIdx].m_key != key; |
| bucketIdx = m_items[bucketIdx].m_next) |
| ; |
| |
| return bucketIdx == _neg ? end() : iterator(&m_items, bucketIdx); |
| } |
| |
| const_iterator find(const K &key) const |
| { |
| return const_iterator(&m_items, const_cast<hash *>(this)->find(key).m_idx); |
| } |
| |
| |
| |
| iterator insert(const K &key, const T &val) |
| { |
| size_t idx = touchKey(key); |
| m_items[idx].m_val = val; |
| return iterator(&m_items, idx); |
| } |
| |
| |
| |
| |
| |
| T &operator[](const K &key) |
| { |
| return m_items[touchKey(key)].m_val; |
| } |
| |
| |
| |
| |
| T &touch(const K &key, const T &val) |
| { |
| return m_items[touchKey(key, val)].m_val; |
| } |
| |
| |
| |
| iterator insert(const std::pair<K, T> &pair) |
| { |
| return insert(pair.first, pair.second); |
| } |
| |
| |
| |
| void erase(iterator it) |
| { |
| BucketNode &node = *it; |
| if (node.m_next != _neg) |
| m_items[node.m_next].m_prev = node.m_prev; |
| if (node.m_prev != _neg) |
| m_items[node.m_prev].m_next = node.m_next; |
| else |
| m_bucketsIdx[m_hash(node.m_key) % m_bucketsIdx.size()] = _neg; |
| |
| m_items.erase(it); |
| } |
| |
| |
| |
| void erase(const K &key) |
| { |
| iterator it = find(key); |
| if (it != end()) |
| erase(it); |
| } |
| |
| |
| |
| friend void swap(hash &a, hash &b) |
| { |
| using std::swap; |
| |
| a.m_bucketsIdx.swap(b.m_bucketsIdx); |
| swap(a.m_items, b.m_items); |
| swap(a.m_hash, b.m_hash); |
| } |
| |
| |
| |
| const tcg::list<BucketNode> &items() const { return m_items; } |
| |
| |
| |
| const std::vector<size_t> &buckets() const { return m_bucketsIdx; } |
| |
| |
| |
| const Hash_functor &hashFunctor() const { return m_hash; } |
| |
| |
| Hash_functor &hashFunctor() { return m_hash; } |
| }; |
| |
| } |
| |
| #endif |