remove duplications in all hash table code + unify hashes and multihashes into a single struct

master
Daniel Kolesa 2015-06-18 02:32:43 +01:00
parent b20a6ec38e
commit f93094735e
3 changed files with 430 additions and 842 deletions

View File

@ -12,6 +12,7 @@
#include "octa/utility.h"
#include "octa/memory.h"
#include "octa/range.h"
#include "octa/initializer_list.h"
namespace octa {
@ -177,7 +178,119 @@ private:
float p_maxlf;
public:
Range each_from(Chain *c, octa::Size h) {
return Range(p_data.first() + h + 1,
p_data.first() + bucket_count(), c);
}
ConstRange each_from(Chain *c, octa::Size h) const {
using RChain = octa::detail::HashChain<const E>;
return ConstRange((RChain **)(p_data.first() + h + 1),
(RChain **)(p_data.first() + bucket_count()),
(RChain *)c);
}
bool find(const K &key, octa::Size &h, Chain *&oc) const {
if (!p_size) return false;
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))) {
oc = c;
return true;
}
}
return false;
}
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;
}
void delete_chunks(Chunk *chunks) {
for (Chunk *nc; chunks; chunks = nc) {
nc = chunks->next;
octa::allocator_destroy(get_challoc(), chunks);
octa::allocator_deallocate(get_challoc(), chunks, 1);
}
}
T *access_base(const K &key, octa::Size &h) const {
if (!p_size) return NULL;
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;
}
void rehash_ahead(octa::Size n) {
if (!bucket_count())
reserve(n);
else if ((float(size() + n) / bucket_count()) > max_load_factor())
rehash(octa::Size((size() + 1) / max_load_factor()) * 2);
}
protected:
template<typename U>
T &insert(octa::Size h, U &&key) {
Chain *c = insert(h);
B::set_key(c->value, octa::forward<U>(key), get_alloc());
return B::get_data(c->value);
}
T &access_or_insert(const K &key) {
octa::Size h;
T *v = access_base(key, h);
if (v) return *v;
rehash_ahead(1);
return insert(h, key);
}
T &access_or_insert(K &&key) {
octa::Size h;
T *v = access_base(key, h);
if (v) return *v;
rehash_ahead(1);
return insert(h, octa::move(key));
}
T &access(const K &key) const {
octa::Size h;
return *access_base(key, h);
}
template<typename R>
void assign_range(R range) {
clear();
reserve_at_least(octa::detail::estimate_hrsize(range));
for (; !range.empty(); range.pop_front())
emplace(range.front());
rehash_up();
}
void assign_init(InitializerList<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(); }
@ -263,12 +376,6 @@ public:
}
}
~Hashtable() {
if (p_size) octa::allocator_deallocate(get_cpalloc(),
p_data.first(), p_size);
delete_chunks(p_chunks);
}
Hashtable &operator=(const Hashtable &ht) {
clear();
if (octa::AllocatorPropagateOnContainerCopyAssignment<A>::value) {
@ -301,92 +408,37 @@ public:
return *this;
}
bool empty() const { return p_len == 0; }
octa::Size size() const { return p_len; }
Size max_size() const { return Size(~0) / sizeof(E); }
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;
void rehash_up() {
if (load_factor() <= max_load_factor()) return;
rehash(octa::Size(size() / max_load_factor()) * 2);
}
template<typename U>
T &insert(octa::Size h, U &&key) {
Chain *c = insert(h);
B::set_key(c->value, octa::forward<U>(key), get_alloc());
return B::get_data(c->value);
void reserve_at_least(octa::Size count) {
octa::Size nc = octa::Size(ceil(count / max_load_factor()));
if (p_size > nc) return;
rehash(nc);
}
T &access_or_insert(const K &key) {
octa::Size h;
T *v = access_base(key, h);
if (v) return *v;
rehash_ahead(1);
return insert(h, key);
void swap(Hashtable &ht) {
octa::swap(p_size, ht.p_size);
octa::swap(p_len, ht.p_len);
octa::swap(p_chunks, ht.p_chunks);
octa::swap(p_unused, ht.p_unused);
octa::swap(p_data.first(), ht.p_data.first());
octa::swap(p_data.second().second(), ht.p_data.second().second());
if (octa::AllocatorPropagateOnContainerSwap<A>::value)
octa::swap(p_data.second().first(), ht.p_data.second().first());
}
T &access_or_insert(K &&key) {
octa::Size h;
T *v = access_base(key, h);
if (v) return *v;
rehash_ahead(1);
return insert(h, octa::move(key));
public:
~Hashtable() {
if (p_size) octa::allocator_deallocate(get_cpalloc(),
p_data.first(), p_size);
delete_chunks(p_chunks);
}
octa::Size remove(const K &key) {
if (!p_len) return 0;
octa::Size olen = p_len;
octa::Size h = get_hash()(key) & (p_size - 1);
Chain **p = &p_data.first()[h], *c = *p;
while (c) {
if (get_eq()(key, B::get_key(c->value))) {
--p_len;
*p = c->next;
c->next = p_unused;
p_unused = c;
octa::allocator_destroy(get_alloc(), &c->value);
octa::allocator_construct(get_alloc(), &c->value);
if (!Multihash) return 1;
} else {
p = &c->next;
}
c = *p;
}
return olen - p_len;
}
octa::Size count(const K &key) {
if (!p_len) return 0;
octa::Size h = get_hash()(key) & (p_size - 1);
octa::Size ret = 0;
for (Chain *c = p_data.first()[h]; c; c = c->next)
if (get_eq()(key, B::get_key(c->value))) {
++ret;
if (!Multihash) break;
}
return ret;
}
void delete_chunks(Chunk *chunks) {
for (Chunk *nc; chunks; chunks = nc) {
nc = chunks->next;
octa::allocator_destroy(get_challoc(), chunks);
octa::allocator_deallocate(get_challoc(), chunks, 1);
}
A get_allocator() const {
return get_alloc();
}
void clear() {
@ -397,19 +449,25 @@ public:
delete_chunks(p_chunks);
}
T *access_base(const K &key, octa::Size &h) const {
if (!p_size) return NULL;
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;
bool empty() const { return p_len == 0; }
octa::Size size() const { return p_len; }
Size max_size() const { return Size(~0) / sizeof(E); }
octa::Size bucket_count() const { return p_size; }
octa::Size max_bucket_count() const { return Size(~0) / sizeof(Chain); }
octa::Size bucket(const K &key) const {
return get_hash()(key) & (p_size - 1);
}
T &access(const K &key) const {
octa::Size h;
return *access_base(key, h);
octa::Size bucket_size(octa::Size n) const {
octa::Size ret = 0;
if (ret >= p_size) return ret;
Chain *c = p_data.first()[n];
if (!c) return ret;
for (; c; c = c->next)
++ret;
return ret;
}
template<typename ...Args>
@ -443,16 +501,38 @@ public:
found), ins);
}
bool find(const K &key, octa::Size &h, Chain *&oc) const {
if (!p_size) return false;
h = get_hash()(key) & (p_size - 1);
for (Chain *c = p_data.first()[h]; c; c = c->next) {
octa::Size erase(const K &key) {
if (!p_len) return 0;
octa::Size olen = p_len;
octa::Size h = get_hash()(key) & (p_size - 1);
Chain **p = &p_data.first()[h], *c = *p;
while (c) {
if (get_eq()(key, B::get_key(c->value))) {
oc = c;
return true;
--p_len;
*p = c->next;
c->next = p_unused;
p_unused = c;
octa::allocator_destroy(get_alloc(), &c->value);
octa::allocator_construct(get_alloc(), &c->value);
if (!Multihash) return 1;
} else {
p = &c->next;
}
c = *p;
}
return false;
return olen - p_len;
}
octa::Size count(const K &key) {
if (!p_len) return 0;
octa::Size h = get_hash()(key) & (p_size - 1);
octa::Size ret = 0;
for (Chain *c = p_data.first()[h]; c; c = c->next)
if (get_eq()(key, B::get_key(c->value))) {
++ret;
if (!Multihash) break;
}
return ret;
}
Range find(const K &key) {
@ -473,23 +553,6 @@ public:
float max_load_factor() const { return p_maxlf; }
void max_load_factor(float lf) { p_maxlf = lf; }
octa::Size bucket_count() const { return p_size; }
octa::Size max_bucket_count() const { return Size(~0) / sizeof(Chain); }
octa::Size bucket(const K &key) const {
return get_hash()(key) & (p_size - 1);
}
octa::Size bucket_size(octa::Size n) const {
octa::Size ret = 0;
if (ret >= p_size) return ret;
Chain *c = p_data.first()[n];
if (!c) return ret;
for (; c; c = c->next)
++ret;
return ret;
}
void rehash(octa::Size count) {
octa::Size fbcount = octa::Size(p_len / max_load_factor());
if (fbcount > count) count = fbcount;
@ -516,28 +579,10 @@ public:
och, osize);
}
void rehash_up() {
if (load_factor() <= max_load_factor()) return;
rehash(octa::Size(size() / max_load_factor()) * 2);
}
void reserve(octa::Size count) {
rehash(octa::Size(ceil(count / max_load_factor())));
}
void reserve_at_least(octa::Size count) {
octa::Size nc = octa::Size(ceil(count / max_load_factor()));
if (p_size > nc) return;
rehash(nc);
}
void rehash_ahead(octa::Size n) {
if (!bucket_count())
reserve(n);
else if ((float(size() + n) / bucket_count()) > max_load_factor())
rehash(octa::Size((size() + 1) / max_load_factor()) * 2);
}
Range each() {
return Range(p_data.first(), p_data.first() + bucket_count());
}
@ -566,28 +611,6 @@ public:
if (n >= p_size) return ConstLocalRange();
return ConstLocalRange((Chain *)p_data.first()[n]);
}
Range each_from(Chain *c, octa::Size h) {
return Range(p_data.first() + h + 1,
p_data.first() + bucket_count(), c);
}
ConstRange each_from(Chain *c, octa::Size h) const {
using RChain = octa::detail::HashChain<const E>;
return ConstRange((RChain **)(p_data.first() + h + 1),
(RChain **)(p_data.first() + bucket_count()),
(RChain *)c);
}
void swap(Hashtable &ht) {
octa::swap(p_size, ht.p_size);
octa::swap(p_len, ht.p_len);
octa::swap(p_chunks, ht.p_chunks);
octa::swap(p_unused, ht.p_unused);
octa::swap(p_data.first(), ht.p_data.first());
octa::swap(p_data.second().second(), ht.p_data.second().second());
if (octa::AllocatorPropagateOnContainerSwap<A>::value)
octa::swap(p_data.second().first(), ht.p_data.second().first());
}
};
} /* namespace detail */

View File

@ -37,6 +37,138 @@ namespace detail {
octa::swap(*((T *)&a.second), *((T *)&b.second));
}
};
template<
typename K, typename T, typename H,
typename C, typename A, bool IsMultihash
> struct MapImpl: octa::detail::Hashtable<
octa::detail::MapBase<K, T, A>, octa::Pair<const K, T>,
K, T, H, C, A, IsMultihash
> {
private:
using Base = octa::detail::Hashtable<
octa::detail::MapBase<K, T, A>, octa::Pair<const K, T>,
K, T, H, C, A, IsMultihash
>;
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 Range = octa::HashRange<octa::Pair<const K, T>>;
using ConstRange = octa::HashRange<const octa::Pair<const K, T>>;
using LocalRange = octa::BucketRange<octa::Pair<const K, T>>;
using ConstLocalRange = octa::BucketRange<const octa::Pair<const K, T>>;
using Allocator = A;
explicit MapImpl(octa::Size size, const H &hf = H(),
const C &eqf = C(), const A &alloc = A()
): Base(size, hf, eqf, alloc) {}
MapImpl(): MapImpl(0) {}
explicit MapImpl(const A &alloc): MapImpl(0, H(), C(), alloc) {}
MapImpl(octa::Size size, const A &alloc):
MapImpl(size, H(), C(), alloc) {}
MapImpl(octa::Size size, const H &hf, const A &alloc):
MapImpl(size, hf, C(), alloc) {}
MapImpl(const MapImpl &m): Base(m,
octa::allocator_container_copy(m.get_alloc())) {}
MapImpl(const MapImpl &m, const A &alloc): Base(m, alloc) {}
MapImpl(MapImpl &&m): Base(octa::move(m)) {}
MapImpl(MapImpl &&m, const A &alloc): Base(octa::move(m), alloc) {}
template<typename R>
MapImpl(R range, octa::Size size = 0, const H &hf = H(),
const C &eqf = C(), const A &alloc = A(),
octa::EnableIf<
octa::IsInputRange<R>::value &&
octa::IsConvertible<RangeReference<R>, Value>::value,
bool
> = true
): Base(size ? size : octa::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, octa::Size size, const A &alloc)
: MapImpl(range, size, H(), C(), alloc) {}
template<typename R>
MapImpl(R range, octa::Size size, const H &hf, const A &alloc)
: MapImpl(range, size, hf, C(), alloc) {}
MapImpl(octa::InitializerList<Value> init, octa::Size size = 0,
const H &hf = H(), const C &eqf = C(), const A &alloc = A()
): MapImpl(octa::each(init), size, hf, eqf, alloc) {}
MapImpl(octa::InitializerList<Value> init, octa::Size size, const A &alloc)
: MapImpl(octa::each(init), size, H(), C(), alloc) {}
MapImpl(octa::InitializerList<Value> init, octa::Size size, const H &hf,
const A &alloc
): MapImpl(octa::each(init), size, hf, C(), alloc) {}
MapImpl &operator=(const MapImpl &m) {
Base::operator=(m);
return *this;
}
MapImpl &operator=(MapImpl &&m) {
Base::operator=(octa::move(m));
return *this;
}
template<typename R>
octa::EnableIf<
octa::IsInputRange<R>::value &&
octa::IsConvertible<RangeReference<R>, Value>::value,
MapImpl &
> operator=(R range) {
Base::assign_range(range);
return *this;
}
MapImpl &operator=(InitializerList<Value> il) {
Base::assign_init(il);
return *this;
}
T &at(const K &key) {
static_assert(!IsMultihash, "at() only allowed on regular maps");
return Base::access(key);
}
const T &at(const K &key) const {
static_assert(!IsMultihash, "at() only allowed on regular maps");
return Base::access(key);
}
T &operator[](const K &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(octa::move(key));
}
void swap(MapImpl &v) {
Base::swap(v);
}
};
}
template<
@ -44,362 +176,15 @@ template<
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, A>, octa::Pair<const K, T>,
K, T, H, C, A, false
>;
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 Range = octa::HashRange<octa::Pair<const K, T>>;
using ConstRange = octa::HashRange<const octa::Pair<const K, T>>;
using LocalRange = octa::BucketRange<octa::Pair<const K, T>>;
using ConstLocalRange = octa::BucketRange<const octa::Pair<const K, T>>;
using Allocator = A;
explicit Map(octa::Size size, const H &hf = H(),
const C &eqf = C(), const A &alloc = A()
): p_table(size, hf, eqf, alloc) {}
Map(): Map(0) {}
explicit Map(const A &alloc): Map(0, H(), C(), alloc) {}
Map(octa::Size size, const A &alloc): Map(size, H(), C(), alloc) {}
Map(octa::Size size, const H &hf, const A &alloc): Map(size, hf, C(), alloc) {}
Map(const Map &m): p_table(m.p_table,
octa::allocator_container_copy(m.p_table.get_alloc())) {}
Map(const Map &m, const A &alloc): p_table(m.p_table, alloc) {}
Map(Map &&m): p_table(octa::move(m.p_table)) {}
Map(Map &&m, const A &alloc): p_table(octa::move(m.p_table), alloc) {}
template<typename R>
Map(R range, octa::Size size = 0, const H &hf = H(),
const C &eqf = C(), const A &alloc = A(),
octa::EnableIf<
octa::IsInputRange<R>::value &&
octa::IsConvertible<RangeReference<R>, Value>::value,
bool
> = true
): p_table(size ? size : octa::detail::estimate_hrsize(range),
hf, eqf, alloc) {
for (; !range.empty(); range.pop_front())
emplace(range.front());
p_table.rehash_up();
}
template<typename R>
Map(R range, octa::Size size, const A &alloc)
: Map(range, size, H(), C(), alloc) {}
template<typename R>
Map(R range, octa::Size size, const H &hf, const A &alloc)
: Map(range, size, hf, C(), alloc) {}
Map(octa::InitializerList<Value> init, octa::Size size = 0,
const H &hf = H(), const C &eqf = C(), const A &alloc = A()
): Map(octa::each(init), size, hf, eqf, alloc) {}
Map(octa::InitializerList<Value> init, octa::Size size, const A &alloc)
: Map(octa::each(init), size, H(), C(), alloc) {}
Map(octa::InitializerList<Value> init, octa::Size size, const H &hf,
const A &alloc
): Map(octa::each(init), size, hf, C(), alloc) {}
Map &operator=(const Map &m) {
p_table = m.p_table;
return *this;
}
Map &operator=(Map &&m) {
p_table = octa::move(m.p_table);
return *this;
}
template<typename R>
octa::EnableIf<
octa::IsInputRange<R>::value &&
octa::IsConvertible<RangeReference<R>, Value>::value,
Map &
> operator=(R range) {
clear();
p_table.reserve_at_least(octa::detail::estimate_hrsize(range));
for (; !range.empty(); range.pop_front())
emplace(range.front());
p_table.rehash_up();
return *this;
}
Map &operator=(InitializerList<Value> il) {
const Value *beg = il.begin(), *end = il.end();
clear();
p_table.reserve_at_least(end - beg);
while (beg != end)
emplace(*beg++);
return *this;
}
bool empty() const { return p_table.empty(); }
octa::Size size() const { return p_table.size(); }
octa::Size max_size() const { return p_table.max_size(); }
octa::Size bucket_count() const { return p_table.bucket_count(); }
octa::Size max_bucket_count() const { return p_table.max_bucket_count(); }
octa::Size bucket(const K &key) const { return p_table.bucket(key); }
octa::Size bucket_size(octa::Size n) const { return p_table.bucket_size(n); }
void clear() { p_table.clear(); }
A get_allocator() const {
return p_table.get_alloc();
}
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) {
return p_table.access_or_insert(key);
}
T &operator[](K &&key) {
return p_table.access_or_insert(octa::move(key));
}
template<typename ...Args>
octa::Pair<Range, bool> emplace(Args &&...args) {
return p_table.emplace(octa::forward<Args>(args)...);
}
octa::Size erase(const K &key) {
return p_table.remove(key);
}
octa::Size count(const K &key) {
return p_table.count(key);
}
Range find(const K &key) { return p_table.find(key); }
ConstRange find(const K &key) const { return p_table.find(key); }
float load_factor() const { return p_table.load_factor(); }
float max_load_factor() const { return p_table.max_load_factor(); }
void max_load_factor(float lf) { p_table.max_load_factor(lf); }
void rehash(octa::Size count) {
p_table.rehash(count);
}
void reserve(octa::Size count) {
p_table.reserve(count);
}
Range each() { return p_table.each(); }
ConstRange each() const { return p_table.each(); }
ConstRange ceach() const { return p_table.ceach(); }
LocalRange each(octa::Size n) { return p_table.each(n); }
ConstLocalRange each(octa::Size n) const { return p_table.each(n); }
ConstLocalRange ceach(octa::Size n) const { return p_table.each(n); }
void swap(Map &v) {
octa::swap(p_table, v.p_table);
}
};
> using Map = octa::detail::MapImpl<K, T, H, C, A, false>;
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 Multimap {
private:
using Base = octa::detail::Hashtable<
octa::detail::MapBase<K, T, A>, octa::Pair<const K, T>,
K, T, H, C, A, true
>;
Base p_table;
> using Multimap = octa::detail::MapImpl<K, T, H, C, A, true>;
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 Range = octa::HashRange<octa::Pair<const K, T>>;
using ConstRange = octa::HashRange<const octa::Pair<const K, T>>;
using LocalRange = octa::BucketRange<octa::Pair<const K, T>>;
using ConstLocalRange = octa::BucketRange<const octa::Pair<const K, T>>;
using Allocator = A;
explicit Multimap(octa::Size size, const H &hf = H(),
const C &eqf = C(), const A &alloc = A()
): p_table(size, hf, eqf, alloc) {}
Multimap(): Multimap(0) {}
explicit Multimap(const A &alloc): Multimap(0, H(), C(), alloc) {}
Multimap(octa::Size size, const A &alloc):
Multimap(size, H(), C(), alloc) {}
Multimap(octa::Size size, const H &hf, const A &alloc):
Multimap(size, hf, C(), alloc) {}
Multimap(const Multimap &m): p_table(m.p_table,
octa::allocator_container_copy(m.p_table.get_alloc())) {}
Multimap(const Multimap &m, const A &alloc): p_table(m.p_table, alloc) {}
Multimap(Multimap &&m): p_table(octa::move(m.p_table)) {}
Multimap(Multimap &&m, const A &alloc):
p_table(octa::move(m.p_table), alloc) {}
template<typename R>
Multimap(R range, octa::Size size = 0, const H &hf = H(),
const C &eqf = C(), const A &alloc = A(),
octa::EnableIf<
octa::IsInputRange<R>::value &&
octa::IsConvertible<RangeReference<R>, Value>::value,
bool
> = true
): p_table(size ? size : octa::detail::estimate_hrsize(range),
hf, eqf, alloc) {
for (; !range.empty(); range.pop_front())
emplace(range.front());
p_table.rehash_up();
}
template<typename R>
Multimap(R range, octa::Size size, const A &alloc)
: Multimap(range, size, H(), C(), alloc) {}
template<typename R>
Multimap(R range, octa::Size size, const H &hf, const A &alloc)
: Multimap(range, size, hf, C(), alloc) {}
Multimap(octa::InitializerList<Value> init, octa::Size size = 0,
const H &hf = H(), const C &eqf = C(), const A &alloc = A()
): Multimap(octa::each(init), size, hf, eqf, alloc) {}
Multimap(octa::InitializerList<Value> init, octa::Size size, const A &alloc)
: Multimap(octa::each(init), size, H(), C(), alloc) {}
Multimap(octa::InitializerList<Value> init, octa::Size size, const H &hf,
const A &alloc
): Multimap(octa::each(init), size, hf, C(), alloc) {}
Multimap &operator=(const Multimap &m) {
p_table = m.p_table;
return *this;
}
Multimap &operator=(Multimap &&m) {
p_table = octa::move(m.p_table);
return *this;
}
template<typename R>
octa::EnableIf<
octa::IsInputRange<R>::value &&
octa::IsConvertible<RangeReference<R>, Value>::value,
Multimap &
> operator=(R range) {
clear();
p_table.reserve_at_least(octa::detail::estimate_hrsize(range));
for (; !range.empty(); range.pop_front())
emplace(range.front());
p_table.rehash_up();
return *this;
}
Multimap &operator=(InitializerList<Value> il) {
const Value *beg = il.begin(), *end = il.end();
clear();
p_table.reserve_at_least(end - beg);
while (beg != end)
emplace(*beg++);
return *this;
}
bool empty() const { return p_table.empty(); }
octa::Size size() const { return p_table.size(); }
octa::Size max_size() const { return p_table.max_size(); }
octa::Size bucket_count() const { return p_table.bucket_count(); }
octa::Size max_bucket_count() const { return p_table.max_bucket_count(); }
octa::Size bucket(const K &key) const { return p_table.bucket(key); }
octa::Size bucket_size(octa::Size n) const { return p_table.bucket_size(n); }
void clear() { p_table.clear(); }
A get_allocator() const {
return p_table.get_alloc();
}
template<typename ...Args>
octa::Pair<Range, bool> emplace(Args &&...args) {
return p_table.emplace(octa::forward<Args>(args)...);
}
octa::Size erase(const K &key) {
return p_table.remove(key);
}
octa::Size count(const K &key) {
return p_table.count(key);
}
Range find(const K &key) { return p_table.find(key); }
ConstRange find(const K &key) const { return p_table.find(key); }
float load_factor() const { return p_table.load_factor(); }
float max_load_factor() const { return p_table.max_load_factor(); }
void max_load_factor(float lf) { p_table.max_load_factor(lf); }
void rehash(octa::Size count) {
p_table.rehash(count);
}
void reserve(octa::Size count) {
p_table.reserve(count);
}
Range each() { return p_table.each(); }
ConstRange each() const { return p_table.each(); }
ConstRange ceach() const { return p_table.ceach(); }
LocalRange each(octa::Size n) { return p_table.each(n); }
ConstLocalRange each(octa::Size n) const { return p_table.each(n); }
ConstLocalRange ceach(octa::Size n) const { return p_table.each(n); }
void swap(Multimap &v) {
octa::swap(p_table, v.p_table);
}
};
} /* namespace detail */
} /* namespace octa */
#endif

