/*
* This module exports three classes, and each instance of those classes has its
* own private registry for temporary reference storage(keeping state between
* calls). A private registry makes managing memory much easier since all we
* have to do is call luaL_unref passing the registry reference when the
* instance is collected by the __gc metamethod.
*
* This private registry is manipulated with `lmpack_ref` / `lmpack_unref` /
* `lmpack_geti`, which are analogous to `luaL_ref` / `luaL_unref` /
* `lua_rawgeti` but operate on the private registry passed as argument.
*
* In order to simplify debug registry leaks during normal operation(with the
* leak_test.lua script), these `lmpack_*` registry functions will target the
* normal lua registry when MPACK_DEBUG_REGISTRY_LEAK is defined during
* compilation.
*/
#define LUA_LIB
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <lauxlib.h>
#include <lua.h>
#include <luaconf.h>
#include "nvim/macros_defs.h"
#include "lmpack.h"
#include "rpc.h"
#define UNPACKER_META_NAME "mpack.Unpacker"
#define UNPACK_FN_NAME "decode"
#define PACKER_META_NAME "mpack.Packer"
#define PACK_FN_NAME "encode"
#define SESSION_META_NAME "mpack.Session"
#define NIL_NAME "mpack.NIL"
#define EMPTY_DICT_NAME "mpack.empty_dict"
/*
* TODO(tarruda): When targeting lua 5.3 and being compiled with `long long`
* support(not -ansi), we should make use of lua 64 bit integers for
* representing msgpack integers, since `double` can't represent the full range.
*/
#ifndef luaL_reg
/* Taken from Lua5.1's lauxlib.h */
#define luaL_reg luaL_Reg
#endif
#if LUA_VERSION_NUM > 501
#ifndef luaL_register
#define luaL_register(L,n,f) luaL_setfuncs(L,f,0)
#endif
#endif
typedef struct {
lua_State *L;
mpack_parser_t *parser;
int reg, ext, unpacking, mtdict;
char *string_buffer;
} Unpacker;
typedef struct {
lua_State *L;
mpack_parser_t *parser;
int reg, ext, root, packing, mtdict;
int is_bin, is_bin_fn;
} Packer;
typedef struct {
lua_State *L;
int reg;
mpack_rpc_session_t *session;
struct {
int type;
mpack_rpc_message_t msg;
int method_or_error;
int args_or_result;
} unpacked;
int unpacker;
} Session;
static int lmpack_ref(lua_State *L, int reg)
{
#ifdef MPACK_DEBUG_REGISTRY_LEAK
return luaL_ref(L, LUA_REGISTRYINDEX);
#else
int rv;
lua_rawgeti(L, LUA_REGISTRYINDEX, reg);
lua_pushvalue(L, -2);
rv = luaL_ref(L, -2);
lua_pop(L, 2);
return rv;
#endif
}
static void lmpack_unref(lua_State *L, int reg, int ref)
{
#ifdef MPACK_DEBUG_REGISTRY_LEAK
luaL_unref(L, LUA_REGISTRYINDEX, ref);
#else
lua_rawgeti(L, LUA_REGISTRYINDEX, reg);
luaL_unref(L, -1, ref);
lua_pop(L, 1);
#endif
}
static void lmpack_geti(lua_State *L, int reg, int ref)
{
#ifdef MPACK_DEBUG_REGISTRY_LEAK
lua_rawgeti(L, LUA_REGISTRYINDEX, ref);
#else
lua_rawgeti(L, LUA_REGISTRYINDEX, reg);
lua_rawgeti(L, -1, ref);
lua_replace(L, -2);
#endif
}
/* make a shallow copy of the table on stack and remove it after the copy is
* done */
static void lmpack_shallow_copy(lua_State *L)
{
lua_newtable(L);
lua_pushnil(L);
while (lua_next(L, -3)) {
lua_pushvalue(L, -2);
lua_insert(L, -2);
lua_settable(L, -4);
}
lua_remove(L, -2);
}
static mpack_parser_t *lmpack_grow_parser(mpack_parser_t *parser)
{
mpack_parser_t *old = parser;
mpack_uint32_t new_capacity = old->capacity * 2;
parser = malloc(MPACK_PARSER_STRUCT_SIZE(new_capacity));
if (!parser) goto end;
mpack_parser_init(parser, new_capacity);
mpack_parser_copy(parser, old);
free(old);
end:
return parser;
}
static mpack_rpc_session_t *lmpack_grow_session(mpack_rpc_session_t *session)
{
mpack_rpc_session_t *old = session;
mpack_uint32_t new_capacity = old->capacity * 2;
session = malloc(MPACK_RPC_SESSION_STRUCT_SIZE(new_capacity));
if (!session) goto end;
mpack_rpc_session_init(session, new_capacity);
mpack_rpc_session_copy(session, old);
free(old);
end:
return session;
}
static Unpacker *lmpack_check_unpacker(lua_State *L, int index)
{
return luaL_checkudata(L, index, UNPACKER_META_NAME);
}
static Packer *lmpack_check_packer(lua_State *L, int index)
{
return luaL_checkudata(L, index, PACKER_META_NAME);
}
static Session *lmpack_check_session(lua_State *L, int index)
{
return luaL_checkudata(L, index, SESSION_META_NAME);
}
static int lmpack_isnil(lua_State *L, int index)
{
int rv;
if (!lua_isuserdata(L, index)) return 0;
lua_getfield(L, LUA_REGISTRYINDEX, NIL_NAME);
rv = lua_rawequal(L, -1, -2);
lua_pop(L, 1);
return rv;
}
static int lmpack_isunpacker(lua_State *L, int index)
{
int rv;
if (!lua_isuserdata(L, index) || !lua_getmetatable(L, index)) return 0;
luaL_getmetatable(L, UNPACKER_META_NAME);
rv = lua_rawequal(L, -1, -2);
lua_pop(L, 2);
return rv;
}
static void lmpack_pushnil(lua_State *L)
{
lua_getfield(L, LUA_REGISTRYINDEX, NIL_NAME);
}
/* adapted from
* https://github.com/antirez/lua-cmsgpack/blob/master/lua_cmsgpack.c */
static mpack_uint32_t lmpack_objlen(lua_State *L, int *is_array)
{
size_t len, max;
int isarr;
lua_Number n;
#ifndef NDEBUG
|