performance improvements

* action() returns void - this gets rid of checks in vt_from_slave()
* split up ACTION_PRINT into ACTION_PRINT (ASCII) and ACTION_UTF8_PRINT
  ACTION_PRINT is on the hot path, and we want it streamlined.
* Remove run-time checkout for unimplemented state transitions, as we
  shouldn't have any of those left.
* Don't re-load current VT state on each iteration in vt_from_slave()
This commit is contained in:
Daniel Eklöf 2019-07-07 16:32:18 +02:00
parent 050f7ea6ea
commit d63629b370
No known key found for this signature in database
GPG key ID: 5BBD4992C116573F
5 changed files with 164 additions and 132 deletions

50
csi.c
View file

@ -91,12 +91,12 @@ sgr_reset(struct terminal *term)
term->vt.attrs.background = term->background;
}
static bool
static void
csi_sgr(struct terminal *term)
{
if (term->vt.params.idx == 0) {
sgr_reset(term);
return true;
return;
}
for (size_t i = 0; i < term->vt.params.idx; i++) {
@ -162,7 +162,8 @@ csi_sgr(struct terminal *term)
i += 4;
} else {
LOG_ERR("invalid CSI SGR sequence");
return false;
abort();
break;
}
break;
}
@ -208,7 +209,8 @@ csi_sgr(struct terminal *term)
i += 4;
} else {
LOG_ERR("invalid CSI SGR sequence");
return false;
abort();
break;
}
break;
}
@ -245,11 +247,10 @@ csi_sgr(struct terminal *term)
default:
LOG_ERR("unimplemented: CSI: SGR: %u", term->vt.params.v[i].value);
return false;
abort();
break;
}
}
return true;
}
static const char *
@ -279,7 +280,7 @@ csi_as_string(struct terminal *term, uint8_t final)
return msg;
}
bool
void
csi_dispatch(struct terminal *term, uint8_t final)
{
LOG_DBG("CSI: %s", csi_as_string(term, final));
@ -287,7 +288,8 @@ csi_dispatch(struct terminal *term, uint8_t final)
if (term->vt.intermediates.idx == 0) {
switch (final) {
case 'c':
return write(term->ptmx, "\033[?6c", 5) == 5;
write(term->ptmx, "\033[?6c", 5);
break;
case 'd': {
/* VPA - vertical line position absolute */
@ -297,7 +299,8 @@ csi_dispatch(struct terminal *term, uint8_t final)
}
case 'm':
return csi_sgr(term);
csi_sgr(term);
break;
case 'A':
term_cursor_up(term, param_get(term, 0, 1));
@ -359,7 +362,8 @@ csi_dispatch(struct terminal *term, uint8_t final)
default:
LOG_ERR("CSI: %s: invalid argument: %d",
csi_as_string(term, final), param);
return false;
abort();
break;
}
term_erase(term, start, end);
@ -394,7 +398,8 @@ csi_dispatch(struct terminal *term, uint8_t final)
default:
LOG_ERR("CSI: %s: invalid argument: %d",
csi_as_string(term, final), param);
return false;
abort();
break;
}
term_erase(term, start, end);
@ -513,7 +518,7 @@ csi_dispatch(struct terminal *term, uint8_t final)
* should provide our own terminfo with *only* \e[?1049h
* (and \e[?1049l for rmcup)
*/
LOG_WARN("ignoring CSI with final 't'");
LOG_WARN("ignoring CSI: %s", csi_as_string(term, final));
break;
case 'n': {
@ -536,13 +541,12 @@ csi_dispatch(struct terminal *term, uint8_t final)
default:
LOG_ERR("unimplemented: CSI: %s, parameter = %d",
csi_as_string(term, final), param);
return false;
abort();
break;
}
return true;
} else {
LOG_ERR("CSI: %s: missing parameter", csi_as_string(term, final));
return false;
abort();
}
break;
}
@ -561,8 +565,6 @@ csi_dispatch(struct terminal *term, uint8_t final)
LOG_ERR("unimplemented: CSI: %s", csi_as_string(term, final));
abort();
}
return true;
}
else if (term->vt.intermediates.idx == 1 &&
@ -746,8 +748,6 @@ csi_dispatch(struct terminal *term, uint8_t final)
LOG_ERR("unimplemented: CSI: %s", csi_as_string(term, final));
abort();
}
return true;
}
else if (term->vt.intermediates.idx == 1 &&
@ -759,10 +759,12 @@ csi_dispatch(struct terminal *term, uint8_t final)
LOG_ERR(
"unimplemented: send device attributes with param = %d",
param);
return false;
abort();
break;
}
return write(term->ptmx, "\033[?6c", 5) == 5;
write(term->ptmx, "\033[?6c", 5);
break;
}
default:
@ -775,6 +777,4 @@ csi_dispatch(struct terminal *term, uint8_t final)
LOG_ERR("unimplemented: CSI: %s", csi_as_string(term, final));
abort();
}
return false;
}

