From 69b8853872273f3a63f5872ce507322aacca946f Mon Sep 17 00:00:00 2001
From: Konstantin Osipov <kostja@tarantool.org>
Date: Tue, 17 Dec 2013 17:34:55 +0400
Subject: [PATCH] Move box_lua.cc and box_lua_space.cc to box/lua/ and split
 into modules.

---
 src/box/CMakeLists.txt                     |    6 +-
 src/box/box.cc                             |    2 +-
 src/box/box_lua.cc                         | 1755 --------------------
 src/box/lua/call.cc                        |  746 +++++++++
 src/box/{box_lua.h => lua/call.h}          |   24 +-
 src/box/{box_lua_space.cc => lua/space.cc} |   49 +-
 src/box/{box_lua_space.h => lua/space.h}   |    3 +
 src/box/request.cc                         |    4 +-
 src/box/schema.cc                          |    2 +-
 9 files changed, 805 insertions(+), 1786 deletions(-)
 delete mode 100644 src/box/box_lua.cc
 create mode 100644 src/box/lua/call.cc
 rename src/box/{box_lua.h => lua/call.h} (77%)
 rename src/box/{box_lua_space.cc => lua/space.cc} (84%)
 rename src/box/{box_lua_space.h => lua/space.h} (97%)

diff --git a/src/box/CMakeLists.txt b/src/box/CMakeLists.txt
index 8a5f305965..d27f8f3851 100644
--- a/src/box/CMakeLists.txt
+++ b/src/box/CMakeLists.txt
@@ -33,7 +33,9 @@ tarantool_module("box"
     txn.cc
     box.cc
     ${lua_sources}
-    box_lua.cc
-    box_lua_space.cc)
+    lua/call.cc
+    lua/tuple.cc
+    lua/index.cc
+    lua/space.cc)
 
 target_link_libraries(tarantool_box bitset msgpuck)
