From 804065367f6cf0f39d520f2e9c238c41deb16286 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Thu, 8 Jan 2026 12:41:29 -0500 Subject: [PATCH] xo-arena: DArenaHashMap: improve sentinel abstraction for control --- xo-arena/include/xo/arena/DArenaHashMap.hpp | 44 ++++++++++------- xo-arena/utest/random_hash_ops.hpp | 52 --------------------- 2 files changed, 27 insertions(+), 69 deletions(-) diff --git a/xo-arena/include/xo/arena/DArenaHashMap.hpp b/xo-arena/include/xo/arena/DArenaHashMap.hpp index 92d0ed87..a9a1c8b7 100644 --- a/xo-arena/include/xo/arena/DArenaHashMap.hpp +++ b/xo-arena/include/xo/arena/DArenaHashMap.hpp @@ -64,6 +64,8 @@ namespace xo { using size_type = std::size_t; using control_type = std::uint8_t; + /** control: mask for sentinel states **/ + static constexpr uint8_t c_sentinel_mask = 0xF0; /** control: sentinel for empty slot **/ static constexpr uint8_t c_empty_slot = 0xFF; /** control: tombstone for deleted slot **/ @@ -75,6 +77,16 @@ namespace xo { /** max load factor **/ static constexpr float c_max_load_factor = 0.875; + /** control: true for sentinel values **/ + static constexpr bool is_sentinel(control_type ctrl) { + return ctrl & c_sentinel_mask; + } + + /** control; true for non-sentinel values **/ + static constexpr bool is_data(control_type ctrl) { + return 0 == (ctrl & c_sentinel_mask); + } + /** find smallest multiple k : k * c_group_size >= n **/ static size_type lub_group_mult(size_t n) { return (n + c_group_size - 1) / c_group_size; @@ -288,7 +300,7 @@ namespace xo { } bool is_sentinel() const { - return ((*ctrl_ == c_tombstone) || (*ctrl_ == c_empty_slot)); + return DArenaHashMapUtil::is_sentinel(*ctrl_); } private: @@ -605,20 +617,18 @@ namespace xo { uint8_t ctrl = store_.control_[i]; value_type & kv_pair = store_.slots_[i]; - if ((ctrl != c_empty_slot) - && (ctrl != c_tombstone)) - { - size_type h = hash_(kv_pair.first); - auto chk = this->_try_insert_aux(h, kv_pair, &store_2x); + if (DArenaHashMapUtil::is_data(ctrl)) { + size_type h = hash_(kv_pair.first); + auto chk = this->_try_insert_aux(h, kv_pair, &store_2x); - if (!chk.second) { - // shenanigans - something isn't right. - // - may have run out of memory - assert(false); + if (!chk.second) { + // shenanigans - something isn't right. + // - may have run out of memory + assert(false); - return false; - } + return false; } + } } this->store_ = std::move(store_2x); @@ -778,7 +788,7 @@ namespace xo { size_type occupied_count = 0; for (size_type i = 0; i < store_.n_slot_; ++i) { uint8_t c = store_.control_[i]; - if ((c != c_empty_slot) && (c != c_tombstone)) { + if (DArenaHashMapUtil::is_data(c)) { ++occupied_count; } } @@ -793,7 +803,7 @@ namespace xo { /* SM4.1.1: if control_[i] is non-sentinel, control_[i] = hash_(slots_[i].first) & 0x7f */ for (size_type i = 0; i < store_.n_slot_; ++i) { uint8_t c = store_.control_[i]; - if ((c != c_empty_slot) && (c != c_tombstone)) { + if (DArenaHashMapUtil::is_data(c)) { uint8_t expected_h2 = hash_(store_.slots_[i].first) & 0x7f; if (c != expected_h2) { return policy.report_error(log, @@ -810,12 +820,12 @@ namespace xo { */ for (size_type i = 0; i < store_.n_slot_; ++i) { uint8_t c = store_.control_[i]; - if ((c != c_empty_slot) && (c != c_tombstone)) { + if (DArenaHashMapUtil::is_data(c)) { size_type h = (hash_(store_.slots_[i].first) >> 7) & (store_.n_slot_ - 1); size_type j = h; while (j != i) { uint8_t cj = store_.control_[j]; - if ((cj == c_empty_slot) || (cj == c_tombstone)) { + if (DArenaHashMapUtil::is_sentinel(cj)) { return policy.report_error(log, c_self, ": expect non-empty slot in probe range [h..i]", xtag("i", i), @@ -831,7 +841,7 @@ namespace xo { /* SM4.2: if control_[i] is empty or tombstone, slots_[i].first = key_type() */ for (size_type i = 0; i < store_.n_slot_; ++i) { uint8_t c = store_.control_[i]; - if ((c == c_empty_slot) || (c == c_tombstone)) { + if (DArenaHashMapUtil::is_sentinel(c)) { if (!(store_.slots_[i].first == key_type())) { return policy.report_error(log, c_self, ": expect empty/tombstone slot has default key", diff --git a/xo-arena/utest/random_hash_ops.hpp b/xo-arena/utest/random_hash_ops.hpp index 85ba8a96..d7f21678 100644 --- a/xo-arena/utest/random_hash_ops.hpp +++ b/xo-arena/utest/random_hash_ops.hpp @@ -596,58 +596,6 @@ namespace utest { } /*check_bidirectional_iterator*/ #endif -#ifdef NOT_YET - /** Require: - * - tree has keys [0..n-1], where n=treesize() - * - tree valu at key k is dvalue+10*k - * - * @p catch_flag. control behavior at each test assertion. - * true -> log to console + interact with catch2 - * false -> verify iteration behavior for return code. - * - **/ - static bool - check_reduced_sum(uint32_t dvalue, - bool catch_flag, - Tree const & rbtree) - { - using xo::scope; - using xo::xtag; - - scope log(XO_DEBUG(catch_flag)); - - /* -> false if/when check fails */ - bool ok_flag = true; - - size_t const n = rbtree.size(); - - for(size_t i = 0; i < n; ++i) { - /* compute reduction up to key=i */ - double reduced_upto - = rbtree.reduce_lub(i /*key*/, - true /*is_closed*/); - - double reduced = (i+1) * (5*i + dvalue); - - INFO(tostr(xtag("i", i), xtag("n", n), - xtag("tree.reduced_upto", reduced_upto), - xtag("reduced", reduced), - xtag("dvalue", dvalue))); - - auto glb_ix = rbtree.cfind_sum_glb(reduced); - - REQUIRE_ORFAIL(ok_flag, catch_flag, reduced_upto == reduced); - REQUIRE_ORFAIL(ok_flag, catch_flag, glb_ix.is_dereferenceable()); - /* glb_ix is truth-y */ - REQUIRE_ORFAIL(ok_flag, catch_flag, glb_ix); - - REQUIRE_ORFAIL(ok_flag, catch_flag, glb_ix->first == i); - } - - return ok_flag; - } /*check_reduced_sum*/ -#endif - #ifdef NOT_YET /* Require: * - *p_rbtree has keys [0..n-1], where n=rbtree.size()