#include "luabson.h"
#include <lualib.h>
#include <lauxlib.h>
+#include <math.h>
static void lua_push_bson_value(lua_State *L, bson_iterator *it);
static void lua_push_bson_table(lua_State *L, bson_iterator *it);
bson_iterator it;
bson_iterator_from_buffer(&it, bsdata);
lua_push_bson_table(L, &it);
- return 0;
+ return 1;
}
int lua_to_bson(lua_State *L) {
+ luaL_checktype(L, lua_gettop(L), LUA_TTABLE);
bson bs;
bson_init_as_query(&bs);
lua_to_bson_impl(L, lua_gettop(L), &bs);
- if (bs.err) {
+ if (bs.err || bson_finish(&bs)) {
lua_pushstring(L, bson_first_errormsg(&bs));
+ bson_destroy(&bs);
return lua_error(L);
}
lua_pushlstring(L, bson_data(&bs), bson_size(&bs));
+ bson_destroy(&bs);
return 1;
}
-static void lua_append_bson(lua_State *L, const char *key, int vpos, bson *bs, int tref) {
+static void lua_val_to_bson(lua_State *L, const char *key, int vpos, bson *bs, int tref) {
int vtype = lua_type(L, vpos);
-
+ char nbuf[TCNUMBUFSIZ];
+ switch (vtype) {
+ case LUA_TTABLE:
+ {
+ if (vpos < 0) vpos = lua_gettop(L) + vpos + 1;
+ lua_checkstack(L, 3);
+ int bsontype_found = luaL_getmetafield(L, vpos, "__bsontype");
+ if (!bsontype_found) {
+ //check if registry tbl contains traversed tbl
+ lua_rawgeti(L, LUA_REGISTRYINDEX, tref);
+ lua_pushvalue(L, vpos);
+ lua_rawget(L, -2);
+ if (lua_toboolean(L, -1)) { //already traversed
+ lua_pop(L, 2);
+ break;
+ }
+ //setup traversed state
+ lua_pop(L, 1);
+ lua_pushvalue(L, vpos);
+ lua_pushboolean(L, 1);
+ lua_rawset(L, -3);
+ lua_pop(L, 1);
+
+ bool array = true;
+ int len = 0;
+ for (lua_pushnil(L); lua_next(L, vpos); lua_pop(L, 1)) {
+ ++len;
+ if ((lua_type(L, -2) != LUA_TNUMBER) || (lua_tointeger(L, -2) != len)) {
+ lua_pop(L, 2);
+ array = false;
+ break;
+ }
+ }
+ if (array) {
+ bson_append_start_array(bs, key);
+ for (int i = 0; i < len; i++) {
+ lua_rawgeti(L, vpos, i + 1);
+ bson_numstrn(nbuf, TCNUMBUFSIZ, (int64_t) i);
+ lua_val_to_bson(L, nbuf, -1, bs, tref);
+ lua_pop(L, 1);
+ }
+ bson_append_finish_array(bs);
+ } else {
+ for (lua_pushnil(L); lua_next(L, vpos); lua_pop(L, 1)) {
+ int ktype = lua_type(L, -2);
+ if (ktype == LUA_TNUMBER) {
+ char key[TCNUMBUFSIZ];
+ bson_numstrn(key, TCNUMBUFSIZ, (int64_t) lua_tointeger(L, -2));
+ lua_val_to_bson(L, key, -1, bs, tref);
+ } else if (ktype == LUA_TSTRING) {
+ lua_val_to_bson(L, lua_tostring(L, -2), -1, bs, tref);
+ }
+ }
+ }
+ } else {
+ int bson_type = lua_tointeger(L, -1);
+ lua_pop(L, 1); //pop metafield __bsontype
+ lua_rawgeti(L, -1, 1); //get first value
+ switch (bson_type) {
+ case BSON_OID:
+ {
+ const char* boid = lua_tostring(L, -1);
+ if (boid && strlen(boid) == 24) {
+ bson_oid_t oid;
+ bson_oid_from_string(&oid, boid);
+ bson_append_oid(bs, key, &oid);
+ }
+ break;
+ }
+ case BSON_DATE:
+ bson_append_date(bs, key, (bson_date_t) lua_tonumber(L, -1));
+ break;
+ case BSON_REGEX:
+ {
+ const char* regex = lua_tostring(L, -1);
+ lua_rawgeti(L, -2, 2); // re opts
+ const char* options = lua_tostring(L, -1);
+ lua_pop(L, 1);
+ if (regex && options) {
+ bson_append_regex(bs, key, regex, options);
+ }
+ break;
+ }
+ case BSON_BINDATA:
+ {
+ size_t len;
+ const char* cbuf = lua_tolstring(L, -1, &len);
+ bson_append_binary(bs, key, BSON_BIN_BINARY, cbuf, len);
+ break;
+ }
+ case BSON_NULL:
+ bson_append_null(bs, key);
+ break;
+ case BSON_UNDEFINED:
+ bson_append_undefined(bs, key);
+ break;
+ default:
+ break;
+ }
+ }
+ break;
+ }
+ case LUA_TNIL:
+ bson_append_null(bs, key);
+ break;
+ case LUA_TNUMBER:
+ {
+ lua_Number numval = lua_tonumber(L, vpos);
+ if (numval == floor(numval)) {
+ int64_t iv = (int64_t) numval;
+ if (-(1LL << 31) <= iv && iv <= (1LL << 31)) {
+ bson_append_int(bs, key, iv);
+ } else {
+ bson_append_long(bs, key, iv);
+ }
+ }
+ break;
+ }
+ case LUA_TBOOLEAN:
+ bson_append_bool(bs, key, lua_toboolean(L, vpos));
+ break;
+
+ case LUA_TSTRING:
+ bson_append_string(bs, key, lua_tostring(L, vpos));
+ break;
+ }
}
static void lua_to_bson_impl(lua_State *L, int tpos, bson *bs) {
- lua_newtable(L);
+ lua_newtable(L); //traverse state table
int tref = luaL_ref(L, LUA_REGISTRYINDEX);
for (lua_pushnil(L); lua_next(L, tpos); lua_pop(L, 1)) {
int ktype = lua_type(L, -2);
if (ktype == LUA_TNUMBER) {
char key[TCNUMBUFSIZ];
bson_numstrn(key, TCNUMBUFSIZ, (int64_t) lua_tonumber(L, -2));
- key[TCNUMBUFSIZ - 1] = '\0';
- lua_append_bson(L, key, -1, bs, tref);
+ lua_val_to_bson(L, key, -1, bs, tref);
} else if (ktype == LUA_TSTRING) {
- lua_append_bson(L, lua_tostring(L, -2), -1, bs, tref);
+ const char* key = lua_tostring(L, -2);
+ if (lua_type(L, -1) == LUA_TSTRING && !strcmp("_id", key)) { //root level OID as string
+ //pack OID as type table
+ lua_pushvalue(L, -1); //dup oid on stack
+ lua_push_bsontype_table(L, BSON_OID); //push type table
+ lua_rawseti(L, -2, 1); //pop oid val
+ }
+ lua_val_to_bson(L, key, -1, bs, tref);
}
}
lua_unref(L, tref);
#! /bin/sh
# Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.69 for tcejdb 1.0.65.
+# Generated by GNU Autoconf 2.69 for tcejdb 1.0.66.
#
#
# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc.
# Identity of this package.
PACKAGE_NAME='tcejdb'
PACKAGE_TARNAME='tcejdb'
-PACKAGE_VERSION='1.0.65'
-PACKAGE_STRING='tcejdb 1.0.65'
+PACKAGE_VERSION='1.0.66'
+PACKAGE_STRING='tcejdb 1.0.66'
PACKAGE_BUGREPORT=''
PACKAGE_URL=''
# Omit some internal or obsolete options to make the list less imposing.
# This message is too long to be a string in the A/UX 3.1 sh.
cat <<_ACEOF
-\`configure' configures tcejdb 1.0.65 to adapt to many kinds of systems.
+\`configure' configures tcejdb 1.0.66 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
if test -n "$ac_init_help"; then
case $ac_init_help in
- short | recursive ) echo "Configuration of tcejdb 1.0.65:";;
+ short | recursive ) echo "Configuration of tcejdb 1.0.66:";;
esac
cat <<\_ACEOF
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
-tcejdb configure 1.0.65
+tcejdb configure 1.0.66
generated by GNU Autoconf 2.69
Copyright (C) 2012 Free Software Foundation, Inc.
This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake.
-It was created by tcejdb $as_me 1.0.65, which was
+It was created by tcejdb $as_me 1.0.66, which was
generated by GNU Autoconf 2.69. Invocation command line was
$ $0 $@
# report actual input values of CONFIG_FILES etc. instead of their
# values after options handling.
ac_log="
-This file was extended by tcejdb $as_me 1.0.65, which was
+This file was extended by tcejdb $as_me 1.0.66, which was
generated by GNU Autoconf 2.69. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
ac_cs_version="\\
-tcejdb config.status 1.0.65
+tcejdb config.status 1.0.66
configured by $0, generated by GNU Autoconf 2.69,
with options \\"\$ac_cs_config\\"