diff --git a/src/box/box.cc b/src/box/box.cc
index 71f05e5bb2..0d2fc8b25a 100644
--- a/src/box/box.cc
+++ b/src/box/box.cc
@@ -42,7 +42,7 @@ extern "C" {
 #include <stat.h>
 #include <tarantool.h>
 #include "tuple.h"
-#include "box_lua.h"
+#include "lua/call.h"
 #include "schema.h"
 #include "space.h"
 #include "port.h"
diff --git a/src/box/box_lua.cc b/src/box/box_lua.cc
deleted file mode 100644
index 1062c1ba1a..0000000000
--- a/src/box/box_lua.cc
+++ /dev/null
@@ -1,1755 +0,0 @@
-/*
- * 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 "box_lua.h"
-
-#include "lua/utils.h"
-#include "lua/msgpack.h"
-#include <fiber.h>
-#include "box/box.h"
-#include "request.h"
-#include "txn.h"
-#include "tuple_update.h"
-
-extern "C" {
-#include <lua.h>
-#include <lauxlib.h>
-#include <lualib.h>
-#include <lj_obj.h>
-#include <lj_ctype.h>
-#include <lj_cdata.h>
-#include <lj_cconv.h>
-#include <arpa/inet.h>
-#include "bit/bit.h"
-} /* extern "C" */
-
-#include "pickle.h"
-#include "tuple.h"
-#include "schema.h"
-#include "space.h"
-#include "port.h"
-#include "tbuf.h"
-#include "scoped_guard.h"
-
-#include "third_party/lua-yaml/lyaml.h"
-
-/* contents of box.lua, misc.lua, box.net.lua respectively */
-extern char schema_lua[], box_lua[], box_net_lua[], misc_lua[], sql_lua[];
-static const char *lua_sources[] = { schema_lua, box_lua, box_net_lua, misc_lua, sql_lua, NULL };
-
-/*
- * Functions, exported in box_lua.h should have prefix
- * "box_lua_"; functions, available in Lua "box"
- * should start with "lbox_".
- */
-
-/** {{{ box.tuple Lua library
- *
- * To avoid extra copying between Lua memory and garbage-collected
- * tuple memory, provide a Lua userdata object 'box.tuple'.  This
- * object refers to a tuple instance in the slab allocator, and
- * allows accessing it using Lua primitives (array subscription,
- * iteration, etc.). When Lua object is garbage-collected,
- * tuple reference counter in the slab allocator is decreased,
- * allowing the tuple to be eventually garbage collected in
- * the slab allocator.
- */
-
-static const char *tuplelib_name = "box.tuple";
-static const char *tuple_iteratorlib_name = "box.tuple.iterator";
-static int tuple_totable_mt_ref = 0; /* a precreated metable for totable() */
-
-
-static struct tuple *
-lua_totuple(struct lua_State *L, int first, int last);
-
-static inline struct tuple *
-lua_checktuple(struct lua_State *L, int narg)
-{
-	struct tuple *t = *(struct tuple **) luaL_checkudata(L, narg, tuplelib_name);
-	assert(t->refs);
-	return t;
-}
-
-struct tuple *
-lua_istuple(struct lua_State *L, int narg)
-{
-	if (lua_getmetatable(L, narg) == 0)
-		return NULL;
-	luaL_getmetatable(L, tuplelib_name);
-	struct tuple *tuple = 0;
-	if (lua_equal(L, -1, -2))
-		tuple = *(struct tuple **) lua_touserdata(L, narg);
-	lua_pop(L, 2);
-	return tuple;
-}
-
-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 tuple *tuple = lua_totuple(L, 1, argc);
-	lbox_pushtuple(L, tuple);
-	return 1;
-}
-
-static int
-lbox_tuple_gc(struct lua_State *L)
-{
-	struct tuple *tuple = lua_checktuple(L, 1);
-	tuple_ref(tuple, -1);
-	return 0;
-}
-
-static int
-lbox_tuple_len(struct lua_State *L)
-{
-	struct tuple *tuple = lua_checktuple(L, 1);
-	lua_pushnumber(L, tuple_arity(tuple));
-	return 1;
-}
-
-static int
-lbox_tuple_slice(struct lua_State *L)
-{
-	struct tuple *tuple = lua_checktuple(L, 1);
-	int argc = lua_gettop(L) - 1;
-	uint32_t start, end;
-	int offset;
-
-	/*
-	 * Prepare the range. The second argument is optional.
-	 * If the end is beyond tuple size, adjust it.
-	 * If no arguments, or start > end, return an error.
-	 */
-	if (argc == 0 || argc > 2)
-		luaL_error(L, "tuple.slice(): bad arguments");
-
-	uint32_t arity = tuple_arity(tuple);
-	offset = lua_tointeger(L, 2);
-	if (offset >= 0 && offset < arity) {
-		start = offset;
-	} else if (offset < 0 && -offset <= arity) {
-		start = offset + arity;
-	} else {
-		return luaL_error(L, "tuple.slice(): start >= field count");
-	}
-
-	if (argc == 2) {
-		offset = lua_tointeger(L, 3);
-		if (offset > 0 && offset <= arity) {
-			end = offset;
-		} else if (offset < 0 && -offset < arity) {
-			end = offset + arity;
-		} else {
-			return luaL_error(L, "tuple.slice(): end > field count");
-		}
-	} else {
-		end = arity;
-	}
-	if (end <= start)
-		return luaL_error(L, "tuple.slice(): start must be less than end");
-
-	struct tuple_iterator it;
-	tuple_rewind(&it, tuple);
-	const char *field;
-
-	assert(start < arity);
-	uint32_t field_no = start;
-	field = tuple_seek(&it, start);
-	while (field && field_no < end) {
-		luamp_decode(L, &field);
-		++field_no;
-		field = tuple_next(&it);
-	}
-	assert(field_no == end);
-	return end - start;
-}
-
-/* A MsgPack extensions handler that supports tuples */
-static void
-luamp_encode_extension_box(struct lua_State *L, int idx, struct tbuf *b)
-{
-	if (lua_type(L, idx) == LUA_TUSERDATA &&
-			lua_istuple(L, idx)) {
-		struct tuple *tuple = lua_checktuple(L, idx);
-		tuple_to_tbuf(tuple, b);
-		return;
-	}
-
-	tnt_raise(ClientError, ER_PROC_RET,
-		  lua_typename(L, lua_type(L, idx)));
-}
-
-/*
- * A luamp_encode wrapper to support old Tarantool 1.5 API.
- * Will be removed after API change.
- */
-static int
-luamp_encodestack(struct lua_State *L, struct tbuf *b, int first, int last)
-{
-	if (first == last && (lua_istable(L, first) ||
-	    (lua_isuserdata(L, first) && lua_istuple(L, first)))) {
-		/* New format */
-		luamp_encode(L, b, first);
-		return 1;
-	} else {
-		/* Backward-compatible format */
-		/* sic: if arg_count is 0, first > last */
-		luamp_encode_array(b, last + 1 - first);
-		for (int k = first; k <= last; ++k) {
-			luamp_encode(L, b, k);
-		}
-		return last + 1 - first;
-	}
-}
-
-static void *
-tuple_update_region_alloc(void *alloc_ctx, size_t size)
-{
-	return region_alloc((struct region *) alloc_ctx, size);
-}
-
-/**
- * Tuple transforming function.
- *
- * Remove the fields designated by 'offset' and 'len' from an tuple,
- * and replace them with the elements of supplied data fields,
- * if any.
- *
- * Function returns newly allocated tuple.
- * It does not change any parent tuple data.
- */
-static int
-lbox_tuple_transform(struct lua_State *L)
-{
-	struct tuple *tuple = lua_checktuple(L, 1);
-	int argc = lua_gettop(L);
-	if (argc < 3)
-		luaL_error(L, "tuple.transform(): bad arguments");
-	lua_Integer offset = lua_tointeger(L, 2);  /* Can be negative and can be > INT_MAX */
-	lua_Integer field_count = lua_tointeger(L, 3);
-
-	uint32_t arity = tuple_arity(tuple);
-	/* validate offset and len */
-	if (offset < 0) {
-		if (-offset > arity)
-			luaL_error(L, "tuple.transform(): offset is out of bound");
-		offset += arity;
-	} else if (offset > arity) {
-		offset = arity;
-	}
-	if (field_count < 0)
-		luaL_error(L, "tuple.transform(): len is negative");
-	if (field_count > arity - offset)
-		field_count = arity - offset;
-
-	assert(offset + field_count <= arity);
-
-	/*
-	 * Calculate the number of operations and length of UPDATE expression
-	 */
-	uint32_t op_cnt = 0;
-	if (offset < arity && field_count > 0)
-		op_cnt++;
-	if (argc > 3)
-		op_cnt += argc - 3;
-
-	if (op_cnt == 0) {
-		/* tuple_update() does not accept an empty operation list. */
-		lbox_pushtuple(L, tuple);
-		return 1;
-	}
-
-	RegionGuard region_guard(&fiber->gc);
-
-	/*
-	 * Prepare UPDATE expression
-	 */
-	struct tbuf *b = tbuf_new(&fiber->gc);
-	tbuf_append(b, (char *) &op_cnt, sizeof(op_cnt));
-	if (field_count > 0) {
-		tbuf_ensure(b, sizeof(uint32_t) + 1 + 9);
-
-		/* offset */
-		char *data = pack_u32(b->data + b->size, offset);
-
-		/* operation */
-		*data++ = UPDATE_OP_DELETE;
-
-		assert(data <= b->data + b->capacity);
-		b->size = data - b->data;
-
-		/* field: count */
-		luamp_encode_uint(b, field_count);
-	}
-
-	for (int i = argc ; i > 3; i--) {
-		tbuf_ensure(b, sizeof(uint32_t) + 1 + 10);
-
-		/* offset */
-		char *data = pack_u32(b->data + b->size, offset);
-
-		/* operation */
-		*data++ = UPDATE_OP_INSERT;
-
-		assert(data <= b->data + b->capacity);
-		b->size = data - b->data;
-
-		/* field */
-		luamp_encode(L, b, i);
-	}
-
-	/* Execute tuple_update */
-	struct tuple *new_tuple = tuple_update(tuple_format_ber,
-					       tuple_update_region_alloc,
-					       &fiber->gc,
-					       tuple, tbuf_str(b), tbuf_end(b));
-	lbox_pushtuple(L, new_tuple);
-	return 1;
-}
-
-/*
- * Tuple find function.
- *
- * Find each or one tuple field according to the specified key.
- *
- * Function returns indexes of the tuple fields that match
- * key criteria.
- *
- */
-static int
-lbox_tuple_find_do(struct lua_State *L, bool all)
-{
-	struct tuple *tuple = lua_checktuple(L, 1);
-	int argc = lua_gettop(L);
-	size_t offset = 0;
-	switch (argc - 1) {
-	case 1: break;
-	case 2:
-		offset = lua_tointeger(L, 2);
-		break;
-	default:
-		luaL_error(L, "tuple.find(): bad arguments");
-	}
-
-	int top = lua_gettop(L);
-	int idx = offset;
-
-	struct luaL_field arg;
-	luaL_checkfield(L, 2, &arg);
-	struct tuple_iterator it;
-	tuple_rewind(&it, tuple);
-	const char *field = tuple_seek(&it, idx);
-	for (; field; field = tuple_next(&it), idx++) {
-		bool found = false;
-		const char *f = field;
-		if (arg.type != mp_typeof(*field))
-			continue;
-
-		switch (arg.type) {
-		case MP_UINT:
-			found = (arg.ival == mp_decode_uint(&f));
-			break;
-		case MP_INT:
-			found = (arg.ival == mp_decode_int(&f));
-			break;
-		case MP_BOOL:
-			found = (arg.bval == mp_decode_bool(&f));
-			break;
-		case MP_DOUBLE:
-			found = (arg.bval == mp_decode_double(&f));
-			break;
-		case MP_STR:
-		{
-			uint32_t len1 = 0;
-			const char *s1 = mp_decode_str(&f, &len1);
-			size_t len2 = arg.sval.len;
-			const char *s2 = arg.sval.data;
-			found = (len1 == len2) && (memcmp(s1, s2, len1) == 0);
-			break;
-		}
-		default:
-			break;
-		}
-		if (found) {
-			lua_pushinteger(L, idx);
-			if (!all)
-				break;
-		}
-	}
-	return lua_gettop(L) - top;
-}
-
-static int
-lbox_tuple_find(struct lua_State *L)
-{
-	return lbox_tuple_find_do(L, false);
-}
-
-static int
-lbox_tuple_findall(struct lua_State *L)
-{
-	return lbox_tuple_find_do(L, true);
-}
-
-static int
-lbox_tuple_unpack(struct lua_State *L)
-{
-	int argc = lua_gettop(L);
-	(void) argc;
-	struct tuple *tuple = lua_checktuple(L, 1);
-
-	struct tuple_iterator it;
-	tuple_rewind(&it, tuple);
-	const char *field;
-	while ((field = tuple_next(&it)))
-		luamp_decode(L, &field);
-
-	assert(lua_gettop(L) == argc + tuple_arity(tuple));
-	(void) argc;
-	return tuple_arity(tuple);
-}
-
-static int
-lbox_tuple_totable(struct lua_State *L)
-{
-	struct tuple *tuple = lua_checktuple(L, 1);
-	lua_newtable(L);
-	int index = 1;
-
-	struct tuple_iterator it;
-	tuple_rewind(&it, tuple);
-	const char *field;
-	while ((field = tuple_next(&it))) {
-		lua_pushnumber(L, index++);
-		luamp_decode(L, &field);
-		lua_rawset(L, -3);
-	}
-
-	/* Hint serializer */
-	assert(tuple_totable_mt_ref != 0);
-	lua_rawgeti(L, LUA_REGISTRYINDEX, tuple_totable_mt_ref);
-	lua_setmetatable(L, -2);
-
-	return 1;
-}
-
-/**
- * Implementation of tuple __index metamethod.
- *
- * Provides operator [] access to individual fields for integer
- * indexes, as well as searches and invokes metatable methods
- * for strings.
- */
-static int
-lbox_tuple_index(struct lua_State *L)
-{
-	struct tuple *tuple = lua_checktuple(L, 1);
-	/* For integer indexes, implement [] operator */
-	if (lua_isnumber(L, 2)) {
-		int i = luaL_checkint(L, 2);
-		const char *field = tuple_field(tuple, i);
-		if (field == NULL) {
-			const char *data = tuple->data;
-			luaL_error(L, "%s: index %d is out of bounds (0..%d)",
-				   tuplelib_name, i, mp_decode_array(&data));
-		}
-		luamp_decode(L, &field);
-		return 1;
-	}
-
-	/* If we got a string, try to find a method for it. */
-	const char *sz = luaL_checkstring(L, 2);
-	lua_getmetatable(L, 1);
-	lua_getfield(L, -1, sz);
-	return 1;
-}
-
-static int
-lbox_tuple_tostring(struct lua_State *L)
-{
-	/*
-	 * The method does next things:
-	 * 1. Calls :unpack
-	 * 2. Serializes the result using yaml
-	 * 3. Strips start and end of yaml document symbols
-	 */
-
-	/* unpack */
-	lbox_tuple_totable(L);
-
-	/* serialize */
-	lua_replace(L, 1);
-	yamlL_encode(L);
-
-	/* strip yaml tags */
-	size_t len;
-	const char *str = lua_tolstring(L, -1, &len);
-	assert(strlen(str) == len);
-	const char *s = index(str, '[');
-	const char *e = rindex(str, ']');
-	assert(s != NULL && e != NULL && s + 1 <= e);
-	lua_pushlstring(L, s, e - s + 1);
-	return 1;
-}
-
-void
-lbox_pushtuple(struct lua_State *L, struct tuple *tuple)
-{
-	if (tuple) {
-		struct tuple **ptr = (struct tuple **)
-				lua_newuserdata(L, sizeof(*ptr));
-		luaL_getmetatable(L, tuplelib_name);
-		lua_setmetatable(L, -2);
-		*ptr = tuple;
-		tuple_ref(tuple, 1);
-	} else {
-		lua_pushnil(L);
-	}
-}
-
-/**
- * Sequential access to tuple fields. Since tuple is a list-like
- * structure, iterating over tuple fields is faster
- * than accessing fields using an index.
- */
-static int
-lbox_tuple_next(struct lua_State *L)
-{
-	struct tuple *tuple = lua_checktuple(L, 1);
-	int argc = lua_gettop(L) - 1;
-
-	struct tuple_iterator *it = NULL;
-	if (argc == 0 || (argc == 1 && lua_type(L, 2) == LUA_TNIL)) {
-		it = (struct tuple_iterator *) lua_newuserdata(L, sizeof(*it));
-		assert(it != NULL);
-		luaL_getmetatable(L, tuple_iteratorlib_name);
-		lua_setmetatable(L, -2);
-		tuple_rewind(it, tuple);
-	} else if (argc == 1 && lua_type(L, 2) == LUA_TUSERDATA) {
-		it = (struct tuple_iterator *)
-			luaL_checkudata(L, 2, tuple_iteratorlib_name);
-		assert(it != NULL);
-		lua_pushvalue(L, 2);
-	} else {
-		return luaL_error(L, "tuple.next(): bad arguments");
-	}
-
-	const char *field = tuple_next(it);
-	if (field == NULL) {
-		lua_pop(L, 1);
-		lua_pushnil(L);
-		return 1;
-	}
-
-	luamp_decode(L, &field);
-	return 2;
-}
-
-/** Iterator over tuple fields. Adapt lbox_tuple_next
- * to Lua iteration conventions.
- */
-static int
-lbox_tuple_pairs(struct lua_State *L)
-{
-	lua_pushcfunction(L, lbox_tuple_next);
-	lua_pushvalue(L, -2); /* tuple */
-	lua_pushnil(L);
-	return 3;
-}
-
-
-/** tuple:bsize()
- *
- */
-static int
-lbox_tuple_bsize(struct lua_State *L)
-{
-	struct tuple *tuple = lua_checktuple(L, 1);
-	lua_pushnumber(L, tuple->bsize);
-	return 1;
-}
-
-static const struct luaL_reg lbox_tuple_meta[] = {
-	{"__gc", lbox_tuple_gc},
-	{"__len", lbox_tuple_len},
-	{"__index", lbox_tuple_index},
-	{"__tostring", lbox_tuple_tostring},
-	{"next", lbox_tuple_next},
-	{"pairs", lbox_tuple_pairs},
-	{"slice", lbox_tuple_slice},
-	{"transform", lbox_tuple_transform},
-	{"find", lbox_tuple_find},
-	{"findall", lbox_tuple_findall},
-	{"unpack", lbox_tuple_unpack},
-	{"totable", lbox_tuple_totable},
-	{"bsize", lbox_tuple_bsize},
-	{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}
-};
-
-/* }}} */
-
-/** {{{ box.index Lua library: access to spaces and indexes
- */
-
-static const char *indexlib_name = "box.index";
-static const char *iteratorlib_name = "box.index.iterator";
-
-/* Index userdata. */
-struct lbox_index
-{
-	Index *index;
-	/* space id. */
-	uint32_t id;
-	/* index id. */
-	uint32_t iid;
-	/* space cache version at the time of push. */
-	int sc_version;
-};
-
-static struct iterator *
-lbox_checkiterator(struct lua_State *L, int i)
-{
-	struct iterator **it = (struct iterator **)
-			luaL_checkudata(L, i, iteratorlib_name);
-	assert(it != NULL);
-	return *it;
-}
-
-static void
-lbox_pushiterator(struct lua_State *L, Index *index,
-		  struct iterator *it, enum iterator_type type,
-		  const char *key, size_t key_size, uint32_t part_count)
-{
-	struct lbox_iterator_udata {
-		struct iterator *it;
-		char key[];
-	};
-
-	struct lbox_iterator_udata *udata = (struct lbox_iterator_udata *)
-		lua_newuserdata(L, sizeof(*udata) + key_size);
-	luaL_getmetatable(L, iteratorlib_name);
-	lua_setmetatable(L, -2);
-
-	udata->it = it;
-	if (key) {
-		memcpy(udata->key, key, key_size);
-		key = udata->key;
-	}
-	key_validate(index->key_def, type, key, part_count);
-	index->initIterator(it, type, key, part_count);
-}
-
-static int
-lbox_iterator_gc(struct lua_State *L)
-{
-	struct iterator *it = lbox_checkiterator(L, -1);
-	it->free(it);
-	return 0;
-}
-
-static Index *
-lua_checkindex(struct lua_State *L, int i)
-{
-	struct lbox_index *index =
-		(struct lbox_index *) luaL_checkudata(L, i, indexlib_name);
-	assert(index != NULL);
-	if (index->sc_version != sc_version) {
-		index->index = index_find(space_find(index->id), index->iid);
-		index->sc_version = sc_version;
-	}
-	return index->index;
-}
-
-static int
-lbox_index_bind(struct lua_State *L)
-{
-	uint32_t id = (uint32_t) luaL_checkint(L, 1); /* get space id */
-	uint32_t iid = (uint32_t) luaL_checkint(L, 2); /* get index id in */
-	/* locate the appropriate index */
-	struct space *space = space_find(id);
-	Index *i = index_find(space, iid);
-
-	/* create a userdata object */
-	struct lbox_index *index = (struct lbox_index *)
-		lua_newuserdata(L, sizeof(struct lbox_index));
-	index->id = id;
-	index->iid = iid;
-	index->sc_version = sc_version;
-	index->index = i;
-	/* set userdata object metatable to indexlib */
-	luaL_getmetatable(L, indexlib_name);
-	lua_setmetatable(L, -2);
-
-	return 1;
-}
-
-static int
-lbox_index_tostring(struct lua_State *L)
-{
-	Index *index = lua_checkindex(L, 1);
-	lua_pushfstring(L, " index %d", (int) index_id(index));
-	return 1;
-}
-
-static int
-lbox_index_len(struct lua_State *L)
-{
-	Index *index = lua_checkindex(L, 1);
-	lua_pushinteger(L, index->size());
-	return 1;
-}
-
-static int
-lbox_index_part_count(struct lua_State *L)
-{
-	Index *index = lua_checkindex(L, 1);
-	lua_pushinteger(L, index->key_def->part_count);
-	return 1;
-}
-
-static int
-lbox_index_min(struct lua_State *L)
-{
-	Index *index = lua_checkindex(L, 1);
-	lbox_pushtuple(L, index->min());
-	return 1;
-}
-
-static int
-lbox_index_max(struct lua_State *L)
-{
-	Index *index = lua_checkindex(L, 1);
-	lbox_pushtuple(L, index->max());
-	return 1;
-}
-
-static int
-lbox_index_random(struct lua_State *L)
-{
-	if (lua_gettop(L) != 2 || lua_isnil(L, 2))
-		luaL_error(L, "Usage: index:random((uint32) rnd)");
-
-	Index *index = lua_checkindex(L, 1);
-	uint32_t rnd = lua_tointeger(L, 2);
-	lbox_pushtuple(L, index->random(rnd));
-	return 1;
-}
-
-
-/*
- * Lua iterator over a Taratnool/Box index.
- *
- *	(iteration_state, tuple) = index.next(index, [params])
- *
- * When [params] are absent or nil
- * returns a pointer to a new ALL iterator and
- * to the first tuple (or nil, if the index is
- * empty).
- *
- * When [params] is a userdata,
- * i.e. we're inside an iteration loop, retrieves
- * the next tuple from the iterator.
- *
- * Otherwise, [params] can be used to seed
- * a new iterator with iterator type and
- * type-specific arguments. For exaple,
- * for GE iterator, a list of Lua scalars
- * cann follow the box.index.GE: this will
- * start iteration from the offset specified by
- * the given (multipart) key.
- *
- * @return Returns an iterator object, either created
- *         or taken from Lua stack.
- */
-
-static inline struct iterator *
-lbox_create_iterator(struct lua_State *L)
-{
-	Index *index = lua_checkindex(L, 1);
-	int argc = lua_gettop(L);
-
-	/* Create a new iterator. */
-	enum iterator_type type = ITER_ALL;
-	uint32_t key_part_count = 0;
-	const char *key = NULL;
-	size_t key_size = 0;
-
-	if (argc > 1 && lua_type(L, 2) != LUA_TNIL) {
-		type = (enum iterator_type) luaL_checkint(L, 2);
-		if (type < ITER_ALL || type >= iterator_type_MAX)
-			luaL_error(L, "unknown iterator type: %d", type);
-	}
-
-	RegionGuard region_guard(&fiber->gc);
-
-	/* What else do we have on the stack? */
-	if (argc > 2 && (lua_type(L, 3) != LUA_TNIL)) {
-		/* Single or multi- part key. */
-		struct tbuf *b = tbuf_new(&fiber->gc);
-		luamp_encodestack(L, b, 3, argc);
-		key = b->data;
-		assert(b->size > 0);
-		if (unlikely(mp_typeof(*key) != MP_ARRAY))
-			tnt_raise(ClientError, ER_TUPLE_NOT_ARRAY);
-		key_part_count = mp_decode_array(&key);
-		key_size = b->data + b->size - key;
-	}
-
-	struct iterator *it = index->allocIterator();
-	lbox_pushiterator(L, index, it, type, key, key_size, key_part_count);
-	return it;
-}
-
-/**
- * Lua-style next() function, for use in pairs().
- * @example:
- * for k, v in box.space[0].index[0].idx.next, box.space[0].index[0].idx, nil do
- *	print(v)
- * end
- */
-static int
-lbox_index_next(struct lua_State *L)
-{
-	int argc = lua_gettop(L);
-	struct iterator *it = NULL;
-	if (argc == 2 && lua_type(L, 2) == LUA_TUSERDATA) {
-		/*
-		 * Apart from the index itself, we have only one
-		 * other argument, and it's a userdata: must be
-		 * iteration state created before.
-		 */
-		it = lbox_checkiterator(L, 2);
-	} else {
-		it = lbox_create_iterator(L);
-	}
-	struct tuple *tuple = it->next(it);
-	/* If tuple is NULL, pushes nil as end indicator. */
-	lbox_pushtuple(L, tuple);
-	return tuple ? 2 : 1;
-}
-
-/** iterator() closure function. */
-static int
-lbox_index_iterator_closure(struct lua_State *L)
-{
-	/* Extract closure arguments. */
-	struct iterator *it = lbox_checkiterator(L, lua_upvalueindex(1));
-
-	struct tuple *tuple = it->next(it);
-
-	/* If tuple is NULL, push nil as end indicator. */
-	lbox_pushtuple(L, tuple);
-	return 1;
-}
-
-/**
- * @brief Create iterator closure over a Taratnool/Box index.
- * @example lua it = box.space[0].index[0]:iterator(box.index.GE, 1);
- *   print(it(), it()).
- * @param L lua stack
- * @see http://www.lua.org/pil/7.1.html
- * @return number of return values put on the stack
- */
-static int
-lbox_index_iterator(struct lua_State *L)
-{
-	/* Create iterator and push it onto the stack. */
-	(void) lbox_create_iterator(L);
-	lua_pushcclosure(L, &lbox_index_iterator_closure, 1);
-	return 1;
-}
-
-
-/**
- * Lua index subtree count function.
- * Iterate over an index, count the number of tuples which equal the
- * provided search criteria. The argument can either point to a
- * tuple, a key, or one or more key parts. Returns the number of matched
- * tuples.
- */
-static int
-lbox_index_count(struct lua_State *L)
-{
-	Index *index = lua_checkindex(L, 1);
-	int argc = lua_gettop(L);
-	if (argc == 0)
-		luaL_error(L, "index.count(): one or more arguments expected");
-
-	/* preparing single or multi-part key */
-	if (argc == 1 || (argc == 2 && lua_type(L, 2) == LUA_TNIL)) {
-		/* Nothing */
-		/* Return index size */
-		lua_pushnumber(L, index->size());
-		return 1;
-	}
-
-	RegionGuard region_guard(&fiber->gc);
-	struct tbuf *b = tbuf_new(&fiber->gc);
-
-	/* Single or multi- part key. */
-	luamp_encodestack(L, b, 2, argc);
-
-	const char *key = b->data;
-	if (unlikely(mp_typeof(*key) != MP_ARRAY))
-		tnt_raise(ClientError, ER_TUPLE_NOT_ARRAY);
-	uint32_t part_count = mp_decode_array(&key);
-	key_validate(index->key_def, ITER_EQ, key, part_count);
-
-	/* Prepare index iterator */
-	struct iterator *it = index->position();
-	index->initIterator(it, ITER_EQ, key, part_count);
-	/* Iterate over the index and count tuples. */
-	struct tuple *tuple;
-	uint32_t count = 0;
-	while ((tuple = it->next(it)) != NULL)
-		count++;
-
-	/* Return subtree size */
-	lua_pushnumber(L, count);
-	return 1;
-}
-
-static const struct luaL_reg lbox_index_meta[] = {
-	{"__tostring", lbox_index_tostring},
-	{"__len", lbox_index_len},
-	{"part_count", lbox_index_part_count},
-	{"min", lbox_index_min},
-	{"max", lbox_index_max},
-	{"random", lbox_index_random},
-	{"next", lbox_index_next},
-	{"iterator", lbox_index_iterator},
-	{"count", lbox_index_count},
-	{NULL, NULL}
-};
-
-static const struct luaL_reg indexlib [] = {
-	{"bind", lbox_index_bind},
-	{NULL, NULL}
-};
-
-static const struct luaL_reg lbox_iterator_meta[] = {
-	{"__gc", lbox_iterator_gc},
-	{NULL, NULL}
-};
-
-/* }}} */
-
-/** {{{ Lua I/O: facilities to intercept box output
- * and push into Lua stack.
- */
-
-struct port_lua
-{
-	struct port_vtab *vtab;
-	struct lua_State *L;
-};
-
-static inline struct port_lua *
-port_lua(struct port *port) { return (struct port_lua *) port; }
-
-/*
- * For addU32/dupU32 do nothing -- the only uint32_t Box can give
- * us is tuple count, and we don't need it, since we intercept
- * everything into Lua stack first.
- * @sa port_add_lua_multret
- */
-
-static void
-port_lua_add_tuple(struct port *port, struct tuple *tuple,
-		   uint32_t flags __attribute__((unused)))
-{
-	lua_State *L = port_lua(port)->L;
-	try {
-		lbox_pushtuple(L, tuple);
-	} catch (...) {
-		tnt_raise(ClientError, ER_PROC_LUA, lua_tostring(L, -1));
-	}
-}
-
-struct port_vtab port_lua_vtab = {
-	port_lua_add_tuple,
-	null_port_eof,
-};
-
-static struct port *
-port_lua_create(struct lua_State *L)
-{
-	struct port_lua *port = (struct port_lua *)
-			region_alloc(&fiber->gc, sizeof(struct port_lua));
-	port->vtab = &port_lua_vtab;
-	port->L = L;
-	return (struct port *) port;
-}
-
-static struct tuple*
-lua_totuple(struct lua_State *L, int first, int last)
-{
-	RegionGuard region_guard(&fiber->gc);
-	struct tbuf *b = tbuf_new(&fiber->gc);
-	try {
-		luamp_encodestack(L, b, first, last);
-	} catch (const Exception &e) {
-		throw;
-	} catch (...) {
-		tnt_raise(ClientError, ER_PROC_LUA, lua_tostring(L, -1));
-	}
-	const char *data = b->data;
-	if (unlikely(mp_typeof(*data) != MP_ARRAY))
-		tnt_raise(ClientError, ER_TUPLE_NOT_ARRAY);
-	struct tuple *tuple = tuple_new(tuple_format_ber, &data, tbuf_end(b));
-	return tuple;
-}
-
-static void
-port_add_lua_ret(struct port *port, struct lua_State *L, int index)
-{
-	struct tuple *tuple = lua_totuple(L, index, index);
-	TupleGuard guard(tuple);
-	port_add_tuple(port, tuple, BOX_RETURN_TUPLE);
-}
-
-/**
- * Add all elements from Lua stack to fiber iov.
- *
- * To allow clients to understand a complex return from
- * a procedure, we are compatible with SELECT protocol,
- * and return the number of return values first, and
- * then each return value as a tuple.
- *
- * If a Lua stack contains at least one scalar, each
- * value on the stack is converted to a tuple. A Lua
- * is converted to a tuple with multiple fields.
- *
- * If the stack is a Lua table, each member of which is
- * not scalar, each member of the table is converted to
- * a tuple. This way very large lists of return values can
- * be used, since Lua stack size is limited by 8000 elements,
- * while Lua table size is pretty much unlimited.
- */
-static void
-port_add_lua_multret(struct port *port, struct lua_State *L)
-{
-	int nargs = lua_gettop(L);
-	/** Check if we deal with a table of tables. */
-	if (nargs == 1 && lua_istable(L, 1)) {
-		/*
-		 * The table is not empty and consists of tables
-		 * or tuples. Treat each table element as a tuple,
-		 * and push it.
-		 */
-		lua_pushnil(L);
-		int has_keys = lua_next(L, 1);
-		if (has_keys  &&
-		    (lua_istable(L, -1) || lua_isuserdata(L, -1))) {
-
-			do {
-				port_add_lua_ret(port, L, lua_gettop(L));
-				lua_pop(L, 1);
-			} while (lua_next(L, 1));
-			return;
-		} else if (has_keys) {
-			lua_pop(L, 1);
-		}
-	}
-	for (int i = 1; i <= nargs; ++i) {
-		port_add_lua_ret(port, L, i);
-	}
-}
-
-/* }}} */
-
-/**
- * The main extension provided to Lua by Tarantool/Box --
- * ability to call INSERT/UPDATE/SELECT/DELETE from within
- * a Lua procedure.
- *
- * This is a low-level API, and it expects
- * all arguments to be packed in accordance
- * with the binary protocol format (iproto
- * header excluded).
- *
- * Signature:
- * box.process(op_code, request)
- */
-static int
-lbox_process(lua_State *L)
-{
-	uint32_t op = lua_tointeger(L, 1); /* Get the first arg. */
-	size_t sz;
-	const char *req = luaL_checklstring(L, 2, &sz); /* Second arg. */
-	if (op == CALL) {
-		/*
-		 * We should not be doing a CALL from within a CALL.
-		 * To invoke one stored procedure from another, one must
-		 * do it in Lua directly. This deals with
-		 * infinite recursion, stack overflow and such.
-		 */
-		return luaL_error(L, "box.process(CALL, ...) is not allowed");
-	}
-	int top = lua_gettop(L); /* to know how much is added by rw_callback */
-
-	size_t allocated_size = region_used(&fiber->gc);
-	struct port *port_lua = port_lua_create(L);
-	try {
-		struct request request;
-		request_create(&request, op, req, sz);
-		box_process(port_lua, &request);
-
-		/*
-		 * This only works as long as port_lua doesn't
-		 * use fiber->cleanup and fiber->gc.
-		 */
-		region_truncate(&fiber->gc, allocated_size);
-	} catch (const Exception &e) {
-		region_truncate(&fiber->gc, allocated_size);
-		throw;
-	}
-	return lua_gettop(L) - top;
-}
-
-static int
-lbox_raise(lua_State *L)
-{
-	if (lua_gettop(L) != 2)
-		luaL_error(L, "box.raise(): bad arguments");
-	uint32_t code = lua_tointeger(L, 1);
-	if (!code)
-		luaL_error(L, "box.raise(): unknown error code");
-	const char *str = lua_tostring(L, 2);
-	tnt_raise(ClientError, str, code);
-	return 0;
-}
-
-/**
- * A helper to find a Lua function by name and put it
- * on top of the stack.
- */
-static int
-box_lua_find(lua_State *L, const char *name, const char *name_end)
-{
-	int index = LUA_GLOBALSINDEX;
-	int objstack = 0;
-	const char *start = name, *end;
-
-	while ((end = (const char *) memchr(start, '.', name_end - start))) {
-		lua_checkstack(L, 3);
-		lua_pushlstring(L, start, end - start);
-		lua_gettable(L, index);
-		if (! lua_istable(L, -1))
-			tnt_raise(ClientError, ER_NO_SUCH_PROC,
-				  name_end - name, name);
-		start = end + 1; /* next piece of a.b.c */
-		index = lua_gettop(L); /* top of the stack */
-	}
-
-	/* box.something:method */
-	if ((end = (const char *) memchr(start, ':', name_end - start))) {
-		lua_checkstack(L, 3);
-		lua_pushlstring(L, start, end - start);
-		lua_gettable(L, index);
-		if (! (lua_istable(L, -1) ||
-			lua_islightuserdata(L, -1) || lua_isuserdata(L, -1) ))
-				tnt_raise(ClientError, ER_NO_SUCH_PROC,
-					  name_end - name, name);
-		start = end + 1; /* next piece of a.b.c */
-		index = lua_gettop(L); /* top of the stack */
-		objstack = 1;
-	}
-
-
-	lua_pushlstring(L, start, name_end - start);
-	lua_gettable(L, index);
-	if (! lua_isfunction(L, -1)) {
-		/* lua_call or lua_gettable would raise a type error
-		 * for us, but our own message is more verbose. */
-		tnt_raise(ClientError, ER_NO_SUCH_PROC,
-			  name_end - name, name);
-	}
-	/* setting stack that it would contain only
-	 * the function pointer. */
-	if (index != LUA_GLOBALSINDEX) {
-		lua_replace(L, 1);
-		if (objstack)
-			lua_replace(L, 2);
-		lua_settop(L, 1 + objstack);
-	}
-	return 1 + objstack;
-}
-
-
-/**
- * A helper to find lua stored procedures for box.call.
- * box.call iteslf is pure Lua, to avoid issues
- * with infinite call recursion smashing C
- * thread stack.
- */
-
-static int
-lbox_call_loadproc(struct lua_State *L)
-{
-	const char *name;
-	size_t name_len;
-	name = lua_tolstring(L, 1, &name_len);
-	return box_lua_find(L, name, name + name_len);
-}
-
-/**
- * Invoke a Lua stored procedure from the binary protocol
- * (implementation of 'CALL' command code).
- */
-void
-box_lua_execute(const struct request *request, struct txn *txn,
-		struct port *port)
-{
-	(void) txn;
-	lua_State *L = lua_newthread(tarantool_L);
-	LuarefGuard coro_ref(tarantool_L);
-
-	/* proc name */
-	int oc = box_lua_find(L, request->c.procname,
-			 request->c.procname + request->c.procname_len);
-	/* Push the rest of args (a tuple). */
-	const char *args = request->c.args;
-	uint32_t arg_count = mp_decode_array(&args);
-	luaL_checkstack(L, arg_count, "call: out of stack");
-
-	for (uint32_t i = 0; i < arg_count; i++) {
-		luamp_decode(L, &args);
-	}
-	lbox_call(L, arg_count + oc - 1, LUA_MULTRET);
-	/* Send results of the called procedure to the client. */
-	port_add_lua_multret(port, L);
-}
-
-static void
-box_index_init_iterator_types(struct lua_State *L, int idx)
-{
-	for (int i = 0; i < iterator_type_MAX; i++) {
-		assert(strncmp(iterator_type_strs[i], "ITER_", 5) == 0);
-		lua_pushnumber(L, i);
-		/* cut ITER_ prefix from enum name */
-		lua_setfield(L, idx, iterator_type_strs[i] + 5);
-	}
-}
-
-/**
- * Convert box.pack() format specifier to Tarantool
- * binary protocol UPDATE opcode
- */
-static char format_to_opcode(char format)
-{
-	switch (format) {
-	case '=': return 0;
-	case '+': return 1;
-	case '&': return 2;
-	case '^': return 3;
-	case '|': return 4;
-	case ':': return 5;
-	case '#': return 6;
-	case '!': return 7;
-	case '-': return 8;
-	default: return format;
-	}
-}
-
-/**
- * Counterpart to @a format_to_opcode
- */
-static char opcode_to_format(char opcode)
-{
-	switch (opcode) {
-	case 0: return '=';
-	case 1: return '+';
-	case 2: return '&';
-	case 3: return '^';
-	case 4: return '|';
-	case 5: return ':';
-	case 6: return '#';
-	case 7: return '!';
-	case 8: return '-';
-	default: return opcode;
-	}
-}
-
-/**
- * To use Tarantool/Box binary protocol primitives from Lua, we
- * need a way to pack Lua variables into a binary representation.
- * We do it by exporting a helper function
- *
- * box.pack(format, args...)
- *
- * which takes the format, which is very similar to Perl 'pack'
- * format, and a list of arguments, and returns a binary string
- * which has the arguments packed according to the format.
- *
- * For example, a typical SELECT packet packs in Lua like this:
- *
- * pkt = box.pack("iiiiiip", -- pack format
- *                         0, -- space id
- *                         0, -- index id
- *                         0, -- offset
- *                         2^32, -- limit
- *                         1, -- number of SELECT arguments
- *                         1, -- tuple cardinality
- *                         key); -- the key to use for SELECT
- *
- * @sa doc/box-protocol.txt, binary protocol description
- * @todo: implement box.unpack(format, str), for testing purposes
- */
-static int
-lbox_pack(struct lua_State *L)
-{
-	const char *format = luaL_checkstring(L, 1);
-	/* first arg comes second */
-	int i = 2;
-	int nargs = lua_gettop(L);
-	size_t size;
-	const char *str;
-
-	RegionGuard region_guard(&fiber->gc);
-	struct tbuf *b = tbuf_new(&fiber->gc);
-
-	struct luaL_field field;
-	double dbl;
-	float flt;
-	char *data;
-	while (*format) {
-		if (i > nargs)
-			luaL_error(L, "box.pack: argument count does not match "
-				   "the format");
-		luaL_tofield(L, i, &field);
-		switch (*format) {
-		case 'B':
-		case 'b':
-			/* signed and unsigned 8-bit integers */
-			if (field.type != MP_UINT && field.type != MP_INT)
-				luaL_error(L, "box.pack: expected 8-bit int");
-
-			tbuf_append(b, (char *) &field.ival, sizeof(uint8_t));
-			break;
-		case 'S':
-		case 's':
-			/* signed and unsigned 16-bit integers */
-			if (field.type != MP_UINT && field.type != MP_INT)
-				luaL_error(L, "box.pack: expected 16-bit int");
-
-			tbuf_append(b, (char *) &field.ival, sizeof(uint16_t));
-			break;
-		case 'n':
-			/* signed and unsigned 16-bit big endian integers */
-			if (field.type != MP_UINT && field.type != MP_INT)
-				luaL_error(L, "box.pack: expected 16-bit int");
-
-			field.ival = (uint16_t) htons((uint16_t) field.ival);
-			tbuf_append(b, (char *) &field.ival, sizeof(uint16_t));
-			break;
-		case 'I':
-		case 'i':
-			/* signed and unsigned 32-bit integers */
-			if (field.type != MP_UINT && field.ival != MP_INT)
-				luaL_error(L, "box.pack: expected 32-bit int");
-
-			tbuf_append(b, (char *) &field.ival, sizeof(uint32_t));
-			break;
-		case 'N':
-			/* signed and unsigned 32-bit big endian integers */
-			if (field.type != MP_UINT && field.ival != MP_INT)
-				luaL_error(L, "box.pack: expected 32-bit int");
-
-			field.ival = htonl(field.ival);
-			tbuf_append(b, (char *) &field.ival, sizeof(uint32_t));
-			break;
-		case 'L':
-		case 'l':
-			/* signed and unsigned 64-bit integers */
-			if (field.type != MP_UINT && field.type != MP_INT)
-				luaL_error(L, "box.pack: expected 64-bit int");
-
-			tbuf_append(b, (char *) &field.ival, sizeof(uint64_t));
-			break;
-		case 'Q':
-		case 'q':
-			/* signed and unsigned 64-bit integers */
-			if (field.type != MP_UINT && field.type != MP_INT)
-				luaL_error(L, "box.pack: expected 64-bit int");
-
-			field.ival = bswap_u64(field.ival);
-			tbuf_append(b, (char *) &field.ival, sizeof(uint64_t));
-			break;
-		case 'd':
-			dbl = (double) lua_tonumber(L, i);
-			tbuf_append(b, (char *) &dbl, sizeof(double));
-			break;
-		case 'f':
-			flt = (float) lua_tonumber(L, i);
-			tbuf_append(b, (char *) &flt, sizeof(float));
-			break;
-		case 'A':
-		case 'a':
-			/* A sequence of bytes */
-			str = luaL_checklstring(L, i, &size);
-			tbuf_append(b, str, size);
-			break;
-		case 'P':
-		case 'p':
-			luamp_encode(L, b, i);
-			break;
-		case 'V':
-		{
-			int arg_count = luaL_checkint(L, i);
-			if (i + arg_count > nargs)
-				luaL_error(L, "box.pack: argument count does not match "
-					   "the format");
-			int first = i + 1;
-			int last = i + arg_count;
-			i += luamp_encodestack(L, b, first, last);
-			break;
-		}
-		case '=':
-			/* update tuple set foo = bar */
-		case '+':
-			/* set field += val */
-		case '-':
-			/* set field -= val */
-		case '&':
-			/* set field & =val */
-		case '|':
-			/* set field |= val */
-		case '^':
-			/* set field ^= val */
-		case ':':
-			/* splice */
-		case '#':
-			/* delete field */
-		case '!':
-			/* insert field */
-			/* field no */
-			tbuf_ensure(b, sizeof(uint32_t) + 1);
-			data = b->data + b->size;
-
-			data = pack_u32(data, lua_tointeger(L, i));
-			*data++ = format_to_opcode(*format);
-
-			assert(data <= b->data + b->capacity);
-			b->size = data - b->data;
-			break;
-		default:
-			luaL_error(L, "box.pack: unsupported pack "
-				   "format specifier '%c'", *format);
-		}
-		i++;
-		format++;
-	}
-
-	lua_pushlstring(L, tbuf_str(b), b->size);
-
-	return 1;
-}
-
-const char *
-box_unpack_response(struct lua_State *L, const char *s, const char *end)
-{
-	uint32_t tuple_count = pick_u32(&s, end);
-
-	/* Unpack and push tuples. */
-	while (tuple_count--) {
-		uint32_t bsize = pick_u32(&s, end);
-		const char *tend = s + bsize;
-		if (tend > end)
-			tnt_raise(IllegalParams, "incorrect packet length");
-
-		const char *t = s;
-		if (unlikely(!mp_check(&s, tend)))
-			tnt_raise(ClientError, ER_INVALID_MSGPACK);
-		if (unlikely(mp_typeof(*t) != MP_ARRAY))
-			tnt_raise(ClientError, ER_TUPLE_NOT_ARRAY);
-		struct tuple *tuple = tuple_new(tuple_format_ber, &t, tend);
-		lbox_pushtuple(L, tuple);
-	}
-	return s;
-}
-
-
-static int
-lbox_unpack(struct lua_State *L)
-{
-	size_t format_size = 0;
-	const char *format = luaL_checklstring(L, 1, &format_size);
-	const char *f = format;
-
-	size_t str_size = 0;
-	const char *str =  luaL_checklstring(L, 2, &str_size);
-	const char *end = str + str_size;
-	const char *s = str;
-
-	int save_stacksize = lua_gettop(L);
-
-	char charbuf;
-	uint8_t  u8buf;
-	uint16_t u16buf;
-	uint32_t u32buf;
-	double dbl;
-	float flt;
-
-#define CHECK_SIZE(cur) if (unlikely((cur) >= end)) {	                \
-	luaL_error(L, "box.unpack('%c'): got %d bytes (expected: %d+)",	\
-		   *f, (int) (end - str), (int) 1 + ((cur) - str));	\
-}
-	while (*f) {
-		switch (*f) {
-		case 'b':
-			CHECK_SIZE(s);
-			u8buf = *(uint8_t *) s;
-			lua_pushnumber(L, u8buf);
-			s++;
-			break;
-		case 's':
-			CHECK_SIZE(s + 1);
-			u16buf = *(uint16_t *) s;
-			lua_pushnumber(L, u16buf);
-			s += 2;
-			break;
-		case 'n':
-			CHECK_SIZE(s + 1);
-			u16buf = ntohs(*(uint16_t *) s);
-			lua_pushnumber(L, u16buf);
-			s += 2;
-			break;
-		case 'i':
-			CHECK_SIZE(s + 3);
-			u32buf = *(uint32_t *) s;
-			lua_pushnumber(L, u32buf);
-			s += 4;
-			break;
-		case 'N':
-			CHECK_SIZE(s + 3);
-			u32buf = ntohl(*(uint32_t *) s);
-			lua_pushnumber(L, u32buf);
-			s += 4;
-			break;
-		case 'l':
-			CHECK_SIZE(s + 7);
-			luaL_pushnumber64(L, *(uint64_t*) s);
-			s += 8;
-			break;
-		case 'q':
-			CHECK_SIZE(s + 7);
-			luaL_pushnumber64(L, bswap_u64(*(uint64_t*) s));
-			s += 8;
-			break;
-		case 'd':
-			CHECK_SIZE(s + 7);
-			dbl = *(double *) s;
-			lua_pushnumber(L, dbl);
-			s += 8;
-			break;
-		case 'f':
-			CHECK_SIZE(s + 3);
-			flt = *(float *) s;
-			lua_pushnumber(L, flt);
-			s += 4;
-			break;
-		case 'a':
-		case 'A': /* The rest of the data is a Lua string. */
-			lua_pushlstring(L, s, end - s);
-			s = end;
-			break;
-		case 'P':
-		case 'p':
-		{
-			const char *data = s;
-			if (unlikely(!mp_check(&s, end)))
-				tnt_raise(ClientError, ER_INVALID_MSGPACK);
-			luamp_decode(L, &data);
-			assert(data == s);
-			break;
-		}
-		case '=':
-			/* update tuple set foo = bar */
-		case '+':
-			/* set field += val */
-		case '-':
-			/* set field -= val */
-		case '&':
-			/* set field & =val */
-		case '|':
-			/* set field |= val */
-		case '^':
-			/* set field ^= val */
-		case ':':
-			/* splice */
-		case '#':
-			/* delete field */
-		case '!':
-			/* insert field */
-			CHECK_SIZE(s + 4);
-
-			/* field no */
-			u32buf = *(uint32_t *) s;
-
-			/* opcode */
-			charbuf = *(s + 4);
-			charbuf = opcode_to_format(charbuf);
-			if (charbuf != *f) {
-				luaL_error(L, "box.unpack('%s'): "
-					   "unexpected opcode: "
-					   "offset %d, expected '%c',"
-					   "found '%c'",
-					   format, s - str, *f, charbuf);
-			}
-
-			lua_pushnumber(L, u32buf);
-			s += 5;
-			break;
-
-		case 'R': /* Unpack server response, IPROTO format. */
-		{
-			s = box_unpack_response(L, s, end);
-			break;
-		}
-		default:
-			luaL_error(L, "box.unpack: unsupported "
-				   "format specifier '%c'", *f);
-		}
-		f++;
-	}
-
-	assert(s <= end);
-
-	if (s != end) {
-		luaL_error(L, "box.unpack('%s'): too many bytes: "
-			   "unpacked %d, total %d",
-			   format, s - str, str_size);
-	}
-
-	return lua_gettop(L) - save_stacksize;
-
-#undef CHECK_SIZE
-}
-
-static const struct luaL_reg boxlib[] = {
-	{"process", lbox_process},
-	{"call_loadproc",  lbox_call_loadproc},
-	{"raise", lbox_raise},
-	{"pack", lbox_pack},
-	{"unpack", lbox_unpack},
-	{NULL, NULL}
-};
-
-void
-schema_lua_init(struct lua_State *L)
-{
-	lua_getfield(L, LUA_GLOBALSINDEX, "box");
-	lua_newtable(L);
-	lua_setfield(L, -2, "schema");
-	lua_getfield(L, -1, "schema");
-	lua_pushnumber(L, SC_SCHEMA_ID);
-	lua_setfield(L, -2, "SCHEMA_ID");
-	lua_pushnumber(L, SC_SPACE_ID);
-	lua_setfield(L, -2, "SPACE_ID");
-	lua_pushnumber(L, SC_INDEX_ID);
-	lua_setfield(L, -2, "INDEX_ID");
-	lua_pushnumber(L, SC_SYSTEM_ID_MIN);
-	lua_setfield(L, -2, "SYSTEM_ID_MIN");
-	lua_pushnumber(L, SC_SYSTEM_ID_MAX);
-	lua_setfield(L, -2, "SYSTEM_ID_MAX");
-	lua_pushnumber(L, BOX_INDEX_MAX);
-	lua_setfield(L, -2, "INDEX_MAX");
-	lua_pushnumber(L, BOX_SPACE_MAX);
-	lua_setfield(L, -2, "SPACE_MAX");
-	lua_pushnumber(L, BOX_FIELD_MAX);
-	lua_setfield(L, -2, "FIELD_MAX");
-	lua_pushnumber(L, BOX_INDEX_FIELD_MAX);
-	lua_setfield(L, -2, "INDEX_FIELD_MAX");
-	lua_pushnumber(L, BOX_INDEX_PART_MAX);
-	lua_setfield(L, -2, "INDEX_PART_MAX");
-	lua_pushnumber(L, BOX_NAME_MAX);
-	lua_setfield(L, -2, "NAME_MAX");
-	lua_pushnumber(L, FORMAT_ID_MAX);
-	lua_setfield(L, -2, "FORMAT_ID_MAX");
-	lua_pop(L, 2); /* box, schema */
-}
-
-void
-box_lua_init(struct lua_State *L)
-{
-	/* box, box.tuple */
-	luaL_register_type(L, tuplelib_name, lbox_tuple_meta);
-	luaL_register(L, tuplelib_name, lbox_tuplelib);
-	lua_pop(L, 1);
-	schema_lua_init(L);
-	luaL_register_type(L, tuple_iteratorlib_name,
-				    lbox_tuple_iterator_meta);
-	luaL_register(L, "box", boxlib);
-	lua_pop(L, 1);
-	/* box.index */
-	luaL_register_type(L, indexlib_name, lbox_index_meta);
-	luaL_register(L, "box.index", indexlib);
-	box_index_init_iterator_types(L, -2);
-	lua_pop(L, 1);
-	luaL_register_type(L, iteratorlib_name, lbox_iterator_meta);
-
-	/* Load Lua extension */
-	for (const char **s = lua_sources; *s; s++) {
-		if (luaL_dostring(L, *s))
-			panic("Error loading Lua source %.160s...: %s",
-			      *s, lua_tostring(L, -1));
-	}
-
-	luamp_set_encode_extension(luamp_encode_extension_box);
-
-
-
-	/* Precreate a metatable for tuple_unpack */
-	lua_newtable(L);
-	lua_pushstring(L, "_serializer_compact");
-	lua_pushboolean(L, true);
-	lua_settable(L, -3);
-	lua_pushstring(L, "_serializer_type");
-	lua_pushstring(L, "array");
-	lua_settable(L, -3);
-	tuple_totable_mt_ref = luaL_ref(L, LUA_REGISTRYINDEX);
-	assert(tuple_totable_mt_ref != 0);
-
-	assert(lua_gettop(L) == 0);
-}
diff --git a/src/box/lua/call.cc b/src/box/lua/call.cc
new file mode 100644
index 0000000000..58862a0705
--- /dev/null
+++ b/src/box/lua/call.cc
@@ -0,0 +1,746 @@
+/*
+ * 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 "box/lua/call.h"
+
+#include <arpa/inet.h>
+
+#include "box/lua/tuple.h"
+#include "box/lua/index.h"
+#include "box/lua/space.h"
+#include "box/tuple.h"
+
+#include "lua/utils.h"
+#include "lua/msgpack.h"
+#include "tbuf.h"
+#include "fiber.h"
+#include "scoped_guard.h"
+#include "box/box.h"
+#include "box/port.h"
+#include "box/request.h"
+#include "bit/bit.h"
+
+/* contents of box.lua, misc.lua, box.net.lua respectively */
+extern char schema_lua[], box_lua[], box_net_lua[], misc_lua[], sql_lua[];
+static const char *lua_sources[] = { schema_lua, box_lua, box_net_lua, misc_lua, sql_lua, NULL };
+
+/*
+ * Functions, exported in box_lua.h should have prefix
+ * "box_lua_"; functions, available in Lua "box"
+ * should start with "lbox_".
+ */
+
+/** {{{ Lua I/O: facilities to intercept box output
+ * and push into Lua stack.
+ */
+
+struct port_lua
+{
+	struct port_vtab *vtab;
+	struct lua_State *L;
+};
+
+static inline struct port_lua *
+port_lua(struct port *port) { return (struct port_lua *) port; }
+
+/*
+ * For addU32/dupU32 do nothing -- the only uint32_t Box can give
+ * us is tuple count, and we don't need it, since we intercept
+ * everything into Lua stack first.
+ * @sa port_add_lua_multret
+ */
+
+static void
+port_lua_add_tuple(struct port *port, struct tuple *tuple,
+		   uint32_t flags __attribute__((unused)))
+{
+	lua_State *L = port_lua(port)->L;
+	try {
+		lbox_pushtuple(L, tuple);
+	} catch (...) {
+		tnt_raise(ClientError, ER_PROC_LUA, lua_tostring(L, -1));
+	}
+}
+
+struct port_vtab port_lua_vtab = {
+	port_lua_add_tuple,
+	null_port_eof,
+};
+
+static struct port *
+port_lua_create(struct lua_State *L)
+{
+	struct port_lua *port = (struct port_lua *)
+			region_alloc(&fiber->gc, sizeof(struct port_lua));
+	port->vtab = &port_lua_vtab;
+	port->L = L;
+	return (struct port *) port;
+}
+
+static void
+port_add_lua_ret(struct port *port, struct lua_State *L, int index)
+{
+	struct tuple *tuple = lua_totuple(L, index, index);
+	TupleGuard guard(tuple);
+	port_add_tuple(port, tuple, BOX_RETURN_TUPLE);
+}
+
+/**
+ * Add all elements from Lua stack to fiber iov.
+ *
+ * To allow clients to understand a complex return from
+ * a procedure, we are compatible with SELECT protocol,
+ * and return the number of return values first, and
+ * then each return value as a tuple.
+ *
+ * If a Lua stack contains at least one scalar, each
+ * value on the stack is converted to a tuple. A Lua
+ * is converted to a tuple with multiple fields.
+ *
+ * If the stack is a Lua table, each member of which is
+ * not scalar, each member of the table is converted to
+ * a tuple. This way very large lists of return values can
+ * be used, since Lua stack size is limited by 8000 elements,
+ * while Lua table size is pretty much unlimited.
+ */
+static void
+port_add_lua_multret(struct port *port, struct lua_State *L)
+{
+	int nargs = lua_gettop(L);
+	/** Check if we deal with a table of tables. */
+	if (nargs == 1 && lua_istable(L, 1)) {
+		/*
+		 * The table is not empty and consists of tables
+		 * or tuples. Treat each table element as a tuple,
+		 * and push it.
+		 */
+		lua_pushnil(L);
+		int has_keys = lua_next(L, 1);
+		if (has_keys  &&
+		    (lua_istable(L, -1) || lua_isuserdata(L, -1))) {
+
+			do {
+				port_add_lua_ret(port, L, lua_gettop(L));
+				lua_pop(L, 1);
+			} while (lua_next(L, 1));
+			return;
+		} else if (has_keys) {
+			lua_pop(L, 1);
+		}
+	}
+	for (int i = 1; i <= nargs; ++i) {
+		port_add_lua_ret(port, L, i);
+	}
+}
+
+/* }}} */
+
+/**
+ * The main extension provided to Lua by Tarantool/Box --
+ * ability to call INSERT/UPDATE/SELECT/DELETE from within
+ * a Lua procedure.
+ *
+ * This is a low-level API, and it expects
+ * all arguments to be packed in accordance
+ * with the binary protocol format (iproto
+ * header excluded).
+ *
+ * Signature:
+ * box.process(op_code, request)
+ */
+static int
+lbox_process(lua_State *L)
+{
+	uint32_t op = lua_tointeger(L, 1); /* Get the first arg. */
+	size_t sz;
+	const char *req = luaL_checklstring(L, 2, &sz); /* Second arg. */
+	if (op == CALL) {
+		/*
+		 * We should not be doing a CALL from within a CALL.
+		 * To invoke one stored procedure from another, one must
+		 * do it in Lua directly. This deals with
+		 * infinite recursion, stack overflow and such.
+		 */
+		return luaL_error(L, "box.process(CALL, ...) is not allowed");
+	}
+	int top = lua_gettop(L); /* to know how much is added by rw_callback */
+
+	size_t allocated_size = region_used(&fiber->gc);
+	struct port *port_lua = port_lua_create(L);
+	try {
+		struct request request;
+		request_create(&request, op, req, sz);
+		box_process(port_lua, &request);
+
+		/*
+		 * This only works as long as port_lua doesn't
+		 * use fiber->cleanup and fiber->gc.
+		 */
+		region_truncate(&fiber->gc, allocated_size);
+	} catch (const Exception &e) {
+		region_truncate(&fiber->gc, allocated_size);
+		throw;
+	}
+	return lua_gettop(L) - top;
+}
+
+static int
+lbox_raise(lua_State *L)
+{
+	if (lua_gettop(L) != 2)
+		luaL_error(L, "box.raise(): bad arguments");
+	uint32_t code = lua_tointeger(L, 1);
+	if (!code)
+		luaL_error(L, "box.raise(): unknown error code");
+	const char *str = lua_tostring(L, 2);
+	tnt_raise(ClientError, str, code);
+	return 0;
+}
+
+/**
+ * A helper to find a Lua function by name and put it
+ * on top of the stack.
+ */
+static int
+box_lua_find(lua_State *L, const char *name, const char *name_end)
+{
+	int index = LUA_GLOBALSINDEX;
+	int objstack = 0;
+	const char *start = name, *end;
+
+	while ((end = (const char *) memchr(start, '.', name_end - start))) {
+		lua_checkstack(L, 3);
+		lua_pushlstring(L, start, end - start);
+		lua_gettable(L, index);
+		if (! lua_istable(L, -1))
+			tnt_raise(ClientError, ER_NO_SUCH_PROC,
+				  name_end - name, name);
+		start = end + 1; /* next piece of a.b.c */
+		index = lua_gettop(L); /* top of the stack */
+	}
+
+	/* box.something:method */
+	if ((end = (const char *) memchr(start, ':', name_end - start))) {
+		lua_checkstack(L, 3);
+		lua_pushlstring(L, start, end - start);
+		lua_gettable(L, index);
+		if (! (lua_istable(L, -1) ||
+			lua_islightuserdata(L, -1) || lua_isuserdata(L, -1) ))
+				tnt_raise(ClientError, ER_NO_SUCH_PROC,
+					  name_end - name, name);
+		start = end + 1; /* next piece of a.b.c */
+		index = lua_gettop(L); /* top of the stack */
+		objstack = 1;
+	}
+
+
+	lua_pushlstring(L, start, name_end - start);
+	lua_gettable(L, index);
+	if (! lua_isfunction(L, -1)) {
+		/* lua_call or lua_gettable would raise a type error
+		 * for us, but our own message is more verbose. */
+		tnt_raise(ClientError, ER_NO_SUCH_PROC,
+			  name_end - name, name);
+	}
+	/* setting stack that it would contain only
+	 * the function pointer. */
+	if (index != LUA_GLOBALSINDEX) {
+		lua_replace(L, 1);
+		if (objstack)
+			lua_replace(L, 2);
+		lua_settop(L, 1 + objstack);
+	}
+	return 1 + objstack;
+}
+
+
+/**
+ * A helper to find lua stored procedures for box.call.
+ * box.call iteslf is pure Lua, to avoid issues
+ * with infinite call recursion smashing C
+ * thread stack.
+ */
+
+static int
+lbox_call_loadproc(struct lua_State *L)
+{
+	const char *name;
+	size_t name_len;
+	name = lua_tolstring(L, 1, &name_len);
+	return box_lua_find(L, name, name + name_len);
+}
+
+/**
+ * Invoke a Lua stored procedure from the binary protocol
+ * (implementation of 'CALL' command code).
+ */
+void
+box_lua_call(const struct request *request, struct txn *txn,
+	     struct port *port)
+{
+	(void) txn;
+	lua_State *L = lua_newthread(tarantool_L);
+	LuarefGuard coro_ref(tarantool_L);
+
+	/* proc name */
+	int oc = box_lua_find(L, request->c.procname,
+			 request->c.procname + request->c.procname_len);
+	/* Push the rest of args (a tuple). */
+	const char *args = request->c.args;
+	uint32_t arg_count = mp_decode_array(&args);
+	luaL_checkstack(L, arg_count, "call: out of stack");
+
+	for (uint32_t i = 0; i < arg_count; i++) {
+		luamp_decode(L, &args);
+	}
+	lbox_call(L, arg_count + oc - 1, LUA_MULTRET);
+	/* Send results of the called procedure to the client. */
+	port_add_lua_multret(port, L);
+}
+
+/**
+ * Convert box.pack() format specifier to Tarantool
+ * binary protocol UPDATE opcode
+ */
+static char format_to_opcode(char format)
+{
+	switch (format) {
+	case '=': return 0;
+	case '+': return 1;
+	case '&': return 2;
+	case '^': return 3;
+	case '|': return 4;
+	case ':': return 5;
+	case '#': return 6;
+	case '!': return 7;
+	case '-': return 8;
+	default: return format;
+	}
+}
+
+/**
+ * Counterpart to @a format_to_opcode
+ */
+static char opcode_to_format(char opcode)
+{
+	switch (opcode) {
+	case 0: return '=';
+	case 1: return '+';
+	case 2: return '&';
+	case 3: return '^';
+	case 4: return '|';
+	case 5: return ':';
+	case 6: return '#';
+	case 7: return '!';
+	case 8: return '-';
+	default: return opcode;
+	}
+}
+
+/**
+ * To use Tarantool/Box binary protocol primitives from Lua, we
+ * need a way to pack Lua variables into a binary representation.
+ * We do it by exporting a helper function
+ *
+ * box.pack(format, args...)
+ *
+ * which takes the format, which is very similar to Perl 'pack'
+ * format, and a list of arguments, and returns a binary string
+ * which has the arguments packed according to the format.
+ *
+ * For example, a typical SELECT packet packs in Lua like this:
+ *
+ * pkt = box.pack("iiiiiip", -- pack format
+ *                         0, -- space id
+ *                         0, -- index id
+ *                         0, -- offset
+ *                         2^32, -- limit
+ *                         1, -- number of SELECT arguments
+ *                         1, -- tuple cardinality
+ *                         key); -- the key to use for SELECT
+ *
+ * @sa doc/box-protocol.txt, binary protocol description
+ * @todo: implement box.unpack(format, str), for testing purposes
+ */
+static int
+lbox_pack(struct lua_State *L)
+{
+	const char *format = luaL_checkstring(L, 1);
+	/* first arg comes second */
+	int i = 2;
+	int nargs = lua_gettop(L);
+	size_t size;
+	const char *str;
+
+	RegionGuard region_guard(&fiber->gc);
+	struct tbuf *b = tbuf_new(&fiber->gc);
+
+	struct luaL_field field;
+	double dbl;
+	float flt;
+	char *data;
+	while (*format) {
+		if (i > nargs)
+			luaL_error(L, "box.pack: argument count does not match "
+				   "the format");
+		luaL_tofield(L, i, &field);
+		switch (*format) {
+		case 'B':
+		case 'b':
+			/* signed and unsigned 8-bit integers */
+			if (field.type != MP_UINT && field.type != MP_INT)
+				luaL_error(L, "box.pack: expected 8-bit int");
+
+			tbuf_append(b, (char *) &field.ival, sizeof(uint8_t));
+			break;
+		case 'S':
+		case 's':
+			/* signed and unsigned 16-bit integers */
+			if (field.type != MP_UINT && field.type != MP_INT)
+				luaL_error(L, "box.pack: expected 16-bit int");
+
+			tbuf_append(b, (char *) &field.ival, sizeof(uint16_t));
+			break;
+		case 'n':
+			/* signed and unsigned 16-bit big endian integers */
+			if (field.type != MP_UINT && field.type != MP_INT)
+				luaL_error(L, "box.pack: expected 16-bit int");
+
+			field.ival = (uint16_t) htons((uint16_t) field.ival);
+			tbuf_append(b, (char *) &field.ival, sizeof(uint16_t));
+			break;
+		case 'I':
+		case 'i':
+			/* signed and unsigned 32-bit integers */
+			if (field.type != MP_UINT && field.ival != MP_INT)
+				luaL_error(L, "box.pack: expected 32-bit int");
+
+			tbuf_append(b, (char *) &field.ival, sizeof(uint32_t));
+			break;
+		case 'N':
+			/* signed and unsigned 32-bit big endian integers */
+			if (field.type != MP_UINT && field.ival != MP_INT)
+				luaL_error(L, "box.pack: expected 32-bit int");
+
+			field.ival = htonl(field.ival);
+			tbuf_append(b, (char *) &field.ival, sizeof(uint32_t));
+			break;
+		case 'L':
+		case 'l':
+			/* signed and unsigned 64-bit integers */
+			if (field.type != MP_UINT && field.type != MP_INT)
+				luaL_error(L, "box.pack: expected 64-bit int");
+
+			tbuf_append(b, (char *) &field.ival, sizeof(uint64_t));
+			break;
+		case 'Q':
+		case 'q':
+			/* signed and unsigned 64-bit integers */
+			if (field.type != MP_UINT && field.type != MP_INT)
+				luaL_error(L, "box.pack: expected 64-bit int");
+
+			field.ival = bswap_u64(field.ival);
+			tbuf_append(b, (char *) &field.ival, sizeof(uint64_t));
+			break;
+		case 'd':
+			dbl = (double) lua_tonumber(L, i);
+			tbuf_append(b, (char *) &dbl, sizeof(double));
+			break;
+		case 'f':
+			flt = (float) lua_tonumber(L, i);
+			tbuf_append(b, (char *) &flt, sizeof(float));
+			break;
+		case 'A':
+		case 'a':
+			/* A sequence of bytes */
+			str = luaL_checklstring(L, i, &size);
+			tbuf_append(b, str, size);
+			break;
+		case 'P':
+		case 'p':
+			luamp_encode(L, b, i);
+			break;
+		case 'V':
+		{
+			int arg_count = luaL_checkint(L, i);
+			if (i + arg_count > nargs)
+				luaL_error(L, "box.pack: argument count does not match "
+					   "the format");
+			int first = i + 1;
+			int last = i + arg_count;
+			i += luamp_encodestack(L, b, first, last);
+			break;
+		}
+		case '=':
+			/* update tuple set foo = bar */
+		case '+':
+			/* set field += val */
+		case '-':
+			/* set field -= val */
+		case '&':
+			/* set field & =val */
+		case '|':
+			/* set field |= val */
+		case '^':
+			/* set field ^= val */
+		case ':':
+			/* splice */
+		case '#':
+			/* delete field */
+		case '!':
+			/* insert field */
+			/* field no */
+			tbuf_ensure(b, sizeof(uint32_t) + 1);
+			data = b->data + b->size;
+
+			data = pack_u32(data, lua_tointeger(L, i));
+			*data++ = format_to_opcode(*format);
+
+			assert(data <= b->data + b->capacity);
+			b->size = data - b->data;
+			break;
+		default:
+			luaL_error(L, "box.pack: unsupported pack "
+				   "format specifier '%c'", *format);
+		}
+		i++;
+		format++;
+	}
+
+	lua_pushlstring(L, tbuf_str(b), b->size);
+
+	return 1;
+}
+
+const char *
+box_unpack_response(struct lua_State *L, const char *s, const char *end)
+{
+	uint32_t tuple_count = pick_u32(&s, end);
+
+	/* Unpack and push tuples. */
+	while (tuple_count--) {
+		uint32_t bsize = pick_u32(&s, end);
+		const char *tend = s + bsize;
+		if (tend > end)
+			tnt_raise(IllegalParams, "incorrect packet length");
+
+		const char *t = s;
+		if (unlikely(!mp_check(&s, tend)))
+			tnt_raise(ClientError, ER_INVALID_MSGPACK);
+		if (unlikely(mp_typeof(*t) != MP_ARRAY))
+			tnt_raise(ClientError, ER_TUPLE_NOT_ARRAY);
+		struct tuple *tuple = tuple_new(tuple_format_ber, &t, tend);
+		lbox_pushtuple(L, tuple);
+	}
+	return s;
+}
+
+static int
+lbox_unpack(struct lua_State *L)
+{
+	size_t format_size = 0;
+	const char *format = luaL_checklstring(L, 1, &format_size);
+	const char *f = format;
+
+	size_t str_size = 0;
+	const char *str =  luaL_checklstring(L, 2, &str_size);
+	const char *end = str + str_size;
+	const char *s = str;
+
+	int save_stacksize = lua_gettop(L);
+
+	char charbuf;
+	uint8_t  u8buf;
+	uint16_t u16buf;
+	uint32_t u32buf;
+	double dbl;
+	float flt;
+
+#define CHECK_SIZE(cur) if (unlikely((cur) >= end)) {	                \
+	luaL_error(L, "box.unpack('%c'): got %d bytes (expected: %d+)",	\
+		   *f, (int) (end - str), (int) 1 + ((cur) - str));	\
+}
+	while (*f) {
+		switch (*f) {
+		case 'b':
+			CHECK_SIZE(s);
+			u8buf = *(uint8_t *) s;
+			lua_pushnumber(L, u8buf);
+			s++;
+			break;
+		case 's':
+			CHECK_SIZE(s + 1);
+			u16buf = *(uint16_t *) s;
+			lua_pushnumber(L, u16buf);
+			s += 2;
+			break;
+		case 'n':
+			CHECK_SIZE(s + 1);
+			u16buf = ntohs(*(uint16_t *) s);
+			lua_pushnumber(L, u16buf);
+			s += 2;
+			break;
+		case 'i':
+			CHECK_SIZE(s + 3);
+			u32buf = *(uint32_t *) s;
+			lua_pushnumber(L, u32buf);
+			s += 4;
+			break;
+		case 'N':
+			CHECK_SIZE(s + 3);
+			u32buf = ntohl(*(uint32_t *) s);
+			lua_pushnumber(L, u32buf);
+			s += 4;
+			break;
+		case 'l':
+			CHECK_SIZE(s + 7);
+			luaL_pushnumber64(L, *(uint64_t*) s);
+			s += 8;
+			break;
+		case 'q':
+			CHECK_SIZE(s + 7);
+			luaL_pushnumber64(L, bswap_u64(*(uint64_t*) s));
+			s += 8;
+			break;
+		case 'd':
+			CHECK_SIZE(s + 7);
+			dbl = *(double *) s;
+			lua_pushnumber(L, dbl);
+			s += 8;
+			break;
+		case 'f':
+			CHECK_SIZE(s + 3);
+			flt = *(float *) s;
+			lua_pushnumber(L, flt);
+			s += 4;
+			break;
+		case 'a':
+		case 'A': /* The rest of the data is a Lua string. */
+			lua_pushlstring(L, s, end - s);
+			s = end;
+			break;
+		case 'P':
+		case 'p':
+		{
+			const char *data = s;
+			if (unlikely(!mp_check(&s, end)))
+				tnt_raise(ClientError, ER_INVALID_MSGPACK);
+			luamp_decode(L, &data);
+			assert(data == s);
+			break;
+		}
+		case '=':
+			/* update tuple set foo = bar */
+		case '+':
+			/* set field += val */
+		case '-':
+			/* set field -= val */
+		case '&':
+			/* set field & =val */
+		case '|':
+			/* set field |= val */
+		case '^':
+			/* set field ^= val */
+		case ':':
+			/* splice */
+		case '#':
+			/* delete field */
+		case '!':
+			/* insert field */
+			CHECK_SIZE(s + 4);
+
+			/* field no */
+			u32buf = *(uint32_t *) s;
+
+			/* opcode */
+			charbuf = *(s + 4);
+			charbuf = opcode_to_format(charbuf);
+			if (charbuf != *f) {
+				luaL_error(L, "box.unpack('%s'): "
+					   "unexpected opcode: "
+					   "offset %d, expected '%c',"
+					   "found '%c'",
+					   format, s - str, *f, charbuf);
+			}
+
+			lua_pushnumber(L, u32buf);
+			s += 5;
+			break;
+
+		case 'R': /* Unpack server response, IPROTO format. */
+		{
+			s = box_unpack_response(L, s, end);
+			break;
+		}
+		default:
+			luaL_error(L, "box.unpack: unsupported "
+				   "format specifier '%c'", *f);
+		}
+		f++;
+	}
+
+	assert(s <= end);
+
+	if (s != end) {
+		luaL_error(L, "box.unpack('%s'): too many bytes: "
+			   "unpacked %d, total %d",
+			   format, s - str, str_size);
+	}
+
+	return lua_gettop(L) - save_stacksize;
+
+#undef CHECK_SIZE
+}
+
+static const struct luaL_reg boxlib[] = {
+	{"process", lbox_process},
+	{"call_loadproc",  lbox_call_loadproc},
+	{"raise", lbox_raise},
+	{"pack", lbox_pack},
+	{"unpack", lbox_unpack},
+	{NULL, NULL}
+};
+
+void
+box_lua_init(struct lua_State *L)
+{
+	luaL_register(L, "box", boxlib);
+	lua_pop(L, 1);
+	box_lua_tuple_init(L);
+	box_lua_index_init(L);
+	box_lua_space_init(L);
+
+	/* Load Lua extension */
+	for (const char **s = lua_sources; *s; s++) {
+		if (luaL_dostring(L, *s))
+			panic("Error loading Lua source %.160s...: %s",
+			      *s, lua_tostring(L, -1));
+	}
+
+	assert(lua_gettop(L) == 0);
+}
diff --git a/src/box/box_lua.h b/src/box/lua/call.h
similarity index 77%
rename from src/box/box_lua.h
rename to src/box/lua/call.h
index f3b428f96b..97f07b2945 100644
--- a/src/box/box_lua.h
+++ b/src/box/lua/call.h
@@ -1,5 +1,5 @@
-#ifndef INCLUDES_TARANTOOL_MOD_BOX_LUA_H
-#define INCLUDES_TARANTOOL_MOD_BOX_LUA_H
+#ifndef INCLUDES_TARANTOOL_MOD_BOX_LUA_CALL_H
+#define INCLUDES_TARANTOOL_MOD_BOX_LUA_CALL_H
 /*
  * Redistribution and use in source and binary forms, with or
  * without modification, are permitted provided that the following
@@ -28,27 +28,17 @@
  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  */