2
csi.h
View file

@ -3,4 +3,4 @@
#include <stdbool.h>
#include "terminal.h"
bool csi_dispatch(struct terminal *term, uint8_t final);
void csi_dispatch(struct terminal *term, uint8_t final);

9
osc.c
View file

@ -7,7 +7,7 @@
#include "log.h"
#include "render.h"
bool
void
osc_dispatch(struct terminal *term)
{
int param = 0;
@ -24,7 +24,7 @@ osc_dispatch(struct terminal *term)
if (!isdigit(c)) {
LOG_ERR("OSC: invalid parameter: %.*s",
(int)term->vt.osc.idx, term->vt.osc.data);
return false;
abort();
}
param *= 10;
@ -47,8 +47,7 @@ osc_dispatch(struct terminal *term)
default:
LOG_ERR("unimplemented: OSC: %.*s",
(int)term->vt.osc.idx, term->vt.osc.data);
return false;
abort();
break;
}
return true;
}

2
osc.h
View file

@ -3,4 +3,4 @@
#include <stdbool.h>
#include "terminal.h"
bool osc_dispatch(struct terminal *term);
void osc_dispatch(struct terminal *term);

233
vt.c
View file

@ -63,8 +63,10 @@ enum action {
ACTION_UTF8_3_ENTRY,
ACTION_UTF8_4_ENTRY,
ACTION_UTF8_COLLECT,
ACTION_UTF8_PRINT,
};
#if 0
static const char *const state_names[] = {
[STATE_SAME] = "no change",
[STATE_GROUND] = "ground",
@ -89,7 +91,7 @@ static const char *const state_names[] = {
[STATE_UTF8_COLLECT] = "UTF-8",
};
#endif
static const char *const action_names[] __attribute__((unused)) = {
[ACTION_NONE] = "no action",
[ACTION_IGNORE] = "ignore",
@ -111,6 +113,7 @@ static const char *const action_names[] __attribute__((unused)) = {
[ACTION_UTF8_3_ENTRY] = "UTF-8 (3 chars) begin",
[ACTION_UTF8_4_ENTRY] = "UTF-8 (4 chars) begin",
[ACTION_UTF8_COLLECT] = "UTF-8 collect",
[ACTION_UTF8_PRINT] = "UTF-8 print",
};
struct state_transition {
@ -566,7 +569,7 @@ esc_as_string(struct terminal *term, uint8_t final)
}
static bool
static void
esc_dispatch(struct terminal *term, uint8_t final)
{
LOG_DBG("ESC: %s", esc_as_string(term, final));
@ -594,7 +597,8 @@ esc_dispatch(struct terminal *term, uint8_t final)
default:
LOG_ERR("%s: invalid charset identifier", esc_as_string(term, final));
return false;
abort();
break;
}
break;
}
@ -611,7 +615,8 @@ esc_dispatch(struct terminal *term, uint8_t final)
default:
LOG_ERR("%s: invalid charset identifier", esc_as_string(term, final));
return false;
abort();
break;
}
break;
}
@ -633,16 +638,104 @@ esc_dispatch(struct terminal *term, uint8_t final)
default:
LOG_ERR("unimplemented: ESC: %s", esc_as_string(term, final));
return false;
abort();
break;
}
}
return true;
}
static bool
action(struct terminal *term, enum action action, uint8_t c)
static inline void
pre_print(struct terminal *term)
{
switch (action) {
if (unlikely(term->print_needs_wrap) && term->auto_margin) {
if (term->cursor.row == term->scroll_region.end - 1) {
term_scroll(term, 1);
term_cursor_to(term, term->cursor.row, 0);
} else
term_cursor_to(term, term->cursor.row + 1, 0);
}
}
static inline void
post_print(struct terminal *term)
{
if (term->cursor.col < term->cols - 1)
term_cursor_right(term, 1);
else
term->print_needs_wrap = true;
}
static inline void
print_insert(struct terminal *term)
{
if (unlikely(term->insert_mode)) {
assert(false && "untested");
grid_memmove(
term->grid, term->cursor.linear + 1, term->cursor.linear,
term->cols - term->cursor.col - 1);
term_damage_update(
term, term->cursor.linear + 1, term->cols - term->cursor.col - 1);
}
}
static void
action_print_utf8(struct terminal *term)
{
pre_print(term);
struct cell *cell = &term->grid->cur_line[term->cursor.col];
term_damage_update(term, term->cursor.linear, 1);
print_insert(term);
//LOG_DBG("print: UTF8: %.*s", (int)term->vt.utf8.idx, term->vt.utf8.data);
memcpy(cell->c, term->vt.utf8.data, term->vt.utf8.idx);
cell->c[term->vt.utf8.idx] = '\0';
term->vt.utf8.idx = 0;
cell->attrs = term->vt.attrs;
post_print(term);
}
static void
action_print(struct terminal *term, uint8_t c)
{
pre_print(term);
struct cell *cell = &term->grid->cur_line[term->cursor.col];
term_damage_update(term, term->cursor.linear, 1);
print_insert(term);
static const char *const vt100_0[62] = { /* 0x41 - 0x7e */
"", "", "", "", "", "", "", /* A - G */
0, 0, 0, 0, 0, 0, 0, 0, /* H - O */
0, 0, 0, 0, 0, 0, 0, 0, /* P - W */
0, 0, 0, 0, 0, 0, 0, " ", /* X - _ */
"", "", "", "", "", "", "°", "±", /* ` - g */
"", "", "", "", "", "", "", "", /* h - o */
"", "", "", "", "", "", "", "", /* p - w */
"", "", "", "π", "", "£", "·", /* x - ~ */
};
if (unlikely(term->charset[term->selected_charset] == CHARSET_GRAPHIC) &&
c >= 0x41 && c <= 0x7e)
{
strcpy(cell->c, vt100_0[c - 0x41]);
} else {
//LOG_DBG("print: ASCII: %c", c);
cell->c[0] = c;
cell->c[1] = '\0';
}
cell->attrs = term->vt.attrs;
post_print(term);
}
static void
action(struct terminal *term, enum action _action, uint8_t c)
{
switch (_action) {
case ACTION_NONE:
break;
@ -685,10 +778,11 @@ action(struct terminal *term, enum action action, uint8_t c)
default:
LOG_ERR("execute: unimplemented: %c (0x%02x)", c, c);
return false;
abort();
break;
}
return true;
break;
case ACTION_CLEAR:
memset(&term->vt.params, 0, sizeof(term->vt.params));
@ -697,64 +791,13 @@ action(struct terminal *term, enum action action, uint8_t c)
term->vt.utf8.idx = 0;
break;
case ACTION_PRINT: {
if (unlikely(term->print_needs_wrap) && term->auto_margin) {
if (term->cursor.row == term->scroll_region.end - 1) {
term_scroll(term, 1);
term_cursor_to(term, term->cursor.row, 0);
} else
term_cursor_to(term, term->cursor.row + 1, 0);
}
struct cell *cell = &term->grid->cur_line[term->cursor.col];
term_damage_update(term, term->cursor.linear, 1);
if (unlikely(term->insert_mode)) {
assert(false && "untested");
grid_memmove(
term->grid, term->cursor.linear + 1, term->cursor.linear,
term->cols - term->cursor.col - 1);
term_damage_update(
term, term->cursor.linear + 1, term->cols - term->cursor.col - 1);
}
if (term->vt.utf8.idx > 0) {
//LOG_DBG("print: UTF8: %.*s", (int)term->vt.utf8.idx, term->vt.utf8.data);
memcpy(cell->c, term->vt.utf8.data, term->vt.utf8.idx);
cell->c[term->vt.utf8.idx] = '\0';
term->vt.utf8.idx = 0;
} else {
static const char *const vt100_0[62] = { /* 0x41 - 0x7e */
"", "", "", "", "", "", "", /* A - G */
0, 0, 0, 0, 0, 0, 0, 0, /* H - O */
0, 0, 0, 0, 0, 0, 0, 0, /* P - W */
0, 0, 0, 0, 0, 0, 0, " ", /* X - _ */
"", "", "", "", "", "", "°", "±", /* ` - g */
"", "", "", "", "", "", "", "", /* h - o */
"", "", "", "", "", "", "", "", /* p - w */
"", "", "", "π", "", "£", "·", /* x - ~ */
};
if (unlikely(term->charset[term->selected_charset] == CHARSET_GRAPHIC) &&
c >= 0x41 && c <= 0x7e)
{
strcpy(cell->c, vt100_0[c - 0x41]);
} else {
//LOG_DBG("print: ASCII: %c", c);
cell->c[0] = c;
cell->c[1] = '\0';
}
}
cell->attrs = term->vt.attrs;
if (term->cursor.col < term->cols - 1)
term_cursor_right(term, 1);
else
term->print_needs_wrap = true;
case ACTION_PRINT:
action_print(term, c);
break;
case ACTION_UTF8_PRINT:
action_print_utf8(term);
break;
}
case ACTION_PARAM:{
if (term->vt.params.idx == 0)
@ -785,14 +828,16 @@ action(struct terminal *term, enum action action, uint8_t c)
break;
LOG_ERR("unimplemented: action ESC dispatch");
return false;
abort();
break;
case ACTION_ESC_DISPATCH:
return esc_dispatch(term, c);
esc_dispatch(term, c);
break;
case ACTION_CSI_DISPATCH:
return csi_dispatch(term, c);
csi_dispatch(term, c);
break;
case ACTION_OSC_START:
term->vt.osc.idx = 0;
@ -806,13 +851,14 @@ action(struct terminal *term, enum action action, uint8_t c)
case ACTION_OSC_END:
assert(term->vt.osc.idx < sizeof(term->vt.osc.data));
term->vt.osc.data[term->vt.osc.idx] = '\0';
return osc_dispatch(term);
osc_dispatch(term);
break;
case ACTION_HOOK:
case ACTION_UNHOOK:
case ACTION_PUT:
LOG_ERR("unimplemented: action %s", action_names[action]);
return false;
LOG_ERR("unimplemented: action %s", action_names[_action]);
abort();
case ACTION_UTF8_2_ENTRY:
term->vt.utf8.idx = 0;
@ -837,51 +883,38 @@ action(struct terminal *term, enum action action, uint8_t c)
case ACTION_UTF8_COLLECT:
term->vt.utf8.data[term->vt.utf8.idx++] = c;
if (--term->vt.utf8.left == 0)
if (--term->vt.utf8.left == 0) {
term->vt.state = STATE_GROUND;
action_print_utf8(term);
}
break;
}
return true;
}
void
vt_from_slave(struct terminal *term, const uint8_t *data, size_t len)
{
//int cursor = term->grid.cursor;
for (size_t i = 0; i < len; i++) {
//LOG_DBG("input: 0x%02x", data[i]);
enum state current_state = term->vt.state;
for (size_t i = 0; i < len; i++) {
if (current_state == STATE_UTF8_COLLECT) {
if (!action(term, ACTION_UTF8_COLLECT, data[i]))
abort();
action(term, ACTION_UTF8_COLLECT, data[i]);
current_state = term->vt.state;
if (current_state == STATE_UTF8_COLLECT)
continue;
if (!action(term, ACTION_PRINT, 0))
abort();
continue;
}
const struct state_transition *transition = &states[current_state][data[i]];
if (transition->action == ACTION_NONE && transition->state == STATE_SAME) {
LOG_ERR("unimplemented transition from %s: 0x%02x",
state_names[current_state], data[i]);
abort();
}
assert(transition->action != ACTION_NONE || transition->state != STATE_SAME);
if (transition->state != STATE_SAME) {
enum action exit_action = exit_actions[current_state];
if (exit_action != ACTION_NONE && !action(term, exit_action, data[i]))
abort();
action(term, exit_action, data[i]);
}
if (!action(term, transition->action, data[i]))
abort();
action(term, transition->action, data[i]);
if (transition->state != STATE_SAME) {
/*
@ -889,10 +922,10 @@ vt_from_slave(struct terminal *term, const uint8_t *data, size_t len)
* state_names[transition->state]);
*/
term->vt.state = transition->state;
current_state = transition->state;
enum action entry_action = entry_actions[transition->state];
if (entry_action != ACTION_NONE && !action(term, entry_action, data[i]))
abort();
action(term, entry_action, data[i]);
}
}
}