mdview2: лимит файла 256 КБ, обрыв с сообщением вместо потери строк
- MAX_PAGES 8->16 (256 КБ), MAX_INDEX_PAGES/MAX_CACHE_DIR_PAGES 8->9 (18432 строк), кап контент-кэша 64->40 стр./набор (80 на оба). Бюджет worst-case (UTF-8 Latin+BOM): ~150 из 215 EMM-страниц. - При исчерпании контент-кэша (cache_reserve==0) или лимита строк индексация обрывается и последняя строка заменяется предупреждением (IF_TRUNC_MSG, рисуется вживую с ATTR_WARN — жёлтый по красному), без хвоста пустых строк. Раньше переполнение молча давало len=0 (пустые строки) без какого-либо индикатора. - help: лимиты обновлены (256 КБ / 18432 строк). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -70,7 +70,7 @@ static const uint8_t md_pallete[16 * 4] = {
|
|||||||
* неинициализированные static-объекты могут конфликтовать по размещению
|
* неинициализированные static-объекты могут конфликтовать по размещению
|
||||||
* и перезаписывать друг друга (см. memory/sdcc_static_storage_gotcha). */
|
* и перезаписывать друг друга (см. memory/sdcc_static_storage_gotcha). */
|
||||||
|
|
||||||
#define MAX_INDEX_PAGES 8u /* максимум 8 * 2048 = 16384 сегментов индекса */
|
#define MAX_INDEX_PAGES 9u /* максимум 9 * 2048 = 18432 сегментов индекса (256 КБ) */
|
||||||
|
|
||||||
/* ==================================================================
|
/* ==================================================================
|
||||||
* Рендер-кэш: директория и пул EMM-страниц
|
* Рендер-кэш: директория и пул EMM-страниц
|
||||||
@@ -97,9 +97,9 @@ static const uint8_t md_pallete[16 * 4] = {
|
|||||||
|
|
||||||
#define CACHE_DIR_REC_SIZE ((uint16_t)sizeof(cache_rec_t))
|
#define CACHE_DIR_REC_SIZE ((uint16_t)sizeof(cache_rec_t))
|
||||||
#define CACHE_DIR_RECS_PER_PAGE (16384 / CACHE_DIR_REC_SIZE) /* 2048 */
|
#define CACHE_DIR_RECS_PER_PAGE (16384 / CACHE_DIR_REC_SIZE) /* 2048 */
|
||||||
#define MAX_CACHE_DIR_PAGES 8u
|
#define MAX_CACHE_DIR_PAGES 9u /* симметрично индексу: 9 * 2048 = 18432 записей */
|
||||||
/* MAX_CACHE_LINE_LEN — в mdview2.h (буфер ячеек g_cells живёт в mdview2_index.c). */
|
/* MAX_CACHE_LINE_LEN — в mdview2.h (буфер ячеек g_cells живёт в mdview2_index.c). */
|
||||||
#define MAX_CACHE_CONTENT_PAGES 64u /* запас: до 1 МБ кэша контента */
|
#define MAX_CACHE_CONTENT_PAGES 40u /* кап контента на набор: 40 * 16 КБ = 640 КБ (80 на оба набора) */
|
||||||
|
|
||||||
static uint8_t cache_dir_blk = 0;
|
static uint8_t cache_dir_blk = 0;
|
||||||
static uint8_t cache_dir_pages = 0;
|
static uint8_t cache_dir_pages = 0;
|
||||||
|
|||||||
@@ -32,7 +32,7 @@
|
|||||||
#define PAGE_BITS 14u
|
#define PAGE_BITS 14u
|
||||||
#define PAGE_SIZE (1u << PAGE_BITS) /* размер EMM-страницы: 16 КБ */
|
#define PAGE_SIZE (1u << PAGE_BITS) /* размер EMM-страницы: 16 КБ */
|
||||||
#define PAGE_MASK ((uint16_t)(PAGE_SIZE - 1u))
|
#define PAGE_MASK ((uint16_t)(PAGE_SIZE - 1u))
|
||||||
#define MAX_PAGES 8 /* 8 страниц × 16 КБ = 128 КБ */
|
#define MAX_PAGES 16 /* 16 страниц × 16 КБ = 256 КБ */
|
||||||
#define MAX_FILE ((uint32_t)MAX_PAGES * PAGE_SIZE)
|
#define MAX_FILE ((uint32_t)MAX_PAGES * PAGE_SIZE)
|
||||||
#define FILE_BUF ((char *)0xC000) /* окно W3, куда мапится текущая EMM-страница */
|
#define FILE_BUF ((char *)0xC000) /* окно W3, куда мапится текущая EMM-страница */
|
||||||
|
|
||||||
@@ -57,6 +57,7 @@
|
|||||||
#define ATTR_BAR_SPINNER COLOR(COLOR_WHITE, COLOR_LIGHTCYAN)
|
#define ATTR_BAR_SPINNER COLOR(COLOR_WHITE, COLOR_LIGHTCYAN)
|
||||||
#define ATTR_MENU_T COLOR(COLOR_BLACK, COLOR_LIGHTCYAN)
|
#define ATTR_MENU_T COLOR(COLOR_BLACK, COLOR_LIGHTCYAN)
|
||||||
#define ATTR_MENU_K COLOR(COLOR_YELLOW, COLOR_BLACK)
|
#define ATTR_MENU_K COLOR(COLOR_YELLOW, COLOR_BLACK)
|
||||||
|
#define ATTR_WARN COLOR(COLOR_YELLOW, COLOR_RED) /* строка-обрыв (исчерпан кэш/лимит) */
|
||||||
|
|
||||||
/* ---- Атрибуты диалога справки ------------------------------------ */
|
/* ---- Атрибуты диалога справки ------------------------------------ */
|
||||||
#define ATTR_HELP_BG COLOR(COLOR_LIGHTGRAY, COLOR_BLACK)
|
#define ATTR_HELP_BG COLOR(COLOR_LIGHTGRAY, COLOR_BLACK)
|
||||||
@@ -87,6 +88,12 @@
|
|||||||
#define IF_HSCROLL 0x10u /* блок горизонтально скроллируется ЦЕЛИКОМ (код/таблица),
|
#define IF_HSCROLL 0x10u /* блок горизонтально скроллируется ЦЕЛИКОМ (код/таблица),
|
||||||
* включая строки короче 80 — двигаются как единый блок.
|
* включая строки короче 80 — двигаются как единый блок.
|
||||||
* НЕ ставится на HR и границы fence (им нечего прятать). */
|
* НЕ ставится на HR и границы fence (им нечего прятать). */
|
||||||
|
#define IF_TRUNC_MSG 0x20u /* строка-обрыв: рисуется ВЖИВУЮ фикс. текстом (без контент-кэша);
|
||||||
|
* cache_rec.reserved несёт код причины (TRUNC_*). */
|
||||||
|
|
||||||
|
/* ---- Причина обрыва индексации (cache_rec.reserved при IF_TRUNC_MSG) ---- */
|
||||||
|
#define TRUNC_CONTENT 1 /* исчерпан контент-кэш (MAX_CACHE_CONTENT_PAGES) */
|
||||||
|
#define TRUNC_LINES 2 /* исчерпана ёмкость индекса (max_lines) */
|
||||||
|
|
||||||
/* ---- Словарь сегментов/стилей (общий для индексатора и таблиц) ---- */
|
/* ---- Словарь сегментов/стилей (общий для индексатора и таблиц) ---- */
|
||||||
/* Тип сегмента-продолжения, передаваемый в emit_seg()/inline_scan(). */
|
/* Тип сегмента-продолжения, передаваемый в emit_seg()/inline_scan(). */
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ void show_help(void)
|
|||||||
help_line(r++, " F8 Cycle CP866 / CP1251 / KOI8-R / UTF-8", ATTR_HELP_BG);
|
help_line(r++, " F8 Cycle CP866 / CP1251 / KOI8-R / UTF-8", ATTR_HELP_BG);
|
||||||
help_line(r++, " Auto-detected on open; F8 to override", ATTR_HELP_BG);
|
help_line(r++, " Auto-detected on open; F8 to override", ATTR_HELP_BG);
|
||||||
help_line(r++, "", ATTR_HELP_BG);
|
help_line(r++, "", ATTR_HELP_BG);
|
||||||
help_line(r++, " File size: up to 128 KB (EMM). Lines: up to 16384.", ATTR_HELP_BG);
|
help_line(r++, " File size: up to 256 KB (EMM). Lines: up to 18432.", ATTR_HELP_BG);
|
||||||
help_line(r++, "", ATTR_HELP_BG);
|
help_line(r++, "", ATTR_HELP_BG);
|
||||||
|
|
||||||
(void)getkey();
|
(void)getkey();
|
||||||
|
|||||||
@@ -241,6 +241,12 @@ uint8_t g_ncells = 0; /* ячеек в текущем сег
|
|||||||
static uint8_t g_ncells_at_space = 0; /* снимок g_ncells на последнем пробеле (для усечения при переносе) */
|
static uint8_t g_ncells_at_space = 0; /* снимок g_ncells на последнем пробеле (для усечения при переносе) */
|
||||||
static uint8_t g_skip_flush = 0; /* следующий emit_seg пропустит lag-флаш (pending уже зафлашен вручную, напр. перед таблицей) */
|
static uint8_t g_skip_flush = 0; /* следующий emit_seg пропустит lag-флаш (pending уже зафлашен вручную, напр. перед таблицей) */
|
||||||
|
|
||||||
|
/* Обрыв индексации: причина (0 / TRUNC_CONTENT / TRUNC_LINES) и строка, на
|
||||||
|
* которой упёрлись. Выставляются ОДИН раз (первая причина побеждает); по ним
|
||||||
|
* index_lines() в конце пишет строку-сообщение (IF_TRUNC_MSG, рисуется вживую). */
|
||||||
|
static uint8_t g_trunc_cause = 0;
|
||||||
|
static uint16_t g_trunc_line = 0;
|
||||||
|
|
||||||
/* Пишет одну (char,attr)-ячейку в g_cells, молча игнорируя переполнение
|
/* Пишет одну (char,attr)-ячейку в g_cells, молча игнорируя переполнение
|
||||||
* MAX_CACHE_LINE_LEN. Оставлена ФУНКЦИЕЙ намеренно: инлайн в сильно
|
* MAX_CACHE_LINE_LEN. Оставлена ФУНКЦИЕЙ намеренно: инлайн в сильно
|
||||||
* регистро-нагруженный скан-цикл на Z80/SDCC увеличивает спиллы и замедляет
|
* регистро-нагруженный скан-цикл на Z80/SDCC увеличивает спиллы и замедляет
|
||||||
@@ -274,6 +280,9 @@ static void seg_commit_cells(uint16_t line_idx)
|
|||||||
if (cache_reserve((uint16_t)(g_ncells * 2u), &out_rec.page, &out_rec.off)) {
|
if (cache_reserve((uint16_t)(g_ncells * 2u), &out_rec.page, &out_rec.off)) {
|
||||||
cache_commit(out_rec.page, out_rec.off, g_cells, (uint16_t)(g_ncells * 2u));
|
cache_commit(out_rec.page, out_rec.off, g_cells, (uint16_t)(g_ncells * 2u));
|
||||||
} else {
|
} else {
|
||||||
|
/* Контент-кэш исчерпан: фиксируем причину и строку обрыва (один раз).
|
||||||
|
* index_lines() оборвёт цикл и перезапишет эту строку сообщением. */
|
||||||
|
if (!g_trunc_cause) { g_trunc_cause = TRUNC_CONTENT; g_trunc_line = line_idx; }
|
||||||
out_rec.page = 0; out_rec.off = 0; out_rec.len = 0;
|
out_rec.page = 0; out_rec.off = 0; out_rec.len = 0;
|
||||||
}
|
}
|
||||||
cache_dir_put(line_idx, &out_rec);
|
cache_dir_put(line_idx, &out_rec);
|
||||||
@@ -283,7 +292,11 @@ static void seg_commit_cells(uint16_t line_idx)
|
|||||||
static void emit_seg(uint32_t off, uint8_t style, uint8_t ckind, uint8_t cont)
|
static void emit_seg(uint32_t off, uint8_t style, uint8_t ckind, uint8_t cont)
|
||||||
{
|
{
|
||||||
(void)ckind; /* тип сегмента восстанавливается флагами idx-записи */
|
(void)ckind; /* тип сегмента восстанавливается флагами idx-записи */
|
||||||
if (n_lines >= max_lines) { index_truncated = 1; return; }
|
if (n_lines >= max_lines) {
|
||||||
|
index_truncated = 1;
|
||||||
|
if (!g_trunc_cause) { g_trunc_cause = TRUNC_LINES; g_trunc_line = (uint16_t)(max_lines - 1); }
|
||||||
|
return;
|
||||||
|
}
|
||||||
cur_rec.off = off;
|
cur_rec.off = off;
|
||||||
cur_rec.flags = (uint8_t)(cont ? IF_CONT : 0);
|
cur_rec.flags = (uint8_t)(cont ? IF_CONT : 0);
|
||||||
cur_rec.style = (uint8_t)(style & 7u);
|
cur_rec.style = (uint8_t)(style & 7u);
|
||||||
@@ -687,11 +700,13 @@ void index_lines(void)
|
|||||||
uint8_t in_block = 0;
|
uint8_t in_block = 0;
|
||||||
n_lines = 0;
|
n_lines = 0;
|
||||||
index_truncated = 0;
|
index_truncated = 0;
|
||||||
|
g_trunc_cause = 0;
|
||||||
|
g_trunc_line = 0;
|
||||||
cur_seg_off = 0xFFFFFFFFu; /* пока не эмитили ни одного сегмента */
|
cur_seg_off = 0xFFFFFFFFu; /* пока не эмитили ни одного сегмента */
|
||||||
if (file_size == 0) return;
|
if (file_size == 0) return;
|
||||||
|
|
||||||
uint32_t p = 0;
|
uint32_t p = 0;
|
||||||
while (p < file_size && n_lines < max_lines && !g_abort) {
|
while (p < file_size && n_lines < max_lines && !g_abort && !g_trunc_cause) {
|
||||||
|
|
||||||
/* UTF-8: держим конвертацию впереди позиции чтения (включая look-ahead
|
/* UTF-8: держим конвертацию впереди позиции чтения (включая look-ahead
|
||||||
* склейки/классификации ~ один абзац). file_size при этом = доступная
|
* склейки/классификации ~ один абзац). file_size при этом = доступная
|
||||||
@@ -957,6 +972,19 @@ void index_lines(void)
|
|||||||
* комментарий) — последняя эмитированная строка файла так и остаётся
|
* комментарий) — последняя эмитированная строка файла так и остаётся
|
||||||
* незафлашенной, её ячейки ещё в g_cells; коммитим здесь. */
|
* незафлашенной, её ячейки ещё в g_cells; коммитим здесь. */
|
||||||
if (n_lines > 0) seg_commit_cells((uint16_t)(n_lines - 1));
|
if (n_lines > 0) seg_commit_cells((uint16_t)(n_lines - 1));
|
||||||
|
|
||||||
|
/* Обрыв по контенту/лимиту строк (не путать с пользовательским g_abort):
|
||||||
|
* документ заканчиваем строкой-сообщением на g_trunc_line. Сообщение
|
||||||
|
* рисуется вживую (IF_TRUNC_MSG) — место в контент-кэше ему не нужно, что
|
||||||
|
* и снимает тупик «кэш полон, а сообщение тоже надо закэшировать». */
|
||||||
|
if (g_trunc_cause && !g_abort) {
|
||||||
|
cache_rec_t r;
|
||||||
|
r.page = 0; r.off = 0; r.len = 0;
|
||||||
|
r.flags = IF_TRUNC_MSG; r.reserved = g_trunc_cause;
|
||||||
|
r.pad[0] = 0; r.pad[1] = 0;
|
||||||
|
cache_dir_put(g_trunc_line, &r);
|
||||||
|
n_lines = (uint16_t)(g_trunc_line + 1); /* документ кончается на строке-обрыве */
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#define EM_NONE 0
|
#define EM_NONE 0
|
||||||
#define EM_BOLD 1
|
#define EM_BOLD 1
|
||||||
|
|||||||
@@ -28,6 +28,19 @@ static void draw_line_from_cache(uint16_t line_idx, uint8_t row)
|
|||||||
cache_rec_t rec;
|
cache_rec_t rec;
|
||||||
cache_dir_get(line_idx, &rec);
|
cache_dir_get(line_idx, &rec);
|
||||||
|
|
||||||
|
/* Строка-обрыв (исчерпан контент-кэш / лимит строк): фикс. текст вживую,
|
||||||
|
* без обращения к контент-кэшу (см. index_lines финализацию). */
|
||||||
|
if (rec.flags & IF_TRUNC_MSG) {
|
||||||
|
bios_set_place(row, 0);
|
||||||
|
bios_fillcharattr(' ', ATTR_WARN, SCREEN_W);
|
||||||
|
put_str_attr(2, row,
|
||||||
|
(rec.reserved == TRUNC_LINES)
|
||||||
|
? "*** Line limit reached - document truncated ***"
|
||||||
|
: "*** Content cache exhausted - document truncated ***",
|
||||||
|
ATTR_WARN);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
bios_set_place(row, 0);
|
bios_set_place(row, 0);
|
||||||
bios_fillcharattr(' ', ATTR_TEXT, SCREEN_W);
|
bios_fillcharattr(' ', ATTR_TEXT, SCREEN_W);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user