aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorBjörn Linse <bjorn.linse@gmail.com>2021-09-09 18:47:03 +0200
committerGitHub <noreply@github.com>2021-09-09 18:47:03 +0200
commitd80aac3b2a3ce4b25d4f12ff64b8c947eaf75826 (patch)
treeb10dcaea8ee55a9f27b528360eb0dab50e165121 /src
parent13748512f6d6dfb5895c2233d2e07480e00eb753 (diff)
parentad8eda3f5b7db424de912ac748bce6b02e43f64f (diff)
downloadrneovim-d80aac3b2a3ce4b25d4f12ff64b8c947eaf75826.tar.gz
rneovim-d80aac3b2a3ce4b25d4f12ff64b8c947eaf75826.tar.bz2
rneovim-d80aac3b2a3ce4b25d4f12ff64b8c947eaf75826.zip
Merge pull request #15566 from bfredl/mpack
libs: vendor libmpack and libmpack-lua
Diffstat (limited to 'src')
-rw-r--r--src/mpack/LICENSE-MIT22
-rw-r--r--src/mpack/conv.c375
-rw-r--r--src/mpack/conv.h55
-rw-r--r--src/mpack/lmpack.c1215
-rw-r--r--src/mpack/lmpack.h3
-rw-r--r--src/mpack/mpack_core.c575
-rw-r--r--src/mpack/mpack_core.h87
-rw-r--r--src/mpack/object.c195
-rw-r--r--src/mpack/object.h86
-rw-r--r--src/mpack/rpc.c331
-rw-r--r--src/mpack/rpc.h83
-rw-r--r--src/nvim/CMakeLists.txt14
-rw-r--r--src/nvim/lua/executor.c18
13 files changed, 3052 insertions, 7 deletions
diff --git a/src/mpack/LICENSE-MIT b/src/mpack/LICENSE-MIT
new file mode 100644
index 0000000000..030ba872c5
--- /dev/null
+++ b/src/mpack/LICENSE-MIT
@@ -0,0 +1,22 @@
+Copyright (c) 2016 Thiago de Arruda
+
+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.
diff --git a/src/mpack/conv.c b/src/mpack/conv.c
new file mode 100644
index 0000000000..203b13fadb
--- /dev/null
+++ b/src/mpack/conv.c
@@ -0,0 +1,375 @@
+#include "conv.h"
+
+static int mpack_fits_single(double v);
+static mpack_value_t mpack_pack_ieee754(double v, unsigned m, unsigned e);
+static int mpack_is_be(void) FPURE;
+static double mpack_fmod_pow2_32(double a);
+
+
+#define POW2(n) \
+ ((double)(1 << (n / 2)) * (double)(1 << (n / 2)) * (double)(1 << (n % 2)))
+
+#define MPACK_SWAP_VALUE(val) \
+ do { \
+ mpack_uint32_t lo = val.lo; \
+ val.lo = val.hi; \
+ val.hi = lo; \
+ } while (0)
+
+MPACK_API mpack_token_t mpack_pack_nil(void)
+{
+ mpack_token_t rv;
+ rv.type = MPACK_TOKEN_NIL;
+ return rv;
+}
+
+MPACK_API mpack_token_t mpack_pack_boolean(unsigned v)
+{
+ mpack_token_t rv;
+ rv.type = MPACK_TOKEN_BOOLEAN;
+ rv.data.value.lo = v ? 1 : 0;
+ rv.data.value.hi = 0;
+ return rv;
+}
+
+MPACK_API mpack_token_t mpack_pack_uint(mpack_uintmax_t v)
+{
+ mpack_token_t rv;
+ rv.data.value.lo = v & 0xffffffff;
+ rv.data.value.hi = (mpack_uint32_t)((v >> 31) >> 1);
+ rv.type = MPACK_TOKEN_UINT;
+ return rv;
+}
+
+MPACK_API mpack_token_t mpack_pack_sint(mpack_sintmax_t v)
+{
+ if (v < 0) {
+ mpack_token_t rv;
+ mpack_uintmax_t tc = -((mpack_uintmax_t)(v + 1)) + 1;
+ tc = ~tc + 1;
+ rv = mpack_pack_uint(tc);
+ rv.type = MPACK_TOKEN_SINT;
+ return rv;
+ }
+
+ return mpack_pack_uint((mpack_uintmax_t)v);
+}
+
+MPACK_API mpack_token_t mpack_pack_float_compat(double v)
+{
+ /* ieee754 single-precision limits to determine if "v" can be fully
+ * represented in 4 bytes */
+ mpack_token_t rv;
+
+ if (mpack_fits_single(v)) {
+ rv.length = 4;
+ rv.data.value = mpack_pack_ieee754(v, 23, 8);
+ } else {
+ rv.length = 8;
+ rv.data.value = mpack_pack_ieee754(v, 52, 11);
+ }
+
+ rv.type = MPACK_TOKEN_FLOAT;
+ return rv;
+}
+
+MPACK_API mpack_token_t mpack_pack_float_fast(double v)
+{
+ /* ieee754 single-precision limits to determine if "v" can be fully
+ * represented in 4 bytes */
+ mpack_token_t rv;
+
+ if (mpack_fits_single(v)) {
+ union {
+ float f;
+ mpack_uint32_t m;
+ } conv;
+ conv.f = (float)v;
+ rv.length = 4;
+ rv.data.value.lo = conv.m;
+ rv.data.value.hi = 0;
+ } else {
+ union {
+ double d;
+ mpack_value_t m;
+ } conv;
+ conv.d = v;
+ rv.length = 8;
+ rv.data.value = conv.m;
+ if (mpack_is_be()) {
+ MPACK_SWAP_VALUE(rv.data.value);
+ }
+ }
+
+ rv.type = MPACK_TOKEN_FLOAT;
+ return rv;
+}
+
+MPACK_API mpack_token_t mpack_pack_number(double v)
+{
+ mpack_token_t tok;
+ double vabs;
+ vabs = v < 0 ? -v : v;
+ assert(v <= 9007199254740991. && v >= -9007199254740991.);
+ tok.data.value.hi = (mpack_uint32_t)(vabs / POW2(32));
+ tok.data.value.lo = (mpack_uint32_t)mpack_fmod_pow2_32(vabs);
+
+ if (v < 0) {
+ /* Compute the two's complement */
+ tok.type = MPACK_TOKEN_SINT;
+ tok.data.value.hi = ~tok.data.value.hi;
+ tok.data.value.lo = ~tok.data.value.lo + 1;
+ if (!tok.data.value.lo) tok.data.value.hi++;
+ if (tok.data.value.lo == 0 && tok.data.value.hi == 0) tok.length = 1;
+ else if (tok.data.value.lo < 0x80000000) tok.length = 8;
+ else if (tok.data.value.lo < 0xffff7fff) tok.length = 4;
+ else if (tok.data.value.lo < 0xffffff7f) tok.length = 2;
+ else tok.length = 1;
+ } else {
+ tok.type = MPACK_TOKEN_UINT;
+ if (tok.data.value.hi) tok.length = 8;
+ else if (tok.data.value.lo > 0xffff) tok.length = 4;
+ else if (tok.data.value.lo > 0xff) tok.length = 2;
+ else tok.length = 1;
+ }
+
+ if (mpack_unpack_number(tok) != v) {
+ return mpack_pack_float(v);
+ }
+
+ return tok;
+}
+
+MPACK_API mpack_token_t mpack_pack_chunk(const char *p, mpack_uint32_t l)
+{
+ mpack_token_t rv;
+ rv.type = MPACK_TOKEN_CHUNK;
+ rv.data.chunk_ptr = p;
+ rv.length = l;
+ return rv;
+}
+
+MPACK_API mpack_token_t mpack_pack_str(mpack_uint32_t l)
+{
+ mpack_token_t rv;
+ rv.type = MPACK_TOKEN_STR;
+ rv.length = l;
+ return rv;
+}
+
+MPACK_API mpack_token_t mpack_pack_bin(mpack_uint32_t l)
+{
+ mpack_token_t rv;
+ rv.type = MPACK_TOKEN_BIN;
+ rv.length = l;
+ return rv;
+}
+
+MPACK_API mpack_token_t mpack_pack_ext(int t, mpack_uint32_t l)
+{
+ mpack_token_t rv;
+ rv.type = MPACK_TOKEN_EXT;
+ rv.length = l;
+ rv.data.ext_type = t;
+ return rv;
+}
+
+MPACK_API mpack_token_t mpack_pack_array(mpack_uint32_t l)
+{
+ mpack_token_t rv;
+ rv.type = MPACK_TOKEN_ARRAY;
+ rv.length = l;
+ return rv;
+}
+
+MPACK_API mpack_token_t mpack_pack_map(mpack_uint32_t l)
+{
+ mpack_token_t rv;
+ rv.type = MPACK_TOKEN_MAP;
+ rv.length = l;
+ return rv;
+}
+
+MPACK_API bool mpack_unpack_boolean(mpack_token_t t)
+{
+ return t.data.value.lo || t.data.value.hi;
+}
+
+MPACK_API mpack_uintmax_t mpack_unpack_uint(mpack_token_t t)
+{
+ return (((mpack_uintmax_t)t.data.value.hi << 31) << 1) | t.data.value.lo;
+}
+
+/* unpack signed integer without relying on two's complement as internal
+ * representation */
+MPACK_API mpack_sintmax_t mpack_unpack_sint(mpack_token_t t)
+{
+ mpack_uint32_t hi = t.data.value.hi;
+ mpack_uint32_t lo = t.data.value.lo;
+ mpack_uintmax_t rv = lo;
+ assert(t.length <= sizeof(mpack_sintmax_t));
+
+ if (t.length == 8) {
+ rv |= (((mpack_uintmax_t)hi) << 31) << 1;
+ }
+ /* reverse the two's complement so that lo/hi contain the absolute value.
+ * note that we have to mask ~rv so that it reflects the two's complement
+ * of the appropriate byte length */
+ rv = (~rv & (((mpack_uintmax_t)1 << ((t.length * 8) - 1)) - 1)) + 1;
+ /* negate and return the absolute value, making sure mpack_sintmax_t can
+ * represent the positive cast. */
+ return -((mpack_sintmax_t)(rv - 1)) - 1;
+}
+
+MPACK_API double mpack_unpack_float_compat(mpack_token_t t)
+{
+ mpack_uint32_t sign;
+ mpack_sint32_t exponent, bias;
+ unsigned mantbits;
+ unsigned expbits;
+ double mant;
+
+ if (t.data.value.lo == 0 && t.data.value.hi == 0)
+ /* nothing to do */
+ return 0;
+
+ if (t.length == 4) mantbits = 23, expbits = 8;
+ else mantbits = 52, expbits = 11;
+ bias = (1 << (expbits - 1)) - 1;
+
+ /* restore sign/exponent/mantissa */
+ if (mantbits == 52) {
+ sign = t.data.value.hi >> 31;
+ exponent = (t.data.value.hi >> 20) & ((1 << 11) - 1);
+ mant = (t.data.value.hi & ((1 << 20) - 1)) * POW2(32);
+ mant += t.data.value.lo;
+ } else {
+ sign = t.data.value.lo >> 31;
+ exponent = (t.data.value.lo >> 23) & ((1 << 8) - 1);
+ mant = t.data.value.lo & ((1 << 23) - 1);
+ }
+
+ mant /= POW2(mantbits);
+ if (exponent) mant += 1.0; /* restore leading 1 */
+ else exponent = 1; /* subnormal */
+ exponent -= bias;
+
+ /* restore original value */
+ while (exponent > 0) mant *= 2.0, exponent--;
+ while (exponent < 0) mant /= 2.0, exponent++;
+ return mant * (sign ? -1 : 1);
+}
+
+MPACK_API double mpack_unpack_float_fast(mpack_token_t t)
+{
+ if (t.length == 4) {
+ union {
+ float f;
+ mpack_uint32_t m;
+ } conv;
+ conv.m = t.data.value.lo;
+ return conv.f;
+ } else {
+ union {
+ double d;
+ mpack_value_t m;
+ } conv;
+ conv.m = t.data.value;
+
+ if (mpack_is_be()) {
+ MPACK_SWAP_VALUE(conv.m);
+ }
+
+ return conv.d;
+ }
+}
+
+MPACK_API double mpack_unpack_number(mpack_token_t t)
+{
+ double rv;
+ mpack_uint32_t hi, lo;
+ if (t.type == MPACK_TOKEN_FLOAT) return mpack_unpack_float(t);
+ assert(t.type == MPACK_TOKEN_UINT || t.type == MPACK_TOKEN_SINT);
+ hi = t.data.value.hi;
+ lo = t.data.value.lo;
+ if (t.type == MPACK_TOKEN_SINT) {
+ /* same idea as mpack_unpack_sint, except here we shouldn't rely on
+ * mpack_uintmax_t having 64-bits, operating on the 32-bit words separately.
+ */
+ if (!hi) {
+ assert(t.length <= 4);
+ hi = 0;
+ lo = (~lo & (((mpack_uint32_t)1 << ((t.length * 8) - 1)) - 1));
+ } else {
+ hi = ~hi;
+ lo = ~lo;
+ }
+ lo++;
+ if (!lo) hi++;
+ }
+ rv = (double)lo + POW2(32) * hi;
+ return t.type == MPACK_TOKEN_SINT ? -rv : rv;
+}
+
+static int mpack_fits_single(double v)
+{
+ return (float)v == v;
+}
+
+static mpack_value_t mpack_pack_ieee754(double v, unsigned mantbits,
+ unsigned expbits)
+{
+ mpack_value_t rv = {0, 0};
+ mpack_sint32_t exponent, bias = (1 << (expbits - 1)) - 1;
+ mpack_uint32_t sign;
+ double mant;
+
+ if (v == 0) {
+ rv.lo = 0;
+ rv.hi = 0;
+ goto end;
+ }
+
+ if (v < 0) sign = 1, mant = -v;
+ else sign = 0, mant = v;
+
+ exponent = 0;
+ while (mant >= 2.0) mant /= 2.0, exponent++;
+ while (mant < 1.0 && exponent > -(bias - 1)) mant *= 2.0, exponent--;
+
+ if (mant < 1.0) exponent = -bias; /* subnormal value */
+ else mant = mant - 1.0; /* remove leading 1 */
+ exponent += bias;
+ mant *= POW2(mantbits);
+
+ if (mantbits == 52) {
+ rv.hi = (mpack_uint32_t)(mant / POW2(32));
+ rv.lo = (mpack_uint32_t)(mant - rv.hi * POW2(32));
+ rv.hi |= ((mpack_uint32_t)exponent << 20) | (sign << 31);
+ } else if (mantbits == 23) {
+ rv.hi = 0;
+ rv.lo = (mpack_uint32_t)mant;
+ rv.lo |= ((mpack_uint32_t)exponent << 23) | (sign << 31);
+ }
+
+end:
+ return rv;
+}
+
+static int mpack_is_be(void)
+{
+ union {
+ mpack_uint32_t i;
+ char c[sizeof(mpack_uint32_t)];
+ } test;
+
+ test.i = 1;
+ return test.c[0] == 0;
+}
+
+/* this simplified version of `fmod` that returns the remainder of double
+ * division by 0xffffffff, which is enough for our purposes */
+static double mpack_fmod_pow2_32(double a)
+{
+ return a - ((double)(mpack_uint32_t)(a / POW2(32)) * POW2(32));
+}
diff --git a/src/mpack/conv.h b/src/mpack/conv.h
new file mode 100644
index 0000000000..71f14a067e
--- /dev/null
+++ b/src/mpack/conv.h
@@ -0,0 +1,55 @@
+#ifndef MPACK_CONV_H
+#define MPACK_CONV_H
+
+#include "mpack_core.h"
+
+#if ULLONG_MAX == 0xffffffffffffffff
+typedef long long mpack_sintmax_t;
+typedef unsigned long long mpack_uintmax_t;
+#elif UINT64_MAX == 0xffffffffffffffff
+typedef int64_t mpack_sintmax_t;
+typedef uint64_t mpack_uintmax_t;
+#else
+typedef mpack_sint32_t mpack_sintmax_t;
+typedef mpack_uint32_t mpack_uintmax_t;
+#endif
+
+#ifndef bool
+# define bool unsigned
+#endif
+
+MPACK_API mpack_token_t mpack_pack_nil(void) FUNUSED FPURE;
+MPACK_API mpack_token_t mpack_pack_boolean(unsigned v) FUNUSED FPURE;
+MPACK_API mpack_token_t mpack_pack_uint(mpack_uintmax_t v) FUNUSED FPURE;
+MPACK_API mpack_token_t mpack_pack_sint(mpack_sintmax_t v) FUNUSED FPURE;
+MPACK_API mpack_token_t mpack_pack_float_compat(double v) FUNUSED FPURE;
+MPACK_API mpack_token_t mpack_pack_float_fast(double v) FUNUSED FPURE;
+MPACK_API mpack_token_t mpack_pack_number(double v) FUNUSED FPURE;
+MPACK_API mpack_token_t mpack_pack_chunk(const char *p, mpack_uint32_t l)
+ FUNUSED FPURE FNONULL;
+MPACK_API mpack_token_t mpack_pack_str(mpack_uint32_t l) FUNUSED FPURE;
+MPACK_API mpack_token_t mpack_pack_bin(mpack_uint32_t l) FUNUSED FPURE;
+MPACK_API mpack_token_t mpack_pack_ext(int type, mpack_uint32_t l)
+ FUNUSED FPURE;
+MPACK_API mpack_token_t mpack_pack_array(mpack_uint32_t l) FUNUSED FPURE;
+MPACK_API mpack_token_t mpack_pack_map(mpack_uint32_t l) FUNUSED FPURE;
+MPACK_API bool mpack_unpack_boolean(mpack_token_t t) FUNUSED FPURE;
+MPACK_API mpack_uintmax_t mpack_unpack_uint(mpack_token_t t) FUNUSED FPURE;
+MPACK_API mpack_sintmax_t mpack_unpack_sint(mpack_token_t t) FUNUSED FPURE;
+MPACK_API double mpack_unpack_float_fast(mpack_token_t t) FUNUSED FPURE;
+MPACK_API double mpack_unpack_float_compat(mpack_token_t t) FUNUSED FPURE;
+MPACK_API double mpack_unpack_number(mpack_token_t t) FUNUSED FPURE;
+
+/* The mpack_{pack,unpack}_float_fast functions should work in 99% of the
+ * platforms. When compiling for a platform where floats don't use ieee754 as
+ * the internal format, pass
+ * -Dmpack_{pack,unpack}_float=mpack_{pack,unpack}_float_compat to the
+ * compiler.*/
+#ifndef mpack_pack_float
+# define mpack_pack_float mpack_pack_float_fast
+#endif
+#ifndef mpack_unpack_float
+# define mpack_unpack_float mpack_unpack_float_fast
+#endif
+
+#endif /* MPACK_CONV_H */
diff --git a/src/mpack/lmpack.c b/src/mpack/lmpack.c
new file mode 100644
index 0000000000..99207246c8
--- /dev/null
+++ b/src/mpack/lmpack.c
@@ -0,0 +1,1215 @@
+/*
+ * This module exports three classes, and each instance of those classes has its
+ * own private registry for temporary reference storage(keeping state between
+ * calls). A private registry makes managing memory much easier since all we
+ * have to do is call luaL_unref passing the registry reference when the
+ * instance is collected by the __gc metamethod.
+ *
+ * This private registry is manipulated with `lmpack_ref` / `lmpack_unref` /
+ * `lmpack_geti`, which are analogous to `luaL_ref` / `luaL_unref` /
+ * `lua_rawgeti` but operate on the private registry passed as argument.
+ *
+ * In order to simplify debug registry leaks during normal operation(with the
+ * leak_test.lua script), these `lmpack_*` registry functions will target the
+ * normal lua registry when MPACK_DEBUG_REGISTRY_LEAK is defined during
+ * compilation.
+ */
+#define LUA_LIB
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include <lauxlib.h>
+#include <lua.h>
+#include <luaconf.h>
+
+#include "nvim/macros.h"
+
+#include "lmpack.h"
+
+#include "rpc.h"
+
+#define UNPACKER_META_NAME "mpack.Unpacker"
+#define PACKER_META_NAME "mpack.Packer"
+#define SESSION_META_NAME "mpack.Session"
+#define NIL_NAME "mpack.NIL"
+#define EMPTY_DICT_NAME "mpack.empty_dict"
+
+/*
+ * TODO(tarruda): When targeting lua 5.3 and being compiled with `long long`
+ * support(not -ansi), we should make use of lua 64 bit integers for
+ * representing msgpack integers, since `double` can't represent the full range.
+ */
+
+#ifndef luaL_reg
+/* Taken from Lua5.1's lauxlib.h */
+#define luaL_reg luaL_Reg
+#endif
+
+#if LUA_VERSION_NUM > 501
+#ifndef luaL_register
+#define luaL_register(L,n,f) luaL_setfuncs(L,f,0)
+#endif
+#endif
+
+typedef struct {
+ lua_State *L;
+ mpack_parser_t *parser;
+ int reg, ext, unpacking, mtdict;
+ char *string_buffer;
+} Unpacker;
+
+typedef struct {
+ lua_State *L;
+ mpack_parser_t *parser;
+ int reg, ext, root, packing, mtdict;
+ int is_bin, is_bin_fn;
+} Packer;
+
+typedef struct {
+ lua_State *L;
+ int reg;
+ mpack_rpc_session_t *session;
+ struct {
+ int type;
+ mpack_rpc_message_t msg;
+ int method_or_error;
+ int args_or_result;
+ } unpacked;
+ int unpacker;
+} Session;
+
+static int lmpack_ref(lua_State *L, int reg)
+{
+#ifdef MPACK_DEBUG_REGISTRY_LEAK
+ return luaL_ref(L, LUA_REGISTRYINDEX);
+#else
+ int rv;
+ lua_rawgeti(L, LUA_REGISTRYINDEX, reg);
+ lua_pushvalue(L, -2);
+ rv = luaL_ref(L, -2);
+ lua_pop(L, 2);
+ return rv;
+#endif
+}
+
+static void lmpack_unref(lua_State *L, int reg, int ref)
+{
+#ifdef MPACK_DEBUG_REGISTRY_LEAK
+ luaL_unref(L, LUA_REGISTRYINDEX, ref);
+#else
+ lua_rawgeti(L, LUA_REGISTRYINDEX, reg);
+ luaL_unref(L, -1, ref);
+ lua_pop(L, 1);
+#endif
+}
+
+static void lmpack_geti(lua_State *L, int reg, int ref)
+{
+#ifdef MPACK_DEBUG_REGISTRY_LEAK
+ lua_rawgeti(L, LUA_REGISTRYINDEX, ref);
+#else
+ lua_rawgeti(L, LUA_REGISTRYINDEX, reg);
+ lua_rawgeti(L, -1, ref);
+ lua_replace(L, -2);
+#endif
+}
+
+/* make a shallow copy of the table on stack and remove it after the copy is
+ * done */
+static void lmpack_shallow_copy(lua_State *L)
+{
+ lua_newtable(L);
+ lua_pushnil(L);
+ while (lua_next(L, -3)) {
+ lua_pushvalue(L, -2);
+ lua_insert(L, -2);
+ lua_settable(L, -4);
+ }
+ lua_remove(L, -2);
+}
+
+static mpack_parser_t *lmpack_grow_parser(mpack_parser_t *parser)
+{
+ mpack_parser_t *old = parser;
+ mpack_uint32_t new_capacity = old->capacity * 2;
+ parser = malloc(MPACK_PARSER_STRUCT_SIZE(new_capacity));
+ if (!parser) goto end;
+ mpack_parser_init(parser, new_capacity);
+ mpack_parser_copy(parser, old);
+ free(old);
+end:
+ return parser;
+}
+
+static mpack_rpc_session_t *lmpack_grow_session(mpack_rpc_session_t *session)
+{
+ mpack_rpc_session_t *old = session;
+ mpack_uint32_t new_capacity = old->capacity * 2;
+ session = malloc(MPACK_RPC_SESSION_STRUCT_SIZE(new_capacity));
+ if (!session) goto end;
+ mpack_rpc_session_init(session, new_capacity);
+ mpack_rpc_session_copy(session, old);
+ free(old);
+end:
+ return session;
+}
+
+static Unpacker *lmpack_check_unpacker(lua_State *L, int index)
+{
+ return luaL_checkudata(L, index, UNPACKER_META_NAME);
+}
+
+static Packer *lmpack_check_packer(lua_State *L, int index)
+{
+ return luaL_checkudata(L, index, PACKER_META_NAME);
+}
+
+static Session *lmpack_check_session(lua_State *L, int index)
+{
+ return luaL_checkudata(L, index, SESSION_META_NAME);
+}
+
+static int lmpack_isnil(lua_State *L, int index)
+{
+ int rv;
+ if (!lua_isuserdata(L, index)) return 0;
+ lua_getfield(L, LUA_REGISTRYINDEX, NIL_NAME);
+ rv = lua_rawequal(L, -1, -2);
+ lua_pop(L, 1);
+ return rv;
+}
+
+static int lmpack_isunpacker(lua_State *L, int index)
+{
+ int rv;
+ if (!lua_isuserdata(L, index) || !lua_getmetatable(L, index)) return 0;
+ luaL_getmetatable(L, UNPACKER_META_NAME);
+ rv = lua_rawequal(L, -1, -2);
+ lua_pop(L, 2);
+ return rv;
+}
+
+static void lmpack_pushnil(lua_State *L)
+{
+ lua_getfield(L, LUA_REGISTRYINDEX, NIL_NAME);
+}
+
+/* adapted from
+ * https://github.com/antirez/lua-cmsgpack/blob/master/lua_cmsgpack.c */
+static mpack_uint32_t lmpack_objlen(lua_State *L, int *is_array)
+{
+ size_t len, max;
+ int isarr, type;
+ lua_Number n;
+#ifndef NDEBUG
+ int top = lua_gettop(L);
+ assert(top);
+#endif
+
+ if ((type = lua_type(L, -1)) != LUA_TTABLE) {
+#if LUA_VERSION_NUM >= 502
+ len = lua_rawlen(L, -1);
+#elif LUA_VERSION_NUM == 501
+ len = lua_objlen(L, -1);
+#else
+ #error You have either broken or too old Lua installation. This library requires Lua>=5.1
+#endif
+ goto end;
+ }
+
+ /* count the number of keys and determine if it is an array */
+ len = 0;
+ max = 0;
+ isarr = 1;
+ lua_pushnil(L);
+
+ while (lua_next(L, -2)) {
+ lua_pop(L, 1); /* pop value */
+ isarr = isarr
+ && lua_isnumber(L, -1) /* lua number */
+ && (n = lua_tonumber(L, -1)) > 0 /* greater than 0 */
+ && (size_t)n == n; /* and integer */
+ max = isarr && (size_t)n > max ? (size_t)n : max;
+ len++;
+ }
+
+ // when len==0, the caller should guess the type!
+ if (len > 0) {
+ *is_array = isarr && max == len;
+ }
+
+end:
+ if ((size_t)-1 > (mpack_uint32_t)-1 && len > (mpack_uint32_t)-1)
+ /* msgpack spec doesn't allow lengths > 32 bits */
+ len = (mpack_uint32_t)-1;
+ assert(top == lua_gettop(L));
+ return (mpack_uint32_t)len;
+}
+
+static int lmpack_unpacker_new(lua_State *L)
+{
+ Unpacker *rv;
+
+ if (lua_gettop(L) > 1)
+ return luaL_error(L, "expecting at most 1 table argument");
+
+ rv = lua_newuserdata(L, sizeof(*rv));
+ rv->parser = malloc(sizeof(*rv->parser));
+ if (!rv->parser) return luaL_error(L, "Failed to allocate memory");
+ mpack_parser_init(rv->parser, 0);
+ rv->parser->data.p = rv;
+ rv->string_buffer = NULL;
+ rv->L = L;
+ rv->unpacking = 0;
+ luaL_getmetatable(L, UNPACKER_META_NAME);
+ lua_setmetatable(L, -2);
+
+#ifndef MPACK_DEBUG_REGISTRY_LEAK
+ lua_newtable(L);
+ rv->reg = luaL_ref(L, LUA_REGISTRYINDEX);
+#endif
+ rv->ext = LUA_NOREF;
+
+ lua_getfield(L, LUA_REGISTRYINDEX, EMPTY_DICT_NAME);
+ rv->mtdict = lmpack_ref(L, rv->reg);
+
+ if (lua_istable(L, 1)) {
+ /* parse options */
+ lua_getfield(L, 1, "ext");
+ if (!lua_isnil(L, -1)) {
+ if (!lua_istable(L, -1))
+ return luaL_error(L, "\"ext\" option must be a table");
+ lmpack_shallow_copy(L);
+ }
+ rv->ext = lmpack_ref(L, rv->reg);
+ }
+
+ return 1;
+}
+
+static int lmpack_unpacker_delete(lua_State *L)
+{
+ Unpacker *unpacker = lmpack_check_unpacker(L, 1);
+ if (unpacker->ext != LUA_NOREF)
+ lmpack_unref(L, unpacker->reg, unpacker->ext);
+#ifndef MPACK_DEBUG_REGISTRY_LEAK
+ luaL_unref(L, LUA_REGISTRYINDEX, unpacker->reg);
+#endif
+ free(unpacker->parser);
+ return 0;
+}
+
+static void lmpack_parse_enter(mpack_parser_t *parser, mpack_node_t *node)
+{
+ Unpacker *unpacker = parser->data.p;
+ lua_State *L = unpacker->L;
+
+ switch (node->tok.type) {
+ case MPACK_TOKEN_NIL:
+ lmpack_pushnil(L); break;
+ case MPACK_TOKEN_BOOLEAN:
+ lua_pushboolean(L, (int)mpack_unpack_boolean(node->tok)); break;
+ case MPACK_TOKEN_UINT:
+ case MPACK_TOKEN_SINT:
+ case MPACK_TOKEN_FLOAT:
+ lua_pushnumber(L, mpack_unpack_number(node->tok)); break;
+ case MPACK_TOKEN_CHUNK:
+ assert(unpacker->string_buffer);
+ memcpy(unpacker->string_buffer + MPACK_PARENT_NODE(node)->pos,
+ node->tok.data.chunk_ptr, node->tok.length);
+ break;
+ case MPACK_TOKEN_BIN:
+ case MPACK_TOKEN_STR:
+ case MPACK_TOKEN_EXT:
+ unpacker->string_buffer = malloc(node->tok.length);
+ if (!unpacker->string_buffer) luaL_error(L, "Failed to allocate memory");
+ break;
+ case MPACK_TOKEN_ARRAY:
+ case MPACK_TOKEN_MAP:
+ lua_newtable(L);
+ node->data[0].i = lmpack_ref(L, unpacker->reg);
+ break;
+ }
+}
+
+static void lmpack_parse_exit(mpack_parser_t *parser, mpack_node_t *node)
+{
+ Unpacker *unpacker = parser->data.p;
+ lua_State *L = unpacker->L;
+ mpack_node_t *parent = MPACK_PARENT_NODE(node);
+
+ switch (node->tok.type) {
+ case MPACK_TOKEN_BIN:
+ case MPACK_TOKEN_STR:
+ case MPACK_TOKEN_EXT:
+ lua_pushlstring(L, unpacker->string_buffer, node->tok.length);
+ free(unpacker->string_buffer);
+ unpacker->string_buffer = NULL;
+ if (node->tok.type == MPACK_TOKEN_EXT && unpacker->ext != LUA_NOREF) {
+ /* check if there's a handler for this type */
+ lmpack_geti(L, unpacker->reg, unpacker->ext);
+ lua_rawgeti(L, -1, node->tok.data.ext_type);
+ if (lua_isfunction(L, -1)) {
+ /* stack:
+ *
+ * -1: ext unpacker function
+ * -2: ext unpackers table
+ * -3: ext string
+ *
+ * We want to call the ext unpacker function with the type and string
+ * as arguments, so push those now
+ */
+ lua_pushinteger(L, node->tok.data.ext_type);
+ lua_pushvalue(L, -4);
+ lua_call(L, 2, 1);
+ /* stack:
+ *
+ * -1: returned object
+ * -2: ext unpackers table
+ * -3: ext string
+ */
+ lua_replace(L, -3);
+ } else {
+ /* the last lua_rawgeti should have pushed nil on the stack,
+ * remove it */
+ lua_pop(L, 1);
+ }
+ /* pop the ext unpackers table */
+ lua_pop(L, 1);
+ }
+ break;
+ case MPACK_TOKEN_ARRAY:
+ case MPACK_TOKEN_MAP:
+ lmpack_geti(L, unpacker->reg, (int)node->data[0].i);
+ lmpack_unref(L, unpacker->reg, (int)node->data[0].i);
+ if (node->key_visited == 0 && node->tok.type == MPACK_TOKEN_MAP) {
+ lmpack_geti(L, unpacker->reg, unpacker->mtdict); // [table, mtdict]
+ lua_setmetatable(L, -2); // [table]
+ }
+
+ break;
+ default:
+ break;
+ }
+
+ if (parent && parent->tok.type < MPACK_TOKEN_BIN) {
+ /* At this point the parsed object is on the stack. Add it to the parent
+ * container. First put the container on the stack. */
+ lmpack_geti(L, unpacker->reg, (int)parent->data[0].i);
+
+ if (parent->tok.type == MPACK_TOKEN_ARRAY) {
+ /* Array, save the value on key equal to `parent->pos` */
+ lua_pushnumber(L, (lua_Number)parent->pos);
+ lua_pushvalue(L, -3);
+ lua_settable(L, -3);
+ } else {
+ assert(parent->tok.type == MPACK_TOKEN_MAP);
+ if (parent->key_visited) {
+ /* save the key on the registry */
+ lua_pushvalue(L, -2);
+ parent->data[1].i = lmpack_ref(L, unpacker->reg);
+ } else {
+ /* set the key/value pair */
+ lmpack_geti(L, unpacker->reg, (int)parent->data[1].i);
+ lmpack_unref(L, unpacker->reg, (int)parent->data[1].i);
+ lua_pushvalue(L, -3);
+ lua_settable(L, -3);
+ }
+ }
+ lua_pop(L, 2); /* pop the container/object */
+ }
+}
+
+static int lmpack_unpacker_unpack_str(lua_State *L, Unpacker *unpacker,
+ const char **str, size_t *len)
+{
+ int rv;
+
+ if (unpacker->unpacking) {
+ return luaL_error(L, "Unpacker instance already working. Use another "
+ "Unpacker or the module's \"unpack\" function if you "
+ "need to unpack from the ext handler");
+ }
+
+ do {
+ unpacker->unpacking = 1;
+ rv = mpack_parse(unpacker->parser, str, len, lmpack_parse_enter,
+ lmpack_parse_exit);
+ unpacker->unpacking = 0;
+
+ if (rv == MPACK_NOMEM) {
+ unpacker->parser = lmpack_grow_parser(unpacker->parser);
+ if (!unpacker->parser) {
+ unpacker->unpacking = 0;
+ return luaL_error(L, "failed to grow Unpacker capacity");
+ }
+ }
+ } while (rv == MPACK_NOMEM);
+
+ if (rv == MPACK_ERROR)
+ return luaL_error(L, "invalid msgpack string");
+
+ return rv;
+}
+
+static int lmpack_unpacker_unpack(lua_State *L)
+{
+ int result, argc;
+ lua_Number startpos;
+ size_t len, offset;
+ const char *str, *str_init;
+ Unpacker *unpacker;
+
+ if ((argc = lua_gettop(L)) > 3 || argc < 2)
+ return luaL_error(L, "expecting between 2 and 3 arguments");
+
+ unpacker = lmpack_check_unpacker(L, 1);
+ unpacker->L = L;
+
+ str_init = str = luaL_checklstring(L, 2, &len);
+ startpos = lua_gettop(L) == 3 ? luaL_checknumber(L, 3) : 1;
+
+ luaL_argcheck(L, startpos > 0, 3,
+ "start position must be greater than zero");
+ luaL_argcheck(L, (size_t)startpos == startpos, 3,
+ "start position must be an integer");
+ luaL_argcheck(L, (size_t)startpos <= len, 3,
+ "start position must be less than or equal to the input string length");
+
+ offset = (size_t)startpos - 1 ;
+ str += offset;
+ len -= offset;
+ result = lmpack_unpacker_unpack_str(L, unpacker, &str, &len);
+
+ if (result == MPACK_EOF)
+ /* if we hit EOF, return nil as the object */
+ lua_pushnil(L);
+
+ /* also return the new position in the input string */
+ lua_pushinteger(L, str - str_init + 1);
+ assert(lua_gettop(L) == argc + 2);
+ return 2;
+}
+
+static int lmpack_packer_new(lua_State *L)
+{
+ Packer *rv;
+
+ if (lua_gettop(L) > 1)
+ return luaL_error(L, "expecting at most 1 table argument");
+
+ rv = lua_newuserdata(L, sizeof(*rv));
+ rv->parser = malloc(sizeof(*rv->parser));
+ if (!rv->parser) return luaL_error(L, "failed to allocate parser memory");
+ mpack_parser_init(rv->parser, 0);
+ rv->parser->data.p = rv;
+ rv->L = L;
+ rv->packing = 0;
+ rv->is_bin = 0;
+ rv->is_bin_fn = LUA_NOREF;
+ luaL_getmetatable(L, PACKER_META_NAME);
+ lua_setmetatable(L, -2);
+
+#ifndef MPACK_DEBUG_REGISTRY_LEAK
+ lua_newtable(L);
+ rv->reg = luaL_ref(L, LUA_REGISTRYINDEX);
+#endif
+ rv->ext = LUA_NOREF;
+
+ lua_getfield(L, LUA_REGISTRYINDEX, EMPTY_DICT_NAME);
+ rv->mtdict = lmpack_ref(L, rv->reg);
+
+ if (lua_istable(L, 1)) {
+ /* parse options */
+ lua_getfield(L, 1, "ext");
+ if (!lua_isnil(L, -1)) {
+ if (!lua_istable(L, -1))
+ return luaL_error(L, "\"ext\" option must be a table");
+ lmpack_shallow_copy(L);
+ }
+ rv->ext = lmpack_ref(L, rv->reg);
+ lua_getfield(L, 1, "is_bin");
+ if (!lua_isnil(L, -1)) {
+ if (!lua_isboolean(L, -1) && !lua_isfunction(L, -1))
+ return luaL_error(L,
+ "\"is_bin\" option must be a boolean or function");
+ rv->is_bin = lua_toboolean(L, -1);
+ if (lua_isfunction(L, -1)) rv->is_bin_fn = lmpack_ref(L, rv->reg);
+ else lua_pop(L, 1);
+ } else {
+ lua_pop(L, 1);
+ }
+
+ }
+
+ return 1;
+}
+
+static int lmpack_packer_delete(lua_State *L)
+{
+ Packer *packer = lmpack_check_packer(L, 1);
+ if (packer->ext != LUA_NOREF)
+ lmpack_unref(L, packer->reg, packer->ext);
+#ifndef MPACK_DEBUG_REGISTRY_LEAK
+ luaL_unref(L, LUA_REGISTRYINDEX, packer->reg);
+#endif
+ free(packer->parser);
+ return 0;
+}
+
+static void lmpack_unparse_enter(mpack_parser_t *parser, mpack_node_t *node)
+{
+ int type;
+ Packer *packer = parser->data.p;
+ lua_State *L = packer->L;
+ mpack_node_t *parent = MPACK_PARENT_NODE(node);
+
+ if (parent) {
+ /* get the parent */
+ lmpack_geti(L, packer->reg, (int)parent->data[0].i);
+
+ if (parent->tok.type > MPACK_TOKEN_MAP) {
+ /* strings are a special case, they are packed as single child chunk
+ * node */
+ const char *str = lua_tolstring(L, -1, NULL);
+ node->tok = mpack_pack_chunk(str, parent->tok.length);
+ lua_pop(L, 1);
+ return;
+ }
+
+ if (parent->tok.type == MPACK_TOKEN_ARRAY) {
+ /* push the next index */
+ lua_pushnumber(L, (lua_Number)(parent->pos + 1));
+ /* push the element */
+ lua_gettable(L, -2);
+ } else if (parent->tok.type == MPACK_TOKEN_MAP) {
+ int result;
+ /* push the previous key */
+ lmpack_geti(L, packer->reg, (int)parent->data[1].i);
+ /* push the pair */
+ result = lua_next(L, -2);
+ assert(result); /* should not be here if the map was fully processed */
+ if (parent->key_visited) {
+ /* release the current key */
+ lmpack_unref(L, packer->reg, (int)parent->data[1].i);
+ /* push key to the top */
+ lua_pushvalue(L, -2);
+ /* set the key for the next iteration, leaving value on top */
+ parent->data[1].i = lmpack_ref(L, packer->reg);
+ /* replace key by the value */
+ lua_replace(L, -2);
+ } else {
+ /* pop value */
+ lua_pop(L, 1);
+ }
+ }
+ /* remove parent, leaving only the object which will be serialized */
+ lua_remove(L, -2);
+ } else {
+ /* root object */
+ lmpack_geti(L, packer->reg, packer->root);
+ }
+
+ type = lua_type(L, -1);
+
+ switch (type) {
+ case LUA_TBOOLEAN:
+ node->tok = mpack_pack_boolean((unsigned)lua_toboolean(L, -1));
+ break;
+ case LUA_TNUMBER:
+ node->tok = mpack_pack_number(lua_tonumber(L, -1));
+ break;
+ case LUA_TSTRING: {
+ int is_bin = packer->is_bin;
+ if (is_bin && packer->is_bin_fn != LUA_NOREF) {
+ lmpack_geti(L, packer->reg, packer->is_bin_fn);
+ lua_pushvalue(L, -2);
+ lua_call(L, 1, 1);
+ is_bin = lua_toboolean(L, -1);
+ lua_pop(L, 1);
+ }
+ if (is_bin) node->tok = mpack_pack_bin(lmpack_objlen(L, NULL));
+ else node->tok = mpack_pack_str(lmpack_objlen(L, NULL));
+ break;
+ }
+ case LUA_TTABLE: {
+ mpack_uint32_t len;
+ mpack_node_t *n;
+
+ int has_meta = lua_getmetatable(L, -1);
+ if (packer->ext != LUA_NOREF && has_meta) {
+ /* check if there's a handler for this metatable */
+ lmpack_geti(L, packer->reg, packer->ext);
+ lua_pushvalue(L, -2);
+ lua_gettable(L, -2);
+ if (lua_isfunction(L, -1)) {
+ lua_Number ext = -1;
+ /* stack:
+ *
+ * -1: ext packer function
+ * -2: ext packers table
+ * -3: metatable
+ * -4: original object
+ *
+ * We want to call the ext packer function with the original object as
+ * argument, so push it on the top
+ */
+ lua_pushvalue(L, -4);
+ /* handler should return type code and string */
+ lua_call(L, 1, 2);
+ if (!lua_isnumber(L, -2) || (ext = lua_tonumber(L, -2)) < 0
+ || ext > 127 || (int)ext != ext)
+ luaL_error(L,
+ "the first result from ext packer must be an integer "
+ "between 0 and 127");
+ if (!lua_isstring(L, -1))
+ luaL_error(L,
+ "the second result from ext packer must be a string");
+ node->tok = mpack_pack_ext((int)ext, lmpack_objlen(L, NULL));
+ /* stack:
+ *
+ * -1: ext string
+ * -2: ext type
+ * -3: ext packers table
+ * -4: metatable
+ * -5: original table
+ *
+ * We want to leave only the returned ext string, so
+ * replace -5 with the string and pop 3
+ */
+ lua_replace(L, -5);
+ lua_pop(L, 3);
+ break; /* done */
+ } else {
+ /* stack:
+ *
+ * -1: ext packers table
+ * -2: metatable
+ * -3: original table
+ *
+ * We want to leave only the original table and metatable since they
+ * will be handled below, so pop 1
+ */
+ lua_pop(L, 1);
+ }
+ }
+
+ int is_array = 1;
+ if (has_meta) {
+ // stack: [table, metatable]
+ if (packer->mtdict != LUA_NOREF) {
+ lmpack_geti(L, packer->reg, packer->mtdict); // [table, metatable, mtdict]
+ is_array = !lua_rawequal(L, -1, -2);
+ lua_pop(L, 1); // [table, metatable];
+ }
+ lua_pop(L, 1); // [table]
+ }
+
+ /* check for cycles */
+ n = node;
+ while ((n = MPACK_PARENT_NODE(n))) {
+ lmpack_geti(L, packer->reg, (int)n->data[0].i);
+ if (lua_rawequal(L, -1, -2)) {
+ /* break out of cycles with NIL */
+ node->tok = mpack_pack_nil();
+ lua_pop(L, 2);
+ lmpack_pushnil(L);
+ goto end;
+ }
+ lua_pop(L, 1);
+ }
+
+ len = lmpack_objlen(L, &is_array);
+ if (is_array) {
+ node->tok = mpack_pack_array(len);
+ } else {
+ node->tok = mpack_pack_map(len);
+ /* save nil as the previous key to start iteration */
+ node->data[1].i = LUA_REFNIL;
+ }
+ break;
+ }
+ case LUA_TUSERDATA:
+ if (lmpack_isnil(L, -1)) {
+ node->tok = mpack_pack_nil();
+ break;
+ }
+ FALLTHROUGH;
+ default:
+ {
+ /* #define FMT */
+ char errmsg[50];
+ snprintf(errmsg, 50, "can't serialize object of type %d", type);
+ luaL_error(L, errmsg);
+ }
+ }
+
+end:
+ node->data[0].i = lmpack_ref(L, packer->reg);
+}
+
+static void lmpack_unparse_exit(mpack_parser_t *parser, mpack_node_t *node)
+{
+ Packer *packer = parser->data.p;
+ lua_State *L = packer->L;
+ if (node->tok.type != MPACK_TOKEN_CHUNK) {
+ /* release the object */
+ lmpack_unref(L, packer->reg, (int)node->data[0].i);
+ if (node->tok.type == MPACK_TOKEN_MAP)
+ lmpack_unref(L, packer->reg, (int)node->data[1].i);
+ }
+}
+
+static int lmpack_packer_pack(lua_State *L)
+{
+ char *b;
+ size_t bl;
+ int result, argc;
+ Packer *packer;
+ luaL_Buffer buffer;
+
+ if ((argc = lua_gettop(L)) != 2)
+ return luaL_error(L, "expecting exactly 2 arguments");
+
+ packer = lmpack_check_packer(L, 1);
+ packer->L = L;
+ packer->root = lmpack_ref(L, packer->reg);
+ luaL_buffinit(L, &buffer);
+ b = luaL_prepbuffer(&buffer);
+ bl = LUAL_BUFFERSIZE;
+
+ if (packer->packing) {
+ return luaL_error(L, "Packer instance already working. Use another Packer "
+ "or the module's \"pack\" function if you need to "
+ "pack from the ext handler");
+ }
+
+ do {
+ size_t bl_init = bl;
+ packer->packing = 1;
+ result = mpack_unparse(packer->parser, &b, &bl, lmpack_unparse_enter,
+ lmpack_unparse_exit);
+ packer->packing = 0;
+
+ if (result == MPACK_NOMEM) {
+ packer->parser = lmpack_grow_parser(packer->parser);
+ if (!packer->parser) {
+ packer->packing = 0;
+ return luaL_error(L, "Failed to grow Packer capacity");
+ }
+ }
+
+ luaL_addsize(&buffer, bl_init - bl);
+
+ if (!bl) {
+ /* buffer empty, resize */
+ b = luaL_prepbuffer(&buffer);
+ bl = LUAL_BUFFERSIZE;
+ }
+ } while (result == MPACK_EOF || result == MPACK_NOMEM);
+
+ lmpack_unref(L, packer->reg, packer->root);
+ luaL_pushresult(&buffer);
+ assert(lua_gettop(L) == argc);
+ return 1;
+}
+
+static int lmpack_session_new(lua_State *L)
+{
+ Session *rv = lua_newuserdata(L, sizeof(*rv));
+ rv->session = malloc(sizeof(*rv->session));
+ if (!rv->session) return luaL_error(L, "Failed to allocate memory");
+ mpack_rpc_session_init(rv->session, 0);
+ rv->L = L;
+ luaL_getmetatable(L, SESSION_META_NAME);
+ lua_setmetatable(L, -2);
+#ifndef MPACK_DEBUG_REGISTRY_LEAK
+ lua_newtable(L);
+ rv->reg = luaL_ref(L, LUA_REGISTRYINDEX);
+#endif
+ rv->unpacker = LUA_REFNIL;
+ rv->unpacked.args_or_result = LUA_NOREF;
+ rv->unpacked.method_or_error = LUA_NOREF;
+ rv->unpacked.type = MPACK_EOF;
+
+ if (lua_istable(L, 1)) {
+ /* parse options */
+ lua_getfield(L, 1, "unpack");
+ if (!lmpack_isunpacker(L, -1)) {
+ return luaL_error(L,
+ "\"unpack\" option must be a " UNPACKER_META_NAME " instance");
+ }
+ rv->unpacker = lmpack_ref(L, rv->reg);
+ }
+
+ return 1;
+}
+
+static int lmpack_session_delete(lua_State *L)
+{
+ Session *session = lmpack_check_session(L, 1);
+ lmpack_unref(L, session->reg, session->unpacker);
+#ifndef MPACK_DEBUG_REGISTRY_LEAK
+ luaL_unref(L, LUA_REGISTRYINDEX, session->reg);
+#endif
+ free(session->session);
+ return 0;
+}
+
+static int lmpack_session_receive(lua_State *L)
+{
+ int argc, done, rcount = 3;
+ lua_Number startpos;
+ size_t len;
+ const char *str, *str_init;
+ Session *session;
+ Unpacker *unpacker = NULL;
+
+ if ((argc = lua_gettop(L)) > 3 || argc < 2)
+ return luaL_error(L, "expecting between 2 and 3 arguments");
+
+ session = lmpack_check_session(L, 1);
+ str_init = str = luaL_checklstring(L, 2, &len);
+ startpos = lua_gettop(L) == 3 ? luaL_checknumber(L, 3) : 1;
+
+ luaL_argcheck(L, startpos > 0, 3,
+ "start position must be greater than zero");
+ luaL_argcheck(L, (size_t)startpos == startpos, 3,
+ "start position must be an integer");
+ luaL_argcheck(L, (size_t)startpos <= len, 3,
+ "start position must be less than or equal to the input string length");
+
+ str += (size_t)startpos - 1;
+
+ if (session->unpacker != LUA_REFNIL) {
+ lmpack_geti(L, session->reg, session->unpacker);
+ unpacker = lmpack_check_unpacker(L, -1);
+ unpacker->L = L;
+ rcount += 2;
+ lua_pop(L, 1);
+ }
+
+ for (;;) {
+ int result;
+
+ if (session->unpacked.type == MPACK_EOF) {
+ session->unpacked.type =
+ mpack_rpc_receive(session->session, &str, &len, &session->unpacked.msg);
+
+ if (!unpacker || session->unpacked.type == MPACK_EOF)
+ break;
+ }
+
+ result = lmpack_unpacker_unpack_str(L, unpacker, &str, &len);
+
+ if (result == MPACK_EOF) break;
+
+ if (session->unpacked.method_or_error == LUA_NOREF) {
+ session->unpacked.method_or_error = lmpack_ref(L, session->reg);
+ } else {
+ session->unpacked.args_or_result = lmpack_ref(L, session->reg);
+ break;
+ }
+ }
+
+ done = session->unpacked.type != MPACK_EOF
+ && (session->unpacked.args_or_result != LUA_NOREF || !unpacker);
+
+ if (!done) {
+ lua_pushnil(L);
+ lua_pushnil(L);
+ if (unpacker) {
+ lua_pushnil(L);
+ lua_pushnil(L);
+ }
+ goto end;
+ }
+
+ switch (session->unpacked.type) {
+ case MPACK_RPC_REQUEST:
+ lua_pushstring(L, "request");
+ lua_pushnumber(L, session->unpacked.msg.id);
+ break;
+ case MPACK_RPC_RESPONSE:
+ lua_pushstring(L, "response");
+ lmpack_geti(L, session->reg, (int)session->unpacked.msg.data.i);
+ break;
+ case MPACK_RPC_NOTIFICATION:
+ lua_pushstring(L, "notification");
+ lua_pushnil(L);
+ break;
+ default:
+ /* In most cases the only sane thing to do when receiving invalid
+ * msgpack-rpc is to close the connection, so handle all errors with
+ * this generic message. Later may add more detailed information. */
+ return luaL_error(L, "invalid msgpack-rpc string");
+ }
+
+ session->unpacked.type = MPACK_EOF;
+
+ if (unpacker) {
+ lmpack_geti(L, session->reg, session->unpacked.method_or_error);
+ lmpack_geti(L, session->reg, session->unpacked.args_or_result);
+ lmpack_unref(L, session->reg, session->unpacked.method_or_error);
+ lmpack_unref(L, session->reg, session->unpacked.args_or_result);
+ session->unpacked.method_or_error = LUA_NOREF;
+ session->unpacked.args_or_result = LUA_NOREF;
+ }
+
+end:
+ lua_pushinteger(L, str - str_init + 1);
+ return rcount;
+}
+
+static int lmpack_session_request(lua_State *L)
+{
+ int result;
+ char buf[16], *b = buf;
+ size_t bl = sizeof(buf);
+ Session *session;
+ mpack_data_t data;
+
+ if (lua_gettop(L) > 2 || lua_gettop(L) < 1)
+ return luaL_error(L, "expecting 1 or 2 arguments");
+
+ session = lmpack_check_session(L, 1);
+ data.i = lua_isnoneornil(L, 2) ? LUA_NOREF : lmpack_ref(L, session->reg);
+ do {
+ result = mpack_rpc_request(session->session, &b, &bl, data);
+ if (result == MPACK_NOMEM) {
+ session->session = lmpack_grow_session(session->session);
+ if (!session->session)
+ return luaL_error(L, "Failed to grow Session capacity");
+ }
+ } while (result == MPACK_NOMEM);
+
+ assert(result == MPACK_OK);
+ lua_pushlstring(L, buf, sizeof(buf) - bl);
+ return 1;
+}
+
+static int lmpack_session_reply(lua_State *L)
+{
+ int result;
+ char buf[16], *b = buf;
+ size_t bl = sizeof(buf);
+ Session *session;
+ lua_Number id;
+
+ if (lua_gettop(L) != 2)
+ return luaL_error(L, "expecting exactly 2 arguments");
+
+ session = lmpack_check_session(L, 1);
+ id = lua_tonumber(L, 2);
+ luaL_argcheck(L, ((size_t)id == id && id >= 0 && id <= 0xffffffff), 2,
+ "invalid request id");
+ result = mpack_rpc_reply(session->session, &b, &bl, (mpack_uint32_t)id);
+ assert(result == MPACK_OK);
+ lua_pushlstring(L, buf, sizeof(buf) - bl);
+ return 1;
+}
+
+static int lmpack_session_notify(lua_State *L)
+{
+ int result;
+ char buf[16], *b = buf;
+ size_t bl = sizeof(buf);
+ Session *session;
+
+ if (lua_gettop(L) != 1)
+ return luaL_error(L, "expecting exactly 1 argument");
+
+ session = lmpack_check_session(L, 1);
+ result = mpack_rpc_notify(session->session, &b, &bl);
+ assert(result == MPACK_OK);
+ lua_pushlstring(L, buf, sizeof(buf) - bl);
+ return 1;
+}
+
+static int lmpack_nil_tostring(lua_State* L)
+{
+ lua_pushfstring(L, NIL_NAME, lua_topointer(L, 1));
+ return 1;
+}
+
+static int lmpack_unpack(lua_State *L)
+{
+ int result;
+ size_t len;
+ const char *str;
+ Unpacker unpacker;
+ mpack_parser_t parser;
+
+ if (lua_gettop(L) != 1)
+ return luaL_error(L, "expecting exactly 1 argument");
+
+ str = luaL_checklstring(L, 1, &len);
+
+ /* initialize unpacker */
+ lua_newtable(L);
+ unpacker.reg = luaL_ref(L, LUA_REGISTRYINDEX);
+ unpacker.ext = LUA_NOREF;
+ unpacker.parser = &parser;
+ mpack_parser_init(unpacker.parser, 0);
+ unpacker.parser->data.p = &unpacker;
+ unpacker.string_buffer = NULL;
+ unpacker.L = L;
+
+ lua_getfield(L, LUA_REGISTRYINDEX, EMPTY_DICT_NAME);
+ unpacker.mtdict = lmpack_ref(L, unpacker.reg);
+
+ result = mpack_parse(&parser, &str, &len, lmpack_parse_enter,
+ lmpack_parse_exit);
+
+ luaL_unref(L, LUA_REGISTRYINDEX, unpacker.reg);
+
+ if (result == MPACK_NOMEM)
+ return luaL_error(L, "object was too deep to unpack");
+ else if (result == MPACK_EOF)
+ return luaL_error(L, "incomplete msgpack string");
+ else if (result == MPACK_ERROR)
+ return luaL_error(L, "invalid msgpack string");
+ else if (result == MPACK_OK && len)
+ return luaL_error(L, "trailing data in msgpack string");
+
+ assert(result == MPACK_OK);
+ return 1;
+}
+
+static int lmpack_pack(lua_State *L)
+{
+ char *b;
+ size_t bl;
+ int result;
+ Packer packer;
+ mpack_parser_t parser;
+ luaL_Buffer buffer;
+
+ if (lua_gettop(L) != 1)
+ return luaL_error(L, "expecting exactly 1 argument");
+
+ /* initialize packer */
+ lua_newtable(L);
+ packer.reg = luaL_ref(L, LUA_REGISTRYINDEX);
+ packer.ext = LUA_NOREF;
+ packer.parser = &parser;
+ mpack_parser_init(packer.parser, 0);
+ packer.parser->data.p = &packer;
+ packer.is_bin = 0;
+ packer.L = L;
+ packer.root = lmpack_ref(L, packer.reg);
+
+ lua_getfield(L, LUA_REGISTRYINDEX, EMPTY_DICT_NAME);
+ packer.mtdict = lmpack_ref(L, packer.reg);
+
+
+ luaL_buffinit(L, &buffer);
+ b = luaL_prepbuffer(&buffer);
+ bl = LUAL_BUFFERSIZE;
+
+ do {
+ size_t bl_init = bl;
+ result = mpack_unparse(packer.parser, &b, &bl, lmpack_unparse_enter,
+ lmpack_unparse_exit);
+
+ if (result == MPACK_NOMEM) {
+ lmpack_unref(L, packer.reg, packer.root);
+ luaL_unref(L, LUA_REGISTRYINDEX, packer.reg);
+ return luaL_error(L, "object was too deep to pack");
+ }
+
+ luaL_addsize(&buffer, bl_init - bl);
+
+ if (!bl) {
+ /* buffer empty, resize */
+ b = luaL_prepbuffer(&buffer);
+ bl = LUAL_BUFFERSIZE;
+ }
+ } while (result == MPACK_EOF);
+
+ lmpack_unref(L, packer.reg, packer.root);
+ luaL_unref(L, LUA_REGISTRYINDEX, packer.reg);
+ luaL_pushresult(&buffer);
+ return 1;
+}
+
+static const luaL_reg unpacker_methods[] = {
+ {"__call", lmpack_unpacker_unpack},
+ {"__gc", lmpack_unpacker_delete},
+ {NULL, NULL}
+};
+
+static const luaL_reg packer_methods[] = {
+ {"__call", lmpack_packer_pack},
+ {"__gc", lmpack_packer_delete},
+ {NULL, NULL}
+};
+
+static const luaL_reg session_methods[] = {
+ {"receive", lmpack_session_receive},
+ {"request", lmpack_session_request},
+ {"reply", lmpack_session_reply},
+ {"notify", lmpack_session_notify},
+ {"__gc", lmpack_session_delete},
+ {NULL, NULL}
+};
+
+static const luaL_reg mpack_functions[] = {
+ {"Unpacker", lmpack_unpacker_new},
+ {"Packer", lmpack_packer_new},
+ {"Session", lmpack_session_new},
+ {"unpack", lmpack_unpack},
+ {"pack", lmpack_pack},
+ {NULL, NULL}
+};
+
+int luaopen_mpack(lua_State *L)
+{
+ /* Unpacker */
+ luaL_newmetatable(L, UNPACKER_META_NAME);
+ lua_pushvalue(L, -1);
+ lua_setfield(L, -2, "__index");
+ luaL_register(L, NULL, unpacker_methods);
+ lua_pop(L, 1);
+ /* Packer */
+ luaL_newmetatable(L, PACKER_META_NAME);
+ lua_pushvalue(L, -1);
+ lua_setfield(L, -2, "__index");
+ luaL_register(L, NULL, packer_methods);
+ lua_pop(L, 1);
+ /* Session */
+ luaL_newmetatable(L, SESSION_META_NAME);
+ lua_pushvalue(L, -1);
+ lua_setfield(L, -2, "__index");
+ luaL_register(L, NULL, session_methods);
+ lua_pop(L, 1);
+ /* NIL */
+ /* Check if NIL is already stored in the registry */
+ lua_getfield(L, LUA_REGISTRYINDEX, NIL_NAME);
+ /* If it isn't, create it */
+ if (lua_isnil(L, -1)) {
+ /* Use a constant userdata to represent NIL */
+ (void)lua_newuserdata(L, sizeof(void *));
+ /* Create a metatable for NIL userdata */
+ lua_createtable(L, 0, 1);
+ lua_pushstring(L, "__tostring");
+ lua_pushcfunction(L, lmpack_nil_tostring);
+ lua_settable(L, -3);
+ /* Assign the metatable to the userdata object */
+ lua_setmetatable(L, -2);
+ /* Save NIL on the registry so we can access it easily from other functions */
+ lua_setfield(L, LUA_REGISTRYINDEX, NIL_NAME);
+ }
+
+ lua_pop(L, 1);
+
+ /* module */
+ lua_newtable(L);
+ luaL_register(L, NULL, mpack_functions);
+ /* save NIL on the module */
+ lua_getfield(L, LUA_REGISTRYINDEX, NIL_NAME);
+ lua_setfield(L, -2, "NIL");
+ return 1;
+}
diff --git a/src/mpack/lmpack.h b/src/mpack/lmpack.h
new file mode 100644
index 0000000000..e35f40fab6
--- /dev/null
+++ b/src/mpack/lmpack.h
@@ -0,0 +1,3 @@
+#include <lua.h>
+
+int luaopen_mpack(lua_State *L);
diff --git a/src/mpack/mpack_core.c b/src/mpack/mpack_core.c
new file mode 100644
index 0000000000..0ad09bd46a
--- /dev/null
+++ b/src/mpack/mpack_core.c
@@ -0,0 +1,575 @@
+#include <string.h>
+
+#include "mpack_core.h"
+
+#define UNUSED(p) (void)p;
+#define ADVANCE(buf, buflen) ((*buflen)--, (unsigned char)*((*buf)++))
+#define TLEN(val, range_start) ((mpack_uint32_t)(1 << (val - range_start)))
+#ifndef MIN
+# define MIN(X, Y) ((X) < (Y) ? (X) : (Y))
+#endif
+
+static int mpack_rtoken(const char **buf, size_t *buflen,
+ mpack_token_t *tok);
+static int mpack_rpending(const char **b, size_t *nl, mpack_tokbuf_t *tb);
+static int mpack_rvalue(mpack_token_type_t t, mpack_uint32_t l,
+ const char **b, size_t *bl, mpack_token_t *tok);
+static int mpack_rblob(mpack_token_type_t t, mpack_uint32_t l,
+ const char **b, size_t *bl, mpack_token_t *tok);
+static int mpack_wtoken(const mpack_token_t *tok, char **b, size_t *bl);
+static int mpack_wpending(char **b, size_t *bl, mpack_tokbuf_t *tb);
+static int mpack_wpint(char **b, size_t *bl, mpack_value_t v);
+static int mpack_wnint(char **b, size_t *bl, mpack_value_t v);
+static int mpack_wfloat(char **b, size_t *bl, const mpack_token_t *v);
+static int mpack_wstr(char **buf, size_t *buflen, mpack_uint32_t len);
+static int mpack_wbin(char **buf, size_t *buflen, mpack_uint32_t len);
+static int mpack_wext(char **buf, size_t *buflen, int type,
+ mpack_uint32_t len);
+static int mpack_warray(char **buf, size_t *buflen, mpack_uint32_t len);
+static int mpack_wmap(char **buf, size_t *buflen, mpack_uint32_t len);
+static int mpack_w1(char **b, size_t *bl, mpack_uint32_t v);
+static int mpack_w2(char **b, size_t *bl, mpack_uint32_t v);
+static int mpack_w4(char **b, size_t *bl, mpack_uint32_t v);
+static mpack_value_t mpack_byte(unsigned char b);
+static int mpack_value(mpack_token_type_t t, mpack_uint32_t l,
+ mpack_value_t v, mpack_token_t *tok);
+static int mpack_blob(mpack_token_type_t t, mpack_uint32_t l, int et,
+ mpack_token_t *tok);
+
+MPACK_API void mpack_tokbuf_init(mpack_tokbuf_t *tokbuf)
+{
+ tokbuf->ppos = 0;
+ tokbuf->plen = 0;
+ tokbuf->passthrough = 0;
+}
+
+MPACK_API int mpack_read(mpack_tokbuf_t *tokbuf, const char **buf,
+ size_t *buflen, mpack_token_t *tok)
+{
+ int status;
+ size_t initial_ppos, ptrlen, advanced;
+ const char *ptr, *ptr_save;
+ assert(*buf && *buflen);
+
+ if (tokbuf->passthrough) {
+ /* pass data from str/bin/ext directly as a MPACK_TOKEN_CHUNK, adjusting
+ * *buf and *buflen */
+ tok->type = MPACK_TOKEN_CHUNK;
+ tok->data.chunk_ptr = *buf;
+ tok->length = MIN((mpack_uint32_t)*buflen, tokbuf->passthrough);
+ tokbuf->passthrough -= tok->length;
+ *buf += tok->length;
+ *buflen -= tok->length;
+ goto done;
+ }
+
+ initial_ppos = tokbuf->ppos;
+
+ if (tokbuf->plen) {
+ if (!mpack_rpending(buf, buflen, tokbuf)) {
+ return MPACK_EOF;
+ }
+ ptr = tokbuf->pending;
+ ptrlen = tokbuf->ppos;
+ } else {
+ ptr = *buf;
+ ptrlen = *buflen;
+ }
+
+ ptr_save = ptr;
+
+ if ((status = mpack_rtoken(&ptr, &ptrlen, tok))) {
+ if (status != MPACK_EOF) return MPACK_ERROR;
+ /* need more data */
+ assert(!tokbuf->plen);
+ /* read the remainder of *buf to tokbuf->pending so it can be parsed
+ * later with more data. only required when tokbuf->plen == 0 or else
+ * it would have been done already. */
+ tokbuf->plen = tok->length + 1;
+ assert(tokbuf->plen <= sizeof(tokbuf->pending));
+ tokbuf->ppos = 0;
+ status = mpack_rpending(buf, buflen, tokbuf);
+ assert(!status);
+ return MPACK_EOF;
+ }
+
+ advanced = (size_t)(ptr - ptr_save) - initial_ppos;
+ tokbuf->plen = tokbuf->ppos = 0;
+ *buflen -= advanced;
+ *buf += advanced;
+
+ if (tok->type > MPACK_TOKEN_MAP) {
+ tokbuf->passthrough = tok->length;
+ }
+
+done:
+ return MPACK_OK;
+}
+
+MPACK_API int mpack_write(mpack_tokbuf_t *tokbuf, char **buf, size_t *buflen,
+ const mpack_token_t *t)
+{
+ int status;
+ char *ptr;
+ size_t ptrlen;
+ mpack_token_t tok = tokbuf->plen ? tokbuf->pending_tok : *t;
+ assert(*buf && *buflen);
+
+ if (tok.type == MPACK_TOKEN_CHUNK) {
+ size_t written, pending, count;
+ if (!tokbuf->plen) tokbuf->ppos = 0;
+ written = tokbuf->ppos;
+ pending = tok.length - written;
+ count = MIN(pending, *buflen);
+ memcpy(*buf, tok.data.chunk_ptr + written, count);
+ *buf += count;
+ *buflen -= count;
+ tokbuf->ppos += count;
+ tokbuf->plen = count == pending ? 0 : tok.length;
+ if (count == pending) {
+ return MPACK_OK;
+ } else {
+ tokbuf->pending_tok = tok;
+ return MPACK_EOF;
+ }
+ }
+
+ if (tokbuf->plen) return mpack_wpending(buf, buflen, tokbuf);
+
+ if (*buflen < MPACK_MAX_TOKEN_LEN) {
+ ptr = tokbuf->pending;
+ ptrlen = sizeof(tokbuf->pending);
+ } else {
+ ptr = *buf;
+ ptrlen = *buflen;
+ }
+
+ if ((status = mpack_wtoken(&tok, &ptr, &ptrlen))) return status;
+
+ if (*buflen < MPACK_MAX_TOKEN_LEN) {
+ size_t toklen = sizeof(tokbuf->pending) - ptrlen;
+ size_t write_cnt = MIN(toklen, *buflen);
+ memcpy(*buf, tokbuf->pending, write_cnt);
+ *buf += write_cnt;
+ *buflen -= write_cnt;
+ if (write_cnt < toklen) {
+ assert(!*buflen);
+ tokbuf->plen = toklen;
+ tokbuf->ppos = write_cnt;
+ tokbuf->pending_tok = tok;
+ return MPACK_EOF;
+ }
+ } else {
+ *buflen -= (size_t)(ptr - *buf);
+ *buf = ptr;
+ }
+
+ return MPACK_OK;
+}
+
+static int mpack_rtoken(const char **buf, size_t *buflen,
+ mpack_token_t *tok)
+{
+ unsigned char t = ADVANCE(buf, buflen);
+ if (t < 0x80) {
+ /* positive fixint */
+ return mpack_value(MPACK_TOKEN_UINT, 1, mpack_byte(t), tok);
+ } else if (t < 0x90) {
+ /* fixmap */
+ return mpack_blob(MPACK_TOKEN_MAP, t & 0xf, 0, tok);
+ } else if (t < 0xa0) {
+ /* fixarray */
+ return mpack_blob(MPACK_TOKEN_ARRAY, t & 0xf, 0, tok);
+ } else if (t < 0xc0) {
+ /* fixstr */
+ return mpack_blob(MPACK_TOKEN_STR, t & 0x1f, 0, tok);
+ } else if (t < 0xe0) {
+ switch (t) {
+ case 0xc0: /* nil */
+ return mpack_value(MPACK_TOKEN_NIL, 0, mpack_byte(0), tok);
+ case 0xc2: /* false */
+ return mpack_value(MPACK_TOKEN_BOOLEAN, 1, mpack_byte(0), tok);
+ case 0xc3: /* true */
+ return mpack_value(MPACK_TOKEN_BOOLEAN, 1, mpack_byte(1), tok);
+ case 0xc4: /* bin 8 */
+ case 0xc5: /* bin 16 */
+ case 0xc6: /* bin 32 */
+ return mpack_rblob(MPACK_TOKEN_BIN, TLEN(t, 0xc4), buf, buflen, tok);
+ case 0xc7: /* ext 8 */
+ case 0xc8: /* ext 16 */
+ case 0xc9: /* ext 32 */
+ return mpack_rblob(MPACK_TOKEN_EXT, TLEN(t, 0xc7), buf, buflen, tok);
+ case 0xca: /* float 32 */
+ case 0xcb: /* float 64 */
+ return mpack_rvalue(MPACK_TOKEN_FLOAT, TLEN(t, 0xc8), buf, buflen, tok);
+ case 0xcc: /* uint 8 */
+ case 0xcd: /* uint 16 */
+ case 0xce: /* uint 32 */
+ case 0xcf: /* uint 64 */
+ return mpack_rvalue(MPACK_TOKEN_UINT, TLEN(t, 0xcc), buf, buflen, tok);
+ case 0xd0: /* int 8 */
+ case 0xd1: /* int 16 */
+ case 0xd2: /* int 32 */
+ case 0xd3: /* int 64 */
+ return mpack_rvalue(MPACK_TOKEN_SINT, TLEN(t, 0xd0), buf, buflen, tok);
+ case 0xd4: /* fixext 1 */
+ case 0xd5: /* fixext 2 */
+ case 0xd6: /* fixext 4 */
+ case 0xd7: /* fixext 8 */
+ case 0xd8: /* fixext 16 */
+ if (*buflen == 0) {
+ /* require only one extra byte for the type code */
+ tok->length = 1;
+ return MPACK_EOF;
+ }
+ tok->length = TLEN(t, 0xd4);
+ tok->type = MPACK_TOKEN_EXT;
+ tok->data.ext_type = ADVANCE(buf, buflen);
+ return MPACK_OK;
+ case 0xd9: /* str 8 */
+ case 0xda: /* str 16 */
+ case 0xdb: /* str 32 */
+ return mpack_rblob(MPACK_TOKEN_STR, TLEN(t, 0xd9), buf, buflen, tok);
+ case 0xdc: /* array 16 */
+ case 0xdd: /* array 32 */
+ return mpack_rblob(MPACK_TOKEN_ARRAY, TLEN(t, 0xdb), buf, buflen, tok);
+ case 0xde: /* map 16 */
+ case 0xdf: /* map 32 */
+ return mpack_rblob(MPACK_TOKEN_MAP, TLEN(t, 0xdd), buf, buflen, tok);
+ default:
+ return MPACK_ERROR;
+ }
+ } else {
+ /* negative fixint */
+ return mpack_value(MPACK_TOKEN_SINT, 1, mpack_byte(t), tok);
+ }
+}
+
+static int mpack_rpending(const char **buf, size_t *buflen,
+ mpack_tokbuf_t *state)
+{
+ size_t count;
+ assert(state->ppos < state->plen);
+ count = MIN(state->plen - state->ppos, *buflen);
+ memcpy(state->pending + state->ppos, *buf, count);
+ state->ppos += count;
+ if (state->ppos < state->plen) {
+ /* consume buffer since no token will be parsed yet. */
+ *buf += *buflen;
+ *buflen = 0;
+ return 0;
+ }
+ return 1;
+}
+
+static int mpack_rvalue(mpack_token_type_t type, mpack_uint32_t remaining,
+ const char **buf, size_t *buflen, mpack_token_t *tok)
+{
+ if (*buflen < remaining) {
+ tok->length = remaining;
+ return MPACK_EOF;
+ }
+
+ mpack_value(type, remaining, mpack_byte(0), tok);
+
+ while (remaining) {
+ mpack_uint32_t byte = ADVANCE(buf, buflen), byte_idx, byte_shift;
+ byte_idx = (mpack_uint32_t)--remaining;
+ byte_shift = (byte_idx % 4) * 8;
+ tok->data.value.lo |= byte << byte_shift;
+ if (remaining == 4) {
+ /* unpacked the first half of a 8-byte value, shift what was parsed to the
+ * "hi" field and reset "lo" for the trailing 4 bytes. */
+ tok->data.value.hi = tok->data.value.lo;
+ tok->data.value.lo = 0;
+ }
+ }
+
+ if (type == MPACK_TOKEN_SINT) {
+ mpack_uint32_t hi = tok->data.value.hi;
+ mpack_uint32_t lo = tok->data.value.lo;
+ mpack_uint32_t msb = (tok->length == 8 && hi >> 31) ||
+ (tok->length == 4 && lo >> 31) ||
+ (tok->length == 2 && lo >> 15) ||
+ (tok->length == 1 && lo >> 7);
+ if (!msb) {
+ tok->type = MPACK_TOKEN_UINT;
+ }
+ }
+
+ return MPACK_OK;
+}
+
+static int mpack_rblob(mpack_token_type_t type, mpack_uint32_t tlen,
+ const char **buf, size_t *buflen, mpack_token_t *tok)
+{
+ mpack_token_t l;
+ mpack_uint32_t required = tlen + (type == MPACK_TOKEN_EXT ? 1 : 0);
+
+ if (*buflen < required) {
+ tok->length = required;
+ return MPACK_EOF;
+ }
+
+ l.data.value.lo = 0;
+ mpack_rvalue(MPACK_TOKEN_UINT, tlen, buf, buflen, &l);
+ tok->type = type;
+ tok->length = l.data.value.lo;
+
+ if (type == MPACK_TOKEN_EXT) {
+ tok->data.ext_type = ADVANCE(buf, buflen);
+ }
+
+ return MPACK_OK;
+}
+
+static int mpack_wtoken(const mpack_token_t *tok, char **buf,
+ size_t *buflen)
+{
+ switch (tok->type) {
+ case MPACK_TOKEN_NIL:
+ return mpack_w1(buf, buflen, 0xc0);
+ case MPACK_TOKEN_BOOLEAN:
+ return mpack_w1(buf, buflen, tok->data.value.lo ? 0xc3 : 0xc2);
+ case MPACK_TOKEN_UINT:
+ return mpack_wpint(buf, buflen, tok->data.value);
+ case MPACK_TOKEN_SINT:
+ return mpack_wnint(buf, buflen, tok->data.value);
+ case MPACK_TOKEN_FLOAT:
+ return mpack_wfloat(buf, buflen, tok);
+ case MPACK_TOKEN_BIN:
+ return mpack_wbin(buf, buflen, tok->length);
+ case MPACK_TOKEN_STR:
+ return mpack_wstr(buf, buflen, tok->length);
+ case MPACK_TOKEN_EXT:
+ return mpack_wext(buf, buflen, tok->data.ext_type, tok->length);
+ case MPACK_TOKEN_ARRAY:
+ return mpack_warray(buf, buflen, tok->length);
+ case MPACK_TOKEN_MAP:
+ return mpack_wmap(buf, buflen, tok->length);
+ default:
+ return MPACK_ERROR;
+ }
+}
+
+static int mpack_wpending(char **buf, size_t *buflen, mpack_tokbuf_t *state)
+{
+ size_t count;
+ assert(state->ppos < state->plen);
+ count = MIN(state->plen - state->ppos, *buflen);
+ memcpy(*buf, state->pending + state->ppos, count);
+ state->ppos += count;
+ *buf += count;
+ *buflen -= count;
+ if (state->ppos == state->plen) {
+ state->plen = 0;
+ return MPACK_OK;
+ }
+ return MPACK_EOF;
+}
+
+static int mpack_wpint(char **buf, size_t *buflen, mpack_value_t val)
+{
+ mpack_uint32_t hi = val.hi;
+ mpack_uint32_t lo = val.lo;
+
+ if (hi) {
+ /* uint 64 */
+ return mpack_w1(buf, buflen, 0xcf) ||
+ mpack_w4(buf, buflen, hi) ||
+ mpack_w4(buf, buflen, lo);
+ } else if (lo > 0xffff) {
+ /* uint 32 */
+ return mpack_w1(buf, buflen, 0xce) ||
+ mpack_w4(buf, buflen, lo);
+ } else if (lo > 0xff) {
+ /* uint 16 */
+ return mpack_w1(buf, buflen, 0xcd) ||
+ mpack_w2(buf, buflen, lo);
+ } else if (lo > 0x7f) {
+ /* uint 8 */
+ return mpack_w1(buf, buflen, 0xcc) ||
+ mpack_w1(buf, buflen, lo);
+ } else {
+ return mpack_w1(buf, buflen, lo);
+ }
+}
+
+static int mpack_wnint(char **buf, size_t *buflen, mpack_value_t val)
+{
+ mpack_uint32_t hi = val.hi;
+ mpack_uint32_t lo = val.lo;
+
+ if (lo < 0x80000000) {
+ /* int 64 */
+ return mpack_w1(buf, buflen, 0xd3) ||
+ mpack_w4(buf, buflen, hi) ||
+ mpack_w4(buf, buflen, lo);
+ } else if (lo < 0xffff7fff) {
+ /* int 32 */
+ return mpack_w1(buf, buflen, 0xd2) ||
+ mpack_w4(buf, buflen, lo);
+ } else if (lo < 0xffffff7f) {
+ /* int 16 */
+ return mpack_w1(buf, buflen, 0xd1) ||
+ mpack_w2(buf, buflen, lo);
+ } else if (lo < 0xffffffe0) {
+ /* int 8 */
+ return mpack_w1(buf, buflen, 0xd0) ||
+ mpack_w1(buf, buflen, lo);
+ } else {
+ /* negative fixint */
+ return mpack_w1(buf, buflen, (mpack_uint32_t)(0x100 + lo));
+ }
+}
+
+static int mpack_wfloat(char **buf, size_t *buflen,
+ const mpack_token_t *tok)
+{
+ if (tok->length == 4) {
+ return mpack_w1(buf, buflen, 0xca) ||
+ mpack_w4(buf, buflen, tok->data.value.lo);
+ } else if (tok->length == 8) {
+ return mpack_w1(buf, buflen, 0xcb) ||
+ mpack_w4(buf, buflen, tok->data.value.hi) ||
+ mpack_w4(buf, buflen, tok->data.value.lo);
+ } else {
+ return MPACK_ERROR;
+ }
+}
+
+static int mpack_wstr(char **buf, size_t *buflen, mpack_uint32_t len)
+{
+ if (len < 0x20) {
+ return mpack_w1(buf, buflen, 0xa0 | len);
+ } else if (len < 0x100) {
+ return mpack_w1(buf, buflen, 0xd9) ||
+ mpack_w1(buf, buflen, len);
+ } else if (len < 0x10000) {
+ return mpack_w1(buf, buflen, 0xda) ||
+ mpack_w2(buf, buflen, len);
+ } else {
+ return mpack_w1(buf, buflen, 0xdb) ||
+ mpack_w4(buf, buflen, len);
+ }
+}
+
+static int mpack_wbin(char **buf, size_t *buflen, mpack_uint32_t len)
+{
+ if (len < 0x100) {
+ return mpack_w1(buf, buflen, 0xc4) ||
+ mpack_w1(buf, buflen, len);
+ } else if (len < 0x10000) {
+ return mpack_w1(buf, buflen, 0xc5) ||
+ mpack_w2(buf, buflen, len);
+ } else {
+ return mpack_w1(buf, buflen, 0xc6) ||
+ mpack_w4(buf, buflen, len);
+ }
+}
+
+static int mpack_wext(char **buf, size_t *buflen, int type,
+ mpack_uint32_t len)
+{
+ mpack_uint32_t t;
+ assert(type >= 0 && type < 0x80);
+ t = (mpack_uint32_t)type;
+ switch (len) {
+ case 1: mpack_w1(buf, buflen, 0xd4); return mpack_w1(buf, buflen, t);
+ case 2: mpack_w1(buf, buflen, 0xd5); return mpack_w1(buf, buflen, t);
+ case 4: mpack_w1(buf, buflen, 0xd6); return mpack_w1(buf, buflen, t);
+ case 8: mpack_w1(buf, buflen, 0xd7); return mpack_w1(buf, buflen, t);
+ case 16: mpack_w1(buf, buflen, 0xd8); return mpack_w1(buf, buflen, t);
+ default:
+ if (len < 0x100) {
+ return mpack_w1(buf, buflen, 0xc7) ||
+ mpack_w1(buf, buflen, len) ||
+ mpack_w1(buf, buflen, t);
+ } else if (len < 0x10000) {
+ return mpack_w1(buf, buflen, 0xc8) ||
+ mpack_w2(buf, buflen, len) ||
+ mpack_w1(buf, buflen, t);
+ } else {
+ return mpack_w1(buf, buflen, 0xc9) ||
+ mpack_w4(buf, buflen, len) ||
+ mpack_w1(buf, buflen, t);
+ }
+ }
+}
+
+static int mpack_warray(char **buf, size_t *buflen, mpack_uint32_t len)
+{
+ if (len < 0x10) {
+ return mpack_w1(buf, buflen, 0x90 | len);
+ } else if (len < 0x10000) {
+ return mpack_w1(buf, buflen, 0xdc) ||
+ mpack_w2(buf, buflen, len);
+ } else {
+ return mpack_w1(buf, buflen, 0xdd) ||
+ mpack_w4(buf, buflen, len);
+ }
+}
+
+static int mpack_wmap(char **buf, size_t *buflen, mpack_uint32_t len)
+{
+ if (len < 0x10) {
+ return mpack_w1(buf, buflen, 0x80 | len);
+ } else if (len < 0x10000) {
+ return mpack_w1(buf, buflen, 0xde) ||
+ mpack_w2(buf, buflen, len);
+ } else {
+ return mpack_w1(buf, buflen, 0xdf) ||
+ mpack_w4(buf, buflen, len);
+ }
+}
+
+static int mpack_w1(char **b, size_t *bl, mpack_uint32_t v)
+{
+ (*bl)--;
+ *(*b)++ = (char)(v & 0xff);
+ return MPACK_OK;
+}
+
+static int mpack_w2(char **b, size_t *bl, mpack_uint32_t v)
+{
+ *bl -= 2;
+ *(*b)++ = (char)((v >> 8) & 0xff);
+ *(*b)++ = (char)(v & 0xff);
+ return MPACK_OK;
+}
+
+static int mpack_w4(char **b, size_t *bl, mpack_uint32_t v)
+{
+ *bl -= 4;
+ *(*b)++ = (char)((v >> 24) & 0xff);
+ *(*b)++ = (char)((v >> 16) & 0xff);
+ *(*b)++ = (char)((v >> 8) & 0xff);
+ *(*b)++ = (char)(v & 0xff);
+ return MPACK_OK;
+}
+
+static int mpack_value(mpack_token_type_t type, mpack_uint32_t length,
+ mpack_value_t value, mpack_token_t *tok)
+{
+ tok->type = type;
+ tok->length = length;
+ tok->data.value = value;
+ return MPACK_OK;
+}
+
+static int mpack_blob(mpack_token_type_t type, mpack_uint32_t length,
+ int ext_type, mpack_token_t *tok)
+{
+ tok->type = type;
+ tok->length = length;
+ tok->data.ext_type = ext_type;
+ return MPACK_OK;
+}
+
+static mpack_value_t mpack_byte(unsigned char byte)
+{
+ mpack_value_t rv;
+ rv.lo = byte;
+ rv.hi = 0;
+ return rv;
+}
diff --git a/src/mpack/mpack_core.h b/src/mpack/mpack_core.h
new file mode 100644
index 0000000000..9edd13c41e
--- /dev/null
+++ b/src/mpack/mpack_core.h
@@ -0,0 +1,87 @@
+#ifndef MPACK_CORE_H
+#define MPACK_CORE_H
+
+#ifndef MPACK_API
+# define MPACK_API extern
+#endif
+
+#include <assert.h>
+#include <limits.h>
+#include <stddef.h>
+
+#ifdef __GNUC__
+# define FPURE __attribute__((const))
+# define FNONULL __attribute__((nonnull))
+# define FNONULL_ARG(x) __attribute__((nonnull x))
+# define FUNUSED __attribute__((unused))
+#else
+# define FPURE
+# define FNONULL
+# define FNONULL_ARG(x)
+# define FUNUSED
+#endif
+
+#if UINT_MAX == 0xffffffff
+typedef int mpack_sint32_t;
+typedef unsigned int mpack_uint32_t;
+#elif ULONG_MAX == 0xffffffff
+typedef long mpack_sint32_t;
+typedef unsigned long mpack_uint32_t;
+#else
+# error "can't find unsigned 32-bit integer type"
+#endif
+
+typedef struct mpack_value_s {
+ mpack_uint32_t lo, hi;
+} mpack_value_t;
+
+
+enum {
+ MPACK_OK = 0,
+ MPACK_EOF = 1,
+ MPACK_ERROR = 2
+};
+
+#define MPACK_MAX_TOKEN_LEN 9 /* 64-bit ints/floats plus type code */
+
+typedef enum {
+ MPACK_TOKEN_NIL = 1,
+ MPACK_TOKEN_BOOLEAN = 2,
+ MPACK_TOKEN_UINT = 3,
+ MPACK_TOKEN_SINT = 4,
+ MPACK_TOKEN_FLOAT = 5,
+ MPACK_TOKEN_CHUNK = 6,
+ MPACK_TOKEN_ARRAY = 7,
+ MPACK_TOKEN_MAP = 8,
+ MPACK_TOKEN_BIN = 9,
+ MPACK_TOKEN_STR = 10,
+ MPACK_TOKEN_EXT = 11
+} mpack_token_type_t;
+
+typedef struct mpack_token_s {
+ mpack_token_type_t type; /* Type of token */
+ mpack_uint32_t length; /* Byte length for str/bin/ext/chunk/float/int/uint.
+ Item count for array/map. */
+ union {
+ mpack_value_t value; /* 32-bit parts of primitives (bool,int,float) */
+ const char *chunk_ptr; /* Chunk of data from str/bin/ext */
+ int ext_type; /* Type field for ext tokens */
+ } data;
+} mpack_token_t;
+
+typedef struct mpack_tokbuf_s {
+ char pending[MPACK_MAX_TOKEN_LEN];
+ mpack_token_t pending_tok;
+ size_t ppos, plen;
+ mpack_uint32_t passthrough;
+} mpack_tokbuf_t;
+
+#define MPACK_TOKBUF_INITIAL_VALUE { { 0 }, { 0, 0, { { 0, 0 } } }, 0, 0, 0 }
+
+MPACK_API void mpack_tokbuf_init(mpack_tokbuf_t *tb) FUNUSED FNONULL;
+MPACK_API int mpack_read(mpack_tokbuf_t *tb, const char **b, size_t *bl,
+ mpack_token_t *tok) FUNUSED FNONULL;
+MPACK_API int mpack_write(mpack_tokbuf_t *tb, char **b, size_t *bl,
+ const mpack_token_t *tok) FUNUSED FNONULL;
+
+#endif /* MPACK_CORE_H */
diff --git a/src/mpack/object.c b/src/mpack/object.c
new file mode 100644
index 0000000000..0c7759ee51
--- /dev/null
+++ b/src/mpack/object.c
@@ -0,0 +1,195 @@
+#include <string.h>
+
+#include "object.h"
+
+static int mpack_parser_full(mpack_parser_t *w);
+static mpack_node_t *mpack_parser_push(mpack_parser_t *w);
+static mpack_node_t *mpack_parser_pop(mpack_parser_t *w);
+
+MPACK_API void mpack_parser_init(mpack_parser_t *parser,
+ mpack_uint32_t capacity)
+{
+ mpack_tokbuf_init(&parser->tokbuf);
+ parser->data.p = NULL;
+ parser->capacity = capacity ? capacity : MPACK_MAX_OBJECT_DEPTH;
+ parser->size = 0;
+ parser->exiting = 0;
+ memset(parser->items, 0, sizeof(mpack_node_t) * (parser->capacity + 1));
+ parser->items[0].pos = (size_t)-1;
+ parser->status = 0;
+}
+
+#define MPACK_EXCEPTION_CHECK(parser) \
+ do { \
+ if (parser->status == MPACK_EXCEPTION) { \
+ return MPACK_EXCEPTION; \
+ } \
+ } while (0)
+
+#define MPACK_WALK(action) \
+ do { \
+ mpack_node_t *n; \
+ \
+ if (parser->exiting) goto exit; \
+ if (mpack_parser_full(parser)) return MPACK_NOMEM; \
+ n = mpack_parser_push(parser); \
+ action; \
+ MPACK_EXCEPTION_CHECK(parser); \
+ parser->exiting = 1; \
+ return MPACK_EOF; \
+ \
+exit: \
+ parser->exiting = 0; \
+ while ((n = mpack_parser_pop(parser))) { \
+ exit_cb(parser, n); \
+ MPACK_EXCEPTION_CHECK(parser); \
+ if (!parser->size) return MPACK_OK; \
+ } \
+ \
+ return MPACK_EOF; \
+ } while (0)
+
+MPACK_API int mpack_parse_tok(mpack_parser_t *parser, mpack_token_t tok,
+ mpack_walk_cb enter_cb, mpack_walk_cb exit_cb)
+{
+ MPACK_EXCEPTION_CHECK(parser);
+ MPACK_WALK({n->tok = tok; enter_cb(parser, n);});
+}
+
+MPACK_API int mpack_unparse_tok(mpack_parser_t *parser, mpack_token_t *tok,
+ mpack_walk_cb enter_cb, mpack_walk_cb exit_cb)
+{
+ MPACK_EXCEPTION_CHECK(parser);
+ MPACK_WALK({enter_cb(parser, n); *tok = n->tok;});
+}
+
+MPACK_API int mpack_parse(mpack_parser_t *parser, const char **buf,
+ size_t *buflen, mpack_walk_cb enter_cb, mpack_walk_cb exit_cb)
+{
+ int status = MPACK_EOF;
+ MPACK_EXCEPTION_CHECK(parser);
+
+ while (*buflen && status) {
+ mpack_token_t tok;
+ mpack_tokbuf_t *tb = &parser->tokbuf;
+ const char *buf_save = *buf;
+ size_t buflen_save = *buflen;
+
+ if ((status = mpack_read(tb, buf, buflen, &tok)) == MPACK_EOF) continue;
+ else if (status == MPACK_ERROR) goto rollback;
+
+ do {
+ status = mpack_parse_tok(parser, tok, enter_cb, exit_cb);
+ MPACK_EXCEPTION_CHECK(parser);
+ } while (parser->exiting);
+
+ if (status != MPACK_NOMEM) continue;
+
+rollback:
+ /* restore buf/buflen so the next call will try to read the same token */
+ *buf = buf_save;
+ *buflen = buflen_save;
+ break;
+ }
+
+ return status;
+}
+
+MPACK_API int mpack_unparse(mpack_parser_t *parser, char **buf, size_t *buflen,
+ mpack_walk_cb enter_cb, mpack_walk_cb exit_cb)
+{
+ int status = MPACK_EOF;
+ MPACK_EXCEPTION_CHECK(parser);
+
+ while (*buflen && status) {
+ int write_status;
+ mpack_token_t tok;
+ mpack_tokbuf_t *tb = &parser->tokbuf;
+
+ if (!tb->plen)
+ parser->status = mpack_unparse_tok(parser, &tok, enter_cb, exit_cb);
+
+ MPACK_EXCEPTION_CHECK(parser);
+
+ status = parser->status;
+
+ if (status == MPACK_NOMEM)
+ break;
+
+ if (parser->exiting) {
+ write_status = mpack_write(tb, buf, buflen, &tok);
+ status = write_status ? write_status : status;
+ }
+ }
+
+ return status;
+}
+
+MPACK_API void mpack_parser_copy(mpack_parser_t *dst, mpack_parser_t *src)
+{
+ mpack_uint32_t i;
+ mpack_uint32_t dst_capacity = dst->capacity;
+ assert(src->capacity <= dst_capacity);
+ /* copy all fields except the stack */
+ memcpy(dst, src, sizeof(mpack_one_parser_t) - sizeof(mpack_node_t));
+ /* reset capacity */
+ dst->capacity = dst_capacity;
+ /* copy the stack */
+ for (i = 0; i <= src->capacity; i++) {
+ dst->items[i] = src->items[i];
+ }
+}
+
+static int mpack_parser_full(mpack_parser_t *parser)
+{
+ return parser->size == parser->capacity;
+}
+
+static mpack_node_t *mpack_parser_push(mpack_parser_t *parser)
+{
+ mpack_node_t *top;
+ assert(parser->size < parser->capacity);
+ top = parser->items + parser->size + 1;
+ top->data[0].p = NULL;
+ top->data[1].p = NULL;
+ top->pos = 0;
+ top->key_visited = 0;
+ /* increase size and invoke callback, passing parent node if any */
+ parser->size++;
+ return top;
+}
+
+static mpack_node_t *mpack_parser_pop(mpack_parser_t *parser)
+{
+ mpack_node_t *top, *parent;
+ assert(parser->size);
+ top = parser->items + parser->size;
+
+ if (top->tok.type > MPACK_TOKEN_CHUNK && top->pos < top->tok.length) {
+ /* continue processing children */
+ return NULL;
+ }
+
+ parent = MPACK_PARENT_NODE(top);
+ if (parent) {
+ /* we use parent->tok.length to keep track of how many children remain.
+ * update it to reflect the processed node. */
+ if (top->tok.type == MPACK_TOKEN_CHUNK) {
+ parent->pos += top->tok.length;
+ } else if (parent->tok.type == MPACK_TOKEN_MAP) {
+ /* maps allow up to 2^32 - 1 pairs, so to allow this many items in a
+ * 32-bit length variable we use an additional flag to determine if the
+ * key of a certain position was visited */
+ if (parent->key_visited) {
+ parent->pos++;
+ }
+ parent->key_visited = !parent->key_visited;
+ } else {
+ parent->pos++;
+ }
+ }
+
+ parser->size--;
+ return top;
+}
+
diff --git a/src/mpack/object.h b/src/mpack/object.h
new file mode 100644
index 0000000000..5327e56e18
--- /dev/null
+++ b/src/mpack/object.h
@@ -0,0 +1,86 @@
+#ifndef MPACK_OBJECT_H
+#define MPACK_OBJECT_H
+
+#include "mpack_core.h"
+#include "conv.h"
+
+#ifndef MPACK_MAX_OBJECT_DEPTH
+# define MPACK_MAX_OBJECT_DEPTH 32
+#endif
+
+#define MPACK_PARENT_NODE(n) (((n) - 1)->pos == (size_t)-1 ? NULL : (n) - 1)
+
+#define MPACK_THROW(parser) \
+ do { \
+ parser->status = MPACK_EXCEPTION; \
+ return; \
+ } while (0)
+
+enum {
+ MPACK_EXCEPTION = -1,
+ MPACK_NOMEM = MPACK_ERROR + 1
+};
+
+/* Storing integer in pointers in undefined behavior according to the C
+ * standard. Define a union type to accomodate arbitrary user data associated
+ * with nodes(and with requests in rpc.h). */
+typedef union {
+ void *p;
+ mpack_uintmax_t u;
+ mpack_sintmax_t i;
+ double d;
+} mpack_data_t;
+
+typedef struct mpack_node_s {
+ mpack_token_t tok;
+ size_t pos;
+ /* flag to determine if the key was visited when traversing a map */
+ int key_visited;
+ /* allow 2 instances mpack_data_t per node. the reason is that when
+ * serializing, the user may need to keep track of traversal state besides the
+ * parent node reference */
+ mpack_data_t data[2];
+} mpack_node_t;
+
+#define MPACK_PARSER_STRUCT(c) \
+ struct { \
+ mpack_data_t data; \
+ mpack_uint32_t size, capacity; \
+ int status; \
+ int exiting; \
+ mpack_tokbuf_t tokbuf; \
+ mpack_node_t items[c + 1]; \
+ }
+
+/* Some compilers warn against anonymous structs:
+ * https://github.com/libmpack/libmpack/issues/6 */
+typedef MPACK_PARSER_STRUCT(0) mpack_one_parser_t;
+
+#define MPACK_PARSER_STRUCT_SIZE(c) \
+ (sizeof(mpack_node_t) * c + \
+ sizeof(mpack_one_parser_t))
+
+typedef MPACK_PARSER_STRUCT(MPACK_MAX_OBJECT_DEPTH) mpack_parser_t;
+typedef void(*mpack_walk_cb)(mpack_parser_t *w, mpack_node_t *n);
+
+MPACK_API void mpack_parser_init(mpack_parser_t *p, mpack_uint32_t c)
+ FUNUSED FNONULL;
+
+MPACK_API int mpack_parse_tok(mpack_parser_t *walker, mpack_token_t tok,
+ mpack_walk_cb enter_cb, mpack_walk_cb exit_cb)
+ FUNUSED FNONULL_ARG((1,3,4));
+MPACK_API int mpack_unparse_tok(mpack_parser_t *walker, mpack_token_t *tok,
+ mpack_walk_cb enter_cb, mpack_walk_cb exit_cb)
+ FUNUSED FNONULL_ARG((1,2,3,4));
+
+MPACK_API int mpack_parse(mpack_parser_t *parser, const char **b, size_t *bl,
+ mpack_walk_cb enter_cb, mpack_walk_cb exit_cb)
+ FUNUSED FNONULL_ARG((1,2,3,4,5));
+MPACK_API int mpack_unparse(mpack_parser_t *parser, char **b, size_t *bl,
+ mpack_walk_cb enter_cb, mpack_walk_cb exit_cb)
+ FUNUSED FNONULL_ARG((1,2,3,4,5));
+
+MPACK_API void mpack_parser_copy(mpack_parser_t *d, mpack_parser_t *s)
+ FUNUSED FNONULL;
+
+#endif /* MPACK_OBJECT_H */
diff --git a/src/mpack/rpc.c b/src/mpack/rpc.c
new file mode 100644
index 0000000000..3b2b328065
--- /dev/null
+++ b/src/mpack/rpc.c
@@ -0,0 +1,331 @@
+#include <string.h>
+
+#include "rpc.h"
+
+enum {
+ MPACK_RPC_RECEIVE_ARRAY = 1,
+ MPACK_RPC_RECEIVE_TYPE,
+ MPACK_RPC_RECEIVE_ID
+};
+
+static mpack_rpc_header_t mpack_rpc_request_hdr(void);
+static mpack_rpc_header_t mpack_rpc_reply_hdr(void);
+static mpack_rpc_header_t mpack_rpc_notify_hdr(void);
+static int mpack_rpc_put(mpack_rpc_session_t *s, mpack_rpc_message_t m);
+static int mpack_rpc_pop(mpack_rpc_session_t *s, mpack_rpc_message_t *m);
+static void mpack_rpc_reset_hdr(mpack_rpc_header_t *hdr);
+
+MPACK_API void mpack_rpc_session_init(mpack_rpc_session_t *session,
+ mpack_uint32_t capacity)
+{
+ session->capacity = capacity ? capacity : MPACK_RPC_MAX_REQUESTS;
+ session->request_id = 0;
+ mpack_tokbuf_init(&session->reader);
+ mpack_tokbuf_init(&session->writer);
+ mpack_rpc_reset_hdr(&session->receive);
+ mpack_rpc_reset_hdr(&session->send);
+ memset(session->slots, 0,
+ sizeof(struct mpack_rpc_slot_s) * session->capacity);
+}
+
+MPACK_API int mpack_rpc_receive_tok(mpack_rpc_session_t *session,
+ mpack_token_t tok, mpack_rpc_message_t *msg)
+{
+ int type;
+
+ if (session->receive.index == 0) {
+ if (tok.type != MPACK_TOKEN_ARRAY)
+ /* not an array */
+ return MPACK_RPC_EARRAY;
+
+ if (tok.length < 3 || tok.length > 4)
+ /* invalid array length */
+ return MPACK_RPC_EARRAYL;
+
+ session->receive.toks[0] = tok;
+ session->receive.index++;
+ return MPACK_EOF; /* get the type */
+ }
+
+ if (session->receive.index == 1) {
+
+ if (tok.type != MPACK_TOKEN_UINT || tok.length > 1 || tok.data.value.lo > 2)
+ /* invalid type */
+ return MPACK_RPC_ETYPE;
+
+ if (tok.data.value.lo < 2 && session->receive.toks[0].length != 4)
+ /* request or response with array length != 4 */
+ return MPACK_RPC_EARRAYL;
+
+ if (tok.data.value.lo == 2 && session->receive.toks[0].length != 3)
+ /* notification with array length != 3 */
+ return MPACK_RPC_EARRAYL;
+
+ session->receive.toks[1] = tok;
+ session->receive.index++;
+
+ if (tok.data.value.lo < 2) return MPACK_EOF;
+
+ type = MPACK_RPC_NOTIFICATION;
+ goto end;
+ }
+
+ assert(session->receive.index == 2);
+
+ if (tok.type != MPACK_TOKEN_UINT || tok.length > 4)
+ /* invalid request/response id */
+ return MPACK_RPC_EMSGID;
+
+ msg->id = tok.data.value.lo;
+ msg->data.p = NULL;
+ type = (int)session->receive.toks[1].data.value.lo + MPACK_RPC_REQUEST;
+
+ if (type == MPACK_RPC_RESPONSE && !mpack_rpc_pop(session, msg))
+ /* response with invalid id */
+ return MPACK_RPC_ERESPID;
+
+end:
+ mpack_rpc_reset_hdr(&session->receive);
+ return type;
+}
+
+MPACK_API int mpack_rpc_request_tok(mpack_rpc_session_t *session,
+ mpack_token_t *tok, mpack_data_t data)
+{
+ if (session->send.index == 0) {
+ int status;
+ mpack_rpc_message_t msg;
+ do {
+ msg.id = session->request_id;
+ msg.data = data;
+ session->send = mpack_rpc_request_hdr();
+ session->send.toks[2].type = MPACK_TOKEN_UINT;
+ session->send.toks[2].data.value.lo = msg.id;
+ session->send.toks[2].data.value.hi = 0;
+ *tok = session->send.toks[0];
+ status = mpack_rpc_put(session, msg);
+ if (status == -1) return MPACK_NOMEM;
+ session->request_id = (session->request_id + 1) % 0xffffffff;
+ } while (!status);
+ session->send.index++;
+ return MPACK_EOF;
+ }
+
+ if (session->send.index == 1) {
+ *tok = session->send.toks[1];
+ session->send.index++;
+ return MPACK_EOF;
+ }
+
+ assert(session->send.index == 2);
+ *tok = session->send.toks[2];
+ mpack_rpc_reset_hdr(&session->send);
+ return MPACK_OK;
+}
+
+MPACK_API int mpack_rpc_reply_tok(mpack_rpc_session_t *session,
+ mpack_token_t *tok, mpack_uint32_t id)
+{
+ if (session->send.index == 0) {
+ session->send = mpack_rpc_reply_hdr();
+ session->send.toks[2].type = MPACK_TOKEN_UINT;
+ session->send.toks[2].data.value.lo = id;
+ session->send.toks[2].data.value.hi = 0;
+ *tok = session->send.toks[0];
+ session->send.index++;
+ return MPACK_EOF;
+ }
+
+ if (session->send.index == 1) {
+ *tok = session->send.toks[1];
+ session->send.index++;
+ return MPACK_EOF;
+ }
+
+ assert(session->send.index == 2);
+ *tok = session->send.toks[2];
+ mpack_rpc_reset_hdr(&session->send);
+ return MPACK_OK;
+}
+
+MPACK_API int mpack_rpc_notify_tok(mpack_rpc_session_t *session,
+ mpack_token_t *tok)
+{
+ if (session->send.index == 0) {
+ session->send = mpack_rpc_notify_hdr();
+ *tok = session->send.toks[0];
+ session->send.index++;
+ return MPACK_EOF;
+ }
+
+ assert(session->send.index == 1);
+ *tok = session->send.toks[1];
+ mpack_rpc_reset_hdr(&session->send);
+ return MPACK_OK;
+}
+
+MPACK_API int mpack_rpc_receive(mpack_rpc_session_t *session, const char **buf,
+ size_t *buflen, mpack_rpc_message_t *msg)
+{
+ int status;
+
+ do {
+ mpack_token_t tok;
+ status = mpack_read(&session->reader, buf, buflen, &tok);
+ if (status) break;
+ status = mpack_rpc_receive_tok(session, tok, msg);
+ if (status >= MPACK_RPC_REQUEST) break;
+ } while (*buflen);
+
+ return status;
+}
+
+MPACK_API int mpack_rpc_request(mpack_rpc_session_t *session, char **buf,
+ size_t *buflen, mpack_data_t data)
+{
+ int status = MPACK_EOF;
+
+ while (status && *buflen) {
+ int write_status;
+ mpack_token_t tok;
+ if (!session->writer.plen) {
+ status = mpack_rpc_request_tok(session, &tok, data);
+ }
+ if (status == MPACK_NOMEM) break;
+ write_status = mpack_write(&session->writer, buf, buflen, &tok);
+ status = write_status ? write_status : status;
+ }
+
+ return status;
+}
+
+MPACK_API int mpack_rpc_reply(mpack_rpc_session_t *session, char **buf,
+ size_t *buflen, mpack_uint32_t id)
+{
+ int status = MPACK_EOF;
+
+ while (status && *buflen) {
+ int write_status;
+ mpack_token_t tok;
+ if (!session->writer.plen) {
+ status = mpack_rpc_reply_tok(session, &tok, id);
+ }
+ write_status = mpack_write(&session->writer, buf, buflen, &tok);
+ status = write_status ? write_status : status;
+ }
+
+ return status;
+}
+
+MPACK_API int mpack_rpc_notify(mpack_rpc_session_t *session, char **buf,
+ size_t *buflen)
+{
+ int status = MPACK_EOF;
+
+ while (status && *buflen) {
+ int write_status;
+ mpack_token_t tok;
+ if (!session->writer.plen) {
+ status = mpack_rpc_notify_tok(session, &tok);
+ }
+ write_status = mpack_write(&session->writer, buf, buflen, &tok);
+ status = write_status ? write_status : status;
+ }
+
+ return status;
+}
+
+MPACK_API void mpack_rpc_session_copy(mpack_rpc_session_t *dst,
+ mpack_rpc_session_t *src)
+{
+ mpack_uint32_t i;
+ mpack_uint32_t dst_capacity = dst->capacity;
+ assert(src->capacity <= dst_capacity);
+ /* copy all fields except slots */
+ memcpy(dst, src, sizeof(mpack_rpc_one_session_t) -
+ sizeof(struct mpack_rpc_slot_s));
+ /* reset capacity */
+ dst->capacity = dst_capacity;
+ /* reinsert requests */
+ memset(dst->slots, 0, sizeof(struct mpack_rpc_slot_s) * dst->capacity);
+ for (i = 0; i < src->capacity; i++) {
+ if (src->slots[i].used) mpack_rpc_put(dst, src->slots[i].msg);
+ }
+}
+
+static mpack_rpc_header_t mpack_rpc_request_hdr(void)
+{
+ mpack_rpc_header_t hdr;
+ hdr.index = 0;
+ hdr.toks[0].type = MPACK_TOKEN_ARRAY;
+ hdr.toks[0].length = 4;
+ hdr.toks[1].type = MPACK_TOKEN_UINT;
+ hdr.toks[1].data.value.lo = 0;
+ hdr.toks[1].data.value.hi = 0;
+ return hdr;
+}
+
+static mpack_rpc_header_t mpack_rpc_reply_hdr(void)
+{
+ mpack_rpc_header_t hdr = mpack_rpc_request_hdr();
+ hdr.toks[1].data.value.lo = 1;
+ hdr.toks[1].data.value.hi = 0;
+ return hdr;
+}
+
+static mpack_rpc_header_t mpack_rpc_notify_hdr(void)
+{
+ mpack_rpc_header_t hdr = mpack_rpc_request_hdr();
+ hdr.toks[0].length = 3;
+ hdr.toks[1].data.value.lo = 2;
+ hdr.toks[1].data.value.hi = 0;
+ return hdr;
+}
+
+static int mpack_rpc_put(mpack_rpc_session_t *session, mpack_rpc_message_t msg)
+{
+ struct mpack_rpc_slot_s *slot = NULL;
+ mpack_uint32_t i;
+ mpack_uint32_t hash = msg.id % session->capacity;
+
+ for (i = 0; i < session->capacity; i++) {
+ if (!session->slots[hash].used || session->slots[hash].msg.id == msg.id) {
+ slot = session->slots + hash;
+ break;
+ }
+ hash = hash > 0 ? hash - 1 : session->capacity - 1;
+ }
+
+ if (!slot) return -1; /* no space */
+ if (slot->msg.id == msg.id && slot->used) return 0; /* duplicate key */
+ slot->msg = msg;
+ slot->used = 1;
+ return 1;
+}
+
+static int mpack_rpc_pop(mpack_rpc_session_t *session, mpack_rpc_message_t *msg)
+{
+ struct mpack_rpc_slot_s *slot = NULL;
+ mpack_uint32_t i;
+ mpack_uint32_t hash = msg->id % session->capacity;
+
+ for (i = 0; i < session->capacity; i++) {
+ if (session->slots[hash].used && session->slots[hash].msg.id == msg->id) {
+ slot = session->slots + hash;
+ break;
+ }
+ hash = hash > 0 ? hash - 1 : session->capacity - 1;
+ }
+
+ if (!slot) return 0;
+
+ *msg = slot->msg;
+ slot->used = 0;
+
+ return 1;
+}
+
+static void mpack_rpc_reset_hdr(mpack_rpc_header_t *hdr)
+{
+ hdr->index = 0;
+}
diff --git a/src/mpack/rpc.h b/src/mpack/rpc.h
new file mode 100644
index 0000000000..c1e8d656b5
--- /dev/null
+++ b/src/mpack/rpc.h
@@ -0,0 +1,83 @@
+#ifndef MPACK_RPC_H
+#define MPACK_RPC_H
+
+#include "mpack_core.h"
+#include "object.h"
+
+#ifndef MPACK_RPC_MAX_REQUESTS
+# define MPACK_RPC_MAX_REQUESTS 32
+#endif
+
+enum {
+ MPACK_RPC_REQUEST = MPACK_NOMEM + 1,
+ MPACK_RPC_RESPONSE,
+ MPACK_RPC_NOTIFICATION,
+ MPACK_RPC_ERROR
+};
+
+enum {
+ MPACK_RPC_EARRAY = MPACK_RPC_ERROR,
+ MPACK_RPC_EARRAYL,
+ MPACK_RPC_ETYPE,
+ MPACK_RPC_EMSGID,
+ MPACK_RPC_ERESPID
+};
+
+typedef struct mpack_rpc_header_s {
+ mpack_token_t toks[3];
+ int index;
+} mpack_rpc_header_t;
+
+typedef struct mpack_rpc_message_s {
+ mpack_uint32_t id;
+ mpack_data_t data;
+} mpack_rpc_message_t;
+
+struct mpack_rpc_slot_s {
+ int used;
+ mpack_rpc_message_t msg;
+};
+
+#define MPACK_RPC_SESSION_STRUCT(c) \
+ struct { \
+ mpack_tokbuf_t reader, writer; \
+ mpack_rpc_header_t receive, send; \
+ mpack_uint32_t request_id, capacity; \
+ struct mpack_rpc_slot_s slots[c]; \
+ }
+
+/* Some compilers warn against anonymous structs:
+ * https://github.com/libmpack/libmpack/issues/6 */
+typedef MPACK_RPC_SESSION_STRUCT(1) mpack_rpc_one_session_t;
+
+#define MPACK_RPC_SESSION_STRUCT_SIZE(c) \
+ (sizeof(struct mpack_rpc_slot_s) * (c - 1) + \
+ sizeof(mpack_rpc_one_session_t))
+
+typedef MPACK_RPC_SESSION_STRUCT(MPACK_RPC_MAX_REQUESTS) mpack_rpc_session_t;
+
+MPACK_API void mpack_rpc_session_init(mpack_rpc_session_t *s, mpack_uint32_t c)
+ FUNUSED FNONULL;
+
+MPACK_API int mpack_rpc_receive_tok(mpack_rpc_session_t *s, mpack_token_t t,
+ mpack_rpc_message_t *msg) FUNUSED FNONULL;
+MPACK_API int mpack_rpc_request_tok(mpack_rpc_session_t *s, mpack_token_t *t,
+ mpack_data_t d) FUNUSED FNONULL_ARG((1,2));
+MPACK_API int mpack_rpc_reply_tok(mpack_rpc_session_t *s, mpack_token_t *t,
+ mpack_uint32_t i) FUNUSED FNONULL;
+MPACK_API int mpack_rpc_notify_tok(mpack_rpc_session_t *s, mpack_token_t *t)
+ FUNUSED FNONULL;
+
+MPACK_API int mpack_rpc_receive(mpack_rpc_session_t *s, const char **b,
+ size_t *bl, mpack_rpc_message_t *m) FUNUSED FNONULL;
+MPACK_API int mpack_rpc_request(mpack_rpc_session_t *s, char **b, size_t *bl,
+ mpack_data_t d) FUNUSED FNONULL_ARG((1,2,3));
+MPACK_API int mpack_rpc_reply(mpack_rpc_session_t *s, char **b, size_t *bl,
+ mpack_uint32_t i) FNONULL FUNUSED;
+MPACK_API int mpack_rpc_notify(mpack_rpc_session_t *s, char **b, size_t *bl)
+ FNONULL FUNUSED;
+
+MPACK_API void mpack_rpc_session_copy(mpack_rpc_session_t *d,
+ mpack_rpc_session_t *s) FUNUSED FNONULL;
+
+#endif /* MPACK_RPC_H */
diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt
index 4a698052ee..331ab16dd7 100644
--- a/src/nvim/CMakeLists.txt
+++ b/src/nvim/CMakeLists.txt
@@ -87,8 +87,8 @@ file(MAKE_DIRECTORY ${LINT_SUPPRESSES_ROOT}/src)
file(GLOB NVIM_SOURCES *.c)
file(GLOB NVIM_HEADERS *.h)
-file(GLOB XDIFF_SOURCES ../xdiff/*.c)
-file(GLOB XDIFF_HEADERS ../xdiff/*.h)
+file(GLOB EXTERNAL_SOURCES ../xdiff/*.c ../mpack/*.c)
+file(GLOB EXTERNAL_HEADERS ../xdiff/*.h ../mpack/*.h)
foreach(subdir
os
@@ -171,8 +171,8 @@ foreach(sfile ${CONV_SOURCES})
message(FATAL_ERROR "${sfile} doesn't exist (it was added to CONV_SOURCES)")
endif()
endforeach()
-# xdiff: inlined external project, we don't maintain it. #9306
-list(APPEND CONV_SOURCES ${XDIFF_SOURCES})
+# xdiff, mpack: inlined external project, we don't maintain it. #9306
+list(APPEND CONV_SOURCES ${EXTERNAL_SOURCES})
if(NOT MSVC)
set_source_files_properties(
@@ -471,7 +471,7 @@ endif()
add_executable(nvim ${NVIM_GENERATED_FOR_SOURCES} ${NVIM_GENERATED_FOR_HEADERS}
${NVIM_GENERATED_SOURCES} ${NVIM_SOURCES} ${NVIM_HEADERS}
- ${XDIFF_SOURCES} ${XDIFF_HEADERS})
+ ${EXTERNAL_SOURCES} ${EXTERNAL_HEADERS})
target_link_libraries(nvim ${NVIM_EXEC_LINK_LIBRARIES})
install_helper(TARGETS nvim)
@@ -602,7 +602,7 @@ add_library(
EXCLUDE_FROM_ALL
${NVIM_SOURCES} ${NVIM_GENERATED_SOURCES}
${NVIM_HEADERS} ${NVIM_GENERATED_FOR_SOURCES} ${NVIM_GENERATED_FOR_HEADERS}
- ${XDIFF_SOURCES} ${XDIFF_HEADERS}
+ ${EXTERNAL_SOURCES} ${EXTERNAL_HEADERS}
)
set_property(TARGET libnvim APPEND PROPERTY
INCLUDE_DIRECTORIES ${LUA_PREFERRED_INCLUDE_DIRS})
@@ -632,7 +632,7 @@ else()
EXCLUDE_FROM_ALL
${NVIM_SOURCES} ${NVIM_GENERATED_SOURCES}
${NVIM_HEADERS} ${NVIM_GENERATED_FOR_SOURCES} ${NVIM_GENERATED_FOR_HEADERS}
- ${XDIFF_SOURCES} ${XDIFF_HEADERS}
+ ${EXTERNAL_SOURCES} ${EXTERNAL_HEADERS}
${UNIT_TEST_FIXTURES}
)
target_link_libraries(nvim-test ${NVIM_TEST_LINK_LIBRARIES})
diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c
index a8b10f86f5..d071203db1 100644
--- a/src/nvim/lua/executor.c
+++ b/src/nvim/lua/executor.c
@@ -33,6 +33,7 @@
#include "nvim/eval/userfunc.h"
#include "nvim/event/time.h"
#include "nvim/event/loop.h"
+#include "mpack/lmpack.h"
#include "nvim/os/os.h"
@@ -506,6 +507,8 @@ static int nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL
lua_setfield(lstate, -2, "__tostring");
lua_setmetatable(lstate, -2);
nlua_nil_ref = nlua_ref(lstate, -1);
+ lua_pushvalue(lstate, -1);
+ lua_setfield(lstate, LUA_REGISTRYINDEX, "mpack.NIL");
lua_setfield(lstate, -2, "NIL");
// vim._empty_dict_mt
@@ -513,8 +516,23 @@ static int nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL
lua_pushcfunction(lstate, &nlua_empty_dict_tostring);
lua_setfield(lstate, -2, "__tostring");
nlua_empty_dict_ref = nlua_ref(lstate, -1);
+ lua_pushvalue(lstate, -1);
+ lua_setfield(lstate, LUA_REGISTRYINDEX, "mpack.empty_dict");
lua_setfield(lstate, -2, "_empty_dict_mt");
+ // vim.mpack
+ luaopen_mpack(lstate);
+ lua_pushvalue(lstate, -1);
+ lua_setfield(lstate, -3, "mpack");
+
+ // package.loaded.mpack = vim.mpack
+ // otherwise luv will be reinitialized when require'mpack'
+ lua_getglobal(lstate, "package");
+ lua_getfield(lstate, -1, "loaded");
+ lua_pushvalue(lstate, -3);
+ lua_setfield(lstate, -2, "mpack");
+ lua_pop(lstate, 3);
+
// internal vim._treesitter... API
nlua_add_treesitter(lstate);