diff options
author | Michael Lingelbach <m.j.lbach@gmail.com> | 2021-09-23 13:39:47 -0400 |
---|---|---|
committer | Michael Lingelbach <m.j.lbach@gmail.com> | 2021-09-26 00:35:55 -0700 |
commit | 30fed27241cc2a8f930212375f994a7fa6c99008 (patch) | |
tree | 37e5ebc68bfbc835021640fda72be82c0074506d | |
parent | 8decc9f52d7dbd8eeed3afdf86f800b3677d25ab (diff) | |
download | rneovim-30fed27241cc2a8f930212375f994a7fa6c99008.tar.gz rneovim-30fed27241cc2a8f930212375f994a7fa6c99008.tar.bz2 rneovim-30fed27241cc2a8f930212375f994a7fa6c99008.zip |
feat(lua): expose lua-cjson as vim.json
* add vim.json.encode and vim.json.decode
* use vim.NIL instead of cjson.null
* resolve strict-prototypes warnings
* The following benchmark shows an approximately 2.5x (750 ms vs 300 ms) improvement in deserialization performance over
vim.fn.json_decode on a medium package.json
```lua
local uv = vim.loop
local function readfile(path)
return
end
local json_url = "https://raw.githubusercontent.com/rust-analyzer/rust-analyzer/b24c8d5c89ee93d1172b4127564f5da3b0c88dad/editors/code/package.json"
io.popen(string.format('curl -v -f -L -O %q &> /dev/null', json_url))
local json_string = io.open('package.json'):read '*a'
uv.update_time()
local start = uv.hrtime()
for i = 1,1000 do
vim.fn.json_decode(json_string)
end
uv.update_time()
print(string.format("Deserialization time vim.fn.json_decode: %s ms", (uv.hrtime() - start) * (1e-6)))
uv.update_time()
local start = uv.hrtime()
for i = 1,1000 do
vim.json.decode(json_string)
end
uv.update_time()
print(string.format("Deserialization time vim.json.decode: %s ms", (uv.hrtime() - start) * (1e-6)))
```
Co-Authored-By: Björn Linse <bjorn.linse@gmail.com>
-rw-r--r-- | src/cjson/fpconv.c | 2 | ||||
-rw-r--r-- | src/cjson/fpconv.h | 2 | ||||
-rw-r--r-- | src/cjson/lua_cjson.c | 41 | ||||
-rw-r--r-- | src/cjson/lua_cjson.h | 10 | ||||
-rw-r--r-- | src/nvim/CMakeLists.txt | 6 | ||||
-rw-r--r-- | src/nvim/lua/executor.c | 4 |
6 files changed, 49 insertions, 16 deletions
diff --git a/src/cjson/fpconv.c b/src/cjson/fpconv.c index 3e480ece0a..b2f7a214c2 100644 --- a/src/cjson/fpconv.c +++ b/src/cjson/fpconv.c @@ -55,7 +55,7 @@ static char locale_decimal_point = '.'; * localconv() may not be thread safe (=>crash), and nl_langinfo() is * not supported on some platforms. Use sprintf() instead - if the * locale does change, at least Lua CJSON won't crash. */ -static void fpconv_update_locale() +static void fpconv_update_locale(void) { char buf[8]; diff --git a/src/cjson/fpconv.h b/src/cjson/fpconv.h index 7b0d0ee313..6ac97808b6 100644 --- a/src/cjson/fpconv.h +++ b/src/cjson/fpconv.h @@ -12,7 +12,7 @@ static inline void fpconv_init() /* Do nothing - not required */ } #else -extern void fpconv_init(); +extern void fpconv_init(void); #endif extern int fpconv_g_fmt(char*, double, int); diff --git a/src/cjson/lua_cjson.c b/src/cjson/lua_cjson.c index 0c41cabac4..92d07963bd 100644 --- a/src/cjson/lua_cjson.c +++ b/src/cjson/lua_cjson.c @@ -44,6 +44,9 @@ #include <lua.h> #include <lauxlib.h> +#include "nvim/lua/executor.h" + +#include "lua_cjson.h" #include "strbuf.h" #include "fpconv.h" @@ -79,7 +82,7 @@ #define DEFAULT_DECODE_INVALID_NUMBERS 1 #define DEFAULT_ENCODE_KEEP_BUFFER 1 #define DEFAULT_ENCODE_NUMBER_PRECISION 14 -#define DEFAULT_ENCODE_EMPTY_TABLE_AS_OBJECT 1 +#define DEFAULT_ENCODE_EMPTY_TABLE_AS_OBJECT 0 #define DEFAULT_DECODE_ARRAY_WITH_ARRAY_MT 0 #define DEFAULT_ENCODE_ESCAPE_FORWARD_SLASH 1 @@ -741,6 +744,7 @@ static void json_append_data(lua_State *l, json_config_t *cfg, { int len; int as_array = 0; + int as_empty_dict = 0; int has_metatable; switch (lua_type(l, -1)) { @@ -763,10 +767,17 @@ static void json_append_data(lua_State *l, json_config_t *cfg, has_metatable = lua_getmetatable(l, -1); if (has_metatable) { + + nlua_pushref(l, nlua_empty_dict_ref); + if (lua_rawequal(l, -2, -1)) { + as_empty_dict = true; + } else { + lua_pop(l, 1); lua_pushlightuserdata(l, json_lightudata_mask(&json_array)); lua_rawget(l, LUA_REGISTRYINDEX); as_array = lua_rawequal(l, -1, -2); - lua_pop(l, 2); + } + lua_pop(l, 2); } if (as_array) { @@ -775,7 +786,7 @@ static void json_append_data(lua_State *l, json_config_t *cfg, } else { len = lua_array_length(l, cfg, json); - if (len > 0 || (len == 0 && !cfg->encode_empty_table_as_object)) { + if (len > 0 || (len == 0 && !cfg->encode_empty_table_as_object && !as_empty_dict)) { json_append_array(l, cfg, current_depth, json, len); } else { if (has_metatable) { @@ -798,12 +809,20 @@ static void json_append_data(lua_State *l, json_config_t *cfg, strbuf_append_mem(json, "null", 4); break; case LUA_TLIGHTUSERDATA: - if (lua_touserdata(l, -1) == NULL) { - strbuf_append_mem(json, "null", 4); - } else if (lua_touserdata(l, -1) == &json_array) { + if (lua_touserdata(l, -1) == &json_array) { json_append_array(l, cfg, current_depth, json, 0); } break; + case LUA_TUSERDATA: + nlua_pushref(l, nlua_nil_ref); + bool is_nil = lua_rawequal(l, -2, -1); + lua_pop(l, 1); + if (is_nil) { + strbuf_append_mem(json, "null", 4); + break; + } else { + FALLTHROUGH; + } default: /* Remaining types (LUA_TFUNCTION, LUA_TUSERDATA, LUA_TTHREAD, * and LUA_TLIGHTUSERDATA) cannot be serialised */ @@ -1258,6 +1277,8 @@ static void json_parse_object_context(lua_State *l, json_parse_t *json) /* Handle empty objects */ if (token.type == T_OBJ_END) { + nlua_pushref(l, nlua_empty_dict_ref); \ + lua_setmetatable(l, -2); \ json_decode_ascend(json); return; } @@ -1360,9 +1381,7 @@ static void json_process_value(lua_State *l, json_parse_t *json, json_parse_array_context(l, json); break;; case T_NULL: - /* In Lua, setting "t[k] = nil" will delete k from the table. - * Hence a NULL pointer lightuserdata object is used instead */ - lua_pushlightuserdata(l, NULL); + nlua_pushref(l, nlua_nil_ref); break;; default: json_throw_parse_error(l, json, "value", token); @@ -1464,7 +1483,7 @@ static int json_protect_conversion(lua_State *l) } /* Return cjson module table */ -static int lua_cjson_new(lua_State *l) +int lua_cjson_new(lua_State *l) { luaL_Reg reg[] = { { "encode", json_encode }, @@ -1517,7 +1536,7 @@ static int lua_cjson_new(lua_State *l) compat_luaL_setfuncs(l, reg, 1); /* Set cjson.null */ - lua_pushlightuserdata(l, NULL); + nlua_pushref(l, nlua_nil_ref); lua_setfield(l, -2, "null"); /* Set cjson.empty_array_mt */ diff --git a/src/cjson/lua_cjson.h b/src/cjson/lua_cjson.h new file mode 100644 index 0000000000..3f70b679be --- /dev/null +++ b/src/cjson/lua_cjson.h @@ -0,0 +1,10 @@ +#ifndef CJSON_LUACJSON_H +#define CJSON_LUACJSON_H + +#include "lua.h" + +int lua_cjson_new(lua_State *l); +int luaopen_cjson(lua_State *l); +int luaopen_cjson_safe(lua_State *l); + +#endif // CJSON_LUACJSON_H diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index 331ab16dd7..e35e7ce2d4 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -87,8 +87,8 @@ file(MAKE_DIRECTORY ${LINT_SUPPRESSES_ROOT}/src) file(GLOB NVIM_SOURCES *.c) file(GLOB NVIM_HEADERS *.h) -file(GLOB EXTERNAL_SOURCES ../xdiff/*.c ../mpack/*.c) -file(GLOB EXTERNAL_HEADERS ../xdiff/*.h ../mpack/*.h) +file(GLOB EXTERNAL_SOURCES ../xdiff/*.c ../mpack/*.c ../cjson/*.c) +file(GLOB EXTERNAL_HEADERS ../xdiff/*.h ../mpack/*.h ../cjson/*.h) foreach(subdir os @@ -171,7 +171,7 @@ foreach(sfile ${CONV_SOURCES}) message(FATAL_ERROR "${sfile} doesn't exist (it was added to CONV_SOURCES)") endif() endforeach() -# xdiff, mpack: inlined external project, we don't maintain it. #9306 +# xdiff, mpack, lua-cjson: inlined external project, we don't maintain it. #9306 list(APPEND CONV_SOURCES ${EXTERNAL_SOURCES}) if(NOT MSVC) diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index 8c7dc90111..9333d781cd 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -40,6 +40,7 @@ #include "nvim/undo.h" #include "nvim/version.h" #include "nvim/vim.h" +#include "cjson/lua_cjson.h" static int in_fast_callback = 0; @@ -531,6 +532,9 @@ static int nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL lua_pushcfunction(lstate, &nlua_xdl_diff); lua_setfield(lstate, -2, "diff"); + lua_cjson_new(lstate); + lua_setfield(lstate, -2, "json"); + lua_setglobal(lstate, "vim"); { |