mirror of
https://github.com/neovim/neovim.git
synced 2025-02-25 18:55:25 -06:00
perf(memory): use an arena for RPC decoding
drawback: tracing memory errors with ASAN is less accurate for arena allocated memory. Therefore, to start with it is being used for Object types around serialization/deserialization exclusively. This is going to have a large impact especially when TUI is refactored as a co-prosess as all UI events will be serialized and deserialized by nvim itself.
This commit is contained in:
parent
7f8f8d6cb7
commit
3f5c647de9
@ -7893,7 +7893,8 @@ static void f_rpcrequest(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|||||||
uint64_t chan_id = (uint64_t)argvars[0].vval.v_number;
|
uint64_t chan_id = (uint64_t)argvars[0].vval.v_number;
|
||||||
const char *method = tv_get_string(&argvars[1]);
|
const char *method = tv_get_string(&argvars[1]);
|
||||||
|
|
||||||
Object result = rpc_send_call(chan_id, method, args, &err);
|
ArenaMem res_mem = NULL;
|
||||||
|
Object result = rpc_send_call(chan_id, method, args, &res_mem, &err);
|
||||||
|
|
||||||
if (l_provider_call_nesting) {
|
if (l_provider_call_nesting) {
|
||||||
current_sctx = save_current_sctx;
|
current_sctx = save_current_sctx;
|
||||||
@ -7928,7 +7929,7 @@ static void f_rpcrequest(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|||||||
}
|
}
|
||||||
|
|
||||||
end:
|
end:
|
||||||
api_free_object(result);
|
arena_mem_free(res_mem, NULL);
|
||||||
api_clear_error(&err);
|
api_clear_error(&err);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1016,10 +1016,11 @@ static int nlua_rpc(lua_State *lstate, bool request)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (request) {
|
if (request) {
|
||||||
Object result = rpc_send_call(chan_id, name, args, &err);
|
ArenaMem res_mem = NULL;
|
||||||
|
Object result = rpc_send_call(chan_id, name, args, &res_mem, &err);
|
||||||
if (!ERROR_SET(&err)) {
|
if (!ERROR_SET(&err)) {
|
||||||
nlua_push_Object(lstate, result, false);
|
nlua_push_Object(lstate, result, false);
|
||||||
api_free_object(result);
|
arena_mem_free(res_mem, NULL);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!rpc_send_event(chan_id, name, args)) {
|
if (!rpc_send_event(chan_id, name, args)) {
|
||||||
|
@ -524,6 +524,87 @@ void time_to_bytes(time_t time_, uint8_t buf[8])
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define ARENA_BLOCK_SIZE 4096
|
||||||
|
|
||||||
|
void arena_start(Arena *arena, ArenaMem *reuse_blk)
|
||||||
|
{
|
||||||
|
if (reuse_blk && *reuse_blk) {
|
||||||
|
arena->cur_blk = (char *)*reuse_blk;
|
||||||
|
*reuse_blk = NULL;
|
||||||
|
} else {
|
||||||
|
arena->cur_blk = xmalloc(ARENA_BLOCK_SIZE);
|
||||||
|
}
|
||||||
|
arena->pos = 0;
|
||||||
|
arena->size = ARENA_BLOCK_SIZE;
|
||||||
|
// address is the same as as (struct consumed_blk *)arena->cur_blk
|
||||||
|
struct consumed_blk *blk = arena_alloc(arena, sizeof(struct consumed_blk), true);
|
||||||
|
assert((char *)blk == (char *)arena->cur_blk);
|
||||||
|
blk->prev = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Finnish the allocations in an arena.
|
||||||
|
///
|
||||||
|
/// This does not immedately free the memory, but leaves existing allocated
|
||||||
|
/// objects valid, and returns an opaque ArenaMem handle, which can be used to
|
||||||
|
/// free the allocations using `arena_mem_free`, when the objects allocated
|
||||||
|
/// from the arena are not needed anymore.
|
||||||
|
ArenaMem arena_finish(Arena *arena)
|
||||||
|
{
|
||||||
|
struct consumed_blk *res = (struct consumed_blk *)arena->cur_blk;
|
||||||
|
*arena = (Arena)ARENA_EMPTY;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *arena_alloc(Arena *arena, size_t size, bool align)
|
||||||
|
{
|
||||||
|
if (align) {
|
||||||
|
arena->pos = (arena->pos + (ARENA_ALIGN - 1)) & ~(ARENA_ALIGN - 1);
|
||||||
|
}
|
||||||
|
if (arena->pos + size > arena->size) {
|
||||||
|
if (size > (arena->size - sizeof(struct consumed_blk)) >> 1) {
|
||||||
|
// if allocation is too big, allocate a large block with the requested
|
||||||
|
// size, but still with block pointer head. We do this even for
|
||||||
|
// arena->size / 2, as there likely is space left for the next
|
||||||
|
// small allocation in the current block.
|
||||||
|
char *alloc = xmalloc(size + sizeof(struct consumed_blk));
|
||||||
|
struct consumed_blk *cur_blk = (struct consumed_blk *)arena->cur_blk;
|
||||||
|
struct consumed_blk *fix_blk = (struct consumed_blk *)alloc;
|
||||||
|
fix_blk->prev = cur_blk->prev;
|
||||||
|
cur_blk->prev = fix_blk;
|
||||||
|
return (alloc + sizeof(struct consumed_blk));
|
||||||
|
} else {
|
||||||
|
struct consumed_blk *prev_blk = (struct consumed_blk *)arena->cur_blk;
|
||||||
|
arena->cur_blk = xmalloc(ARENA_BLOCK_SIZE);
|
||||||
|
arena->pos = 0;
|
||||||
|
arena->size = ARENA_BLOCK_SIZE;
|
||||||
|
struct consumed_blk *blk = arena_alloc(arena, sizeof(struct consumed_blk), true);
|
||||||
|
blk->prev = prev_blk;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
char *mem = arena->cur_blk + arena->pos;
|
||||||
|
arena->pos += size;
|
||||||
|
return mem;
|
||||||
|
}
|
||||||
|
|
||||||
|
void arena_mem_free(ArenaMem mem, ArenaMem *reuse_blk)
|
||||||
|
{
|
||||||
|
struct consumed_blk *b = mem;
|
||||||
|
// peel of the first block, as it is guaranteed to be ARENA_BLOCK_SIZE,
|
||||||
|
// not a custom fix_blk
|
||||||
|
if (reuse_blk && *reuse_blk == NULL && b != NULL) {
|
||||||
|
*reuse_blk = b;
|
||||||
|
b = b->prev;
|
||||||
|
(*reuse_blk)->prev = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (b) {
|
||||||
|
struct consumed_blk *prev = b->prev;
|
||||||
|
xfree(b);
|
||||||
|
b = prev;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#if defined(EXITFREE)
|
#if defined(EXITFREE)
|
||||||
|
|
||||||
# include "nvim/buffer.h"
|
# include "nvim/buffer.h"
|
||||||
|
@ -37,6 +37,20 @@ extern MemRealloc mem_realloc;
|
|||||||
extern bool entered_free_all_mem;
|
extern bool entered_free_all_mem;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
typedef struct consumed_blk {
|
||||||
|
struct consumed_blk *prev;
|
||||||
|
} *ArenaMem;
|
||||||
|
|
||||||
|
#define ARENA_ALIGN sizeof(void *)
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
char *cur_blk;
|
||||||
|
size_t pos, size;
|
||||||
|
} Arena;
|
||||||
|
|
||||||
|
// inits an empty arena. use arena_start() to actually allocate space!
|
||||||
|
#define ARENA_EMPTY { .cur_blk = NULL, .pos = 0, .size = 0 }
|
||||||
|
|
||||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||||
# include "memory.h.generated.h"
|
# include "memory.h.generated.h"
|
||||||
#endif
|
#endif
|
||||||
|
@ -26,6 +26,7 @@
|
|||||||
#include "nvim/message.h"
|
#include "nvim/message.h"
|
||||||
#include "nvim/msgpack_rpc/channel.h"
|
#include "nvim/msgpack_rpc/channel.h"
|
||||||
#include "nvim/msgpack_rpc/helpers.h"
|
#include "nvim/msgpack_rpc/helpers.h"
|
||||||
|
#include "nvim/msgpack_rpc/unpacker.h"
|
||||||
#include "nvim/os/input.h"
|
#include "nvim/os/input.h"
|
||||||
#include "nvim/os_unix.h"
|
#include "nvim/os_unix.h"
|
||||||
#include "nvim/ui.h"
|
#include "nvim/ui.h"
|
||||||
@ -113,7 +114,8 @@ bool rpc_send_event(uint64_t id, const char *name, Array args)
|
|||||||
/// @param args Array with method arguments
|
/// @param args Array with method arguments
|
||||||
/// @param[out] error True if the return value is an error
|
/// @param[out] error True if the return value is an error
|
||||||
/// @return Whatever the remote method returned
|
/// @return Whatever the remote method returned
|
||||||
Object rpc_send_call(uint64_t id, const char *method_name, Array args, Error *err)
|
Object rpc_send_call(uint64_t id, const char *method_name, Array args, ArenaMem *result_mem,
|
||||||
|
Error *err)
|
||||||
{
|
{
|
||||||
Channel *channel = NULL;
|
Channel *channel = NULL;
|
||||||
|
|
||||||
@ -130,7 +132,7 @@ Object rpc_send_call(uint64_t id, const char *method_name, Array args, Error *er
|
|||||||
send_request(channel, request_id, method_name, args);
|
send_request(channel, request_id, method_name, args);
|
||||||
|
|
||||||
// Push the frame
|
// Push the frame
|
||||||
ChannelCallFrame frame = { request_id, false, false, NIL };
|
ChannelCallFrame frame = { request_id, false, false, NIL, NULL };
|
||||||
kv_push(rpc->call_stack, &frame);
|
kv_push(rpc->call_stack, &frame);
|
||||||
LOOP_PROCESS_EVENTS_UNTIL(&main_loop, channel->events, -1, frame.returned);
|
LOOP_PROCESS_EVENTS_UNTIL(&main_loop, channel->events, -1, frame.returned);
|
||||||
(void)kv_pop(rpc->call_stack);
|
(void)kv_pop(rpc->call_stack);
|
||||||
@ -155,11 +157,15 @@ Object rpc_send_call(uint64_t id, const char *method_name, Array args, Error *er
|
|||||||
api_set_error(err, kErrorTypeException, "%s", "unknown error");
|
api_set_error(err, kErrorTypeException, "%s", "unknown error");
|
||||||
}
|
}
|
||||||
|
|
||||||
api_free_object(frame.result);
|
// frame.result was allocated in an arena
|
||||||
|
arena_mem_free(frame.result_mem, &rpc->unpacker->reuse_blk);
|
||||||
|
frame.result_mem = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
channel_decref(channel);
|
channel_decref(channel);
|
||||||
|
|
||||||
|
*result_mem = frame.result_mem;
|
||||||
|
|
||||||
return frame.errored ? NIL : frame.result;
|
return frame.errored ? NIL : frame.result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -249,17 +255,17 @@ static void parse_msgpack(Channel *channel)
|
|||||||
if (frame->errored) {
|
if (frame->errored) {
|
||||||
frame->result = p->error;
|
frame->result = p->error;
|
||||||
// TODO(bfredl): p->result should not even be decoded
|
// TODO(bfredl): p->result should not even be decoded
|
||||||
api_free_object(p->result);
|
// api_free_object(p->result);
|
||||||
} else {
|
} else {
|
||||||
frame->result = p->result;
|
frame->result = p->result;
|
||||||
}
|
}
|
||||||
|
frame->result_mem = arena_finish(&p->arena);
|
||||||
} else {
|
} else {
|
||||||
log_client_msg(channel->id, p->type == kMessageTypeRequest, p->handler.name);
|
log_client_msg(channel->id, p->type == kMessageTypeRequest, p->handler.name);
|
||||||
|
|
||||||
Object res = p->result;
|
Object res = p->result;
|
||||||
if (p->result.type != kObjectTypeArray) {
|
if (p->result.type != kObjectTypeArray) {
|
||||||
chan_close_with_error(channel, "msgpack-rpc request args has to be an array", LOGLVL_ERR);
|
chan_close_with_error(channel, "msgpack-rpc request args has to be an array", LOGLVL_ERR);
|
||||||
api_free_object(p->result);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Array arg = res.data.array;
|
Array arg = res.data.array;
|
||||||
@ -282,7 +288,7 @@ static void handle_request(Channel *channel, Unpacker *p, Array args)
|
|||||||
if (!p->handler.fn) {
|
if (!p->handler.fn) {
|
||||||
send_error(channel, p->type, p->request_id, p->unpack_error.msg);
|
send_error(channel, p->type, p->request_id, p->unpack_error.msg);
|
||||||
api_clear_error(&p->unpack_error);
|
api_clear_error(&p->unpack_error);
|
||||||
api_free_array(args);
|
arena_mem_free(arena_finish(&p->arena), &p->reuse_blk);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -291,6 +297,7 @@ static void handle_request(Channel *channel, Unpacker *p, Array args)
|
|||||||
evdata->channel = channel;
|
evdata->channel = channel;
|
||||||
evdata->handler = p->handler;
|
evdata->handler = p->handler;
|
||||||
evdata->args = args;
|
evdata->args = args;
|
||||||
|
evdata->used_mem = arena_finish(&p->arena);
|
||||||
evdata->request_id = p->request_id;
|
evdata->request_id = p->request_id;
|
||||||
channel_incref(channel);
|
channel_incref(channel);
|
||||||
if (p->handler.fast) {
|
if (p->handler.fast) {
|
||||||
@ -346,7 +353,8 @@ static void request_event(void **argv)
|
|||||||
}
|
}
|
||||||
|
|
||||||
free_ret:
|
free_ret:
|
||||||
api_free_array(e->args);
|
// e->args is allocated in an arena
|
||||||
|
arena_mem_free(e->used_mem, &channel->rpc.unpacker->reuse_blk);
|
||||||
channel_decref(channel);
|
channel_decref(channel);
|
||||||
xfree(e);
|
xfree(e);
|
||||||
api_clear_error(&error);
|
api_clear_error(&error);
|
||||||
@ -523,6 +531,7 @@ static void exit_event(void **argv)
|
|||||||
void rpc_free(Channel *channel)
|
void rpc_free(Channel *channel)
|
||||||
{
|
{
|
||||||
remote_ui_disconnect(channel->id);
|
remote_ui_disconnect(channel->id);
|
||||||
|
unpacker_teardown(channel->rpc.unpacker);
|
||||||
xfree(channel->rpc.unpacker);
|
xfree(channel->rpc.unpacker);
|
||||||
|
|
||||||
// Unsubscribe from all events
|
// Unsubscribe from all events
|
||||||
@ -543,7 +552,6 @@ static void chan_close_with_error(Channel *channel, char *msg, int loglevel)
|
|||||||
ChannelCallFrame *frame = kv_A(channel->rpc.call_stack, i);
|
ChannelCallFrame *frame = kv_A(channel->rpc.call_stack, i);
|
||||||
frame->returned = true;
|
frame->returned = true;
|
||||||
frame->errored = true;
|
frame->errored = true;
|
||||||
api_free_object(frame->result);
|
|
||||||
frame->result = STRING_OBJ(cstr_to_string(msg));
|
frame->result = STRING_OBJ(cstr_to_string(msg));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,15 +9,16 @@
|
|||||||
#include "nvim/api/private/dispatch.h"
|
#include "nvim/api/private/dispatch.h"
|
||||||
#include "nvim/event/process.h"
|
#include "nvim/event/process.h"
|
||||||
#include "nvim/event/socket.h"
|
#include "nvim/event/socket.h"
|
||||||
#include "nvim/msgpack_rpc/unpacker.h"
|
|
||||||
#include "nvim/vim.h"
|
#include "nvim/vim.h"
|
||||||
|
|
||||||
typedef struct Channel Channel;
|
typedef struct Channel Channel;
|
||||||
|
typedef struct Unpacker Unpacker;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint32_t request_id;
|
uint32_t request_id;
|
||||||
bool returned, errored;
|
bool returned, errored;
|
||||||
Object result;
|
Object result;
|
||||||
|
ArenaMem result_mem;
|
||||||
} ChannelCallFrame;
|
} ChannelCallFrame;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@ -26,6 +27,7 @@ typedef struct {
|
|||||||
MsgpackRpcRequestHandler handler;
|
MsgpackRpcRequestHandler handler;
|
||||||
Array args;
|
Array args;
|
||||||
uint32_t request_id;
|
uint32_t request_id;
|
||||||
|
ArenaMem used_mem;
|
||||||
} RequestEvent;
|
} RequestEvent;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
#include "nvim/api/private/helpers.h"
|
#include "nvim/api/private/helpers.h"
|
||||||
#include "nvim/log.h"
|
#include "nvim/log.h"
|
||||||
|
#include "nvim/memory.h"
|
||||||
#include "nvim/msgpack_rpc/helpers.h"
|
#include "nvim/msgpack_rpc/helpers.h"
|
||||||
#include "nvim/msgpack_rpc/unpacker.h"
|
#include "nvim/msgpack_rpc/unpacker.h"
|
||||||
|
|
||||||
@ -10,6 +11,10 @@
|
|||||||
# include "msgpack_rpc/unpacker.c.generated.h"
|
# include "msgpack_rpc/unpacker.c.generated.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define kv_fixsize_arena(a, v, s) \
|
||||||
|
((v).capacity = (s), \
|
||||||
|
(v).items = (void *)arena_alloc(a, sizeof((v).items[0]) * (v).capacity, true))
|
||||||
|
|
||||||
Object unpack(const char *data, size_t size, Error *err)
|
Object unpack(const char *data, size_t size, Error *err)
|
||||||
{
|
{
|
||||||
Unpacker unpacker;
|
Unpacker unpacker;
|
||||||
@ -34,7 +39,7 @@ Object unpack(const char *data, size_t size, Error *err)
|
|||||||
|
|
||||||
static void api_parse_enter(mpack_parser_t *parser, mpack_node_t *node)
|
static void api_parse_enter(mpack_parser_t *parser, mpack_node_t *node)
|
||||||
{
|
{
|
||||||
Unpacker *unpacker = parser->data.p;
|
Unpacker *p = parser->data.p;
|
||||||
Object *result = NULL;
|
Object *result = NULL;
|
||||||
String *key_location = NULL;
|
String *key_location = NULL;
|
||||||
|
|
||||||
@ -69,7 +74,7 @@ static void api_parse_enter(mpack_parser_t *parser, mpack_node_t *node)
|
|||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
result = &unpacker->result;
|
result = &p->result;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (node->tok.type) {
|
switch (node->tok.type) {
|
||||||
@ -88,16 +93,17 @@ static void api_parse_enter(mpack_parser_t *parser, mpack_node_t *node)
|
|||||||
case MPACK_TOKEN_FLOAT:
|
case MPACK_TOKEN_FLOAT:
|
||||||
*result = FLOAT_OBJ(mpack_unpack_float(node->tok));
|
*result = FLOAT_OBJ(mpack_unpack_float(node->tok));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MPACK_TOKEN_BIN:
|
case MPACK_TOKEN_BIN:
|
||||||
case MPACK_TOKEN_STR: {
|
case MPACK_TOKEN_STR: {
|
||||||
String str = { .data = xmallocz(node->tok.length), .size = node->tok.length };
|
char *mem = arena_alloc(&p->arena, node->tok.length + 1, false);
|
||||||
|
mem[node->tok.length] = NUL;
|
||||||
|
String str = { .data = mem, .size = node->tok.length };
|
||||||
if (key_location) {
|
if (key_location) {
|
||||||
*key_location = str;
|
*key_location = str;
|
||||||
} else {
|
} else {
|
||||||
*result = STRING_OBJ(str);
|
*result = STRING_OBJ(str);
|
||||||
}
|
}
|
||||||
|
|
||||||
node->data[0].p = str.data;
|
node->data[0].p = str.data;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -105,7 +111,6 @@ static void api_parse_enter(mpack_parser_t *parser, mpack_node_t *node)
|
|||||||
// handled in chunk; but save result location
|
// handled in chunk; but save result location
|
||||||
node->data[0].p = result;
|
node->data[0].p = result;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MPACK_TOKEN_CHUNK:
|
case MPACK_TOKEN_CHUNK:
|
||||||
assert(parent);
|
assert(parent);
|
||||||
if (parent->tok.type == MPACK_TOKEN_STR || parent->tok.type == MPACK_TOKEN_BIN) {
|
if (parent->tok.type == MPACK_TOKEN_STR || parent->tok.type == MPACK_TOKEN_BIN) {
|
||||||
@ -120,12 +125,12 @@ static void api_parse_enter(mpack_parser_t *parser, mpack_node_t *node)
|
|||||||
*res = NIL;
|
*res = NIL;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
memcpy(unpacker->ext_buf + parent->pos,
|
memcpy(p->ext_buf + parent->pos,
|
||||||
node->tok.data.chunk_ptr, node->tok.length);
|
node->tok.data.chunk_ptr, node->tok.length);
|
||||||
if (parent->pos + node->tok.length < parent->tok.length) {
|
if (parent->pos + node->tok.length < parent->tok.length) {
|
||||||
break; // EOF, let's get back to it later
|
break; // EOF, let's get back to it later
|
||||||
}
|
}
|
||||||
const char *buf = unpacker->ext_buf;
|
const char *buf = p->ext_buf;
|
||||||
size_t size = parent->tok.length;
|
size_t size = parent->tok.length;
|
||||||
mpack_token_t ext_tok;
|
mpack_token_t ext_tok;
|
||||||
int status = mpack_rtoken(&buf, &size, &ext_tok);
|
int status = mpack_rtoken(&buf, &size, &ext_tok);
|
||||||
@ -148,7 +153,7 @@ static void api_parse_enter(mpack_parser_t *parser, mpack_node_t *node)
|
|||||||
|
|
||||||
case MPACK_TOKEN_ARRAY: {
|
case MPACK_TOKEN_ARRAY: {
|
||||||
Array arr = KV_INITIAL_VALUE;
|
Array arr = KV_INITIAL_VALUE;
|
||||||
kv_resize(arr, node->tok.length);
|
kv_fixsize_arena(&p->arena, arr, node->tok.length);
|
||||||
kv_size(arr) = node->tok.length;
|
kv_size(arr) = node->tok.length;
|
||||||
*result = ARRAY_OBJ(arr);
|
*result = ARRAY_OBJ(arr);
|
||||||
node->data[0].p = result;
|
node->data[0].p = result;
|
||||||
@ -156,12 +161,13 @@ static void api_parse_enter(mpack_parser_t *parser, mpack_node_t *node)
|
|||||||
}
|
}
|
||||||
case MPACK_TOKEN_MAP: {
|
case MPACK_TOKEN_MAP: {
|
||||||
Dictionary dict = KV_INITIAL_VALUE;
|
Dictionary dict = KV_INITIAL_VALUE;
|
||||||
kv_resize(dict, node->tok.length);
|
kv_fixsize_arena(&p->arena, dict, node->tok.length);
|
||||||
kv_size(dict) = node->tok.length;
|
kv_size(dict) = node->tok.length;
|
||||||
*result = DICTIONARY_OBJ(dict);
|
*result = DICTIONARY_OBJ(dict);
|
||||||
node->data[0].p = result;
|
node->data[0].p = result;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
@ -176,6 +182,15 @@ void unpacker_init(Unpacker *p)
|
|||||||
p->parser.data.p = p;
|
p->parser.data.p = p;
|
||||||
mpack_tokbuf_init(&p->reader);
|
mpack_tokbuf_init(&p->reader);
|
||||||
p->unpack_error = (Error)ERROR_INIT;
|
p->unpack_error = (Error)ERROR_INIT;
|
||||||
|
|
||||||
|
p->arena = (Arena)ARENA_EMPTY;
|
||||||
|
p->reuse_blk = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void unpacker_teardown(Unpacker *p)
|
||||||
|
{
|
||||||
|
arena_mem_free(p->reuse_blk, NULL);
|
||||||
|
arena_mem_free(arena_finish(&p->arena), NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool unpacker_parse_header(Unpacker *p)
|
bool unpacker_parse_header(Unpacker *p)
|
||||||
@ -282,6 +297,7 @@ bool unpacker_advance(Unpacker *p)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
p->state = p->type == kMessageTypeResponse ? 1 : 2;
|
p->state = p->type == kMessageTypeResponse ? 1 : 2;
|
||||||
|
arena_start(&p->arena, &p->reuse_blk);
|
||||||
}
|
}
|
||||||
|
|
||||||
int result;
|
int result;
|
||||||
|
@ -9,8 +9,10 @@
|
|||||||
#include "mpack/object.h"
|
#include "mpack/object.h"
|
||||||
#include "nvim/api/private/dispatch.h"
|
#include "nvim/api/private/dispatch.h"
|
||||||
#include "nvim/api/private/helpers.h"
|
#include "nvim/api/private/helpers.h"
|
||||||
|
#include "nvim/memory.h"
|
||||||
|
#include "nvim/msgpack_rpc/channel_defs.h"
|
||||||
|
|
||||||
typedef struct {
|
struct Unpacker {
|
||||||
mpack_parser_t parser;
|
mpack_parser_t parser;
|
||||||
mpack_tokbuf_t reader;
|
mpack_tokbuf_t reader;
|
||||||
|
|
||||||
@ -28,7 +30,11 @@ typedef struct {
|
|||||||
Object error; // error return
|
Object error; // error return
|
||||||
Object result; // arg list or result
|
Object result; // arg list or result
|
||||||
Error unpack_error;
|
Error unpack_error;
|
||||||
} Unpacker;
|
|
||||||
|
Arena arena;
|
||||||
|
// one lenght free-list of reusable blocks
|
||||||
|
ArenaMem reuse_blk;
|
||||||
|
};
|
||||||
|
|
||||||
// unrecovareble error. unpack_error should be set!
|
// unrecovareble error. unpack_error should be set!
|
||||||
#define unpacker_closed(p) ((p)->state < 0)
|
#define unpacker_closed(p) ((p)->state < 0)
|
||||||
|
@ -144,8 +144,10 @@ static void tinput_wait_enqueue(void **argv)
|
|||||||
Error err = ERROR_INIT;
|
Error err = ERROR_INIT;
|
||||||
ADD(args, STRING_OBJ(copy_string(keys)));
|
ADD(args, STRING_OBJ(copy_string(keys)));
|
||||||
// TODO(bfredl): could be non-blocking now with paste?
|
// TODO(bfredl): could be non-blocking now with paste?
|
||||||
Object result = rpc_send_call(ui_client_channel_id, "nvim_input", args, &err);
|
ArenaMem res_mem = NULL;
|
||||||
|
Object result = rpc_send_call(ui_client_channel_id, "nvim_input", args, &res_mem, &err);
|
||||||
consumed = result.type == kObjectTypeInteger ? (size_t)result.data.integer : 0;
|
consumed = result.type == kObjectTypeInteger ? (size_t)result.data.integer : 0;
|
||||||
|
arena_mem_free(res_mem, NULL);
|
||||||
} else {
|
} else {
|
||||||
consumed = input_enqueue(keys);
|
consumed = input_enqueue(keys);
|
||||||
}
|
}
|
||||||
|
@ -229,15 +229,9 @@ void ui_refresh(void)
|
|||||||
p_lz = save_p_lz;
|
p_lz = save_p_lz;
|
||||||
} else {
|
} else {
|
||||||
Array args = ARRAY_DICT_INIT;
|
Array args = ARRAY_DICT_INIT;
|
||||||
Error err = ERROR_INIT;
|
|
||||||
ADD(args, INTEGER_OBJ((int)width));
|
ADD(args, INTEGER_OBJ((int)width));
|
||||||
ADD(args, INTEGER_OBJ((int)height));
|
ADD(args, INTEGER_OBJ((int)height));
|
||||||
rpc_send_call(ui_client_channel_id, "nvim_ui_try_resize", args, &err);
|
rpc_send_event(ui_client_channel_id, "nvim_ui_try_resize", args);
|
||||||
|
|
||||||
if (ERROR_SET(&err)) {
|
|
||||||
ELOG("ui_client resize: %s", err.msg);
|
|
||||||
}
|
|
||||||
api_clear_error(&err);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ext_widgets[kUIMessages]) {
|
if (ext_widgets[kUIMessages]) {
|
||||||
|
Loading…
Reference in New Issue
Block a user