#ifndef DYNAMICHASHTABLE_H
#define DYNAMICHASHTABLE_H
#include <mutex>
#include <new>
#include <stdint.h>
#include "heaplayers.h"
#include "checkpoweroftwo.h"
template <class VALUE_TYPE,
size_t LOAD_FACTOR_RECIPROCAL = 2,
size_t INIT_SIZE = 4096,
class SourceHeap = HL::MallocHeap,
class LockType = HL::PosixLockType>
class DynamicHashTable {
enum { ExpansionFactor = 2 };
public:
DynamicHashTable() :
_size (INIT_SIZE),
_entries (allocTable (INIT_SIZE)),
_numElements (0)
{
HL::sassert<(LOAD_FACTOR_RECIPROCAL > 1)> verify0;
CheckPowerOfTwo<ExpansionFactor> verify1;
CheckPowerOfTwo<INIT_SIZE> verify2;
verify0 = verify0;
verify1 = verify1;
verify2 = verify2;
}
~DynamicHashTable() {
Guard<LockType> l (_lock);
_sh.free (_entries);
}
bool get (unsigned long k, VALUE_TYPE& value) {
Guard<LockType> l (_lock);
return find (k, value);
}
void insert (const VALUE_TYPE& s)
{
Guard<LockType> l (_lock);
if ((_numElements+1) > _size / LOAD_FACTOR_RECIPROCAL) {
grow();
}
insertOne (s);
_numElements++;
}
bool erase (unsigned long key)
{
Guard<LockType> l (_lock);
unsigned long index;
bool r = findIndex (key, index);
if (r) {
_entries[index].erase();
_numElements--;
if (_numElements < _size / (2 * ExpansionFactor * LOAD_FACTOR_RECIPROCAL)) {
if (_numElements >= 2 * INIT_SIZE) {
shrink();
}
}
}
return r;
}
private:
void insertOne (const VALUE_TYPE& s)
{
unsigned long i = s.hashCode() & (_size - 1);
while (true) {
if (!_entries[i].isValid()) {
_entries[i].put (s);
return;
}
i = (i+1) & (_size - 1);
}
}
void grow()
{
size_t old_size = _size;
StoredObject * old_entries = _entries;
unsigned long old_elt_count = _numElements;
_size = _size * ExpansionFactor;
_entries = allocTable (_size);
#if 0#endif
if (_entries == nullptr) {
abort();
}
unsigned long ct = 0;
for (unsigned long i = 0; i < old_size; i++) {
VALUE_TYPE v;
bool isValid = old_entries[i].get (v);
if (isValid) {
ct++;
insertOne (v);
}
}
assert (ct == _numElements);
_sh.free (old_entries);
}
void shrink()
{
size_t old_size = _size;
StoredObject * old_entries = _entries;
unsigned long old_elt_count = _numElements;
_size = _size / ExpansionFactor;
_entries = allocTable (_size);
#if 0#endif
if (_entries == nullptr) {
abort();
}
unsigned long ct = 0;
for (unsigned long i = 0; i < old_size; i++) {
VALUE_TYPE v;
bool isValid = old_entries[i].get (v);
if (isValid) {
ct++;
insertOne (v);
}
}
assert (ct == _numElements);
_sh.free (old_entries);
}
bool find (unsigned long key, VALUE_TYPE& value)
{
unsigned long index;
bool r = findIndex (key, index);
if (r) {
_entries[index].get (value);
}
return r;
}
bool findIndex (unsigned long key, unsigned long& index) {
unsigned long i = key & (_size - 1);
while (true) {
VALUE_TYPE v;
bool isValid = _entries[i].get (v);
if (!isValid) {
return false;
}
if (v.hashCode() == key) {
index = i;
return true;
}
i = (i+1) & (_size - 1);
}
}
class StoredObject {
private:
typedef enum { EMPTY, DELETED, OCCUPIED } Status;
public:
StoredObject()
: _status (EMPTY)
{}
bool isValid() const {
return (_status == OCCUPIED);
}
void erase() {
_status = DELETED;
}
bool get (VALUE_TYPE& v) const {
if (_status != OCCUPIED) {
return false;
}
v = _value;
return true;
}
void put (const VALUE_TYPE& v) {
_value = v;
_status = OCCUPIED;
}
private:
Status _status;
VALUE_TYPE _value;
};
StoredObject * allocTable (unsigned long nElts)
{
void * ptr =
_sh.malloc (nElts * sizeof(StoredObject));
return new (ptr) StoredObject[nElts];
}
LockType _lock;
SourceHeap _sh;
size_t _size;
StoredObject * _entries;
size_t _numElements;
};
#endif