-#include "request.h"
 
-struct lua_State;
 struct txn;
-struct tuple;
+struct request;
+struct port;
 
 /**
  * Invoke a Lua stored procedure from the binary protocol
  * (implementation of 'CALL' command code).
  */
 void
-box_lua_execute(const struct request *request, struct txn *txn,
-		struct port *port);
+box_lua_call(const struct request *request, struct txn *txn,
+	     struct port *port);
 
-
-/**
- * Push tuple on lua stack
- */
-void
-lbox_pushtuple(struct lua_State *L, struct tuple *tuple);
-
-
-struct tuple *lua_istuple(struct lua_State *L, int narg);
-#endif /* INCLUDES_TARANTOOL_MOD_BOX_LUA_H */
+#endif /* INCLUDES_TARANTOOL_MOD_BOX_LUA_CALL_H */
diff --git a/src/box/box_lua_space.cc b/src/box/lua/space.cc
similarity index 84%
rename from src/box/box_lua_space.cc
rename to src/box/lua/space.cc
index 138e079d24..02549dccd1 100644
--- a/src/box/box_lua_space.cc
+++ b/src/box/lua/space.cc
@@ -26,7 +26,8 @@
  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  */
-#include "box_lua_space.h"
+#include "box/lua/space.h"
+#include "box/lua/tuple.h"
 #include "lua/utils.h"
 #include "lua/trigger.h"
 
