From 4741f7a6dfa144ce65cf3bbdd4a1bc6bee6df2e5 Mon Sep 17 00:00:00 2001 From: Berke Güzel Date: Tue, 13 Oct 2026 13:36:27 +0300 Subject: Optimize and simplify loops, table lookups, and branchless parsers - Replace sequential colon-counting loop with branchless CTZ bitmask in src/pen.h - Replace linear strchr with 256-bit ASCII punctuation bitmask in src/selection.c - Flatten 3D color cube loop and precompute constants in src/palette.c - Use static 256-byte LUT for hex decoding and bsearch for termcap in src/escape.c - Replace capacity doubling while loops with branchless CLZ pow2 helpers across core - Use direct memset/memcpy in src/snapshot.c - Optimize input digit/byte formatting, alt-prefix path, and make keyboard state optional in src/input.c --- src/input.c | 228 +++++++++++++++++++++++++++--------------------------------- 1 file changed, 102 insertions(+), 126 deletions(-) (limited to 'src/input.c') diff --git a/src/input.c b/src/input.c index 49e41b2..63313be 100644 --- a/src/input.c +++ b/src/input.c @@ -23,20 +23,28 @@ static inline void sb_ch(struct seqbuf *sb, char c) sb->left--; } +static inline void sb_bytes(struct seqbuf *sb, const char *s, size_t len) +{ + if (sb->overflow || len > sb->left) { + sb->overflow = true; + sb->left = 0; + return; + } + memcpy(sb->p, s, len); + sb->p += len; + sb->left -= len; +} + static inline void sb_u32(struct seqbuf *sb, uint32_t val) { char tmp[10]; - size_t n = 0; + char *end = tmp + sizeof(tmp); + char *p = end; do { - tmp[n++] = (char)('0' + (val % 10)); + *--p = (char)('0' + (val % 10)); val /= 10; } while (val); - while (n) sb_ch(sb, tmp[--n]); -} - -static inline void sb_str(struct seqbuf *sb, const char *s, size_t len) -{ - for (size_t i = 0; i < len; i++) sb_ch(sb, s[i]); + sb_bytes(sb, p, (size_t)(end - p)); } static inline size_t sb_len(const struct seqbuf *sb, const char *buf) @@ -44,14 +52,6 @@ static inline size_t sb_len(const struct seqbuf *sb, const char *buf) return (size_t)(sb->p - buf); } -static inline void sb_bytes(struct seqbuf *sb, const char *s, size_t len) -{ - if (len > sb->left) { sb->overflow = true; return; } - memcpy(sb->p, s, len); - sb->p += len; - sb->left -= len; -} - struct kitty_desc { uint32_t code; char final; @@ -323,16 +323,9 @@ static unsigned toggle_locks(enum term_key key, enum term_key_action action, } /* Convert logical input modifiers to terminal protocol bits. */ -static unsigned protocol_modifiers(unsigned mods) +static inline unsigned protocol_modifiers(unsigned mods) { - return ((mods & TERM_MOD_SHIFT) ? 1u : 0u) | - ((mods & TERM_MOD_ALT) ? 2u : 0u) | - ((mods & TERM_MOD_CTRL) ? 4u : 0u) | - ((mods & TERM_MOD_SUPER) ? 8u : 0u) | - ((mods & TERM_MOD_HYPER) ? 16u : 0u) | - ((mods & TERM_MOD_META) ? 32u : 0u) | - ((mods & TERM_MOD_CAPS) ? 64u : 0u) | - ((mods & TERM_MOD_NUM) ? 128u : 0u); + return mods & 0xff; } static void format_kitty(struct seqbuf *sb, uint32_t code, char final, @@ -340,51 +333,38 @@ static void format_kitty(struct seqbuf *sb, uint32_t code, char final, uint32_t base, unsigned mods, unsigned event, bool report_text, uint32_t text) { - char tmp[64]; - struct seqbuf body; - sb_init(&body, tmp, sizeof(tmp)); - + sb_bytes(sb, "\033[", 2); if (final != 'u' && final != '~' && code <= 1 && !shifted && !base && !mods && !(flags & TERM_KEYBOARD_EVENT_TYPES) && !report_text) { + sb_ch(sb, final); + return; + } - sb_ch(&body, '\033'); - sb_ch(&body, '['); - sb_ch(&body, final); - } else { - sb_ch(&body, '\033'); - sb_ch(&body, '['); - sb_u32(&body, code); - if ((flags & TERM_KEYBOARD_ALTERNATE_KEYS) && !functional) { - if (shifted) { - sb_ch(&body, ':'); - sb_u32(&body, shifted); - } - if (base) { - sb_ch(&body, ':'); - if (!shifted) sb_ch(&body, ':'); - sb_u32(&body, base); - } - } - if (mods || (flags & TERM_KEYBOARD_EVENT_TYPES) || report_text) { - sb_ch(&body, ';'); - sb_u32(&body, mods + 1); - } - if ((flags & TERM_KEYBOARD_EVENT_TYPES) && event != 1) { - sb_ch(&body, ':'); - sb_u32(&body, event); + sb_u32(sb, code); + if ((flags & TERM_KEYBOARD_ALTERNATE_KEYS) && !functional) { + if (shifted) { + sb_ch(sb, ':'); + sb_u32(sb, shifted); } - if (report_text) { - sb_ch(&body, ';'); - sb_u32(&body, text); + if (base) { + sb_ch(sb, ':'); + if (!shifted) sb_ch(sb, ':'); + sb_u32(sb, base); } - sb_ch(&body, final); } - - if (body.overflow) { - sb->overflow = true; - return; + if (mods || (flags & TERM_KEYBOARD_EVENT_TYPES) || report_text) { + sb_ch(sb, ';'); + sb_u32(sb, mods + 1); } - sb_str(sb, tmp, sb_len(&body, tmp)); + if ((flags & TERM_KEYBOARD_EVENT_TYPES) && event != 1) { + sb_ch(sb, ':'); + sb_u32(sb, event); + } + if (report_text) { + sb_ch(sb, ';'); + sb_u32(sb, text); + } + sb_ch(sb, final); } static bool encode_kitty(const struct term_keyboard_state *s, @@ -458,8 +438,7 @@ static bool encode_legacy(const struct term_key_event *ev, { if (m->app_keypad && (unsigned)ev->keypad < TERM_KEY_COUNT && legacy_desc[ev->keypad].keypad_ss3) { - sb_ch(sb, '\033'); - sb_ch(sb, 'O'); + sb_bytes(sb, "\033O", 2); sb_ch(sb, legacy_desc[ev->keypad].keypad_ss3); return true; } @@ -486,8 +465,7 @@ static bool encode_legacy(const struct term_key_event *ev, const struct legacy_desc *d = &legacy_desc[k]; if (d->final) { if (d->tilde) { - sb_ch(sb, '\033'); - sb_ch(sb, '['); + sb_bytes(sb, "\033[", 2); sb_u32(sb, d->tilde); if (csi) { sb_ch(sb, ';'); @@ -497,10 +475,7 @@ static bool encode_legacy(const struct term_key_event *ev, return true; } if (csi) { - sb_ch(sb, '\033'); - sb_ch(sb, '['); - sb_ch(sb, '1'); - sb_ch(sb, ';'); + sb_bytes(sb, "\033[1;", 4); sb_u32(sb, p); sb_ch(sb, d->final); return true; @@ -518,7 +493,7 @@ static bool encode_legacy(const struct term_key_event *ev, sb_ch(sb, m->backarrow_bs ? '\x08' : '\x7f'); return true; case TERM_KEY_TAB: - if (shift) { sb_str(sb, "\033[Z", 3); } + if (shift) { sb_bytes(sb, "\033[Z", 3); } else { sb_ch(sb, '\t'); } return true; case TERM_KEY_ESCAPE: sb_ch(sb, '\033'); return true; @@ -528,7 +503,7 @@ static bool encode_legacy(const struct term_key_event *ev, if (ev->text && !ev->text_deferred) { char tmp[8]; size_t n = (size_t)utf8_encode(ev->text, tmp); - sb_str(sb, tmp, n); + sb_bytes(sb, tmp, n); return true; } return false; @@ -550,13 +525,35 @@ bool term_keyboard_key_down(const struct term_keyboard_state *s, enum term_key k ((s->down[key >> 6] >> (key & 63)) & 1); } +static inline bool legacy_is_escape(const struct term_key_event *ev, + const struct term_modes *m) +{ + if (m->app_keypad && (unsigned)ev->keypad < TERM_KEY_COUNT && + legacy_desc[ev->keypad].keypad_ss3) + return true; + if ((ev->mods & TERM_MOD_CTRL) && control_base_char(ev) == '[') + return true; + enum term_key k = ev->key; + if ((unsigned)k < TERM_KEY_COUNT && legacy_desc[k].equiv != TERM_KEY_NONE) + k = legacy_desc[k].equiv; + if ((unsigned)k < TERM_KEY_COUNT && legacy_desc[k].final) + return true; + if (k == TERM_KEY_ESCAPE) + return true; + if (k == TERM_KEY_TAB && (ev->mods & TERM_MOD_SHIFT)) + return true; + if (ev->text == '\033') + return true; + return false; +} + struct term_input_result term_input_encode_key(const struct term_keyboard_state *s, const struct term_key_event *ev, const struct term_modes *m, char *buf, size_t buf_sz) { struct term_input_result out = {0}; - if (!s || !ev || !m || !buf || + if (!ev || !m || !buf || (ev->action != TERM_KEY_PRESS && ev->action != TERM_KEY_REPEAT && ev->action != TERM_KEY_RELEASE) || (unsigned)ev->key >= TERM_KEY_COUNT) @@ -570,46 +567,20 @@ struct term_input_result term_input_encode_key(const struct term_keyboard_state if (ev->action == TERM_KEY_RELEASE) return out; - bool alt = (ev->mods & TERM_MOD_ALT) != 0; - struct seqbuf body; - char tmp[64]; - if (alt) { - sb_init(&body, tmp, sizeof(tmp)); - } else { - body = sb; - } + if ((ev->mods & TERM_MOD_ALT) && !legacy_is_escape(ev, m)) + sb_ch(&sb, '\033'); - handled = encode_legacy(ev, m, &body); - if (handled) { - if (body.overflow) { - sb.overflow = true; - } else { - size_t n = sb_len(&body, alt ? tmp : buf); - if (alt) { - bool already_esc = n && tmp[0] == '\033'; - size_t prefix = !already_esc; - if (n + prefix <= buf_sz) { - if (prefix) buf[0] = '\033'; - memcpy(buf + prefix, tmp, n); - n += prefix; - } else { - sb.overflow = true; - n = 0; - } - } - out.len = n; - } - } - } else { - out.len = sb_len(&sb, buf); + handled = encode_legacy(ev, m, &sb); } if (!handled) return out; + if (sb.overflow) { out.status = TERM_INPUT_BUFFER_FULL; out.len = 0; } else { + out.len = sb_len(&sb, buf); out.status = out.len ? TERM_INPUT_BYTES : TERM_INPUT_CONSUMED; } return out; @@ -665,18 +636,15 @@ struct term_input_result term_input_encode_mouse(const struct term_mouse_event * if (m->mouse_encoding == TERM_MOUSE_ENC_SGR || m->mouse_encoding == TERM_MOUSE_ENC_SGR_PIXELS) { - sb_ch(&sb, '\033'); - sb_ch(&sb, '['); - sb_ch(&sb, '<'); + sb_bytes(&sb, "\033[<", 3); sb_u32(&sb, (uint32_t)code); sb_ch(&sb, ';'); - sb_u32(&sb, (uint32_t)report_x); + sb_u32(&sb, report_x); sb_ch(&sb, ';'); - sb_u32(&sb, (uint32_t)report_y); + sb_u32(&sb, report_y); sb_ch(&sb, ev->kind == TERM_MOUSE_RELEASE ? 'm' : 'M'); } else if (m->mouse_encoding == TERM_MOUSE_ENC_URXVT) { - sb_ch(&sb, '\033'); - sb_ch(&sb, '['); + sb_bytes(&sb, "\033[", 2); sb_u32(&sb, (uint32_t)(legacy_code + 32)); sb_ch(&sb, ';'); sb_u32(&sb, (uint32_t)(cell.x + 1)); @@ -688,21 +656,22 @@ struct term_input_result term_input_encode_mouse(const struct term_mouse_event * (unsigned)legacy_code + 32, (unsigned)cell.x + 33, (unsigned)cell.y + 33, }; - sb_str(&sb, "\033[M", 3); + sb_bytes(&sb, "\033[M", 3); for (int i = 0; i < 3; i++) { char tmp[8]; size_t n = (size_t)utf8_encode(values[i], tmp); - sb_str(&sb, tmp, n); + sb_bytes(&sb, tmp, n); } } else { if (legacy_code > 223 || cell.x > 222 || cell.y > 222) return out; - sb_ch(&sb, '\033'); - sb_ch(&sb, '['); - sb_ch(&sb, 'M'); - sb_ch(&sb, (char)(legacy_code + 32)); - sb_ch(&sb, (char)(cell.x + 33)); - sb_ch(&sb, (char)(cell.y + 33)); + char tmp[6] = { + '\033', '[', 'M', + (char)(legacy_code + 32), + (char)(cell.x + 33), + (char)(cell.y + 33), + }; + sb_bytes(&sb, tmp, 6); } if (sb.overflow) { @@ -737,6 +706,7 @@ struct term_input_result term_input_encode_paste(const struct term_modes *m, if (m->bracketed_paste) sb_bytes(&sb, "\033[200~", 6); size_t body = sb_len(&sb, buf); for (size_t i = 0; i < len;) { + if (sb.overflow) break; size_t start = i; while (i < len && (unsigned char)text[i] >= 0x20 && (unsigned char)text[i] < 0x7f) i++; @@ -744,15 +714,21 @@ struct term_input_result term_input_encode_paste(const struct term_modes *m, sb_bytes(&sb, text + start, i - start); continue; } + unsigned char c = (unsigned char)text[i]; + if (c < 0x80) { + if (c == '\r' || c == '\n') { + sb_ch(&sb, '\r'); + if (c == '\r' && i + 1 < len && text[i + 1] == '\n') i++; + } else if (c == '\t') { + sb_ch(&sb, '\t'); + } + i++; + continue; + } uint32_t cp; int n = utf8_decode_one(text + i, len - i, &cp); if (!n) { i++; continue; } - if (cp == '\r' || cp == '\n') { - sb_ch(&sb, '\r'); - if (cp == '\r' && i + 1 < len && text[i + 1] == '\n') i++; - } else if (cp == '\t') { - sb_ch(&sb, '\t'); - } else if (cp >= 0xa0 && !is_direction_format(cp)) { + if (cp >= 0xa0 && !is_direction_format(cp)) { sb_bytes(&sb, text + i, (size_t)n); } i += (size_t)n; -- cgit v1.2.3