remove custom hashtable; no longer necessary

This commit is contained in:
q66 2017-01-31 19:31:55 +01:00
parent 1f83e8c1b0
commit e0072773e1
4 changed files with 0 additions and 1368 deletions

View file

@ -1,754 +0,0 @@
/* 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 OSTD_INTERNAL_HASHTABLE_HH
#define OSTD_INTERNAL_HASHTABLE_HH
#include <string.h>
#include <math.h>
#include "ostd/types.hh"
#include "ostd/utility.hh"
#include "ostd/memory.hh"
#include "ostd/range.hh"
#include "ostd/initializer_list.hh"
namespace ostd {
namespace detail {
template<typename T>
struct HashChain {
HashChain<T> *prev;
HashChain<T> *next;
T value;
};
template<typename R>
static inline size_t estimate_hrsize(
const R &range, EnableIf<IsFiniteRandomAccessRange<R>, bool> = true
) {
return range.size();
}
template<typename R>
static inline size_t estimate_hrsize(
const R &, EnableIf<!IsFiniteRandomAccessRange<R>, bool> = true
) {
/* we have no idea how big the range actually is */
return 16;
}
}
template<typename T>
struct HashRange: InputRange<HashRange<T>, ForwardRangeTag, T> {
private:
template<typename U>
friend struct HashRange;
using Chain = detail::HashChain<T>;
Chain *p_node;
public:
HashRange(): p_node(nullptr) {}
HashRange(const HashRange &v): p_node(v.p_node) {}
HashRange(Chain *node): p_node(node) {}
template<typename U>
HashRange(
const HashRange<U> &v, EnableIf<
IsSame<RemoveCv<T>, RemoveCv<U>> && IsConvertible<U *, T *>, bool
> = true
):
p_node(const_cast<Chain *>(v.p_node))
{}
HashRange &operator=(const HashRange &v) {
p_node = v.p_node;
return *this;
}
bool empty() const { return !p_node; }
bool pop_front() {
if (!p_node) {
return false;
}
p_node = p_node->next;
return true;
}
bool equals_front(const HashRange &v) const {
return p_node == v.p_node;
}
T &front() const { return p_node->value; }
};
template<typename T>
struct BucketRange: InputRange<BucketRange<T>, ForwardRangeTag, T> {
private:
template<typename U>
friend struct BucketRange;
using Chain = detail::HashChain<T>;
Chain *p_node, *p_end;
public:
BucketRange(): p_node(nullptr) {}
BucketRange(Chain *node, Chain *end): p_node(node), p_end(end) {}
BucketRange(const BucketRange &v): p_node(v.p_node), p_end(v.p_end) {}
template<typename U>
BucketRange(
const BucketRange<U> &v, EnableIf<
IsSame<RemoveCv<T>, RemoveCv<U>> && IsConvertible<U *, T *>, bool
> = true
):
p_node(const_cast<Chain *>(v.p_node)),
p_end(const_cast<Chain *>(v.p_end))
{}
BucketRange &operator=(const BucketRange &v) {
p_node = v.p_node;
p_end = v.p_end;
return *this;
}
bool empty() const { return p_node == p_end; }
bool pop_front() {
if (p_node == p_end) {
return false;
}
p_node = p_node->next;
return true;
}
bool equals_front(const BucketRange &v) const {
return p_node == v.p_node;
}
T &front() const { return p_node->value; }
};
namespace detail {
/* Use template metaprogramming to figure out a correct number
* of elements to use per chunk for proper cache alignment
* (i.e. sizeof(Chunk) % CacheLineSize == 0).
*
* If this is not possible, use the upper bound and pad the
* structure with some extra bytes.
*/
static constexpr size_t CacheLineSize = 64;
static constexpr size_t ChunkLowerBound = 32;
static constexpr size_t ChunkUpperBound = 128;
template<typename E, size_t N> constexpr size_t HashChainAlign =
(((sizeof(HashChain<E>[N]) + sizeof(void *)) % CacheLineSize) == 0)
? N : HashChainAlign<E, N + 1>;
template<typename E>
constexpr size_t HashChainAlign<E, ChunkUpperBound> = ChunkUpperBound;
template<size_t N, bool B>
struct HashChainPad;
template<size_t N>
struct HashChainPad<N, true> {};
template<size_t N>
struct HashChainPad<N, false> {
byte pad[CacheLineSize - (N % CacheLineSize)];
};
template<size_t N>
struct HashPad: HashChainPad<N, N % CacheLineSize == 0> {};
template<
typename E, size_t V = HashChainAlign<E, ChunkLowerBound>,
bool P = (V == ChunkUpperBound)
>
struct HashChunk;
template<typename E, size_t V>
struct HashChunk<E, V, false> {
static constexpr size_t num = V;
HashChain<E> chains[num];
HashChunk *next;
};
template<typename E, size_t V>
struct HashChunk<E, V, true>: HashPad<
sizeof(HashChain<E>[V]) + sizeof(void *)
> {
static constexpr size_t num = V;
HashChain<E> chains[num];
HashChunk *next;
};
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 */
bool Multihash
>
struct Hashtable {
private:
using Chain = HashChain<E>;
using Chunk = HashChunk<E>;
size_t p_size;
size_t p_len;
Chunk *p_chunks;
Chain *p_unused;
using CPA = AllocatorRebind<A, Chain *>;
using CHA = AllocatorRebind<A, Chunk>;
using CoreAllocPair = detail::CompressedPair<CPA, CHA>;
using AllocPair = detail::CompressedPair<A, CoreAllocPair>;
using FuncPair = detail::CompressedPair<H, C>;
using FAPair = detail::CompressedPair<AllocPair, FuncPair>;
using DataPair = detail::CompressedPair<Chain **, FAPair>;
using Range = HashRange<E>;
using ConstRange = HashRange<const E>;
using LocalRange = BucketRange<E>;
using ConstLocalRange = BucketRange<const E>;
DataPair p_data;
float p_maxlf;
void clear_buckets() {
memset(p_data.first(), 0, (p_size + 1) * sizeof(Chain *));
}
void init_buckets() {
p_data.first() = allocator_allocate(get_cpalloc(), p_size + 1);
clear_buckets();
}
Chain *find(const K &key, size_t &h) const {
if (!p_size) {
return nullptr;
}
h = bucket(key);
Chain **cp = p_data.first();
for (Chain *c = cp[h], *e = cp[h + 1]; c != e; c = c->next) {
if (get_eq()(key, B::get_key(c->value))) {
return c;
}
}
return nullptr;
}
Chain *insert_node(size_t h, Chain *c) {
Chain **cp = p_data.first();
Chain *it = cp[h + 1];
c->next = it;
if (it) {
c->prev = it->prev;
it->prev = c;
if (c->prev) {
c->prev->next = c;
}
} else {
size_t nb = h;
while (nb && !cp[nb]) {
--nb;
}
Chain *prev = cp[nb];
while (prev && prev->next) {
prev = prev->next;
}
c->prev = prev;
if (prev) {
prev->next = c;
}
}
for (; it == cp[h]; --h) {
cp[h] = c;
if (!h) {
break;
}
}
return c;
}
Chain *request_node() {
if (!p_unused) {
Chunk *chunk = allocator_allocate(get_challoc(), 1);
allocator_construct(get_challoc(), chunk);
chunk->next = p_chunks;
p_chunks = chunk;
for (size_t i = 0; i < (Chunk::num - 1); ++i) {
chunk->chains[i].next = &chunk->chains[i + 1];
}
chunk->chains[Chunk::num - 1].next = p_unused;
p_unused = chunk->chains;
}
++p_len;
Chain *c = p_unused;
p_unused = p_unused->next;
return c;
}
Chain *insert(size_t h) {
return insert_node(h, request_node());
}
void delete_chunks(Chunk *chunks) {
for (Chunk *nc; chunks; chunks = nc) {
nc = chunks->next;
allocator_destroy(get_challoc(), chunks);
allocator_deallocate(get_challoc(), chunks, 1);
}
}
void rehash_ahead(size_t n) {
if (!bucket_count()) {
reserve(n);
} else if ((float(size() + n) / bucket_count()) > max_load_factor()) {
rehash(size_t((size() + 1) / max_load_factor()) * 2);
}
}
protected:
template<typename U>
T &insert(size_t h, U &&key) {
Chain *c = insert(h);
B::set_key(c->value, std::forward<U>(key), get_alloc());
return B::get_data(c->value);
}
T &access_or_insert(const K &key) {
size_t h = 0;
Chain *c = find(key, h);
if (c) {
return B::get_data(c->value);
}
rehash_ahead(1);
return insert(bucket(key), key);
}
T &access_or_insert(K &&key) {
size_t h = 0;
Chain *c = find(key, h);
if (c) {
return B::get_data(c->value);
}
rehash_ahead(1);
return insert(bucket(key), std::move(key));
}
T *access(const K &key) const {
size_t h;
Chain *c = find(key, h);
if (c) {
return &B::get_data(c->value);
}
return nullptr;
}
template<typename R>
void assign_range(R range) {
clear();
reserve_at_least(detail::estimate_hrsize(range));
for (; !range.empty(); range.pop_front()) {
emplace(range.front());
}
rehash_up();
}
void assign_init(std::initializer_list<E> il) {
const E *beg = il.begin(), *end = il.end();
clear();
reserve_at_least(end - beg);
while (beg != end) {
emplace(*beg++);
}
}
const H &get_hash() const { return p_data.second().second().first(); }
const C &get_eq() const { return p_data.second().second().second(); }
A &get_alloc() { return p_data.second().first().first(); }
const A &get_alloc() const { return p_data.second().first().first(); }
CPA &get_cpalloc() { return p_data.second().first().second().first(); }
const CPA &get_cpalloc() const {
return p_data.second().first().second().first();
}
CHA &get_challoc() { return p_data.second().first().second().second(); }
const CHA &get_challoc() const {
return p_data.second().first().second().second();
}
Hashtable(size_t 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, CoreAllocPair(alloc, alloc)),
FuncPair(hf, eqf))),
p_maxlf(1.0f) {
if (!size) {
return;
}
init_buckets();
}
Hashtable(const Hashtable &ht, const A &alloc): p_size(ht.p_size),
p_len(0), p_chunks(nullptr), p_unused(nullptr),
p_data(nullptr, FAPair(AllocPair(alloc, CoreAllocPair(alloc, alloc)),
FuncPair(ht.get_hash(), ht.get_eq()))),
p_maxlf(ht.p_maxlf) {
if (!p_size) {
return;
}
init_buckets();
Chain **och = ht.p_data.first();
for (Chain *oc = *och; oc; oc = oc->next) {
size_t h = bucket(B::get_key(oc->value));
Chain *nc = insert(h);
allocator_destroy(get_alloc(), &nc->value);
allocator_construct(get_alloc(), &nc->value, oc->value);
}
}
Hashtable(Hashtable &&ht): p_size(ht.p_size), p_len(ht.p_len),
p_chunks(ht.p_chunks), p_unused(ht.p_unused),
p_data(std::move(ht.p_data)), p_maxlf(ht.p_maxlf) {
ht.p_size = ht.p_len = 0;
ht.p_chunks = nullptr;
ht.p_unused = nullptr;
ht.p_data.first() = nullptr;
}
Hashtable(Hashtable &&ht, const A &alloc): p_data(nullptr,
FAPair(AllocPair(alloc, CoreAllocPair(alloc, alloc)),
FuncPair(ht.get_hash(), ht.get_eq()))) {
p_size = ht.p_size;
if (alloc == ht.get_alloc()) {
p_len = ht.p_len;
p_chunks = ht.p_chunks;
p_unused = ht.p_unused;
p_data.first() = ht.p_data.first();
p_maxlf = ht.p_maxlf;
ht.p_size = ht.p_len = 0;
ht.p_chunks = nullptr;
ht.p_unused = nullptr;
ht.p_data.first() = nullptr;
return;
}
p_len = 0;
p_chunks = nullptr;
p_unused = nullptr;
init_buckets();
Chain **och = ht.p_data.first();
for (Chain *oc = *och; oc; oc = oc->next) {
size_t h = bucket(B::get_key(oc->value));
Chain *nc = insert(h);
B::swap_elem(oc->value, nc->value);
}
}
Hashtable &operator=(const Hashtable &ht) {
clear();
if (AllocatorPropagateOnContainerCopyAssignment<A>) {
if ((get_cpalloc() != ht.get_cpalloc()) && p_size) {
allocator_deallocate(
get_cpalloc(), p_data.first(), p_size + 1
);
init_buckets();
}
get_alloc() = ht.get_alloc();
get_cpalloc() = ht.get_cpalloc();
get_challoc() = ht.get_challoc();
}
for (ConstRange range = ht.iter(); !range.empty(); range.pop_front()) {
emplace(range.front());
}
return *this;
}
Hashtable &operator=(Hashtable &&ht) {
using std::swap;
clear();
swap(p_size, ht.p_size);
swap(p_len, ht.p_len);
swap(p_chunks, ht.p_chunks);
swap(p_unused, ht.p_unused);
swap(p_data.first(), ht.p_data.first());
swap(p_data.second().second(), ht.p_data.second().second());
if (AllocatorPropagateOnContainerMoveAssignment<A>) {
swap(p_data.second().first(), ht.p_data.second().first());
}
return *this;
}
void rehash_up() {
if (load_factor() <= max_load_factor()) {
return;
}
rehash(size_t(size() / max_load_factor()) * 2);
}
void reserve_at_least(size_t count) {
size_t nc = size_t(ceil(count / max_load_factor()));
if (p_size > nc) {
return;
}
rehash(nc);
}
void swap(Hashtable &ht) {
using std::swap;
swap(p_size, ht.p_size);
swap(p_len, ht.p_len);
swap(p_chunks, ht.p_chunks);
swap(p_unused, ht.p_unused);
swap(p_data.first(), ht.p_data.first());
swap(p_data.second().second(), ht.p_data.second().second());
if (AllocatorPropagateOnContainerSwap<A>) {
swap(p_data.second().first(), ht.p_data.second().first());
}
}
public:
~Hashtable() {
if (p_size) {
allocator_deallocate(get_cpalloc(), p_data.first(), p_size + 1);
}
delete_chunks(p_chunks);
}
A get_allocator() const {
return get_alloc();
}
void clear() {
if (!p_len) {
return;
}
clear_buckets();
p_len = 0;
p_unused = nullptr;
delete_chunks(p_chunks);
}
bool empty() const { return p_len == 0; }
size_t size() const { return p_len; }
size_t max_size() const { return size_t(~0) / sizeof(E); }
size_t bucket_count() const { return p_size; }
size_t max_bucket_count() const { return size_t(~0) / sizeof(Chain); }
size_t bucket(const K &key) const {
return get_hash()(key) & (p_size - 1);
}
size_t bucket_size(size_t n) const {
size_t ret = 0;
if (n >= p_size) {
return ret;
}
Chain **cp = p_data.first();
for (Chain *c = cp[n], *e = cp[n + 1]; c != e; c = c->next) {
++ret;
}
return ret;
}
template<typename ...Args>
std::pair<Range, bool> emplace(Args &&...args) {
rehash_ahead(1);
E elem(std::forward<Args>(args)...);
if (Multihash) {
/* multihash: always insert
* gotta make sure that equal keys always come after
* each other (this is then used by other APIs)
*/
size_t h = bucket(B::get_key(elem));
Chain *ch = insert(h);
B::swap_elem(ch->value, elem);
return std::make_pair(Range(ch), true);
}
size_t h = bucket(B::get_key(elem));
Chain *found = nullptr;
bool ins = true;
Chain **cp = p_data.first();
for (Chain *c = cp[h], *e = cp[h + 1]; c != e; c = c->next) {
if (get_eq()(B::get_key(elem), B::get_key(c->value))) {
found = c;
ins = false;
break;
}
}
if (!found) {
found = insert(h);
B::swap_elem(found->value, elem);
}
return std::make_pair(Range(found), ins);
}
size_t erase(const K &key) {
size_t h = 0;
Chain *c = find(key, h);
if (!c) {
return 0;
}
Chain **cp = p_data.first();
size_t olen = p_len;
for (Chain *e = cp[h + 1]; c != e; c = c->next) {
if (!get_eq()(key, B::get_key(c->value))) {
break;
}
--p_len;
size_t hh = h;
Chain *next = c->next;
for (; cp[hh] == c; --hh) {
cp[hh] = next;
if (!hh) {
break;
}
}
if (c->prev) {
c->prev->next = next;
}
if (next) {
next->prev = c->prev;
}
c->next = p_unused;
c->prev = nullptr;
p_unused = c;
allocator_destroy(get_alloc(), &c->value);
allocator_construct(get_alloc(), &c->value);
if (!Multihash) {
return 1;
}
}
return olen - p_len;
}
size_t count(const K &key) {
size_t h = 0;
Chain *c = find(key, h);
if (!c) {
return 0;
}
size_t ret = 1;
if (!Multihash) {
return ret;
}
for (c = c->next; c; c = c->next) {
if (get_eq()(key, B::get_key(c->value))) {
++ret;
}
}
return ret;
}
Range find(const K &key) {
size_t h = 0;
return Range(find(key, h));
}
ConstRange find(const K &key) const {
size_t h = 0;
return ConstRange(reinterpret_cast<detail::HashChain<const E> *>(
find(key, h)
));
}
float load_factor() const { return float(p_len) / p_size; }
float max_load_factor() const { return p_maxlf; }
void max_load_factor(float lf) { p_maxlf = lf; }
void rehash(size_t count) {
size_t fbcount = size_t(p_len / max_load_factor());
if (fbcount > count) {
count = fbcount;
}
Chain **och = p_data.first();
size_t osize = p_size;
p_size = count;
init_buckets();
Chain *p = och ? *och : nullptr;
while (p) {
Chain *pp = p->next;
size_t h = bucket(B::get_key(p->value));
p->prev = p->next = nullptr;
insert_node(h, p);
p = pp;
}
if (och && osize) {
allocator_deallocate(get_cpalloc(), och, osize + 1);
}
}
void reserve(size_t count) {
rehash(size_t(ceil(count / max_load_factor())));
}
Range iter() {
if (!p_len) {
return Range();
}
return Range(*p_data.first());
}
ConstRange iter() const {
using Chain = detail::HashChain<const E>;
if (!p_len) {
return ConstRange();
}
return ConstRange(reinterpret_cast<Chain *>(*p_data.first()));
}
ConstRange citer() const {
using Chain = detail::HashChain<const E>;
if (!p_len) {
return ConstRange();
}
return ConstRange(reinterpret_cast<Chain *>(*p_data.first()));
}
LocalRange iter(size_t n) {
if (n >= p_size) {
return LocalRange();
}
return LocalRange(p_data.first()[n], p_data.first()[n + 1]);
}
ConstLocalRange iter(size_t n) const {
using Chain = detail::HashChain<const E>;
if (n >= p_size) {
return ConstLocalRange();
}
return ConstLocalRange(
reinterpret_cast<Chain *>(p_data.first()[n]),
reinterpret_cast<Chain *>(p_data.first()[n + 1])
);
}
ConstLocalRange citer(size_t n) const {
using Chain = detail::HashChain<const E>;
if (n >= p_size) {
return ConstLocalRange();
}
return ConstLocalRange(
reinterpret_cast<Chain *>(p_data.first()[n]),
reinterpret_cast<Chain *>(p_data.first()[n + 1])
);
}
};
} /* namespace detail */
} /* namespace ostd */
#endif

