mirror of
https://github.com/dragonflydb/dragonfly.git
synced 2025-05-11 10:25:47 +02:00
feat(core): Add lua modules (#273)
feat(server): Add modules to lua interpreter
This commit is contained in:
parent
cfa1265b29
commit
a43badf50c
14 changed files with 3700 additions and 1 deletions
|
@ -1,7 +1,7 @@
|
|||
add_library(dfly_core compact_object.cc dragonfly_core.cc extent_tree.cc
|
||||
external_alloc.cc interpreter.cc mi_memory_resource.cc
|
||||
segment_allocator.cc small_string.cc tx_queue.cc)
|
||||
cxx_link(dfly_core base absl::flat_hash_map absl::str_format redis_lib TRDP::lua
|
||||
cxx_link(dfly_core base absl::flat_hash_map absl::str_format redis_lib TRDP::lua lua_modules
|
||||
Boost::fiber crypto)
|
||||
|
||||
|
||||
|
|
|
@ -14,6 +14,11 @@ extern "C" {
|
|||
#include <lauxlib.h>
|
||||
#include <lua.h>
|
||||
#include <lualib.h>
|
||||
|
||||
LUALIB_API int (luaopen_cjson) (lua_State *L);
|
||||
LUALIB_API int (luaopen_struct) (lua_State *L);
|
||||
LUALIB_API int (luaopen_cmsgpack) (lua_State *L);
|
||||
LUALIB_API int (luaopen_bit) (lua_State *L);
|
||||
}
|
||||
|
||||
#include <absl/strings/str_format.h>
|
||||
|
@ -196,6 +201,12 @@ int RaiseError(lua_State* lua) {
|
|||
return lua_error(lua);
|
||||
}
|
||||
|
||||
void LoadLibrary(lua_State *lua, const char *libname, lua_CFunction luafunc) {
|
||||
lua_pushcfunction(lua, luafunc);
|
||||
lua_pushstring(lua, libname);
|
||||
lua_call(lua, 1, 0);
|
||||
}
|
||||
|
||||
void InitLua(lua_State* lua) {
|
||||
Require(lua, "", luaopen_base);
|
||||
Require(lua, LUA_TABLIBNAME, luaopen_table);
|
||||
|
@ -203,6 +214,12 @@ void InitLua(lua_State* lua) {
|
|||
Require(lua, LUA_MATHLIBNAME, luaopen_math);
|
||||
Require(lua, LUA_DBLIBNAME, luaopen_debug);
|
||||
|
||||
LoadLibrary(lua, "cjson", luaopen_cjson);
|
||||
LoadLibrary(lua, "struct", luaopen_struct);
|
||||
LoadLibrary(lua, "cmsgpack", luaopen_cmsgpack);
|
||||
LoadLibrary(lua, "bit", luaopen_bit);
|
||||
|
||||
|
||||
/* Add a helper function we use for pcall error reporting.
|
||||
* Note that when the error is in the C function we want to report the
|
||||
* information about the caller, that's what makes sense from the point
|
||||
|
|
|
@ -301,4 +301,24 @@ TEST_F(InterpreterTest, ArgKeys) {
|
|||
EXPECT_EQ("[str(foo) str(key1) str(key2)]", ser_.res);
|
||||
}
|
||||
|
||||
TEST_F(InterpreterTest, Modules) {
|
||||
// cjson module
|
||||
EXPECT_TRUE(Execute("return cjson.encode({1, 2, 3})"));
|
||||
EXPECT_EQ("str([1,2,3])", ser_.res);
|
||||
EXPECT_TRUE(Execute("return cjson.decode('{\"a\": 1}')['a']"));
|
||||
EXPECT_EQ("d(1)", ser_.res);
|
||||
|
||||
// cmsgpack module
|
||||
EXPECT_TRUE(Execute("return cmsgpack.pack('ok', true)"));
|
||||
EXPECT_EQ("str(\xA2ok\xC3)", ser_.res);
|
||||
|
||||
// bit module
|
||||
EXPECT_TRUE(Execute("return bit.bor(8, 4, 5)"));
|
||||
EXPECT_EQ("i(13)", ser_.res);
|
||||
|
||||
// struct module
|
||||
EXPECT_TRUE(Execute("return struct.pack('bbc4', 1, 2, 'test')"));
|
||||
EXPECT_EQ("str(\x1\x2test)", ser_.res);
|
||||
}
|
||||
|
||||
} // namespace dfly
|
||||
|
|
|
@ -20,3 +20,5 @@ target_compile_options(redis_lib PRIVATE -Wno-maybe-uninitialized)
|
|||
if (REDIS_ZMALLOC_MI)
|
||||
target_compile_definitions(redis_lib PUBLIC USE_ZMALLOC_MI)
|
||||
endif()
|
||||
|
||||
add_subdirectory(lua)
|
||||
|
|
11
src/redis/lua/CMakeLists.txt
Normal file
11
src/redis/lua/CMakeLists.txt
Normal file
|
@ -0,0 +1,11 @@
|
|||
add_library(lua_modules STATIC
|
||||
cjson/fpconv.c cjson/strbuf.c cjson/lua_cjson.c
|
||||
cmsgpack/lua_cmsgpack.c
|
||||
struct/lua_struct.c
|
||||
bit/bit.c
|
||||
)
|
||||
|
||||
target_compile_options(lua_modules PRIVATE
|
||||
-Wno-sign-compare -Wno-misleading-indentation -Wno-implicit-fallthrough -Wno-undefined-inline)
|
||||
|
||||
target_link_libraries(lua_modules TRDP::lua)
|
3
src/redis/lua/README.md
Normal file
3
src/redis/lua/README.md
Normal file
|
@ -0,0 +1,3 @@
|
|||
Since version 5.2 `luaL_register` is deprecated and removed. The new `luaL_newlib` function doesn't make the module globally available upon registration and is ment to be used with the `require` function.
|
||||
|
||||
To provide the modules globally, `luaL_newlib` is followed by a `lua_setglobal` for bit and struct.
|
196
src/redis/lua/bit/bit.c
Normal file
196
src/redis/lua/bit/bit.c
Normal file
|
@ -0,0 +1,196 @@
|
|||
/*
|
||||
** Lua BitOp -- a bit operations library for Lua 5.1/5.2.
|
||||
** http://bitop.luajit.org/
|
||||
**
|
||||
** Copyright (C) 2008-2012 Mike Pall. All rights reserved.
|
||||
**
|
||||
** Permission is hereby granted, free of charge, to any person obtaining
|
||||
** a copy of this software and associated documentation files (the
|
||||
** "Software"), to deal in the Software without restriction, including
|
||||
** without limitation the rights to use, copy, modify, merge, publish,
|
||||
** distribute, sublicense, and/or sell copies of the Software, and to
|
||||
** permit persons to whom the Software is furnished to do so, subject to
|
||||
** the following conditions:
|
||||
**
|
||||
** The above copyright notice and this permission notice shall be
|
||||
** included in all copies or substantial portions of the Software.
|
||||
**
|
||||
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
** SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
**
|
||||
** [ MIT license: http://www.opensource.org/licenses/mit-license.php ]
|
||||
*/
|
||||
|
||||
#define LUA_BITOP_VERSION "1.0.3"
|
||||
|
||||
#define LUA_LIB
|
||||
#include "lua.h"
|
||||
#include "lauxlib.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
/* MSVC is stuck in the last century and doesn't have C99's stdint.h. */
|
||||
typedef __int32 int32_t;
|
||||
typedef unsigned __int32 uint32_t;
|
||||
typedef unsigned __int64 uint64_t;
|
||||
#else
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
typedef int32_t SBits;
|
||||
typedef uint32_t UBits;
|
||||
|
||||
typedef union {
|
||||
lua_Number n;
|
||||
#if defined(LUA_NUMBER_DOUBLE) || defined(LUA_FLOAT_DOUBLE)
|
||||
uint64_t b;
|
||||
#else
|
||||
UBits b;
|
||||
#endif
|
||||
} BitNum;
|
||||
|
||||
/* Convert argument to bit type. */
|
||||
static UBits barg(lua_State *L, int idx)
|
||||
{
|
||||
BitNum bn;
|
||||
UBits b;
|
||||
#if LUA_VERSION_NUM < 502
|
||||
bn.n = lua_tonumber(L, idx);
|
||||
#else
|
||||
bn.n = luaL_checknumber(L, idx);
|
||||
#endif
|
||||
#if defined(LUA_NUMBER_DOUBLE) || defined(LUA_FLOAT_DOUBLE)
|
||||
bn.n += 6755399441055744.0; /* 2^52+2^51 */
|
||||
#ifdef SWAPPED_DOUBLE
|
||||
b = (UBits)(bn.b >> 32);
|
||||
#else
|
||||
b = (UBits)bn.b;
|
||||
#endif
|
||||
#elif defined(LUA_NUMBER_INT) || defined(LUA_INT_INT) || \
|
||||
defined(LUA_NUMBER_LONG) || defined(LUA_INT_LONG) || \
|
||||
defined(LUA_NUMBER_LONGLONG) || defined(LUA_INT_LONGLONG) || \
|
||||
defined(LUA_NUMBER_LONG_LONG) || defined(LUA_NUMBER_LLONG)
|
||||
if (sizeof(UBits) == sizeof(lua_Number))
|
||||
b = bn.b;
|
||||
else
|
||||
b = (UBits)(SBits)bn.n;
|
||||
#elif defined(LUA_NUMBER_FLOAT) || defined(LUA_FLOAT_FLOAT)
|
||||
#error "A 'float' lua_Number type is incompatible with this library"
|
||||
#else
|
||||
#error "Unknown number type, check LUA_NUMBER_*, LUA_FLOAT_*, LUA_INT_* in luaconf.h"
|
||||
#endif
|
||||
#if LUA_VERSION_NUM < 502
|
||||
if (b == 0 && !lua_isnumber(L, idx)) {
|
||||
luaL_typerror(L, idx, "number");
|
||||
}
|
||||
#endif
|
||||
return b;
|
||||
}
|
||||
|
||||
/* Return bit type. */
|
||||
#if LUA_VERSION_NUM < 503
|
||||
#define BRET(b) lua_pushnumber(L, (lua_Number)(SBits)(b)); return 1;
|
||||
#else
|
||||
#define BRET(b) lua_pushinteger(L, (lua_Integer)(SBits)(b)); return 1;
|
||||
#endif
|
||||
|
||||
static int bit_tobit(lua_State *L) { BRET(barg(L, 1)) }
|
||||
static int bit_bnot(lua_State *L) { BRET(~barg(L, 1)) }
|
||||
|
||||
#define BIT_OP(func, opr) \
|
||||
static int func(lua_State *L) { int i; UBits b = barg(L, 1); \
|
||||
for (i = lua_gettop(L); i > 1; i--) b opr barg(L, i); BRET(b) }
|
||||
BIT_OP(bit_band, &=)
|
||||
BIT_OP(bit_bor, |=)
|
||||
BIT_OP(bit_bxor, ^=)
|
||||
|
||||
#define bshl(b, n) (b << n)
|
||||
#define bshr(b, n) (b >> n)
|
||||
#define bsar(b, n) ((SBits)b >> n)
|
||||
#define brol(b, n) ((b << n) | (b >> (32-n)))
|
||||
#define bror(b, n) ((b << (32-n)) | (b >> n))
|
||||
#define BIT_SH(func, fn) \
|
||||
static int func(lua_State *L) { \
|
||||
UBits b = barg(L, 1); UBits n = barg(L, 2) & 31; BRET(fn(b, n)) }
|
||||
BIT_SH(bit_lshift, bshl)
|
||||
BIT_SH(bit_rshift, bshr)
|
||||
BIT_SH(bit_arshift, bsar)
|
||||
BIT_SH(bit_rol, brol)
|
||||
BIT_SH(bit_ror, bror)
|
||||
|
||||
static int bit_bswap(lua_State *L)
|
||||
{
|
||||
UBits b = barg(L, 1);
|
||||
b = (b >> 24) | ((b >> 8) & 0xff00) | ((b & 0xff00) << 8) | (b << 24);
|
||||
BRET(b)
|
||||
}
|
||||
|
||||
static int bit_tohex(lua_State *L)
|
||||
{
|
||||
UBits b = barg(L, 1);
|
||||
SBits n = lua_isnone(L, 2) ? 8 : (SBits)barg(L, 2);
|
||||
const char *hexdigits = "0123456789abcdef";
|
||||
char buf[8];
|
||||
int i;
|
||||
if (n < 0) { n = -n; hexdigits = "0123456789ABCDEF"; }
|
||||
if (n > 8) n = 8;
|
||||
for (i = (int)n; --i >= 0; ) { buf[i] = hexdigits[b & 15]; b >>= 4; }
|
||||
lua_pushlstring(L, buf, (size_t)n);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const struct luaL_Reg bit_funcs[] = {
|
||||
{ "tobit", bit_tobit },
|
||||
{ "bnot", bit_bnot },
|
||||
{ "band", bit_band },
|
||||
{ "bor", bit_bor },
|
||||
{ "bxor", bit_bxor },
|
||||
{ "lshift", bit_lshift },
|
||||
{ "rshift", bit_rshift },
|
||||
{ "arshift", bit_arshift },
|
||||
{ "rol", bit_rol },
|
||||
{ "ror", bit_ror },
|
||||
{ "bswap", bit_bswap },
|
||||
{ "tohex", bit_tohex },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
/* Signed right-shifts are implementation-defined per C89/C99.
|
||||
** But the de facto standard are arithmetic right-shifts on two's
|
||||
** complement CPUs. This behaviour is required here, so test for it.
|
||||
*/
|
||||
#define BAD_SAR (bsar(-8, 2) != (SBits)-2)
|
||||
|
||||
LUALIB_API int luaopen_bit(lua_State *L)
|
||||
{
|
||||
UBits b;
|
||||
#if LUA_VERSION_NUM < 503
|
||||
lua_pushnumber(L, (lua_Number)1437217655L);
|
||||
#else
|
||||
lua_pushinteger(L, (lua_Integer)1437217655L);
|
||||
#endif
|
||||
b = barg(L, -1);
|
||||
if (b != (UBits)1437217655L || BAD_SAR) { /* Perform a simple self-test. */
|
||||
const char *msg = "compiled with incompatible luaconf.h";
|
||||
#if defined(LUA_NUMBER_DOUBLE) || defined(LUA_FLOAT_DOUBLE)
|
||||
#ifdef _WIN32
|
||||
if (b == (UBits)1610612736L)
|
||||
msg = "use D3DCREATE_FPU_PRESERVE with DirectX";
|
||||
#endif
|
||||
if (b == (UBits)1127743488L)
|
||||
msg = "not compiled with SWAPPED_DOUBLE";
|
||||
#endif
|
||||
if (BAD_SAR)
|
||||
msg = "arithmetic right-shift broken";
|
||||
luaL_error(L, "bit library self-test failed (%s)", msg);
|
||||
}
|
||||
|
||||
luaL_newlib(L, bit_funcs);
|
||||
lua_setglobal(L, "bit");
|
||||
|
||||
return 1;
|
||||
}
|
205
src/redis/lua/cjson/fpconv.c
Normal file
205
src/redis/lua/cjson/fpconv.c
Normal file
|
@ -0,0 +1,205 @@
|
|||
/* fpconv - Floating point conversion routines
|
||||
*
|
||||
* Copyright (c) 2011-2012 Mark Pulford <mark@kyne.com.au>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* JSON uses a '.' decimal separator. strtod() / sprintf() under C libraries
|
||||
* with locale support will break when the decimal separator is a comma.
|
||||
*
|
||||
* fpconv_* will around these issues with a translation buffer if required.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "fpconv.h"
|
||||
|
||||
/* Lua CJSON assumes the locale is the same for all threads within a
|
||||
* process and doesn't change after initialisation.
|
||||
*
|
||||
* This avoids the need for per thread storage or expensive checks
|
||||
* for call. */
|
||||
static char locale_decimal_point = '.';
|
||||
|
||||
/* In theory multibyte decimal_points are possible, but
|
||||
* Lua CJSON only supports UTF-8 and known locales only have
|
||||
* single byte decimal points ([.,]).
|
||||
*
|
||||
* localconv() may not be thread safe (=>crash), and nl_langinfo() is
|
||||
* not supported on some platforms. Use sprintf() instead - if the
|
||||
* locale does change, at least Lua CJSON won't crash. */
|
||||
static void fpconv_update_locale()
|
||||
{
|
||||
char buf[8];
|
||||
|
||||
snprintf(buf, sizeof(buf), "%g", 0.5);
|
||||
|
||||
/* Failing this test might imply the platform has a buggy dtoa
|
||||
* implementation or wide characters */
|
||||
if (buf[0] != '0' || buf[2] != '5' || buf[3] != 0) {
|
||||
fprintf(stderr, "Error: wide characters found or printf() bug.");
|
||||
abort();
|
||||
}
|
||||
|
||||
locale_decimal_point = buf[1];
|
||||
}
|
||||
|
||||
/* Check for a valid number character: [-+0-9a-yA-Y.]
|
||||
* Eg: -0.6e+5, infinity, 0xF0.F0pF0
|
||||
*
|
||||
* Used to find the probable end of a number. It doesn't matter if
|
||||
* invalid characters are counted - strtod() will find the valid
|
||||
* number if it exists. The risk is that slightly more memory might
|
||||
* be allocated before a parse error occurs. */
|
||||
static inline int valid_number_character(char ch)
|
||||
{
|
||||
char lower_ch;
|
||||
|
||||
if ('0' <= ch && ch <= '9')
|
||||
return 1;
|
||||
if (ch == '-' || ch == '+' || ch == '.')
|
||||
return 1;
|
||||
|
||||
/* Hex digits, exponent (e), base (p), "infinity",.. */
|
||||
lower_ch = ch | 0x20;
|
||||
if ('a' <= lower_ch && lower_ch <= 'y')
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Calculate the size of the buffer required for a strtod locale
|
||||
* conversion. */
|
||||
static int strtod_buffer_size(const char *s)
|
||||
{
|
||||
const char *p = s;
|
||||
|
||||
while (valid_number_character(*p))
|
||||
p++;
|
||||
|
||||
return p - s;
|
||||
}
|
||||
|
||||
/* Similar to strtod(), but must be passed the current locale's decimal point
|
||||
* character. Guaranteed to be called at the start of any valid number in a string */
|
||||
double fpconv_strtod(const char *nptr, char **endptr)
|
||||
{
|
||||
char localbuf[FPCONV_G_FMT_BUFSIZE];
|
||||
char *buf, *endbuf, *dp;
|
||||
int buflen;
|
||||
double value;
|
||||
|
||||
/* System strtod() is fine when decimal point is '.' */
|
||||
if (locale_decimal_point == '.')
|
||||
return strtod(nptr, endptr);
|
||||
|
||||
buflen = strtod_buffer_size(nptr);
|
||||
if (!buflen) {
|
||||
/* No valid characters found, standard strtod() return */
|
||||
*endptr = (char *)nptr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Duplicate number into buffer */
|
||||
if (buflen >= FPCONV_G_FMT_BUFSIZE) {
|
||||
/* Handle unusually large numbers */
|
||||
buf = malloc(buflen + 1);
|
||||
if (!buf) {
|
||||
fprintf(stderr, "Out of memory");
|
||||
abort();
|
||||
}
|
||||
} else {
|
||||
/* This is the common case.. */
|
||||
buf = localbuf;
|
||||
}
|
||||
memcpy(buf, nptr, buflen);
|
||||
buf[buflen] = 0;
|
||||
|
||||
/* Update decimal point character if found */
|
||||
dp = strchr(buf, '.');
|
||||
if (dp)
|
||||
*dp = locale_decimal_point;
|
||||
|
||||
value = strtod(buf, &endbuf);
|
||||
*endptr = (char *)&nptr[endbuf - buf];
|
||||
if (buflen >= FPCONV_G_FMT_BUFSIZE)
|
||||
free(buf);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/* "fmt" must point to a buffer of at least 6 characters */
|
||||
static void set_number_format(char *fmt, int precision)
|
||||
{
|
||||
int d1, d2, i;
|
||||
|
||||
assert(1 <= precision && precision <= 14);
|
||||
|
||||
/* Create printf format (%.14g) from precision */
|
||||
d1 = precision / 10;
|
||||
d2 = precision % 10;
|
||||
fmt[0] = '%';
|
||||
fmt[1] = '.';
|
||||
i = 2;
|
||||
if (d1) {
|
||||
fmt[i++] = '0' + d1;
|
||||
}
|
||||
fmt[i++] = '0' + d2;
|
||||
fmt[i++] = 'g';
|
||||
fmt[i] = 0;
|
||||
}
|
||||
|
||||
/* Assumes there is always at least 32 characters available in the target buffer */
|
||||
int fpconv_g_fmt(char *str, double num, int precision)
|
||||
{
|
||||
char buf[FPCONV_G_FMT_BUFSIZE];
|
||||
char fmt[6];
|
||||
int len;
|
||||
char *b;
|
||||
|
||||
set_number_format(fmt, precision);
|
||||
|
||||
/* Pass through when decimal point character is dot. */
|
||||
if (locale_decimal_point == '.')
|
||||
return snprintf(str, FPCONV_G_FMT_BUFSIZE, fmt, num);
|
||||
|
||||
/* snprintf() to a buffer then translate for other decimal point characters */
|
||||
len = snprintf(buf, FPCONV_G_FMT_BUFSIZE, fmt, num);
|
||||
|
||||
/* Copy into target location. Translate decimal point if required */
|
||||
b = buf;
|
||||
do {
|
||||
*str++ = (*b == locale_decimal_point ? '.' : *b);
|
||||
} while(*b++);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
void fpconv_init()
|
||||
{
|
||||
fpconv_update_locale();
|
||||
}
|
||||
|
||||
/* vi:ai et sw=4 ts=4:
|
||||
*/
|
22
src/redis/lua/cjson/fpconv.h
Normal file
22
src/redis/lua/cjson/fpconv.h
Normal file
|
@ -0,0 +1,22 @@
|
|||
/* Lua CJSON floating point conversion routines */
|
||||
|
||||
/* Buffer required to store the largest string representation of a double.
|
||||
*
|
||||
* Longest double printed with %.14g is 21 characters long:
|
||||
* -1.7976931348623e+308 */
|
||||
# define FPCONV_G_FMT_BUFSIZE 32
|
||||
|
||||
#ifdef USE_INTERNAL_FPCONV
|
||||
static inline void fpconv_init()
|
||||
{
|
||||
/* Do nothing - not required */
|
||||
}
|
||||
#else
|
||||
extern void fpconv_init();
|
||||
#endif
|
||||
|
||||
extern int fpconv_g_fmt(char*, double, int);
|
||||
extern double fpconv_strtod(const char*, char**);
|
||||
|
||||
/* vi:ai et sw=4 ts=4:
|
||||
*/
|
1422
src/redis/lua/cjson/lua_cjson.c
Normal file
1422
src/redis/lua/cjson/lua_cjson.c
Normal file
File diff suppressed because it is too large
Load diff
251
src/redis/lua/cjson/strbuf.c
Normal file
251
src/redis/lua/cjson/strbuf.c
Normal file
|
@ -0,0 +1,251 @@
|
|||
/* strbuf - String buffer routines
|
||||
*
|
||||
* Copyright (c) 2010-2012 Mark Pulford <mark@kyne.com.au>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "strbuf.h"
|
||||
|
||||
static void die(const char *fmt, ...)
|
||||
{
|
||||
va_list arg;
|
||||
|
||||
va_start(arg, fmt);
|
||||
vfprintf(stderr, fmt, arg);
|
||||
va_end(arg);
|
||||
fprintf(stderr, "\n");
|
||||
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
void strbuf_init(strbuf_t *s, int len)
|
||||
{
|
||||
int size;
|
||||
|
||||
if (len <= 0)
|
||||
size = STRBUF_DEFAULT_SIZE;
|
||||
else
|
||||
size = len + 1; /* \0 terminator */
|
||||
|
||||
s->buf = NULL;
|
||||
s->size = size;
|
||||
s->length = 0;
|
||||
s->increment = STRBUF_DEFAULT_INCREMENT;
|
||||
s->dynamic = 0;
|
||||
s->reallocs = 0;
|
||||
s->debug = 0;
|
||||
|
||||
s->buf = malloc(size);
|
||||
if (!s->buf)
|
||||
die("Out of memory");
|
||||
|
||||
strbuf_ensure_null(s);
|
||||
}
|
||||
|
||||
strbuf_t *strbuf_new(int len)
|
||||
{
|
||||
strbuf_t *s;
|
||||
|
||||
s = malloc(sizeof(strbuf_t));
|
||||
if (!s)
|
||||
die("Out of memory");
|
||||
|
||||
strbuf_init(s, len);
|
||||
|
||||
/* Dynamic strbuf allocation / deallocation */
|
||||
s->dynamic = 1;
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
void strbuf_set_increment(strbuf_t *s, int increment)
|
||||
{
|
||||
/* Increment > 0: Linear buffer growth rate
|
||||
* Increment < -1: Exponential buffer growth rate */
|
||||
if (increment == 0 || increment == -1)
|
||||
die("BUG: Invalid string increment");
|
||||
|
||||
s->increment = increment;
|
||||
}
|
||||
|
||||
static inline void debug_stats(strbuf_t *s)
|
||||
{
|
||||
if (s->debug) {
|
||||
fprintf(stderr, "strbuf(%lx) reallocs: %d, length: %d, size: %d\n",
|
||||
(long)s, s->reallocs, s->length, s->size);
|
||||
}
|
||||
}
|
||||
|
||||
/* If strbuf_t has not been dynamically allocated, strbuf_free() can
|
||||
* be called any number of times strbuf_init() */
|
||||
void strbuf_free(strbuf_t *s)
|
||||
{
|
||||
debug_stats(s);
|
||||
|
||||
if (s->buf) {
|
||||
free(s->buf);
|
||||
s->buf = NULL;
|
||||
}
|
||||
if (s->dynamic)
|
||||
free(s);
|
||||
}
|
||||
|
||||
char *strbuf_free_to_string(strbuf_t *s, int *len)
|
||||
{
|
||||
char *buf;
|
||||
|
||||
debug_stats(s);
|
||||
|
||||
strbuf_ensure_null(s);
|
||||
|
||||
buf = s->buf;
|
||||
if (len)
|
||||
*len = s->length;
|
||||
|
||||
if (s->dynamic)
|
||||
free(s);
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
static int calculate_new_size(strbuf_t *s, int len)
|
||||
{
|
||||
int reqsize, newsize;
|
||||
|
||||
if (len <= 0)
|
||||
die("BUG: Invalid strbuf length requested");
|
||||
|
||||
/* Ensure there is room for optional NULL termination */
|
||||
reqsize = len + 1;
|
||||
|
||||
/* If the user has requested to shrink the buffer, do it exactly */
|
||||
if (s->size > reqsize)
|
||||
return reqsize;
|
||||
|
||||
newsize = s->size;
|
||||
if (s->increment < 0) {
|
||||
/* Exponential sizing */
|
||||
while (newsize < reqsize)
|
||||
newsize *= -s->increment;
|
||||
} else {
|
||||
/* Linear sizing */
|
||||
newsize = ((newsize + s->increment - 1) / s->increment) * s->increment;
|
||||
}
|
||||
|
||||
return newsize;
|
||||
}
|
||||
|
||||
|
||||
/* Ensure strbuf can handle a string length bytes long (ignoring NULL
|
||||
* optional termination). */
|
||||
void strbuf_resize(strbuf_t *s, int len)
|
||||
{
|
||||
int newsize;
|
||||
|
||||
newsize = calculate_new_size(s, len);
|
||||
|
||||
if (s->debug > 1) {
|
||||
fprintf(stderr, "strbuf(%lx) resize: %d => %d\n",
|
||||
(long)s, s->size, newsize);
|
||||
}
|
||||
|
||||
s->size = newsize;
|
||||
s->buf = realloc(s->buf, s->size);
|
||||
if (!s->buf)
|
||||
die("Out of memory");
|
||||
s->reallocs++;
|
||||
}
|
||||
|
||||
void strbuf_append_string(strbuf_t *s, const char *str)
|
||||
{
|
||||
int space, i;
|
||||
|
||||
space = strbuf_empty_length(s);
|
||||
|
||||
for (i = 0; str[i]; i++) {
|
||||
if (space < 1) {
|
||||
strbuf_resize(s, s->length + 1);
|
||||
space = strbuf_empty_length(s);
|
||||
}
|
||||
|
||||
s->buf[s->length] = str[i];
|
||||
s->length++;
|
||||
space--;
|
||||
}
|
||||
}
|
||||
|
||||
/* strbuf_append_fmt() should only be used when an upper bound
|
||||
* is known for the output string. */
|
||||
void strbuf_append_fmt(strbuf_t *s, int len, const char *fmt, ...)
|
||||
{
|
||||
va_list arg;
|
||||
int fmt_len;
|
||||
|
||||
strbuf_ensure_empty_length(s, len);
|
||||
|
||||
va_start(arg, fmt);
|
||||
fmt_len = vsnprintf(s->buf + s->length, len, fmt, arg);
|
||||
va_end(arg);
|
||||
|
||||
if (fmt_len < 0)
|
||||
die("BUG: Unable to convert number"); /* This should never happen.. */
|
||||
|
||||
s->length += fmt_len;
|
||||
}
|
||||
|
||||
/* strbuf_append_fmt_retry() can be used when the there is no known
|
||||
* upper bound for the output string. */
|
||||
void strbuf_append_fmt_retry(strbuf_t *s, const char *fmt, ...)
|
||||
{
|
||||
va_list arg;
|
||||
int fmt_len, try;
|
||||
int empty_len;
|
||||
|
||||
/* If the first attempt to append fails, resize the buffer appropriately
|
||||
* and try again */
|
||||
for (try = 0; ; try++) {
|
||||
va_start(arg, fmt);
|
||||
/* Append the new formatted string */
|
||||
/* fmt_len is the length of the string required, excluding the
|
||||
* trailing NULL */
|
||||
empty_len = strbuf_empty_length(s);
|
||||
/* Add 1 since there is also space to store the terminating NULL. */
|
||||
fmt_len = vsnprintf(s->buf + s->length, empty_len + 1, fmt, arg);
|
||||
va_end(arg);
|
||||
|
||||
if (fmt_len <= empty_len)
|
||||
break; /* SUCCESS */
|
||||
if (try > 0)
|
||||
die("BUG: length of formatted string changed");
|
||||
|
||||
strbuf_resize(s, s->length + fmt_len);
|
||||
}
|
||||
|
||||
s->length += fmt_len;
|
||||
}
|
||||
|
||||
/* vi:ai et sw=4 ts=4:
|
||||
*/
|
154
src/redis/lua/cjson/strbuf.h
Normal file
154
src/redis/lua/cjson/strbuf.h
Normal file
|
@ -0,0 +1,154 @@
|
|||
/* strbuf - String buffer routines
|
||||
*
|
||||
* Copyright (c) 2010-2012 Mark Pulford <mark@kyne.com.au>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
/* Size: Total bytes allocated to *buf
|
||||
* Length: String length, excluding optional NULL terminator.
|
||||
* Increment: Allocation increments when resizing the string buffer.
|
||||
* Dynamic: True if created via strbuf_new()
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
char *buf;
|
||||
int size;
|
||||
int length;
|
||||
int increment;
|
||||
int dynamic;
|
||||
int reallocs;
|
||||
int debug;
|
||||
} strbuf_t;
|
||||
|
||||
#ifndef STRBUF_DEFAULT_SIZE
|
||||
#define STRBUF_DEFAULT_SIZE 1023
|
||||
#endif
|
||||
#ifndef STRBUF_DEFAULT_INCREMENT
|
||||
#define STRBUF_DEFAULT_INCREMENT -2
|
||||
#endif
|
||||
|
||||
/* Initialise */
|
||||
extern strbuf_t *strbuf_new(int len);
|
||||
extern void strbuf_init(strbuf_t *s, int len);
|
||||
extern void strbuf_set_increment(strbuf_t *s, int increment);
|
||||
|
||||
/* Release */
|
||||
extern void strbuf_free(strbuf_t *s);
|
||||
extern char *strbuf_free_to_string(strbuf_t *s, int *len);
|
||||
|
||||
/* Management */
|
||||
extern void strbuf_resize(strbuf_t *s, int len);
|
||||
static int strbuf_empty_length(strbuf_t *s);
|
||||
static int strbuf_length(strbuf_t *s);
|
||||
static char *strbuf_string(strbuf_t *s, int *len);
|
||||
static void strbuf_ensure_empty_length(strbuf_t *s, int len);
|
||||
static char *strbuf_empty_ptr(strbuf_t *s);
|
||||
static void strbuf_extend_length(strbuf_t *s, int len);
|
||||
|
||||
/* Update */
|
||||
extern void strbuf_append_fmt(strbuf_t *s, int len, const char *fmt, ...);
|
||||
extern void strbuf_append_fmt_retry(strbuf_t *s, const char *format, ...);
|
||||
static void strbuf_append_mem(strbuf_t *s, const char *c, int len);
|
||||
extern void strbuf_append_string(strbuf_t *s, const char *str);
|
||||
static void strbuf_append_char(strbuf_t *s, const char c);
|
||||
static void strbuf_ensure_null(strbuf_t *s);
|
||||
|
||||
/* Reset string for before use */
|
||||
static inline void strbuf_reset(strbuf_t *s)
|
||||
{
|
||||
s->length = 0;
|
||||
}
|
||||
|
||||
static inline int strbuf_allocated(strbuf_t *s)
|
||||
{
|
||||
return s->buf != NULL;
|
||||
}
|
||||
|
||||
/* Return bytes remaining in the string buffer
|
||||
* Ensure there is space for a NULL terminator. */
|
||||
static inline int strbuf_empty_length(strbuf_t *s)
|
||||
{
|
||||
return s->size - s->length - 1;
|
||||
}
|
||||
|
||||
static inline void strbuf_ensure_empty_length(strbuf_t *s, int len)
|
||||
{
|
||||
if (len > strbuf_empty_length(s))
|
||||
strbuf_resize(s, s->length + len);
|
||||
}
|
||||
|
||||
static inline char *strbuf_empty_ptr(strbuf_t *s)
|
||||
{
|
||||
return s->buf + s->length;
|
||||
}
|
||||
|
||||
static inline void strbuf_extend_length(strbuf_t *s, int len)
|
||||
{
|
||||
s->length += len;
|
||||
}
|
||||
|
||||
static inline int strbuf_length(strbuf_t *s)
|
||||
{
|
||||
return s->length;
|
||||
}
|
||||
|
||||
static inline void strbuf_append_char(strbuf_t *s, const char c)
|
||||
{
|
||||
strbuf_ensure_empty_length(s, 1);
|
||||
s->buf[s->length++] = c;
|
||||
}
|
||||
|
||||
static inline void strbuf_append_char_unsafe(strbuf_t *s, const char c)
|
||||
{
|
||||
s->buf[s->length++] = c;
|
||||
}
|
||||
|
||||
static inline void strbuf_append_mem(strbuf_t *s, const char *c, int len)
|
||||
{
|
||||
strbuf_ensure_empty_length(s, len);
|
||||
memcpy(s->buf + s->length, c, len);
|
||||
s->length += len;
|
||||
}
|
||||
|
||||
static inline void strbuf_append_mem_unsafe(strbuf_t *s, const char *c, int len)
|
||||
{
|
||||
memcpy(s->buf + s->length, c, len);
|
||||
s->length += len;
|
||||
}
|
||||
|
||||
static inline void strbuf_ensure_null(strbuf_t *s)
|
||||
{
|
||||
s->buf[s->length] = 0;
|
||||
}
|
||||
|
||||
static inline char *strbuf_string(strbuf_t *s, int *len)
|
||||
{
|
||||
if (len)
|
||||
*len = s->length;
|
||||
|
||||
return s->buf;
|
||||
}
|
||||
|
||||
/* vi:ai et sw=4 ts=4:
|
||||
*/
|
974
src/redis/lua/cmsgpack/lua_cmsgpack.c
Normal file
974
src/redis/lua/cmsgpack/lua_cmsgpack.c
Normal file
|
@ -0,0 +1,974 @@
|
|||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "lua.h"
|
||||
#include "lauxlib.h"
|
||||
|
||||
#define LUACMSGPACK_NAME "cmsgpack"
|
||||
#define LUACMSGPACK_SAFE_NAME "cmsgpack_safe"
|
||||
#define LUACMSGPACK_VERSION "lua-cmsgpack 0.4.0"
|
||||
#define LUACMSGPACK_COPYRIGHT "Copyright (C) 2012, Salvatore Sanfilippo"
|
||||
#define LUACMSGPACK_DESCRIPTION "MessagePack C implementation for Lua"
|
||||
|
||||
/* Allows a preprocessor directive to override MAX_NESTING */
|
||||
#ifndef LUACMSGPACK_MAX_NESTING
|
||||
#define LUACMSGPACK_MAX_NESTING 16 /* Max tables nesting. */
|
||||
#endif
|
||||
|
||||
/* Check if float or double can be an integer without loss of precision */
|
||||
#define IS_INT_TYPE_EQUIVALENT(x, T) (!isinf(x) && (T)(x) == (x))
|
||||
|
||||
#define IS_INT64_EQUIVALENT(x) IS_INT_TYPE_EQUIVALENT(x, int64_t)
|
||||
#define IS_INT_EQUIVALENT(x) IS_INT_TYPE_EQUIVALENT(x, int)
|
||||
|
||||
/* If size of pointer is equal to a 4 byte integer, we're on 32 bits. */
|
||||
#if UINTPTR_MAX == UINT_MAX
|
||||
#define BITS_32 1
|
||||
#else
|
||||
#define BITS_32 0
|
||||
#endif
|
||||
|
||||
#if BITS_32
|
||||
#define lua_pushunsigned(L, n) lua_pushnumber(L, n)
|
||||
#else
|
||||
#define lua_pushunsigned(L, n) lua_pushinteger(L, n)
|
||||
#endif
|
||||
|
||||
/* =============================================================================
|
||||
* MessagePack implementation and bindings for Lua 5.1/5.2.
|
||||
* 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).
|
||||
* 04-Apr-2014 (ver 0.3.1): Lua 5.2 support and minor bug fix.
|
||||
* 07-Apr-2014 (ver 0.4.0): Multiple pack/unpack, lua allocator, efficiency.
|
||||
* ========================================================================== */
|
||||
|
||||
/* -------------------------- Endian conversion --------------------------------
|
||||
* We use it only for floats and doubles, all the other conversions performed
|
||||
* in an endian independent fashion. So the only thing we need is a function
|
||||
* that swaps a binary string if 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 check for endianess at runtime.
|
||||
* The performance difference should be acceptable. */
|
||||
void memrevifle(void *ptr, size_t len) {
|
||||
unsigned char *p = (unsigned char *)ptr,
|
||||
*e = (unsigned char *)p+len-1,
|
||||
aux;
|
||||
int test = 1;
|
||||
unsigned char *testp = (unsigned char*) &test;
|
||||
|
||||
if (testp[0] == 0) return; /* Big endian, nothing 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 operation
|
||||
* 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;
|
||||
|
||||
void *mp_realloc(lua_State *L, void *target, size_t osize,size_t nsize) {
|
||||
void *(*local_realloc) (void *, void *, size_t osize, size_t nsize) = NULL;
|
||||
void *ud;
|
||||
|
||||
local_realloc = lua_getallocf(L, &ud);
|
||||
|
||||
return local_realloc(ud, target, osize, nsize);
|
||||
}
|
||||
|
||||
mp_buf *mp_buf_new(lua_State *L) {
|
||||
mp_buf *buf = NULL;
|
||||
|
||||
/* Old size = 0; new size = sizeof(*buf) */
|
||||
buf = (mp_buf*)mp_realloc(L, NULL, 0, sizeof(*buf));
|
||||
|
||||
buf->b = NULL;
|
||||
buf->len = buf->free = 0;
|
||||
return buf;
|
||||
}
|
||||
|
||||
void mp_buf_append(lua_State *L, mp_buf *buf, const unsigned char *s, size_t len) {
|
||||
if (buf->free < len) {
|
||||
size_t newsize = (buf->len+len)*2;
|
||||
|
||||
buf->b = (unsigned char*)mp_realloc(L, buf->b, buf->len + buf->free, newsize);
|
||||
buf->free = newsize - buf->len;
|
||||
}
|
||||
memcpy(buf->b+buf->len,s,len);
|
||||
buf->len += len;
|
||||
buf->free -= len;
|
||||
}
|
||||
|
||||
void mp_buf_free(lua_State *L, mp_buf *buf) {
|
||||
mp_realloc(L, buf->b, buf->len + buf->free, 0); /* realloc to 0 = free */
|
||||
mp_realloc(L, buf, sizeof(*buf), 0);
|
||||
}
|
||||
|
||||
/* ---------------------------- 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 operation. */
|
||||
#define MP_CUR_ERROR_BADFMT 2 /* Bad data format */
|
||||
|
||||
typedef struct mp_cur {
|
||||
const unsigned char *p;
|
||||
size_t left;
|
||||
int err;
|
||||
} mp_cur;
|
||||
|
||||
void mp_cur_init(mp_cur *cursor, const unsigned char *s, size_t len) {
|
||||
cursor->p = s;
|
||||
cursor->left = len;
|
||||
cursor->err = MP_CUR_ERROR_NONE;
|
||||
}
|
||||
|
||||
#define mp_cur_consume(_c,_len) do { _c->p += _len; _c->left -= _len; } while(0)
|
||||
|
||||
/* 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 -------------------------- */
|
||||
|
||||
void mp_encode_bytes(lua_State *L, 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 <= 0xff) {
|
||||
hdr[0] = 0xd9;
|
||||
hdr[1] = len;
|
||||
hdrlen = 2;
|
||||
} 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(L,buf,hdr,hdrlen);
|
||||
mp_buf_append(L,buf,s,len);
|
||||
}
|
||||
|
||||
/* we assume IEEE 754 internal format for single and double precision floats. */
|
||||
void mp_encode_double(lua_State *L, 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(L,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(L,buf,b,9);
|
||||
}
|
||||
}
|
||||
|
||||
void mp_encode_int(lua_State *L, 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] = ((signed 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(L,buf,b,enclen);
|
||||
}
|
||||
|
||||
void mp_encode_array(lua_State *L, 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(L,buf,b,enclen);
|
||||
}
|
||||
|
||||
void mp_encode_map(lua_State *L, 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(L,buf,b,enclen);
|
||||
}
|
||||
|
||||
/* --------------------------- Lua types encoding --------------------------- */
|
||||
|
||||
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(L,buf,(const unsigned char*)s,len);
|
||||
}
|
||||
|
||||
void mp_encode_lua_bool(lua_State *L, mp_buf *buf) {
|
||||
unsigned char b = lua_toboolean(L,-1) ? 0xc3 : 0xc2;
|
||||
mp_buf_append(L,buf,&b,1);
|
||||
}
|
||||
|
||||
/* Lua 5.3 has a built in 64-bit integer type */
|
||||
void mp_encode_lua_integer(lua_State *L, mp_buf *buf) {
|
||||
#if (LUA_VERSION_NUM < 503) && BITS_32
|
||||
lua_Number i = lua_tonumber(L,-1);
|
||||
#else
|
||||
lua_Integer i = lua_tointeger(L,-1);
|
||||
#endif
|
||||
mp_encode_int(L, buf, (int64_t)i);
|
||||
}
|
||||
|
||||
/* Lua 5.2 and lower only has 64-bit doubles, so we need to
|
||||
* detect if the double may be representable as an int
|
||||
* for Lua < 5.3 */
|
||||
void mp_encode_lua_number(lua_State *L, mp_buf *buf) {
|
||||
lua_Number n = lua_tonumber(L,-1);
|
||||
|
||||
if (IS_INT64_EQUIVALENT(n)) {
|
||||
mp_encode_lua_integer(L, buf);
|
||||
} else {
|
||||
mp_encode_double(L,buf,(double)n);
|
||||
}
|
||||
}
|
||||
|
||||
void mp_encode_lua_type(lua_State *L, mp_buf *buf, int level);
|
||||
|
||||
/* Convert a lua table into a message pack list. */
|
||||
void mp_encode_lua_table_as_array(lua_State *L, mp_buf *buf, int level) {
|
||||
#if LUA_VERSION_NUM < 502
|
||||
size_t len = lua_objlen(L,-1), j;
|
||||
#else
|
||||
size_t len = lua_rawlen(L,-1), j;
|
||||
#endif
|
||||
|
||||
mp_encode_array(L,buf,len);
|
||||
luaL_checkstack(L, 1, "in function mp_encode_lua_table_as_array");
|
||||
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. */
|
||||
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 hackish for this lib. */
|
||||
luaL_checkstack(L, 3, "in function mp_encode_lua_table_as_map");
|
||||
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(L,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. */
|
||||
int table_is_an_array(lua_State *L) {
|
||||
int count = 0, max = 0;
|
||||
#if LUA_VERSION_NUM < 503
|
||||
lua_Number n;
|
||||
#else
|
||||
lua_Integer n;
|
||||
#endif
|
||||
|
||||
/* Stack top on function entry */
|
||||
int stacktop;
|
||||
|
||||
stacktop = lua_gettop(L);
|
||||
|
||||
lua_pushnil(L);
|
||||
while(lua_next(L,-2)) {
|
||||
/* Stack: ... key value */
|
||||
lua_pop(L,1); /* Stack: ... key */
|
||||
/* The <= 0 check is valid here because we're comparing indexes. */
|
||||
#if LUA_VERSION_NUM < 503
|
||||
if ((LUA_TNUMBER != lua_type(L,-1)) || (n = lua_tonumber(L, -1)) <= 0 ||
|
||||
!IS_INT_EQUIVALENT(n))
|
||||
#else
|
||||
if (!lua_isinteger(L,-1) || (n = lua_tointeger(L, -1)) <= 0)
|
||||
#endif
|
||||
{
|
||||
lua_settop(L, stacktop);
|
||||
return 0;
|
||||
}
|
||||
max = (n > max ? n : max);
|
||||
count++;
|
||||
}
|
||||
/* We have the total number of elements in "count". Also we have
|
||||
* the max index encountered in "max". 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 max==count you are sure
|
||||
* that there are all the keys form 1 to count (both included). */
|
||||
lua_settop(L, stacktop);
|
||||
return max == count;
|
||||
}
|
||||
|
||||
/* 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. */
|
||||
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);
|
||||
}
|
||||
|
||||
void mp_encode_lua_null(lua_State *L, mp_buf *buf) {
|
||||
unsigned char b[1];
|
||||
|
||||
b[0] = 0xc0;
|
||||
mp_buf_append(L,buf,b,1);
|
||||
}
|
||||
|
||||
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 specified 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:
|
||||
#if LUA_VERSION_NUM < 503
|
||||
mp_encode_lua_number(L,buf); break;
|
||||
#else
|
||||
if (lua_isinteger(L, -1)) {
|
||||
mp_encode_lua_integer(L, buf);
|
||||
} else {
|
||||
mp_encode_lua_number(L, buf);
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
case LUA_TTABLE: mp_encode_lua_table(L,buf,level); break;
|
||||
default: mp_encode_lua_null(L,buf); break;
|
||||
}
|
||||
lua_pop(L,1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Packs all arguments as a stream for multiple upacking later.
|
||||
* Returns error if no arguments provided.
|
||||
*/
|
||||
int mp_pack(lua_State *L) {
|
||||
int nargs = lua_gettop(L);
|
||||
int i;
|
||||
mp_buf *buf;
|
||||
|
||||
if (nargs == 0)
|
||||
return luaL_argerror(L, 0, "MessagePack pack needs input.");
|
||||
|
||||
if (!lua_checkstack(L, nargs))
|
||||
return luaL_argerror(L, 0, "Too many arguments for MessagePack pack.");
|
||||
|
||||
buf = mp_buf_new(L);
|
||||
for(i = 1; i <= nargs; i++) {
|
||||
/* Copy argument i to top of stack for _encode processing;
|
||||
* the encode function pops it from the stack when complete. */
|
||||
luaL_checkstack(L, 1, "in function mp_check");
|
||||
lua_pushvalue(L, i);
|
||||
|
||||
mp_encode_lua_type(L,buf,0);
|
||||
|
||||
lua_pushlstring(L,(char*)buf->b,buf->len);
|
||||
|
||||
/* Reuse the buffer for the next operation by
|
||||
* setting its free count to the total buffer size
|
||||
* and the current position to zero. */
|
||||
buf->free += buf->len;
|
||||
buf->len = 0;
|
||||
}
|
||||
mp_buf_free(L, buf);
|
||||
|
||||
/* Concatenate all nargs buffers together */
|
||||
lua_concat(L, nargs);
|
||||
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) {
|
||||
assert(len <= UINT_MAX);
|
||||
int index = 1;
|
||||
|
||||
lua_newtable(L);
|
||||
luaL_checkstack(L, 1, "in function mp_decode_to_lua_array");
|
||||
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) {
|
||||
assert(len <= UINT_MAX);
|
||||
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);
|
||||
|
||||
/* If we return more than 18 elements, we must resize the stack to
|
||||
* fit all our return values. But, there is no way to
|
||||
* determine how many objects a msgpack will unpack to up front, so
|
||||
* we request a +1 larger stack on each iteration (noop if stack is
|
||||
* big enough, and when stack does require resize it doubles in size) */
|
||||
luaL_checkstack(L, 1,
|
||||
"too many return values at once; "
|
||||
"use unpack_one or unpack_limit instead.");
|
||||
|
||||
switch(c->p[0]) {
|
||||
case 0xcc: /* uint 8 */
|
||||
mp_cur_need(c,2);
|
||||
lua_pushunsigned(L,c->p[1]);
|
||||
mp_cur_consume(c,2);
|
||||
break;
|
||||
case 0xd0: /* int 8 */
|
||||
mp_cur_need(c,2);
|
||||
lua_pushinteger(L,(signed char)c->p[1]);
|
||||
mp_cur_consume(c,2);
|
||||
break;
|
||||
case 0xcd: /* uint 16 */
|
||||
mp_cur_need(c,3);
|
||||
lua_pushunsigned(L,
|
||||
(c->p[1] << 8) |
|
||||
c->p[2]);
|
||||
mp_cur_consume(c,3);
|
||||
break;
|
||||
case 0xd1: /* int 16 */
|
||||
mp_cur_need(c,3);
|
||||
lua_pushinteger(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_pushunsigned(L,
|
||||
((uint32_t)c->p[1] << 24) |
|
||||
((uint32_t)c->p[2] << 16) |
|
||||
((uint32_t)c->p[3] << 8) |
|
||||
(uint32_t)c->p[4]);
|
||||
mp_cur_consume(c,5);
|
||||
break;
|
||||
case 0xd2: /* int 32 */
|
||||
mp_cur_need(c,5);
|
||||
lua_pushinteger(L,
|
||||
((int32_t)c->p[1] << 24) |
|
||||
((int32_t)c->p[2] << 16) |
|
||||
((int32_t)c->p[3] << 8) |
|
||||
(int32_t)c->p[4]);
|
||||
mp_cur_consume(c,5);
|
||||
break;
|
||||
case 0xcf: /* uint 64 */
|
||||
mp_cur_need(c,9);
|
||||
lua_pushunsigned(L,
|
||||
((uint64_t)c->p[1] << 56) |
|
||||
((uint64_t)c->p[2] << 48) |
|
||||
((uint64_t)c->p[3] << 40) |
|
||||
((uint64_t)c->p[4] << 32) |
|
||||
((uint64_t)c->p[5] << 24) |
|
||||
((uint64_t)c->p[6] << 16) |
|
||||
((uint64_t)c->p[7] << 8) |
|
||||
(uint64_t)c->p[8]);
|
||||
mp_cur_consume(c,9);
|
||||
break;
|
||||
case 0xd3: /* int 64 */
|
||||
mp_cur_need(c,9);
|
||||
#if LUA_VERSION_NUM < 503
|
||||
lua_pushnumber(L,
|
||||
#else
|
||||
lua_pushinteger(L,
|
||||
#endif
|
||||
((int64_t)c->p[1] << 56) |
|
||||
((int64_t)c->p[2] << 48) |
|
||||
((int64_t)c->p[3] << 40) |
|
||||
((int64_t)c->p[4] << 32) |
|
||||
((int64_t)c->p[5] << 24) |
|
||||
((int64_t)c->p[6] << 16) |
|
||||
((int64_t)c->p[7] << 8) |
|
||||
(int64_t)c->p[8]);
|
||||
mp_cur_consume(c,9);
|
||||
break;
|
||||
case 0xc0: /* nil */
|
||||
lua_pushnil(L);
|
||||
mp_cur_consume(c,1);
|
||||
break;
|
||||
case 0xc3: /* true */
|
||||
lua_pushboolean(L,1);
|
||||
mp_cur_consume(c,1);
|
||||
break;
|
||||
case 0xc2: /* false */
|
||||
lua_pushboolean(L,0);
|
||||
mp_cur_consume(c,1);
|
||||
break;
|
||||
case 0xca: /* float */
|
||||
mp_cur_need(c,5);
|
||||
assert(sizeof(float) == 4);
|
||||
{
|
||||
float f;
|
||||
memcpy(&f,c->p+1,4);
|
||||
memrevifle(&f,4);
|
||||
lua_pushnumber(L,f);
|
||||
mp_cur_consume(c,5);
|
||||
}
|
||||
break;
|
||||
case 0xcb: /* double */
|
||||
mp_cur_need(c,9);
|
||||
assert(sizeof(double) == 8);
|
||||
{
|
||||
double d;
|
||||
memcpy(&d,c->p+1,8);
|
||||
memrevifle(&d,8);
|
||||
lua_pushnumber(L,d);
|
||||
mp_cur_consume(c,9);
|
||||
}
|
||||
break;
|
||||
case 0xd9: /* raw 8 */
|
||||
mp_cur_need(c,2);
|
||||
{
|
||||
size_t l = c->p[1];
|
||||
mp_cur_need(c,2+l);
|
||||
lua_pushlstring(L,(char*)c->p+2,l);
|
||||
mp_cur_consume(c,2+l);
|
||||
}
|
||||
break;
|
||||
case 0xda: /* raw 16 */
|
||||
mp_cur_need(c,3);
|
||||
{
|
||||
size_t l = (c->p[1] << 8) | c->p[2];
|
||||
mp_cur_need(c,3+l);
|
||||
lua_pushlstring(L,(char*)c->p+3,l);
|
||||
mp_cur_consume(c,3+l);
|
||||
}
|
||||
break;
|
||||
case 0xdb: /* raw 32 */
|
||||
mp_cur_need(c,5);
|
||||
{
|
||||
size_t l = ((size_t)c->p[1] << 24) |
|
||||
((size_t)c->p[2] << 16) |
|
||||
((size_t)c->p[3] << 8) |
|
||||
(size_t)c->p[4];
|
||||
mp_cur_consume(c,5);
|
||||
mp_cur_need(c,l);
|
||||
lua_pushlstring(L,(char*)c->p,l);
|
||||
mp_cur_consume(c,l);
|
||||
}
|
||||
break;
|
||||
case 0xdc: /* array 16 */
|
||||
mp_cur_need(c,3);
|
||||
{
|
||||
size_t l = (c->p[1] << 8) | c->p[2];
|
||||
mp_cur_consume(c,3);
|
||||
mp_decode_to_lua_array(L,c,l);
|
||||
}
|
||||
break;
|
||||
case 0xdd: /* array 32 */
|
||||
mp_cur_need(c,5);
|
||||
{
|
||||
size_t l = ((size_t)c->p[1] << 24) |
|
||||
((size_t)c->p[2] << 16) |
|
||||
((size_t)c->p[3] << 8) |
|
||||
(size_t)c->p[4];
|
||||
mp_cur_consume(c,5);
|
||||
mp_decode_to_lua_array(L,c,l);
|
||||
}
|
||||
break;
|
||||
case 0xde: /* map 16 */
|
||||
mp_cur_need(c,3);
|
||||
{
|
||||
size_t l = (c->p[1] << 8) | c->p[2];
|
||||
mp_cur_consume(c,3);
|
||||
mp_decode_to_lua_hash(L,c,l);
|
||||
}
|
||||
break;
|
||||
case 0xdf: /* map 32 */
|
||||
mp_cur_need(c,5);
|
||||
{
|
||||
size_t l = ((size_t)c->p[1] << 24) |
|
||||
((size_t)c->p[2] << 16) |
|
||||
((size_t)c->p[3] << 8) |
|
||||
(size_t)c->p[4];
|
||||
mp_cur_consume(c,5);
|
||||
mp_decode_to_lua_hash(L,c,l);
|
||||
}
|
||||
break;
|
||||
default: /* types that can't be idenitified by first byte value. */
|
||||
if ((c->p[0] & 0x80) == 0) { /* positive fixnum */
|
||||
lua_pushunsigned(L,c->p[0]);
|
||||
mp_cur_consume(c,1);
|
||||
} else if ((c->p[0] & 0xe0) == 0xe0) { /* negative fixnum */
|
||||
lua_pushinteger(L,(signed char)c->p[0]);
|
||||
mp_cur_consume(c,1);
|
||||
} else if ((c->p[0] & 0xe0) == 0xa0) { /* fix raw */
|
||||
size_t l = c->p[0] & 0x1f;
|
||||
mp_cur_need(c,1+l);
|
||||
lua_pushlstring(L,(char*)c->p+1,l);
|
||||
mp_cur_consume(c,1+l);
|
||||
} else if ((c->p[0] & 0xf0) == 0x90) { /* fix map */
|
||||
size_t l = c->p[0] & 0xf;
|
||||
mp_cur_consume(c,1);
|
||||
mp_decode_to_lua_array(L,c,l);
|
||||
} else if ((c->p[0] & 0xf0) == 0x80) { /* fix map */
|
||||
size_t l = c->p[0] & 0xf;
|
||||
mp_cur_consume(c,1);
|
||||
mp_decode_to_lua_hash(L,c,l);
|
||||
} else {
|
||||
c->err = MP_CUR_ERROR_BADFMT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int mp_unpack_full(lua_State *L, int limit, int offset) {
|
||||
size_t len;
|
||||
const char *s;
|
||||
mp_cur c;
|
||||
int cnt; /* Number of objects unpacked */
|
||||
int decode_all = (!limit && !offset);
|
||||
|
||||
s = luaL_checklstring(L,1,&len); /* if no match, exits */
|
||||
|
||||
if (offset < 0 || limit < 0) /* requesting negative off or lim is invalid */
|
||||
return luaL_error(L,
|
||||
"Invalid request to unpack with offset of %d and limit of %d.",
|
||||
offset, len);
|
||||
else if (offset > len)
|
||||
return luaL_error(L,
|
||||
"Start offset %d greater than input length %d.", offset, len);
|
||||
|
||||
if (decode_all) limit = INT_MAX;
|
||||
|
||||
mp_cur_init(&c,(const unsigned char *)s+offset,len-offset);
|
||||
|
||||
/* We loop over the decode because this could be a stream
|
||||
* of multiple top-level values serialized together */
|
||||
for(cnt = 0; c.left > 0 && cnt < limit; cnt++) {
|
||||
mp_decode_to_lua_type(L,&c);
|
||||
|
||||
if (c.err == MP_CUR_ERROR_EOF) {
|
||||
return luaL_error(L,"Missing bytes in input.");
|
||||
} else if (c.err == MP_CUR_ERROR_BADFMT) {
|
||||
return luaL_error(L,"Bad data format in input.");
|
||||
}
|
||||
}
|
||||
|
||||
if (!decode_all) {
|
||||
/* c->left is the remaining size of the input buffer.
|
||||
* subtract the entire buffer size from the unprocessed size
|
||||
* to get our next start offset */
|
||||
int offset = len - c.left;
|
||||
|
||||
luaL_checkstack(L, 1, "in function mp_unpack_full");
|
||||
|
||||
/* Return offset -1 when we have have processed the entire buffer. */
|
||||
lua_pushinteger(L, c.left == 0 ? -1 : offset);
|
||||
/* Results are returned with the arg elements still
|
||||
* in place. Lua takes care of only returning
|
||||
* elements above the args for us.
|
||||
* In this case, we have one arg on the stack
|
||||
* for this function, so we insert our first return
|
||||
* value at position 2. */
|
||||
lua_insert(L, 2);
|
||||
cnt += 1; /* increase return count by one to make room for offset */
|
||||
}
|
||||
|
||||
return cnt;
|
||||
}
|
||||
|
||||
int mp_unpack(lua_State *L) {
|
||||
return mp_unpack_full(L, 0, 0);
|
||||
}
|
||||
|
||||
int mp_unpack_one(lua_State *L) {
|
||||
int offset = luaL_optinteger(L, 2, 0);
|
||||
/* Variable pop because offset may not exist */
|
||||
lua_pop(L, lua_gettop(L)-1);
|
||||
return mp_unpack_full(L, 1, offset);
|
||||
}
|
||||
|
||||
int mp_unpack_limit(lua_State *L) {
|
||||
int limit = luaL_checkinteger(L, 2);
|
||||
int offset = luaL_optinteger(L, 3, 0);
|
||||
/* Variable pop because offset may not exist */
|
||||
lua_pop(L, lua_gettop(L)-1);
|
||||
|
||||
return mp_unpack_full(L, limit, offset);
|
||||
}
|
||||
|
||||
int mp_safe(lua_State *L) {
|
||||
int argc, err, total_results;
|
||||
|
||||
argc = lua_gettop(L);
|
||||
|
||||
/* This adds our function to the bottom of the stack
|
||||
* (the "call this function" position) */
|
||||
lua_pushvalue(L, lua_upvalueindex(1));
|
||||
lua_insert(L, 1);
|
||||
|
||||
err = lua_pcall(L, argc, LUA_MULTRET, 0);
|
||||
total_results = lua_gettop(L);
|
||||
|
||||
if (!err) {
|
||||
return total_results;
|
||||
} else {
|
||||
lua_pushnil(L);
|
||||
lua_insert(L,-2);
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
const struct luaL_Reg cmds[] = {
|
||||
{"pack", mp_pack},
|
||||
{"unpack", mp_unpack},
|
||||
{"unpack_one", mp_unpack_one},
|
||||
{"unpack_limit", mp_unpack_limit},
|
||||
{0}
|
||||
};
|
||||
|
||||
int luaopen_create(lua_State *L) {
|
||||
int i;
|
||||
/* Manually construct our module table instead of
|
||||
* relying on _register or _newlib */
|
||||
lua_newtable(L);
|
||||
|
||||
for (i = 0; i < (sizeof(cmds)/sizeof(*cmds) - 1); i++) {
|
||||
lua_pushcfunction(L, cmds[i].func);
|
||||
lua_setfield(L, -2, cmds[i].name);
|
||||
}
|
||||
|
||||
/* Add metadata */
|
||||
lua_pushliteral(L, LUACMSGPACK_NAME);
|
||||
lua_setfield(L, -2, "_NAME");
|
||||
lua_pushliteral(L, LUACMSGPACK_VERSION);
|
||||
lua_setfield(L, -2, "_VERSION");
|
||||
lua_pushliteral(L, LUACMSGPACK_COPYRIGHT);
|
||||
lua_setfield(L, -2, "_COPYRIGHT");
|
||||
lua_pushliteral(L, LUACMSGPACK_DESCRIPTION);
|
||||
lua_setfield(L, -2, "_DESCRIPTION");
|
||||
return 1;
|
||||
}
|
||||
|
||||
LUALIB_API int luaopen_cmsgpack(lua_State *L) {
|
||||
luaopen_create(L);
|
||||
|
||||
lua_pushvalue(L, -1);
|
||||
lua_setglobal(L, LUACMSGPACK_NAME);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
LUALIB_API int luaopen_cmsgpack_safe(lua_State *L) {
|
||||
int i;
|
||||
|
||||
luaopen_cmsgpack(L);
|
||||
|
||||
/* Wrap all functions in the safe handler */
|
||||
for (i = 0; i < (sizeof(cmds)/sizeof(*cmds) - 1); i++) {
|
||||
lua_getfield(L, -1, cmds[i].name);
|
||||
lua_pushcclosure(L, mp_safe, 1);
|
||||
lua_setfield(L, -2, cmds[i].name);
|
||||
}
|
||||
|
||||
#if LUA_VERSION_NUM < 502
|
||||
/* Register name globally for 5.1 */
|
||||
lua_pushvalue(L, -1);
|
||||
lua_setglobal(L, LUACMSGPACK_SAFE_NAME);
|
||||
#endif
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* Copyright (C) 2012 Salvatore Sanfilippo. All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
******************************************************************************/
|
422
src/redis/lua/struct/lua_struct.c
Normal file
422
src/redis/lua/struct/lua_struct.c
Normal file
|
@ -0,0 +1,422 @@
|
|||
/*
|
||||
** {======================================================
|
||||
** Library for packing/unpacking structures.
|
||||
** $Id: struct.c,v 1.7 2018/05/11 22:04:31 roberto Exp $
|
||||
** See Copyright Notice at the end of this file
|
||||
** =======================================================
|
||||
*/
|
||||
/*
|
||||
** Valid formats:
|
||||
** > - big endian
|
||||
** < - little endian
|
||||
** ![num] - alignment
|
||||
** x - pading
|
||||
** b/B - signed/unsigned byte
|
||||
** h/H - signed/unsigned short
|
||||
** l/L - signed/unsigned long
|
||||
** T - size_t
|
||||
** i/In - signed/unsigned integer with size 'n' (default is size of int)
|
||||
** cn - sequence of 'n' chars (from/to a string); when packing, n==0 means
|
||||
the whole string; when unpacking, n==0 means use the previous
|
||||
read number as the string length
|
||||
** s - zero-terminated string
|
||||
** f - float
|
||||
** d - double
|
||||
** ' ' - ignored
|
||||
*/
|
||||
|
||||
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <limits.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
#include "lua.h"
|
||||
#include "lauxlib.h"
|
||||
|
||||
|
||||
/* basic integer type */
|
||||
#if !defined(STRUCT_INT)
|
||||
#define STRUCT_INT long
|
||||
#endif
|
||||
|
||||
typedef STRUCT_INT Inttype;
|
||||
|
||||
/* corresponding unsigned version */
|
||||
typedef unsigned STRUCT_INT Uinttype;
|
||||
|
||||
|
||||
/* maximum size (in bytes) for integral types */
|
||||
#define MAXINTSIZE 32
|
||||
|
||||
/* is 'x' a power of 2? */
|
||||
#define isp2(x) ((x) > 0 && ((x) & ((x) - 1)) == 0)
|
||||
|
||||
/* dummy structure to get alignment requirements */
|
||||
struct cD {
|
||||
char c;
|
||||
double d;
|
||||
};
|
||||
|
||||
|
||||
#define PADDING (sizeof(struct cD) - sizeof(double))
|
||||
#define MAXALIGN (PADDING > sizeof(int) ? PADDING : sizeof(int))
|
||||
|
||||
|
||||
/* endian options */
|
||||
#define BIG 0
|
||||
#define LITTLE 1
|
||||
|
||||
|
||||
static union {
|
||||
int dummy;
|
||||
char endian;
|
||||
} const native = {1};
|
||||
|
||||
|
||||
typedef struct Header {
|
||||
int endian;
|
||||
int align;
|
||||
} Header;
|
||||
|
||||
|
||||
static int getnum (lua_State *L, const char **fmt, int df) {
|
||||
if (!isdigit(**fmt)) /* no number? */
|
||||
return df; /* return default value */
|
||||
else {
|
||||
int a = 0;
|
||||
do {
|
||||
if (a > (INT_MAX / 10) || a * 10 > (INT_MAX - (**fmt - '0')))
|
||||
luaL_error(L, "integral size overflow");
|
||||
a = a*10 + *((*fmt)++) - '0';
|
||||
} while (isdigit(**fmt));
|
||||
return a;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#define defaultoptions(h) ((h)->endian = native.endian, (h)->align = 1)
|
||||
|
||||
|
||||
|
||||
static size_t optsize (lua_State *L, char opt, const char **fmt) {
|
||||
switch (opt) {
|
||||
case 'B': case 'b': return sizeof(char);
|
||||
case 'H': case 'h': return sizeof(short);
|
||||
case 'L': case 'l': return sizeof(long);
|
||||
case 'T': return sizeof(size_t);
|
||||
case 'f': return sizeof(float);
|
||||
case 'd': return sizeof(double);
|
||||
case 'x': return 1;
|
||||
case 'c': return getnum(L, fmt, 1);
|
||||
case 'i': case 'I': {
|
||||
int sz = getnum(L, fmt, sizeof(int));
|
||||
if (sz > MAXINTSIZE)
|
||||
luaL_error(L, "integral size %d is larger than limit of %d",
|
||||
sz, MAXINTSIZE);
|
||||
return sz;
|
||||
}
|
||||
default: return 0; /* other cases do not need alignment */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** return number of bytes needed to align an element of size 'size'
|
||||
** at current position 'len'
|
||||
*/
|
||||
static int gettoalign (size_t len, Header *h, int opt, size_t size) {
|
||||
if (size == 0 || opt == 'c') return 0;
|
||||
if (size > (size_t)h->align)
|
||||
size = h->align; /* respect max. alignment */
|
||||
return (size - (len & (size - 1))) & (size - 1);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** options to control endianess and alignment
|
||||
*/
|
||||
static void controloptions (lua_State *L, int opt, const char **fmt,
|
||||
Header *h) {
|
||||
switch (opt) {
|
||||
case ' ': return; /* ignore white spaces */
|
||||
case '>': h->endian = BIG; return;
|
||||
case '<': h->endian = LITTLE; return;
|
||||
case '!': {
|
||||
int a = getnum(L, fmt, MAXALIGN);
|
||||
if (!isp2(a))
|
||||
luaL_error(L, "alignment %d is not a power of 2", a);
|
||||
h->align = a;
|
||||
return;
|
||||
}
|
||||
default: {
|
||||
const char *msg = lua_pushfstring(L, "invalid format option '%c'", opt);
|
||||
luaL_argerror(L, 1, msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void putinteger (lua_State *L, luaL_Buffer *b, int arg, int endian,
|
||||
int size) {
|
||||
lua_Number n = luaL_checknumber(L, arg);
|
||||
Uinttype value;
|
||||
char buff[MAXINTSIZE];
|
||||
if (n < 0)
|
||||
value = (Uinttype)(Inttype)n;
|
||||
else
|
||||
value = (Uinttype)n;
|
||||
if (endian == LITTLE) {
|
||||
int i;
|
||||
for (i = 0; i < size; i++) {
|
||||
buff[i] = (value & 0xff);
|
||||
value >>= 8;
|
||||
}
|
||||
}
|
||||
else {
|
||||
int i;
|
||||
for (i = size - 1; i >= 0; i--) {
|
||||
buff[i] = (value & 0xff);
|
||||
value >>= 8;
|
||||
}
|
||||
}
|
||||
luaL_addlstring(b, buff, size);
|
||||
}
|
||||
|
||||
|
||||
static void correctbytes (char *b, int size, int endian) {
|
||||
if (endian != native.endian) {
|
||||
int i = 0;
|
||||
while (i < --size) {
|
||||
char temp = b[i];
|
||||
b[i++] = b[size];
|
||||
b[size] = temp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int b_pack (lua_State *L) {
|
||||
luaL_Buffer b;
|
||||
const char *fmt = luaL_checkstring(L, 1);
|
||||
Header h;
|
||||
int arg = 2;
|
||||
size_t totalsize = 0;
|
||||
defaultoptions(&h);
|
||||
lua_pushnil(L); /* mark to separate arguments from string buffer */
|
||||
luaL_buffinit(L, &b);
|
||||
while (*fmt != '\0') {
|
||||
int opt = *fmt++;
|
||||
size_t size = optsize(L, opt, &fmt);
|
||||
int toalign = gettoalign(totalsize, &h, opt, size);
|
||||
totalsize += toalign;
|
||||
while (toalign-- > 0) luaL_addchar(&b, '\0');
|
||||
switch (opt) {
|
||||
case 'b': case 'B': case 'h': case 'H':
|
||||
case 'l': case 'L': case 'T': case 'i': case 'I': { /* integer types */
|
||||
putinteger(L, &b, arg++, h.endian, size);
|
||||
break;
|
||||
}
|
||||
case 'x': {
|
||||
luaL_addchar(&b, '\0');
|
||||
break;
|
||||
}
|
||||
case 'f': {
|
||||
float f = (float)luaL_checknumber(L, arg++);
|
||||
correctbytes((char *)&f, size, h.endian);
|
||||
luaL_addlstring(&b, (char *)&f, size);
|
||||
break;
|
||||
}
|
||||
case 'd': {
|
||||
double d = luaL_checknumber(L, arg++);
|
||||
correctbytes((char *)&d, size, h.endian);
|
||||
luaL_addlstring(&b, (char *)&d, size);
|
||||
break;
|
||||
}
|
||||
case 'c': case 's': {
|
||||
size_t l;
|
||||
const char *s = luaL_checklstring(L, arg++, &l);
|
||||
if (size == 0) size = l;
|
||||
luaL_argcheck(L, l >= (size_t)size, arg, "string too short");
|
||||
luaL_addlstring(&b, s, size);
|
||||
if (opt == 's') {
|
||||
luaL_addchar(&b, '\0'); /* add zero at the end */
|
||||
size++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: controloptions(L, opt, &fmt, &h);
|
||||
}
|
||||
totalsize += size;
|
||||
}
|
||||
luaL_pushresult(&b);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static lua_Number getinteger (const char *buff, int endian,
|
||||
int issigned, int size) {
|
||||
Uinttype l = 0;
|
||||
int i;
|
||||
if (endian == BIG) {
|
||||
for (i = 0; i < size; i++) {
|
||||
l <<= 8;
|
||||
l |= (Uinttype)(unsigned char)buff[i];
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (i = size - 1; i >= 0; i--) {
|
||||
l <<= 8;
|
||||
l |= (Uinttype)(unsigned char)buff[i];
|
||||
}
|
||||
}
|
||||
if (!issigned)
|
||||
return (lua_Number)l;
|
||||
else { /* signed format */
|
||||
Uinttype mask = (Uinttype)(~((Uinttype)0)) << (size*8 - 1);
|
||||
if (l & mask) /* negative value? */
|
||||
l |= mask; /* signal extension */
|
||||
return (lua_Number)(Inttype)l;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int b_unpack (lua_State *L) {
|
||||
Header h;
|
||||
const char *fmt = luaL_checkstring(L, 1);
|
||||
size_t ld;
|
||||
const char *data = luaL_checklstring(L, 2, &ld);
|
||||
size_t pos = luaL_optinteger(L, 3, 1);
|
||||
luaL_argcheck(L, pos > 0, 3, "offset must be 1 or greater");
|
||||
pos--; /* Lua indexes are 1-based, but here we want 0-based for C
|
||||
* pointer math. */
|
||||
int n = 0; /* number of results */
|
||||
defaultoptions(&h);
|
||||
while (*fmt) {
|
||||
int opt = *fmt++;
|
||||
size_t size = optsize(L, opt, &fmt);
|
||||
pos += gettoalign(pos, &h, opt, size);
|
||||
luaL_argcheck(L, size <= ld && pos <= ld - size,
|
||||
2, "data string too short");
|
||||
/* stack space for item + next position */
|
||||
luaL_checkstack(L, 2, "too many results");
|
||||
switch (opt) {
|
||||
case 'b': case 'B': case 'h': case 'H':
|
||||
case 'l': case 'L': case 'T': case 'i': case 'I': { /* integer types */
|
||||
int issigned = islower(opt);
|
||||
lua_Number res = getinteger(data+pos, h.endian, issigned, size);
|
||||
lua_pushnumber(L, res); n++;
|
||||
break;
|
||||
}
|
||||
case 'x': {
|
||||
break;
|
||||
}
|
||||
case 'f': {
|
||||
float f;
|
||||
memcpy(&f, data+pos, size);
|
||||
correctbytes((char *)&f, sizeof(f), h.endian);
|
||||
lua_pushnumber(L, f); n++;
|
||||
break;
|
||||
}
|
||||
case 'd': {
|
||||
double d;
|
||||
memcpy(&d, data+pos, size);
|
||||
correctbytes((char *)&d, sizeof(d), h.endian);
|
||||
lua_pushnumber(L, d); n++;
|
||||
break;
|
||||
}
|
||||
case 'c': {
|
||||
if (size == 0) {
|
||||
if (n == 0 || !lua_isnumber(L, -1))
|
||||
luaL_error(L, "format 'c0' needs a previous size");
|
||||
size = lua_tonumber(L, -1);
|
||||
lua_pop(L, 1); n--;
|
||||
luaL_argcheck(L, size <= ld && pos <= ld - size,
|
||||
2, "data string too short");
|
||||
}
|
||||
lua_pushlstring(L, data+pos, size); n++;
|
||||
break;
|
||||
}
|
||||
case 's': {
|
||||
const char *e = (const char *)memchr(data+pos, '\0', ld - pos);
|
||||
if (e == NULL)
|
||||
luaL_error(L, "unfinished string in data");
|
||||
size = (e - (data+pos)) + 1;
|
||||
lua_pushlstring(L, data+pos, size - 1); n++;
|
||||
break;
|
||||
}
|
||||
default: controloptions(L, opt, &fmt, &h);
|
||||
}
|
||||
pos += size;
|
||||
}
|
||||
lua_pushinteger(L, pos + 1); /* next position */
|
||||
return n + 1;
|
||||
}
|
||||
|
||||
|
||||
static int b_size (lua_State *L) {
|
||||
Header h;
|
||||
const char *fmt = luaL_checkstring(L, 1);
|
||||
size_t pos = 0;
|
||||
defaultoptions(&h);
|
||||
while (*fmt) {
|
||||
int opt = *fmt++;
|
||||
size_t size = optsize(L, opt, &fmt);
|
||||
pos += gettoalign(pos, &h, opt, size);
|
||||
if (opt == 's')
|
||||
luaL_argerror(L, 1, "option 's' has no fixed size");
|
||||
else if (opt == 'c' && size == 0)
|
||||
luaL_argerror(L, 1, "option 'c0' has no fixed size");
|
||||
if (!isalnum(opt))
|
||||
controloptions(L, opt, &fmt, &h);
|
||||
pos += size;
|
||||
}
|
||||
lua_pushinteger(L, pos);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* }====================================================== */
|
||||
|
||||
|
||||
|
||||
static const struct luaL_Reg thislib[] = {
|
||||
{"pack", b_pack},
|
||||
{"unpack", b_unpack},
|
||||
{"size", b_size},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
|
||||
LUALIB_API int luaopen_struct (lua_State *L);
|
||||
|
||||
LUALIB_API int luaopen_struct (lua_State *L) {
|
||||
luaL_newlib(L, thislib);
|
||||
lua_setglobal(L, "struct");
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* Copyright (C) 2010-2018 Lua.org, PUC-Rio. All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
******************************************************************************/
|
Loading…
Add table
Add a link
Reference in a new issue