2016-05-17 03:04:11 +12:00
|
|
|
#pragma once
|
2016-03-19 06:57:51 +13:00
|
|
|
|
|
|
|
#ifndef TCG_HASH_H
|
|
|
|
#define TCG_HASH_H
|
|
|
|
|
|
|
|
// tcg includes
|
|
|
|
#include "list.h"
|
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
namespace tcg {
|
2016-03-19 06:57:51 +13:00
|
|
|
|
|
|
|
//============================================================================================
|
|
|
|
|
|
|
|
/*!
|
|
|
|
The hash class implements a hash map using tcg lists
|
|
|
|
*/
|
|
|
|
|
|
|
|
template <typename K, typename T, typename Hash_functor = size_t (*)(const K &)>
|
2016-06-15 18:43:10 +12:00
|
|
|
class hash {
|
2016-03-19 06:57:51 +13:00
|
|
|
public:
|
2016-06-15 18:43:10 +12:00
|
|
|
typedef K key_type;
|
|
|
|
typedef T value_type;
|
|
|
|
typedef Hash_functor hash_type;
|
2016-03-19 06:57:51 +13:00
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
struct BucketNode {
|
|
|
|
K m_key;
|
|
|
|
T m_val;
|
|
|
|
size_t m_next;
|
|
|
|
size_t m_prev;
|
2016-03-19 06:57:51 +13:00
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
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() {}
|
|
|
|
};
|
2016-03-19 06:57:51 +13:00
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
typedef typename tcg::list<BucketNode>::size_t size_t;
|
2016-03-19 06:57:51 +13:00
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
typedef typename tcg::list<BucketNode>::iterator iterator;
|
|
|
|
typedef typename tcg::list<BucketNode>::const_iterator const_iterator;
|
2016-03-19 06:57:51 +13:00
|
|
|
|
|
|
|
private:
|
2016-06-15 18:43:10 +12:00
|
|
|
std::vector<size_t> m_bucketsIdx;
|
|
|
|
tcg::list<BucketNode> m_items;
|
|
|
|
Hash_functor m_hash;
|
2016-03-19 06:57:51 +13:00
|
|
|
|
|
|
|
private:
|
2016-06-15 18:43:10 +12:00
|
|
|
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; // Please, note that 2n here would be moronic
|
|
|
|
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) {
|
|
|
|
// A new bucket is created
|
|
|
|
bool rehashed = createItem(key, val);
|
|
|
|
bucketIdx = m_items.last().m_idx;
|
|
|
|
|
|
|
|
if (!rehashed)
|
|
|
|
// Need to manually update the stored bucket index
|
|
|
|
m_bucketsIdx[hashValue] = bucketIdx;
|
|
|
|
|
|
|
|
return bucketIdx;
|
|
|
|
} else {
|
|
|
|
// Bucket exists - search key in it
|
|
|
|
for (; bucketIdx != _neg && m_items[bucketIdx].m_key != key;
|
|
|
|
oldIdx = bucketIdx, bucketIdx = m_items[bucketIdx].m_next)
|
|
|
|
;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (bucketIdx == _neg) {
|
|
|
|
// Key was not found - create an item and insert it
|
|
|
|
bool rehashed = createItem(key, val);
|
|
|
|
bucketIdx = m_items.last().m_idx;
|
|
|
|
|
|
|
|
if (!rehashed && oldIdx != _neg) {
|
|
|
|
// Need to relink manually
|
|
|
|
m_items[oldIdx].m_next = bucketIdx;
|
|
|
|
m_items[bucketIdx].m_prev = oldIdx;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return bucketIdx;
|
|
|
|
}
|
2016-03-19 06:57:51 +13:00
|
|
|
|
|
|
|
public:
|
2016-06-15 18:43:10 +12:00
|
|
|
// NOTE: The defaulted 89 is a *good* initial buckets size when expanding by
|
|
|
|
// the (2n+1) rule.
|
|
|
|
// See http://www.concentric.net/~ttwang/tech/hashsize.htm for details
|
2016-03-19 06:57:51 +13:00
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
hash(const Hash_functor &func = Hash_functor(), size_t bucketsCount = 89)
|
|
|
|
: m_bucketsIdx(bucketsCount, _neg), m_hash(func) {}
|
2016-03-19 06:57:51 +13:00
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
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);
|
|
|
|
}
|
2016-03-19 06:57:51 +13:00
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
//! Constructs from a range of (index, value) item pairs
|
|
|
|
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);
|
|
|
|
}
|
2016-03-19 06:57:51 +13:00
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
//--------------------------------------------------------------------------
|
2016-03-19 06:57:51 +13:00
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
void rehash(size_t newSize) {
|
|
|
|
m_bucketsIdx.clear();
|
|
|
|
m_bucketsIdx.resize(newSize, _neg);
|
2016-03-19 06:57:51 +13:00
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
size_t bucketIdx;
|
2016-03-19 06:57:51 +13:00
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
iterator it, iEnd = end();
|
|
|
|
for (it = begin(); it != iEnd; ++it) {
|
|
|
|
bucketIdx = m_hash(it->m_key) % newSize;
|
|
|
|
size_t &idx = m_bucketsIdx[bucketIdx];
|
2016-03-19 06:57:51 +13:00
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
it->m_next = idx;
|
|
|
|
it->m_prev = _neg;
|
2016-03-19 06:57:51 +13:00
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
if (idx != _neg) m_items[idx].m_prev = it.m_idx;
|
|
|
|
idx = it.m_idx;
|
|
|
|
}
|
|
|
|
}
|
2016-03-19 06:57:51 +13:00
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
//--------------------------------------------------------------------------
|
2016-03-19 06:57:51 +13:00
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
size_t size() const { return m_items.size(); }
|
|
|
|
bool empty() { return size() == 0; }
|
2016-03-19 06:57:51 +13:00
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
void clear() {
|
|
|
|
m_items.clear();
|
|
|
|
m_bucketsIdx.clear();
|
|
|
|
m_bucketsIdx.resize(1, _neg);
|
|
|
|
}
|
2016-03-19 06:57:51 +13:00
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
//--------------------------------------------------------------------------
|
2016-03-19 06:57:51 +13:00
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
iterator begin() { return m_items.begin(); }
|
|
|
|
iterator last() { return m_items.last(); }
|
|
|
|
iterator end() { return m_items.end(); }
|
2016-03-19 06:57:51 +13:00
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
//--------------------------------------------------------------------------
|
2016-03-19 06:57:51 +13:00
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
const_iterator begin() const { return m_items.begin(); }
|
|
|
|
const_iterator last() const { return m_items.last(); }
|
|
|
|
const_iterator end() const { return m_items.end(); }
|
2016-03-19 06:57:51 +13:00
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
//--------------------------------------------------------------------------
|
2016-03-19 06:57:51 +13:00
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
iterator find(const K &key) {
|
|
|
|
size_t hashValue = m_hash(key) % m_bucketsIdx.size();
|
|
|
|
size_t bucketIdx = m_bucketsIdx[hashValue];
|
2016-03-19 06:57:51 +13:00
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
if (bucketIdx == _neg) return end();
|
2016-03-19 06:57:51 +13:00
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
for (; bucketIdx != _neg && m_items[bucketIdx].m_key != key;
|
|
|
|
bucketIdx = m_items[bucketIdx].m_next)
|
|
|
|
;
|
2016-03-19 06:57:51 +13:00
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
return bucketIdx == _neg ? end() : iterator(&m_items, bucketIdx);
|
|
|
|
}
|
2016-03-19 06:57:51 +13:00
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
const_iterator find(const K &key) const {
|
|
|
|
return const_iterator(&m_items, const_cast<hash *>(this)->find(key).m_idx);
|
|
|
|
}
|
2016-03-19 06:57:51 +13:00
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
//--------------------------------------------------------------------------
|
2016-03-19 06:57:51 +13:00
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
iterator insert(const K &key, const T &val) {
|
|
|
|
size_t idx = touchKey(key);
|
|
|
|
m_items[idx].m_val = val;
|
|
|
|
return iterator(&m_items, idx);
|
|
|
|
}
|
2016-03-19 06:57:51 +13:00
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
//--------------------------------------------------------------------------
|
2016-03-19 06:57:51 +13:00
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
//!\warning Assignment of the kind hash_map[i1] = hash_map[i2] are DANGEROUS!
|
|
|
|
//!The
|
|
|
|
//! reference returned on the right may be INVALIDATED if the first key is
|
|
|
|
//! inserted!
|
|
|
|
T &operator[](const K &key) { return m_items[touchKey(key)].m_val; }
|
2016-03-19 06:57:51 +13:00
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
//--------------------------------------------------------------------------
|
2016-03-19 06:57:51 +13:00
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
//!\warning The same remark of operator[] applies here!
|
|
|
|
T &touch(const K &key, const T &val) {
|
|
|
|
return m_items[touchKey(key, val)].m_val;
|
|
|
|
}
|
2016-03-19 06:57:51 +13:00
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
//--------------------------------------------------------------------------
|
|
|
|
|
|
|
|
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; }
|
|
|
|
|
|
|
|
//! Remember to rehash() if the hash functor changes
|
|
|
|
Hash_functor &hashFunctor() { return m_hash; }
|
2016-03-19 06:57:51 +13:00
|
|
|
};
|
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
} // namespace tcg
|
2016-03-19 06:57:51 +13:00
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
#endif // TCG_HASH_H
|