View file

@ -1,213 +0,0 @@
/* Keyed set for OctaSTD. Implemented as a hash table.
*
* This file is part of OctaSTD. See COPYING.md for futher information.
*/
#ifndef OSTD_KEYSET_HH
#define OSTD_KEYSET_HH
#include "ostd/types.hh"
#include "ostd/utility.hh"
#include "ostd/memory.hh"
#include "ostd/functional.hh"
#include "ostd/initializer_list.hh"
#include "ostd/type_traits.hh"
#include "ostd/internal/hashtable.hh"
namespace ostd {
namespace detail {
template<typename T>
using KeysetKeyRet = decltype(std::declval<T const &>().get_key());
template<typename T>
using KeysetKey = Decay<KeysetKeyRet<T>> const;
template<typename T, typename A> struct KeysetBase {
using Key = KeysetKey<T>;
using RetKey = Conditional<IsReference<KeysetKeyRet<T>>, Key &, Key>;
static inline RetKey get_key(T const &e) {
return e.get_key();
}
static inline T &get_data(T &e) {
return e;
}
template<typename U>
static inline void set_key(T &, U const &, A &) {}
static inline void swap_elem(T &a, T &b) {
using std::swap;
swap(a, b);
}
};
template<
typename T, typename H, typename C, typename A, bool IsMultihash
>
struct KeysetImpl: detail::Hashtable<detail::KeysetBase<T, A>,
T, KeysetKey<T>, T, H, C, A, IsMultihash
> {
private:
using Base = detail::Hashtable<detail::KeysetBase<T, A>,
T, KeysetKey<T>, T, H, C, A, IsMultihash
>;
public:
using Key = KeysetKey<T>;
using Mapped = T;
using Size = size_t;
using Difference = ptrdiff_t;
using Hasher = H;
using KeyEqual = C;
using Value = T;
using Reference = Value &;
using Pointer = AllocatorPointer<A>;
using ConstPointer = AllocatorConstPointer<A>;
using Range = HashRange<T>;
using ConstRange = HashRange<T const>;
using LocalRange = BucketRange<T>;
using ConstLocalRange = BucketRange<T const>;
using Allocator = A;
explicit KeysetImpl(
Size size, H const &hf = H(), C const &eqf = C(),
A const &alloc = A()
): Base(size, hf, eqf, alloc) {}
KeysetImpl(): KeysetImpl(0) {}
explicit KeysetImpl(A const &alloc): KeysetImpl(0, H(), C(), alloc) {}
KeysetImpl(Size size, A const &alloc):
KeysetImpl(size, H(), C(), alloc)
{}
KeysetImpl(Size size, H const &hf, A const &alloc):
KeysetImpl(size, hf, C(), alloc)
{}
KeysetImpl(KeysetImpl const &m):
Base(m, allocator_container_copy(m.get_alloc()))
{}
KeysetImpl(KeysetImpl const &m, A const &alloc): Base(m, alloc) {}
KeysetImpl(KeysetImpl &&m): Base(std::move(m)) {}
KeysetImpl(KeysetImpl &&m, A const &alloc): Base(std::move(m), alloc) {}
template<typename R, typename = EnableIf<
IsInputRange<R> && IsConvertible<RangeReference<R>, Value>
>>
KeysetImpl(
R range, Size size = 0, H const &hf = H(),
C const &eqf = C(), A const &alloc = A()
):
Base(size ? size : detail::estimate_hrsize(range), hf, eqf, alloc)
{
for (; !range.empty(); range.pop_front()) {
Base::emplace(range.front());
}
Base::rehash_up();
}
template<typename R>
KeysetImpl(R range, Size size, A const &alloc):
KeysetImpl(range, size, H(), C(), alloc)
{}
template<typename R>
KeysetImpl(R range, Size size, H const &hf, A const &alloc):
KeysetImpl(range, size, hf, C(), alloc)
{}
KeysetImpl(
std::initializer_list<Value> init, Size size = 0,
H const &hf = H(), C const &eqf = C(), A const &alloc = A()
):
KeysetImpl(iter(init), size, hf, eqf, alloc)
{}
KeysetImpl(std::initializer_list<Value> init, Size size, A const &alloc):
KeysetImpl(iter(init), size, H(), C(), alloc)
{}
KeysetImpl(
std::initializer_list<Value> init, Size size, H const &hf, A const &alloc
):
KeysetImpl(iter(init), size, hf, C(), alloc)
{}
KeysetImpl &operator=(KeysetImpl const &m) {
Base::operator=(m);
return *this;
}
KeysetImpl &operator=(KeysetImpl &&m) {
Base::operator=(std::move(m));
return *this;
}
template<typename R, typename = EnableIf<
IsInputRange<R> && IsConvertible<RangeReference<R>, Value>
>>
KeysetImpl &operator=(R range) {
Base::assign_range(range);
return *this;
}
KeysetImpl &operator=(std::initializer_list<Value> il) {
Base::assign_init(il);
return *this;
}
T *at(Key const &key) {
static_assert(!IsMultihash, "at() only allowed on regular keysets");
return Base::access(key);
}
T const *at(Key const &key) const {
static_assert(!IsMultihash, "at() only allowed on regular keysets");
return Base::access(key);
}
T &operator[](Key const &key) {
static_assert(!IsMultihash, "operator[] only allowed on regular keysets");
return Base::access_or_insert(key);
}
T &operator[](Key &&key) {
static_assert(!IsMultihash, "operator[] only allowed on regular keysets");
return Base::access_or_insert(std::move(key));
}
void swap(KeysetImpl &v) {
Base::swap(v);
}
};
}
template<
typename T,
typename H = std::hash<detail::KeysetKey<T>>,
typename C = EqualWithCstr<detail::KeysetKey<T>>,
typename A = Allocator<T>
>
using Keyset = detail::KeysetImpl<T, H, C, A, false>;
template<typename T, typename H, typename C, typename A>
inline void swap(Keyset<T, H, C, A> &a, Keyset<T, H, C, A> &b) {
a.swap(b);
}
template<
typename T,
typename H = std::hash<detail::KeysetKey<T>>,
typename C = EqualWithCstr<detail::KeysetKey<T>>,
typename A = Allocator<T>
>
using Multikeyset = detail::KeysetImpl<T, H, C, A, true>;
template<typename T, typename H, typename C, typename A>
inline void swap(Multikeyset<T, H, C, A> &a, Multikeyset<T, H, C, A> &b) {
a.swap(b);
}
} /* namespace ostd */
#endif

