diff --git a/build/rules.mk b/build/rules.mk index e999984c7..503136ce7 100644 --- a/build/rules.mk +++ b/build/rules.mk @@ -101,3 +101,6 @@ o/$(MODE)/%.pyc: %.py o/$(MODE)/third_party/python/pycomp.com o/$(MODE)/%.lua: %.lua o/$(MODE)/third_party/lua/luac.com @$(COMPILE) -ALUAC o/$(MODE)/third_party/lua/luac.com -s -o $@ $< + +o/$(MODE)/%.lua.runs: %.lua o/$(MODE)/tool/net/redbean.com + @$(COMPILE) -ALUA -tT$@ o/$(MODE)/tool/net/redbean.com $(LUAFLAGS) -i $< diff --git a/net/http/underlong.c b/net/http/underlong.c index e6e1c959b..b158745f5 100644 --- a/net/http/underlong.c +++ b/net/http/underlong.c @@ -44,7 +44,7 @@ char *Underlong(const char *p, size_t n, size_t *z) { int8_t v1[16], v2[16], vz[16]; if (z) *z = 0; if (n == -1) n = p ? strlen(p) : 0; - if ((q = r = malloc(n + 1))) { + if ((q = r = malloc(n * 2 + 1))) { for (i = 0; i < n;) { bzero(vz, 16); /* 50x speedup for ASCII */ while (i + 16 < n) { diff --git a/test/libc/unicode/wcwidth_test.c b/test/libc/unicode/wcwidth_test.c index febdc7e13..d23ec23ef 100644 --- a/test/libc/unicode/wcwidth_test.c +++ b/test/libc/unicode/wcwidth_test.c @@ -22,6 +22,11 @@ #include "libc/testlib/testlib.h" #include "libc/unicode/unicode.h" +TEST(wcwidth, test) { + ASSERT_EQ(0, wcwidth(0)); + ASSERT_EQ(-1, wcwidth(1)); +} + TEST(strwidth, testCjkWidesAndCombiningLowLines_withThompsonPikeEncoding) { /*───────────────────────────────────────────────────┬─*/ EXPECT_EQ(20, strwidth(/**/ "𐌰𐌱𐌲𐌳𐌴𐌵𐌶𐌷▒▒▒▒▒▒▒▒▒▒▒▒" /*│*/, 0)); @@ -63,6 +68,8 @@ TEST(wcwidth, testCjkWidesAndCombiningLowLines_widthIsNotLength) { TEST(wcwidth, block) { EXPECT_EQ(1, wcwidth(u'▄')); + EXPECT_EQ(0x3061, L'ち'); + EXPECT_EQ(2, wcwidth(L'ち')); } TEST(strwidth, testTextDelimitingControlCodes_dontHaveSubstance) { diff --git a/test/net/http/underlong_test.c b/test/net/http/underlong_test.c index 3c7221a8f..303e052b5 100644 --- a/test/net/http/underlong_test.c +++ b/test/net/http/underlong_test.c @@ -23,14 +23,21 @@ #include "libc/testlib/testlib.h" #include "net/http/escape.h" +size_t n; + TEST(Underlong, test) { - size_t n; EXPECT_BINEQ(u"e e", gc(Underlong("e\300\200e", -1, &n))); EXPECT_EQ(3, n); + EXPECT_BINEQ(u"e ", gc(Underlong("e\300\200", -1, &n))); + EXPECT_EQ(2, n); +} + +TEST(Underlong, testWeirdInvalidLatin1) { + EXPECT_BINEQ(u"e├Çe ", gc(Underlong("e\300e", -1, &n))); + EXPECT_EQ(4, n); } TEST(Underlong, testNormalText) { - size_t n; EXPECT_STREQ(kHyperion, gc(Underlong(kHyperion, kHyperionSize, &n))); EXPECT_EQ(kHyperionSize, n); } diff --git a/test/tool/net/lfuncs_test.lua b/test/tool/net/lfuncs_test.lua new file mode 100644 index 000000000..954ee875a --- /dev/null +++ b/test/tool/net/lfuncs_test.lua @@ -0,0 +1,97 @@ +-- 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. + +x = Rdtsc() +y = Rdtsc() +assert(y > x) + +assert(Bsr(1) == 0) +assert(Bsr(2) == 1) +assert(Bsr(3) == 1) +assert(Bsr(4) == 2) +assert(Bsr(0x80000001) == 31) + +assert(Bsf(1) == 0) +assert(Bsf(2) == 1) +assert(Bsf(3) == 0) +assert(Bsf(4) == 2) +assert(Bsf(0x80000001) == 0) + +assert(Lemur64() == 0x1940efe9d47ae889) +assert(Lemur64() == 0xd4b3103f567f9974) + +assert(hex(0x1940efe9d47ae889) == "0x1940efe9d47ae889") +assert(oct(0x1940efe9d47ae889) == "0145007376472436564211") +assert(bin(0x1940efe9d47ae889) == "0b0001100101000000111011111110100111010100011110101110100010001001") + +assert(DecodeBase64("abcdefgABCDE") == "\x69\xb7\x1d\x79\xf8\x00\x04\x20\xc4") +assert(EncodeBase64("\x69\xb7\x1d\x79\xf8\x00\x04\x20\xc4") == "abcdefgABCDE") + +assert(Decimate("\xff\xff\x00\x00\xff\xff\x00\x00\xff\xff\x00\x00") == "\xff\x00\xff\x00\xff\x00") + +assert(Underlong("hello") == "hello") +assert(Underlong("hello\xc0\x80") == "hello\x00") + +assert(ParseIp("x") == -1) +assert(ParseIp("127.0.0.1") == 0x7f000001) + +assert(GetMonospaceWidth('c') == 1) +assert(GetMonospaceWidth(string.byte('c')) == 1) +assert(GetMonospaceWidth(0) == 0) +assert(GetMonospaceWidth(1) == -1) +assert(GetMonospaceWidth(32) == 1) +assert(GetMonospaceWidth(0x3061) == 2) +assert(GetMonospaceWidth("ちち") == 4) + +assert(IsPublicIp(0x08080808)) +assert(not IsPublicIp(0x0a000000)) +assert(not IsPublicIp(0x7f000001)) + +assert(not IsPrivateIp(0x08080808)) +assert(IsPrivateIp(0x0a000000)) +assert(not IsPrivateIp(0x7f000001)) + +assert(not IsLoopbackIp(0x08080808)) +assert(not IsLoopbackIp(0x0a000000)) +assert(IsLoopbackIp(0x7f000001)) + +assert(IndentLines("hi\nthere") == " hi\n there") +assert(IndentLines("hi\nthere\n") == " hi\n there\n") +assert(IndentLines("hi\nthere\n", 2) == " hi\n there\n") + +assert(ParseHttpDateTime("Fri, 08 Jul 2022 16:17:43 GMT") == 1657297063) + +assert(VisualizeControlCodes("hello\x00") == "hello␀") + +assert(math.floor(10 * MeasureEntropy(" ") + .5) == 0) +assert(math.floor(10 * MeasureEntropy("abcabcabcabc") + .5) == 16) + +assert(Crc32(0, "123456789") == 0xcbf43926) +assert(Crc32c(0, "123456789") == 0xe3069283) +assert(Md5("hello") == "\x5d\x41\x40\x2a\xbc\x4b\x2a\x76\xb9\x71\x9d\x91\x10\x17\xc5\x92") +assert(Sha1("hello") == "\xaa\xf4\xc6\x1d\xdc\xc5\xe8\xa2\xda\xbe\xde\x0f\x3b\x48\x2c\xd9\xae\xa9\x43\x4d") +assert(Sha224("hello") == "\xea\x09\xae\x9c\xc6\x76\x8c\x50\xfc\xee\x90\x3e\xd0\x54\x55\x6e\x5b\xfc\x83\x47\x90\x7f\x12\x59\x8a\xa2\x41\x93") +assert(Sha256("hello") == "\x2c\xf2\x4d\xba\x5f\xb0\xa3\x0e\x26\xe8\x3b\x2a\xc5\xb9\xe2\x9e\x1b\x16\x1e\x5c\x1f\xa7\x42\x5e\x73\x04\x33\x62\x93\x8b\x98\x24") +assert(Sha384("hello") == "\x59\xe1\x74\x87\x77\x44\x8c\x69\xde\x6b\x80\x0d\x7a\x33\xbb\xfb\x9f\xf1\xb4\x63\xe4\x43\x54\xc3\x55\x3b\xcd\xb9\xc6\x66\xfa\x90\x12\x5a\x3c\x79\xf9\x03\x97\xbd\xf5\xf6\xa1\x3d\xe8\x28\x68\x4f") +assert(Sha512("hello") == "\x9b\x71\xd2\x24\xbd\x62\xf3\x78\x5d\x96\xd4\x6a\xd3\xea\x3d\x73\x31\x9b\xfb\xc2\x89\x0c\xaa\xda\xe2\xdf\xf7\x25\x19\x67\x3c\xa7\x23\x23\xc3\xd9\x9b\xa5\xc1\x1d\x7c\x7a\xcc\x6e\x14\xb8\xc5\xda\x0c\x46\x63\x47\x5c\x2e\x5c\x3a\xde\xf4\x6f\x73\xbc\xde\xc0\x43") + +assert(Deflate("hello") == "\xcbH\xcd\xc9\xc9\x07\x00") +assert(Inflate("\xcbH\xcd\xc9\xc9\x07\x00", 5) == "hello") + +-- deprecated compression api we wish to forget as quickly as possible +assert(Uncompress(Compress("hello")) == "hello") +assert(Compress("hello") == "\x05\x86\xa6\x106x\x9c\xcbH\xcd\xc9\xc9\x07\x00\x06,\x02\x15") +assert(Compress("hello", 0) == "\x05\x86\xa6\x106x\x01\x01\x05\x00\xfa\xffhello\x06,\x02\x15") +assert(Compress("hello", 0, true) == "x\x01\x01\x05\x00\xfa\xffhello\x06,\x02\x15") diff --git a/test/tool/net/lunix_test.lua b/test/tool/net/lunix_test.lua new file mode 100644 index 000000000..d5338800c --- /dev/null +++ b/test/tool/net/lunix_test.lua @@ -0,0 +1,52 @@ +-- 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. + +-- dup()+close() +fd = assert(unix.dup(2)) +assert(unix.close(fd)) + +-- dup2()+close() +assert(assert(unix.dup(2, 10)) == 10) +assert(unix.close(10)) + +-- fork()+exit() +if assert(unix.fork()) == 0 then + unix.exit(42) +end +pid, ws = assert(unix.wait()) +assert(unix.WIFEXITED(ws)) +assert(unix.WEXITSTATUS(ws) == 42) + +-- pledge() +if GetHostOs() == "LINUX" then + if assert(unix.fork()) == 0 then + assert(unix.pledge("stdio")) + _, err = unix.socket() + assert(err:errno() == unix.EPERM) + unix.exit(0) + end + pid, ws = assert(unix.wait()) + assert(unix.WIFEXITED(ws)) + assert(unix.WEXITSTATUS(ws) == 0) +elseif GetHostOs() == "OPENBSD" then + if assert(unix.fork()) == 0 then + assert(unix.pledge("stdio")) + unix.socket() + unix.exit(1) + end + pid, ws = assert(unix.wait()) + assert(unix.WIFSIGNALED(ws)) + assert(unix.WTERMSIG(ws) == unix.SIGABRT) +end diff --git a/test/tool/net/test.mk b/test/tool/net/test.mk index c652a90a0..9027f77c8 100644 --- a/test/tool/net/test.mk +++ b/test/tool/net/test.mk @@ -8,70 +8,71 @@ TEST_TOOL_NET_A = o/$(MODE)/test/tool/net/net.a TEST_TOOL_NET_FILES := $(wildcard test/tool/net/*) TEST_TOOL_NET_SRCS = $(filter %.c,$(TEST_TOOL_NET_FILES)) TEST_TOOL_NET_SRCS_TEST = $(filter %_test.c,$(TEST_TOOL_NET_SRCS)) +TEST_TOOL_NET_LUAS_TEST = $(filter %_test.lua,$(TEST_TOOL_NET_FILES)) TEST_TOOL_NET_HDRS = $(filter %.h,$(TEST_TOOL_NET_FILES)) -TEST_TOOL_NET_COMS = $(TEST_TOOL_NET_OBJS:%.o=%.com) TEST_TOOL_NET_COMS = $(TEST_TOOL_NET_SRCS:%.c=o/$(MODE)/%.com) -TEST_TOOL_NET_OBJS = \ - $(TEST_TOOL_NET_SRCS:%.c=o/$(MODE)/%.o) \ +TEST_TOOL_NET_OBJS = \ + $(TEST_TOOL_NET_SRCS:%.c=o/$(MODE)/%.o) \ o/$(MODE)/tool/net/redbean.com.zip.o -TEST_TOOL_NET_BINS = \ - $(TEST_TOOL_NET_COMS) \ +TEST_TOOL_NET_BINS = \ + $(TEST_TOOL_NET_COMS) \ $(TEST_TOOL_NET_COMS:%=%.dbg) -TEST_TOOL_NET_TESTS = \ +TEST_TOOL_NET_TESTS = \ $(TEST_TOOL_NET_SRCS_TEST:%.c=o/$(MODE)/%.com.ok) -TEST_TOOL_NET_CHECKS = \ - $(TEST_TOOL_NET_HDRS:%=o/$(MODE)/%.ok) \ - $(TEST_TOOL_NET_SRCS_TEST:%.c=o/$(MODE)/%.com.runs) +TEST_TOOL_NET_CHECKS = \ + $(TEST_TOOL_NET_HDRS:%=o/$(MODE)/%.ok) \ + $(TEST_TOOL_NET_SRCS_TEST:%.c=o/$(MODE)/%.com.runs) \ + $(TEST_TOOL_NET_LUAS_TEST:%.lua=o/$(MODE)/%.lua.runs) -TEST_TOOL_NET_DIRECTDEPS = \ - LIBC_CALLS \ - LIBC_FMT \ - LIBC_INTRIN \ - LIBC_LOG \ - LIBC_MEM \ - LIBC_NEXGEN32E \ - LIBC_RAND \ - LIBC_RUNTIME \ - LIBC_SOCK \ - LIBC_STDIO \ - LIBC_STR \ - LIBC_STUBS \ - LIBC_SYSV \ - LIBC_TESTLIB \ - LIBC_UNICODE \ - LIBC_X \ - LIBC_ZIPOS \ - THIRD_PARTY_REGEX \ - THIRD_PARTY_MBEDTLS \ +TEST_TOOL_NET_DIRECTDEPS = \ + LIBC_CALLS \ + LIBC_FMT \ + LIBC_INTRIN \ + LIBC_LOG \ + LIBC_MEM \ + LIBC_NEXGEN32E \ + LIBC_RAND \ + LIBC_RUNTIME \ + LIBC_SOCK \ + LIBC_STDIO \ + LIBC_STR \ + LIBC_STUBS \ + LIBC_SYSV \ + LIBC_TESTLIB \ + LIBC_UNICODE \ + LIBC_X \ + LIBC_ZIPOS \ + THIRD_PARTY_REGEX \ + THIRD_PARTY_MBEDTLS \ THIRD_PARTY_SQLITE3 -TEST_TOOL_NET_DEPS := \ +TEST_TOOL_NET_DEPS := \ $(call uniq,$(foreach x,$(TEST_TOOL_NET_DIRECTDEPS),$($(x)))) -$(TEST_TOOL_NET_A): \ - test/tool/net/ \ - $(TEST_TOOL_NET_A).pkg \ +$(TEST_TOOL_NET_A): \ + test/tool/net/ \ + $(TEST_TOOL_NET_A).pkg \ $(TEST_TOOL_NET_OBJS) -$(TEST_TOOL_NET_A).pkg: \ - $(TEST_TOOL_NET_OBJS) \ +$(TEST_TOOL_NET_A).pkg: \ + $(TEST_TOOL_NET_OBJS) \ $(foreach x,$(TEST_TOOL_NET_DIRECTDEPS),$($(x)_A).pkg) -o/$(MODE)/test/tool/net/%.com.dbg: \ - $(TEST_TOOL_NET_DEPS) \ - $(TEST_TOOL_NET_A) \ - o/$(MODE)/test/tool/net/%.o \ - $(TEST_TOOL_NET_A).pkg \ - $(LIBC_TESTMAIN) \ - $(CRT) \ +o/$(MODE)/test/tool/net/%.com.dbg: \ + $(TEST_TOOL_NET_DEPS) \ + $(TEST_TOOL_NET_A) \ + o/$(MODE)/test/tool/net/%.o \ + $(TEST_TOOL_NET_A).pkg \ + $(LIBC_TESTMAIN) \ + $(CRT) \ $(APE_NO_MODIFY_SELF) @$(APELINK) .PHONY: o/$(MODE)/test/tool/net -o/$(MODE)/test/tool/net: \ - $(TEST_TOOL_NET_BINS) \ +o/$(MODE)/test/tool/net: \ + $(TEST_TOOL_NET_BINS) \ $(TEST_TOOL_NET_CHECKS) diff --git a/tool/emacs/cosmo-stuff.el b/tool/emacs/cosmo-stuff.el index e9ce1053f..a7cbf1749 100644 --- a/tool/emacs/cosmo-stuff.el +++ b/tool/emacs/cosmo-stuff.el @@ -624,7 +624,11 @@ ((eq major-mode 'sh-mode) (compile (format "sh %s" file))) ((eq major-mode 'lua-mode) - (compile (format "lua.com %s" file))) + (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))))) ((and (eq major-mode 'python-mode) (cosmo-startswith "third_party/python/Lib/test/" file)) (let ((mode (cosmo--make-mode arg))) diff --git a/tool/net/help.txt b/tool/net/help.txt index 852f3b0d7..a260de688 100644 --- a/tool/net/help.txt +++ b/tool/net/help.txt @@ -1359,8 +1359,8 @@ FUNCTIONS Shrinks byte buffer in half using John Costella's magic kernel. This downscales data 2x using an eight-tap convolution, e.g. - >: Decimate(b'\\xff\\xff\\x00\\x00\\xff\\xff\\x00\\x00\\xff\\xff\\x00\\x00') - b'\\xff\\x00\\xff\\x00\\xff\\x00' + >: Decimate('\xff\xff\x00\x00\xff\xff\x00\x00\xff\xff\x00\x00') + "\xff\x00\xff\x00\xff\x00" This is very fast if SSSE3 is available (Intel 2004+ / AMD 2011+). @@ -1369,56 +1369,40 @@ FUNCTIONS the density of information. Cryptographic random should be in the ballpark of 7.9 whereas plaintext will be more like 4.5. - Compress(uncompdata:str[, level:int[, raw:bool]]) → compdata:str + Deflate(uncompressed:str[, level:int]) + ├─→ compressed:str + └─→ nil, error:str - Compresses data using DEFLATE algorithm. The compression - format here is defined to be quick and handy for things like - database fields. For example: + Compresses data. - >: Compress('hello') - "\x05\x86\xa6\x106x\x9c\xcbH\xcd\xc9\xc9\x07\x00\x06,\x02\x15" - >: Uncompress(Compress('hello')) + >: Deflate("hello") + "\xcbH\xcd\xc9\xc9\x07\x00" + >: Inflate("\xcbH\xcd\xc9\xc9\x07\x00", 5) "hello" - The binary wire format is defined as follows: - - 1. uleb64 uncompressed byte size (1 to 10 bytes) - 2. uint32_t crc32 (4 bytes; zlib polynomial) - 3. data (created by zlib compress function) + The output format is raw DEFLATE that's suitable for embedding + into formats like a ZIP file. It's recommended that, like ZIP, + you also store separately a Crc32() checksum in addition to + the original uncompressed size. `level` is the compression level, which defaults to 7. The max - is 9. Lower numbers go faster. Higher numbers go slower, but - have better compression ratios. + is 9. Lower numbers go faster (4 for instance is a sweet spot) + and higher numbers go slower but have better compression. - `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. + Inflate(compressed:str, maxoutsize:int) + ├─→ uncompressed:str + └─→ nil, error:str - >: a = 'hello' - >: b = Compress(a, 9, true) - >: Uncompress(b, #a) - "hello" + Decompresses data. - Uncompress(compdata:str[, uncomplen:int]) → uncompdata:str + This function performs the inverse of Deflate(). It's + recommended that you perform a Crc32() check on the output + string after this function succeeds. - Uncompresses data using DEFLATE algorithm. This applies the - inverse transform of the Compress() function. See its docs for - further details on usage and encoding. - - This function throws exceptions in the event that the value - couldn't be decoded. There's a crc32 check to make our check - of validity iron-clad. It's implemented using Intel CLMUL so - 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. + `maxoutsize` is the uncompressed size, which should be known. + However, it is permissable (although not advised) to specify + some large number in which case (on success) the byte length + of the output string may be less than `maxoutsize`. Benchmark(func[, count[, maxattempts]]) └─→ nanos:real, ticks:int, overhead-ticks:int, tries:int diff --git a/tool/net/lfuncs.c b/tool/net/lfuncs.c index 2b4df020e..89ad63112 100644 --- a/tool/net/lfuncs.c +++ b/tool/net/lfuncs.c @@ -163,6 +163,7 @@ int LuaDecimate(lua_State *L) { s = luaL_checklstring(L, 1, &n); m = ROUNDUP(n, 16); p = luaL_buffinitsize(L, &buf, m); + memcpy(p, s, n); bzero(p + n, m - n); cDecimate2xUint8x8(m, (unsigned char *)p, (signed char[8]){-1, -3, 3, 17, 17, 3, -3, -1}); @@ -355,7 +356,7 @@ int LuaIndentLines(lua_State *L) { int LuaGetMonospaceWidth(lua_State *L) { int w; - if (lua_isinteger(L, 1)) { + if (lua_isnumber(L, 1)) { w = wcwidth(lua_tointeger(L, 1)); } else if (lua_isstring(L, 1)) { w = strwidth(luaL_checkstring(L, 1), luaL_optinteger(L, 2, 0) & 7); @@ -766,9 +767,9 @@ int LuaCompress(lua_State *L) { } int LuaUncompress(lua_State *L) { + int rc; char *q; uint32_t crc; - int rc, level; const char *p; luaL_Buffer buf; size_t n, m, len; @@ -797,3 +798,89 @@ int LuaUncompress(lua_State *L) { luaL_pushresultsize(&buf, m); return 1; } + +// unix.deflate(uncompressed:str[, level:int]) +// ├─→ compressed:str +// └─→ nil, error:str +int LuaDeflate(lua_State *L) { + char *out; + z_stream zs; + int rc, level; + const char *in; + luaL_Buffer buf; + size_t insize, outsize, actualoutsize; + in = luaL_checklstring(L, 1, &insize); + level = luaL_optinteger(L, 2, Z_DEFAULT_COMPRESSION); + outsize = compressBound(insize); + out = luaL_buffinitsize(L, &buf, outsize); + + zs.next_in = (const uint8_t *)in; + zs.avail_in = insize; + zs.next_out = (uint8_t *)out; + zs.avail_out = outsize; + zs.zalloc = Z_NULL; + zs.zfree = Z_NULL; + + if ((rc = deflateInit2(&zs, level, Z_DEFLATED, -MAX_WBITS, MAX_MEM_LEVEL, + Z_DEFAULT_STRATEGY)) != Z_OK) { + lua_pushnil(L); + lua_pushfstring(L, "%s() failed: %d", "deflateInit2", rc); + return 2; + } + + rc = deflate(&zs, Z_FINISH); + actualoutsize = outsize - zs.avail_out; + deflateEnd(&zs); + + if (rc != Z_STREAM_END) { + lua_pushnil(L); + lua_pushfstring(L, "%s() failed: %d", "deflate", rc); + return 2; + } + + luaL_pushresultsize(&buf, actualoutsize); + return 1; +} + +// unix.inflate(compressed:str, maxoutsize:int) +// ├─→ uncompressed:str +// └─→ nil, error:str +int LuaInflate(lua_State *L) { + int rc; + char *out; + z_stream zs; + const char *in; + luaL_Buffer buf; + size_t insize, outsize, actualoutsize; + in = luaL_checklstring(L, 1, &insize); + outsize = luaL_checkinteger(L, 2); + out = luaL_buffinitsize(L, &buf, outsize); + + zs.next_in = (const uint8_t *)in; + zs.avail_in = insize; + zs.total_in = insize; + zs.next_out = (uint8_t *)out; + zs.avail_out = outsize; + zs.total_out = outsize; + zs.zalloc = Z_NULL; + zs.zfree = Z_NULL; + + if ((rc = inflateInit2(&zs, -MAX_WBITS)) != Z_OK) { + lua_pushnil(L); + lua_pushfstring(L, "%s() failed: %d", "inflateInit2", rc); + return 2; + } + + rc = inflate(&zs, Z_FINISH); + actualoutsize = outsize - zs.avail_out; + inflateEnd(&zs); + + if (rc != Z_STREAM_END) { + lua_pushnil(L); + lua_pushfstring(L, "%s() failed: %d", "inflate", rc); + return 2; + } + + luaL_pushresultsize(&buf, actualoutsize); + return 1; +} diff --git a/tool/net/lfuncs.h b/tool/net/lfuncs.h index 6a8c43fca..9c83af624 100644 --- a/tool/net/lfuncs.h +++ b/tool/net/lfuncs.h @@ -21,6 +21,7 @@ int LuaCrc32c(lua_State *); int LuaDecimate(lua_State *); int LuaDecodeBase64(lua_State *); int LuaDecodeLatin1(lua_State *); +int LuaDeflate(lua_State *); int LuaEncodeBase64(lua_State *); int LuaEncodeLatin1(lua_State *); int LuaEscapeFragment(lua_State *); @@ -48,6 +49,7 @@ int LuaGetTime(lua_State *); int LuaHasControlCodes(lua_State *); int LuaHex(lua_State *); int LuaIndentLines(lua_State *); +int LuaInflate(lua_State *); int LuaIsAcceptableHost(lua_State *); int LuaIsAcceptablePath(lua_State *); int LuaIsAcceptablePort(lua_State *); diff --git a/tool/net/redbean.c b/tool/net/redbean.c index c852d6f74..b9cbcb15a 100644 --- a/tool/net/redbean.c +++ b/tool/net/redbean.c @@ -3896,7 +3896,8 @@ static int LuaFetch(lua_State *L) { freeaddrinfo(addr), addr = 0; if (rc == -1) { close(sock); - return LuaNilError(L, "connect(%s:%s) error: %s", host, port, strerror(errno)); + return LuaNilError(L, "connect(%s:%s) error: %s", host, port, + strerror(errno)); } #ifndef UNSECURE @@ -4978,6 +4979,7 @@ static bool LuaRunAsset(const char *path, bool mandatory) { // // list of functions that can't be run from the repl static const char *const kDontAutoComplete[] = { + "Compress", // deprecated "GetBody", // "GetClientAddr", // "GetClientFd", // @@ -5037,6 +5039,7 @@ static const char *const kDontAutoComplete[] = { "SetCookie", // "SetHeader", // "SslInit", // TODO + "Uncompress", // deprecated "Write", // }; // @@ -5052,6 +5055,7 @@ static const luaL_Reg kLuaFuncs[] = { {"Decimate", LuaDecimate}, // {"DecodeBase64", LuaDecodeBase64}, // {"DecodeLatin1", LuaDecodeLatin1}, // + {"Deflate", LuaDeflate}, // {"EncodeBase64", LuaEncodeBase64}, // {"EncodeJson", LuaEncodeJson}, // {"EncodeLatin1", LuaEncodeLatin1}, // @@ -5113,11 +5117,12 @@ static const luaL_Reg kLuaFuncs[] = { {"HasParam", LuaHasParam}, // {"HidePath", LuaHidePath}, // {"IndentLines", LuaIndentLines}, // + {"Inflate", LuaInflate}, // {"IsAcceptableHost", LuaIsAcceptableHost}, // {"IsAcceptablePath", LuaIsAcceptablePath}, // {"IsAcceptablePort", LuaIsAcceptablePort}, // - {"IsClientUsingSsl", LuaIsClientUsingSsl}, // {"IsAssetCompressed", LuaIsAssetCompressed}, // + {"IsClientUsingSsl", LuaIsClientUsingSsl}, // {"IsDaemon", LuaIsDaemon}, // {"IsHeaderRepeatable", LuaIsHeaderRepeatable}, // {"IsHiddenPath", LuaIsHiddenPath}, // @@ -5333,7 +5338,7 @@ static void DisableRawMode(void) { ++__strace; } -static void LuaInterpreter(lua_State *L) { +static int LuaInterpreter(lua_State *L) { int i, n, sig, status; const char *script; if (optind < __argc) { @@ -5349,7 +5354,7 @@ static void LuaInterpreter(lua_State *L) { status = lua_runchunk(L, n, LUA_MULTRET); TRACE_END; } - lua_report(L, status); + return lua_report(L, status); } else { lua_repl_blocking = true; lua_repl_completions_callback = HandleCompletions; @@ -5384,6 +5389,7 @@ static void LuaInterpreter(lua_State *L) { if ((sig = linenoiseGetInterrupt())) { raise(sig); } + return status; } } @@ -5392,8 +5398,7 @@ static void LuaInit(void) { lua_State *L = GL; LuaSetArgv(L); if (interpretermode) { - LuaInterpreter(L); - exit(0); + exit(LuaInterpreter(L)); } if (LuaRunAsset("/.init.lua", true)) { hasonhttprequest = IsHookDefined("OnHttpRequest");