Skip to content
Snippets Groups Projects
Commit c452cb5b authored by Konstantin Osipov's avatar Konstantin Osipov
Browse files

Lua: implement tuple iterators and methods.

Add tuple::pairs() and tuple:next() implementation.

Update tuple metatable __index implementation to do
method lookup.

Fix a bug in box.unpack() where we wouldn't check that
lua_tolstring() can return NULL (which happens if nil
is on the stack).

Add tests.
parent 93751d3f
No related merge requests found
...@@ -165,6 +165,7 @@ lbox_unpack(struct lua_State *L) ...@@ -165,6 +165,7 @@ lbox_unpack(struct lua_State *L)
int i = 2; /* first arg comes second */ int i = 2; /* first arg comes second */
int nargs = lua_gettop(L); int nargs = lua_gettop(L);
size_t size; size_t size;
const char *str;
u32 u32buf; u32 u32buf;
while (*format) { while (*format) {
...@@ -172,9 +173,10 @@ lbox_unpack(struct lua_State *L) ...@@ -172,9 +173,10 @@ lbox_unpack(struct lua_State *L)
luaL_error(L, "box.unpack: argument count does not match the format"); luaL_error(L, "box.unpack: argument count does not match the format");
switch (*format) { switch (*format) {
case 'i': case 'i':
u32buf = * (u32 *) lua_tolstring(L, i, &size); str = lua_tolstring(L, i, &size);
if (size != sizeof(u32)) if (str == NULL || size != sizeof(u32))
luaL_error(L, "box.unpack('%c'): got %d bytes (expected: 4)", *format, (int) size); luaL_error(L, "box.unpack('%c'): got %d bytes (expected: 4)", *format, (int) size);
u32buf = * (u32 *) str;
lua_pushnumber(L, u32buf); lua_pushnumber(L, u32buf);
break; break;
default: default:
......
...@@ -104,17 +104,31 @@ lbox_tuple_len(struct lua_State *L) ...@@ -104,17 +104,31 @@ lbox_tuple_len(struct lua_State *L)
return 1; 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 static int
lbox_tuple_index(struct lua_State *L) lbox_tuple_index(struct lua_State *L)
{ {
struct box_tuple *tuple = lua_checktuple(L, 1); struct box_tuple *tuple = lua_checktuple(L, 1);
int i = luaL_checkint(L, 2); /* For integer indexes, implement [] operator */
if (i >= tuple->cardinality) if (lua_isnumber(L, 2)) {
luaL_error(L, "%s: index %d is out of bounds (0..%d)", int i = luaL_checkint(L, 2);
tuplelib_name, i, tuple->cardinality-1); if (i >= tuple->cardinality)
void *field = tuple_field(tuple, i); luaL_error(L, "%s: index %d is out of bounds (0..%d)",
u32 len = load_varint32(&field); tuplelib_name, i, tuple->cardinality-1);
lua_pushlstring(L, field, len); void *field = tuple_field(tuple, i);
u32 len = load_varint32(&field);
lua_pushlstring(L, field, len);
return 1;
}
/* If we got a string, try to find a method for it. */
lua_getmetatable(L, 1);
lua_getfield(L, -1, lua_tostring(L, 2));
return 1; return 1;
} }
...@@ -143,18 +157,62 @@ lbox_pushtuple(struct lua_State *L, struct box_tuple *tuple) ...@@ -143,18 +157,62 @@ lbox_pushtuple(struct lua_State *L, struct box_tuple *tuple)
} }
} }
/**
* 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 box_tuple *tuple = lua_checktuple(L, 1);
int argc = lua_gettop(L) - 1;
u8 *field;
size_t len;
if (argc == 0 || (argc == 1 && lua_type(L, 2) == LUA_TNIL))
field = tuple->data;
else if (argc == 1 && lua_islightuserdata(L, 2))
field = lua_touserdata(L, 2);
else
luaL_error(L, "tuple.next(): bad arguments");
assert(field >= tuple->data);
if (field < tuple->data + tuple->bsize) {
len = load_varint32((void **) &field);
lua_pushlightuserdata(L, field + len);
lua_pushlstring(L, (char *) field, len);
return 2;
}
lua_pushnil(L);
return 1;
}
/** 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;
}
static const struct luaL_reg lbox_tuple_meta [] = { static const struct luaL_reg lbox_tuple_meta [] = {
{"__gc", lbox_tuple_gc}, {"__gc", lbox_tuple_gc},
{"__len", lbox_tuple_len}, {"__len", lbox_tuple_len},
{"__index", lbox_tuple_index}, {"__index", lbox_tuple_index},
{"__tostring", lbox_tuple_tostring}, {"__tostring", lbox_tuple_tostring},
{"next", lbox_tuple_next},
{"pairs", lbox_tuple_pairs},
{NULL, NULL} {NULL, NULL}
}; };
/* }}} */ /* }}} */
/** /** {{{ box.index Lua library: access to spaces and indexes
* {{{ Lua box.index library: access to spaces and indexes
*/ */
static const char *indexlib_name = "box.index"; static const char *indexlib_name = "box.index";
...@@ -573,27 +631,37 @@ void box_lua_call(struct box_txn *txn __attribute__((unused)), ...@@ -573,27 +631,37 @@ void box_lua_call(struct box_txn *txn __attribute__((unused)),
} }
} }
/** A helper to register a single type metatable. */
static void
lua_register_type(struct lua_State *L, const char *type_name,
const struct luaL_reg *methods)
{
luaL_newmetatable(L, type_name);
/*
* Conventionally, make the metatable point to itself
* in __index. If 'methods' contain a field for __index,
* this is a no-op.
*/
lua_pushvalue(L, -1);
lua_setfield(L, -2, "__index");
lua_pushstring(L, type_name);
lua_setfield(L, -2, "__metatable");
luaL_register(L, NULL, methods);
lua_pop(L, 1);
}
struct lua_State * struct lua_State *
mod_lua_init(struct lua_State *L) mod_lua_init(struct lua_State *L)
{ {
lua_atpanic(L, box_lua_panic); lua_atpanic(L, box_lua_panic);
/* box, box.tuple */ /* box, box.tuple */
lua_register_type(L, tuplelib_name, lbox_tuple_meta);
luaL_register(L, "box", boxlib); luaL_register(L, "box", boxlib);
luaL_newmetatable(L, tuplelib_name); lua_pop(L, 1);
lua_pushstring(L, tuplelib_name);
lua_setfield(L, -2, "__metatable");
luaL_register(L, NULL, lbox_tuple_meta);
lua_pop(L, 2);
/* box.index */ /* box.index */
luaL_newmetatable(L, indexlib_name); lua_register_type(L, indexlib_name, lbox_index_meta);
/* Make the metatable point to itself in __index. */
lua_pushvalue(L, -1);
lua_setfield(L, -2, "__index");
lua_pushstring(L, indexlib_name);
lua_setfield(L, -2, "__metatable");
luaL_register(L, NULL, lbox_index_meta);
luaL_register(L, "box.index", indexlib); luaL_register(L, "box.index", indexlib);
lua_pop(L, 2); lua_pop(L, 1);
/* Load box.lua */ /* Load box.lua */
if (luaL_dostring(L, &_binary_box_lua_start)) if (luaL_dostring(L, &_binary_box_lua_start))
panic("Error loading box.lua: %s", lua_tostring(L, -1)); panic("Error loading box.lua: %s", lua_tostring(L, -1));
......
No preview for this file type
...@@ -130,3 +130,15 @@ exec admin "lua #box.index.new(0,0)" ...@@ -130,3 +130,15 @@ exec admin "lua #box.index.new(0,0)"
exec admin "lua box.space[0]:insert('test', 'hello world')" exec admin "lua box.space[0]:insert('test', 'hello world')"
exec admin "lua box.space[0]:update('test', '=p', 1, 'bye, world')" exec admin "lua box.space[0]:update('test', '=p', 1, 'bye, world')"
exec admin "lua box.space[0]:delete('test')" exec admin "lua box.space[0]:delete('test')"
# test tuple iterators
exec admin "lua t=box.space[0]:insert('test')"
exec admin "lua t:next('abcd')"
exec admin "lua t:next(1)"
exec admin "lua t:next(t)"
exec admin "lua t:next(t:next())"
exec admin "lua for k, v in t:pairs() do print(v) end"
exec admin "lua t=box.space[0]:replace('test', 'another field')"
exec admin "lua for k, v in t:pairs() do print(v) end"
exec admin "lua t=box.space[0]:replace('test', 'another field', 'one more')"
exec admin "lua for k, v in t:pairs() do print(v) end"
exec admin "lua box.space[0]:truncate()"
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment