From 93f64130b0ad21e418d8d8d5278c67c4de02c00b Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Thu, 8 Jan 2026 18:46:45 -0500 Subject: [PATCH] xo-arena: move HashMapStore to dedicated file --- xo-arena/include/xo/arena/DArenaHashMap.hpp | 117 +--------------- .../include/xo/arena/hashmap/HashMapStore.hpp | 132 ++++++++++++++++++ 2 files changed, 133 insertions(+), 116 deletions(-) create mode 100644 xo-arena/include/xo/arena/hashmap/HashMapStore.hpp diff --git a/xo-arena/include/xo/arena/DArenaHashMap.hpp b/xo-arena/include/xo/arena/DArenaHashMap.hpp index 77689f2f..75b2f958 100644 --- a/xo-arena/include/xo/arena/DArenaHashMap.hpp +++ b/xo-arena/include/xo/arena/DArenaHashMap.hpp @@ -7,8 +7,7 @@ #include "DArenaVector.hpp" #include "hashmap/verify_policy.hpp" -#include "hashmap/DArenaHashMapUtil.hpp" -#include "hashmap/ControlGroup.hpp" +#include "hashmap/HashMapStore.hpp" #include #include #include @@ -27,120 +26,6 @@ namespace xo { #endif namespace detail { - template - struct HashMapStore : DArenaHashMapUtil { - public: - using value_type = std::pair; - using group_type = detail::ControlGroup; - - 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_{DArenaVector::map(ArenaConfig{.size_ = control_size(n_slot_)})}, - slots_{DArenaVector::map(ArenaConfig{.size_ = n_slot_ * sizeof(value_type)})} - { - /* here: arenas have allocated address range, but no committed memory yet */ - - this->_init(); - } - - size_type empty() const noexcept { return size_ == 0; } - size_type capacity() const noexcept { return n_group_ * c_group_size; } - float load_factor() const noexcept { return size_ / static_cast(n_slot_); } - - void resize_from_empty(const std::pair & group_exp2) - { - assert(size_ == 0); - - this->n_group_exponent_ = group_exp2.first; - this->n_group_ = group_exp2.second; - this->n_slot_ = group_exp2.second * c_group_size; - - this->_init(); - } - - void clear() { - /* remark: discontinuity in the sense that we lose n_group_ = 2 ^ n_group_epxonent_ - * - * juice may not be worth the squeeze here, - * since DArena doesn't yet (Jan 2026) unmap on clear - */ - - this->size_ = 0; - this->n_group_exponent_ = 0; - this->n_group_ = 0; - this->n_slot_ = 0; - this->control_.resize(0); - this->slots_.resize(0); - } - - public: - void _init() { - this->control_.resize(control_size(n_slot_)); - - /* front stub: iterator bookend */ - std::fill(this->control_.begin(), - this->control_.begin() + c_control_stub, - c_iterator_bookend); - - /* all slots marked empty initially */ - std::fill(this->control_.begin() + c_control_stub, - this->control_.end() - c_control_stub, - c_empty_slot); - - /* end stub: iterator bookend */ - std::fill(this->control_.end() - c_control_stub, - this->control_.end(), - c_iterator_bookend); - - this->slots_.resize(n_slot_); - } - - /** load control group for slot range [ix .. ix+c_group_size) **/ - group_type _load_group(size_type ix) { - return group_type(&(control_[ix + c_control_stub])); - } - - /** update control group for slot number @p ix, replace with @p h2 **/ - void _update_control(size_type ix, uint8_t h2) { - this->control_[ix + c_control_stub] = h2; - - if (ix < c_group_size) { - size_type N = this->capacity(); - - // refresh end-of-array copy - std::memcpy(&(control_[N + c_control_stub]), - &(control_[c_control_stub]), - c_group_size); - } - } - - public: - /** number of pairs in this table **/ - size_type size_ = 0; - /** base-2 logarithm of n_group_ **/ - size_type n_group_exponent_ = 0; - /** table has capacity for this number of groups. - * always an exact power of two. - * number of slots is n_group_ * c_group_size - **/ - size_type n_group_ = (1 << n_group_exponent_); - /** table has capacity for this number of {key,value} pairs **/ - size_type n_slot_ = n_group_ * c_group_size; - /** control_[] partitioned into groups of - * c_group_size (16) consecutive elements - **/ - DArenaVector control_; - /** slots_[] holds {key,value} pairs **/ - DArenaVector slots_; - }; template diff --git a/xo-arena/include/xo/arena/hashmap/HashMapStore.hpp b/xo-arena/include/xo/arena/hashmap/HashMapStore.hpp new file mode 100644 index 00000000..130f4f54 --- /dev/null +++ b/xo-arena/include/xo/arena/hashmap/HashMapStore.hpp @@ -0,0 +1,132 @@ +/** @file HashMapStore.hpp +* + * @author Roland Conybeare, Jan 2026 + **/ + +#pragma once + +#include "hashmap/DArenaHashMapUtil.hpp" +#include "hashmap/ControlGroup.hpp" + +namespace xo { + namespace mm { + namespace detail { + template + struct HashMapStore : DArenaHashMapUtil { + public: + using value_type = std::pair; + using group_type = detail::ControlGroup; + + 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_{DArenaVector::map(ArenaConfig{.size_ = control_size(n_slot_)})}, + slots_{DArenaVector::map(ArenaConfig{.size_ = n_slot_ * sizeof(value_type)})} + { + /* here: arenas have allocated address range, but no committed memory yet */ + + this->_init(); + } + + size_type empty() const noexcept { return size_ == 0; } + size_type capacity() const noexcept { return n_group_ * c_group_size; } + float load_factor() const noexcept { return size_ / static_cast(n_slot_); } + + void resize_from_empty(const std::pair & group_exp2) + { + assert(size_ == 0); + + this->n_group_exponent_ = group_exp2.first; + this->n_group_ = group_exp2.second; + this->n_slot_ = group_exp2.second * c_group_size; + + this->_init(); + } + + void clear() { + /* remark: discontinuity in the sense that we lose n_group_ = 2 ^ n_group_epxonent_ + * + * juice may not be worth the squeeze here, + * since DArena doesn't yet (Jan 2026) unmap on clear + */ + + this->size_ = 0; + this->n_group_exponent_ = 0; + this->n_group_ = 0; + this->n_slot_ = 0; + this->control_.resize(0); + this->slots_.resize(0); + } + + public: + void _init() { + this->control_.resize(control_size(n_slot_)); + + /* front stub: iterator bookend */ + std::fill(this->control_.begin(), + this->control_.begin() + c_control_stub, + c_iterator_bookend); + + /* all slots marked empty initially */ + std::fill(this->control_.begin() + c_control_stub, + this->control_.end() - c_control_stub, + c_empty_slot); + + /* end stub: iterator bookend */ + std::fill(this->control_.end() - c_control_stub, + this->control_.end(), + c_iterator_bookend); + + this->slots_.resize(n_slot_); + } + + /** load control group for slot range [ix .. ix+c_group_size) **/ + group_type _load_group(size_type ix) { + return group_type(&(control_[ix + c_control_stub])); + } + + /** update control group for slot number @p ix, replace with @p h2 **/ + void _update_control(size_type ix, uint8_t h2) { + this->control_[ix + c_control_stub] = h2; + + if (ix < c_group_size) { + size_type N = this->capacity(); + + // refresh end-of-array copy + std::memcpy(&(control_[N + c_control_stub]), + &(control_[c_control_stub]), + c_group_size); + } + } + + public: + /** number of pairs in this table **/ + size_type size_ = 0; + /** base-2 logarithm of n_group_ **/ + size_type n_group_exponent_ = 0; + /** table has capacity for this number of groups. + * always an exact power of two. + * number of slots is n_group_ * c_group_size + **/ + size_type n_group_ = (1 << n_group_exponent_); + /** table has capacity for this number of {key,value} pairs **/ + size_type n_slot_ = n_group_ * c_group_size; + /** control_[] partitioned into groups of + * c_group_size (16) consecutive elements + **/ + DArenaVector control_; + /** slots_[] holds {key,value} pairs **/ + DArenaVector slots_; + }; + } + } /*namespace mm*/ +} /*namespace xo*/ + +/* end HashMapStore.hpp */