From d6d870336d1d85ee2e4103b85c87c9abc926f455 Mon Sep 17 00:00:00 2001 From: konard Date: Mon, 11 May 2026 07:28:08 +0000 Subject: [PATCH 1/5] Initial commit with task details Adding .gitkeep for PR creation (default mode). This file will be removed when the task is complete. Issue: https://github.com/netkeep80/PersistMemoryManager/issues/382 --- .gitkeep | 1 + 1 file changed, 1 insertion(+) create mode 100644 .gitkeep diff --git a/.gitkeep b/.gitkeep new file mode 100644 index 00000000..b36c66d3 --- /dev/null +++ b/.gitkeep @@ -0,0 +1 @@ +# .gitkeep file auto-generated at 2026-05-11T07:28:08.162Z for PR creation at branch issue-382-7ed88e193dfe for issue https://github.com/netkeep80/PersistMemoryManager/issues/382 \ No newline at end of file From 5695406448b94e44212fe822af47c764577c31f6 Mon Sep 17 00:00:00 2001 From: konard Date: Mon, 11 May 2026 07:45:39 +0000 Subject: [PATCH 2/5] docs: recover archive requirements and remove docs/archive MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Resolves #382. Each docs/archive/* file was audited and its valuable content either confirmed represented in canonical docs / req/, or recovered into the req/ catalog as Draft entries when it described a backlog feature (encryption, compression, transactions, GC, shared memory) or a public API still in code (pptr byte_offset round-trip). req/ additions - fr-035 / if-012 / ac-013: byte_offset ↔ pptr round-trip (Recovered) - fr-036, fr-037, feat-011, qa-sec-001, asm-007: encryption / compression (Draft / Could, tracking #239) - fr-038, feat-012: transactional API (Draft / Could) - fr-039, feat-013: Mark & Sweep GC (Draft / Could) - fr-040, feat-014, if-013: shared memory storage backend (Draft / Could) Anchor / test coverage - Add include/pmm/typed_manager_api.h anchor for pptr_from_byte_offset - Add tests/test_issue211_byte_offset.cpp req-trace block (fr-035, if-012, ac-013) - Backfill req:* @see comments on issue#43 phase1/phase2 and issue#213 fuzz/overflow/concurrent tests so traceability check passes Docs cleanup - README.md: replace archive paragraph with reference to req/ - docs/index.md: remove Archive section from doc index - docs/repository_shape.md: replace Archive section with removal notice - docs/deletion_policy.md: replace 'archive' lifecycle category with 'recover-into-req' (history goes to req/ as Draft, then is deleted) - Delete docs/archive/ in full (12 files, ~3000 lines removed) Dispositions not requiring req/ additions - AVL Forest material: already covered by docs/pmm_avl_forest.md §7.1 - demo.md: already covered by if-011 + dep-002 - ppool: removed in #349, intentionally not re-added - BinDiffSynchronizer plan: explicitly out of PMM scope --- README.md | 6 +- docs/archive/PMM_AVL_Forest_Concept.md | 502 ------------------ docs/archive/avl_forest_analysis_ru.md | 57 -- docs/archive/demo.md | 429 --------------- docs/archive/phase1_safety.md | 70 --- docs/archive/phase2_persistence.md | 73 --- docs/archive/phase3_types.md | 430 --------------- docs/archive/phase4_api.md | 318 ----------- docs/archive/phase5_testing.md | 269 ---------- docs/archive/phase6_documentation.md | 228 -------- .../phase7_4_encryption_compression.md | 462 ---------------- docs/archive/plan.md | 375 ------------- docs/archive/plan4BinDiffSynchronizer.md | 253 --------- docs/deletion_policy.md | 31 +- docs/index.md | 5 - docs/repository_shape.md | 21 +- include/pmm/pptr.h | 2 +- include/pmm/typed_manager_api.h | 4 + req/04_features.md | 44 ++ req/05_functional_requirements.md | 64 +++ req/07_external_interfaces.md | 21 + req/08_quality_attributes.md | 13 + req/11_assumptions_dependencies.md | 9 + req/12_acceptance_criteria.md | 9 + tests/fuzz_allocator.cpp | 2 +- tests/test_issue211_byte_offset.cpp | 5 + tests/test_issue213_concurrent.cpp | 2 +- tests/test_issue213_fuzz.cpp | 2 +- tests/test_issue213_overflow.cpp | 2 +- tests/test_issue43_phase1_safety.cpp | 2 +- tests/test_issue43_phase2_persistence.cpp | 2 +- 31 files changed, 204 insertions(+), 3508 deletions(-) delete mode 100644 docs/archive/PMM_AVL_Forest_Concept.md delete mode 100644 docs/archive/avl_forest_analysis_ru.md delete mode 100644 docs/archive/demo.md delete mode 100644 docs/archive/phase1_safety.md delete mode 100644 docs/archive/phase2_persistence.md delete mode 100644 docs/archive/phase3_types.md delete mode 100644 docs/archive/phase4_api.md delete mode 100644 docs/archive/phase5_testing.md delete mode 100644 docs/archive/phase6_documentation.md delete mode 100644 docs/archive/phase7_4_encryption_compression.md delete mode 100644 docs/archive/plan.md delete mode 100644 docs/archive/plan4BinDiffSynchronizer.md diff --git a/README.md b/README.md index d9fa0f1e..6c7cffbe 100644 --- a/README.md +++ b/README.md @@ -338,8 +338,10 @@ cmake --build build --target pmm_demo - [Thread Safety](docs/thread_safety.md) - lock policies и concurrent usage; - [Block Header Semantics](docs/block_and_treenode_semantics.md) - семантика block header. -Исторические документы находятся в `docs/archive/` и не входят в основной -маршрут чтения. +Исторические phase-планы и проработка опциональных надстроек (compression / +encryption, transactions, GC, shared memory IPC) переосмыслены и зафиксированы +в каталоге требований [`req/`](req/) — соответствующие черновые `feat-*`/`fr-*` +имеют статус `Draft` или `Could` (Issue #382). ## Контрибьюция diff --git a/docs/archive/PMM_AVL_Forest_Concept.md b/docs/archive/PMM_AVL_Forest_Concept.md deleted file mode 100644 index ab244693..00000000 --- a/docs/archive/PMM_AVL_Forest_Concept.md +++ /dev/null @@ -1,502 +0,0 @@ -# PMM AVL-Forest Concept - -> Основной архитектурный документ по этой теме: [pmm_avl_forest.md](pmm_avl_forest.md). -> Этот файл сохраняет более широкую концептуальную и target-state записку. - -## Назначение документа - -Этот документ описывает **расширенную концептуальную модель `AVL-forest` для [PersistMemoryManager](../../include/pmm/persist_memory_manager.h#pmm-persistmemorymanager)** как -основы глобального проекта: - -- `PMM` как persistent address space manager; -- `pjson` как canonical dynamic data model; -- `pjson_db` как persistent relation/object store; -- `jsonRVM -> pjsonAVM` как execution layer; -- продуктовых режимов `Secure Vault` и `Secure Messenger`. - -Документ описывает **не текущее случайное состояние кода**, а **идеальное целевое понимание**, -к которому должен быть приведён `PMM`. - ---- - -## 1. Главная идея - -`PMM` хранит память как **персистентное линейное адресное пространство, измеряемое в гранулах**. - -Каждый блок памяти является одновременно: - -1. элементом линейного порядка блоков в ПАП; -2. потенциальным узлом одного из AVL-деревьев леса; -3. носителем базовой семантической информации о том, что лежит в блоке, - как он индексируется и к какому домену/типу принадлежит. - -Следовательно, `PMM` — это не просто allocator. -Это **persistent arena manager + intrusive index substrate**. - ---- - -## 2. Что такое AVL-forest - -### 2.1. Каноническое определение - -`AVL-forest` — это **лес персистентных интрузивных AVL-деревьев**, размещённых -поверх общего линейного ПАП. - -Слово **intrusive** означает: -метаданные узла дерева не вынесены в отдельную структуру, -а живут прямо в заголовке каждого блока. - -Слово **persistent** означает: -связи между узлами задаются не обычными указателями процесса, -а **индексами гранул / offsets**, поэтому после reload и при другом virtual base address -структура остаётся корректной. - -### 2.2. Главное дерево - -Главное дерево леса — это **дерево свободного ПАП**. - -Оно первично, потому что: -- именно через него allocator находит подходящий свободный блок; -- именно оно удерживает текущую картину свободного адресного пространства; -- оно связывает лес с линейным ПАП как с материальным substrate. - -Все остальные деревья являются вторичными индексами, -построенными поверх уже существующих блоков. - -Каноническая forest-policy для free-tree описана -в [free_tree_forest_policy.md](free_tree_forest_policy.md). - ---- - -## 3. Гранулы как универсальная единица - -В `PMM` всё измеряется в **гранулах**: - -- адреса блоков; -- смещения; -- ссылки между узлами; -- размеры блоков; -- ключи индексации; -- идентификаторы доменов/типов; -- некоторые служебные значения. - -Из этого следует каноническое правило: - -**поля заголовка блока должны интерпретироваться как значения в пространстве гранул**, -если явно не оговорено иное. - ---- - -## 4. Блок как атом AVL-forest - -Блок является базовым атомом `PMM`. - -Он содержит две логические части: - -1. **TreeNode** — семантика узла дерева; -2. **Block-level links** — семантика линейного порядка ПАП. - -Для `DefaultAddressTraits` layout таков: - -- AVL slot inside [BlockHeader](../../include/pmm/block_header.h#pmm-blockheader) = 24 байта; -- [Block](../../include/pmm/block.h#pmm-block) = 32 байта; -- блок идеально укладывается в 2 гранулы по 16 байт. - -Это важное архитектурное ограничение: -**размер блока желательно не увеличивать**. -Если нужна новая семантика, сначала надо пытаться: -- переосмыслить существующие поля; -- использовать service-blocks; -- использовать внешние index-nodes; -- использовать доменные структуры поверх block header, -а не раздувать сам заголовок. - ---- - -## 5. Каноническая семантика полей TreeNode - -### 5.1. `weight` - -`weight` — это **универсальное гранульное scalar-поле индексации**. - -Оно имеет тип `index_type`, то есть живёт в том же адресном/гранульном пространстве, -что и другие ключевые индексы `PMM`. - -Его общий смысл: - -- это значение, по которому дерево может выполнять упорядочивание и балансировку; -- это не обязательно “размер блока”; -- это не обязательно “вес” в узком AVL-смысле; -- это **универсальный индексируемый scalar леса**. - -#### Примеры интерпретации `weight` - -1. **В дереве свободных блоков**: - `weight` естественно означает размер блока в гранулах. - Тогда дерево свободного ПАП балансируется по размеру блока, - а поиск best-fit работает как поиск минимального подходящего размера. - -2. **В словаре [pstringview](../../include/pmm/pstringview.h#pmm-pstringview)**: - `weight` может означать гранульный индекс строки или производный ключ, - по которому дерево индексируется лексикографически или через внешний comparator. - -3. **В каталоге объектов / secondary index**: - `weight` может означать индекс type-name, symbol-id, entity-id, - либо другой scalar, связанный с узлом. - -Ключевая мысль: - -**`weight` — это не “поле только allocator-а”, а универсальное granule-key поле леса.** - -### 5.2. `left_offset` - -Гранульный индекс левого потомка внутри текущего дерева. - -### 5.3. `right_offset` - -Гранульный индекс правого потомка внутри текущего дерева. - -### 5.4. `parent_offset` - -Гранульный индекс родителя внутри текущего дерева. - -В идеальной модели это поле несёт двойной смысл: - -1. как обычный parent-link текущего AVL-дерева; -2. как семантический указатель на то, **в каком дереве/домене** этот узел сейчас живёт, - если это требуется лесной политикой и root registry. - -На практике это означает: -- текущий код может использовать его как parent узла; -- forest documentation должна отдельно описывать, - как дерево идентифицируется и как root-domain привязывается к узлу. - -### 5.5. `root_offset` - -`root_offset` — это **идентификатор дерева / домена / корня индексации**. - -В минимальной исторической модели он уже использовался как: -- `0` для дерева свободных блоков; -- `own_idx` для занятого блока. - -В канонической модели его роль должна быть расширена и формализована: - -- `0` — allocator free-space domain; -- другие значения — идентификаторы конкретных forest roots / domains; -- через root registry это поле связывает узел с именованным деревом; -- через него дерево становится не просто структурой, а **семантическим индексом**. - -Именно `root_offset` должен стать опорой для идеи: - -**лес AVL-деревьев семантически описывает то, что лежит в ПАП.** - -### 5.6. `avl_height` - -Высота AVL-поддерева. - -Это чисто структурное поле: -- используется для балансировки; -- `0` удобно трактовать как “узел сейчас не состоит в дереве”; -- не должно перегружаться произвольной прикладной семантикой. - -### 5.7. `node_type` - -`node_type` — это **тип содержимого блока**, а не просто “rw/ro flag”. - -Исторически поле уже использовалось как read/write marker, -но в идеальной модели оно должно стать: - -- либо индексом гранулы, указывающим на описание типа; -- либо compact type code / subtype code, - который может разрешаться через type dictionary; -- либо комбинацией subtype + флагов режима. - -Каноническая идея такая: - -- в блоке лежит не просто payload; -- у payload есть тип; -- этот тип тоже живёт в ПАП; -- тип может быть описан через [pstringview](../../include/pmm/pstringview.h#pmm-pstringview) или через специальный type-node; -- следовательно, `node_type` должен быть частью **самоописательности ПАП**. - -Именно здесь находится важное поле для будущего сближения с `pjson`, -но без смешения уровней: - -- `PMM` может уметь хранить и разрешать type descriptors; -- но `PMM` не обязан знать JSON semantics. - ---- - -## 6. Каноническая семантика полей Block - -### 6.1. `prev_offset` - -Гранульный индекс предыдущего блока в линейном ПАП. - -### 6.2. `next_offset` - -Гранульный индекс следующего блока в линейном ПАП. - -Эти два поля не относятся к логике дерева. -Они задают **физический порядок блоков в persistent address space**. - -Они нужны для: -- split; -- coalesce; -- physical traversal; -- repair linked list; -- диагностики целостности линейного ПАП. - -Это отдельный фундаментальный слой: - -**лес деревьев живёт поверх линейного ПАП, но не заменяет его.** - ---- - -## 7. Два одновременно существующих порядка - -Блок всегда может участвовать сразу в двух отношениях: - -1. **линейный порядок памяти** — через `prev_offset/next_offset`; -2. **иерархия текущего дерева** — через `left/right/parent/root/height/weight`. - -Это ключ к пониманию PMM: - -- ПАП материален и линеен; -- индексы поверх него логичны и древовидны; -- один и тот же блок живёт в обоих мирах одновременно. - ---- - -## 8. Семантическая самоописательность ПАП - -### 8.1. Главная идея - -Лес AVL-деревьев должен позволять **семантически описывать содержимое ПАП**. - -То есть в идеальной модели в памяти живут не только “данные”, -но и структуры, которые описывают: -- какие домены существуют; -- какие типы существуют; -- какие деревья существуют; -- как они называются; -- что в них индексируется; -- какие корни являются системными. - -### 8.2. Почему это допустимо - -Это не означает, что содержимое автоматически читаемо посторонним. - -ПАП может быть зашифрован. - -Самоописательность нужна не для внешнего наблюдателя, -а для: -- introspection; -- recovery; -- migration; -- tooling; -- semantic debugging; -- platform evolution. - -### 8.3. Как это должно выглядеть - -При инициализации ПАП должны создаваться как минимум: - -1. **корневой блок дерева свободных блоков**; -2. **базовый словарь символов / имён** ([pstringview](../../include/pmm/pstringview.h#pmm-pstringview) tree или его канонический преемник); -3. **служебные записи о доменах и типах**, описывающие базовые системные деревья. - -То есть `PMM` с самого начала должен создавать не пустую арену, -а **минимально самоописанную persistent environment**. - ---- - -## 9. Базовый системный словарь - -Нужен базовый forest-backed словарь, который описывает: -- названия системных деревьев; -- имена доменов; -- имена типов блоков; -- системные reserved symbols. - -Это может быть отдельное AVL-дерево на [pstringview](../../include/pmm/pstringview.h#pmm-pstringview) или на более общей symbol-node модели. - -Минимально там должны быть сущности, обозначающие, например: -- free-block-tree; -- system-dictionary; -- type-catalog; -- domain-catalog; -- root-registry. - -Это превращает `PMM` из просто “менеджера памяти” -в **менеджер самоописанного persistent substrate**. - ---- - -## 10. Root registry и domain registry - -`AVL-forest` не может быть полностью оформлен без явного реестра корней. - -Нужна каноническая система: - -- forest root registry; -- именованные домены; -- сопоставление domain-name -> root block; -- сопоставление type-name -> type descriptor; -- системные well-known roots. - -Это даёт: -- множественность деревьев; -- именованность деревьев; -- восстановимость их жизненного цикла; -- основу для path-like symbolic addressing. - ---- - -## 11. PMM path-like support: что именно допустимо - -Полная path semantics — это уровень `pjson` и `pjson_db`. - -Но в самом `PMM` допустим и полезен более низкий уровень: - -- symbolic domain names; -- root lookup by name; -- type lookup by name; -- служебные path-like адреса для системных доменов. - -Например: -- `system/free_tree` -- `system/type_catalog` -- `system/symbols` -- `system/domain_registry` - -Это не JSON path. -Это **symbolic addressing substrate**. - ---- - -## 12. Как это связано с pjson, pjson_db и pjsonAVM - -### 12.1. Что PMM обязан дать выше - -`PMM` должен дать: -- persistent address space; -- relocatable offsets; -- block headers; -- intrusive AVL-forest; -- root/domain registry; -- symbol/type dictionary foundation; -- recovery/verify discipline; -- encryption/compression seams. - -### 12.2. Чего PMM не должен знать - -`PMM` не должен знать: -- JSON object semantics; -- array semantics; -- `$ref` semantics; -- relation semantics; -- AVM execution semantics; -- pjson path traversal. - -### 12.3. Что будет делать pjson_db - -`pjson_db` уже поверх `PMM` будет задавать: -- dynamic values; -- object/array/string/binary layout; -- refs; -- entity identity; -- path traversal; -- entity catalogs; -- secondary indexes; -- journaling/snapshots. - -### 12.4. Что будет делать pjsonAVM - -`pjsonAVM` будет использовать `pjson_db` как persistent body, -но для быстрых lookup, symbol resolution, catalogs и runtime indexes -ей нужен зрелый `PMM AVL-forest` как нижний индексный substrate. - ---- - -## 13. Основные инженерные следствия - -### 13.1. Документация важнее преждевременных issue - -Сначала должен быть зафиксирован **канонический документ по AVL-forest**, -а уже потом backlog по доведению `PMM` до этой модели. - -### 13.2. Размер блока желательно сохранить - -Новые поля в блок добавлять крайне нежелательно. -Сначала надо использовать: -- reinterpretation existing fields; -- service blocks; -- root catalogs; -- external index nodes; -- policy layer. - -### 13.3. Один tree-slot на блок — фундаментальное ограничение - -Текущий block header естественно поддерживает один intrusive tree-slot на объект. - -Если нужен настоящий multi-index одного и того же payload, -то канонический путь — не обязательно расширять header, -а использовать **внешние index-node blocks**. - -### 13.4. `weight` должен быть документирован, а не “упрощён” - -Главная ошибка анализа возникает тогда, -когда `weight` пытаются понять как частный allocator-size field. - -Канонически это **универсальный granule-key forest field**. - -### 13.5. `node_type` должен стать частью type/self-description model - -Это поле не должно навсегда остаться только read-only flag. -Оно должно быть связано с type identity содержимого блока. - ---- - -## 14. Минимальное состояние PMM после `create()` - -После инициализации нового ПАП должны существовать хотя бы: - -1. линейный список блоков адресного пространства; -2. дерево свободных блоков как главное дерево allocator-а; -3. системный словарь символов/имён; -4. записи о базовых системных типах; -5. записи о базовых системных доменах; -6. root registry системных деревьев. - -То есть `create()` должна порождать не просто “кусок памяти”, -а **минимально оформленный persistent semantic substrate**. - ---- - -## 15. Короткая каноническая формула - -**PMM = persistent address space manager** - -**AVL-forest = semantic intrusive indexing layer inside PMM** - -**Block = атом линейного ПАП + атом одного дерева forest** - -**weight = универсальный granule-key** - -**node_type = тип содержимого блока / ссылка на описание типа** - -**root/domain registry = способ превратить множество деревьев в именованный forest** - -**pjson_db = слой, который поверх этого substrate задаёт динамическую модель значений** - -**pjsonAVM = слой, который поверх pjson_db даёт execution semantics** - ---- - -## 16. Что делать дальше - -1. Принять этот документ как каноническую target-модель `PMM AVL-forest`. -2. Вывести из него gap-analysis между текущим кодом и целевой моделью. -3. После этого разложить gaps в приоритетный backlog issue. -4. Уже затем переходить к уточнению persistent layout для `pjson_db`. diff --git a/docs/archive/avl_forest_analysis_ru.md b/docs/archive/avl_forest_analysis_ru.md deleted file mode 100644 index 0e7b6905..00000000 --- a/docs/archive/avl_forest_analysis_ru.md +++ /dev/null @@ -1,57 +0,0 @@ -# AVL-Forest: анализ идеи, применимость и направления развития - -## Короткий вывод -Идея AVL-forest в этом коде уже частично реализована как набор интрузивных AVL-индексов поверх общего persistent-аллокатора. Самая сильная сторона подхода — хранение ссылок дерева в заголовке блока и адресация через offsets, что делает структуры перемещаемыми и пригодными для сохранения образа памяти. Главные ограничения — forest пока не выделен как явная абстракция, нет реестра корней, слабая валидация указателей и есть несколько опасных мест для повреждения persistent state. - -## Что здесь уже есть по сути -Текущая реализация уже содержит ядро идеи AVL-forest: каждый выделенный или свободный блок несёт в заголовке поля `left/right/parent/height`, а сами связи задаются не обычными указателями, а индексами в гранулах. Это превращает любой блок памяти в потенциальный узел AVL-дерева без дополнительной node-wrapper структуры. - -На практике поверх этого механизма реализованы как минимум три уровня индексации: дерево свободных блоков для best-fit аллокации, обобщённые AVL-примитивы (`avl_insert`, `avl_remove`, `avl_find`) и контейнеры вроде [pmap](../../include/pmm/pmap.h#pmm-pmap) и таблицы интернированных строк. - -## Сильные стороны идеи -- Персистентность: offsets сохраняют корректность после reload и не зависят от виртуального адреса отображения памяти. -- Плотность хранения: метаданные узла живут в заголовке блока, отдельные heap-узлы не нужны. -- Интрузивность: любой объект, который лежит в allocator-managed block, автоматически может участвовать в AVL-индексе. -- Унификация: один и тот же набор примитивов годится и для allocator index, и для map/set/string interning. -- Предсказуемость: AVL даёт строгую верхнюю границу по высоте. - -## Где идея пока недоформализована -Сейчас forest существует скорее как скрытая архитектурная возможность, чем как отдельная сущность API. У менеджера есть только один глобальный `root_offset`, а остальные деревья либо живут внутри объектов, либо используют process-local static root. Из-за этого множество деревьев реально поддерживается, но их жизненный цикл и точки восстановления описаны неравномерно. - -## Варианты развития -1. **Forest of roots** — реестр корней в manager header или root-catalog object. -2. **Bucketed free forest** — отдельное дерево на класс размеров, внутри — порядок по адресу. -3. **Multi-index forest** — несколько независимых наборов link-полей на одном объекте. -4. **Transactional forest** — журнал/epoch/version для операций rotate/insert/remove. - -## Практические сценарии применения -- Persistent allocator. -- Persistent map/set. -- String interning / symbol tables. -- Каталоги объектов и secondary indexes. -- Metadata index для блочного стора. - -## Code review: что удачно -- Хорошо выделен слой адресации ([AddressTraits](../../include/pmm/address_traits.h#pmm-addresstraits)). -- Интрузивные поля дерева в header-е блока — сильное инженерное решение. -- AVL-примитивы вынесены в generic layer. -- Есть попытка восстановительного прохода при `load()`. - -## Code review: главные проблемы -1. **Критично**: валидация пользовательского указателя в `header_from_ptr_t()` слишком слабая. -2. **Высокий риск**: `recover_state()` может маскировать corruption вместо детектирования. -3. **Высокий риск**: корень таблицы интернированных строк хранится в `static inline _root_idx`, а не в persistent image. -4. **Средний риск**: typestate через `reinterpret_cast` и overlay-классы формально рискован по модели C++. -5. **Средний риск**: AVL-логика частично дублируется. -6. **Средний риск**: API ошибок в путях `deallocate`/`lock_block_permanent` слишком тихий. - -## Что делать дальше -1. Ввести явный `forest_root_registry`. -2. Усилить pointer validation. -3. Разделить режимы `repair` и `verify`. -4. Оформить forest как policy-abstraction. -5. Добавить property-based tests и recovery tests. -6. Для настоящего multi-index — либо расширить header, либо ввести внешние index nodes. - -## Итог -Идея сильная. Это не просто «AVL-дерево в аллокаторе», а почти универсальный механизм persistent intrusive indexing над общим arena image. Если оформить его как AVL-forest первого класса — с реестром корней, восстановлением, жёсткой валидацией и тестами инвариантов — получится хороший фундамент и для memory manager, и для встроенных persistent-контейнеров. diff --git a/docs/archive/demo.md b/docs/archive/demo.md deleted file mode 100644 index 1b3d1205..00000000 --- a/docs/archive/demo.md +++ /dev/null @@ -1,429 +0,0 @@ -# Техническое задание: Визуальное демо PersistMemoryManager - -## 1. Обзор - -Разработать кросс-платформенное демонстрационное приложение для визуализации работы -**PersistMemoryManager** (PMM) в реальном времени. - -Приложение строится на базе [Dear ImGui](https://github.com/ocornut/imgui) + OpenGL 3 / GLFW и -компилируется под Windows, Linux и macOS без изменений кода. - ---- - -## 2. Цели - -| # | Цель | -|---|------| -| 1 | Наглядно показать состояние управляемой памяти (карта байт в реальном времени) | -| 2 | Дать возможность запускать и останавливать тестовые сценарии в отдельных потоках | -| 3 | Отображать метрики менеджера в реальном времени | -| 4 | Позволять просматривать внутренние структуры данных в виде дерева | - ---- - -## 3. Архитектура - -``` -┌─────────────────────────────────────────────────┐ -│ DemoApp │ -│ ┌──────────┐ ┌──────────┐ ┌──────────────┐ │ -│ │MemMapView│ │MetricsView│ │StructTreeView│ │ -│ └──────────┘ └──────────┘ └──────────────┘ │ -│ │ -│ ┌──────────────────────────────────────────┐ │ -│ │ ScenarioManager │ │ -│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │ -│ │ │Thread 1 │ │Thread 2 │ │Thread N │ │ │ -│ │ └─────────┘ └─────────┘ └─────────┘ │ │ -│ └──────────────────────────────────────────┘ │ -│ │ -│ ┌──────────────────────────────────────────┐ │ -│ │ PersistMemoryManager │ │ -│ │ (pmm::PersistMemoryManager) │ │ -│ └──────────────────────────────────────────┘ │ -└─────────────────────────────────────────────────┘ -``` - -### 3.1 Потоковая модель - -- **Основной поток (UI)** — Dear ImGui render loop (~60 FPS). -- **Потоки сценариев** — каждый сценарий выполняется в `std::thread`; доступ к PMM - защищён встроенным `shared_mutex` библиотеки. -- Данные для отображения копируются из PMM-структур в UI-буферы под коротким локом - (`std::shared_lock`) один раз за кадр. - ---- - -## 4. Структура файлов - -``` -demo/ -├── CMakeLists.txt # Сборка демо -├── main.cpp # Точка входа, ImGui setup -├── demo_app.h/.cpp # DemoApp — главный класс приложения -├── mem_map_view.h/.cpp # Виджет карты памяти -├── metrics_view.h/.cpp # Виджет метрик -├── struct_tree_view.h/.cpp # Виджет дерева структур -├── scenario_manager.h/.cpp # Управление тестовыми потоками -├── scenarios.h/.cpp # Реализации 7 сценариев -└── third_party/ - ├── imgui/ # Dear ImGui (submodule / FetchContent) - └── glfw/ # GLFW (submodule / FetchContent) -``` - ---- - -## 5. Модуль: Карта памяти (`MemMapView`) - -### 5.1 Описание - -Отображает управляемую область PMM как двумерный растр: **1 байт = 1 пиксель**. - -| Состояние байта | Цвет | -|-----------------|------| -| Заголовок менеджера ([ManagerHeader](../../include/pmm/types.h#pmm-detail-managerheader)) | Синий `#4488FF` | -| Заголовок блока (`BlockHeader`), занятый | Тёмно-красный `#882222` | -| Данные пользователя, занятый блок | Красный `#FF4444` | -| Заголовок блока, свободный | Тёмно-серый `#444444` | -| Данные (свободный блок) | Белый `#FFFFFF` | -| Вне блоков | Чёрный `#000000` | - -### 5.2 Параметры отображения - -- **Ширина растра** — настраивается ползунком (32 … 512 пикс.), по умолчанию 256. -- **Масштаб** — от 1×1 до 4×4 пикселей на байт. -- **Авто-масштаб** — автоматически подбирает ширину, чтобы карта помещалась в окно. -- При наведении курсора на пиксель — всплывающая подсказка: - смещение, тип (заголовок / данные / свободно), принадлежность блоку. - -### 5.3 Обновление - -Карта перерисовывается каждый кадр из снимка состояния PMM, созданного за один -`std::shared_lock`-проход по всем блокам. - ---- - -## 6. Модуль: Метрики (`MetricsView`) - -Отображает в реальном времени следующие показатели (обновляются каждый кадр): - -| Метрика | Источник PMM API | -|---------|-----------------| -| Общий размер памяти | `total_size()` | -| Занято | `used_size()` | -| Свободно | `free_size()` | -| Всего блоков | `MemoryStats::total_blocks` | -| Занятых блоков | `MemoryStats::allocated_blocks` | -| Свободных блоков | `MemoryStats::free_blocks` | -| Фрагментация (число свободных сегментов) | `fragmentation()` | -| Наибольший свободный блок | `MemoryStats::largest_free` | -| Наименьший свободный блок | `MemoryStats::smallest_free` | -| Всего операций alloc/dealloc | внутренние счётчики сценариев | -| Скорость операций (ops/s) | скользящее среднее за 1 с | - -Дополнительно строятся **графики истории** (scrolling plot, 256 точек): - -- `used_size` во времени. -- `fragmentation` во времени. -- ops/s во времени. - ---- - -## 7. Модуль: Дерево структур (`StructTreeView`) - -Иерархическое дерево на основе `ImGui::TreeNode`: - -``` -PersistMemoryManager -├── ManagerHeader -│ ├── magic: 0x504D4D5F56303130 -│ ├── total_size: 1048576 -│ ├── used_size: 204800 -│ ├── block_count: 42 -│ ├── free_count: 17 -│ ├── alloc_count: 25 -│ ├── first_block_offset: 128 -│ └── first_free_offset: 256 -└── Blocks [42] - ├── Block #0 offset=128 size=4096 FREE - ├── Block #1 offset=4224 size=8192 USED user_size=8000 align=16 - ├── Block #2 offset=12416 size=128 FREE - ... - └── Block #N ... -``` - -- Клик на блок — выделяет соответствующий регион на карте памяти (подсветка). -- При количестве блоков > 1000 показывается только первые 500 и последние 500 с - сообщением «... X блоков скрыто ...». - ---- - -## 8. Модуль: Управление сценариями (`ScenarioManager`) - -### 8.1 UI - -Панель «Scenarios»: - -``` -[▶ Start All] [■ Stop All] -────────────────────────────────────────────────── - Scenario Status Ops alloc/s dealloc/s -────────────────────────────────────────────────── -[▶][■] Linear Fill RUNNING 12 034 1 200 1 200 -[▶][■] Random Stress STOPPED 0 0 0 -[▶][■] Fragmentation Demo RUNNING 8 210 410 410 -[▶][■] Large Blocks STOPPED 0 0 0 -[▶][■] Tiny Blocks RUNNING 45 003 4 500 4 500 -[▶][■] Mixed Sizes STOPPED 0 0 0 -[▶][■] Persistence Cycle STOPPED 0 0 0 -────────────────────────────────────────────────── -``` - -### 8.2 Параметры сценария (на каждый) - -| Параметр | Тип | Диапазон | По умолчанию | -|----------|-----|----------|--------------| -| Min block size | `size_t` | 8 … 1 MB | зависит от сценария | -| Max block size | `size_t` | 8 … 1 MB | зависит от сценария | -| Alloc frequency | `float` (ops/s) | 1 … 100 000 | зависит от сценария | -| Dealloc frequency | `float` (ops/s) | 1 … 100 000 | зависит от сценария | -| Max live blocks | `int` | 1 … 10 000 | 100 | - -Параметры доступны в collapsible-секции под каждым сценарием. - ---- - -## 9. Описание сценариев - -### 9.1 Linear Fill - -Последовательно выделяет блоки одинакового размера до заполнения памяти, затем -освобождает всё и повторяет. Демонстрирует линейное заполнение и полную очистку. - -| Параметр | Значение по умолчанию | -|----------|-----------------------| -| Min/Max block size | 256 / 256 байт | -| Alloc frequency | 500 ops/s | -| Dealloc frequency | 0 (только после полного заполнения) | - -### 9.2 Random Stress - -Случайно чередует `allocate` и `deallocate` в случайном порядке. -Демонстрирует стрессовую нагрузку со случайными размерами. - -| Параметр | Значение по умолчанию | -|----------|-----------------------| -| Min/Max block size | 64 / 4096 байт | -| Alloc frequency | 2 000 ops/s | -| Dealloc frequency | 1 800 ops/s | - -### 9.3 Fragmentation Demo - -Чередует выделение маленьких и больших блоков, освобождает только маленькие, -демонстрирует фрагментацию. - -| Параметр | Значение по умолчанию | -|----------|-----------------------| -| Min/Max block size | 16 / 16 384 байт | -| Alloc frequency | 300 ops/s | -| Dealloc frequency | 250 ops/s | - -### 9.4 Large Blocks - -Работает исключительно с крупными блоками. Показывает автоматическое расширение -памяти при нехватке. - -| Параметр | Значение по умолчанию | -|----------|-----------------------| -| Min/Max block size | 65 536 / 262 144 байт | -| Alloc frequency | 20 ops/s | -| Dealloc frequency | 18 ops/s | - -### 9.5 Tiny Blocks - -Интенсивно выделяет и освобождает микроблоки. Проверяет накладные расходы на -управление большим числом маленьких блоков. - -| Параметр | Значение по умолчанию | -|----------|-----------------------| -| Min/Max block size | 8 / 32 байт | -| Alloc frequency | 10 000 ops/s | -| Dealloc frequency | 9 500 ops/s | - -### 9.6 Mixed Sizes - -Несколько параллельных «рабочих» потоков с разными профилями выделения. -Имитирует реальную нагрузку приложения. - -| Параметр | Значение по умолчанию | -|----------|-----------------------| -| Min/Max block size | 32 / 32 768 байт | -| Alloc frequency | 1 000 ops/s | -| Dealloc frequency | 950 ops/s | - -### 9.7 Persistence Cycle - -Сохраняет образ PMM в файл (`pmm_demo.bin`) с помощью `save()`, затем перезагружает -через `load()`. Демонстрирует персистентность данных. - -| Параметр | Значение по умолчанию | -|----------|-----------------------| -| Min/Max block size | 128 / 1024 байт | -| Cycle period | 5 s | - ---- - -## 10. Главное окно и навигация - -``` -┌────────────────────────────────────────────────────────────────────┐ -│ PersistMemoryManager Demo [?] [Settings] │ -├───────────────┬──────────────────────────┬─────────────────────────┤ -│ Scenarios │ Memory Map │ Metrics │ -│ (§ 8) │ (§ 5) │ (§ 6) │ -│ │ │ │ -│ ├──────────────────────────┤ │ -│ │ Struct Tree │ │ -│ │ (§ 7) │ │ -└───────────────┴──────────────────────────┴─────────────────────────┘ -``` - -- Все панели — `ImGui::Begin` / `End` и могут быть отстыкованы (`dockspace`). -- Кнопка `[?]` — встроенная справка с описанием цветового кодирования. -- Кнопка `[Settings]` — диалог настроек: - - Начальный размер памяти PMM (1 MB … 256 MB, по умолчанию 8 MB). - - FPS-лимит (10 … 144, по умолчанию 60). - - Тема ImGui (Dark / Light / Classic). - ---- - -## 11. Используемые вызовы PMM API - -| Вызов | Где используется | -|-------|-----------------| -| `PersistMemoryManager::create(buf, size)` | Инициализация при старте | -| `PersistMemoryManager::load(buf, size)` | Сценарий Persistence Cycle | -| [PersistMemoryManager::destroy()](../../include/pmm/persist_memory_manager.h#pmm-persistmemorymanager-destroy) | Сценарий Persistence Cycle, выход | -| `PersistMemoryManager::instance()` | Получение синглтона в сценариях | -| `mgr->allocate(size, align)` | Все сценарии | -| `mgr->deallocate(ptr)` | Все сценарии | -| `mgr->reallocate(ptr, new_size)` | Сценарий Mixed Sizes | -| `mgr->total_size()` | MetricsView | -| `mgr->used_size()` | MetricsView | -| `mgr->free_size()` | MetricsView | -| `mgr->fragmentation()` | MetricsView | -| `mgr->validate()` | Фоновая проверка каждые 5 с | -| `mgr->dump_stats()` | Кнопка «Dump to stdout» | -| `pmm::get_stats(mgr)` | MetricsView (MemoryStats) | -| `pmm::get_info(mgr, ptr)` | Подсказка в StructTreeView | - ---- - -## 12. Требования к сборке - -| Компонент | Версия | -|-----------|--------| -| CMake | ≥ 3.16 | -| C++ стандарт | C++17 | -| OpenGL | ≥ 3.3 | -| GLFW | 3.3+ (FetchContent) | -| Dear ImGui | 1.90+ (FetchContent, ветка `docking`) | -| Потоки | `std::thread` (POSIX / Win32) | - -### 12.1 Команды сборки - -```bash -cmake -B build -DCMAKE_BUILD_TYPE=Release -cmake --build build --target pmm_demo -./build/demo/pmm_demo -``` - -### 12.2 CMakeLists.txt (demo/) - -```cmake -cmake_minimum_required(VERSION 3.16) -project(pmm_demo LANGUAGES CXX) - -set(CMAKE_CXX_STANDARD 17) -set(CMAKE_CXX_STANDARD_REQUIRED ON) - -include(FetchContent) - -FetchContent_Declare( - glfw - GIT_REPOSITORY https://github.com/glfw/glfw.git - GIT_TAG 3.4 -) -FetchContent_Declare( - imgui - GIT_REPOSITORY https://github.com/ocornut/imgui.git - GIT_TAG docking -) -FetchContent_MakeAvailable(glfw imgui) - -add_executable(pmm_demo - main.cpp - demo_app.cpp - mem_map_view.cpp - metrics_view.cpp - struct_tree_view.cpp - scenario_manager.cpp - scenarios.cpp - ${imgui_SOURCE_DIR}/imgui.cpp - ${imgui_SOURCE_DIR}/imgui_draw.cpp - ${imgui_SOURCE_DIR}/imgui_tables.cpp - ${imgui_SOURCE_DIR}/imgui_widgets.cpp - ${imgui_SOURCE_DIR}/backends/imgui_impl_glfw.cpp - ${imgui_SOURCE_DIR}/backends/imgui_impl_opengl3.cpp -) - -target_include_directories(pmm_demo PRIVATE - ${imgui_SOURCE_DIR} - ${imgui_SOURCE_DIR}/backends - ${CMAKE_SOURCE_DIR}/include # persist_memory_manager.h -) - -find_package(OpenGL REQUIRED) -target_link_libraries(pmm_demo PRIVATE - glfw - OpenGL::GL - Threads::Threads -) -``` - ---- - -## 13. Нефункциональные требования - -| Требование | Значение | -|------------|---------| -| FPS при 1 сценарии | ≥ 60 FPS | -| FPS при 7 сценариях | ≥ 30 FPS | -| Задержка обновления карты | ≤ 1 кадр (≤ ~17 мс при 60 FPS) | -| Потребление памяти процессом | ≤ 512 MB | -| Корректное завершение | без утечек памяти и гонок данных | - ---- - -## 14. Этапы разработки - -| Этап | Содержание | Результат | -|------|-----------|-----------| -| 1 | Настройка ImGui + GLFW окно, базовая структура `DemoApp` | Окно открывается | -| 2 | `MemMapView`: рендеринг карты памяти из снимка PMM | Карта отображается | -| 3 | `MetricsView`: метрики + scrolling plots | Цифры и графики в реальном времени | -| 4 | `ScenarioManager` + 3 базовых сценария (Linear Fill, Random Stress, Tiny Blocks) | Потоки запускаются и останавливаются | -| 5 | Оставшиеся 4 сценария | Все 7 сценариев работают | -| 6 | `StructTreeView` + подсветка блока в карте | Дерево кликабельно | -| 7 | Settings dialog, docking, polish | Финальный вид | -| 8 | Тесты, CI, документация | Проект готов к релизу | - ---- - -## 15. Ссылки - -- Репозиторий PersistMemoryManager: https://github.com/netkeep80/PersistMemoryManager -- Dear ImGui: https://github.com/ocornut/imgui -- GLFW: https://github.com/glfw/glfw -- Техническое задание на библиотеку: [tz.md](tz.md) -- Архитектура библиотеки: [docs/architecture.md](docs/architecture.md) diff --git a/docs/archive/phase1_safety.md b/docs/archive/phase1_safety.md deleted file mode 100644 index 6bc2800d..00000000 --- a/docs/archive/phase1_safety.md +++ /dev/null @@ -1,70 +0,0 @@ -# Phase 1: Safety and Robustness (Issue #43) - -This document describes the safety and robustness improvements implemented in Phase 1 of the development plan. - -## 1.1 Exception Safety in `create_typed` - -**Problem:** `create_typed()` and `destroy_typed()` are marked `noexcept`, but placement-new could call throwing constructors. If the constructor throws, the allocated memory would leak. - -**Solution:** Added `static_assert` constraints: -- `create_typed(args...)` requires `std::is_nothrow_constructible_v` -- `destroy_typed(p)` requires `std::is_nothrow_destructible_v` - -For types with throwing constructors, use `allocate_typed()` followed by manual placement-new with try/catch. - -**Files:** `include/pmm/persist_memory_manager.h` - -## 1.2 Bounds Check in `resolve()` - -**Problem:** `resolve()` and `resolve_at()` did not verify that the computed offset was within the managed heap. A corrupted [pptr](../../include/pmm/pptr.h#pmm-pptr) could lead to out-of-bounds memory access. - -**Solution:** -- Added `assert(byte_off + sizeof(T) <= total_size)` in `resolve()` for debug builds -- Added new public method `is_valid_ptr(pptr)` for explicit runtime validation - -```cpp -Mgr::pptr p = Mgr::allocate_typed(); -if (Mgr::is_valid_ptr(p)) { - // safe to dereference - *p = 42; -} -``` - -**Files:** `include/pmm/persist_memory_manager.h` - -## 1.3 Integer Overflow Protection - -**Problem:** In `allocate_from_block()`, the computation `needed_gran = kBlkHdrGran + data_gran` could overflow for extremely large allocation requests. Similarly, `needed_gran + min_rem_gran` in the split check could overflow. - -**Solution:** Added explicit overflow checks before each addition: -```cpp -if (data_gran > std::numeric_limits::max() - kBlkHdrGran) - return nullptr; // overflow protection -``` - -The same protection was added in [PersistMemoryManager::allocate()](../../include/pmm/persist_memory_manager.h#pmm-persistmemorymanager-allocate) for the `kBlockHdrGranules + data_gran` computation. - -**Files:** `include/pmm/allocator_policy.h`, `include/pmm/persist_memory_manager.h` - -## 1.4 Runtime Checks for Critical State Transitions - -**Problem:** `assert()` is disabled in Release builds. Critical block state checks in [FreeBlock::cast_from_raw()](../../include/pmm/block_state.h#pmm-freeblock-cast_from_raw) and [AllocatedBlock::cast_from_raw()](../../include/pmm/block_state.h#pmm-allocatedblock-cast_from_raw) relied solely on `assert`, meaning corrupted block state would go undetected in production. - -**Solution:** Replaced assert-only checks with runtime checks that return `nullptr` in both Debug and Release builds: -- `FreeBlock::cast_from_raw(nullptr)` returns `nullptr` (was: `assert(false)` + UB) -- `FreeBlock::cast_from_raw(non_free_block)` returns `nullptr` (was: `assert(false)` + UB) -- `AllocatedBlock::cast_from_raw(nullptr)` returns `nullptr` -- `AllocatedBlock::cast_from_raw(free_block)` returns `nullptr` - -The `assert` is still present for Debug diagnostics, but the code no longer relies on it for correctness. - -**Files:** `include/pmm/block_state.h` - -## Tests - -All Phase 1 improvements are covered by `tests/test_issue43_phase1_safety.cpp`: -- Compile-time noexcept enforcement (1.1) -- `is_valid_ptr()` with null, valid, and uninitialized pointers (1.2) -- Overflow protection with large allocation sizes (1.3) -- `cast_from_raw()` null-safety (1.4) -- Full allocation/deallocation integration test diff --git a/docs/archive/phase2_persistence.md b/docs/archive/phase2_persistence.md deleted file mode 100644 index 41ae320d..00000000 --- a/docs/archive/phase2_persistence.md +++ /dev/null @@ -1,73 +0,0 @@ -# Phase 2: Persistence and Reliability (Issue #43) - -This document describes the persistence and reliability improvements implemented in Phase 2 of the development plan. - -## 2.1 CRC32 Checksum for Persisted Images - -**Problem:** `save_manager()` / `load_manager_from_file()` performed raw binary copy without any integrity checks. A corrupted file would be loaded silently, potentially corrupting the heap. - -**Solution:** Added CRC32 (ISO 3309, polynomial 0xEDB88320) checksum computation and verification: - -- **On save:** `save_manager()` computes CRC32 over the entire managed region (treating the `crc32` field itself as zero) and stores it in `ManagerHeader.crc32`. -- **On load:** `load_manager_from_file()` recomputes the CRC32 and compares it to the stored value. If they don't match, load fails. -- **Backward compatibility:** Images with `crc32 == 0` (saved before Phase 2.1) are accepted without CRC verification. - -The CRC32 field uses 4 bytes from the previously reserved `_reserved[8]` space in [ManagerHeader](../../include/pmm/types.h#pmm-detail-managerheader). The struct size remains unchanged (64 bytes for DefaultAddressTraits). - -**Files:** `include/pmm/types.h` (CRC32 utility + header field), `include/pmm/io.h` (save/load integration) - -## 2.2 Atomic Save (Write-Then-Rename) - -**Problem:** If the process crashed during `fwrite()`, the output file would be corrupted with a partial write, destroying the previous valid image. - -**Solution:** `save_manager()` now uses the write-then-rename pattern: -1. Writes the entire image to a temporary file (`filename.tmp`) -2. Flushes the file buffer with `fflush()` -3. Atomically renames the temporary file to the target filename - -On POSIX, `rename()` is atomic if source and destination are on the same filesystem. On Windows, `MoveFileExA` with `MOVEFILE_REPLACE_EXISTING` is used. - -If any step fails, the temporary file is cleaned up and the original file remains untouched. - -**Files:** `include/pmm/io.h` - -## 2.3 MMapStorage Expand Support - -**Problem:** [MMapStorage::expand()](../../include/pmm/mmap_storage.h#pmm-mmapstorage-expand) always returned `false`. Persistent databases backed by mmap files could not grow dynamically. - -**Solution:** Implemented `expand()` through remap: - -**POSIX:** -1. `munmap()` the current mapping -2. `ftruncate()` the file to the new size -3. `mmap()` a new mapping at the new size - -**Windows:** -1. `FlushViewOfFile()` + `UnmapViewOfFile()` the current view -2. Close the file mapping handle -3. `SetFilePointerEx()` + `SetEndOfFile()` to resize the file -4. `CreateFileMappingA()` + `MapViewOfFile()` to create a new mapping - -Growth strategy mirrors HeapStorage: grow by 25% plus the requested additional bytes. - -**Important:** After `expand()`, `base_ptr()` returns a new address. All previously obtained raw pointers into the mapping are invalidated. Persistent pointers (`pptr`) remain valid since they use granule indices, not raw pointers. - -If the remap fails, the implementation attempts to restore the mapping at the old size. - -**Files:** `include/pmm/mmap_storage.h` - -## Tests - -All Phase 2 improvements are covered by `tests/test_issue43_phase2_persistence.cpp`: - -- CRC32 known test vectors (2.1) -- CRC32 save/load roundtrip (2.1) -- CRC32 corruption detection (2.1) -- CRC32 backward compatibility with pre-Phase 2 images (2.1) -- `compute_image_crc32` ignores the crc32 field itself (2.1) -- Atomic save leaves no temporary files (2.2) -- Atomic save correctly replaces previous files (2.2) -- MMapStorage expand basic functionality (2.3) -- MMapStorage expand with zero bytes (2.3) -- MMapStorage expand on unmapped storage (2.3) -- MMapStorage expand multiple times (2.3) diff --git a/docs/archive/phase3_types.md b/docs/archive/phase3_types.md deleted file mode 100644 index 55ba4bc0..00000000 --- a/docs/archive/phase3_types.md +++ /dev/null @@ -1,430 +0,0 @@ -# Фаза 3: Типы для BinDiffSynchronizer - -> Реализация начата в Issue #45. Продолжена в Issue #195. - -## 3.1 Мутабельная персистентная строка `pstring` ✅ - -**Issue:** #45 -**Файл:** `include/pmm/pstring.h` -**Тесты:** `tests/test_issue45_pstring.cpp` (21 тест) - -### Описание - -`pstring` — мутабельная строка, хранящаяся в персистентном адресном пространстве (ПАП). -В отличие от [pstringview](../../include/pmm/pstringview.h#pmm-pstringview) (read-only, interned), [pstring](../../include/pmm/pstring.h#pmm-pstring) поддерживает изменение содержимого. - -### Архитектура - -``` -pstring (в ПАП) Блок данных (в ПАП) -┌───────────────┐ ┌─────────────────────┐ -│ _length: u32 │ │ h e l l o \0 │ -│ _capacity: u32│──idx──→ │ │ -│ _data_idx │ └─────────────────────┘ -└───────────────┘ -``` - -- **Заголовок** (pstring struct): хранит длину, ёмкость и гранульный индекс блока данных -- **Блок данных**: отдельный блок в ПАП, содержащий null-terminated строку -- **Переаллокация**: при росте выделяется новый блок с удвоенной ёмкостью (amortized O(1)) -- **POD-структура**: `std::is_trivially_copyable_v == true` - -### API - -```cpp -using Mgr = pmm::presets::SingleThreadedHeap; -Mgr::create(64 * 1024); - -// Создание -Mgr::pptr p = Mgr::create_typed(); - -// Присвоение -p->assign("hello"); // assign(const char*) → bool -p->append(" world"); // append(const char*) → bool - -// Чтение -const char* s = p->c_str(); // "hello world" -std::size_t n = p->size(); // 11 -bool empty = p->empty(); // false -char c = (*p)[0]; // 'h' - -// Изменение -p->assign("new value"); // переаллокация при необходимости -p->clear(); // length = 0, буфер сохраняется - -// Сравнение -*p == "test"; // operator==(const char*) -*p != "test"; // operator!=(const char*) -*p1 == *p2; // operator==(const pstring&) -*p1 < *p2; // operator<(const pstring&) — лексикографическое - -// Освобождение -p->free_data(); // деаллоцировать блок данных -Mgr::destroy_typed(p); // деаллоцировать сам pstring - -Mgr::destroy(); -``` - -### Отличия от pstringview - -| Свойство | pstringview | pstring | -|----------|-------------|---------| -| Мутабельность | Read-only | Мутабельная | -| Интернирование | Да (дедупликация) | Нет | -| Блокировка блока | Навечно (lock_block_permanent) | Обычный блок | -| Хранение данных | Встроенное (flexible array) | Отдельный блок | -| Использование | Константные строки, ключи | JSON-значения, динамические данные | - -## 3.2 Персистентный массив `parray` с O(1) индексацией ✅ - -**Issue:** #195 -**Файл:** `include/pmm/parray.h` -**Тесты:** `tests/test_issue195_parray.cpp` (22 теста) - -### Описание - -`parray` — динамический массив в персистентном адресном пространстве (ПАП) -с O(1) произвольным доступом. [parray](../../include/pmm/parray.h#pmm-parray) хранит элементы в непрерывном блоке памяти, -аналогично `std::vector`. - -### Архитектура - -``` -parray (в ПАП) Блок данных (в ПАП) -┌───────────────┐ ┌──────────────────────┐ -│ _size: u32 │ │ T[0] T[1] ... T[n-1] │ -│ _capacity: u32│──idx────→ │ │ -│ _data_idx │ └──────────────────────┘ -└───────────────┘ -``` - -- **Заголовок** (parray struct): хранит размер, ёмкость и гранульный индекс блока данных -- **Блок данных**: отдельный непрерывный блок в ПАП, содержащий массив элементов T -- **Переаллокация**: при росте выделяется новый блок с удвоенной ёмкостью (amortized O(1)) -- **POD-структура**: `std::is_trivially_copyable_v == true` -- **Требование к T**: тип элемента должен быть trivially copyable - -### API - -```cpp -using Mgr = pmm::presets::SingleThreadedHeap; -Mgr::create(64 * 1024); - -// Создание -Mgr::pptr> p = Mgr::create_typed>(); - -// Добавление элементов -p->push_back(10); // push_back(const T&) → bool -p->push_back(20); -p->push_back(30); - -// O(1) произвольный доступ -int* elem = p->at(1); // указатель на элемент, nullptr при выходе за границы -int val = (*p)[0]; // значение по индексу (без проверки границ) -int* first = p->front(); // первый элемент -int* last = p->back(); // последний элемент -int* raw = p->data(); // указатель на блок данных - -// Модификация -p->set(1, 42); // set(i, value) → bool -p->pop_back(); // удалить последний элемент - -// Запрос состояния -std::size_t n = p->size(); // 2 -std::size_t cap = p->capacity();// >= 2 -bool empty = p->empty(); // false - -// Управление ёмкостью -p->reserve(100); // предварительное выделение -p->resize(50); // изменение размера (новые элементы = T{}) - -// Очистка -p->clear(); // size = 0, буфер сохраняется - -// Сравнение -*p1 == *p2; // operator==(const parray&) -*p1 != *p2; // operator!=(const parray&) - -// Освобождение -p->free_data(); // деаллоцировать блок данных -Mgr::destroy_typed(p); // деаллоцировать сам parray - -Mgr::destroy(); -``` - -## 3.3 Доработка `pmap` — erase, size, iterator, clear ✅ - -**Issue:** #196 -**Файл:** `include/pmm/pmap.h` -**Тесты:** `tests/test_issue196_pmap_erase.cpp` (22 теста) - -### Описание - -Доработка персистентного словаря `pmap<_K, _V, ManagerT>` для полноценного использования -в качестве замены `pmap_pmm` из BinDiffSynchronizer. Добавлены операции удаления, подсчёта -элементов, итерации и очистки. - -### Новые методы - -```cpp -using Mgr = pmm::presets::SingleThreadedHeap; -Mgr::create(64 * 1024); - -using MyMap = Mgr::pmap; -MyMap map; - -// Вставка -map.insert(10, 100); -map.insert(20, 200); -map.insert(30, 300); - -// Удаление по ключу — O(log n) -bool removed = map.erase(20); // true -bool missing = map.erase(99); // false - -// Количество элементов — O(n) -std::size_t n = map.size(); // 2 - -// Итерация в порядке ключей (in-order обход AVL) -for (auto it = map.begin(); it != map.end(); ++it) { - auto node = *it; - // node->key, node->value — в порядке возрастания ключей -} - -// Очистка (удаление всех элементов с деаллокацией) -map.clear(); - -Mgr::destroy(); -``` - -### Детали реализации - -- **erase(key)**: находит узел через `avl_find()`, удаляет из AVL-дерева через - `detail::avl_remove()`, деаллоцирует блок в ПАП через `deallocate_typed()` -- **size()**: подсчёт элементов за O(n) через `detail::avl_subtree_count()` -- **iterator**: используется общий `detail::AvlInorderIterator` из - `avl_tree_mixin.h` — in-order обход AVL-дерева в порядке ключей -- **clear()**: рекурсивная post-order деаллокация через `detail::avl_clear_subtree()` -- **Инициализация**: AVL-полей нового узла через `detail::avl_init_node()` с `no_block` - sentinel вместо 0 для корректной работы итератора и AVL-операций - -> **Примечание (Issue #188):** Все AVL-операции в pmap делегированы общим шаблонным -> функциям из `avl_tree_mixin.h`. pmap использует стандартный [AvlUpdateHeightOnly](../../include/pmm/avl_tree_mixin.h#pmm-detail-avlupdateheightonly) -> callback (обновление только высоты). - -## ~~3.4 Доработка `pvector` — метод `erase(index)`~~ УДАЛЕНО (#224) - -> Тип `pvector` удалён, так как полностью заменён [parray](../../include/pmm/parray.h#pmm-parray) (Issue #224). - -## 3.5 STL-совместимый аллокатор `pallocator` ✅ - -**Issue:** #198 -**Файл:** `include/pmm/pallocator.h` -**Тесты:** `tests/test_issue198_pallocator.cpp` (18 тестов) - -### Описание - -`pallocator` — STL-совместимый аллокатор, делегирующий управление памятью -в PersistMemoryManager. Позволяет использовать STL-контейнеры (`std::vector`, и т.д.) -с персистентным адресным пространством. - -### Архитектура - -``` -std::vector> - │ - ▼ -pallocator - ├── allocate(n) → Mgr::allocate(n * sizeof(int)) → void* в ПАП - └── deallocate(p) → Mgr::deallocate(p) → освобождение в ПАП -``` - -- **Stateless**: все состояние хранится в статическом ManagerT -- **Все экземпляры с одним ManagerT равны** (`is_always_equal = true_type`) -- **Rebind**: поддерживается через converting constructor -- **Исключения**: `allocate()` бросает `std::bad_alloc` при неудаче (требование STL) - -### API - -```cpp -using Mgr = pmm::presets::SingleThreadedHeap; -Mgr::create(64 * 1024); - -// Использование со std::vector -std::vector> vec; -vec.push_back(42); -vec.push_back(100); -assert(vec[0] == 42); - -// Прямое использование аллокатора -Mgr::pallocator alloc; -int* p = alloc.allocate(10); // 10 int'ов в ПАП -p[0] = 1; -alloc.deallocate(p, 10); // освобождение - -// Rebinding -Mgr::pallocator alloc_d(alloc); // converting constructor - -// Сравнение (всегда равны) -assert(alloc == alloc_d); - -Mgr::destroy(); -``` - -### Отличия от стандартного std::allocator - -| Свойство | std::allocator | pallocator | -|----------|---------------|------------| -| Хранилище | Системная куча (malloc) | Персистентное адресное пространство (ПАП) | -| Персистентность | Нет | Данные сохраняются при save/load | -| Потокобезопасность | Да (через malloc) | Определяется LockPolicy менеджера | -| Адресная независимость | Нет | Да (через гранульные индексы pmm) | -| Использование | Общее назначение | STL-контейнеры в ПАП | - -## 3.6 Пул объектов `ppool` ✅ - -**Issue:** #199 -**Файл:** `include/pmm/ppool.h` -**Тесты:** `tests/test_issue199_ppool.cpp` (18 тестов) - -### Описание - -`ppool` — пул объектов фиксированного размера в персистентном адресном пространстве (ПАП). -Обеспечивает O(1) выделение и освобождение объектов через встроенный free-list. -Идеально подходит для массового создания узлов деревьев, списков, графов и JSON-узлов. - -### Архитектура - -``` -ppool (в ПАП) -┌─────────────────────┐ -│ _free_head_idx │──→ первый свободный слот -│ _chunk_head_idx │──→ первый чанк -│ _objects_per_chunk │ объектов в чанке (по умолчанию 64) -│ _total_allocated │ текущее число живых объектов -│ _total_capacity │ общее число слотов -└─────────────────────┘ - -Чанк (в ПАП): -┌──────────────────┬────────┬────────┬─────┬────────┐ -│ next_chunk_idx │ slot_0 │ slot_1 │ ... │ slot_N │ -│ (1 гранула) │ (G гранул каждый) │ -└──────────────────┴────────┴────────┴─────┴────────┘ - -Свободный слот хранит гранульный индекс следующего свободного слота -(встроенный free-list). -``` - -- **Чанки**: крупные блоки, выделяемые из ПАП через менеджер -- **Слоты**: гранульно-выровнены для корректной адресации через гранульные индексы -- **Free-list**: встроенный в неиспользуемые слоты (zero overhead) -- **POD-структура**: `std::is_trivially_copyable_v == true` -- **Требование к T**: тип элемента должен быть trivially copyable - -### API - -```cpp -using Mgr = pmm::presets::SingleThreadedHeap; -Mgr::create(256 * 1024); - -// Создание пула -Mgr::pptr> pool = Mgr::create_typed>(); - -// Настройка размера чанка (до первой аллокации) -pool->set_objects_per_chunk(128); - -// O(1) аллокация -int* a = pool->allocate(); // nullptr при неудаче -int* b = pool->allocate(); -*a = 42; -*b = 99; - -// O(1) деаллокация (возврат в free-list) -pool->deallocate(a); - -// Статистика -pool->allocated_count(); // число живых объектов -pool->total_capacity(); // общее число слотов -pool->free_count(); // свободные слоты -pool->empty(); // true если нет живых объектов - -// Освобождение всех чанков -pool->free_all(); -Mgr::destroy_typed(pool); - -Mgr::destroy(); -``` - -### Отличия от pallocator и стандартных аллокаторов - -| Свойство | pallocator | ppool | -|----------|-----------|-------| -| Гранулярность | Произвольный размер | Фиксированный размер T | -| Скорость | O(log n) (поиск best-fit) | O(1) (free-list) | -| Использование | STL-контейнеры | Массовое создание однотипных объектов | -| Overhead | Заголовок блока на каждый объект | Один заголовок блока на чанк | -| Фрагментация | Возможна | Минимальная (фиксированные слоты) | - -## 3.7 Корневой объект в ManagerHeader ✅ - -**Issue:** #200 -**Файл:** `include/pmm/persist_memory_manager.h`, `include/pmm/types.h` -**Тесты:** `tests/test_issue200_root_object.cpp` (13 тестов) - -### Описание - -Единственный именованный указатель `root_offset` в [ManagerHeader](../../include/pmm/types.h#pmm-detail-managerheader), позволяющий хранить -корневой объект (например, `pmap>`) и находить его после загрузки -образа. Заменяет паттерн `pam_pmm.h` из BinDiffSynchronizer. - -### Архитектура - -``` -ManagerHeader (в ПАП) -┌─────────────────────┐ -│ magic │ -│ total_size │ -│ ... (counters) │ -│ free_tree_root │ -│ crc32 │ -│ root_offset ───────│──→ pptr корневого объекта (или no_block) -└─────────────────────┘ -``` - -- **root_offset** (index_type): гранульный индекс корневого объекта -- При инициализации (`create()`) устанавливается в `no_block` (нет корня) -- Сохраняется и восстанавливается при `save_manager()`/`load_manager_from_file()` -- Заменяет поле `_reserved[4]` — размер `ManagerHeader` остаётся 64 байта - -### API - -```cpp -using Mgr = pmm::presets::SingleThreadedHeap; -Mgr::create(64 * 1024); - -// Создать корневой объект (например, реестр) -using Registry = Mgr::pmap; -auto reg = Mgr::create_typed(); -reg->insert(1, 100); - -// Установить как корень -Mgr::set_root(reg); - -// Получить корень (например, после load) -auto root = Mgr::get_root(); -auto found = root->find(1); -// found->value == 100 - -// Сбросить корень -Mgr::set_root(Mgr::pptr()); - -Mgr::destroy(); -``` - -### Особенности - -- Один корневой указатель на менеджер (мультитон — у каждого экземпляра свой) -- Типобезопасность: `set_root()` / `get_root()` — тип T должен совпадать -- Потокобезопасность: `set_root` под `unique_lock`, `get_root` под `shared_lock` -- Персистентность: корень сохраняется в образе через `root_offset` в [ManagerHeader](../../include/pmm/types.h#pmm-detail-managerheader) -- Работает со всеми address traits: `SmallAddressTraits`, `DefaultAddressTraits`, `LargeAddressTraits` diff --git a/docs/archive/phase4_api.md b/docs/archive/phase4_api.md deleted file mode 100644 index 6952168c..00000000 --- a/docs/archive/phase4_api.md +++ /dev/null @@ -1,318 +0,0 @@ -# Фаза 4: API и удобство использования - -Документация по реализации задач Фазы 4 плана развития PersistMemoryManager. - ---- - -## 4.1 Коды ошибок вместо bool ✅ (#201) - -**Проблема:** Методы `create()`, `load()`, `allocate()` возвращали только `bool` / `nullptr`. -При неудаче причина ошибки была неизвестна — невозможно отличить нехватку памяти от -ошибки CRC, переполнения или неверного magic-числа. - -**Решение:** - -### `enum class PmmError` (types.h) - -Перечисление кодов ошибок: - -| Код | Значение | Описание | -|-----|----------|----------| -| `Ok` | 0 | Операция успешна | -| `NotInitialized` | 1 | Менеджер не инициализирован | -| `InvalidSize` | 2 | Некорректный размер (ноль, слишком мал и т.д.) | -| `Overflow` | 3 | Арифметическое переполнение при вычислении размеров | -| `OutOfMemory` | 4 | Аллокация не удалась — недостаточно свободной памяти | -| `ExpandFailed` | 5 | Расширение бэкенда (`expand()`) не удалось | -| `InvalidMagic` | 6 | Несовпадение magic-числа при `load()` | -| `CrcMismatch` | 7 | Несовпадение CRC32 при загрузке (повреждённый образ) | -| `SizeMismatch` | 8 | Сохранённый `total_size` не совпадает с бэкендом | -| `GranuleMismatch` | 9 | Сохранённый `granule_size` не совпадает с `address_traits` | -| `BackendError` | 10 | Бэкенд вернул `nullptr` или недопустимое состояние | -| `InvalidPointer` | 11 | Указатель `nullptr` или вне границ | -| `BlockLocked` | 12 | Блок перманентно заблокирован (нельзя освободить) | - -### API (persist_memory_manager.h) - -```cpp -/// Последний код ошибки (один на специализацию менеджера). -static PmmError last_error() noexcept; - -/// Сбросить код ошибки в Ok. -static void clear_error() noexcept; - -/// Установить код ошибки (для утилитных функций, например io.h). -static void set_last_error(PmmError err) noexcept; -``` - -### Какие методы устанавливают код ошибки - -- **`create(initial_size)`** — `InvalidSize`, `Overflow`, `ExpandFailed`, `BackendError`, `Ok` -- **`create()`** — `BackendError`, `InvalidSize`, `Ok` -- **`load()`** — `BackendError`, `InvalidSize`, `InvalidMagic`, `SizeMismatch`, `GranuleMismatch`, `Ok` -- **`allocate(user_size)`** — `NotInitialized`, `InvalidSize`, `Overflow`, `OutOfMemory`, `Ok` -- **`load_manager_from_file()` (io.h)** — `CrcMismatch` (+ коды из `load()`) - -### Обратная совместимость - -Все существующие методы по-прежнему возвращают `bool` / `nullptr`. -`last_error()` — дополнительный механизм диагностики, не заменяющий возвращаемые значения. -Код, не использующий `last_error()`, продолжает работать без изменений. - -### Тесты - -18 тестов в `tests/test_issue201_error_codes.cpp`: -- Проверка всех значений enum -- Ошибки create: InvalidSize, Overflow, BackendError -- Ошибки allocate: NotInitialized, InvalidSize, OutOfMemory -- Ошибки load: InvalidMagic, SizeMismatch, GranuleMismatch -- CrcMismatch через corrupted file -- clear_error / set_last_error -- SmallAddressTraits (uint16_t) и LargeAddressTraits (uint64_t) - ---- - -## 4.2 Хуки логирования ✅ (#202) - -**Проблема:** Нет механизма отслеживания событий менеджера (аллокации, ошибки, расширения). -При отладке и мониторинге невозможно узнать о внутренних событиях без модификации библиотеки. - -**Решение:** - -### Политики логирования (logging_policy.h) - -Шаблонный параметр `LoggingPolicyT` в конфигурации менеджера. Политика определяет набор -статических `noexcept` методов-хуков, вызываемых при ключевых событиях. - -Встроенные политики: - -| Политика | Описание | -|----------|----------| -| `logging::NoLogging` | Заглушка — все методы пустые inline (нулевые накладные расходы, по умолчанию) | -| `logging::StderrLogging` | Логирование событий и ошибок в stderr | - -### Хуки - -```cpp -/// Не удалось выделить память. -static void on_allocation_failure(std::size_t user_size, PmmError err) noexcept; - -/// Бэкенд расширен. -static void on_expand(std::size_t old_size, std::size_t new_size) noexcept; - -/// Обнаружено повреждение данных (InvalidMagic, CrcMismatch, SizeMismatch, GranuleMismatch). -static void on_corruption_detected(PmmError err) noexcept; - -/// Менеджер успешно создан. -static void on_create(std::size_t initial_size) noexcept; - -/// Менеджер сброшен. -static void on_destroy() noexcept; - -/// Менеджер загружен из образа. -static void on_load() noexcept; -``` - -### Где вызываются хуки - -- **`create(initial_size)` / `create()`** — `on_create()` при успехе -- **`destroy()`** — `on_destroy()` -- **`load()`** — `on_load()` при успехе; `on_corruption_detected()` при InvalidMagic, SizeMismatch, GranuleMismatch -- **`allocate(user_size)`** — `on_allocation_failure()` при NotInitialized, InvalidSize, Overflow, OutOfMemory -- **`do_expand()`** — `on_expand()` при успешном расширении -- **`load_manager_from_file()` (io.h)** — `on_corruption_detected()` при CrcMismatch - -### Обратная совместимость - -Политика `logging_policy` автоматически определяется из конфигурации через SFINAE. -Если конфигурация не определяет `logging_policy`, используется `logging::NoLogging`. -Пользовательские конфигурации без `logging_policy` продолжают работать без изменений. - -### Пример пользовательской политики - -```cpp -struct MyLogging { - static void on_allocation_failure(std::size_t size, pmm::PmmError err) noexcept { - spdlog::warn("pmm: alloc({}) failed: {}", size, static_cast(err)); - } - static void on_expand(std::size_t old_sz, std::size_t new_sz) noexcept { - spdlog::info("pmm: expanded {} -> {}", old_sz, new_sz); - } - static void on_corruption_detected(pmm::PmmError err) noexcept { - spdlog::error("pmm: corruption: {}", static_cast(err)); - } - static void on_create(std::size_t size) noexcept {} - static void on_destroy() noexcept {} - static void on_load() noexcept {} -}; - -using MyConfig = pmm::BasicConfig< - pmm::DefaultAddressTraits, - pmm::config::NoLock, - 5, 4, 64, - MyLogging ->; -using MyMgr = pmm::PersistMemoryManager; -``` - -### Тесты - -14 тестов в `tests/test_issue202_logging_hooks.cpp`: -- NoLogging компилируется с предустановленными конфигурациями -- on_create / on_destroy / on_load хуки вызываются при соответствующих событиях -- on_allocation_failure вызывается при OOM, InvalidSize, NotInitialized -- on_expand вызывается при расширении бэкенда -- on_corruption_detected вызывается при InvalidMagic, SizeMismatch, GranuleMismatch, CrcMismatch -- Работа с SmallAddressTraits (uint16_t) и LargeAddressTraits (uint64_t) - ---- - -## 4.3 Нативное перераспределение `reallocate_typed()` (Issue #210) - -### Проблема - -Отсутствие нативного перераспределения памяти. Пользователь должен вручную: выделить новый блок, скопировать данные, освободить старый. - -### Решение - -Добавлен метод `reallocate_typed(pptr p, old_count, new_count)` в [PersistMemoryManager](../../include/pmm/persist_memory_manager.h#pmm-persistmemorymanager): - -```cpp -template -static pptr reallocate_typed( pptr p, std::size_t old_count, std::size_t new_count ) noexcept; -``` - -**Стратегия:** -1. **Одинаковый размер** — возврат того же pptr. -2. **Уменьшение (shrink)** — обновление weight на месте; если остаток достаточен, он выделяется в новый свободный блок с коалесценцией с соседями. -3. **Расширение (grow)** — если следующий блок свободен и его размер достаточен, расширение на месте (in-place absorb); иначе — fallback. -4. **Fallback** — аллокация нового блока + `memmove` + деаллокация старого. Выполняется под одним мьютексом для совместимости с [NoLock](../../include/pmm/config.h#pmm-config-nolock). - -**Ограничения:** -- `T` должен быть `trivially_copyable` (проверка через `static_assert`). -- При неудаче старый блок НЕ освобождается — вызывающий код сохраняет владение. -- Для [AddressTraits](../../include/pmm/address_traits.h#pmm-addresstraits) с `sizeof(Block) % granule_size != 0` (SmallAddressTraits) in-place пути пропускаются, так как `resolve()` возвращает адрес, перекрывающий заголовок блока. - -### Использование - -```cpp -using Mgr = pmm::PersistMemoryManager; - -Mgr::create(64 * 1024); - -// Выделить массив из 10 int -auto p = Mgr::allocate_typed(10); -for (std::size_t i = 0; i < 10; ++i) - Mgr::resolve(p)[i] = static_cast(i); - -// Расширить до 20 int — данные первых 10 элементов сохранены -auto p2 = Mgr::reallocate_typed(p, 10, 20); -// p2 может совпадать с p (in-place) или быть новым указателем (fallback) - -// Уменьшить до 5 int -auto p3 = Mgr::reallocate_typed(p2, 20, 5); - -Mgr::deallocate_typed(p3); -Mgr::destroy(); -``` - -### Коды ошибок - -- `PmmError::Ok` — успешное перераспределение. -- `PmmError::InvalidSize` — `new_count == 0`. -- `PmmError::Overflow` — арифметическое переполнение `sizeof(T) * new_count`. -- `PmmError::NotInitialized` — менеджер не инициализирован. -- `PmmError::OutOfMemory` — недостаточно свободной памяти (fallback path). - -### Тесты - -15 тестов в `tests/test_issue210_reallocate_typed.cpp`: -- Расширение с сохранением данных (grow_preserves_data) -- Уменьшение с сохранением данных (shrink_preserves_data) -- In-place расширение за счёт соседнего блока (inplace_expansion) -- Fallback-путь при отсутствии свободного соседа (fallback_allocation) -- Нулевой указатель → аллокация (null_pointer) -- Нулевой new_count → ошибка (zero_new_count) -- Одинаковый размер → тот же pptr (same_size) -- Уменьшение с созданием свободного блока (shrink_creates_free_block) -- Работа с SmallAddressTraits (small_address_traits) -- Работа с LargeAddressTraits (large_address_traits) -- Последовательные перераспределения (sequential_reallocations) -- Защита от переполнения (overflow_protection) -- Ошибка при неинициализированном менеджере (not_initialized) -- Сохранение старого блока при неудаче (old_block_preserved_on_failure) -- Уменьшение и расширение (shrink_then_grow) - ---- - -## 4.4 Конверсия pptr ↔ байтовые смещения ✅ (#211) - -### Проблема - -BinDiffSynchronizer реализует `pam_adapter.h` для конверсии между гранульными индексами -и байтовыми смещениями. Отсутствие такого API в pmm вынуждает пользователей вручную -вычислять `offset * granule_size` и обратно. - -### Решение - -Добавлены два метода для прямой и обратной конверсии между pptr и байтовыми смещениями: - -#### `pptr::byte_offset()` (pptr.h) - -```cpp -/// Получить байтовое смещение из гранульного индекса. -/// Возвращает offset() * granule_size. Для null pptr возвращает 0. -constexpr std::size_t byte_offset() const noexcept; -``` - -#### `PersistMemoryManager::pptr_from_byte_offset()` (persist_memory_manager.h) - -```cpp -/// Создать pptr из байтового смещения в управляемой области. -/// Смещение должно быть кратно granule_size. -template -static pptr pptr_from_byte_offset( std::size_t byte_off ) noexcept; -``` - -### Использование - -```cpp -using Mgr = pmm::PersistMemoryManager; - -Mgr::create(64 * 1024); - -auto p = Mgr::allocate_typed(); -*p = 42; - -// Получить байтовое смещение для передачи во внешнюю систему -std::size_t boff = p.byte_offset(); - -// Восстановить pptr из байтового смещения (полученного от внешней системы) -auto p2 = Mgr::pptr_from_byte_offset(boff); -assert(*p2 == 42); // данные доступны через восстановленный указатель - -Mgr::deallocate_typed(p); -Mgr::destroy(); -``` - -### Коды ошибок (pptr_from_byte_offset) - -- Байтовое смещение 0 → null pptr (без ошибки). -- Некратное `granule_size` смещение → null pptr + `PmmError::InvalidPointer`. -- Смещение, превышающее `index_type::max()` → null pptr + `PmmError::Overflow`. - -### Тесты - -12 тестов в `tests/test_issue211_byte_offset.cpp`: -- byte_offset() возвращает offset * granule_size (byte_offset_basic) -- Null pptr byte_offset() возвращает 0 (byte_offset_null) -- pptr_from_byte_offset создаёт корректный pptr (from_byte_offset_basic) -- pptr_from_byte_offset(0) возвращает null (from_byte_offset_zero) -- Некратное смещение возвращает null с ошибкой (from_byte_offset_unaligned) -- Round-trip сохраняет идентичность указателя (round_trip) -- Round-trip с данными: доступ через оба пути (round_trip_data) -- Работа с SmallAddressTraits (small_address_traits) -- Работа с LargeAddressTraits (large_address_traits) -- Защита от переполнения (overflow_protection) -- Коды ошибок корректны (error_codes) -- Множественные аллокации round-trip (multiple_allocations) diff --git a/docs/archive/phase5_testing.md b/docs/archive/phase5_testing.md deleted file mode 100644 index 8bbc608f..00000000 --- a/docs/archive/phase5_testing.md +++ /dev/null @@ -1,269 +0,0 @@ -# Фаза 5: Тестирование и качество - -Документация по реализации задач Фазы 5 плана развития PersistMemoryManager. - ---- - -## 5.1 Миграция на Catch2 ✅ (#212) - -### Проблема - -Проект использовал собственные макросы `PMM_TEST(expr)` / `PMM_RUN(name, fn)`, определённые -локально в каждом из 75 тестовых файлов. Это приводило к: -- Дублированию кода макросов (каждый файл содержал ~20 строк определений) -- Отсутствию стандартных средств: `SECTION()` для изоляции, `INFO()` для контекста, `CHECK()` для soft-assertions -- Минимальной диагностике при неудаче (только `FAIL [file:line] expr`) -- Необходимости вручную писать `main()` и `all_passed` логику в каждом файле - -### Решение - -Миграция всех тестов на Catch2 v3 — зрелый, header-only фреймворк тестирования для C++. - -#### Зависимость (CMakeLists.txt) - -```cmake -include(FetchContent) -FetchContent_Declare( - Catch2 - GIT_REPOSITORY https://github.com/catchorg/Catch2.git - GIT_TAG v3.7.1 -) -FetchContent_MakeAvailable(Catch2) -``` - -#### Схема миграции - -Три паттерна тестов были мигрированы: - -**Паттерн A** (65 файлов): `PMM_TEST(expr)` + `PMM_RUN(name, fn)` + `static bool test_xxx()`: -```cpp -// Было: -#define PMM_TEST(expr) do { if (!(expr)) { std::cerr << "FAIL..."; return false; } } while(false) -static bool test_basic() { PMM_TEST(x == 42); return true; } -int main() { bool all_passed = true; PMM_RUN("basic", test_basic); return all_passed ? 0 : 1; } - -// Стало: -#include -TEST_CASE("basic", "[test_suite]") { REQUIRE(x == 42); } -``` - -**Паттерн B** (2 файла): `PMM_TEST(cond, msg)` + `static void test_xxx()`: -```cpp -// Было: -#define PMM_TEST(cond, msg) do { if (!(cond)) { std::cerr << msg; std::exit(1); } } while(false) - -// Стало: -INFO(msg); REQUIRE(cond); -``` - -**Паттерн C** (8 файлов): `assert(expr)` в single-header тестах: -```cpp -// Было: -int main() { assert(created); assert(STH::is_initialized()); return 0; } - -// Стало: -TEST_CASE("test_name", "[test_name]") { REQUIRE( created ); REQUIRE( STH::is_initialized() ); } -``` - -#### Обновление CMake - -```cmake -# tests/CMakeLists.txt — все тесты линкуются с Catch2WithMain: -function(pmm_add_test name source) - add_executable(${name} ${source}) - target_link_libraries(${name} PRIVATE pmm Catch2::Catch2WithMain) - add_test(NAME ${name} COMMAND ${name}) -endfunction() -``` - -`Catch2::Catch2WithMain` предоставляет `main()` автоматически — не нужно писать вручную. - -### Тесты - -67 тестовых исполняемых файлов, все проходят (100% pass rate). - -Миграция включает: -- 73 файла мигрированы (65 паттерн A + 2 паттерн B + 8 паттерн C) -- 2 файла пропущены (demo-only: `test_demo_headless.cpp`, `test_background_validator.cpp`) -- Удалено ~4200 строк дублирующегося кода макросов и ручной `main()` логики -- Скрипт миграции сохранён как `scripts/migrate_to_catch2.py` для справки - -### Преимущества - -- **Стандартизация:** Catch2 — де-факто стандарт тестирования в C++ проектах -- **Диагностика:** При неудаче Catch2 показывает значения обеих сторон выражения -- **Изоляция:** `SECTION()` позволяет группировать связанные проверки -- **Масштабируемость:** Новые тесты пишутся без boilerplate (нет `main()`, нет макросов) -- **CI-интеграция:** Catch2 поддерживает JUnit XML, TAP и другие форматы отчётов - ---- - -## 5.2 Расширение покрытия тестами ✅ (#213) - -### Проблема - -Существующие тесты покрывали функциональные сценарии, но не проверяли: -- Граничные случаи арифметического переполнения при вычислении размеров -- Конкурентные операции аллокации/деаллокации при высокой нагрузке -- Случайные последовательности операций (fuzz-тестирование) - -### Решение - -Добавлены три новых тестовых файла с 31 тест-кейсом, а также libFuzzer-совместимый harness. - -#### 5.2.1 Тесты переполнения (`test_issue213_overflow.cpp`) - -19 тест-кейсов, проверяющих: -- `bytes_to_granules_t` возвращает 0 при переполнении `size_t` -- `bytes_to_granules_t` возвращает 0 при превышении `index_type::max` -- `bytes_to_idx_t` возвращает `no_block` при переполнении -- `allocate()` / `allocate_typed()` / `reallocate_typed()` для `size_t::max` -- `allocate(0)` возвращает `nullptr` с `PmmError::InvalidSize` -- `allocate_typed(0)` возвращает нулевой [pptr](../../include/pmm/pptr.h#pmm-pptr) -- Статическое хранилище: отказ при выходе за пределы буфера -- 16-bit индекс (`SmallAddressTraits`): переполнение при малом адресном пространстве -- 64-bit индекс (`LargeAddressTraits`): базовая аллокация и устойчивость к переполнению -- Исчерпание памяти и восстановление (exhaust → free → re-allocate) -- Циклы аллокации/деаллокации с проверкой целостности -- Граничные размеры: ровно 1 гранула, на границе гранулы, между гранулами - -Все варианты [AddressTraits](../../include/pmm/address_traits.h#pmm-addresstraits) протестированы: `SmallAddressTraits` (16-bit), -`DefaultAddressTraits` (32-bit), `LargeAddressTraits` (64-bit). - -#### 5.2.2 Конкурентные тесты (`test_issue213_concurrent.cpp`) - -6 тест-кейсов, проверяющих: -- Конкурентная аллокация с варьирующимися размерами (8 потоков, 200 операций каждый) -- LIFO-деаллокация (4 потока × 100 блоков, освобождение в обратном порядке) -- Случайный порядок деаллокации (Fisher-Yates shuffle в каждом потоке) -- Высокая конкуренция (16 потоков, малый heap 4 МБ) -- Проверка целостности данных под конкуренцией (8 потоков, write/read/verify) -- Конкурентный `reallocate_typed` (4 потока, рост массивов с проверкой sentinel-значений) - -Все тесты используют `MultiThreadedHeap` с [SharedMutexLock](../../include/pmm/config.h#pmm-config-sharedmutexlock). - -#### 5.2.3 Fuzz-тестирование (`test_issue213_fuzz.cpp` + `fuzz_allocator.cpp`) - -**Детерминистические fuzz-тесты** (6 тест-кейсов через Catch2): -- Случайные alloc/dealloc на статическом хранилище (5000 итераций, проверка паттернов) -- Случайные alloc/dealloc на heap-хранилище (5000 итераций, три класса размеров) -- Смешанные alloc/dealloc/reallocate (3000 итераций, проверка сохранности данных) -- 16-bit индекс стресс (2000 итераций на 8 КБ буфере) -- Фрагментационный стресс (заполнение → освобождение через один → средние блоки) -- Sweep по 6 различным seed-значениям (1000 итераций каждый) - -Все fuzz-тесты используют LCG PRNG для детерминистической воспроизводимости. -Каждый тест записывает уникальный паттерн в каждый блок и проверяет его перед деаллокацией. - -**libFuzzer harness** (`tests/fuzz_allocator.cpp`): -- Покрытие-управляемый фаззинг для Clang с `-fsanitize=fuzzer,address` -- Интерпретирует входные байты как последовательность команд (alloc/dealloc/reallocate) -- Использует статическое хранилище 64 КБ для ограниченного состояния -- Инструкции по сборке и запуску в заголовке файла - -### Тесты - -70 тестовых исполняемых файлов (67 + 3 новых), все проходят (100% pass rate). -31 новый тест-кейс суммарно в трёх файлах. - ---- - -## 5.3 Бенчмарки производительности ✅ (#214) - -### Проблема - -Проект не имел формальных бенчмарков производительности. Существовал только -`examples/benchmark.cpp` с ручным замером времени и `test_performance.cpp` с -проверкой «≤ 100 мс». Не было инструмента для систематического измерения -производительности различных операций и сравнения с базовым аллокатором (malloc). - -### Решение - -Интеграция Google Benchmark v1.9.1 через CMake FetchContent с опциональной сборкой -(`PMM_BUILD_BENCHMARKS=ON`). Один бенчмарк-файл `benchmarks/bench_allocator.cpp` -покрывает все ключевые операции библиотеки. - -#### Зависимость (CMakeLists.txt) - -```cmake -option(PMM_BUILD_BENCHMARKS "Build Google Benchmark performance benchmarks" OFF) -if(PMM_BUILD_BENCHMARKS) - FetchContent_Declare( - benchmark - GIT_REPOSITORY https://github.com/google/benchmark.git - GIT_TAG v1.9.1 - ) - set(BENCHMARK_ENABLE_TESTING OFF CACHE BOOL "" FORCE) - set(BENCHMARK_ENABLE_INSTALL OFF CACHE BOOL "" FORCE) - FetchContent_MakeAvailable(benchmark) - add_subdirectory(benchmarks) -endif() -``` - -#### Бенчмарки - -**Аллокатор** (5 бенчмарков): -- `BM_Allocate` — аллокация одного блока (16, 64, 256, 1024, 4096 байт) -- `BM_Deallocate` — деаллокация одного блока (64, 256, 1024 байт) -- `BM_AllocDeallocMixed` — цикл alloc/dealloc со смешанными размерами (32–256 байт) -- `BM_ReallocateTyped` — нативное перераспределение (64↔128 байт) -- `BM_AllocateBatch` — пакетная аллокация (1K, 10K, 100K блоков по 64 байт) - -**Сравнение с malloc** (2 бенчмарка): -- `BM_MallocFree` — malloc/free одного блока (16–4096 байт) -- `BM_MallocBatch` — пакетная аллокация через malloc (1K, 10K, 100K блоков) - -**pmap** (3 бенчмарка): -- `BM_PmapInsert` — вставка N элементов (100, 1K, 10K) -- `BM_PmapFind` — поиск по ключу в дереве из N элементов -- `BM_PmapErase` — удаление N элементов (100, 1K) - - -**parray** (2 бенчмарка): -- `BM_ParrayPushBack` — добавление N элементов (100, 1K, 10K) -- `BM_ParrayRandomAccess` — O(1) произвольный доступ - -**ppool** (2 бенчмарка): -- `BM_PpoolAllocate` — O(1) аллокация/деаллокация одного объекта -- `BM_PpoolBatch` — пакетная аллокация из пула (100, 1K, 10K объектов) - -**pstring** (2 бенчмарка): -- `BM_PstringAssign` — присвоение строки (16, 256, 4096 символов) -- `BM_PstringAppend` — amortized O(1) append - -**pstringview** (1 бенчмарк): -- `BM_PstringviewInternExisting` — поиск уже интернированной строки (AVL lookup) - -**Многопоточность** (1 бенчмарк): -- `BM_AllocateMT` — alloc/dealloc с SharedMutexLock (1, 2, 4, 8 потоков) - -#### Сборка и запуск - -```bash -cmake -B build -DPMM_BUILD_BENCHMARKS=ON -DCMAKE_BUILD_TYPE=Release -cmake --build build --target pmm_benchmarks -./build/benchmarks/pmm_benchmarks -``` - -#### Результаты (GCC 13, Linux x86-64, Release) - -| Операция | Время (нс) | Примечание | -|----------|-----------|------------| -| `allocate(64)` | ~20 | Одиночная аллокация | -| `deallocate(64)` | ~680 | С PauseTiming на подготовку | -| `alloc+dealloc mixed` | ~22 | Смешанные размеры 32–256 | -| `reallocate_typed` | ~12 | Чередование 64↔128 | -| `malloc+free(64)` | ~7 | Базовый аллокатор | -| `pmap find(10K)` | ~49 | O(log n) AVL-поиск | -| `pmap insert(10K)` | ~102 нс/эл | С аллокацией узлов | -| `parray [](10K)` | ~3 | O(1) произвольный доступ | -| `ppool allocate` | ~1.4 | O(1) из free-list | -| `pstring append` | ~1.1 | Amortized O(1) | -| `pstringview intern(10K)` | ~107 | AVL lookup существующей строки | - -### Преимущества - -- **Воспроизводимость:** Google Benchmark обеспечивает статистическую надёжность -- **Сравнимость:** Прямое сравнение pmm с malloc для оценки накладных расходов -- **Полнота:** Покрыты все ключевые типы и операции библиотеки -- **Опциональность:** Сборка бенчмарков по флагу `PMM_BUILD_BENCHMARKS=ON` diff --git a/docs/archive/phase6_documentation.md b/docs/archive/phase6_documentation.md deleted file mode 100644 index 1cb9cdf0..00000000 --- a/docs/archive/phase6_documentation.md +++ /dev/null @@ -1,228 +0,0 @@ -# Фаза 6: Документация - -Документация по реализации задач Фазы 6 плана развития PersistMemoryManager. - ---- - -## 6.1 Документация потокобезопасности ✅ (#215) - -### Проблема - -Проект использует политики блокировок ([SharedMutexLock](../../include/pmm/config.h#pmm-config-sharedmutexlock), [NoLock](../../include/pmm/config.h#pmm-config-nolock)) и атомарные операции -для обеспечения потокобезопасности, однако модель синхронизации не была формально -задокументирована. Разработчикам приходилось изучать исходный код, чтобы понять: - -- Какие операции защищены мьютексом, а какие нет -- Почему `resolve()` не захватывает мьютекс и каковы последствия -- Как безопасно использовать персистентные контейнеры из нескольких потоков -- Какие паттерны использования корректны, а какие приводят к гонкам данных - -Существовала лишь краткая справка в `docs/architecture.md` (4 строки) и пометки -в `docs/api_reference.md`. - -### Решение - -Создан документ `docs/thread_safety.md` — полное руководство по потокобезопасности, -включающее: - -#### Модель потокобезопасности - -- Описание стратегии readers–writer lock через шаблонные политики -- Сравнительная таблица политик [SharedMutexLock](../../include/pmm/config.h#pmm-config-sharedmutexlock) и [NoLock](../../include/pmm/config.h#pmm-config-nolock) -- Готовые конфигурации и пресеты с указанием политики блокировок - -#### Контракты операций - -Три категории операций с указанием типа блокировки: - -- **unique_lock** (эксклюзивные): `create`, `destroy`, `allocate`, `deallocate`, - `set_root`, `lock_block_permanent` и все типизированные варианты -- **shared_lock** (конкурентное чтение): `get_root`, `is_permanently_locked`, - все методы статистики, `for_each_block`, `for_each_free_block` -- **Без блокировки**: `is_initialized` (atomic), `resolve` (lock-free fast path), - `is_valid_ptr`, `pptr_from_byte_offset` - -#### resolve() и pptr - -- Объяснение, почему `resolve()` не захватывает мьютекс (производительность hot path) -- Гарантии: атомарная проверка инициализации, стабильность базового адреса -- Ограничения: данные не защищены, нельзя вызывать одновременно с `destroy()` - -#### Атомарный флаг _initialized - -- Механизм lock-free fast path в статистических методах -- Двойная проверка: atomic load → shared_lock → relaxed load - -#### Правила конкурентного доступа - -- Безопасные паттерны с примерами кода: - - Конкурентная аллокация/деаллокация - - Конкурентное чтение данных - - Чтение статистики во время аллокаций -- Опасные паттерны (антипаттерны) с объяснением и решениями: - - Конкурентная запись в одну ячейку (data race) - - destroy() во время работы других потоков - - allocate/deallocate из callback for_each_block (дедлок) - -#### Персистентные контейнеры - -- Правила использования [pstring](../../include/pmm/pstring.h#pmm-pstring), [parray](../../include/pmm/parray.h#pmm-parray), [pmap](../../include/pmm/pmap.h#pmm-pmap), `ppool` в многопоточной среде -- Разные экземпляры безопасны, один экземпляр требует внешней синхронизации - -#### Рекомендации - -- Выбор политики в зависимости от сценария -- Советы по производительности (группировка аллокаций, hot path) -- Полный пример многопоточного сценария - -#### Покрытие тестами - -- Ссылки на 14 конкурентных тест-кейсов в 3 файлах -- Многопоточный бенчмарк (1–8 потоков) - ---- - -## 6.2 Документация восстановления после сбоев ✅ (#216) - -### Проблема - -Библиотека реализует многофазное восстановление при загрузке (`load()`), -атомарное сохранение через write-then-rename, CRC32 верификацию и машину -состояний блоков. Однако эти механизмы не были документированы в виде -пользовательского руководства. Разработчикам приходилось изучать -`docs/atomic_writes.md` (техническая спецификация) и исходный код, чтобы понять: - -- Какие гарантии предоставляет pmm при аварийном завершении -- Что происходит при сбое во время аллокации, деаллокации или сохранения -- Как правильно организовать персистентность в приложении -- Какие ограничения существуют (что НЕ восстанавливается) - -### Решение - -Создан документ `docs/recovery.md` — пользовательское руководство по -восстановлению после сбоев, включающее: - -#### Уровни гарантий - -- Таблица гарантий: структурная целостность, данные в блоках, атомарность - операций, файловое сохранение, CRC32 - -#### Сценарии сбоев - -Пять детальных сценариев с таблицами «момент прерывания → состояние после load()»: - -1. **Сбой во время аллокации** — прерванная аллокация трактуется как не выполненная -2. **Сбой во время деаллокации** — блок корректно освобождён -3. **Сбой во время reallocate_typed** — старые данные сохранены -4. **Сбой во время сохранения в файл** — оригинальный файл не повреждён -5. **Сбой при работе с MMapStorage** — структурная целостность восстановлена - -#### Механизм восстановления при load() - -- Пятифазное восстановление: валидация заголовка → сброс runtime-полей → - repair_linked_list → recompute_counters → rebuild_free_tree -- Псевдокод каждой фазы с пояснениями - -#### Блоки в переходных состояниях - -- Таблица переходных состояний и их восстановления -- Диаграмма корректных состояний (FreeBlock ↔ AllocatedBlock) - -#### CRC32 - -- Алгоритм вычисления и проверки (ISO 3309) -- Обратная совместимость с образами без CRC -- Ограничения (обнаружение, но не коррекция) - -#### Атомарное сохранение - -- Алгоритм write-then-rename по шагам -- Гарантии на POSIX и Windows - -#### Персистентные контейнеры - -- Таблица последствий сбоя для каждого контейнера -- Важное примечание: пользовательские AVL-деревья не восстанавливаются -- Рекомендации по надёжности - -#### Ограничения - -- Что load() НЕ может восстановить (5 пунктов) -- Разъяснение: pmm не предоставляет транзакции - -#### Типичные сценарии использования - -- Периодическое сохранение (рекомендуемый паттерн) — с примером кода -- Восстановление с проверкой ошибок (PmmError) — с примером кода -- MMapStorage с явной синхронизацией — с примером кода - ---- - -## 6.3 Устранение дублирования кода (метапрограммирование) ✅ (#188) - -### Проблема - -В кодовой базе pmm обнаружено значительное дублирование: - -- AVL-код (ротации, ребалансировка, обход, итераторы) дублировался в `pmap.h`, - `pstringview.h` и `free_block_tree.h` (~250 строк) -- Конверсии гранульный_индекс↔указатель дублировались в `parray.h`, `pstring.h`, - `ppool.h`, `pstringview.h` -- CRC32-вычисления дублировались 4 раза в `compute_crc32`/`compute_image_crc32` -- Конфигурации `SmallEmbeddedStaticConfig` и `EmbeddedStaticConfig` содержали - идентичный код с единственным различием в типе address traits - -### Решение - -Применено шаблонное метапрограммирование для устранения дублирования: - -#### Расширенный `avl_tree_mixin.h` - -Все AVL-операции вынесены в общие шаблонные функции в `pmm::detail`: - -- `avl_rotate_right/left(y, root_idx, update_node)` — ротации с настраиваемым - callback для обновления атрибутов узла -- `avl_rebalance_up(p, root_idx, update_node)` — ребалансировка вверх по дереву -- `avl_insert(new_node, root_idx, go_left, resolve, update_node)` — вставка -- `avl_remove(target, root_idx, update_node)` — удаление с in-order successor -- `avl_find(root_idx, compare, resolve)` — поиск с пользовательским компаратором -- `avl_min_node/max_node(p)` — поиск минимума/максимума -- `avl_inorder_successor(cur)` — переход к следующему узлу -- `avl_init_node(p)` — инициализация узла (sentinel вместо 0) -- `avl_subtree_count(p)` — подсчёт узлов в поддереве -- `avl_clear_subtree(p, dealloc)` — рекурсивная деаллокация - -Ключевая идея — параметр `NodeUpdateFn`: callback, вызываемый после каждой ротации. -Это позволяет [pmap](../../include/pmm/pmap.h#pmm-pmap) и [pstringview](../../include/pmm/pstringview.h#pmm-pstringview) обновлять только высоту. Один и тот же код ротаций -обслуживает все контейнеры. - -#### `BlockPPtr` адаптер - -Легковесная обёртка `(base_ptr, block_index)`, имитирующая интерфейс [pptr](../../include/pmm/pptr.h#pmm-pptr) через -[BlockHeader](../../include/pmm/block_header.h#pmm-blockheader). Позволяет [AvlFreeTree](../../include/pmm/free_block_tree.h#pmm-avlfreetree) делегировать ротации, ребалансировку -и поиск минимума общим AVL-функциям вместо дублирования ~120 строк кода. - -#### `AvlInorderIterator` - -Общий шаблонный итератор для in-order обхода AVL-дерева. Заменяет идентичные -структуры итераторов, ранее дублировавшиеся в AVL-контейнерах. - -#### Общие хелперы в `types.h` - -- `resolve_granule_ptr()` — преобразование гранульного индекса в указатель -- `ptr_to_granule_idx()` — преобразование указателя в гранульный индекс -- `crc32_accumulate_byte()` — обработка одного байта CRC32 - -#### Дедупликация конфигураций в `manager_configs.h` - -`StaticConfig` — общий базовый шаблон для статических -конфигураций. `SmallEmbeddedStaticConfig` и `EmbeddedStaticConfig` теперь являются -алиасами [StaticConfig](../../include/pmm/manager_configs.h#pmm-staticconfig) с соответствующими параметрами. - -### Результат - -- **−383 строк** (4029 вставок, 4412 удалений) -- Все 70 существующих тестов проходят -- Контейнеры ([pmap](../../include/pmm/pmap.h#pmm-pmap), [pstringview](../../include/pmm/pstringview.h#pmm-pstringview)) делегируют AVL-операции общим функциям -- [AvlFreeTree](../../include/pmm/free_block_tree.h#pmm-avlfreetree) использует [BlockPPtr](../../include/pmm/avl_tree_mixin.h#pmm-detail-blockpptr) адаптер вместо собственных ротаций -- Единообразная инициализация узлов через `avl_init_node` (sentinel `no_block`) diff --git a/docs/archive/phase7_4_encryption_compression.md b/docs/archive/phase7_4_encryption_compression.md deleted file mode 100644 index c01a556b..00000000 --- a/docs/archive/phase7_4_encryption_compression.md +++ /dev/null @@ -1,462 +0,0 @@ -# Фаза 7.4: Сжатие и шифрование образов ПАП - -Документ содержит проработку вариантов сжатия и шифрования образов Персистентного Адресного -Пространства (ПАП) для PersistMemoryManager. - -Issue: [#239](https://github.com/netkeep80/PersistMemoryManager/issues/239) - ---- - -## Содержание - -1. [Текущее состояние](#текущее-состояние) -2. [Вариант A: Полное сжатие/шифрование образа](#вариант-a-полное-сжатиешифрование-образа) -3. [Вариант B: Частичное шифрование содержимого блоков данных](#вариант-b-частичное-шифрование-содержимого-блоков-данных) -4. [Вариант C: Гибридная схема (сжатие блоков + шифрование данных)](#вариант-c-гибридная-схема-сжатие-блоков--шифрование-данных) -5. [Анализ утечек метаданных при частичном шифровании](#анализ-утечек-метаданных-при-частичном-шифровании) -6. [Рекомендация](#рекомендация) -7. [Предлагаемый дизайн API](#предлагаемый-дизайн-api) -8. [Матрица сравнения вариантов](#матрица-сравнения-вариантов) - ---- - -## Текущее состояние - -### Бинарная раскладка образа ПАП (DefaultAddressTraits) - -``` -Байтовое смещение | Гранулы | Содержимое -------------------|---------|----------------------------------- -[0..31] | 0–1 | Block_0 (метаданные-блок) -[32..95] | 2–5 | ManagerHeader (magic, counters, CRC32, root_offset) -[96..127] | 6–7 | Block_1 header (первый свободный блок) -[128..] | 8+ | Заголовки блоков + данные пользователя -``` - -Каждый блок в ПАП состоит из: -- **Заголовок блока** (Block\, 32 байта / 2 гранулы): TreeNode-поля AVL-дерева - (`weight`, `left_offset`, `right_offset`, `parent_offset`, `root_offset`, `avl_height`, - `node_type`) + поля связного списка (`prev_offset`, `next_offset`). -- **Данные блока** (N гранул): пользовательские данные — содержимое [pstring](../../include/pmm/pstring.h#pmm-pstring), элементы - [parray](../../include/pmm/parray.h#pmm-parray), узлы [pmap](../../include/pmm/pmap.h#pmm-pmap), и т.д. - -### Текущая защита целостности - -- CRC32 (ISO 3309) всего образа при `save_manager()` / `load_manager_from_file()` (`io.h`) -- Atomic write-then-rename для защиты от сбоев при записи -- Валидация magic-числа и `granule_size` при загрузке - -### Что не защищено - -- **Конфиденциальность**: образ записывается на диск в открытом виде -- **Сжатие**: образ не сжимается, свободные блоки содержат мусор - ---- - -## Вариант A: Полное сжатие/шифрование образа - -### Описание - -Весь образ ПАП обрабатывается как единый blob: -1. При сохранении: `compress(image)` → `encrypt(compressed)` → `write(file)` -2. При загрузке: `read(file)` → `decrypt(data)` → `decompress(data)` → `load(image)` - -### Алгоритмы - -| Операция | Алгоритм | Характеристики | -|-----------|-----------------|--------------------------------------------------| -| Сжатие | LZ4 (default) | Скорость: ~5 GB/s decode, ratio ~2:1 | -| Сжатие | zstd (optional) | Скорость: ~1.5 GB/s decode, ratio ~3:1 | -| Шифрование| AES-256-GCM | Authenticated encryption, аппаратное ускорение | -| Шифрование| ChaCha20-Poly1305| Высокая скорость без AES-NI, authenticated | - -### Преимущества - -1. **Максимальная конфиденциальность**: скрыта вся информация, включая структуру дерева, - количество блоков, размеры, паттерны аллокации -2. **Простота реализации**: минимальные изменения — обёртки вокруг `save_manager()` / - `load_manager_from_file()` -3. **Лучшее сжатие**: доступ ко всему контексту данных, LZ4/zstd могут находить повторения - между блоками -4. **Нет утечек метаданных**: атакующий видит только размер файла (±) - -### Недостатки - -1. **Полная перезапись при любом изменении**: изменение 1 байта → перезапись всего образа. - Для образа 1 GB это ~1 GB записи на диск -2. **Невозможность частичного обновления**: несовместимо с [MMapStorage](../../include/pmm/mmap_storage.h#pmm-mmapstorage) при online-записи. - Нужно загрузить весь образ в память, расшифровать, работать, зашифровать и записать -3. **Задержка при загрузке**: весь образ нужно расшифровать и распаковать до начала работы -4. **Невозможность binary diff**: BinDiffSynchronizer не может вычислять diff между - зашифрованными образами — зашифрованные данные некоррелированы -5. **Несовместимость с инкрементальным бэкапом**: каждое сохранение порождает полностью - новый файл - ---- - -## Вариант B: Частичное шифрование содержимого блоков данных - -### Описание - -Шифрование применяется только к **данным блоков** (гранулы после заголовка Block\), -оставляя **структуру леса AVL-деревьев** (заголовки блоков, ManagerHeader) в открытом виде. - -``` -┌──────────────────────────────────────────────────────────────────┐ -│ Block_0 (32 B) │ ManagerHeader (64 B) │ ОТКРЫТО │ -├──────────────────────────────────────────────────────────────────┤ -│ Block_1 header │ ОТКРЫТО │ │ -│ Block_1 data │ ████ ЗАШИФРОВАНО ████ │ │ -├──────────────────────────────────────────────────────────────────┤ -│ Block_2 header │ ОТКРЫТО │ │ -│ Block_2 data │ ████ ЗАШИФРОВАНО ████ │ │ -├──────────────────────────────────────────────────────────────────┤ -│ ... │ │ │ -└──────────────────────────────────────────────────────────────────┘ -``` - -### Механизм шифрования блока данных - -Каждый блок данных шифруется **индивидуально** с собственным IV (Initialization Vector): - -``` -IV = HMAC-SHA256(master_key, block_granule_index || block_generation_counter) -``` - -Где `block_generation_counter` — счётчик перезаписей блока (предотвращает reuse IV при -перезаписи данных по тому же адресу). - -**Алгоритм шифрования блока:** -1. Вычислить IV из гранульного индекса блока -2. `AES-256-CTR(key, IV, plaintext_data)` → `ciphertext_data` -3. Записать `ciphertext_data` в гранулы данных блока -4. HMAC-SHA256 для аутентификации хранится в ManagerHeader (или в отдельном блоке) - -### Обработка при сохранении/загрузке - -**save_manager (модифицированный):** -1. Обойти связный список блоков (Block_0 → Block_1 → ... → Block_N) -2. Для каждого **аллоцированного** блока (root_offset == own_index): - - Определить область данных: `[block_start + sizeof(Block), block_start + weight * granule_size)` - - Зашифровать данные на месте (in-place) -3. Вычислить CRC32 зашифрованного образа -4. Записать образ в файл (atomic write) - -**load_manager_from_file (модифицированный):** -1. Загрузить файл, проверить CRC32 -2. Вызвать `MgrT::load()` (восстановление AVL-дерева, связного списка) -3. Обойти блоки, для каждого аллоцированного — расшифровать данные на месте - -### Преимущества - -1. **Частичное обновление**: при изменении одного блока нужно перешифровать только его данные - (~десятки байт), а не весь образ -2. **Совместимость с MMapStorage**: можно шифровать/расшифровывать блоки по мере обращения (on-demand) -3. **Совместимость с BinDiffSynchronizer**: заголовки блоков остаются стабильными, - binary diff между образами вычисляется эффективно — изменения локализованы -4. **Инкрементальный бэкап**: можно определить изменённые блоки по CRC или generation counter -5. **Быстрая загрузка**: можно расшифровывать блоки лениво (on-access) вместо всего образа - -### Недостатки - -1. **Утечка метаданных структуры** (подробный анализ в разделе ниже) -2. **Сложность реализации**: нужно управлять per-block IV, generation counters -3. **Overhead хранения**: IV или generation counter для каждого блока (+4..16 байт/блок) -4. **Не защищает от анализа паттернов**: размеры блоков, топология AVL-дерева видны - ---- - -## Вариант C: Гибридная схема (сжатие блоков + шифрование данных) - -### Описание - -Комбинация сжатия данных блоков и их последующего шифрования: - -1. **Сжатие**: каждый блок данных сжимается индивидуально (LZ4 frame) -2. **Шифрование**: сжатые данные шифруются (AES-256-CTR) -3. **Структура**: заголовки блоков остаются открытыми, но содержат дополнительные метаданные: - - `compressed_size` — размер сжатых данных - - `original_size` — размер исходных данных (для аллокации при распаковке) - -### Преимущества - -- Сочетает экономию места (сжатие) с конфиденциальностью (шифрование) -- Сжатие перед шифрованием эффективнее, чем после (шифрованные данные несжимаемы) -- Сохраняет возможность частичного обновления - -### Недостатки - -- Максимальная сложность реализации из трёх вариантов -- Сжатие отдельных блоков даёт худший ratio, чем сжатие всего образа -- Нужна дополнительная индирекция: блок данных может быть меньше выделенных гранул -- При росте данных после сжатия (редкие данные) нужна реаллокация - ---- - -## Анализ утечек метаданных при частичном шифровании - -### Что видит атакующий при варианте B - -Из **открытых** заголовков блоков атакующий может извлечь: - -| Метаданные | Источник | Значимость | -|---------------------------|-----------------------------------|------------| -| Количество блоков | `ManagerHeader.block_count` | Низкая | -| Количество аллоцированных | `ManagerHeader.alloc_count` | Низкая | -| Размер каждого блока | `Block.weight` (в гранулах) | Средняя | -| Топология AVL-дерева | `left/right/parent_offset` | Низкая | -| Порядок аллокации | Связный список блоков | Низкая | -| Общий объём данных | `ManagerHeader.used_size` | Низкая | -| Наличие root_object | `ManagerHeader.root_offset != 0` | Низкая | - -### Анализ для pjson-хранения в ПАП - -При хранении pjson (JSON-данных) через BinDiffSynchronizer: - -**Структура pjson в ПАП:** -- Каждый JSON-узел — отдельный блок через `ppool` (пулы фиксированного размера) -- JSON-строки — блоки через [pstring](../../include/pmm/pstring.h#pmm-pstring) (мутабельные строки, данные в отдельном блоке) -- JSON-объекты — `pmap` (AVL-дерево ключ-значение) -- JSON-массивы — `parray` (динамический массив) - -**Что может узнать атакующий из открытых метаданных:** - -1. **Количество JSON-узлов**: по `alloc_count` и размерам блоков можно оценить количество - узлов в JSON-документе -2. **Глубина вложенности**: размеры блоков [pmap](../../include/pmm/pmap.h#pmm-pmap)-узлов косвенно отражают количество ключей - на каждом уровне -3. **Длины строк**: размер блока данных [pstring](../../include/pmm/pstring.h#pmm-pstring) = `ceil(length / granule_size)` гранул, - что даёт верхнюю оценку длины строки с точностью до `granule_size` (16 байт) -4. **Тип структуры**: блоки пула (`ppool`) имеют характерный фиксированный размер, - отличающийся от блоков [parray](../../include/pmm/parray.h#pmm-parray) или [pstring](../../include/pmm/pstring.h#pmm-pstring) -5. **Паттерн доступа**: при наблюдении за последовательными образами — какие блоки менялись - -### Оценка рисков - -**Низкий риск** (для большинства сценариев): -- Атакующий не может восстановить ключи или значения JSON без ключа шифрования -- Информация о количестве узлов и длинах строк недостаточна для реконструкции данных -- Реальный JSON содержит много узлов с похожими размерами, затрудняя идентификацию - -**Средний риск** (для чувствительных данных): -- Если атакующий знает **схему** JSON-документа (например, всегда `{"password": "...", "token": "..."}`), - он может определить длину пароля/токена с точностью до 16 байт по размеру блока [pstring](../../include/pmm/pstring.h#pmm-pstring) -- Наблюдая за изменениями между образами, можно определить какие именно поля изменились - -**Высокий риск** (при длительном наблюдении): -- Частотный анализ размеров блоков при многократных обновлениях может коррелировать - с паттернами данных - -### Меры митигации утечек метаданных - -1. **Паддинг данных блоков**: дополнять данные каждого блока до фиксированного размера - (например, до ближайшей степени двойки в гранулах). Это скрывает точные длины строк: - ``` - Без паддинга: pstring "abc" → 1 гранула (длина 1–16 байт) - Без паддинга: pstring "hello" → 1 гранула (длина 1–16 байт) - Без паддинга: pstring "secret_password_123" → 2 гранулы (длина 17–32 байт) - - С паддингом (степень 2): все строки до 2 гранул → 2 гранулы - ``` - -2. **Обфускация weight**: шифровать поле `weight` в заголовке блока (оставляя навигационные - поля AVL-дерева открытыми). Это скрывает размеры блоков данных - -3. **Dummy-блоки**: периодически создавать фиктивные аллокации для маскировки реального - количества узлов - -4. **Фиксированный размер файла**: при сохранении дополнять файл до фиксированного размера, - скрывая `total_size` - ---- - -## Рекомендация - -### Для PersistMemoryManager рекомендуется **Вариант B** (частичное шифрование содержимого блоков данных) - -**Обоснование:** - -1. **Архитектурная совместимость**: ПАП использует гранульные индексы и блочную аллокацию — - побочное шифрование (per-block) органично вписывается в существующую архитектуру -2. **Частичное обновление**: ключевое преимущество ПАП — инкрементальная персистентность. - Полное шифрование (вариант A) уничтожает это свойство -3. **BinDiffSynchronizer**: является основным потребителем pmm. Ему необходима возможность - вычислять binary diff — вариант A это исключает -4. **Достаточная конфиденциальность**: для типичных сценариев (хранение конфигураций, - пользовательских данных, кэшей) частичное шифрование обеспечивает адекватную защиту -5. **Расширяемость**: при необходимости можно добавить полное шифрование как отдельную - опцию поверх частичного (defence in depth) - -### Вариант A рекомендуется как **опциональная надстройка** для случаев, когда: -- Утечка метаданных неприемлема (государственная тайна, медицинские данные) -- Образ записывается редко и целиком (batch-сценарий) -- BinDiffSynchronizer не используется - -### Рекомендация по сжатию - -Сжатие рекомендуется реализовать **отдельно** от шифрования, как независимую политику: -- Сжатие применять **до** шифрования (сжатие шифрованных данных неэффективно) -- LZ4 как дефолт (скорость), zstd как опция (ratio) -- Сжатие целого образа (вариант A) для offline-бэкапов -- Сжатие отдельных блоков (вариант C) для online-работы с MMapStorage - ---- - -## Предлагаемый дизайн API - -### Шаблонная политика шифрования - -По аналогии с существующими политиками (`LockPolicyT`, `LoggingPolicyT`), шифрование -реализуется как шаблонная политика: - -```cpp -namespace pmm { namespace encryption { - -/// Политика без шифрования (по умолчанию, нулевые накладные расходы) -struct NoEncryption -{ - static void encrypt_block(std::uint8_t* data, std::size_t size, - std::uint64_t block_index) noexcept {} - static void decrypt_block(std::uint8_t* data, std::size_t size, - std::uint64_t block_index) noexcept {} - static bool is_enabled() noexcept { return false; } -}; - -/// AES-256-CTR шифрование данных блоков -struct Aes256CtrEncryption -{ - /// Установить мастер-ключ (32 байта) - static void set_key(const std::uint8_t key[32]) noexcept; - - /// Зашифровать данные блока (in-place) - /// IV = SHA256(master_key || block_index) - static void encrypt_block(std::uint8_t* data, std::size_t size, - std::uint64_t block_index) noexcept; - - /// Расшифровать данные блока (in-place) - static void decrypt_block(std::uint8_t* data, std::size_t size, - std::uint64_t block_index) noexcept; - - static bool is_enabled() noexcept { return true; } -}; - -}} // namespace pmm::encryption -``` - -### Конфигурация менеджера - -```cpp -struct EncryptedHeapConfig -{ - using address_traits = DefaultAddressTraits; - using storage_policy = HeapStorage; - using lock_policy = NoLock; - using logging_policy = logging::NoLogging; - using encryption_policy = encryption::Aes256CtrEncryption; // Новое -}; -``` - -### Модификация io.h - -```cpp -template -inline bool save_manager_encrypted(const char* filename) -{ - using AT = typename MgrT::address_traits; - using EP = typename MgrT::encryption_policy; - - if constexpr (EP::is_enabled()) { - // Обойти все аллоцированные блоки и зашифровать данные - for_each_allocated_block([](Block* block, std::uint64_t idx) { - std::uint8_t* data = block_data_ptr(block); - std::size_t size = block_data_size(block); - EP::encrypt_block(data, size, idx); - }); - } - - bool ok = save_manager(filename); - - if constexpr (EP::is_enabled()) { - // Расшифровать обратно для продолжения работы в памяти - for_each_allocated_block([](Block* block, std::uint64_t idx) { - std::uint8_t* data = block_data_ptr(block); - std::size_t size = block_data_size(block); - EP::decrypt_block(data, size, idx); - }); - } - - return ok; -} -``` - -### Модификация ManagerHeader - -```cpp -template -struct ManagerHeader -{ - // ... существующие поля ... - - std::uint32_t crc32; ///< CRC32 checksum - index_type root_offset; ///< Root object granule index - - // Новые поля (за счёт зарезервированного пространства или расширения): - std::uint8_t encryption_algo; ///< 0=none, 1=AES-256-CTR, 2=ChaCha20 - std::uint8_t _reserved_enc[3]; ///< Reserved for future encryption metadata -}; -``` - ---- - -## Матрица сравнения вариантов - -| Критерий | Вариант A (полное) | Вариант B (частичное) | Вариант C (гибрид) | -|---------------------------------|--------------------|-----------------------|---------------------| -| Конфиденциальность данных | ★★★ | ★★☆ | ★★☆ | -| Скрытие метаданных структуры | ★★★ | ☆☆☆ | ☆☆☆ | -| Частичное обновление файла | ☆☆☆ | ★★★ | ★★★ | -| Совместимость с MMapStorage | ☆☆☆ | ★★★ | ★★☆ | -| Совместимость с BinDiffSync | ☆☆☆ | ★★★ | ★★☆ | -| Сжатие данных | ★★★ | ☆☆☆ | ★★☆ | -| Скорость загрузки | ★☆☆ | ★★★ | ★★☆ | -| Простота реализации | ★★★ | ★★☆ | ★☆☆ | -| Совместимость с CRC32 | ★★★ | ★★★ | ★★☆ | -| Инкрементальный бэкап | ☆☆☆ | ★★★ | ★★☆ | - ---- - -## План реализации - -### Этап 1: Инфраструктура (оценка: средняя сложность) - -1. Определить `encryption_policy` как шаблонный параметр в [BasicConfig](../../include/pmm/manager_configs.h#pmm-basicconfig) -2. Добавить SFINAE-детекцию `encryption_policy` для обратной совместимости - (аналогично `logging_policy`) -3. Добавить `encryption_algo` в [ManagerHeader](../../include/pmm/types.h#pmm-detail-managerheader) (использовать резервные байты) -4. Реализовать `NoEncryption` политику (нулевые накладные расходы) - -### Этап 2: Реализация шифрования блоков (оценка: средняя сложность) - -1. Реализовать `Aes256CtrEncryption` с использованием OpenSSL / libsodium / header-only AES -2. Добавить `for_each_allocated_block()` утилиту для обхода аллоцированных блоков -3. Модифицировать `save_manager()` / `load_manager_from_file()` — добавить - encrypt/decrypt при наличии `encryption_policy` -4. Валидация: проверять `encryption_algo` при загрузке, отвергать несовместимые образы - -### Этап 3: Тестирование (оценка: средняя сложность) - -1. Round-trip тесты: save encrypted → load → verify data integrity -2. Тест совместимости: загрузка незашифрованного образа менеджером с `NoEncryption` -3. Тест отклонения: загрузка зашифрованного образа без правильного ключа → ошибка -4. Тест с BinDiffSynchronizer: diff между двумя зашифрованными образами -5. Тест с pjson: шифрование/расшифрование реального JSON-документа в ПАП - -### Этап 4 (опционально): Полное шифрование как надстройка - -1. Реализовать `save_manager_full_encrypted()` / `load_manager_full_encrypted()` -2. Алгоритм: encrypt(compress(image)) при сохранении -3. Не связан с policy-based подходом — отдельные функции в `io.h` - ---- - -*Документ составлен 2026-03-30 в рамках Issue #239.* diff --git a/docs/archive/plan.md b/docs/archive/plan.md deleted file mode 100644 index c21c4894..00000000 --- a/docs/archive/plan.md +++ /dev/null @@ -1,375 +0,0 @@ -# План развития PersistMemoryManager - -Документ составлен на основе code review pmm (v0.26.0) и [BinDiffSynchronizer](https://github.com/netkeep80/BinDiffSynchronizer) (pjson_db_pmm). - ---- - -## Текущее состояние - -PersistMemoryManager — header-only C++20 библиотека для управления Персистентным Адресным Пространством (ПАП) -с блочным аллокатором, AVL-деревом свободных блоков и адресно-независимыми гранульными индексами. - -**Версия:** v0.26.0 -**Оценка: 8.5/10** — качественная архитектура, хорошее покрытие тестами, зрелый API. - -### Что есть в pmm - -- Статический API без экземпляров (мультитон через `InstanceId`) -- Конфигурируемые бэкенды хранения ([HeapStorage](../../include/pmm/heap_storage.h#pmm-heapstorage), [StaticStorage](../../include/pmm/static_storage.h#pmm-staticstorage), [MMapStorage](../../include/pmm/mmap_storage.h#pmm-mmapstorage)) -- Политики потокобезопасности ([NoLock](../../include/pmm/config.h#pmm-config-nolock), [SharedMutexLock](../../include/pmm/config.h#pmm-config-sharedmutexlock)) -- Персистентные структуры данных: [pstringview](../../include/pmm/pstringview.h#pmm-pstringview), `pmap`, `parray` -- Типизированный персистентный указатель `pptr` с гранульными индексами -- CI/CD на 4 платформах (Linux GCC/Clang, macOS, Windows MSVC) -- 130+ тестов - -### Что нужно BinDiffSynchronizer от pmm - -BinDiffSynchronizer (pjson_db_pmm) — это персистентная JSON-база данных, построенная поверх pmm. -Анализ показал, что BinDiffSynchronizer вынужден реализовывать собственные типы вместо использования -встроенных типов pmm из-за следующих ограничений: - -1. **Нет мутабельной строки** ([pstring](../../include/pmm/pstring.h#pmm-pstring)) — BinDiffSynchronizer реализует `pstring_pmm` для JSON string-value узлов -2. **Нет `parray` с O(1) случайным доступом** — BinDiffSynchronizer реализует `pmem_array_pmm` с O(1) индексацией -3. **`pmap` — нет `erase()`** — BinDiffSynchronizer реализует `pmap_pmm` на сортированных массивах -4. **Нет STL-аллокатора** — BinDiffSynchronizer реализует `pallocator_pmm` -5. **Нет пулов объектов** — BinDiffSynchronizer реализует `pjson_pool_pmm` для массового выделения узлов -6. **Адаптер для конверсии pptr ↔ байтовые смещения** — BinDiffSynchronizer реализует `pam_adapter.h` -7. **Нет реестра именованных объектов** — BinDiffSynchronizer реализует `pam_pmm.h` с registry -8. **Нет `fptr`** — типизированный указатель с привязкой к реестру по имени - -**Принцип:** pmm — только менеджер ПАП и специальные типы объектов для использования в ПАП. -pmm не должен содержать реализацию pjson, а только предоставлять для него типы и хранилище. - ---- - -## Выполненные фазы - -### Фаза 1: Безопасность и устойчивость ✅ ВЫПОЛНЕНО (v0.25.0–v0.26.0) - -> Реализовано в Issue #43. Подробности: [docs/phase1_safety.md](phase1_safety.md) - -- 1.1 `static_assert` для `create_typed` — проверка `is_nothrow_constructible` -- 1.2 Проверка границ в `resolve()` — `is_valid_ptr()`, `assert` в debug -- 1.3 Overflow-защита в `allocator_policy` — проверка переполнения при вычислении гранул -- 1.4 Runtime-проверки в `cast_from_raw()` — `nullptr` в Release вместо только `assert` - -### Фаза 2: Персистентность и надёжность ✅ ВЫПОЛНЕНО (v0.25.0–v0.26.0) - -> Реализовано в Issue #43. Подробности: [docs/phase2_persistence.md](phase2_persistence.md) - -- 2.1 CRC32 для персистентных образов — вычисление при сохранении, проверка при загрузке -- 2.2 Атомарное сохранение — write-then-rename паттерн -- 2.3 [MMapStorage::expand()](../../include/pmm/mmap_storage.h#pmm-mmapstorage-expand) — динамическое расширение mmap-файлов - ---- - -## Фаза 3: Типы для BinDiffSynchronizer (приоритет: высокий) - -> Начата в Issue #45. Подробности: [docs/phase3_types.md](phase3_types.md) - -Цель: предоставить в pmm все базовые персистентные типы, которые BinDiffSynchronizer -сейчас реализует самостоятельно, чтобы исключить дублирование кода. - -### 3.1 Мутабельная персистентная строка `pstring` ✅ ВЫПОЛНЕНО (#45) - -**Текущая ситуация:** pmm имеет только [pstringview](../../include/pmm/pstringview.h#pmm-pstringview) (read-only, interned). BinDiffSynchronizer -реализует `pstring_pmm` — мутабельную строку для JSON string-value узлов. - -**Решение:** -- Добавить `include/pmm/pstring.h` — мутабельная строка в ПАП -- API: `assign(const char*)`, `append(const char*)`, `c_str()`, `size()`, `clear()`, `free_data()`, `operator[]` -- Данные хранятся в отдельном блоке (аллокация через pmm) -- При изменении — переаллокация блока данных с удвоением ёмкости (amortized O(1)) -- POD-структура (trivially copyable) для прямой сериализации в ПАП -- Операторы сравнения: `==`, `!=`, `<` с C-строками и другими pstring - -### 3.2 Персистентный массив `parray` с O(1) индексацией ✅ ВЫПОЛНЕНО (#195) - -**Текущая ситуация:** BinDiffSynchronizer реализует `pmem_array_pmm` — динамический массив с O(1) индексацией. - -**Решение:** -- Добавить `include/pmm/parray.h` — динамический массив в ПАП -- Заголовок: `[size | capacity | data_offset]` (3 × index_type) -- O(1) `at(i)`, `push_back()`, `pop_back()` -- Рост ёмкости удвоением (amortized O(1) `push_back`) -- Данные в отдельном блоке — переаллокация при росте -- `reserve(n)` для предварительного выделения - -### 3.3 Доработка `pmap` — метод `erase(key)` ✅ ВЫПОЛНЕНО (#196) - -**Текущая ситуация:** [pmap](../../include/pmm/pmap.h#pmm-pmap) не имеет `erase()`. Код уже есть в `avl_tree_mixin.h::avl_remove()`, -но он не подключён к [pmap](../../include/pmm/pmap.h#pmm-pmap). BinDiffSynchronizer из-за этого реализует свой `pmap_pmm` на -сортированных массивах. - -**Решение:** -- Добавить `pmap::erase(key)` через `avl_remove()` из `avl_tree_mixin.h` -- Добавить [pmap::size()](../../include/pmm/pmap.h#pmm-pmap-size) — количество элементов -- Добавить итератор [pmap::begin()](../../include/pmm/pmap.h#pmm-pmap-begin)/`end()` для обхода в порядке ключей -- Добавить [pmap::clear()](../../include/pmm/pmap.h#pmm-pmap-clear) — удаление всех элементов с деаллокацией - -### 3.4 ~~Доработка `pvector` — метод `erase(index)`~~ УДАЛЕНО (#224) - -> Тип `pvector` удалён, так как полностью заменён [parray](../../include/pmm/parray.h#pmm-parray) (Issue #224). - -### 3.5 STL-совместимый аллокатор `pallocator` ✅ ВЫПОЛНЕНО (#198) - -**Текущая ситуация:** BinDiffSynchronizer реализует `pallocator_pmm` для использования -STL-контейнеров с ПАП. - -**Решение:** -- Добавить `include/pmm/pallocator.h` — аллокатор, совместимый с `std::allocator_traits` -- `allocate(n)` → `ManagerT::allocate(n * sizeof(T))` -- `deallocate(p, n)` → `ManagerT::deallocate(p)` -- Позволит: `std::vector>` - -### 3.6 Пул объектов `ppool` ✅ ВЫПОЛНЕНО (#199) - -**Текущая ситуация:** BinDiffSynchronizer реализует `pjson_pool_pmm` для массового -выделения JSON-узлов. - -**Решение:** -- Добавить `include/pmm/ppool.h` — пул объектов фиксированного размера -- O(1) `allocate()` / `deallocate()` через free-list внутри пула -- Блоки пула выделяются крупными чанками через pmm -- Идеально для массового создания узлов деревьев, списков, графов - -### 3.7 Реестр именованных объектов ✅ ВЫПОЛНЕНО (#200) - -**Текущая ситуация:** BinDiffSynchronizer реализует `pam_pmm.h` с реестром -(name → slot_info) для обнаружения объектов по имени. - -**Решение:** -- Добавить API для корневого объекта в [PersistMemoryManager](../../include/pmm/persist_memory_manager.h#pmm-persistmemorymanager): - - `set_root(pptr)` / `get_root()` — один корневой указатель в [ManagerHeader](../../include/pmm/types.h#pmm-detail-managerheader) -- Расширить [ManagerHeader](../../include/pmm/types.h#pmm-detail-managerheader) — добавить `root_object_offset` (index_type) в зарезервированные поля -- Пользователь через корневой объект может хранить свой реестр (например, `pmap>`) - ---- - -## Выполненные фазы (продолжение) - -### Фаза 4: API и удобство использования ✅ ВЫПОЛНЕНО (v0.27.0–v0.37.0) - -> Начата в Issue #201. Подробности: [docs/phase4_api.md](phase4_api.md) - -### 4.1 Коды ошибок вместо bool ✅ ВЫПОЛНЕНО (#201) - -**Проблема:** `create()`, `load()`, `allocate()` возвращают `bool` или `nullptr`. -Причина ошибки неизвестна. - -**Решение:** -- Ввести `enum class PmmError { Ok, OutOfMemory, InvalidSize, Overflow, InvalidMagic, CrcMismatch, ... }` -- Добавить `static PmmError last_error() noexcept` -- Добавить `static void clear_error() noexcept` и `static void set_last_error(PmmError) noexcept` -- Все методы жизненного цикла и аллокации устанавливают код ошибки при неудаче - -### 4.2 Хуки для логирования ✅ ВЫПОЛНЕНО (#202) - -**Проблема:** Нет механизма отслеживания событий (аллокации, ошибки, расширения). - -**Решение:** -- Шаблонный параметр `LoggingPolicyT` в конфигурации (через [BasicConfig](../../include/pmm/manager_configs.h#pmm-basicconfig)) -- Политики: `logging::NoLogging` (по умолчанию, нулевые накладные расходы), `logging::StderrLogging` -- Хуки: `on_allocation_failure()`, `on_expand()`, `on_corruption_detected()`, `on_create()`, `on_destroy()`, `on_load()` -- Обратная совместимость: SFINAE-детекция `logging_policy` в конфигурации - -### 4.3 Метод `reallocate_typed()` ✅ ВЫПОЛНЕНО (#210) - -**Проблема:** Нет нативного перераспределения. Пользователь должен вручную выделить, -скопировать, освободить. - -**Решение:** -- Если следующий блок свободен и достаточного размера — расширить на месте -- Иначе — аллокация + memcpy + деаллокация - -### 4.4 Конверсия pptr ↔ байтовые смещения ✅ ВЫПОЛНЕНО (#211) - -**Текущая ситуация:** BinDiffSynchronizer реализует `pam_adapter.h` для конверсии -между гранульными индексами и байтовыми смещениями. - -**Решение:** -- Добавлен `pptr::byte_offset()` — возвращает `offset() * granule_size` -- Добавлен `PersistMemoryManager::pptr_from_byte_offset(size_t)` — создаёт pptr из байтового смещения -- Упрощает интеграцию с внешними системами, работающими с байтовыми адресами - ---- - -## Фаза 5: Тестирование и качество ✅ ВЫПОЛНЕНО (v0.39.0–v0.42.0) - -> Начата в Issue #212. Подробности: [docs/phase5_testing.md](phase5_testing.md) - -### 5.1 Миграция на Catch2 ✅ ВЫПОЛНЕНО (#212) - -**Текущая ситуация:** Проект использовал собственные макросы `PMM_TEST()` / `PMM_RUN()`, -определённые локально в каждом тестовом файле, с ручной `main()` логикой. - -**Решение:** -- Мигрированы 73 тестовых файла на Catch2 v3.7.1 (FetchContent) -- `REQUIRE()` / `CHECK()` вместо `PMM_TEST()` -- `TEST_CASE()` вместо `static bool test_xxx()` + `PMM_RUN()` -- Catch2WithMain предоставляет `main()` автоматически -- Удалено ~4200 строк дублирующегося кода макросов -- 67 тестов проходят (100% pass rate) - -### 5.2 Расширение покрытия тестами ✅ ВЫПОЛНЕНО (#213) - -**Текущая ситуация:** Тесты покрывали функциональные сценарии, но не проверяли -переполнения, конкурентные паттерны и случайные последовательности операций. - -**Решение:** -- 19 тест-кейсов на переполнение: `size_t` overflow, `index_type` overflow, граничные - размеры, исчерпание памяти, все варианты AddressTraits (16/32/64-bit) -- 6 конкурентных тест-кейсов: варьирующиеся размеры, LIFO/random деаллокация, - высокая конкуренция (16 потоков), проверка целостности данных, concurrent reallocate -- 6 детерминистических fuzz тест-кейсов: PRNG-управляемые alloc/dealloc/reallocate - на static/heap/16-bit хранилищах, фрагментационный стресс, sweep по seed-ам -- libFuzzer harness (`tests/fuzz_allocator.cpp`) для покрытие-управляемого фаззинга - -### 5.3 Бенчмарки производительности ✅ ВЫПОЛНЕНО (#214) - -**Проблема:** Нет формальных бенчмарков производительности с Google Benchmark. - -**Решение:** -- Google Benchmark v1.9.1 через FetchContent (опционально: `PMM_BUILD_BENCHMARKS=ON`) -- 16 бенчмарков в `benchmarks/bench_allocator.cpp`: - - Аллокатор: allocate, deallocate, mixed, reallocate_typed, batch (5) - - Сравнение с malloc/free (2) - - pmap: insert, find, erase (3) - - parray: push_back, random access (2) - - ppool: allocate, batch (2) - - pstring: assign, append (2) - - pstringview: intern existing (1) - - Многопоточность: alloc/dealloc с SharedMutexLock (1, 2, 4, 8 потоков) (1) - ---- - -## Фаза 6: Документация (приоритет: средний) - -> Начата в Issue #215. Подробности: [docs/phase6_documentation.md](phase6_documentation.md) - -### 6.1 Документация потокобезопасности ✅ ВЫПОЛНЕНО (#215) - -- `docs/thread_safety.md` — контракт для [pptr](../../include/pmm/pptr.h#pmm-pptr), `resolve()`, concurrent access -- Примеры корректного и некорректного использования - -### 6.2 Документация восстановления после сбоев ✅ ВЫПОЛНЕНО (#216) - -- `docs/recovery.md` — сценарии сбоя, гарантии и ограничения - -### 6.3 Устранение дублирования кода (метапрограммирование) ✅ ВЫПОЛНЕНО (#188) - -**Проблема:** AVL-код (ротации, ребалансировка, обход, итераторы) дублировался в -[pmap](../../include/pmm/pmap.h#pmm-pmap), [pstringview](../../include/pmm/pstringview.h#pmm-pstringview) и `free_block_tree`. Аналогично, конверсии -индекс↔указатель и CRC32-вычисления дублировались в нескольких контейнерах. - -**Решение:** -- Расширен `avl_tree_mixin.h` — все AVL-операции (rotate, rebalance, insert, remove, - find, min/max, init_node, subtree_count, clear_subtree) вынесены в общие шаблонные - функции с настраиваемым `NodeUpdateFn` callback -- Добавлен `BlockPPtr` адаптер — позволяет [AvlFreeTree](../../include/pmm/free_block_tree.h#pmm-avlfreetree) использовать общие - AVL-операции через легковесную обёртку вместо дублирования ~120 строк кода -- Добавлен `AvlInorderIterator` — общий итератор для pmap -- Вынесены `resolve_granule_ptr()` / `ptr_to_granule_idx()` в `types.h` -- Вынесен `crc32_accumulate_byte()` в `types.h` -- Конфигурации `SmallEmbeddedStaticConfig` и `EmbeddedStaticConfig` сведены к - алиасам базового шаблона [StaticConfig](../../include/pmm/manager_configs.h#pmm-staticconfig) -- **Результат:** −383 строки (4029 вставок, 4412 удалений) - -### 6.4 Обновление версий в документации - -- Автоматизация обновления badge-ов версий через CI - ---- - -## Фаза 7: Расширенные возможности (приоритет: низкий) - -### 7.1 Транзакционная семантика - -- Redo-log или undo-log для групп операций -- API: `begin_transaction()` / `commit()` / `rollback()` - -### 7.2 Сборщик мусора (Mark & Sweep) - -- Пользователь регистрирует корневые указатели -- `collect()` обходит все достижимые блоки и освобождает недостижимые -- Опционально: reference counting через `shared_pptr` - -### 7.3 Поддержка shared memory (IPC) - -- Бэкенд `SharedMemoryStorage` на основе POSIX `shm_open` / Windows `CreateFileMapping` -- Адресная независимость уже обеспечена через гранульные индексы -- Межпроцессная синхронизация - -### 7.4 Сжатие и шифрование образов - -> Проработка вариантов: [docs/phase7_4_encryption_compression.md](phase7_4_encryption_compression.md) - -**Проблема:** Образы ПАП записываются на диск в открытом виде. Нет защиты конфиденциальности. -Нет сжатия для уменьшения размера файла. - -**Проработанные варианты:** - -- **Вариант A — Полное сжатие/шифрование:** весь образ обрабатывается как blob. - Максимальная конфиденциальность, но полная перезапись при любом изменении. - Несовместимо с частичным обновлением, MMapStorage, BinDiffSynchronizer. - -- **Вариант B — Частичное шифрование данных блоков (рекомендуемый):** шифрование только - содержимого блоков данных (гранулы после заголовка Block\). Структура леса AVL-деревьев - остаётся открытой. Обеспечивает частичное обновление, совместимость с MMapStorage и - BinDiffSynchronizer. Утечка метаданных (количество блоков, размеры) — низкий риск - для типичных сценариев. - -- **Вариант C — Гибрид (сжатие + шифрование блоков):** сжатие данных блоков (LZ4/zstd) - перед шифрованием. Максимальная сложность реализации. - -**Рекомендуемое решение (вариант B):** -- Шаблонная политика `EncryptionPolicyT` (аналогично `LockPolicyT`, `LoggingPolicyT`) -- Политики: `encryption::NoEncryption` (по умолчанию, zero overhead), `encryption::Aes256CtrEncryption` -- Per-block encryption: IV = f(master_key, block_granule_index) -- Обратная совместимость: SFINAE-детекция `encryption_policy` в конфигурации -- Поле `encryption_algo` в [ManagerHeader](../../include/pmm/types.h#pmm-detail-managerheader) для идентификации алгоритма при загрузке -- Вариант A доступен как отдельные функции `save_manager_full_encrypted()` / `load_manager_full_encrypted()` - для offline-бэкапов - -**Сжатие (отдельная политика):** -- LZ4 (default, ~5 GB/s decode) или zstd (optional, лучший ratio) -- Сжатие целого образа — для offline-бэкапов -- Сжатие отдельных блоков — для online-работы (вариант C) - ---- - -## Приоритеты и порядок реализации - -| # | Задача | Фаза | Приоритет | Сложность | Статус | -|---|--------|------|-----------|-----------|--------| -| 1 | ~~static_assert для create_typed~~ | 1.1 | ~~Высокий~~ | ~~Низкая~~ | ✅ #43 | -| 2 | ~~Проверка границ в resolve()~~ | 1.2 | ~~Высокий~~ | ~~Низкая~~ | ✅ #43 | -| 3 | ~~Overflow-защита в allocator_policy~~ | 1.3 | ~~Высокий~~ | ~~Низкая~~ | ✅ #43 | -| 4 | ~~CRC для персистентных образов~~ | 2.1 | ~~Высокий~~ | ~~Средняя~~ | ✅ #43 | -| 5 | ~~Атомарное сохранение~~ | 2.2 | ~~Высокий~~ | ~~Средняя~~ | ✅ #43 | -| 6 | ~~MMapStorage::expand()~~ | 2.3 | ~~Высокий~~ | ~~Высокая~~ | ✅ #43 | -| 7 | ~~Мутабельная строка [pstring](../../include/pmm/pstring.h#pmm-pstring)~~ | 3.1 | ~~Высокий~~ | ~~Средняя~~ | ✅ #45 | -| 8 | ~~Массив `parray` с O(1) доступом~~ | 3.2 | ~~Высокий~~ | ~~Средняя~~ | ✅ #195 | -| 9 | ~~`erase()` для [pmap](../../include/pmm/pmap.h#pmm-pmap)~~ | 3.3 | ~~Высокий~~ | ~~Низкая~~ | ✅ #196 | -| 10 | ~~`erase(index)` для `pvector`~~ | 3.4 | ~~Удалено~~ | ~~—~~ | Удалено #224 | -| 11 | ~~STL-аллокатор `pallocator`~~ | 3.5 | ~~Высокий~~ | ~~Средняя~~ | ✅ #198 | -| 12 | ~~Пул объектов `ppool`~~ | 3.6 | ~~Высокий~~ | ~~Средняя~~ | ✅ #199 | -| 13 | ~~Корневой объект в ManagerHeader~~ | 3.7 | ~~Высокий~~ | ~~Низкая~~ | ✅ #200 | -| 14 | ~~Коды ошибок~~ | 4.1 | ~~Средний~~ | ~~Средняя~~ | ✅ #201 | -| 15 | ~~Хуки логирования~~ | 4.2 | ~~Средний~~ | ~~Средняя~~ | ✅ #202 | -| 16 | ~~`reallocate_typed`~~ | 4.3 | ~~Средний~~ | ~~Средняя~~ | ✅ #210 | -| 17 | ~~Конверсия pptr ↔ байтовые смещения~~ | 4.4 | ~~Средний~~ | ~~Низкая~~ | ✅ #211 | -| 18 | ~~Миграция на Catch2~~ | 5.1 | ~~Средний~~ | ~~Высокая~~ | ✅ #212 | -| 19 | ~~Fuzz-тестирование~~ | 5.2 | ~~Средний~~ | ~~Средняя~~ | ✅ #213 | -| 20 | ~~Бенчмарки~~ | 5.3 | ~~Средний~~ | ~~Средняя~~ | ✅ #214 | -| 21 | ~~Документация thread safety~~ | 6.1 | ~~Средний~~ | ~~Низкая~~ | ✅ #215 | -| 22 | ~~Документация recovery~~ | 6.2 | ~~Средний~~ | ~~Низкая~~ | ✅ #216 | -| 23 | ~~Устранение дублирования кода~~ | 6.3 | ~~Средний~~ | ~~Высокая~~ | ✅ #188 | -| 24 | Транзакции | 7.1 | Низкий | Высокая | | -| 25 | Сборщик мусора | 7.2 | Низкий | Высокая | | -| 26 | Shared memory IPC | 7.3 | Низкий | Высокая | | -| 27 | Сжатие/шифрование | 7.4 | Низкий | Средняя | Проработано #239 | - ---- - -*Документ обновлён 2026-03-30. Phase 3.1 (pstring) реализована в Issue #45. Phase 3.2 (parray) реализована в Issue #195. Phase 3.3 (pmap erase/size/iterator/clear) реализована в Issue #196. Phase 3.4 (pvector) удалена — pvector заменён parray (Issue #224). Phase 3.5 (pallocator) реализована в Issue #198. Phase 3.6 (ppool) реализована в Issue #199. Phase 3.7 (root object) реализована в Issue #200. Фаза 3 полностью завершена. Phase 4.1 (error codes) реализована в Issue #201. Phase 4.2 (logging hooks) реализована в Issue #202. Phase 4.3 (reallocate_typed) реализована в Issue #210. Phase 4.4 (pptr byte offset conversion) реализована в Issue #211. Фаза 4 полностью завершена. Phase 5.1 (Catch2 migration) реализована в Issue #212. Phase 5.2 (extended test coverage) реализована в Issue #213. Phase 5.3 (Google Benchmark) реализована в Issue #214. Фаза 5 полностью завершена. Phase 6.1 (thread safety documentation) реализована в Issue #215. Phase 6.2 (recovery documentation) реализована в Issue #216. Phase 6.3 (code deduplication via template metaprogramming) реализована в Issue #188. Phase 7.4 (сжатие/шифрование) проработана в Issue #239 — рекомендован вариант частичного шифрования данных блоков.* diff --git a/docs/archive/plan4BinDiffSynchronizer.md b/docs/archive/plan4BinDiffSynchronizer.md deleted file mode 100644 index 3ea3c35c..00000000 --- a/docs/archive/plan4BinDiffSynchronizer.md +++ /dev/null @@ -1,253 +0,0 @@ -# План переделки BinDiffSynchronizer под новый pmm - -Документ описывает план миграции [BinDiffSynchronizer](https://github.com/netkeep80/BinDiffSynchronizer) -(pjson_db_pmm) на обновлённый PersistMemoryManager после выполнения [Фазы 3 плана pmm](plan.md). - ---- - -## Текущее состояние BinDiffSynchronizer - -BinDiffSynchronizer (pjson_db_pmm) — header-only C++20 библиотека, реализующая персистентную -JSON-базу данных поверх PMM. Текущая архитектура имеет 4 уровня: - -``` -Уровень D: pjson_db_pmm (высокоуровневый API: path-навигация, $ref, метрики) -Уровень C: pjson_node + codec (модель узлов, парсинг/сериализация JSON) -Уровень B: Персистентные примитивы (pstringview_pmm, pstring_pmm, pvector_pmm, pmap_pmm, pmem_array_pmm) -Уровень A: Адаптер PMM (pam_adapter.h, pam_pmm.h, pam_pmm_config.h, fptr_pmm.h, pallocator_pmm.h) -``` - -### Проблема - -Уровни A и B содержат значительный объём кода (~2500 строк), дублирующего или -оборачивающего функциональность, которая должна предоставляться самим pmm. -Это приводит к: - -- **Дублированию кода** — `pstring_pmm`, `pmem_array_pmm`, `pmap_pmm`, `pallocator_pmm` - повторяют логику, которую pmm мог бы предоставить -- **Несогласованности API** — BinDiffSynchronizer работает с байтовыми смещениями (`uintptr_t`), - а pmm — с гранульными индексами, требуя постоянных конверсий -- **Сложности поддержки** — два набора персистентных контейнеров с разными API и гарантиями - ---- - -## Предварительные условия - -Перед началом миграции BinDiffSynchronizer должны быть выполнены следующие задачи -из [Фазы 3 плана pmm](plan.md): - -| Задача pmm | Заменяет в BinDiffSynchronizer | -|------------|-------------------------------| -| 3.1 `pstring` | `pstring_pmm` | -| 3.2 `parray` | `pmem_array_pmm` | -| 3.3 [pmap::erase()](../../include/pmm/pmap.h#pmm-pmap-erase) + итератор | `pmap_pmm` (частично) | -| 3.5 `pallocator` | `pallocator_pmm` | -| 3.6 `ppool` | `pjson_pool_pmm` | -| 3.7 Корневой объект | `pam_pmm_root` в `pam_pmm.h` | -| 4.4 Конверсия pptr ↔ байтовые смещения | `pam_adapter.h` | - ---- - -## Этап 1: Миграция уровня A (адаптер PMM) - -### 1.1 Удаление pam_adapter.h - -**Текущая ситуация:** `pam_adapter.h` конвертирует между `pptr` (гранульные индексы) -и `uintptr_t` (байтовые смещения). - -**После pmm 4.4:** pmm будет предоставлять [pptr::byte_offset()](../../include/pmm/pptr.h#pmm-pptr-byte_offset) и -`PersistMemoryManager::pptr_from_byte_offset()`. - -**Действия:** -- Заменить все вызовы `pptr_to_offset()` / `offset_to_pptr()` на новые методы pmm -- Удалить `pam_adapter.h` -- Обновить все файлы, использующие конверсию - -### 1.2 Упрощение pam_pmm.h — использование корневого объекта pmm - -**Текущая ситуация:** `pam_pmm.h` хранит `pam_pmm_root` с magic/version/registry_offset -в отдельном блоке. При инициализации ищет его в ПАП. - -**После pmm 3.7:** pmm будет предоставлять `set_root()` / `get_root()`. - -**Действия:** -- `pam_pmm_root` → хранить через `Mgr::set_root(root_pptr)` вместо ручного поиска -- Упростить `pam_pmm_init()` — загрузка корня через `Mgr::get_root()` -- Удалить magic-number поиск по ПАП - -### 1.3 Удаление pallocator_pmm.h - -**После pmm 3.5:** pmm будет предоставлять `pmm::pallocator`. - -**Действия:** -- Заменить `pjson::pallocator_pmm` на `pmm::pallocator` -- Удалить `pallocator_pmm.h` - -### 1.4 Упрощение fptr_pmm.h - -**Текущая ситуация:** `fptr_pmm` хранит байтовое смещение и предоставляет -`operator*`, `operator->`, `operator[]`, `New()`, `Delete()`. - -**Действия:** -- Рассмотреть замену `fptr_pmm` на `pptr` с именованным реестром -- Если API `fptr` критичен для совместимости — оставить как тонкую обёртку над `pptr` - ---- - -## Этап 2: Миграция уровня B (персистентные примитивы) - -### 2.1 Замена pstring_pmm на pmm::pstring - -**Текущая ситуация:** `pstring_pmm` — мутабельная строка с `assign()`, `c_str()`, `clear()`. - -**После pmm 3.1:** pmm будет предоставлять `pstring` с аналогичным API. - -**Действия:** -- Заменить `pstring_pmm` на `Mgr::pstring` (вложенный alias в PersistMemoryManager) -- Обновить `pjson_node` — тип `node_tag::string` использует [pstring](../../include/pmm/pstring.h#pmm-pstring) вместо `pstring_pmm` -- Удалить `pstring_pmm.h` - -### 2.2 Замена pmem_array_pmm на pmm::parray - -**Текущая ситуация:** `pmem_array_pmm` — низкоуровневый массив с заголовком -`[size | capacity | data_offset]`. - -**После pmm 3.2:** pmm будет предоставлять `parray` с тем же API. - -**Действия:** -- Заменить все вызовы `pmem_array_pmm_*` на методы `parray` -- Обновить `pvector_pmm` — делегировать к `parray` вместо `pmem_array_pmm` -- Обновить `pmap_pmm` — использовать `parray>` для хранения -- Удалить `pmem_array_pmm.h` - -### 2.3 Миграция pmap_pmm - -**Текущая ситуация:** `pmap_pmm` — ассоциативный контейнер на сортированном массиве. - -**Варианты:** - -**Вариант A:** Заменить на pmm `pmap` (AVL-дерево) -- Плюсы: единый API, O(log n) все операции, не нужна переаллокация при вставке -- Минусы: хуже кэш-локальность для итерации, больше накладных расходов на узел - -**Вариант B:** Оставить как `parray>` с бинарным поиском -- Плюсы: лучше кэш-локальность, проще код -- Минусы: O(n) вставка/удаление - -**Рекомендация:** Вариант A для больших коллекций (реестр объектов), Вариант B для -маленьких коллекций (JSON-объекты с <100 ключей). - -### 2.4 Замена pjson_pool_pmm на pmm::ppool - -**После pmm 3.6:** pmm будет предоставлять `ppool`. - -**Действия:** -- Заменить `pjson_pool_pmm` на `pmm::ppool` -- Обновить `pjson_node` — аллокация/деаллокация через `ppool` -- Удалить `pjson_pool_pmm.h` - -### 2.5 Упрощение pstringview_pmm - -**Текущая ситуация:** `pstringview_pmm` кэширует `length` и `chars_offset` и оборачивает -[pmm::pstringview](../../include/pmm/pstringview.h#pmm-pstringview). - -**Действия:** -- Оценить, можно ли использовать [pmm::pstringview](../../include/pmm/pstringview.h#pmm-pstringview) напрямую -- Если кэширование необходимо для производительности — оставить как тонкую обёртку -- Удалить дублирование логики интернирования - ---- - -## Этап 3: Обновление уровней C и D - -### 3.1 Обновление pjson_node - -**Действия:** -- Обновить union в `pjson_node` для использования типов pmm: - - `node_tag::string` → [pmm::pstring](../../include/pmm/pstring.h#pmm-pstring) вместо `pstring_pmm` - - `node_tag::array` → `pmm::parray` вместо `pvector_pmm` - - `node_tag::object` → `pmm::pmap` или `pmm::parray` вместо `pmap_pmm` - - `node_tag::binary` → `pmm::parray` вместо `pvector_pmm` -- Обеспечить, что все типы остаются POD (trivially copyable) - -### 3.2 Обновление pjson_codec - -**Действия:** -- Обновить парсер/сериализатор для работы с новыми типами pmm -- Проверить, что Base64 кодирование/декодирование работает с `parray` - -### 3.3 Обновление pjson_db_pmm (публичный API) - -**Действия:** -- Обновить `put()`, `get()`, `erase()` для работы с новыми типами -- Обновить `batch_begin()`/`batch_end()` — совместимость с `ppool` -- Обновить метрики — использовать `Mgr::get_stats()` вместо ручного подсчёта - ---- - -## Этап 4: Тестирование и валидация - -### 4.1 Регрессионное тестирование - -- Все существующие тесты BinDiffSynchronizer должны проходить после миграции -- Дополнительные тесты на граничные случаи новых типов pmm - -### 4.2 Тестирование совместимости - -- Проверить save/load совместимость с существующими файлами БД -- Определить стратегию миграции для существующих данных (если формат изменился) - -### 4.3 Бенчмарки - -- Сравнить производительность до и после миграции -- Измерить: вставка JSON-объектов, поиск по пути, итерация, save/load - ---- - -## Ожидаемые результаты - -### Количественные - -| Метрика | До | После | -|---------|-----|-------| -| Файлы в уровне A | 5 | 1–2 | -| Файлы в уровне B | 5 | 1 (pstringview_pmm wrapper) | -| Строки кода (уровни A+B) | ~2500 | ~300 | -| Собственные персистентные типы | 7 | 1–2 (тонкие обёртки) | - -### Качественные - -- **Единый API** — все персистентные типы предоставляются pmm -- **Меньше кода для поддержки** — основная логика в pmm, BinDiffSynchronizer фокусируется на JSON -- **Лучшая тестируемость** — типы pmm тестируются отдельно -- **Согласованность** — единые гарантии потокобезопасности, персистентности, управления памятью -- **Проще интеграция** — новые проекты на базе pmm могут переиспользовать те же типы - ---- - -## Порядок выполнения - -``` -pmm Фаза 3 (параллельно с планированием миграции) - │ - ├── 3.1 pstring ──────────── Этап 2.1 (замена pstring_pmm) - ├── 3.2 parray ──────────── Этап 2.2 (замена pmem_array_pmm) - ├── 3.3 pmap::erase ─────────── Этап 2.3 (миграция pmap_pmm) - ├── 3.5 pallocator ─────────── Этап 1.3 (замена pallocator_pmm) - ├── 3.6 ppool ──────────── Этап 2.4 (замена pjson_pool_pmm) - ├── 3.7 root object ─────────── Этап 1.2 (упрощение pam_pmm) - │ - └── pmm 4.4 byte offset ─────── Этап 1.1 (удаление pam_adapter) - │ - Этап 3 (обновление pjson_node, codec, db) - │ - Этап 4 (тестирование и валидация) -``` - -Этапы 1 и 2 можно выполнять параллельно по мере готовности соответствующих типов в pmm. -Этап 3 выполняется после завершения этапов 1 и 2. -Этап 4 — финальная валидация. - ---- - -*Документ создан 2026-03-19 на основе анализа BinDiffSynchronizer и плана pmm Фазы 3.* diff --git a/docs/deletion_policy.md b/docs/deletion_policy.md index 48f537c6..b20b6683 100644 --- a/docs/deletion_policy.md +++ b/docs/deletion_policy.md @@ -37,23 +37,26 @@ - Старое расположение не сохраняется (ни symlink, ни redirect). - После перемещения статус становится `keep`. -### `archive` — справочный материал, не участвующий в навигации +### `recover-into-req` — план/идея, требующая фиксации как требование -Элемент имеет историческую ценность, но не является частью канонической -документации и не участвует в основном маршруте чтения. +Элемент описывает будущую или опциональную возможность системы, либо проработку +дизайна, и должен быть зафиксирован в каталоге требований [`req/`](../req/) как +черновое `feat-*`/`fr-*`/`qa-*` со статусом `Draft` (или `Could`/`Won't` в +зависимости от приоритета). **Правила:** -- Архивные документы перемещаются в `docs/archive/`. -- Из канонических docs и README на них не ведут ссылки. -- Они не обновляются при изменении API. -- При дальнейшей компактификации могут быть удалены, если их содержание - полностью покрыто каноническими документами. - -**Когда элемент архивируется:** -- Описывает историческую фазу развития проекта. -- Пересекается с каноническим документом, но содержит дополнительный контекст. -- Является планом или анализом, чьи выводы уже реализованы. -- Является спецификацией внешнего проекта. +- Содержание phase-плана / design-документа переосмысляется автором PR. +- В `req/` добавляются новые записи со ссылкой на исходный issue/контекст. +- Исходный документ удаляется из рабочего дерева (история остаётся в Git). + +**Когда применять:** +- Фазовый план будущей работы (Phase 7.x, backlog). +- Проработка вариантов опциональной надстройки (encryption, compression). +- Target-model заметка, дублирующая активный документ. + +Каталог `docs/archive/` упразднён в Issue #382 как промежуточная форма этой +дисциплины: вся ценная содержательная часть мигрирована в `req/`, остальное +удалено. ### `delete` — не должен оставаться в репозитории diff --git a/docs/index.md b/docs/index.md index 460663bf..0a0dca25 100644 --- a/docs/index.md +++ b/docs/index.md @@ -37,11 +37,6 @@ Single entry point for PMM documentation. The canonical set below must match | [Internal Structure](internal_structure.md) | Supporting implementation structure notes | | [Test Matrix](test_matrix.md) | Supporting test coverage matrix | -## Archive - -Historical, phase, and planning documents are preserved in [`archive/`](archive/) for reference. -They do not participate in the official reading path. - ## Reading Order For newcomers, the recommended path is: diff --git a/docs/repository_shape.md b/docs/repository_shape.md index cbd9e5ba..d929c69b 100644 --- a/docs/repository_shape.md +++ b/docs/repository_shape.md @@ -186,20 +186,13 @@ registry. - `comment_policy.md` — дисциплина текстовой поверхности - `index.md` — единая точка входа в документацию -#### Archive (`docs/archive/`) - -- `PMM_AVL_Forest_Concept.md` — пересекается с `pmm_avl_forest.md` -- `avl_forest_analysis_ru.md` — исторический анализ -- `demo.md` — ТЗ на визуальное демо -- `phase1_safety.md` — фазовый документ -- `phase2_persistence.md` — фазовый документ -- `phase3_types.md` — фазовый документ -- `phase4_api.md` — фазовый документ -- `phase5_testing.md` — фазовый документ -- `phase6_documentation.md` — фазовый документ -- `phase7_4_encryption_compression.md` — фазовый документ -- `plan.md` — исторический план -- `plan4BinDiffSynchronizer.md` — план внешнего проекта +Каталог `docs/archive/` удалён в Issue #382. Содержимое расформировано: +ценные phase-планы и проработки опциональных надстроек переосмыслены в каталог +требований [`req/`](../req/) (черновые `feat-*`/`fr-*` со статусами `Draft`/`Could`, +например encryption — [feat-011](../req/04_features.md#feat-011), transactions — +[feat-012](../req/04_features.md#feat-012), GC — [feat-013](../req/04_features.md#feat-013), +shared memory IPC — [feat-014](../req/04_features.md#feat-014)); материалы, уже +покрытые активными документами или реализованным кодом, удалены без переноса. ## Out of scope diff --git a/include/pmm/pptr.h b/include/pmm/pptr.h index 81c74da4..9ca76260 100644 --- a/include/pmm/pptr.h +++ b/include/pmm/pptr.h @@ -49,7 +49,7 @@ class pptr constexpr index_type offset() const noexcept { return _idx; } /* ### pmm-pptr-byte_offset -req: dr-007, qa-port-001 +req: dr-007, qa-port-001, fr-035, if-012 */ constexpr size_t byte_offset() const noexcept { diff --git a/include/pmm/typed_manager_api.h b/include/pmm/typed_manager_api.h index 7ee58b3e..725c586a 100644 --- a/include/pmm/typed_manager_api.h +++ b/include/pmm/typed_manager_api.h @@ -295,6 +295,10 @@ req: dr-019, fr-005, fr-006, fr-029, rule-007, ur-002 T* base_elem = resolve_checked( p ); return ( base_elem == nullptr ) ? nullptr : base_elem + i; } +/* +#### pmm-detail-persistmemorytypedapi-pptr_from_byte_offset +req: fr-035, if-012 +*/ template static pmm::pptr pptr_from_byte_offset( size_t byte_off ) noexcept { using address_traits = typename ManagerT::address_traits; diff --git a/req/04_features.md b/req/04_features.md index 118f60ae..1e42484b 100644 --- a/req/04_features.md +++ b/req/04_features.md @@ -138,3 +138,47 @@ - [qa-diag-001](08_quality_attributes.md#qa-diag-001) - [pmm-verifyresult](../include/pmm/diagnostics.h#pmm-verifyresult) - [pmm-pmmerror](../include/pmm/types.h#pmm-pmmerror) + +## feat-011 + +- **Характеристика:** Optional per-block encryption и image compression как самостоятельные политики (`EncryptionPolicyT`, compression-уровня политика) без обязательной зависимости ядра. +- **Приоритет:** Could +- **Статус:** Draft +- **Tracking issue:** #239 +- **Реализует:** [br-003](01_business_requirements.md#br-003) +- **Реализуется в:** + - [fr-036](05_functional_requirements.md#fr-036), [fr-037](05_functional_requirements.md#fr-037) + - [qa-sec-001](08_quality_attributes.md#qa-sec-001) + - [asm-007](11_assumptions_dependencies.md#asm-007) +- **Примечания:** Рекомендованный вариант — per-block AES-256-CTR (вариант B из проработки #239). Полное шифрование/сжатие образа возможно как опциональная надстройка для offline-бэкапов. Компрессия применяется до шифрования. + +## feat-012 + +- **Характеристика:** Транзакционная семантика над persistent storage: redo/undo log, `begin_transaction()` / `commit()` / `rollback()`. +- **Приоритет:** Could +- **Статус:** Draft +- **Реализует:** [br-003](01_business_requirements.md#br-003) +- **Реализуется в:** + - [fr-038](05_functional_requirements.md#fr-038) +- **Примечания:** Phase 7.1 из дорожной карты PMM. Точная модель журнала ещё не зафиксирована. + +## feat-013 + +- **Характеристика:** Сборщик мусора (Mark & Sweep) с регистрируемыми пользователем корневыми указателями; опционально `shared_pptr` через reference counting. +- **Приоритет:** Could +- **Статус:** Draft +- **Реализует:** [br-004](01_business_requirements.md#br-004) +- **Реализуется в:** + - [fr-039](05_functional_requirements.md#fr-039) +- **Примечания:** Phase 7.2 из дорожной карты PMM. Альтернативы (manual deallocate, RAII через `make_guard`) остаются основным способом управления временем жизни. + +## feat-014 + +- **Характеристика:** Storage backend для shared memory IPC (POSIX `shm_open` / Windows `CreateFileMapping`) с межпроцессной синхронизацией. +- **Приоритет:** Could +- **Статус:** Draft +- **Реализует:** [br-002](01_business_requirements.md#br-002) +- **Реализуется в:** + - [fr-040](05_functional_requirements.md#fr-040) + - [if-013](07_external_interfaces.md#if-013) +- **Примечания:** Phase 7.3 из дорожной карты PMM. Адресная независимость уже обеспечена через гранульные индексы, нужна только новая реализация storage backend concept. diff --git a/req/05_functional_requirements.md b/req/05_functional_requirements.md index 90117bbf..2d8b9c13 100644 --- a/req/05_functional_requirements.md +++ b/req/05_functional_requirements.md @@ -363,3 +363,67 @@ - **Реализует:** [rule-005](02_business_rules.md#rule-005), [con-010](09_constraints.md#con-010) - **Реализуется в:** - [scripts/check-source-loc-budget.sh](../scripts/check-source-loc-budget.sh), [scripts/source-loc-baseline.txt](../scripts/source-loc-baseline.txt) + +## fr-035 + +- **Требование:** `pptr::byte_offset()` и `PersistMemoryManager::pptr_from_byte_offset(byte_off)` должны давать round-trip конверсию между persistent index и байтовым смещением: `byte_offset()` возвращает `offset() * granule_size` (0 для null), а `pptr_from_byte_offset` возвращает null на 0, выставляет `PmmError::InvalidPointer` при несовпадении granule alignment и `PmmError::Overflow` при превышении `index_type`. +- **Приоритет:** Should +- **Статус:** Recovered +- **Основание:** Issue #211, публичные API в `include/pmm/pptr.h` и `include/pmm/typed_manager_api.h` +- **Реализует:** [feat-003](04_features.md#feat-003), [if-012](07_external_interfaces.md#if-012) +- **Реализуется в:** + - [pmm-pptr-byte_offset](../include/pmm/pptr.h#pmm-pptr-byte_offset) + - [pmm-detail-persistmemorytypedapi-pptr_from_byte_offset](../include/pmm/typed_manager_api.h#pmm-detail-persistmemorytypedapi-pptr_from_byte_offset) +- **Проверяется в:** [ac-013](12_acceptance_criteria.md#ac-013), [test_issue211_byte_offset.cpp](../tests/test_issue211_byte_offset.cpp) + +## fr-036 + +- **Требование:** PMM должен поддерживать опциональную политику шифрования (`EncryptionPolicyT`) уровня содержимого блоков (per-block AES-256-CTR с IV, производным от master key и granule index блока); политика-по-умолчанию `NoEncryption` обеспечивает zero overhead. `ManagerHeader` хранит идентификатор алгоритма (`encryption_algo`) для проверки совместимости при загрузке; загрузка несовместимого алгоритма отвергается через `PmmError`. +- **Приоритет:** Could +- **Статус:** Draft +- **Tracking issue:** #239 +- **Основание:** Issue #239 (проработка вариантов сжатия и шифрования образов ПАП) +- **Реализует:** [feat-011](04_features.md#feat-011), [qa-sec-001](08_quality_attributes.md#qa-sec-001) +- **Связано с:** [asm-007](11_assumptions_dependencies.md#asm-007) +- **Примечания:** Headers блоков и `ManagerHeader` остаются открытыми (вариант B из проработки #239), что сохраняет совместимость с частичным обновлением, `MMapStorage` и binary-diff потребителями. Меры митигации утечек метаданных (padding, dummy-блоки) обсуждаются отдельно. + +## fr-037 + +- **Требование:** PMM должен поддерживать опциональную compression-политику для image save/load (LZ4 по умолчанию, zstd опционально). Сжатие применяется до шифрования; для online-сценариев применяется per-block сжатие, для offline-бэкапов — сжатие целого образа. +- **Приоритет:** Could +- **Статус:** Draft +- **Tracking issue:** #239 +- **Основание:** Issue #239 (проработка вариантов сжатия и шифрования образов ПАП) +- **Реализует:** [feat-011](04_features.md#feat-011) +- **Связано с:** [fr-036](#fr-036) + +## fr-038 + +- **Требование:** PMM может предоставлять транзакционный API (`begin_transaction()`, `commit()`, `rollback()`) с redo- или undo-журналом, обеспечивающий атомарность группы операций над persistent storage. +- **Приоритет:** Could +- **Статус:** Draft +- **Основание:** Дорожная карта PMM, Phase 7.1 (recovered в рамках Issue #382) +- **Реализует:** [feat-012](04_features.md#feat-012) +- **Связано с:** [qa-rec-001](08_quality_attributes.md#qa-rec-001) +- **Примечания:** Конкретная модель журнала (redo vs undo, фиксированный vs ring buffer) не выбрана; текущий API остаётся не-транзакционным. + +## fr-039 + +- **Требование:** PMM может предоставлять Mark & Sweep сборщик мусора с пользовательскими корневыми указателями и опциональным `shared_pptr` для reference counting; недостижимые блоки освобождаются через `collect()`. +- **Приоритет:** Could +- **Статус:** Draft +- **Основание:** Дорожная карта PMM, Phase 7.2 (recovered в рамках Issue #382) +- **Реализует:** [feat-013](04_features.md#feat-013) +- **Связано с:** [qa-mem-001](08_quality_attributes.md#qa-mem-001) +- **Примечания:** Альтернатива — оставить управление временем жизни ручным/RAII (`make_guard`, явный `deallocate_typed`). + +## fr-040 + +- **Требование:** PMM может предоставлять storage backend для shared memory (POSIX `shm_open` + `mmap`, Windows `CreateFileMapping`), реализующий тот же storage backend concept, что и `HeapStorage`/`StaticStorage`/`MMapStorage`, с межпроцессной синхронизацией. +- **Приоритет:** Could +- **Статус:** Draft +- **Основание:** Дорожная карта PMM, Phase 7.3 (recovered в рамках Issue #382) +- **Реализует:** [feat-014](04_features.md#feat-014) +- **Реализуется в:** + - [if-013](07_external_interfaces.md#if-013) +- **Примечания:** Адресная независимость уже обеспечена гранульными индексами; для IPC требуется только новая реализация backend и межпроцессный lock policy. diff --git a/req/07_external_interfaces.md b/req/07_external_interfaces.md index b093afb7..06f6cbd9 100644 --- a/req/07_external_interfaces.md +++ b/req/07_external_interfaces.md @@ -119,3 +119,24 @@ - **Основание:** README - **Реализуется в:** - [demo/](../demo/), [dep-002](11_assumptions_dependencies.md#dep-002) + +## if-012 + +- **Требование:** Public API должен экспонировать `pptr::byte_offset()` и `PersistMemoryManager::pptr_from_byte_offset(byte_off)` как documented round-trip для интеграции с external storage layers; ошибки сигнализируются через `PmmError::InvalidPointer` (misalignment) и `PmmError::Overflow` (overflow). +- **Приоритет:** Should +- **Статус:** Recovered +- **Основание:** Issue #211, README API +- **Реализует:** [fr-035](05_functional_requirements.md#fr-035), [feat-003](04_features.md#feat-003) +- **Реализуется в:** + - [pmm-pptr-byte_offset](../include/pmm/pptr.h#pmm-pptr-byte_offset) + - [pmm-detail-persistmemorytypedapi-pptr_from_byte_offset](../include/pmm/typed_manager_api.h#pmm-detail-persistmemorytypedapi-pptr_from_byte_offset) +- **Проверяется в:** [ac-013](12_acceptance_criteria.md#ac-013), [test_issue211_byte_offset.cpp](../tests/test_issue211_byte_offset.cpp) + +## if-013 + +- **Требование:** Опциональный shared memory storage backend должен удовлетворять тому же storage backend concept, что и `HeapStorage`/`StaticStorage`/`MMapStorage`, и предоставлять межпроцессный доступ к persistent image через POSIX `shm_open` (Linux/macOS) или `CreateFileMapping` (Windows). +- **Приоритет:** Could +- **Статус:** Draft +- **Основание:** Дорожная карта PMM, Phase 7.3 (recovered в рамках Issue #382) +- **Реализует:** [feat-014](04_features.md#feat-014), [fr-040](05_functional_requirements.md#fr-040) +- **Связано с:** [qa-thread-001](08_quality_attributes.md#qa-thread-001) diff --git a/req/08_quality_attributes.md b/req/08_quality_attributes.md index c499f3d8..8187cbd6 100644 --- a/req/08_quality_attributes.md +++ b/req/08_quality_attributes.md @@ -172,3 +172,16 @@ - **Реализуется в:** - [tests/CMakeLists.txt](../tests/CMakeLists.txt), [CMakeLists.txt](../CMakeLists.txt) - **Проверяется в:** [ac-012](12_acceptance_criteria.md#ac-012) + +## qa-sec-001 + +- **Атрибут:** При активной политике per-block encryption содержимое блоков данных в persistent image должно быть зашифровано (read-back через `save_manager` без ключа не должен давать plaintext). Headers блоков и `ManagerHeader` остаются открытыми для совместимости с частичным обновлением и diff-потребителями. +- **Категория:** Security / Confidentiality +- **Приоритет:** Could +- **Статус:** Draft +- **Tracking issue:** #239 +- **Основание:** Issue #239 (проработка вариантов сжатия и шифрования образов ПАП) +- **Реализует:** [feat-011](04_features.md#feat-011) +- **Реализуется в:** + - [fr-036](05_functional_requirements.md#fr-036) +- **Примечания:** Уровень конфиденциальности соответствует варианту B из проработки #239: данные блоков защищены, метаданные структуры — нет. Полное шифрование образа возможно как опциональная offline-надстройка (`save_manager_full_encrypted` / `load_manager_full_encrypted`). diff --git a/req/11_assumptions_dependencies.md b/req/11_assumptions_dependencies.md index 2e51759f..5d85a254 100644 --- a/req/11_assumptions_dependencies.md +++ b/req/11_assumptions_dependencies.md @@ -54,6 +54,15 @@ - **Основание:** [docs/pmm_target_model.md](../docs/pmm_target_model.md), [docs/validation_model.md](../docs/validation_model.md) - **Связано с:** [rule-002](02_business_rules.md#rule-002), [sys-002](10_system_requirements.md#sys-002) +## asm-007 + +- **Предположение:** Если активируется политика шифрования образов ПАП, управление мастер-ключом (генерация, ротация, secure storage, доставка) выполняется клиентским кодом — PMM не управляет ключевым материалом и не предоставляет key derivation/storage. +- **Приоритет:** Could +- **Статус:** Draft +- **Tracking issue:** #239 +- **Основание:** Issue #239 (проработка вариантов сжатия и шифрования образов ПАП) +- **Связано с:** [feat-011](04_features.md#feat-011), [fr-036](05_functional_requirements.md#fr-036), [qa-sec-001](08_quality_attributes.md#qa-sec-001) + ### Зависимости ## dep-001 diff --git a/req/12_acceptance_criteria.md b/req/12_acceptance_criteria.md index 4422f6e1..a1d075d0 100644 --- a/req/12_acceptance_criteria.md +++ b/req/12_acceptance_criteria.md @@ -111,3 +111,12 @@ - **Основание:** README - **Проверяет:** [con-002](09_constraints.md#con-002), [qa-test-001](08_quality_attributes.md#qa-test-001) - **Тесты:** [tests/CMakeLists.txt](../tests/CMakeLists.txt), [CMakeLists.txt](../CMakeLists.txt) + +## ac-013 + +- **Критерий:** Round-trip конверсия `pptr → byte_offset() → pptr_from_byte_offset` возвращает исходный pptr для валидных значений; misaligned byte offsets возвращают null + `PmmError::InvalidPointer`; overflowing offsets возвращают null + `PmmError::Overflow`. +- **Приоритет:** Should +- **Статус:** Recovered +- **Основание:** Issue #211 +- **Проверяет:** [fr-035](05_functional_requirements.md#fr-035), [if-012](07_external_interfaces.md#if-012) +- **Тесты:** [test_issue211_byte_offset.cpp](../tests/test_issue211_byte_offset.cpp) diff --git a/tests/fuzz_allocator.cpp b/tests/fuzz_allocator.cpp index 593b0c93..75dd1fb2 100644 --- a/tests/fuzz_allocator.cpp +++ b/tests/fuzz_allocator.cpp @@ -16,7 +16,7 @@ * byte 0: command (0=alloc, 1=dealloc, 2=reallocate) * byte 1-2: size parameter (little-endian uint16_t) * - * @see docs/phase5_testing.md §5.2 + * @see Issue #213 (extended test coverage), req/qa-test-001 * @version 0.1 */ diff --git a/tests/test_issue211_byte_offset.cpp b/tests/test_issue211_byte_offset.cpp index 57773f95..03a806ca 100644 --- a/tests/test_issue211_byte_offset.cpp +++ b/tests/test_issue211_byte_offset.cpp @@ -1,3 +1,8 @@ +/* +## test-issue211-byte-offset +req: fr-035, if-012, ac-013 +*/ + /** * @file test_issue211_byte_offset.cpp * @brief Tests for pptr ↔ byte offset conversion. diff --git a/tests/test_issue213_concurrent.cpp b/tests/test_issue213_concurrent.cpp index 67f682f7..feef37a2 100644 --- a/tests/test_issue213_concurrent.cpp +++ b/tests/test_issue213_concurrent.cpp @@ -9,7 +9,7 @@ * - High contention with many threads * - Concurrent operations on typed containers (pstring, parray, pmap) * - * @see docs/phase5_testing.md §5.2 + * @see Issue #213 (extended test coverage), req/qa-thread-001, req/qa-test-001 * @version 0.1 */ diff --git a/tests/test_issue213_fuzz.cpp b/tests/test_issue213_fuzz.cpp index 6fdaf3a7..aa9972c9 100644 --- a/tests/test_issue213_fuzz.cpp +++ b/tests/test_issue213_fuzz.cpp @@ -13,7 +13,7 @@ * For true coverage-guided fuzzing, a libFuzzer harness is provided * in tests/fuzz_allocator.cpp (built separately with -fsanitize=fuzzer). * - * @see docs/phase5_testing.md §5.2 + * @see Issue #213 (extended test coverage), req/qa-test-001 * @version 0.1 */ diff --git a/tests/test_issue213_overflow.cpp b/tests/test_issue213_overflow.cpp index a44161be..f70c8034 100644 --- a/tests/test_issue213_overflow.cpp +++ b/tests/test_issue213_overflow.cpp @@ -6,7 +6,7 @@ * in granule computations, allocation size calculations, and edge cases * across all AddressTraits variants (16-bit, 32-bit, 64-bit indexes). * - * @see docs/phase5_testing.md §5.2 + * @see Issue #213 (extended test coverage), req/fr-021, req/fr-023 * @version 0.1 */ diff --git a/tests/test_issue43_phase1_safety.cpp b/tests/test_issue43_phase1_safety.cpp index e20ef434..350bd388 100644 --- a/tests/test_issue43_phase1_safety.cpp +++ b/tests/test_issue43_phase1_safety.cpp @@ -8,7 +8,7 @@ * - 1.3 Overflow protection in allocator_policy and allocate() * - 1.4 Runtime checks in cast_from_raw (FreeBlock, AllocatedBlock) * - * @see docs/phase1_safety.md + * @see Issue #43 (Phase 1 safety), req/fr-006, req/qa-rel-001 * @version 0.1 */ diff --git a/tests/test_issue43_phase2_persistence.cpp b/tests/test_issue43_phase2_persistence.cpp index 4cdf5c12..d3c20d7d 100644 --- a/tests/test_issue43_phase2_persistence.cpp +++ b/tests/test_issue43_phase2_persistence.cpp @@ -7,7 +7,7 @@ * - 2.2 Atomic save (write-then-rename via temporary file) * - 2.3 MMapStorage expand() support * - * @see docs/phase2_persistence.md + * @see Issue #43 (Phase 2 persistence), req/fr-014, req/fr-016, req/qa-rec-001 * @version 0.1 */ From 47ab493b82f207828a061fdc8f4fadd93e5f4c67 Mon Sep 17 00:00:00 2001 From: konard Date: Mon, 11 May 2026 08:01:22 +0000 Subject: [PATCH 3/5] =?UTF-8?q?issue-382:=20fix=20CI=20=E2=80=94=20regener?= =?UTF-8?q?ate=20single-header,=20bump=20LOC=20baseline,=20add=20changelog?= =?UTF-8?q?=20fragment,=20sync=20README=20badge?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Regenerate single_include/pmm/pmm.h via scripts/generate-single-headers.sh to incorporate the new req: anchor lines added in include/pmm/pptr.h and include/pmm/typed_manager_api.h. Lines: 6352 → 6356 (doc-only growth). - Bump scripts/source-loc-baseline.txt 6352 → 6356 to match the regenerated single-header. No production code added — the 4 extra lines are anchor metadata for the recovered byte-offset API requirement (fr-035, if-012). - Add changelog.d/20260511_080000_issue382_recover_archive_requirements.md documenting the archive recovery, new req entries, and CI-only changes. - Bump README.md version badge 5.0.1 → 6.2.1 to satisfy release-owned version-consistency: this docs PR must touch README.md to drop the docs/archive/ reference, which triggers the release-owned check; the badge was already stale on main so it is bumped in sync with CMakeLists.txt and CHANGELOG.md. --- README.md | 2 +- ...0_issue382_recover_archive_requirements.md | 36 +++++++++++++++++++ scripts/source-loc-baseline.txt | 2 +- single_include/pmm/pmm.h | 6 +++- 4 files changed, 43 insertions(+), 3 deletions(-) create mode 100644 changelog.d/20260511_080000_issue382_recover_archive_requirements.md diff --git a/README.md b/README.md index 6c7cffbe..ded5f146 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ AVL-based allocator, проверкой структуры и восстанов [![CI](https://github.com/netkeep80/PersistMemoryManager/actions/workflows/ci.yml/badge.svg)](https://github.com/netkeep80/PersistMemoryManager/actions/workflows/ci.yml) [![License: Unlicense](https://img.shields.io/badge/license-Unlicense-blue.svg)](LICENSE) [![C++20](https://img.shields.io/badge/C%2B%2B-20-blue.svg)](https://isocpp.org/std/the-standard) -[![Version](https://img.shields.io/badge/version-5.0.1-green.svg)](CHANGELOG.md) +[![Version](https://img.shields.io/badge/version-6.2.1-green.svg)](CHANGELOG.md) ## Что это diff --git a/changelog.d/20260511_080000_issue382_recover_archive_requirements.md b/changelog.d/20260511_080000_issue382_recover_archive_requirements.md new file mode 100644 index 00000000..fa0a6df6 --- /dev/null +++ b/changelog.d/20260511_080000_issue382_recover_archive_requirements.md @@ -0,0 +1,36 @@ +--- +bump: patch +--- + +### Added +- Recover backlog material from deleted `docs/archive/` into the `req/` + catalog as `Draft`/`Could`/`Recovered` entries: encryption/compression + (`fr-036`, `fr-037`, `feat-011`, `qa-sec-001`, `asm-007`), transactions + (`fr-038`, `feat-012`), garbage collection (`fr-039`, `feat-013`), + shared-memory backend (`fr-040`, `feat-014`, `if-013`), and the + byte-offset conversion public API (`fr-035`, `if-012`, `ac-013`). +- New code anchor `pmm-detail-persistmemorytypedapi-pptr_from_byte_offset` + in `include/pmm/typed_manager_api.h` and an extended `req:` list on + `pmm-pptr-byte_offset` in `include/pmm/pptr.h` for the recovered + byte-offset API. +- `req:` traceability block on `tests/test_issue211_byte_offset.cpp` and + `@see req/...` lines on six other tests to back-link verification to + the catalog. + +### Changed +- Bump `scripts/source-loc-baseline.txt` 6352 → 6356 to absorb the new + doc-only anchor lines in the regenerated single-header. Production + code is unchanged. +- Regenerate `single_include/pmm/pmm.h` to incorporate the new anchor + metadata. +- Refresh README badge (release-owned) to match `CMakeLists.txt` + `6.2.1`; the docs-archive cleanup unavoidably touches `README.md`, + so the version-consistency check is no longer skipped. + +### Removed +- Delete `docs/archive/` (12 historical phase plans / target-model and + backlog sketches, ~3 000 lines). Each file was audited; valuable + material moved into `req/` (see Added) or recorded as out-of-scope + in the PR body. References from `README.md`, `docs/index.md`, + `docs/repository_shape.md`, and `docs/deletion_policy.md` are + updated accordingly. diff --git a/scripts/source-loc-baseline.txt b/scripts/source-loc-baseline.txt index 8be99a45..d28a8c5a 100644 --- a/scripts/source-loc-baseline.txt +++ b/scripts/source-loc-baseline.txt @@ -1 +1 @@ -6352 +6356 diff --git a/single_include/pmm/pmm.h b/single_include/pmm/pmm.h index 8632cc46..be775ce6 100644 --- a/single_include/pmm/pmm.h +++ b/single_include/pmm/pmm.h @@ -3457,7 +3457,7 @@ class pptr constexpr index_type offset() const noexcept { return _idx; } /* ### pmm-pptr-byte_offset -req: dr-007, qa-port-001 +req: dr-007, qa-port-001, fr-035, if-012 */ constexpr size_t byte_offset() const noexcept { @@ -4540,6 +4540,10 @@ req: dr-019, fr-005, fr-006, fr-029, rule-007, ur-002 T* base_elem = resolve_checked( p ); return ( base_elem == nullptr ) ? nullptr : base_elem + i; } +/* +#### pmm-detail-persistmemorytypedapi-pptr_from_byte_offset +req: fr-035, if-012 +*/ template static pmm::pptr pptr_from_byte_offset( size_t byte_off ) noexcept { using address_traits = typename ManagerT::address_traits; From 52702074e29727a86f4226d7c87b8e6ef4a6a15a Mon Sep 17 00:00:00 2001 From: konard Date: Mon, 11 May 2026 08:12:14 +0000 Subject: [PATCH 4/5] Revert "Initial commit with task details" This reverts commit d6d870336d1d85ee2e4103b85c87c9abc926f455. --- .gitkeep | 1 - 1 file changed, 1 deletion(-) delete mode 100644 .gitkeep diff --git a/.gitkeep b/.gitkeep deleted file mode 100644 index b36c66d3..00000000 --- a/.gitkeep +++ /dev/null @@ -1 +0,0 @@ -# .gitkeep file auto-generated at 2026-05-11T07:28:08.162Z for PR creation at branch issue-382-7ed88e193dfe for issue https://github.com/netkeep80/PersistMemoryManager/issues/382 \ No newline at end of file From 52b26a34db487703c91fbdf539e3da0d593c010a Mon Sep 17 00:00:00 2001 From: konard Date: Mon, 11 May 2026 08:55:55 +0000 Subject: [PATCH 5/5] =?UTF-8?q?issue-382:=20address=20PR=20review=20?= =?UTF-8?q?=E2=80=94=20strengthen=20fr-036=20crypto,=20mark=20demo=20spec?= =?UTF-8?q?=20Won't,=20update=20matrix?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Addresses owner review feedback on PR #383: 1. fr-036 / qa-sec-001: replace the granule-index-only IV with a unique per-encryption nonce derived from (granule_index, block_generation_counter) to prevent CTR-mode IV reuse on block rewrites (the original docs/archive/phase7_4_encryption_compression.md proposal already used a block_generation_counter — its omission was a regression in the recovered requirement). Also require authenticated encryption (AEAD AES-256-GCM, or AES-256-CTR + HMAC) so wrong-key / tampered-ciphertext / replayed-nonce loads are rejected via PmmError::AuthenticationFailed. feat-011 notes are aligned with the AEAD recommendation. 2. demo.md disposition: add feat-015 (Won't / Deprecated) explicitly marking the detailed visual demo spec (memory-map view, metrics view, structure tree, scenario manager, FPS/memory constraints) as out of PMM scope. The optional demo-target build fact remains represented via if-011 + dep-002; if-011 now references feat-015 for the deliberate scope decision. 3. req/13_traceability_matrix.md: add feature-level trace entries for feat-011…feat-015 plus a dedicated recovered byte-offset API block covering fr-035 / if-012 / ac-013. The matrix previously had no link to the new requirements. 4. changelog.d/...issue382...md: extend the fragment with the new feat-015 Won't entry, the matrix update, and the fixed fr-036 nonce/AEAD wording. All checks pass: check-requirements-catalog.py, check-requirements-traceability.py, check-docs-consistency.sh, check-include-anchor-comments.sh, check-version-consistency.sh, check-source-loc-budget.sh, check-changelog-fragment.sh. --- ...0_issue382_recover_archive_requirements.md | 18 ++++++++++ req/04_features.md | 11 +++++- req/05_functional_requirements.md | 6 ++-- req/07_external_interfaces.md | 1 + req/08_quality_attributes.md | 6 ++-- req/13_traceability_matrix.md | 35 +++++++++++++++++++ 6 files changed, 70 insertions(+), 7 deletions(-) diff --git a/changelog.d/20260511_080000_issue382_recover_archive_requirements.md b/changelog.d/20260511_080000_issue382_recover_archive_requirements.md index fa0a6df6..a1112007 100644 --- a/changelog.d/20260511_080000_issue382_recover_archive_requirements.md +++ b/changelog.d/20260511_080000_issue382_recover_archive_requirements.md @@ -16,6 +16,24 @@ bump: patch - `req:` traceability block on `tests/test_issue211_byte_offset.cpp` and `@see req/...` lines on six other tests to back-link verification to the catalog. +- Explicit `Won't`/`Deprecated` entry `feat-015` recording that the + detailed visual demo specification from `docs/archive/demo.md` + (memory-map view, metrics view, structure tree, scenario manager, + FPS/memory constraints) is intentionally out of PMM scope; the + optional demo-target and its build dependency remain represented via + `if-011` and `dep-002`. +- Feature-level trace entries for `feat-011…feat-015` and a recovered + byte-offset API block in `req/13_traceability_matrix.md` so the new + requirements are surfaced in the matrix. + +### Fixed +- Strengthen the per-block encryption policy in `fr-036` / `qa-sec-001`: + require a unique nonce per encryption via `block_generation_counter` + (recovered from the original `docs/archive/phase7_4_encryption_compression.md` + proposal) and require authenticated encryption (AEAD AES-256-GCM, or + AES-256-CTR with HMAC) so wrong-key / tampered-ciphertext / replayed-nonce + load attempts are rejected through `PmmError::AuthenticationFailed`. + Closes the CTR-mode IV-reuse gap raised in PR review. ### Changed - Bump `scripts/source-loc-baseline.txt` 6352 → 6356 to absorb the new diff --git a/req/04_features.md b/req/04_features.md index 1e42484b..4a7b4950 100644 --- a/req/04_features.md +++ b/req/04_features.md @@ -150,7 +150,7 @@ - [fr-036](05_functional_requirements.md#fr-036), [fr-037](05_functional_requirements.md#fr-037) - [qa-sec-001](08_quality_attributes.md#qa-sec-001) - [asm-007](11_assumptions_dependencies.md#asm-007) -- **Примечания:** Рекомендованный вариант — per-block AES-256-CTR (вариант B из проработки #239). Полное шифрование/сжатие образа возможно как опциональная надстройка для offline-бэкапов. Компрессия применяется до шифрования. +- **Примечания:** Рекомендованный вариант — per-block AEAD-шифр (AES-256-GCM; либо AES-256-CTR + HMAC) с уникальным nonce на каждое шифрование блока (через `block_generation_counter`), вариант B из проработки #239. Полное шифрование/сжатие образа возможно как опциональная надстройка для offline-бэкапов. Компрессия применяется до шифрования. ## feat-012 @@ -182,3 +182,12 @@ - [fr-040](05_functional_requirements.md#fr-040) - [if-013](07_external_interfaces.md#if-013) - **Примечания:** Phase 7.3 из дорожной карты PMM. Адресная независимость уже обеспечена через гранульные индексы, нужна только новая реализация storage backend concept. + +## feat-015 + +- **Характеристика:** Детальная визуальная демо-спецификация PMM (Dear ImGui + GLFW + OpenGL UI с memory map view, metrics view, structure tree view, scenario manager, многопоточные сценарии, persistence-cycle сценарий, FPS/memory constraints). +- **Приоритет:** Won't +- **Статус:** Deprecated +- **Основание:** `docs/archive/demo.md` (recovered в рамках Issue #382). Архивный документ был полноценным ТЗ визуального демо, однако в текущей PMM-зоне ответственности удерживается только факт опционального демо-таргета и его сборочных зависимостей. Полная UI-спецификация намеренно не переносится в PMM `req/` каталог, чтобы не раздувать canonical scope; если визуальное демо станет приоритетом, оно должно вернуться через отдельную feature/issue с полным набором требований. +- **Связано с:** [if-011](07_external_interfaces.md#if-011), [dep-002](11_assumptions_dependencies.md#dep-002) +- **Примечания:** Фактическое опциональное демо в репозитории (`demo/`) и его сборочный optional flag остаются представлены через [if-011](07_external_interfaces.md#if-011) и [dep-002](11_assumptions_dependencies.md#dep-002); это `Won't`-требование фиксирует только сознательный отказ от широкой UI-спецификации. diff --git a/req/05_functional_requirements.md b/req/05_functional_requirements.md index 2d8b9c13..6acd7f4a 100644 --- a/req/05_functional_requirements.md +++ b/req/05_functional_requirements.md @@ -378,14 +378,14 @@ ## fr-036 -- **Требование:** PMM должен поддерживать опциональную политику шифрования (`EncryptionPolicyT`) уровня содержимого блоков (per-block AES-256-CTR с IV, производным от master key и granule index блока); политика-по-умолчанию `NoEncryption` обеспечивает zero overhead. `ManagerHeader` хранит идентификатор алгоритма (`encryption_algo`) для проверки совместимости при загрузке; загрузка несовместимого алгоритма отвергается через `PmmError`. +- **Требование:** PMM должен поддерживать опциональную политику шифрования (`EncryptionPolicyT`) уровня содержимого блоков с аутентификацией (рекомендованный профиль — AEAD AES-256-GCM; альтернативный профиль AES-256-CTR + HMAC допустим только при эквивалентной аутентификации). Каждое шифрование блока обязано использовать уникальный nonce/IV на пару `(master_key, block)`: nonce формируется из `(granule_index, block_generation_counter)`, где `block_generation_counter` инкрементируется при каждой перезаписи блока и сохраняется в открытом per-block header — это исключает повторное использование пары `(key, IV)` при изменении содержимого того же granule. Аутентификационный тег (GMAC/HMAC) хранится рядом с шифротекстом и проверяется при загрузке; неверный ключ, повреждение шифротекста или подмена nonce должны отвергаться через `PmmError::AuthenticationFailed`. Политика-по-умолчанию `NoEncryption` обеспечивает zero overhead. `ManagerHeader` хранит идентификатор алгоритма (`encryption_algo`) и идентификатор схемы nonce/tag для проверки совместимости при загрузке; загрузка несовместимого алгоритма отвергается через `PmmError`. - **Приоритет:** Could - **Статус:** Draft - **Tracking issue:** #239 -- **Основание:** Issue #239 (проработка вариантов сжатия и шифрования образов ПАП) +- **Основание:** Issue #239 (проработка вариантов сжатия и шифрования образов ПАП), `docs/archive/phase7_4_encryption_compression.md` (recovered в рамках Issue #382 — оригинальная проработка требовала `block_generation_counter` для предотвращения повторного использования IV). - **Реализует:** [feat-011](04_features.md#feat-011), [qa-sec-001](08_quality_attributes.md#qa-sec-001) - **Связано с:** [asm-007](11_assumptions_dependencies.md#asm-007) -- **Примечания:** Headers блоков и `ManagerHeader` остаются открытыми (вариант B из проработки #239), что сохраняет совместимость с частичным обновлением, `MMapStorage` и binary-diff потребителями. Меры митигации утечек метаданных (padding, dummy-блоки) обсуждаются отдельно. +- **Примечания:** Headers блоков и `ManagerHeader` остаются открытыми (вариант B из проработки #239), что сохраняет совместимость с частичным обновлением, `MMapStorage` и binary-diff потребителями. Открытый `block_generation_counter` в per-block header допустимо, поскольку утечкой считается только метаданные, не содержимое. Меры митигации утечек метаданных (padding, dummy-блоки) обсуждаются отдельно. ## fr-037 diff --git a/req/07_external_interfaces.md b/req/07_external_interfaces.md index 06f6cbd9..d948ef9a 100644 --- a/req/07_external_interfaces.md +++ b/req/07_external_interfaces.md @@ -119,6 +119,7 @@ - **Основание:** README - **Реализуется в:** - [demo/](../demo/), [dep-002](11_assumptions_dependencies.md#dep-002) +- **Примечания:** Детальная визуальная спецификация (memory map view, metrics view, structure tree, scenario manager, многопоточные сценарии, persistence-cycle сценарий, FPS/memory constraints) из `docs/archive/demo.md` намеренно **не** перенесена в PMM `req/` каталог — см. [feat-015](04_features.md#feat-015) (`Won't`). ## if-012 diff --git a/req/08_quality_attributes.md b/req/08_quality_attributes.md index 8187cbd6..d3c6d61c 100644 --- a/req/08_quality_attributes.md +++ b/req/08_quality_attributes.md @@ -175,13 +175,13 @@ ## qa-sec-001 -- **Атрибут:** При активной политике per-block encryption содержимое блоков данных в persistent image должно быть зашифровано (read-back через `save_manager` без ключа не должен давать plaintext). Headers блоков и `ManagerHeader` остаются открытыми для совместимости с частичным обновлением и diff-потребителями. +- **Атрибут:** При активной политике per-block encryption содержимое блоков данных в persistent image должно быть зашифровано аутентифицированным шифром (AEAD AES-256-GCM или AES-256-CTR + HMAC), так чтобы read-back через `save_manager` без ключа не давал plaintext и попытка загрузки с неверным ключом, изменённым шифротекстом или повторно использованным nonce отвергалась через ошибку аутентификации (`PmmError::AuthenticationFailed`). Каждое шифрование блока обязано использовать уникальный nonce/IV (через `block_generation_counter` или сохранённый per-block nonce), что исключает CTR-mode IV reuse при изменении содержимого того же granule. Headers блоков и `ManagerHeader` остаются открытыми для совместимости с частичным обновлением и diff-потребителями. - **Категория:** Security / Confidentiality - **Приоритет:** Could - **Статус:** Draft - **Tracking issue:** #239 -- **Основание:** Issue #239 (проработка вариантов сжатия и шифрования образов ПАП) +- **Основание:** Issue #239 (проработка вариантов сжатия и шифрования образов ПАП), `docs/archive/phase7_4_encryption_compression.md` — recovered в рамках Issue #382. - **Реализует:** [feat-011](04_features.md#feat-011) - **Реализуется в:** - [fr-036](05_functional_requirements.md#fr-036) -- **Примечания:** Уровень конфиденциальности соответствует варианту B из проработки #239: данные блоков защищены, метаданные структуры — нет. Полное шифрование образа возможно как опциональная offline-надстройка (`save_manager_full_encrypted` / `load_manager_full_encrypted`). +- **Примечания:** Уровень конфиденциальности соответствует варианту B из проработки #239: данные блоков защищены и аутентифицированы, метаданные структуры (включая `block_generation_counter` в per-block header) остаются открытыми. Полное шифрование образа возможно как опциональная offline-надстройка (`save_manager_full_encrypted` / `load_manager_full_encrypted`). diff --git a/req/13_traceability_matrix.md b/req/13_traceability_matrix.md index baa48134..cc33668f 100644 --- a/req/13_traceability_matrix.md +++ b/req/13_traceability_matrix.md @@ -102,3 +102,38 @@ - **Feature:** [feat-010](04_features.md#feat-010) - **Functional/data/interface:** [fr-015](05_functional_requirements.md#fr-015), [if-010](07_external_interfaces.md#if-010) - **Quality/constraints:** [qa-diag-001](08_quality_attributes.md#qa-diag-001) + +### feat-011 +- **Feature:** [feat-011](04_features.md#feat-011) (Draft / Could, tracking #239) +- **Functional/data/interface:** [fr-036](05_functional_requirements.md#fr-036), [fr-037](05_functional_requirements.md#fr-037) +- **Quality/constraints:** [qa-sec-001](08_quality_attributes.md#qa-sec-001), [asm-007](11_assumptions_dependencies.md#asm-007) + +### feat-012 +- **Feature:** [feat-012](04_features.md#feat-012) (Draft / Could, Phase 7.1 backlog) +- **Functional/data/interface:** [fr-038](05_functional_requirements.md#fr-038) +- **Quality/constraints:** [qa-rec-001](08_quality_attributes.md#qa-rec-001) + +### feat-013 +- **Feature:** [feat-013](04_features.md#feat-013) (Draft / Could, Phase 7.2 backlog) +- **Functional/data/interface:** [fr-039](05_functional_requirements.md#fr-039) +- **Quality/constraints:** [qa-mem-001](08_quality_attributes.md#qa-mem-001) + +### feat-014 +- **Feature:** [feat-014](04_features.md#feat-014) (Draft / Could, Phase 7.3 backlog) +- **Functional/data/interface:** [fr-040](05_functional_requirements.md#fr-040), [if-013](07_external_interfaces.md#if-013) +- **Quality/constraints:** [qa-thread-001](08_quality_attributes.md#qa-thread-001) + +### feat-015 +- **Feature:** [feat-015](04_features.md#feat-015) (Won't / Deprecated — detailed visual demo spec intentionally out of PMM scope) +- **Functional/data/interface:** (none; only [if-011](07_external_interfaces.md#if-011) retains the optional-demo-target fact) +- **Quality/constraints:** [dep-002](11_assumptions_dependencies.md#dep-002) + +## Recovered byte-offset API trace + +### fr-035 / if-012 / ac-013 +- **Functional:** [fr-035](05_functional_requirements.md#fr-035) (Should / Recovered, Issue #211) +- **Interface:** [if-012](07_external_interfaces.md#if-012) +- **Feature:** [feat-003](04_features.md#feat-003) +- **Implementation anchors:** [pmm-pptr-byte_offset](../include/pmm/pptr.h#pmm-pptr-byte_offset), [pmm-detail-persistmemorytypedapi-pptr_from_byte_offset](../include/pmm/typed_manager_api.h#pmm-detail-persistmemorytypedapi-pptr_from_byte_offset) +- **Acceptance:** [ac-013](12_acceptance_criteria.md#ac-013) +- **Tests:** [tests/test_issue211_byte_offset.cpp](../tests/test_issue211_byte_offset.cpp)