From 0f05f6980d58b3f6e611a311576eab1be7e81980 Mon Sep 17 00:00:00 2001
From: Roman Tsisyk <roman@tsisyk.com>
Date: Mon, 14 Sep 2015 13:44:29 +0300
Subject: [PATCH] Fix #1007: restore Lua/C version of box.tuple.new()

Partially reverts squashed 316d4e3a6ba4ef7d566b42b592efb00594828cac
---
 src/box/lua/tuple.cc  | 40 ++++++++++++++++++++++++++++++++++++++++
 src/box/lua/tuple.lua | 33 ++-------------------------------
 test/big/lua.result   |  2 +-
 test/box/tuple.result | 20 ++++++++++----------
 4 files changed, 53 insertions(+), 42 deletions(-)

diff --git a/src/box/lua/tuple.cc b/src/box/lua/tuple.cc
index f67e23d59b..00d8856bc9 100644
--- a/src/box/lua/tuple.cc
+++ b/src/box/lua/tuple.cc
@@ -90,6 +90,39 @@ lua_istuple(struct lua_State *L, int narg)
 	return t;
 }
 
+static int
+lbox_tuple_new(lua_State *L)
+{
+	int argc = lua_gettop(L);
+	if (unlikely(argc < 1)) {
+		lua_newtable(L); /* create an empty tuple */
+		++argc;
+	}
+
+	struct region *gc = &fiber()->gc;
+	RegionGuard guard(gc);
+	struct mpstream stream;
+	mpstream_init(&stream, gc, region_reserve_cb, region_alloc_cb);
+
+	if (argc == 1 && (lua_istable(L, 1) || lua_istuple(L, 1))) {
+		/* New format: box.tuple.new({1, 2, 3}) */
+		luamp_encode_tuple(L, luaL_msgpack_default, &stream, 1);
+	} else {
+		/* Backward-compatible format: box.tuple.new(1, 2, 3). */
+		luamp_encode_array(luaL_msgpack_default, &stream, argc);
+		for (int k = 1; k <= argc; ++k) {
+			luamp_encode(L, luaL_msgpack_default, &stream, k);
+		}
+	}
+	mpstream_flush(&stream);
+
+	size_t tuple_len = region_used(gc) - guard.used;
+	const char *data = (char *) region_join(gc, tuple_len);
+	struct tuple *tuple = tuple_new(tuple_format_ber, data, data + tuple_len);
+	lbox_pushtuple(L, tuple);
+	return 1;
+}
+
 static int
 lbox_tuple_gc(struct lua_State *L)
 {
@@ -326,6 +359,11 @@ static const struct luaL_reg lbox_tuple_meta[] = {
 	{NULL, NULL}
 };
 
+static const struct luaL_reg lbox_tuplelib[] = {
+	{"new", lbox_tuple_new},
+	{NULL, NULL}
+};
+
 static const struct luaL_reg lbox_tuple_iterator_meta[] = {
 	{NULL, NULL}
 };
@@ -342,6 +380,8 @@ box_lua_tuple_init(struct lua_State *L)
 	lua_setglobal(L, "cfuncs");
 	luaL_register_type(L, tuple_iteratorlib_name,
 			   lbox_tuple_iterator_meta);
+	luaL_register_module(L, tuplelib_name, lbox_tuplelib);
+	lua_pop(L, 1);
 
 	luamp_set_encode_extension(luamp_encode_extension_box);
 
diff --git a/src/box/lua/tuple.lua b/src/box/lua/tuple.lua
index a394abad97..f7dba0df87 100644
--- a/src/box/lua/tuple.lua
+++ b/src/box/lua/tuple.lua
@@ -85,32 +85,6 @@ local tuple_bless = function(tuple)
     return ffi.gc(ffi.cast(const_tuple_ref_t, tuple), tuple_gc)
 end
 
-local format_lua = nil -- cached box_tuple_format
-local function tuple_new(...)
-    if format_lua == nil then
-        format_lua = builtin.box_tuple_format_default()
-    end
-
-    local obj = ...
-    if select('#', ...) > 1 or type(obj) ~= 'table' then
-        -- Backward-compatible format: box.tuple.new(1, 2, 3).
-        obj = {}
-        for i=1,select('#', ...) do
-            local val = select(i, ...)
-            if val == nil then
-                val = msgpackffi.NULL
-            end
-            obj[i] = val
-        end
-    end
-    local data, data_end = msgpackffi.encode_tuple(obj)
-    local tuple = builtin.box_tuple_new(format_lua, data, data_end)
-    if tuple == nil then
-        return box.error()
-    end
-    return tuple_bless(tuple)
-end
-
 local tuple_iterator_t = ffi.typeof('box_tuple_iterator_t')
 local tuple_iterator_ref_t = ffi.typeof('box_tuple_iterator_t &')
 
@@ -322,8 +296,5 @@ ffi.metatype(tuple_iterator_t, {
 -- Remove the global variable
 cfuncs = nil
 
-box.tuple = {
-    new = tuple_new;
-    -- internal api for box.select and iterators
-    bless = tuple_bless;
-}
+-- internal api for box.select and iterators
+box.tuple.bless = tuple_bless
diff --git a/test/big/lua.result b/test/big/lua.result
index 4274109f5f..bc373b70c2 100644
--- a/test/big/lua.result
+++ b/test/big/lua.result
@@ -734,7 +734,7 @@ t:find(2, '2')
 ...
 t:find(89, '2')
 ---
-- error: '[string "-- tuple.lua (internal file)..."]:151: error: invalid key to ''next'''
+- error: '[string "-- tuple.lua (internal file)..."]:125: error: invalid key to ''next'''
 ...
 t:findall(4, '3')
 ---
diff --git a/test/box/tuple.result b/test/box/tuple.result
index b7eb8ad455..15975979bd 100644
--- a/test/box/tuple.result
+++ b/test/box/tuple.result
@@ -305,12 +305,12 @@ t:unpack(2, 1)
 ...
 t:totable(0)
 ---
-- error: '[string "-- tuple.lua (internal file)..."]:182: tuple.totable: invalid second
+- error: '[string "-- tuple.lua (internal file)..."]:156: tuple.totable: invalid second
     argument'
 ...
 t:totable(1, 0)
 ---
-- error: '[string "-- tuple.lua (internal file)..."]:191: tuple.totable: invalid third
+- error: '[string "-- tuple.lua (internal file)..."]:165: tuple.totable: invalid third
     argument'
 ...
 --
@@ -443,7 +443,7 @@ t:next(-1)
 ...
 t:next("fdsaf")
 ---
-- error: '[string "-- tuple.lua (internal file)..."]:163: bad argument #2 to ''box_tuple_field''
+- error: '[string "-- tuple.lua (internal file)..."]:137: bad argument #2 to ''box_tuple_field''
     (cannot convert ''string'' to ''unsigned int'')'
 ...
 box.tuple.new({'x', 'y', 'z'}):next()
@@ -612,7 +612,7 @@ r = {}
 ...
 for _state, val in t:pairs(10) do table.insert(r, val) end
 ---
-- error: '[string "-- tuple.lua (internal file)..."]:151: error: invalid key to ''next'''
+- error: '[string "-- tuple.lua (internal file)..."]:125: error: invalid key to ''next'''
 ...
 r
 ---
@@ -698,19 +698,19 @@ t:findall(1, 'xxxxx')
 ...
 t:find(100, 'a')
 ---
-- error: '[string "-- tuple.lua (internal file)..."]:151: error: invalid key to ''next'''
+- error: '[string "-- tuple.lua (internal file)..."]:125: error: invalid key to ''next'''
 ...
 t:findall(100, 'a')
 ---
-- error: '[string "-- tuple.lua (internal file)..."]:151: error: invalid key to ''next'''
+- error: '[string "-- tuple.lua (internal file)..."]:125: error: invalid key to ''next'''
 ...
 t:find(100, 'xxxxx')
 ---
-- error: '[string "-- tuple.lua (internal file)..."]:151: error: invalid key to ''next'''
+- error: '[string "-- tuple.lua (internal file)..."]:125: error: invalid key to ''next'''
 ...
 t:findall(100, 'xxxxx')
 ---
-- error: '[string "-- tuple.lua (internal file)..."]:151: error: invalid key to ''next'''
+- error: '[string "-- tuple.lua (internal file)..."]:125: error: invalid key to ''next'''
 ...
 ---
 -- Lua type coercion
@@ -804,12 +804,12 @@ t = box.tuple.new({'a', 'b', 'c', 'd', 'e'})
 ...
 t:update()
 ---
-- error: '[string "-- tuple.lua (internal file)..."]:231: Usage: tuple:update({ {
+- error: '[string "-- tuple.lua (internal file)..."]:205: Usage: tuple:update({ {
     op, field, arg}+ })'
 ...
 t:update(10)
 ---
-- error: '[string "-- tuple.lua (internal file)..."]:231: Usage: tuple:update({ {
+- error: '[string "-- tuple.lua (internal file)..."]:205: Usage: tuple:update({ {
     op, field, arg}+ })'
 ...
 t:update({})
-- 
GitLab