#define __STDC_LIMIT_MACROS
#include "ltpack.h"
#ifndef CC_MSVC
#include
#endif
#define MAX_DEPTH 256
#define TYPE_INT8 0
#define TYPE_INT16 1
#define TYPE_INT32 2
#define TYPE_INT64 3
#define TYPE_DOUBLE 4
#define TYPE_STRING 5
#define TYPE_BOOL 6
#define TYPE_TABLE_BEG 7
#define TYPE_TABLE_END 8
static inline void
pack_integer(tpack_t *pack, int64_t v) {
if (v >= INT8_MIN && v <= INT8_MAX) {
pack->m_data = sdscat(pack->m_data, (int8_t)TYPE_INT8);
pack->m_data = sdscat(pack->m_data, (int8_t)v);
return;
}
if (v >= INT16_MIN && v <= INT16_MAX) {
pack->m_data = sdscat(pack->m_data, (int8_t)TYPE_INT16);
pack->m_data = sdscat(pack->m_data, (int16_t)v);
return;
}
if (v >= INT32_MIN && v <= INT32_MAX) {
pack->m_data = sdscat(pack->m_data, (int8_t)TYPE_INT32);
pack->m_data = sdscat(pack->m_data, (int32_t)v);
return;
}
pack->m_data = sdscat(pack->m_data, (int8_t)TYPE_INT64);
pack->m_data = sdscat(pack->m_data, (int64_t)v);
}
static inline void
pack_double(tpack_t *pack, double v) {
pack->m_data = sdscat(pack->m_data, (int8_t)TYPE_DOUBLE);
pack->m_data = sdscat(pack->m_data, v);
}
static inline void
pack_boolean(tpack_t *pack, bool v) {
pack->m_data = sdscat(pack->m_data, (int8_t)TYPE_BOOL);
pack->m_data = sdscat(pack->m_data, v);
}
static inline void
pack_string(tpack_t *pack, const char* v, size_t len) {
pack->m_data = sdscat(pack->m_data, (int8_t)TYPE_STRING);
pack->m_data = sdscat(pack->m_data, len);
pack->m_data = sdscatlen(pack->m_data, v, len);
}
static void
pack_lua_data(tpack_t *pack, lua_State *L, int idx, int depth);
static void
pack_table(tpack_t *pack, lua_State *L, int idx, int depth) {
if (depth > MAX_DEPTH) {
luaL_error(L, "too deep(%d, limit %d) while packing table", depth, MAX_DEPTH);
}
idx = lua_absindex(L, idx);
pack->m_data = sdscat(pack->m_data, (int8_t)TYPE_TABLE_BEG);
luaL_checkstack(L, 2, NULL);
lua_pushnil(L);
while (lua_next(L, idx) != 0) {
pack_lua_data(pack, L, -2, depth);
pack_lua_data(pack, L, -1, depth);
lua_pop(L, 1);
}
pack->m_data = sdscat(pack->m_data, (int8_t)TYPE_TABLE_END);
}
static void
pack_lua_data(tpack_t *pack, lua_State *L, int idx, int depth) {
int data_type = lua_type(L, idx);
const char * str = NULL;
size_t len;
switch (data_type) {
case LUA_TNUMBER:
if (lua_isinteger(L, idx)) {
pack_integer(pack, lua_tointeger(L, idx));
} else {
pack_double(pack, lua_tonumber(L, idx));
}
return;
case LUA_TSTRING:
str = lua_tolstring(L, idx, &len);
pack_string(pack, str, len);
return;
case LUA_TBOOLEAN:
pack_boolean(pack, (bool)lua_toboolean(L, idx));
return;
case LUA_TTABLE:
pack_table(pack, L, idx, depth + 1);
return;
default:
luaL_error(L, "attempt to pack a %s value", lua_typename(L, data_type));
return;
}
}
static int8_t
unpack_lua_data(tpack_t *pack, size_t& rpos, lua_State *L) {
luaL_checkstack(L, 1, NULL);
int8_t data_type = sdsread(pack->m_data, rpos);
size_t len;
++rpos;
switch (data_type) {
case TYPE_INT8:
lua_pushinteger(L, sdsread(pack->m_data, rpos));
rpos += 1;
break;
case TYPE_INT16:
lua_pushinteger(L, sdsread(pack->m_data, rpos));
rpos += 2;
break;
case TYPE_INT32:
lua_pushinteger(L, sdsread(pack->m_data, rpos));
rpos += 4;
break;
case TYPE_INT64:
lua_pushinteger(L, sdsread(pack->m_data, rpos));
rpos += 8;
break;
case TYPE_DOUBLE:
lua_pushnumber(L, sdsread(pack->m_data, rpos));
rpos += sizeof(double);
break;
case TYPE_STRING:
len = sdsread(pack->m_data, rpos);
rpos += sizeof(size_t);
lua_pushlstring(L, sdsread(pack->m_data, rpos, len), len);
rpos += len;
break;
case TYPE_BOOL:
lua_pushboolean(L, sdsread(pack->m_data, rpos));
rpos += sizeof(bool);
break;
case TYPE_TABLE_BEG:
lua_createtable(L, 0, 0);
while (unpack_lua_data(pack, rpos, L) != TYPE_TABLE_END) {
unpack_lua_data(pack, rpos, L);
lua_rawset(L, -3);
}
break;
case TYPE_TABLE_END:
break;
default:
break;
}
return data_type;
}
/////////////////////////////////////////////////////////////////////////
static int pack_safe(lua_State *L) {
tpack_t* pack = (tpack_t*)lua_touserdata(L, 2); //arg1: table, arg2: tpack_t ptr.
lua_settop(L, 1);
pack_lua_data(pack, L, 1, 0);
return 0;
}
bool ltpack(tpack_t* pack, lua_State *L, int idx) {
if (lua_istable(L, idx)) {
lua_checkstack(L, 3);
lua_pushcfunction(L, pack_safe);
lua_pushvalue(L, idx);
lua_pushlightuserdata(L, pack);
if (lua_pcall(L, 2, LUA_MULTRET, NULL) == LUA_OK) {
return true;
}
return false;
}
lua_checkstack(L, 1);
lua_pushstring(L, "table expected");
return false;
}
static int unpack_safe(lua_State *L) {
size_t rpos = 0;
tpack_t* pack = (tpack_t*)lua_touserdata(L, 1); //arg1: tpack_t ptr.
lua_settop(L, 1);
unpack_lua_data(pack, rpos, L);
return 1;
}
extern bool ltunpack(tpack_t* pack, lua_State *L) {
lua_checkstack(L, 3);
lua_pushcfunction(L, unpack_safe);
lua_pushlightuserdata(L, pack);
return lua_pcall(L, 1, LUA_MULTRET, NULL) == LUA_OK;
}