Skip to content
Permalink
master
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Go to file
 
 
Cannot retrieve contributors at this time
#define LUA_LIB
#include <lua.h>
#include <lauxlib.h>
#include <time.h>
#include <unistd.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#if defined(_WIN32) || defined(_WIN64)
#include <winsock2.h>
static void
init_winsock() {
WSADATA wsaData;
WSAStartup(MAKEWORD(2,2), &wsaData);
}
#else
static void
init_winsock() {
}
#endif
#define DEFAULT_CAP 64
#define MAX_NUMBER 1024
// avoid circular reference while encoding
#define MAX_DEPTH 128
#define BSON_REAL 1
#define BSON_STRING 2
#define BSON_DOCUMENT 3
#define BSON_ARRAY 4
#define BSON_BINARY 5
#define BSON_UNDEFINED 6
#define BSON_OBJECTID 7
#define BSON_BOOLEAN 8
#define BSON_DATE 9
#define BSON_NULL 10
#define BSON_REGEX 11
#define BSON_DBPOINTER 12
#define BSON_JSCODE 13
#define BSON_SYMBOL 14
#define BSON_CODEWS 15
#define BSON_INT32 16
#define BSON_TIMESTAMP 17
#define BSON_INT64 18
#define BSON_MINKEY 255
#define BSON_MAXKEY 127
#define BSON_TYPE_SHIFT 5
static char bson_numstrs[MAX_NUMBER][4];
static int bson_numstr_len[MAX_NUMBER];
struct bson {
int size;
int cap;
uint8_t *ptr;
uint8_t buffer[DEFAULT_CAP];
};
struct bson_reader {
const uint8_t * ptr;
int size;
};
static inline int32_t
get_length(const uint8_t * data) {
const uint8_t * b = (const uint8_t *)data;
int32_t len = b[0] | b[1]<<8 | b[2]<<16 | b[3]<<24;
return len;
}
static inline void
bson_destroy(struct bson *b) {
if (b->ptr != b->buffer) {
free(b->ptr);
}
}
static inline void
bson_create(struct bson *b) {
b->size = 0;
b->cap = DEFAULT_CAP;
b->ptr = b->buffer;
}
static inline void
bson_reserve(struct bson *b, int sz) {
if (b->size + sz <= b->cap)
return;
do {
b->cap *= 2;
} while (b->cap <= b->size + sz);
if (b->ptr == b->buffer) {
b->ptr = malloc(b->cap);
memcpy(b->ptr, b->buffer, b->size);
} else {
b->ptr = realloc(b->ptr, b->cap);
}
}
static inline void
check_reader(lua_State *L, struct bson_reader *br, int sz) {
if (br->size < sz) {
luaL_error(L, "Invalid bson block (%d:%d)", br->size, sz);
}
}
static inline int
read_byte(lua_State *L, struct bson_reader *br) {
check_reader(L, br, 1);
const uint8_t * b = br->ptr;
int r = b[0];
++br->ptr;
--br->size;
return r;
}
static inline int32_t
read_int32(lua_State *L, struct bson_reader *br) {
check_reader(L, br, 4);
const uint8_t * b = br->ptr;
uint32_t v = b[0] | b[1]<<8 | b[2]<<16 | b[3]<<24;
br->ptr+=4;
br->size-=4;
return (int32_t)v;
}
static inline int64_t
read_int64(lua_State *L, struct bson_reader *br) {
check_reader(L, br, 8);
const uint8_t * b = br->ptr;
uint32_t lo = b[0] | b[1]<<8 | b[2]<<16 | b[3]<<24;
uint32_t hi = b[4] | b[5]<<8 | b[6]<<16 | b[7]<<24;
uint64_t v = (uint64_t)lo | (uint64_t)hi<<32;
br->ptr+=8;
br->size-=8;
return (int64_t)v;
}
static inline lua_Number
read_double(lua_State *L, struct bson_reader *br) {
check_reader(L, br, 8);
union {
uint64_t i;
double d;
} v;
const uint8_t * b = br->ptr;
uint32_t lo = b[0] | b[1]<<8 | b[2]<<16 | b[3]<<24;
uint32_t hi = b[4] | b[5]<<8 | b[6]<<16 | b[7]<<24;
v.i = (uint64_t)lo | (uint64_t)hi<<32;
br->ptr+=8;
br->size-=8;
return v.d;
}
static inline const void *
read_bytes(lua_State *L, struct bson_reader *br, int sz) {
const void * r = br->ptr;
check_reader(L, br, sz);
br->ptr+=sz;
br->size-=sz;
return r;
}
static inline const char *
read_cstring(lua_State *L, struct bson_reader *br, size_t *sz) {
int i;
for (i=0;;i++) {
if (i==br->size) {
luaL_error(L, "Invalid bson block : cstring");
}
if (br->ptr[i] == '\0') {
break;
}
}
*sz = i;
const char * r = (const char *)br->ptr;
br->ptr += i+1;
br->size -= i+1;
return r;
}
static inline void
write_byte(struct bson *b, uint8_t v) {
bson_reserve(b,1);
b->ptr[b->size++] = v;
}
static inline void
write_int32(struct bson *b, int32_t v) {
uint32_t uv = (uint32_t)v;
bson_reserve(b,4);
b->ptr[b->size++] = uv & 0xff;
b->ptr[b->size++] = (uv >> 8)&0xff;
b->ptr[b->size++] = (uv >> 16)&0xff;
b->ptr[b->size++] = (uv >> 24)&0xff;
}
static inline void
write_length(struct bson *b, int32_t v, int off) {
uint32_t uv = (uint32_t)v;
b->ptr[off++] = uv & 0xff;
b->ptr[off++] = (uv >> 8)&0xff;
b->ptr[off++] = (uv >> 16)&0xff;
b->ptr[off++] = (uv >> 24)&0xff;
}
static void
write_string(struct bson *b, const char *key, size_t sz) {
bson_reserve(b,sz+1);
memcpy(b->ptr + b->size, key, sz);
b->ptr[b->size+sz] = '\0';
b->size+=sz+1;
}
static inline int
reserve_length(struct bson *b) {
int sz = b->size;
bson_reserve(b,4);
b->size +=4;
return sz;
}
static inline void
write_int64(struct bson *b, int64_t v) {
uint64_t uv = (uint64_t)v;
int i;
bson_reserve(b,8);
for (i=0;i<64;i+=8) {
b->ptr[b->size++] = (uv>>i) & 0xff;
}
}
static inline void
write_double(struct bson *b, lua_Number d) {
union {
double d;
uint64_t i;
} v;
v.d = d;
int i;
bson_reserve(b,8);
for (i=0;i<64;i+=8) {
b->ptr[b->size++] = (v.i>>i) & 0xff;
}
}
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);
write_string(bs, key, sz);
}
#if LUA_VERSION_NUM >= 503
static inline int
is_32bit(int64_t v) {
return v >= INT32_MIN && v <= INT32_MAX;
}
static void
append_number(struct bson *bs, lua_State *L, const char *key, size_t sz) {
if (lua_isinteger(L, -1)) {
int64_t i = lua_tointeger(L, -1);
if (is_32bit(i)) {
append_key(bs, BSON_INT32, key, sz);
write_int32(bs, i);
} else {
append_key(bs, BSON_INT64, key, sz);
write_int64(bs, i);
}
} else {
lua_Number d = lua_tonumber(L,-1);
append_key(bs, BSON_REAL, key, sz);
write_double(bs, d);
}
}
#else
static void
append_number(struct bson *bs, lua_State *L, const char *key, size_t sz) {
int64_t i = lua_tointeger(L, -1);
lua_Number d = lua_tonumber(L,-1);
if (i != d) {
append_key(bs, BSON_REAL, key, sz);
write_double(bs, d);
} else {
if (is_32bit(i)) {
append_key(bs, BSON_INT32, key, sz);
write_int32(bs, i);
} else {
append_key(bs, BSON_INT64, key, sz);
write_int64(bs, i);
}
}
}
#endif
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
write_binary(struct bson *b, const void * buffer, size_t sz) {
int length = reserve_length(b);
bson_reserve(b,sz);
memcpy(b->ptr + b->size, buffer, sz); // include sub type
b->size+=sz;
write_length(b, sz-1, length); // not include sub type
}
static void
append_one(struct bson *bs, lua_State *L, const char *key, size_t sz, int depth) {
int vt = lua_type(L,-1);
switch(vt) {
case LUA_TNUMBER:
append_number(bs, L, key, sz);
break;
case LUA_TUSERDATA: {
append_key(bs, BSON_DOCUMENT, key, sz);
int32_t * doc = lua_touserdata(L,-1);
int32_t sz = *doc;
bson_reserve(bs,sz);
memcpy(bs->ptr + bs->size, doc, sz);
bs->size += sz;
break;
}
case LUA_TSTRING: {
size_t len;
const char * str = lua_tolstring(L,-1,&len);
if (len > 1 && str[0]==0) {
int subt = (uint8_t)str[1];
append_key(bs, subt, key, sz);
switch(subt) {
case BSON_BINARY:
write_binary(bs, str+2, len-2);
break;
case BSON_OBJECTID:
if (len != 2+12) {
luaL_error(L, "Invalid object id %s", str+2);
}
// go though
case BSON_JSCODE:
case BSON_DBPOINTER:
case BSON_SYMBOL:
case BSON_CODEWS:
bson_reserve(bs,len-2);
memcpy(bs->ptr + bs->size, str+2, len-2);
bs->size += len-2;
break;
case BSON_DATE: {
if (len != 2+4) {
luaL_error(L, "Invalid date");
}
const uint32_t * ts = (const uint32_t *)(str + 2);
int64_t v = (int64_t)*ts * 1000;
write_int64(bs, v);
break;
}
case BSON_TIMESTAMP: {
if (len != 2+8) {
luaL_error(L, "Invalid timestamp");
}
const uint32_t * inc = (const uint32_t *)(str + 2);
const uint32_t * ts = (const uint32_t *)(str + 6);
write_int32(bs, *inc);
write_int32(bs, *ts);
break;
}
case BSON_REGEX: {
str+=2;
len-=3;
size_t i;
for (i=0;i<len;i++) {
if (str[len-i-1]==0) {
break;
}
}
write_string(bs, str, len-i-1);
write_string(bs, str + len-i, i);
break;
}
case BSON_MINKEY:
case BSON_MAXKEY:
case BSON_NULL:
break;
default:
luaL_error(L,"Invalid subtype %d", subt);
}
} else {
size_t len;
const char * str = lua_tolstring(L,-1,&len);
append_key(bs, BSON_STRING, key, sz);
int off = reserve_length(bs);
write_string(bs, str, len);
write_length(bs, len+1, off);
}
break;
}
case LUA_TTABLE:
append_table(bs, L, key, sz, depth + 1);
break;
case LUA_TBOOLEAN:
append_key(bs, BSON_BOOLEAN, key, sz);
write_byte(bs, lua_toboolean(L,-1));
break;
default:
luaL_error(L, "Invalid value type : %s", lua_typename(L,vt));
}
}
static inline int
bson_numstr( char *str, unsigned int i ) {
if ( i < MAX_NUMBER) {
memcpy( str, bson_numstrs[i], 4 );
return bson_numstr_len[i];
} else {
return sprintf( str,"%u", 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
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);
}
write_byte(b,0);
write_length(b, b->size - length, length);
}
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");
}
luaL_checkstack(L, 16, NULL); // reserve enough stack space to pack table
int length = reserve_length(b);
lua_pushvalue(L, -2); // push meta_obj
lua_call(L, 1, 3); // call __pairs_func => next_func, t_data, first_k
for(;;) {
lua_pushvalue(L, -2); // copy data
lua_pushvalue(L, -2); // copy k
lua_copy(L, -5, -3); // copy next_func replace old_k
lua_call(L, 2, 2); // call next_func
int kt = lua_type(L, -2);
if (kt == LUA_TNIL) {
lua_pop(L, 4); // pop all k, v, next_func, obj
break;
}
pack_dict_data(L, b, isarray, depth, kt);
}
write_byte(b,0);
write_length(b, b->size - length, length);
}
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);
} else {
pack_simple_dict(L, b, isarray, depth);
}
}
static void
pack_ordered_dict(lua_State *L, struct bson *b, int n, int depth) {
int length = reserve_length(b);
int i;
for (i=0;i<n;i+=2) {
size_t sz;
const char * key = lua_tolstring(L, i+1, &sz);
if (key == NULL) {
luaL_error(L, "Argument %d need a string", i+1);
}
lua_pushvalue(L, i+2);
append_one(b, L, key, sz, depth);
lua_pop(L,1);
}
write_byte(b,0);
write_length(b, b->size - length, length);
}
static int
ltostring(lua_State *L) {
size_t sz = lua_rawlen(L, 1);
void * ud = lua_touserdata(L,1);
lua_pushlstring(L, ud, sz);
return 1;
}
static int
llen(lua_State *L) {
size_t sz = lua_rawlen(L, 1);
lua_pushinteger(L, sz);
return 1;
}
static void
make_object(lua_State *L, int type, const void * ptr, size_t len) {
luaL_Buffer b;
luaL_buffinit(L, &b);
luaL_addchar(&b, 0);
luaL_addchar(&b, type);
luaL_addlstring(&b, ptr, len);
luaL_pushresult(&b);
}
static void
unpack_dict(lua_State *L, struct bson_reader *br, bool array) {
luaL_checkstack(L, 16, NULL); // reserve enough stack space to unpack table
int sz = read_int32(L, br);
const void * bytes = read_bytes(L, br, sz-5);
struct bson_reader t = { bytes, sz-5 };
int end = read_byte(L, br);
if (end != '\0') {
luaL_error(L, "Invalid document end");
}
lua_newtable(L);
for (;;) {
if (t.size == 0)
break;
int bt = read_byte(L, &t);
size_t klen = 0;
const char * key = read_cstring(L, &t, &klen);
if (array) {
int id = strtol(key, NULL, 10) + 1;
lua_pushinteger(L,id);
} else {
lua_pushlstring(L, key, klen);
}
switch (bt) {
case BSON_REAL:
lua_pushnumber(L, read_double(L, &t));
break;
case BSON_BOOLEAN:
lua_pushboolean(L, read_byte(L, &t));
break;
case BSON_STRING: {
int sz = read_int32(L, &t);
if (sz <= 0) {
luaL_error(L, "Invalid bson string , length = %d", sz);
}
lua_pushlstring(L, read_bytes(L, &t, sz), sz-1);
break;
}
case BSON_DOCUMENT:
unpack_dict(L, &t, false);
break;
case BSON_ARRAY:
unpack_dict(L, &t, true);
break;
case BSON_BINARY: {
int sz = read_int32(L, &t);
int subtype = read_byte(L, &t);
luaL_Buffer b;
luaL_buffinit(L, &b);
luaL_addchar(&b, 0);
luaL_addchar(&b, BSON_BINARY);
luaL_addchar(&b, subtype);
luaL_addlstring(&b, read_bytes(L, &t, sz), sz);
luaL_pushresult(&b);
break;
}
case BSON_OBJECTID:
make_object(L, BSON_OBJECTID, read_bytes(L, &t, 12), 12);
break;
case BSON_DATE: {
int64_t date = read_int64(L, &t);
uint32_t v = date / 1000;
make_object(L, BSON_DATE, &v, 4);
break;
}
case BSON_MINKEY:
case BSON_MAXKEY:
case BSON_NULL: {
char key[] = { 0, bt };
lua_pushlstring(L, key, sizeof(key));
break;
}
case BSON_REGEX: {
size_t rlen1=0;
size_t rlen2=0;
const char * r1 = read_cstring(L, &t, &rlen1);
const char * r2 = read_cstring(L, &t, &rlen2);
luaL_Buffer b;
luaL_buffinit(L, &b);
luaL_addchar(&b, 0);
luaL_addchar(&b, BSON_REGEX);
luaL_addlstring(&b, r1, rlen1);
luaL_addchar(&b,0);
luaL_addlstring(&b, r2, rlen2);
luaL_addchar(&b,0);
luaL_pushresult(&b);
break;
}
case BSON_INT32:
lua_pushinteger(L, read_int32(L, &t));
break;
case BSON_TIMESTAMP: {
int32_t inc = read_int32(L, &t);
int32_t ts = read_int32(L, &t);
luaL_Buffer b;
luaL_buffinit(L, &b);
luaL_addchar(&b, 0);
luaL_addchar(&b, BSON_TIMESTAMP);
luaL_addlstring(&b, (const char *)&inc, 4);
luaL_addlstring(&b, (const char *)&ts, 4);
luaL_pushresult(&b);
break;
}
case BSON_INT64:
lua_pushinteger(L, read_int64(L, &t));
break;
case BSON_DBPOINTER: {
const void * ptr = t.ptr;
int sz = read_int32(L, &t);
read_bytes(L, &t, sz+12);
make_object(L, BSON_DBPOINTER, ptr, sz + 16);
break;
}
case BSON_JSCODE:
case BSON_SYMBOL: {
const void * ptr = t.ptr;
int sz = read_int32(L, &t);
read_bytes(L, &t, sz);
make_object(L, bt, ptr, sz + 4);
break;
}
case BSON_CODEWS: {
const void * ptr = t.ptr;
int sz = read_int32(L, &t);
read_bytes(L, &t, sz-4);
make_object(L, bt, ptr, sz);
break;
}
default:
// unsupported
luaL_error(L, "Invalid bson type : %d", bt);
lua_pop(L,1);
continue;
}
lua_rawset(L,-3);
}
}
static int
lmakeindex(lua_State *L) {
int32_t *bson = luaL_checkudata(L,1,"bson");
const uint8_t * start = (const uint8_t *)bson;
struct bson_reader br = { start+4, get_length(start) - 5 };
lua_newtable(L);
for (;;) {
if (br.size == 0)
break;
int bt = read_byte(L, &br);
size_t klen = 0;
const char * key = read_cstring(L, &br, &klen);
int field_size = 0;
switch (bt) {
case BSON_INT64:
case BSON_TIMESTAMP:
case BSON_DATE:
case BSON_REAL:
field_size = 8;
break;
case BSON_BOOLEAN:
field_size = 1;
break;
case BSON_JSCODE:
case BSON_SYMBOL:
case BSON_STRING: {
int sz = read_int32(L, &br);
read_bytes(L, &br, sz);
break;
}
case BSON_CODEWS:
case BSON_ARRAY:
case BSON_DOCUMENT: {
int sz = read_int32(L, &br);
read_bytes(L, &br, sz-4);
break;
}
case BSON_BINARY: {
int sz = read_int32(L, &br);
read_bytes(L, &br, sz+1);
break;
}
case BSON_OBJECTID:
field_size = 12;
break;
case BSON_MINKEY:
case BSON_MAXKEY:
case BSON_NULL:
break;
case BSON_REGEX: {
size_t rlen1=0;
size_t rlen2=0;
read_cstring(L, &br, &rlen1);
read_cstring(L, &br, &rlen2);
break;
}
case BSON_INT32:
field_size = 4;
break;
case BSON_DBPOINTER: {
int sz = read_int32(L, &br);
read_bytes(L, &br, sz+12);
break;
}
default:
// unsupported
luaL_error(L, "Invalid bson type : %d", bt);
lua_pop(L,1);
continue;
}
if (field_size > 0) {
int id = bt | (int)(br.ptr - start) << BSON_TYPE_SHIFT;
read_bytes(L, &br, field_size);
lua_pushlstring(L, key, klen);
lua_pushinteger(L,id);
lua_rawset(L,-3);
}
}
lua_setuservalue(L,1);
lua_settop(L,1);
return 1;
}
static void
replace_object(lua_State *L, int type, struct bson * bs) {
size_t len = 0;
const char * data = luaL_checklstring(L,3, &len);
if (len < 6 || data[0] != 0 || data[1] != type) {
luaL_error(L, "Type mismatch, need bson type %d", type);
}
switch (type) {
case BSON_OBJECTID:
if (len != 2+12) {
luaL_error(L, "Invalid object id");
}
memcpy(bs->ptr, data+2, 12);
break;
case BSON_DATE: {
if (len != 2+4) {
luaL_error(L, "Invalid date");
}
const uint32_t * ts = (const uint32_t *)(data + 2);
int64_t v = (int64_t)*ts * 1000;
write_int64(bs, v);
break;
}
case BSON_TIMESTAMP: {
if (len != 2+8) {
luaL_error(L, "Invalid timestamp");
}
const uint32_t * inc = (const uint32_t *)(data + 2);
const uint32_t * ts = (const uint32_t *)(data + 6);
write_int32(bs, *inc);
write_int32(bs, *ts);
break;
}
}
}
static int
lreplace(lua_State *L) {
lua_getuservalue(L,1);
if (!lua_istable(L,-1)) {
return luaL_error(L, "call makeindex first");
}
lua_pushvalue(L,2);
lua_rawget(L, -2);
if (!lua_isnumber(L,-1)) {
return luaL_error(L, "Can't replace key : %s", lua_tostring(L,2));
}
int id = lua_tointeger(L, -1);
int type = id & ((1<<(BSON_TYPE_SHIFT)) - 1);
int offset = id >> BSON_TYPE_SHIFT;
uint8_t * start = lua_touserdata(L,1);
struct bson b = { 0,16, start + offset };
switch (type) {
case BSON_REAL:
write_double(&b, luaL_checknumber(L, 3));
break;
case BSON_BOOLEAN:
write_byte(&b, lua_toboolean(L,3));
break;
case BSON_OBJECTID:
case BSON_DATE:
case BSON_TIMESTAMP:
replace_object(L, type, &b);
break;
#if LUA_VERSION_NUM >= 503
case BSON_INT32: {
if (!lua_isinteger(L, 3)) {
luaL_error(L, "%f must be a 32bit integer ", lua_tonumber(L, 3));
}
int32_t i = lua_tointeger(L,3);
write_int32(&b, i);
break;
}
case BSON_INT64: {
if (!lua_isinteger(L, 3)) {
luaL_error(L, "%f must be a 64bit integer ", lua_tonumber(L, 3));
}
int64_t i = lua_tointeger(L,3);
write_int64(&b, i);
break;
}
#else
case BSON_INT32: {
double d = luaL_checknumber(L,3);
int32_t i = lua_tointeger(L,3);
if ((int32_t)d != i) {
luaL_error(L, "%f must be a 32bit integer ", d);
}
write_int32(&b, i);
break;
}
case BSON_INT64: {
double d = luaL_checknumber(L,3);
lua_Integer i = lua_tointeger(L,3);
if ((lua_Integer)d != i) {
luaL_error(L, "%f must be a 64bit integer ", d);
}
write_int64(&b, i);
break;
}
#endif
default:
luaL_error(L, "Can't replace type %d", type);
break;
}
return 0;
}
static int
ldecode(lua_State *L) {
const int32_t * data = lua_touserdata(L,1);
if (data == NULL) {
return 0;
}
const uint8_t * b = (const uint8_t *)data;
int32_t len = get_length(b);
struct bson_reader br = { b , len };
unpack_dict(L, &br, false);
return 1;
}
static void
bson_meta(lua_State *L) {
if (luaL_newmetatable(L, "bson")) {
luaL_Reg l[] = {
{ "decode", ldecode },
{ "makeindex", lmakeindex },
{ NULL, NULL },
};
luaL_newlib(L,l);
lua_setfield(L, -2, "__index");
lua_pushcfunction(L, ltostring);
lua_setfield(L, -2, "__tostring");
lua_pushcfunction(L, llen);
lua_setfield(L, -2, "__len");
lua_pushcfunction(L, lreplace);
lua_setfield(L, -2, "__newindex");
}
lua_setmetatable(L, -2);
}
static int
encode_bson(lua_State *L) {
struct bson *b = lua_touserdata(L, 2);
lua_settop(L, 1);
pack_dict(L, b, false, 0);
void * ud = lua_newuserdata(L, b->size);
memcpy(ud, b->ptr, b->size);
return 1;
}
static int
lencode(lua_State *L) {
struct bson b;
lua_settop(L,1);
luaL_checktype(L, 1, LUA_TTABLE);
bson_create(&b);
lua_pushcfunction(L, encode_bson);
lua_pushvalue(L, 1);
lua_pushlightuserdata(L, &b);
if (lua_pcall(L, 2, 1, 0) != LUA_OK) {
bson_destroy(&b);
return lua_error(L);
}
bson_destroy(&b);
bson_meta(L);
return 1;
}
static int
encode_bson_byorder(lua_State *L) {
int n = lua_gettop(L);
struct bson *b = lua_touserdata(L, n);
lua_settop(L, n-1);
pack_ordered_dict(L, b, n-1, 0);
lua_settop(L,0);
void * ud = lua_newuserdata(L, b->size);
memcpy(ud, b->ptr, b->size);
return 1;
}
static int
lencode_order(lua_State *L) {
struct bson b;
int n = lua_gettop(L);
if (n%2 != 0) {
return luaL_error(L, "Invalid ordered dict");
}
bson_create(&b);
lua_pushcfunction(L, encode_bson_byorder);
lua_insert(L, 1);
lua_pushlightuserdata(L, &b);
if (lua_pcall(L, n+1, 1, 0) != LUA_OK) {
bson_destroy(&b);
return lua_error(L);
}
bson_destroy(&b);
bson_meta(L);
return 1;
}
static int
ldate(lua_State *L) {
int d = luaL_checkinteger(L,1);
luaL_Buffer b;
luaL_buffinit(L, &b);
luaL_addchar(&b, 0);
luaL_addchar(&b, BSON_DATE);
luaL_addlstring(&b, (const char *)&d, sizeof(d));
luaL_pushresult(&b);
return 1;
}
static int
ltimestamp(lua_State *L) {
int d = luaL_checkinteger(L,1);
luaL_Buffer b;
luaL_buffinit(L, &b);
luaL_addchar(&b, 0);
luaL_addchar(&b, BSON_TIMESTAMP);
if (lua_isnoneornil(L,2)) {
static uint32_t inc = 0;
luaL_addlstring(&b, (const char *)&inc, sizeof(inc));
++inc;
} else {
uint32_t i = (uint32_t)lua_tointeger(L,2);
luaL_addlstring(&b, (const char *)&i, sizeof(i));
}
luaL_addlstring(&b, (const char *)&d, sizeof(d));
luaL_pushresult(&b);
return 1;
}
static int
lregex(lua_State *L) {
luaL_checkstring(L,1);
if (lua_gettop(L) < 2) {
lua_pushliteral(L,"");
}
luaL_Buffer b;
luaL_buffinit(L, &b);
luaL_addchar(&b, 0);
luaL_addchar(&b, BSON_REGEX);
lua_pushvalue(L,1);
luaL_addvalue(&b);
luaL_addchar(&b,0);
lua_pushvalue(L,2);
luaL_addvalue(&b);
luaL_addchar(&b,0);
luaL_pushresult(&b);
return 1;
}
static int
lbinary(lua_State *L) {
lua_settop(L,1);
luaL_Buffer b;
luaL_buffinit(L, &b);
luaL_addchar(&b, 0);
luaL_addchar(&b, BSON_BINARY);
luaL_addchar(&b, 0); // sub type
lua_pushvalue(L, 1);
luaL_addvalue(&b);
luaL_pushresult(&b);
return 1;
}
static int
lsubtype(lua_State *L, int subtype, const uint8_t * buf, size_t sz) {
switch(subtype) {
case BSON_BINARY:
lua_pushvalue(L, lua_upvalueindex(6));
lua_pushlstring(L, (const char *)buf+1, sz-1);
lua_pushinteger(L, buf[0]);
return 3;
case BSON_OBJECTID: {
if (sz != 12) {
return luaL_error(L, "Invalid object id");
}
char oid[24];
int i;
const uint8_t * id = buf;
static char *hex = "0123456789abcdef";
for (i=0;i<12;i++) {
oid[i*2] = hex[id[i] >> 4];
oid[i*2+1] = hex[id[i] & 0xf];
}
lua_pushvalue(L, lua_upvalueindex(7));
lua_pushlstring(L, oid, 24);
return 2;
}
case BSON_DATE: {
if (sz != 4) {
return luaL_error(L, "Invalid date");
}
int d = *(const int *)buf;
lua_pushvalue(L, lua_upvalueindex(9));
lua_pushinteger(L, d);
return 2;
}
case BSON_TIMESTAMP: {
if (sz != 8) {
return luaL_error(L, "Invalid timestamp");
}
const uint32_t * ts = (const uint32_t *)buf;
lua_pushvalue(L, lua_upvalueindex(8));
lua_pushinteger(L, (lua_Integer)ts[1]);
lua_pushinteger(L, (lua_Integer)ts[0]);
return 3;
}
case BSON_REGEX: {
--sz;
size_t i;
const uint8_t *str = buf;
for (i=0;i<sz;i++) {
if (str[sz-i-1]==0) {
break;
}
}
lua_pushvalue(L, lua_upvalueindex(10));
if (i==sz) {
return luaL_error(L, "Invalid regex");
}
lua_pushlstring(L, (const char *)str, sz - i - 1);
lua_pushlstring(L, (const char *)str+sz-i, i);
return 3;
}
case BSON_MINKEY:
lua_pushvalue(L, lua_upvalueindex(11));
return 1;
case BSON_MAXKEY:
lua_pushvalue(L, lua_upvalueindex(12));
return 1;
case BSON_NULL:
lua_pushvalue(L, lua_upvalueindex(4));
return 1;
case BSON_JSCODE:
case BSON_DBPOINTER:
case BSON_SYMBOL:
case BSON_CODEWS:
lua_pushvalue(L, lua_upvalueindex(13));
lua_pushlstring(L, (const char *)buf, sz);
return 2;
default:
return luaL_error(L, "Invalid subtype %d", subtype);
}
}
static int
ltype(lua_State *L) {
int t = lua_type(L,1);
int type = 0;
switch (t) {
case LUA_TNUMBER:
type = 1;
break;
case LUA_TBOOLEAN:
type = 2;
break;
case LUA_TTABLE:
type = 3;
break;
case LUA_TNIL:
lua_pushvalue(L, lua_upvalueindex(4));
return 1;
case LUA_TSTRING: {
size_t len = 0;
const char * str = lua_tolstring(L,1,&len);
if (str[0] == 0 && len >= 2) {
return lsubtype(L, (uint8_t)str[1], (const uint8_t *)str+2, len-2);
} else {
type = 5;
break;
}
}
default:
return luaL_error(L, "Invalid type %s",lua_typename(L,t));
}
lua_pushvalue(L, lua_upvalueindex(type));
lua_pushvalue(L,1);
return 2;
}
static void
typeclosure(lua_State *L) {
static const char * typename[] = {
"number", // 1
"boolean", // 2
"table", // 3
"nil", // 4
"string", // 5
"binary", // 6
"objectid", // 7
"timestamp",// 8
"date", // 9
"regex", // 10
"minkey", // 11
"maxkey", // 12
"unsupported", // 13
};
int i;
int n = sizeof(typename)/sizeof(typename[0]);
for (i=0;i<n;i++) {
lua_pushstring(L,typename[i]);
}
lua_pushcclosure(L, ltype, n);
}
static uint8_t oid_header[5];
static uint32_t oid_counter;
static void
init_oid_header() {
init_winsock();
pid_t pid = getpid();
uint32_t h = 0;
char hostname[256];
if (gethostname(hostname, sizeof(hostname))==0) {
int i;
for (i=0;i<sizeof(hostname) && hostname[i];i++) {
h = h ^ ((h<<5)+(h>>2)+hostname[i]);
}
h ^= i;
}
oid_header[0] = h & 0xff;
oid_header[1] = (h>>8) & 0xff;
oid_header[2] = (h>>16) & 0xff;
oid_header[3] = pid & 0xff;
oid_header[4] = (pid >> 8) & 0xff;
oid_counter = h ^ time(NULL) ^ (uintptr_t)&h;
}
static inline int
hextoint(char c) {
if (c>='0' && c<='9')
return c-'0';
if (c>='a' && c<='z')
return c-'a'+10;
if (c>='A' && c<='Z')
return c-'A'+10;
return 0;
}
static int
lobjectid(lua_State *L) {
uint8_t oid[14] = { 0, BSON_OBJECTID };
if (lua_isstring(L,1)) {
size_t len;
const char * str = lua_tolstring(L,1,&len);
if (len != 24) {
return luaL_error(L, "Invalid objectid %s", str);
}
int i;
for (i=0;i<12;i++) {
oid[i+2] = hextoint(str[i*2]) << 4 | hextoint(str[i*2+1]);
}
} else {
time_t ti = time(NULL);
oid[2] = (ti>>24) & 0xff;
oid[3] = (ti>>16) & 0xff;
oid[4] = (ti>>8) & 0xff;
oid[5] = ti & 0xff;
memcpy(oid+6 , oid_header, 5);
oid[11] = (oid_counter>>16) & 0xff;
oid[12] = (oid_counter>>8) & 0xff;
oid[13] = oid_counter & 0xff;
++oid_counter;
}
lua_pushlstring( L, (const char *)oid, 14);
return 1;
}
LUAMOD_API int
luaopen_bson(lua_State *L) {
luaL_checkversion(L);
int i;
for (i=0;i<MAX_NUMBER;i++) {
char tmp[8];
bson_numstr_len[i] = sprintf(tmp,"%d",i);
memcpy(bson_numstrs[i], tmp, bson_numstr_len[i]);
}
luaL_Reg l[] = {
{ "encode", lencode },
{ "encode_order", lencode_order },
{ "date", ldate },
{ "timestamp", ltimestamp },
{ "regex", lregex },
{ "binary", lbinary },
{ "objectid", lobjectid },
{ "decode", ldecode },
{ NULL, NULL },
};
luaL_newlib(L,l);
typeclosure(L);
lua_setfield(L,-2,"type");
char null[] = { 0, BSON_NULL };
lua_pushlstring(L, null, sizeof(null));
lua_setfield(L,-2,"null");
char minkey[] = { 0, BSON_MINKEY };
lua_pushlstring(L, minkey, sizeof(minkey));
lua_setfield(L,-2,"minkey");
char maxkey[] = { 0, BSON_MAXKEY };
lua_pushlstring(L, maxkey, sizeof(maxkey));
lua_setfield(L,-2,"maxkey");
init_oid_header();
return 1;
}