diff --git a/deps/pmm/pmm.h b/deps/pmm/pmm.h index 93aae9f..449304d 100644 --- a/deps/pmm/pmm.h +++ b/deps/pmm/pmm.h @@ -382,17 +382,15 @@ inline constexpr std::size_t kDefaultGrowDenominator = 4; /** * @file pmm/avl_tree_mixin.h - * @brief Shared AVL tree helper functions for pmap, pstringview and pvector (Issue #155, #162, #188). + * @brief Shared AVL tree helper functions for pmap and pstringview (Issue #155, #162, #188). * * Provides a set of free static template functions implementing the core AVL * tree operations (height, balance factor, rotations, rebalancing, insert, find, - * remove) that are shared between pmap<_K,_V,ManagerT>, pstringview - * and pvector. + * remove) that are shared between pmap<_K,_V,ManagerT> and pstringview. * * All rotation and rebalance functions accept an optional NodeUpdateFn callback * that is invoked after structural changes to update derived node attributes. - * By default avl_update_height is used (height-only). pvector passes a custom - * callback that also updates the order-statistic weight field (Issue #188). + * By default avl_update_height is used (height-only). * * Template parameter PPtr must support: * - is_null() @@ -407,11 +405,10 @@ inline constexpr std::size_t kDefaultGrowDenominator = 4; * its own duplicate rotation/rebalancing code (Issue #188). * * Additionally provides AvlInorderIterator — a shared in-order - * iterator template used by both pmap and pvector (Issue #188). + * iterator template used by pmap (Issue #188). * * @see pmap.h — pmap<_K,_V,ManagerT> (Issue #153) * @see pstringview.h — pstringview (Issue #151) - * @see pvector.h — pvector (Issue #186, #188) * @see free_block_tree.h — AvlFreeTree uses BlockPPtr adapter (Issue #188) * @version 0.5 (Issue #188 — BlockPPtr adapter for free_block_tree, AvlInorderIterator) */ @@ -2474,7 +2471,7 @@ static PPtr avl_find( IndexType root_idx, CompareThreeWayFn&& compare_three_way, } /// @brief In-order successor: next node in sorted order (Issue #188 deduplication). -/// Shared between pvector::iterator and pmap::iterator — eliminates ~50 lines of duplication. +/// Shared between pmap::iterator and other AVL-based containers. template static PPtr avl_inorder_successor( PPtr cur ) noexcept { if ( cur.is_null() ) @@ -2500,7 +2497,7 @@ template static PPtr avl_inorder_successor( PPtr cur ) noexcept /// @brief Initialize AVL tree node fields to empty state (Issue #188 deduplication). /// Sets left, right, parent to sentinel and height to 1. -/// Shared between pvector::push_back, pmap::insert, pstringview::_intern. +/// Shared between pmap::insert, pstringview::_intern. template static void avl_init_node( PPtr p ) noexcept { auto& tn = p.tree_node(); @@ -2673,12 +2670,12 @@ static BlockPPtr pptr_make( BlockPPtr return BlockPPtr( source._base, idx ); } -// ─── AvlInorderIterator: shared in-order iterator for pmap and pvector ─────── +// ─── AvlInorderIterator: shared in-order iterator for pmap ─────────────────── /** * @brief Shared in-order AVL tree iterator template (Issue #188). * - * Eliminates identical iterator structs in pmap and pvector (~35 lines each). + * Eliminates identical iterator structs in AVL-based containers. * Both containers can use this template directly or as a base. * * @tparam NodePPtr Persistent pointer type to tree nodes (e.g. pptr). @@ -4283,8 +4280,7 @@ template struct pallocator * @brief parray — persistent dynamic array with O(1) random access (Issue #195, Phase 3.2). * * Implements a dynamic array in the persistent address space (PAP). - * Unlike pvector (AVL-tree based, O(log n) random access), parray provides - * O(1) indexed access via a contiguous data block, similar to std::vector. + * Provides O(1) indexed access via a contiguous data block, similar to std::vector. * * Key properties: * - O(1) random access: at(i) / operator[] resolve directly to the i-th element. @@ -4329,7 +4325,6 @@ template struct pallocator * Mgr::destroy(); * @endcode * - * @see pvector.h — pvector (AVL-tree based persistent vector) * @see pstring.h — pstring (mutable persistent string) * @see persist_memory_manager.h — PersistMemoryManager (static model) * @see pptr.h — pptr (persistent pointer) @@ -5929,400 +5924,6 @@ template struct pstring } // namespace pmm -/** - * @file pmm/pvector.h - * @brief pvector — персистентный вектор для менеджера ПАП (Issue #186). - * - * Реализует шаблонный последовательный контейнер в персистентном адресном пространстве (ПАП). - * Каждый элемент вектора — это блок в ПАП, хранящий значение типа T. - * Вектор использует встроенные поля TreeNode для построения AVL-дерева, индексированного - * по позиции элемента в последовательности (order-statistic tree). - * - * Ключевые особенности: - * - Персистентный: гранульные индексы адресно-независимы при перезагрузке ПАП. - * - O(log n) для push_back, pop_back и доступа по индексу at(i). - * - O(1) для size(), front() и back(). - * - Узлы НЕ блокируются навечно — они могут быть освобождены после удаления из вектора. - * - * Реализация: - * Каждый узел хранит в поле `weight` общий размер своего поддерева (сам узел + потомки). - * Поля `left_offset`, `right_offset`, `parent_offset`, `avl_height` — стандартные - * поля AVL-дерева. Это позволяет за O(log n) находить k-й по порядку элемент. - * - * Пример использования: - * @code - * using Mgr = pmm::PersistMemoryManager; - * Mgr::create(64 * 1024); - * - * // Создать вектор - * using MyVec = Mgr::pvector; - * - * MyVec vec; - * vec.push_back(10); - * vec.push_back(20); - * vec.push_back(30); - * - * auto p = vec.at(1); // O(log n) доступ по индексу - * if (!p.is_null()) { - * int val = p->value; // 20 - * } - * - * std::size_t n = vec.size(); // O(1): 3 - * - * Mgr::destroy(); - * @endcode - * - * @see pmap.h — аналогичный персистентный контейнер (Issue #153) - * @see avl_tree_mixin.h — общие AVL-операции (Issue #155, #188) - * @see pptr.h — pptr (персистентный указатель) - * @see tree_node.h — TreeNode (встроенные поля каждого блока) - * @version 0.3 (Issue #188 — deduplicate AVL ops via avl_tree_mixin.h NodeUpdateFn hook) - */ - -#include -#include -#include - -namespace pmm -{ - -// Forward declaration -template struct pvector; - -// ─── pvector_node ───────────────────────────────────────────────────────────── - -/** - * @brief Узел pvector — хранит значение в ПАП (Issue #186). - * - * Каждый узел является отдельным блоком в ПАП. Встроенные поля TreeNode - * используются для организации AVL-дерева, индексированного по позиции: - * - weight → размер поддерева (сам узел + все потомки) - * - left_offset → левый потомок (меньшие индексы) - * - right_offset → правый потомок (большие индексы) - * - parent_offset→ родительский узел - * - avl_height → высота поддерева для балансировки - * - * @tparam T Тип хранимого значения. - */ -template struct pvector_node -{ - T value; ///< Значение узла -}; - -// ─── pvector ────────────────────────────────────────────────────────────────── - -/** - * @brief Персистентный последовательный контейнер (вектор) для ПАП (Issue #186). - * - * Объект pvector сам по себе не хранится в ПАП — он является хелпером на стеке, - * содержащим гранульный индекс корня AVL-дерева узлов элементов. - * Элементы (pvector_node) хранятся в ПАП и используют встроенные TreeNode-поля - * для организации AVL-дерева, индексированного по позиции (order-statistic tree). - * - * Поле `weight` каждого узла хранит размер его поддерева (сам + все потомки), - * что позволяет находить k-й элемент за O(log n) обходом дерева. - * - * Особенности: - * - push_back() за O(log n) — вставка в крайнюю правую позицию. - * - at(i) за O(log n) — поиск по позиции через размеры поддеревьев. - * - size() за O(1) — хранится в weight корня. - * - front()/back() за O(log n) — обход до крайнего левого/правого узла. - * - pop_back() за O(log n) — удаление крайнего правого узла. - * - Узлы НЕ блокируются навечно: можно освобождать через clear(). - * - * @tparam T Тип элемента. - * @tparam ManagerT Тип менеджера памяти (PersistMemoryManager). - */ -template struct pvector -{ - using manager_type = ManagerT; - using index_type = typename ManagerT::index_type; - using node_type = pvector_node; - using node_pptr = typename ManagerT::template pptr; - - /// @brief Sentinel value for "no node" in TreeNode fields. - static constexpr index_type no_block = ManagerT::address_traits::no_block; - - /// @brief Гранульный индекс корня AVL-дерева; 0 = пустой вектор. - index_type _root_idx; - - // ─── Конструктор ────────────────────────────────────────────────────────── - - /// @brief Создать пустой вектор. - pvector() noexcept : _root_idx( static_cast( 0 ) ) {} - - // ─── Методы доступа ─────────────────────────────────────────────────────── - - /// @brief Проверить, пуст ли вектор. - bool empty() const noexcept { return _root_idx == static_cast( 0 ); } - - /// @brief Получить количество элементов в векторе за O(1). - /// @return Количество элементов (из weight корня). - std::size_t size() const noexcept - { - if ( _root_idx == static_cast( 0 ) ) - return 0; - node_pptr root( _root_idx ); - return static_cast( root.tree_node().get_weight() ); - } - - // ─── Операции с вектором ────────────────────────────────────────────────── - - /** - * @brief Добавить элемент в конец вектора за O(log n). - * - * Создаёт новый узел в ПАП и вставляет его в крайнюю правую позицию AVL-дерева. - * - * @param val Значение для добавления. - * @return pptr на добавленный узел. Нулевой pptr при ошибке аллокации. - */ - node_pptr push_back( const T& val ) noexcept - { - node_pptr new_node = ManagerT::template allocate_typed(); - if ( new_node.is_null() ) - return node_pptr(); - - node_type* obj = ManagerT::template resolve( new_node ); - if ( obj == nullptr ) - return node_pptr(); - - obj->value = val; - - // Инициализируем поля нового узла (Issue #188: shared avl_init_node). - detail::avl_init_node( new_node ); - new_node.tree_node().set_weight( static_cast( 1 ) ); // поддерево размером 1 - - _avl_insert_rightmost( new_node ); - - return new_node; - } - - /** - * @brief Получить элемент по индексу за O(log n). - * - * Использует размеры поддеревьев (weight) для O(log n) поиска k-го элемента. - * - * @param index Индекс элемента (0-based). - * @return pptr на узел с данным индексом, или нулевой pptr если индекс вне диапазона. - */ - node_pptr at( std::size_t index ) const noexcept - { - if ( _root_idx == static_cast( 0 ) ) - return node_pptr(); - - node_pptr root( _root_idx ); - if ( index >= static_cast( root.tree_node().get_weight() ) ) - return node_pptr(); - - return _avl_find_by_index( node_pptr( _root_idx ), index ); - } - - /** - * @brief Получить первый элемент вектора за O(log n). - * - * @return pptr на первый узел, или нулевой pptr если вектор пуст. - */ - node_pptr front() const noexcept - { - if ( _root_idx == static_cast( 0 ) ) - return node_pptr(); - return detail::avl_min_node( node_pptr( _root_idx ) ); - } - - /** - * @brief Получить последний элемент вектора за O(log n). - * - * @return pptr на последний узел, или нулевой pptr если вектор пуст. - */ - node_pptr back() const noexcept - { - if ( _root_idx == static_cast( 0 ) ) - return node_pptr(); - return detail::avl_max_node( node_pptr( _root_idx ) ); - } - - /** - * @brief Удалить последний элемент вектора за O(log n). - * - * Освобождает память узла в ПАП и перебалансирует AVL-дерево. - * - * @return true если элемент был удалён, false если вектор пуст. - */ - bool pop_back() noexcept - { - if ( _root_idx == static_cast( 0 ) ) - return false; - - // Находим крайний правый узел (Issue #188: shared avl_max_node). - node_pptr target = detail::avl_max_node( node_pptr( _root_idx ) ); - - _avl_remove( target ); - ManagerT::template deallocate_typed( target ); - return true; - } - - /** - * @brief Удалить элемент по индексу за O(log n). - * - * Находит узел по индексу через order-statistic tree, удаляет его из AVL-дерева - * с перебалансировкой и освобождает память узла в ПАП. - * - * @param index Индекс элемента для удаления (0-based). - * @return true если элемент был удалён, false если индекс вне диапазона. - */ - bool erase( std::size_t index ) noexcept - { - if ( _root_idx == static_cast( 0 ) ) - return false; - - node_pptr root( _root_idx ); - if ( index >= static_cast( root.tree_node().get_weight() ) ) - return false; - - node_pptr target = _avl_find_by_index( node_pptr( _root_idx ), index ); - if ( target.is_null() ) - return false; - - _avl_remove( target ); - ManagerT::template deallocate_typed( target ); - return true; - } - - /** - * @brief Очистить вектор (удалить все элементы). - * - * Освобождает память всех узлов в ПАП. - */ - void clear() noexcept - { - while ( !empty() ) - { - pop_back(); - } - } - - /** - * @brief Сбросить вектор (для тестов). - * - * Сбрасывает _root_idx, но не освобождает данные в ПАП. - */ - void reset() noexcept { _root_idx = static_cast( 0 ); } - - // ─── Итератор ───────────────────────────────────────────────────────────── - - /// @brief Простой итератор для обхода вектора в порядке индексов (in-order). - /// Issue #188: uses shared AvlInorderIterator template to eliminate duplication. - using iterator = detail::AvlInorderIterator; - - /// @brief Начало итерации (самый левый узел = первый элемент). - /// Issue #188: delegates to shared avl_min_node. - iterator begin() const noexcept - { - if ( _root_idx == static_cast( 0 ) ) - return iterator(); - node_pptr min = detail::avl_min_node( node_pptr( _root_idx ) ); - return iterator( min.offset() ); - } - - /// @brief Конец итерации (sentinel = 0). - iterator end() const noexcept { return iterator( static_cast( 0 ) ); } - - private: - // ─── AVL order-statistic tree via avl_tree_mixin.h (Issue #188) ─────────── - - /// @brief Получить размер поддерева (weight), или 0 если узел нулевой. - static index_type _subtree_size( node_pptr p ) noexcept - { - if ( p.is_null() ) - return static_cast( 0 ); - auto idx = p.offset(); - if ( idx == no_block ) - return static_cast( 0 ); - return p.tree_node().get_weight(); - } - - /// @brief Node-update functor: updates both height and weight (Issue #188). - /// Used as NodeUpdateFn parameter for avl_tree_mixin rotation/rebalance functions. - struct _WeightUpdateFn - { - void operator()( node_pptr p ) const noexcept - { - if ( p.is_null() ) - return; - // Update height via shared helper. - detail::avl_update_height( p ); - // Update weight (subtree size) = 1 + left_weight + right_weight. - auto& tn = p.tree_node(); - - auto left_idx = tn.get_left(); - auto right_idx = tn.get_right(); - node_pptr left_p = ( left_idx != no_block ) ? node_pptr( left_idx ) : node_pptr(); - node_pptr right_p = ( right_idx != no_block ) ? node_pptr( right_idx ) : node_pptr(); - index_type lw = _subtree_size( left_p ); - index_type rw = _subtree_size( right_p ); - tn.set_weight( static_cast( 1 + lw + rw ) ); - } - }; - - /// @brief Вставить new_node в крайнюю правую позицию (конец последовательности). - void _avl_insert_rightmost( node_pptr new_node ) noexcept - { - // Use shared avl_insert with "always go right" comparator (Issue #188). - detail::avl_insert( - new_node, _root_idx, []( node_pptr ) -> bool { return false; }, // never go left — always rightmost - []( node_pptr p ) -> node_type* { return manager_type::template resolve( p ); }, - _WeightUpdateFn{} ); - } - - /** - * @brief Найти k-й по порядку элемент за O(log n). - * - * Использует поле weight (размер поддерева) для навигации. - * - * @param p Корень поддерева для поиска. - * @param index Индекс для поиска (0-based относительно поддерева p). - * @return pptr на найденный узел. - */ - static node_pptr _avl_find_by_index( node_pptr p, std::size_t index ) noexcept - { - while ( !p.is_null() ) - { - auto& tn = p.tree_node(); - auto left_idx = tn.get_left(); - - index_type left_size = ( left_idx != no_block ) ? node_pptr( left_idx ).tree_node().get_weight() - : static_cast( 0 ); - - if ( index < static_cast( left_size ) ) - { - // Ищем в левом поддереве. - p = node_pptr( left_idx ); - } - else if ( index == static_cast( left_size ) ) - { - // Нашли! - return p; - } - else - { - // Ищем в правом поддереве с скорректированным индексом. - index -= static_cast( left_size ) + 1; - auto right_idx = tn.get_right(); - if ( right_idx == no_block ) - return node_pptr(); - p = node_pptr( right_idx ); - } - } - return node_pptr(); - } - - /// @brief Удалить узел target из AVL-дерева и перебалансировать (Issue #188). - /// Delegates to detail::avl_remove with _WeightUpdateFn. - void _avl_remove( node_pptr target ) noexcept { detail::avl_remove( target, _root_idx, _WeightUpdateFn{} ); } -}; - -} // namespace pmm - /** * @file pmm/pstringview.h * @brief pstringview — персистентная строка только для чтения с интернированием (Issue #151, #184). @@ -6852,21 +6453,6 @@ template cla */ template using pmap = pmm::pmap<_K, _V, manager_type>; - /** - * @brief Алиас для персистентного вектора, привязанного к данному менеджеру (Issue #186). - * - * Позволяет писать: - * @code - * Mgr::pvector vec; - * vec.push_back(42); - * auto p = vec.at(0); - * @endcode - * вместо `pmm::pvector vec;` - * - * @tparam T Тип элемента. - */ - template using pvector = pmm::pvector; - /** * @brief Алиас для персистентного массива с O(1) индексацией (Issue #195, Phase 3.2). * diff --git a/plan.md b/plan.md index fb069b7..8c77ffb 100644 --- a/plan.md +++ b/plan.md @@ -217,6 +217,11 @@ pvector был бы предпочтительнее **только** при ч Этап 12: Проблема 3 — Этап C (потокобезопасная инициализация) 12.1 ✅ std::mutex в pam_pmm_state; lock_guard в init/destroy/reset/save/is_initialized/validate → тесты: все 715 тестов проходят (6 новых тестов на потокобезопасность) + +Этап 13: Обновление PMM до последней версии + 13.1 ✅ Обновление deps/pmm/pmm.h с v0.45.0 до v0.47.0 + Изменения в PMM v0.47.0: удалён тип pvector (полностью заменён на parray) + → тесты: все 715 тестов проходят ``` --- @@ -225,6 +230,7 @@ pvector был бы предпочтительнее **только** при ч | Дата | Изменение | |------|-----------| +| 2026-03-22 | Этап 13.1: обновление PMM с v0.45.0 до v0.47.0 — удалён pvector, заменён на parray (Issue #211) | | 2026-03-22 | Этап 12.1: std::mutex в pam_pmm_state для потокобезопасной инициализации (Issue #210) | | 2026-03-22 | Этап 11.1: pam_pmm_state& как явный параметр pam_pmm_* функций; pjson_db_pmm хранит ссылку на состояние (Issue #209) | | 2026-03-22 | Этап 10.4: const-корректность _walk_path — разделение на _walk_path_read (const) и _walk_path_create (Issue #208) |