mirror of
https://github.com/neovim/neovim.git
synced 2025-02-25 18:55:25 -06:00
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>
This commit is contained in:
parent
8decc9f52d
commit
30fed27241
@ -55,7 +55,7 @@ static char locale_decimal_point = '.';
|
|||||||
* localconv() may not be thread safe (=>crash), and nl_langinfo() is
|
* localconv() may not be thread safe (=>crash), and nl_langinfo() is
|
||||||
* not supported on some platforms. Use sprintf() instead - if the
|
* not supported on some platforms. Use sprintf() instead - if the
|
||||||
* locale does change, at least Lua CJSON won't crash. */
|
* locale does change, at least Lua CJSON won't crash. */
|
||||||
static void fpconv_update_locale()
|
static void fpconv_update_locale(void)
|
||||||
{
|
{
|
||||||
char buf[8];
|
char buf[8];
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ static inline void fpconv_init()
|
|||||||
/* Do nothing - not required */
|
/* Do nothing - not required */
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
extern void fpconv_init();
|
extern void fpconv_init(void);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
extern int fpconv_g_fmt(char*, double, int);
|
extern int fpconv_g_fmt(char*, double, int);
|
||||||
|
@ -44,6 +44,9 @@
|
|||||||
#include <lua.h>
|
#include <lua.h>
|
||||||
#include <lauxlib.h>
|
#include <lauxlib.h>
|
||||||
|
|
||||||
|
#include "nvim/lua/executor.h"
|
||||||
|
|
||||||
|
#include "lua_cjson.h"
|
||||||
#include "strbuf.h"
|
#include "strbuf.h"
|
||||||
#include "fpconv.h"
|
#include "fpconv.h"
|
||||||
|
|
||||||
@ -79,7 +82,7 @@
|
|||||||
#define DEFAULT_DECODE_INVALID_NUMBERS 1
|
#define DEFAULT_DECODE_INVALID_NUMBERS 1
|
||||||
#define DEFAULT_ENCODE_KEEP_BUFFER 1
|
#define DEFAULT_ENCODE_KEEP_BUFFER 1
|
||||||
#define DEFAULT_ENCODE_NUMBER_PRECISION 14
|
#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_DECODE_ARRAY_WITH_ARRAY_MT 0
|
||||||
#define DEFAULT_ENCODE_ESCAPE_FORWARD_SLASH 1
|
#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 len;
|
||||||
int as_array = 0;
|
int as_array = 0;
|
||||||
|
int as_empty_dict = 0;
|
||||||
int has_metatable;
|
int has_metatable;
|
||||||
|
|
||||||
switch (lua_type(l, -1)) {
|
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);
|
has_metatable = lua_getmetatable(l, -1);
|
||||||
|
|
||||||
if (has_metatable) {
|
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_pushlightuserdata(l, json_lightudata_mask(&json_array));
|
||||||
lua_rawget(l, LUA_REGISTRYINDEX);
|
lua_rawget(l, LUA_REGISTRYINDEX);
|
||||||
as_array = lua_rawequal(l, -1, -2);
|
as_array = lua_rawequal(l, -1, -2);
|
||||||
lua_pop(l, 2);
|
}
|
||||||
|
lua_pop(l, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (as_array) {
|
if (as_array) {
|
||||||
@ -775,7 +786,7 @@ static void json_append_data(lua_State *l, json_config_t *cfg,
|
|||||||
} else {
|
} else {
|
||||||
len = lua_array_length(l, cfg, json);
|
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);
|
json_append_array(l, cfg, current_depth, json, len);
|
||||||
} else {
|
} else {
|
||||||
if (has_metatable) {
|
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);
|
strbuf_append_mem(json, "null", 4);
|
||||||
break;
|
break;
|
||||||
case LUA_TLIGHTUSERDATA:
|
case LUA_TLIGHTUSERDATA:
|
||||||
if (lua_touserdata(l, -1) == NULL) {
|
if (lua_touserdata(l, -1) == &json_array) {
|
||||||
strbuf_append_mem(json, "null", 4);
|
|
||||||
} else if (lua_touserdata(l, -1) == &json_array) {
|
|
||||||
json_append_array(l, cfg, current_depth, json, 0);
|
json_append_array(l, cfg, current_depth, json, 0);
|
||||||
}
|
}
|
||||||
break;
|
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:
|
default:
|
||||||
/* Remaining types (LUA_TFUNCTION, LUA_TUSERDATA, LUA_TTHREAD,
|
/* Remaining types (LUA_TFUNCTION, LUA_TUSERDATA, LUA_TTHREAD,
|
||||||
* and LUA_TLIGHTUSERDATA) cannot be serialised */
|
* 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 */
|
/* Handle empty objects */
|
||||||
if (token.type == T_OBJ_END) {
|
if (token.type == T_OBJ_END) {
|
||||||
|
nlua_pushref(l, nlua_empty_dict_ref); \
|
||||||
|
lua_setmetatable(l, -2); \
|
||||||
json_decode_ascend(json);
|
json_decode_ascend(json);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -1360,9 +1381,7 @@ static void json_process_value(lua_State *l, json_parse_t *json,
|
|||||||
json_parse_array_context(l, json);
|
json_parse_array_context(l, json);
|
||||||
break;;
|
break;;
|
||||||
case T_NULL:
|
case T_NULL:
|
||||||
/* In Lua, setting "t[k] = nil" will delete k from the table.
|
nlua_pushref(l, nlua_nil_ref);
|
||||||
* Hence a NULL pointer lightuserdata object is used instead */
|
|
||||||
lua_pushlightuserdata(l, NULL);
|
|
||||||
break;;
|
break;;
|
||||||
default:
|
default:
|
||||||
json_throw_parse_error(l, json, "value", token);
|
json_throw_parse_error(l, json, "value", token);
|
||||||
@ -1464,7 +1483,7 @@ static int json_protect_conversion(lua_State *l)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Return cjson module table */
|
/* Return cjson module table */
|
||||||
static int lua_cjson_new(lua_State *l)
|
int lua_cjson_new(lua_State *l)
|
||||||
{
|
{
|
||||||
luaL_Reg reg[] = {
|
luaL_Reg reg[] = {
|
||||||
{ "encode", json_encode },
|
{ "encode", json_encode },
|
||||||
@ -1517,7 +1536,7 @@ static int lua_cjson_new(lua_State *l)
|
|||||||
compat_luaL_setfuncs(l, reg, 1);
|
compat_luaL_setfuncs(l, reg, 1);
|
||||||
|
|
||||||
/* Set cjson.null */
|
/* Set cjson.null */
|
||||||
lua_pushlightuserdata(l, NULL);
|
nlua_pushref(l, nlua_nil_ref);
|
||||||
lua_setfield(l, -2, "null");
|
lua_setfield(l, -2, "null");
|
||||||
|
|
||||||
/* Set cjson.empty_array_mt */
|
/* Set cjson.empty_array_mt */
|
||||||
|
10
src/cjson/lua_cjson.h
Normal file
10
src/cjson/lua_cjson.h
Normal file
@ -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
|
@ -87,8 +87,8 @@ file(MAKE_DIRECTORY ${LINT_SUPPRESSES_ROOT}/src)
|
|||||||
|
|
||||||
file(GLOB NVIM_SOURCES *.c)
|
file(GLOB NVIM_SOURCES *.c)
|
||||||
file(GLOB NVIM_HEADERS *.h)
|
file(GLOB NVIM_HEADERS *.h)
|
||||||
file(GLOB EXTERNAL_SOURCES ../xdiff/*.c ../mpack/*.c)
|
file(GLOB EXTERNAL_SOURCES ../xdiff/*.c ../mpack/*.c ../cjson/*.c)
|
||||||
file(GLOB EXTERNAL_HEADERS ../xdiff/*.h ../mpack/*.h)
|
file(GLOB EXTERNAL_HEADERS ../xdiff/*.h ../mpack/*.h ../cjson/*.h)
|
||||||
|
|
||||||
foreach(subdir
|
foreach(subdir
|
||||||
os
|
os
|
||||||
@ -171,7 +171,7 @@ foreach(sfile ${CONV_SOURCES})
|
|||||||
message(FATAL_ERROR "${sfile} doesn't exist (it was added to CONV_SOURCES)")
|
message(FATAL_ERROR "${sfile} doesn't exist (it was added to CONV_SOURCES)")
|
||||||
endif()
|
endif()
|
||||||
endforeach()
|
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})
|
list(APPEND CONV_SOURCES ${EXTERNAL_SOURCES})
|
||||||
|
|
||||||
if(NOT MSVC)
|
if(NOT MSVC)
|
||||||
|
@ -40,6 +40,7 @@
|
|||||||
#include "nvim/undo.h"
|
#include "nvim/undo.h"
|
||||||
#include "nvim/version.h"
|
#include "nvim/version.h"
|
||||||
#include "nvim/vim.h"
|
#include "nvim/vim.h"
|
||||||
|
#include "cjson/lua_cjson.h"
|
||||||
|
|
||||||
static int in_fast_callback = 0;
|
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_pushcfunction(lstate, &nlua_xdl_diff);
|
||||||
lua_setfield(lstate, -2, "diff");
|
lua_setfield(lstate, -2, "diff");
|
||||||
|
|
||||||
|
lua_cjson_new(lstate);
|
||||||
|
lua_setfield(lstate, -2, "json");
|
||||||
|
|
||||||
lua_setglobal(lstate, "vim");
|
lua_setglobal(lstate, "vim");
|
||||||
|
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user