Make redbean serialization deterministic

This commit is contained in:
Justine Tunney 2022-07-09 04:09:51 -07:00
parent 85aecbda67
commit c9e68b0ebc
15 changed files with 452 additions and 150 deletions

View file

@ -625,10 +625,8 @@
(compile (format "sh %s" file)))
((eq major-mode 'lua-mode)
(let* ((mode (cosmo--make-mode arg))
(redbean (format "%s/o/%s/tool/net/redbean.com" root mode)))
(if (file-executable-p redbean)
(compile (format "%s -i %s" redbean file))
(compile (format "lua.com %s" file)))))
(redbean ))
(compile (format "make -j16 MODE=%s o/%s/tool/net/redbean.com && o/%s/tool/net/redbean.com -i %s" mode mode mode file))))
((and (eq major-mode 'python-mode)
(cosmo-startswith "third_party/python/Lib/test/" file))
(let ((mode (cosmo--make-mode arg)))

View file

@ -647,26 +647,66 @@ FUNCTIONS
URIs that do things like embed a PNG file in a web page. See
encodebase64.c.
EncodeJson(value[,options:table]) → json:str
Turns passed Lua value into a JSON string. Tables with non-zero
length (as reported by `#`) are encoded as arrays with non-array
elements ignored. Empty tables are encoded as empty arrays. All
other tables are encoded as objects with numerical keys
converted to strings (so `{[3]=1}` is encoded as `{"3":1}`).
The following options can be used:
EncodeJson(value[,options:table])
├─→ json:str
├─→ true [if useoutput]
└─→ nil, error:str
Turns Lua data structure into a JSON string.
Tables with non-zero length (as reported by `#`) are encoded
as arrays with non-array elements ignored. Empty tables are
encoded as empty arrays. All other tables are encoded as
objects with numerical keys converted to strings (so `{[3]=1}`
is encoded as `{"3":1}`).
The following options may be used:
- useoutput: (bool=false) encodes the result directly to the
output buffer and returns `nil` value. This option is
ignored if used outside of request handling code.
- numformat: sets numeric format to be used, which can be 'g',
'f', or 'a' [experimental api]
EncodeLua(value[,options:table]) → json:str
Turns passed Lua value into a Lua string. The following options
can be used:
This function will fail if:
- `value` is cyclic
- `value` has depth greater than 64
- `value` contains functions, user data, or threads
When arrays and objects are serialized, entries will be sorted
in a deterministic order.
EncodeLua(value[,options:table])
├─→ luacode:str
├─→ true [if useoutput]
└─→ nil, error:str
Turns Lua data structure into Lua code string.
The following options may be used:
- useoutput: (bool=false) encodes the result directly to the
output buffer and returns `nil` value. This option is
ignored if used outside of request handling code.
This function will fail if:
- `value` has depth greater than 64
If a user data object has a `__repr` or `__tostring` meta
method, then that'll be used to encode the Lua code.
Non-encodable value types (e.g. threads, functions) will be
represented as a string literal with the type name and pointer
address. Note this is subject to change in the future.
This encoder detects cyclic tables, and encodes a string
literal saying it's cyclic when cycles are encountered.
When tables are serialized, entries will be sorted in a
deterministic order.
EncodeLatin1(utf-8:str[,flags:int]) → iso-8859-1:str
Turns UTF-8 into ISO-8859-1 string.

View file

@ -4231,9 +4231,14 @@ static int LuaEncodeSmth(lua_State *L,
numformat = luaL_optstring(L, -1, numformat);
}
lua_settop(L, 1); // keep the passed argument on top
Encoder(L, useoutput ? &outbuf : &p, numformat, -1);
if (useoutput) {
if (Encoder(L, useoutput ? &outbuf : &p, numformat, -1) == -1) {
free(p);
lua_pushnil(L);
lua_pushstring(L, "serialization failed");
return 2;
}
if (useoutput) {
lua_pushboolean(L, true);
} else {
lua_pushstring(L, p);
free(p);
@ -5394,12 +5399,45 @@ static int LuaInterpreter(lua_State *L) {
}
}
static void LuaDestroy(void) {
#ifndef STATIC
lua_State *L = GL;
lua_close(L);
free(g_lua_path_default);
#endif
}
static void MemDestroy(void) {
FreeAssets();
CollectGarbage();
inbuf.p = 0, inbuf.n = 0, inbuf.c = 0;
Free(&inbuf_actual.p), inbuf_actual.n = inbuf_actual.c = 0;
Free(&unmaplist.p), unmaplist.n = unmaplist.c = 0;
Free(&freelist.p), freelist.n = freelist.c = 0;
Free(&hdrbuf.p), hdrbuf.n = hdrbuf.c = 0;
Free(&servers.p), servers.n = 0;
Free(&ports.p), ports.n = 0;
Free(&ips.p), ips.n = 0;
Free(&outbuf);
FreeStrings(&stagedirs);
FreeStrings(&hidepaths);
Free(&launchbrowser);
Free(&serverheader);
Free(&extrahdrs);
Free(&pidpath);
Free(&logpath);
Free(&brand);
Free(&polls);
}
static void LuaInit(void) {
#ifndef STATIC
lua_State *L = GL;
LuaSetArgv(L);
if (interpretermode) {
int rc = LuaInterpreter(L);
LuaDestroy();
MemDestroy();
if (IsModeDbg()) {
CheckForMemoryLeaks();
}
@ -5426,14 +5464,6 @@ static void LuaReload(void) {
#endif
}
static void LuaDestroy(void) {
#ifndef STATIC
lua_State *L = GL;
lua_close(L);
free(g_lua_path_default);
#endif
}
static const char *DescribeClose(void) {
if (killed) return "killed";
if (meltdown) return "meltdown";
@ -7183,29 +7213,6 @@ static void TlsDestroy(void) {
#endif
}
static void MemDestroy(void) {
FreeAssets();
CollectGarbage();
inbuf.p = 0, inbuf.n = 0, inbuf.c = 0;
Free(&inbuf_actual.p), inbuf_actual.n = inbuf_actual.c = 0;
Free(&unmaplist.p), unmaplist.n = unmaplist.c = 0;
Free(&freelist.p), freelist.n = freelist.c = 0;
Free(&hdrbuf.p), hdrbuf.n = hdrbuf.c = 0;
Free(&servers.p), servers.n = 0;
Free(&ports.p), ports.n = 0;
Free(&ips.p), ips.n = 0;
Free(&outbuf);
FreeStrings(&stagedirs);
FreeStrings(&hidepaths);
Free(&launchbrowser);
Free(&serverheader);
Free(&extrahdrs);
Free(&pidpath);
Free(&logpath);
Free(&brand);
Free(&polls);
}
static void GetOpts(int argc, char *argv[]) {
int opt;
bool storeasset = false;