View File

@ -28,6 +28,115 @@ namespace detail {
static inline void set_key(T &, const U &, A &) {}
static inline void swap_elem(T &a, T &b) { octa::swap(a, b); }
};
template<typename T, typename H, typename C, typename A, bool IsMultihash>
struct SetImpl: octa::detail::Hashtable<
octa::detail::SetBase<T, A>, T, T, T, H, C, A, IsMultihash
> {
private:
using Base = octa::detail::Hashtable<
octa::detail::SetBase<T, A>, T, T, T, H, C, A, IsMultihash
>;
public:
using Key = T;
using Size = octa::Size;
using Difference = octa::Ptrdiff;
using Hasher = H;
using KeyEqual = C;
using Value = T;
using Reference = Value &;
using Pointer = octa::AllocatorPointer<A>;
using ConstPointer = octa::AllocatorConstPointer<A>;
using Range = octa::HashRange<T>;
using ConstRange = octa::HashRange<const T>;
using LocalRange = octa::BucketRange<T>;
using ConstLocalRange = octa::BucketRange<const T>;
using Allocator = A;
explicit SetImpl(octa::Size size, const H &hf = H(),
const C &eqf = C(), const A &alloc = A()
): Base(size, hf, eqf, alloc) {}
SetImpl(): SetImpl(0) {}
explicit SetImpl(const A &alloc): SetImpl(0, H(), C(), alloc) {}
SetImpl(octa::Size size, const A &alloc):
SetImpl(size, H(), C(), alloc) {}
SetImpl(octa::Size size, const H &hf, const A &alloc):
SetImpl(size, hf, C(), alloc) {}
SetImpl(const SetImpl &m): Base(m,
octa::allocator_container_copy(m.get_alloc())) {}
SetImpl(const SetImpl &m, const A &alloc): Base(m, alloc) {}
SetImpl(SetImpl &&m): Base(octa::move(m)) {}
SetImpl(SetImpl &&m, const A &alloc): Base(octa::move(m), alloc) {}
template<typename R>
SetImpl(R range, octa::Size size = 0, const H &hf = H(),
const C &eqf = C(), const A &alloc = A(),
octa::EnableIf<
octa::IsInputRange<R>::value &&
octa::IsConvertible<RangeReference<R>, Value>::value,
bool
> = true
): Base(size ? size : octa::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, octa::Size size, const A &alloc)
: SetImpl(range, size, H(), C(), alloc) {}
template<typename R>
SetImpl(R range, octa::Size size, const H &hf, const A &alloc)
: SetImpl(range, size, hf, C(), alloc) {}
SetImpl(octa::InitializerList<Value> init, octa::Size size = 0,
const H &hf = H(), const C &eqf = C(), const A &alloc = A()
): SetImpl(octa::each(init), size, hf, eqf, alloc) {}
SetImpl(octa::InitializerList<Value> init, octa::Size size, const A &alloc)
: SetImpl(octa::each(init), size, H(), C(), alloc) {}
SetImpl(octa::InitializerList<Value> init, octa::Size size, const H &hf,
const A &alloc
): SetImpl(octa::each(init), size, hf, C(), alloc) {}
SetImpl &operator=(const SetImpl &m) {
Base::operator=(m);
return *this;
}
SetImpl &operator=(SetImpl &&m) {
Base::operator=(octa::move(m));
return *this;
}
template<typename R>
octa::EnableIf<
octa::IsInputRange<R>::value &&
octa::IsConvertible<RangeReference<R>, Value>::value,
SetImpl &
> operator=(R range) {
Base::assign_range(range);
return *this;
}
SetImpl &operator=(InitializerList<Value> il) {
Base::assign_init(il);
return *this;
}
void swap(SetImpl &v) {
Base::swap(v);
}
};
}
template<
@ -35,344 +144,15 @@ template<
typename H = octa::ToHash<T>,
typename C = octa::Equal<T>,
typename A = octa::Allocator<T>
> struct Set {
private:
using Base = octa::detail::Hashtable<
octa::detail::SetBase<T, A>, T, T, T, H, C, A, false
>;
Base p_table;
public:
using Key = T;
using Size = octa::Size;
using Difference = octa::Ptrdiff;
using Hasher = H;
using KeyEqual = C;
using Value = T;
using Reference = Value &;
using Pointer = octa::AllocatorPointer<A>;
using ConstPointer = octa::AllocatorConstPointer<A>;
using Range = octa::HashRange<T>;
using ConstRange = octa::HashRange<const T>;
using LocalRange = octa::BucketRange<T>;
using ConstLocalRange = octa::BucketRange<const T>;
using Allocator = A;
explicit Set(octa::Size size, const H &hf = H(),
const C &eqf = C(), const A &alloc = A()
): p_table(size, hf, eqf, alloc) {}
Set(): Set(0) {}
explicit Set(const A &alloc): Set(0, H(), C(), alloc) {}
Set(octa::Size size, const A &alloc): Set(size, H(), C(), alloc) {}
Set(octa::Size size, const H &hf, const A &alloc): Set(size, hf, C(), alloc) {}
Set(const Set &m): p_table(m.p_table,
octa::allocator_container_copy(m.p_table.get_alloc())) {}
Set(const Set &m, const A &alloc): p_table(m.p_table, alloc) {}
Set(Set &&m): p_table(octa::move(m.p_table)) {}
Set(Set &&m, const A &alloc): p_table(octa::move(m.p_table), alloc) {}
template<typename R>
Set(R range, octa::Size size = 0, const H &hf = H(),
const C &eqf = C(), const A &alloc = A(),
octa::EnableIf<
octa::IsInputRange<R>::value &&
octa::IsConvertible<RangeReference<R>, Value>::value,
bool
> = true
): p_table(size ? size : octa::detail::estimate_hrsize(range),
hf, eqf, alloc) {
for (; !range.empty(); range.pop_front())
emplace(range.front());
p_table.rehash_up();
}
template<typename R>
Set(R range, octa::Size size, const A &alloc)
: Set(range, size, H(), C(), alloc) {}
template<typename R>
Set(R range, octa::Size size, const H &hf, const A &alloc)
: Set(range, size, hf, C(), alloc) {}
Set(octa::InitializerList<Value> init, octa::Size size = 0,
const H &hf = H(), const C &eqf = C(), const A &alloc = A()
): Set(octa::each(init), size, hf, eqf, alloc) {}
Set(octa::InitializerList<Value> init, octa::Size size, const A &alloc)
: Set(octa::each(init), size, H(), C(), alloc) {}
Set(octa::InitializerList<Value> init, octa::Size size, const H &hf,
const A &alloc
): Set(octa::each(init), size, hf, C(), alloc) {}
Set &operator=(const Set &m) {
p_table = m.p_table;
return *this;
}
Set &operator=(Set &&m) {
p_table = octa::move(m.p_table);
return *this;
}
template<typename R>
octa::EnableIf<
octa::IsInputRange<R>::value &&
octa::IsConvertible<RangeReference<R>, Value>::value,
Set &
> operator=(R range) {
clear();
p_table.reserve_at_least(octa::detail::estimate_hrsize(range));
for (; !range.empty(); range.pop_front())
emplace(range.front());
p_table.rehash_up();
return *this;
}
Set &operator=(InitializerList<Value> il) {
const Value *beg = il.begin(), *end = il.end();
clear();
p_table.reserve_at_least(end - beg);
while (beg != end)
emplace(*beg++);
return *this;
}
bool empty() const { return p_table.empty(); }
octa::Size size() const { return p_table.size(); }
octa::Size max_size() const { return p_table.max_size(); }
octa::Size bucket_count() const { return p_table.bucket_count(); }
octa::Size max_bucket_count() const { return p_table.max_bucket_count(); }
octa::Size bucket(const T &key) const { return p_table.bucket(key); }
octa::Size bucket_size(octa::Size n) const { return p_table.bucket_size(n); }
void clear() { p_table.clear(); }
A get_allocator() const {
return p_table.get_alloc();
}
template<typename ...Args>
octa::Pair<Range, bool> emplace(Args &&...args) {
return p_table.emplace(octa::forward<Args>(args)...);
}
octa::Size erase(const T &key) {
return p_table.remove(key);
}
octa::Size count(const T &key) {
return p_table.count(key);
}
Range find(const Key &key) { return p_table.find(key); }
ConstRange find(const Key &key) const { return p_table.find(key); }
float load_factor() const { return p_table.load_factor(); }
float max_load_factor() const { return p_table.max_load_factor(); }
void max_load_factor(float lf) { p_table.max_load_factor(lf); }
void rehash(octa::Size count) {
p_table.rehash(count);
}
void reserve(octa::Size count) {
p_table.reserve(count);
}
Range each() { return p_table.each(); }
ConstRange each() const { return p_table.each(); }
ConstRange ceach() const { return p_table.ceach(); }
LocalRange each(octa::Size n) { return p_table.each(n); }
ConstLocalRange each(octa::Size n) const { return p_table.each(n); }
ConstLocalRange ceach(octa::Size n) const { return p_table.each(n); }
void swap(Set &v) {
octa::swap(p_table, v.p_table);
}
};
> using Set = octa::detail::SetImpl<T, H, C, A, false>;
template<
typename T,
typename H = octa::ToHash<T>,
typename C = octa::Equal<T>,
typename A = octa::Allocator<T>
> struct Multiset {
private:
using Base = octa::detail::Hashtable<
octa::detail::SetBase<T, A>, T, T, T, H, C, A, true
>;
Base p_table;
> using Multiset = octa::detail::SetImpl<T, H, C, A, true>;
public:
using Key = T;
using Size = octa::Size;
using Difference = octa::Ptrdiff;
using Hasher = H;
using KeyEqual = C;
using Value = T;
using Reference = Value &;
using Pointer = octa::AllocatorPointer<A>;
using ConstPointer = octa::AllocatorConstPointer<A>;
using Range = octa::HashRange<T>;
using ConstRange = octa::HashRange<const T>;
using LocalRange = octa::BucketRange<T>;
using ConstLocalRange = octa::BucketRange<const T>;
using Allocator = A;
explicit Multiset(octa::Size size, const H &hf = H(),
const C &eqf = C(), const A &alloc = A()
): p_table(size, hf, eqf, alloc) {}
Multiset(): Multiset(0) {}
explicit Multiset(const A &alloc): Multiset(0, H(), C(), alloc) {}
Multiset(octa::Size size, const A &alloc):
Multiset(size, H(), C(), alloc) {}
Multiset(octa::Size size, const H &hf, const A &alloc):
Multiset(size, hf, C(), alloc) {}
Multiset(const Multiset &m): p_table(m.p_table,
octa::allocator_container_copy(m.p_table.get_alloc())) {}
Multiset(const Multiset &m, const A &alloc): p_table(m.p_table, alloc) {}
Multiset(Multiset &&m): p_table(octa::move(m.p_table)) {}
Multiset(Multiset &&m, const A &alloc):
p_table(octa::move(m.p_table), alloc) {}
template<typename R>
Multiset(R range, octa::Size size = 0, const H &hf = H(),
const C &eqf = C(), const A &alloc = A(),
octa::EnableIf<
octa::IsInputRange<R>::value &&
octa::IsConvertible<RangeReference<R>, Value>::value,
bool
> = true
): p_table(size ? size : octa::detail::estimate_hrsize(range),
hf, eqf, alloc) {
for (; !range.empty(); range.pop_front())
emplace(range.front());
p_table.rehash_up();
}
template<typename R>
Multiset(R range, octa::Size size, const A &alloc)
: Multiset(range, size, H(), C(), alloc) {}
template<typename R>
Multiset(R range, octa::Size size, const H &hf, const A &alloc)
: Multiset(range, size, hf, C(), alloc) {}
Multiset(octa::InitializerList<Value> init, octa::Size size = 0,
const H &hf = H(), const C &eqf = C(), const A &alloc = A()
): Multiset(octa::each(init), size, hf, eqf, alloc) {}
Multiset(octa::InitializerList<Value> init, octa::Size size, const A &alloc)
: Multiset(octa::each(init), size, H(), C(), alloc) {}
Multiset(octa::InitializerList<Value> init, octa::Size size, const H &hf,
const A &alloc
): Multiset(octa::each(init), size, hf, C(), alloc) {}
Multiset &operator=(const Multiset &m) {
p_table = m.p_table;
return *this;
}
Multiset &operator=(Multiset &&m) {
p_table = octa::move(m.p_table);
return *this;
}
template<typename R>
octa::EnableIf<
octa::IsInputRange<R>::value &&
octa::IsConvertible<RangeReference<R>, Value>::value,
Multiset &
> operator=(R range) {
clear();
p_table.reserve_at_least(octa::detail::estimate_hrsize(range));
for (; !range.empty(); range.pop_front())
emplace(range.front());
p_table.rehash_up();
return *this;
}
Multiset &operator=(InitializerList<Value> il) {
const Value *beg = il.begin(), *end = il.end();
clear();
p_table.reserve_at_least(end - beg);
while (beg != end)
emplace(*beg++);
return *this;
}
bool empty() const { return p_table.empty(); }
octa::Size size() const { return p_table.size(); }
octa::Size max_size() const { return p_table.max_size(); }
octa::Size bucket_count() const { return p_table.bucket_count(); }
octa::Size max_bucket_count() const { return p_table.max_bucket_count(); }
octa::Size bucket(const T &key) const { return p_table.bucket(key); }
octa::Size bucket_size(octa::Size n) const { return p_table.bucket_size(n); }
void clear() { p_table.clear(); }
A get_allocator() const {
return p_table.get_alloc();
}
template<typename ...Args>
octa::Pair<Range, bool> emplace(Args &&...args) {
return p_table.emplace(octa::forward<Args>(args)...);
}
octa::Size erase(const T &key) {
return p_table.remove(key);
}
octa::Size count(const T &key) {
return p_table.count(key);
}
Range find(const T &key) { return p_table.find(key); }
ConstRange find(const T &key) const { return p_table.find(key); }
float load_factor() const { return p_table.load_factor(); }
float max_load_factor() const { return p_table.max_load_factor(); }
void max_load_factor(float lf) { p_table.max_load_factor(lf); }
void rehash(octa::Size count) {
p_table.rehash(count);
}
void reserve(octa::Size count) {
p_table.reserve(count);
}
Range each() { return p_table.each(); }
ConstRange each() const { return p_table.each(); }
ConstRange ceach() const { return p_table.ceach(); }
LocalRange each(octa::Size n) { return p_table.each(n); }
ConstLocalRange each(octa::Size n) const { return p_table.each(n); }
ConstLocalRange ceach(octa::Size n) const { return p_table.each(n); }
void swap(Multiset &v) {
octa::swap(p_table, v.p_table);
}
};
} /* namespace detail */
} /* namespace octa */
#endif