#define LUA_LIB #include #include #include #include #include #include #include "lua.h" #include "lauxlib.h" #include "libretrodb.h" #include "lua_common.h" struct libretrodb { RFILE *fd; uint64_t root; uint64_t count; uint64_t first_index_offset; char path[1024]; }; static void push_rmsgpack_value(lua_State *L, struct rmsgpack_dom_value *value) { uint32_t i; switch(value->type) { case RDT_INT: lua_pushnumber(L, value->val.int_); break; case RDT_UINT: lua_pushnumber(L, value->val.uint_); break; case RDT_BINARY: lua_pushlstring(L, value->val.binary.buff, value->val.binary.len); break; case RDT_BOOL: lua_pushboolean(L, value->val.bool_); break; case RDT_NULL: lua_pushnil(L); break; case RDT_STRING: lua_pushlstring(L, value->val.string.buff, value->val.binary.len); break; case RDT_MAP: lua_createtable(L, 0, value->val.map.len); for (i = 0; i < value->val.map.len; i++) { push_rmsgpack_value(L, &value->val.map.items[i].key); push_rmsgpack_value(L, &value->val.map.items[i].value); lua_settable(L, -3); } break; case RDT_ARRAY: lua_createtable(L, value->val.array.len, 0); for (i = 0; i < value->val.array.len; i++) { lua_pushnumber(L, i + 1); push_rmsgpack_value(L, &value->val.array.items[i]); lua_settable(L, -3); } break; } } static int value_provider(void *ctx, struct rmsgpack_dom_value *out) { int rv = 0; lua_State *L = ctx; lua_getfield(L, LUA_REGISTRYINDEX, "testlib_get_value"); if (lua_pcall(L, 0, 1, 0) != 0) { printf( "error running function `get_value': %s\n", lua_tostring(L, -1) ); } if (lua_isnil(L, -1)) rv = 1; else if (lua_istable(L, -1)) rv = libretrodb_lua_to_rmsgpack_value(L, -1, out); else printf("function `get_value' must return a table or nil\n"); lua_pop(L, 1); return rv; } static int create_db(lua_State *L) { RFILE *dst; const char *db_file = luaL_checkstring(L, -2); if (!lua_isfunction(L, -1)) { lua_pushstring(L, "second argument must be a function"); lua_error(L); } lua_setfield(L, LUA_REGISTRYINDEX, "testlib_get_value"); dst = filestream_open(db_file, RFILE_MODE_WRITE, -1); if (!dst) { lua_pushstring(L, "Could not open destination file"); lua_error(L); } libretrodb_create(dst, &value_provider, L); filestream_close(dst); return 0; } static int db_new(lua_State *L) { libretrodb_t *db = NULL; const char *db_file = NULL; int rv; db_file = luaL_checkstring(L, -1); db = lua_newuserdata(L, sizeof(libretrodb_t)); if ((rv = libretrodb_open(db_file, db)) == 0) { luaL_getmetatable(L, "RarchDB.DB"); lua_setmetatable(L, -2); lua_pushnil(L); } else { lua_pop(L, 1); lua_pushnil(L); lua_pushstring(L, strerror(-rv)); } return 2; } static libretrodb_t *checkdb(lua_State *L) { void *ud = luaL_checkudata(L, 1, "RarchDB.DB"); luaL_argcheck(L, ud != NULL, 1, "`RarchDB.DB' expected"); return ud; } static int db_close(lua_State *L) { libretrodb_t *db = checkdb(L); libretrodb_close(db); return 0; } static int db_cursor_open(lua_State *L) { int rv; libretrodb_cursor_t *cursor = NULL; libretrodb_t *db = checkdb(L); cursor = lua_newuserdata(L, sizeof(libretrodb_t)); if ((rv = libretrodb_cursor_open(db, cursor, NULL)) == 0) { luaL_getmetatable(L, "RarchDB.Cursor"); lua_setmetatable(L, -2); lua_pushnil(L); } else { lua_pop(L, 1); lua_pushnil(L); lua_pushstring(L, strerror(-rv)); } return 2; } static int db_query(lua_State *L) { libretrodb_t *db = checkdb(L); const char *query = luaL_checkstring(L, -1); const char *error = NULL; libretrodb_query_t *q = libretrodb_query_compile( db, query, strlen(query), &error); if (error) { lua_pushnil(L); lua_pushstring(L, error); } else { int rv; libretrodb_cursor_t *cursor = lua_newuserdata(L, sizeof(libretrodb_t)); if ((rv = libretrodb_cursor_open(db, cursor, q)) == 0) { luaL_getmetatable(L, "RarchDB.Cursor"); lua_setmetatable(L, -2); lua_pushnil(L); } else { lua_pop(L, 1); lua_pushnil(L); lua_pushstring(L, strerror(-rv)); } libretrodb_query_free(q); } return 2; } static libretrodb_cursor_t *checkcursor(lua_State *L) { void *ud = luaL_checkudata(L, 1, "RarchDB.Cursor"); luaL_argcheck(L, ud != NULL, 1, "`RarchDB.Cursor' expected"); return ud; } static int cursor_close(lua_State *L) { libretrodb_cursor_t *cursor = checkcursor(L); libretrodb_cursor_close(cursor); return 0; } static int cursor_read(lua_State *L) { libretrodb_cursor_t *cursor = checkcursor(L); struct rmsgpack_dom_value value; if (libretrodb_cursor_read_item(cursor, &value) == 0) push_rmsgpack_value(L, &value); else lua_pushnil(L); return 1; } static int cursor_iter(lua_State *L) { libretrodb_cursor_t *cursor = checkcursor(L); (void)cursor; luaL_getmetafield(L, -1, "read"); lua_pushvalue(L, -2); return 2; } static const luaL_Reg testlib[] = { {"create_db", create_db}, {"RarchDB", db_new}, {NULL, NULL} }; static const struct luaL_Reg cursor_mt [] = { {"__gc", cursor_close}, {"read", cursor_read}, {"iter", cursor_iter}, {NULL, NULL} }; static const struct luaL_Reg libretrodb_mt [] = { {"__gc", db_close}, {"list_all", db_cursor_open}, {"query", db_query}, {NULL, NULL} }; LUALIB_API int luaopen_testlib(lua_State *L) { luaL_newmetatable(L, "RarchDB.DB"); lua_pushstring(L, "__index"); lua_pushvalue(L, -2); lua_settable(L, -3); luaL_openlib(L, NULL, libretrodb_mt, 0); luaL_newmetatable(L, "RarchDB.Cursor"); lua_pushstring(L, "__index"); lua_pushvalue(L, -2); lua_settable(L, -3); luaL_openlib(L, NULL, cursor_mt, 0); luaL_register(L, "testlib", testlib); return 1; }