View file

@ -1,215 +0,0 @@
/* Associative array for OctaSTD. Implemented as a hash table.
*
* This file is part of OctaSTD. See COPYING.md for futher information.
*/
#ifndef OSTD_MAP_HH
#define OSTD_MAP_HH
#include <utility>
#include "ostd/types.hh"
#include "ostd/utility.hh"
#include "ostd/memory.hh"
#include "ostd/functional.hh"
#include "ostd/initializer_list.hh"
#include "ostd/internal/hashtable.hh"
namespace ostd {
namespace detail {
template<typename K, typename T, typename A> struct MapBase {
using Element = std::pair<K const, T>;
static inline K const &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, U &&key, A &alloc) {
allocator_destroy(alloc, &e);
allocator_construct(alloc, &e, std::forward<U>(key), std::move(T()));
}
static inline void swap_elem(Element &a, Element &b) {
using std::swap;
swap(const_cast<K &>(a.first), const_cast<K &>(b.first));
swap(a.second, b.second);
}
};
template<
typename K, typename T, typename H,
typename C, typename A, bool IsMultihash
>
struct MapImpl: detail::Hashtable<detail::MapBase<K, T, A>,
std::pair<K const, T>, K, T, H, C, A, IsMultihash
> {
private:
using Base = detail::Hashtable<detail::MapBase<K, T, A>,
std::pair<K const, T>, K, T, H, C, A, IsMultihash
>;
public:
using Key = K;
using Mapped = T;
using Size = size_t;
using Difference = ptrdiff_t;
using Hasher = H;
using KeyEqual = C;
using Value = std::pair<K const, T>;
using Reference = Value &;
using Pointer = AllocatorPointer<A>;
using ConstPointer = AllocatorConstPointer<A>;
using Range = HashRange<std::pair<K const, T>>;
using ConstRange = HashRange<std::pair<K const, T> const>;
using LocalRange = BucketRange<std::pair<K const, T>>;
using ConstLocalRange = BucketRange<std::pair<K const, T> const>;
using Allocator = A;
explicit MapImpl(
Size size, H const &hf = H(),
C const &eqf = C(), A const &alloc = A()
):
Base(size, hf, eqf, alloc)
{}
MapImpl(): MapImpl(0) {}
explicit MapImpl(A const &alloc): MapImpl(0, H(), C(), alloc) {}
MapImpl(Size size, A const &alloc):
MapImpl(size, H(), C(), alloc)
{}
MapImpl(Size size, H const &hf, A const &alloc):
MapImpl(size, hf, C(), alloc)
{}
MapImpl(MapImpl const &m):
Base(m, allocator_container_copy(m.get_alloc()))
{}
MapImpl(MapImpl const &m, A const &alloc): Base(m, alloc) {}
MapImpl(MapImpl &&m): Base(std::move(m)) {}
MapImpl(MapImpl &&m, A const &alloc): Base(std::move(m), alloc) {}
template<typename R, typename = EnableIf<
IsInputRange<R> && IsConvertible<RangeReference<R>, Value>
>>
MapImpl(
R range, Size size = 0, H const &hf = H(),
C const &eqf = C(), A const &alloc = A()
):
Base(size ? size : detail::estimate_hrsize(range), hf, eqf, alloc)
{
for (; !range.empty(); range.pop_front()) {
Base::emplace(range.front());
}
Base::rehash_up();
}
template<typename R>
MapImpl(R range, Size size, A const &alloc):
MapImpl(range, size, H(), C(), alloc)
{}
template<typename R>
MapImpl(R range, Size size, H const &hf, A const &alloc):
MapImpl(range, size, hf, C(), alloc)
{}
MapImpl(
std::initializer_list<Value> init, Size size = 0,
H const &hf = H(), C const &eqf = C(), A const &alloc = A()
):
MapImpl(iter(init), size, hf, eqf, alloc)
{}
MapImpl(std::initializer_list<Value> init, Size size, A const &alloc):
MapImpl(iter(init), size, H(), C(), alloc)
{}
MapImpl(
std::initializer_list<Value> init, Size size, H const &hf, A const &alloc
):
MapImpl(iter(init), size, hf, C(), alloc)
{}
MapImpl &operator=(MapImpl const &m) {
Base::operator=(m);
return *this;
}
MapImpl &operator=(MapImpl &&m) {
Base::operator=(std::move(m));
return *this;
}
template<typename R, typename = EnableIf<
IsInputRange<R> && IsConvertible<RangeReference<R>, Value>
>>
MapImpl &operator=(R range) {
Base::assign_range(range);
return *this;
}
MapImpl &operator=(std::initializer_list<Value> il) {
Base::assign_init(il);
return *this;
}
T *at(K const &key) {
static_assert(!IsMultihash, "at() only allowed on regular maps");
return Base::access(key);
}
T const *at(K const &key) const {
static_assert(!IsMultihash, "at() only allowed on regular maps");
return Base::access(key);
}
T &operator[](K const &key) {
static_assert(!IsMultihash, "operator[] only allowed on regular maps");
return Base::access_or_insert(key);
}
T &operator[](K &&key) {
static_assert(!IsMultihash, "operator[] only allowed on regular maps");
return Base::access_or_insert(std::move(key));
}
void swap(MapImpl &v) {
Base::swap(v);
}
};
}
template<
typename K, typename T,
typename H = std::hash<K>,
typename C = EqualWithCstr<K>,
typename A = Allocator<std::pair<K const, T>>
>
using Map = detail::MapImpl<K, T, H, C, A, false>;
template<typename K, typename T, typename H, typename C, typename A>
inline void swap(Map<K, T, H, C, A> &a, Map<K, T, H, C, A> &b) {
a.swap(b);
}
template<
typename K, typename T,
typename H = std::hash<K>,
typename C = EqualWithCstr<K>,
typename A = Allocator<std::pair<K const, T>>
>
using Multimap = detail::MapImpl<K, T, H, C, A, true>;
template<typename K, typename T, typename H, typename C, typename A>
inline void swap(Multimap<K, T, H, C, A> &a, Multimap<K, T, H, C, A> &b) {
a.swap(b);
}
} /* namespace ostd */
#endif

