This is an automated email from the git hooks/post-receive script.
git pushed a commit to branch master
in repository terminology.
View the commit online.
commit 4a0f442e9caf891e350ede54a56ad0a84fb50b5e
Author: Cedric BAIL <[email protected]>
AuthorDate: Mon Aug 3 13:28:28 2026 -0600
termptyops: write runs of plain ASCII cells in bulk
termpty_text_append() asked, for every single character, which charset is
mapped, whether insert mode is on, how wide the character is and where the
right margin sits -- then made two out-of-line calls, to
_termpty_charset_trans() and termpty_cell_codepoint_att_fill(), to translate
and store it. Callgrind put those two at roughly a third of the cost of
plain-text output.
For a run of plain printable ASCII every one of those answers is the same
throughout: the charset mapping is the identity, nothing is double width, and
the attributes do not change. Hoist them and write the cells directly.
Finding the end of such a run is the codepoint scan kernel added here, the
same question the byte scan answers but over already-decoded cells. One
unsigned compare instead of two: g - 0x20 wraps for anything below 0x20,
pushing it above the 0x5e span that 0x20..0x7e occupies.
The run is deliberately not taken on an iteration that has just wrapped.
max_right is computed at the top of the loop from the pre-wrap cursor, and
the general path keeps using that stale value for the first character on the
new line; a whole run written against it sails past the right margin. That
was caught by tests/text_append_after_right_margin.sh.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
---
src/bin/termptyops.c | 95 ++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 95 insertions(+)
diff --git a/src/bin/termptyops.c b/src/bin/termptyops.c
index ed744a07..447d8c4b 100644
--- a/src/bin/termptyops.c
+++ b/src/bin/termptyops.c
@@ -6,6 +6,7 @@
#include "termptyops.h"
#include "termptygfx.h"
#include "backlog.h"
+#include "simd/simd.h"
#include "miniview.h"
#include <assert.h>
@@ -234,18 +235,87 @@ _dblwidth_pair(Termpty *ty, Termcell *cell, Termcell *partner)
termpty_cell_codepoint_att_fill(ty, 0, cell->att, partner, 1);
}
+/* Whether a run of plain ASCII can take the bulk path. Fixed for the duration
+ * of one text run, so answered once rather than per character.
+ * combining_strike is re-checked per run since the general loop sets it. */
+static Eina_Bool
+_append_fast_ok(const Termpty *ty)
+{
+ /* Charset translation has to be the identity. */
+ if ((ty->termstate.charsetch == '0') || (ty->termstate.charsetch == 'A'))
+ return EINA_FALSE;
+ if (ty->termstate.att.fraktur || ty->termstate.att.encircled)
+ return EINA_FALSE;
+ /* Insert mode shifts the rest of the line for every character. */
+ if (ty->termstate.insert) return EINA_FALSE;
+ /* Autowrap off clamps the cursor instead of wrapping it, with different
+ * enough semantics that it is not worth duplicating here. */
+ if (!ty->termstate.wrap) return EINA_FALSE;
+ /* A link on the incoming cells needs refcounting per run; rare enough that
+ * the general loop can keep it. */
+ if (ty->termstate.att.link_id) return EINA_FALSE;
+ return EINA_TRUE;
+}
+
+/* Write 'n' cells of plain printable ASCII at the cursor. Everything the
+ * general loop re-asks per character is constant across such a run, so it is
+ * hoisted. The caller guarantees n <= max_right - cx, so the run cannot cross
+ * the margin. */
+static void
+_append_ascii_run(Termpty *ty, Termcell *cells, const Eina_Unicode *cp, int n,
+ int max_right)
+{
+ Termatt att = ty->termstate.att;
+ int cx = ty->cursor_state.cx;
+ int k;
+
+ /* Nothing in 0x20..0x7e is ever double width. */
+ att.dblwidth = 0;
+
+ for (k = 0; k < n; k++)
+ {
+ Termcell *dst = &cells[cx + k];
+
+ HANDLE_BLOCK_CODEPOINT_OVERWRITE(ty, dst->codepoint, cp[k]);
+ if (EINA_UNLIKELY(dst->att.link_id))
+ term_link_refcount_dec(ty, dst->att.link_id, 1);
+ dst->codepoint = cp[k];
+ dst->att = att;
+ }
+
+ /* Recorded before the cursor moves, as the per-character path does. */
+ ty->vs16_base_x = cx + n - 1;
+ ty->vs16_base_y = ty->cursor_state.cy;
+
+ /* As the per-character path leaves it: on the last cell written, with
+ * wrapnext set once the margin is reached. */
+ cx += n;
+ if (cx >= max_right)
+ {
+ ty->cursor_state.cx = max_right - 1;
+ ty->cursor_state.wrapnext = 1;
+ }
+ else
+ {
+ ty->cursor_state.cx = cx;
+ ty->cursor_state.wrapnext = 0;
+ }
+}
+
void
termpty_text_append(Termpty *ty, const Eina_Unicode *codepoints, int len)
{
Termcell *cells;
int i, j;
int origin = ty->termstate.left_margin;
+ Eina_Bool fast_ok = _append_fast_ok(ty);
cells = &(TERMPTY_SCREEN(ty, 0, ty->cursor_state.cy));
for (i = 0; i < len; i++)
{
int max_right = ty->w;
Eina_Unicode g;
+ Eina_Bool wrapped = EINA_FALSE;
if (ty->termstate.right_margin &&
(ty->cursor_state.cx < ty->termstate.right_margin))
@@ -311,6 +381,7 @@ termpty_text_append(Termpty *ty, const Eina_Unicode *codepoints, int len)
ty->cursor_state.cy++;
termpty_text_scroll_test(ty, EINA_TRUE);
cells = &(TERMPTY_SCREEN(ty, 0, ty->cursor_state.cy));
+ wrapped = EINA_TRUE;
}
if (ty->termstate.insert)
{
@@ -318,6 +389,30 @@ termpty_text_append(Termpty *ty, const Eina_Unicode *codepoints, int len)
TERMPTY_CELL_COPY(ty, &(cells[j - 1]), &(cells[j]), 1);
}
+ /* Not on an iteration that just wrapped: max_right was computed above
+ * from the pre-wrap cursor, and a whole run written against that stale
+ * value would sail past the right margin. */
+ if (fast_ok && !wrapped && !ty->termstate.combining_strike &&
+ (codepoints[i] >= 0x20) && (codepoints[i] < 0x7f))
+ {
+ int avail = max_right - ty->cursor_state.cx;
+
+ if (avail > 0)
+ {
+ size_t run = simd_scan_plain_ascii_u32(codepoints + i,
+ (size_t)(len - i));
+
+ if (run > (size_t)avail) run = (size_t)avail;
+ if (run >= 2)
+ {
+ _append_ascii_run(ty, cells, codepoints + i, (int)run,
+ max_right);
+ i += (int)run - 1;
+ continue;
+ }
+ }
+ }
+
/* Skip 0-width space or RTL/LTR marks */
if (EINA_UNLIKELY(g >= 0x200b && g <= 0x200f))
{
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.