729 lines · 22.3 KB · C
RawDownload
#include <math.h>#include <stdlib.h>#include <stdint.h>#include <string.h>#include <assert.h> #include "lua.h"#include "lauxlib.h" #define LUACMSGPACK_VERSION     "lua-cmsgpack 0.3.0"#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. * 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). * ============================================================================ */ /* --------------------------- 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 <= 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);} /* ----------------------------- 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) {    size_t len = lua_objlen(L,-1), j;     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     * map opcodes for message pack. Too hachish for this lib. */    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) {    long count = 0, max = 0, idx = 0;    lua_Number n;     lua_pushnil(L);    while(lua_next(L,-2)) {        /* Stack: ... key value */        lua_pop(L,1); /* Stack: ... key */        if (!lua_isnumber(L,-1)) goto not_array;        n = lua_tonumber(L,-1);        idx = n;        if (idx != n || idx < 1) goto not_array;        count++;        max = idx;    }    /* We have the total number of elements in "count". Also we have     * the max index encountered in "idx". We can't reach this code     * if there are indexes <= 0. If you also note that there can not be     * repeated keys into a table, you have that if idx==count you are sure     * that there are all the keys form 1 to count (both included). */    return idx == count; not_array:    lua_pop(L,1);    return 0;} /* 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];     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);     /* Limit the encoding of nested tables to a specfiied maximum depth, so that     * 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;    case LUA_TNUMBER: mp_encode_lua_number(L,buf); break;    case LUA_TTABLE: mp_encode_lua_table(L,buf,level); break;    default: mp_encode_lua_null(L,buf); break;    }    lua_pop(L,1);} static int mp_pack(lua_State *L) {    mp_buf *buf = mp_buf_new();     mp_encode_lua_type(L,buf,0);    lua_pushlstring(L,(char*)buf->b,buf->len);    mp_buf_free(buf);    return 1;} /* --------------------------------- Decoding --------------------------------- */ 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);    switch(c->p[0]) {    case 0xcc:  /* uint 8 */        mp_cur_need(c,2);        lua_pushnumber(L,c->p[1]);        mp_cur_consume(c,2);        break;    case 0xd0:  /* int 8 */        mp_cur_need(c,2);        lua_pushnumber(L,(char)c->p[1]);        mp_cur_consume(c,2);        break;    case 0xcd:  /* uint 16 */        mp_cur_need(c,3);        lua_pushnumber(L,            (c->p[1] << 8) |             c->p[2]);        mp_cur_consume(c,3);        break;    case 0xd1:  /* int 16 */        mp_cur_need(c,3);        lua_pushnumber(L,(int16_t)            (c->p[1] << 8) |             c->p[2]);        mp_cur_consume(c,3);        break;    case 0xce:  /* uint 32 */        mp_cur_need(c,5);        lua_pushnumber(L,            ((uint32_t)c->p[1] << 24) |            ((uint32_t)c->p[2] << 16) |            ((uint32_t)c->p[3] << 8) |             (uint32_t)c->p[4]);

Lines 1–500 of 729.