Skip to content

Commit

Permalink
add mp.hencode()
Browse files Browse the repository at this point in the history
  • Loading branch information
starwing committed Mar 2, 2021
1 parent b025bb4 commit 35c292e
Show file tree
Hide file tree
Showing 2 changed files with 90 additions and 45 deletions.
124 changes: 79 additions & 45 deletions mp.c
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ static int Lmap(lua_State *L) { return lmp_object(L, 1, "map"); }

typedef struct lmp_HeapBuffer {
unsigned cap;
char *data;
unsigned char *data;
} lmp_HeapBuffer;

typedef struct lmp_Buffer {
Expand All @@ -170,7 +170,7 @@ typedef struct lmp_Buffer {
unsigned sso : 1;
union {
lmp_HeapBuffer heap;
char buff[LMP_SSO_SIZE];
unsigned char buff[LMP_SSO_SIZE];
} u;
} lmp_Buffer;

Expand All @@ -181,13 +181,13 @@ typedef struct lmp_Buffer {
#define lmp_nomem(B) luaL_error((B)->L, "out of memory")
#define lmp_toobig(B,i,n,l) lmp_error(B,i,0,"%s too large (count=%d)",(n),(int)(l))

static int lmp_encode (lmp_Buffer *B, int idx, int type);
static int lmp_encode (lmp_Buffer *B, int idx, int type, int hidx);
static int lmp_pack (lmp_Buffer *B, int idx, const char *type, int fetch);

static void lmp_resetbuffer(lmp_Buffer *B)
{ if (B->sso) { free(lmp_data(B)); memset(B, 0, sizeof(lmp_Buffer)); } }

static char *lmp_prepare(lmp_Buffer *B, size_t len) {
static unsigned char *lmp_prepare(lmp_Buffer *B, size_t len) {
unsigned expected = B->len + (unsigned)len;
unsigned cap = B->sso ? B->u.heap.cap : LMP_SSO_SIZE;
if (expected > cap) {
Expand All @@ -199,22 +199,22 @@ static char *lmp_prepare(lmp_Buffer *B, size_t len) {
if (!(newptr = realloc(oldptr, newsize))) lmp_nomem(B);
if (!B->sso) memcpy(newptr, lmp_data(B), B->len);
B->sso = 1;
B->u.heap.data = (char*)newptr;
B->u.heap.data = (unsigned char*)newptr;
B->u.heap.cap = newsize;
}
return lmp_data(B) + B->len;
}

static void lmp_writeuint(lmp_Buffer *B, lmp_U64 v, int len) {
char buff[8];
unsigned char buff[8];
switch (len) {
case 8: buff[0] = (v >> 56) & 0xFF;
buff[1] = (v >> 48) & 0xFF;
buff[2] = (v >> 40) & 0xFF;
buff[3] = (v >> 32) & 0xFF; /* FALLTHROUGH */
case 4: buff[4] = (v >> 24) & 0xFF;
buff[5] = (v >> 16) & 0xFF; /* FALLTHROUGH */
case 2: buff[6] = (v >> 8) & 0xFF;
case 2: buff[6] = (v >> 8) & 0xFF; /* FALLTHROUGH */
case 1: buff[7] = (v ) & 0xFF; /* FALLTHROUGH */
}
lmp_addchars(B, buff+8-len, len);
Expand All @@ -237,8 +237,8 @@ static void lmp_writefloat(lmp_Buffer *B, lua_Number n, int len) {
union {
float f32;
double f64;
lmp_U64 u64;
unsigned u32;
lmp_U64 u64;
} u;
if (len == 4) {
u.f32 = (float)n;
Expand All @@ -262,7 +262,7 @@ static void lmp_writestring(lmp_Buffer *B, int base, const char *s, size_t len)
}

static void lmp_writeext(lmp_Buffer *B, int type, const char *s, size_t len) {
char *buff = lmp_prepare(B, 2);
unsigned char *buff = lmp_prepare(B, 2);
int o;
buff[1] = type, B->len += 2;
switch (len) {
Expand Down Expand Up @@ -310,7 +310,7 @@ static int lmp_addstring(lmp_Buffer *B, int idx, int type) {
return 1;
}

static int lmp_addarray(lmp_Buffer *B, int idx) {
static int lmp_addarray(lmp_Buffer *B, int idx, int hidx) {
int i, len = (int)luaL_len(B->L, idx);
int top = lua_gettop(B->L);
if (top > LMP_MAX_STACK || !lua_checkstack(B->L, 5))
Expand All @@ -325,7 +325,7 @@ static int lmp_addarray(lmp_Buffer *B, int idx) {
lmp_writeuint(B, len, 4);
}
for (i = 1; i <= len; ++i) {
if (!lmp_encode(B, top+1, lua53_geti(B->L, idx, i)))
if (!lmp_encode(B, top+1, lua53_geti(B->L, idx, i), hidx))
return lmp_error(B, idx,top+1, "invalid element '%d' in array", i);
lua_pop(B->L, 1);
}
Expand All @@ -334,7 +334,7 @@ static int lmp_addarray(lmp_Buffer *B, int idx) {

static void lmp_fixmapszie(lmp_Buffer *B, unsigned off, unsigned count) {
unsigned len = B->len - off;
char *buff;
unsigned char *buff;
lmp_prepare(B, 5);
buff = lmp_data(B) + off;
if (count < 16)
Expand All @@ -350,17 +350,17 @@ static void lmp_fixmapszie(lmp_Buffer *B, unsigned off, unsigned count) {
}
}

static int lmp_addmap(lmp_Buffer *B, int idx) {
static int lmp_addmap(lmp_Buffer *B, int idx, int hidx) {
unsigned off = B->len + 1, count = 0;
int top = lua_gettop(B->L);
if (top > LMP_MAX_STACK || !lua_checkstack(B->L, 10))
return lmp_error(B, idx,0, "map level too deep");
lmp_addchar(B, 0x80);
lua_pushnil(B->L);
for (; lua_next(B->L, idx); ++count) {
if (!lmp_encode(B, top+1, 0))
if (!lmp_encode(B, top+1, 0, hidx))
return lmp_error(B, idx,top+1, "invalid key in map");
if (!lmp_encode(B, top+2, 0))
if (!lmp_encode(B, top+2, 0, hidx))
return lmp_error(B, idx,top+2, "invalid value for key '%s' in map",
luaL_tolstring(B->L, top+1, NULL));
lua_pop(B->L, 1);
Expand Down Expand Up @@ -393,15 +393,9 @@ static int lmp_addext(lmp_Buffer *B, int idx, int fetch) {
return 1;
}

static int lmp_addhandler(lmp_Buffer *B, int idx, int fetch) {
static int lmp_handlerresult(lmp_Buffer *B, int idx, int top) {
int r;
const char *type;
int r, top = lua_gettop(B->L)+1;
if (!fetch)
lua_pushvalue(B->L, idx);
else if (lua53_getfield(B->L, idx, "pack") == LUA_TNIL)
return lmp_error(B, idx,0, "'pack' field expected in handler object");
if (fetch) lua_pushvalue(B->L, idx);
lua_call(B->L, fetch, 3);
if ((type = lua_tostring(B->L, top)) == NULL)
return lmp_error(B, idx,0, "type expected from handler, got %s",
luaL_typename(B->L, top));
Expand All @@ -411,6 +405,17 @@ static int lmp_addhandler(lmp_Buffer *B, int idx, int fetch) {
return (lua_pop(B->L, 3), 1);
}

static int lmp_addhandler(lmp_Buffer *B, int idx, int fetch) {
int top = lua_gettop(B->L)+1;
if (!fetch)
lua_pushvalue(B->L, idx);
else if (lua53_getfield(B->L, idx, "pack") == LUA_TNIL)
return lmp_error(B, idx,0, "'pack' field expected in handler object");
if (fetch) lua_pushvalue(B->L, idx);
lua_call(B->L, fetch, 3);
return lmp_handlerresult(B, idx, top);
}

static int lmp_check(lmp_Buffer *B, int idx, int fetch, int type) {
int rt;
if (!fetch)
Expand All @@ -437,10 +442,10 @@ static int lmp_pack(lmp_Buffer *B, int idx, const char *type, int fetch) {
case 'd': return check(LUA_TNUMBER, lmp_addfloat(B,idx,8));
case 's': return check(LUA_TSTRING, lmp_addstring(B,idx,0xD9));
case 'b': return check(LUA_TSTRING, lmp_addstring(B,idx,0xC4));
case 'v': return check(0, lmp_encode(B, idx, 0));
case 'v': return check(0, lmp_encode(B, idx, 0, 0));
case 'h': return lmp_addhandler(B, idx, 1);
case 'a': return lmp_addarray(B, idx);
case 'm': return lmp_addmap(B, idx);
case 'a': return lmp_addarray(B, idx, 0);
case 'm': return lmp_addmap(B, idx, 0);
case 'e': return lmp_addext(B, idx, 1);
}
#undef check
Expand All @@ -461,51 +466,77 @@ static int lmp_addnumber(lmp_Buffer *B, int idx) {
return 1;
}

static int lmp_addtable(lmp_Buffer *B, int idx) {
static int lmp_addtable(lmp_Buffer *B, int idx, int hidx) {
const char *type = lmp_type(B->L, idx);
int r = lmp_pack(B, idx, type, 1);
if (r == 0 || r == 1) return r;
return luaL_len(B->L, idx) > 0 ? lmp_addarray(B, idx) : lmp_addmap(B, idx);
return luaL_len(B->L, idx) > 0 ? lmp_addarray(B, idx, hidx) : lmp_addmap(B, idx, hidx);
}

static int lmp_encode(lmp_Buffer *B, int idx, int type) {
static int lmp_encode(lmp_Buffer *B, int idx, int type, int hidx) {
switch (type ? type : (type = lua_type(B->L, idx))) {
case LUA_TNONE:
case LUA_TNIL: lmp_addchar(B, 0xC0); return 1;
case LUA_TBOOLEAN: lmp_addchar(B, 0xC2+lua_toboolean(B->L,idx)); return 1;
case LUA_TNUMBER: return lmp_addnumber(B, idx);
case LUA_TSTRING: return lmp_addstring(B, idx, 0xD9);
case LUA_TFUNCTION: return lmp_addhandler(B, idx, 0);
case LUA_TTABLE: return lmp_addtable(B, idx);
case LUA_TTABLE: return lmp_addtable(B, idx, hidx);
}
if (hidx) {
int top = lua_gettop(B->L)+1;
lua_pushvalue(B->L, idx);
lua_pushvalue(B->L, hidx);
lua_insert(B->L, -2);
lua_call(B->L, 1, 3);
return lmp_handlerresult(B, idx, top);
}
return lmp_error(B, idx,0, "invalid type '%s'", lua_typename(B->L, type));
}

static int lmp_encode_helper(lua_State *L, lua_CFunction encode) {
lmp_Buffer B;
int r;
memset(&B, 0, sizeof(B));
lua_pushcfunction(L, encode);
lua_insert(L, 1);
lua_pushlightuserdata(L, &B);
lua_insert(L, 2);
r = lua_pcall(L, lua_gettop(L)-1, 1, 0) == LUA_OK;
lmp_resetbuffer(&B);
return r ? 1 : luaL_error(L, "%s", lua_tostring(L, -1));
}

static int Lencode_aux(lua_State *L) {
lmp_Buffer *B = (lmp_Buffer*)lua_touserdata(L, 1);
int i, top = lua_gettop(L);
B->L = L;
for (i = 2; i <= top; ++i)
if (!lmp_encode(B, i, 0))
if (!lmp_encode(B, i, 0, 0))
return luaL_error(L, "bad argument to #%d: %s",
i-1, lua_tostring(L, i));
lua_pushlstring(L, lmp_data(B), B->len);
lua_pushlstring(L, (const char*)lmp_data(B), B->len);
return 1;
}

static int Lencode(lua_State *L) {
lmp_Buffer B;
int r;
memset(&B, 0, sizeof(B));
lua_pushcfunction(L, Lencode_aux);
lua_insert(L, 1);
lua_pushlightuserdata(L, &B);
lua_insert(L, 2);
r = lua_pcall(L, lua_gettop(L)-1, 1, 0) == LUA_OK;
lmp_resetbuffer(&B);
return r ? 1 : luaL_error(L, "%s", lua_tostring(L, -1));
static int Lhencode_aux(lua_State *L) {
lmp_Buffer *B = (lmp_Buffer*)lua_touserdata(L, 1);
int i, top = lua_gettop(L);
B->L = L;
for (i = 3; i <= top; ++i)
if (!lmp_encode(B, i, 0, 2))
return luaL_error(L, "bad argument to #%d: %s",
i-1, lua_tostring(L, i));
lua_pushlstring(L, (const char*)lmp_data(B), B->len);
return 1;
}

static int Lencode(lua_State *L)
{ return lmp_encode_helper(L, Lencode_aux); }

static int Lhencode(lua_State *L)
{ return lmp_encode_helper(L, Lhencode_aux); }


/* decode */

Expand Down Expand Up @@ -558,7 +589,9 @@ static lmp_U64 lmp_readuint(lmp_Slice *S, int len, const char *tname) {

static lmp_I64 lmp_u2s(lmp_U64 v, int len) {
const lmp_I64 m = 1LL << (len*8 - 1);
return v&m ? (lmp_I64)(v^m) - m : (lmp_I64)v;
if (len == 8 || !(v & (lmp_U64)m)) return (lmp_I64)v;
v = v & ((1ULL << len*8) - 1);
return (lmp_I64)(v^m) - m;
}

static lua_Number lmp_readfloat(lmp_Slice *S, int len) {
Expand Down Expand Up @@ -759,6 +792,7 @@ LUALIB_API int luaopen_mp(lua_State *L) {
ENTRY(map),
ENTRY(meta),
ENTRY(encode),
ENTRY(hencode),
ENTRY(decode),
ENTRY(fromhex),
ENTRY(tohex),
Expand All @@ -773,5 +807,5 @@ LUALIB_API int luaopen_mp(lua_State *L) {
/* cc: flags+='-march=native -O3 -Wextra -pedantic --coverage'
* unixcc: flags+='-shared -fPIC ' output='mp.so'
* maccc: flags+='-undefined dynamic_lookup'
* win32cc: flags+='-mdll -DLUA_BUILD_AS_DLL ' libs+='-llua53' output='mp.dll' */
* win32cc: flags+='-mdll -DLUA_BUILD_AS_DLL ' libs+='-llua54' output='mp.dll' */

11 changes: 11 additions & 0 deletions test.lua
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,17 @@ function _G.test_handler()
check_eq(function() return "s", "foo" end, "foo")
check_eq(function() return "b", "foo" end, "foo")

local co = coroutine.create(function()end)
local c = 0
local v = mp.hencode(function(v)
if v == co then
c = c + 1
return "int", 1
end
return "nil"
end, co, co, co)
eq(c, 3)
eq(v, "\1\1\1")
end

function _G.test_error()
Expand Down

0 comments on commit 35c292e

Please sign in to comment.