aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbfredl <bjorn.linse@gmail.com>2022-06-29 22:49:37 +0200
committerbfredl <bjorn.linse@gmail.com>2022-07-19 12:38:37 +0200
commit0b63f5afad852d077bb430ad9c9ba4301980d500 (patch)
tree158a26cbf426bdee345a52d9b27c78b8b9cf5681
parentbe3d2f512528f8a99acc262ead8b88d38153759d (diff)
downloadrneovim-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.h1
-rw-r--r--src/nvim/grid_defs.h9
-rw-r--r--src/nvim/msgpack_rpc/channel.c8
-rw-r--r--src/nvim/msgpack_rpc/unpacker.c173
-rw-r--r--src/nvim/msgpack_rpc/unpacker.h2
-rw-r--r--src/nvim/ui_client.c95
-rw-r--r--src/nvim/ui_client.h6
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"