Add raw parameter to redbean lua compress

This commit is contained in:
Justine Tunney 2022-05-17 10:41:23 -07:00
parent f37524ef4f
commit 3d99ebb68c
4 changed files with 92 additions and 50 deletions

View file

@ -30,14 +30,14 @@ char testlib_enable_tmp_setup_teardown;
TEST(getcwd, test) { TEST(getcwd, test) {
char buf[PATH_MAX]; char buf[PATH_MAX];
EXPECT_NE(-1, mkdir("subdir", 0755)); EXPECT_SYS(0, 0, mkdir("subdir", 0755));
EXPECT_NE(-1, chdir("subdir")); EXPECT_SYS(0, 0, chdir("subdir"));
EXPECT_STREQ("subdir", basename(getcwd(buf, ARRAYLEN(buf)))); EXPECT_STREQ("subdir", basename(getcwd(buf, ARRAYLEN(buf))));
} }
TEST(getcwd, testNullBuf_allocatesResult) { TEST(getcwd, testNullBuf_allocatesResult) {
EXPECT_NE(-1, mkdir("subdir", 0755)); EXPECT_SYS(0, 0, mkdir("subdir", 0755));
EXPECT_NE(-1, chdir("subdir")); EXPECT_SYS(0, 0, chdir("subdir"));
EXPECT_STREQ("subdir", basename(gc(getcwd(0, 0)))); EXPECT_STREQ("subdir", basename(gc(getcwd(0, 0))));
} }

View file

