Add pretty printing to redbean serializers

This commit is contained in:
Justine Tunney 2022-07-22 10:10:33 -07:00
parent 6bb8b26d70
commit 516b68606f
11 changed files with 327 additions and 91 deletions

View file

@ -45,6 +45,36 @@ assert(EncodeJson("\"") == [["\""]])
assert(EncodeJson("\'") == [["\'"]])
assert(EncodeJson("\\") == [["\\"]])
assert(EncodeJson(
{yo=2,
bye={yo=2,
dawg=3},
there={yo=2},
sup={yo=2},
hi="hello"},
{pretty=true}) ==
"{\n"..
" \"bye\": {\n"..
" \"dawg\": 3,\n"..
" \"yo\": 2\n"..
" },\n"..
" \"hi\": \"hello\",\n"..
" \"sup\": {\"yo\": 2},\n"..
" \"there\": {\"yo\": 2},\n"..
" \"yo\": 2\n"..
"}")
assert(EncodeJson(
{yo=2, bye=1, there=10, sup=3, hi="hello"},
{pretty=true, indent=" "}) ==
"{\n"..
" \"bye\": 1,\n"..
" \"hi\": \"hello\",\n"..
" \"sup\": 3,\n"..
" \"there\": 10,\n"..
" \"yo\": 2\n"..
"}")
val, err = EncodeJson({[3]=3, [2]=3})
assert(val == nil)
assert(err == 'json objects must only use string keys')
@ -69,7 +99,12 @@ x.a = 'a'
x.b = 'b'
assert(EncodeJson(x) == '{"a":"a","b":"b","c":"c"}')
assert(EncodeJson({{{{{{{{{{{},{{{{},{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}) ==
assert(EncodeJson(0, {maxdepth=1}))
val, err = EncodeJson(0, {maxdepth=0})
assert(val == nil)
assert(err == 'table has great depth')
assert(EncodeJson({{{{{{{{{{{},{{{{},{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}, {maxdepth=64}) ==
'[[[[[[[[[[{},[[[{},[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[{}]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]')
val, err = EncodeJson({{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}})
assert(val == nil)
@ -82,7 +117,7 @@ assert(EncodeJson(
{k={k={k={k={k={k={k={k={k={k={k={k={k={k={k=
{k={k={k={k={k={k={k={k={k={k={k={k={k={k={k=
{k={k={k={k=0}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}) ==
}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}, {maxdepth=64}) ==
"{\"k\":{\"k\":{\"k\":{\"k\":{\"k\":{\"k\":{\"k\":{\"k\":{\"k\":{\"k\":{\"k\":{\"k\":{\"k\":{\"k\":{\"k\":{\"k\":"..
"{\"k\":{\"k\":{\"k\":{\"k\":{\"k\":{\"k\":{\"k\":{\"k\":{\"k\":{\"k\":{\"k\":{\"k\":{\"k\":{\"k\":{\"k\":{\"k\":"..
"{\"k\":{\"k\":{\"k\":{\"k\":{\"k\":{\"k\":{\"k\":{\"k\":{\"k\":{\"k\":{\"k\":{\"k\":{\"k\":{\"k\":{\"k\":{\"k\":"..
@ -97,7 +132,7 @@ res, err = EncodeJson(
{k={k={k={k={k={k={k={k={k={k={k={k={k={k={k=
{k={k={k={k={k={k={k={k={k={k={k={k={k={k={k=
{k={k={k={k=0}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}})
}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}, {maxdepth=64})
assert(res == nil)
assert(err == "table has great depth")

View file

@ -53,6 +53,36 @@ assert(EncodeLua("\e") == [["\e"]])
assert(EncodeLua("\"") == [["\""]])
assert(EncodeLua("\\") == [["\\"]])
assert(EncodeLua(
{yo=2,
bye={yo=2,
dawg=3},
there={yo=2},
sup={yo=2},
hi="hello"},
{pretty=true}) ==
"{\n"..
" bye={\n"..
" dawg=3,\n"..
" yo=2\n"..
" },\n"..
" hi=\"hello\",\n"..
" sup={yo=2},\n"..
" there={yo=2},\n"..
" yo=2\n"..
"}")
assert(EncodeLua(
{yo=2, bye=1, there=10, sup=3, hi="hello"},
{pretty=true, indent=" "}) ==
"{\n"..
" bye=1,\n"..
" hi=\"hello\",\n"..
" sup=3,\n"..
" there=10,\n"..
" yo=2\n"..
"}")
x = {}
x.c = 'c'
x.a = 'a'
@ -70,7 +100,7 @@ assert(EncodeLua(
{k={k={k={k={k={k={k={k={k={k={k={k={k={k={k=
{k={k={k={k={k={k={k={k={k={k={k={k={k={k={k=
{k={k={k={k=0}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}) ==
}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}, {maxdepth=64}) ==
"{k={k={k={k={k={k={k={k={k={k={k={k={k={k={k={k="..
"{k={k={k={k={k={k={k={k={k={k={k={k={k={k={k={k="..
"{k={k={k={k={k={k={k={k={k={k={k={k={k={k={k={k="..
@ -85,7 +115,7 @@ assert(EncodeLua(
{k={k={k={k={k={k={k={k={k={k={k={k={k={k={k=
{k={k={k={k={k={k={k={k={k={k={k={k={k={k={k=
{k={k={k={k=0}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}, {maxdepth=64}
) ==
"{k={k={k={k={k={k={k={k={k={k={k={k={k={k={k={k="..
"{k={k={k={k={k={k={k={k={k={k={k={k={k={k={k={k="..

View file

@ -154,6 +154,8 @@ $(THIRD_PARTY_LIBCXX_A_OBJS): \
-ffunction-sections \
-fdata-sections
o/$(MODE)/third_party/libcxx/locale.o: QUOTA = -C32 -M1024m
THIRD_PARTY_LIBCXX_LIBS = $(foreach x,$(THIRD_PARTY_LIBCXX_ARTIFACTS),$($(x)))
THIRD_PARTY_LIBCXX_SRCS = $(foreach x,$(THIRD_PARTY_LIBCXX_ARTIFACTS),$($(x)_SRCS))
THIRD_PARTY_LIBCXX_HDRS = $(foreach x,$(THIRD_PARTY_LIBCXX_ARTIFACTS),$($(x)_HDRS))

View file

@ -3,13 +3,39 @@
#include "net/http/http.h"
#include "net/http/url.h"
#include "third_party/lua/lauxlib.h"
#include "third_party/lua/visitor.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
struct EncoderConfig {
short maxdepth;
bool sorted;
bool pretty;
const char *indent;
};
struct Serializer {
struct LuaVisited visited;
struct EncoderConfig conf;
const char *reason;
char *strbuf;
size_t strbuflen;
};
struct SerializerJoin {
struct Serializer *z;
char **buf;
bool multi;
int depth;
int i;
const char *indent;
};
bool LuaHasMultipleItems(lua_State *);
char *LuaFormatStack(lua_State *) dontdiscard;
int LuaCallWithTrace(lua_State *, int, int, lua_State *);
int LuaEncodeJsonData(lua_State *, char **, int, bool);
int LuaEncodeLuaData(lua_State *, char **, int, bool);
int LuaEncodeJsonData(lua_State *, char **, int, struct EncoderConfig);
int LuaEncodeLuaData(lua_State *, char **, int, struct EncoderConfig);
int LuaEncodeUrl(lua_State *);
int LuaParseUrl(lua_State *);
int LuaPushHeader(lua_State *, struct HttpMessage *, char *, int);
@ -17,6 +43,9 @@ int LuaPushHeaders(lua_State *, struct HttpMessage *, const char *);
void LuaPrintStack(lua_State *);
void LuaPushLatin1(lua_State *, const char *, size_t);
void LuaPushUrlParams(lua_State *, struct UrlParams *);
int SerializeObjectStart(char **, struct Serializer *, int, bool);
int SerializeObjectEnd(char **, struct Serializer *, int, bool);
int SerializeObjectIndent(char **, struct Serializer *, int);
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */

View file

@ -109,6 +109,7 @@ THIRD_PARTY_LUA_A_SRCS = \
third_party/lua/lutf8lib.c \
third_party/lua/lvm.c \
third_party/lua/lzio.c \
third_party/lua/serialize.c \
third_party/lua/visitor.c
THIRD_PARTY_LUA_A_OBJS = \

View file

@ -21,7 +21,6 @@
#include "libc/bits/bits.h"
#include "libc/bits/likely.h"
#include "libc/fmt/itoa.h"
#include "libc/intrin/kprintf.h"
#include "libc/log/log.h"
#include "libc/log/rop.h"
#include "libc/mem/mem.h"
@ -36,19 +35,6 @@
#include "third_party/lua/lua.h"
#include "third_party/lua/visitor.h"
struct Serializer {
struct LuaVisited visited;
const char *reason;
char *strbuf;
size_t strbuflen;
bool sorted;
};
struct Joiner {
char **buf;
int i;
};
static int Serialize(lua_State *, char **, int, struct Serializer *, int);
static int SerializeNull(lua_State *L, char **buf) {
@ -96,13 +82,13 @@ OnError:
}
static int SerializeArray(lua_State *L, char **buf, struct Serializer *z,
int level, size_t tbllen) {
int depth, size_t tbllen) {
size_t i;
RETURN_ON_ERROR(appendw(buf, '['));
for (i = 1; i <= tbllen; i++) {
lua_rawgeti(L, -1, i); // +2
if (i > 1) RETURN_ON_ERROR(appendw(buf, ','));
RETURN_ON_ERROR(Serialize(L, buf, -1, z, level - 1));
RETURN_ON_ERROR(Serialize(L, buf, -1, z, depth + 1));
lua_pop(L, 1);
}
RETURN_ON_ERROR(appendw(buf, ']'));
@ -112,38 +98,44 @@ OnError:
}
static int SerializeObject(lua_State *L, char **buf, struct Serializer *z,
int level) {
int depth, bool multi) {
bool comma = false;
RETURN_ON_ERROR(appendw(buf, '{'));
RETURN_ON_ERROR(SerializeObjectStart(buf, z, depth, multi));
lua_pushnil(L); // +2
while (lua_next(L, -2)) { // +3
if (lua_type(L, -2) == LUA_TSTRING) {
if (comma) {
RETURN_ON_ERROR(appendw(buf, ','));
if (multi) {
RETURN_ON_ERROR(SerializeObjectIndent(buf, z, depth + 1));
}
} else {
comma = true;
}
RETURN_ON_ERROR(SerializeString(L, buf, -2, z));
RETURN_ON_ERROR(appendw(buf, ':'));
RETURN_ON_ERROR(Serialize(L, buf, -1, z, level - 1));
RETURN_ON_ERROR(appendw(buf, z->conf.pretty ? READ16LE(": ") : ':'));
RETURN_ON_ERROR(Serialize(L, buf, -1, z, depth + 1));
lua_pop(L, 1);
} else {
z->reason = "json objects must only use string keys";
goto OnError;
}
}
RETURN_ON_ERROR(appendw(buf, '}'));
RETURN_ON_ERROR(SerializeObjectEnd(buf, z, depth, multi));
return 0;
OnError:
return -1;
}
static intptr_t Join(const char *elem, void *arg) {
struct Joiner *j = arg;
struct SerializerJoin *j = arg;
if (!j->i) {
++j->i;
} else {
RETURN_ON_ERROR(appendw(j->buf, ','));
if (j->multi) {
RETURN_ON_ERROR(SerializeObjectIndent(j->buf, j->z, j->depth + 1));
}
}
RETURN_ON_ERROR(appends(j->buf, elem));
return 0;
@ -152,18 +144,17 @@ OnError:
}
static int SerializeSorted(lua_State *L, char **buf, struct Serializer *z,
int level) {
int depth, bool multi) {
int i;
char *b = 0;
struct Joiner j = {buf};
struct critbit0 t = {0};
lua_pushnil(L);
while (lua_next(L, -2)) {
if (lua_type(L, -2) == LUA_TSTRING) {
RETURN_ON_ERROR(appendr(&b, 0));
RETURN_ON_ERROR(SerializeString(L, &b, -2, z));
RETURN_ON_ERROR(appendw(&b, ':'));
RETURN_ON_ERROR(Serialize(L, &b, -1, z, level - 1));
RETURN_ON_ERROR(appendw(&b, z->conf.pretty ? READ16LE(": ") : ':'));
RETURN_ON_ERROR(Serialize(L, &b, -1, z, depth + 1));
RETURN_ON_ERROR(critbit0_insert(&t, b));
lua_pop(L, 1);
} else {
@ -171,9 +162,15 @@ static int SerializeSorted(lua_State *L, char **buf, struct Serializer *z,
goto OnError;
}
}
RETURN_ON_ERROR(appendw(buf, '{'));
struct SerializerJoin j = {
.z = z,
.buf = buf,
.multi = multi,
.depth = depth,
};
RETURN_ON_ERROR(SerializeObjectStart(buf, z, depth, multi));
RETURN_ON_ERROR(critbit0_allprefixed(&t, "", Join, &j));
RETURN_ON_ERROR(appendw(buf, '}'));
RETURN_ON_ERROR(SerializeObjectEnd(buf, z, depth, multi));
critbit0_clear(&t);
free(b);
return 0;
@ -184,8 +181,9 @@ OnError:
}
static int SerializeTable(lua_State *L, char **buf, int idx,
struct Serializer *z, int level) {
struct Serializer *z, int depth) {
int rc;
bool multi;
bool isarray;
lua_Unsigned n;
if (UNLIKELY(!HaveStackMemory(PAGESIZE))) {
@ -206,11 +204,14 @@ static int SerializeTable(lua_State *L, char **buf, int idx,
lua_pop(L, 1);
}
if (isarray) {
RETURN_ON_ERROR(SerializeArray(L, buf, z, level, n));
} else if (z->sorted) {
RETURN_ON_ERROR(SerializeSorted(L, buf, z, level));
RETURN_ON_ERROR(SerializeArray(L, buf, z, depth, n));
} else {
RETURN_ON_ERROR(SerializeObject(L, buf, z, level));
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));
}
}
LuaPopVisit(&z->visited);
lua_pop(L, 1); // table ref
@ -224,8 +225,8 @@ OnError:
}
static int Serialize(lua_State *L, char **buf, int idx, struct Serializer *z,
int level) {
if (level > 0) {
int depth) {
if (depth < z->conf.maxdepth) {
switch (lua_type(L, idx)) {
case LUA_TNIL:
return SerializeNull(L, buf);
@ -236,7 +237,7 @@ static int Serialize(lua_State *L, char **buf, int idx, struct Serializer *z,
case LUA_TNUMBER:
return SerializeNumber(L, buf, idx);
case LUA_TTABLE:
return SerializeTable(L, buf, idx, z, level);
return SerializeTable(L, buf, idx, z, depth);
default:
z->reason = "unsupported lua type";
return -1;
@ -275,11 +276,12 @@ static int Serialize(lua_State *L, char **buf, int idx, struct Serializer *z,
* @param idx is index of item on Lua stack
* @return 0 on success, or -1 on error
*/
int LuaEncodeJsonData(lua_State *L, char **buf, int idx, bool sorted) {
int rc, depth = 64;
struct Serializer z = {.reason = "out of memory", .sorted = sorted};
if (lua_checkstack(L, depth * 3 + LUA_MINSTACK)) {
rc = Serialize(L, buf, idx, &z, depth);
int LuaEncodeJsonData(lua_State *L, char **buf, int idx,
struct EncoderConfig conf) {
int rc;
struct Serializer z = {.reason = "out of memory", .conf = conf};
if (lua_checkstack(L, conf.maxdepth * 3 + LUA_MINSTACK)) {
rc = Serialize(L, buf, idx, &z, 0);
free(z.visited.p);
free(z.strbuf);
if (rc == -1) {

View file

@ -33,17 +33,6 @@
#include "third_party/lua/lua.h"
#include "third_party/lua/visitor.h"
struct Serializer {
struct LuaVisited visited;
const char *reason;
bool sorted;
};
struct Joiner {
char **buf;
int i;
};
static int Serialize(lua_State *, char **, int, struct Serializer *, int);
static bool IsLuaIdentifier(lua_State *L, int idx) {
@ -260,7 +249,7 @@ static int SerializeArray(lua_State *L, char **buf, struct Serializer *z,
for (i = 1; i <= n; i++) {
lua_rawgeti(L, -1, i);
if (i > 1) RETURN_ON_ERROR(appendw(buf, READ16LE(", ")));
RETURN_ON_ERROR(Serialize(L, buf, -1, z, depth - 1));
RETURN_ON_ERROR(Serialize(L, buf, -1, z, depth + 1));
lua_pop(L, 1);
}
RETURN_ON_ERROR(appendw(buf, '}'));
@ -270,16 +259,21 @@ OnError:
}
static int SerializeObject(lua_State *L, char **buf, struct Serializer *z,
int depth) {
int depth, bool multi) {
int rc;
size_t n;
const char *s;
bool comma = false;
RETURN_ON_ERROR(appendw(buf, '{'));
RETURN_ON_ERROR(SerializeObjectStart(buf, z, depth, multi));
lua_pushnil(L);
while (lua_next(L, -2)) {
if (comma) {
RETURN_ON_ERROR(appendw(buf, READ16LE(", ")));
if (multi) {
RETURN_ON_ERROR(appendw(buf, ','));
RETURN_ON_ERROR(SerializeObjectIndent(buf, z, depth + 1));
} else {
RETURN_ON_ERROR(appendw(buf, READ16LE(", ")));
}
} else {
comma = true;
}
@ -291,24 +285,29 @@ static int SerializeObject(lua_State *L, char **buf, struct Serializer *z,
} else {
// use {[𝑘]=𝑣} otherwise
RETURN_ON_ERROR(appendw(buf, '['));
RETURN_ON_ERROR(Serialize(L, buf, -2, z, depth - 1));
RETURN_ON_ERROR(Serialize(L, buf, -2, z, depth + 1));
RETURN_ON_ERROR(appendw(buf, READ16LE("]=")));
}
RETURN_ON_ERROR(Serialize(L, buf, -1, z, depth - 1));
RETURN_ON_ERROR(Serialize(L, buf, -1, z, depth + 1));
lua_pop(L, 1);
}
RETURN_ON_ERROR(appendw(buf, '}'));
RETURN_ON_ERROR(SerializeObjectEnd(buf, z, depth, multi));
return 0;
OnError:
return -1;
}
static intptr_t Join(const char *elem, void *arg) {
struct Joiner *j = arg;
struct SerializerJoin *j = arg;
if (!j->i) {
++j->i;
} else {
RETURN_ON_ERROR(appendw(j->buf, READ16LE(", ")));
if (j->multi) {
RETURN_ON_ERROR(appendw(j->buf, ','));
RETURN_ON_ERROR(SerializeObjectIndent(j->buf, j->z, j->depth + 1));
} else {
RETURN_ON_ERROR(appendw(j->buf, READ16LE(", ")));
}
}
RETURN_ON_ERROR(appends(j->buf, elem));
return 0;
@ -317,12 +316,11 @@ OnError:
}
static int SerializeSorted(lua_State *L, char **buf, struct Serializer *z,
int depth) {
int depth, bool multi) {
size_t n;
int i, rc;
char *b = 0;
const char *s;
struct Joiner j = {buf};
struct critbit0 t = {0};
lua_pushnil(L);
while (lua_next(L, -2)) {
@ -335,16 +333,22 @@ static int SerializeSorted(lua_State *L, char **buf, struct Serializer *z,
} else {
// use {[𝑘]=𝑣} otherwise
RETURN_ON_ERROR(appendw(&b, '['));
RETURN_ON_ERROR(Serialize(L, &b, -2, z, depth - 1));
RETURN_ON_ERROR(Serialize(L, &b, -2, z, depth + 1));
RETURN_ON_ERROR(appendw(&b, ']' | '=' << 010));
}
RETURN_ON_ERROR(Serialize(L, &b, -1, z, depth - 1));
RETURN_ON_ERROR(Serialize(L, &b, -1, z, depth + 1));
RETURN_ON_ERROR(critbit0_insert(&t, b));
lua_pop(L, 1);
}
RETURN_ON_ERROR(appendw(buf, '{'));
struct SerializerJoin j = {
.z = z,
.buf = buf,
.multi = multi,
.depth = depth,
};
RETURN_ON_ERROR(SerializeObjectStart(buf, z, depth, multi));
RETURN_ON_ERROR(critbit0_allprefixed(&t, "", Join, &j));
RETURN_ON_ERROR(appendw(buf, '}'));
RETURN_ON_ERROR(SerializeObjectEnd(buf, z, depth, multi));
critbit0_clear(&t);
free(b);
return 0;
@ -357,6 +361,7 @@ OnError:
static int SerializeTable(lua_State *L, char **buf, int idx,
struct Serializer *z, int depth) {
int rc;
bool multi;
intptr_t rsp, bot;
if (UNLIKELY(!HaveStackMemory(PAGESIZE))) {
z->reason = "out of stack";
@ -367,10 +372,13 @@ static int SerializeTable(lua_State *L, char **buf, int idx,
lua_pushvalue(L, idx); // idx becomes invalid once we change stack
if (IsLuaArray(L)) {
RETURN_ON_ERROR(SerializeArray(L, buf, z, depth));
} else if (z->sorted) {
RETURN_ON_ERROR(SerializeSorted(L, buf, z, depth));
} else {
RETURN_ON_ERROR(SerializeObject(L, buf, z, depth));
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));
}
}
LuaPopVisit(&z->visited);
lua_pop(L, 1); // table ref
@ -381,7 +389,7 @@ OnError:
static int Serialize(lua_State *L, char **buf, int idx, struct Serializer *z,
int depth) {
if (depth > 0) {
if (depth < z->conf.maxdepth) {
switch (lua_type(L, idx)) {
case LUA_TNIL:
return SerializeNil(L, buf);
@ -425,11 +433,12 @@ static int Serialize(lua_State *L, char **buf, int idx, struct Serializer *z,
* @param sorted is ignored (always sorted)
* @return 0 on success, or -1 on error
*/
int LuaEncodeLuaData(lua_State *L, char **buf, int idx, bool sorted) {
int rc, depth = 64;
struct Serializer z = {.reason = "out of memory", .sorted = sorted};
if (lua_checkstack(L, depth * 3 + LUA_MINSTACK)) {
rc = Serialize(L, buf, idx, &z, depth);
int LuaEncodeLuaData(lua_State *L, char **buf, int idx,
struct EncoderConfig conf) {
int rc;
struct Serializer z = {.reason = "out of memory", .conf = conf};
if (lua_checkstack(L, conf.maxdepth * 3 + LUA_MINSTACK)) {
rc = Serialize(L, buf, idx, &z, 0);
free(z.visited.p);
if (rc == -1) {
lua_pushnil(L);

View file

@ -24,11 +24,17 @@ dontdiscard char *LuaFormatStack(lua_State *L) {
size_t l;
int i, top;
char *p, *b = 0;
struct EncoderConfig conf = {
.maxdepth = 64,
.sorted = true,
.pretty = false,
.indent = " ",
};
top = lua_gettop(L);
for (i = 1; i <= top; i++) {
if (i > 1) appendw(&b, '\n');
appendf(&b, "\t%d\t%s\t", i, luaL_typename(L, i));
LuaEncodeLuaData(L, &b, i, true);
LuaEncodeLuaData(L, &b, i, conf);
}
return b;
}

68
third_party/lua/serialize.c vendored Normal file
View file

@ -0,0 +1,68 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net 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.
*/
#include "libc/log/rop.h"
#include "libc/stdio/append.internal.h"
#include "third_party/lua/cosmo.h"
#include "third_party/lua/lua.h"
bool LuaHasMultipleItems(lua_State *L) {
int i;
lua_pushnil(L);
for (i = 0; lua_next(L, -2); ++i) {
if (i > 0) {
lua_pop(L, 2);
return true;
}
lua_pop(L, 1);
}
return false;
}
int SerializeObjectIndent(char **buf, struct Serializer *z, int depth) {
int i;
RETURN_ON_ERROR(appendw(buf, '\n'));
for (i = 0; i < depth; ++i) {
RETURN_ON_ERROR(appends(buf, z->conf.indent));
}
return 0;
OnError:
return -1;
}
int SerializeObjectStart(char **buf, struct Serializer *z, int depth,
bool multi) {
RETURN_ON_ERROR(appendw(buf, '{'));
if (multi) {
RETURN_ON_ERROR(SerializeObjectIndent(buf, z, depth + 1));
}
return 0;
OnError:
return -1;
}
int SerializeObjectEnd(char **buf, struct Serializer *z, int depth,
bool multi) {
if (multi) {
RETURN_ON_ERROR(SerializeObjectIndent(buf, z, depth));
}
RETURN_ON_ERROR(appendw(buf, '}'));
return 0;
OnError:
return -1;
}

View file

@ -787,6 +787,20 @@ FUNCTIONS
don't care about ordering then setting `sorted=false`
should yield a performance boost in serialization.
- pretty: (bool=false) Setting this option to true will
cause tables with more than one entry to be formatted
across multiple lines for readability.
- indent: (str=" ") This option controls the indentation of
pretty formatting. This field is ignored if `pretty` isn't
true.
- maxdepth: (int=64) This option controls the maximum amount
of recursion the serializer is allowed to perform. The max
is 32767. You might not be able to set it that high if
there isn't enough C stack memory. Your serializer checks
for this and will return an error rather than crashing.
This function will return an error if:
- `value` is cyclic
@ -844,6 +858,20 @@ FUNCTIONS
don't care about ordering then setting `sorted=false`
should yield a performance boost in serialization.
- pretty: (bool=false) Setting this option to true will
cause tables with more than one entry to be formatted
across multiple lines for readability.
- indent: (str=" ") This option controls the indentation of
pretty formatting. This field is ignored if `pretty` isn't
true.
- maxdepth: (int=64) This option controls the maximum amount
of recursion the serializer is allowed to perform. The max
is 32767. You might not be able to set it that high if
there isn't enough C stack memory. Your serializer checks
for this and will return an error rather than crashing.
If a user data object has a `__repr` or `__tostring` meta
method, then that'll be used to encode the Lua code.

View file

@ -4239,12 +4239,16 @@ static int LuaLog(lua_State *L) {
return 0;
}
static int LuaEncodeSmth(lua_State *L,
int Encoder(lua_State *, char **, int, bool)) {
static int LuaEncodeSmth(lua_State *L, int Encoder(lua_State *, char **, int,
struct EncoderConfig)) {
char *p = 0;
int maxdepth = 64;
int sorted = true;
int useoutput = false;
struct EncoderConfig conf = {
.maxdepth = 64,
.sorted = true,
.pretty = false,
.indent = " ",
};
if (lua_istable(L, 2)) {
lua_settop(L, 2); // discard any extra arguments
lua_getfield(L, 2, "useoutput");
@ -4252,11 +4256,27 @@ static int LuaEncodeSmth(lua_State *L,
if (ishandlingrequest && lua_isboolean(L, -1)) {
useoutput = lua_toboolean(L, -1);
}
lua_getfield(L, 2, "maxdepth");
if (!lua_isnoneornil(L, -1)) {
lua_Integer n = lua_tointeger(L, -1);
n = MAX(0, MIN(n, SHRT_MAX));
conf.maxdepth = n;
}
lua_getfield(L, 2, "sorted");
sorted = lua_toboolean(L, -1);
if (!lua_isnoneornil(L, -1)) {
conf.sorted = lua_toboolean(L, -1);
}
lua_getfield(L, 2, "pretty");
if (!lua_isnoneornil(L, -1)) {
conf.pretty = lua_toboolean(L, -1);
lua_getfield(L, 2, "indent");
if (!lua_isnoneornil(L, -1)) {
conf.indent = luaL_checkstring(L, -1);
}
}
}
lua_settop(L, 1); // keep the passed argument on top
if (Encoder(L, useoutput ? &outbuf : &p, -1, sorted) == -1) {
if (Encoder(L, useoutput ? &outbuf : &p, -1, conf) == -1) {
free(p);
return 2;
}
@ -5373,7 +5393,13 @@ static void LuaPrint(lua_State *L) {
if (n > 0) {
for (i = 1; i <= n; i++) {
if (i > 1) appendw(&b, '\t');
LuaEncodeLuaData(L, &b, i, true);
struct EncoderConfig conf = {
.maxdepth = 64,
.sorted = true,
.pretty = true,
.indent = " ",
};
LuaEncodeLuaData(L, &b, i, conf);
}
appendw(&b, '\n');
WRITE(1, b, appendz(b).i);