diff options
author | bfredl <bjorn.linse@gmail.com> | 2022-06-29 22:49:37 +0200 |
---|---|---|
committer | bfredl <bjorn.linse@gmail.com> | 2022-07-19 12:38:37 +0200 |
commit | 0b63f5afad852d077bb430ad9c9ba4301980d500 (patch) | |
tree | 158a26cbf426bdee345a52d9b27c78b8b9cf5681 | |
parent | be3d2f512528f8a99acc262ead8b88d38153759d (diff) | |
download | rneovim-0b63f5afad852d077bb430ad9c9ba4301980d500.tar.gz rneovim-0b63f5afad852d077bb430ad9c9ba4301980d500.tar.bz2 rneovim-0b63f5afad852d077bb430ad9c9ba4301980d500.zip |
perf(ui): unpack grid_line (screen contents) directly
-rw-r--r-- | src/nvim/api/private/defs.h | 1 | ||||
-rw-r--r-- | src/nvim/grid_defs.h | 9 | ||||
-rw-r--r-- | src/nvim/msgpack_rpc/channel.c | 8 | ||||
-rw-r--r-- | src/nvim/msgpack_rpc/unpacker.c | 173 | ||||
-rw-r--r-- | src/nvim/msgpack_rpc/unpacker.h | 2 | ||||
-rw-r--r-- | src/nvim/ui_client.c | 95 | ||||
-rw-r--r-- | src/nvim/ui_client.h | 6 |
7 files changed, 175 insertions, 119 deletions
diff --git a/src/nvim/api/private/defs.h b/src/nvim/api/private/defs.h index b1e0dd364c..9c7e59e4b3 100644 --- a/src/nvim/api/private/defs.h +++ b/src/nvim/api/private/defs.h @@ -36,6 +36,7 @@ typedef enum { kMessageTypeRequest = 0, kMessageTypeResponse = 1, kMessageTypeNotification = 2, + kMessageTypeRedrawEvent = 3, } MessageType; /// Mask for all internal calls diff --git a/src/nvim/grid_defs.h b/src/nvim/grid_defs.h index 1571340849..9252b8a371 100644 --- a/src/nvim/grid_defs.h +++ b/src/nvim/grid_defs.h @@ -128,4 +128,13 @@ typedef struct { const char *start; ///< Location where region starts. } StlClickRecord; +typedef struct { + int args[3]; + int icell; + int ncells; + int coloff; + int cur_attr; + int clear_width; +} GridLineEvent; + #endif // NVIM_GRID_DEFS_H diff --git a/src/nvim/msgpack_rpc/channel.c b/src/nvim/msgpack_rpc/channel.c index 0bb5d800f1..1ca68979f4 100644 --- a/src/nvim/msgpack_rpc/channel.c +++ b/src/nvim/msgpack_rpc/channel.c @@ -239,11 +239,13 @@ static void parse_msgpack(Channel *channel) { Unpacker *p = channel->rpc.unpacker; while (unpacker_advance(p)) { - if (p->is_ui) { - if (p->ui_handler.fn != NULL && p->result.type == kObjectTypeArray) { + if (p->type == kMessageTypeRedrawEvent) { + if (p->grid_line_event) { + ui_client_event_raw_line(p->grid_line_event); + } else if (p->ui_handler.fn != NULL && p->result.type == kObjectTypeArray) { p->ui_handler.fn(p->result.data.array); + arena_mem_free(arena_finish(&p->arena), &p->reuse_blk); } - arena_mem_free(arena_finish(&p->arena), &p->reuse_blk); } else if (p->type == kMessageTypeResponse) { ChannelCallFrame *frame = kv_last(channel->rpc.call_stack); if (p->request_id != frame->request_id) { diff --git a/src/nvim/msgpack_rpc/unpacker.c b/src/nvim/msgpack_rpc/unpacker.c index efbb3110d9..d73557ec11 100644 --- a/src/nvim/msgpack_rpc/unpacker.c +++ b/src/nvim/msgpack_rpc/unpacker.c @@ -6,6 +6,7 @@ #include "nvim/memory.h" #include "nvim/msgpack_rpc/helpers.h" #include "nvim/msgpack_rpc/unpacker.h" +#include "nvim/ui_client.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "msgpack_rpc/unpacker.c.generated.h" @@ -280,10 +281,20 @@ error: // is relatively small, just ~10 bytes + the method name. Thus we can simply refuse // to advance the stream beyond the header until it can be parsed in its entirety. // -// Of course, later on, we want to specialize state 2 into sub-states depending +// Later on, we want to specialize state 2 into more sub-states depending // on the specific method. "nvim_exec_lua" should just decode direct into lua -// objects, and "redraw/grid_line" should use a hand-rolled decoder to avoid -// a blizzard of small objects for each screen cell. +// objects. For the moment "redraw/grid_line" uses a hand-rolled decoder, +// to avoid a blizzard of small objects for each screen cell. +// +// <0>[2, "redraw", <10>[{11}["method", <12>[args], <12>[args], ...], <11>[...], ...]] +// +// Where [args] gets unpacked as an Array. Note: first {11} is not saved as a state. +// +// When method is "grid_line", we furthermore decode a cell at a time like: +// +// <0>[2, "redraw", <10>[{11}["grid_line", <13>[g, r, c, [<14>[cell], <14>[cell], ...]], ...], <11>[...], ...]] +// +// where [cell] is [char, repeat, attr], where 'repeat' and 'attr' is optional bool unpacker_advance(Unpacker *p) { @@ -293,6 +304,7 @@ bool unpacker_advance(Unpacker *p) return false; } if (p->type == kMessageTypeNotification && p->handler.fn == handle_ui_client_redraw) { + p->type = kMessageTypeRedrawEvent; p->state = 10; } else { p->state = p->type == kMessageTypeResponse ? 1 : 2; @@ -300,11 +312,18 @@ bool unpacker_advance(Unpacker *p) } } - if (p->state == 10 || p->state == 11) { + if (p->state >= 10 && p->state != 12) { if (!unpacker_parse_redraw(p)) { return false; } - arena_start(&p->arena, &p->reuse_blk); + + if (p->state == 14) { + // grid_line event already unpacked + goto done; + } else { + // unpack other ui events using mpack_parse() + arena_start(&p->arena, &p->reuse_blk); + } } int result; @@ -321,6 +340,7 @@ rerun: return false; } +done: switch (p->state) { case 1: p->error = p->result; @@ -328,28 +348,23 @@ rerun: goto rerun; case 2: p->state = 0; - p->is_ui = false; return true; case 12: + case 14: p->ncalls--; if (p->ncalls > 0) { - p->state = 12; + p->state = (p->state == 14) ? 13 : 12; } else if (p->nevents > 0) { p->state = 11; } else { p->state = 0; } - // TODO: fold into p->type - p->is_ui = true; return true; default: abort(); } } -// note: first {11} is not saved as a state -// <0>[2, "redraw", <10>[{11}["method", <12>[args], <12>[args], ...], <11>[...], ...]] -// bool unpacker_parse_redraw(Unpacker *p) { mpack_token_t tok; @@ -357,40 +372,36 @@ bool unpacker_parse_redraw(Unpacker *p) const char *data = p->read_ptr; size_t size = p->read_size; + GridLineEvent *g = p->grid_line_event; + +#define NEXT_TYPE(tok, typ) \ + result = mpack_rtoken(&data, &size, &tok); \ + if (result == MPACK_EOF) { \ + return false; \ + } else if (result || (tok.type != typ \ + && !(typ == MPACK_TOKEN_STR && tok.type == MPACK_TOKEN_BIN) \ + && !(typ == MPACK_TOKEN_SINT && tok.type == MPACK_TOKEN_UINT))) { \ + p->state = -1; \ + return false; \ + } + +redo: switch (p->state) { case 10: - result = mpack_rtoken(&data, &size, &tok); - if (result == MPACK_EOF) { - return false; - } else if (result || tok.type != MPACK_TOKEN_ARRAY) { - p->state = -1; - return false; - } - + NEXT_TYPE(tok, MPACK_TOKEN_ARRAY); p->nevents = (int)tok.length; FALLTHROUGH; case 11: - result = mpack_rtoken(&data, &size, &tok); - if (result == MPACK_EOF) { - return false; - } else if (result || tok.type != MPACK_TOKEN_ARRAY) { - abort(); - } - + NEXT_TYPE(tok, MPACK_TOKEN_ARRAY); p->ncalls = (int)tok.length; + if (p->ncalls-- == 0) { p->state = -1; return false; } - result = mpack_rtoken(&data, &size, &tok); - if (result == MPACK_EOF) { - return false; - } else if (result || (tok.type != MPACK_TOKEN_STR && tok.type != MPACK_TOKEN_BIN)) { - abort(); - } - + NEXT_TYPE(tok, MPACK_TOKEN_STR); if (tok.length > size) { return false; } @@ -402,8 +413,98 @@ bool unpacker_parse_redraw(Unpacker *p) p->nevents--; p->read_ptr = data; p->read_size = size; - p->state = 12; - return true; + if (p->ui_handler.fn != ui_client_event_grid_line) { + p->state = 12; + if (p->grid_line_event) { + arena_mem_free(arena_finish(&p->arena), &p->reuse_blk); + p->grid_line_event = NULL; + } + return true; + } else { + p->state = 13; + arena_start(&p->arena, &p->reuse_blk); + p->grid_line_event = arena_alloc(&p->arena, sizeof *p->grid_line_event, true); + g = p->grid_line_event; + FALLTHROUGH; + } + + case 13: + NEXT_TYPE(tok, MPACK_TOKEN_ARRAY); + int eventarrsize = (int)tok.length; + if (eventarrsize != 4) { + p->state = -1; + return false; + } + + for (int i = 0; i < 3; i++) { + NEXT_TYPE(tok, MPACK_TOKEN_UINT); + g->args[i] = (int)tok.data.value.lo; + } + + NEXT_TYPE(tok, MPACK_TOKEN_ARRAY); + g->ncells = (int)tok.length; + g->icell = 0; + g->coloff = 0; + g->cur_attr = -1; + + p->read_ptr = data; + p->read_size = size; + p->state = 14; + FALLTHROUGH; + + case 14: + assert(g->icell < g->ncells); + + NEXT_TYPE(tok, MPACK_TOKEN_ARRAY); + int cellarrsize = (int)tok.length; + if (cellarrsize < 1 || cellarrsize > 3) { + p->state = -1; + return false; + } + + NEXT_TYPE(tok, MPACK_TOKEN_STR); + if (tok.length > size) { + return false; + } + + const char *cellbuf = data; + size_t cellsize = tok.length; + data += cellsize; + size -= cellsize; + + if (cellarrsize >= 2) { + NEXT_TYPE(tok, MPACK_TOKEN_SINT); + g->cur_attr = (int)tok.data.value.lo; + } + + int repeat = 1; + if (cellarrsize >= 3) { + NEXT_TYPE(tok, MPACK_TOKEN_UINT); + repeat = (int)tok.data.value.lo; + } + + g->clear_width = 0; + if (g->icell == g->ncells - 1 && cellsize == 1 && cellbuf[0] == ' ' && repeat > 1) { + g->clear_width = repeat; + } else { + for (int r = 0; r < repeat; r++) { + if (g->coloff >= (int)grid_line_buf_size) { + p->state = -1; + return false; + } + memcpy(grid_line_buf_char[g->coloff], cellbuf, cellsize); + grid_line_buf_char[g->coloff][cellsize] = NUL; + grid_line_buf_attr[g->coloff++] = g->cur_attr; + } + } + + g->icell++; + p->read_ptr = data; + p->read_size = size; + if (g->icell == g->ncells) { + return true; + } + goto redo; default: abort(); diff --git a/src/nvim/msgpack_rpc/unpacker.h b/src/nvim/msgpack_rpc/unpacker.h index 3a5476cd21..f39439be63 100644 --- a/src/nvim/msgpack_rpc/unpacker.h +++ b/src/nvim/msgpack_rpc/unpacker.h @@ -35,10 +35,10 @@ struct Unpacker { // one length free-list of reusable blocks ArenaMem reuse_blk; - bool is_ui; int nevents; int ncalls; UIClientHandler ui_handler; + GridLineEvent *grid_line_event; }; // unrecovareble error. unpack_error should be set! diff --git a/src/nvim/ui_client.c b/src/nvim/ui_client.c index 5f4cb63f87..09e971f03f 100644 --- a/src/nvim/ui_client.c +++ b/src/nvim/ui_client.c @@ -22,11 +22,6 @@ # include "ui_events_client.generated.h" #endif -// Temporary buffer for converting a single grid_line event -static size_t buf_size = 0; -static schar_T *buf_char = NULL; -static sattr_T *buf_attr = NULL; - void ui_client_init(uint64_t chan) { Array args = ARRAY_DICT_INIT; @@ -106,88 +101,30 @@ void ui_client_event_grid_resize(Array args) Integer height = args.items[2].data.integer; ui_call_grid_resize(grid, width, height); - if (buf_size < (size_t)width) { - xfree(buf_char); - xfree(buf_attr); - buf_size = (size_t)width; - buf_char = xmalloc(buf_size * sizeof(schar_T)); - buf_attr = xmalloc(buf_size * sizeof(sattr_T)); + if (grid_line_buf_size < (size_t)width) { + xfree(grid_line_buf_char); + xfree(grid_line_buf_attr); + grid_line_buf_size = (size_t)width; + grid_line_buf_char = xmalloc(grid_line_buf_size * sizeof(schar_T)); + grid_line_buf_attr = xmalloc(grid_line_buf_size * sizeof(sattr_T)); } } void ui_client_event_grid_line(Array args) + FUNC_ATTR_NORETURN { - if (args.size < 4 - || args.items[0].type != kObjectTypeInteger - || args.items[1].type != kObjectTypeInteger - || args.items[2].type != kObjectTypeInteger - || args.items[3].type != kObjectTypeArray) { - goto error; - } + abort(); // unreachable +} - Integer grid = args.items[0].data.integer; - Integer row = args.items[1].data.integer; - Integer startcol = args.items[2].data.integer; - Array cells = args.items[3].data.array; +void ui_client_event_raw_line(GridLineEvent *g) +{ + int grid = g->args[0], row = g->args[1], startcol = g->args[2]; + Integer endcol = startcol + g->coloff; + Integer clearcol = endcol + g->clear_width; // TODO(hlpr98): Accommodate other LineFlags when included in grid_line LineFlags lineflags = 0; - size_t j = 0; - int cur_attr = 0; - int clear_attr = 0; - int clear_width = 0; - for (size_t i = 0; i < cells.size; i++) { - if (cells.items[i].type != kObjectTypeArray) { - goto error; - } - Array cell = cells.items[i].data.array; - - if (cell.size < 1 || cell.items[0].type != kObjectTypeString) { - goto error; - } - String sstring = cell.items[0].data.string; - - char *schar = sstring.data; - int repeat = 1; - if (cell.size >= 2) { - if (cell.items[1].type != kObjectTypeInteger - || cell.items[1].data.integer < 0) { - goto error; - } - cur_attr = (int)cell.items[1].data.integer; - } - - if (cell.size >= 3) { - if (cell.items[2].type != kObjectTypeInteger - || cell.items[2].data.integer < 0) { - goto error; - } - repeat = (int)cell.items[2].data.integer; - } - - if (i == cells.size - 1 && sstring.size == 1 && sstring.data[0] == ' ' && repeat > 1) { - clear_width = repeat; - break; - } - - for (int r = 0; r < repeat; r++) { - if (j >= buf_size) { - goto error; // _YIKES_ - } - STRLCPY(buf_char[j], schar, sizeof(schar_T)); - buf_attr[j++] = cur_attr; - } - } - - Integer endcol = startcol + (int)j; - Integer clearcol = endcol + clear_width; - clear_attr = cur_attr; - - ui_call_raw_line(grid, row, startcol, endcol, clearcol, clear_attr, lineflags, - (const schar_T *)buf_char, (const sattr_T *)buf_attr); - return; - -error: - ELOG("Error handling ui event 'grid_line'"); + ui_call_raw_line(grid, row, startcol, endcol, clearcol, g->cur_attr, lineflags, + (const schar_T *)grid_line_buf_char, grid_line_buf_attr); } diff --git a/src/nvim/ui_client.h b/src/nvim/ui_client.h index 41d9fa6227..311dafaa0b 100644 --- a/src/nvim/ui_client.h +++ b/src/nvim/ui_client.h @@ -2,12 +2,18 @@ #define NVIM_UI_CLIENT_H #include "nvim/api/private/defs.h" +#include "nvim/grid_defs.h" typedef struct { const char *name; void (*fn)(Array args); } UIClientHandler; +// Temporary buffer for converting a single grid_line event +EXTERN size_t grid_line_buf_size INIT(= 0); +EXTERN schar_T *grid_line_buf_char INIT(= NULL); +EXTERN sattr_T *grid_line_buf_attr INIT(= NULL); + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "ui_client.h.generated.h" |