From ea70d2e4673208ad64e90ce66d6a958b0b60b473 Mon Sep 17 00:00:00 2001 From: q66 Date: Wed, 17 Jun 2015 21:25:34 +0100 Subject: [PATCH] add octa::Multimap and octa::Multiset --- octa/internal/hashtable.h | 53 +++++++--- octa/map.h | 198 ++++++++++++++++++++++++++++++++++++-- octa/set.h | 196 +++++++++++++++++++++++++++++++++++-- 3 files changed, 421 insertions(+), 26 deletions(-) diff --git a/octa/internal/hashtable.h b/octa/internal/hashtable.h index a48557a..fac1f22 100644 --- a/octa/internal/hashtable.h +++ b/octa/internal/hashtable.h @@ -313,25 +313,40 @@ namespace detail { return B::get_data(c->value); } - template - bool remove(const U &key) { + template + octa::Size remove(const U &key) { + if (!p_len) return 0; + octa::Size olen = p_len; octa::Size h = get_hash()(key) & (p_size - 1); - Chain *c = p_data.first()[h]; - Chain **p = &c; + Chain **p = &p_data.first()[h], *c = *p; while (c) { if (get_eq()(key, B::get_key(c->value))) { + --p_len; *p = c->next; - octa::allocator_destroy(get_alloc(), &c->value); - octa::allocator_construct(get_alloc(), &c->value); c->next = p_unused; p_unused = c; - --p_len; - return true; + octa::allocator_destroy(get_alloc(), &c->value); + octa::allocator_construct(get_alloc(), &c->value); + if (!multi) return 1; + } else { + p = &c->next; } - c = c->next; - p = &c; + c = *p; } - return false; + return olen - p_len; + } + + template + octa::Size count(const U &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 (!multi) break; + } + return ret; } void delete_chunks(Chunk *chunks) { @@ -394,10 +409,20 @@ namespace detail { B::swap_elem(found->value, elem); } Chain **hch = p_data.first(); - auto ret = octa::make_pair(Range(hch + h + 1, hch + bucket_count(), + return octa::make_pair(Range(hch + h + 1, hch + bucket_count(), found), ins); - rehash_up(); - return octa::move(ret); + } + + template + octa::Pair emplace_multi(Args &&...args) { + rehash_ahead(1); + E elem(octa::forward(args)...); + octa::Size h = get_hash()(B::get_key(elem)) & (p_size - 1); + Chain *ch = insert(h); + B::swap_elem(ch->value, elem); + Chain **hch = p_data.first(); + return octa::make_pair(Range(hch + h + 1, hch + bucket_count(), + ch), true); } template diff --git a/octa/map.h b/octa/map.h index f49c351..de04a63 100644 --- a/octa/map.h +++ b/octa/map.h @@ -212,15 +212,11 @@ public: } octa::Size erase(const K &key) { - if (p_table.remove(key)) return 1; - return 0; + return p_table.remove(key); } octa::Size count(const K &key) { - octa::Size h; - T *v = p_table.access_base(key, h); - if (v) return 1; - return 0; + return p_table.count(key); } Range find(const K &key) { return p_table.find(key); } @@ -251,6 +247,196 @@ public: } }; +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 + >; + Base p_table; + + template + octa::Size estimate_rsize(const R &range, + octa::EnableIf::value, bool> = true + ) { + return range.size(); + } + + template + octa::Size estimate_rsize(const R &, + octa::EnableIf::value, bool> = true + ) { + /* we have no idea how big the range actually is */ + return 16; + } + +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 : estimate_rsize(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(estimate_rsize(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_multi(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 */ #endif \ No newline at end of file diff --git a/octa/set.h b/octa/set.h index 52dacfe..1fa9abf 100644 --- a/octa/set.h +++ b/octa/set.h @@ -180,15 +180,11 @@ public: } octa::Size erase(const T &key) { - if (p_table.remove(key)) return 1; - return 0; + return p_table.remove(key); } octa::Size count(const T &key) { - octa::Size h; - T *v = p_table.access_base(key, h); - if (v) return 1; - return 0; + return p_table.count(key); } Range find(const Key &key) { return p_table.find(key); } @@ -219,6 +215,194 @@ public: } }; +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 + >; + Base p_table; + + template + octa::Size estimate_rsize(const R &range, + octa::EnableIf::value, bool> = true + ) { + return range.size(); + } + + template + octa::Size estimate_rsize(const R &, + octa::EnableIf::value, bool> = true + ) { + /* we have no idea how big the range actually is */ + return 16; + } + +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 : estimate_rsize(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(estimate_rsize(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_multi(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 */ #endif \ No newline at end of file