@@ -36,13 +37,10 @@ extern "C" {
 	#include <lualib.h>
 } /* extern "C" */
 
-#include "space.h"
-#include "schema.h"
-#include <trigger.h>
-#include "salad/rlist.h"
-#include <scoped_guard.h>
-#include "box_lua.h"
-#include "txn.h"
+#include "box/space.h"
+#include "box/schema.h"
+#include "box/tuple.h"
+#include "box/txn.h"
 
 /**
  * Trigger function for all spaces
@@ -262,3 +260,38 @@ box_lua_space_delete(struct lua_State *L, uint32_t id)
 	lua_rawseti(L, -2, id);
 	lua_pop(L, 2); /* box, space */
 }
+
+
+void
+box_lua_space_init(struct lua_State *L)
+{
+	lua_getfield(L, LUA_GLOBALSINDEX, "box");
+	lua_newtable(L);
+	lua_setfield(L, -2, "schema");
+	lua_getfield(L, -1, "schema");
+	lua_pushnumber(L, SC_SCHEMA_ID);
+	lua_setfield(L, -2, "SCHEMA_ID");
+	lua_pushnumber(L, SC_SPACE_ID);
+	lua_setfield(L, -2, "SPACE_ID");
+	lua_pushnumber(L, SC_INDEX_ID);
+	lua_setfield(L, -2, "INDEX_ID");
+	lua_pushnumber(L, SC_SYSTEM_ID_MIN);
+	lua_setfield(L, -2, "SYSTEM_ID_MIN");
+	lua_pushnumber(L, SC_SYSTEM_ID_MAX);
+	lua_setfield(L, -2, "SYSTEM_ID_MAX");
+	lua_pushnumber(L, BOX_INDEX_MAX);
+	lua_setfield(L, -2, "INDEX_MAX");
+	lua_pushnumber(L, BOX_SPACE_MAX);
+	lua_setfield(L, -2, "SPACE_MAX");
+	lua_pushnumber(L, BOX_FIELD_MAX);
+	lua_setfield(L, -2, "FIELD_MAX");
+	lua_pushnumber(L, BOX_INDEX_FIELD_MAX);
+	lua_setfield(L, -2, "INDEX_FIELD_MAX");
+	lua_pushnumber(L, BOX_INDEX_PART_MAX);
+	lua_setfield(L, -2, "INDEX_PART_MAX");
+	lua_pushnumber(L, BOX_NAME_MAX);
+	lua_setfield(L, -2, "NAME_MAX");
+	lua_pushnumber(L, FORMAT_ID_MAX);
+	lua_setfield(L, -2, "FORMAT_ID_MAX");
+	lua_pop(L, 2); /* box, schema */
+}
diff --git a/src/box/box_lua_space.h b/src/box/lua/space.h
similarity index 97%
rename from src/box/box_lua_space.h
rename to src/box/lua/space.h
index bc8852dc29..8437009f26 100644
--- a/src/box/box_lua_space.h
+++ b/src/box/lua/space.h
@@ -39,4 +39,7 @@ box_lua_space_new(struct lua_State *L, struct space *space);
 void
 box_lua_space_delete(struct lua_State *L, uint32_t id);
 
