1362 lines
29 KiB
C
1362 lines
29 KiB
C
#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>
|
|
#include "atomic.h"
|
|
|
|
#define DEFAULT_CAP 64
|
|
#define MAX_NUMBER 1024
|
|
// avoid circular reference while encodeing
|
|
#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;
|
|
}
|
|
|
|
#define MAXUNICODE 0x10FFFF
|
|
|
|
static int
|
|
utf8_copy(const char *s, char *d, size_t limit) {
|
|
static const unsigned int limits[] = {0xFF, 0x7F, 0x7FF, 0xFFFF};
|
|
unsigned int c = s[0];
|
|
unsigned int res = 0;
|
|
if (limit < 1)
|
|
return 0;
|
|
d[0] = s[0];
|
|
if (c < 0x80) {
|
|
return 1;
|
|
} else {
|
|
int count = 0;
|
|
while (c & 0x40) {
|
|
int cc = s[++count];
|
|
if (limit <= count || (cc & 0xC0) != 0x80)
|
|
return 0;
|
|
d[count] = s[count];
|
|
res = (res << 6) | (cc & 0x3F);
|
|
c <<= 1;
|
|
}
|
|
res |= ((c & 0x7F) << (count * 5));
|
|
if (count > 3 || res > MAXUNICODE || res <= limits[count])
|
|
return 0;
|
|
return count+1;
|
|
}
|
|
}
|
|
|
|
static void
|
|
write_string(struct bson *b, lua_State *L, const char *key, size_t sz) {
|
|
bson_reserve(b,sz+1);
|
|
char *dst = (char *)(b->ptr + b->size);
|
|
const char *src = key;
|
|
size_t n = sz;
|
|
while(n > 0) {
|
|
int c = utf8_copy(src, dst, n);
|
|
if (c == 0) {
|
|
luaL_error(L, "Invalid utf8 string");
|
|
}
|
|
src += c;
|
|
dst += c;
|
|
n -= c;
|
|
}
|
|
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 inline void
|
|
append_key(struct bson *bs, lua_State *L, int type, const char *key, size_t sz) {
|
|
write_byte(bs, type);
|
|
write_string(bs, L, key, sz);
|
|
}
|
|
|
|
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, L, BSON_INT32, key, sz);
|
|
write_int32(bs, i);
|
|
} else {
|
|
append_key(bs, L, BSON_INT64, key, sz);
|
|
write_int64(bs, i);
|
|
}
|
|
} else {
|
|
lua_Number d = lua_tonumber(L,-1);
|
|
append_key(bs, L, BSON_REAL, key, sz);
|
|
write_double(bs, d);
|
|
}
|
|
}
|
|
|
|
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) {
|
|
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, L, 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, L, 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, L, str, len-i-1);
|
|
write_string(bs, L, str + len-i, i);
|
|
break;
|
|
}
|
|
case BSON_MINKEY:
|
|
case BSON_MAXKEY:
|
|
case BSON_NULL:
|
|
break;
|
|
case BSON_INT64: {
|
|
if (len != 2 + 8) {
|
|
luaL_error(L, "Invalid int64");
|
|
}
|
|
const int64_t * v = (const int64_t *)(str + 2);
|
|
write_int64(bs, *v);
|
|
break;
|
|
}
|
|
default:
|
|
luaL_error(L,"Invalid subtype %d", subt);
|
|
}
|
|
} else {
|
|
size_t len;
|
|
const char * str = lua_tolstring(L,-1,&len);
|
|
append_key(bs, L, BSON_STRING, key, sz);
|
|
int off = reserve_length(bs);
|
|
write_string(bs, L, 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, L, BSON_BOOLEAN, key, sz);
|
|
write_byte(bs, lua_toboolean(L,-1));
|
|
break;
|
|
case LUA_TNIL:
|
|
luaL_error(L, "Bson array has a hole (nil), Use bson.null instead");
|
|
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_array(lua_State *L, struct bson *b, int depth, size_t len) {
|
|
int length = reserve_length(b);
|
|
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:
|
|
luaL_error(L, "Bson dictionary's key can't be number");
|
|
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, 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);
|
|
}
|
|
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
|
|
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, 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
|
|
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, L, BSON_ARRAY, key, sz);
|
|
pack_array(L, bs, depth, len);
|
|
} else if (luaL_getmetafield(L, -1, "__pairs") != LUA_TNIL) {
|
|
append_key(bs, L, BSON_DOCUMENT, key, sz);
|
|
pack_meta_dict(L, bs, depth);
|
|
} else if (is_rawarray(L)) {
|
|
append_key(bs, L, BSON_ARRAY, key, sz);
|
|
pack_array(L, bs, depth, lua_rawlen(L, -1));
|
|
} else {
|
|
append_key(bs, L, BSON_DOCUMENT, key, sz);
|
|
pack_simple_dict(L, bs, depth);
|
|
}
|
|
}
|
|
|
|
static void
|
|
pack_ordered_dict(lua_State *L, struct bson *b, int n, int depth) {
|
|
int length = reserve_length(b);
|
|
int i;
|
|
size_t sz;
|
|
// the first key is at index n
|
|
const char * key = lua_tolstring(L, n, &sz);
|
|
for (i=0;i<n;i+=2) {
|
|
if (key == NULL) {
|
|
luaL_error(L, "Argument %d need a string", i+1);
|
|
}
|
|
lua_pushvalue(L, i+1);
|
|
append_one(b, L, key, sz, depth);
|
|
lua_pop(L,1);
|
|
key = lua_tolstring(L, i+2, &sz); // next key
|
|
}
|
|
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_setiuservalue(L,1,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_getiuservalue(L,1,1);
|
|
if (!lua_istable(L,-1)) {
|
|
return luaL_error(L, "call makeindex first");
|
|
}
|
|
lua_pushvalue(L,2);
|
|
if (lua_rawget(L, -2) != LUA_TNUMBER) {
|
|
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;
|
|
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;
|
|
}
|
|
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);
|
|
if (luaL_getmetafield(L, -1, "__pairs") != LUA_TNIL) {
|
|
pack_meta_dict(L, b, 0);
|
|
} else {
|
|
pack_simple_dict(L, b, 0);
|
|
}
|
|
void * ud = lua_newuserdatauv(L, b->size, 1);
|
|
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);
|
|
pack_ordered_dict(L, b, n, 0);
|
|
lua_settop(L,0);
|
|
void * ud = lua_newuserdatauv(L, b->size, 1);
|
|
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_pushvalue(L, 1); // copy the first arg to n
|
|
lua_pushcfunction(L, encode_bson_byorder);
|
|
lua_replace(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
|
|
lint64(lua_State *L) {
|
|
int64_t d = luaL_checkinteger(L, 1);
|
|
luaL_Buffer b;
|
|
luaL_buffinit(L, &b);
|
|
luaL_addchar(&b, 0);
|
|
luaL_addchar(&b, BSON_INT64);
|
|
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
|
|
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(14));
|
|
lua_pushlstring(L, (const char *)buf, sz);
|
|
return 2;
|
|
case BSON_INT64: {
|
|
if (sz != 8) {
|
|
return luaL_error(L, "Invalid int64");
|
|
}
|
|
int64_t d = *(const int64_t *)buf;
|
|
lua_pushvalue(L, lua_upvalueindex(13));
|
|
lua_pushinteger(L, d);
|
|
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
|
|
"int64", // 13
|
|
"unsupported", // 14
|
|
};
|
|
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() {
|
|
if (oid_counter) {
|
|
// already init
|
|
return;
|
|
}
|
|
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;
|
|
|
|
uint32_t c = h ^ time(NULL) ^ (uintptr_t)&h;
|
|
if (c == 0) {
|
|
c = 1;
|
|
}
|
|
oid_counter = c;
|
|
}
|
|
|
|
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);
|
|
// old_counter is a static var, use atom inc.
|
|
uint32_t id = ATOM_FINC(&oid_counter);
|
|
|
|
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] = (id>>16) & 0xff;
|
|
oid[12] = (id>>8) & 0xff;
|
|
oid[13] = id & 0xff;
|
|
}
|
|
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 },
|
|
{ "int64", lint64 },
|
|
{ "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;
|
|
}
|
|
|