diff --git a/changelog.d/20260420_184500_forest_domain_protocol.md b/changelog.d/20260420_184500_forest_domain_protocol.md new file mode 100644 index 00000000..ad62077f --- /dev/null +++ b/changelog.d/20260420_184500_forest_domain_protocol.md @@ -0,0 +1,8 @@ +--- +bump: patch +--- + +### Changed +- Made `ForestDomainViewOps`/`ForestDomainOps` the canonical read-only/mutable protocol surfaces for + AVL-backed forest domains and moved `pmap` onto them. +- Routed symbol and legacy root access through the canonical forest-domain root-index helpers. diff --git a/include/pmm/avl_tree_mixin.h b/include/pmm/avl_tree_mixin.h index 3433b759..5df06401 100644 --- a/include/pmm/avl_tree_mixin.h +++ b/include/pmm/avl_tree_mixin.h @@ -512,48 +512,95 @@ static void avl_insert( PPtr new_node, IndexType& root_idx, GoLeftFn&& go_left, avl_rebalance_up( parent, root_idx, update_node ); } -// ─── Forest-domain descriptor/policy seam ──────────────────────────────────── +// ─── Forest-domain protocol ────────────────────────────────────────────────── -template -concept ForestDomainDescriptorForKey = requires( typename Domain::node_pptr p, const Key& key ) { +template +concept ForestDomainViewDescriptor = requires( const Domain domain, typename Domain::node_pptr p ) { typename Domain::index_type; typename Domain::node_type; typename Domain::node_pptr; - { Domain::name() } -> std::convertible_to; - { Domain::root_index() } -> std::convertible_to; - { Domain::root_index_ptr() } -> std::same_as; - { Domain::resolve_node( p ) } -> std::convertible_to; - { Domain::compare_key( key, p ) } -> std::convertible_to; - { Domain::less_node( p, p ) } -> std::convertible_to; + { domain.name() } -> std::convertible_to; + { domain.root_index() } -> std::convertible_to; + { domain.resolve_node( p ) } -> std::convertible_to; }; -template static bool forest_domain_validate_node( typename Domain::node_pptr p ) noexcept +template +concept ForestDomainDescriptor = + ForestDomainViewDescriptor && requires( Domain domain, typename Domain::node_pptr p ) { + { domain.root_index_ptr() } -> std::same_as; + { domain.less_node( p, p ) } -> std::convertible_to; + }; + +template +concept ForestDomainDescriptorForKey = ForestDomainViewDescriptor && + requires( const Domain domain, typename Domain::node_pptr p, const Key& key ) { + { domain.compare_key( key, p ) } -> std::convertible_to; + }; + +template +static bool forest_domain_validate_node( const Domain& domain, typename Domain::node_pptr p ) noexcept { if constexpr ( requires { - { Domain::validate_node( p ) } -> std::convertible_to; + { domain.validate_node( p ) } -> std::convertible_to; } ) - return Domain::validate_node( p ); + return domain.validate_node( p ); else return true; } /** - * @brief Generic AVL-backed forest-domain operations for a concrete descriptor. + * @brief Generic read-only AVL-backed forest-domain operations for a concrete descriptor. * - * The descriptor owns domain identity, root binding, node resolution, ordering, - * and optional node validation. This wrapper keeps the AVL substrate reusable - * without forcing allocator and non-allocator domains into the same runtime type. + * The view descriptor supplies domain identity, read-only root binding, node + * resolution, and optional external-key comparison. */ -template struct ForestDomainOps +template struct ForestDomainViewOps { using index_type = typename Domain::index_type; + using node_type = typename Domain::node_type; using node_pptr = typename Domain::node_pptr; - static constexpr const char* name() noexcept { return Domain::name(); } - static index_type root_index() noexcept { return Domain::root_index(); } - static index_type* root_index_ptr() noexcept { return Domain::root_index_ptr(); } + Domain domain; - static bool reset_root() noexcept + constexpr explicit ForestDomainViewOps( Domain d = Domain{} ) noexcept : domain( d ) {} + + const char* name() const noexcept { return domain.name(); } + index_type root_index() const noexcept { return domain.root_index(); } + + template + requires ForestDomainDescriptorForKey + node_pptr find( const Key& key ) const noexcept + { + return avl_find( + domain.root_index(), [&]( node_pptr cur ) -> int { return domain.compare_key( key, cur ); }, + [this]( node_pptr p ) -> node_type* { return domain.resolve_node( p ); } ); + } +}; + +/** + * @brief Generic mutable AVL-backed forest-domain operations for a concrete descriptor. + * + * The mutable descriptor adds root-slot access and node ordering. Mutation is + * intentionally kept off the const surface: callers that only have a const + * handle can read identity/root state and perform keyed lookup, but cannot + * obtain or rewrite the root slot. + */ +template struct ForestDomainOps : ForestDomainViewOps +{ + using view_base = ForestDomainViewOps; + using index_type = typename view_base::index_type; + using node_type = typename view_base::node_type; + using node_pptr = typename view_base::node_pptr; + + using view_base::find; + using view_base::name; + using view_base::root_index; + + constexpr explicit ForestDomainOps( Domain d = Domain{} ) noexcept : view_base( d ) {} + + index_type* root_index_ptr() noexcept { return this->domain.root_index_ptr(); } + + bool reset_root() noexcept { index_type* root = root_index_ptr(); if ( root == nullptr ) @@ -562,25 +609,18 @@ template struct ForestDomainOps return true; } - template - requires ForestDomainDescriptorForKey - static node_pptr find( const Key& key ) noexcept - { - return avl_find( - Domain::root_index(), [&]( node_pptr cur ) -> int { return Domain::compare_key( key, cur ); }, - []( node_pptr p ) -> typename Domain::node_type* { return Domain::resolve_node( p ); } ); - } - - static void insert( node_pptr new_node ) noexcept + void insert( node_pptr new_node ) noexcept { - index_type* root = Domain::root_index_ptr(); + index_type* root = this->domain.root_index_ptr(); if ( root == nullptr || new_node.is_null() ) return; - if ( Domain::resolve_node( new_node ) == nullptr || !forest_domain_validate_node( new_node ) ) + if ( this->domain.resolve_node( new_node ) == nullptr || + !forest_domain_validate_node( this->domain, new_node ) ) return; avl_insert( - new_node, *root, [new_node]( node_pptr cur ) -> bool { return Domain::less_node( new_node, cur ); }, - []( node_pptr p ) -> typename Domain::node_type* { return Domain::resolve_node( p ); } ); + new_node, *root, + [this, new_node]( node_pptr cur ) -> bool { return this->domain.less_node( new_node, cur ); }, + [this]( node_pptr p ) -> node_type* { return this->domain.resolve_node( p ); } ); } }; diff --git a/include/pmm/forest_domain_mixin.inc b/include/pmm/forest_domain_mixin.inc index f20b5937..d8433777 100644 --- a/include/pmm/forest_domain_mixin.inc +++ b/include/pmm/forest_domain_mixin.inc @@ -85,9 +85,10 @@ static forest_domain* find_domain_by_symbol_unlocked( pptr symbol ) return nullptr; } -static index_type domain_root_offset_unlocked( const forest_domain* rec, - const detail::ManagerHeader* hdr ) noexcept +static index_type forest_domain_root_index_unlocked( const forest_domain* rec ) noexcept { + const detail::ManagerHeader* hdr = + ( _backend.base_ptr() != nullptr ) ? get_header_c( _backend.base_ptr() ) : nullptr; if ( rec == nullptr || hdr == nullptr ) return 0; if ( rec->binding_kind == detail::kForestBindingFreeTree ) @@ -95,34 +96,29 @@ static index_type domain_root_offset_unlocked( const forest_domain* return rec->root_offset; } -// ─── Legacy root helpers ────────────────────────────────────────────────────── - -static index_type get_legacy_root_offset_unlocked() noexcept +static index_type* forest_domain_root_index_ptr_unlocked( forest_domain* rec ) noexcept { - const forest_domain* rec = find_domain_by_name_unlocked( detail::kServiceNameLegacyRoot ); - return domain_root_offset_unlocked( rec, get_header_c( _backend.base_ptr() ) ); + if ( rec == nullptr || rec->binding_kind != detail::kForestBindingDirectRoot ) + return nullptr; + return &rec->root_offset; } -static void set_legacy_root_offset_unlocked( index_type off ) noexcept +static bool set_forest_domain_root_index_unlocked( forest_domain* rec, index_type root ) noexcept { - forest_domain* rec = find_domain_by_name_unlocked( detail::kServiceNameLegacyRoot ); - if ( rec != nullptr && rec->binding_kind == detail::kForestBindingDirectRoot ) - rec->root_offset = off; + index_type* root_ptr = forest_domain_root_index_ptr_unlocked( rec ); + if ( root_ptr == nullptr ) + return false; + *root_ptr = root; + return true; } -// ─── Symbol domain helpers ──────────────────────────────────────────────────── +// ─── Canonical system domain records ───────────────────────────────────────── static forest_domain* symbol_domain_record_unlocked() noexcept { return find_domain_by_name_unlocked( detail::kSystemDomainSymbols ); } -static index_type symbol_domain_root_offset_unlocked() noexcept -{ - forest_domain* rec = symbol_domain_record_unlocked(); - return ( rec != nullptr ) ? rec->root_offset : static_cast( 0 ); -} - // ─── Domain registration ───────────────────────────────────────────────────── static bool register_domain_unlocked( const char* name, std::uint8_t flags, std::uint8_t binding_kind, @@ -179,11 +175,11 @@ static pptr intern_symbol_unlocked( const char* s ) noexcept if ( s == nullptr ) s = ""; - using symbol_policy = typename pstringview::forest_domain_policy; - if ( symbol_policy::root_index_ptr() == nullptr ) + auto symbol_policy = pstringview::forest_domain_ops(); + if ( symbol_policy.root_index_ptr() == nullptr ) return pptr(); - pptr found = symbol_policy::find( s ); + pptr found = symbol_policy.find( s ); if ( !found.is_null() ) return found; @@ -209,7 +205,7 @@ static pptr intern_symbol_unlocked( const char* s ) noexcept if ( !lock_block_permanent_unlocked( public_raw ) ) return pptr(); - symbol_policy::insert( new_node ); + symbol_policy.insert( new_node ); return new_node; } @@ -375,7 +371,7 @@ static bool validate_bootstrap_invariants_unlocked() noexcept if ( free_rec->binding_kind != detail::kForestBindingFreeTree ) return false; // 5. Symbol dictionary root is non-zero (at least bootstrap symbols exist) - if ( symbol_domain_root_offset_unlocked() == 0 ) + if ( pstringview::forest_domain_ops().root_index() == 0 ) return false; // 6. Registry domain root matches header root_offset const forest_domain* reg_rec = find_domain_by_name_unlocked( detail::kSystemDomainRegistry ); @@ -404,7 +400,8 @@ static bool validate_or_bootstrap_forest_registry_unlocked() noexcept detail::kForestBindingFreeTree, 0 ) ) return false; if ( !register_domain_unlocked( detail::kSystemDomainSymbols, detail::kForestDomainFlagSystem, - detail::kForestBindingDirectRoot, symbol_domain_root_offset_unlocked() ) ) + detail::kForestBindingDirectRoot, + pstringview::forest_domain_ops().root_index() ) ) return false; if ( !register_domain_unlocked( detail::kSystemDomainRegistry, detail::kForestDomainFlagSystem, detail::kForestBindingDirectRoot, hdr->root_offset ) ) diff --git a/include/pmm/persist_memory_manager.h b/include/pmm/persist_memory_manager.h index 225e7f78..5871f6c5 100644 --- a/include/pmm/persist_memory_manager.h +++ b/include/pmm/persist_memory_manager.h @@ -517,7 +517,8 @@ class PersistMemoryManager : public detail::PersistMemoryTypedApi( 0 ) : p.offset() ); + set_forest_domain_root_index_unlocked( find_domain_by_name_unlocked( detail::kServiceNameLegacyRoot ), + p.is_null() ? static_cast( 0 ) : p.offset() ); } /** @@ -531,7 +532,8 @@ class PersistMemoryManager : public detail::PersistMemoryTypedApi(); - index_type legacy_root = get_legacy_root_offset_unlocked(); + index_type legacy_root = + forest_domain_root_index_unlocked( find_domain_by_name_unlocked( detail::kServiceNameLegacyRoot ) ); if ( legacy_root == static_cast( 0 ) ) return pptr(); return pptr( legacy_root ); @@ -587,7 +589,7 @@ class PersistMemoryManager : public detail::PersistMemoryTypedApi symbol ) noexcept @@ -605,7 +607,7 @@ class PersistMemoryManager : public detail::PersistMemoryTypedApi static pptr get_domain_root( const char* name ) noexcept @@ -632,10 +634,8 @@ class PersistMemoryManager : public detail::PersistMemoryTypedApibinding_kind != detail::kForestBindingDirectRoot ) - return false; - rec->root_offset = root.is_null() ? static_cast( 0 ) : root.offset(); - return true; + return set_forest_domain_root_index_unlocked( rec, + root.is_null() ? static_cast( 0 ) : root.offset() ); } // ─── Методы доступа к полям AVL-узла блока ───────────── diff --git a/include/pmm/pmap.h b/include/pmm/pmap.h index 790c76b9..b259848b 100644 --- a/include/pmm/pmap.h +++ b/include/pmm/pmap.h @@ -138,6 +138,54 @@ template struct pmap using node_type = pmap_node<_K, _V>; using node_pptr = typename ManagerT::template pptr; + struct forest_domain_descriptor + { + using index_type = typename ManagerT::index_type; + using node_type = pmap_node<_K, _V>; + using node_pptr = typename ManagerT::template pptr; + + const index_type* root_index_slot; + index_type* mutable_root_index_slot; + + constexpr explicit forest_domain_descriptor( index_type* root = nullptr ) noexcept + : root_index_slot( root ), mutable_root_index_slot( root ) + { + } + + constexpr explicit forest_domain_descriptor( const index_type* root ) noexcept + : root_index_slot( root ), mutable_root_index_slot( nullptr ) + { + } + + static constexpr const char* name() noexcept { return "container/pmap"; } + + index_type root_index() const noexcept { return root_index_slot != nullptr ? *root_index_slot : 0; } + + index_type* root_index_ptr() noexcept { return mutable_root_index_slot; } + + static node_type* resolve_node( node_pptr p ) noexcept { return ManagerT::template resolve( p ); } + + static int compare_key( const _K& key, node_pptr cur ) noexcept + { + node_type* obj = resolve_node( cur ); + if ( obj == nullptr ) + return 0; + return ( key == obj->key ) ? 0 : ( ( key < obj->key ) ? -1 : 1 ); + } + + static bool less_node( node_pptr lhs, node_pptr rhs ) noexcept + { + node_type* lhs_obj = resolve_node( lhs ); + node_type* rhs_obj = resolve_node( rhs ); + return lhs_obj != nullptr && rhs_obj != nullptr && lhs_obj->key < rhs_obj->key; + } + + static bool validate_node( node_pptr p ) noexcept { return resolve_node( p ) != nullptr; } + }; + + using forest_domain_view_policy = detail::ForestDomainViewOps; + using forest_domain_policy = detail::ForestDomainOps; + /// @brief Sentinel value for "no node" in TreeNode fields. static constexpr index_type no_block = ManagerT::address_traits::no_block; @@ -151,6 +199,16 @@ template struct pmap // ─── Методы доступа ─────────────────────────────────────────────────────── + forest_domain_policy forest_domain_ops() noexcept + { + return forest_domain_policy( forest_domain_descriptor( &_root_idx ) ); + } + + forest_domain_view_policy forest_domain_view_ops() const noexcept + { + return forest_domain_view_policy( forest_domain_descriptor( &_root_idx ) ); + } + /// @brief Проверить, пуст ли словарь. bool empty() const noexcept { return _root_idx == static_cast( 0 ); } @@ -177,8 +235,10 @@ template struct pmap */ node_pptr insert( const _K& key, const _V& val ) noexcept { + auto ops = forest_domain_ops(); + // Ищем существующий узел. - node_pptr existing = _avl_find( key ); + node_pptr existing = ops.find( key ); if ( !existing.is_null() ) { // Ключ найден — обновляем значение. @@ -204,7 +264,7 @@ template struct pmap detail::avl_init_node( new_node ); // Вставляем в AVL-дерево. - _avl_insert( new_node ); + ops.insert( new_node ); return new_node; } @@ -215,7 +275,7 @@ template struct pmap * @param key Ключ для поиска. * @return pptr на найденный узел, или нулевой pptr если не найден. */ - node_pptr find( const _K& key ) const noexcept { return _avl_find( key ); } + node_pptr find( const _K& key ) const noexcept { return forest_domain_view_ops().find( key ); } /** * @brief Проверить, содержит ли словарь заданный ключ. @@ -223,7 +283,7 @@ template struct pmap * @param key Ключ для проверки. * @return true если ключ найден. */ - bool contains( const _K& key ) const noexcept { return !_avl_find( key ).is_null(); } + bool contains( const _K& key ) const noexcept { return !forest_domain_view_ops().find( key ).is_null(); } /** * @brief Удалить узел по ключу. @@ -236,7 +296,7 @@ template struct pmap */ bool erase( const _K& key ) noexcept { - node_pptr target = _avl_find( key ); + node_pptr target = forest_domain_ops().find( key ); if ( target.is_null() ) return false; @@ -263,7 +323,7 @@ template struct pmap * * Сбрасывает _root_idx, но не освобождает данные в ПАП. */ - void reset() noexcept { _root_idx = static_cast( 0 ); } + void reset() noexcept { forest_domain_ops().reset_root(); } // ─── Итератор ─────────────────────────────────────────────── @@ -282,43 +342,6 @@ template struct pmap /// @brief Конец итерации (sentinel = 0). iterator end() const noexcept { return iterator( static_cast( 0 ) ); } - - // ─── AVL-дерево (использует встроенные TreeNode-поля каждого узла) ──────── - - private: - /// @brief Найти узел AVL-дерева с заданным ключом. Возвращает null если не найден. - node_pptr _avl_find( const _K& key ) const noexcept - { - return detail::avl_find( - _root_idx, - [&]( node_pptr cur ) -> int - { - node_type* obj = ManagerT::template resolve( cur ); - if ( obj == nullptr ) - return 0; - if ( key == obj->key ) - return 0; - return ( key < obj->key ) ? -1 : 1; - }, - []( node_pptr p ) -> node_type* { return ManagerT::template resolve( p ); } ); - } - - /// @brief Вставить новый узел в AVL-дерево. Предполагается, что ключ ещё не в дереве. - void _avl_insert( node_pptr new_node ) noexcept - { - node_type* new_obj = ManagerT::template resolve( new_node ); - detail::avl_insert( - new_node, _root_idx, - [&]( node_pptr cur ) -> bool - { - node_type* obj = ManagerT::template resolve( cur ); - return ( obj != nullptr ) && ( new_obj->key < obj->key ); - }, - []( node_pptr p ) -> node_type* { return ManagerT::template resolve( p ); } ); - } - - // _subtree_count and _clear_subtree replaced by shared - // detail::avl_subtree_count and detail::avl_clear_subtree. }; } // namespace pmm diff --git a/include/pmm/pstringview.h b/include/pmm/pstringview.h index 469b0e0f..6bfd2b7a 100644 --- a/include/pmm/pstringview.h +++ b/include/pmm/pstringview.h @@ -113,13 +113,13 @@ template struct pstringview static index_type root_index() noexcept { auto* domain = ManagerT::symbol_domain_record_unlocked(); - return ( domain != nullptr ) ? domain->root_offset : static_cast( 0 ); + return ManagerT::forest_domain_root_index_unlocked( domain ); } static index_type* root_index_ptr() noexcept { auto* domain = ManagerT::symbol_domain_record_unlocked(); - return ( domain != nullptr ) ? &domain->root_offset : nullptr; + return ManagerT::forest_domain_root_index_ptr_unlocked( domain ); } static node_type* resolve_node( node_pptr p ) noexcept { return ManagerT::template resolve( p ); } @@ -144,6 +144,8 @@ template struct pstringview using forest_domain_policy = detail::ForestDomainOps; + static forest_domain_policy forest_domain_ops() noexcept { return forest_domain_policy{}; } + std::uint32_t length; ///< Длина строки (без нулевого терминатора) char str[1]; ///< Строковые данные (flexible array member pattern) @@ -240,7 +242,7 @@ template struct pstringview if ( !ManagerT::is_initialized() ) return; typename ManagerT::thread_policy::unique_lock_type lock( ManagerT::_mutex ); - forest_domain_policy::reset_root(); + forest_domain_ops().reset_root(); } /// @brief Текущий persistent root словаря интернирования; 0 = пустое дерево. @@ -249,7 +251,7 @@ template struct pstringview if ( !ManagerT::is_initialized() ) return static_cast( 0 ); typename ManagerT::thread_policy::shared_lock_type lock( ManagerT::_mutex ); - return forest_domain_policy::root_index(); + return forest_domain_ops().root_index(); } // Public destructor required for stack-temporary construction via pstringview("hello"). @@ -265,8 +267,10 @@ template struct pstringview if ( s == nullptr ) s = ""; + auto ops = forest_domain_ops(); + // Ищем в AVL-дереве. - psview_pptr found = _avl_find( s ); + psview_pptr found = ops.find( s ); if ( !found.is_null() ) return found; @@ -324,18 +328,10 @@ template struct pstringview ManagerT::lock_block_permanent( obj ); // Вставляем в AVL-дерево. - _avl_insert( new_node ); + ops.insert( new_node ); return new_node; } - - // ─── AVL-дерево (использует встроенные TreeNode-поля каждого pstringview-блока) ─ - - /// @brief Найти узел AVL-дерева с заданной строкой. Возвращает null если не найден. - static psview_pptr _avl_find( const char* s ) noexcept { return forest_domain_policy::find( s ); } - - /// @brief Вставить новый узел в AVL-дерево. Предполагается, что строка ещё не в дереве. - static void _avl_insert( psview_pptr new_node ) noexcept { forest_domain_policy::insert( new_node ); } }; } // namespace pmm diff --git a/single_include/pmm/pmm.h b/single_include/pmm/pmm.h index 286d1e3c..14cf7e1b 100644 --- a/single_include/pmm/pmm.h +++ b/single_include/pmm/pmm.h @@ -3181,48 +3181,95 @@ static void avl_insert( PPtr new_node, IndexType& root_idx, GoLeftFn&& go_left, avl_rebalance_up( parent, root_idx, update_node ); } -// ─── Forest-domain descriptor/policy seam ──────────────────────────────────── +// ─── Forest-domain protocol ────────────────────────────────────────────────── -template -concept ForestDomainDescriptorForKey = requires( typename Domain::node_pptr p, const Key& key ) { +template +concept ForestDomainViewDescriptor = requires( const Domain domain, typename Domain::node_pptr p ) { typename Domain::index_type; typename Domain::node_type; typename Domain::node_pptr; - { Domain::name() } -> std::convertible_to; - { Domain::root_index() } -> std::convertible_to; - { Domain::root_index_ptr() } -> std::same_as; - { Domain::resolve_node( p ) } -> std::convertible_to; - { Domain::compare_key( key, p ) } -> std::convertible_to; - { Domain::less_node( p, p ) } -> std::convertible_to; + { domain.name() } -> std::convertible_to; + { domain.root_index() } -> std::convertible_to; + { domain.resolve_node( p ) } -> std::convertible_to; }; -template static bool forest_domain_validate_node( typename Domain::node_pptr p ) noexcept +template +concept ForestDomainDescriptor = + ForestDomainViewDescriptor && requires( Domain domain, typename Domain::node_pptr p ) { + { domain.root_index_ptr() } -> std::same_as; + { domain.less_node( p, p ) } -> std::convertible_to; + }; + +template +concept ForestDomainDescriptorForKey = ForestDomainViewDescriptor && + requires( const Domain domain, typename Domain::node_pptr p, const Key& key ) { + { domain.compare_key( key, p ) } -> std::convertible_to; + }; + +template +static bool forest_domain_validate_node( const Domain& domain, typename Domain::node_pptr p ) noexcept { if constexpr ( requires { - { Domain::validate_node( p ) } -> std::convertible_to; + { domain.validate_node( p ) } -> std::convertible_to; } ) - return Domain::validate_node( p ); + return domain.validate_node( p ); else return true; } /** - * @brief Generic AVL-backed forest-domain operations for a concrete descriptor. + * @brief Generic read-only AVL-backed forest-domain operations for a concrete descriptor. * - * The descriptor owns domain identity, root binding, node resolution, ordering, - * and optional node validation. This wrapper keeps the AVL substrate reusable - * without forcing allocator and non-allocator domains into the same runtime type. + * The view descriptor supplies domain identity, read-only root binding, node + * resolution, and optional external-key comparison. */ -template struct ForestDomainOps +template struct ForestDomainViewOps { using index_type = typename Domain::index_type; + using node_type = typename Domain::node_type; using node_pptr = typename Domain::node_pptr; - static constexpr const char* name() noexcept { return Domain::name(); } - static index_type root_index() noexcept { return Domain::root_index(); } - static index_type* root_index_ptr() noexcept { return Domain::root_index_ptr(); } + Domain domain; + + constexpr explicit ForestDomainViewOps( Domain d = Domain{} ) noexcept : domain( d ) {} + + const char* name() const noexcept { return domain.name(); } + index_type root_index() const noexcept { return domain.root_index(); } + + template + requires ForestDomainDescriptorForKey + node_pptr find( const Key& key ) const noexcept + { + return avl_find( + domain.root_index(), [&]( node_pptr cur ) -> int { return domain.compare_key( key, cur ); }, + [this]( node_pptr p ) -> node_type* { return domain.resolve_node( p ); } ); + } +}; + +/** + * @brief Generic mutable AVL-backed forest-domain operations for a concrete descriptor. + * + * The mutable descriptor adds root-slot access and node ordering. Mutation is + * intentionally kept off the const surface: callers that only have a const + * handle can read identity/root state and perform keyed lookup, but cannot + * obtain or rewrite the root slot. + */ +template struct ForestDomainOps : ForestDomainViewOps +{ + using view_base = ForestDomainViewOps; + using index_type = typename view_base::index_type; + using node_type = typename view_base::node_type; + using node_pptr = typename view_base::node_pptr; + + using view_base::find; + using view_base::name; + using view_base::root_index; + + constexpr explicit ForestDomainOps( Domain d = Domain{} ) noexcept : view_base( d ) {} + + index_type* root_index_ptr() noexcept { return this->domain.root_index_ptr(); } - static bool reset_root() noexcept + bool reset_root() noexcept { index_type* root = root_index_ptr(); if ( root == nullptr ) @@ -3231,25 +3278,18 @@ template struct ForestDomainOps return true; } - template - requires ForestDomainDescriptorForKey - static node_pptr find( const Key& key ) noexcept - { - return avl_find( - Domain::root_index(), [&]( node_pptr cur ) -> int { return Domain::compare_key( key, cur ); }, - []( node_pptr p ) -> typename Domain::node_type* { return Domain::resolve_node( p ); } ); - } - - static void insert( node_pptr new_node ) noexcept + void insert( node_pptr new_node ) noexcept { - index_type* root = Domain::root_index_ptr(); + index_type* root = this->domain.root_index_ptr(); if ( root == nullptr || new_node.is_null() ) return; - if ( Domain::resolve_node( new_node ) == nullptr || !forest_domain_validate_node( new_node ) ) + if ( this->domain.resolve_node( new_node ) == nullptr || + !forest_domain_validate_node( this->domain, new_node ) ) return; avl_insert( - new_node, *root, [new_node]( node_pptr cur ) -> bool { return Domain::less_node( new_node, cur ); }, - []( node_pptr p ) -> typename Domain::node_type* { return Domain::resolve_node( p ); } ); + new_node, *root, + [this, new_node]( node_pptr cur ) -> bool { return this->domain.less_node( new_node, cur ); }, + [this]( node_pptr p ) -> node_type* { return this->domain.resolve_node( p ); } ); } }; @@ -6050,6 +6090,54 @@ template struct pmap using node_type = pmap_node<_K, _V>; using node_pptr = typename ManagerT::template pptr; + struct forest_domain_descriptor + { + using index_type = typename ManagerT::index_type; + using node_type = pmap_node<_K, _V>; + using node_pptr = typename ManagerT::template pptr; + + const index_type* root_index_slot; + index_type* mutable_root_index_slot; + + constexpr explicit forest_domain_descriptor( index_type* root = nullptr ) noexcept + : root_index_slot( root ), mutable_root_index_slot( root ) + { + } + + constexpr explicit forest_domain_descriptor( const index_type* root ) noexcept + : root_index_slot( root ), mutable_root_index_slot( nullptr ) + { + } + + static constexpr const char* name() noexcept { return "container/pmap"; } + + index_type root_index() const noexcept { return root_index_slot != nullptr ? *root_index_slot : 0; } + + index_type* root_index_ptr() noexcept { return mutable_root_index_slot; } + + static node_type* resolve_node( node_pptr p ) noexcept { return ManagerT::template resolve( p ); } + + static int compare_key( const _K& key, node_pptr cur ) noexcept + { + node_type* obj = resolve_node( cur ); + if ( obj == nullptr ) + return 0; + return ( key == obj->key ) ? 0 : ( ( key < obj->key ) ? -1 : 1 ); + } + + static bool less_node( node_pptr lhs, node_pptr rhs ) noexcept + { + node_type* lhs_obj = resolve_node( lhs ); + node_type* rhs_obj = resolve_node( rhs ); + return lhs_obj != nullptr && rhs_obj != nullptr && lhs_obj->key < rhs_obj->key; + } + + static bool validate_node( node_pptr p ) noexcept { return resolve_node( p ) != nullptr; } + }; + + using forest_domain_view_policy = detail::ForestDomainViewOps; + using forest_domain_policy = detail::ForestDomainOps; + /// @brief Sentinel value for "no node" in TreeNode fields. static constexpr index_type no_block = ManagerT::address_traits::no_block; @@ -6063,6 +6151,16 @@ template struct pmap // ─── Методы доступа ─────────────────────────────────────────────────────── + forest_domain_policy forest_domain_ops() noexcept + { + return forest_domain_policy( forest_domain_descriptor( &_root_idx ) ); + } + + forest_domain_view_policy forest_domain_view_ops() const noexcept + { + return forest_domain_view_policy( forest_domain_descriptor( &_root_idx ) ); + } + /// @brief Проверить, пуст ли словарь. bool empty() const noexcept { return _root_idx == static_cast( 0 ); } @@ -6089,8 +6187,10 @@ template struct pmap */ node_pptr insert( const _K& key, const _V& val ) noexcept { + auto ops = forest_domain_ops(); + // Ищем существующий узел. - node_pptr existing = _avl_find( key ); + node_pptr existing = ops.find( key ); if ( !existing.is_null() ) { // Ключ найден — обновляем значение. @@ -6116,7 +6216,7 @@ template struct pmap detail::avl_init_node( new_node ); // Вставляем в AVL-дерево. - _avl_insert( new_node ); + ops.insert( new_node ); return new_node; } @@ -6127,7 +6227,7 @@ template struct pmap * @param key Ключ для поиска. * @return pptr на найденный узел, или нулевой pptr если не найден. */ - node_pptr find( const _K& key ) const noexcept { return _avl_find( key ); } + node_pptr find( const _K& key ) const noexcept { return forest_domain_view_ops().find( key ); } /** * @brief Проверить, содержит ли словарь заданный ключ. @@ -6135,7 +6235,7 @@ template struct pmap * @param key Ключ для проверки. * @return true если ключ найден. */ - bool contains( const _K& key ) const noexcept { return !_avl_find( key ).is_null(); } + bool contains( const _K& key ) const noexcept { return !forest_domain_view_ops().find( key ).is_null(); } /** * @brief Удалить узел по ключу. @@ -6148,7 +6248,7 @@ template struct pmap */ bool erase( const _K& key ) noexcept { - node_pptr target = _avl_find( key ); + node_pptr target = forest_domain_ops().find( key ); if ( target.is_null() ) return false; @@ -6175,7 +6275,7 @@ template struct pmap * * Сбрасывает _root_idx, но не освобождает данные в ПАП. */ - void reset() noexcept { _root_idx = static_cast( 0 ); } + void reset() noexcept { forest_domain_ops().reset_root(); } // ─── Итератор ─────────────────────────────────────────────── @@ -6194,43 +6294,6 @@ template struct pmap /// @brief Конец итерации (sentinel = 0). iterator end() const noexcept { return iterator( static_cast( 0 ) ); } - - // ─── AVL-дерево (использует встроенные TreeNode-поля каждого узла) ──────── - - private: - /// @brief Найти узел AVL-дерева с заданным ключом. Возвращает null если не найден. - node_pptr _avl_find( const _K& key ) const noexcept - { - return detail::avl_find( - _root_idx, - [&]( node_pptr cur ) -> int - { - node_type* obj = ManagerT::template resolve( cur ); - if ( obj == nullptr ) - return 0; - if ( key == obj->key ) - return 0; - return ( key < obj->key ) ? -1 : 1; - }, - []( node_pptr p ) -> node_type* { return ManagerT::template resolve( p ); } ); - } - - /// @brief Вставить новый узел в AVL-дерево. Предполагается, что ключ ещё не в дереве. - void _avl_insert( node_pptr new_node ) noexcept - { - node_type* new_obj = ManagerT::template resolve( new_node ); - detail::avl_insert( - new_node, _root_idx, - [&]( node_pptr cur ) -> bool - { - node_type* obj = ManagerT::template resolve( cur ); - return ( obj != nullptr ) && ( new_obj->key < obj->key ); - }, - []( node_pptr p ) -> node_type* { return ManagerT::template resolve( p ); } ); - } - - // _subtree_count and _clear_subtree replaced by shared - // detail::avl_subtree_count and detail::avl_clear_subtree. }; } // namespace pmm @@ -7237,13 +7300,13 @@ template struct pstringview static index_type root_index() noexcept { auto* domain = ManagerT::symbol_domain_record_unlocked(); - return ( domain != nullptr ) ? domain->root_offset : static_cast( 0 ); + return ManagerT::forest_domain_root_index_unlocked( domain ); } static index_type* root_index_ptr() noexcept { auto* domain = ManagerT::symbol_domain_record_unlocked(); - return ( domain != nullptr ) ? &domain->root_offset : nullptr; + return ManagerT::forest_domain_root_index_ptr_unlocked( domain ); } static node_type* resolve_node( node_pptr p ) noexcept { return ManagerT::template resolve( p ); } @@ -7268,6 +7331,8 @@ template struct pstringview using forest_domain_policy = detail::ForestDomainOps; + static forest_domain_policy forest_domain_ops() noexcept { return forest_domain_policy{}; } + std::uint32_t length; ///< Длина строки (без нулевого терминатора) char str[1]; ///< Строковые данные (flexible array member pattern) @@ -7364,7 +7429,7 @@ template struct pstringview if ( !ManagerT::is_initialized() ) return; typename ManagerT::thread_policy::unique_lock_type lock( ManagerT::_mutex ); - forest_domain_policy::reset_root(); + forest_domain_ops().reset_root(); } /// @brief Текущий persistent root словаря интернирования; 0 = пустое дерево. @@ -7373,7 +7438,7 @@ template struct pstringview if ( !ManagerT::is_initialized() ) return static_cast( 0 ); typename ManagerT::thread_policy::shared_lock_type lock( ManagerT::_mutex ); - return forest_domain_policy::root_index(); + return forest_domain_ops().root_index(); } // Public destructor required for stack-temporary construction via pstringview("hello"). @@ -7389,8 +7454,10 @@ template struct pstringview if ( s == nullptr ) s = ""; + auto ops = forest_domain_ops(); + // Ищем в AVL-дереве. - psview_pptr found = _avl_find( s ); + psview_pptr found = ops.find( s ); if ( !found.is_null() ) return found; @@ -7448,18 +7515,10 @@ template struct pstringview ManagerT::lock_block_permanent( obj ); // Вставляем в AVL-дерево. - _avl_insert( new_node ); + ops.insert( new_node ); return new_node; } - - // ─── AVL-дерево (использует встроенные TreeNode-поля каждого pstringview-блока) ─ - - /// @brief Найти узел AVL-дерева с заданной строкой. Возвращает null если не найден. - static psview_pptr _avl_find( const char* s ) noexcept { return forest_domain_policy::find( s ); } - - /// @brief Вставить новый узел в AVL-дерево. Предполагается, что строка ещё не в дереве. - static void _avl_insert( psview_pptr new_node ) noexcept { forest_domain_policy::insert( new_node ); } }; } // namespace pmm @@ -8382,7 +8441,8 @@ class PersistMemoryManager : public detail::PersistMemoryTypedApi( 0 ) : p.offset() ); + set_forest_domain_root_index_unlocked( find_domain_by_name_unlocked( detail::kServiceNameLegacyRoot ), + p.is_null() ? static_cast( 0 ) : p.offset() ); } /** @@ -8396,7 +8456,8 @@ class PersistMemoryManager : public detail::PersistMemoryTypedApi(); - index_type legacy_root = get_legacy_root_offset_unlocked(); + index_type legacy_root = + forest_domain_root_index_unlocked( find_domain_by_name_unlocked( detail::kServiceNameLegacyRoot ) ); if ( legacy_root == static_cast( 0 ) ) return pptr(); return pptr( legacy_root ); @@ -8452,7 +8513,7 @@ class PersistMemoryManager : public detail::PersistMemoryTypedApi symbol ) noexcept @@ -8470,7 +8531,7 @@ class PersistMemoryManager : public detail::PersistMemoryTypedApi static pptr get_domain_root( const char* name ) noexcept @@ -8497,10 +8558,8 @@ class PersistMemoryManager : public detail::PersistMemoryTypedApibinding_kind != detail::kForestBindingDirectRoot ) - return false; - rec->root_offset = root.is_null() ? static_cast( 0 ) : root.offset(); - return true; + return set_forest_domain_root_index_unlocked( rec, + root.is_null() ? static_cast( 0 ) : root.offset() ); } // ─── Методы доступа к полям AVL-узла блока ───────────── @@ -9034,9 +9093,10 @@ static forest_domain* find_domain_by_symbol_unlocked( pptr symbol ) return nullptr; } -static index_type domain_root_offset_unlocked( const forest_domain* rec, - const detail::ManagerHeader* hdr ) noexcept +static index_type forest_domain_root_index_unlocked( const forest_domain* rec ) noexcept { + const detail::ManagerHeader* hdr = + ( _backend.base_ptr() != nullptr ) ? get_header_c( _backend.base_ptr() ) : nullptr; if ( rec == nullptr || hdr == nullptr ) return 0; if ( rec->binding_kind == detail::kForestBindingFreeTree ) @@ -9044,34 +9104,29 @@ static index_type domain_root_offset_unlocked( const forest_domain* return rec->root_offset; } -// ─── Legacy root helpers ────────────────────────────────────────────────────── - -static index_type get_legacy_root_offset_unlocked() noexcept +static index_type* forest_domain_root_index_ptr_unlocked( forest_domain* rec ) noexcept { - const forest_domain* rec = find_domain_by_name_unlocked( detail::kServiceNameLegacyRoot ); - return domain_root_offset_unlocked( rec, get_header_c( _backend.base_ptr() ) ); + if ( rec == nullptr || rec->binding_kind != detail::kForestBindingDirectRoot ) + return nullptr; + return &rec->root_offset; } -static void set_legacy_root_offset_unlocked( index_type off ) noexcept +static bool set_forest_domain_root_index_unlocked( forest_domain* rec, index_type root ) noexcept { - forest_domain* rec = find_domain_by_name_unlocked( detail::kServiceNameLegacyRoot ); - if ( rec != nullptr && rec->binding_kind == detail::kForestBindingDirectRoot ) - rec->root_offset = off; + index_type* root_ptr = forest_domain_root_index_ptr_unlocked( rec ); + if ( root_ptr == nullptr ) + return false; + *root_ptr = root; + return true; } -// ─── Symbol domain helpers ──────────────────────────────────────────────────── +// ─── Canonical system domain records ───────────────────────────────────────── static forest_domain* symbol_domain_record_unlocked() noexcept { return find_domain_by_name_unlocked( detail::kSystemDomainSymbols ); } -static index_type symbol_domain_root_offset_unlocked() noexcept -{ - forest_domain* rec = symbol_domain_record_unlocked(); - return ( rec != nullptr ) ? rec->root_offset : static_cast( 0 ); -} - // ─── Domain registration ───────────────────────────────────────────────────── static bool register_domain_unlocked( const char* name, std::uint8_t flags, std::uint8_t binding_kind, @@ -9128,11 +9183,11 @@ static pptr intern_symbol_unlocked( const char* s ) noexcept if ( s == nullptr ) s = ""; - using symbol_policy = typename pstringview::forest_domain_policy; - if ( symbol_policy::root_index_ptr() == nullptr ) + auto symbol_policy = pstringview::forest_domain_ops(); + if ( symbol_policy.root_index_ptr() == nullptr ) return pptr(); - pptr found = symbol_policy::find( s ); + pptr found = symbol_policy.find( s ); if ( !found.is_null() ) return found; @@ -9158,7 +9213,7 @@ static pptr intern_symbol_unlocked( const char* s ) noexcept if ( !lock_block_permanent_unlocked( public_raw ) ) return pptr(); - symbol_policy::insert( new_node ); + symbol_policy.insert( new_node ); return new_node; } @@ -9324,7 +9379,7 @@ static bool validate_bootstrap_invariants_unlocked() noexcept if ( free_rec->binding_kind != detail::kForestBindingFreeTree ) return false; // 5. Symbol dictionary root is non-zero (at least bootstrap symbols exist) - if ( symbol_domain_root_offset_unlocked() == 0 ) + if ( pstringview::forest_domain_ops().root_index() == 0 ) return false; // 6. Registry domain root matches header root_offset const forest_domain* reg_rec = find_domain_by_name_unlocked( detail::kSystemDomainRegistry ); @@ -9353,7 +9408,8 @@ static bool validate_or_bootstrap_forest_registry_unlocked() noexcept detail::kForestBindingFreeTree, 0 ) ) return false; if ( !register_domain_unlocked( detail::kSystemDomainSymbols, detail::kForestDomainFlagSystem, - detail::kForestBindingDirectRoot, symbol_domain_root_offset_unlocked() ) ) + detail::kForestBindingDirectRoot, + pstringview::forest_domain_ops().root_index() ) ) return false; if ( !register_domain_unlocked( detail::kSystemDomainRegistry, detail::kForestDomainFlagSystem, detail::kForestBindingDirectRoot, hdr->root_offset ) ) diff --git a/single_include/pmm/pmm_no_comments.h b/single_include/pmm/pmm_no_comments.h index 289c47a3..89fa9950 100644 --- a/single_include/pmm/pmm_no_comments.h +++ b/single_include/pmm/pmm_no_comments.h @@ -2000,39 +2000,79 @@ static void avl_insert( PPtr new_node, IndexType& root_idx, GoLeftFn&& go_left, avl_rebalance_up( parent, root_idx, update_node ); } -template -concept ForestDomainDescriptorForKey = requires( typename Domain::node_pptr p, const Key& key ) { +template +concept ForestDomainViewDescriptor = requires( const Domain domain, typename Domain::node_pptr p ) { typename Domain::index_type; typename Domain::node_type; typename Domain::node_pptr; - { Domain::name() } -> std::convertible_to; - { Domain::root_index() } -> std::convertible_to; - { Domain::root_index_ptr() } -> std::same_as; - { Domain::resolve_node( p ) } -> std::convertible_to; - { Domain::compare_key( key, p ) } -> std::convertible_to; - { Domain::less_node( p, p ) } -> std::convertible_to; + { domain.name() } -> std::convertible_to; + { domain.root_index() } -> std::convertible_to; + { domain.resolve_node( p ) } -> std::convertible_to; }; -template static bool forest_domain_validate_node( typename Domain::node_pptr p ) noexcept +template +concept ForestDomainDescriptor = + ForestDomainViewDescriptor && requires( Domain domain, typename Domain::node_pptr p ) { + { domain.root_index_ptr() } -> std::same_as; + { domain.less_node( p, p ) } -> std::convertible_to; + }; + +template +concept ForestDomainDescriptorForKey = ForestDomainViewDescriptor && + requires( const Domain domain, typename Domain::node_pptr p, const Key& key ) { + { domain.compare_key( key, p ) } -> std::convertible_to; + }; + +template +static bool forest_domain_validate_node( const Domain& domain, typename Domain::node_pptr p ) noexcept { if constexpr ( requires { - { Domain::validate_node( p ) } -> std::convertible_to; + { domain.validate_node( p ) } -> std::convertible_to; } ) - return Domain::validate_node( p ); + return domain.validate_node( p ); else return true; } -template struct ForestDomainOps +template struct ForestDomainViewOps { using index_type = typename Domain::index_type; + using node_type = typename Domain::node_type; using node_pptr = typename Domain::node_pptr; - static constexpr const char* name() noexcept { return Domain::name(); } - static index_type root_index() noexcept { return Domain::root_index(); } - static index_type* root_index_ptr() noexcept { return Domain::root_index_ptr(); } + Domain domain; + + constexpr explicit ForestDomainViewOps( Domain d = Domain{} ) noexcept : domain( d ) {} + + const char* name() const noexcept { return domain.name(); } + index_type root_index() const noexcept { return domain.root_index(); } + + template + requires ForestDomainDescriptorForKey + node_pptr find( const Key& key ) const noexcept + { + return avl_find( + domain.root_index(), [&]( node_pptr cur ) -> int { return domain.compare_key( key, cur ); }, + [this]( node_pptr p ) -> node_type* { return domain.resolve_node( p ); } ); + } +}; + +template struct ForestDomainOps : ForestDomainViewOps +{ + using view_base = ForestDomainViewOps; + using index_type = typename view_base::index_type; + using node_type = typename view_base::node_type; + using node_pptr = typename view_base::node_pptr; + + using view_base::find; + using view_base::name; + using view_base::root_index; + + constexpr explicit ForestDomainOps( Domain d = Domain{} ) noexcept : view_base( d ) {} - static bool reset_root() noexcept + index_type* root_index_ptr() noexcept { return this->domain.root_index_ptr(); } + + bool reset_root() noexcept { index_type* root = root_index_ptr(); if ( root == nullptr ) @@ -2041,25 +2081,18 @@ template struct ForestDomainOps return true; } - template - requires ForestDomainDescriptorForKey - static node_pptr find( const Key& key ) noexcept - { - return avl_find( - Domain::root_index(), [&]( node_pptr cur ) -> int { return Domain::compare_key( key, cur ); }, - []( node_pptr p ) -> typename Domain::node_type* { return Domain::resolve_node( p ); } ); - } - - static void insert( node_pptr new_node ) noexcept + void insert( node_pptr new_node ) noexcept { - index_type* root = Domain::root_index_ptr(); + index_type* root = this->domain.root_index_ptr(); if ( root == nullptr || new_node.is_null() ) return; - if ( Domain::resolve_node( new_node ) == nullptr || !forest_domain_validate_node( new_node ) ) + if ( this->domain.resolve_node( new_node ) == nullptr || + !forest_domain_validate_node( this->domain, new_node ) ) return; avl_insert( - new_node, *root, [new_node]( node_pptr cur ) -> bool { return Domain::less_node( new_node, cur ); }, - []( node_pptr p ) -> typename Domain::node_type* { return Domain::resolve_node( p ); } ); + new_node, *root, + [this, new_node]( node_pptr cur ) -> bool { return this->domain.less_node( new_node, cur ); }, + [this]( node_pptr p ) -> node_type* { return this->domain.resolve_node( p ); } ); } }; @@ -3771,12 +3804,70 @@ template struct pmap using node_type = pmap_node<_K, _V>; using node_pptr = typename ManagerT::template pptr; + struct forest_domain_descriptor + { + using index_type = typename ManagerT::index_type; + using node_type = pmap_node<_K, _V>; + using node_pptr = typename ManagerT::template pptr; + + const index_type* root_index_slot; + index_type* mutable_root_index_slot; + + constexpr explicit forest_domain_descriptor( index_type* root = nullptr ) noexcept + : root_index_slot( root ), mutable_root_index_slot( root ) + { + } + + constexpr explicit forest_domain_descriptor( const index_type* root ) noexcept + : root_index_slot( root ), mutable_root_index_slot( nullptr ) + { + } + + static constexpr const char* name() noexcept { return "container/pmap"; } + + index_type root_index() const noexcept { return root_index_slot != nullptr ? *root_index_slot : 0; } + + index_type* root_index_ptr() noexcept { return mutable_root_index_slot; } + + static node_type* resolve_node( node_pptr p ) noexcept { return ManagerT::template resolve( p ); } + + static int compare_key( const _K& key, node_pptr cur ) noexcept + { + node_type* obj = resolve_node( cur ); + if ( obj == nullptr ) + return 0; + return ( key == obj->key ) ? 0 : ( ( key < obj->key ) ? -1 : 1 ); + } + + static bool less_node( node_pptr lhs, node_pptr rhs ) noexcept + { + node_type* lhs_obj = resolve_node( lhs ); + node_type* rhs_obj = resolve_node( rhs ); + return lhs_obj != nullptr && rhs_obj != nullptr && lhs_obj->key < rhs_obj->key; + } + + static bool validate_node( node_pptr p ) noexcept { return resolve_node( p ) != nullptr; } + }; + + using forest_domain_view_policy = detail::ForestDomainViewOps; + using forest_domain_policy = detail::ForestDomainOps; + static constexpr index_type no_block = ManagerT::address_traits::no_block; index_type _root_idx; pmap() noexcept : _root_idx( static_cast( 0 ) ) {} + forest_domain_policy forest_domain_ops() noexcept + { + return forest_domain_policy( forest_domain_descriptor( &_root_idx ) ); + } + + forest_domain_view_policy forest_domain_view_ops() const noexcept + { + return forest_domain_view_policy( forest_domain_descriptor( &_root_idx ) ); + } + bool empty() const noexcept { return _root_idx == static_cast( 0 ); } std::size_t size() const noexcept @@ -3788,8 +3879,9 @@ template struct pmap node_pptr insert( const _K& key, const _V& val ) noexcept { + auto ops = forest_domain_ops(); - node_pptr existing = _avl_find( key ); + node_pptr existing = ops.find( key ); if ( !existing.is_null() ) { @@ -3812,18 +3904,18 @@ template struct pmap detail::avl_init_node( new_node ); - _avl_insert( new_node ); + ops.insert( new_node ); return new_node; } - node_pptr find( const _K& key ) const noexcept { return _avl_find( key ); } + node_pptr find( const _K& key ) const noexcept { return forest_domain_view_ops().find( key ); } - bool contains( const _K& key ) const noexcept { return !_avl_find( key ).is_null(); } + bool contains( const _K& key ) const noexcept { return !forest_domain_view_ops().find( key ).is_null(); } bool erase( const _K& key ) noexcept { - node_pptr target = _avl_find( key ); + node_pptr target = forest_domain_ops().find( key ); if ( target.is_null() ) return false; @@ -3840,7 +3932,7 @@ template struct pmap _root_idx = static_cast( 0 ); } - void reset() noexcept { _root_idx = static_cast( 0 ); } + void reset() noexcept { forest_domain_ops().reset_root(); } using iterator = detail::AvlInorderIterator; @@ -3853,38 +3945,6 @@ template struct pmap } iterator end() const noexcept { return iterator( static_cast( 0 ) ); } - - private: - - node_pptr _avl_find( const _K& key ) const noexcept - { - return detail::avl_find( - _root_idx, - [&]( node_pptr cur ) -> int - { - node_type* obj = ManagerT::template resolve( cur ); - if ( obj == nullptr ) - return 0; - if ( key == obj->key ) - return 0; - return ( key < obj->key ) ? -1 : 1; - }, - []( node_pptr p ) -> node_type* { return ManagerT::template resolve( p ); } ); - } - - void _avl_insert( node_pptr new_node ) noexcept - { - node_type* new_obj = ManagerT::template resolve( new_node ); - detail::avl_insert( - new_node, _root_idx, - [&]( node_pptr cur ) -> bool - { - node_type* obj = ManagerT::template resolve( cur ); - return ( obj != nullptr ) && ( new_obj->key < obj->key ); - }, - []( node_pptr p ) -> node_type* { return ManagerT::template resolve( p ); } ); - } - }; } @@ -4355,13 +4415,13 @@ template struct pstringview static index_type root_index() noexcept { auto* domain = ManagerT::symbol_domain_record_unlocked(); - return ( domain != nullptr ) ? domain->root_offset : static_cast( 0 ); + return ManagerT::forest_domain_root_index_unlocked( domain ); } static index_type* root_index_ptr() noexcept { auto* domain = ManagerT::symbol_domain_record_unlocked(); - return ( domain != nullptr ) ? &domain->root_offset : nullptr; + return ManagerT::forest_domain_root_index_ptr_unlocked( domain ); } static node_type* resolve_node( node_pptr p ) noexcept { return ManagerT::template resolve( p ); } @@ -4386,6 +4446,8 @@ template struct pstringview using forest_domain_policy = detail::ForestDomainOps; + static forest_domain_policy forest_domain_ops() noexcept { return forest_domain_policy{}; } + std::uint32_t length; char str[1]; @@ -4430,7 +4492,7 @@ template struct pstringview if ( !ManagerT::is_initialized() ) return; typename ManagerT::thread_policy::unique_lock_type lock( ManagerT::_mutex ); - forest_domain_policy::reset_root(); + forest_domain_ops().reset_root(); } static index_type root_index() noexcept @@ -4438,7 +4500,7 @@ template struct pstringview if ( !ManagerT::is_initialized() ) return static_cast( 0 ); typename ManagerT::thread_policy::shared_lock_type lock( ManagerT::_mutex ); - return forest_domain_policy::root_index(); + return forest_domain_ops().root_index(); } ~pstringview() = default; @@ -4451,7 +4513,9 @@ template struct pstringview if ( s == nullptr ) s = ""; - psview_pptr found = _avl_find( s ); + auto ops = forest_domain_ops(); + + psview_pptr found = ops.find( s ); if ( !found.is_null() ) return found; @@ -4500,14 +4564,10 @@ template struct pstringview ManagerT::lock_block_permanent( obj ); - _avl_insert( new_node ); + ops.insert( new_node ); return new_node; } - - static psview_pptr _avl_find( const char* s ) noexcept { return forest_domain_policy::find( s ); } - - static void _avl_insert( psview_pptr new_node ) noexcept { forest_domain_policy::insert( new_node ); } }; } @@ -5183,7 +5243,8 @@ class PersistMemoryManager : public detail::PersistMemoryTypedApi( 0 ) : p.offset() ); + set_forest_domain_root_index_unlocked( find_domain_by_name_unlocked( detail::kServiceNameLegacyRoot ), + p.is_null() ? static_cast( 0 ) : p.offset() ); } template static pptr get_root() noexcept @@ -5191,7 +5252,8 @@ class PersistMemoryManager : public detail::PersistMemoryTypedApi(); - index_type legacy_root = get_legacy_root_offset_unlocked(); + index_type legacy_root = + forest_domain_root_index_unlocked( find_domain_by_name_unlocked( detail::kServiceNameLegacyRoot ) ); if ( legacy_root == static_cast( 0 ) ) return pptr(); return pptr( legacy_root ); @@ -5245,7 +5307,7 @@ class PersistMemoryManager : public detail::PersistMemoryTypedApi symbol ) noexcept @@ -5263,7 +5325,7 @@ class PersistMemoryManager : public detail::PersistMemoryTypedApi static pptr get_domain_root( const char* name ) noexcept @@ -5290,10 +5352,8 @@ class PersistMemoryManager : public detail::PersistMemoryTypedApibinding_kind != detail::kForestBindingDirectRoot ) - return false; - rec->root_offset = root.is_null() ? static_cast( 0 ) : root.offset(); - return true; + return set_forest_domain_root_index_unlocked( rec, + root.is_null() ? static_cast( 0 ) : root.offset() ); } private: @@ -5732,9 +5792,10 @@ static forest_domain* find_domain_by_symbol_unlocked( pptr symbol ) return nullptr; } -static index_type domain_root_offset_unlocked( const forest_domain* rec, - const detail::ManagerHeader* hdr ) noexcept +static index_type forest_domain_root_index_unlocked( const forest_domain* rec ) noexcept { + const detail::ManagerHeader* hdr = + ( _backend.base_ptr() != nullptr ) ? get_header_c( _backend.base_ptr() ) : nullptr; if ( rec == nullptr || hdr == nullptr ) return 0; if ( rec->binding_kind == detail::kForestBindingFreeTree ) @@ -5742,17 +5803,20 @@ static index_type domain_root_offset_unlocked( const forest_domain* return rec->root_offset; } -static index_type get_legacy_root_offset_unlocked() noexcept +static index_type* forest_domain_root_index_ptr_unlocked( forest_domain* rec ) noexcept { - const forest_domain* rec = find_domain_by_name_unlocked( detail::kServiceNameLegacyRoot ); - return domain_root_offset_unlocked( rec, get_header_c( _backend.base_ptr() ) ); + if ( rec == nullptr || rec->binding_kind != detail::kForestBindingDirectRoot ) + return nullptr; + return &rec->root_offset; } -static void set_legacy_root_offset_unlocked( index_type off ) noexcept +static bool set_forest_domain_root_index_unlocked( forest_domain* rec, index_type root ) noexcept { - forest_domain* rec = find_domain_by_name_unlocked( detail::kServiceNameLegacyRoot ); - if ( rec != nullptr && rec->binding_kind == detail::kForestBindingDirectRoot ) - rec->root_offset = off; + index_type* root_ptr = forest_domain_root_index_ptr_unlocked( rec ); + if ( root_ptr == nullptr ) + return false; + *root_ptr = root; + return true; } static forest_domain* symbol_domain_record_unlocked() noexcept @@ -5760,12 +5824,6 @@ static forest_domain* symbol_domain_record_unlocked() noexcept return find_domain_by_name_unlocked( detail::kSystemDomainSymbols ); } -static index_type symbol_domain_root_offset_unlocked() noexcept -{ - forest_domain* rec = symbol_domain_record_unlocked(); - return ( rec != nullptr ) ? rec->root_offset : static_cast( 0 ); -} - static bool register_domain_unlocked( const char* name, std::uint8_t flags, std::uint8_t binding_kind, index_type initial_root ) noexcept { @@ -5818,11 +5876,11 @@ static pptr intern_symbol_unlocked( const char* s ) noexcept if ( s == nullptr ) s = ""; - using symbol_policy = typename pstringview::forest_domain_policy; - if ( symbol_policy::root_index_ptr() == nullptr ) + auto symbol_policy = pstringview::forest_domain_ops(); + if ( symbol_policy.root_index_ptr() == nullptr ) return pptr(); - pptr found = symbol_policy::find( s ); + pptr found = symbol_policy.find( s ); if ( !found.is_null() ) return found; @@ -5848,7 +5906,7 @@ static pptr intern_symbol_unlocked( const char* s ) noexcept if ( !lock_block_permanent_unlocked( public_raw ) ) return pptr(); - symbol_policy::insert( new_node ); + symbol_policy.insert( new_node ); return new_node; } @@ -6007,7 +6065,7 @@ static bool validate_bootstrap_invariants_unlocked() noexcept if ( free_rec->binding_kind != detail::kForestBindingFreeTree ) return false; - if ( symbol_domain_root_offset_unlocked() == 0 ) + if ( pstringview::forest_domain_ops().root_index() == 0 ) return false; const forest_domain* reg_rec = find_domain_by_name_unlocked( detail::kSystemDomainRegistry ); @@ -6034,7 +6092,8 @@ static bool validate_or_bootstrap_forest_registry_unlocked() noexcept detail::kForestBindingFreeTree, 0 ) ) return false; if ( !register_domain_unlocked( detail::kSystemDomainSymbols, detail::kForestDomainFlagSystem, - detail::kForestBindingDirectRoot, symbol_domain_root_offset_unlocked() ) ) + detail::kForestBindingDirectRoot, + pstringview::forest_domain_ops().root_index() ) ) return false; if ( !register_domain_unlocked( detail::kSystemDomainRegistry, detail::kForestDomainFlagSystem, detail::kForestBindingDirectRoot, hdr->root_offset ) ) diff --git a/tests/test_issue151_pstringview.cpp b/tests/test_issue151_pstringview.cpp index c46c795e..4a688a3c 100644 --- a/tests/test_issue151_pstringview.cpp +++ b/tests/test_issue151_pstringview.cpp @@ -309,6 +309,8 @@ TEST_CASE( " AVL root tracked by persistent symbol domain", "[test_issue151_p TEST_CASE( " forest-domain descriptor drives symbol dictionary", "[test_issue151_pstringview]" ) { using Domain = TestPsv::forest_domain_descriptor; + static_assert( pmm::detail::ForestDomainDescriptor ); + static_assert( pmm::detail::ForestDomainViewDescriptor ); static_assert( pmm::detail::ForestDomainDescriptorForKey ); TestMgr::destroy(); @@ -324,9 +326,10 @@ TEST_CASE( " forest-domain descriptor drives symbol dictionary", "[test_issue TestMgr_pptr_psv beta = TestMgr::pstringview( "descriptor_beta" ); REQUIRE( ( !alpha.is_null() && !beta.is_null() ) ); - REQUIRE( TestPsv::forest_domain_policy::find( "descriptor_alpha" ) == alpha ); - REQUIRE( TestPsv::forest_domain_policy::find( "descriptor_beta" ) == beta ); - REQUIRE( TestPsv::forest_domain_policy::find( "descriptor_missing" ).is_null() ); + auto ops = TestPsv::forest_domain_ops(); + REQUIRE( ops.find( "descriptor_alpha" ) == alpha ); + REQUIRE( ops.find( "descriptor_beta" ) == beta ); + REQUIRE( ops.find( "descriptor_missing" ).is_null() ); REQUIRE( Domain::validate_node( alpha ) ); REQUIRE( *Domain::root_index_ptr() == TestPsv::root_index() ); diff --git a/tests/test_issue153_pmap.cpp b/tests/test_issue153_pmap.cpp index 2559beb7..a38920eb 100644 --- a/tests/test_issue153_pmap.cpp +++ b/tests/test_issue153_pmap.cpp @@ -52,6 +52,21 @@ using TestMgr = pmm::PersistMemoryManager; +template +concept HasConstForestDomainOps = requires( const MapT& const_map ) { const_map.forest_domain_ops(); }; + +template +concept HasConstForestDomainViewOps = requires( const MapT& const_map ) { const_map.forest_domain_view_ops(); }; + +template +concept HasConstForestDomainInsert = requires( const OpsT& ops, NodePPtr node ) { ops.insert( node ); }; + +template +concept HasConstForestDomainResetRoot = requires( const OpsT& ops ) { ops.reset_root(); }; + +template +concept HasConstForestDomainRootIndexPtr = requires( const OpsT& ops ) { ops.root_index_ptr(); }; + // ============================================================================= // I153-A: Basic insert and find with int keys // ============================================================================= @@ -78,6 +93,55 @@ TEST_CASE( " insert single key-value pair", "[test_issue153_pmap]" ) TestMgr::destroy(); } +/// @brief pmap exposes the same minimal forest-domain descriptor/ops contract as other AVL-backed domains. +TEST_CASE( " forest-domain descriptor drives pmap dictionary", "[test_issue153_pmap][issue335]" ) +{ + using Map = TestMgr::pmap; + using Domain = Map::forest_domain_descriptor; + static_assert( pmm::detail::ForestDomainDescriptor ); + static_assert( pmm::detail::ForestDomainViewDescriptor ); + static_assert( pmm::detail::ForestDomainDescriptorForKey ); + static_assert( !HasConstForestDomainOps ); + static_assert( HasConstForestDomainViewOps ); + static_assert( !HasConstForestDomainInsert ); + static_assert( !HasConstForestDomainResetRoot ); + static_assert( !HasConstForestDomainRootIndexPtr ); + + TestMgr::destroy(); + REQUIRE( TestMgr::create( 64 * 1024 ) ); + + Map map; + auto ops = map.forest_domain_ops(); + + REQUIRE( std::strcmp( ops.name(), "container/pmap" ) == 0 ); + REQUIRE( ops.root_index() == static_cast( 0 ) ); + REQUIRE( ops.root_index_ptr() == &map._root_idx ); + + auto p10 = map.insert( 10, 100 ); + auto p20 = map.insert( 20, 200 ); + REQUIRE( ( !p10.is_null() && !p20.is_null() ) ); + + REQUIRE( ops.root_index() != static_cast( 0 ) ); + REQUIRE( ops.find( 10 ) == p10 ); + REQUIRE( ops.find( 20 ) == p20 ); + REQUIRE( ops.find( 30 ).is_null() ); + + const Map& const_map = map; + auto view_ops = const_map.forest_domain_view_ops(); + REQUIRE( std::strcmp( view_ops.name(), "container/pmap" ) == 0 ); + REQUIRE( view_ops.root_index() == ops.root_index() ); + REQUIRE( view_ops.find( 10 ) == p10 ); + REQUIRE( view_ops.find( 20 ) == p20 ); + REQUIRE( const_map.find( 10 ) == p10 ); + REQUIRE( const_map.contains( 20 ) ); + + REQUIRE( ops.reset_root() ); + REQUIRE( map.empty() ); + REQUIRE( ops.root_index() == static_cast( 0 ) ); + + TestMgr::destroy(); +} + /// @brief insert() with multiple distinct keys. TEST_CASE( " insert multiple distinct keys", "[test_issue153_pmap]" ) { diff --git a/tests/test_issue162_deduplication.cpp b/tests/test_issue162_deduplication.cpp index eecb7b84..5283847e 100644 --- a/tests/test_issue162_deduplication.cpp +++ b/tests/test_issue162_deduplication.cpp @@ -4,8 +4,8 @@ * * Проверяет: * - detail::avl_find() — новая обобщённая функция поиска в AVL-дереве - * - pstringview::_avl_find() делегирует в detail::avl_find() - * - pmap::_avl_find() делегирует в detail::avl_find() + * - pstringview forest-domain ops delegate to detail::avl_find() + * - pmap forest-domain ops delegate to detail::avl_find() * - Корректность поиска в pstringview после рефакторинга * - Корректность поиска в pmap после рефакторинга * - Поиск несуществующих ключей возвращает null pptr @@ -247,7 +247,7 @@ TEST_CASE( "I162-B5: pmap::contains() consistent with find()", "[test_issue162_d TEST_CASE( "I162-C1: detail::avl_find() template available and correct", "[test_issue162_deduplication]" ) { // Verify that detail::avl_find() compiles and can be instantiated. - // We use a pmap to test it indirectly since pmap::_avl_find() delegates to it. + // We use a pmap to test it indirectly since pmap forest-domain ops delegate to it. TestMgr::create( 64 * 1024 ); TestMgr::pmap map; @@ -271,7 +271,7 @@ TEST_CASE( "I162-C1: detail::avl_find() template available and correct", "[test_ TestMgr::destroy(); } -/// @brief Both pstringview and pmap use detail::avl_find() from avl_tree_mixin.h. +/// @brief Both pstringview and pmap use detail::avl_find() through forest-domain ops. /// This test verifies that the shared helper works correctly for both users. TEST_CASE( "I162-C2: detail::avl_find() shared correctly by pstringview and pmap", "[test_issue162_deduplication]" ) {