diff --git a/src/box/lua/index.cc b/src/box/lua/index.cc index bb4d155dbbf0103a0c4e7eeb21bc72831307b318..aef6f2e2abb06256dc70de3ff336a614a640d8f1 100644 --- a/src/box/lua/index.cc +++ b/src/box/lua/index.cc @@ -42,6 +42,8 @@ /** {{{ box.index Lua library: access to spaces and indexes */ +static int CTID_STRUCT_ITERATOR_REF = 0; + static inline Index * check_index(uint32_t space_id, uint32_t index_id) { @@ -307,6 +309,30 @@ boxffi_index_iterator(uint32_t space_id, uint32_t index_id, int type, } } +static int +lbox_index_iterator(lua_State *L) +{ + if (lua_gettop(L) != 4 || !lua_isnumber(L, 1) || !lua_isnumber(L, 2) || + !lua_isnumber(L, 3)) + return luaL_error(L, "usage index.iterator(space_id, index_id, type, key)"); + + uint32_t space_id = lua_tointeger(L, 1); + uint32_t index_id = lua_tointeger(L, 2); + uint32_t iterator = lua_tointeger(L, 3); + /* const char *key = lbox_tokey(L, 4); */ + const char *mpkey = lua_tolstring(L, 4, NULL); /* Key encoded by Lua */ + struct iterator *it = boxffi_index_iterator(space_id, index_id, + iterator, mpkey); + if (it == NULL) + return lbox_error(L); + + assert(CTID_STRUCT_ITERATOR_REF != 0); + struct iterator **ptr = (struct iterator **) luaL_pushcdata(L, + CTID_STRUCT_ITERATOR_REF, sizeof(struct iterator *)); + *ptr = it; /* NULL handled by Lua, gc also set by Lua */ + return 1; +} + struct tuple* boxffi_iterator_next(struct iterator *itr) { @@ -333,11 +359,36 @@ boxffi_iterator_next(struct iterator *itr) } } +static int +lbox_iterator_next(lua_State *L) +{ + /* first argument is key buffer */ + if (lua_gettop(L) < 1 || lua_type(L, 1) != LUA_TCDATA) + return luaL_error(L, "usage: next(state)"); + + assert(CTID_STRUCT_ITERATOR_REF != 0); + uint32_t ctypeid; + void *data = luaL_checkcdata(L, 1, &ctypeid); + if (ctypeid != CTID_STRUCT_ITERATOR_REF) + return luaL_error(L, "usage: next(state)"); + + struct iterator *itr = *(struct iterator **) data; + struct tuple *tuple = boxffi_iterator_next(itr); + return lbox_returntuple(L, tuple); +} + /* }}} */ void box_lua_index_init(struct lua_State *L) { + /* Get CTypeIDs */ + int rc = luaL_cdef(L, "struct iterator;"); + assert(rc == 0); + (void) rc; + CTID_STRUCT_ITERATOR_REF = luaL_ctypeid(L, "struct iterator&"); + assert(CTID_STRUCT_ITERATOR_REF != 0); + static const struct luaL_reg indexlib [] = { {NULL, NULL} }; @@ -353,6 +404,8 @@ box_lua_index_init(struct lua_State *L) {"min", lbox_index_min}, {"max", lbox_index_max}, {"count", lbox_index_count}, + {"iterator", lbox_index_iterator}, + {"iterator_next", lbox_iterator_next}, {NULL, NULL} }; diff --git a/src/box/lua/schema.lua b/src/box/lua/schema.lua index 415e9928c3e3dc7a1fbed088a744104ad94a7975..fa392735e8142ba2d9934a0566d6cc8aa551fb82 100644 --- a/src/box/lua/schema.lua +++ b/src/box/lua/schema.lua @@ -1,6 +1,7 @@ -- schema.lua (internal file) -- local ffi = require('ffi') +local msgpack = require('msgpack') local msgpackffi = require('msgpackffi') local fun = require('fun') local session = box.session @@ -524,7 +525,7 @@ local iterator_gen = function(param, state) information. --]] if not ffi.istype(iterator_t, state) then - error('usage gen(param, state)') + error('usage: next(param, state)') end -- next() modifies state in-place local tuple = builtin.boxffi_iterator_next(state) @@ -537,6 +538,15 @@ local iterator_gen = function(param, state) end end +local iterator_gen_luac = function(param, state) + local tuple = internal.iterator_next(state) + if tuple ~= nil then + return state, tuple -- new state, value + else + return nil + end +end + local iterator_cdata_gc = function(iterator) return iterator.free(iterator) end @@ -650,7 +660,7 @@ function box.schema.space.bless(space) return internal.random(index.space_id, index.id, rnd); end -- iteration - index_mt.pairs = function(index, key, opts) + index_mt.pairs_ffi = function(index, key, opts) local pkey, pkey_end = msgpackffi.encode_tuple(key) local itype = check_iterator_type(opts, pkey + 1 >= pkey_end); @@ -660,11 +670,18 @@ function box.schema.space.bless(space) if cdata == nil then box.error() end - return fun.wrap(iterator_gen, keybuf, ffi.gc(cdata, iterator_cdata_gc)) end - index_mt.__pairs = index_mt.pairs -- Lua 5.2 compatibility - index_mt.__ipairs = index_mt.pairs -- Lua 5.2 compatibility + index_mt.pairs_luac = function(index, key, opts) + key = keify(key) + local itype = check_iterator_type(opts, #key == 0); + local keymp = msgpack.encode(key) + local keybuf = ffi.string(keymp, #keymp) + local cdata = internal.iterator(index.space_id, index.id, itype, keymp); + return fun.wrap(iterator_gen_luac, keybuf, + ffi.gc(cdata, iterator_cdata_gc)) + end + -- index subtree size index_mt.count_ffi = function(index, key, opts) local pkey, pkey_end = msgpackffi.encode_tuple(key) @@ -764,7 +781,7 @@ function box.schema.space.bless(space) -- true if reading operations may yield local read_yields = space.engine == 'sophia' - local read_ops = {'select', 'get', 'min', 'max', 'count', 'random'} + local read_ops = {'select', 'get', 'min', 'max', 'count', 'random', 'pairs'} for _, op in ipairs(read_ops) do if read_yields then -- use Lua/C implmenetation @@ -774,6 +791,8 @@ function box.schema.space.bless(space) index_mt[op] = index_mt[op .. "_ffi"] end end + index_mt.__pairs = index_mt.pairs -- Lua 5.2 compatibility + index_mt.__ipairs = index_mt.pairs -- Lua 5.2 compatibility -- local space_mt = {} space_mt.len = function(space) diff --git a/src/lua/utils.cc b/src/lua/utils.cc index fa62f3901be48d6fe14501b760ed2dfef73863e0..93cf32ce9845cc9b52c59851d96442c5d9950847 100644 --- a/src/lua/utils.cc +++ b/src/lua/utils.cc @@ -96,6 +96,23 @@ luaL_ctypeid(struct lua_State *L, const char *ctypename) return ctypeid; } +int +luaL_cdef(struct lua_State *L, const char *what) +{ + int idx = lua_gettop(L); + /* This function calls ffi.cdef */ + + /* Get ffi.typeof function */ + luaL_loadstring(L, "return require('ffi').cdef"); + lua_call(L, 0, 1); + /* FFI must exist */ + assert(lua_gettop(L) == idx + 1 && lua_isfunction(L, idx + 1)); + /* Push the argument to ffi.cdef */ + lua_pushstring(L, what); + /* Call ffi.cdef() */ + return lua_pcall(L, 1, 0, 0); +} + int luaL_setcdatagc(struct lua_State *L, int idx) { diff --git a/src/lua/utils.h b/src/lua/utils.h index f1d346695eb1e4f2e3ae449f4cb755ae8bee1bda..3e5cc54eadd966704035a4ab0047826da772faf7 100644 --- a/src/lua/utils.h +++ b/src/lua/utils.h @@ -101,6 +101,17 @@ luaL_setcdatagc(struct lua_State *L, int idx); LUA_API uint32_t luaL_ctypeid(struct lua_State *L, const char *ctypename); +/** +* @brief Declare symbols for FFI +* @param L Lua State +* @param what C definitions +* @sa ffi.cdef(def) +* @retval 0 on success +* @retval LUA_ERRRUN, LUA_ERRMEM, LUA_ERRERR otherwise +*/ +LUA_API int +luaL_cdef(struct lua_State *L, const char *ctypename); + /** \endcond public */ static inline lua_Integer