diff --git a/include/xo/arena/DArenaHashMap.hpp b/include/xo/arena/DArenaHashMap.hpp index aef9ed3..cf50895 100644 --- a/include/xo/arena/DArenaHashMap.hpp +++ b/include/xo/arena/DArenaHashMap.hpp @@ -98,8 +98,8 @@ namespace xo { * Replaces any previous value stored under the same key. * * Return pair retval with: - * reval.first: true if size incremented; - * retval.second: address of slots_[p] at which pair inserted/updated + * retval.first: address of slots_[p] at which pair inserted/updated + * retval.second: true if size incremented; * * When table is full retval.second will be nullptr, * with error captured in last_error_ @@ -119,6 +119,9 @@ namespace xo { **/ iterator find(const key_type & key); + /** establish kv pair for @p key in this table; return address of value part **/ + mapped_type & operator[](const key_type & key); + private: /** insert @p kv_pair, * where key hashes to @p hash_value, into @p *store @@ -169,7 +172,7 @@ namespace xo { bool debug_flag) : hash_{std::move(hash)}, equal_{std::move(eq)}, - store_{lub_exp2(lub_group_mult(hint_max_capacity))}, + store_{"arenahashmap", lub_exp2(lub_group_mult(hint_max_capacity))}, debug_flag_{debug_flag} { } @@ -332,7 +335,8 @@ namespace xo { } else { log && log("duplicate-and-replace branch"); - detail::HashMapStore store_2x(std::make_pair(n_group_exponent_2x, + detail::HashMapStore store_2x("arenahashmap", + std::make_pair(n_group_exponent_2x, n_group_2x)); /* rehash everything in store_, * into store_2x @@ -459,6 +463,42 @@ namespace xo { } } + template + auto + DArenaHashMap::operator[](const key_type & key) -> mapped_type & + { + { + auto ix = this->find(key); + + if (ix != this->end()) + return ix->second; + } + + // key-value pair + value_type kv_pair = std::make_pair(key, mapped_type{}); + + auto [slot_addr, ins_flag] = this->try_insert(kv_pair); + + if (slot_addr) + return slot_addr->second; + + if (!this->_try_grow()) { + // we are out of room + + throw std::runtime_error("DArenaHashMap::operator[]: table capacity exhausted"); + } + + /* retry insert, now with bigger capacity */ + std::tie(slot_addr, ins_flag) = this->try_insert(kv_pair); + + assert(slot_addr); + + return slot_addr->second; + } + /** * Verify DArenaHashMap class invariants. * diff --git a/include/xo/arena/hashmap/HashMapStore.hpp b/include/xo/arena/hashmap/HashMapStore.hpp index 545aa1c..dd8ace1 100644 --- a/include/xo/arena/hashmap/HashMapStore.hpp +++ b/include/xo/arena/hashmap/HashMapStore.hpp @@ -22,14 +22,15 @@ namespace xo { public: /** group_exp2: number of groups {x, 2^x} **/ - explicit HashMapStore(const std::pair & group_exp2) : size_{0}, n_group_exponent_{group_exp2.first}, n_group_{group_exp2.second}, n_slot_{group_exp2.second * c_group_size}, - control_{control_vector_type::map(xo::mm::ArenaConfig{.size_ = control_size(n_slot_)})}, - slots_{slot_vector_type::map(xo::mm::ArenaConfig{.size_ = n_slot_ * sizeof(value_type)})} + control_{control_vector_type::map(xo::mm::ArenaConfig{.name_ = name, .size_ = control_size(n_slot_)})}, + slots_{slot_vector_type::map(xo::mm::ArenaConfig{.name_ = name, .size_ = n_slot_ * sizeof(value_type)})} { /* here: arenas have allocated address range, but no committed memory yet */ diff --git a/utest/DArenaHashMap.test.cpp b/utest/DArenaHashMap.test.cpp index 088edf2..28da62c 100644 --- a/utest/DArenaHashMap.test.cpp +++ b/utest/DArenaHashMap.test.cpp @@ -226,6 +226,54 @@ namespace xo { } } + TEST_CASE("DArenaHashMap-operator-bracket", "[arena][DArenaHashMap]") + { + using HashMap = DArenaHashMap; + + HashMap map; + + // insert via operator[] + map[1] = 100; + map[2] = 200; + map[3] = 300; + + REQUIRE(map.size() == 3); + + // read back via operator[] + REQUIRE(map[1] == 100); + REQUIRE(map[2] == 200); + REQUIRE(map[3] == 300); + + // update via operator[] + map[2] = 250; + REQUIRE(map[2] == 250); + REQUIRE(map.size() == 3); // size unchanged + + // verify via find + { + auto it = map.find(1); + REQUIRE(it != map.end()); + REQUIRE(it->second == 100); + } + { + auto it = map.find(2); + REQUIRE(it != map.end()); + REQUIRE(it->second == 250); + } + { + auto it = map.find(3); + REQUIRE(it != map.end()); + REQUIRE(it->second == 300); + } + + // operator[] on non-existent key creates default entry + int & val = map[999]; + REQUIRE(map.size() == 4); + REQUIRE(val == 0); // default-initialized + val = 999; + REQUIRE(map[999] == 999); + } + // TODO: // - let's try getting lcov to work in xo-umbrella2 }