#ifndef LITB_HASHTABLE_H
#define LITB_HASHTABLE_H
#include "hash.hpp"
#include "vector.hpp"
#include "utils.hpp"
#include <utility>
#include <iterator>
#include <cstdint>
namespace litb {
// A hash table mapping "Key" to "Value".
// "Key" must provide a "hash" function that is
// found by argument-dependent-lookup.
template<typename Key, typename Value>
class HashTable {
struct Bucket;
template<typename BucketType>
struct Iterator;
typedef Iterator<Bucket> iterator;
typedef Iterator<const Bucket> const_iterator;
typedef Pair<const Key, Value> value_type;
typedef Key key_type;
typedef Value mapped_type;
public:
HashTable():_size() { _buckets.append((Bucket*)-1); }
~HashTable() {
clear();
}
iterator begin() { return iterator(_buckets.begin()); }
iterator end() { return iterator(_buckets.end() - 1); }
const_iterator begin() const { return const_iterator(_buckets.begin()); }
const_iterator end() const { return const_iterator(_buckets.end() - 1); }
public:
// returns the size (number of keys) of this table
int size() const {
return _size;
}
// inserts the given mapping, returning false if the key was already mapped. in that case,
// the value will not be updated.
bool insert(const Pair<Key, Value>& mapping) {
struct C {
const Value &operator()() const {
return value;
}
const Value& value;
} creator = { mapping.second };
return internalInsert(mapping.first, creator);
}
// inserts a mapping of key to value.
// returns true if the mapping was inserted, and false if the key was already
// mapped. In that case, the value will not be updated.
bool insert(const Key& key, const Value& value) {
struct C {
const Value &operator()() const {
return value;
}
const Value& value;
} creator = { value };
return internalInsert(key, creator);
}
// returns true if the table contains a mapping from key to a value
bool contains(const Key& key) const {
return getBucket(key) != nullptr;
}
// returns a reference to the value mapped by the given key. If the element
// does not exist yet, it is value-initialized and copied to the map.
Value &at(const Key& key) {
struct C {
Value operator()() const {
return Value();
}
} creator;
return getOrCreateBucket(key, creator)->_data.second;
}
Value &operator[](const Key& key) {
return at(key);
}
// if for this overload the element does not exist, behavior is undefined
const Value& at(const Key& key) const {
return getBucket(key)->_data.second;
}
const Value& operator[](const Key& key) const {
return at(key);
}
// after calling this the map contains no elements anymore and the bucket array
// is empty
void clear() {
for(auto it = _buckets.begin(), ite = _buckets.end()-1; it != ite; ++it) {
Bucket *bucket = *it;
while(bucket) {
Bucket *bucket1 = bucket;
delete bucket1;
bucket = bucket->_next;
}
}
_buckets.clear();
_buckets.append((Bucket*)-1);
_size = 0;
}
// erases the item at the given position
void erase(iterator it) {
Bucket ** head = it._bucketHead;
if(*head == it._bucket) {
*head = (*head)->next;
} else {
Bucket *bucket = *head;
for(; bucket->_next != it._bucket; bucket = bucket->_next)
;
bucket->_next = it._bucket->next;
}
delete it._bucket;
_size--;
}
private:
// one bucket in the buckets array
struct Bucket {
Bucket(const Key& key, const Value& value, Bucket *next)
:_data(key, value), _next(next)
{ }
value_type _data;
Bucket *_next;
};
template<typename BucketType>
struct Iterator {
template<typename Key1, typename Value1>
friend class HashTable;
typedef HashTable::value_type value_type;
typedef typename TransferCv<value_type, BucketType>::type &reference_type;
typedef typename TransferCv<value_type, BucketType>::type *pointer;
typedef int difference_type;
typedef std::forward_iterator_tag iterator_category;
Iterator(typename TransferCv<BucketType*, BucketType>::type *bucketHead)
:_bucketHead(bucketHead)
{ alignToNextBucket(); }
public:
friend bool operator==(const Iterator& a, const Iterator& b) {
return a._bucket == b._bucket;
}
friend bool operator!=(const Iterator& a, const Iterator& b) {
return !(a == b);
}
public:
reference_type operator*() const {
return _bucket->_data;
}
pointer operator->() const {
return &_bucket->_data;
}
// convenience wrappers key() and value() return the key and the mapped data
// respectively
const key_type& key() const {
return (*this)->first;
}
typename TransferCv<mapped_type, BucketType>::type &value() const {
return (*this)->second;
}
public:
Iterator operator++(int) {
Iterator it(*this);
++it;
return it;
}
Iterator &operator++() {
if(!_bucket->_next) {
_bucketHead++;
alignToNextBucket();
} else {
_bucket = _bucket->_next;
}
return *this;
}
private:
void alignToNextBucket() {
while (!*_bucketHead && *_bucketHead != (Bucket*)-1) {
_bucketHead++;
}
_bucket = *_bucketHead;
}
typename TransferCv<BucketType*, BucketType>::type *_bucketHead;
BucketType *_bucket;
};
private:
static std::uint32_t hash(const Key& key) {
using litb::hash;
return hash(key);
}
template<typename Creator>
bool internalInsert(const Key& key, const Creator& c) {
int oldSize = size();
Bucket *bucket = getOrCreateBucket(key, c);
return size() != oldSize;
}
// returns a bucket for the given key and value.
// if not yet existing, call "creator()" to create and return a new
// instance of the value.
template<typename Creator>
Bucket *getOrCreateBucket(const Key& key, const Creator& c) {
if(_buckets.size() == 1) {
_buckets.clear();
_buckets.resize(32);
_buckets.append((Bucket*)-1);
} else {
const float loadFactor = size() / (float)(_buckets.size() - 1);
if(loadFactor > 0.75) {
rehash();
}
}
int bucketIndex = hash(key) % (_buckets.size() - 1);
Bucket *bucket = _buckets[bucketIndex];
for(Bucket *bucket1 = bucket; bucket1; bucket1 = bucket1->_next) {
if(bucket1->_data.first == key) {
return bucket1;
}
}
bucket = new Bucket(key, c(), bucket);
_buckets[bucketIndex] = bucket;
_size++;
return bucket;
}
// returns the bucket for the given key and nullptr if it is not found
const Bucket *getBucket(const Key& key) const {
if(_buckets.size() == 1)
return nullptr;
int bucketIndex = hash(key) % (_buckets.size() - 1);
const Bucket *bucket = _buckets[bucketIndex];
for(const Bucket *bucket1 = bucket; bucket1; bucket1 = bucket1->_next) {
if(bucket1->_data.first == key) {
return bucket1;
}
}
return nullptr;
}
// this will reallocate the bucket array and rehash everything
void rehash() {
int newSize = (_buckets.size() - 1) * 2;
Vector<Bucket*, 1> newBuckets;
newBuckets.resize(newSize);
for(auto it = _buckets.begin(), ite = _buckets.end()-1; it != ite; ++it) {
Bucket *bucket = *it;
while(bucket) {
int bucketIndex = hash(bucket->_data.first) % newBuckets.size();
Bucket *bucket1 = bucket;
bucket = bucket->_next;
bucket1->_next = newBuckets[bucketIndex];
newBuckets[bucketIndex] = bucket1;
}
}
newBuckets.append((Bucket*)-1);
_buckets = std::move(newBuckets);
}
private:
// the last bucket pointer of _buckets designates the end() position
// and always has the value (Bucket*)-1
Vector<Bucket*, 1> _buckets;
int _size;
};
}
#endif