mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-07-03 09:48:29 +00:00
Polish redbean serialization
This commit is contained in:
parent
7aafa64ab3
commit
2d1731b995
24 changed files with 828 additions and 158 deletions
2
third_party/lua/README.cosmo
vendored
2
third_party/lua/README.cosmo
vendored
|
@ -24,6 +24,8 @@ LOCAL MODIFICATIONS
|
|||
|
||||
Integer literals such as `033` will now be interpreted as octal.
|
||||
|
||||
Integer literals such as `0b10` will now be interpreted as binary.
|
||||
|
||||
The `\e` string literal escape sequence has been added, which is
|
||||
equivalent to `\27` (the Lua version of `\033`) or the ASCII ESC
|
||||
character. It may be used for teletypewriter control like having
|
||||
|
|
2
third_party/lua/cosmo.h
vendored
2
third_party/lua/cosmo.h
vendored
|
@ -15,9 +15,9 @@ int LuaParseUrl(lua_State *);
|
|||
int LuaPushHeader(lua_State *, struct HttpMessage *, char *, int);
|
||||
int LuaPushHeaders(lua_State *, struct HttpMessage *, const char *);
|
||||
void EscapeLuaString(char *, size_t, char **);
|
||||
void LuaPrintStack(lua_State *);
|
||||
void LuaPushLatin1(lua_State *, const char *, size_t);
|
||||
void LuaPushUrlParams(lua_State *, struct UrlParams *);
|
||||
void LuaPrintStack(lua_State *);
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
|
|
101
third_party/lua/lapi.c
vendored
101
third_party/lua/lapi.c
vendored
|
@ -504,26 +504,51 @@ static void *touserdata (const TValue *o) {
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* lua_touserdata [-0, +0, –]
|
||||
*
|
||||
* If the value at the given index is a full userdata, returns its
|
||||
* memory-block address. If the value is a light userdata, returns its value
|
||||
* (a pointer). Otherwise, returns NULL.
|
||||
*/
|
||||
LUA_API void *lua_touserdata (lua_State *L, int idx) {
|
||||
const TValue *o = index2value(L, idx);
|
||||
return touserdata(o);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* lua_tothread [-0, +0, –]
|
||||
*
|
||||
* Converts the value at the given index to a Lua thread (represented as
|
||||
* lua_State*). This value must be a thread; otherwise, the function returns
|
||||
* NULL.
|
||||
*/
|
||||
LUA_API lua_State *lua_tothread (lua_State *L, int idx) {
|
||||
const TValue *o = index2value(L, idx);
|
||||
return (!ttisthread(o)) ? NULL : thvalue(o);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Returns a pointer to the internal representation of an object.
|
||||
** Note that ANSI C does not allow the conversion of a pointer to
|
||||
** function to a 'void*', so the conversion here goes through
|
||||
** a 'size_t'. (As the returned pointer is only informative, this
|
||||
** conversion should not be a problem.)
|
||||
*/
|
||||
/**
|
||||
* lua_topointer [-0, +0, –]
|
||||
*
|
||||
* Converts the value at the given index to a generic C pointer (void*). The
|
||||
* value can be a userdata, a table, a thread, a string, or a function;
|
||||
* otherwise, lua_topointer returns NULL. Different objects will give
|
||||
* different pointers. There is no way to convert the pointer back to its
|
||||
* original value.
|
||||
*
|
||||
* Typically this function is used only for hashing and debug information.
|
||||
*/
|
||||
LUA_API const void *lua_topointer (lua_State *L, int idx) {
|
||||
/*
|
||||
** Returns a pointer to the internal representation of an object.
|
||||
** Note that ANSI C does not allow the conversion of a pointer to
|
||||
** function to a 'void*', so the conversion here goes through
|
||||
** a 'size_t'. (As the returned pointer is only informative, this
|
||||
** conversion should not be a problem.)
|
||||
*/
|
||||
const TValue *o = index2value(L, idx);
|
||||
switch (ttypetag(o)) {
|
||||
case LUA_VLCF: return cast_voidp(cast_sizet(fvalue(o)));
|
||||
|
@ -881,6 +906,12 @@ static Table *gettable (lua_State *L, int idx) {
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* lua_rawget [-1, +1, –]
|
||||
*
|
||||
* Similar to lua_gettable, but does a raw access (i.e., without
|
||||
* metamethods).
|
||||
*/
|
||||
LUA_API int lua_rawget (lua_State *L, int idx) {
|
||||
Table *t;
|
||||
const TValue *val;
|
||||
|
@ -901,6 +932,15 @@ LUA_API int lua_rawgeti (lua_State *L, int idx, lua_Integer n) {
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* lua_rawgetp [-0, +1, –]
|
||||
*
|
||||
* Pushes onto the stack the value t[k], where t is the table at the given
|
||||
* index and k is the pointer p represented as a light userdata. The access
|
||||
* is raw; that is, it does not use the __index metavalue.
|
||||
*
|
||||
* Returns the type of the pushed value.
|
||||
*/
|
||||
LUA_API int lua_rawgetp (lua_State *L, int idx, const void *p) {
|
||||
Table *t;
|
||||
TValue k;
|
||||
|
@ -911,6 +951,17 @@ LUA_API int lua_rawgetp (lua_State *L, int idx, const void *p) {
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* lua_createtable [-0, +1, m]
|
||||
*
|
||||
* Creates a new empty table and pushes it onto the stack. Parameter narr is
|
||||
* a hint for how many elements the table will have as a sequence; parameter
|
||||
* nrec is a hint for how many other elements the table will have. Lua may
|
||||
* use these hints to preallocate memory for the new table. This
|
||||
* preallocation may help performance when you know in advance how many
|
||||
* elements the table will have. Otherwise you can use the function
|
||||
* lua_newtable.
|
||||
*/
|
||||
LUA_API void lua_createtable (lua_State *L, int narray, int nrec) {
|
||||
Table *t;
|
||||
lua_lock(L);
|
||||
|
@ -924,6 +975,15 @@ LUA_API void lua_createtable (lua_State *L, int narray, int nrec) {
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* lua_getmetatable [-0, +(0|1), –]
|
||||
*
|
||||
* int lua_getmetatable (lua_State *L, int index);
|
||||
*
|
||||
* If the value at the given index has a metatable, the function pushes that
|
||||
* metatable onto the stack and returns 1. Otherwise, the function returns 0
|
||||
* and pushes nothing on the stack.
|
||||
*/
|
||||
LUA_API int lua_getmetatable (lua_State *L, int objindex) {
|
||||
const TValue *obj;
|
||||
Table *mt;
|
||||
|
@ -951,6 +1011,17 @@ LUA_API int lua_getmetatable (lua_State *L, int objindex) {
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* lua_getiuservalue [-0, +1, –]
|
||||
*
|
||||
* int lua_getiuservalue (lua_State *L, int index, int n);
|
||||
*
|
||||
* Pushes onto the stack the n-th user value associated with the full
|
||||
* userdata at the given index and returns the type of the pushed value.
|
||||
*
|
||||
* If the userdata does not have that value, pushes nil and returns
|
||||
* LUA_TNONE.
|
||||
*/
|
||||
LUA_API int lua_getiuservalue (lua_State *L, int idx, int n) {
|
||||
TValue *o;
|
||||
int t;
|
||||
|
@ -1116,6 +1187,15 @@ LUA_API void lua_rawseti (lua_State *L, int idx, lua_Integer n) {
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* lua_setmetatable [-1, +0, –]
|
||||
*
|
||||
* Pops a table or nil from the stack and sets that value as the new
|
||||
* metatable for the value at the given index. (nil means no metatable.)
|
||||
*
|
||||
* (For historical reasons, this function returns an int, which now is always
|
||||
* 1.)
|
||||
*/
|
||||
LUA_API int lua_setmetatable (lua_State *L, int objindex) {
|
||||
TValue *obj;
|
||||
Table *mt;
|
||||
|
@ -1156,6 +1236,13 @@ LUA_API int lua_setmetatable (lua_State *L, int objindex) {
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* lua_setiuservalue [-1, +0, –]
|
||||
*
|
||||
* Pops a value from the stack and sets it as the new n-th user value
|
||||
* associated to the full userdata at the given index. Returns 0 if the
|
||||
* userdata does not have that value.
|
||||
*/
|
||||
LUA_API int lua_setiuservalue (lua_State *L, int idx, int n) {
|
||||
TValue *o;
|
||||
int res;
|
||||
|
|
6
third_party/lua/lctype.h
vendored
6
third_party/lua/lctype.h
vendored
|
@ -45,6 +45,12 @@
|
|||
('a' <= c_ && c_ <= 'f')); \
|
||||
})
|
||||
|
||||
#define lisbdigit(C) \
|
||||
({ \
|
||||
unsigned char c_ = (C); \
|
||||
'0' <= c_&& c_ <= '1'; \
|
||||
})
|
||||
|
||||
#define lisprint(C) \
|
||||
({ \
|
||||
unsigned char c_ = (C); \
|
||||
|
|
9
third_party/lua/lobject.c
vendored
9
third_party/lua/lobject.c
vendored
|
@ -27,7 +27,6 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#define lobject_c
|
||||
#define LUA_CORE
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "third_party/lua/lctype.h"
|
||||
#include "third_party/lua/ldebug.h"
|
||||
#include "third_party/lua/ldo.h"
|
||||
|
@ -285,6 +284,14 @@ static const char *l_str2int (const char *s, lua_Integer *result) {
|
|||
empty = 0;
|
||||
}
|
||||
}
|
||||
if (s[0] == '0' &&
|
||||
(s[1] == 'b' || s[1] == 'B')) { /* [jart] binary */
|
||||
s += 2; /* skip '0b' */
|
||||
for (; lisbdigit(cast_uchar(*s)); s++) {
|
||||
a = a * 2 + (*s - '0');
|
||||
empty = 0;
|
||||
}
|
||||
}
|
||||
else if (s[0] == '0') { /* [jart] octal is the best radix */
|
||||
for (s += 1; lisdigit(cast_uchar(*s)); s++) {
|
||||
int d = *s - '0';
|
||||
|
|
4
third_party/lua/lrepl.c
vendored
4
third_party/lua/lrepl.c
vendored
|
@ -30,13 +30,13 @@
|
|||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/sigbits.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/intrin/nomultics.internal.h"
|
||||
#include "libc/log/check.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/runtime/gc.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/sa.h"
|
||||
#include "third_party/linenoise/linenoise.h"
|
||||
|
@ -330,7 +330,7 @@ void lua_initrepl(lua_State *L, const char *progname) {
|
|||
prompt = get_prompt(L, 1);
|
||||
if ((g_historypath = linenoiseGetHistoryPath(progname))) {
|
||||
if (linenoiseHistoryLoad(g_historypath) == -1) {
|
||||
kprintf("%r%s: failed to load history: %m%n", g_historypath);
|
||||
fprintf(stderr, "%r%s: failed to load history: %m%n", g_historypath);
|
||||
free(g_historypath);
|
||||
g_historypath = 0;
|
||||
}
|
||||
|
|
139
third_party/lua/luaencodejsondata.c
vendored
139
third_party/lua/luaencodejsondata.c
vendored
|
@ -16,23 +16,37 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/assert.h"
|
||||
#include "libc/bits/bits.h"
|
||||
#include "libc/fmt/itoa.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/runtime/gc.internal.h"
|
||||
#include "libc/stdio/append.internal.h"
|
||||
#include "net/http/escape.h"
|
||||
#include "third_party/lua/cosmo.h"
|
||||
#include "third_party/lua/lauxlib.h"
|
||||
#include "third_party/lua/lua.h"
|
||||
#include "third_party/lua/visitor.h"
|
||||
|
||||
static int LuaEncodeJsonDataImpl(lua_State *L, char **buf, int level,
|
||||
char *numformat, int idx) {
|
||||
char *numformat, int idx,
|
||||
struct LuaVisited *visited) {
|
||||
char *s;
|
||||
bool isarray;
|
||||
size_t tbllen, z;
|
||||
size_t tbllen, i, z;
|
||||
char ibuf[21], fmt[] = "%.14g";
|
||||
if (level > 0) {
|
||||
switch (lua_type(L, idx)) {
|
||||
|
||||
case LUA_TNIL:
|
||||
appendw(buf, READ32LE("null"));
|
||||
return 0;
|
||||
|
||||
case LUA_TBOOLEAN:
|
||||
appendw(buf, lua_toboolean(L, idx) ? READ32LE("true")
|
||||
: READ64LE("false\0\0"));
|
||||
return 0;
|
||||
|
||||
case LUA_TSTRING:
|
||||
s = lua_tolstring(L, idx, &z);
|
||||
s = EscapeJsStringLiteral(s, z, &z);
|
||||
|
@ -41,21 +55,7 @@ static int LuaEncodeJsonDataImpl(lua_State *L, char **buf, int level,
|
|||
appendw(buf, '"');
|
||||
free(s);
|
||||
return 0;
|
||||
case LUA_TNIL:
|
||||
appendw(buf, READ32LE("null"));
|
||||
return 0;
|
||||
case LUA_TFUNCTION:
|
||||
appendf(buf, "\"func@%p\"", lua_touserdata(L, idx));
|
||||
return 0;
|
||||
case LUA_TUSERDATA:
|
||||
appendf(buf, "\"udata@%p\"", lua_touserdata(L, idx));
|
||||
return 0;
|
||||
case LUA_TLIGHTUSERDATA:
|
||||
appendf(buf, "\"light@%p\"", lua_touserdata(L, idx));
|
||||
return 0;
|
||||
case LUA_TTHREAD:
|
||||
appendf(buf, "\"thread@%p\"", lua_touserdata(L, idx));
|
||||
return 0;
|
||||
|
||||
case LUA_TNUMBER:
|
||||
if (lua_isinteger(L, idx)) {
|
||||
appendd(buf, ibuf,
|
||||
|
@ -78,58 +78,64 @@ static int LuaEncodeJsonDataImpl(lua_State *L, char **buf, int level,
|
|||
appendf(buf, fmt, lua_tonumber(L, idx));
|
||||
}
|
||||
return 0;
|
||||
case LUA_TBOOLEAN:
|
||||
appends(buf, lua_toboolean(L, idx) ? "true" : "false");
|
||||
return 0;
|
||||
|
||||
case LUA_TTABLE:
|
||||
tbllen = lua_rawlen(L, idx);
|
||||
// encode tables with numeric indices and empty tables as arrays
|
||||
isarray =
|
||||
tbllen > 0 || // integer keys present
|
||||
(lua_pushnil(L), lua_next(L, -2) == 0) || // no non-integer keys
|
||||
(lua_pop(L, 2), false); // pop key/value pushed by lua_next
|
||||
appendw(buf, isarray ? '[' : '{');
|
||||
if (isarray) {
|
||||
for (size_t i = 1; i <= tbllen; i++) {
|
||||
if (i > 1) appendw(buf, ',');
|
||||
lua_rawgeti(L, -1, i); // table/-2, value/-1
|
||||
LuaEncodeJsonDataImpl(L, buf, level - 1, numformat, -1);
|
||||
lua_pop(L, 1);
|
||||
if (LuaPushVisit(visited, lua_topointer(L, idx))) {
|
||||
tbllen = lua_rawlen(L, idx);
|
||||
// encode tables with numeric indices and empty tables as arrays
|
||||
isarray =
|
||||
tbllen > 0 || // integer keys present
|
||||
(lua_pushnil(L), !lua_next(L, -2)) || // no non-integer keys
|
||||
(lua_pop(L, 2), false); // pop key/value pushed by lua_next
|
||||
appendw(buf, isarray ? '[' : '{');
|
||||
if (isarray) {
|
||||
for (i = 1; i <= tbllen; i++) {
|
||||
if (i > 1) appendw(buf, ',');
|
||||
lua_rawgeti(L, -1, i); // table/-2, value/-1
|
||||
LuaEncodeJsonDataImpl(L, buf, level - 1, numformat, -1, visited);
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
} else {
|
||||
i = 1;
|
||||
lua_pushnil(L); // push the first key
|
||||
while (lua_next(L, -2)) {
|
||||
if (!lua_isstring(L, -2)) {
|
||||
luaL_error(L, "expected number or string as key value");
|
||||
unreachable;
|
||||
}
|
||||
if (i++ > 1) appendw(buf, ',');
|
||||
appendw(buf, '"');
|
||||
if (lua_type(L, -2) == LUA_TSTRING) {
|
||||
s = lua_tolstring(L, -2, &z);
|
||||
s = EscapeJsStringLiteral(s, z, &z);
|
||||
appendd(buf, s, z);
|
||||
free(s);
|
||||
} else {
|
||||
// we'd still prefer to use lua_tostring on a numeric index, but
|
||||
// can't use it in-place, as it breaks lua_next (changes numeric
|
||||
// key to a string)
|
||||
lua_pushvalue(L, -2); // table/-4, key/-3, value/-2, key/-1
|
||||
s = lua_tolstring(L, idx, &z);
|
||||
appendd(buf, s, z); // use the copy
|
||||
lua_remove(L, -1); // remove copied key: tab/-3, key/-2, val/-1
|
||||
}
|
||||
appendw(buf, '"' | ':' << 010);
|
||||
LuaEncodeJsonDataImpl(L, buf, level - 1, numformat, -1, visited);
|
||||
lua_pop(L, 1); // table/-2, key/-1
|
||||
}
|
||||
// stack: table/-1, as the key was popped by lua_next
|
||||
}
|
||||
appendw(buf, isarray ? ']' : '}');
|
||||
LuaPopVisit(visited);
|
||||
return 0;
|
||||
} else {
|
||||
int i = 1;
|
||||
lua_pushnil(L); // push the first key
|
||||
while (lua_next(L, -2)) {
|
||||
if (!lua_isstring(L, -2)) {
|
||||
luaL_error(L, "expected number or string as key value");
|
||||
unreachable;
|
||||
}
|
||||
if (i++ > 1) appendw(buf, ',');
|
||||
appendw(buf, '"');
|
||||
if (lua_type(L, -2) == LUA_TSTRING) {
|
||||
s = lua_tolstring(L, -2, &z);
|
||||
s = EscapeJsStringLiteral(s, z, &z);
|
||||
appendd(buf, s, z);
|
||||
free(s);
|
||||
} else {
|
||||
// we'd still prefer to use lua_tostring on a numeric index, but
|
||||
// can't use it in-place, as it breaks lua_next (changes numeric
|
||||
// key to a string)
|
||||
lua_pushvalue(L, -2); // table/-4, key/-3, value/-2, key/-1
|
||||
s = lua_tolstring(L, idx, &z);
|
||||
appendd(buf, s, z); // use the copy
|
||||
lua_remove(L, -1); // remove copied key: tab/-3, key/-2, val/-1
|
||||
}
|
||||
appendw(buf, '"' | ':' << 010);
|
||||
LuaEncodeJsonDataImpl(L, buf, level - 1, numformat, -1);
|
||||
lua_pop(L, 1); // table/-2, key/-1
|
||||
}
|
||||
// stack: table/-1, as the key was popped by lua_next
|
||||
// TODO(jart): don't leak memory with longjmp
|
||||
luaL_error(L, "can't serialize cyclic data structure to json");
|
||||
unreachable;
|
||||
}
|
||||
appendw(buf, isarray ? ']' : '}');
|
||||
return 0;
|
||||
|
||||
default:
|
||||
luaL_error(L, "can't serialize value of this type");
|
||||
luaL_error(L, "won't serialize %s to json", luaL_typename(L, idx));
|
||||
unreachable;
|
||||
}
|
||||
} else {
|
||||
|
@ -140,6 +146,9 @@ static int LuaEncodeJsonDataImpl(lua_State *L, char **buf, int level,
|
|||
|
||||
int LuaEncodeJsonData(lua_State *L, char **buf, char *numformat, int idx) {
|
||||
int rc;
|
||||
rc = LuaEncodeJsonDataImpl(L, buf, 64, numformat, idx);
|
||||
struct LuaVisited visited = {0};
|
||||
rc = LuaEncodeJsonDataImpl(L, buf, 64, numformat, idx, &visited);
|
||||
assert(!visited.n);
|
||||
free(visited.p);
|
||||
return rc;
|
||||
}
|
||||
|
|
115
third_party/lua/luaencodeluadata.c
vendored
115
third_party/lua/luaencodeluadata.c
vendored
|
@ -25,39 +25,44 @@
|
|||
#include "libc/x/x.h"
|
||||
#include "third_party/lua/cosmo.h"
|
||||
#include "third_party/lua/lauxlib.h"
|
||||
#include "third_party/lua/lctype.h"
|
||||
#include "third_party/lua/lua.h"
|
||||
#include "third_party/lua/visitor.h"
|
||||
|
||||
struct Visited {
|
||||
int n;
|
||||
void **p;
|
||||
};
|
||||
|
||||
static bool PushVisit(struct Visited *visited, void *p) {
|
||||
int i;
|
||||
for (i = 0; i < visited->n; ++i) {
|
||||
if (visited->p[i] == p) {
|
||||
return false;
|
||||
}
|
||||
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;
|
||||
}
|
||||
visited->p = xrealloc(visited->p, ++visited->n * sizeof(*visited->p));
|
||||
visited->p[visited->n - 1] = p;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void PopVisit(struct Visited *visited) {
|
||||
assert(visited->n > 0);
|
||||
--visited->n;
|
||||
// TODO: Can we be smarter with lua_rawlen?
|
||||
static bool IsLuaArray(lua_State *L) {
|
||||
int i;
|
||||
lua_pushnil(L);
|
||||
for (i = 1; lua_next(L, -2); ++i) {
|
||||
if (!lua_isinteger(L, -2) || lua_tointeger(L, -2) != i) {
|
||||
lua_pop(L, 2);
|
||||
return false;
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static int LuaEncodeLuaDataImpl(lua_State *L, char **buf, int level,
|
||||
char *numformat, int idx,
|
||||
struct Visited *visited) {
|
||||
struct LuaVisited *visited) {
|
||||
char *s;
|
||||
bool didcomma;
|
||||
bool isarray;
|
||||
lua_Integer i;
|
||||
int ktype, vtype;
|
||||
size_t tbllen, buflen, slen;
|
||||
char ibuf[21], fmt[] = "%.14g";
|
||||
char ibuf[24], fmt[] = "%.14g";
|
||||
if (level > 0) {
|
||||
switch (lua_type(L, idx)) {
|
||||
|
||||
|
@ -71,15 +76,15 @@ static int LuaEncodeLuaDataImpl(lua_State *L, char **buf, int level,
|
|||
return 0;
|
||||
|
||||
case LUA_TFUNCTION:
|
||||
appendf(buf, "\"func@%p\"", lua_touserdata(L, idx));
|
||||
appendf(buf, "\"func@%p\"", lua_topointer(L, idx));
|
||||
return 0;
|
||||
|
||||
case LUA_TLIGHTUSERDATA:
|
||||
appendf(buf, "\"light@%p\"", lua_touserdata(L, idx));
|
||||
appendf(buf, "\"light@%p\"", lua_topointer(L, idx));
|
||||
return 0;
|
||||
|
||||
case LUA_TTHREAD:
|
||||
appendf(buf, "\"thread@%p\"", lua_touserdata(L, idx));
|
||||
appendf(buf, "\"thread@%p\"", lua_topointer(L, idx));
|
||||
return 0;
|
||||
|
||||
case LUA_TUSERDATA:
|
||||
|
@ -111,7 +116,7 @@ static int LuaEncodeLuaDataImpl(lua_State *L, char **buf, int level,
|
|||
case LUA_TNUMBER:
|
||||
if (lua_isinteger(L, idx)) {
|
||||
appendd(buf, ibuf,
|
||||
FormatInt64(ibuf, luaL_checkinteger(L, idx)) - ibuf);
|
||||
FormatFlex64(ibuf, luaL_checkinteger(L, idx), 2) - ibuf);
|
||||
} else {
|
||||
// TODO(jart): replace this api
|
||||
while (*numformat == '%' || *numformat == '.' ||
|
||||
|
@ -133,58 +138,52 @@ static int LuaEncodeLuaDataImpl(lua_State *L, char **buf, int level,
|
|||
return 0;
|
||||
|
||||
case LUA_TBOOLEAN:
|
||||
if (lua_toboolean(L, idx)) {
|
||||
appendw(buf, READ32LE("true"));
|
||||
} else {
|
||||
appendw(buf, READ64LE("false\0\0"));
|
||||
}
|
||||
appendw(buf, lua_toboolean(L, idx) ? READ32LE("true")
|
||||
: READ64LE("false\0\0"));
|
||||
return 0;
|
||||
|
||||
case LUA_TTABLE:
|
||||
i = 0;
|
||||
didcomma = false;
|
||||
appendw(buf, '{');
|
||||
lua_pushvalue(L, idx);
|
||||
lua_pushnil(L); // push the first key
|
||||
while (lua_next(L, -2)) {
|
||||
++i;
|
||||
ktype = lua_type(L, -2);
|
||||
vtype = lua_type(L, -1);
|
||||
if (ktype != LUA_TNUMBER || lua_tointeger(L, -2) != i) {
|
||||
if (PushVisit(visited, lua_touserdata(L, -2))) {
|
||||
if (i > 1) appendw(buf, ',' | ' ' << 8);
|
||||
didcomma = true;
|
||||
if (LuaPushVisit(visited, lua_topointer(L, idx))) {
|
||||
appendw(buf, '{');
|
||||
lua_pushvalue(L, idx); // idx becomes invalid once we change stack
|
||||
isarray = IsLuaArray(L);
|
||||
lua_pushnil(L); // push the first key
|
||||
while (lua_next(L, -2)) {
|
||||
ktype = lua_type(L, -2);
|
||||
vtype = lua_type(L, -1);
|
||||
if (i++ > 0) appendw(buf, ',' | ' ' << 8);
|
||||
if (isarray) {
|
||||
// use {v₁′,v₂′,...} for lua-normal integer keys
|
||||
} else if (ktype == LUA_TSTRING && IsLuaIdentifier(L, -2)) {
|
||||
// use {𝑘=𝑣′} syntax when 𝑘 is legal as a lua identifier
|
||||
s = lua_tolstring(L, -2, &slen);
|
||||
appendd(buf, s, slen);
|
||||
appendw(buf, '=');
|
||||
} else {
|
||||
// use {[𝑘′]=𝑣′} otherwise
|
||||
appendw(buf, '[');
|
||||
LuaEncodeLuaDataImpl(L, buf, level - 1, numformat, -2, visited);
|
||||
appendw(buf, ']' | '=' << 010);
|
||||
PopVisit(visited);
|
||||
} else {
|
||||
// TODO: Strange Lua data structure, do nothing.
|
||||
lua_pop(L, 1);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (PushVisit(visited, lua_touserdata(L, -1))) {
|
||||
if (!didcomma && i > 1) appendw(buf, ',' | ' ' << 8);
|
||||
LuaEncodeLuaDataImpl(L, buf, level - 1, numformat, -1, visited);
|
||||
PopVisit(visited);
|
||||
} else {
|
||||
// TODO: Strange Lua data structure, do nothing.
|
||||
lua_pop(L, 1);
|
||||
continue;
|
||||
lua_pop(L, 1); // table/-2, key/-1
|
||||
}
|
||||
lua_pop(L, 1); // table/-2, key/-1
|
||||
lua_pop(L, 1); // table ref
|
||||
LuaPopVisit(visited);
|
||||
appendw(buf, '}');
|
||||
} else {
|
||||
appendf(buf, "\"cyclic@%p\"", lua_topointer(L, idx));
|
||||
}
|
||||
lua_pop(L, 1); // table
|
||||
// stack: table/-1, as the key was popped by lua_next
|
||||
appendw(buf, '}');
|
||||
return 0;
|
||||
|
||||
default:
|
||||
// TODO(jart): don't leak memory with longjmp
|
||||
luaL_error(L, "can't serialize value of this type");
|
||||
unreachable;
|
||||
}
|
||||
} else {
|
||||
// TODO(jart): don't leak memory with longjmp
|
||||
luaL_error(L, "too many nested tables");
|
||||
unreachable;
|
||||
}
|
||||
|
@ -192,7 +191,7 @@ static int LuaEncodeLuaDataImpl(lua_State *L, char **buf, int level,
|
|||
|
||||
int LuaEncodeLuaData(lua_State *L, char **buf, char *numformat, int idx) {
|
||||
int rc;
|
||||
struct Visited visited = {0};
|
||||
struct LuaVisited visited = {0};
|
||||
rc = LuaEncodeLuaDataImpl(L, buf, 64, numformat, idx, &visited);
|
||||
assert(!visited.n);
|
||||
free(visited.p);
|
||||
|
|
38
third_party/lua/visitor.c
vendored
Normal file
38
third_party/lua/visitor.c
vendored
Normal file
|
@ -0,0 +1,38 @@
|
|||
/*-*- 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/assert.h"
|
||||
#include "libc/x/x.h"
|
||||
#include "third_party/lua/visitor.h"
|
||||
|
||||
bool LuaPushVisit(struct LuaVisited *visited, const void *p) {
|
||||
int i;
|
||||
for (i = 0; i < visited->n; ++i) {
|
||||
if (visited->p[i] == p) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
visited->p = xrealloc(visited->p, ++visited->n * sizeof(*visited->p));
|
||||
visited->p[visited->n - 1] = p;
|
||||
return true;
|
||||
}
|
||||
|
||||
void LuaPopVisit(struct LuaVisited *visited) {
|
||||
assert(visited->n > 0);
|
||||
--visited->n;
|
||||
}
|
16
third_party/lua/visitor.h
vendored
Normal file
16
third_party/lua/visitor.h
vendored
Normal file
|
@ -0,0 +1,16 @@
|
|||
#ifndef COSMOPOLITAN_THIRD_PARTY_LUA_VISITOR_H_
|
||||
#define COSMOPOLITAN_THIRD_PARTY_LUA_VISITOR_H_
|
||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
struct LuaVisited {
|
||||
int n;
|
||||
const void **p;
|
||||
};
|
||||
|
||||
bool LuaPushVisit(struct LuaVisited *, const void *);
|
||||
void LuaPopVisit(struct LuaVisited *);
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
#endif /* COSMOPOLITAN_THIRD_PARTY_LUA_VISITOR_H_ */
|
Loading…
Add table
Add a link
Reference in a new issue