2012-02-24 15:46:25 +01:00
|
|
|
#include <math.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <assert.h>
|
|
|
|
|
|
|
|
#include "lua.h"
|
|
|
|
#include "lauxlib.h"
|
|
|
|
|
2014-04-04 16:06:33 -04:00
|
|
|
#define LUACMSGPACK_NAME "cmsgpack"
|
|
|
|
#define LUACMSGPACK_SAFE_NAME "cmsgpack_safe"
|
|
|
|
#define LUACMSGPACK_VERSION "lua-cmsgpack 0.4.0"
|
2012-02-24 15:46:25 +01:00
|
|
|
#define LUACMSGPACK_COPYRIGHT "Copyright (C) 2012, Salvatore Sanfilippo"
|
|
|
|
#define LUACMSGPACK_DESCRIPTION "MessagePack C implementation for Lua"
|
|
|
|
|
2014-04-04 16:06:33 -04:00
|
|
|
/* Allows a preprocessor directive to override MAX_NESTING */
|
|
|
|
#ifndef LUACMSGPACK_MAX_NESTING
|
2014-11-24 12:37:17 -05:00
|
|
|
#define LUACMSGPACK_MAX_NESTING 16 /* Max tables nesting. */
|
2014-04-04 16:06:33 -04:00
|
|
|
#endif
|
|
|
|
|
|
|
|
#if (_XOPEN_SOURCE >= 600 || _ISOC99_SOURCE || _POSIX_C_SOURCE >= 200112L)
|
|
|
|
#define IS_FINITE(x) isfinite(x)
|
|
|
|
#else
|
|
|
|
#define IS_FINITE(x) ((x) == (x) && (x) + 1 > (x))
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* Check if float or double can be an integer without loss of precision */
|
|
|
|
#define IS_INT_TYPE_EQUIVALENT(x, T) (IS_FINITE(x) && (T)(x) == (x))
|
|
|
|
|
|
|
|
#define IS_INT64_EQUIVALENT(x) IS_INT_TYPE_EQUIVALENT(x, int64_t)
|
|
|
|
#define IS_INT_EQUIVALENT(x) IS_INT_TYPE_EQUIVALENT(x, int)
|
|
|
|
|
2014-11-24 12:37:17 -05:00
|
|
|
/* If size of pointer is equal to a 4 byte integer, we're on 32 bits. */
|
|
|
|
#if UINTPTR_MAX == UINT_MAX
|
|
|
|
#define BITS_32 1
|
|
|
|
#else
|
|
|
|
#define BITS_32 0
|
|
|
|
#endif
|
|
|
|
|
2014-04-04 16:06:33 -04:00
|
|
|
#if LUA_VERSION_NUM < 503
|
2014-11-24 12:37:17 -05:00
|
|
|
#if BITS_32
|
|
|
|
#define lua_pushunsigned(L, n) lua_pushnumber(L, n)
|
|
|
|
#else
|
|
|
|
#define lua_pushunsigned(L, n) lua_pushinteger(L, n)
|
|
|
|
#endif
|
2014-04-04 16:06:33 -04:00
|
|
|
#endif
|
|
|
|
|
|
|
|
/* =============================================================================
|
|
|
|
* MessagePack implementation and bindings for Lua 5.1/5.2.
|
2012-02-24 15:46:25 +01:00
|
|
|
* Copyright(C) 2012 Salvatore Sanfilippo <antirez@gmail.com>
|
|
|
|
*
|
|
|
|
* 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).
|
2014-04-04 16:06:33 -04:00
|
|
|
* 04-Apr-2014 (ver 0.3.1): Lua 5.2 support and minor bug fix.
|
|
|
|
* 07-Apr-2014 (ver 0.4.0): Multiple pack/unpack, lua allocator, efficiency.
|
|
|
|
* ========================================================================== */
|
2012-02-24 15:46:25 +01:00
|
|
|
|
2014-04-04 16:06:33 -04:00
|
|
|
/* -------------------------- Endian conversion --------------------------------
|
|
|
|
* We use it only for floats and doubles, all the other conversions performed
|
2012-02-24 15:46:25 +01:00
|
|
|
* in an endian independent fashion. So the only thing we need is a function
|
2014-04-04 16:06:33 -04:00
|
|
|
* that swaps a binary string if arch is little endian (and left it untouched
|
2012-02-24 15:46:25 +01:00
|
|
|
* otherwise). */
|
|
|
|
|
|
|
|
/* Reverse memory bytes if arch is little endian. Given the conceptual
|
2014-04-04 16:06:33 -04:00
|
|
|
* simplicity of the Lua build system we prefer check for endianess at runtime.
|
2012-02-24 15:46:25 +01:00
|
|
|
* The performance difference should be acceptable. */
|
|
|
|
static void memrevifle(void *ptr, size_t len) {
|
2014-04-04 16:06:33 -04:00
|
|
|
unsigned char *p = (unsigned char *)ptr,
|
|
|
|
*e = (unsigned char *)p+len-1,
|
|
|
|
aux;
|
2012-02-24 15:46:25 +01:00
|
|
|
int test = 1;
|
|
|
|
unsigned char *testp = (unsigned char*) &test;
|
|
|
|
|
2014-11-24 12:37:17 -05:00
|
|
|
if (testp[0] == 0) return; /* Big endian, nothing to do. */
|
2012-02-24 15:46:25 +01:00
|
|
|
len /= 2;
|
|
|
|
while(len--) {
|
|
|
|
aux = *p;
|
|
|
|
*p = *e;
|
|
|
|
*e = aux;
|
|
|
|
p++;
|
|
|
|
e--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-04-04 16:06:33 -04:00
|
|
|
/* ---------------------------- String buffer ----------------------------------
|
2014-11-24 12:37:17 -05:00
|
|
|
* This is a simple implementation of string buffers. The only operation
|
2012-02-24 15:46:25 +01:00
|
|
|
* 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 {
|
2014-04-04 16:06:33 -04:00
|
|
|
lua_State *L;
|
2012-02-24 15:46:25 +01:00
|
|
|
unsigned char *b;
|
|
|
|
size_t len, free;
|
|
|
|
} mp_buf;
|
|
|
|
|
2014-04-04 16:06:33 -04:00
|
|
|
static void *mp_realloc(lua_State *L, void *target, size_t osize,size_t nsize) {
|
|
|
|
void *(*local_realloc) (void *, void *, size_t osize, size_t nsize) = NULL;
|
|
|
|
void *ud;
|
|
|
|
|
|
|
|
local_realloc = lua_getallocf(L, &ud);
|
|
|
|
|
|
|
|
return local_realloc(ud, target, osize, nsize);
|
|
|
|
}
|
|
|
|
|
|
|
|
static mp_buf *mp_buf_new(lua_State *L) {
|
|
|
|
mp_buf *buf = NULL;
|
|
|
|
|
|
|
|
/* Old size = 0; new size = sizeof(*buf) */
|
2014-11-24 12:37:17 -05:00
|
|
|
buf = (mp_buf*)mp_realloc(L, NULL, 0, sizeof(*buf));
|
2014-04-04 16:06:33 -04:00
|
|
|
|
|
|
|
buf->L = L;
|
2012-02-24 15:46:25 +01:00
|
|
|
buf->b = NULL;
|
|
|
|
buf->len = buf->free = 0;
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
2014-04-04 16:06:33 -04:00
|
|
|
static void mp_buf_append(mp_buf *buf, const unsigned char *s, size_t len) {
|
2012-02-24 15:46:25 +01:00
|
|
|
if (buf->free < len) {
|
|
|
|
size_t newlen = buf->len+len;
|
|
|
|
|
2014-04-04 16:06:33 -04:00
|
|
|
buf->b = (unsigned char*)mp_realloc(buf->L, buf->b, buf->len, newlen*2);
|
2012-02-24 15:46:25 +01:00
|
|
|
buf->free = newlen;
|
|
|
|
}
|
|
|
|
memcpy(buf->b+buf->len,s,len);
|
|
|
|
buf->len += len;
|
|
|
|
buf->free -= len;
|
|
|
|
}
|
|
|
|
|
|
|
|
void mp_buf_free(mp_buf *buf) {
|
2014-04-04 16:06:33 -04:00
|
|
|
mp_realloc(buf->L, buf->b, buf->len, 0); /* realloc to 0 = free */
|
|
|
|
mp_realloc(buf->L, buf, sizeof(*buf), 0);
|
2012-02-24 15:46:25 +01:00
|
|
|
}
|
|
|
|
|
2014-04-04 16:06:33 -04:00
|
|
|
/* ---------------------------- String cursor ----------------------------------
|
2012-02-24 15:46:25 +01:00
|
|
|
* 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
|
2014-11-24 12:37:17 -05:00
|
|
|
#define MP_CUR_ERROR_EOF 1 /* Not enough data to complete operation. */
|
2012-02-24 15:46:25 +01:00
|
|
|
#define MP_CUR_ERROR_BADFMT 2 /* Bad data format */
|
|
|
|
|
|
|
|
typedef struct mp_cur {
|
|
|
|
const unsigned char *p;
|
|
|
|
size_t left;
|
|
|
|
int err;
|
|
|
|
} mp_cur;
|
|
|
|
|
2014-04-04 16:06:33 -04:00
|
|
|
static void mp_cur_init(mp_cur *cursor, const unsigned char *s, size_t len) {
|
2012-02-24 15:46:25 +01:00
|
|
|
cursor->p = s;
|
|
|
|
cursor->left = len;
|
|
|
|
cursor->err = MP_CUR_ERROR_NONE;
|
|
|
|
}
|
|
|
|
|
|
|
|
#define mp_cur_consume(_c,_len) do { _c->p += _len; _c->left -= _len; } while(0)
|
|
|
|
|
2014-11-24 12:37:17 -05:00
|
|
|
/* When there is not enough room we set an error in the cursor and return. This
|
2012-02-24 15:46:25 +01:00
|
|
|
* 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)
|
|
|
|
|
2014-04-04 16:06:33 -04:00
|
|
|
/* ------------------------- Low level MP encoding -------------------------- */
|
2012-02-24 15:46:25 +01:00
|
|
|
|
|
|
|
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 <= 0xffff) {
|
|
|
|
hdr[0] = 0xda;
|
|
|
|
hdr[1] = (len&0xff00)>>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 <= 127) {
|
|
|
|
b[0] = n & 0x7f; /* positive fixnum */
|
|
|
|
enclen = 1;
|
|
|
|
} else if (n <= 0xff) {
|
|
|
|
b[0] = 0xcc; /* uint 8 */
|
|
|
|
b[1] = n & 0xff;
|
|
|
|
enclen = 2;
|
|
|
|
} else if (n <= 0xffff) {
|
|
|
|
b[0] = 0xcd; /* uint 16 */
|
|
|
|
b[1] = (n & 0xff00) >> 8;
|
|
|
|
b[2] = n & 0xff;
|
|
|
|
enclen = 3;
|
|
|
|
} else if (n <= 0xffffffffLL) {
|
|
|
|
b[0] = 0xce; /* uint 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] = 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 <= 15) {
|
|
|
|
b[0] = 0x90 | (n & 0xf); /* fix array */
|
|
|
|
enclen = 1;
|
|
|
|
} else if (n <= 65535) {
|
|
|
|
b[0] = 0xdc; /* array 16 */
|
|
|
|
b[1] = (n & 0xff00) >> 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 <= 15) {
|
|
|
|
b[0] = 0x80 | (n & 0xf); /* fix map */
|
|
|
|
enclen = 1;
|
|
|
|
} else if (n <= 65535) {
|
|
|
|
b[0] = 0xde; /* map 16 */
|
|
|
|
b[1] = (n & 0xff00) >> 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);
|
|
|
|
}
|
|
|
|
|
2014-04-04 16:06:33 -04:00
|
|
|
/* --------------------------- Lua types encoding --------------------------- */
|
2012-02-24 15:46:25 +01:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2014-04-04 16:06:33 -04:00
|
|
|
/* Lua 5.3 has a built in 64-bit integer type */
|
|
|
|
static void mp_encode_lua_integer(lua_State *L, mp_buf *buf) {
|
2014-11-24 12:37:17 -05:00
|
|
|
#if (LUA_VERSION_NUM < 503) && BITS_32
|
|
|
|
lua_Number i = lua_tonumber(L,-1);
|
|
|
|
#else
|
2014-04-04 16:06:33 -04:00
|
|
|
lua_Integer i = lua_tointeger(L,-1);
|
2014-11-24 12:37:17 -05:00
|
|
|
#endif
|
2014-04-04 16:06:33 -04:00
|
|
|
mp_encode_int(buf, (int64_t)i);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Lua 5.2 and lower only has 64-bit doubles, so we need to
|
|
|
|
* detect if the double may be representable as an int
|
|
|
|
* for Lua < 5.3 */
|
2012-02-24 15:46:25 +01:00
|
|
|
static void mp_encode_lua_number(lua_State *L, mp_buf *buf) {
|
|
|
|
lua_Number n = lua_tonumber(L,-1);
|
|
|
|
|
2014-04-04 16:06:33 -04:00
|
|
|
if (IS_INT64_EQUIVALENT(n)) {
|
|
|
|
mp_encode_lua_integer(L, buf);
|
2012-02-24 15:46:25 +01:00
|
|
|
} else {
|
2014-04-04 16:06:33 -04:00
|
|
|
mp_encode_double(buf,(double)n);
|
2012-02-24 15:46:25 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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) {
|
2014-04-04 16:06:33 -04:00
|
|
|
#if LUA_VERSION_NUM < 502
|
2012-02-24 15:46:25 +01:00
|
|
|
size_t len = lua_objlen(L,-1), j;
|
2014-04-04 16:06:33 -04:00
|
|
|
#else
|
|
|
|
size_t len = lua_rawlen(L,-1), j;
|
|
|
|
#endif
|
2012-02-24 15:46:25 +01:00
|
|
|
|
|
|
|
mp_encode_array(buf,len);
|
|
|
|
for (j = 1; j <= len; j++) {
|
|
|
|
lua_pushnumber(L,j);
|
|
|
|
lua_gettable(L,-2);
|
|
|
|
mp_encode_lua_type(L,buf,level+1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Convert a lua table into a message pack key-value map. */
|
|
|
|
static void mp_encode_lua_table_as_map(lua_State *L, mp_buf *buf, int level) {
|
|
|
|
size_t len = 0;
|
|
|
|
|
|
|
|
/* First step: count keys into table. No other way to do it with the
|
|
|
|
* Lua API, we need to iterate a first time. Note that an alternative
|
|
|
|
* would be to do a single run, and then hack the buffer to insert the
|
2014-11-24 12:37:17 -05:00
|
|
|
* map opcodes for message pack. Too hackish for this lib. */
|
2012-02-24 15:46:25 +01:00
|
|
|
lua_pushnil(L);
|
|
|
|
while(lua_next(L,-2)) {
|
|
|
|
lua_pop(L,1); /* remove value, keep key for next iteration. */
|
|
|
|
len++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Step two: actually encoding of the map. */
|
|
|
|
mp_encode_map(buf,len);
|
|
|
|
lua_pushnil(L);
|
|
|
|
while(lua_next(L,-2)) {
|
|
|
|
/* Stack: ... key value */
|
|
|
|
lua_pushvalue(L,-2); /* Stack: ... key value key */
|
|
|
|
mp_encode_lua_type(L,buf,level+1); /* encode key */
|
|
|
|
mp_encode_lua_type(L,buf,level+1); /* encode val */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Returns true if the Lua table on top of the stack is exclusively composed
|
|
|
|
* of keys from numerical keys from 1 up to N, with N being the total number
|
|
|
|
* of elements, without any hole in the middle. */
|
|
|
|
static int table_is_an_array(lua_State *L) {
|
2014-04-04 16:06:33 -04:00
|
|
|
int count = 0, max = 0;
|
|
|
|
#if LUA_VERSION_NUM < 503
|
2012-02-24 15:46:25 +01:00
|
|
|
lua_Number n;
|
2014-04-04 16:06:33 -04:00
|
|
|
#else
|
|
|
|
lua_Integer n;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* Stack top on function entry */
|
|
|
|
int stacktop;
|
|
|
|
|
|
|
|
stacktop = lua_gettop(L);
|
2012-02-24 15:46:25 +01:00
|
|
|
|
|
|
|
lua_pushnil(L);
|
|
|
|
while(lua_next(L,-2)) {
|
|
|
|
/* Stack: ... key value */
|
|
|
|
lua_pop(L,1); /* Stack: ... key */
|
2014-04-04 16:06:33 -04:00
|
|
|
/* The <= 0 check is valid here because we're comparing indexes. */
|
|
|
|
#if LUA_VERSION_NUM < 503
|
2014-11-24 12:37:17 -05:00
|
|
|
if ((LUA_TNUMBER != lua_type(L,-1)) || (n = lua_tonumber(L, -1)) <= 0 ||
|
|
|
|
!IS_INT_EQUIVALENT(n))
|
2014-04-04 16:06:33 -04:00
|
|
|
#else
|
2014-11-24 12:37:17 -05:00
|
|
|
if (!lua_isinteger(L,-1) || (n = lua_tointeger(L, -1)) <= 0)
|
2014-04-04 16:06:33 -04:00
|
|
|
#endif
|
2014-11-24 12:37:17 -05:00
|
|
|
{
|
2014-04-04 16:06:33 -04:00
|
|
|
lua_settop(L, stacktop);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
max = (n > max ? n : max);
|
2012-02-24 15:46:25 +01:00
|
|
|
count++;
|
|
|
|
}
|
|
|
|
/* We have the total number of elements in "count". Also we have
|
2014-04-04 16:06:33 -04:00
|
|
|
* the max index encountered in "max". We can't reach this code
|
2012-02-24 15:46:25 +01:00
|
|
|
* if there are indexes <= 0. If you also note that there can not be
|
2014-04-04 16:06:33 -04:00
|
|
|
* repeated keys into a table, you have that if max==count you are sure
|
2012-02-24 15:46:25 +01:00
|
|
|
* that there are all the keys form 1 to count (both included). */
|
2014-04-04 16:06:33 -04:00
|
|
|
lua_settop(L, stacktop);
|
|
|
|
return max == count;
|
2012-02-24 15:46:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* If the length operator returns non-zero, that is, there is at least
|
|
|
|
* an object at key '1', we serialize to message pack list. Otherwise
|
|
|
|
* we use a map. */
|
|
|
|
static void mp_encode_lua_table(lua_State *L, mp_buf *buf, int level) {
|
|
|
|
if (table_is_an_array(L))
|
|
|
|
mp_encode_lua_table_as_array(L,buf,level);
|
|
|
|
else
|
|
|
|
mp_encode_lua_table_as_map(L,buf,level);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void mp_encode_lua_null(lua_State *L, mp_buf *buf) {
|
|
|
|
unsigned char b[1];
|
2014-04-04 16:06:33 -04:00
|
|
|
(void)L;
|
2012-02-24 15:46:25 +01:00
|
|
|
|
|
|
|
b[0] = 0xc0;
|
|
|
|
mp_buf_append(buf,b,1);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void mp_encode_lua_type(lua_State *L, mp_buf *buf, int level) {
|
|
|
|
int t = lua_type(L,-1);
|
|
|
|
|
2014-11-24 12:37:17 -05:00
|
|
|
/* Limit the encoding of nested tables to a specified maximum depth, so that
|
2012-02-24 15:46:25 +01:00
|
|
|
* we survive when called against circular references in tables. */
|
|
|
|
if (t == LUA_TTABLE && level == LUACMSGPACK_MAX_NESTING) t = LUA_TNIL;
|
|
|
|
switch(t) {
|
|
|
|
case LUA_TSTRING: mp_encode_lua_string(L,buf); break;
|
|
|
|
case LUA_TBOOLEAN: mp_encode_lua_bool(L,buf); break;
|
2014-04-04 16:06:33 -04:00
|
|
|
case LUA_TNUMBER:
|
|
|
|
#if LUA_VERSION_NUM < 503
|
|
|
|
mp_encode_lua_number(L,buf); break;
|
|
|
|
#else
|
|
|
|
if (lua_isinteger(L, -1)) {
|
|
|
|
mp_encode_lua_integer(L, buf);
|
|
|
|
} else {
|
|
|
|
mp_encode_lua_number(L, buf);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
#endif
|
2012-02-24 15:46:25 +01:00
|
|
|
case LUA_TTABLE: mp_encode_lua_table(L,buf,level); break;
|
|
|
|
default: mp_encode_lua_null(L,buf); break;
|
|
|
|
}
|
|
|
|
lua_pop(L,1);
|
|
|
|
}
|
|
|
|
|
2014-04-04 16:06:33 -04:00
|
|
|
/*
|
|
|
|
* Packs all arguments as a stream for multiple upacking later.
|
|
|
|
* Returns error if no arguments provided.
|
|
|
|
*/
|
2012-02-24 15:46:25 +01:00
|
|
|
static int mp_pack(lua_State *L) {
|
2014-04-04 16:06:33 -04:00
|
|
|
int nargs = lua_gettop(L);
|
|
|
|
int i;
|
|
|
|
mp_buf *buf;
|
|
|
|
|
|
|
|
if (nargs == 0)
|
|
|
|
return luaL_argerror(L, 0, "MessagePack pack needs input.");
|
|
|
|
|
|
|
|
buf = mp_buf_new(L);
|
|
|
|
for(i = 1; i <= nargs; i++) {
|
|
|
|
/* Copy argument i to top of stack for _encode processing;
|
|
|
|
* the encode function pops it from the stack when complete. */
|
|
|
|
lua_pushvalue(L, i);
|
|
|
|
|
|
|
|
mp_encode_lua_type(L,buf,0);
|
2012-02-24 15:46:25 +01:00
|
|
|
|
2014-04-04 16:06:33 -04:00
|
|
|
lua_pushlstring(L,(char*)buf->b,buf->len);
|
|
|
|
|
|
|
|
/* Reuse the buffer for the next operation by
|
|
|
|
* setting its free count to the total buffer size
|
|
|
|
* and the current position to zero. */
|
|
|
|
buf->free += buf->len;
|
|
|
|
buf->len = 0;
|
|
|
|
}
|
2012-02-24 15:46:25 +01:00
|
|
|
mp_buf_free(buf);
|
2014-04-04 16:06:33 -04:00
|
|
|
|
|
|
|
/* Concatenate all nargs buffers together */
|
|
|
|
lua_concat(L, nargs);
|
2012-02-24 15:46:25 +01:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2014-04-04 16:06:33 -04:00
|
|
|
/* ------------------------------- Decoding --------------------------------- */
|
2012-02-24 15:46:25 +01:00
|
|
|
|
|
|
|
void mp_decode_to_lua_type(lua_State *L, mp_cur *c);
|
|
|
|
|
|
|
|
void mp_decode_to_lua_array(lua_State *L, mp_cur *c, size_t len) {
|
|
|
|
int index = 1;
|
|
|
|
|
|
|
|
lua_newtable(L);
|
|
|
|
while(len--) {
|
|
|
|
lua_pushnumber(L,index++);
|
|
|
|
mp_decode_to_lua_type(L,c);
|
|
|
|
if (c->err) return;
|
|
|
|
lua_settable(L,-3);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void mp_decode_to_lua_hash(lua_State *L, mp_cur *c, size_t len) {
|
|
|
|
lua_newtable(L);
|
|
|
|
while(len--) {
|
|
|
|
mp_decode_to_lua_type(L,c); /* key */
|
|
|
|
if (c->err) return;
|
|
|
|
mp_decode_to_lua_type(L,c); /* value */
|
|
|
|
if (c->err) return;
|
|
|
|
lua_settable(L,-3);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Decode a Message Pack raw object pointed by the string cursor 'c' to
|
|
|
|
* a Lua type, that is left as the only result on the stack. */
|
|
|
|
void mp_decode_to_lua_type(lua_State *L, mp_cur *c) {
|
|
|
|
mp_cur_need(c,1);
|
2014-04-04 16:06:33 -04:00
|
|
|
|
|
|
|
/* If we return more than 18 elements, we must resize the stack to
|
|
|
|
* fit all our return values. But, there is no way to
|
|
|
|
* determine how many objects a msgpack will unpack to up front, so
|
|
|
|
* we request a +1 larger stack on each iteration (noop if stack is
|
|
|
|
* big enough, and when stack does require resize it doubles in size) */
|
|
|
|
luaL_checkstack(L, 1,
|
|
|
|
"too many return values at once; "
|
|
|
|
"use unpack_one or unpack_limit instead.");
|
|
|
|
|
2012-02-24 15:46:25 +01:00
|
|
|
switch(c->p[0]) {
|
|
|
|
case 0xcc: /* uint 8 */
|
|
|
|
mp_cur_need(c,2);
|
2014-04-04 16:06:33 -04:00
|
|
|
lua_pushunsigned(L,c->p[1]);
|
2012-02-24 15:46:25 +01:00
|
|
|
mp_cur_consume(c,2);
|
|
|
|
break;
|
|
|
|
case 0xd0: /* int 8 */
|
|
|
|
mp_cur_need(c,2);
|
2014-04-04 16:06:33 -04:00
|
|
|
lua_pushinteger(L,(char)c->p[1]);
|
2012-02-24 15:46:25 +01:00
|
|
|
mp_cur_consume(c,2);
|
|
|
|
break;
|
|
|
|
case 0xcd: /* uint 16 */
|
|
|
|
mp_cur_need(c,3);
|
2014-04-04 16:06:33 -04:00
|
|
|
lua_pushunsigned(L,
|
2012-02-24 15:46:25 +01:00
|
|
|
(c->p[1] << 8) |
|
|
|
|
c->p[2]);
|
|
|
|
mp_cur_consume(c,3);
|
|
|
|
break;
|
|
|
|
case 0xd1: /* int 16 */
|
|
|
|
mp_cur_need(c,3);
|
2014-04-04 16:06:33 -04:00
|
|
|
lua_pushinteger(L,(int16_t)
|
2012-02-24 15:46:25 +01:00
|
|
|
(c->p[1] << 8) |
|
|
|
|
c->p[2]);
|
|
|
|
mp_cur_consume(c,3);
|
|
|
|
break;
|
|
|
|
case 0xce: /* uint 32 */
|
|
|
|
mp_cur_need(c,5);
|
2014-04-04 16:06:33 -04:00
|
|
|
lua_pushunsigned(L,
|
2012-02-24 15:46:25 +01:00
|
|
|
((uint32_t)c->p[1] << 24) |
|
|
|
|
((uint32_t)c->p[2] << 16) |
|
|
|
|
((uint32_t)c->p[3] << 8) |
|
|
|
|
(uint32_t)c->p[4]);
|
|
|
|
mp_cur_consume(c,5);
|
|
|
|
break;
|
|
|
|
case 0xd2: /* int 32 */
|
|
|
|
mp_cur_need(c,5);
|
2014-04-04 16:06:33 -04:00
|
|
|
lua_pushinteger(L,
|
2012-02-24 15:46:25 +01:00
|
|
|
((int32_t)c->p[1] << 24) |
|
|
|
|
((int32_t)c->p[2] << 16) |
|
|
|
|
((int32_t)c->p[3] << 8) |
|
|
|
|
(int32_t)c->p[4]);
|
|
|
|
mp_cur_consume(c,5);
|
|
|
|
break;
|
|
|
|
case 0xcf: /* uint 64 */
|
|
|
|
mp_cur_need(c,9);
|
2014-04-04 16:06:33 -04:00
|
|
|
lua_pushunsigned(L,
|
2012-02-24 15:46:25 +01:00
|
|
|
((uint64_t)c->p[1] << 56) |
|
|
|
|
((uint64_t)c->p[2] << 48) |
|
|
|
|
((uint64_t)c->p[3] << 40) |
|
|
|
|
((uint64_t)c->p[4] << 32) |
|
|
|
|
((uint64_t)c->p[5] << 24) |
|
|
|
|
((uint64_t)c->p[6] << 16) |
|
|
|
|
((uint64_t)c->p[7] << 8) |
|
|
|
|
(uint64_t)c->p[8]);
|
|
|
|
mp_cur_consume(c,9);
|
|
|
|
break;
|
|
|
|
case 0xd3: /* int 64 */
|
|
|
|
mp_cur_need(c,9);
|
2014-11-24 12:37:17 -05:00
|
|
|
#if LUA_VERSION_NUM < 503
|
|
|
|
lua_pushnumber(L,
|
|
|
|
#else
|
2014-04-04 16:06:33 -04:00
|
|
|
lua_pushinteger(L,
|
2014-11-24 12:37:17 -05:00
|
|
|
#endif
|
2012-02-24 15:46:25 +01:00
|
|
|
((int64_t)c->p[1] << 56) |
|
|
|
|
((int64_t)c->p[2] << 48) |
|
|
|
|
((int64_t)c->p[3] << 40) |
|
|
|
|
((int64_t)c->p[4] << 32) |
|
|
|
|
((int64_t)c->p[5] << 24) |
|
|
|
|
((int64_t)c->p[6] << 16) |
|
|
|
|
((int64_t)c->p[7] << 8) |
|
|
|
|
(int64_t)c->p[8]);
|
|
|
|
mp_cur_consume(c,9);
|
|
|
|
break;
|
|
|
|
case 0xc0: /* nil */
|
|
|
|
lua_pushnil(L);
|
|
|
|
mp_cur_consume(c,1);
|
|
|
|
break;
|
|
|
|
case 0xc3: /* true */
|
|
|
|
lua_pushboolean(L,1);
|
|
|
|
mp_cur_consume(c,1);
|
|
|
|
break;
|
|
|
|
case 0xc2: /* false */
|
|
|
|
lua_pushboolean(L,0);
|
|
|
|
mp_cur_consume(c,1);
|
|
|
|
break;
|
|
|
|
case 0xca: /* float */
|
|
|
|
mp_cur_need(c,5);
|
|
|
|
assert(sizeof(float) == 4);
|
|
|
|
{
|
|
|
|
float f;
|
|
|
|
memcpy(&f,c->p+1,4);
|
|
|
|
memrevifle(&f,4);
|
|
|
|
lua_pushnumber(L,f);
|
|
|
|
mp_cur_consume(c,5);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 0xcb: /* double */
|
|
|
|
mp_cur_need(c,9);
|
|
|
|
assert(sizeof(double) == 8);
|
|
|
|
{
|
|
|
|
double d;
|
|
|
|
memcpy(&d,c->p+1,8);
|
|
|
|
memrevifle(&d,8);
|
|
|
|
lua_pushnumber(L,d);
|
|
|
|
mp_cur_consume(c,9);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 0xda: /* raw 16 */
|
|
|
|
mp_cur_need(c,3);
|
|
|
|
{
|
|
|
|
size_t l = (c->p[1] << 8) | c->p[2];
|
|
|
|
mp_cur_need(c,3+l);
|
|
|
|
lua_pushlstring(L,(char*)c->p+3,l);
|
|
|
|
mp_cur_consume(c,3+l);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 0xdb: /* raw 32 */
|
|
|
|
mp_cur_need(c,5);
|
|
|
|
{
|
|
|
|
size_t l = (c->p[1] << 24) |
|
|
|
|
(c->p[2] << 16) |
|
|
|
|
(c->p[3] << 8) |
|
|
|
|
c->p[4];
|
|
|
|
mp_cur_need(c,5+l);
|
|
|
|
lua_pushlstring(L,(char*)c->p+5,l);
|
|
|
|
mp_cur_consume(c,5+l);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 0xdc: /* array 16 */
|
|
|
|
mp_cur_need(c,3);
|
|
|
|
{
|
|
|
|
size_t l = (c->p[1] << 8) | c->p[2];
|
|
|
|
mp_cur_consume(c,3);
|
|
|
|
mp_decode_to_lua_array(L,c,l);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 0xdd: /* array 32 */
|
|
|
|
mp_cur_need(c,5);
|
|
|
|
{
|
|
|
|
size_t l = (c->p[1] << 24) |
|
|
|
|
(c->p[2] << 16) |
|
|
|
|
(c->p[3] << 8) |
|
|
|
|
c->p[4];
|
|
|
|
mp_cur_consume(c,5);
|
|
|
|
mp_decode_to_lua_array(L,c,l);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 0xde: /* map 16 */
|
|
|
|
mp_cur_need(c,3);
|
|
|
|
{
|
|
|
|
size_t l = (c->p[1] << 8) | c->p[2];
|
|
|
|
mp_cur_consume(c,3);
|
|
|
|
mp_decode_to_lua_hash(L,c,l);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 0xdf: /* map 32 */
|
|
|
|
mp_cur_need(c,5);
|
|
|
|
{
|
|
|
|
size_t l = (c->p[1] << 24) |
|
|
|
|
(c->p[2] << 16) |
|
|
|
|
(c->p[3] << 8) |
|
|
|
|
c->p[4];
|
|
|
|
mp_cur_consume(c,5);
|
|
|
|
mp_decode_to_lua_hash(L,c,l);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default: /* types that can't be idenitified by first byte value. */
|
|
|
|
if ((c->p[0] & 0x80) == 0) { /* positive fixnum */
|
2014-04-04 16:06:33 -04:00
|
|
|
lua_pushunsigned(L,c->p[0]);
|
2012-02-24 15:46:25 +01:00
|
|
|
mp_cur_consume(c,1);
|
|
|
|
} else if ((c->p[0] & 0xe0) == 0xe0) { /* negative fixnum */
|
2014-04-04 16:06:33 -04:00
|
|
|
lua_pushinteger(L,(signed char)c->p[0]);
|
2012-02-24 15:46:25 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-04-04 16:06:33 -04:00
|
|
|
static int mp_unpack_full(lua_State *L, int limit, int offset) {
|
2012-02-24 15:46:25 +01:00
|
|
|
size_t len;
|
2014-04-04 16:06:33 -04:00
|
|
|
const char *s;
|
|
|
|
mp_cur c;
|
|
|
|
int cnt; /* Number of objects unpacked */
|
|
|
|
int decode_all = (!limit && !offset);
|
|
|
|
|
|
|
|
s = luaL_checklstring(L,1,&len); /* if no match, exits */
|
|
|
|
|
|
|
|
if (offset < 0 || limit < 0) /* requesting negative off or lim is invalid */
|
|
|
|
return luaL_error(L,
|
|
|
|
"Invalid request to unpack with offset of %d and limit of %d.",
|
|
|
|
offset, len);
|
|
|
|
else if (offset > len)
|
|
|
|
return luaL_error(L,
|
|
|
|
"Start offset %d greater than input length %d.", offset, len);
|
2012-02-24 15:46:25 +01:00
|
|
|
|
2014-04-04 16:06:33 -04:00
|
|
|
if (decode_all) limit = INT_MAX;
|
|
|
|
|
|
|
|
mp_cur_init(&c,(const unsigned char *)s+offset,len-offset);
|
|
|
|
|
|
|
|
/* We loop over the decode because this could be a stream
|
|
|
|
* of multiple top-level values serialized together */
|
|
|
|
for(cnt = 0; c.left > 0 && cnt < limit; cnt++) {
|
|
|
|
mp_decode_to_lua_type(L,&c);
|
|
|
|
|
|
|
|
if (c.err == MP_CUR_ERROR_EOF) {
|
|
|
|
return luaL_error(L,"Missing bytes in input.");
|
|
|
|
} else if (c.err == MP_CUR_ERROR_BADFMT) {
|
|
|
|
return luaL_error(L,"Bad data format in input.");
|
|
|
|
}
|
2012-02-24 15:46:25 +01:00
|
|
|
}
|
|
|
|
|
2014-04-04 16:06:33 -04:00
|
|
|
if (!decode_all) {
|
|
|
|
/* c->left is the remaining size of the input buffer.
|
|
|
|
* subtract the entire buffer size from the unprocessed size
|
|
|
|
* to get our next start offset */
|
|
|
|
int offset = len - c.left;
|
|
|
|
/* Return offset -1 when we have have processed the entire buffer. */
|
|
|
|
lua_pushinteger(L, c.left == 0 ? -1 : offset);
|
|
|
|
/* Results are returned with the arg elements still
|
|
|
|
* in place. Lua takes care of only returning
|
|
|
|
* elements above the args for us.
|
|
|
|
* In this case, we have one arg on the stack
|
|
|
|
* for this function, so we insert our first return
|
|
|
|
* value at position 2. */
|
|
|
|
lua_insert(L, 2);
|
|
|
|
cnt += 1; /* increase return count by one to make room for offset */
|
2012-02-24 15:46:25 +01:00
|
|
|
}
|
2014-04-04 16:06:33 -04:00
|
|
|
|
|
|
|
return cnt;
|
2012-02-24 15:46:25 +01:00
|
|
|
}
|
|
|
|
|
2014-04-04 16:06:33 -04:00
|
|
|
static int mp_unpack(lua_State *L) {
|
|
|
|
return mp_unpack_full(L, 0, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int mp_unpack_one(lua_State *L) {
|
|
|
|
int offset = luaL_optint(L, 2, 0);
|
|
|
|
/* Variable pop because offset may not exist */
|
|
|
|
lua_pop(L, lua_gettop(L)-1);
|
|
|
|
return mp_unpack_full(L, 1, offset);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int mp_unpack_limit(lua_State *L) {
|
|
|
|
int limit = luaL_checkint(L, 2);
|
|
|
|
int offset = luaL_optint(L, 3, 0);
|
|
|
|
/* Variable pop because offset may not exist */
|
|
|
|
lua_pop(L, lua_gettop(L)-1);
|
|
|
|
|
|
|
|
return mp_unpack_full(L, limit, offset);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int mp_safe(lua_State *L) {
|
|
|
|
int argc, err, total_results;
|
|
|
|
|
|
|
|
argc = lua_gettop(L);
|
|
|
|
|
|
|
|
/* This adds our function to the bottom of the stack
|
|
|
|
* (the "call this function" position) */
|
|
|
|
lua_pushvalue(L, lua_upvalueindex(1));
|
|
|
|
lua_insert(L, 1);
|
|
|
|
|
|
|
|
err = lua_pcall(L, argc, LUA_MULTRET, 0);
|
|
|
|
total_results = lua_gettop(L);
|
|
|
|
|
|
|
|
if (!err) {
|
|
|
|
return total_results;
|
|
|
|
} else {
|
|
|
|
lua_pushnil(L);
|
|
|
|
lua_insert(L,-2);
|
|
|
|
return 2;
|
|
|
|
}
|
|
|
|
}
|
2012-02-24 15:46:25 +01:00
|
|
|
|
2014-04-04 16:06:33 -04:00
|
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
static const struct luaL_Reg cmds[] = {
|
2012-02-24 15:46:25 +01:00
|
|
|
{"pack", mp_pack},
|
|
|
|
{"unpack", mp_unpack},
|
2014-04-04 16:06:33 -04:00
|
|
|
{"unpack_one", mp_unpack_one},
|
|
|
|
{"unpack_limit", mp_unpack_limit},
|
|
|
|
{0}
|
2012-02-24 15:46:25 +01:00
|
|
|
};
|
|
|
|
|
2014-04-04 16:06:33 -04:00
|
|
|
static int luaopen_create(lua_State *L) {
|
|
|
|
int i;
|
|
|
|
/* Manually construct our module table instead of
|
|
|
|
* relying on _register or _newlib */
|
|
|
|
lua_newtable(L);
|
|
|
|
|
|
|
|
for (i = 0; i < (sizeof(cmds)/sizeof(*cmds) - 1); i++) {
|
|
|
|
lua_pushcfunction(L, cmds[i].func);
|
|
|
|
lua_setfield(L, -2, cmds[i].name);
|
|
|
|
}
|
2012-02-24 15:46:25 +01:00
|
|
|
|
2014-04-04 16:06:33 -04:00
|
|
|
/* Add metadata */
|
|
|
|
lua_pushliteral(L, LUACMSGPACK_NAME);
|
|
|
|
lua_setfield(L, -2, "_NAME");
|
2012-02-24 15:46:25 +01:00
|
|
|
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);
|
2014-04-04 16:06:33 -04:00
|
|
|
lua_setfield(L, -2, "_DESCRIPTION");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
LUALIB_API int luaopen_cmsgpack(lua_State *L) {
|
|
|
|
luaopen_create(L);
|
|
|
|
|
|
|
|
#if LUA_VERSION_NUM < 502
|
|
|
|
/* Register name globally for 5.1 */
|
|
|
|
lua_pushvalue(L, -1);
|
|
|
|
lua_setglobal(L, LUACMSGPACK_NAME);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
LUALIB_API int luaopen_cmsgpack_safe(lua_State *L) {
|
|
|
|
int i;
|
|
|
|
|
|
|
|
luaopen_cmsgpack(L);
|
|
|
|
|
|
|
|
/* Wrap all functions in the safe handler */
|
|
|
|
for (i = 0; i < (sizeof(cmds)/sizeof(*cmds) - 1); i++) {
|
|
|
|
lua_getfield(L, -1, cmds[i].name);
|
|
|
|
lua_pushcclosure(L, mp_safe, 1);
|
|
|
|
lua_setfield(L, -2, cmds[i].name);
|
|
|
|
}
|
|
|
|
|
|
|
|
#if LUA_VERSION_NUM < 502
|
|
|
|
/* Register name globally for 5.1 */
|
|
|
|
lua_pushvalue(L, -1);
|
|
|
|
lua_setglobal(L, LUACMSGPACK_SAFE_NAME);
|
|
|
|
#endif
|
|
|
|
|
2012-02-24 15:46:25 +01:00
|
|
|
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.
|
|
|
|
******************************************************************************/
|