From fe5c475f8317244a417c7fdce2919084ea308ce3 Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Fri, 8 Jul 2022 09:47:04 -0700 Subject: [PATCH] Add Deflate() / Inflate() to redbean and fix bugs The Compress() and Uncompress() APIs were a mistake. The functions themselves work fine, but it's a design blemish and does superfluous work. Since they were only introduced in the last few weeks, they're now deprecated and references to them have been scrubbed from the website and other documentation. Please use the new APIs since the old APIs will be removed at some point in the future. This change introduces automated Lua unit tests for the Redbean APIs. There's a few functions that were broken which have now been fixed, e.g. Underlong() and Decimate(). --- build/rules.mk | 3 + net/http/underlong.c | 2 +- test/libc/unicode/wcwidth_test.c | 7 +++ test/net/http/underlong_test.c | 11 +++- test/tool/net/lfuncs_test.lua | 97 ++++++++++++++++++++++++++++++++ test/tool/net/lunix_test.lua | 52 +++++++++++++++++ test/tool/net/test.mk | 89 ++++++++++++++--------------- tool/emacs/cosmo-stuff.el | 6 +- tool/net/help.txt | 68 +++++++++------------- tool/net/lfuncs.c | 91 +++++++++++++++++++++++++++++- tool/net/lfuncs.h | 2 + tool/net/redbean.c | 17 ++++-- 12 files changed, 347 insertions(+), 98 deletions(-) create mode 100644 test/tool/net/lfuncs_test.lua create mode 100644 test/tool/net/lunix_test.lua 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");