@ -23,6 +23,7 @@
#include "libc/calls/ucontext.h" #include "libc/calls/ucontext.h"
#include "libc/dce.h" #include "libc/dce.h"
#include "libc/errno.h" #include "libc/errno.h"
#include "libc/nexgen32e/nexgen32e.h"
#include "libc/runtime/runtime.h" #include "libc/runtime/runtime.h"
#include "libc/sysv/consts/sa.h" #include "libc/sysv/consts/sa.h"
#include "libc/sysv/consts/sig.h" #include "libc/sysv/consts/sig.h"
@ -34,6 +35,7 @@ struct sigaction oldsa;
volatile bool gotsigint; volatile bool gotsigint;
void OnSigInt(int sig) { void OnSigInt(int sig) {
_checkstackalign();
gotsigint = true; gotsigint = true;
} }
@ -46,11 +48,11 @@ void SetUp(void) {
TEST(sigaction, raise) { TEST(sigaction, raise) {
struct sigaction saint = {.sa_handler = OnSigInt}; struct sigaction saint = {.sa_handler = OnSigInt};
EXPECT_NE(-1, sigaction(SIGINT, &saint, &oldsa)); EXPECT_SYS(0, 0, sigaction(SIGINT, &saint, &oldsa));
ASSERT_FALSE(gotsigint); ASSERT_FALSE(gotsigint);
EXPECT_NE(-1, raise(SIGINT)); EXPECT_NE(-1, raise(SIGINT));
ASSERT_TRUE(gotsigint); ASSERT_TRUE(gotsigint);
EXPECT_NE(-1, sigaction(SIGINT, &oldsa, NULL)); EXPECT_SYS(0, 0, sigaction(SIGINT, &oldsa, NULL));
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -68,37 +70,36 @@ TEST(sigaction, testPingPongParentChildWithSigint) {
// kind of runner. todo(fixme!) // kind of runner. todo(fixme!)
return; return;
} }
EXPECT_NE(-1, sigemptyset(&blockint)); EXPECT_SYS(0, 0, sigemptyset(&blockint));
EXPECT_NE(-1, sigaddset(&blockint, SIGINT)); EXPECT_SYS(0, 0, sigaddset(&blockint, SIGINT));
EXPECT_NE(-1, sigprocmask(SIG_BLOCK, &blockint, &oldmask)); EXPECT_SYS(0, 0, sigprocmask(SIG_BLOCK, &blockint, &oldmask));
EXPECT_NE(-1, sigaction(SIGINT, &catchint, &oldint)); EXPECT_SYS(0, 0, sigaction(SIGINT, &catchint, &oldint));
ASSERT_NE(-1, (pid = fork())); ASSERT_NE(-1, (pid = fork()));
if (!pid) { if (!pid) {
// ping // ping
EXPECT_NE(-1, kill(getppid(), SIGINT)); EXPECT_SYS(0, 0, kill(getppid(), SIGINT));
EXPECT_FALSE(gotsigint); EXPECT_FALSE(gotsigint);
// pong // pong
EXPECT_NE(-1, sigaction(SIGINT, &catchint, 0)); EXPECT_SYS(0, 0, sigaction(SIGINT, &catchint, 0));
EXPECT_EQ(-1, sigsuspend(0)); EXPECT_SYS(EINTR, -1, sigsuspend(0));
EXPECT_EQ(EINTR, errno);
EXPECT_TRUE(gotsigint); EXPECT_TRUE(gotsigint);
_exit(0); _exit(0);
} }
// pong // pong
EXPECT_FALSE(gotsigint); EXPECT_FALSE(gotsigint);
EXPECT_NE(-1, sigaction(SIGINT, &catchint, 0)); EXPECT_SYS(0, 0, sigaction(SIGINT, &catchint, 0));
EXPECT_EQ(-1, sigsuspend(0)); EXPECT_SYS(EINTR, -1, sigsuspend(0));
EXPECT_TRUE(gotsigint); EXPECT_TRUE(gotsigint);
// ping // ping
EXPECT_NE(-1, sigaction(SIGINT, &ignoreint, 0)); EXPECT_SYS(0, 0, sigaction(SIGINT, &ignoreint, 0));
EXPECT_NE(-1, kill(pid, SIGINT)); EXPECT_SYS(0, 0, kill(pid, SIGINT));
// cleanup // cleanup
EXPECT_NE(-1, wait4(pid, &status, 0, 0)); EXPECT_NE(-1, wait4(pid, &status, 0, 0));
EXPECT_EQ(1, WIFEXITED(status)); EXPECT_EQ(1, WIFEXITED(status));
EXPECT_EQ(0, WEXITSTATUS(status)); EXPECT_EQ(0, WEXITSTATUS(status));
EXPECT_EQ(0, WTERMSIG(status)); EXPECT_EQ(0, WTERMSIG(status));
EXPECT_NE(-1, sigaction(SIGINT, &oldint, 0)); EXPECT_SYS(0, 0, sigaction(SIGINT, &oldint, 0));
EXPECT_NE(-1, sigprocmask(SIG_BLOCK, &oldmask, 0)); EXPECT_SYS(0, 0, sigprocmask(SIG_BLOCK, &oldmask, 0));
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -108,6 +109,7 @@ TEST(sigaction, testPingPongParentChildWithSigint) {
volatile int trapeax; volatile int trapeax;
void OnTrap(int sig, struct siginfo *si, struct ucontext *ctx) { void OnTrap(int sig, struct siginfo *si, struct ucontext *ctx) {
_checkstackalign();
trapeax = ctx->uc_mcontext.rax; trapeax = ctx->uc_mcontext.rax;
} }
@ -116,7 +118,7 @@ TEST(sigaction, debugBreak_handlerCanReadCpuState) {
EXPECT_NE(-1, sigaction(SIGTRAP, &saint, &oldsa)); EXPECT_NE(-1, sigaction(SIGTRAP, &saint, &oldsa));
asm("int3" : /* no outputs */ : "a"(0x31337)); asm("int3" : /* no outputs */ : "a"(0x31337));
EXPECT_EQ(0x31337, trapeax); EXPECT_EQ(0x31337, trapeax);
EXPECT_NE(-1, sigaction(SIGTRAP, &oldsa, NULL)); EXPECT_SYS(0, 0, sigaction(SIGTRAP, &oldsa, NULL));
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -124,6 +126,7 @@ TEST(sigaction, debugBreak_handlerCanReadCpuState) {
// test signal handler can modify cpu registers (now it's recoverable!) // test signal handler can modify cpu registers (now it's recoverable!)
void SkipOverFaultingInstruction(struct ucontext *ctx) { void SkipOverFaultingInstruction(struct ucontext *ctx) {
_checkstackalign();
struct XedDecodedInst xedd; struct XedDecodedInst xedd;
xed_decoded_inst_zero_set_mode(&xedd, XED_MACHINE_MODE_LONG_64); xed_decoded_inst_zero_set_mode(&xedd, XED_MACHINE_MODE_LONG_64);
xed_instruction_length_decode(&xedd, (void *)ctx->uc_mcontext.rip, 15); xed_instruction_length_decode(&xedd, (void *)ctx->uc_mcontext.rip, 15);
@ -131,6 +134,7 @@ void SkipOverFaultingInstruction(struct ucontext *ctx) {
} }
void OnFpe(int sig, struct siginfo *si, struct ucontext *ctx) { void OnFpe(int sig, struct siginfo *si, struct ucontext *ctx) {
_checkstackalign();
SkipOverFaultingInstruction(ctx); SkipOverFaultingInstruction(ctx);
ctx->uc_mcontext.rax = 42; ctx->uc_mcontext.rax = 42;
ctx->uc_mcontext.rdx = 0; ctx->uc_mcontext.rdx = 0;
@ -138,10 +142,10 @@ void OnFpe(int sig, struct siginfo *si, struct ucontext *ctx) {
noubsan void ubsanTrumpsSystemsEngineering(void) { noubsan void ubsanTrumpsSystemsEngineering(void) {
struct sigaction saint = {.sa_sigaction = OnFpe, .sa_flags = SA_SIGINFO}; struct sigaction saint = {.sa_sigaction = OnFpe, .sa_flags = SA_SIGINFO};
EXPECT_NE(-1, sigaction(SIGFPE, &saint, &oldsa)); EXPECT_SYS(0, 0, sigaction(SIGFPE, &saint, &oldsa));
volatile long x = 0; volatile long x = 0;
EXPECT_EQ(42, 666 / x); /* systems engineering trumps math */ EXPECT_EQ(42, 666 / x); /* systems engineering trumps math */
EXPECT_NE(-1, sigaction(SIGFPE, &oldsa, NULL)); EXPECT_SYS(0, 0, sigaction(SIGFPE, &oldsa, NULL));
} }
TEST(sigaction, sigFpe_handlerCanEditProcessStateAndRecoverExecution) { TEST(sigaction, sigFpe_handlerCanEditProcessStateAndRecoverExecution) {

View file

@ -1308,7 +1308,7 @@ FUNCTIONS
the density of information. Cryptographic random should be in the density of information. Cryptographic random should be in
the ballpark of 7.9 whereas plaintext will be more like 4.5. the ballpark of 7.9 whereas plaintext will be more like 4.5.
Compress(uncompdata:str[, level:int]) → compdata:str Compress(uncompdata:str[, level:int[, raw:bool]]) → compdata:str
Compresses data using DEFLATE algorithm. The compression Compresses data using DEFLATE algorithm. The compression
format here is defined to be quick and handy for things like format here is defined to be quick and handy for things like
@ -1319,18 +1319,27 @@ FUNCTIONS
>: Uncompress(Compress('hello')) >: Uncompress(Compress('hello'))
"hello" "hello"
`level` is the compression level, which defaults to 7. The max
is 10. Lower numbers go faster. Higher numbers go slower, but
have better compression ratios.
[implementation details]
The binary wire format is defined as follows: The binary wire format is defined as follows:
1. uleb64 uncompressed byte size (1 to 10 bytes) 1. uleb64 uncompressed byte size (1 to 10 bytes)
2. uint32_t crc32 (4 bytes; zlib polynomial) 2. uint32_t crc32 (4 bytes; zlib polynomial)
3. data (created by zlib compress function) 3. data (created by zlib compress function)
Uncompress(compdata:str) → uncompdata:str `level` is the compression level, which defaults to 7. The max
is 10. Lower numbers go faster. Higher numbers go slower, but
have better compression ratios.
`raw` may be set to true if you only want `data` (3) to be
returned. In this case, it's assumed the caller will take
responsibility for storing the length (and optionall crc)
separately. See the redbean Crc32() API.
>: a = 'hello'
>: b = Compress(a, 9, true)
>: Uncompress(b, #a)
"hello"
Uncompress(compdata:str[, uncomplen:int]) → uncompdata:str
Uncompresses data using DEFLATE algorithm. This applies the Uncompresses data using DEFLATE algorithm. This applies the
inverse transform of the Compress() function. See its docs for inverse transform of the Compress() function. See its docs for
@ -1341,6 +1350,15 @@ FUNCTIONS
of validity iron-clad. It's implemented using Intel CLMUL so of validity iron-clad. It's implemented using Intel CLMUL so
it has ludicrous speed performance as well. it has ludicrous speed performance as well.
If you used the `raw` parameter when calling Compress() i.e.
`compdata` doesn't have the redbean header described above,
then the `uncomplen` parameter may be supplied. IN that case
your data is handed over directly to zlib `uncompress()`. In
this case an exception will be raised if the value couldn't be
decoded, or if the resulting length differed from the supplied
length. It's recommended that Crc32() check still be performed
manually after using this method.
Benchmark(func[, count[, maxattempts]]) Benchmark(func[, count[, maxattempts]])
└─→ nanos:real, ticks:int, overhead-ticks:int, tries:int └─→ nanos:real, ticks:int, overhead-ticks:int, tries:int

View file

@ -683,6 +683,7 @@ int LuaBenchmark(lua_State *L) {
} }
int LuaCompress(lua_State *L) { int LuaCompress(lua_State *L) {
bool raw;
size_t n, m; size_t n, m;
char *q, *e; char *q, *e;
uint32_t crc; uint32_t crc;
@ -691,14 +692,23 @@ int LuaCompress(lua_State *L) {
p = luaL_checklstring(L, 1, &n); p = luaL_checklstring(L, 1, &n);
level = luaL_optinteger(L, 2, Z_DEFAULT_COMPRESSION); level = luaL_optinteger(L, 2, Z_DEFAULT_COMPRESSION);
m = compressBound(n); m = compressBound(n);
CHECK_NOTNULL((q = LuaAlloc(L, 10 + 4 + m))); if (lua_toboolean(L, 3)) {
crc = crc32_z(0, p, n); // raw mode
e = uleb64(q, n); CHECK_NOTNULL((q = LuaAlloc(L, m)));
e = WRITE32LE(e, crc); CHECK_EQ(Z_OK,
hdrlen = e - q; compress2((unsigned char *)q, &m, (unsigned char *)p, n, level));
CHECK_EQ(Z_OK, compress2((unsigned char *)(q + hdrlen), &m, lua_pushlstring(L, q, m);
(unsigned char *)p, n, level)); } else {
lua_pushlstring(L, q, hdrlen + m); // easy mode
CHECK_NOTNULL((q = LuaAlloc(L, 10 + 4 + m)));
crc = crc32_z(0, p, n);
e = uleb64(q, n);
e = WRITE32LE(e, crc);
hdrlen = e - q;
CHECK_EQ(Z_OK, compress2((unsigned char *)(q + hdrlen), &m,
(unsigned char *)p, n, level));
lua_pushlstring(L, q, hdrlen + m);
}
free(q); free(q);
return 1; return 1;
} }
@ -710,18 +720,28 @@ int LuaUncompress(lua_State *L) {
const char *p; const char *p;
size_t n, m, len; size_t n, m, len;
p = luaL_checklstring(L, 1, &n); p = luaL_checklstring(L, 1, &n);
if ((rc = unuleb64(p, n, &m)) == -1 || n < rc + 4) { if (lua_isnoneornil(L, 2)) {
luaL_error(L, "compressed value too short to be valid"); if ((rc = unuleb64(p, n, &m)) == -1 || n < rc + 4) {
unreachable; luaL_error(L, "compressed value too short to be valid");
} unreachable;
len = m; }
crc = READ32LE(p + rc); len = m;
CHECK_NOTNULL((q = LuaAlloc(L, m))); crc = READ32LE(p + rc);
if (uncompress((void *)q, &m, (unsigned char *)p + rc + 4, n) != Z_OK || CHECK_NOTNULL((q = LuaAlloc(L, m)));
m != len || crc32_z(0, q, m) != crc) { if (uncompress((void *)q, &m, (unsigned char *)p + rc + 4, n) != Z_OK ||
free(q); m != len || crc32_z(0, q, m) != crc) {
luaL_error(L, "compressed value is corrupted"); free(q);
unreachable; luaL_error(L, "compressed value is corrupted");
unreachable;
}
} else {
len = m = luaL_checkinteger(L, 2);
CHECK_NOTNULL((q = LuaAlloc(L, m)));
if (uncompress((void *)q, &m, (void *)p, n) != Z_OK || m != len) {
free(q);
luaL_error(L, "compressed value is corrupted");
unreachable;
}
} }
lua_pushlstring(L, q, m); lua_pushlstring(L, q, m);
free(q); free(q);