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 store_type = detail::HashMapStore<Key, Value>;
using insert_value_type = std::pair<value_type *, bool>; using insert_value_type = std::pair<value_type *, bool>;
using iterator = detail::DArenaHashMapIterator<Key, Value>; using iterator = detail::DArenaHashMapIterator<Key, Value>;
using const_iterator = detail::DArenaHashMapConstIterator<Key, Value>;
public:
/** create hash map **/ /** create hash map **/
DArenaHashMap(size_type hint_max_capacity, DArenaHashMap(size_type hint_max_capacity,
bool debug_flag = false); bool debug_flag = false);
@ -71,28 +73,14 @@ namespace xo {
**/ **/
bool verify_ok(verify_policy p = verify_policy::throw_only()) const; bool verify_ok(verify_policy p = verify_policy::throw_only()) const;
iterator begin() { const_iterator cbegin() const { return this->_begin_aux(); }
if (this->empty()) [[unlikely]] { const_iterator cend() const { return this->_end_aux(); }
return this->end();
}
iterator ix(&(store_.control_[c_control_stub]), const_iterator begin() const { return this->_begin_aux(); }
&(store_.slots_[0])); const_iterator end() const { return this->_end_aux(); }
if (ix._at_slot_sentinel()) { iterator begin() { return _promote_iterator(_begin_aux()); }
/* advance to first occupied position in table */ iterator end() { return _promote_iterator(_end_aux()); }
++ix;
}
return ix;
}
iterator end() {
iterator ix(&(store_.control_[c_control_stub + store_.capacity()]),
&(store_.slots_[store_.capacity()]));
return ix;
}
/** insert @p kv_pair into hash map. /** insert @p kv_pair into hash map.
* Replaces any previous value stored under the same key. * Replaces any previous value stored under the same key.
@ -117,12 +105,46 @@ namespace xo {
/** find element with key @p key. /** find element with key @p key.
* @return iterator to element if found, end() otherwise * @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 **/ /** establish kv pair for @p key in this table; return address of value part **/
mapped_type & operator[](const key_type & key); mapped_type & operator[](const key_type & key);
private: 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, /** insert @p kv_pair,
* where key hashes to @p hash_value, into @p *store * where key hashes to @p hash_value, into @p *store
**/ **/
@ -416,12 +438,12 @@ namespace xo {
typename Hash, typename Hash,
typename Equal> typename Equal>
auto 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(); size_type N = store_.capacity();
if (N == 0) [[unlikely]] { if (N == 0) [[unlikely]] {
return this->end(); return this->cend();
} }
size_type h = hash_(key); size_type h = hash_(key);
@ -443,8 +465,8 @@ namespace xo {
auto & slot = store_.slots_[slot_ix]; auto & slot = store_.slots_[slot_ix];
if (equal_(slot.first, key)) { 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); &slot);
} }
m &= (m - 1); m &= (m - 1);

View file

@ -21,7 +21,7 @@ namespace xo {
std::array<uint8_t, DArenaHashMapUtil::c_group_size> ctrl_; std::array<uint8_t, DArenaHashMapUtil::c_group_size> ctrl_;
/** Require: lo is aligned on c_group_size (probably 16 bytes) **/ /** 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); ::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_; }
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 !) **/ /** true iff iterator at sentinel position (not dereferencable state !) **/
bool _at_slot_sentinel() const { return is_sentinel(*ctrl_) && (*ctrl_ != c_iterator_bookend); } bool _at_slot_sentinel() const { return is_sentinel(*ctrl_) && (*ctrl_ != c_iterator_bookend); }
@ -36,8 +39,8 @@ namespace xo {
DArenaHashMapIterator & operator++() { DArenaHashMapIterator & operator++() {
do { do {
++ctrl_; ++(this->ctrl_);
++pos_; ++(this->pos_);
/** end condition: iterator ends at last non-wrapped position. /** end condition: iterator ends at last non-wrapped position.
* relyin on bookend sentinel values at known offset from 'wrap' section * relyin on bookend sentinel values at known offset from 'wrap' section
@ -62,8 +65,8 @@ namespace xo {
* precedes control byte for first slot * precedes control byte for first slot
*/ */
do { do {
--ctrl_; --(this->ctrl_);
--pos_; --(this->pos_);
} while (is_sentinel(*ctrl_) } while (is_sentinel(*ctrl_)
&& (*ctrl_ != c_iterator_bookend)); && (*ctrl_ != c_iterator_bookend));
@ -75,6 +78,74 @@ namespace xo {
value_type * pos_ = nullptr; 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 map*/
} /*namespace xo*/ } /*namespace xo*/

View file

@ -91,7 +91,7 @@ namespace xo {
} }
/** load control group for slot range [ix .. ix+c_group_size) **/ /** 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])); return group_type(&(control_[ix + c_control_stub]));
} }