mdview2: оптимизация размера — exe 28215 → 25616 (−2599 Б)
Раунд 1 (−1411): --max-allocs 100000 в Makefile (−848 кода) + снятие
всех нулевых инициализаторов file-scope переменных (_INITIALIZER
583→24; _DATA теперь зануляется в crt0).
Раунд 2 (−1188, индексатор 11019→9839): дедупликации в mdview2_index.c:
- classify_line: копия HR-проверки → вызов is_hr_raw;
- next_line() поверх row_end() вместо 9 копий «домотать до \n»;
- inline_marker: emph_to_attr(ls, base) вычисляется один раз (at);
- set_{nowrap,blank,code,hscroll}_cur → set_cur_flags(mask): строка
code-блока делает один idx_put вместо трёх.
Проверено в MAME (README/UTF8TEST: маркеры, списки, цитаты, таблицы,
code-блоки, F8).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -20,6 +20,14 @@
|
||||
#include "mdview2.h"
|
||||
|
||||
static uint8_t emph_to_attr(uint8_t emph, uint8_t base_attr); /* forward (нужна в inline-сканере) */
|
||||
static uint8_t is_hr_raw(uint32_t p); /* forward (нужна в classify_line) */
|
||||
|
||||
/* Начало следующей физической строки (байт после '\n', либо file_size). */
|
||||
static uint32_t next_line(uint32_t p)
|
||||
{
|
||||
p = row_end(p);
|
||||
return (p < file_size) ? p + 1 : p;
|
||||
}
|
||||
|
||||
typedef struct idx_rec_s {
|
||||
uint32_t off; /* смещение первого байта исходного текста для сегмента */
|
||||
@@ -31,7 +39,7 @@ typedef struct idx_rec_s {
|
||||
|
||||
#define INDEX_REC_SIZE 8u
|
||||
|
||||
static uint32_t cur_seg_off = 0; /* смещение последнего emit_seg (кэш в near-памяти) */
|
||||
static uint32_t cur_seg_off; /* смещение последнего emit_seg (кэш в near-памяти) */
|
||||
static idx_rec_t cur_rec; /* near-копия последней записанной idx-записи */
|
||||
|
||||
/* Для проверки границ выделения считаем пробел/таб/конец строки/0 пробельными. */
|
||||
@@ -90,12 +98,9 @@ uint32_t seg_off(uint16_t idx)
|
||||
return r.off;
|
||||
}
|
||||
|
||||
/* Установка флагов всегда идёт в последний emit_seg (n_lines-1).
|
||||
/* Установка флагов (маска IF_*) всегда идёт в последний emit_seg (n_lines-1).
|
||||
* Используется near-копия cur_rec, чтобы не делать обратное чтение из банка. */
|
||||
static void set_nowrap_cur(void) { cur_rec.flags |= IF_NOWRAP; idx_put((uint16_t)(n_lines - 1), &cur_rec); }
|
||||
static void set_blank_cur(void) { cur_rec.flags |= IF_BLANK; idx_put((uint16_t)(n_lines - 1), &cur_rec); }
|
||||
static void set_code_cur(void) { cur_rec.flags |= IF_CODE; idx_put((uint16_t)(n_lines - 1), &cur_rec); }
|
||||
static void set_hscroll_cur(void){ cur_rec.flags |= IF_HSCROLL; idx_put((uint16_t)(n_lines - 1), &cur_rec); }
|
||||
static void set_cur_flags(uint8_t f) { cur_rec.flags |= f; idx_put((uint16_t)(n_lines - 1), &cur_rec); }
|
||||
|
||||
/* ==================================================================
|
||||
* Индексация строк
|
||||
@@ -157,22 +162,7 @@ static uint8_t classify_line(uint32_t p_start, uint32_t *out_content)
|
||||
|
||||
/* Горизонтальный разделитель.
|
||||
* Проверяется до списков, чтобы `- - -` не стало маркером списка. */
|
||||
if (c0 == '-' || c0 == '*' || c0 == '_') {
|
||||
char marker = 0;
|
||||
uint8_t count = 0;
|
||||
uint8_t ok = 1;
|
||||
uint32_t p = p_start;
|
||||
while (p < file_size) {
|
||||
char ch = fb(p++);
|
||||
if (ch == '\n' || ch == '\r') break;
|
||||
if (ch == ' ' || ch == '\t') continue;
|
||||
if (ch != '-' && ch != '*' && ch != '_') { ok = 0; break; }
|
||||
if (marker == 0) marker = ch;
|
||||
else if (ch != marker) { ok = 0; break; }
|
||||
count++;
|
||||
}
|
||||
if (ok && count >= 3) return LK_HR;
|
||||
}
|
||||
if (is_hr_raw(p_start)) return LK_HR;
|
||||
|
||||
/* Для списков/цитат дополнительно допускаем ведущие пробелы
|
||||
* (базовая поддержка вложенности). */
|
||||
@@ -237,15 +227,15 @@ static uint8_t marker_visible_col(uint8_t kind, uint32_t p_start, uint32_t conte
|
||||
* g_ncells_at_space — снимок длины на последнем пробеле для усечения буфера
|
||||
* при переносе строки (отбрасываем уехавшее за границу слово). */
|
||||
static uint8_t g_cells[MAX_CACHE_LINE_LEN * 2];
|
||||
uint8_t g_ncells = 0; /* ячеек в текущем сегменте (общий с таблицами) */
|
||||
static uint8_t g_ncells_at_space = 0; /* снимок g_ncells на последнем пробеле (для усечения при переносе) */
|
||||
static uint8_t g_skip_flush = 0; /* следующий emit_seg пропустит lag-флаш (pending уже зафлашен вручную, напр. перед таблицей) */
|
||||
uint8_t g_ncells; /* ячеек в текущем сегменте (общий с таблицами) */
|
||||
static uint8_t g_ncells_at_space; /* снимок g_ncells на последнем пробеле (для усечения при переносе) */
|
||||
static uint8_t g_skip_flush; /* следующий 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;
|
||||
static uint8_t g_trunc_cause;
|
||||
static uint16_t g_trunc_line;
|
||||
|
||||
/* Пишет одну (char,attr)-ячейку в g_cells, молча игнорируя переполнение
|
||||
* MAX_CACHE_LINE_LEN. Оставлена ФУНКЦИЕЙ намеренно: инлайн в сильно
|
||||
@@ -390,10 +380,11 @@ static uint8_t inline_marker(uint32_t *pq, uint32_t bound, uint8_t *pls,
|
||||
uint32_t q = *pq;
|
||||
uint8_t ls = *pls;
|
||||
char ch = fb(q);
|
||||
uint8_t at = emph_to_attr(ls, base); /* атрибут литералов текущего стиля */
|
||||
|
||||
if (ls != INIT_STYLE_CODE && ch == '\\' && (q + 1) < bound && is_escapable(fb(q + 1))) {
|
||||
char nb = fb(q + 1);
|
||||
gc_put(nb, emph_to_attr(ls, base));
|
||||
gc_put(nb, at);
|
||||
*pseg += 1; *pq = q + 2; *pprev = nb; return 1;
|
||||
}
|
||||
if (ls != INIT_STYLE_CODE && ch == '[' && (q + 2) < bound && fb(q + 2) == ']') {
|
||||
@@ -403,7 +394,7 @@ static uint8_t inline_marker(uint32_t *pq, uint32_t bound, uint8_t *pls,
|
||||
if (ch == '`') {
|
||||
if (ls == INIT_STYLE_PLAIN) { *pls = INIT_STYLE_CODE; *pq = q + 1; return 1; }
|
||||
if (ls == INIT_STYLE_CODE) { *pls = INIT_STYLE_PLAIN; *pq = q + 1; return 1; }
|
||||
gc_put('`', emph_to_attr(ls, base)); *pseg += 1; *pq = q + 1; *pprev = '`'; return 1;
|
||||
gc_put('`', at); *pseg += 1; *pq = q + 1; *pprev = '`'; return 1;
|
||||
}
|
||||
if (ch == '*' && (q + 1) < bound && fb(q + 1) == '*') {
|
||||
char nx = ((q + 2) < bound) ? fb(q + 2) : '\n';
|
||||
@@ -411,7 +402,7 @@ static uint8_t inline_marker(uint32_t *pq, uint32_t bound, uint8_t *pls,
|
||||
if (ls == INIT_STYLE_PLAIN && ws_or_eol(*pprev)) { *pls = INIT_STYLE_BOLD; *pq = q + 2; return 1; }
|
||||
if (ls == INIT_STYLE_BOLD && ws_or_eol_or_delim(nx)) { *pls = INIT_STYLE_PLAIN; *pq = q + 2; return 1; }
|
||||
}
|
||||
gc_put('*', emph_to_attr(ls, base)); gc_put('*', emph_to_attr(ls, base));
|
||||
gc_put('*', at); gc_put('*', at);
|
||||
*pseg += 2; *pq = q + 2; *pprev = '*'; return 1;
|
||||
}
|
||||
if (ch == '~' && (q + 1) < bound && fb(q + 1) == '~') {
|
||||
@@ -420,7 +411,7 @@ static uint8_t inline_marker(uint32_t *pq, uint32_t bound, uint8_t *pls,
|
||||
if (ls == INIT_STYLE_PLAIN && ws_or_eol(*pprev)) { *pls = INIT_STYLE_STRIKE; *pq = q + 2; return 1; }
|
||||
if (ls == INIT_STYLE_STRIKE && ws_or_eol_or_delim(nx)) { *pls = INIT_STYLE_PLAIN; *pq = q + 2; return 1; }
|
||||
}
|
||||
gc_put('~', emph_to_attr(ls, base)); gc_put('~', emph_to_attr(ls, base));
|
||||
gc_put('~', at); gc_put('~', at);
|
||||
*pseg += 2; *pq = q + 2; *pprev = '~'; return 1;
|
||||
}
|
||||
if (ch == '*' || ch == '_') {
|
||||
@@ -430,13 +421,13 @@ static uint8_t inline_marker(uint32_t *pq, uint32_t bound, uint8_t *pls,
|
||||
if (ls == INIT_STYLE_PLAIN && ws_or_eol(*pprev)) { *pls = ns; *pq = q + 1; return 1; }
|
||||
if (ls != INIT_STYLE_PLAIN && ws_or_eol_or_delim(nx)) { *pls = INIT_STYLE_PLAIN; *pq = q + 1; return 1; }
|
||||
}
|
||||
gc_put(ch, emph_to_attr(ls, base)); *pseg += 1; *pq = q + 1; *pprev = ch; return 1;
|
||||
gc_put(ch, at); *pseg += 1; *pq = q + 1; *pprev = ch; return 1;
|
||||
}
|
||||
if (ch == '\t') {
|
||||
uint8_t old_sc = *pseg;
|
||||
uint8_t tgt_full = (uint8_t)((old_sc & (uint8_t)~(TAB_STOP - 1)) + TAB_STOP);
|
||||
uint8_t tgt = tgt_full; if (tgt > SCREEN_W) tgt = SCREEN_W;
|
||||
gc_fill(' ', emph_to_attr(ls, base), (uint8_t)(tgt_full - old_sc));
|
||||
gc_fill(' ', at, (uint8_t)(tgt_full - old_sc));
|
||||
*pseg = tgt; *pq = q + 1; *pprev = ' '; return 1;
|
||||
}
|
||||
return 0; /* пробел или обычный символ — обрабатывает вызывающий */
|
||||
@@ -556,8 +547,7 @@ static uint32_t scan_join_stream(uint32_t q, uint32_t para_start, uint8_t col,
|
||||
if (next_kind != LK_QUOTE) break;
|
||||
|
||||
{
|
||||
uint32_t next_line_end = next_content;
|
||||
while (next_line_end < file_size && fb(next_line_end) != '\n') next_line_end++;
|
||||
uint32_t next_line_end = row_end(next_content);
|
||||
|
||||
{
|
||||
uint8_t empty_quote = 1;
|
||||
@@ -722,12 +712,11 @@ void index_lines(void)
|
||||
/* Пустая строка — разделитель параграфов.
|
||||
* В индекс добавляем не более одной пустой экранной строки подряд. */
|
||||
if (is_line_blank(p)) {
|
||||
while (p < file_size && fb(p) != '\n') p++;
|
||||
if (p < file_size) p++;
|
||||
p = next_line(p);
|
||||
/* Серия пустых строк схлопывается в одну визуальную строку. */
|
||||
if (n_lines == 0 || !(cur_rec.flags & IF_BLANK)) {
|
||||
emit_seg(p, INIT_STYLE_PLAIN, CK_PLAIN, 0);
|
||||
set_blank_cur();
|
||||
set_cur_flags(IF_BLANK);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
@@ -736,16 +725,13 @@ void index_lines(void)
|
||||
if (is_fence_raw(p)) {
|
||||
in_block = !in_block;
|
||||
emit_seg(p, INIT_STYLE_PLAIN, CK_OTHER, 0);
|
||||
set_nowrap_cur();
|
||||
while (p < file_size && fb(p) != '\n') p++;
|
||||
if (p < file_size) p++;
|
||||
set_cur_flags(IF_NOWRAP);
|
||||
p = next_line(p);
|
||||
continue;
|
||||
}
|
||||
if (in_block) {
|
||||
emit_seg(p, INIT_STYLE_PLAIN, CK_OTHER, 0);
|
||||
set_nowrap_cur();
|
||||
set_code_cur();
|
||||
set_hscroll_cur(); /* код скроллится целым блоком */
|
||||
set_cur_flags(IF_NOWRAP | IF_CODE | IF_HSCROLL); /* код скроллится целым блоком */
|
||||
{
|
||||
uint8_t ccx = 0;
|
||||
uint32_t pp = p;
|
||||
@@ -763,18 +749,16 @@ void index_lines(void)
|
||||
pp++;
|
||||
}
|
||||
}
|
||||
while (p < file_size && fb(p) != '\n') p++;
|
||||
if (p < file_size) p++;
|
||||
p = next_line(p);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* ---- горизонтальный разделитель ---- */
|
||||
if (is_hr_raw(p)) {
|
||||
emit_seg(p, INIT_STYLE_PLAIN, CK_OTHER, 0);
|
||||
set_nowrap_cur();
|
||||
set_cur_flags(IF_NOWRAP);
|
||||
gc_fill(0xC4, ATTR_HR, SCREEN_W);
|
||||
while (p < file_size && fb(p) != '\n') p++;
|
||||
if (p < file_size) p++;
|
||||
p = next_line(p);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -814,21 +798,21 @@ void index_lines(void)
|
||||
|
||||
/* Проход 2: верхняя рамка → строки/разделитель → нижняя рамка. */
|
||||
emit_seg(p, INIT_STYLE_PLAIN, CK_OTHER, 0);
|
||||
set_nowrap_cur(); set_hscroll_cur();
|
||||
set_cur_flags(IF_NOWRAP | IF_HSCROLL);
|
||||
table_border(widths, ncols, TBL_TL, TBL_TM, TBL_TR);
|
||||
|
||||
r = p;
|
||||
while (r < tbl_end) {
|
||||
uint32_t le = row_end(r);
|
||||
emit_seg(r, INIT_STYLE_PLAIN, CK_OTHER, 0);
|
||||
set_nowrap_cur(); set_hscroll_cur();
|
||||
set_cur_flags(IF_NOWRAP | IF_HSCROLL);
|
||||
if (table_is_sep_row(r, le)) table_border(widths, ncols, TBL_ML, TBL_MM, TBL_MR);
|
||||
else table_data_row(r, le, widths, ncols);
|
||||
r = (le < file_size) ? le + 1 : le;
|
||||
}
|
||||
|
||||
emit_seg(tbl_end, INIT_STYLE_PLAIN, CK_OTHER, 0);
|
||||
set_nowrap_cur(); set_hscroll_cur();
|
||||
set_cur_flags(IF_NOWRAP | IF_HSCROLL);
|
||||
table_border(widths, ncols, TBL_BL, TBL_BM, TBL_BR);
|
||||
|
||||
p = tbl_end;
|
||||
@@ -849,11 +833,9 @@ void index_lines(void)
|
||||
default: hbase = ATTR_TEXT_TITLE4; break;
|
||||
}
|
||||
emit_seg(p, INIT_STYLE_PLAIN, CK_OTHER, 0);
|
||||
uint32_t line_end = content_off;
|
||||
while (line_end < file_size && fb(line_end) != '\n') line_end++;
|
||||
uint32_t line_end = row_end(content_off);
|
||||
(void)inline_scan(content_off, line_end, 0, CK_OTHER, INIT_STYLE_PLAIN, hbase, 0);
|
||||
while (p < file_size && fb(p) != '\n') p++;
|
||||
if (p < file_size) p++;
|
||||
p = next_line(p);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -882,10 +864,7 @@ void index_lines(void)
|
||||
q = scan_join_stream(q, p, col, CK_LIST, &line_style, JOIN_MODE_LIST);
|
||||
|
||||
/* Переходим к началу следующей физической строки после пункта. */
|
||||
if (q < file_size) {
|
||||
while (q < file_size && fb(q) != '\n') q++;
|
||||
if (q < file_size) q++;
|
||||
}
|
||||
q = next_line(q);
|
||||
|
||||
/* Одна пустая строка между соседними маркерами списка подавляется.
|
||||
* Две и более пустых строки оставляют видимый разделитель. */
|
||||
@@ -893,8 +872,7 @@ void index_lines(void)
|
||||
uint32_t t = q;
|
||||
uint8_t blanks = 0;
|
||||
while (t < file_size && is_line_blank(t)) {
|
||||
while (t < file_size && fb(t) != '\n') t++;
|
||||
if (t < file_size) t++;
|
||||
t = next_line(t);
|
||||
blanks++;
|
||||
// if (blanks >= 2) break;
|
||||
}
|
||||
@@ -934,9 +912,7 @@ void index_lines(void)
|
||||
}
|
||||
q = scan_join_stream(q, p, col, CK_QUOTE, &line_style, JOIN_MODE_QUOTE);
|
||||
|
||||
while (q < file_size && fb(q) != '\n') q++;
|
||||
if (q < file_size) q++;
|
||||
p = q;
|
||||
p = next_line(q);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -962,9 +938,7 @@ void index_lines(void)
|
||||
/* q стоит на завершающем newline параграфа.
|
||||
* Переходим на следующую строку, чтобы внешняя петля её
|
||||
* корректно переклассифицировала. */
|
||||
while (q < file_size && fb(q) != '\n') q++;
|
||||
if (q < file_size) q++;
|
||||
p = q;
|
||||
p = next_line(q);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user