From 098d44162b8df9d8001f99213b3ec45668203c55 Mon Sep 17 00:00:00 2001 From: Cloud Wu Date: Thu, 23 Jun 2016 16:14:16 +0800 Subject: [PATCH] bson support meta array --- lualib-src/lua-bson.c | 165 ++++++++++++++++++++++-------------------- test/testbson.lua | 95 ++++++++++++++++++++++++ 2 files changed, 183 insertions(+), 77 deletions(-) create mode 100644 test/testbson.lua diff --git a/lualib-src/lua-bson.c b/lualib-src/lua-bson.c index 2024ed69..680a4f24 100644 --- a/lualib-src/lua-bson.c +++ b/lualib-src/lua-bson.c @@ -238,8 +238,6 @@ write_double(struct bson *b, lua_Number d) { } } -static void pack_dict(lua_State *L, struct bson *b, bool array, int depth); - static inline void append_key(struct bson *bs, int type, const char *key, size_t sz) { write_byte(bs, type); @@ -269,25 +267,7 @@ append_number(struct bson *bs, lua_State *L, const char *key, size_t sz) { } } -static void -append_table(struct bson *bs, lua_State *L, const char *key, size_t sz, int depth) { - size_t len = lua_rawlen(L, -1); - bool isarray = false; - if (len > 0) { - lua_pushinteger(L, len); - if (lua_next(L,-2) == 0) { - isarray = true; - } else { - lua_pop(L,2); - } - } - if (isarray) { - append_key(bs, BSON_ARRAY, key, sz); - } else { - append_key(bs, BSON_DOCUMENT, key, sz); - } - pack_dict(L, bs, isarray, depth); -} +static void append_table(struct bson *bs, lua_State *L, const char *key, size_t sz, int depth); static void write_binary(struct bson *b, const void * buffer, size_t sz) { @@ -409,65 +389,59 @@ bson_numstr( char *str, unsigned int i ) { } static void -pack_dict_data(lua_State *L, struct bson *b, bool isarray, int depth, int kt) { - char numberkey[32]; - const char * key = NULL; - size_t sz; - if (isarray) { - if (kt != LUA_TNUMBER) { - luaL_error(L, "Invalid array key type : %s", lua_typename(L, kt)); - return; - } - sz = bson_numstr(numberkey, (unsigned int)lua_tointeger(L,-2)-1); - key = numberkey; - - append_one(b, L, key, sz, depth); - lua_pop(L,1); - } else { - switch(kt) { - case LUA_TNUMBER: - // copy key, don't change key type - lua_pushvalue(L,-2); - lua_insert(L,-2); - key = lua_tolstring(L,-2,&sz); - append_one(b, L, key, sz, depth); - lua_pop(L,2); - break; - case LUA_TSTRING: - key = lua_tolstring(L,-2,&sz); - append_one(b, L, key, sz, depth); - lua_pop(L,1); - break; - default: - luaL_error(L, "Invalid key type : %s", lua_typename(L, kt)); - return; - } - } -} - -static void -pack_simple_dict(lua_State *L, struct bson *b, bool isarray, int depth) { - if (depth > MAX_DEPTH) { - luaL_error(L, "Too depth while encoding bson"); - } - luaL_checkstack(L, 16, NULL); // reserve enough stack space to pack table +pack_array(lua_State *L, struct bson *b, int depth, size_t len) { int length = reserve_length(b); - lua_pushnil(L); - while(lua_next(L,-2) != 0) { - int kt = lua_type(L, -2); - pack_dict_data(L, b, isarray, depth, kt); + size_t i; + for (i=1;i<=len;i++) { + char numberkey[32]; + size_t sz = bson_numstr(numberkey, i - 1); + const char * key = numberkey; + lua_geti(L, -1, i); + append_one(b, L, key, sz, depth); + lua_pop(L, 1); } write_byte(b,0); write_length(b, b->size - length, length); } +static void +pack_dict_data(lua_State *L, struct bson *b, int depth, int kt) { + const char * key = NULL; + size_t sz; + switch(kt) { + case LUA_TNUMBER: + // copy key, don't change key type + lua_pushvalue(L,-2); + lua_insert(L,-2); + key = lua_tolstring(L,-2,&sz); + append_one(b, L, key, sz, depth); + lua_pop(L,2); + break; + case LUA_TSTRING: + key = lua_tolstring(L,-2,&sz); + append_one(b, L, key, sz, depth); + lua_pop(L,1); + break; + default: + luaL_error(L, "Invalid key type : %s", lua_typename(L, kt)); + return; + } +} static void -pack_meta_dict(lua_State *L, struct bson *b, bool isarray, int depth) { - if (depth > MAX_DEPTH) { - luaL_error(L, "Too depth while encoding bson"); +pack_simple_dict(lua_State *L, struct bson *b, int depth) { + int length = reserve_length(b); + lua_pushnil(L); + while(lua_next(L,-2) != 0) { + int kt = lua_type(L, -2); + pack_dict_data(L, b, depth, kt); } - luaL_checkstack(L, 16, NULL); // reserve enough stack space to pack table + write_byte(b,0); + write_length(b, b->size - length, length); +} + +static void +pack_meta_dict(lua_State *L, struct bson *b, int depth) { int length = reserve_length(b); lua_pushvalue(L, -2); // push meta_obj @@ -483,18 +457,51 @@ pack_meta_dict(lua_State *L, struct bson *b, bool isarray, int depth) { lua_pop(L, 4); // pop all k, v, next_func, obj break; } - pack_dict_data(L, b, isarray, depth, kt); + pack_dict_data(L, b, depth, kt); } write_byte(b,0); write_length(b, b->size - length, length); } +static bool +is_rawarray(lua_State *L) { + size_t len = lua_rawlen(L, -1); + if (len > 0) { + lua_pushinteger(L, len); + if (lua_next(L,-2) == 0) { + return true; + } else { + lua_pop(L,2); + } + } + return false; +} + static void -pack_dict(lua_State *L, struct bson *b, bool isarray, int depth) { - if (luaL_getmetafield(L, -1, "__pairs") != LUA_TNIL) { - pack_meta_dict(L, b, isarray, depth); +append_table(struct bson *bs, lua_State *L, const char *key, size_t sz, int depth) { + if (depth > MAX_DEPTH) { + luaL_error(L, "Too depth while encoding bson"); + } + luaL_checkstack(L, 16, NULL); // reserve enough stack space to pack table + if (luaL_getmetafield(L, -1, "__len") != LUA_TNIL) { + lua_pushvalue(L, -2); + lua_call(L, 1, 1); + if (!lua_isinteger(L, -1)) { + luaL_error(L, "__len should return integer"); + } + size_t len = lua_tointeger(L, -1); + lua_pop(L, 1); + append_key(bs, BSON_ARRAY, key, sz); + pack_array(L, bs, depth, len); + } else if (luaL_getmetafield(L, -1, "__pairs") != LUA_TNIL) { + append_key(bs, BSON_DOCUMENT, key, sz); + pack_meta_dict(L, bs, depth); + } else if (is_rawarray(L)) { + append_key(bs, BSON_ARRAY, key, sz); + pack_array(L, bs, depth, lua_rawlen(L, -1)); } else { - pack_simple_dict(L, b, isarray, depth); + append_key(bs, BSON_DOCUMENT, key, sz); + pack_simple_dict(L, bs, depth); } } @@ -895,7 +902,11 @@ lencode(lua_State *L) { bson_create(&b); lua_settop(L,1); luaL_checktype(L, 1, LUA_TTABLE); - pack_dict(L, &b, false, 0); + if (luaL_getmetafield(L, -1, "__pairs") != LUA_TNIL) { + pack_meta_dict(L, &b, 0); + } else { + pack_simple_dict(L, &b, 0); + } void * ud = lua_newuserdata(L, b.size); memcpy(ud, b.ptr, b.size); bson_destroy(&b); diff --git a/test/testbson.lua b/test/testbson.lua new file mode 100644 index 00000000..d589f01f --- /dev/null +++ b/test/testbson.lua @@ -0,0 +1,95 @@ +local bson = require "bson" + +sub = bson.encode_order( "hello", 1, "world", 2 ) + +local function tbl_next(...) + print("--- next.a", ...) + local k, v = next(...) + print("--- next.b", k, v) + return k, v +end + +local function tbl_pairs(obj) + return tbl_next, obj.__data, nil +end + +local obj_a = { + __data = { + [1] = 2, + [3] = 4, + [5] = 6, + } +} + +setmetatable( + obj_a, + { + __index = obj_a.__data, + __pairs = tbl_pairs, + } +) + +local obj_b = { + __data = { + [7] = 8, + [9] = 10, + [11] = obj_a, + } +} + +setmetatable( + obj_b, + { + __index = obj_b.__data, + __pairs = tbl_pairs, + } +) + +local metaarray = setmetatable({ n = 5 }, { + __len = function(self) return self.n end, + __index = function(self, idx) return tostring(idx) end, +}) + +b = bson.encode { + a = 1, + b = true, + c = bson.null, + d = { 1,2,3,4 }, + e = bson.binary "hello", + f = bson.regex ("*","i"), + g = bson.regex "hello", + h = bson.date (os.time()), + i = bson.timestamp(os.time()), + j = bson.objectid(), + k = { a = false, b = true }, + l = {}, + m = bson.minkey, + n = bson.maxkey, + o = sub, + p = 2^32-1, + q = obj_b, + r = metaarray, +} + +print "\n[before replace]" +t = b:decode() + +for k, v in pairs(t) do + print(k,type(v)) +end + +for k,v in ipairs(t.r) do + print(k,v) +end + +b:makeindex() +b.a = 2 +b.b = false +b.h = bson.date(os.time()) +b.i = bson.timestamp(os.time()) +b.j = bson.objectid() + +print "\n[after replace]" +t = b:decode() + +print("o.hello", bson.type(t.o.hello))