[Tarantool-patches] [PATCH 1/4] lua: move serializer helpers into its own file
Alexander Turenko
alexander.turenko at tarantool.org
Wed Jun 23 22:12:39 MSK 2021
It is easier to glance on tightly coupled structures and functions, when
they're not mixed with others.
Just move without actual changes.
Part of #3228
---
src/CMakeLists.txt | 1 +
src/box/lua/call.c | 1 +
src/box/lua/console.c | 1 +
src/box/lua/execute.c | 1 +
src/box/lua/info.c | 1 +
src/box/lua/init.c | 2 +
src/box/lua/serialize_lua.c | 1 +
src/box/lua/slab.c | 1 +
src/box/lua/tuple.c | 1 +
src/box/sql/mem.c | 1 +
src/lua/decimal.c | 2 +
src/lua/fiber.c | 1 +
src/lua/init.c | 5 +
src/lua/msgpack.c | 1 +
src/lua/msgpack.h | 1 +
src/lua/pickle.c | 1 +
src/lua/serializer.c | 651 ++++++++++++++++++++++++++++++
src/lua/serializer.h | 344 ++++++++++++++++
src/lua/utils.c | 601 +--------------------------
src/lua/utils.h | 292 +-------------
third_party/lua-cjson/lua_cjson.c | 1 +
third_party/lua-yaml/lyaml.cc | 1 +
22 files changed, 1023 insertions(+), 889 deletions(-)
create mode 100644 src/lua/serializer.c
create mode 100644 src/lua/serializer.h
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 324574fec..c0e272bd9 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -122,6 +122,7 @@ set (server_sources
lua/trigger.c
lua/msgpack.c
lua/utils.c
+ lua/serializer.c
lua/errno.c
lua/tnt_iconv.c
lua/error.c
diff --git a/src/box/lua/call.c b/src/box/lua/call.c
index 0315e720c..d42b54d42 100644
--- a/src/box/lua/call.c
+++ b/src/box/lua/call.c
@@ -38,6 +38,7 @@
#include "tt_static.h"
#include "lua/utils.h"
+#include "lua/serializer.h"
#include "lua/msgpack.h"
#include "lua/trigger.h"
diff --git a/src/box/lua/console.c b/src/box/lua/console.c
index 2e8204db6..d97c3ff30 100644
--- a/src/box/lua/console.c
+++ b/src/box/lua/console.c
@@ -34,6 +34,7 @@
#include "box/port.h"
#include "box/error.h"
#include "lua/utils.h"
+#include "lua/serializer.h"
#include "lua/fiber.h"
#include "fiber.h"
#include "coio.h"
diff --git a/src/box/lua/execute.c b/src/box/lua/execute.c
index 926a0a61c..1b59b2e4a 100644
--- a/src/box/lua/execute.c
+++ b/src/box/lua/execute.c
@@ -1,5 +1,6 @@
#include "execute.h"
#include "lua/utils.h"
+#include "lua/serializer.h"
#include "lua/msgpack.h"
#include "box/sql/sqlInt.h"
#include "box/port.h"
diff --git a/src/box/lua/info.c b/src/box/lua/info.c
index 0eb48b823..f2bc42eee 100644
--- a/src/box/lua/info.c
+++ b/src/box/lua/info.c
@@ -52,6 +52,7 @@
#include "box/raft.h"
#include "box/txn_limbo.h"
#include "lua/utils.h"
+#include "lua/serializer.h" /* luaL_setmaphint */
#include "fiber.h"
#include "sio.h"
diff --git a/src/box/lua/init.c b/src/box/lua/init.c
index 3a6d60864..6eb2ab651 100644
--- a/src/box/lua/init.c
+++ b/src/box/lua/init.c
@@ -34,6 +34,8 @@
#include <lauxlib.h>
#include <lualib.h>
+#include "lib/core/mp_extension_types.h"
+
#include "lua/utils.h" /* luaT_error() */
#include "lua/trigger.h"
#include "lua/msgpack.h"
diff --git a/src/box/lua/serialize_lua.c b/src/box/lua/serialize_lua.c
index caa08a60f..7144305cf 100644
--- a/src/box/lua/serialize_lua.c
+++ b/src/box/lua/serialize_lua.c
@@ -34,6 +34,7 @@
#include "trivia/util.h"
#include "lua/utils.h"
+#include "lua/serializer.h"
#include "say.h"
#include "lib/core/decimal.h"
diff --git a/src/box/lua/slab.c b/src/box/lua/slab.c
index 9f5e7e95c..dd89a980f 100644
--- a/src/box/lua/slab.c
+++ b/src/box/lua/slab.c
@@ -32,6 +32,7 @@
#include "box/lua/slab.h"
#include "lua/utils.h"
+#include "lua/serializer.h" /* luaL_setmaphint */
#include <lua.h>
#include <lauxlib.h>
diff --git a/src/box/lua/tuple.c b/src/box/lua/tuple.c
index f7198a025..609f2eda0 100644
--- a/src/box/lua/tuple.c
+++ b/src/box/lua/tuple.c
@@ -32,6 +32,7 @@
#include "box/xrow_update.h"
#include "lua/utils.h" /* luaT_error() */
+#include "lua/serializer.h"
#include "lua/msgpack.h" /* luamp_encode_XXX() */
#include "diag.h" /* diag_set() */
#include <small/ibuf.h>
diff --git a/src/box/sql/mem.c b/src/box/sql/mem.c
index 6f3bf52e5..2595e2fd4 100644
--- a/src/box/sql/mem.c
+++ b/src/box/sql/mem.c
@@ -38,6 +38,7 @@
#include "mpstream/mpstream.h"
#include "box/port.h"
#include "lua/utils.h"
+#include "lua/serializer.h"
#include "lua/msgpack.h"
#include "uuid/mp_uuid.h"
#include "mp_decimal.h"
diff --git a/src/lua/decimal.c b/src/lua/decimal.c
index d3400521d..003680a48 100644
--- a/src/lua/decimal.c
+++ b/src/lua/decimal.c
@@ -79,6 +79,8 @@ ldecimal_##name(struct lua_State *L) { \
return 1; \
}
+uint32_t CTID_DECIMAL;
+
/** Push a new decimal on the stack and return a pointer to it. */
decimal_t *
lua_pushdecimal(struct lua_State *L)
diff --git a/src/lua/fiber.c b/src/lua/fiber.c
index 91898c283..d236e50c4 100644
--- a/src/lua/fiber.c
+++ b/src/lua/fiber.c
@@ -32,6 +32,7 @@
#include <fiber.h>
#include "lua/utils.h"
+#include "lua/serializer.h"
#include "backtrace.h"
#include "tt_static.h"
diff --git a/src/lua/init.c b/src/lua/init.c
index 93e93a103..ff11d202b 100644
--- a/src/lua/init.c
+++ b/src/lua/init.c
@@ -69,6 +69,10 @@
#include <readline/readline.h>
#include <readline/history.h>
+/* Don't include the entire header only for *_init(). */
+int
+tarantool_lua_serializer_init(struct lua_State *L);
+
/**
* The single Lua state of the transaction processor (tx) thread.
*/
@@ -475,6 +479,7 @@ tarantool_lua_init(const char *tarantool_bin, int argc, char **argv)
tarantool_lua_socket_init(L);
tarantool_lua_pickle_init(L);
tarantool_lua_digest_init(L);
+ tarantool_lua_serializer_init(L);
tarantool_lua_swim_init(L);
tarantool_lua_decimal_init(L);
luaopen_http_client_driver(L);
diff --git a/src/lua/msgpack.c b/src/lua/msgpack.c
index 1e74a6a3c..b6ecf2b1e 100644
--- a/src/lua/msgpack.c
+++ b/src/lua/msgpack.c
@@ -31,6 +31,7 @@
#include "lua/msgpack.h"
#include "mpstream/mpstream.h"
#include "lua/utils.h"
+#include "lua/serializer.h"
#if defined(LUAJIT)
#include <lj_ctype.h>
diff --git a/src/lua/msgpack.h b/src/lua/msgpack.h
index 5a91e2812..cf85654a5 100644
--- a/src/lua/msgpack.h
+++ b/src/lua/msgpack.h
@@ -41,6 +41,7 @@ extern "C" {
#include <lua.h>
+struct luaL_field;
struct luaL_serializer;
struct mpstream;
struct serializer_opts;
diff --git a/src/lua/pickle.c b/src/lua/pickle.c
index 65208b5b3..b88f5ac75 100644
--- a/src/lua/pickle.c
+++ b/src/lua/pickle.c
@@ -38,6 +38,7 @@
#include <lualib.h>
#include "lua/utils.h"
+#include "lua/serializer.h"
#include "lua/msgpack.h" /* luaL_msgpack_default */
#include <fiber.h>
#include "bit/bit.h"
diff --git a/src/lua/serializer.c b/src/lua/serializer.c
new file mode 100644
index 000000000..6c3dd73af
--- /dev/null
+++ b/src/lua/serializer.c
@@ -0,0 +1,651 @@
+/*
+ * Copyright 2010-2021, Tarantool AUTHORS, please see AUTHORS file.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include <assert.h>
+#include <stdbool.h>
+#include <math.h> /* modf, isfinite */
+#include <lua.h>
+#include <lauxlib.h>
+
+#include "lua/serializer.h"
+
+#include "trigger.h"
+#include "lib/core/decimal.h" /* decimal_t */
+#include "lib/core/mp_extension_types.h"
+#include "lua/error.h"
+
+#include "trivia/util.h"
+#include "diag.h"
+#include "serializer_opts.h"
+#include "lua/utils.h"
+
+int luaL_map_metatable_ref = LUA_REFNIL;
+int luaL_array_metatable_ref = LUA_REFNIL;
+extern uint32_t CTID_UUID;
+extern uint32_t CTID_DECIMAL;
+
+#define OPTION(type, name, defvalue) { #name, \
+ offsetof(struct luaL_serializer, name), type, defvalue}
+/**
+ * Configuration options for serializers
+ * @sa struct luaL_serializer
+ */
+static struct {
+ const char *name;
+ size_t offset; /* offset in structure */
+ int type;
+ int defvalue;
+} OPTIONS[] = {
+ OPTION(LUA_TBOOLEAN, encode_sparse_convert, 1),
+ OPTION(LUA_TNUMBER, encode_sparse_ratio, 2),
+ OPTION(LUA_TNUMBER, encode_sparse_safe, 10),
+ OPTION(LUA_TNUMBER, encode_max_depth, 128),
+ OPTION(LUA_TBOOLEAN, encode_deep_as_nil, 0),
+ OPTION(LUA_TBOOLEAN, encode_invalid_numbers, 1),
+ OPTION(LUA_TNUMBER, encode_number_precision, 14),
+ OPTION(LUA_TBOOLEAN, encode_load_metatables, 1),
+ OPTION(LUA_TBOOLEAN, encode_use_tostring, 0),
+ OPTION(LUA_TBOOLEAN, encode_invalid_as_nil, 0),
+ OPTION(LUA_TBOOLEAN, decode_invalid_numbers, 1),
+ OPTION(LUA_TBOOLEAN, decode_save_metatables, 1),
+ OPTION(LUA_TNUMBER, decode_max_depth, 128),
+ { NULL, 0, 0, 0},
+};
+
+void
+luaL_serializer_create(struct luaL_serializer *cfg)
+{
+ rlist_create(&cfg->on_update);
+ for (int i = 0; OPTIONS[i].name != NULL; i++) {
+ int *pval = (int *) ((char *) cfg + OPTIONS[i].offset);
+ *pval = OPTIONS[i].defvalue;
+ }
+}
+
+void
+luaL_serializer_copy_options(struct luaL_serializer *dst,
+ const struct luaL_serializer *src)
+{
+ memcpy(dst, src, offsetof(struct luaL_serializer, end_of_options));
+}
+
+/**
+ * Configure one field in @a cfg. Value of the field is kept on
+ * Lua stack after this function, and should be popped manually.
+ * @param L Lua stack.
+ * @param i Index of option in OPTIONS[].
+ * @param cfg Serializer to inherit configuration.
+ * @retval Pointer to the value of option.
+ * @retval NULL if option is not in the table.
+ */
+static int *
+luaL_serializer_parse_option(struct lua_State *L, int i,
+ struct luaL_serializer *cfg)
+{
+ lua_getfield(L, 2, OPTIONS[i].name);
+ if (lua_isnil(L, -1))
+ return NULL;
+ /*
+ * Update struct luaL_serializer using pointer to a
+ * configuration value (all values must be `int` for that).
+ */
+ int *pval = (int *) ((char *) cfg + OPTIONS[i].offset);
+ switch (OPTIONS[i].type) {
+ case LUA_TBOOLEAN:
+ *pval = lua_toboolean(L, -1);
+ break;
+ case LUA_TNUMBER:
+ *pval = lua_tointeger(L, -1);
+ break;
+ default:
+ unreachable();
+ }
+ return pval;
+}
+
+void
+luaL_serializer_parse_options(struct lua_State *L,
+ struct luaL_serializer *cfg)
+{
+ for (int i = 0; OPTIONS[i].name != NULL; ++i) {
+ luaL_serializer_parse_option(L, i, cfg);
+ lua_pop(L, 1);
+ }
+}
+
+/**
+ * @brief serializer.cfg{} Lua binding for serializers.
+ * serializer.cfg is a table that contains current configuration values from
+ * luaL_serializer structure. serializer.cfg has overriden __call() method
+ * to change configuration keys in internal userdata (like box.cfg{}).
+ * Please note that direct change in serializer.cfg.key will not affect
+ * internal state of userdata. Changes via cfg() are reflected in
+ * both Lua cfg table, and C serializer structure.
+ * @param L lua stack
+ * @return 0
+ */
+static int
+luaL_serializer_cfg(struct lua_State *L)
+{
+ /* Serializer.cfg */
+ luaL_checktype(L, 1, LUA_TTABLE);
+ /* Updated parameters. */
+ luaL_checktype(L, 2, LUA_TTABLE);
+ struct luaL_serializer *cfg = luaL_checkserializer(L);
+ for (int i = 0; OPTIONS[i].name != NULL; ++i) {
+ if (luaL_serializer_parse_option(L, i, cfg) == NULL)
+ lua_pop(L, 1);
+ else
+ lua_setfield(L, 1, OPTIONS[i].name);
+ }
+ trigger_run(&cfg->on_update, cfg);
+ return 0;
+}
+
+/**
+ * @brief serializer.new() Lua binding.
+ * @param L stack
+ * @param reg methods to register
+ * @param parent parent serializer to inherit configuration
+ * @return new serializer
+ */
+struct luaL_serializer *
+luaL_newserializer(struct lua_State *L, const char *modname, const luaL_Reg *reg)
+{
+ luaL_checkstack(L, 1, "too many upvalues");
+
+ /* Create new module */
+ lua_newtable(L);
+
+ /* Create new configuration */
+ struct luaL_serializer *serializer = (struct luaL_serializer *)
+ lua_newuserdata(L, sizeof(*serializer));
+ luaL_getmetatable(L, LUAL_SERIALIZER);
+ lua_setmetatable(L, -2);
+ luaL_serializer_create(serializer);
+
+ for (; reg->name != NULL; reg++) {
+ /* push luaL_serializer as upvalue */
+ lua_pushvalue(L, -1);
+ /* register method */
+ lua_pushcclosure(L, reg->func, 1);
+ lua_setfield(L, -3, reg->name);
+ }
+
+ /* Add cfg{} */
+ lua_newtable(L); /* cfg */
+ lua_newtable(L); /* metatable */
+ lua_pushvalue(L, -3); /* luaL_serializer */
+ lua_pushcclosure(L, luaL_serializer_cfg, 1);
+ lua_setfield(L, -2, "__call");
+ lua_setmetatable(L, -2);
+ /* Save configuration values to serializer.cfg */
+ for (int i = 0; OPTIONS[i].name != NULL; i++) {
+ int *pval = (int *) ((char *) serializer + OPTIONS[i].offset);
+ switch (OPTIONS[i].type) {
+ case LUA_TBOOLEAN:
+ lua_pushboolean(L, *pval);
+ break;
+ case LUA_TNUMBER:
+ lua_pushinteger(L, *pval);
+ break;
+ default:
+ unreachable();
+ }
+ lua_setfield(L, -2, OPTIONS[i].name);
+ }
+ lua_setfield(L, -3, "cfg");
+
+ lua_pop(L, 1); /* remove upvalues */
+
+ luaL_pushnull(L);
+ lua_setfield(L, -2, "NULL");
+ lua_rawgeti(L, LUA_REGISTRYINDEX, luaL_array_metatable_ref);
+ lua_setfield(L, -2, "array_mt");
+ lua_rawgeti(L, LUA_REGISTRYINDEX, luaL_map_metatable_ref);
+ lua_setfield(L, -2, "map_mt");
+
+ if (modname != NULL) {
+ /* Register module */
+ lua_getfield(L, LUA_REGISTRYINDEX, "_LOADED");
+ lua_pushstring(L, modname); /* add alias */
+ lua_pushvalue(L, -3);
+ lua_settable(L, -3);
+ lua_pop(L, 1); /* _LOADED */
+ }
+
+ return serializer;
+}
+
+static int
+lua_gettable_wrapper(lua_State *L)
+{
+ lua_gettable(L, -2);
+ return 1;
+}
+
+static void
+lua_field_inspect_ucdata(struct lua_State *L, struct luaL_serializer *cfg,
+ int idx, struct luaL_field *field)
+{
+ if (!cfg->encode_load_metatables)
+ return;
+
+ /*
+ * Try to call LUAL_SERIALIZE method on udata/cdata
+ * LuaJIT specific: lua_getfield/lua_gettable raises exception on
+ * cdata if field doesn't exist.
+ */
+ int top = lua_gettop(L);
+ lua_pushcfunction(L, lua_gettable_wrapper);
+ lua_pushvalue(L, idx);
+ lua_pushliteral(L, LUAL_SERIALIZE);
+ if (lua_pcall(L, 2, 1, 0) == 0 && !lua_isnil(L, -1)) {
+ if (!lua_isfunction(L, -1))
+ luaL_error(L, "invalid " LUAL_SERIALIZE " value");
+ /* copy object itself */
+ lua_pushvalue(L, idx);
+ lua_pcall(L, 1, 1, 0);
+ /* replace obj with the unpacked value */
+ lua_replace(L, idx);
+ if (luaL_tofield(L, cfg, NULL, idx, field) < 0)
+ luaT_error(L);
+ } /* else ignore lua_gettable exceptions */
+ lua_settop(L, top); /* remove temporary objects */
+}
+
+/**
+ * Call __serialize method of a table object by index
+ * if the former exists.
+ *
+ * If __serialize does not exist then function does nothing
+ * and the function returns 1;
+ *
+ * If __serialize exists, is a function (which doesn't
+ * raise any error) then a result of serialization
+ * replaces old value by the index and the function returns 0;
+ *
+ * If the serialization is a hint string (like 'array' or 'map'),
+ * then field->type, field->size and field->compact
+ * are set if necessary and the function returns 0;
+ *
+ * Otherwise it is an error, set diag and the funciton returns -1;
+ *
+ * Return values:
+ * -1 - error occurs, diag is set, the top of guest stack
+ * is undefined.
+ * 0 - __serialize field is available in the metatable,
+ * the result value is put in the origin slot,
+ * encoding is finished.
+ * 1 - __serialize field is not available in the metatable,
+ * proceed with default table encoding.
+ */
+static int
+lua_field_try_serialize(struct lua_State *L, struct luaL_serializer *cfg,
+ int idx, struct luaL_field *field)
+{
+ if (luaL_getmetafield(L, idx, LUAL_SERIALIZE) == 0)
+ return 1;
+ if (lua_isfunction(L, -1)) {
+ /* copy object itself */
+ lua_pushvalue(L, idx);
+ if (lua_pcall(L, 1, 1, 0) != 0) {
+ diag_set(LuajitError, lua_tostring(L, -1));
+ return -1;
+ }
+ if (luaL_tofield(L, cfg, NULL, -1, field) != 0)
+ return -1;
+ lua_replace(L, idx);
+ return 0;
+ }
+ if (!lua_isstring(L, -1)) {
+ diag_set(LuajitError, "invalid " LUAL_SERIALIZE " value");
+ return -1;
+ }
+ const char *type = lua_tostring(L, -1);
+ if (strcmp(type, "array") == 0 || strcmp(type, "seq") == 0 ||
+ strcmp(type, "sequence") == 0) {
+ field->type = MP_ARRAY; /* Override type */
+ field->size = luaL_arrlen(L, idx);
+ /* YAML: use flow mode if __serialize == 'seq' */
+ if (cfg->has_compact && type[3] == '\0')
+ field->compact = true;
+ } else if (strcmp(type, "map") == 0 || strcmp(type, "mapping") == 0) {
+ field->type = MP_MAP; /* Override type */
+ field->size = luaL_maplen(L, idx);
+ /* YAML: use flow mode if __serialize == 'map' */
+ if (cfg->has_compact && type[3] == '\0')
+ field->compact = true;
+ } else {
+ diag_set(LuajitError, "invalid " LUAL_SERIALIZE " value");
+ return -1;
+ }
+ /* Remove value set by luaL_getmetafield. */
+ lua_pop(L, 1);
+ return 0;
+}
+
+static int
+lua_field_inspect_table(struct lua_State *L, struct luaL_serializer *cfg,
+ int idx, struct luaL_field *field)
+{
+ assert(lua_type(L, idx) == LUA_TTABLE);
+ uint32_t size = 0;
+ uint32_t max = 0;
+
+ if (cfg->encode_load_metatables) {
+ int top = lua_gettop(L);
+ int res = lua_field_try_serialize(L, cfg, idx, field);
+ if (res == -1)
+ return -1;
+ assert(lua_gettop(L) == top);
+ (void)top;
+ if (res == 0)
+ return 0;
+ /* Fallthrough with res == 1 */
+ }
+
+ field->type = MP_ARRAY;
+
+ /* Calculate size and check that table can represent an array */
+ lua_pushnil(L);
+ while (lua_next(L, idx)) {
+ size++;
+ lua_pop(L, 1); /* pop the value */
+ lua_Number k;
+ if (lua_type(L, -1) != LUA_TNUMBER ||
+ ((k = lua_tonumber(L, -1)) != size &&
+ (k < 1 || floor(k) != k))) {
+ /* Finish size calculation */
+ while (lua_next(L, idx)) {
+ size++;
+ lua_pop(L, 1); /* pop the value */
+ }
+ field->type = MP_MAP;
+ field->size = size;
+ return 0;
+ }
+ if (k > max)
+ max = k;
+ }
+
+ /* Encode excessively sparse arrays as objects (if enabled) */
+ if (cfg->encode_sparse_ratio > 0 &&
+ max > size * (uint32_t)cfg->encode_sparse_ratio &&
+ max > (uint32_t)cfg->encode_sparse_safe) {
+ if (!cfg->encode_sparse_convert) {
+ diag_set(LuajitError, "excessively sparse array");
+ return -1;
+ }
+ field->type = MP_MAP;
+ field->size = size;
+ return 0;
+ }
+
+ assert(field->type == MP_ARRAY);
+ field->size = max;
+ return 0;
+}
+
+static void
+lua_field_tostring(struct lua_State *L, struct luaL_serializer *cfg, int idx,
+ struct luaL_field *field)
+{
+ int top = lua_gettop(L);
+ lua_getglobal(L, "tostring");
+ lua_pushvalue(L, idx);
+ lua_call(L, 1, 1);
+ lua_replace(L, idx);
+ lua_settop(L, top);
+ if (luaL_tofield(L, cfg, NULL, idx, field) < 0)
+ luaT_error(L);
+}
+
+int
+luaL_tofield(struct lua_State *L, struct luaL_serializer *cfg,
+ const struct serializer_opts *opts, int index,
+ struct luaL_field *field)
+{
+ if (index < 0)
+ index = lua_gettop(L) + index + 1;
+
+ double num;
+ double intpart;
+ size_t size;
+
+#define CHECK_NUMBER(x) ({ \
+ if (!isfinite(x) && !cfg->encode_invalid_numbers) { \
+ if (!cfg->encode_invalid_as_nil) { \
+ diag_set(LuajitError, "number must not be NaN or Inf"); \
+ return -1; \
+ } \
+ field->type = MP_NIL; \
+ }})
+
+ switch (lua_type(L, index)) {
+ case LUA_TNUMBER:
+ num = lua_tonumber(L, index);
+ if (isfinite(num) && modf(num, &intpart) != 0.0) {
+ field->type = MP_DOUBLE;
+ field->dval = num;
+ } else if (num >= 0 && num < exp2(64)) {
+ field->type = MP_UINT;
+ field->ival = (uint64_t) num;
+ } else if (num >= -exp2(63) && num < exp2(63)) {
+ field->type = MP_INT;
+ field->ival = (int64_t) num;
+ } else {
+ field->type = MP_DOUBLE;
+ field->dval = num;
+ CHECK_NUMBER(num);
+ }
+ return 0;
+ case LUA_TCDATA:
+ {
+ GCcdata *cd = cdataV(L->base + index - 1);
+ void *cdata = (void *)cdataptr(cd);
+
+ int64_t ival;
+ switch (cd->ctypeid) {
+ case CTID_BOOL:
+ field->type = MP_BOOL;
+ field->bval = *(bool*) cdata;
+ return 0;
+ case CTID_CCHAR:
+ case CTID_INT8:
+ ival = *(int8_t *) cdata;
+ field->type = (ival >= 0) ? MP_UINT : MP_INT;
+ field->ival = ival;
+ return 0;
+ case CTID_INT16:
+ ival = *(int16_t *) cdata;
+ field->type = (ival >= 0) ? MP_UINT : MP_INT;
+ field->ival = ival;
+ return 0;
+ case CTID_INT32:
+ ival = *(int32_t *) cdata;
+ field->type = (ival >= 0) ? MP_UINT : MP_INT;
+ field->ival = ival;
+ return 0;
+ case CTID_INT64:
+ ival = *(int64_t *) cdata;
+ field->type = (ival >= 0) ? MP_UINT : MP_INT;
+ field->ival = ival;
+ return 0;
+ case CTID_UINT8:
+ field->type = MP_UINT;
+ field->ival = *(uint8_t *) cdata;
+ return 0;
+ case CTID_UINT16:
+ field->type = MP_UINT;
+ field->ival = *(uint16_t *) cdata;
+ return 0;
+ case CTID_UINT32:
+ field->type = MP_UINT;
+ field->ival = *(uint32_t *) cdata;
+ return 0;
+ case CTID_UINT64:
+ field->type = MP_UINT;
+ field->ival = *(uint64_t *) cdata;
+ return 0;
+ case CTID_FLOAT:
+ field->type = MP_FLOAT;
+ field->fval = *(float *) cdata;
+ CHECK_NUMBER(field->fval);
+ return 0;
+ case CTID_DOUBLE:
+ field->type = MP_DOUBLE;
+ field->dval = *(double *) cdata;
+ CHECK_NUMBER(field->dval);
+ return 0;
+ case CTID_P_CVOID:
+ case CTID_P_VOID:
+ if (*(void **) cdata == NULL) {
+ field->type = MP_NIL;
+ return 0;
+ }
+ /* Fall through */
+ default:
+ field->type = MP_EXT;
+ if (cd->ctypeid == CTID_DECIMAL) {
+ field->ext_type = MP_DECIMAL;
+ field->decval = (decimal_t *) cdata;
+ } else if (cd->ctypeid == CTID_UUID) {
+ field->ext_type = MP_UUID;
+ field->uuidval = (struct tt_uuid *) cdata;
+ } else if (cd->ctypeid == CTID_CONST_STRUCT_ERROR_REF &&
+ opts != NULL &&
+ opts->error_marshaling_enabled) {
+ field->ext_type = MP_ERROR;
+ } else {
+ field->ext_type = MP_UNKNOWN_EXTENSION;
+ }
+ }
+ return 0;
+ }
+ case LUA_TBOOLEAN:
+ field->type = MP_BOOL;
+ field->bval = lua_toboolean(L, index);
+ return 0;
+ case LUA_TNIL:
+ field->type = MP_NIL;
+ return 0;
+ case LUA_TSTRING:
+ field->sval.data = lua_tolstring(L, index, &size);
+ field->sval.len = (uint32_t) size;
+ field->type = MP_STR;
+ return 0;
+ case LUA_TTABLE:
+ {
+ field->compact = false;
+ return lua_field_inspect_table(L, cfg, index, field);
+ }
+ case LUA_TLIGHTUSERDATA:
+ case LUA_TUSERDATA:
+ field->sval.data = NULL;
+ field->sval.len = 0;
+ if (lua_touserdata(L, index) == NULL) {
+ field->type = MP_NIL;
+ return 0;
+ }
+ /* Fall through */
+ default:
+ field->type = MP_EXT;
+ field->ext_type = MP_UNKNOWN_EXTENSION;
+ }
+#undef CHECK_NUMBER
+ return 0;
+}
+
+void
+luaL_convertfield(struct lua_State *L, struct luaL_serializer *cfg, int idx,
+ struct luaL_field *field)
+{
+ if (idx < 0)
+ idx = lua_gettop(L) + idx + 1;
+ assert(field->type == MP_EXT && field->ext_type == MP_UNKNOWN_EXTENSION); /* must be called after tofield() */
+
+ if (cfg->encode_load_metatables) {
+ int type = lua_type(L, idx);
+ if (type == LUA_TCDATA) {
+ /*
+ * Don't call __serialize on primitive types
+ * https://github.com/tarantool/tarantool/issues/1226
+ */
+ GCcdata *cd = cdataV(L->base + idx - 1);
+ if (cd->ctypeid > CTID_CTYPEID)
+ lua_field_inspect_ucdata(L, cfg, idx, field);
+ } else if (type == LUA_TUSERDATA) {
+ lua_field_inspect_ucdata(L, cfg, idx, field);
+ }
+ }
+
+ if (field->type == MP_EXT && field->ext_type == MP_UNKNOWN_EXTENSION &&
+ cfg->encode_use_tostring)
+ lua_field_tostring(L, cfg, idx, field);
+
+ if (field->type != MP_EXT || field->ext_type != MP_UNKNOWN_EXTENSION)
+ return;
+
+ if (cfg->encode_invalid_as_nil) {
+ field->type = MP_NIL;
+ return;
+ }
+
+ luaL_error(L, "unsupported Lua type '%s'",
+ lua_typename(L, lua_type(L, idx)));
+}
+
+int
+tarantool_lua_serializer_init(struct lua_State *L)
+{
+ static const struct luaL_Reg serializermeta[] = {
+ {NULL, NULL},
+ };
+ luaL_register_type(L, LUAL_SERIALIZER, serializermeta);
+
+ lua_createtable(L, 0, 1);
+ lua_pushliteral(L, "map"); /* YAML will use flow mode */
+ lua_setfield(L, -2, LUAL_SERIALIZE);
+ /* automatically reset hints on table change */
+ luaL_loadstring(L, "setmetatable((...), nil); return rawset(...)");
+ lua_setfield(L, -2, "__newindex");
+ luaL_map_metatable_ref = luaL_ref(L, LUA_REGISTRYINDEX);
+
+ lua_createtable(L, 0, 1);
+ lua_pushliteral(L, "seq"); /* YAML will use flow mode */
+ lua_setfield(L, -2, LUAL_SERIALIZE);
+ /* automatically reset hints on table change */
+ luaL_loadstring(L, "setmetatable((...), nil); return rawset(...)");
+ lua_setfield(L, -2, "__newindex");
+ luaL_array_metatable_ref = luaL_ref(L, LUA_REGISTRYINDEX);
+
+ return 0;
+}
diff --git a/src/lua/serializer.h b/src/lua/serializer.h
new file mode 100644
index 000000000..54b0bc11a
--- /dev/null
+++ b/src/lua/serializer.h
@@ -0,0 +1,344 @@
+#ifndef TARANTOOL_LUA_SERIALIZER_H_INCLUDED
+#define TARANTOOL_LUA_SERIALIZER_H_INCLUDED
+/*
+ * Copyright 2010-2021, Tarantool AUTHORS, please see AUTHORS file.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(__cplusplus)
+extern "C" {
+#endif /* defined(__cplusplus) */
+
+#include <assert.h>
+#include <stdbool.h>
+#include <math.h> /* isfinite */
+#include <lua.h>
+#include <lauxlib.h>
+
+#include "trigger.h"
+#include "lib/core/decimal.h" /* decimal_t */
+#include "lib/core/mp_extension_types.h"
+#include "lua/error.h"
+
+struct serializer_opts;
+struct lua_State;
+struct tt_uuid;
+
+#define LUAL_SERIALIZER "serializer"
+#define LUAL_SERIALIZE "__serialize"
+
+extern int luaL_map_metatable_ref;
+extern int luaL_array_metatable_ref;
+
+/**
+ * Common configuration options for Lua serializers (MsgPack, YAML, JSON)
+ */
+struct luaL_serializer {
+ /**
+ * luaL_tofield tries to classify table into one of four kinds
+ * during encoding:
+ *
+ * + map - at least one table index is not unsigned integer.
+ * + regular array - all array indexes are available.
+ * + sparse array - at least one array index is missing.
+ * + excessively sparse arrat - the number of values missing
+ * exceeds the configured ratio.
+ *
+ * An array is excessively sparse when **all** the following
+ * conditions are met:
+ *
+ * + encode_sparse_ratio > 0.
+ * + max(table) > encode_sparse_safe.
+ * + max(table) > count(table) * encode_sparse_ratio.
+ *
+ * luaL_tofield will never consider an array to be excessively sparse
+ * when encode_sparse_ratio = 0. The encode_sparse_safe limit ensures
+ * that small Lua arrays are always encoded as sparse arrays.
+ * By default, attempting to encode an excessively sparse array will
+ * generate an error. If encode_sparse_convert is set to true,
+ * excessively sparse arrays will be handled as maps.
+ *
+ * This conversion logic is modeled after Mark Pulford's CJSON module.
+ * @sa http://www.kyne.com.au/~mark/software/lua-cjson-manual.html
+ */
+ int encode_sparse_convert;
+ /** @see encode_sparse_convert */
+ int encode_sparse_ratio;
+ /** @see encode_sparse_convert */
+ int encode_sparse_safe;
+ /** Max recursion depth for encoding (MsgPack, CJSON only) */
+ int encode_max_depth;
+ /**
+ * A flag whether a table with too high nest level should
+ * be cropped. The not-encoded fields are replaced with
+ * one null. If not set, too high nesting is considered an
+ * error.
+ */
+ int encode_deep_as_nil;
+ /** Enables encoding of NaN and Inf numbers */
+ int encode_invalid_numbers;
+ /** Floating point numbers precision (YAML, CJSON only) */
+ int encode_number_precision;
+
+ /**
+ * Enables __serialize meta-value checking:
+ *
+ * + 'seq', 'sequence', 'array' - table encoded as an array
+ * + 'map', 'mappping' - table encoded as a map.
+ * 'seq' or 'map' also enable flow (compact) mode for YAML serializer
+ * (flow="[1,2,3]" vs block=" - 1\n - 2\n - 3\n").
+ * + function - the meta-method is called to unpack serializable
+ * representation of table, cdata or userdata objects.
+ */
+ int encode_load_metatables;
+ /** Enables tostring() usage for unknown types */
+ int encode_use_tostring;
+ /** Use NULL for all unrecognizable types */
+ int encode_invalid_as_nil;
+
+ /** Enables decoding NaN and Inf numbers */
+ int decode_invalid_numbers;
+ /** Save __serialize meta-value for decoded arrays and maps */
+ int decode_save_metatables;
+ /** Max recursion depts for decoding (CJSON only) */
+ int decode_max_depth;
+
+ /** Enable support for compact represenation (internal, YAML-only). */
+ int has_compact;
+ /**
+ * Border where copyable fields end. Is used to copy
+ * serializer options into an existing serializer without
+ * erasure of its non-option fields like triggers.
+ */
+ char end_of_options[0];
+ /**
+ * Trigger object to subscribe on updates of a more
+ * general serializer. For example, tuple serializer
+ * subscribes on msgpack.
+ */
+ struct trigger update_trigger;
+ /**
+ * List of triggers on update of this serializer. To push
+ * updates down to dependent serializers.
+ */
+ struct rlist on_update;
+};
+
+struct luaL_serializer *
+luaL_newserializer(struct lua_State *L, const char *modname, const luaL_Reg *reg);
+
+/**
+ * Copy all option fields of @a src into @a dst. Other fields,
+ * such as triggers, are not touched.
+ */
+void
+luaL_serializer_copy_options(struct luaL_serializer *dst,
+ const struct luaL_serializer *src);
+
+static inline struct luaL_serializer *
+luaL_checkserializer(struct lua_State *L) {
+ return (struct luaL_serializer *)
+ luaL_checkudata(L, lua_upvalueindex(1), LUAL_SERIALIZER);
+}
+
+/**
+ * Initialize serializer with default parameters.
+ * @param cfg Serializer to inherit configuration.
+ */
+void
+luaL_serializer_create(struct luaL_serializer *cfg);
+
+/**
+ * Parse configuration table into @a cfg.
+ * @param L Lua stack.
+ * @param cfg Serializer to inherit configuration.
+ */
+void
+luaL_serializer_parse_options(struct lua_State *l,
+ struct luaL_serializer *cfg);
+
+/** A single value on the Lua stack. */
+struct luaL_field {
+ union {
+ struct {
+ const char *data;
+ uint32_t len;
+ } sval;
+ int64_t ival;
+ double dval;
+ float fval;
+ bool bval;
+ /* Array or map. */
+ uint32_t size;
+ decimal_t *decval;
+ struct tt_uuid *uuidval;
+ };
+ enum mp_type type;
+ /* subtypes of MP_EXT */
+ enum mp_extension_type ext_type;
+ bool compact; /* a flag used by YAML serializer */
+};
+
+/**
+ * @brief Convert a value from the Lua stack to a lua_field structure.
+ * This function is designed for use with Lua bindings and data
+ * serialization functions (YAML, MsgPack, JSON, etc.).
+ *
+ * Conversion rules:
+ * - LUA_TNUMBER when is integer and >= 0 -> UINT
+ * - LUA_TNUMBER when is integer and < 0 -> INT
+ * - LUA_TNUMBER when is not integer -> DOUBLE
+ * - LUA_TBOOLEAN -> BOOL
+ * - LUA_TSTRING -> STRING
+ * - LUA_TNIL -> NIL
+ * - LUA_TTABLE when is array -> ARRAY
+ * - LUA_TTABLE when is not array -> MAP
+ * - LUA_TUSERDATA, LUA_TLIGHTUSERDATA, CTID_P_VOID when == NULL -> NIL
+ * - CTID_INT*, CTID_CCHAR when >= 0 -> UINT
+ * - CTID_INT*, CTID_CCHAR when < 0 -> INT
+ * - CTID_FLOAT -> FLOAT
+ * - CTID_DOUBLE -> DOUBLE
+ * - CTID_BOOL -> BOOL
+ * - otherwise -> EXT
+ *
+ * ARRAY vs MAP recognition works based on encode_sparse_convert,
+ * encode_sparse_ratio, encode_sparse_safe and encode_load_metatables config
+ * parameters (see above). Tables are not saved to lua_field structure and
+ * should be processed manually, according to returned type and size value.
+ *
+ * This function doesn't try to unpack unknown types and simple returns MP_EXT.
+ * The caller can use luaL_tofield() for basic conversion, then invoke internal
+ * hooks(if available) and then call luaL_checkfield(), which will try to
+ * unpack cdata/userdata objects or raise and error.
+ *
+ * @param L stack
+ * @param cfg configuration
+ * @param opts the Lua serializer additional options.
+ * @param index stack index
+ * @param field conversion result
+ *
+ * @retval 0 Success.
+ * @retval -1 Error.
+ */
+int
+luaL_tofield(struct lua_State *L, struct luaL_serializer *cfg,
+ const struct serializer_opts *opts, int index,
+ struct luaL_field *field);
+
+/**
+ * @brief Try to convert userdata/cdata values using defined conversion logic.
+ * Must be used only after lua_tofield().
+ *
+ * @param L stack
+ * @param cfg configuration
+ * @param idx stack index
+ * @param field conversion result
+ */
+void
+luaL_convertfield(struct lua_State *L, struct luaL_serializer *cfg, int idx,
+ struct luaL_field *field);
+
+/**
+ * @brief A wrapper for luaL_tofield() and luaL_convertfield() that
+ * tries to convert value or raise an error.
+ * @param L stack
+ * @param cfg configuration
+ * @param idx stack index
+ * @param field conversion result
+ * @sa lua_tofield()
+ * @sa luaL_convertfield()
+ *
+ * Common conversion order for tables:
+ * size/count detection -> (sparse array checking) -> (__serialize)
+ *
+ * Common conversion order for userdata/cdata objects:
+ * (internal trigger) -> (__serialize) -> (tostring) -> (nil) -> exception
+ *
+ * Common conversion order for other types:
+ * (tostring) -> (nil) -> exception
+ */
+static inline void
+luaL_checkfield(struct lua_State *L, struct luaL_serializer *cfg, int idx,
+ struct luaL_field *field)
+{
+ if (luaL_tofield(L, cfg, NULL, idx, field) < 0)
+ luaT_error(L);
+ if (field->type != MP_EXT || field->ext_type != MP_UNKNOWN_EXTENSION)
+ return;
+ luaL_convertfield(L, cfg, idx, field);
+}
+
+/**
+ * Push Lua Table with __serialize = 'map' hint onto the stack.
+ * Tables with __serialize hint are properly handled by all serializers.
+ * @param L stack
+ * @param idx index in the stack
+ */
+static inline void
+luaL_setmaphint(struct lua_State *L, int idx)
+{
+ if (idx < 0)
+ idx = lua_gettop(L) + idx + 1;
+ assert(lua_type(L, idx) == LUA_TTABLE);
+ lua_rawgeti(L, LUA_REGISTRYINDEX, luaL_map_metatable_ref);
+ lua_setmetatable(L, idx);
+}
+
+/**
+ * Push Lua Table with __serialize = 'seq' hint onto the stack.
+ * Tables with __serialize hint are properly handled by all serializers.
+ * @param L stack
+ * @param idx index in the stack
+ */
+static inline void
+luaL_setarrayhint(struct lua_State *L, int idx)
+{
+ if (idx < 0)
+ idx = lua_gettop(L) + idx + 1;
+ assert(lua_type(L, idx) == LUA_TTABLE);
+ lua_rawgeti(L, LUA_REGISTRYINDEX, luaL_array_metatable_ref);
+ lua_setmetatable(L, idx);
+}
+
+static inline void
+luaL_checkfinite(struct lua_State *L, struct luaL_serializer *cfg,
+ lua_Number number)
+{
+ if (!cfg->decode_invalid_numbers && !isfinite(number))
+ luaL_error(L, "number must not be NaN or Inf");
+}
+
+int
+tarantool_lua_serializer_init(struct lua_State *L);
+
+#if defined(__cplusplus)
+} /* extern "C" */
+#endif /* defined(__cplusplus) */
+
+#endif /* TARANTOOL_LUA_SERIALIZER_H_INCLUDED */
diff --git a/src/lua/utils.c b/src/lua/utils.c
index 3ce821374..34cec0eed 100644
--- a/src/lua/utils.c
+++ b/src/lua/utils.c
@@ -38,11 +38,7 @@
#include <diag.h>
#include <fiber.h>
-#include "serializer_opts.h"
-
int luaL_nil_ref = LUA_REFNIL;
-int luaL_map_metatable_ref = LUA_REFNIL;
-int luaL_array_metatable_ref = LUA_REFNIL;
static int luaT_newthread_ref = LUA_NOREF;
@@ -50,9 +46,7 @@ static uint32_t CTID_STRUCT_IBUF;
static uint32_t CTID_STRUCT_IBUF_PTR;
static uint32_t CTID_CHAR_PTR;
static uint32_t CTID_CONST_CHAR_PTR;
-static uint32_t CTID_UUID;
-uint32_t CTID_DECIMAL;
-
+uint32_t CTID_UUID;
void *
luaL_pushcdata(struct lua_State *L, uint32_t ctypeid)
@@ -239,578 +233,6 @@ luaL_setcdatagc(struct lua_State *L, int idx)
}
-#define OPTION(type, name, defvalue) { #name, \
- offsetof(struct luaL_serializer, name), type, defvalue}
-/**
- * Configuration options for serializers
- * @sa struct luaL_serializer
- */
-static struct {
- const char *name;
- size_t offset; /* offset in structure */
- int type;
- int defvalue;
-} OPTIONS[] = {
- OPTION(LUA_TBOOLEAN, encode_sparse_convert, 1),
- OPTION(LUA_TNUMBER, encode_sparse_ratio, 2),
- OPTION(LUA_TNUMBER, encode_sparse_safe, 10),
- OPTION(LUA_TNUMBER, encode_max_depth, 128),
- OPTION(LUA_TBOOLEAN, encode_deep_as_nil, 0),
- OPTION(LUA_TBOOLEAN, encode_invalid_numbers, 1),
- OPTION(LUA_TNUMBER, encode_number_precision, 14),
- OPTION(LUA_TBOOLEAN, encode_load_metatables, 1),
- OPTION(LUA_TBOOLEAN, encode_use_tostring, 0),
- OPTION(LUA_TBOOLEAN, encode_invalid_as_nil, 0),
- OPTION(LUA_TBOOLEAN, decode_invalid_numbers, 1),
- OPTION(LUA_TBOOLEAN, decode_save_metatables, 1),
- OPTION(LUA_TNUMBER, decode_max_depth, 128),
- { NULL, 0, 0, 0},
-};
-
-void
-luaL_serializer_create(struct luaL_serializer *cfg)
-{
- rlist_create(&cfg->on_update);
- for (int i = 0; OPTIONS[i].name != NULL; i++) {
- int *pval = (int *) ((char *) cfg + OPTIONS[i].offset);
- *pval = OPTIONS[i].defvalue;
- }
-}
-
-void
-luaL_serializer_copy_options(struct luaL_serializer *dst,
- const struct luaL_serializer *src)
-{
- memcpy(dst, src, offsetof(struct luaL_serializer, end_of_options));
-}
-
-/**
- * Configure one field in @a cfg. Value of the field is kept on
- * Lua stack after this function, and should be popped manually.
- * @param L Lua stack.
- * @param i Index of option in OPTIONS[].
- * @param cfg Serializer to inherit configuration.
- * @retval Pointer to the value of option.
- * @retval NULL if option is not in the table.
- */
-static int *
-luaL_serializer_parse_option(struct lua_State *L, int i,
- struct luaL_serializer *cfg)
-{
- lua_getfield(L, 2, OPTIONS[i].name);
- if (lua_isnil(L, -1))
- return NULL;
- /*
- * Update struct luaL_serializer using pointer to a
- * configuration value (all values must be `int` for that).
- */
- int *pval = (int *) ((char *) cfg + OPTIONS[i].offset);
- switch (OPTIONS[i].type) {
- case LUA_TBOOLEAN:
- *pval = lua_toboolean(L, -1);
- break;
- case LUA_TNUMBER:
- *pval = lua_tointeger(L, -1);
- break;
- default:
- unreachable();
- }
- return pval;
-}
-
-void
-luaL_serializer_parse_options(struct lua_State *L,
- struct luaL_serializer *cfg)
-{
- for (int i = 0; OPTIONS[i].name != NULL; ++i) {
- luaL_serializer_parse_option(L, i, cfg);
- lua_pop(L, 1);
- }
-}
-
-/**
- * @brief serializer.cfg{} Lua binding for serializers.
- * serializer.cfg is a table that contains current configuration values from
- * luaL_serializer structure. serializer.cfg has overriden __call() method
- * to change configuration keys in internal userdata (like box.cfg{}).
- * Please note that direct change in serializer.cfg.key will not affect
- * internal state of userdata. Changes via cfg() are reflected in
- * both Lua cfg table, and C serializer structure.
- * @param L lua stack
- * @return 0
- */
-static int
-luaL_serializer_cfg(struct lua_State *L)
-{
- /* Serializer.cfg */
- luaL_checktype(L, 1, LUA_TTABLE);
- /* Updated parameters. */
- luaL_checktype(L, 2, LUA_TTABLE);
- struct luaL_serializer *cfg = luaL_checkserializer(L);
- for (int i = 0; OPTIONS[i].name != NULL; ++i) {
- if (luaL_serializer_parse_option(L, i, cfg) == NULL)
- lua_pop(L, 1);
- else
- lua_setfield(L, 1, OPTIONS[i].name);
- }
- trigger_run(&cfg->on_update, cfg);
- return 0;
-}
-
-/**
- * @brief serializer.new() Lua binding.
- * @param L stack
- * @param reg methods to register
- * @param parent parent serializer to inherit configuration
- * @return new serializer
- */
-struct luaL_serializer *
-luaL_newserializer(struct lua_State *L, const char *modname, const luaL_Reg *reg)
-{
- luaL_checkstack(L, 1, "too many upvalues");
-
- /* Create new module */
- lua_newtable(L);
-
- /* Create new configuration */
- struct luaL_serializer *serializer = (struct luaL_serializer *)
- lua_newuserdata(L, sizeof(*serializer));
- luaL_getmetatable(L, LUAL_SERIALIZER);
- lua_setmetatable(L, -2);
- luaL_serializer_create(serializer);
-
- for (; reg->name != NULL; reg++) {
- /* push luaL_serializer as upvalue */
- lua_pushvalue(L, -1);
- /* register method */
- lua_pushcclosure(L, reg->func, 1);
- lua_setfield(L, -3, reg->name);
- }
-
- /* Add cfg{} */
- lua_newtable(L); /* cfg */
- lua_newtable(L); /* metatable */
- lua_pushvalue(L, -3); /* luaL_serializer */
- lua_pushcclosure(L, luaL_serializer_cfg, 1);
- lua_setfield(L, -2, "__call");
- lua_setmetatable(L, -2);
- /* Save configuration values to serializer.cfg */
- for (int i = 0; OPTIONS[i].name != NULL; i++) {
- int *pval = (int *) ((char *) serializer + OPTIONS[i].offset);
- switch (OPTIONS[i].type) {
- case LUA_TBOOLEAN:
- lua_pushboolean(L, *pval);
- break;
- case LUA_TNUMBER:
- lua_pushinteger(L, *pval);
- break;
- default:
- unreachable();
- }
- lua_setfield(L, -2, OPTIONS[i].name);
- }
- lua_setfield(L, -3, "cfg");
-
- lua_pop(L, 1); /* remove upvalues */
-
- luaL_pushnull(L);
- lua_setfield(L, -2, "NULL");
- lua_rawgeti(L, LUA_REGISTRYINDEX, luaL_array_metatable_ref);
- lua_setfield(L, -2, "array_mt");
- lua_rawgeti(L, LUA_REGISTRYINDEX, luaL_map_metatable_ref);
- lua_setfield(L, -2, "map_mt");
-
- if (modname != NULL) {
- /* Register module */
- lua_getfield(L, LUA_REGISTRYINDEX, "_LOADED");
- lua_pushstring(L, modname); /* add alias */
- lua_pushvalue(L, -3);
- lua_settable(L, -3);
- lua_pop(L, 1); /* _LOADED */
- }
-
- return serializer;
-}
-
-static int
-lua_gettable_wrapper(lua_State *L)
-{
- lua_gettable(L, -2);
- return 1;
-}
-
-static void
-lua_field_inspect_ucdata(struct lua_State *L, struct luaL_serializer *cfg,
- int idx, struct luaL_field *field)
-{
- if (!cfg->encode_load_metatables)
- return;
-
- /*
- * Try to call LUAL_SERIALIZE method on udata/cdata
- * LuaJIT specific: lua_getfield/lua_gettable raises exception on
- * cdata if field doesn't exist.
- */
- int top = lua_gettop(L);
- lua_pushcfunction(L, lua_gettable_wrapper);
- lua_pushvalue(L, idx);
- lua_pushliteral(L, LUAL_SERIALIZE);
- if (lua_pcall(L, 2, 1, 0) == 0 && !lua_isnil(L, -1)) {
- if (!lua_isfunction(L, -1))
- luaL_error(L, "invalid " LUAL_SERIALIZE " value");
- /* copy object itself */
- lua_pushvalue(L, idx);
- lua_pcall(L, 1, 1, 0);
- /* replace obj with the unpacked value */
- lua_replace(L, idx);
- if (luaL_tofield(L, cfg, NULL, idx, field) < 0)
- luaT_error(L);
- } /* else ignore lua_gettable exceptions */
- lua_settop(L, top); /* remove temporary objects */
-}
-
-/**
- * Call __serialize method of a table object by index
- * if the former exists.
- *
- * If __serialize does not exist then function does nothing
- * and the function returns 1;
- *
- * If __serialize exists, is a function (which doesn't
- * raise any error) then a result of serialization
- * replaces old value by the index and the function returns 0;
- *
- * If the serialization is a hint string (like 'array' or 'map'),
- * then field->type, field->size and field->compact
- * are set if necessary and the function returns 0;
- *
- * Otherwise it is an error, set diag and the funciton returns -1;
- *
- * Return values:
- * -1 - error occurs, diag is set, the top of guest stack
- * is undefined.
- * 0 - __serialize field is available in the metatable,
- * the result value is put in the origin slot,
- * encoding is finished.
- * 1 - __serialize field is not available in the metatable,
- * proceed with default table encoding.
- */
-static int
-lua_field_try_serialize(struct lua_State *L, struct luaL_serializer *cfg,
- int idx, struct luaL_field *field)
-{
- if (luaL_getmetafield(L, idx, LUAL_SERIALIZE) == 0)
- return 1;
- if (lua_isfunction(L, -1)) {
- /* copy object itself */
- lua_pushvalue(L, idx);
- if (lua_pcall(L, 1, 1, 0) != 0) {
- diag_set(LuajitError, lua_tostring(L, -1));
- return -1;
- }
- if (luaL_tofield(L, cfg, NULL, -1, field) != 0)
- return -1;
- lua_replace(L, idx);
- return 0;
- }
- if (!lua_isstring(L, -1)) {
- diag_set(LuajitError, "invalid " LUAL_SERIALIZE " value");
- return -1;
- }
- const char *type = lua_tostring(L, -1);
- if (strcmp(type, "array") == 0 || strcmp(type, "seq") == 0 ||
- strcmp(type, "sequence") == 0) {
- field->type = MP_ARRAY; /* Override type */
- field->size = luaL_arrlen(L, idx);
- /* YAML: use flow mode if __serialize == 'seq' */
- if (cfg->has_compact && type[3] == '\0')
- field->compact = true;
- } else if (strcmp(type, "map") == 0 || strcmp(type, "mapping") == 0) {
- field->type = MP_MAP; /* Override type */
- field->size = luaL_maplen(L, idx);
- /* YAML: use flow mode if __serialize == 'map' */
- if (cfg->has_compact && type[3] == '\0')
- field->compact = true;
- } else {
- diag_set(LuajitError, "invalid " LUAL_SERIALIZE " value");
- return -1;
- }
- /* Remove value set by luaL_getmetafield. */
- lua_pop(L, 1);
- return 0;
-}
-
-static int
-lua_field_inspect_table(struct lua_State *L, struct luaL_serializer *cfg,
- int idx, struct luaL_field *field)
-{
- assert(lua_type(L, idx) == LUA_TTABLE);
- uint32_t size = 0;
- uint32_t max = 0;
-
- if (cfg->encode_load_metatables) {
- int top = lua_gettop(L);
- int res = lua_field_try_serialize(L, cfg, idx, field);
- if (res == -1)
- return -1;
- assert(lua_gettop(L) == top);
- (void)top;
- if (res == 0)
- return 0;
- /* Fallthrough with res == 1 */
- }
-
- field->type = MP_ARRAY;
-
- /* Calculate size and check that table can represent an array */
- lua_pushnil(L);
- while (lua_next(L, idx)) {
- size++;
- lua_pop(L, 1); /* pop the value */
- lua_Number k;
- if (lua_type(L, -1) != LUA_TNUMBER ||
- ((k = lua_tonumber(L, -1)) != size &&
- (k < 1 || floor(k) != k))) {
- /* Finish size calculation */
- while (lua_next(L, idx)) {
- size++;
- lua_pop(L, 1); /* pop the value */
- }
- field->type = MP_MAP;
- field->size = size;
- return 0;
- }
- if (k > max)
- max = k;
- }
-
- /* Encode excessively sparse arrays as objects (if enabled) */
- if (cfg->encode_sparse_ratio > 0 &&
- max > size * (uint32_t)cfg->encode_sparse_ratio &&
- max > (uint32_t)cfg->encode_sparse_safe) {
- if (!cfg->encode_sparse_convert) {
- diag_set(LuajitError, "excessively sparse array");
- return -1;
- }
- field->type = MP_MAP;
- field->size = size;
- return 0;
- }
-
- assert(field->type == MP_ARRAY);
- field->size = max;
- return 0;
-}
-
-static void
-lua_field_tostring(struct lua_State *L, struct luaL_serializer *cfg, int idx,
- struct luaL_field *field)
-{
- int top = lua_gettop(L);
- lua_getglobal(L, "tostring");
- lua_pushvalue(L, idx);
- lua_call(L, 1, 1);
- lua_replace(L, idx);
- lua_settop(L, top);
- if (luaL_tofield(L, cfg, NULL, idx, field) < 0)
- luaT_error(L);
-}
-
-int
-luaL_tofield(struct lua_State *L, struct luaL_serializer *cfg,
- const struct serializer_opts *opts, int index,
- struct luaL_field *field)
-{
- if (index < 0)
- index = lua_gettop(L) + index + 1;
-
- double num;
- double intpart;
- size_t size;
-
-#define CHECK_NUMBER(x) ({ \
- if (!isfinite(x) && !cfg->encode_invalid_numbers) { \
- if (!cfg->encode_invalid_as_nil) { \
- diag_set(LuajitError, "number must not be NaN or Inf"); \
- return -1; \
- } \
- field->type = MP_NIL; \
- }})
-
- switch (lua_type(L, index)) {
- case LUA_TNUMBER:
- num = lua_tonumber(L, index);
- if (isfinite(num) && modf(num, &intpart) != 0.0) {
- field->type = MP_DOUBLE;
- field->dval = num;
- } else if (num >= 0 && num < exp2(64)) {
- field->type = MP_UINT;
- field->ival = (uint64_t) num;
- } else if (num >= -exp2(63) && num < exp2(63)) {
- field->type = MP_INT;
- field->ival = (int64_t) num;
- } else {
- field->type = MP_DOUBLE;
- field->dval = num;
- CHECK_NUMBER(num);
- }
- return 0;
- case LUA_TCDATA:
- {
- GCcdata *cd = cdataV(L->base + index - 1);
- void *cdata = (void *)cdataptr(cd);
-
- int64_t ival;
- switch (cd->ctypeid) {
- case CTID_BOOL:
- field->type = MP_BOOL;
- field->bval = *(bool*) cdata;
- return 0;
- case CTID_CCHAR:
- case CTID_INT8:
- ival = *(int8_t *) cdata;
- field->type = (ival >= 0) ? MP_UINT : MP_INT;
- field->ival = ival;
- return 0;
- case CTID_INT16:
- ival = *(int16_t *) cdata;
- field->type = (ival >= 0) ? MP_UINT : MP_INT;
- field->ival = ival;
- return 0;
- case CTID_INT32:
- ival = *(int32_t *) cdata;
- field->type = (ival >= 0) ? MP_UINT : MP_INT;
- field->ival = ival;
- return 0;
- case CTID_INT64:
- ival = *(int64_t *) cdata;
- field->type = (ival >= 0) ? MP_UINT : MP_INT;
- field->ival = ival;
- return 0;
- case CTID_UINT8:
- field->type = MP_UINT;
- field->ival = *(uint8_t *) cdata;
- return 0;
- case CTID_UINT16:
- field->type = MP_UINT;
- field->ival = *(uint16_t *) cdata;
- return 0;
- case CTID_UINT32:
- field->type = MP_UINT;
- field->ival = *(uint32_t *) cdata;
- return 0;
- case CTID_UINT64:
- field->type = MP_UINT;
- field->ival = *(uint64_t *) cdata;
- return 0;
- case CTID_FLOAT:
- field->type = MP_FLOAT;
- field->fval = *(float *) cdata;
- CHECK_NUMBER(field->fval);
- return 0;
- case CTID_DOUBLE:
- field->type = MP_DOUBLE;
- field->dval = *(double *) cdata;
- CHECK_NUMBER(field->dval);
- return 0;
- case CTID_P_CVOID:
- case CTID_P_VOID:
- if (*(void **) cdata == NULL) {
- field->type = MP_NIL;
- return 0;
- }
- /* Fall through */
- default:
- field->type = MP_EXT;
- if (cd->ctypeid == CTID_DECIMAL) {
- field->ext_type = MP_DECIMAL;
- field->decval = (decimal_t *) cdata;
- } else if (cd->ctypeid == CTID_UUID) {
- field->ext_type = MP_UUID;
- field->uuidval = (struct tt_uuid *) cdata;
- } else if (cd->ctypeid == CTID_CONST_STRUCT_ERROR_REF &&
- opts != NULL &&
- opts->error_marshaling_enabled) {
- field->ext_type = MP_ERROR;
- } else {
- field->ext_type = MP_UNKNOWN_EXTENSION;
- }
- }
- return 0;
- }
- case LUA_TBOOLEAN:
- field->type = MP_BOOL;
- field->bval = lua_toboolean(L, index);
- return 0;
- case LUA_TNIL:
- field->type = MP_NIL;
- return 0;
- case LUA_TSTRING:
- field->sval.data = lua_tolstring(L, index, &size);
- field->sval.len = (uint32_t) size;
- field->type = MP_STR;
- return 0;
- case LUA_TTABLE:
- {
- field->compact = false;
- return lua_field_inspect_table(L, cfg, index, field);
- }
- case LUA_TLIGHTUSERDATA:
- case LUA_TUSERDATA:
- field->sval.data = NULL;
- field->sval.len = 0;
- if (lua_touserdata(L, index) == NULL) {
- field->type = MP_NIL;
- return 0;
- }
- /* Fall through */
- default:
- field->type = MP_EXT;
- field->ext_type = MP_UNKNOWN_EXTENSION;
- }
-#undef CHECK_NUMBER
- return 0;
-}
-
-void
-luaL_convertfield(struct lua_State *L, struct luaL_serializer *cfg, int idx,
- struct luaL_field *field)
-{
- if (idx < 0)
- idx = lua_gettop(L) + idx + 1;
- assert(field->type == MP_EXT && field->ext_type == MP_UNKNOWN_EXTENSION); /* must be called after tofield() */
-
- if (cfg->encode_load_metatables) {
- int type = lua_type(L, idx);
- if (type == LUA_TCDATA) {
- /*
- * Don't call __serialize on primitive types
- * https://github.com/tarantool/tarantool/issues/1226
- */
- GCcdata *cd = cdataV(L->base + idx - 1);
- if (cd->ctypeid > CTID_CTYPEID)
- lua_field_inspect_ucdata(L, cfg, idx, field);
- } else if (type == LUA_TUSERDATA) {
- lua_field_inspect_ucdata(L, cfg, idx, field);
- }
- }
-
- if (field->type == MP_EXT && field->ext_type == MP_UNKNOWN_EXTENSION &&
- cfg->encode_use_tostring)
- lua_field_tostring(L, cfg, idx, field);
-
- if (field->type != MP_EXT || field->ext_type != MP_UNKNOWN_EXTENSION)
- return;
-
- if (cfg->encode_invalid_as_nil) {
- field->type = MP_NIL;
- return;
- }
-
- luaL_error(L, "unsupported Lua type '%s'",
- lua_typename(L, lua_type(L, idx)));
-}
-
/**
* A helper to register a single type metatable.
*/
@@ -1263,31 +685,10 @@ luaT_newthread(struct lua_State *L)
int
tarantool_lua_utils_init(struct lua_State *L)
{
- static const struct luaL_Reg serializermeta[] = {
- {NULL, NULL},
- };
-
- luaL_register_type(L, LUAL_SERIALIZER, serializermeta);
/* Create NULL constant */
*(void **) luaL_pushcdata(L, CTID_P_VOID) = NULL;
luaL_nil_ref = luaL_ref(L, LUA_REGISTRYINDEX);
- lua_createtable(L, 0, 1);
- lua_pushliteral(L, "map"); /* YAML will use flow mode */
- lua_setfield(L, -2, LUAL_SERIALIZE);
- /* automatically reset hints on table change */
- luaL_loadstring(L, "setmetatable((...), nil); return rawset(...)");
- lua_setfield(L, -2, "__newindex");
- luaL_map_metatable_ref = luaL_ref(L, LUA_REGISTRYINDEX);
-
- lua_createtable(L, 0, 1);
- lua_pushliteral(L, "seq"); /* YAML will use flow mode */
- lua_setfield(L, -2, LUAL_SERIALIZE);
- /* automatically reset hints on table change */
- luaL_loadstring(L, "setmetatable((...), nil); return rawset(...)");
- lua_setfield(L, -2, "__newindex");
- luaL_array_metatable_ref = luaL_ref(L, LUA_REGISTRYINDEX);
-
int rc = luaL_cdef(L, "struct ibuf;");
assert(rc == 0);
CTID_STRUCT_IBUF = luaL_ctypeid(L, "struct ibuf");
diff --git a/src/lua/utils.h b/src/lua/utils.h
index 4a164868b..947d9240b 100644
--- a/src/lua/utils.h
+++ b/src/lua/utils.h
@@ -33,10 +33,9 @@
#include <stdint.h>
#include <string.h>
-#include <math.h> /* modf, isfinite */
+#include <math.h> /* floor */
#include <msgpuck.h> /* enum mp_type */
-#include "trigger.h"
#if defined(__cplusplus)
extern "C" {
@@ -56,10 +55,6 @@ extern "C" {
#include "lua/error.h"
-#include "lib/core/mp_extension_types.h"
-#include "lib/core/decimal.h" /* decimal_t */
-
-struct serializer_opts;
struct lua_State;
struct ibuf;
typedef struct ibuf box_ibuf_t;
@@ -73,6 +68,8 @@ struct tt_uuid;
*/
extern struct lua_State *tarantool_L;
+extern uint32_t CTID_UUID;
+
struct tt_uuid *
luaL_pushuuid(struct lua_State *L);
@@ -197,250 +194,7 @@ luaL_maplen(struct lua_State *L, int idx)
return size;
}
-/**
- * Common configuration options for Lua serializers (MsgPack, YAML, JSON)
- */
-struct luaL_serializer {
- /**
- * luaL_tofield tries to classify table into one of four kinds
- * during encoding:
- *
- * + map - at least one table index is not unsigned integer.
- * + regular array - all array indexes are available.
- * + sparse array - at least one array index is missing.
- * + excessively sparse arrat - the number of values missing
- * exceeds the configured ratio.
- *
- * An array is excessively sparse when **all** the following
- * conditions are met:
- *
- * + encode_sparse_ratio > 0.
- * + max(table) > encode_sparse_safe.
- * + max(table) > count(table) * encode_sparse_ratio.
- *
- * luaL_tofield will never consider an array to be excessively sparse
- * when encode_sparse_ratio = 0. The encode_sparse_safe limit ensures
- * that small Lua arrays are always encoded as sparse arrays.
- * By default, attempting to encode an excessively sparse array will
- * generate an error. If encode_sparse_convert is set to true,
- * excessively sparse arrays will be handled as maps.
- *
- * This conversion logic is modeled after Mark Pulford's CJSON module.
- * @sa http://www.kyne.com.au/~mark/software/lua-cjson-manual.html
- */
- int encode_sparse_convert;
- /** @see encode_sparse_convert */
- int encode_sparse_ratio;
- /** @see encode_sparse_convert */
- int encode_sparse_safe;
- /** Max recursion depth for encoding (MsgPack, CJSON only) */
- int encode_max_depth;
- /**
- * A flag whether a table with too high nest level should
- * be cropped. The not-encoded fields are replaced with
- * one null. If not set, too high nesting is considered an
- * error.
- */
- int encode_deep_as_nil;
- /** Enables encoding of NaN and Inf numbers */
- int encode_invalid_numbers;
- /** Floating point numbers precision (YAML, CJSON only) */
- int encode_number_precision;
-
- /**
- * Enables __serialize meta-value checking:
- *
- * + 'seq', 'sequence', 'array' - table encoded as an array
- * + 'map', 'mappping' - table encoded as a map.
- * 'seq' or 'map' also enable flow (compact) mode for YAML serializer
- * (flow="[1,2,3]" vs block=" - 1\n - 2\n - 3\n").
- * + function - the meta-method is called to unpack serializable
- * representation of table, cdata or userdata objects.
- */
- int encode_load_metatables;
- /** Enables tostring() usage for unknown types */
- int encode_use_tostring;
- /** Use NULL for all unrecognizable types */
- int encode_invalid_as_nil;
-
- /** Enables decoding NaN and Inf numbers */
- int decode_invalid_numbers;
- /** Save __serialize meta-value for decoded arrays and maps */
- int decode_save_metatables;
- /** Max recursion depts for decoding (CJSON only) */
- int decode_max_depth;
-
- /** Enable support for compact represenation (internal, YAML-only). */
- int has_compact;
- /**
- * Border where copyable fields end. Is used to copy
- * serializer options into an existing serializer without
- * erasure of its non-option fields like triggers.
- */
- char end_of_options[0];
- /**
- * Trigger object to subscribe on updates of a more
- * general serializer. For example, tuple serializer
- * subscribes on msgpack.
- */
- struct trigger update_trigger;
- /**
- * List of triggers on update of this serializer. To push
- * updates down to dependent serializers.
- */
- struct rlist on_update;
-};
-
extern int luaL_nil_ref;
-extern int luaL_map_metatable_ref;
-extern int luaL_array_metatable_ref;
-
-#define LUAL_SERIALIZER "serializer"
-#define LUAL_SERIALIZE "__serialize"
-
-struct luaL_serializer *
-luaL_newserializer(struct lua_State *L, const char *modname, const luaL_Reg *reg);
-
-/**
- * Copy all option fields of @a src into @a dst. Other fields,
- * such as triggers, are not touched.
- */
-void
-luaL_serializer_copy_options(struct luaL_serializer *dst,
- const struct luaL_serializer *src);
-
-static inline struct luaL_serializer *
-luaL_checkserializer(struct lua_State *L) {
- return (struct luaL_serializer *)
- luaL_checkudata(L, lua_upvalueindex(1), LUAL_SERIALIZER);
-}
-
-/**
- * Initialize serializer with default parameters.
- * @param cfg Serializer to inherit configuration.
- */
-void
-luaL_serializer_create(struct luaL_serializer *cfg);
-
-/**
- * Parse configuration table into @a cfg.
- * @param L Lua stack.
- * @param cfg Serializer to inherit configuration.
- */
-void
-luaL_serializer_parse_options(struct lua_State *l,
- struct luaL_serializer *cfg);
-
-/** A single value on the Lua stack. */
-struct luaL_field {
- union {
- struct {
- const char *data;
- uint32_t len;
- } sval;
- int64_t ival;
- double dval;
- float fval;
- bool bval;
- /* Array or map. */
- uint32_t size;
- decimal_t *decval;
- struct tt_uuid *uuidval;
- };
- enum mp_type type;
- /* subtypes of MP_EXT */
- enum mp_extension_type ext_type;
- bool compact; /* a flag used by YAML serializer */
-};
-
-/**
- * @brief Convert a value from the Lua stack to a lua_field structure.
- * This function is designed for use with Lua bindings and data
- * serialization functions (YAML, MsgPack, JSON, etc.).
- *
- * Conversion rules:
- * - LUA_TNUMBER when is integer and >= 0 -> UINT
- * - LUA_TNUMBER when is integer and < 0 -> INT
- * - LUA_TNUMBER when is not integer -> DOUBLE
- * - LUA_TBOOLEAN -> BOOL
- * - LUA_TSTRING -> STRING
- * - LUA_TNIL -> NIL
- * - LUA_TTABLE when is array -> ARRAY
- * - LUA_TTABLE when is not array -> MAP
- * - LUA_TUSERDATA, LUA_TLIGHTUSERDATA, CTID_P_VOID when == NULL -> NIL
- * - CTID_INT*, CTID_CCHAR when >= 0 -> UINT
- * - CTID_INT*, CTID_CCHAR when < 0 -> INT
- * - CTID_FLOAT -> FLOAT
- * - CTID_DOUBLE -> DOUBLE
- * - CTID_BOOL -> BOOL
- * - otherwise -> EXT
- *
- * ARRAY vs MAP recognition works based on encode_sparse_convert,
- * encode_sparse_ratio, encode_sparse_safe and encode_load_metatables config
- * parameters (see above). Tables are not saved to lua_field structure and
- * should be processed manually, according to returned type and size value.
- *
- * This function doesn't try to unpack unknown types and simple returns MP_EXT.
- * The caller can use luaL_tofield() for basic conversion, then invoke internal
- * hooks(if available) and then call luaL_checkfield(), which will try to
- * unpack cdata/userdata objects or raise and error.
- *
- * @param L stack
- * @param cfg configuration
- * @param opts the Lua serializer additional options.
- * @param index stack index
- * @param field conversion result
- *
- * @retval 0 Success.
- * @retval -1 Error.
- */
-int
-luaL_tofield(struct lua_State *L, struct luaL_serializer *cfg,
- const struct serializer_opts *opts, int index,
- struct luaL_field *field);
-
-/**
- * @brief Try to convert userdata/cdata values using defined conversion logic.
- * Must be used only after lua_tofield().
- *
- * @param L stack
- * @param cfg configuration
- * @param idx stack index
- * @param field conversion result
- */
-void
-luaL_convertfield(struct lua_State *L, struct luaL_serializer *cfg, int idx,
- struct luaL_field *field);
-
-/**
- * @brief A wrapper for luaL_tofield() and luaL_convertfield() that
- * tries to convert value or raise an error.
- * @param L stack
- * @param cfg configuration
- * @param idx stack index
- * @param field conversion result
- * @sa lua_tofield()
- * @sa luaL_convertfield()
- *
- * Common conversion order for tables:
- * size/count detection -> (sparse array checking) -> (__serialize)
- *
- * Common conversion order for userdata/cdata objects:
- * (internal trigger) -> (__serialize) -> (tostring) -> (nil) -> exception
- *
- * Common conversion order for other types:
- * (tostring) -> (nil) -> exception
- */
-static inline void
-luaL_checkfield(struct lua_State *L, struct luaL_serializer *cfg, int idx,
- struct luaL_field *field)
-{
- if (luaL_tofield(L, cfg, NULL, idx, field) < 0)
- luaT_error(L);
- if (field->type != MP_EXT || field->ext_type != MP_UNKNOWN_EXTENSION)
- return;
- luaL_convertfield(L, cfg, idx, field);
-}
void
luaL_register_type(struct lua_State *L, const char *type_name,
@@ -556,38 +310,6 @@ luaT_toibuf(struct lua_State *L, int idx);
int
luaT_toerror(lua_State *L);
-/**
- * Push Lua Table with __serialize = 'map' hint onto the stack.
- * Tables with __serialize hint are properly handled by all serializers.
- * @param L stack
- * @param idx index in the stack
- */
-static inline void
-luaL_setmaphint(struct lua_State *L, int idx)
-{
- if (idx < 0)
- idx = lua_gettop(L) + idx + 1;
- assert(lua_type(L, idx) == LUA_TTABLE);
- lua_rawgeti(L, LUA_REGISTRYINDEX, luaL_map_metatable_ref);
- lua_setmetatable(L, idx);
-}
-
-/**
- * Push Lua Table with __serialize = 'seq' hint onto the stack.
- * Tables with __serialize hint are properly handled by all serializers.
- * @param L stack
- * @param idx index in the stack
- */
-static inline void
-luaL_setarrayhint(struct lua_State *L, int idx)
-{
- if (idx < 0)
- idx = lua_gettop(L) + idx + 1;
- assert(lua_type(L, idx) == LUA_TTABLE);
- lua_rawgeti(L, LUA_REGISTRYINDEX, luaL_array_metatable_ref);
- lua_setmetatable(L, idx);
-}
-
/**
* Push ffi's NULL (cdata<void *>: NULL) onto the stack.
* Can be used as replacement of nil in Lua tables.
@@ -616,14 +338,6 @@ luaL_isnull(struct lua_State *L, int idx)
return false;
}
-static inline void
-luaL_checkfinite(struct lua_State *L, struct luaL_serializer *cfg,
- lua_Number number)
-{
- if (!cfg->decode_invalid_numbers && !isfinite(number))
- luaL_error(L, "number must not be NaN or Inf");
-}
-
/**
* @brief Creates a new Lua coroutine in a protected frame. If
* <lua_newthread> call underneath succeeds, the created Lua state
diff --git a/third_party/lua-cjson/lua_cjson.c b/third_party/lua-cjson/lua_cjson.c
index dbd86cbab..5123b9a74 100644
--- a/third_party/lua-cjson/lua_cjson.c
+++ b/third_party/lua-cjson/lua_cjson.c
@@ -48,6 +48,7 @@
#include "strbuf.h"
#include "lua/utils.h"
+#include "lua/serializer.h"
#include "mp_extension_types.h" /* MP_DECIMAL, MP_UUID */
#include "tt_static.h"
#include "uuid/tt_uuid.h" /* tt_uuid_to_string(), UUID_STR_LEN */
diff --git a/third_party/lua-yaml/lyaml.cc b/third_party/lua-yaml/lyaml.cc
index 9c3a4a646..5469e9f4f 100644
--- a/third_party/lua-yaml/lyaml.cc
+++ b/third_party/lua-yaml/lyaml.cc
@@ -49,6 +49,7 @@ extern "C" {
#include "b64.h"
} /* extern "C" */
#include "lua/utils.h"
+#include "lua/serializer.h"
#include "lib/core/decimal.h"
#include "tt_static.h"
#include "mp_extension_types.h" /* MP_DECIMAL, MP_UUID */
--
2.31.1
More information about the Tarantool-patches
mailing list