mirror of
https://github.com/neovim/neovim.git
synced 2025-10-06 18:06:30 +00:00
perf(ui): unpack grid_line (screen contents) directly
This commit is contained in:
@@ -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) {
|
||||
|
@@ -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();
|
||||
|
@@ -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!
|
||||
|
Reference in New Issue
Block a user