From f16e701a404b52ed1e9b2c00c1361691a512d630 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Thu, 15 Jan 2026 20:32:10 -0500 Subject: [PATCH] xo-arena: + const_iterator support for DValueHashMap --- xo-arena/include/xo/arena/DArenaHashMap.hpp | 72 +++++++++++------ .../include/xo/arena/hashmap/ControlGroup.hpp | 2 +- .../arena/hashmap/DArenaHashMapIterator.hpp | 79 ++++++++++++++++++- .../include/xo/arena/hashmap/HashMapStore.hpp | 2 +- 4 files changed, 124 insertions(+), 31 deletions(-) diff --git a/xo-arena/include/xo/arena/DArenaHashMap.hpp b/xo-arena/include/xo/arena/DArenaHashMap.hpp index cf508958..ee5fb794 100644 --- a/xo-arena/include/xo/arena/DArenaHashMap.hpp +++ b/xo-arena/include/xo/arena/DArenaHashMap.hpp @@ -50,7 +50,9 @@ namespace xo { using store_type = detail::HashMapStore; using insert_value_type = std::pair; using iterator = detail::DArenaHashMapIterator; + using const_iterator = detail::DArenaHashMapConstIterator; + public: /** create hash map **/ DArenaHashMap(size_type hint_max_capacity, bool debug_flag = false); @@ -71,28 +73,14 @@ namespace xo { **/ bool verify_ok(verify_policy p = verify_policy::throw_only()) const; - iterator begin() { - if (this->empty()) [[unlikely]] { - return this->end(); - } + const_iterator cbegin() const { return this->_begin_aux(); } + const_iterator cend() const { return this->_end_aux(); } - iterator ix(&(store_.control_[c_control_stub]), - &(store_.slots_[0])); + const_iterator begin() const { return this->_begin_aux(); } + const_iterator end() const { return this->_end_aux(); } - if (ix._at_slot_sentinel()) { - /* advance to first occupied position in table */ - ++ix; - } - - return ix; - } - - iterator end() { - iterator ix(&(store_.control_[c_control_stub + store_.capacity()]), - &(store_.slots_[store_.capacity()])); - - return ix; - } + iterator begin() { return _promote_iterator(_begin_aux()); } + iterator end() { return _promote_iterator(_end_aux()); } /** insert @p kv_pair into hash map. * Replaces any previous value stored under the same key. @@ -117,12 +105,46 @@ namespace xo { /** find element with key @p key. * @return iterator to element if found, end() otherwise **/ - iterator find(const key_type & key); + const_iterator find(const key_type & key) const { return _find(key); } + iterator find(const key_type & key) { return _promote_iterator(_find(key)); } /** establish kv pair for @p key in this table; return address of value part **/ mapped_type & operator[](const key_type & key); private: + iterator _promote_iterator(const_iterator ix) { + return iterator(const_cast(ix._ctrl()), + const_cast(ix._pos())); + } + + const_iterator _begin_aux() const { + if (this->empty()) [[unlikely]] { + return this->end(); + } + + const_iterator ix(&(store_.control_[c_control_stub]), + &(store_.slots_[0])); + + if (ix._at_slot_sentinel()) { + /* advance to first occupied position in table */ + ++ix; + } + + return ix; + } + + const_iterator _end_aux() const { + const_iterator ix(&(store_.control_[c_control_stub + store_.capacity()]), + &(store_.slots_[store_.capacity()])); + + return ix; + } + + /** search hash map on key @p key, return iterator to table member. + * return end-iterator if @p key not found + **/ + const_iterator _find(const key_type & key) const; + /** insert @p kv_pair, * where key hashes to @p hash_value, into @p *store **/ @@ -416,12 +438,12 @@ namespace xo { typename Hash, typename Equal> auto - DArenaHashMap::find(const key_type & key) -> iterator + DArenaHashMap::_find(const key_type & key) const -> const_iterator { size_type N = store_.capacity(); if (N == 0) [[unlikely]] { - return this->end(); + return this->cend(); } size_type h = hash_(key); @@ -443,8 +465,8 @@ namespace xo { auto & slot = store_.slots_[slot_ix]; if (equal_(slot.first, key)) { - return iterator(&(store_.control_[c_control_stub + slot_ix]), - &slot); + return const_iterator(&(store_.control_[c_control_stub + slot_ix]), + &slot); } m &= (m - 1); diff --git a/xo-arena/include/xo/arena/hashmap/ControlGroup.hpp b/xo-arena/include/xo/arena/hashmap/ControlGroup.hpp index 7d3a129e..744ddb5b 100644 --- a/xo-arena/include/xo/arena/hashmap/ControlGroup.hpp +++ b/xo-arena/include/xo/arena/hashmap/ControlGroup.hpp @@ -21,7 +21,7 @@ namespace xo { std::array ctrl_; /** Require: lo is aligned on c_group_size (probably 16 bytes) **/ - explicit ControlGroup(uint8_t * lo) { + explicit ControlGroup(const uint8_t * lo) { ::memcpy(ctrl_.data(), lo, DArenaHashMapUtil::c_group_size); } diff --git a/xo-arena/include/xo/arena/hashmap/DArenaHashMapIterator.hpp b/xo-arena/include/xo/arena/hashmap/DArenaHashMapIterator.hpp index 57573b49..ea87c0ce 100644 --- a/xo-arena/include/xo/arena/hashmap/DArenaHashMapIterator.hpp +++ b/xo-arena/include/xo/arena/hashmap/DArenaHashMapIterator.hpp @@ -23,6 +23,9 @@ namespace xo { value_type & operator*() const { return *pos_; } value_type * operator->() const { return pos_; } + uint8_t * _ctrl() const { return ctrl_; } + value_type * _pos() const { return pos_; } + /** true iff iterator at sentinel position (not dereferencable state !) **/ bool _at_slot_sentinel() const { return is_sentinel(*ctrl_) && (*ctrl_ != c_iterator_bookend); } @@ -36,8 +39,8 @@ namespace xo { DArenaHashMapIterator & operator++() { do { - ++ctrl_; - ++pos_; + ++(this->ctrl_); + ++(this->pos_); /** end condition: iterator ends at last non-wrapped position. * relyin on bookend sentinel values at known offset from 'wrap' section @@ -62,8 +65,8 @@ namespace xo { * precedes control byte for first slot */ do { - --ctrl_; - --pos_; + --(this->ctrl_); + --(this->pos_); } while (is_sentinel(*ctrl_) && (*ctrl_ != c_iterator_bookend)); @@ -75,6 +78,74 @@ namespace xo { value_type * pos_ = nullptr; }; + template + struct DArenaHashMapConstIterator : public DArenaHashMapUtil { + using value_type = std::pair; + + public: + DArenaHashMapConstIterator(const uint8_t * c, const value_type * p) + : ctrl_{c}, pos_{p} {} + + const value_type & operator*() const { return *pos_; } + const value_type * operator->() const { return pos_; } + + const uint8_t * _ctrl() const { return ctrl_; } + const value_type * _pos() const { return pos_; } + + /** true iff iterator at sentinel position (not dereferencable state !) **/ + bool _at_slot_sentinel() const { + return is_sentinel(*ctrl_) && (*ctrl_ != c_iterator_bookend); + } + + bool operator==(const DArenaHashMapConstIterator & x) const { + return this->pos_ == x.pos_; + } + + bool operator!=(const DArenaHashMapConstIterator & x) const { + return this->pos_ != x.pos_; + } + + DArenaHashMapConstIterator & operator++() { + do { + ++(this->ctrl_); + ++(this->pos_); + + /** end condition: iterator ends at last non-wrapped position. + * relyin on bookend sentinel values at known offset from 'wrap' section + * + * ctrl_ ctrl_ + c_group_size + * | | + * v v + * <----------------- control_size(n_slot) ----------------> + * <-stub-> <----------- n_slot ----------> <-stub-> + * +--------+-------------------------------+-------+--------+ + * | 0xF0 | empty / data / tombstone | wrap | 0xF0 | + * +--------+-------------------------------+-------+--------+ + **/ + } while (is_sentinel(*ctrl_) + && (*(ctrl_ + c_group_size) != c_iterator_bookend)); + + return *this; + } + + DArenaHashMapConstIterator & operator--() { + /* simpler than forward iteration, since bookend immediately + * precedes control byte for first slot + */ + do { + --(this->ctrl_); + --(this->pos_); + } while (is_sentinel(*ctrl_) + && (*ctrl_ != c_iterator_bookend)); + + return *this; + } + + private: + const uint8_t * ctrl_ = nullptr; + const value_type * pos_ = nullptr; + }; } } /*namespace map*/ } /*namespace xo*/ diff --git a/xo-arena/include/xo/arena/hashmap/HashMapStore.hpp b/xo-arena/include/xo/arena/hashmap/HashMapStore.hpp index dd8ace12..661a6916 100644 --- a/xo-arena/include/xo/arena/hashmap/HashMapStore.hpp +++ b/xo-arena/include/xo/arena/hashmap/HashMapStore.hpp @@ -91,7 +91,7 @@ namespace xo { } /** load control group for slot range [ix .. ix+c_group_size) **/ - group_type _load_group(size_type ix) { + group_type _load_group(size_type ix) const { return group_type(&(control_[ix + c_control_stub])); }