diff options
author | Thiago de Arruda <tpadilha84@gmail.com> | 2014-06-23 11:42:29 -0300 |
---|---|---|
committer | Thiago de Arruda <tpadilha84@gmail.com> | 2014-06-24 13:02:24 -0300 |
commit | bc0380038e0a4ff4f4bfaa939b0cef26c5e53582 (patch) | |
tree | 64356539713a1c21c0cf1906a924f0aac9c11b0f | |
parent | ea7a389ec77c0031160ce860129101c603d8e0ec (diff) | |
download | rneovim-bc0380038e0a4ff4f4bfaa939b0cef26c5e53582.tar.gz rneovim-bc0380038e0a4ff4f4bfaa939b0cef26c5e53582.tar.bz2 rneovim-bc0380038e0a4ff4f4bfaa939b0cef26c5e53582.zip |
channel/msgpack_rpc: Refactor to better split functions across modules
Move validation/conversion functions and to msgpack_rpc_helpers to separate
those from the functions that are used from the channel module
-rw-r--r-- | scripts/msgpack-gen.lua | 1 | ||||
-rw-r--r-- | src/nvim/eval.c | 2 | ||||
-rw-r--r-- | src/nvim/os/channel.c | 82 | ||||
-rw-r--r-- | src/nvim/os/msgpack_rpc.c | 453 | ||||
-rw-r--r-- | src/nvim/os/msgpack_rpc.h | 158 | ||||
-rw-r--r-- | src/nvim/os/msgpack_rpc_helpers.c | 331 | ||||
-rw-r--r-- | src/nvim/os/msgpack_rpc_helpers.h | 124 |
7 files changed, 590 insertions, 561 deletions
diff --git a/scripts/msgpack-gen.lua b/scripts/msgpack-gen.lua index e7d5d5a503..91bdf3df21 100644 --- a/scripts/msgpack-gen.lua +++ b/scripts/msgpack-gen.lua @@ -91,6 +91,7 @@ output:write([[ #include <msgpack.h> #include "nvim/os/msgpack_rpc.h" +#include "nvim/os/msgpack_rpc_helpers.h" ]]) for i = 1, #headers do diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 4c39950344..adc411afc7 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -71,7 +71,7 @@ #include "nvim/os/time.h" #include "nvim/os/channel.h" #include "nvim/api/private/helpers.h" -#include "nvim/os/msgpack_rpc.h" +#include "nvim/os/msgpack_rpc_helpers.h" #define DICT_MAXNEST 100 /* maximum nesting of lists and dicts */ diff --git a/src/nvim/os/channel.c b/src/nvim/os/channel.c index 552c962b3a..b44b1d13a4 100644 --- a/src/nvim/os/channel.c +++ b/src/nvim/os/channel.c @@ -13,6 +13,7 @@ #include "nvim/os/job.h" #include "nvim/os/job_defs.h" #include "nvim/os/msgpack_rpc.h" +#include "nvim/os/msgpack_rpc_helpers.h" #include "nvim/vim.h" #include "nvim/memory.h" #include "nvim/message.h" @@ -47,7 +48,7 @@ typedef struct { static uint64_t next_id = 1; static PMap(uint64_t) *channels = NULL; static PMap(cstr_t) *event_strings = NULL; -static msgpack_sbuffer msgpack_event_buffer; +static msgpack_sbuffer out_buffer; #ifdef INCLUDE_GENERATED_DECLARATIONS # include "os/channel.c.generated.h" @@ -58,7 +59,7 @@ void channel_init() { channels = pmap_new(uint64_t)(); event_strings = pmap_new(cstr_t)(); - msgpack_sbuffer_init(&msgpack_event_buffer); + msgpack_sbuffer_init(&out_buffer); } /// Teardown the module @@ -137,7 +138,7 @@ bool channel_send_event(uint64_t id, char *name, Object arg) msgpack_rpc_free_object(arg); return false; } - send_message(channel, 2, 0, name, arg); + send_event(channel, name, arg); } else { broadcast_event(name, arg); } @@ -172,7 +173,7 @@ bool channel_send_call(uint64_t id, uint64_t request_id = channel->next_request_id++; // Send the msgpack-rpc request - channel_write(channel, serialize_message(0, request_id, name, arg)); + send_request(channel, request_id, name, arg); if (!kv_size(channel->call_stack)) { // This is the first frame, we must disable event deferral for this @@ -341,9 +342,9 @@ static void parse_msgpack(RStream *rstream, void *data, bool eof) // A not so uncommon cause for this might be deserializing objects with // a high nesting level: msgpack will break when it's internal parse stack // size exceeds MSGPACK_EMBED_STACK_SIZE(defined as 32 by default) - send_error(channel, "Invalid msgpack payload. " - "This error can also happen when deserializing " - "an object with high level of nesting"); + send_error(channel, 0, "Invalid msgpack payload. " + "This error can also happen when deserializing " + "an object with high level of nesting"); } end: @@ -354,31 +355,6 @@ end: } } -static void send_error(Channel *channel, char *msg) -{ - msgpack_packer err; - // See src/msgpack/unpack_template.h in msgpack source tree for - // causes for this error(search for 'goto _failed') - // - // A not so uncommon cause for this might be deserializing objects with - // a high nesting level: msgpack will break when it's internal parse stack - // size exceeds MSGPACK_EMBED_STACK_SIZE(defined as 32 by default) - msgpack_packer_init(&err, channel->sbuffer, msgpack_sbuffer_write); - msgpack_pack_array(&err, 4); - msgpack_pack_int(&err, 1); - msgpack_pack_int(&err, 0); - msgpack_rpc_error(msg, &err); - WBuffer *buffer = wstream_new_buffer(xmemdup(channel->sbuffer->data, - channel->sbuffer->size), - channel->sbuffer->size, - free); - if (!channel_write(channel, buffer)) { - return; - } - // Clear the buffer for future calls - msgpack_sbuffer_clear(channel->sbuffer); -} - static bool channel_write(Channel *channel, WBuffer *buffer) { bool success; @@ -403,13 +379,27 @@ static bool channel_write(Channel *channel, WBuffer *buffer) return success; } -static void send_message(Channel *channel, - int type, +static void send_error(Channel *channel, uint64_t id, char *err_msg) +{ + String err = {.size = strlen(err_msg), .data = err_msg}; + channel_write(channel, serialize_response(id, err, NIL, channel->sbuffer)); +} + +static void send_request(Channel *channel, uint64_t id, char *name, Object arg) { - channel_write(channel, serialize_message(type, id, name, arg)); + String method = {.size = strlen(name), .data = name}; + channel_write(channel, serialize_request(id, method, arg, &out_buffer)); +} + +static void send_event(Channel *channel, + char *name, + Object arg) +{ + String method = {.size = strlen(name), .data = name}; + channel_write(channel, serialize_request(0, method, arg, &out_buffer)); } static void broadcast_event(char *name, Object arg) @@ -429,7 +419,8 @@ static void broadcast_event(char *name, Object arg) goto end; } - WBuffer *buffer = serialize_message(2, 0, name, arg); + String method = {.size = strlen(name), .data = name}; + WBuffer *buffer = serialize_request(0, method, arg, &out_buffer); for (size_t i = 0; i < kv_size(subscribed); i++) { channel_write(kv_A(subscribed, i), buffer); @@ -488,25 +479,6 @@ static void close_cb(uv_handle_t *handle) free(handle); } -static WBuffer *serialize_message(int type, - uint64_t id, - char *method, - Object arg) -{ - String method_str = {.size = strnlen(method, METHOD_MAXLEN), .data = method}; - msgpack_packer packer; - msgpack_packer_init(&packer, &msgpack_event_buffer, msgpack_sbuffer_write); - msgpack_rpc_message(type, id, method_str, arg, &packer); - WBuffer *rv = wstream_new_buffer(xmemdup(msgpack_event_buffer.data, - msgpack_event_buffer.size), - msgpack_event_buffer.size, - free); - msgpack_rpc_free_object(arg); - msgpack_sbuffer_clear(&msgpack_event_buffer); - - return rv; -} - static Channel *register_channel() { Channel *rv = xmalloc(sizeof(Channel)); diff --git a/src/nvim/os/msgpack_rpc.c b/src/nvim/os/msgpack_rpc.c index 0d9a7ae3de..5e2ec6aa10 100644 --- a/src/nvim/os/msgpack_rpc.c +++ b/src/nvim/os/msgpack_rpc.c @@ -3,59 +3,25 @@ #include <msgpack.h> -#include "nvim/os/msgpack_rpc.h" #include "nvim/vim.h" #include "nvim/memory.h" - -#define REMOTE_FUNCS_IMPL(t, lt) \ - bool msgpack_rpc_to_##lt(msgpack_object *obj, t *arg) \ - { \ - *arg = obj->via.u64; \ - return obj->type == MSGPACK_OBJECT_POSITIVE_INTEGER; \ - } \ - \ - void msgpack_rpc_from_##lt(t result, msgpack_packer *res) \ - { \ - msgpack_pack_uint64(res, result); \ - } - -#define TYPED_ARRAY_IMPL(t, lt) \ - bool msgpack_rpc_to_##lt##array(msgpack_object *obj, t##Array *arg) \ - { \ - if (obj->type != MSGPACK_OBJECT_ARRAY) { \ - return false; \ - } \ - \ - arg->size = obj->via.array.size; \ - arg->items = xcalloc(obj->via.array.size, sizeof(t)); \ - \ - for (size_t i = 0; i < obj->via.array.size; i++) { \ - if (!msgpack_rpc_to_##lt(obj->via.array.ptr + i, &arg->items[i])) { \ - return false; \ - } \ - } \ - \ - return true; \ - } \ - \ - void msgpack_rpc_from_##lt##array(t##Array result, msgpack_packer *res) \ - { \ - msgpack_pack_array(res, result.size); \ - \ - for (size_t i = 0; i < result.size; i++) { \ - msgpack_rpc_from_##lt(result.items[i], res); \ - } \ - } \ - \ - void msgpack_rpc_free_##lt##array(t##Array value) { \ - for (size_t i = 0; i < value.size; i++) { \ - msgpack_rpc_free_##lt(value.items[i]); \ - } \ - \ - free(value.items); \ - } - +#include "nvim/os/wstream.h" +#include "nvim/os/msgpack_rpc.h" +#include "nvim/os/msgpack_rpc_helpers.h" +#include "nvim/func_attr.h" + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "os/msgpack_rpc.c.generated.h" +#endif + +/// Validates the basic structure of the msgpack-rpc call and fills `res` +/// with the basic response structure. +/// +/// @param id The channel id +/// @param req The parsed request object +/// @param res A packer that contains the response void msgpack_rpc_call(uint64_t id, msgpack_object *req, msgpack_packer *res) + FUNC_ATTR_NONNULL_ARG(2) FUNC_ATTR_NONNULL_ARG(3) { // The initial response structure is the same no matter what happens, // we set it up here @@ -115,302 +81,19 @@ void msgpack_rpc_call(uint64_t id, msgpack_object *req, msgpack_packer *res) msgpack_rpc_dispatch(id, req, res); } -void msgpack_rpc_message(int type, - uint64_t id, - String method, - Object arg, - msgpack_packer *pac) -{ - msgpack_pack_array(pac, id ? 4 : 3); - msgpack_pack_int(pac, type); - - if (id) { - msgpack_pack_uint64(pac, id); - } - - msgpack_pack_raw(pac, method.size); - msgpack_pack_raw_body(pac, method.data, method.size); - msgpack_rpc_from_object(arg, pac); -} - -void msgpack_rpc_error(char *msg, msgpack_packer *res) -{ - size_t len = strlen(msg); - - // error message - msgpack_pack_raw(res, len); - msgpack_pack_raw_body(res, msg, len); - // Nil result - msgpack_pack_nil(res); -} - -bool msgpack_rpc_to_boolean(msgpack_object *obj, Boolean *arg) -{ - *arg = obj->via.boolean; - return obj->type == MSGPACK_OBJECT_BOOLEAN; -} - -bool msgpack_rpc_to_integer(msgpack_object *obj, Integer *arg) -{ - if (obj->type == MSGPACK_OBJECT_POSITIVE_INTEGER - && obj->via.u64 <= INT64_MAX) { - *arg = (int64_t)obj->via.u64; - return true; - } - - *arg = obj->via.i64; - return obj->type == MSGPACK_OBJECT_NEGATIVE_INTEGER; -} - -bool msgpack_rpc_to_float(msgpack_object *obj, Float *arg) -{ - *arg = obj->via.dec; - return obj->type == MSGPACK_OBJECT_DOUBLE; -} - -bool msgpack_rpc_to_string(msgpack_object *obj, String *arg) -{ - if (obj->type != MSGPACK_OBJECT_RAW) { - return false; - } - - arg->data = xmemdupz(obj->via.raw.ptr, obj->via.raw.size); - arg->size = obj->via.raw.size; - return true; -} - -bool msgpack_rpc_to_object(msgpack_object *obj, Object *arg) -{ - switch (obj->type) { - case MSGPACK_OBJECT_NIL: - arg->type = kObjectTypeNil; - return true; - - case MSGPACK_OBJECT_BOOLEAN: - arg->type = kObjectTypeBoolean; - return msgpack_rpc_to_boolean(obj, &arg->data.boolean); - - case MSGPACK_OBJECT_POSITIVE_INTEGER: - case MSGPACK_OBJECT_NEGATIVE_INTEGER: - arg->type = kObjectTypeInteger; - return msgpack_rpc_to_integer(obj, &arg->data.integer); - - case MSGPACK_OBJECT_DOUBLE: - arg->type = kObjectTypeFloat; - return msgpack_rpc_to_float(obj, &arg->data.floating); - - case MSGPACK_OBJECT_RAW: - arg->type = kObjectTypeString; - return msgpack_rpc_to_string(obj, &arg->data.string); - - case MSGPACK_OBJECT_ARRAY: - arg->type = kObjectTypeArray; - return msgpack_rpc_to_array(obj, &arg->data.array); - - case MSGPACK_OBJECT_MAP: - arg->type = kObjectTypeDictionary; - return msgpack_rpc_to_dictionary(obj, &arg->data.dictionary); - - default: - return false; - } -} - -bool msgpack_rpc_to_position(msgpack_object *obj, Position *arg) -{ - return obj->type == MSGPACK_OBJECT_ARRAY - && obj->via.array.size == 2 - && msgpack_rpc_to_integer(obj->via.array.ptr, &arg->row) - && msgpack_rpc_to_integer(obj->via.array.ptr + 1, &arg->col); -} - - -bool msgpack_rpc_to_array(msgpack_object *obj, Array *arg) -{ - if (obj->type != MSGPACK_OBJECT_ARRAY) { - return false; - } - - arg->size = obj->via.array.size; - arg->items = xcalloc(obj->via.array.size, sizeof(Object)); - - for (uint32_t i = 0; i < obj->via.array.size; i++) { - if (!msgpack_rpc_to_object(obj->via.array.ptr + i, &arg->items[i])) { - return false; - } - } - - return true; -} - -bool msgpack_rpc_to_dictionary(msgpack_object *obj, Dictionary *arg) -{ - if (obj->type != MSGPACK_OBJECT_MAP) { - return false; - } - - arg->size = obj->via.array.size; - arg->items = xcalloc(obj->via.map.size, sizeof(KeyValuePair)); - - - for (uint32_t i = 0; i < obj->via.map.size; i++) { - if (!msgpack_rpc_to_string(&obj->via.map.ptr[i].key, - &arg->items[i].key)) { - return false; - } - - if (!msgpack_rpc_to_object(&obj->via.map.ptr[i].val, - &arg->items[i].value)) { - return false; - } - } - - return true; -} - -void msgpack_rpc_from_boolean(Boolean result, msgpack_packer *res) -{ - if (result) { - msgpack_pack_true(res); - } else { - msgpack_pack_false(res); - } -} - -void msgpack_rpc_from_integer(Integer result, msgpack_packer *res) -{ - msgpack_pack_int64(res, result); -} - -void msgpack_rpc_from_float(Float result, msgpack_packer *res) -{ - msgpack_pack_double(res, result); -} - -void msgpack_rpc_from_string(String result, msgpack_packer *res) -{ - msgpack_pack_raw(res, result.size); - msgpack_pack_raw_body(res, result.data, result.size); -} - -void msgpack_rpc_from_object(Object result, msgpack_packer *res) -{ - switch (result.type) { - case kObjectTypeNil: - msgpack_pack_nil(res); - break; - - case kObjectTypeBoolean: - msgpack_rpc_from_boolean(result.data.boolean, res); - break; - - case kObjectTypeInteger: - msgpack_rpc_from_integer(result.data.integer, res); - break; - - case kObjectTypeFloat: - msgpack_rpc_from_float(result.data.floating, res); - break; - - case kObjectTypeString: - msgpack_rpc_from_string(result.data.string, res); - break; - - case kObjectTypeArray: - msgpack_rpc_from_array(result.data.array, res); - break; - - case kObjectTypeDictionary: - msgpack_rpc_from_dictionary(result.data.dictionary, res); - break; - - default: - abort(); - } -} - -void msgpack_rpc_from_position(Position result, msgpack_packer *res) -{ - msgpack_pack_array(res, 2);; - msgpack_pack_int64(res, result.row); - msgpack_pack_int64(res, result.col); -} - -void msgpack_rpc_from_array(Array result, msgpack_packer *res) -{ - msgpack_pack_array(res, result.size); - - for (size_t i = 0; i < result.size; i++) { - msgpack_rpc_from_object(result.items[i], res); - } -} - -void msgpack_rpc_from_dictionary(Dictionary result, msgpack_packer *res) -{ - msgpack_pack_map(res, result.size); - - for (size_t i = 0; i < result.size; i++) { - msgpack_rpc_from_string(result.items[i].key, res); - msgpack_rpc_from_object(result.items[i].value, res); - } -} - -void msgpack_rpc_free_string(String value) -{ - if (!value.data) { - return; - } - - free(value.data); -} - -void msgpack_rpc_free_object(Object value) -{ - switch (value.type) { - case kObjectTypeNil: - case kObjectTypeBoolean: - case kObjectTypeInteger: - case kObjectTypeFloat: - break; - - case kObjectTypeString: - msgpack_rpc_free_string(value.data.string); - break; - - case kObjectTypeArray: - msgpack_rpc_free_array(value.data.array); - break; - - case kObjectTypeDictionary: - msgpack_rpc_free_dictionary(value.data.dictionary); - break; - - default: - abort(); - } -} - -void msgpack_rpc_free_array(Array value) -{ - for (uint32_t i = 0; i < value.size; i++) { - msgpack_rpc_free_object(value.items[i]); - } - - free(value.items); -} - -void msgpack_rpc_free_dictionary(Dictionary value) -{ - for (uint32_t i = 0; i < value.size; i++) { - msgpack_rpc_free_string(value.items[i].key); - msgpack_rpc_free_object(value.items[i].value); - } - - free(value.items); -} - +/// Try to unpack a msgpack document from the data in the unpacker buffer. This +/// function is a replacement to msgpack.h `msgpack_unpack_next` that lets +/// the called know if the unpacking failed due to bad input or due to missing +/// data. +/// +/// @param unpacker The unpacker containing the parse buffer +/// @param result The result which will contain the parsed object +/// @return kUnpackResultOk : An object was parsed +/// kUnpackResultFail : Got bad input +/// kUnpackResultNeedMore: Need more data UnpackResult msgpack_rpc_unpack(msgpack_unpacker* unpacker, msgpack_unpacked* result) + FUNC_ATTR_NONNULL_ALL { if (result->zone != NULL) { msgpack_zone_free(result->zone); @@ -434,12 +117,80 @@ UnpackResult msgpack_rpc_unpack(msgpack_unpacker* unpacker, return kUnpackResultNeedMore; } -REMOTE_FUNCS_IMPL(Buffer, buffer) -REMOTE_FUNCS_IMPL(Window, window) -REMOTE_FUNCS_IMPL(Tabpage, tabpage) +/// Finishes the msgpack-rpc call with an error message. +/// +/// @param msg The error message +/// @param res A packer that contains the response +void msgpack_rpc_error(char *msg, msgpack_packer *res) + FUNC_ATTR_NONNULL_ALL +{ + size_t len = strlen(msg); -TYPED_ARRAY_IMPL(Buffer, buffer) -TYPED_ARRAY_IMPL(Window, window) -TYPED_ARRAY_IMPL(Tabpage, tabpage) -TYPED_ARRAY_IMPL(String, string) + // error message + msgpack_pack_raw(res, len); + msgpack_pack_raw_body(res, msg, len); + // Nil result + msgpack_pack_nil(res); +} + +/// Serializes a msgpack-rpc request or notification(id == 0) +WBuffer *serialize_request(uint64_t id, + String method, + Object arg, + msgpack_sbuffer *sbuffer) + FUNC_ATTR_NONNULL_ALL +{ + msgpack_packer pac; + msgpack_packer_init(&pac, sbuffer, msgpack_sbuffer_write); + msgpack_pack_array(&pac, id ? 4 : 3); + msgpack_pack_int(&pac, id ? 0 : 2); + + if (id) { + msgpack_pack_uint64(&pac, id); + } + + msgpack_pack_raw(&pac, method.size); + msgpack_pack_raw_body(&pac, method.data, method.size); + msgpack_rpc_from_object(arg, &pac); + WBuffer *rv = wstream_new_buffer(xmemdup(sbuffer->data, sbuffer->size), + sbuffer->size, + free); + msgpack_rpc_free_object(arg); + msgpack_sbuffer_clear(sbuffer); + return rv; +} + +/// Serializes a msgpack-rpc response +WBuffer *serialize_response(uint64_t id, + String err, + Object arg, + msgpack_sbuffer *sbuffer) + FUNC_ATTR_NONNULL_ALL +{ + msgpack_packer pac; + msgpack_packer_init(&pac, sbuffer, msgpack_sbuffer_write); + msgpack_pack_array(&pac, 4); + msgpack_pack_int(&pac, 1); + msgpack_pack_uint64(&pac, id); + + if (err.size) { + // error message + msgpack_pack_raw(&pac, err.size); + msgpack_pack_raw_body(&pac, err.data, err.size); + // Nil result + msgpack_pack_nil(&pac); + } else { + // Nil error + msgpack_pack_nil(&pac); + // Return value + msgpack_rpc_from_object(arg, &pac); + } + + WBuffer *rv = wstream_new_buffer(xmemdup(sbuffer->data, sbuffer->size), + sbuffer->size, + free); + msgpack_rpc_free_object(arg); + msgpack_sbuffer_clear(sbuffer); + return rv; +} diff --git a/src/nvim/os/msgpack_rpc.h b/src/nvim/os/msgpack_rpc.h index 9858eab960..cbb487b44a 100644 --- a/src/nvim/os/msgpack_rpc.h +++ b/src/nvim/os/msgpack_rpc.h @@ -8,6 +8,7 @@ #include "nvim/func_attr.h" #include "nvim/api/private/defs.h" +#include "nvim/os/wstream.h" typedef enum { kUnpackResultOk, /// Successfully parsed a document @@ -15,30 +16,6 @@ typedef enum { kUnpackResultNeedMore /// Need more data } UnpackResult; -/// Validates the basic structure of the msgpack-rpc call and fills `res` -/// with the basic response structure. -/// -/// @param id The channel id -/// @param req The parsed request object -/// @param res A packer that contains the response -void msgpack_rpc_call(uint64_t id, msgpack_object *req, msgpack_packer *res) - FUNC_ATTR_NONNULL_ARG(2) FUNC_ATTR_NONNULL_ARG(3); - -/// Packs a message -/// -/// @param type The message type -/// @param id The message id, must be an unique integer > 0 or will be -/// ignored(the message array will have 3 elements instead of 4). -/// @param method The message name, an arbitrary string -/// @param arg The message argument -/// @param packer Where the notification will be packed to -void msgpack_rpc_message(int type, - uint64_t id, - String method, - Object arg, - msgpack_packer *pac) - FUNC_ATTR_NONNULL_ARG(5); - /// 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. @@ -53,136 +30,9 @@ void msgpack_rpc_dispatch(uint64_t id, msgpack_packer *res) FUNC_ATTR_NONNULL_ARG(2) FUNC_ATTR_NONNULL_ARG(3); -/// Try to unpack a msgpack document from the data in the unpacker buffer. This -/// function is a replacement to msgpack.h `msgpack_unpack_next` that lets -/// the called know if the unpacking failed due to bad input or due to missing -/// data. -/// -/// @param unpacker The unpacker containing the parse buffer -/// @param result The result which will contain the parsed object -/// @return kUnpackResultOk : An object was parsed -/// kUnpackResultFail : Got bad input -/// kUnpackResultNeedMore: Need more data -UnpackResult msgpack_rpc_unpack(msgpack_unpacker* unpacker, - msgpack_unpacked* result); - -/// Finishes the msgpack-rpc call with an error message. -/// -/// @param msg The error message -/// @param res A packer that contains the response -void msgpack_rpc_error(char *msg, msgpack_packer *res) - FUNC_ATTR_NONNULL_ALL; - -/// Functions for validating and converting from msgpack types to C types. -/// These are used by `msgpack_rpc_dispatch` to validate and convert each -/// argument. -/// -/// @param obj The object to convert -/// @param[out] arg A pointer to the avalue -/// @return true if the conversion succeeded, false otherwise -bool msgpack_rpc_to_boolean(msgpack_object *obj, Boolean *arg) - FUNC_ATTR_NONNULL_ALL; -bool msgpack_rpc_to_integer(msgpack_object *obj, Integer *arg) - FUNC_ATTR_NONNULL_ALL; -bool msgpack_rpc_to_float(msgpack_object *obj, Float *arg) - FUNC_ATTR_NONNULL_ALL; -bool msgpack_rpc_to_position(msgpack_object *obj, Position *arg) - FUNC_ATTR_NONNULL_ALL; -bool msgpack_rpc_to_string(msgpack_object *obj, String *arg) - FUNC_ATTR_NONNULL_ALL; -bool msgpack_rpc_to_buffer(msgpack_object *obj, Buffer *arg) - FUNC_ATTR_NONNULL_ALL; -bool msgpack_rpc_to_window(msgpack_object *obj, Window *arg) - FUNC_ATTR_NONNULL_ALL; -bool msgpack_rpc_to_tabpage(msgpack_object *obj, Tabpage *arg) - FUNC_ATTR_NONNULL_ALL; -bool msgpack_rpc_to_object(msgpack_object *obj, Object *arg) - FUNC_ATTR_NONNULL_ALL; -bool msgpack_rpc_to_stringarray(msgpack_object *obj, StringArray *arg) - FUNC_ATTR_NONNULL_ALL; -bool msgpack_rpc_to_bufferarray(msgpack_object *obj, BufferArray *arg) - FUNC_ATTR_NONNULL_ALL; -bool msgpack_rpc_to_windowarray(msgpack_object *obj, WindowArray *arg) - FUNC_ATTR_NONNULL_ALL; -bool msgpack_rpc_to_tabpagearray(msgpack_object *obj, TabpageArray *arg) - FUNC_ATTR_NONNULL_ALL; -bool msgpack_rpc_to_array(msgpack_object *obj, Array *arg) - FUNC_ATTR_NONNULL_ALL; -bool msgpack_rpc_to_dictionary(msgpack_object *obj, Dictionary *arg) - FUNC_ATTR_NONNULL_ALL; - -/// Functions for converting from C types to msgpack types. -/// These are used by `msgpack_rpc_dispatch` to convert return values -/// from the API -/// -/// @param result A pointer to the result -/// @param res A packer that contains the response -void msgpack_rpc_from_boolean(Boolean result, msgpack_packer *res) - FUNC_ATTR_NONNULL_ARG(2); -void msgpack_rpc_from_integer(Integer result, msgpack_packer *res) - FUNC_ATTR_NONNULL_ARG(2); -void msgpack_rpc_from_float(Float result, msgpack_packer *res) - FUNC_ATTR_NONNULL_ARG(2); -void msgpack_rpc_from_position(Position result, msgpack_packer *res) - FUNC_ATTR_NONNULL_ARG(2); -void msgpack_rpc_from_string(String result, msgpack_packer *res) - FUNC_ATTR_NONNULL_ARG(2); -void msgpack_rpc_from_buffer(Buffer result, msgpack_packer *res) - FUNC_ATTR_NONNULL_ARG(2); -void msgpack_rpc_from_window(Window result, msgpack_packer *res) - FUNC_ATTR_NONNULL_ARG(2); -void msgpack_rpc_from_tabpage(Tabpage result, msgpack_packer *res) - FUNC_ATTR_NONNULL_ARG(2); -void msgpack_rpc_from_object(Object result, msgpack_packer *res) - FUNC_ATTR_NONNULL_ARG(2); -void msgpack_rpc_from_stringarray(StringArray result, msgpack_packer *res) - FUNC_ATTR_NONNULL_ARG(2); -void msgpack_rpc_from_bufferarray(BufferArray result, msgpack_packer *res) - FUNC_ATTR_NONNULL_ARG(2); -void msgpack_rpc_from_windowarray(WindowArray result, msgpack_packer *res) - FUNC_ATTR_NONNULL_ARG(2); -void msgpack_rpc_from_tabpagearray(TabpageArray result, msgpack_packer *res) - FUNC_ATTR_NONNULL_ARG(2); -void msgpack_rpc_from_array(Array result, msgpack_packer *res) - FUNC_ATTR_NONNULL_ARG(2); -void msgpack_rpc_from_dictionary(Dictionary result, msgpack_packer *res) - FUNC_ATTR_NONNULL_ARG(2); - -/// Helpers for initializing types that may be freed later -#define msgpack_rpc_init_boolean -#define msgpack_rpc_init_integer -#define msgpack_rpc_init_float -#define msgpack_rpc_init_position -#define msgpack_rpc_init_string = STRING_INIT -#define msgpack_rpc_init_buffer -#define msgpack_rpc_init_window -#define msgpack_rpc_init_tabpage -#define msgpack_rpc_init_object = {.type = kObjectTypeNil} -#define msgpack_rpc_init_stringarray = ARRAY_DICT_INIT -#define msgpack_rpc_init_bufferarray = ARRAY_DICT_INIT -#define msgpack_rpc_init_windowarray = ARRAY_DICT_INIT -#define msgpack_rpc_init_tabpagearray = ARRAY_DICT_INIT -#define msgpack_rpc_init_array = ARRAY_DICT_INIT -#define msgpack_rpc_init_dictionary = ARRAY_DICT_INIT - -/// Helpers for freeing arguments/return value -/// -/// @param value The value to be freed -#define msgpack_rpc_free_boolean(value) -#define msgpack_rpc_free_integer(value) -#define msgpack_rpc_free_float(value) -#define msgpack_rpc_free_position(value) -void msgpack_rpc_free_string(String value); -#define msgpack_rpc_free_buffer(value) -#define msgpack_rpc_free_window(value) -#define msgpack_rpc_free_tabpage(value) -void msgpack_rpc_free_object(Object value); -void msgpack_rpc_free_stringarray(StringArray value); -void msgpack_rpc_free_bufferarray(BufferArray value); -void msgpack_rpc_free_windowarray(WindowArray value); -void msgpack_rpc_free_tabpagearray(TabpageArray value); -void msgpack_rpc_free_array(Array value); -void msgpack_rpc_free_dictionary(Dictionary value); +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "os/msgpack_rpc.h.generated.h" +#endif #endif // NVIM_OS_MSGPACK_RPC_H diff --git a/src/nvim/os/msgpack_rpc_helpers.c b/src/nvim/os/msgpack_rpc_helpers.c new file mode 100644 index 0000000000..3af6794169 --- /dev/null +++ b/src/nvim/os/msgpack_rpc_helpers.c @@ -0,0 +1,331 @@ +#include <stdint.h> +#include <stdbool.h> + +#include <msgpack.h> + +#include "nvim/os/msgpack_rpc_helpers.h" +#include "nvim/vim.h" +#include "nvim/memory.h" + +#define REMOTE_FUNCS_IMPL(t, lt) \ + bool msgpack_rpc_to_##lt(msgpack_object *obj, t *arg) \ + { \ + *arg = obj->via.u64; \ + return obj->type == MSGPACK_OBJECT_POSITIVE_INTEGER; \ + } \ + \ + void msgpack_rpc_from_##lt(t result, msgpack_packer *res) \ + { \ + msgpack_pack_uint64(res, result); \ + } + +#define TYPED_ARRAY_IMPL(t, lt) \ + bool msgpack_rpc_to_##lt##array(msgpack_object *obj, t##Array *arg) \ + { \ + if (obj->type != MSGPACK_OBJECT_ARRAY) { \ + return false; \ + } \ + \ + arg->size = obj->via.array.size; \ + arg->items = xcalloc(obj->via.array.size, sizeof(t)); \ + \ + for (size_t i = 0; i < obj->via.array.size; i++) { \ + if (!msgpack_rpc_to_##lt(obj->via.array.ptr + i, &arg->items[i])) { \ + return false; \ + } \ + } \ + \ + return true; \ + } \ + \ + void msgpack_rpc_from_##lt##array(t##Array result, msgpack_packer *res) \ + { \ + msgpack_pack_array(res, result.size); \ + \ + for (size_t i = 0; i < result.size; i++) { \ + msgpack_rpc_from_##lt(result.items[i], res); \ + } \ + } \ + \ + void msgpack_rpc_free_##lt##array(t##Array value) { \ + for (size_t i = 0; i < value.size; i++) { \ + msgpack_rpc_free_##lt(value.items[i]); \ + } \ + \ + free(value.items); \ + } + +bool msgpack_rpc_to_boolean(msgpack_object *obj, Boolean *arg) +{ + *arg = obj->via.boolean; + return obj->type == MSGPACK_OBJECT_BOOLEAN; +} + +bool msgpack_rpc_to_integer(msgpack_object *obj, Integer *arg) +{ + if (obj->type == MSGPACK_OBJECT_POSITIVE_INTEGER + && obj->via.u64 <= INT64_MAX) { + *arg = (int64_t)obj->via.u64; + return true; + } + + *arg = obj->via.i64; + return obj->type == MSGPACK_OBJECT_NEGATIVE_INTEGER; +} + +bool msgpack_rpc_to_float(msgpack_object *obj, Float *arg) +{ + *arg = obj->via.dec; + return obj->type == MSGPACK_OBJECT_DOUBLE; +} + +bool msgpack_rpc_to_string(msgpack_object *obj, String *arg) +{ + if (obj->type != MSGPACK_OBJECT_RAW) { + return false; + } + + arg->data = xmemdupz(obj->via.raw.ptr, obj->via.raw.size); + arg->size = obj->via.raw.size; + return true; +} + +bool msgpack_rpc_to_object(msgpack_object *obj, Object *arg) +{ + switch (obj->type) { + case MSGPACK_OBJECT_NIL: + arg->type = kObjectTypeNil; + return true; + + case MSGPACK_OBJECT_BOOLEAN: + arg->type = kObjectTypeBoolean; + return msgpack_rpc_to_boolean(obj, &arg->data.boolean); + + case MSGPACK_OBJECT_POSITIVE_INTEGER: + case MSGPACK_OBJECT_NEGATIVE_INTEGER: + arg->type = kObjectTypeInteger; + return msgpack_rpc_to_integer(obj, &arg->data.integer); + + case MSGPACK_OBJECT_DOUBLE: + arg->type = kObjectTypeFloat; + return msgpack_rpc_to_float(obj, &arg->data.floating); + + case MSGPACK_OBJECT_RAW: + arg->type = kObjectTypeString; + return msgpack_rpc_to_string(obj, &arg->data.string); + + case MSGPACK_OBJECT_ARRAY: + arg->type = kObjectTypeArray; + return msgpack_rpc_to_array(obj, &arg->data.array); + + case MSGPACK_OBJECT_MAP: + arg->type = kObjectTypeDictionary; + return msgpack_rpc_to_dictionary(obj, &arg->data.dictionary); + + default: + return false; + } +} + +bool msgpack_rpc_to_position(msgpack_object *obj, Position *arg) +{ + return obj->type == MSGPACK_OBJECT_ARRAY + && obj->via.array.size == 2 + && msgpack_rpc_to_integer(obj->via.array.ptr, &arg->row) + && msgpack_rpc_to_integer(obj->via.array.ptr + 1, &arg->col); +} + + +bool msgpack_rpc_to_array(msgpack_object *obj, Array *arg) +{ + if (obj->type != MSGPACK_OBJECT_ARRAY) { + return false; + } + + arg->size = obj->via.array.size; + arg->items = xcalloc(obj->via.array.size, sizeof(Object)); + + for (uint32_t i = 0; i < obj->via.array.size; i++) { + if (!msgpack_rpc_to_object(obj->via.array.ptr + i, &arg->items[i])) { + return false; + } + } + + return true; +} + +bool msgpack_rpc_to_dictionary(msgpack_object *obj, Dictionary *arg) +{ + if (obj->type != MSGPACK_OBJECT_MAP) { + return false; + } + + arg->size = obj->via.array.size; + arg->items = xcalloc(obj->via.map.size, sizeof(KeyValuePair)); + + + for (uint32_t i = 0; i < obj->via.map.size; i++) { + if (!msgpack_rpc_to_string(&obj->via.map.ptr[i].key, + &arg->items[i].key)) { + return false; + } + + if (!msgpack_rpc_to_object(&obj->via.map.ptr[i].val, + &arg->items[i].value)) { + return false; + } + } + + return true; +} + +void msgpack_rpc_from_boolean(Boolean result, msgpack_packer *res) +{ + if (result) { + msgpack_pack_true(res); + } else { + msgpack_pack_false(res); + } +} + +void msgpack_rpc_from_integer(Integer result, msgpack_packer *res) +{ + msgpack_pack_int64(res, result); +} + +void msgpack_rpc_from_float(Float result, msgpack_packer *res) +{ + msgpack_pack_double(res, result); +} + +void msgpack_rpc_from_string(String result, msgpack_packer *res) +{ + msgpack_pack_raw(res, result.size); + msgpack_pack_raw_body(res, result.data, result.size); +} + +void msgpack_rpc_from_object(Object result, msgpack_packer *res) +{ + switch (result.type) { + case kObjectTypeNil: + msgpack_pack_nil(res); + break; + + case kObjectTypeBoolean: + msgpack_rpc_from_boolean(result.data.boolean, res); + break; + + case kObjectTypeInteger: + msgpack_rpc_from_integer(result.data.integer, res); + break; + + case kObjectTypeFloat: + msgpack_rpc_from_float(result.data.floating, res); + break; + + case kObjectTypeString: + msgpack_rpc_from_string(result.data.string, res); + break; + + case kObjectTypeArray: + msgpack_rpc_from_array(result.data.array, res); + break; + + case kObjectTypeDictionary: + msgpack_rpc_from_dictionary(result.data.dictionary, res); + break; + + default: + abort(); + } +} + +void msgpack_rpc_from_position(Position result, msgpack_packer *res) +{ + msgpack_pack_array(res, 2);; + msgpack_pack_int64(res, result.row); + msgpack_pack_int64(res, result.col); +} + +void msgpack_rpc_from_array(Array result, msgpack_packer *res) +{ + msgpack_pack_array(res, result.size); + + for (size_t i = 0; i < result.size; i++) { + msgpack_rpc_from_object(result.items[i], res); + } +} + +void msgpack_rpc_from_dictionary(Dictionary result, msgpack_packer *res) +{ + msgpack_pack_map(res, result.size); + + for (size_t i = 0; i < result.size; i++) { + msgpack_rpc_from_string(result.items[i].key, res); + msgpack_rpc_from_object(result.items[i].value, res); + } +} + +void msgpack_rpc_free_string(String value) +{ + if (!value.data) { + return; + } + + free(value.data); +} + +void msgpack_rpc_free_object(Object value) +{ + switch (value.type) { + case kObjectTypeNil: + case kObjectTypeBoolean: + case kObjectTypeInteger: + case kObjectTypeFloat: + break; + + case kObjectTypeString: + msgpack_rpc_free_string(value.data.string); + break; + + case kObjectTypeArray: + msgpack_rpc_free_array(value.data.array); + break; + + case kObjectTypeDictionary: + msgpack_rpc_free_dictionary(value.data.dictionary); + break; + + default: + abort(); + } +} + +void msgpack_rpc_free_array(Array value) +{ + for (uint32_t i = 0; i < value.size; i++) { + msgpack_rpc_free_object(value.items[i]); + } + + free(value.items); +} + +void msgpack_rpc_free_dictionary(Dictionary value) +{ + for (uint32_t i = 0; i < value.size; i++) { + msgpack_rpc_free_string(value.items[i].key); + msgpack_rpc_free_object(value.items[i].value); + } + + free(value.items); +} + +REMOTE_FUNCS_IMPL(Buffer, buffer) +REMOTE_FUNCS_IMPL(Window, window) +REMOTE_FUNCS_IMPL(Tabpage, tabpage) + +TYPED_ARRAY_IMPL(Buffer, buffer) +TYPED_ARRAY_IMPL(Window, window) +TYPED_ARRAY_IMPL(Tabpage, tabpage) +TYPED_ARRAY_IMPL(String, string) + diff --git a/src/nvim/os/msgpack_rpc_helpers.h b/src/nvim/os/msgpack_rpc_helpers.h new file mode 100644 index 0000000000..e3d1e756ef --- /dev/null +++ b/src/nvim/os/msgpack_rpc_helpers.h @@ -0,0 +1,124 @@ +#ifndef NVIM_OS_MSGPACK_RPC_HELPERS_H +#define NVIM_OS_MSGPACK_RPC_HELPERS_H + +#include <stdint.h> +#include <stdbool.h> + +#include <msgpack.h> + +#include "nvim/func_attr.h" +#include "nvim/api/private/defs.h" + +/// Functions for validating and converting from msgpack types to C types. +/// These are used by `msgpack_rpc_dispatch` to validate and convert each +/// argument. +/// +/// @param obj The object to convert +/// @param[out] arg A pointer to the avalue +/// @return true if the conversion succeeded, false otherwise +bool msgpack_rpc_to_boolean(msgpack_object *obj, Boolean *arg) + FUNC_ATTR_NONNULL_ALL; +bool msgpack_rpc_to_integer(msgpack_object *obj, Integer *arg) + FUNC_ATTR_NONNULL_ALL; +bool msgpack_rpc_to_float(msgpack_object *obj, Float *arg) + FUNC_ATTR_NONNULL_ALL; +bool msgpack_rpc_to_position(msgpack_object *obj, Position *arg) + FUNC_ATTR_NONNULL_ALL; +bool msgpack_rpc_to_string(msgpack_object *obj, String *arg) + FUNC_ATTR_NONNULL_ALL; +bool msgpack_rpc_to_buffer(msgpack_object *obj, Buffer *arg) + FUNC_ATTR_NONNULL_ALL; +bool msgpack_rpc_to_window(msgpack_object *obj, Window *arg) + FUNC_ATTR_NONNULL_ALL; +bool msgpack_rpc_to_tabpage(msgpack_object *obj, Tabpage *arg) + FUNC_ATTR_NONNULL_ALL; +bool msgpack_rpc_to_object(msgpack_object *obj, Object *arg) + FUNC_ATTR_NONNULL_ALL; +bool msgpack_rpc_to_stringarray(msgpack_object *obj, StringArray *arg) + FUNC_ATTR_NONNULL_ALL; +bool msgpack_rpc_to_bufferarray(msgpack_object *obj, BufferArray *arg) + FUNC_ATTR_NONNULL_ALL; +bool msgpack_rpc_to_windowarray(msgpack_object *obj, WindowArray *arg) + FUNC_ATTR_NONNULL_ALL; +bool msgpack_rpc_to_tabpagearray(msgpack_object *obj, TabpageArray *arg) + FUNC_ATTR_NONNULL_ALL; +bool msgpack_rpc_to_array(msgpack_object *obj, Array *arg) + FUNC_ATTR_NONNULL_ALL; +bool msgpack_rpc_to_dictionary(msgpack_object *obj, Dictionary *arg) + FUNC_ATTR_NONNULL_ALL; + +/// Functions for converting from C types to msgpack types. +/// These are used by `msgpack_rpc_dispatch` to convert return values +/// from the API +/// +/// @param result A pointer to the result +/// @param res A packer that contains the response +void msgpack_rpc_from_boolean(Boolean result, msgpack_packer *res) + FUNC_ATTR_NONNULL_ARG(2); +void msgpack_rpc_from_integer(Integer result, msgpack_packer *res) + FUNC_ATTR_NONNULL_ARG(2); +void msgpack_rpc_from_float(Float result, msgpack_packer *res) + FUNC_ATTR_NONNULL_ARG(2); +void msgpack_rpc_from_position(Position result, msgpack_packer *res) + FUNC_ATTR_NONNULL_ARG(2); +void msgpack_rpc_from_string(String result, msgpack_packer *res) + FUNC_ATTR_NONNULL_ARG(2); +void msgpack_rpc_from_buffer(Buffer result, msgpack_packer *res) + FUNC_ATTR_NONNULL_ARG(2); +void msgpack_rpc_from_window(Window result, msgpack_packer *res) + FUNC_ATTR_NONNULL_ARG(2); +void msgpack_rpc_from_tabpage(Tabpage result, msgpack_packer *res) + FUNC_ATTR_NONNULL_ARG(2); +void msgpack_rpc_from_object(Object result, msgpack_packer *res) + FUNC_ATTR_NONNULL_ARG(2); +void msgpack_rpc_from_stringarray(StringArray result, msgpack_packer *res) + FUNC_ATTR_NONNULL_ARG(2); +void msgpack_rpc_from_bufferarray(BufferArray result, msgpack_packer *res) + FUNC_ATTR_NONNULL_ARG(2); +void msgpack_rpc_from_windowarray(WindowArray result, msgpack_packer *res) + FUNC_ATTR_NONNULL_ARG(2); +void msgpack_rpc_from_tabpagearray(TabpageArray result, msgpack_packer *res) + FUNC_ATTR_NONNULL_ARG(2); +void msgpack_rpc_from_array(Array result, msgpack_packer *res) + FUNC_ATTR_NONNULL_ARG(2); +void msgpack_rpc_from_dictionary(Dictionary result, msgpack_packer *res) + FUNC_ATTR_NONNULL_ARG(2); + +/// Helpers for initializing types that may be freed later +#define msgpack_rpc_init_boolean +#define msgpack_rpc_init_integer +#define msgpack_rpc_init_float +#define msgpack_rpc_init_position +#define msgpack_rpc_init_string = STRING_INIT +#define msgpack_rpc_init_buffer +#define msgpack_rpc_init_window +#define msgpack_rpc_init_tabpage +#define msgpack_rpc_init_object = {.type = kObjectTypeNil} +#define msgpack_rpc_init_stringarray = ARRAY_DICT_INIT +#define msgpack_rpc_init_bufferarray = ARRAY_DICT_INIT +#define msgpack_rpc_init_windowarray = ARRAY_DICT_INIT +#define msgpack_rpc_init_tabpagearray = ARRAY_DICT_INIT +#define msgpack_rpc_init_array = ARRAY_DICT_INIT +#define msgpack_rpc_init_dictionary = ARRAY_DICT_INIT + +/// Helpers for freeing arguments/return value +/// +/// @param value The value to be freed +#define msgpack_rpc_free_boolean(value) +#define msgpack_rpc_free_integer(value) +#define msgpack_rpc_free_float(value) +#define msgpack_rpc_free_position(value) +void msgpack_rpc_free_string(String value); +#define msgpack_rpc_free_buffer(value) +#define msgpack_rpc_free_window(value) +#define msgpack_rpc_free_tabpage(value) +void msgpack_rpc_free_object(Object value); +void msgpack_rpc_free_stringarray(StringArray value); +void msgpack_rpc_free_bufferarray(BufferArray value); +void msgpack_rpc_free_windowarray(WindowArray value); +void msgpack_rpc_free_tabpagearray(TabpageArray value); +void msgpack_rpc_free_array(Array value); +void msgpack_rpc_free_dictionary(Dictionary value); + +#endif // NVIM_OS_MSGPACK_RPC_HELPERS_H + |