forked from OctaForge/libostd
remove custom hashtable; no longer necessary
This commit is contained in:
parent
1f83e8c1b0
commit
e0072773e1
|
@ -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
|
|
213
ostd/keyset.hh
213
ostd/keyset.hh
|
@ -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
|
|
215
ostd/map.hh
215
ostd/map.hh
|
@ -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
|
|
186
ostd/set.hh
186
ostd/set.hh
|
@ -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
|
|
Loading…
Reference in a new issue