From 4ddaa7bd3771777e8f82edd3904aa456f40ca741 Mon Sep 17 00:00:00 2001 From: konard Date: Sun, 22 Mar 2026 08:03:00 +0000 Subject: [PATCH 1/4] 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/BinDiffSynchronizer/issues/205 --- .gitkeep | 1 + 1 file changed, 1 insertion(+) create mode 100644 .gitkeep diff --git a/.gitkeep b/.gitkeep new file mode 100644 index 0000000..9f684c0 --- /dev/null +++ b/.gitkeep @@ -0,0 +1 @@ +# .gitkeep file auto-generated at 2026-03-22T08:03:00.572Z for PR creation at branch issue-205-3a84e21338b1 for issue https://github.com/netkeep80/BinDiffSynchronizer/issues/205 \ No newline at end of file From 82e173b228adaccf4d4fcbce55bc3c2ed6c31ba1 Mon Sep 17 00:00:00 2001 From: konard Date: Sun, 22 Mar 2026 08:10:51 +0000 Subject: [PATCH 2/4] =?UTF-8?q?refactor:=20=D0=B8=D0=BD=D0=BA=D0=B0=D0=BF?= =?UTF-8?q?=D1=81=D1=83=D0=BB=D1=8F=D1=86=D0=B8=D1=8F=20=D0=B3=D0=BB=D0=BE?= =?UTF-8?q?=D0=B1=D0=B0=D0=BB=D1=8C=D0=BD=D0=BE=D0=B3=D0=BE=20=D1=81=D0=BE?= =?UTF-8?q?=D1=81=D1=82=D0=BE=D1=8F=D0=BD=D0=B8=D1=8F=20pam=5Fpmm=20=D0=B2?= =?UTF-8?q?=20=D1=81=D1=82=D1=80=D1=83=D0=BA=D1=82=D1=83=D1=80=D1=83=20pam?= =?UTF-8?q?=5Fpmm=5Fstate=20(=D0=AD=D1=82=D0=B0=D0=BF=2010.1,=20Issue=20#2?= =?UTF-8?q?05)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Три разрозненные статические переменные (filename, root_offset, initialized) объединены в структуру pam_pmm_state с методом reset(). Глобальный синглтон pam_pmm_global_state() заменяет прямой доступ к переменным. Функции detail:: делегируют синглтону для обратной совместимости. Добавлены 4 теста (659 тестов, все проходят). Co-Authored-By: Claude Opus 4.6 --- pam_pmm.h | 52 ++++++++++++++++++++++++++++++------------ plan.md | 28 +++++++---------------- readme.md | 4 ++-- tests/test_pam_pmm.cpp | 51 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 99 insertions(+), 36 deletions(-) diff --git a/pam_pmm.h b/pam_pmm.h index 361ee2c..30226d0 100644 --- a/pam_pmm.h +++ b/pam_pmm.h @@ -158,37 +158,61 @@ static_assert( std::is_trivially_copyable::value, "pam_pmm_registry должен быть тривиально копируемым" ); // ═══════════════════════════════════════════════════════════════════════════ -// ГЛОБАЛЬНОЕ СОСТОЯНИЕ PAM_PMM +// ГЛОБАЛЬНОЕ СОСТОЯНИЕ PAM_PMM (Этап 10.1: инкапсуляция в структуру) // ═══════════════════════════════════════════════════════════════════════════ -namespace detail +/** + * @brief Инкапсулированное состояние PMM-фасада. + * + * Три ранее разрозненные статические переменные (filename, root_offset, + * initialized) объединены в одну структуру. Это первый шаг к поддержке + * нескольких экземпляров БД в одном процессе (Проблема 3, plan.md). + * + * Текущая реализация использует глобальный синглтон (pam_pmm_global_state()), + * а функции detail:: делегируют ему. В дальнейшем (Этап 10.1b) состояние + * будет передаваться как явный параметр. + */ +struct pam_pmm_state { + char filename[256] = {}; ///< Имя файла хранилища + uintptr_t root_offset = 0; ///< Смещение корневой структуры в ПАП + bool initialized = false; ///< Флаг инициализации + + /// Сбросить все поля к начальным значениям. + void reset() + { + filename[0] = '\0'; + root_offset = 0; + initialized = false; + } +}; -/// Имя файла хранилища (глобальное состояние). -inline char& pam_pmm_filename_char( std::size_t idx ) +/// Глобальный синглтон состояния PMM. +inline pam_pmm_state& pam_pmm_global_state() { - static char filename[256] = {}; - return filename[idx]; + static pam_pmm_state state; + return state; } +namespace detail +{ + +/// Имя файла хранилища (делегирует глобальному состоянию). inline char* pam_pmm_filename() { - return &pam_pmm_filename_char( 0 ); + return pam_pmm_global_state().filename; } -/// Смещение корневой структуры в ПАП (глобальное состояние). -/// Корневая структура аллоцируется первой и хранится по известному смещению. +/// Смещение корневой структуры в ПАП (делегирует глобальному состоянию). inline uintptr_t& pam_pmm_root_offset() { - static uintptr_t offset = 0; - return offset; + return pam_pmm_global_state().root_offset; } -/// Флаг инициализации. +/// Флаг инициализации (делегирует глобальному состоянию). inline bool& pam_pmm_initialized() { - static bool initialized = false; - return initialized; + return pam_pmm_global_state().initialized; } } // namespace detail diff --git a/plan.md b/plan.md index f1199fb..0fe8768 100644 --- a/plan.md +++ b/plan.md @@ -21,7 +21,7 @@ | 7. Унификация итераторов | CRTP-база pjson_iterator_base + шаблонный pjson_range; ~22 строки удалено | ✅ | **Итого удалено:** ~5 файлов (~1900 строк), ~381 строка дублирования. -**Тесты:** 655 тестов, ~360 000 assertion. +**Тесты:** 659 тестов, ~360 000 assertion. --- @@ -39,25 +39,12 @@ --- -### Проблема 3: Глобальное состояние в pam_pmm.h +### ~~Проблема 3: Глобальное состояние в pam_pmm.h~~ (Этап A ✅) -**Файл:** `pam_pmm.h`, строки 164–194 (namespace detail) +**Решено (Этап A) в Этапе 10.1:** Три разрозненные статические переменные (`filename`, `root_offset`, `initialized`) инкапсулированы в структуру `pam_pmm_state` с методом `reset()`. Глобальный синглтон `pam_pmm_global_state()` заменяет прямой доступ к переменным. Функции `detail::` делегируют синглтону для обратной совместимости. Добавлены 4 теста. -Три статические переменные хранят глобальное состояние PMM: - -```cpp -static char filename[256] = {}; -static uintptr_t offset = 0; -static bool initialized = false; -``` - -Это делает невозможным: -- Одновременную работу с несколькими БД в одном процессе -- Потокобезопасную инициализацию (data races при конкурентном вызове `pam_pmm_init`) -- Юнит-тестирование с изоляцией (тесты зависят от глобального состояния) - -**Решение (поэтапно):** -1. **Этап A:** Инкапсулировать три переменные в структуру `pam_pmm_state` +**Остающиеся этапы (будущие задачи):** +1. ~~**Этап A:** Инкапсулировать три переменные в структуру `pam_pmm_state`~~ ✅ 2. **Этап B:** Передавать `pam_pmm_state&` как явный параметр вместо обращения к глобальным переменным 3. **Этап C:** Опционально — защита `std::mutex` для потокобезопасной инициализации @@ -202,7 +189,7 @@ pvector был бы предпочтительнее **только** при ч | # | Проблема | Файл | Сложность | Влияние | |---|----------|------|-----------|---------| -| 3 | Глобальное состояние PMM | pam_pmm.h | Высокая | Архитектура | +| ~~3~~ | ~~Глобальное состояние PMM (Этап A)~~ | ~~pam_pmm.h~~ | ~~Высокая~~ | ✅ | | 7 | Нет escaping '/' в путях | pjson_db_pmm.h | Средняя | Совместимость | | 10 | Многократный resolve в is_*() | pjson_node.h | Средняя | Производительность | | 11 | const-корректность _walk_path | pjson_db_pmm.h | Средняя | Корректность | @@ -227,7 +214,7 @@ pvector был бы предпочтительнее **только** при ч → тесты: все 655 тестов проходят Этап 10: Приоритет 3 — архитектурные улучшения - 10.1 Инкапсуляция глобального состояния pam_pmm + 10.1 ✅ Инкапсуляция глобального состояния pam_pmm (Этап A: структура pam_pmm_state + синглтон) 10.2 Поддержка RFC 6901 (JSON Pointer) для путей 10.3 Оптимизация tag-проверок на горячих путях 10.4 const-корректность с явной передачей состояния @@ -239,6 +226,7 @@ pvector был бы предпочтительнее **только** при ч | Дата | Изменение | |------|-----------| +| 2026-03-22 | Этап 10.1: инкапсуляция глобального состояния pam_pmm в структуру pam_pmm_state (Issue #205) | | 2026-03-22 | Этап 9.4: parse_object() в один проход без двойного парсинга (Issue #192) | | 2026-03-22 | Этап 9.3: _free_node_tree и _resolve_refs_in_subtree через pjson_traverse_subtree с visitor-функторами (Issue #191) | | 2026-03-22 | Этап 9.2: прямая вставка node_id в массив/объект без временных слотов (Issue #190) | diff --git a/readme.md b/readme.md index 805e165..d28ba31 100644 --- a/readme.md +++ b/readme.md @@ -139,7 +139,7 @@ int main() { | `pjson_db_pmm.h` | D | Менеджер персистной JSON-БД: path-адресация, `put`/`get`/`erase`, `$ref`, метрики, поиск, клонирование | | `deps/pmm/pmm.h` | A | [PersistMemoryManager](https://github.com/netkeep80/PersistMemoryManager) — бэкенд ПАП | | `main.cpp` | — | Демонстрационная программа | -| `tests/` | — | Тесты на Catch2 (655 тестов, ~360 000 assertion) | +| `tests/` | — | Тесты на Catch2 (659 тестов, ~360 000 assertion) | | `CMakeLists.txt` | — | Система сборки (CMake 3.16+, C++20) | --- @@ -438,7 +438,7 @@ db.put("/copy/name", "Bob"); ## Известные ограничения -- **Глобальное состояние PMM** — в одном процессе может быть открыта только одна БД (см. [plan.md](plan.md), Проблема 3) +- **Глобальное состояние PMM** — в одном процессе может быть открыта только одна БД (см. [plan.md](plan.md), Проблема 3); состояние инкапсулировано в `pam_pmm_state`, передача как параметра — в будущих версиях - **Нет escaping `/` в путях** — ключи объектов, содержащие `/`, недоступны через path-адресацию (см. [plan.md](plan.md), Проблема 7) - ~~**Утечка временных узлов метрик**~~ — **Исправлено** в Этапе 8.4: один pre-allocated узел переиспользуется для всех вызовов метрик - **Не потокобезопасно** — CacheManagerConfig (по умолчанию) использует NoLock; для многопоточности нужен PersistentDataConfig diff --git a/tests/test_pam_pmm.cpp b/tests/test_pam_pmm.cpp index 230dc58..1531272 100644 --- a/tests/test_pam_pmm.cpp +++ b/tests/test_pam_pmm.cpp @@ -541,3 +541,54 @@ TEST_CASE( "pam_pmm: root object survives reset", "[pam_pmm]" ) pam_pmm_destroy(); } + +// ═══════════════════════════════════════════════════════════════════════════ +// ТЕСТЫ pam_pmm_state (Этап 10.1: инкапсуляция глобального состояния) +// ═══════════════════════════════════════════════════════════════════════════ + +TEST_CASE( "pam_pmm_state: default construction zeroed", "[pam_pmm][pam_pmm_state]" ) +{ + pam_pmm_state s{}; + REQUIRE( s.filename[0] == '\0' ); + REQUIRE( s.root_offset == 0 ); + REQUIRE( s.initialized == false ); +} + +TEST_CASE( "pam_pmm_state: reset clears all fields", "[pam_pmm][pam_pmm_state]" ) +{ + pam_pmm_state s{}; + std::strncpy( s.filename, "test.pam", sizeof( s.filename ) - 1 ); + s.root_offset = 42; + s.initialized = true; + + s.reset(); + + REQUIRE( s.filename[0] == '\0' ); + REQUIRE( s.root_offset == 0 ); + REQUIRE( s.initialized == false ); +} + +TEST_CASE( "pam_pmm_state: global_state singleton is consistent with detail:: accessors", "[pam_pmm][pam_pmm_state]" ) +{ + pam_pmm_state& gs = pam_pmm_global_state(); + + // detail:: functions should return pointers/references into the same state. + REQUIRE( detail::pam_pmm_filename() == gs.filename ); + REQUIRE( &detail::pam_pmm_root_offset() == &gs.root_offset ); + REQUIRE( &detail::pam_pmm_initialized() == &gs.initialized ); +} + +TEST_CASE( "pam_pmm_state: init/destroy cycle reflected in global state", "[pam_pmm][pam_pmm_state]" ) +{ + pam_pmm_init( nullptr ); + + pam_pmm_state& gs = pam_pmm_global_state(); + REQUIRE( gs.initialized == true ); + REQUIRE( gs.root_offset != 0 ); + + pam_pmm_destroy(); + + REQUIRE( gs.initialized == false ); + REQUIRE( gs.root_offset == 0 ); + REQUIRE( gs.filename[0] == '\0' ); +} From 423396b87930ec3de1aaaa021367358c1c5d45ca Mon Sep 17 00:00:00 2001 From: konard Date: Sun, 22 Mar 2026 08:11:43 +0000 Subject: [PATCH 3/4] Revert "Initial commit with task details" This reverts commit 4ddaa7bd3771777e8f82edd3904aa456f40ca741. --- .gitkeep | 1 - 1 file changed, 1 deletion(-) delete mode 100644 .gitkeep diff --git a/.gitkeep b/.gitkeep deleted file mode 100644 index 9f684c0..0000000 --- a/.gitkeep +++ /dev/null @@ -1 +0,0 @@ -# .gitkeep file auto-generated at 2026-03-22T08:03:00.572Z for PR creation at branch issue-205-3a84e21338b1 for issue https://github.com/netkeep80/BinDiffSynchronizer/issues/205 \ No newline at end of file From 5f1da91e22f0e79d5f2f1c122ca54d1d79563360 Mon Sep 17 00:00:00 2001 From: konard Date: Sun, 22 Mar 2026 08:20:37 +0000 Subject: [PATCH 4/4] style: fix clang-format violations in pam_pmm_state Co-Authored-By: Claude Opus 4.6 --- pam_pmm.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pam_pmm.h b/pam_pmm.h index 30226d0..164c9ea 100644 --- a/pam_pmm.h +++ b/pam_pmm.h @@ -174,16 +174,16 @@ static_assert( std::is_trivially_copyable::value, */ struct pam_pmm_state { - char filename[256] = {}; ///< Имя файла хранилища - uintptr_t root_offset = 0; ///< Смещение корневой структуры в ПАП + char filename[256] = {}; ///< Имя файла хранилища + uintptr_t root_offset = 0; ///< Смещение корневой структуры в ПАП bool initialized = false; ///< Флаг инициализации /// Сбросить все поля к начальным значениям. void reset() { - filename[0] = '\0'; - root_offset = 0; - initialized = false; + filename[0] = '\0'; + root_offset = 0; + initialized = false; } };