initial hashtable/map implementation

master
Daniel Kolesa 2015-06-14 02:46:46 +01:00
parent 850d20093a
commit 48b8ba7522
2 changed files with 268 additions and 0 deletions

View File

@ -0,0 +1,172 @@
/* Internal hash table implementation. Used as a base for set, map etc.
*
* This file is part of OctaSTD. See COPYING.md for futher information.
*/
#ifndef OCTA_INTERNAL_HASHTABLE_H
#define OCTA_INTERNAL_HASHTABLE_H
#include <string.h>
#include "octa/types.h"
#include "octa/utility.h"
#include "octa/memory.h"
namespace octa {
namespace detail {
template<
typename B, /* contains methods specific to each ht type */
typename E, /* element type */
typename K, /* key type */
typename T, /* value type */
typename H, /* hash func */
typename C, /* equality check */
typename A /* allocator */
> struct Hashtable {
static constexpr octa::Size CHUNKSIZE = 64;
struct Chain {
E value;
Chain *next;
};
struct Chunk {
Chain chains[CHUNKSIZE];
Chunk *next;
};
octa::Size p_size;
octa::Size p_len;
Chunk *p_chunks;
Chain *p_unused;
using CPA = octa::AllocatorRebind<A, Chain *>;
using CHA = octa::AllocatorRebind<A, Chunk>;
using AllocPair = octa::detail::CompressedPair<CPA, CHA>;
using FuncPair = octa::detail::CompressedPair<H, C>;
using FAPair = octa::detail::CompressedPair<AllocPair, FuncPair>;
using DataPair = octa::detail::CompressedPair<Chain **, FAPair>;
DataPair p_data;
const H &get_hash() const { return p_data.second().second().first(); }
const C &get_eq() const { return p_data.second().second().second(); }
CPA &get_cpalloc() { return p_data.second().first().first(); }
CHA &get_challoc() { return p_data.second().first().second(); }
Hashtable(octa::Size size, const H &hf, const C &eqf, const A &alloc):
p_size(size), p_len(0), p_chunks(nullptr), p_unused(nullptr),
p_data(nullptr, FAPair(AllocPair(alloc, alloc), FuncPair(hf, eqf))) {
p_data.first() = octa::allocator_allocate(get_cpalloc(), size);
memset(p_data.first(), 0, size * sizeof(Chain *));
}
~Hashtable() {
octa::allocator_deallocate(get_cpalloc(), p_data.first(), p_size);
delete_chunks();
}
Chain *insert(octa::Size h) {
if (!p_unused) {
Chunk *chunk = octa::allocator_allocate(get_challoc(), 1);
octa::allocator_construct(get_challoc(), chunk);
chunk->next = p_chunks;
p_chunks = chunk;
for (Size i = 0; i < (CHUNKSIZE - 1); ++i)
chunk->chains[i].next = &chunk->chains[i + 1];
chunk->chains[CHUNKSIZE - 1].next = p_unused;
p_unused = chunk->chains;
}
Chain *c = p_unused;
p_unused = p_unused->next;
c->next = p_data.first()[h];
p_data.first()[h] = c;
++p_len;
return c;
}
template<typename U>
T &insert(octa::Size h, const U &key) {
Chain *c = insert(h);
B::set_key(c->value, key);
return B::get_data(c->value);
}
template<typename U>
bool remove(const U &key) {
octa::Size h = get_hash()(key) & (p_size - 1);
Chain *c = p_data.first()[h];
Chain **p = &c;
while (c) {
if (get_eq()(key, B::get_key(c->value))) {
*p = c->next;
c->value.~E();
new (&c->value) E;
c->next = p_unused;
p_unused = c;
--p_len;
return true;
}
c = c->next;
p = &c;
}
return false;
}
void delete_chunks() {
for (Chunk *nc; p_chunks; p_chunks = nc) {
nc = p_chunks->next;
octa::allocator_destroy(get_challoc(), p_chunks);
octa::allocator_deallocate(get_challoc(), p_chunks, 1);
}
}
void clear() {
if (!p_len) return;
memset(p_data.first(), 0, p_size * sizeof(Chain *));
p_len = 0;
p_unused = nullptr;
delete_chunks();
}
template<typename U>
T *access_base(const U &key, octa::Size &h) const {
h = get_hash()(key) & (p_size - 1);
for (Chain *c = p_data.first()[h]; c; c = c->next) {
if (get_eq()(key, B::get_key(c->value)))
return &B::get_data(c->value);
}
return NULL;
}
template<typename U>
T *access(const U &key) const {
octa::Size h;
return access_base(key, h);
}
template<typename U, typename V>
T &access(const U &key, const V &val) {
octa::Size h;
T *found = access_base(key, h);
if (found) return *found;
return (insert(h, key) = val);
}
void swap(Hashtable &h) {
octa::swap(p_size, h.p_size);
octa::swap(p_len, h.p_len);
octa::swap(p_chunks, h.p_chunks);
octa::swap(p_unused, h.p_unused);
octa::swap(p_data, h.p_data);
}
};
} /* namespace detail */
} /* namespace octa */
#endif

96
octa/map.h 100644
View File

@ -0,0 +1,96 @@
/* Associative array for OctaSTD. Implemented as a hash table.
*
* This file is part of OctaSTD. See COPYING.md for futher information.
*/
#ifndef OCTA_MAP_H
#define OCTA_MAP_H
#include "octa/types.h"
#include "octa/utility.h"
#include "octa/memory.h"
#include "octa/functional.h"
#include "octa/internal/hashtable.h"
namespace octa {
namespace detail {
template<typename K, typename T> struct MapBase {
typedef octa::Pair<const K, T> Element;
static inline const K &get_key(Element &e) {
return e.first;
}
static inline T &get_data(Element &e) {
return e.second;
}
template<typename U>
static inline void set_key(Element &e, const U &key) {
e.first.~K();
new ((K *)&e.first) K(key);
}
};
}
template<
typename K, typename T,
typename H = octa::ToHash<K>,
typename C = octa::Equal<K>,
typename A = octa::Allocator<octa::Pair<const K, T>>
> struct Map {
private:
using Base = octa::detail::Hashtable<
octa::detail::MapBase<K, T>, octa::Pair<const K, T>, K, T, H, C, A
>;
Base p_table;
public:
using Key = K;
using Mapped = T;
using Size = octa::Size;
using Difference = octa::Ptrdiff;
using Hasher = H;
using KeyEqual = C;
using Value = octa::Pair<const K, T>;
using Reference = Value &;
using Pointer = octa::AllocatorPointer<A>;
using ConstPointer = octa::AllocatorConstPointer<A>;
using Allocator = A;
Map(octa::Size size = 1 << 10, const H &hf = H(), const C &eqf = C(),
const A &alloc = A()): p_table(size, hf, eqf, alloc) {}
bool empty() const { return p_table.p_len == 0; }
octa::Size size() const { return p_table.p_len; }
octa::Size bucket_count() const { return p_table.p_size; }
void clear() { p_table.clear(); }
A get_allocator() const {
return p_table.get_challoc();
}
T &at(const K &key) {
return *p_table.access(key);
}
const T &at(const K &key) const {
return *p_table.access(key);
}
T &operator[](const K &key) {
octa::Size h;
T *v = p_table.access_base(key, h);
if (v) return *v;
return p_table.insert(h, key);
}
void swap(Map &v) {
octa::swap(p_table, v.p_table);
}
};
} /* namespace detail */
#endif