+void
+box_lua_space_init(struct lua_State *L);
+
 #endif /* INCLUDES_TARANTOOL_LUA_SPACE_H */
diff --git a/src/box/request.cc b/src/box/request.cc
index 143ee3172b..942e3dce43 100644
--- a/src/box/request.cc
+++ b/src/box/request.cc
@@ -33,7 +33,7 @@
 #include "space.h"
 #include "schema.h"
 #include "port.h"
-#include "box_lua.h"
+#include "lua/call.h"
 #include <errinj.h>
 #include <pickle.h>
 #include <fiber.h>
@@ -287,7 +287,7 @@ request_create(struct request *request, uint32_t type, const char *data,
 			tnt_raise(IllegalParams, "can't unpack request");
 		break;
 	case CALL:
-		request->execute = box_lua_execute;
+		request->execute = box_lua_call;
 		request->flags |= (pick_u32(reqpos, reqend) &
 				   BOX_ALLOWED_REQUEST_FLAGS);
 		s = *reqpos;
diff --git a/src/box/schema.cc b/src/box/schema.cc
index 8eb4f3c790..5e242e2ab1 100644
--- a/src/box/schema.cc
+++ b/src/box/schema.cc
@@ -31,7 +31,7 @@
 #include "tuple.h"
 #include "assoc.h"
 #include "lua/utils.h"
-#include "box_lua_space.h"
+#include "lua/space.h"
 #include "key_def.h"
 #include "alter.h"
 #include "scoped_guard.h"
-- 
GitLab