refactor(api): use hashy hash for looking up api method and event names

This avoids generating khash tables at runtime, and is consistent with
how evalfuncs lookup work.
This commit is contained in:
bfredl
2022-05-30 00:59:06 +02:00
parent e9803e1de6
commit 1f63052b68
10 changed files with 65 additions and 79 deletions

View File

@@ -32,37 +32,22 @@
#include "nvim/api/window.h" #include "nvim/api/window.h"
#include "nvim/ui_client.h" #include "nvim/ui_client.h"
static Map(String, MsgpackRpcRequestHandler) methods = MAP_INIT; #ifdef INCLUDE_GENERATED_DECLARATIONS
# include "api/private/dispatch_wrappers.generated.h"
static void msgpack_rpc_add_method_handler(String method, MsgpackRpcRequestHandler handler) #endif
{
map_put(String, MsgpackRpcRequestHandler)(&methods, method, handler);
}
void msgpack_rpc_add_redraw(void)
{
msgpack_rpc_add_method_handler(STATIC_CSTR_AS_STRING("redraw"),
(MsgpackRpcRequestHandler) { .fn = ui_client_handle_redraw,
.fast = true });
}
/// @param name API method name /// @param name API method name
/// @param name_len name size (includes terminating NUL) /// @param name_len name size (includes terminating NUL)
MsgpackRpcRequestHandler msgpack_rpc_get_handler_for(const char *name, size_t name_len, MsgpackRpcRequestHandler msgpack_rpc_get_handler_for(const char *name, size_t name_len,
Error *error) Error *error)
{ {
String m = { .data = (char *)name, .size = name_len }; int hash = msgpack_rpc_get_handler_for_hash(name, name_len);
MsgpackRpcRequestHandler rv =
map_get(String, MsgpackRpcRequestHandler)(&methods, m);
if (!rv.fn) { if (hash < 0) {
api_set_error(error, kErrorTypeException, "Invalid method: %.*s", api_set_error(error, kErrorTypeException, "Invalid method: %.*s",
m.size > 0 ? (int)m.size : (int)sizeof("<empty>"), name_len > 0 ? (int)name_len : (int)sizeof("<empty>"),
m.size > 0 ? m.data : "<empty>"); name_len > 0 ? name : "<empty>");
return (MsgpackRpcRequestHandler){ 0 };
} }
return rv; return method_handlers[hash];
} }
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "api/private/dispatch_wrappers.generated.h"
#endif

View File

