xo-arena: + const_iterator support for DValueHashMap

This commit is contained in:
Roland Conybeare 2026-01-15 20:32:10 -05:00
commit 7aa232f1ea
4 changed files with 124 additions and 31 deletions

View file

@ -50,7 +50,9 @@ namespace xo {
using store_type = detail::HashMapStore<Key, Value>;
using insert_value_type = std::pair<value_type *, bool>;
using iterator = detail::DArenaHashMapIterator<Key, Value>;
using const_iterator = detail::DArenaHashMapConstIterator<Key, Value>;
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<uint8_t *>(ix._ctrl()),
const_cast<value_type *>(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<Key, Value, Hash, Equal>::find(const key_type & key) -> iterator
DArenaHashMap<Key, Value, Hash, Equal>::_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,7 +465,7 @@ namespace xo {
auto & slot = store_.slots_[slot_ix];
if (equal_(slot.first, key)) {
return iterator(&(store_.control_[c_control_stub + slot_ix]),
return const_iterator(&(store_.control_[c_control_stub + slot_ix]),
&slot);
}

View file

@ -21,7 +21,7 @@ namespace xo {
std::array<uint8_t, DArenaHashMapUtil::c_group_size> 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);
}

View file

@ -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 <typename Key,
typename Value>
struct DArenaHashMapConstIterator : public DArenaHashMapUtil {
using value_type = std::pair<const Key, Value>;
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 ----------> <group> <-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*/

View file

@ -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]));
}