diff --git a/octa/internal/hashtable.h b/octa/internal/hashtable.h index 2d3cadc..a88864c 100644 --- a/octa/internal/hashtable.h +++ b/octa/internal/hashtable.h @@ -339,6 +339,7 @@ namespace detail { template T *access_base(const U &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))) @@ -363,6 +364,7 @@ namespace detail { template octa::Pair emplace(Args &&...args) { + rehash_ahead(1); E elem(octa::forward(args)...); octa::Size h = get_hash()(B::get_key(elem)) & (p_size - 1); Chain *found = nullptr; @@ -379,8 +381,10 @@ namespace detail { B::swap_elem(found->value, elem); } Chain **hch = p_data.first(); - return octa::make_pair(Range(hch + h + 1, hch + bucket_count(), + auto ret = octa::make_pair(Range(hch + h + 1, hch + bucket_count(), found), ins); + rehash_up(); + return octa::move(ret); } float load_factor() const { return float(p_len) / p_size; } @@ -435,10 +439,28 @@ namespace detail { delete_chunks(chunks); } + 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()); } diff --git a/octa/map.h b/octa/map.h index ba40cdd..5e8f877 100644 --- a/octa/map.h +++ b/octa/map.h @@ -50,6 +50,22 @@ private: 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; @@ -72,8 +88,8 @@ public: const C &eqf = C(), const A &alloc = A() ): p_table(size, hf, eqf, alloc) {} - Map(): Map(octa::Size(1 << 10)) {} - explicit Map(const A &alloc): Map(octa::Size(1 << 10), H(), C(), 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) {} @@ -87,16 +103,17 @@ public: Map(Map &&m, const A &alloc): p_table(octa::move(m.p_table), alloc) {} template - Map(R range, octa::Size size = 1 << 10, const H &hf = H(), + 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, hf, eqf, alloc) { + ): p_table(size ? size : estimate_rsize(range), hf, eqf, alloc) { for (; !range.empty(); range.pop_front()) emplace(range.front()); + p_table.rehash_up(); } template @@ -107,7 +124,7 @@ public: 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 = 1 << 10, + 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) {} @@ -135,14 +152,17 @@ public: Map & > 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; } Map &operator=(InitializerList il) { - Value *beg = il.begin(), *end = il.end(); + const Value *beg = il.begin(), *end = il.end(); clear(); + p_table.reserve_at_least(end - beg); while (beg != end) emplace(*beg++); return *this; @@ -175,12 +195,14 @@ public: octa::Size h; T *v = p_table.access_base(key, h); if (v) return *v; + p_table.rehash_ahead(1); return p_table.insert(h, key); } T &operator[](K &&key) { octa::Size h; T *v = p_table.access_base(key, h); if (v) return *v; + p_table.rehash_ahead(1); return p_table.insert(h, octa::move(key)); }