st: add bulk ASCII fast path to twrite
Streaming text spent ~50% of its cycles in per-character tputc/tsetchar dispatch. tputcrun() writes runs of plain printable ASCII directly into the current line as whole cells, bailing out to tputc() whenever anything non-trivial is in play (escape state, print/insert/sixel modes, GRAPHIC0 charset, pending wrap, active selection, non-ASCII bytes). Cuts user time ~3x (0.79s -> 0.26s) and wall time ~45% when streaming 54MB of text. Verified pixel-identical rendering against the previous build for escape-sequence torture tests, wide/CJK/emoji glyphs, box-drawing, alt screen, scroll regions, insert/delete, resize reflow, X primary selection contents and a syntax-highlighted vim session.
This commit is contained in:
parent
3a4e096d9b
commit
6fdc12966f
86
st.c
86
st.c
@ -3707,6 +3707,84 @@ check_control_code:
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Print a run of plain printable ASCII characters using the current cursor
|
||||
* attributes, writing whole cells directly into the current line. Returns the
|
||||
* number of bytes consumed, or 0 when the fast path does not apply and the
|
||||
* caller must fall back to tputc().
|
||||
*/
|
||||
static int
|
||||
tputcrun(const char *buf, int buflen)
|
||||
{
|
||||
Glyph attr = term.c.attr;
|
||||
Glyph *gp;
|
||||
int i, run, avail;
|
||||
|
||||
/* Only handle the simple, overwhelmingly common case. */
|
||||
if (term.esc || (term.mode & (MODE_PRINT|MODE_INSERT|MODE_SIXEL)) ||
|
||||
term.trantbl[term.charset] == CS_GRAPHIC0 ||
|
||||
(term.c.state & CURSOR_WRAPNEXT) ||
|
||||
sel.mode != SEL_IDLE || sel.ob.x != -1)
|
||||
return 0;
|
||||
|
||||
avail = term.col - term.c.x;
|
||||
if (avail <= 0)
|
||||
return 0;
|
||||
|
||||
/* Count leading printable ASCII bytes, bounded by the line width. */
|
||||
for (run = 0; run < buflen && run < avail; run++) {
|
||||
unsigned char c = (unsigned char)buf[run];
|
||||
if (c < 0x20 || c >= 0x7f)
|
||||
break;
|
||||
}
|
||||
if (run < 2)
|
||||
return 0; /* not worth it; keep tputc's semantics */
|
||||
|
||||
attr.mode |= ATTR_SET;
|
||||
attr.mode &= ~(ATTR_WIDE|ATTR_WDUMMY|ATTR_BOXDRAW|ATTR_KITTYPH);
|
||||
#if OSC7_OSC8_PATCH
|
||||
attr.link = curlink;
|
||||
#endif // OSC7_OSC8_PATCH
|
||||
#if KITTY_GRAPHICS_PATCH
|
||||
attr.kitty = 0;
|
||||
#endif // KITTY_GRAPHICS_PATCH
|
||||
|
||||
gp = &term.line[term.c.y][term.c.x];
|
||||
|
||||
/* A wide glyph may straddle the start of the run. */
|
||||
if (gp->mode & ATTR_WDUMMY) {
|
||||
if (term.c.x == 0)
|
||||
return 0;
|
||||
gp[-1].u = ' ';
|
||||
gp[-1].mode &= ~ATTR_WIDE;
|
||||
}
|
||||
/* ...or the end of it. */
|
||||
if (run < avail && (gp[run].mode & ATTR_WDUMMY)) {
|
||||
gp[run].u = ' ';
|
||||
gp[run].mode &= ~ATTR_WDUMMY;
|
||||
}
|
||||
|
||||
for (i = 0; i < run; i++) {
|
||||
gp[i] = attr;
|
||||
gp[i].u = (unsigned char)buf[i];
|
||||
}
|
||||
|
||||
term.dirty[term.c.y] = 1;
|
||||
term.lastc = (unsigned char)buf[run-1];
|
||||
|
||||
if (term.c.x + run < term.col) {
|
||||
term.c.x += run;
|
||||
} else {
|
||||
term.c.x = term.col - 1;
|
||||
#if REFLOW_PATCH
|
||||
term.wrapcwidth[IS_SET(MODE_ALTSCREEN)] = 1;
|
||||
#endif // REFLOW_PATCH
|
||||
term.c.state |= CURSOR_WRAPNEXT;
|
||||
}
|
||||
|
||||
return run;
|
||||
}
|
||||
|
||||
int
|
||||
twrite(const char *buf, int buflen, int show_ctrl)
|
||||
{
|
||||
@ -3720,6 +3798,14 @@ twrite(const char *buf, int buflen, int show_ctrl)
|
||||
#endif // SYNC_PATCH
|
||||
|
||||
for (n = 0; n < buflen; n += charsize) {
|
||||
/*
|
||||
* Fast path: a run of plain printable ASCII printed with the
|
||||
* current attributes onto the current line. This skips the
|
||||
* per-character tputc/tsetchar dispatch, which dominates the
|
||||
* profile while streaming large amounts of text.
|
||||
*/
|
||||
if (!show_ctrl && (charsize = tputcrun(buf + n, buflen - n)) > 0)
|
||||
continue;
|
||||
#if SIXEL_PATCH
|
||||
if (IS_SET(MODE_SIXEL) && sixel_st.state != PS_ESC) {
|
||||
charsize = sixel_parser_parse(&sixel_st, (const unsigned char*)buf + n, buflen - n);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user