From f93094735edb329a45e0d36eb3ac28caea80ee47 Mon Sep 17 00:00:00 2001 From: q66 Date: Thu, 18 Jun 2015 02:32:43 +0100 Subject: [PATCH] remove duplications in all hash table code + unify hashes and multihashes into a single struct --- octa/internal/hashtable.h | 343 ++++++++++++++------------- octa/map.h | 485 +++++++++++--------------------------- octa/set.h | 444 +++++++++------------------------- 3 files changed, 430 insertions(+), 842 deletions(-) diff --git a/octa/internal/hashtable.h b/octa/internal/hashtable.h index 022da92..652deec 100644 --- a/octa/internal/hashtable.h +++ b/octa/internal/hashtable.h @@ -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; + 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 + T &insert(octa::Size h, U &&key) { + Chain *c = insert(h); + B::set_key(c->value, octa::forward(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 + 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 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::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 - T &insert(octa::Size h, U &&key) { - Chain *c = insert(h); - B::set_key(c->value, octa::forward(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::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 @@ -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; - 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::value) - octa::swap(p_data.second().first(), ht.p_data.second().first()); - } }; } /* namespace detail */ diff --git a/octa/map.h b/octa/map.h index 4b4fb21..0693d8d 100644 --- a/octa/map.h +++ b/octa/map.h @@ -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, octa::Pair, + K, T, H, C, A, IsMultihash + > { + private: + using Base = octa::detail::Hashtable< + octa::detail::MapBase, octa::Pair, + 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; + using Reference = Value &; + using Pointer = octa::AllocatorPointer; + using ConstPointer = octa::AllocatorConstPointer; + using Range = octa::HashRange>; + using ConstRange = octa::HashRange>; + using LocalRange = octa::BucketRange>; + using ConstLocalRange = octa::BucketRange>; + 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 + MapImpl(R range, octa::Size size = 0, const H &hf = H(), + const C &eqf = C(), const A &alloc = A(), + octa::EnableIf< + octa::IsInputRange::value && + octa::IsConvertible, 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 + MapImpl(R range, octa::Size size, const A &alloc) + : MapImpl(range, size, H(), C(), alloc) {} + + template + MapImpl(R range, octa::Size size, const H &hf, const A &alloc) + : MapImpl(range, size, hf, C(), alloc) {} + + MapImpl(octa::InitializerList 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 init, octa::Size size, const A &alloc) + : MapImpl(octa::each(init), size, H(), C(), alloc) {} + + MapImpl(octa::InitializerList 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 + octa::EnableIf< + octa::IsInputRange::value && + octa::IsConvertible, Value>::value, + MapImpl & + > operator=(R range) { + Base::assign_range(range); + return *this; + } + + MapImpl &operator=(InitializerList 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, typename C = octa::Equal, typename A = octa::Allocator> -> struct Map { -private: - using Base = octa::detail::Hashtable< - octa::detail::MapBase, octa::Pair, - 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; - using Reference = Value &; - using Pointer = octa::AllocatorPointer; - using ConstPointer = octa::AllocatorConstPointer; - using Range = octa::HashRange>; - using ConstRange = octa::HashRange>; - using LocalRange = octa::BucketRange>; - using ConstLocalRange = octa::BucketRange>; - 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 - Map(R range, octa::Size size = 0, const H &hf = H(), - const C &eqf = C(), const A &alloc = A(), - octa::EnableIf< - octa::IsInputRange::value && - octa::IsConvertible, 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 - Map(R range, octa::Size size, const A &alloc) - : Map(range, size, H(), C(), alloc) {} - - template - Map(R range, octa::Size size, const H &hf, const A &alloc) - : Map(range, size, hf, C(), alloc) {} - - Map(octa::InitializerList 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 init, octa::Size size, const A &alloc) - : Map(octa::each(init), size, H(), C(), alloc) {} - - Map(octa::InitializerList 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 - octa::EnableIf< - octa::IsInputRange::value && - octa::IsConvertible, 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 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 - octa::Pair emplace(Args &&...args) { - return p_table.emplace(octa::forward(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; template< typename K, typename T, typename H = octa::ToHash, typename C = octa::Equal, typename A = octa::Allocator> -> struct Multimap { -private: - using Base = octa::detail::Hashtable< - octa::detail::MapBase, octa::Pair, - K, T, H, C, A, true - >; - Base p_table; +> using Multimap = octa::detail::MapImpl; -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; - using Reference = Value &; - using Pointer = octa::AllocatorPointer; - using ConstPointer = octa::AllocatorConstPointer; - using Range = octa::HashRange>; - using ConstRange = octa::HashRange>; - using LocalRange = octa::BucketRange>; - using ConstLocalRange = octa::BucketRange>; - 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 - Multimap(R range, octa::Size size = 0, const H &hf = H(), - const C &eqf = C(), const A &alloc = A(), - octa::EnableIf< - octa::IsInputRange::value && - octa::IsConvertible, 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 - Multimap(R range, octa::Size size, const A &alloc) - : Multimap(range, size, H(), C(), alloc) {} - - template - Multimap(R range, octa::Size size, const H &hf, const A &alloc) - : Multimap(range, size, hf, C(), alloc) {} - - Multimap(octa::InitializerList 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 init, octa::Size size, const A &alloc) - : Multimap(octa::each(init), size, H(), C(), alloc) {} - - Multimap(octa::InitializerList 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 - octa::EnableIf< - octa::IsInputRange::value && - octa::IsConvertible, 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 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 - octa::Pair emplace(Args &&...args) { - return p_table.emplace(octa::forward(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 \ No newline at end of file diff --git a/octa/set.h b/octa/set.h index 768d612..c97a6e8 100644 --- a/octa/set.h +++ b/octa/set.h @@ -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 + struct SetImpl: octa::detail::Hashtable< + octa::detail::SetBase, T, T, T, H, C, A, IsMultihash + > { + private: + using Base = octa::detail::Hashtable< + octa::detail::SetBase, 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; + using ConstPointer = octa::AllocatorConstPointer; + using Range = octa::HashRange; + using ConstRange = octa::HashRange; + using LocalRange = octa::BucketRange; + using ConstLocalRange = octa::BucketRange; + 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 + SetImpl(R range, octa::Size size = 0, const H &hf = H(), + const C &eqf = C(), const A &alloc = A(), + octa::EnableIf< + octa::IsInputRange::value && + octa::IsConvertible, 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 + SetImpl(R range, octa::Size size, const A &alloc) + : SetImpl(range, size, H(), C(), alloc) {} + + template + SetImpl(R range, octa::Size size, const H &hf, const A &alloc) + : SetImpl(range, size, hf, C(), alloc) {} + + SetImpl(octa::InitializerList 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 init, octa::Size size, const A &alloc) + : SetImpl(octa::each(init), size, H(), C(), alloc) {} + + SetImpl(octa::InitializerList 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 + octa::EnableIf< + octa::IsInputRange::value && + octa::IsConvertible, Value>::value, + SetImpl & + > operator=(R range) { + Base::assign_range(range); + return *this; + } + + SetImpl &operator=(InitializerList il) { + Base::assign_init(il); + return *this; + } + + void swap(SetImpl &v) { + Base::swap(v); + } + }; } template< @@ -35,344 +144,15 @@ template< typename H = octa::ToHash, typename C = octa::Equal, typename A = octa::Allocator -> struct Set { -private: - using Base = octa::detail::Hashtable< - octa::detail::SetBase, 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; - using ConstPointer = octa::AllocatorConstPointer; - using Range = octa::HashRange; - using ConstRange = octa::HashRange; - using LocalRange = octa::BucketRange; - using ConstLocalRange = octa::BucketRange; - 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 - Set(R range, octa::Size size = 0, const H &hf = H(), - const C &eqf = C(), const A &alloc = A(), - octa::EnableIf< - octa::IsInputRange::value && - octa::IsConvertible, 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 - Set(R range, octa::Size size, const A &alloc) - : Set(range, size, H(), C(), alloc) {} - - template - Set(R range, octa::Size size, const H &hf, const A &alloc) - : Set(range, size, hf, C(), alloc) {} - - Set(octa::InitializerList 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 init, octa::Size size, const A &alloc) - : Set(octa::each(init), size, H(), C(), alloc) {} - - Set(octa::InitializerList 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 - octa::EnableIf< - octa::IsInputRange::value && - octa::IsConvertible, 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 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 - octa::Pair emplace(Args &&...args) { - return p_table.emplace(octa::forward(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; template< typename T, typename H = octa::ToHash, typename C = octa::Equal, typename A = octa::Allocator -> struct Multiset { -private: - using Base = octa::detail::Hashtable< - octa::detail::SetBase, T, T, T, H, C, A, true - >; - Base p_table; +> using Multiset = octa::detail::SetImpl; -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; - using ConstPointer = octa::AllocatorConstPointer; - using Range = octa::HashRange; - using ConstRange = octa::HashRange; - using LocalRange = octa::BucketRange; - using ConstLocalRange = octa::BucketRange; - 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 - Multiset(R range, octa::Size size = 0, const H &hf = H(), - const C &eqf = C(), const A &alloc = A(), - octa::EnableIf< - octa::IsInputRange::value && - octa::IsConvertible, 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 - Multiset(R range, octa::Size size, const A &alloc) - : Multiset(range, size, H(), C(), alloc) {} - - template - Multiset(R range, octa::Size size, const H &hf, const A &alloc) - : Multiset(range, size, hf, C(), alloc) {} - - Multiset(octa::InitializerList 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 init, octa::Size size, const A &alloc) - : Multiset(octa::each(init), size, H(), C(), alloc) {} - - Multiset(octa::InitializerList 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 - octa::EnableIf< - octa::IsInputRange::value && - octa::IsConvertible, 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 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 - octa::Pair emplace(Args &&...args) { - return p_table.emplace(octa::forward(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 \ No newline at end of file