From aa23d2f835e32c01b318712459ba7b9f55922469 Mon Sep 17 00:00:00 2001 From: Thiago de Arruda Date: Thu, 28 Aug 2014 15:46:21 -0300 Subject: [PATCH] msgpack-rpc: Accept method names in requests --- scripts/msgpack-gen.lua | 40 ++++++++++++++++++++++++++++++++++++++- src/nvim/os/msgpack_rpc.c | 12 ++++++------ src/nvim/os/msgpack_rpc.h | 7 ++++++- src/nvim/os_unix.c | 2 ++ 4 files changed, 53 insertions(+), 8 deletions(-) diff --git a/scripts/msgpack-gen.lua b/scripts/msgpack-gen.lua index c9b6018222..8940cc72f6 100644 --- a/scripts/msgpack-gen.lua +++ b/scripts/msgpack-gen.lua @@ -91,6 +91,7 @@ output:write([[ #include #include +#include "nvim/map.h" #include "nvim/log.h" #include "nvim/os/msgpack_rpc.h" #include "nvim/os/msgpack_rpc_helpers.h" @@ -241,12 +242,49 @@ for i = 1, #api.functions do end output:write('\n};\n\n') +-- Generate a function that initializes method names with handler functions output:write([[ +static Map(cstr_t, uint64_t) *rpc_method_ids = NULL; + +void msgpack_rpc_init(void) +{ + rpc_method_ids = map_new(cstr_t, uint64_t)(); + +]]) + +-- Msgpack strings must be copied to a 0-terminated temporary buffer before +-- searching in the map, so we keep track of the maximum method name length in +-- order to create the smallest possible buffer for xstrlcpy +local max_fname_len = 0 +for i = 1, #api.functions do + local fn = api.functions[i] + output:write(' map_put(cstr_t, uint64_t)(rpc_method_ids, "' + ..fn.name..'", '..i..');\n') + if #fn.name > max_fname_len then + max_fname_len = #fn.name + end +end + +output:write('\n}\n\n') + +output:write([[ +#define min(X, Y) (X < Y ? X : Y) + Object msgpack_rpc_dispatch(uint64_t channel_id, - uint64_t method_id, msgpack_object *req, Error *error) { + msgpack_object method = req->via.array.ptr[2]; + uint64_t method_id = method.via.u64; + + if (method.type == MSGPACK_OBJECT_RAW) { + char method_name[]]..(max_fname_len + 1)..[[]; + xstrlcpy(method_name, method.via.raw.ptr, min(method.via.raw.size, ]] ..(max_fname_len)..[[) + 1); + method_id = map_get(cstr_t, uint64_t)(rpc_method_ids, method_name); + if (!method_id) { + method_id = UINT64_MAX; + } + } ]]) output:write('\n // method_id=0 is specially handled') output:write('\n assert(method_id > 0);') diff --git a/src/nvim/os/msgpack_rpc.c b/src/nvim/os/msgpack_rpc.c index c03d8dccca..90673fef13 100644 --- a/src/nvim/os/msgpack_rpc.c +++ b/src/nvim/os/msgpack_rpc.c @@ -39,15 +39,14 @@ WBuffer *msgpack_rpc_call(uint64_t channel_id, return serialize_response(response_id, err, NIL, sbuffer); } - uint64_t method_id = req->via.array.ptr[2].via.u64; - - if (method_id == 0) { + if (req->via.array.ptr[2].type == MSGPACK_OBJECT_POSITIVE_INTEGER + && req->via.array.ptr[2].via.u64 == 0) { return serialize_metadata(response_id, channel_id, sbuffer); } // dispatch the call Error error = { .set = false }; - Object rv = msgpack_rpc_dispatch(channel_id, method_id, req, &error); + Object rv = msgpack_rpc_dispatch(channel_id, req, &error); // send the response msgpack_packer response; msgpack_packer_init(&response, sbuffer, msgpack_sbuffer_write); @@ -235,8 +234,9 @@ static char *msgpack_rpc_validate(uint64_t *response_id, msgpack_object *req) return "Message type must be 0"; } - if (req->via.array.ptr[2].type != MSGPACK_OBJECT_POSITIVE_INTEGER) { - return "Method id must be a positive integer"; + if (req->via.array.ptr[2].type != MSGPACK_OBJECT_POSITIVE_INTEGER + && req->via.array.ptr[2].type != MSGPACK_OBJECT_RAW) { + return "Method must be a positive integer or a string"; } if (req->via.array.ptr[3].type != MSGPACK_OBJECT_ARRAY) { diff --git a/src/nvim/os/msgpack_rpc.h b/src/nvim/os/msgpack_rpc.h index 5aca900d2e..35f175d2a0 100644 --- a/src/nvim/os/msgpack_rpc.h +++ b/src/nvim/os/msgpack_rpc.h @@ -21,6 +21,11 @@ typedef Object (*rpc_method_handler_fn)(uint64_t channel_id, msgpack_object *req, Error *error); + +/// Initializes the msgpack-rpc method table +void msgpack_rpc_init(void); + + /// Dispatches to the actual API function after basic payload validation by /// `msgpack_rpc_call`. It is responsible for validating/converting arguments /// to C types, and converting the return value back to msgpack types. @@ -33,11 +38,11 @@ typedef Object (*rpc_method_handler_fn)(uint64_t channel_id, /// @param error Pointer to error structure /// @return Some object Object msgpack_rpc_dispatch(uint64_t channel_id, - uint64_t method_id, msgpack_object *req, Error *error) FUNC_ATTR_NONNULL_ARG(2) FUNC_ATTR_NONNULL_ARG(3); + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "os/msgpack_rpc.h.generated.h" #endif diff --git a/src/nvim/os_unix.c b/src/nvim/os_unix.c index 1b092c8261..33b08d7df6 100644 --- a/src/nvim/os_unix.c +++ b/src/nvim/os_unix.c @@ -54,6 +54,7 @@ #include "nvim/os/shell.h" #include "nvim/os/signal.h" #include "nvim/os/job.h" +#include "nvim/os/msgpack_rpc.h" #if defined(HAVE_SYS_IOCTL_H) # include @@ -164,6 +165,7 @@ void mch_init(void) mac_conv_init(); #endif + msgpack_rpc_init(); event_init(); }