#ifndef LITB_HASHTABLE_H
#define LITB_HASHTABLE_H
#include <type_traits>
#include <utility>
namespace litb {
// A pair similar to std::pair, but a bit simplier
template<typename First, typename Second>
struct Pair {
Pair()
:first(), second()
{ }
// for being able to construct us with non-deducible arguments
// (e.g initializer lists)
Pair(const First& first, const Second& second)
:first(first), second(second)
{ }
// for moving first and second into us
template<typename First1, typename Second1,
typename std::enable_if<
std::is_convertible<First1, First>::value &&
std::is_convertible<Second1, Second>::value,
int>::type = 0>
Pair(First1&& first, Second1&& second)
:first(std::forward<First1>(first)),
second(std::forward<Second1>(second))
{ }
// converting constructors from convertible pairs
template<typename First1, typename Second1>
Pair(const Pair<First1, Second1>& other)
:first(other.first), second(other.second)
{ }
// moving convertible pairs into us
template<typename First1, typename Second1>
Pair(Pair<First1, Second1>&& other)
:first(std::move(other.first)),
second(std::move(other.second))
{ }
// public, hence not prefixed by _
First first;
Second second;
};
}
namespace litb {
template<typename T>
T min(const T& a, const T& b) {
return a < b ? a : b;
}
template<typename T>
T max(const T& a, const T& b) {
return a > b ? a : b;
}
// add the cv-qualifiers of MaybeCv to T
template<typename T, typename MaybeCv>
struct TransferCv {
typedef T type;
};
template<typename T, typename MaybeCv>
struct TransferCv<T, const MaybeCv> {
typedef const T type;
};
template<typename T, typename MaybeCv>
struct TransferCv<T, volatile MaybeCv> {
typedef volatile T type;
};
template<typename T, typename MaybeCv>
struct TransferCv<T, const volatile MaybeCv> {
typedef const volatile T type;
};
}
#include <type_traits>
#include <cstdint>
namespace litb {
inline uint32_t hash(int n) { return n; }
inline uint32_t hash(unsigned n) { return n; }
inline uint32_t hash(uint32_t n, std::true_type /* 32bit */) {
return n;
}
inline uint32_t hash(uint64_t n, std::false_type /* 64bit */) {
uint32_t n32 = (uint32_t)n;
return n32 ^ (uint32_t)(n >> 32);
}
inline uint32_t hash(unsigned long n) {
return hash(n, std::integral_constant<bool, sizeof(unsigned long) == sizeof(uint32_t)>());
}
inline uint32_t hash(long n) {
return hash((unsigned long)n);
}
inline uint32_t hash(unsigned long long n) {
return hash(n, std::false_type());
}
inline uint32_t hash(long long n) {
return hash((unsigned long long)n);
}
}
#ifndef LITB_VECTOR_H
#define LITB_VECTOR_H
#include <new>
#include <type_traits>
namespace litb {
// The functionality of Vector that does not depend on the "SmallSize"
// template parameter.
//
template<typename T>
class VectorBase {
public:
typedef T *iterator;
typedef const T *const_iterator;
typedef T value_type;
public:
~VectorBase() {
clear();
if(usesHeap()) {
operator delete(_data);
}
}
public:
iterator begin() { return _data; }
iterator end() { return _endData; }
const_iterator begin() const { return _data; }
const_iterator end() const { return _endData; }
public:
// returns a reference to the element at the given index
T &at(int index) {
return _data[index];
}
const T& at(int index) const {
return _data[index];
}
T &operator[](int index) {
return at(index);
}
const T& operator[](int index) const {
return at(index);
}
// appends a new item to the back of the vector
void append(const T& t) {
insert(end(), t);
}
void prepend(const T& t) {
insert(begin(), t);
}
// inserts a new item before the given iterator, and return
// an iterator to the new item
iterator insert(iterator it, const T& t) {
it = grow(it, 1);
new ((void*)it) T(t);
_endData++;
return it;
}
// assigns the given range to this vector
template<typename Iterator>
void assign(Iterator b, Iterator e) {
clear();
insert(end(), b, e);
}
// inserts the range [b, e) before iterator position "it", and return
// a pointer to the first item inserted
template<typename Iterator>
iterator insert(iterator it, Iterator b, Iterator e) {
it = grow(it, e - b);
for(; b != e; b++, it++) {
new ((void*)it) T(*b);
_endData++;
}
return it;
}
// returns true if the vector contains no elements
bool isEmpty() const {
return _data == _endData;
}
bool empty() const {
return isEmpty();
}
// resizes the vector to be of the given size afterwards, initializing all
// new elements by value initialization
void resize(int newSize) {
if(size() < newSize) {
iterator it = grow(end(), newSize - size());
for(; newSize > 0; newSize--, it++) {
new ((void*)it) T();
_endData++;
}
} else if(size() > newSize) {
erase(end() - (size() - newSize), end());
}
}
// makes the vector have size() 0, without changing the capacity
void clear() {
for(; _endData != _data; _endData--) {
_endData[-1].~T();
}
_endData = _data;
}
// erases the portion [b, e) of the vector, without changing the capacity
void erase(iterator b, iterator e) {
if(b == e) {
clear();
return;
}
for(; e != _endData; e++, b++) {
*b = *e;
}
T *dptr = _endData;
for(T *edptr = dptr - (e - b); dptr != edptr; dptr--)
dptr->~T();
_endData = dptr;
}
// returns the actual size of the vector
int size() const { return _endData - _data; }
// returns the reserved capacity of the vector that it can insert without
// reallocating
int capacity() const { return _endReserve - _data; }
// returns true if we use the heap memory currently.
bool usesHeap() const { return (const void*)_data != (this + 1); }
// reserves space for at least "space" items
void reserve(int space) {
if(space <= capacity())
return;
grow(end(), space - capacity());
}
// simply uses the assign function
VectorBase &operator=(const VectorBase& other) {
if(this == &other)
return *this;
assign(other.begin(), other.end());
return *this;
}
protected:
// VectorBase cannot be created on its own
VectorBase(T *data, T *endData)
:_data(data), _endData(data), _endReserve(endData)
{ }
private:
// reserves space for "elements" items before iterator "it".
// returns an iterator pointing at the first free slot.
iterator grow(iterator it, int elements) {
if(elements == 0)
return it;
int index = it - begin();
if((_endReserve - _endData) < elements) {
// reallocate, leaving "elements" free slots at "index"
int newCapacity = max(capacity() * 2, capacity() + elements);
T *data1 = (T*)operator new(sizeof(T) * newCapacity);
T *endData1 = data1;
T *endReserve1 = data1 + newCapacity;
for(T *data = _data; data != _endData; data++, endData1++) {
if(data - _data == index) {
endData1 += elements;
}
new ((void*)endData1) T(*data);
}
for(; _endData != _data; _endData--)
_endData[-1].~T();
if(usesHeap()) {
operator delete(_data);
}
_data = data1;
_endData= endData1;
_endReserve = endReserve1;
} else if(it != end()) {
T *endData1 = _endData, *endDataNew1 = _endData + elements;
int newlyAllocated = min(elements, end() - it);
for(; newlyAllocated > 0; newlyAllocated--, endDataNew1--, endData1--) {
new ((void*)&endDataNew1[-1]) T(endData1[-1]);
}
for(; endData1 != it; endData1--, endDataNew1--) {
endDataNew1[-1] = endData1[-1];
}
// the hole should contain unconstructed items that the caller can placement-new into
int dtorsInHole = min(elements, (end() - it));
for(iterator itHole = it; dtorsInHole > 0; dtorsInHole--, itHole++)
itHole->~T();
}
return begin() + index;
}
private:
T *_data;
T *_endData;
T *_endReserve;
};
// A vector that allocates "SmallSize" elements in its own memory and
// only if more elements are needed, reverts to heap memory.
template<typename T, int SmallSize>
class Vector : public VectorBase<T> {
union {
typename std::aligned_storage<sizeof(T), alignof(T)>::type
_aligner;
unsigned char _buffer[sizeof(T) * SmallSize];
};
public:
Vector():VectorBase<T>((T*)_buffer, (T*)_buffer + SmallSize) {}
Vector(const Vector& other):Vector() {
this->insert(this->end(), other.begin(), other.end());
}
template<int SmallSize1>
Vector(const Vector<T, SmallSize1>& other):Vector() {
this->insert(this->end(), other.begin(), other.end());
}
};
// A vector similar to std::vector.
template<typename T>
class DynamicVector : public VectorBase<T> {
// this padding. VectorBase can then determine that its pointers do never
// point to the in-object buffer (as it cound otherise happen if VectorBase and its
// malloced buffer would be adjacent in memory).
char padding;
public:
DynamicVector():VectorBase<T>(nullptr, nullptr) {}
DynamicVector(const DynamicVector& other):DynamicVector() {
this->insert(this->end(), other.begin(), other.end());
}
};
}
#endif
#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 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
#include <iostream>
struct A {
A():x(-1) {
std::cout << "A()" << std::endl;
}
A(int x):x(x) {
std::cout << "A(" << x << ")" << std::endl;
}
~A() {
std::cout << "~A(" << x << ")" << std::endl;
}
A(A const& other):x(other.x) {
std::cout << "A(A const&)(" << other.x << ")" << std::endl;
}
int x;
};
int main() {
litb::HashTable<int, A> hashTable;
for(int i = 0; i < 100; i++)
hashTable[i] = A(i * 2);
std::cout << "\n";
for(auto it = hashTable.begin(), ite = hashTable.end(); it != ite; ++it) {
std::cout << "> (" << it->first << ") -> (" << it->second.x << ")\n";
}
}