116 lines
10 KiB
Markdown
116 lines
10 KiB
Markdown
# Модуль объекта отчёта в расширении
|
||
|
||
Статус: чтение, точное разрешение владельца и контролируемая запись короткого
|
||
фрагмента поддержаны для доказанного hash-keyed saved-state маршрута.
|
||
|
||
Наблюдение в `upo_test`, расширение `test2`, отчёт `tt_Отчет`: сохранённый файл
|
||
`<extension-guid>__<report-guid>.0` является `raw_deflate` контейнером из пяти
|
||
потоков. BSL-модуль расположен в потоке `4`.
|
||
|
||
Поток начинается читаемым UTF-8-комментарием, но последующий текст содержит
|
||
нулевые байты и смешанное представление символов. Общий потоковый декодер
|
||
позволяет найти комментарий, однако его обратное кодирование меняет байты
|
||
неизменённого хвоста BSL. Экспериментальная запись показала это в Конфигураторе
|
||
и была немедленно восстановлена из парной резервной копии.
|
||
|
||
Правило: наличие читаемого BSL-фрагмента не доказывает возможность записи.
|
||
Для потока с `NUL` адаптер возвращает
|
||
`mixed_encoding_module_stream_unsupported` и не создаёт SQL-изменений.
|
||
Это не означает, что для каждого отчёта нужен свой кодер: один доказанный
|
||
кодек может обслуживать все модули с одинаковым физическим носителем.
|
||
|
||
Два ручных образца определили безопасную границу записи: редактируется только
|
||
объявленный UTF-8-префикс, а непрозрачный хвост и остальные потоки сохраняются
|
||
побайтно. Для hash-keyed overlay рабочий слой создаётся доказанным копированием
|
||
подтверждённых ключей `ConfigCAS → ConfigCASSave`; `__configinfo` для него не
|
||
создаётся и не предполагается.
|
||
|
||
Текущая реализация `parser.cas_payload.stream_blocks_with_data` ищет похожие
|
||
заголовки регулярным выражением по всему распакованному буферу. В потоке
|
||
отчёта такие последовательности встречаются и внутри данных, поэтому это
|
||
эвристика для чтения, а не структурный декодер. Нельзя использовать её индекс
|
||
потока как основание для обратной записи.
|
||
|
||
Структурный read-only декодер `decode_declared_utf8_bsl_prefix` подтверждён на
|
||
этом образце: пять последовательных блоков; пятый имеет `declared_1 = 68` и
|
||
`declared_2 = 512`. Первые 68 байт — UTF-8 BOM и точный BSL-текст, оставшиеся
|
||
444 байта — непрозрачный служебный хвост. Декодер вернул только:
|
||
`// protocol-report-baseline-1` и `// protocol-report-manual-change-4`.
|
||
|
||
## Пара ручных образцов `2 → 3`
|
||
|
||
Образцы `samples/manual-change-2.json` и `samples/manual-change-3.json`
|
||
содержат raw-deflate байты, сохранённые человеком в Конфигураторе. В
|
||
распакованном контейнере длиной 1283 байта замена цифры `2` на `3` изменила
|
||
BSL ровно в смещении `838` (`0x32 → 0x33`). Одновременно платформа изменила
|
||
шесть служебных диапазонов: `110..113`, `230..252`, `437..464`, `590..593`,
|
||
`598..601`, `716..719`. Трёхбайтовое значение повторяется в нескольких
|
||
местах, а два диапазона содержат связанные Base64-представления.
|
||
|
||
Это доказывает, что нельзя перепаковывать поток общим writer'ом. Отдельный
|
||
fixed-width кодек меняет только первые `declared_1` байт: короткий текст
|
||
дополняется пробелами внутри этого поля, хвост и размер члена не меняются.
|
||
Рост префикса или структурная правка процедуры явно отклоняются.
|
||
|
||
## Полный объявленный поток: переменная длина
|
||
|
||
Нельзя переносить ограничение fixed-width с описанного выше носителя на все
|
||
объектные BSL-модули. На рабочем маршруте `upo / фс_Отчеты /
|
||
Report.УОП_ПечатьЦенниковАссортимента / .2 / stream:4` подтверждён другой
|
||
контейнер: у выбранного BSL-потока `declared_1 == declared_2 == 36101` и
|
||
`opaque_tail_bytes == 0`. Это полный UTF-8 поток, а не префикс перед
|
||
непрозрачными данными.
|
||
|
||
Для такого носителя адаптер использует обычный структурный stream writer:
|
||
он меняет текст, пересобирает оба объявленных размера в заголовке и сдвигает
|
||
только последующие байты контейнера. Локальная обратная проверка целевой
|
||
замены `НоваяСтрока.Выбран = Истина;` на более длинный фрагмент дала размер
|
||
потока `36101 → 36198`, новый заголовок `36198/36198`, одно новое вхождение и
|
||
нулевое старое. Все байты до заголовка выбранного потока сохранились.
|
||
|
||
Правило выбора кодека: fixed-width применяется **только** если доказан
|
||
ненулевой непрозрачный хвост; если `declared_1 == declared_2` и хвоста нет,
|
||
безопасна контролируемая замена переменной длины через структурный writer.
|
||
Неизвестный или частично декодированный контейнер остаётся заблокированным,
|
||
а не переводится в переменную длину по предположению.
|
||
|
||
## Правило публичного маршрута
|
||
|
||
Если объектный модуль состоит только из комментариев, это всё равно BSL-модуль:
|
||
у него нет маркеров `Процедура`/`Функция`, но его наличие подтверждает
|
||
структурный UTF-8-префикс в потоке. Адаптер обязан вернуть владельца и точный
|
||
селектор чтения, не заставляя клиента искать поток. При записи он обязан
|
||
использовать только fixed-width кодек, а не общий stream writer, который
|
||
перезаписывает непрозрачный хвост. Парное обновление `__configinfo` допустимо
|
||
только в отдельно подтверждённом каноническом layout.
|
||
|
||
## Повтор `code.write` после успешной записи
|
||
|
||
Повтор одного и того же публичного `code.write` не является новой операцией.
|
||
До автоматической подготовки `ConfigCASSave` адаптер читает указанную
|
||
процедуру в `effective_working`. Если старого фрагмента уже нет, а точный
|
||
новый фрагмент присутствует ровно один раз в этой же процедуре, результат —
|
||
`status: already_applied`, `applied: false`. В этом случае запрещены и
|
||
подготовка saved-state, и новая SQL-запись.
|
||
|
||
Это правило предотвращает опасный путь: повторный запрос нельзя начинать с
|
||
активного `ConfigCAS`, потому что его копирование способно заново построить
|
||
рабочую копию из доизменённого источника и скрыть факт уже выполненной
|
||
операции. Если оба фрагмента отсутствуют, новый фрагмент встречается
|
||
несколько раз либо процедура не подтверждена, идемпотентность не
|
||
предполагается: применяется обычная безопасная ошибка `not_found`/
|
||
`ambiguous` или диагностика маршрута.
|
||
|
||
## Цепочка версий `2 → 3 → 4`
|
||
|
||
Третий live-SQL образец подтвердил повторяемую часть протокола. 20-байтовое
|
||
Base64-поле в каждой новой версии равно SHA-1 сырого файла предыдущей версии:
|
||
запись `3` хранит SHA-1 записи `2`, а запись `4` — SHA-1 записи `3`. Это
|
||
доказанная ссылка версии, а не случайный текст. Его контрольный SHA-1:
|
||
`fc84f0a9ef17034f8d82f44c5f9b07064864b524`.
|
||
|
||
Рядом расположен 16-байтовый токен, который меняется при каждом сохранении и
|
||
дублируется фрагментами в трёх служебных местах. Алгоритм его создания не
|
||
декодирован: адаптер его не генерирует и не изменяет. Его нельзя считать
|
||
основанием для создания или изменения `__configinfo` в hash-keyed overlay.
|