View file

@ -1,186 +0,0 @@
/* A set container for OctaSTD. Implemented as a hash table.
*
* This file is part of OctaSTD. See COPYING.md for futher information.
*/
#ifndef OSTD_SET_HH
#define OSTD_SET_HH
#include "ostd/types.hh"
#include "ostd/utility.hh"
#include "ostd/memory.hh"
#include "ostd/functional.hh"
#include "ostd/initializer_list.hh"
#include "ostd/internal/hashtable.hh"
namespace ostd {
namespace detail {
template<typename T, typename A>
struct SetBase {
static inline T const &get_key(T const &e) {
return e;
}
static inline T &get_data(T &e) {
return e;
}
template<typename U>
static inline void set_key(T &, U const &, A &) {}
static inline void swap_elem(T &a, T &b) {
using std::swap;
swap(a, b);
}
};
template<typename T, typename H, typename C, typename A, bool IsMultihash>
struct SetImpl: detail::Hashtable<
detail::SetBase<T, A>, T, T, T, H, C, A, IsMultihash
> {
private:
using Base = detail::Hashtable<
detail::SetBase<T, A>, T, T, T, H, C, A, IsMultihash
>;
public:
using Key = T;
using Size = size_t;
using Difference = ptrdiff_t;
using Hasher = H;
using KeyEqual = C;
using Value = T;
using Reference = Value &;
using Pointer = AllocatorPointer<A>;
using ConstPointer = AllocatorConstPointer<A>;
using Range = HashRange<T>;
using ConstRange = HashRange<T const>;
using LocalRange = BucketRange<T>;
using ConstLocalRange = BucketRange<T const>;
using Allocator = A;
explicit SetImpl(
Size size, H const &hf = H(),
C const &eqf = C(), A const &alloc = A()
):
Base(size, hf, eqf, alloc)
{}
SetImpl(): SetImpl(0) {}
explicit SetImpl(A const &alloc): SetImpl(0, H(), C(), alloc) {}
SetImpl(Size size, A const &alloc):
SetImpl(size, H(), C(), alloc)
{}
SetImpl(Size size, H const &hf, A const &alloc):
SetImpl(size, hf, C(), alloc)
{}
SetImpl(SetImpl const &m):
Base(m, allocator_container_copy(m.get_alloc()))
{}
SetImpl(SetImpl const &m, A const &alloc): Base(m, alloc) {}
SetImpl(SetImpl &&m): Base(std::move(m)) {}
SetImpl(SetImpl &&m, A const &alloc): Base(std::move(m), alloc) {}
template<typename R, typename = EnableIf<
IsInputRange<R> && IsConvertible<RangeReference<R>, Value>
>>
SetImpl(
R range, Size size = 0, H const &hf = H(),
C const &eqf = C(), A const &alloc = A()
):
Base(size ? size : detail::estimate_hrsize(range), hf, eqf, alloc)
{
for (; !range.empty(); range.pop_front()) {
Base::emplace(range.front());
}
Base::rehash_up();
}
template<typename R>
SetImpl(R range, Size size, A const &alloc):
SetImpl(range, size, H(), C(), alloc)
{}
template<typename R>
SetImpl(R range, Size size, H const &hf, A const &alloc):
SetImpl(range, size, hf, C(), alloc)
{}
SetImpl(
std::initializer_list<Value> init, Size size = 0,
H const &hf = H(), C const &eqf = C(), A const &alloc = A()
):
SetImpl(iter(init), size, hf, eqf, alloc)
{}
SetImpl(std::initializer_list<Value> init, Size size, A const &alloc):
SetImpl(iter(init), size, H(), C(), alloc)
{}
SetImpl(
std::initializer_list<Value> init, Size size, H const &hf, A const &alloc
):
SetImpl(iter(init), size, hf, C(), alloc)
{}
SetImpl &operator=(SetImpl const &m) {
Base::operator=(m);
return *this;
}
SetImpl &operator=(SetImpl &&m) {
Base::operator=(std::move(m));
return *this;
}
template<typename R, typename = EnableIf<
IsInputRange<R> && IsConvertible<RangeReference<R>, Value>
>>
SetImpl &operator=(R range) {
Base::assign_range(range);
return *this;
}
SetImpl &operator=(std::initializer_list<Value> il) {
Base::assign_init(il);
return *this;
}
void swap(SetImpl &v) {
Base::swap(v);
}
};
}
template<
typename T,
typename H = std::hash<T>,
typename C = EqualWithCstr<T>,
typename A = Allocator<T>
>
using Set = detail::SetImpl<T, H, C, A, false>;
template<typename T, typename H, typename C, typename A>
inline void swap(Set<T, H, C, A> &a, Set<T, H, C, A> &b) {
a.swap(b);
}
template<
typename T,
typename H = std::hash<T>,
typename C = EqualWithCstr<T>,
typename A = Allocator<T>
>
using Multiset = detail::SetImpl<T, H, C, A, true>;
template<typename T, typename H, typename C, typename A>
inline void swap(Multiset<T, H, C, A> &a, Multiset<T, H, C, A> &b) {
a.swap(b);
}
} /* namespace ostd */
#endif