#include
#include
#include
#include
#include
#include "lua.h"
#include "lauxlib.h"
#define LUACMSGPACK_VERSION "lua-cmsgpack 0.3.1"
#define LUACMSGPACK_COPYRIGHT "Copyright (C) 2012, Salvatore Sanfilippo"
#define LUACMSGPACK_DESCRIPTION "MessagePack C implementation for Lua"
#define LUACMSGPACK_MAX_NESTING 16 /* Max tables nesting. */
/* ==============================================================================
* MessagePack implementation and bindings for Lua 5.1/5.2.
* Copyright(C) 2012 Salvatore Sanfilippo
*
* http://github.com/antirez/lua-cmsgpack
*
* For MessagePack specification check the following web site:
* http://wiki.msgpack.org/display/MSGPACK/Format+specification
*
* See Copyright Notice at the end of this file.
*
* CHANGELOG:
* 19-Feb-2012 (ver 0.1.0): Initial release.
* 20-Feb-2012 (ver 0.2.0): Tables encoding improved.
* 20-Feb-2012 (ver 0.2.1): Minor bug fixing.
* 20-Feb-2012 (ver 0.3.0): Module renamed lua-cmsgpack (was lua-msgpack).
* 04-Apr-2014 (ver 0.3.1): Lua 5.2 support and minor bug fix.
* ============================================================================ */
/* --------------------------- Endian conversion --------------------------------
* We use it only for floats and doubles, all the other conversions are performed
* in an endian independent fashion. So the only thing we need is a function
* that swaps a binary string if the arch is little endian (and left it untouched
* otherwise). */
/* Reverse memory bytes if arch is little endian. Given the conceptual
* simplicity of the Lua build system we prefer to check for endianess at runtime.
* The performance difference should be acceptable. */
static void memrevifle(void *ptr, size_t len) {
unsigned char *p = ptr, *e = p+len-1, aux;
int test = 1;
unsigned char *testp = (unsigned char*) &test;
if (testp[0] == 0) return; /* Big endian, nothign to do. */
len /= 2;
while(len--) {
aux = *p;
*p = *e;
*e = aux;
p++;
e--;
}
}
/* ----------------------------- String buffer ----------------------------------
* This is a simple implementation of string buffers. The only opereation
* supported is creating empty buffers and appending bytes to it.
* The string buffer uses 2x preallocation on every realloc for O(N) append
* behavior. */
typedef struct mp_buf {
unsigned char *b;
size_t len, free;
} mp_buf;
static mp_buf *mp_buf_new(void) {
mp_buf *buf = malloc(sizeof(*buf));
buf->b = NULL;
buf->len = buf->free = 0;
return buf;
}
void mp_buf_append(mp_buf *buf, const unsigned char *s, size_t len) {
if (buf->free < len) {
size_t newlen = buf->len+len;
buf->b = realloc(buf->b,newlen*2);
buf->free = newlen;
}
memcpy(buf->b+buf->len,s,len);
buf->len += len;
buf->free -= len;
}
void mp_buf_free(mp_buf *buf) {
free(buf->b);
free(buf);
}
/* ------------------------------ String cursor ----------------------------------
* This simple data structure is used for parsing. Basically you create a cursor
* using a string pointer and a length, then it is possible to access the
* current string position with cursor->p, check the remaining length
* in cursor->left, and finally consume more string using
* mp_cur_consume(cursor,len), to advance 'p' and subtract 'left'.
* An additional field cursor->error is set to zero on initialization and can
* be used to report errors. */
#define MP_CUR_ERROR_NONE 0
#define MP_CUR_ERROR_EOF 1 /* Not enough data to complete the opereation. */
#define MP_CUR_ERROR_BADFMT 2 /* Bad data format */
typedef struct mp_cur {
const unsigned char *p;
size_t left;
int err;
} mp_cur;
static mp_cur *mp_cur_new(const unsigned char *s, size_t len) {
mp_cur *cursor = malloc(sizeof(*cursor));
cursor->p = s;
cursor->left = len;
cursor->err = MP_CUR_ERROR_NONE;
return cursor;
}
static void mp_cur_free(mp_cur *cursor) {
free(cursor);
}
#define mp_cur_consume(_c,_len) do { _c->p += _len; _c->left -= _len; } while(0)
/* When there is not enough room we set an error in the cursor and return, this
* is very common across the code so we have a macro to make the code look
* a bit simpler. */
#define mp_cur_need(_c,_len) do { \
if (_c->left < _len) { \
_c->err = MP_CUR_ERROR_EOF; \
return; \
} \
} while(0)
/* --------------------------- Low level MP encoding -------------------------- */
static void mp_encode_bytes(mp_buf *buf, const unsigned char *s, size_t len) {
unsigned char hdr[5];
int hdrlen;
if (len < 32) {
hdr[0] = 0xa0 | (len&0xff); /* fix raw */
hdrlen = 1;
} else if (len >8;
hdr[2] = len&0xff;
hdrlen = 3;
} else {
hdr[0] = 0xdb;
hdr[1] = (len&0xff000000)>>24;
hdr[2] = (len&0xff0000)>>16;
hdr[3] = (len&0xff00)>>8;
hdr[4] = len&0xff;
hdrlen = 5;
}
mp_buf_append(buf,hdr,hdrlen);
mp_buf_append(buf,s,len);
}
/* we assume IEEE 754 internal format for single and double precision floats. */
static void mp_encode_double(mp_buf *buf, double d) {
unsigned char b[9];
float f = d;
assert(sizeof(f) == 4 && sizeof(d) == 8);
if (d == (double)f) {
b[0] = 0xca; /* float IEEE 754 */
memcpy(b+1,&f,4);
memrevifle(b+1,4);
mp_buf_append(buf,b,5);
} else if (sizeof(d) == 8) {
b[0] = 0xcb; /* double IEEE 754 */
memcpy(b+1,&d,8);
memrevifle(b+1,8);
mp_buf_append(buf,b,9);
}
}
static void mp_encode_int(mp_buf *buf, int64_t n) {
unsigned char b[9];
int enclen;
if (n >= 0) {
if (n 24;
b[2] = (n & 0xff0000) >> 16;
b[3] = (n & 0xff00) >> 8;
b[4] = n & 0xff;
enclen = 5;
} else {
b[0] = 0xcf; /* uint 64 */
b[1] = (n & 0xff00000000000000LL) >> 56;
b[2] = (n & 0xff000000000000LL) >> 48;
b[3] = (n & 0xff0000000000LL) >> 40;
b[4] = (n & 0xff00000000LL) >> 32;
b[5] = (n & 0xff000000) >> 24;
b[6] = (n & 0xff0000) >> 16;
b[7] = (n & 0xff00) >> 8;
b[8] = n & 0xff;
enclen = 9;
}
} else {
if (n >= -32) {
b[0] = ((char)n); /* negative fixnum */
enclen = 1;
} else if (n >= -128) {
b[0] = 0xd0; /* int 8 */
b[1] = n & 0xff;
enclen = 2;
} else if (n >= -32768) {
b[0] = 0xd1; /* int 16 */
b[1] = (n & 0xff00) >> 8;
b[2] = n & 0xff;
enclen = 3;
} else if (n >= -2147483648LL) {
b[0] = 0xd2; /* int 32 */
b[1] = (n & 0xff000000) >> 24;
b[2] = (n & 0xff0000) >> 16;
b[3] = (n & 0xff00) >> 8;
b[4] = n & 0xff;
enclen = 5;
} else {
b[0] = 0xd3; /* int 64 */
b[1] = (n & 0xff00000000000000LL) >> 56;
b[2] = (n & 0xff000000000000LL) >> 48;
b[3] = (n & 0xff0000000000LL) >> 40;
b[4] = (n & 0xff00000000LL) >> 32;
b[5] = (n & 0xff000000) >> 24;
b[6] = (n & 0xff0000) >> 16;
b[7] = (n & 0xff00) >> 8;
b[8] = n & 0xff;
enclen = 9;
}
}
mp_buf_append(buf,b,enclen);
}
static void mp_encode_array(mp_buf *buf, int64_t n) {
unsigned char b[5];
int enclen;
if (n 8;
b[2] = n & 0xff;
enclen = 3;
} else {
b[0] = 0xdd; /* array 32 */
b[1] = (n & 0xff000000) >> 24;
b[2] = (n & 0xff0000) >> 16;
b[3] = (n & 0xff00) >> 8;
b[4] = n & 0xff;
enclen = 5;
}
mp_buf_append(buf,b,enclen);
}
static void mp_encode_map(mp_buf *buf, int64_t n) {
unsigned char b[5];
int enclen;
if (n 8;
b[2] = n & 0xff;
enclen = 3;
} else {
b[0] = 0xdf; /* map 32 */
b[1] = (n & 0xff000000) >> 24;
b[2] = (n & 0xff0000) >> 16;
b[3] = (n & 0xff00) >> 8;
b[4] = n & 0xff;
enclen = 5;
}
mp_buf_append(buf,b,enclen);
}
/* ----------------------------- Lua types encoding --------------------------- */
static void mp_encode_lua_string(lua_State *L, mp_buf *buf) {
size_t len;
const char *s;
s = lua_tolstring(L,-1,&len);
mp_encode_bytes(buf,(const unsigned char*)s,len);
}
static void mp_encode_lua_bool(lua_State *L, mp_buf *buf) {
unsigned char b = lua_toboolean(L,-1) ? 0xc3 : 0xc2;
mp_buf_append(buf,&b,1);
}
static void mp_encode_lua_number(lua_State *L, mp_buf *buf) {
lua_Number n = lua_tonumber(L,-1);
if (floor(n) != n) {
mp_encode_double(buf,(double)n);
} else {
mp_encode_int(buf,(int64_t)n);
}
}
static void mp_encode_lua_type(lua_State *L, mp_buf *buf, int level);
/* Convert a lua table into a message pack list. */
static void mp_encode_lua_table_as_array(lua_State *L, mp_buf *buf, int level) {
#if LUA_VERSION_NUM < 502
size_t len = lua_objlen(L,-1), j;
#else
size_t len = lua_rawlen(L,-1), j;
#endif
mp_encode_array(buf,len);
for (j = 1; j p[0] & 0x80) == 0) { /* positive fixnum */
lua_pushnumber(L,c->p[0]);
mp_cur_consume(c,1);
} else if ((c->p[0] & 0xe0) == 0xe0) { /* negative fixnum */
lua_pushnumber(L,(signed char)c->p[0]);
mp_cur_consume(c,1);
} else if ((c->p[0] & 0xe0) == 0xa0) { /* fix raw */
size_t l = c->p[0] & 0x1f;
mp_cur_need(c,1+l);
lua_pushlstring(L,(char*)c->p+1,l);
mp_cur_consume(c,1+l);
} else if ((c->p[0] & 0xf0) == 0x90) { /* fix map */
size_t l = c->p[0] & 0xf;
mp_cur_consume(c,1);
mp_decode_to_lua_array(L,c,l);
} else if ((c->p[0] & 0xf0) == 0x80) { /* fix map */
size_t l = c->p[0] & 0xf;
mp_cur_consume(c,1);
mp_decode_to_lua_hash(L,c,l);
} else {
c->err = MP_CUR_ERROR_BADFMT;
}
}
}
static int mp_unpack(lua_State *L) {
size_t len;
const unsigned char *s;
mp_cur *c;
if (!lua_isstring(L,-1)) {
lua_pushstring(L,"MessagePack decoding needs a string as input.");
lua_error(L);
}
s = (const unsigned char*) lua_tolstring(L,-1,&len);
c = mp_cur_new(s,len);
mp_decode_to_lua_type(L,c);
if (c->err == MP_CUR_ERROR_EOF) {
mp_cur_free(c);
lua_pushstring(L,"Missing bytes in input.");
lua_error(L);
} else if (c->err == MP_CUR_ERROR_BADFMT) {
mp_cur_free(c);
lua_pushstring(L,"Bad data format in input.");
lua_error(L);
} else if (c->left != 0) {
mp_cur_free(c);
lua_pushstring(L,"Extra bytes in input.");
lua_error(L);
} else {
mp_cur_free(c);
}
return 1;
}
/* ---------------------------------------------------------------------------- */
#if LUA_VERSION_NUM < 502
static const struct luaL_reg thislib[] = {
#else
static const struct luaL_Reg thislib[] = {
#endif
{"pack", mp_pack},
{"unpack", mp_unpack},
{NULL, NULL}
};
LUALIB_API int luaopen_cmsgpack (lua_State *L) {
#if LUA_VERSION_NUM < 502
luaL_register(L, "cmsgpack", thislib);
#else
luaL_newlib(L, thislib);
#endif
lua_pushliteral(L, LUACMSGPACK_VERSION);
lua_setfield(L, -2, "_VERSION");
lua_pushliteral(L, LUACMSGPACK_COPYRIGHT);
lua_setfield(L, -2, "_COPYRIGHT");
lua_pushliteral(L, LUACMSGPACK_DESCRIPTION);
lua_setfield(L, -2, "_DESCRIPTION");
return 1;
}
/******************************************************************************
* Copyright (C) 2012 Salvatore Sanfilippo. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
******************************************************************************/