@@ -10,6 +10,7 @@ typedef Object (*ApiDispatchWrapper)(uint64_t channel_id,
/// The rpc_method_handlers table, used in msgpack_rpc_dispatch(), stores /// The rpc_method_handlers table, used in msgpack_rpc_dispatch(), stores
/// functions of this type. /// functions of this type.
typedef struct { typedef struct {
const char *name;
ApiDispatchWrapper fn; ApiDispatchWrapper fn;
bool fast; // Function is safe to be executed immediately while running the bool fast; // Function is safe to be executed immediately while running the
// uv loop (the loop is run very frequently due to breakcheck). // uv loop (the loop is run very frequently due to breakcheck).

View File

@@ -16,6 +16,10 @@ local functions = {}
local nvimdir = arg[1] local nvimdir = arg[1]
package.path = nvimdir .. '/?.lua;' .. package.path package.path = nvimdir .. '/?.lua;' .. package.path
_G.vim = loadfile(nvimdir..'/../../runtime/lua/vim/shared.lua')()
local hashy = require'generators.hashy'
-- names of all headers relative to the source root (for inclusion in the -- names of all headers relative to the source root (for inclusion in the
-- generated file) -- generated file)
local headers = {} local headers = {}
@@ -339,24 +343,27 @@ for i = 1, #functions do
end end
end end
-- Generate a function that initializes method names with handler functions local remote_fns = {}
output:write([[ for _,fn in ipairs(functions) do
void msgpack_rpc_init_method_table(void)
{
]])
for i = 1, #functions do
local fn = functions[i]
if fn.remote then if fn.remote then
output:write(' msgpack_rpc_add_method_handler('.. remote_fns[fn.name] = fn
'(String) {.data = "'..fn.name..'", '..
'.size = sizeof("'..fn.name..'") - 1}, '..
'(MsgpackRpcRequestHandler) {.fn = handle_'.. (fn.impl_name or fn.name)..
', .fast = '..tostring(fn.fast)..'});\n')
end end
end end
remote_fns.redraw = {impl_name="ui_client_redraw", fast=true}
local hashorder, hashfun = hashy.hashy_hash("msgpack_rpc_get_handler_for", vim.tbl_keys(remote_fns), function (idx)
return "method_handlers["..idx.."].name"
end)
output:write("static const MsgpackRpcRequestHandler method_handlers[] = {\n")
for _, name in ipairs(hashorder) do
local fn = remote_fns[name]
output:write(' { .name = "'..name..'", .fn = handle_'.. (fn.impl_name or fn.name)..
', .fast = '..tostring(fn.fast)..'},\n')
end
output:write("};\n\n")
output:write(hashfun)
output:write('\n}\n\n')
output:close() output:close()
local mpack_output = io.open(mpack_outputf, 'wb') local mpack_output = io.open(mpack_outputf, 'wb')

View File

@@ -15,6 +15,9 @@ local client_output = io.open(arg[8], 'wb')
local c_grammar = require('generators.c_grammar') local c_grammar = require('generators.c_grammar')
local events = c_grammar.grammar:match(input:read('*all')) local events = c_grammar.grammar:match(input:read('*all'))
_G.vim = loadfile(nvimdir..'/../../runtime/lua/vim/shared.lua')()
local hashy = require'generators.hashy'
local function write_signature(output, ev, prefix, notype) local function write_signature(output, ev, prefix, notype)
output:write('('..prefix) output:write('('..prefix)
if prefix == "" and #ev.parameters == 0 then if prefix == "" and #ev.parameters == 0 then
@@ -213,24 +216,25 @@ for i = 1, #events do
end end
end end
-- Generate the map_init method for client handlers local client_events = {}
client_output:write([[ for _,ev in ipairs(events) do
void ui_client_methods_table_init(void) if (not ev.noexport) and ((not ev.remote_only) or ev.client_impl) then
{ client_events[ev.name] = ev
]])
for i = 1, #events do
local fn = events[i]
if (not fn.noexport) and ((not fn.remote_only) or fn.client_impl) then
client_output:write(' add_ui_client_event_handler('..
'(String) {.data = "'..fn.name..'", '..
'.size = sizeof("'..fn.name..'") - 1}, '..
'(UIClientHandler) ui_client_event_'..fn.name..');\n')
end end
end end
client_output:write('\n}\n\n') local hashorder, hashfun = hashy.hashy_hash("ui_client_handler", vim.tbl_keys(client_events), function (idx)
return "event_handlers["..idx.."].name"
end)
client_output:write("static const UIClientHandler event_handlers[] = {\n")
for _, name in ipairs(hashorder) do
client_output:write(' { .name = "'..name..'", .fn = ui_client_event_'..name..'},\n')
end
client_output:write('\n};\n\n')
client_output:write(hashfun)
proto_output:close() proto_output:close()
call_output:close() call_output:close()

View File

@@ -121,7 +121,6 @@ void event_init(void)
resize_events = multiqueue_new_child(main_loop.events); resize_events = multiqueue_new_child(main_loop.events);
// early msgpack-rpc initialization // early msgpack-rpc initialization
msgpack_rpc_init_method_table();
msgpack_rpc_helpers_init(); msgpack_rpc_helpers_init();
input_init(); input_init();
signal_init(); signal_init();

View File

@@ -14,7 +14,6 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include "nvim/api/private/dispatch.h"
#include "nvim/lib/khash.h" #include "nvim/lib/khash.h"
#include "nvim/map.h" #include "nvim/map.h"
#include "nvim/map_defs.h" #include "nvim/map_defs.h"
@@ -171,13 +170,10 @@ MAP_IMPL(uint64_t, ssize_t, SSIZE_INITIALIZER)
MAP_IMPL(uint64_t, uint64_t, DEFAULT_INITIALIZER) MAP_IMPL(uint64_t, uint64_t, DEFAULT_INITIALIZER)
MAP_IMPL(uint32_t, uint32_t, DEFAULT_INITIALIZER) MAP_IMPL(uint32_t, uint32_t, DEFAULT_INITIALIZER)
MAP_IMPL(handle_T, ptr_t, DEFAULT_INITIALIZER) MAP_IMPL(handle_T, ptr_t, DEFAULT_INITIALIZER)
#define MSGPACK_HANDLER_INITIALIZER { .fn = NULL, .fast = false }
MAP_IMPL(String, MsgpackRpcRequestHandler, MSGPACK_HANDLER_INITIALIZER)
MAP_IMPL(HlEntry, int, DEFAULT_INITIALIZER) MAP_IMPL(HlEntry, int, DEFAULT_INITIALIZER)
MAP_IMPL(String, handle_T, 0) MAP_IMPL(String, handle_T, 0)
MAP_IMPL(String, int, DEFAULT_INITIALIZER) MAP_IMPL(String, int, DEFAULT_INITIALIZER)
MAP_IMPL(int, String, DEFAULT_INITIALIZER) MAP_IMPL(int, String, DEFAULT_INITIALIZER)
MAP_IMPL(String, UIClientHandler, NULL)
MAP_IMPL(ColorKey, ColorItem, COLOR_ITEM_INITIALIZER) MAP_IMPL(ColorKey, ColorItem, COLOR_ITEM_INITIALIZER)

View File

@@ -4,7 +4,6 @@
#include <stdbool.h> #include <stdbool.h>
#include "nvim/api/private/defs.h" #include "nvim/api/private/defs.h"
#include "nvim/api/private/dispatch.h"
#include "nvim/extmark_defs.h" #include "nvim/extmark_defs.h"
#include "nvim/highlight_defs.h" #include "nvim/highlight_defs.h"
#include "nvim/map_defs.h" #include "nvim/map_defs.h"
@@ -44,12 +43,10 @@ MAP_DECLS(uint64_t, uint64_t)
MAP_DECLS(uint32_t, uint32_t) MAP_DECLS(uint32_t, uint32_t)
MAP_DECLS(handle_T, ptr_t) MAP_DECLS(handle_T, ptr_t)
MAP_DECLS(String, MsgpackRpcRequestHandler)
MAP_DECLS(HlEntry, int) MAP_DECLS(HlEntry, int)
MAP_DECLS(String, handle_T) MAP_DECLS(String, handle_T)
MAP_DECLS(String, int) MAP_DECLS(String, int)
MAP_DECLS(int, String) MAP_DECLS(int, String)
MAP_DECLS(String, UIClientHandler)
MAP_DECLS(ColorKey, ColorItem) MAP_DECLS(ColorKey, ColorItem)

View File

@@ -6,6 +6,7 @@
#include <uv.h> #include <uv.h>
#include "nvim/api/private/defs.h" #include "nvim/api/private/defs.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/vim.h" #include "nvim/vim.h"

View File

@@ -16,18 +16,17 @@
#include "nvim/ui_client.h" #include "nvim/ui_client.h"
#include "nvim/vim.h" #include "nvim/vim.h"
static Map(String, UIClientHandler) ui_client_handlers = MAP_INIT; #ifdef INCLUDE_GENERATED_DECLARATIONS
# include "ui_client.c.generated.h"
# include "ui_events_client.generated.h"
#endif
// Temporary buffer for converting a single grid_line event // Temporary buffer for converting a single grid_line event
static size_t buf_size = 0; static size_t buf_size = 0;
static schar_T *buf_char = NULL; static schar_T *buf_char = NULL;
static sattr_T *buf_attr = NULL; static sattr_T *buf_attr = NULL;
static void add_ui_client_event_handler(String method, UIClientHandler handler)
{
map_put(String, UIClientHandler)(&ui_client_handlers, method, handler);
}
void ui_client_init(uint64_t chan) void ui_client_init(uint64_t chan)
{ {
Array args = ARRAY_DICT_INIT; Array args = ARRAY_DICT_INIT;
@@ -44,9 +43,6 @@ void ui_client_init(uint64_t chan)
ADD(args, DICTIONARY_OBJ(opts)); ADD(args, DICTIONARY_OBJ(opts));
rpc_send_event(chan, "nvim_ui_attach", args); rpc_send_event(chan, "nvim_ui_attach", args);
msgpack_rpc_add_redraw(); // GAME!
// TODO(bfredl): use a keyset instead
ui_client_methods_table_init();
ui_client_channel_id = chan; ui_client_channel_id = chan;
} }
@@ -61,22 +57,23 @@ void ui_client_init(uint64_t chan)
/// @param channel_id: The id of the rpc channel /// @param channel_id: The id of the rpc channel
/// @param uidata: The dense array containing the ui_events sent by the server /// @param uidata: The dense array containing the ui_events sent by the server
/// @param[out] err Error details, if any /// @param[out] err Error details, if any
Object ui_client_handle_redraw(uint64_t channel_id, Array args, Error *error) Object handle_ui_client_redraw(uint64_t channel_id, Array args, Error *error)
{ {
for (size_t i = 0; i < args.size; i++) { for (size_t i = 0; i < args.size; i++) {
Array call = args.items[i].data.array; Array call = args.items[i].data.array;
String name = call.items[0].data.string; String name = call.items[0].data.string;
UIClientHandler handler = map_get(String, UIClientHandler)(&ui_client_handlers, name); int hash = ui_client_handler_hash(name.data, name.size);
if (!handler) { if (hash < 0) {
ELOG("No ui client handler for %s", name.size ? name.data : "<empty>"); ELOG("No ui client handler for %s", name.size ? name.data : "<empty>");
continue; continue;
} }
UIClientHandler handler = event_handlers[hash];
// fprintf(stderr, "%s: %zu\n", name.data, call.size-1); // fprintf(stderr, "%s: %zu\n", name.data, call.size-1);
DLOG("Invoke ui client handler for %s", name.data); DLOG("Invoke ui client handler for %s", name.data);
for (size_t j = 1; j < call.size; j++) { for (size_t j = 1; j < call.size; j++) {
handler(call.items[j].data.array); handler.fn(call.items[j].data.array);
} }
} }
@@ -108,10 +105,6 @@ static HlAttrs ui_client_dict2hlattrs(Dictionary d, bool rgb)
return dict2hlattrs(&dict, true, NULL, &err); return dict2hlattrs(&dict, true, NULL, &err);
} }
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "ui_events_client.generated.h"
#endif
void ui_client_event_grid_resize(Array args) void ui_client_event_grid_resize(Array args)
{ {
if (args.size < 3 if (args.size < 3

View File

@@ -3,7 +3,10 @@
#include "nvim/api/private/defs.h" #include "nvim/api/private/defs.h"
typedef void (*UIClientHandler)(Array args); typedef struct {
const char *name;
void (*fn)(Array args);
} UIClientHandler;
#ifdef INCLUDE_GENERATED_DECLARATIONS #ifdef INCLUDE_GENERATED_DECLARATIONS
# include "ui_client.h.generated.h" # include "ui_client.h.generated.h"