2022-03-22 02:31:03 +00:00
|
|
|
|
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
|
|
|
|
│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │
|
|
|
|
|
╞══════════════════════════════════════════════════════════════════════════════╡
|
|
|
|
|
│ Copyright 2022 Justine Alexandra Roberts Tunney │
|
|
|
|
|
│ │
|
|
|
|
|
│ Permission to use, copy, modify, and/or distribute this software for │
|
|
|
|
|
│ any purpose with or without fee is hereby granted, provided that the │
|
|
|
|
|
│ above copyright notice and this permission notice appear in all copies. │
|
|
|
|
|
│ │
|
|
|
|
|
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
|
|
|
|
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
|
|
|
|
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
|
|
|
|
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
|
|
|
|
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
|
|
|
|
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
|
|
|
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
|
|
|
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
|
|
|
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
2022-04-27 12:39:39 +00:00
|
|
|
|
#include "libc/assert.h"
|
2022-04-25 15:30:14 +00:00
|
|
|
|
#include "libc/fmt/itoa.h"
|
2023-11-28 22:24:28 +00:00
|
|
|
|
#include "libc/serialize.h"
|
2023-06-10 01:02:06 +00:00
|
|
|
|
#include "libc/log/rop.internal.h"
|
2022-03-22 02:31:03 +00:00
|
|
|
|
#include "libc/math.h"
|
2022-04-27 12:39:39 +00:00
|
|
|
|
#include "libc/mem/mem.h"
|
2023-07-26 20:54:49 +00:00
|
|
|
|
#include "libc/runtime/runtime.h"
|
2022-07-14 06:02:19 +00:00
|
|
|
|
#include "libc/runtime/stack.h"
|
2022-09-13 06:10:38 +00:00
|
|
|
|
#include "libc/stdio/append.h"
|
2022-09-07 03:07:29 +00:00
|
|
|
|
#include "libc/stdio/strlist.internal.h"
|
2022-07-23 13:47:01 +00:00
|
|
|
|
#include "libc/str/str.h"
|
2023-07-26 20:54:49 +00:00
|
|
|
|
#include "libc/sysv/consts/auxv.h"
|
2022-04-27 12:39:39 +00:00
|
|
|
|
#include "libc/x/x.h"
|
2022-07-09 23:27:26 +00:00
|
|
|
|
#include "third_party/double-conversion/wrapper.h"
|
2022-03-22 02:31:03 +00:00
|
|
|
|
#include "third_party/lua/cosmo.h"
|
|
|
|
|
#include "third_party/lua/lauxlib.h"
|
2022-04-29 13:06:23 +00:00
|
|
|
|
#include "third_party/lua/lctype.h"
|
2022-03-22 02:31:03 +00:00
|
|
|
|
#include "third_party/lua/lua.h"
|
2022-04-29 13:06:23 +00:00
|
|
|
|
#include "third_party/lua/visitor.h"
|
2022-03-22 02:31:03 +00:00
|
|
|
|
|
2022-07-13 06:31:06 +00:00
|
|
|
|
static int Serialize(lua_State *, char **, int, struct Serializer *, int);
|
|
|
|
|
|
2022-04-29 13:06:23 +00:00
|
|
|
|
static bool IsLuaIdentifier(lua_State *L, int idx) {
|
|
|
|
|
size_t i, n;
|
|
|
|
|
const char *p;
|
|
|
|
|
p = luaL_checklstring(L, idx, &n);
|
|
|
|
|
if (!lislalpha(p[0])) return false;
|
|
|
|
|
for (i = 1; i < n; ++i) {
|
|
|
|
|
if (!lislalnum(p[i])) return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2022-04-27 12:39:39 +00:00
|
|
|
|
|
2022-07-13 06:31:06 +00:00
|
|
|
|
// returns true if table at index -1 is an array
|
|
|
|
|
//
|
|
|
|
|
// for the purposes of lua serialization, we can only serialize using
|
|
|
|
|
// array ordering when a table is an array in the strictest sense. we
|
|
|
|
|
// consider a lua table an array if the following conditions are met:
|
|
|
|
|
//
|
|
|
|
|
// 1. for all 𝑘=𝑣 in table, 𝑘 is an integer ≥1
|
|
|
|
|
// 2. no holes exist between MIN(𝑘) and MAX(𝑘)
|
|
|
|
|
// 3. if non-empty, MIN(𝑘) is 1
|
|
|
|
|
//
|
|
|
|
|
// we need to do this because
|
|
|
|
|
//
|
|
|
|
|
// "the order in which the indices are enumerated is not specified,
|
|
|
|
|
// even for numeric indices" ──quoth lua 5.4 manual § next()
|
|
|
|
|
//
|
|
|
|
|
// we're able to implement this check in one pass, since lua_rawlen()
|
|
|
|
|
// reports the number of integers keys up until the first hole. so we
|
|
|
|
|
// simply need to check if any non-integers keys exist or any integer
|
|
|
|
|
// keys greater than the raw length.
|
|
|
|
|
//
|
|
|
|
|
// plesae note this is a more expensive check than the one we use for
|
|
|
|
|
// the json serializer, because lua doesn't require objects have only
|
|
|
|
|
// string keys. we want to be able to display mixed tables. it's just
|
|
|
|
|
// they won't be displayed with specified ordering, unless sorted.
|
2022-04-29 13:06:23 +00:00
|
|
|
|
static bool IsLuaArray(lua_State *L) {
|
2022-07-13 06:31:06 +00:00
|
|
|
|
lua_Integer i;
|
|
|
|
|
lua_Unsigned n;
|
|
|
|
|
n = lua_rawlen(L, -1);
|
2022-04-29 13:06:23 +00:00
|
|
|
|
lua_pushnil(L);
|
2022-07-13 06:31:06 +00:00
|
|
|
|
while (lua_next(L, -2)) {
|
|
|
|
|
if (!lua_isinteger(L, -2) || (i = lua_tointeger(L, -2)) < 1 || i > n) {
|
2022-04-29 13:06:23 +00:00
|
|
|
|
lua_pop(L, 2);
|
2022-04-27 12:39:39 +00:00
|
|
|
|
return false;
|
|
|
|
|
}
|
2022-04-29 13:06:23 +00:00
|
|
|
|
lua_pop(L, 1);
|
2022-04-27 12:39:39 +00:00
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-13 06:31:06 +00:00
|
|
|
|
static int SerializeNil(lua_State *L, char **buf) {
|
|
|
|
|
RETURN_ON_ERROR(appendw(buf, READ32LE("nil")));
|
|
|
|
|
return 0;
|
|
|
|
|
OnError:
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int SerializeBoolean(lua_State *L, char **buf, int idx) {
|
|
|
|
|
RETURN_ON_ERROR(appendw(
|
|
|
|
|
buf, lua_toboolean(L, idx) ? READ32LE("true") : READ64LE("false\0\0")));
|
|
|
|
|
return 0;
|
|
|
|
|
OnError:
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int SerializeOpaque(lua_State *L, char **buf, int idx,
|
|
|
|
|
const char *kind) {
|
|
|
|
|
RETURN_ON_ERROR(appendf(buf, "\"%s@%p\"", kind, lua_topointer(L, idx)));
|
|
|
|
|
return 0;
|
|
|
|
|
OnError:
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int SerializeNumber(lua_State *L, char **buf, int idx) {
|
|
|
|
|
int64_t x;
|
2023-09-02 03:49:13 +00:00
|
|
|
|
char ibuf[128];
|
2022-07-13 06:31:06 +00:00
|
|
|
|
if (lua_isinteger(L, idx)) {
|
|
|
|
|
x = luaL_checkinteger(L, idx);
|
|
|
|
|
if (x == -9223372036854775807 - 1) {
|
|
|
|
|
RETURN_ON_ERROR(appends(buf, "-9223372036854775807 - 1"));
|
|
|
|
|
} else {
|
|
|
|
|
RETURN_ON_ERROR(appendd(buf, ibuf, FormatFlex64(ibuf, x, 2) - ibuf));
|
|
|
|
|
}
|
2022-07-12 06:06:49 +00:00
|
|
|
|
} else {
|
2022-07-13 06:31:06 +00:00
|
|
|
|
RETURN_ON_ERROR(appends(buf, DoubleToLua(ibuf, lua_tonumber(L, idx))));
|
2022-07-12 06:06:49 +00:00
|
|
|
|
}
|
2022-07-13 06:31:06 +00:00
|
|
|
|
return 0;
|
|
|
|
|
OnError:
|
|
|
|
|
return -1;
|
2022-07-12 06:06:49 +00:00
|
|
|
|
}
|
|
|
|
|
|
2022-07-13 06:31:06 +00:00
|
|
|
|
#if 0
|
|
|
|
|
int main(int argc, char *argv[]) {
|
|
|
|
|
int i, j;
|
|
|
|
|
signed char tab[256] = {0};
|
|
|
|
|
for (i = 0; i < 256; ++i) {
|
|
|
|
|
if (i < 0x20) tab[i] = 1; // hex
|
2022-07-23 13:47:01 +00:00
|
|
|
|
if (i >= 0x7f) tab[i] = 2; // hex/utf8
|
2022-07-13 06:31:06 +00:00
|
|
|
|
}
|
|
|
|
|
tab['\e'] = 'e';
|
|
|
|
|
tab['\a'] = 'a';
|
|
|
|
|
tab['\b'] = 'b';
|
|
|
|
|
tab['\f'] = 'f';
|
|
|
|
|
tab['\n'] = 'n';
|
|
|
|
|
tab['\r'] = 'r';
|
|
|
|
|
tab['\t'] = 't';
|
|
|
|
|
tab['\v'] = 'v';
|
|
|
|
|
tab['\\'] = '\\';
|
|
|
|
|
tab['\"'] = '"';
|
|
|
|
|
tab['\v'] = 'v';
|
|
|
|
|
printf("const char kBase64[256] = {\n");
|
|
|
|
|
for (i = 0; i < 16; ++i) {
|
|
|
|
|
printf(" ");
|
|
|
|
|
for (j = 0; j < 16; ++j) {
|
|
|
|
|
if (isprint(tab[i * 16 + j])) {
|
|
|
|
|
printf("'%c',", tab[i * 16 + j]);
|
|
|
|
|
} else {
|
|
|
|
|
printf("%d,", tab[i * 16 + j]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
printf(" // 0x%02x\n", i * 16);
|
|
|
|
|
}
|
|
|
|
|
printf("};\n");
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
2022-04-27 12:39:39 +00:00
|
|
|
|
|
2022-07-13 06:31:06 +00:00
|
|
|
|
static const char kLuaStrXlat[256] = {
|
|
|
|
|
1,1,1,1,1,1,1,'a','b','t','n','v','f','r',1,1, // 0x00
|
|
|
|
|
1,1,1,1,1,1,1,1,1,1,1,'e',1,1,1,1, // 0x10
|
|
|
|
|
0,0,'"',0,0,0,0,0,0,0,0,0,0,0,0,0, // 0x20
|
|
|
|
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 0x30
|
|
|
|
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 0x40
|
|
|
|
|
0,0,0,0,0,0,0,0,0,0,0,0,'\\',0,0,0, // 0x50
|
|
|
|
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 0x60
|
|
|
|
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, // 0x70
|
2022-07-23 13:47:01 +00:00
|
|
|
|
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, // 0x80
|
|
|
|
|
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, // 0x90
|
|
|
|
|
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, // 0xa0
|
|
|
|
|
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, // 0xb0
|
|
|
|
|
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, // 0xc0
|
|
|
|
|
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, // 0xd0
|
|
|
|
|
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, // 0xe0
|
|
|
|
|
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, // 0xf0
|
2022-07-13 06:31:06 +00:00
|
|
|
|
};
|
|
|
|
|
// clang-format on
|
2022-04-27 12:39:39 +00:00
|
|
|
|
|
2022-07-13 06:31:06 +00:00
|
|
|
|
static int SerializeString(lua_State *L, char **buf, int idx) {
|
2022-07-23 13:47:01 +00:00
|
|
|
|
int c, x;
|
|
|
|
|
bool utf8;
|
2022-07-13 06:31:06 +00:00
|
|
|
|
size_t i, n;
|
|
|
|
|
const char *s;
|
|
|
|
|
s = lua_tolstring(L, idx, &n);
|
2023-08-21 09:28:24 +00:00
|
|
|
|
utf8 = isutf8(s, n);
|
2022-07-13 06:31:06 +00:00
|
|
|
|
RETURN_ON_ERROR(appendw(buf, '"'));
|
|
|
|
|
for (i = 0; i < n; i++) {
|
2022-07-23 13:47:01 +00:00
|
|
|
|
switch ((x = kLuaStrXlat[(c = s[i] & 255)])) {
|
2022-07-13 06:31:06 +00:00
|
|
|
|
case 0:
|
2022-07-23 13:47:01 +00:00
|
|
|
|
EmitByte:
|
|
|
|
|
RETURN_ON_ERROR(appendw(buf, c));
|
2022-07-13 06:31:06 +00:00
|
|
|
|
break;
|
2022-07-23 13:47:01 +00:00
|
|
|
|
case 2:
|
|
|
|
|
if (utf8) goto EmitByte;
|
|
|
|
|
// fallthrough
|
2022-07-13 06:31:06 +00:00
|
|
|
|
case 1:
|
|
|
|
|
RETURN_ON_ERROR(
|
|
|
|
|
appendw(buf, '\\' | 'x' << 010 |
|
2022-07-23 13:47:01 +00:00
|
|
|
|
"0123456789abcdef"[(c & 0xF0) >> 4] << 020 |
|
|
|
|
|
"0123456789abcdef"[(c & 0x0F) >> 0] << 030));
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
RETURN_ON_ERROR(appendw(buf, READ32LE("\\\x00\x00") | (x << 8)));
|
2022-07-13 06:31:06 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
RETURN_ON_ERROR(appendw(buf, '"'));
|
|
|
|
|
return 0;
|
|
|
|
|
OnError:
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
2022-04-27 12:39:39 +00:00
|
|
|
|
|
2022-07-13 06:31:06 +00:00
|
|
|
|
static int SerializeUserData(lua_State *L, char **buf, int idx) {
|
|
|
|
|
size_t n;
|
|
|
|
|
const char *s;
|
|
|
|
|
if (luaL_callmeta(L, idx, "__repr")) {
|
|
|
|
|
if (lua_type(L, -1) == LUA_TSTRING) {
|
|
|
|
|
s = lua_tolstring(L, -1, &n);
|
|
|
|
|
RETURN_ON_ERROR(appendd(buf, s, n));
|
|
|
|
|
} else {
|
|
|
|
|
RETURN_ON_ERROR(appendf(buf, "[[error %s returned a %s value]]", "__repr",
|
|
|
|
|
luaL_typename(L, -1)));
|
|
|
|
|
}
|
|
|
|
|
lua_pop(L, 1);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
if (luaL_callmeta(L, idx, "__tostring")) {
|
|
|
|
|
if (lua_type(L, -1) == LUA_TSTRING) {
|
|
|
|
|
RETURN_ON_ERROR(SerializeString(L, buf, -1));
|
|
|
|
|
} else {
|
|
|
|
|
RETURN_ON_ERROR(appendf(buf, "[[error %s returned a %s value]]",
|
|
|
|
|
"__tostring", luaL_typename(L, -1)));
|
|
|
|
|
}
|
|
|
|
|
lua_pop(L, 1);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
return SerializeOpaque(L, buf, idx, "udata");
|
|
|
|
|
OnError:
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
2022-04-27 12:39:39 +00:00
|
|
|
|
|
2022-07-13 06:31:06 +00:00
|
|
|
|
static int SerializeArray(lua_State *L, char **buf, struct Serializer *z,
|
|
|
|
|
int depth) {
|
|
|
|
|
size_t i, n;
|
|
|
|
|
RETURN_ON_ERROR(appendw(buf, '{'));
|
|
|
|
|
n = lua_rawlen(L, -1);
|
|
|
|
|
for (i = 1; i <= n; i++) {
|
|
|
|
|
lua_rawgeti(L, -1, i);
|
|
|
|
|
if (i > 1) RETURN_ON_ERROR(appendw(buf, READ16LE(", ")));
|
2022-07-22 17:10:33 +00:00
|
|
|
|
RETURN_ON_ERROR(Serialize(L, buf, -1, z, depth + 1));
|
2022-07-13 06:31:06 +00:00
|
|
|
|
lua_pop(L, 1);
|
|
|
|
|
}
|
|
|
|
|
RETURN_ON_ERROR(appendw(buf, '}'));
|
|
|
|
|
return 0;
|
|
|
|
|
OnError:
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
2022-04-27 12:39:39 +00:00
|
|
|
|
|
2022-07-13 06:31:06 +00:00
|
|
|
|
static int SerializeObject(lua_State *L, char **buf, struct Serializer *z,
|
2022-07-22 17:10:33 +00:00
|
|
|
|
int depth, bool multi) {
|
2022-07-13 06:31:06 +00:00
|
|
|
|
size_t n;
|
|
|
|
|
const char *s;
|
|
|
|
|
bool comma = false;
|
2022-07-22 17:10:33 +00:00
|
|
|
|
RETURN_ON_ERROR(SerializeObjectStart(buf, z, depth, multi));
|
2022-07-13 06:31:06 +00:00
|
|
|
|
lua_pushnil(L);
|
|
|
|
|
while (lua_next(L, -2)) {
|
|
|
|
|
if (comma) {
|
2022-07-22 17:10:33 +00:00
|
|
|
|
if (multi) {
|
|
|
|
|
RETURN_ON_ERROR(appendw(buf, ','));
|
|
|
|
|
RETURN_ON_ERROR(SerializeObjectIndent(buf, z, depth + 1));
|
|
|
|
|
} else {
|
|
|
|
|
RETURN_ON_ERROR(appendw(buf, READ16LE(", ")));
|
|
|
|
|
}
|
2022-07-13 06:31:06 +00:00
|
|
|
|
} else {
|
|
|
|
|
comma = true;
|
|
|
|
|
}
|
|
|
|
|
if (lua_type(L, -2) == LUA_TSTRING && IsLuaIdentifier(L, -2)) {
|
|
|
|
|
// use {𝑘=𝑣′} syntax when 𝑘 is a legal lua identifier
|
|
|
|
|
s = lua_tolstring(L, -2, &n);
|
|
|
|
|
RETURN_ON_ERROR(appendd(buf, s, n));
|
|
|
|
|
RETURN_ON_ERROR(appendw(buf, '='));
|
|
|
|
|
} else {
|
|
|
|
|
// use {[𝑘′]=𝑣′} otherwise
|
|
|
|
|
RETURN_ON_ERROR(appendw(buf, '['));
|
2022-07-22 17:10:33 +00:00
|
|
|
|
RETURN_ON_ERROR(Serialize(L, buf, -2, z, depth + 1));
|
2022-07-13 06:31:06 +00:00
|
|
|
|
RETURN_ON_ERROR(appendw(buf, READ16LE("]=")));
|
|
|
|
|
}
|
2022-07-22 17:10:33 +00:00
|
|
|
|
RETURN_ON_ERROR(Serialize(L, buf, -1, z, depth + 1));
|
2022-07-13 06:31:06 +00:00
|
|
|
|
lua_pop(L, 1);
|
|
|
|
|
}
|
2022-07-22 17:10:33 +00:00
|
|
|
|
RETURN_ON_ERROR(SerializeObjectEnd(buf, z, depth, multi));
|
2022-07-13 06:31:06 +00:00
|
|
|
|
return 0;
|
|
|
|
|
OnError:
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
2022-04-27 12:39:39 +00:00
|
|
|
|
|
2022-07-13 06:31:06 +00:00
|
|
|
|
static int SerializeSorted(lua_State *L, char **buf, struct Serializer *z,
|
2022-07-22 17:10:33 +00:00
|
|
|
|
int depth, bool multi) {
|
2023-09-02 03:49:13 +00:00
|
|
|
|
int i;
|
2022-07-13 06:31:06 +00:00
|
|
|
|
size_t n;
|
|
|
|
|
const char *s;
|
2022-09-07 03:07:29 +00:00
|
|
|
|
struct StrList sl = {0};
|
2022-07-13 06:31:06 +00:00
|
|
|
|
lua_pushnil(L);
|
|
|
|
|
while (lua_next(L, -2)) {
|
2022-09-07 03:07:29 +00:00
|
|
|
|
RETURN_ON_ERROR(i = AppendStrList(&sl));
|
2022-07-13 06:31:06 +00:00
|
|
|
|
if (lua_type(L, -2) == LUA_TSTRING && IsLuaIdentifier(L, -2)) {
|
|
|
|
|
// use {𝑘=𝑣′} syntax when 𝑘 is a legal lua identifier
|
|
|
|
|
s = lua_tolstring(L, -2, &n);
|
2022-09-07 03:07:29 +00:00
|
|
|
|
RETURN_ON_ERROR(appendd(sl.p + i, s, n));
|
|
|
|
|
RETURN_ON_ERROR(appendw(sl.p + i, '='));
|
2022-07-13 06:31:06 +00:00
|
|
|
|
} else {
|
|
|
|
|
// use {[𝑘′]=𝑣′} otherwise
|
2022-09-07 03:07:29 +00:00
|
|
|
|
RETURN_ON_ERROR(appendw(sl.p + i, '['));
|
|
|
|
|
RETURN_ON_ERROR(Serialize(L, sl.p + i, -2, z, depth + 1));
|
|
|
|
|
RETURN_ON_ERROR(appendw(sl.p + i, ']' | '=' << 010));
|
2022-07-13 06:31:06 +00:00
|
|
|
|
}
|
2022-09-07 03:07:29 +00:00
|
|
|
|
RETURN_ON_ERROR(Serialize(L, sl.p + i, -1, z, depth + 1));
|
2022-07-13 06:31:06 +00:00
|
|
|
|
lua_pop(L, 1);
|
|
|
|
|
}
|
2022-09-07 03:07:29 +00:00
|
|
|
|
SortStrList(&sl);
|
2022-07-22 17:10:33 +00:00
|
|
|
|
RETURN_ON_ERROR(SerializeObjectStart(buf, z, depth, multi));
|
2022-09-07 03:07:29 +00:00
|
|
|
|
for (i = 0; i < sl.i; ++i) {
|
|
|
|
|
if (i) {
|
|
|
|
|
if (multi) {
|
|
|
|
|
RETURN_ON_ERROR(appendw(buf, ','));
|
|
|
|
|
RETURN_ON_ERROR(SerializeObjectIndent(buf, z, depth + 1));
|
|
|
|
|
} else {
|
|
|
|
|
RETURN_ON_ERROR(appendw(buf, READ16LE(", ")));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
RETURN_ON_ERROR(appends(buf, sl.p[i]));
|
|
|
|
|
}
|
2022-07-22 17:10:33 +00:00
|
|
|
|
RETURN_ON_ERROR(SerializeObjectEnd(buf, z, depth, multi));
|
2022-09-07 03:07:29 +00:00
|
|
|
|
FreeStrList(&sl);
|
2022-07-13 06:31:06 +00:00
|
|
|
|
return 0;
|
|
|
|
|
OnError:
|
2022-09-07 03:07:29 +00:00
|
|
|
|
FreeStrList(&sl);
|
2022-07-13 06:31:06 +00:00
|
|
|
|
return -1;
|
|
|
|
|
}
|
2022-04-27 12:39:39 +00:00
|
|
|
|
|
2022-07-13 06:31:06 +00:00
|
|
|
|
static int SerializeTable(lua_State *L, char **buf, int idx,
|
|
|
|
|
struct Serializer *z, int depth) {
|
|
|
|
|
int rc;
|
2022-07-22 17:10:33 +00:00
|
|
|
|
bool multi;
|
2024-06-21 03:46:42 +00:00
|
|
|
|
if (UNLIKELY(GetStackPointer() < z->bsp)) {
|
2022-07-14 06:02:19 +00:00
|
|
|
|
z->reason = "out of stack";
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
2022-07-13 06:31:06 +00:00
|
|
|
|
RETURN_ON_ERROR(rc = LuaPushVisit(&z->visited, lua_topointer(L, idx)));
|
|
|
|
|
if (rc) return SerializeOpaque(L, buf, idx, "cyclic");
|
|
|
|
|
lua_pushvalue(L, idx); // idx becomes invalid once we change stack
|
|
|
|
|
if (IsLuaArray(L)) {
|
|
|
|
|
RETURN_ON_ERROR(SerializeArray(L, buf, z, depth));
|
|
|
|
|
} else {
|
2022-07-22 17:10:33 +00:00
|
|
|
|
multi = z->conf.pretty && LuaHasMultipleItems(L);
|
|
|
|
|
if (z->conf.sorted) {
|
|
|
|
|
RETURN_ON_ERROR(SerializeSorted(L, buf, z, depth, multi));
|
|
|
|
|
} else {
|
|
|
|
|
RETURN_ON_ERROR(SerializeObject(L, buf, z, depth, multi));
|
|
|
|
|
}
|
2022-07-13 06:31:06 +00:00
|
|
|
|
}
|
|
|
|
|
LuaPopVisit(&z->visited);
|
|
|
|
|
lua_pop(L, 1); // table ref
|
|
|
|
|
return 0;
|
|
|
|
|
OnError:
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
2022-04-27 12:39:39 +00:00
|
|
|
|
|
2022-07-13 06:31:06 +00:00
|
|
|
|
static int Serialize(lua_State *L, char **buf, int idx, struct Serializer *z,
|
|
|
|
|
int depth) {
|
2022-07-22 17:10:33 +00:00
|
|
|
|
if (depth < z->conf.maxdepth) {
|
2022-07-13 06:31:06 +00:00
|
|
|
|
switch (lua_type(L, idx)) {
|
|
|
|
|
case LUA_TNIL:
|
|
|
|
|
return SerializeNil(L, buf);
|
2022-04-25 15:30:14 +00:00
|
|
|
|
case LUA_TBOOLEAN:
|
2022-07-13 06:31:06 +00:00
|
|
|
|
return SerializeBoolean(L, buf, idx);
|
|
|
|
|
case LUA_TNUMBER:
|
|
|
|
|
return SerializeNumber(L, buf, idx);
|
|
|
|
|
case LUA_TSTRING:
|
|
|
|
|
return SerializeString(L, buf, idx);
|
2022-04-25 15:30:14 +00:00
|
|
|
|
case LUA_TTABLE:
|
2022-07-13 06:31:06 +00:00
|
|
|
|
return SerializeTable(L, buf, idx, z, depth);
|
|
|
|
|
case LUA_TUSERDATA:
|
|
|
|
|
return SerializeUserData(L, buf, idx);
|
|
|
|
|
case LUA_TFUNCTION:
|
|
|
|
|
return SerializeOpaque(L, buf, idx, "func");
|
|
|
|
|
case LUA_TLIGHTUSERDATA:
|
|
|
|
|
return SerializeOpaque(L, buf, idx, "light");
|
|
|
|
|
case LUA_TTHREAD:
|
|
|
|
|
return SerializeOpaque(L, buf, idx, "thread");
|
2022-04-25 15:30:14 +00:00
|
|
|
|
default:
|
2022-07-13 06:31:06 +00:00
|
|
|
|
return SerializeOpaque(L, buf, idx, "unsupported");
|
2022-03-22 02:31:03 +00:00
|
|
|
|
}
|
|
|
|
|
} else {
|
2022-07-13 06:31:06 +00:00
|
|
|
|
return SerializeOpaque(L, buf, idx, "greatdepth");
|
2022-03-22 02:31:03 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2022-04-27 12:39:39 +00:00
|
|
|
|
|
2022-07-09 11:09:51 +00:00
|
|
|
|
/**
|
|
|
|
|
* Encodes Lua data structure as Lua code string.
|
|
|
|
|
*
|
2022-07-12 06:06:49 +00:00
|
|
|
|
* This serializer is intended primarily for describing the data
|
|
|
|
|
* structure. For example, it's used by the REPL where we need to be
|
|
|
|
|
* able to ignore errors when displaying data structures, since showing
|
|
|
|
|
* most things imperfectly is better than crashing. Therefore this isn't
|
|
|
|
|
* the kind of serializer you'd want to use to persist data in prod. Try
|
|
|
|
|
* using the JSON serializer for that purpose.
|
|
|
|
|
*
|
2022-07-09 11:09:51 +00:00
|
|
|
|
* @param L is Lua interpreter state
|
|
|
|
|
* @param buf receives encoded output string
|
|
|
|
|
* @param idx is index of item on Lua stack
|
2022-07-13 06:31:06 +00:00
|
|
|
|
* @param sorted is ignored (always sorted)
|
2022-07-09 11:09:51 +00:00
|
|
|
|
* @return 0 on success, or -1 on error
|
|
|
|
|
*/
|
2022-07-22 17:10:33 +00:00
|
|
|
|
int LuaEncodeLuaData(lua_State *L, char **buf, int idx,
|
|
|
|
|
struct EncoderConfig conf) {
|
|
|
|
|
int rc;
|
2024-06-21 03:46:42 +00:00
|
|
|
|
struct Serializer z = {
|
|
|
|
|
.reason = "out of memory",
|
|
|
|
|
.bsp = GetStackBottom() + 4096,
|
|
|
|
|
.conf = conf,
|
|
|
|
|
};
|
2022-07-22 17:10:33 +00:00
|
|
|
|
if (lua_checkstack(L, conf.maxdepth * 3 + LUA_MINSTACK)) {
|
|
|
|
|
rc = Serialize(L, buf, idx, &z, 0);
|
2022-07-13 06:31:06 +00:00
|
|
|
|
free(z.visited.p);
|
|
|
|
|
if (rc == -1) {
|
|
|
|
|
lua_pushnil(L);
|
|
|
|
|
lua_pushstring(L, z.reason);
|
|
|
|
|
}
|
|
|
|
|
return rc;
|
|
|
|
|
} else {
|
|
|
|
|
luaL_error(L, "can't set stack depth");
|
2023-06-09 06:44:03 +00:00
|
|
|
|
__builtin_unreachable();
|
2022-07-12 06:06:49 +00:00
|
|
|
|
}
|
2022-04-27 12:39:39 +00:00
|
|
|
|
}
|