diff --git a/build/bootstrap/compile.com b/build/bootstrap/compile.com index c460b3c60..179038020 100755 Binary files a/build/bootstrap/compile.com and b/build/bootstrap/compile.com differ diff --git a/libc/stdio/appendd.c b/libc/stdio/appendd.c index ffffbcd9b..4a6c63775 100644 --- a/libc/stdio/appendd.c +++ b/libc/stdio/appendd.c @@ -39,15 +39,17 @@ * @param l is byte length of `s` * @return bytes appended (always `l`) or -1 if `ENOMEM` * @see appendz(b).i to get buffer length + * @note 20% faster than appends() */ ssize_t appendd(char **b, const void *s, size_t l) { char *p; + size_t n; struct appendz z; - assert(b); z = appendz((p = *b)); - if (ROUNDUP(z.i + l + 1, 8) + W > z.n) { + n = ROUNDUP(z.i + l + 1, 8) + W; + if (n > z.n) { if (!z.n) z.n = W * 2; - while (ROUNDUP(z.i + l + 1, 8) + W > z.n) z.n += z.n >> 1; + while (n > z.n) z.n += z.n >> 1; z.n = ROUNDUP(z.n, W); if ((p = realloc(p, z.n))) { z.n = malloc_usable_size(p); @@ -59,9 +61,7 @@ ssize_t appendd(char **b, const void *s, size_t l) { } *(char *)mempcpy(p + z.i, s, l) = 0; z.i += l; - if (!IsTiny() && W == 8) { - z.i |= (size_t)APPEND_COOKIE << 48; - } + if (!IsTiny() && W == 8) z.i |= (size_t)APPEND_COOKIE << 48; *(size_t *)(p + z.n - W) = z.i; return l; } diff --git a/libc/stdio/appendf.c b/libc/stdio/appendf.c index 3991d4a9a..aa1487313 100644 --- a/libc/stdio/appendf.c +++ b/libc/stdio/appendf.c @@ -27,6 +27,7 @@ * * @return bytes appended or -1 if `ENOMEM` * @see appendz(b).i to get buffer length + * @note O(1) amortized buffer growth */ ssize_t(appendf)(char **b, const char *fmt, ...) { int n; diff --git a/libc/stdio/appendr.c b/libc/stdio/appendr.c index 6e3a365fb..6b520adf5 100644 --- a/libc/stdio/appendr.c +++ b/libc/stdio/appendr.c @@ -20,6 +20,7 @@ #include "libc/dce.h" #include "libc/macros.internal.h" #include "libc/mem/mem.h" +#include "libc/nexgen32e/bsr.h" #include "libc/stdio/append.internal.h" #include "libc/str/str.h" @@ -37,35 +38,39 @@ * free(b); * * If `i` is greater than the current length then the extra bytes are - * filled with NUL characters. + * filled with NUL characters. If `i` is less than the current length + * then memory is released to the system. * * The resulting buffer is guaranteed to be NUL-terminated, i.e. - * `!b[appendz(b).i]` will be the case. + * `!b[appendz(b).i]` will be the case even if both params are 0. * * @return `i` or -1 if `ENOMEM` * @see appendz(b).i to get buffer length */ ssize_t appendr(char **b, size_t i) { char *p; + size_t n; struct appendz z; assert(b); z = appendz((p = *b)); - z.n = ROUNDUP(i + 1, 8) + W; - if ((p = realloc(p, z.n))) { - z.n = malloc_usable_size(p); - assert(!(z.n & (W - 1))); - *b = p; - } else { - return -1; + if (i != z.i || !p) { + n = ROUNDUP(i + 1, 8) + W; + if (n > z.n || bsrl(n) < bsrl(z.n)) { + if ((p = realloc(p, n))) { + n = malloc_usable_size(p); + assert(!(n & (W - 1))); + *b = p; + } else { + return -1; + } + } + if (i > z.i) { + memset(p + z.i, 0, i - z.i + 1); + } else { + p[i] = 0; + } + *(size_t *)(p + n - W) = + i | (!IsTiny() && W == 8 ? (size_t)APPEND_COOKIE << 48 : 0); } - if (i > z.i) { - memset(p, z.i, i - z.i); - } - z.i = i; - p[z.i] = 0; - if (!IsTiny() && W == 8) { - z.i |= (size_t)APPEND_COOKIE << 48; - } - *(size_t *)(p + z.n - W) = z.i; return i; } diff --git a/libc/stdio/appends.c b/libc/stdio/appends.c index e2fc22743..39755ffc0 100644 --- a/libc/stdio/appends.c +++ b/libc/stdio/appends.c @@ -31,6 +31,7 @@ * * @return bytes appended (always `strlen(s)`) or -1 if `ENOMEM` * @see appendz(b).i to get buffer length + * @note 2x faster than appendf() */ ssize_t appends(char **b, const char *s) { return appendd(b, s, strlen(s)); diff --git a/libc/stdio/appendw.c b/libc/stdio/appendw.c index 4ea9a1615..c85bf6b2b 100644 --- a/libc/stdio/appendw.c +++ b/libc/stdio/appendw.c @@ -16,18 +16,23 @@ │ 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/macros.internal.h" #include "libc/nexgen32e/bsr.h" #include "libc/stdio/append.internal.h" +#define W sizeof(size_t) + /** * Appends character or word-encoded string to buffer. * * Up to eight characters can be appended. For example: * * appendw(&s, 'h'|'i'<<8); + * appendw(&s, READ64LE("hi\0\0\0\0\0")); * - * Is equivalent to: + * Are equivalent to: * * appends(&s, "hi"); * @@ -37,15 +42,40 @@ * * Will append a single NUL character. * + * This function is slightly faster than appendd() and appends(). Its + * main advantage is it improves code size in functions that call it. + * * The resulting buffer is guaranteed to be NUL-terminated, i.e. * `!b[appendz(b).i]` will be the case. * * @return bytes appended or -1 if `ENOMEM` + * @see appendz(b).i to get buffer length */ ssize_t appendw(char **b, uint64_t w) { - char t[8]; - unsigned n = 1; - WRITE64LE(t, w); - if (w) n += bsrl(w) >> 3; - return appendd(b, t, n); + size_t n; + unsigned l; + char *p, *q; + struct appendz z; + z = appendz((p = *b)); + l = w ? (bsrl(w) >> 3) + 1 : 1; + n = ROUNDUP(z.i + 8 + 1, 8) + W; + if (n > z.n) { + if (!z.n) z.n = W * 2; + while (n > z.n) z.n += z.n >> 1; + z.n = ROUNDUP(z.n, W); + if ((p = realloc(p, z.n))) { + z.n = malloc_usable_size(p); + assert(!(z.n & (W - 1))); + *b = p; + } else { + return -1; + } + } + q = p + z.i; + WRITE64LE(q, w); + q[8] = 0; + z.i += l; + if (!IsTiny() && W == 8) z.i |= (size_t)APPEND_COOKIE << 48; + *(size_t *)(p + z.n - W) = z.i; + return l; } diff --git a/libc/stdio/appendz.c b/libc/stdio/appendz.c index 12133dd68..f56d0da8c 100644 --- a/libc/stdio/appendz.c +++ b/libc/stdio/appendz.c @@ -26,6 +26,7 @@ /** * Returns size of append buffer. * + * @param p must be 0 or have been allocated by an append*() function * @return i is number of bytes stored in buffer * @return n is number of bytes in allocation */ @@ -36,6 +37,12 @@ struct appendz appendz(char *p) { assert(z.n >= W * 2 && !(z.n & (W - 1))); z.i = *(size_t *)(p + z.n - W); if (!IsTiny() && W == 8) { + /* + * This check should fail if an append*() function was passed a + * pointer that was allocated manually by malloc(). Append ptrs + * can be free()'d safely, but they need to be allocated by the + * append library, because we write a special value to the end. + */ assert((z.i >> 48) == APPEND_COOKIE); z.i &= 0x0000ffffffffffff; } diff --git a/libc/stdio/dumphexc.c b/libc/stdio/dumphexc.c new file mode 100644 index 000000000..c359e10ca --- /dev/null +++ b/libc/stdio/dumphexc.c @@ -0,0 +1,52 @@ +/*-*- 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 2021 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/macros.internal.h" +#include "libc/stdio/append.internal.h" +#include "libc/stdio/hex.internal.h" +#include "libc/str/str.h" + +/** + * Turns data into "\x00..." string literal. + */ +char *DumpHexc(const char *p, size_t n, size_t *z) { + long o; + int i, m; + char A[128], *q, *s = 0; + if (n == -1) n = p ? strlen(p) : 0; + appendw(&s, '"' | '\\' << 8 | '\n' << 16); + for (o = 0; (m = MIN(16, n)); p += m, n -= m) { + q = A; + for (i = 0; i < m; ++i) { + *q++ = '\\'; + *q++ = 'x'; + *q++ = "0123456789abcdef"[(p[i] & 0xF0) >> 4]; + *q++ = "0123456789abcdef"[(p[i] & 0x0F) >> 0]; + } + if (o) appendw(&s, '\\' | '\n' << 8); + appendd(&s, A, q - A); + o += m; + } + if (appendw(&s, '"') != -1) { + if (z) *z = appendz(s).i; + return s; + } else { + free(s); + return 0; + } +} diff --git a/libc/stdio/hex.internal.h b/libc/stdio/hex.internal.h new file mode 100644 index 000000000..f4740417d --- /dev/null +++ b/libc/stdio/hex.internal.h @@ -0,0 +1,11 @@ +#ifndef COSMOPOLITAN_LIBC_STDIO_HEX_INTERNAL_H_ +#define COSMOPOLITAN_LIBC_STDIO_HEX_INTERNAL_H_ +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +char *DumpHex(const char *, size_t, size_t *); +char *DumpHexc(const char *, size_t, size_t *); + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_LIBC_STDIO_HEX_INTERNAL_H_ */ diff --git a/libc/stdio/stdio.mk b/libc/stdio/stdio.mk index ad5ae77ae..9c0b34f6d 100644 --- a/libc/stdio/stdio.mk +++ b/libc/stdio/stdio.mk @@ -57,7 +57,7 @@ o/$(MODE)/libc/stdio/fputc.o: \ o//libc/stdio/appendw.o: \ OVERRIDE_CFLAGS += \ - -O2 + -Os LIBC_STDIO_LIBS = $(foreach x,$(LIBC_STDIO_ARTIFACTS),$($(x))) LIBC_STDIO_SRCS = $(foreach x,$(LIBC_STDIO_ARTIFACTS),$($(x)_SRCS)) diff --git a/libc/stdio/vappendf.c b/libc/stdio/vappendf.c index 531f7ca86..139d6ffeb 100644 --- a/libc/stdio/vappendf.c +++ b/libc/stdio/vappendf.c @@ -30,14 +30,16 @@ ssize_t(vappendf)(char **b, const char *f, va_list v) { char *p; int r, s; + size_t n; va_list w; struct appendz z; z = appendz((p = *b)); va_copy(w, v); if ((r = (vsnprintf)(p + z.i, z.n ? z.n - W - z.i : 0, f, v)) >= 0) { - if (ROUNDUP(z.i + r + 1, 8) + W > z.n) { + n = ROUNDUP(z.i + r + 1, 8) + W; + if (n > z.n) { if (!z.n) z.n = W * 2; - while (ROUNDUP(z.i + r + 1, 8) + W > z.n) z.n += z.n >> 1; + while (n > z.n) z.n += z.n >> 1; z.n = ROUNDUP(z.n, W); if ((p = realloc(p, z.n))) { z.n = malloc_usable_size(p); diff --git a/libc/testlib/bench.h b/libc/testlib/bench.h index c64f8c9f8..e0664e195 100644 --- a/libc/testlib/bench.h +++ b/libc/testlib/bench.h @@ -1,27 +1,28 @@ #ifndef COSMOPOLITAN_LIBC_BENCH_H_ #define COSMOPOLITAN_LIBC_BENCH_H_ +#include "libc/bits/safemacros.internal.h" #include "libc/nexgen32e/bench.h" #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ /** - * @fileoverview Microbenchmarking tools. + * @fileoverview Microbenchmarking Toolz. */ #ifndef BENCHLOOP -#define BENCHLOOP(START, STOP, N, INIT, EXPR) \ - ({ \ - unsigned long Iter, Count; \ - double Average, Sample, Time1, Time2; \ - for (Average = 1, Iter = 1, Count = (N); Iter < Count; ++Iter) { \ - INIT; \ - Time1 = START(); \ - EXPR; \ - Time2 = STOP(); \ - Sample = Time2 - Time1; \ - Average += 1. / Iter * (Sample - Average); \ - } \ - Average; \ +#define BENCHLOOP(START, STOP, N, INIT, EXPR) \ + ({ \ + unsigned long Iter, Count; \ + uint64_t Time1, Time2; \ + double Average; \ + for (Average = 1, Iter = 1, Count = (N); Iter < Count; ++Iter) { \ + INIT; \ + Time1 = START(); \ + EXPR; \ + Time2 = STOP(); \ + Average += 1. / Iter * (unsignedsubtract(Time2, Time1) - Average); \ + } \ + Average; \ }) #endif /* BENCHLOOP */ diff --git a/test/libc/stdio/dumphexc_test.c b/test/libc/stdio/dumphexc_test.c new file mode 100644 index 000000000..d7bce6d58 --- /dev/null +++ b/test/libc/stdio/dumphexc_test.c @@ -0,0 +1,34 @@ +/*-*- 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 2021 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/stdio/hex.internal.h" +#include "libc/testlib/ezbench.h" +#include "libc/testlib/hyperion.h" +#include "libc/testlib/testlib.h" + +TEST(DumpHexc, test) { + EXPECT_STREQ("\"\\\n\ +\\x68\\x65\\x6c\\x6c\\x6f\\xe2\\x86\\x92\\x0a\\x01\\x02\\x74\\x68\\x65\\x65\\x72\\\n\ +\\x68\\x75\\x72\\x63\\x65\\x6f\\x61\\x68\\x72\\x63\\x75\\x6f\\x65\\x61\\x75\\x68\\\n\ +\\x63\\x72\"", + DumpHexc("hello→\n\1\2theerhurceoahrcuoeauhcr", -1, 0)); +} + +BENCH(DumpHexc, bench) { + EZBENCH2("dumphexc", donothing, free(DumpHexc(kHyperion, kHyperionSize, 0))); +} diff --git a/test/libc/stdio/vappendf_test.c b/test/libc/stdio/vappendf_test.c index e6632e62b..063ab43c4 100644 --- a/test/libc/stdio/vappendf_test.c +++ b/test/libc/stdio/vappendf_test.c @@ -16,6 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/bits/bits.h" #include "libc/stdio/append.internal.h" #include "libc/testlib/ezbench.h" #include "libc/testlib/testlib.h" @@ -87,8 +88,7 @@ TEST(appendw, test) { EXPECT_EQ(0, b[0]); EXPECT_EQ('h', b[1]); EXPECT_EQ(0, b[2]); - ASSERT_NE(-1, appendw(&b, 'e' | 'l' << 8 | 'l' << 16 | 'o' << 24 | - (uint64_t)'!' << 32)); + ASSERT_NE(-1, appendw(&b, READ64LE("ello!\0\0"))); EXPECT_EQ(7, appendz(b).i); EXPECT_EQ(0, b[0]); EXPECT_EQ('h', b[1]); @@ -101,7 +101,7 @@ TEST(appendw, test) { free(b); } -TEST(appendr, test) { +TEST(appendr, testShrink_nulTerminates) { char *b = 0; ASSERT_NE(-1, appends(&b, "hello")); EXPECT_EQ(5, appendz(b).i); @@ -121,15 +121,54 @@ TEST(appendr, test) { free(b); } +TEST(appendr, testExtend_zeroFills) { + char *b = 0; + ASSERT_NE(-1, appends(&b, "hello")); + EXPECT_EQ(5, appendz(b).i); + ASSERT_NE(-1, appendr(&b, 20)); + EXPECT_EQ(20, appendz(b).i); + ASSERT_BINEQ(u"hello                ", b); + ASSERT_NE(-1, appendr(&b, 20)); + EXPECT_EQ(20, appendz(b).i); + ASSERT_BINEQ(u"hello                ", b); + free(b); +} + +TEST(appendr, testAbsent_allocatesNul) { + char *b = 0; + ASSERT_NE(-1, appendr(&b, 0)); + EXPECT_EQ(0, appendz(b).i); + ASSERT_BINEQ(u" ", b); + free(b); +} + +TEST(appendd, testMemFail_doesntInitializePointer) { + char *b = 0; + ASSERT_EQ(-1, appendd(&b, 0, -1ull >> 8)); + EXPECT_EQ(0, b); +} + +TEST(appendd, testMemFail_doesntFreeExistingAllocation) { + char *b = 0; + ASSERT_NE(-1, appends(&b, "hello")); + EXPECT_STREQ("hello", b); + ASSERT_EQ(-1, appendd(&b, 0, -1ull >> 8)); + EXPECT_STREQ("hello", b); + free(b); +} + BENCH(vappendf, bench) { const char t[] = {0}; char *b = 0; - EZBENCH2("appendf", donothing, appendf(&b, "1")); + EZBENCH2("appendf", donothing, appendf(&b, "hello")); free(b), b = 0; - EZBENCH2("appends", donothing, appends(&b, "1")); + EZBENCH2("appends", donothing, appends(&b, "hello")); free(b), b = 0; - EZBENCH2("appendw", donothing, appendw(&b, 'B')); + EZBENCH2("appendw", donothing, + appendw(&b, 'h' | 'e' << 8 | 'l' << 16 | 'l' << 24 | + (uint64_t)'o' << 32)); free(b), b = 0; - EZBENCH2("appendd", donothing, appendd(&b, t, 1)); + EZBENCH2("appendd", donothing, appendd(&b, "hello", 5)); + EZBENCH2("appendr", donothing, appendr(&b, 0)); free(b), b = 0; } diff --git a/third_party/lua/lapi.c b/third_party/lua/lapi.c index 067f60a84..ce9ba51d1 100644 --- a/third_party/lua/lapi.c +++ b/third_party/lua/lapi.c @@ -18,7 +18,6 @@ #include "third_party/lua/lstate.h" #include "third_party/lua/lstring.h" #include "third_party/lua/ltable.h" -#include "third_party/lua/larray.h" #include "third_party/lua/ltm.h" #include "third_party/lua/lua.h" #include "third_party/lua/lundump.h" @@ -313,12 +312,6 @@ LUA_API int lua_isuserdata (lua_State *L, int idx) { } -LUA_API int lua_isarray (lua_State *L, int idx) { - const TValue *o = index2value(L, idx); - return (isvalid(L, o) && ttype(o) == LUA_TTABLE && hvalue(o)->truearray); -} - - LUA_API int lua_rawequal (lua_State *L, int index1, int index2) { const TValue *o1 = index2value(L, index1); const TValue *o2 = index2value(L, index2); @@ -766,41 +759,6 @@ LUA_API void lua_createtable (lua_State *L, int narray, int nrec) { } -LUA_API void lua_createarray (lua_State *L, int narray) { - Array *a; - lua_lock(L); - a = luaA_new(L); - setavalue2s(L, L->top, a); - api_incr_top(L); - if (narray > 0) - luaA_resize(L, a, narray); - luaC_checkGC(L); - lua_unlock(L); -} - - -LUA_API void lua_resize (lua_State *L, int idx, unsigned int size) { - TValue *o; - Table *t; - unsigned int i, oldsize; - lua_lock(L); - o = index2value(L, idx); - api_check(L, ttistable(o), "table expected"); - t = hvalue(o); - oldsize = t->sizeused; - if (size > luaH_realasize(t)) { - luaH_resizearray(L, t, size); - } - lua_unlock(L); - /* set removed elements to nil when shrinking array size */ - for(i = size + 1; i <= oldsize; i++) { - lua_pushnil(L); - lua_seti(L, idx, i); - } - t->sizeused = size; -} - - LUA_API int lua_getmetatable (lua_State *L, int objindex) { const TValue *obj; Table *mt; @@ -909,10 +867,7 @@ LUA_API void lua_seti (lua_State *L, int idx, lua_Integer n) { lua_lock(L); api_checknelems(L, 1); t = index2value(L, idx); - if (ttisarray(t)) { - luaA_setint(L, avalue(t), n, s2v(L->top - 1)); - } - else if (luaV_fastgeti(L, t, n, slot)) { + if (luaV_fastgeti(L, t, n, slot)) { luaV_finishfastset(L, t, slot, s2v(L->top - 1)); } else { diff --git a/third_party/lua/larray.c b/third_party/lua/larray.c deleted file mode 100644 index 42252823c..000000000 --- a/third_party/lua/larray.c +++ /dev/null @@ -1,107 +0,0 @@ -/* -** Lua arrays -** See Copyright Notice in lua.h -*/ - -asm(".ident\t\"\\n\\n\ -lua-array (MIT License)\\n\ -Copyright 2018-2021 Petri Häkkinen\""); -asm(".include \"libc/disclaimer.inc\""); - -#define larray_c -#define LUA_CORE - -#include "third_party/lua/lprefix.h" - -#include -#include - -#include "third_party/lua/lua.h" - -#include "third_party/lua/ldebug.h" -#include "third_party/lua/ldo.h" -#include "third_party/lua/lgc.h" -#include "third_party/lua/lmem.h" -#include "third_party/lua/lobject.h" -#include "third_party/lua/lstate.h" -#include "third_party/lua/lstring.h" -#include "third_party/lua/ltable.h" -#include "third_party/lua/larray.h" -#include "third_party/lua/lvm.h" - -Array *luaA_new (lua_State *L) { - GCObject *o = luaC_newobj(L, LUA_TARRAY, sizeof(Array)); - Array *a = gco2a(o); - a->array = NULL; - a->alimit = 0; - return a; -} - - -void luaA_resize (lua_State *L, Array *a, unsigned int newsize) { - unsigned int i; - unsigned int oldsize = a->alimit; - TValue *newarray; - - /* allocate new array */ - newarray = luaM_reallocvector(L, a->array, oldsize, newsize, TValue); - if (newarray == NULL && newsize > 0) { /* allocation failed? */ - luaM_error(L); /* raise error (with array unchanged) */ - } - - /* allocation ok; initialize new part of the array */ - a->array = newarray; /* set new array part */ - a->alimit = newsize; - for (i = oldsize; i < newsize; i++) /* clear new slice of the array */ - setempty(&a->array[i]); -} - - -void luaA_free (lua_State *L, Array *a) { - luaM_freearray(L, a->array, a->alimit); - luaM_free(L, a); -} - - -const TValue *luaA_getint (lua_State *L, Array *a, lua_Integer key) { - /* (1 <= key && key <= t->alimit) */ - if (l_castS2U(key) - 1u < a->alimit) - return &a->array[key - 1]; - else - return luaO_nilobject; -} - -const TValue *luaA_get (lua_State *L, Array *a, const TValue *key) { - if (ttypetag(key) == LUA_VNUMINT) { - lua_Integer ikey = ivalue(key); - if (l_castS2U(ikey) - 1u < a->alimit) - return &a->array[ikey - 1]; - else - return luaO_nilobject; - } else { - return luaO_nilobject; - } -} - -void luaA_setint (lua_State *L, Array *a, lua_Integer key, TValue *value) { - if (l_castS2U(key) - 1u < a->alimit) { - /* set value! */ - TValue* val = &a->array[key - 1]; - val->value_ = value->value_; - val->tt_ = value->tt_; - checkliveness(L,val); - } else { - /* TODO: this error message could be improved! */ - luaG_runerror(L, "array index out of bounds"); - } -} - -void luaA_set (lua_State *L, Array *a, const TValue* key, TValue *value) { - if (ttypetag(key) == LUA_VNUMINT) { - lua_Integer ikey = ivalue(key); - luaA_setint(L, a, ikey, value); - } else { - /* TODO: the error message could be improved */ - luaG_runerror(L, "attempt to index array with a non-integer value"); - } -} diff --git a/third_party/lua/larray.h b/third_party/lua/larray.h deleted file mode 100644 index f8ebfc0a3..000000000 --- a/third_party/lua/larray.h +++ /dev/null @@ -1,22 +0,0 @@ -/* -** Lua arrays -** See Copyright Notice in lua.h -*/ - -#ifndef larray_h -#define larray_h - -#include "third_party/lua/lobject.h" - -#define luaO_nilobject (&G(L)->nilvalue) - -LUAI_FUNC Array *luaA_new (lua_State *L); -LUAI_FUNC void luaA_resize (lua_State *L, Array *a, unsigned int nsize); -LUAI_FUNC void luaA_free (lua_State *L, Array *a); - -LUAI_FUNC const TValue *luaA_getint (lua_State *L, Array *a, lua_Integer key); -LUAI_FUNC void luaA_setint (lua_State *L, Array *a, lua_Integer key, TValue *value); -LUAI_FUNC const TValue *luaA_get (lua_State *L, Array *a, const TValue *key); -LUAI_FUNC void luaA_set (lua_State *L, Array *a, const TValue *key, TValue *value); - -#endif diff --git a/third_party/lua/lbaselib.c b/third_party/lua/lbaselib.c index e9a5dcd41..25f37e902 100644 --- a/third_party/lua/lbaselib.c +++ b/third_party/lua/lbaselib.c @@ -254,12 +254,9 @@ static int luaB_next (lua_State *L) { } } -static int luaB_ipairs (lua_State *L); static int luaB_pairs (lua_State *L) { luaL_checkany(L, 1); - if (lua_isarray(L, 1)) - return luaB_ipairs(L); if (luaL_getmetafield(L, 1, "__pairs") == LUA_TNIL) { /* no metamethod? */ lua_pushcfunction(L, luaB_next); /* will return generator, */ lua_pushvalue(L, 1); /* state, */ @@ -282,16 +279,6 @@ static int ipairsaux (lua_State *L) { return (lua_geti(L, 1, i) == LUA_TNIL) ? 1 : 2; } -/* -** Traversal function for 'ipairs' specialized for arrays. -*/ -static int ipairsauxarray (lua_State *L) { - lua_Integer i = luaL_checkinteger(L, 2) + 1; - lua_pushinteger(L, i); - lua_geti(L, 1, i); - return (lua_rawlen(L, 1) == i-1) ? 1 : 2; -} - /* ** 'ipairs' function. Returns 'ipairsaux', given "table", 0. @@ -299,7 +286,7 @@ static int ipairsauxarray (lua_State *L) { */ static int luaB_ipairs (lua_State *L) { luaL_checkany(L, 1); - lua_pushcfunction(L, lua_isarray(L, 1) ? ipairsauxarray : ipairsaux); /* iteration function */ + lua_pushcfunction(L, ipairsaux); /* iteration function */ lua_pushvalue(L, 1); /* state */ lua_pushinteger(L, 0); /* initial value */ return 3; diff --git a/third_party/lua/ldebug.c b/third_party/lua/ldebug.c index 1657922d9..1f2c0785c 100644 --- a/third_party/lua/ldebug.c +++ b/third_party/lua/ldebug.c @@ -688,7 +688,7 @@ static const char *varinfo (lua_State *L, const TValue *o) { l_noret luaG_typeerror (lua_State *L, const TValue *o, const char *op) { const char *t = luaT_objtypename(L, o); - luaG_runerror(L, "attempt to %s a%s %s value%s", op, ttisarray(o) ? "n" : "", t, varinfo(L, o)); + luaG_runerror(L, "attempt to %s a %s value%s", op, t, varinfo(L, o)); } diff --git a/third_party/lua/lgc.c b/third_party/lua/lgc.c index 310c87133..da7894b72 100644 --- a/third_party/lua/lgc.c +++ b/third_party/lua/lgc.c @@ -17,7 +17,6 @@ #include "third_party/lua/lstate.h" #include "third_party/lua/lstring.h" #include "third_party/lua/ltable.h" -#include "third_party/lua/larray.h" #include "third_party/lua/ltm.h" #include "third_party/lua/lua.h" @@ -778,9 +777,6 @@ static void freeobj (lua_State *L, GCObject *o) { case LUA_VTABLE: luaH_free(L, gco2t(o)); break; - case LUA_TARRAY: - luaA_free(L, gco2a(o)); - break; case LUA_VTHREAD: luaE_freethread(L, gco2th(o)); break; diff --git a/third_party/lua/lobject.h b/third_party/lua/lobject.h index 717a2e563..33dbba4fb 100644 --- a/third_party/lua/lobject.h +++ b/third_party/lua/lobject.h @@ -722,8 +722,6 @@ typedef struct Table { lu_byte flags; /* 1<

'{' [ field { sep field } [sep] ] '}' sep -> ',' | ';' */ - - /* special case for arrays (when 'array' is true): - constructor -> '[' [ listfield { sep listfield } [sep] ] ']' - sep -> ',' | ';' */ - FuncState *fs = ls->fs; int line = ls->linenumber; int pc = luaK_codeABC(fs, OP_NEWTABLE, 0, 0, 0); @@ -923,32 +918,18 @@ static void constructor (LexState *ls, expdesc *t, int array) { init_exp(t, VNONRELOC, fs->freereg); /* table will be at stack top */ luaK_reserveregs(fs, 1); init_exp(&cc.v, VVOID, 0); /* no value (yet) */ - luaK_exp2nextreg(ls->fs, t); /* fix it at stack top */ - checknext(ls, (array ? '[' : '{')); + checknext(ls, '{'); do { lua_assert(cc.v.k == VVOID || cc.tostore > 0); - if (ls->t.token == (array ? ']' : '}')) break; + if (ls->t.token == '}') break; closelistfield(fs, &cc); - if (array) - listfield(ls, &cc); - else - field(ls, &cc); + field(ls, &cc); } while (testnext(ls, ',') || testnext(ls, ';')); - check_match(ls, array ? ']' : '}', array ? '[' : '{', line); + check_match(ls, '}', '{', line); lastlistfield(fs, &cc); luaK_settablesize(fs, pc, t->u.info, cc.na, cc.nh); - /* encode arrayness by setting B to max value (255) */ - if (array) { - /* make sure B is not already 255 */ - /* I don't this can happen in practice (max size is luaO_fb2int(255) = 3221225472), but let's be sure... */ - unsigned int b = GETARG_B(fs->f->code[pc]); - if (b == 255) - luaX_syntaxerror(fs->ls, "table too large"); - SETARG_B(fs->f->code[pc], 255); - } } - /* }====================================================================== */ @@ -1042,7 +1023,7 @@ static void funcargs (LexState *ls, expdesc *f, int line) { break; } case '{': { /* funcargs -> constructor */ - constructor(ls, &args, 0); + constructor(ls, &args); break; } case TK_STRING: { /* funcargs -> STRING */ @@ -1177,11 +1158,7 @@ static void simpleexp (LexState *ls, expdesc *v) { break; } case '{': { /* constructor */ - constructor(ls, v, 0); - return; - } - case '[': { /* array constructor */ - constructor(ls, v, 1); + constructor(ls, v); return; } case TK_FUNCTION: { diff --git a/third_party/lua/lstate.h b/third_party/lua/lstate.h index 04b444894..7b5aed28d 100644 --- a/third_party/lua/lstate.h +++ b/third_party/lua/lstate.h @@ -351,7 +351,6 @@ union GCUnion { struct Udata u; union Closure cl; struct Table h; - struct Array a; struct Proto p; struct lua_State th; /* thread */ struct UpVal upv; @@ -374,7 +373,6 @@ union GCUnion { #define gco2cl(o) \ check_exp(novariant((o)->tt) == LUA_TFUNCTION, &((cast_u(o))->cl)) #define gco2t(o) check_exp((o)->tt == LUA_VTABLE, &((cast_u(o))->h)) -#define gco2a(o) check_exp((o)->tt == LUA_TARRAY, &((cast_u(o))->a)) #define gco2p(o) check_exp((o)->tt == LUA_VPROTO, &((cast_u(o))->p)) #define gco2th(o) check_exp((o)->tt == LUA_VTHREAD, &((cast_u(o))->th)) #define gco2upv(o) check_exp((o)->tt == LUA_VUPVAL, &((cast_u(o))->upv)) diff --git a/third_party/lua/ltable.c b/third_party/lua/ltable.c index 29f642026..4d021a141 100644 --- a/third_party/lua/ltable.c +++ b/third_party/lua/ltable.c @@ -565,7 +565,7 @@ void luaH_resize (lua_State *L, Table *t, unsigned int newasize, t->array = newarray; /* set new array part */ t->alimit = newasize; for (i = oldasize; i < newasize; i++) /* clear new slice of the array */ - setempty(&t->array[i]); + setempty(&t->array[i]); /* re-insert elements from old hash part into new parts */ reinsert(L, &newt, t); /* 'newt' now has the old hash */ freehash(L, &newt); /* free old hash part */ @@ -613,10 +613,8 @@ Table *luaH_new (lua_State *L) { Table *t = gco2t(o); t->metatable = NULL; t->flags = cast_byte(maskflags); /* table has no metamethod fields */ - t->truearray = 0; t->array = NULL; t->alimit = 0; - t->sizeused = 0; setnodevector(L, t, 0); return t; } @@ -654,25 +652,7 @@ void luaH_newkey (lua_State *L, Table *t, const TValue *key, TValue *value) { TValue aux; if (l_unlikely(ttisnil(key))) luaG_runerror(L, "table index is nil"); - else if (t->truearray) { - /* set new value to true array */ - int capacity; - int asize = luaH_realasize(t); - int idx = ivalue(key); /* TODO: does not handle numbers larger than fits into a 32-bit signed integer! */ - if (!ttisinteger(key) || idx < 1) - luaG_runerror(L, "invalid array index"); - /* enlarge capacity */ - if (asize < idx) { - capacity = asize + (asize >> 1); - if (capacity < idx) - capacity = idx; - luaH_resizearray(L, t, capacity); - } - t->sizeused = idx; // since this is guaranteed to be a new key, it exceeds t->sizeused - luaC_barrierback(L, obj2gco(t), key); - setobj2t(L, cast(TValue *, t->array + idx - 1), value); - return; - } else if (ttisfloat(key)) { + else if (ttisfloat(key)) { lua_Number f = fltvalue(key); lua_Integer k; if (luaV_flttointeger(f, &k, F2Ieq)) { /* does key fit in an integer? */ @@ -927,8 +907,6 @@ static unsigned int binsearch (const TValue *array, unsigned int i, */ lua_Unsigned luaH_getn (Table *t) { unsigned int limit = t->alimit; - if (t->truearray) - return t->sizeused; if (limit > 0 && isempty(&t->array[limit - 1])) { /* (1)? */ /* there must be a boundary before 'limit' */ if (limit >= 2 && !isempty(&t->array[limit - 2])) { diff --git a/third_party/lua/ltablib.c b/third_party/lua/ltablib.c index 76ef39665..553f2a7e6 100644 --- a/third_party/lua/ltablib.c +++ b/third_party/lua/ltablib.c @@ -401,18 +401,6 @@ static int sort (lua_State *L) { return 0; } - -static int resize (lua_State *L) { - /* reserve capacity of the array part -- useful when filling large tables/arrays */ - int size; - luaL_checktype(L, 1, LUA_TTABLE); - size = luaL_checkinteger(L, 2); - luaL_argcheck(L, size >= 0, 2, "invalid size"); - lua_resize(L, 1, (unsigned int)size); - return 0; -} - - /* }====================================================== */ @@ -424,7 +412,6 @@ static const luaL_Reg tab_funcs[] = { {"remove", tremove}, {"move", tmove}, {"sort", sort}, - {"resize", resize}, {NULL, NULL} }; diff --git a/third_party/lua/ltm.c b/third_party/lua/ltm.c index 45849eb35..75f67c5f6 100644 --- a/third_party/lua/ltm.c +++ b/third_party/lua/ltm.c @@ -26,7 +26,7 @@ static const char udatatypename[] = "userdata"; LUAI_DDEF const char *const luaT_typenames_[LUA_TOTALTYPES] = { "no value", "nil", "boolean", udatatypename, "number", - "string", "table", "function", udatatypename, "thread", "array", + "string", "table", "function", udatatypename, "thread", "upvalue", "proto" /* these last cases are used for tests only */ }; @@ -264,3 +264,4 @@ void luaT_getvarargs (lua_State *L, CallInfo *ci, StkId where, int wanted) { for (; i < wanted; i++) /* complete required results with nil */ setnilvalue(s2v(where + i)); } + diff --git a/third_party/lua/lua.h b/third_party/lua/lua.h index f13f63f57..4e9b825f2 100644 --- a/third_party/lua/lua.h +++ b/third_party/lua/lua.h @@ -60,8 +60,8 @@ typedef struct lua_State lua_State; #define LUA_TFUNCTION 6 #define LUA_TUSERDATA 7 #define LUA_TTHREAD 8 -#define LUA_TARRAY 9 -#define LUA_NUMTYPES 10 + +#define LUA_NUMTYPES 9 @@ -173,7 +173,6 @@ LUA_API int (lua_isstring) (lua_State *L, int idx); LUA_API int (lua_iscfunction) (lua_State *L, int idx); LUA_API int (lua_isinteger) (lua_State *L, int idx); LUA_API int (lua_isuserdata) (lua_State *L, int idx); -LUA_API int (lua_isarray) (lua_State *L, int idx); LUA_API int (lua_type) (lua_State *L, int idx); LUA_API const char *(lua_typename) (lua_State *L, int tp); @@ -246,8 +245,6 @@ LUA_API int (lua_rawgeti) (lua_State *L, int idx, lua_Integer n); LUA_API int (lua_rawgetp) (lua_State *L, int idx, const void *p); LUA_API void (lua_createtable) (lua_State *L, int narr, int nrec); -LUA_API void (lua_createarray) (lua_State *L, int narr); -LUA_API void (lua_resize) (lua_State *L, int idx, unsigned int size); LUA_API void *(lua_newuserdatauv) (lua_State *L, size_t sz, int nuvalue); LUA_API int (lua_getmetatable) (lua_State *L, int objindex); LUA_API int (lua_getiuservalue) (lua_State *L, int idx, int n); diff --git a/third_party/lua/lvm.c b/third_party/lua/lvm.c index 069275b2c..7780b974a 100644 --- a/third_party/lua/lvm.c +++ b/third_party/lua/lvm.c @@ -17,7 +17,6 @@ #include "third_party/lua/lstate.h" #include "third_party/lua/lstring.h" #include "third_party/lua/ltable.h" -#include "third_party/lua/larray.h" #include "third_party/lua/ltm.h" #include "third_party/lua/lua.h" #include "third_party/lua/lvm.h" @@ -284,11 +283,6 @@ void luaV_finishget (lua_State *L, const TValue *t, TValue *key, StkId val, for (loop = 0; loop < MAXTAGLOOP; loop++) { if (slot == NULL) { /* 't' is not a table? */ lua_assert(!ttistable(t)); - if (ttisarray(t)) { - /* get array value */ - setobj2s(L, val, luaA_get(L, avalue(t), key)); - return; - } tm = luaT_gettmbyobj(L, t, TM_INDEX); if (l_unlikely(notm(tm))) luaG_typeerror(L, t, "index"); /* no metamethod */ @@ -337,17 +331,12 @@ void luaV_finishset (lua_State *L, const TValue *t, TValue *key, if (tm == NULL) { /* no metamethod? */ luaH_finishset(L, h, key, slot, val); /* set new value */ invalidateTMcache(h); - /* enlarge array length when necessary */ - /* this should be quite fast as fields of h and key have already been loaded to CPU cache at this point */ - h->sizeused += (val_(key).i > h->sizeused) & ttisinteger(key) & 1; luaC_barrierback(L, obj2gco(h), val); return; } /* else will try the metamethod */ } else { /* not a table; check metamethod */ - if(ttisarray(t)) - luaG_typeerror(L, t, "set non-integer index of"); tm = luaT_gettmbyobj(L, t, TM_NEWINDEX); if (l_unlikely(notm(tm))) luaG_typeerror(L, t, "index"); @@ -693,11 +682,6 @@ void luaV_objlen (lua_State *L, StkId ra, const TValue *rb) { setivalue(s2v(ra), luaH_getn(h)); /* else primitive len */ return; } - case LUA_TARRAY: { - Array *a = avalue(rb); - setivalue(s2v(ra), a->alimit); - return; - } case LUA_VSHRSTR: { setivalue(s2v(ra), tsvalue(rb)->shrlen); return; @@ -1257,10 +1241,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) { const TValue *slot; TValue *rb = vRB(i); int c = GETARG_C(i); - if(ttisarray(rb)) { - setobj2s(L, ra, luaA_getint(L, avalue(rb), c)); - } - else if (luaV_fastgeti(L, rb, c, slot)) { + if (luaV_fastgeti(L, rb, c, slot)) { setobj2s(L, ra, slot); } else { @@ -1313,10 +1294,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) { const TValue *slot; int c = GETARG_B(i); TValue *rc = RKC(i); - if(ttisarray(s2v(ra))) { - luaA_setint(L, avalue(s2v(ra)), c, rc); - } - else if (luaV_fastgeti(L, s2v(ra), c, slot)) { + if (luaV_fastgeti(L, s2v(ra), c, slot)) { luaV_finishfastset(L, s2v(ra), slot, rc); } else { @@ -1341,8 +1319,6 @@ void luaV_execute (lua_State *L, CallInfo *ci) { vmcase(OP_NEWTABLE) { int b = GETARG_B(i); /* log2(hash size) + 1 */ int c = GETARG_C(i); /* array size */ - /* decode arrayness */ - int array = (b == 255); Table *t; if (b > 0) b = 1 << (b - 1); /* size is 2^(b - 1) */ @@ -1352,16 +1328,9 @@ void luaV_execute (lua_State *L, CallInfo *ci) { pc++; /* skip extra argument */ L->top = ra + 1; /* correct top in case of emergency GC */ t = luaH_new(L); /* memory allocation */ - t->truearray = array; - t->sizeused = c; sethvalue2s(L, ra, t); - if (b != 0 || c != 0) { - if (array) { - luaH_resizearray(L, t, c); /* idem */ - } else { - luaH_resize(L, t, c, b); /* idem */ - } - } + if (b != 0 || c != 0) + luaH_resize(L, t, c, b); /* idem */ checkGC(L, ra + 1); vmbreak; } diff --git a/third_party/lua/lvm.h b/third_party/lua/lvm.h index f07eacad4..8fd25ed22 100644 --- a/third_party/lua/lvm.h +++ b/third_party/lua/lvm.h @@ -88,13 +88,6 @@ typedef enum { : (slot = f(hvalue(t), k), /* else, do raw access */ \ !isempty(slot))) /* result not empty? */ -/* This one supports arrays as well as tables. */ -#define luaV_fastget2(L,t,k,slot,f) \ - (ttisarray(t) ? (slot = f(avalue(t), k), !isempty(slot)) : \ - (!ttistable(t) \ - ? (slot = NULL, 0) /* not a table; 'slot' is NULL and result is 0 */ \ - : (slot = f(hvalue(t), k), /* else, do raw access */ \ - !isempty(slot)))) /* result not empty? */ /* ** Special case of 'luaV_fastget' for integers, inlining the fast case @@ -107,14 +100,6 @@ typedef enum { ? &hvalue(t)->array[k - 1] : luaH_getint(hvalue(t), k), \ !isempty(slot))) /* result not empty? */ -/* This one supports arrays as well as tables. */ -#define luaV_fastgeti2(L,t,k,slot) \ - (ttisarray(t) ? (slot = (l_castS2U(k) - 1u < avalue(t)->sizearray) ? &avalue(t)->array[k - 1] : luaO_nilobject, !isempty(slot)) : \ - (!ttistable(t) \ - ? (slot = NULL, 0) /* not a table; 'slot' is NULL and result is 0 */ \ - : (slot = (l_castS2U(k) - 1u < hvalue(t)->sizearray) \ - ? &hvalue(t)->array[k - 1] : luaH_getint(hvalue(t), k), \ - !isempty(slot)))) /* result not empty? */ /* ** Finish a fast set operation (when fast get succeeds). In that case, diff --git a/third_party/lua/test/arrays.lua b/third_party/lua/test/arrays.lua deleted file mode 100644 index 27517787a..000000000 --- a/third_party/lua/test/arrays.lua +++ /dev/null @@ -1,211 +0,0 @@ -local errors = false - -function assert(cond) - if not cond then - local line = debug.getinfo(2).currentline - print("test failed at line " .. line) - errors = true - end -end - --- test array constructor -do - local a = [1, 2, 3] - assert(a[1] == 1) - assert(a[2] == 2) - assert(a[3] == 3) - assert(#a == 3) -end - --- test nested array constructor -do - local a = [1, 2, 3, [4, 5]] - assert(a[1] == 1) - assert(a[2] == 2) - assert(a[3] == 3) - assert(a[4][1] == 4) - assert(a[4][2] == 5) - assert(#a == 4) -end - --- test array write -do - local a = [1, 2, 3] - a[1] = -1 - assert(a[1] == -1) - assert(#a == 3) -end - --- test array extend -do - local a = [1, 2, 3] - a[7] = 5 - assert(#a == 7) - assert(a[7] == 5) -end - --- test array extend 2 -do - local a = [] - for i=5,15 do - a[i] = i - assert(a[i] == i) - assert(#a == i) - end -end - --- test setting element to nil (should not affect array size) -do - local a = [1, 2, 3] - a[3] = nil - assert(a[3] == nil) - assert(#a == 3) -end - --- test array with holes -do - local a = [1, nil, 3] - assert(a[1] == 1) - assert(a[2] == nil) - assert(a[3] == 3) - assert(#a == 3) - a[1] = nil - assert(#a == 3) -end - --- test filling hole in array -do - local a = [1, nil, 3] - a[2] = 2 - assert(a[2] == 2) - assert(#a == 3) -end - --- test filling hole in array 2 -do - local a = [1, nil, 3] - local i = 2 - a[i] = 2 - assert(a[2] == 2) - assert(#a == 3) -end - --- test read out of bounds -do - local a = [1, 2, 3] - assert(#a == 3) - assert(a[0] == nil) - assert(a[4] == nil) - assert(#a == 3) -end - --- test array resize (array growing) -do - local a = [1, 2, 3] - table.resize(a, 1000) - assert(a[4] == nil) - assert(#a == 1000) - a[1] = 4 - a[10] = 10 - a[11] = 11 - assert(#a == 1000) -end - --- test array resize (array shrinking) -do - local a = [1, 2, 3, 4, 5] - table.resize(a, 3) - assert(a[1] == 1) - assert(a[2] == 2) - assert(a[3] == 3) - assert(a[4] == nil) - assert(a[5] == nil) - assert(#a == 3) -end - --- test non-const integer -do - local a = [] - local y = 3 - a[y] = 66 - assert(a[3] == 66) - assert(#a == 3) -end - --- test table.insert() -do - local a = [1, 2, 3] - table.insert(a, 1, "new") - assert(a[1] == "new") - assert(a[2] == 1) - assert(a[3] == 2) - assert(a[4] == 3) - assert(#a == 4) -end - --- test table.remove() -do - local a = [1, 2, 3] - table.remove(a, 1) - assert(a[1] == 2) - assert(a[2] == 3) - -- TODO: fix the implementation, as after upgrading to Lua 5.4.3 - -- it keeps the array size the same after table.remove() - -- assert(#a == 2) -end - --- test ipairs --- expected behavior: equivalent to for i=1,#a do print(i, a[i]) end -do - local a = [1, nil, 3, nil] - local cnt = 0 - for k,v in ipairs(a) do - assert(v == a[k]) - cnt = cnt + 1 - end - assert(cnt == #a) -end - --- test pairs --- expected behavior: same as ipairs? -do - local a = [1, nil, 3] - local cnt = 0 - for k,v in pairs(a) do - assert(v == a[k]) - cnt = cnt + 1 - end - assert(cnt == 3) -end - --- test normal insert/remove operations -local function test (a) - assert(not pcall(table.insert, a, 2, 20)); - table.insert(a, 10); - table.insert(a, 2, 20); - table.insert(a, 1, -1); - table.insert(a, 40); - table.insert(a, #a+1, 50) - table.insert(a, 2, -2) - assert(a[2] ~= undef) - assert(a["2"] == undef) - assert(not pcall(table.insert, a, 0, 20)); - assert(not pcall(table.insert, a, #a + 2, 20)); - assert(table.remove(a,1) == -1) - assert(table.remove(a,1) == -2) - assert(table.remove(a,1) == 10) - assert(table.remove(a,1) == 20) - assert(table.remove(a,1) == 40) - assert(table.remove(a,1) == 50) - assert(table.remove(a,1) == nil) - assert(table.remove(a) == nil) - assert(table.remove(a, #a) == nil) -end - -a = {n=0, [-7] = "ban"} -test(a) -assert(a.n == 0 and a[-7] == "ban") - -if not errors then - print("All tests passed!") -end diff --git a/third_party/mbedtls/config.h b/third_party/mbedtls/config.h index f7242ab26..a6aa5cb66 100644 --- a/third_party/mbedtls/config.h +++ b/third_party/mbedtls/config.h @@ -64,6 +64,7 @@ /* key exchange */ #define MBEDTLS_RSA_C #define MBEDTLS_KEY_EXCHANGE_RSA_ENABLED +#define MBEDTLS_KEY_EXCHANGE_PSK_ENABLED #ifndef TINY #define MBEDTLS_ECP_C #define MBEDTLS_ECDH_C @@ -72,13 +73,12 @@ #define MBEDTLS_ECDH_VARIANT_EVEREST_ENABLED #define MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED #define MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED +#define MBEDTLS_KEY_EXCHANGE_ECDHE_PSK_ENABLED /*#define MBEDTLS_DHM_C*/ /*#define MBEDTLS_KEY_EXCHANGE_DHE_RSA_ENABLED*/ /*#define MBEDTLS_KEY_EXCHANGE_ECDH_RSA_ENABLED*/ /*#define MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA_ENABLED*/ -/*#define MBEDTLS_KEY_EXCHANGE_PSK_ENABLED*/ /*#define MBEDTLS_KEY_EXCHANGE_RSA_PSK_ENABLED*/ -/*#define MBEDTLS_KEY_EXCHANGE_ECDHE_PSK_ENABLED*/ /*#define MBEDTLS_KEY_EXCHANGE_DHE_PSK_ENABLED*/ #endif diff --git a/third_party/mbedtls/dhm.c b/third_party/mbedtls/dhm.c index 6574cfcf4..d91d9e01b 100644 --- a/third_party/mbedtls/dhm.c +++ b/third_party/mbedtls/dhm.c @@ -17,6 +17,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" #include "third_party/mbedtls/asn1.h" +#include "third_party/mbedtls/bignum.h" #include "third_party/mbedtls/common.h" #include "third_party/mbedtls/dhm.h" #include "third_party/mbedtls/error.h" @@ -322,7 +323,7 @@ static int dhm_random_below( mbedtls_mpi *R, const mbedtls_mpi *M, MBEDTLS_MPI_CHK( mbedtls_mpi_fill_random( R, mbedtls_mpi_size( M ), f_rng, p_rng ) ); while( mbedtls_mpi_cmp_mpi( R, M ) >= 0 ) - mbedtls_mpi_shift_r( &R, 1 ); + mbedtls_mpi_shift_r( R, 1 ); if( count++ > 10 ) return( MBEDTLS_ERR_MPI_NOT_ACCEPTABLE ); diff --git a/third_party/mbedtls/ssl.h b/third_party/mbedtls/ssl.h index 6d9ae60a3..1c0ac6e34 100644 --- a/third_party/mbedtls/ssl.h +++ b/third_party/mbedtls/ssl.h @@ -1419,7 +1419,7 @@ int mbedtls_ssl_conf_dh_param_ctx( mbedtls_ssl_config *, mbedtls_dhm_context * ) int mbedtls_ssl_conf_dtls_srtp_protection_profiles( mbedtls_ssl_config *, const mbedtls_ssl_srtp_profile * ); int mbedtls_ssl_conf_max_frag_len( mbedtls_ssl_config *, unsigned char ); int mbedtls_ssl_conf_own_cert( mbedtls_ssl_config *, mbedtls_x509_crt *, mbedtls_pk_context * ); -int mbedtls_ssl_conf_psk( mbedtls_ssl_config *, const unsigned char *, size_t, const unsigned char *, size_t ); +int mbedtls_ssl_conf_psk( mbedtls_ssl_config *, const void *, size_t, const void *, size_t ); int mbedtls_ssl_context_load( mbedtls_ssl_context *, const unsigned char *, size_t ); int mbedtls_ssl_context_save( mbedtls_ssl_context *, unsigned char *, size_t, size_t * ); int mbedtls_ssl_get_ciphersuite_id( const char * ); diff --git a/third_party/mbedtls/ssl_ciphersuites.c b/third_party/mbedtls/ssl_ciphersuites.c index 3f3ecbe5d..7a949bdaf 100644 --- a/third_party/mbedtls/ssl_ciphersuites.c +++ b/third_party/mbedtls/ssl_ciphersuites.c @@ -72,16 +72,19 @@ static const uint16_t ciphersuite_preference[] = #endif #ifdef MBEDTLS_KEY_EXCHANGE_SOME_PSK_ENABLED - MBEDTLS_TLS_DHE_PSK_WITH_AES_256_GCM_SHA384, - MBEDTLS_TLS_DHE_PSK_WITH_AES_128_GCM_SHA256, + MBEDTLS_TLS_ECDHE_PSK_WITH_AES_256_GCM_SHA384, + MBEDTLS_TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256, + MBEDTLS_TLS_ECDHE_PSK_WITH_AES_128_CCM_SHA256, MBEDTLS_TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256, MBEDTLS_TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256, - MBEDTLS_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256, + MBEDTLS_TLS_DHE_PSK_WITH_AES_256_GCM_SHA384, + MBEDTLS_TLS_DHE_PSK_WITH_AES_128_GCM_SHA256, MBEDTLS_TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384, + MBEDTLS_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256, MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CBC_SHA384, MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CBC_SHA256, - MBEDTLS_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA, MBEDTLS_TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA, + MBEDTLS_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA, MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CBC_SHA, MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CBC_SHA, #endif @@ -165,17 +168,25 @@ static const uint16_t ciphersuite_preference[] = static const mbedtls_ssl_ciphersuite_t ciphersuite_definitions[] = { -#if defined(MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED) && defined(MBEDTLS_AES_C) && defined(MBEDTLS_GCM_C) && defined(MBEDTLS_SHA256_C) - { MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, "ECDHE-ECDSA-AES128-GCM-SHA256", - MBEDTLS_CIPHER_AES_128_GCM, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA, +#if defined(MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED) && defined(MBEDTLS_AES_C) && defined(MBEDTLS_GCM_C) && defined(MBEDTLS_SHA512_C) && !defined(MBEDTLS_SHA512_NO_SHA384) + { MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, "ECDHE-ECDSA-AES256-GCM-SHA384", + MBEDTLS_CIPHER_AES_256_GCM, MBEDTLS_MD_SHA384, MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA, MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3, MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3, 0 }, #endif -#if defined(MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED) && defined(MBEDTLS_AES_C) && defined(MBEDTLS_GCM_C) && defined(MBEDTLS_SHA512_C) - { MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, "ECDHE-ECDSA-AES256-GCM-SHA384", - MBEDTLS_CIPHER_AES_256_GCM, MBEDTLS_MD_SHA384, MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA, +#if defined(MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED) && defined(MBEDTLS_AES_C) && defined(MBEDTLS_GCM_C) && defined(MBEDTLS_SHA512_C) && !defined(MBEDTLS_SHA512_NO_SHA384) + { MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, "ECDHE-RSA-AES256-GCM-SHA384", + MBEDTLS_CIPHER_AES_256_GCM, MBEDTLS_MD_SHA384, MBEDTLS_KEY_EXCHANGE_ECDHE_RSA, + MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3, + MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3, + 0 }, +#endif + +#if defined(MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED) && defined(MBEDTLS_AES_C) && defined(MBEDTLS_GCM_C) && defined(MBEDTLS_SHA256_C) + { MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, "ECDHE-ECDSA-AES128-GCM-SHA256", + MBEDTLS_CIPHER_AES_128_GCM, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA, MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3, MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3, 0 }, @@ -189,9 +200,9 @@ static const mbedtls_ssl_ciphersuite_t ciphersuite_definitions[] = 0 }, #endif -#if defined(MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED) && defined(MBEDTLS_AES_C) && defined(MBEDTLS_GCM_C) && defined(MBEDTLS_SHA512_C) - { MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, "ECDHE-RSA-AES256-GCM-SHA384", - MBEDTLS_CIPHER_AES_256_GCM, MBEDTLS_MD_SHA384, MBEDTLS_KEY_EXCHANGE_ECDHE_RSA, +#if defined(MBEDTLS_KEY_EXCHANGE_PSK_ENABLED) && defined(MBEDTLS_KEY_EXCHANGE_ECDHE_PSK_ENABLED) && defined(MBEDTLS_AES_C) && defined(MBEDTLS_GCM_C) && defined(MBEDTLS_SHA512_C) && !defined(MBEDTLS_SHA512_NO_SHA384) + { MBEDTLS_TLS_ECDHE_PSK_WITH_AES_256_GCM_SHA384, "ECDHE-PSK-AES256-GCM-SHA384", + MBEDTLS_CIPHER_AES_256_GCM, MBEDTLS_MD_SHA384, MBEDTLS_KEY_EXCHANGE_ECDHE_PSK, MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3, MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3, 0 }, @@ -267,6 +278,22 @@ static const mbedtls_ssl_ciphersuite_t ciphersuite_definitions[] = MBEDTLS_SHA256_C && MBEDTLS_SSL_PROTO_TLS1_2 */ +#if defined(MBEDTLS_KEY_EXCHANGE_PSK_ENABLED) && defined(MBEDTLS_KEY_EXCHANGE_ECDHE_PSK_ENABLED) && defined(MBEDTLS_AES_C) && defined(MBEDTLS_GCM_C) && defined(MBEDTLS_SHA512_C) + { MBEDTLS_TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256, "ECDHE-PSK-AES128-GCM-SHA256", + MBEDTLS_CIPHER_AES_128_GCM, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_ECDHE_PSK, + MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3, + MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3, + 0 }, +#endif + +#if defined(MBEDTLS_KEY_EXCHANGE_PSK_ENABLED) && defined(MBEDTLS_KEY_EXCHANGE_ECDHE_PSK_ENABLED) && defined(MBEDTLS_AES_C) && defined(MBEDTLS_CCM_C) && defined(MBEDTLS_SHA512_C) + { MBEDTLS_TLS_ECDHE_PSK_WITH_AES_128_CCM_SHA256, "ECDHE-PSK-AES128-CCM-SHA256", + MBEDTLS_CIPHER_AES_128_CCM, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_ECDHE_PSK, + MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3, + MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3, + 0 }, +#endif + #if defined(MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED) #if defined(MBEDTLS_AES_C) #if defined(MBEDTLS_SHA1_C) diff --git a/third_party/mbedtls/ssl_ciphersuites.h b/third_party/mbedtls/ssl_ciphersuites.h index 24d17b3ed..b61ff3407 100644 --- a/third_party/mbedtls/ssl_ciphersuites.h +++ b/third_party/mbedtls/ssl_ciphersuites.h @@ -255,6 +255,12 @@ COSMOPOLITAN_C_START_ #define MBEDTLS_TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256 0xCCAD /**< TLS 1.2 */ #define MBEDTLS_TLS_RSA_PSK_WITH_CHACHA20_POLY1305_SHA256 0xCCAE /**< TLS 1.2 */ +/* RFC 8442 */ +#define MBEDTLS_TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256 0xD001 /**< TLS 1.2 */ +#define MBEDTLS_TLS_ECDHE_PSK_WITH_AES_256_GCM_SHA384 0xD002 /**< TLS 1.2 */ +#define MBEDTLS_TLS_ECDHE_PSK_WITH_AES_128_CCM_8_SHA256 0xD003 /**< TLS 1.2 */ +#define MBEDTLS_TLS_ECDHE_PSK_WITH_AES_128_CCM_SHA256 0xD005 /**< TLS 1.2 */ + /* Reminder: update mbedtls_ssl_premaster_secret when adding a new key exchange. * Reminder: update MBEDTLS_KEY_EXCHANGE__xxx below */ diff --git a/third_party/mbedtls/ssl_tls.c b/third_party/mbedtls/ssl_tls.c index 2cf965c9f..3208b9e8a 100644 --- a/third_party/mbedtls/ssl_tls.c +++ b/third_party/mbedtls/ssl_tls.c @@ -4415,8 +4415,8 @@ static int ssl_conf_set_psk_identity( mbedtls_ssl_config *conf, * \return An \c MBEDTLS_ERR_SSL_XXX error code on failure. */ int mbedtls_ssl_conf_psk( mbedtls_ssl_config *conf, - const unsigned char *psk, size_t psk_len, - const unsigned char *psk_identity, + const void *psk, size_t psk_len, + const void *psk_identity, size_t psk_identity_len ) { int ret = MBEDTLS_ERR_THIS_CORRUPTION; diff --git a/tool/build/build.mk b/tool/build/build.mk index f9448be1b..d5820a54a 100644 --- a/tool/build/build.mk +++ b/tool/build/build.mk @@ -55,6 +55,7 @@ TOOL_BUILD_DIRECTDEPS = \ THIRD_PARTY_GETOPT \ THIRD_PARTY_STB \ THIRD_PARTY_XED \ + THIRD_PARTY_MBEDTLS \ THIRD_PARTY_ZLIB \ TOOL_BUILD_LIB diff --git a/tool/build/compile.c b/tool/build/compile.c index 43709fbe2..2c1cae0d7 100644 --- a/tool/build/compile.c +++ b/tool/build/compile.c @@ -117,6 +117,9 @@ struct Command command; const char *const kSafeEnv[] = { "ADDR2LINE", // needed by GetAddr2linePath + "HOME", // needed by ~/.runit.psk + "HOMEDRIVE", // needed by ~/.runit.psk + "HOMEPATH", // needed by ~/.runit.psk "MAKEFLAGS", // needed by IsRunningUnderMake "MODE", // needed by test scripts "PATH", // needed by clang diff --git a/tool/build/lib/buildlib.mk b/tool/build/lib/buildlib.mk index 6bb6634e3..a3133eb9e 100644 --- a/tool/build/lib/buildlib.mk +++ b/tool/build/lib/buildlib.mk @@ -46,6 +46,7 @@ TOOL_BUILD_LIB_A_DIRECTDEPS = \ LIBC_UNICODE \ LIBC_X \ THIRD_PARTY_COMPILER_RT \ + THIRD_PARTY_MBEDTLS \ THIRD_PARTY_XED TOOL_BUILD_LIB_A_DEPS := \ diff --git a/tool/build/lib/eztls.c b/tool/build/lib/eztls.c new file mode 100644 index 000000000..a1168fe8a --- /dev/null +++ b/tool/build/lib/eztls.c @@ -0,0 +1,185 @@ +/*-*- 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 2021 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/calls/calls.h" +#include "libc/calls/struct/iovec.h" +#include "libc/errno.h" +#include "libc/log/check.h" +#include "libc/log/log.h" +#include "libc/rand/rand.h" +#include "libc/sock/sock.h" +#include "libc/sysv/consts/sig.h" +#include "libc/x/x.h" +#include "third_party/mbedtls/ctr_drbg.h" +#include "third_party/mbedtls/ecp.h" +#include "third_party/mbedtls/error.h" +#include "third_party/mbedtls/ssl.h" +#include "tool/build/lib/eztls.h" +#include "tool/build/lib/psk.h" + +struct EzTlsBio ezbio; +mbedtls_ssl_config ezconf; +mbedtls_ssl_context ezssl; +mbedtls_ctr_drbg_context ezrng; + +static char *EzTlsError(int r) { + static char b[128]; + mbedtls_strerror(r, b, sizeof(b)); + return b; +} + +static wontreturn void EzTlsDie(const char *s, int r) { + if (IsTiny()) { + fprintf(stderr, "error: %s (-0x%04x %s)\n", s, -r, EzTlsError(r)); + } else { + fprintf(stderr, "error: %s (grep -0x%04x)\n", s, -r); + } + exit(1); +} + +static int EzGetEntropy(void *c, unsigned char *p, size_t n) { + CHECK_EQ(n, getrandom(p, n, 0)); + return 0; +} + +static void EzInitializeRng(mbedtls_ctr_drbg_context *r) { + volatile unsigned char b[64]; + mbedtls_ctr_drbg_init(r); + CHECK(getrandom(b, 64, 0) == 64); + CHECK(!mbedtls_ctr_drbg_seed(r, EzGetEntropy, 0, b, 64)); + mbedtls_platform_zeroize(b, 64); +} + +static ssize_t EzWritevAll(int fd, struct iovec *iov, int iovlen) { + int i; + ssize_t rc; + size_t wrote, total; + i = 0; + total = 0; + do { + if (i) { + while (i < iovlen && !iov[i].iov_len) ++i; + if (i == iovlen) break; + } + if ((rc = writev(fd, iov + i, iovlen - i)) != -1) { + wrote = rc; + total += wrote; + do { + if (wrote >= iov[i].iov_len) { + wrote -= iov[i++].iov_len; + } else { + iov[i].iov_base = (char *)iov[i].iov_base + wrote; + iov[i].iov_len -= wrote; + wrote = 0; + } + } while (wrote); + } else if (errno != EINTR) { + return total ? total : -1; + } + } while (i < iovlen); + return total; +} + +int EzTlsFlush(struct EzTlsBio *bio, const unsigned char *buf, size_t len) { + struct iovec v[2]; + if (len || bio->c > 0) { + v[0].iov_base = bio->u; + v[0].iov_len = MAX(0, bio->c); + v[1].iov_base = buf; + v[1].iov_len = len; + if (EzWritevAll(bio->fd, v, 2) != -1) { + if (bio->c > 0) bio->c = 0; + } else if (errno == EINTR) { + return MBEDTLS_ERR_NET_CONN_RESET; + } else if (errno == EAGAIN) { + return MBEDTLS_ERR_SSL_TIMEOUT; + } else if (errno == EPIPE || errno == ECONNRESET || errno == ENETRESET) { + return MBEDTLS_ERR_NET_CONN_RESET; + } else { + WARNF("EzTlsSend error %s", strerror(errno)); + return MBEDTLS_ERR_NET_SEND_FAILED; + } + } + return 0; +} + +static int EzTlsSend(void *ctx, const unsigned char *buf, size_t len) { + int rc; + struct iovec v[2]; + struct EzTlsBio *bio = ctx; + if (bio->c >= 0 && bio->c + len <= sizeof(bio->u)) { + memcpy(bio->u + bio->c, buf, len); + bio->c += len; + return len; + } + if ((rc = EzTlsFlush(bio, buf, len)) < 0) return rc; + return len; +} + +static int EzTlsRecvImpl(void *ctx, unsigned char *p, size_t n, uint32_t o) { + int r; + ssize_t s; + struct iovec v[2]; + struct EzTlsBio *bio = ctx; + if ((r = EzTlsFlush(bio, 0, 0)) < 0) return r; + if (bio->a < bio->b) { + r = MIN(n, bio->b - bio->a); + memcpy(p, bio->t + bio->a, r); + if ((bio->a += r) == bio->b) bio->a = bio->b = 0; + return r; + } + v[0].iov_base = p; + v[0].iov_len = n; + v[1].iov_base = bio->t; + v[1].iov_len = sizeof(bio->t); + while ((r = readv(bio->fd, v, 2)) == -1) { + if (errno == EINTR) { + return MBEDTLS_ERR_SSL_WANT_READ; + } else if (errno == EAGAIN) { + return MBEDTLS_ERR_SSL_TIMEOUT; + } else if (errno == EPIPE || errno == ECONNRESET || errno == ENETRESET) { + return MBEDTLS_ERR_NET_CONN_RESET; + } else { + WARNF("tls read() error %s", strerror(errno)); + return MBEDTLS_ERR_NET_RECV_FAILED; + } + } + if (r > n) bio->b = r - n; + return MIN(n, r); +} + +static int EzTlsRecv(void *ctx, unsigned char *buf, size_t len, uint32_t tmo) { + return EzTlsRecvImpl(ctx, buf, len, tmo); +} + +/* + * openssl s_client -connect 127.0.0.1:31337 \ + * -psk $(hex <~/.runit.psk) \ + * -psk_identity runit + */ + +void SetupPresharedKeySsl(int endpoint) { + xsigaction(SIGPIPE, SIG_IGN, 0, 0, 0); + EzInitializeRng(&ezrng); + mbedtls_ssl_config_defaults(&ezconf, endpoint, MBEDTLS_SSL_TRANSPORT_STREAM, + MBEDTLS_SSL_PRESET_SUITEC); + mbedtls_ssl_conf_rng(&ezconf, mbedtls_ctr_drbg_random, &ezrng); + DCHECK_EQ(0, mbedtls_ssl_conf_psk(&ezconf, GetRunitPsk(), 32, "runit", 5)); + DCHECK_EQ(0, mbedtls_ssl_setup(&ezssl, &ezconf)); + mbedtls_ssl_set_bio(&ezssl, &ezbio, EzTlsSend, 0, EzTlsRecv); +} diff --git a/tool/build/lib/eztls.h b/tool/build/lib/eztls.h new file mode 100644 index 000000000..36a6b7d28 --- /dev/null +++ b/tool/build/lib/eztls.h @@ -0,0 +1,25 @@ +#ifndef COSMOPOLITAN_TOOL_BUILD_LIB_EZTLS_H_ +#define COSMOPOLITAN_TOOL_BUILD_LIB_EZTLS_H_ +#include "third_party/mbedtls/ctr_drbg.h" +#include "third_party/mbedtls/ssl.h" +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +struct EzTlsBio { + int fd, c; + unsigned a, b; + unsigned char t[4000]; + unsigned char u[1430]; +}; + +extern struct EzTlsBio ezbio; +extern mbedtls_ssl_config ezconf; +extern mbedtls_ssl_context ezssl; +extern mbedtls_ctr_drbg_context ezrng; + +void SetupPresharedKeySsl(int); +int EzTlsFlush(struct EzTlsBio *, const unsigned char *, size_t); + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_TOOL_BUILD_LIB_EZTLS_H_ */ diff --git a/tool/build/lib/psk.c b/tool/build/lib/psk.c new file mode 100644 index 000000000..5c2d84186 --- /dev/null +++ b/tool/build/lib/psk.c @@ -0,0 +1,61 @@ +/*-*- 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 2021 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/calls/calls.h" +#include "libc/calls/struct/stat.h" +#include "libc/dce.h" +#include "libc/fmt/fmt.h" +#include "libc/log/check.h" +#include "libc/mem/mem.h" +#include "libc/runtime/runtime.h" +#include "libc/stdio/stdio.h" +#include "libc/sysv/consts/o.h" +#include "tool/build/lib/psk.h" + +/** + * Returns preshared key for runit testing infrastructure. + */ +void *GetRunitPsk(void) { + int fd; + struct stat st; + const char *a, *b; + char *r, p[PATH_MAX + 1]; + if ((a = getenv("HOME"))) { + b = ""; + } else if (IsWindows()) { + a = getenv("HOMEDRIVE"); + b = getenv("HOMEPATH"); + if (!a || !b) { + a = "C:"; + b = ""; + } + } else { + fprintf(stderr, "need $HOME\n"); + exit(1); + } + snprintf(p, sizeof(p), "%s%s/.runit.psk", a, b); + if (stat(p, &st) == -1 || st.st_size != 32) { + fprintf(stderr, "need o//examples/getrandom.com -bn32 >~/.runit.psk\n"); + exit(1); + } + CHECK_NOTNULL((r = malloc(32))); + CHECK_NE(-1, (fd = open(p, O_RDONLY))); + CHECK_EQ(32, read(fd, r, 32)); + CHECK_NE(-1, close(fd)); + return r; +} diff --git a/tool/build/lib/psk.h b/tool/build/lib/psk.h new file mode 100644 index 000000000..1f06ecade --- /dev/null +++ b/tool/build/lib/psk.h @@ -0,0 +1,10 @@ +#ifndef COSMOPOLITAN_TOOL_BUILD_LIB_PSK_H_ +#define COSMOPOLITAN_TOOL_BUILD_LIB_PSK_H_ +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +void *GetRunitPsk(void); + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_TOOL_BUILD_LIB_PSK_H_ */ diff --git a/tool/build/runit.c b/tool/build/runit.c index cdf58e524..ee41c8af7 100644 --- a/tool/build/runit.c +++ b/tool/build/runit.c @@ -16,63 +16,55 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/alg/alg.h" #include "libc/bits/bits.h" #include "libc/bits/safemacros.internal.h" #include "libc/calls/calls.h" #include "libc/calls/sigbits.h" #include "libc/calls/struct/flock.h" -#include "libc/calls/struct/itimerval.h" -#include "libc/calls/struct/sigaction.h" -#include "libc/calls/struct/stat.h" -#include "libc/calls/struct/timeval.h" -#include "libc/dce.h" #include "libc/dns/dns.h" -#include "libc/errno.h" #include "libc/fmt/conv.h" -#include "libc/fmt/fmt.h" #include "libc/limits.h" #include "libc/log/check.h" #include "libc/log/log.h" -#include "libc/mem/mem.h" +#include "libc/macros.internal.h" #include "libc/runtime/gc.internal.h" -#include "libc/runtime/runtime.h" #include "libc/sock/ipclassify.internal.h" -#include "libc/sock/sock.h" #include "libc/stdio/stdio.h" -#include "libc/str/str.h" #include "libc/sysv/consts/af.h" #include "libc/sysv/consts/ex.h" -#include "libc/sysv/consts/exit.h" -#include "libc/sysv/consts/f.h" -#include "libc/sysv/consts/fd.h" #include "libc/sysv/consts/fileno.h" #include "libc/sysv/consts/ipproto.h" #include "libc/sysv/consts/itimer.h" #include "libc/sysv/consts/lock.h" +#include "libc/sysv/consts/map.h" #include "libc/sysv/consts/o.h" -#include "libc/sysv/consts/pr.h" -#include "libc/sysv/consts/shut.h" -#include "libc/sysv/consts/sig.h" +#include "libc/sysv/consts/prot.h" #include "libc/sysv/consts/sock.h" #include "libc/time/time.h" #include "libc/x/x.h" +#include "third_party/mbedtls/ssl.h" +#include "tool/build/lib/eztls.h" #include "tool/build/runit.h" /** * @fileoverview Remote test runner. * - * This is able to upload and run test binaries on remote operating - * systems with about 30 milliseconds of latency. It requires zero ops - * work too, since it deploys the ephemeral runit daemon via SSH upon - * ECONNREFUSED. That takes 10x longer (300 milliseconds). Further note - * there's no make -j race conditions here, thanks to SO_REUSEPORT. + * We want to scp .com binaries to remote machines and run them. The + * problem is that SSH is the slowest thing imaginable, taking about + * 300ms to connect to a host that's merely half a millisecond away. + * + * This program takes 17ms using elliptic curve diffie hellman exchange + * where we favor a 32-byte binary preshared key (~/.runit.psk) instead + * of certificates. It's how long it takes to connect, copy the binary, + * and run it. The remote daemon is deployed via SSH if it's not there. * * o/default/tool/build/runit.com \ * o/default/tool/build/runitd.com \ * o/default/test/libc/alg/qsort_test.com \ * freebsd.test.:31337:22 * + * APE binaries are hermetic and embed dependent files within their zip + * structure, which is why all we need is this simple test runner tool. * The only thing that needs to be configured is /etc/hosts or Bind, to * assign numbers to the officially reserved canned names. For example: * @@ -97,12 +89,7 @@ * iptables -I INPUT 1 -s 10.0.0.0/8 -p tcp --dport 31337 -j ACCEPT * iptables -I INPUT 1 -s 192.168.0.0/16 -p tcp --dport 31337 -j ACCEPT * - * If your system administrator blocks all ICMP, you'll likely encounter - * difficulties. Consider offering feedback to his/her manager and grand - * manager. - * - * Finally note this tool isn't designed for untrustworthy environments. - * It also isn't designed to process untrustworthy inputs. + * This tool may be used in zero trust environments. */ static const struct addrinfo kResolvHints = {.ai_family = AF_INET, @@ -301,7 +288,9 @@ TryAgain: void SendRequest(void) { int fd; - int64_t off; + char *p; + size_t i; + ssize_t rc; struct stat st; const char *name; unsigned char *hdr; @@ -309,6 +298,7 @@ void SendRequest(void) { DEBUGF("running %s on %s", g_prog, g_hostname); CHECK_NE(-1, (fd = open(g_prog, O_RDONLY))); CHECK_NE(-1, fstat(fd, &st)); + CHECK_NE(MAP_FAILED, (p = mmap(0, st.st_size, PROT_READ, MAP_SHARED, fd, 0))); CHECK_LE((namesize = strlen((name = basename(g_prog)))), PATH_MAX); CHECK_LE((progsize = st.st_size), INT_MAX); CHECK_NOTNULL((hdr = gc(calloc(1, (hdrsize = 4 + 1 + 4 + 4 + namesize))))); @@ -326,25 +316,27 @@ void SendRequest(void) { hdr[9 + 2] = (unsigned char)((unsigned)progsize >> 010); hdr[9 + 3] = (unsigned char)((unsigned)progsize >> 000); memcpy(&hdr[4 + 1 + 4 + 4], name, namesize); - CHECK_EQ(hdrsize, write(g_sock, hdr, hdrsize)); - for (off = 0; off < progsize;) { - CHECK_GT(sendfile(g_sock, fd, &off, progsize - off), 0); + CHECK_EQ(hdrsize, mbedtls_ssl_write(&ezssl, hdr, hdrsize)); + for (i = 0; i < progsize; i += rc) { + CHECK_GT((rc = mbedtls_ssl_write(&ezssl, p + i, progsize - i)), 0); } - CHECK_NE(-1, shutdown(g_sock, SHUT_WR)); + CHECK_NE(-1, EzTlsFlush(&ezbio, 0, 0)); + CHECK_NE(-1, munmap(p, st.st_size)); + CHECK_NE(-1, close(fd)); } int ReadResponse(void) { int res; - uint32_t size; ssize_t rc; size_t n, m; + uint32_t size; unsigned char *p; enum RunitCommand cmd; static long backoff; static unsigned char msg[512]; res = -1; for (;;) { - if ((rc = recv(g_sock, msg, sizeof(msg), 0)) == -1) { + if ((rc = mbedtls_ssl_read(&ezssl, msg, sizeof(msg))) == -1) { CHECK_EQ(ECONNRESET, errno); usleep((backoff = (backoff + 1000) * 2)); break; @@ -369,7 +361,7 @@ int ReadResponse(void) { size = READ32BE(p), p += 4, n -= 4; while (size) { if (n) { - CHECK_NE(-1, (rc = write(STDERR_FILENO, p, min(n, size)))); + CHECK_NE(-1, (rc = write(STDERR_FILENO, p, MIN(n, size)))); CHECK_NE(0, (m = (size_t)rc)); p += m, n -= m, size -= m; } else { @@ -400,7 +392,11 @@ int RunOnHost(char *spec) { 1); if (!strchr(g_hostname, '.')) strcat(g_hostname, ".test."); do { + mbedtls_ssl_session_reset(&ezssl); Connect(); + ezbio.fd = g_sock; + CHECK_EQ(0, mbedtls_ssl_handshake(&ezssl)); + CHECK_NE(-1, EzTlsFlush(&ezbio, 0, 0)); SendRequest(); } while ((rc = ReadResponse()) == -1); return rc; @@ -464,6 +460,7 @@ int RunRemoteTestsInParallel(char *hosts[], int count) { int main(int argc, char *argv[]) { showcrashreports(); + SetupPresharedKeySsl(MBEDTLS_SSL_IS_CLIENT); /* __log_level = kLogDebug; */ if (argc > 1 && (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0)) { diff --git a/tool/build/runitd.c b/tool/build/runitd.c index 3094a0b00..63739e22c 100644 --- a/tool/build/runitd.c +++ b/tool/build/runitd.c @@ -17,49 +17,37 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/bits/bits.h" -#include "libc/bits/safemacros.internal.h" #include "libc/calls/calls.h" #include "libc/calls/sigbits.h" -#include "libc/calls/struct/sigaction.h" -#include "libc/calls/struct/stat.h" -#include "libc/dce.h" #include "libc/errno.h" #include "libc/fmt/conv.h" -#include "libc/fmt/fmt.h" #include "libc/log/check.h" #include "libc/log/log.h" -#include "libc/macros.internal.h" -#include "libc/nt/runtime.h" -#include "libc/paths.h" #include "libc/runtime/gc.internal.h" #include "libc/runtime/runtime.h" #include "libc/sock/sock.h" #include "libc/stdio/stdio.h" -#include "libc/stdio/temp.h" #include "libc/str/str.h" #include "libc/sysv/consts/af.h" -#include "libc/sysv/consts/auxv.h" #include "libc/sysv/consts/ex.h" #include "libc/sysv/consts/exit.h" #include "libc/sysv/consts/f.h" #include "libc/sysv/consts/fd.h" -#include "libc/sysv/consts/fileno.h" #include "libc/sysv/consts/inaddr.h" #include "libc/sysv/consts/ipproto.h" #include "libc/sysv/consts/itimer.h" #include "libc/sysv/consts/o.h" #include "libc/sysv/consts/poll.h" #include "libc/sysv/consts/sa.h" -#include "libc/sysv/consts/shut.h" -#include "libc/sysv/consts/sig.h" #include "libc/sysv/consts/so.h" #include "libc/sysv/consts/sock.h" #include "libc/sysv/consts/sol.h" #include "libc/sysv/consts/w.h" -#include "libc/testlib/testlib.h" #include "libc/time/time.h" #include "libc/x/x.h" #include "third_party/getopt/getopt.h" +#include "third_party/mbedtls/ssl.h" +#include "tool/build/lib/eztls.h" #include "tool/build/runit.h" /** @@ -208,7 +196,7 @@ void StartTcpServer(void) { } } -void SendExitMessage(int sock, int rc) { +void SendExitMessage(int rc) { unsigned char msg[4 + 1 + 1]; msg[0 + 0] = (unsigned char)((unsigned)RUNITD_MAGIC >> 030); msg[0 + 1] = (unsigned char)((unsigned)RUNITD_MAGIC >> 020); @@ -216,11 +204,12 @@ void SendExitMessage(int sock, int rc) { msg[0 + 3] = (unsigned char)((unsigned)RUNITD_MAGIC >> 000); msg[4] = kRunitExit; msg[5] = (unsigned char)rc; - CHECK_EQ(sizeof(msg), send(sock, msg, sizeof(msg), 0)); + CHECK_EQ(sizeof(msg), mbedtls_ssl_write(&ezssl, msg, sizeof(msg))); + CHECK_NE(-1, EzTlsFlush(&ezbio, 0, 0)); } -void SendOutputFragmentMessage(int sock, enum RunitCommand kind, - unsigned char *buf, size_t size) { +void SendOutputFragmentMessage(enum RunitCommand kind, unsigned char *buf, + size_t size) { ssize_t rc; size_t sent; unsigned char msg[4 + 1 + 4]; @@ -233,13 +222,14 @@ void SendOutputFragmentMessage(int sock, enum RunitCommand kind, msg[5 + 1] = (unsigned char)((unsigned)size >> 020); msg[5 + 2] = (unsigned char)((unsigned)size >> 010); msg[5 + 3] = (unsigned char)((unsigned)size >> 000); - CHECK_EQ(sizeof(msg), send(sock, msg, sizeof(msg), 0)); + CHECK_EQ(sizeof(msg), mbedtls_ssl_write(&ezssl, msg, sizeof(msg))); while (size) { - CHECK_NE(-1, (rc = send(sock, buf, size, 0))); + CHECK_NE(-1, (rc = mbedtls_ssl_write(&ezssl, buf, size))); CHECK_LE((sent = (size_t)rc), size); size -= sent; buf += sent; } + CHECK_NE(-1, EzTlsFlush(&ezbio, 0, 0)); } void OnAlarm(int sig) { @@ -274,9 +264,12 @@ void HandleClient(void) { close(g_clifd); return; } + ezbio.fd = g_clifd; + CHECK_EQ(0, mbedtls_ssl_handshake(&ezssl)); + CHECK_NE(-1, EzTlsFlush(&ezbio, 0, 0)); addrstr = gc(DescribeAddress(&addr)); DEBUGF("%s %s %s", gc(DescribeAddress(&g_servaddr)), "accepted", addrstr); - got = recv(g_clifd, (p = &g_buf[0]), sizeof(g_buf), 0); + got = mbedtls_ssl_read(&ezssl, (p = &g_buf[0]), sizeof(g_buf)); CHECK_GE(got, kMinMsgSize); CHECK_LE(got, sizeof(g_buf)); CHECK_EQ(RUNITD_MAGIC, READ32BE(p)); @@ -304,7 +297,7 @@ void HandleClient(void) { remaining -= got; } while (remaining) { - CHECK_NE(-1, (got = recv(g_clifd, g_buf, sizeof(g_buf), 0))); + CHECK_NE(-1, (got = mbedtls_ssl_read(&ezssl, g_buf, sizeof(g_buf)))); CHECK_LE(got, remaining); if (!got) { LOGF("%s %s %,u/%,u %s", addrstr, "sent", remaining, filesize, @@ -351,7 +344,7 @@ void HandleClient(void) { break; } fwrite(g_buf, got, 1, stderr); - SendOutputFragmentMessage(g_clifd, kRunitStderr, g_buf, got); + SendOutputFragmentMessage(kRunitStderr, g_buf, got); } else { CHECK_EQ(EINTR, errno); } @@ -381,7 +374,8 @@ void HandleClient(void) { /* let client know how it went */ LOGIFNEG1(unlink(g_exepath)); - SendExitMessage(g_clifd, exitcode); + SendExitMessage(exitcode); + mbedtls_ssl_close_notify(&ezssl); LOGIFNEG1(close(g_clifd)); _exit(0); } @@ -442,6 +436,7 @@ void Daemonize(void) { int main(int argc, char *argv[]) { showcrashreports(); + SetupPresharedKeySsl(MBEDTLS_SSL_IS_SERVER); /* __log_level = kLogDebug; */ GetOpts(argc, argv); CHECK_NE(-1, (g_devnullfd = open("/dev/null", O_RDWR))); diff --git a/tool/net/help.txt b/tool/net/help.txt index c51893cc1..1206e5000 100644 --- a/tool/net/help.txt +++ b/tool/net/help.txt @@ -639,6 +639,9 @@ FUNCTIONS Returns host associated with request. This will be the Host header, if it's supplied. Otherwise it's the bind address. + GetHostOs() → str + Returns string that describes the host OS. + GetMonospaceWidth(str|char) → int Returns monospace display width of string. This is useful for fixed-width formatting. For example, CJK characters typically take @@ -1101,6 +1104,8 @@ CONSTANTS kLogFatal Integer for fatal logging level, which is less than kLogError. + Logging anything at this level will result in a backtrace and + process exit. SEE ALSO diff --git a/tool/net/redbean.c b/tool/net/redbean.c index 62a5ef794..e008ccc0e 100644 --- a/tool/net/redbean.c +++ b/tool/net/redbean.c @@ -4911,7 +4911,7 @@ static int LuaLog(lua_State *L) { level = luaL_checkinteger(L, 1); if (LOGGABLE(level)) { msg = luaL_checkstring(L, 2); - lua_getstack(L, 1, &ar); + lua_getstack(L, 0, &ar); lua_getinfo(L, "nSl", &ar); if (!strcmp(ar.name, "main")) { module = strndup(effectivepath.p, effectivepath.n); @@ -5035,6 +5035,32 @@ static int LuaGetComment(lua_State *L) { return 1; } + +static int LuaGetHostOs(lua_State *L) { + const char *s = NULL; + if (IsLinux()) { + s = "LINUX"; + } else if (IsMetal()) { + s = "METAL"; + } else if (IsWindows()) { + s = "WINDOWS"; + } else if (IsXnu()) { + s = "XNU"; + } else if (IsOpenbsd()) { + s = "OPENBSD"; + } else if (IsFreebsd()) { + s = "FREEBSD"; + } else if (IsNetbsd()) { + s = "NETBSD"; + } + if (s) { + lua_pushstring(L, s); + } else { + lua_pushnil(L); + } + return 1; +} + static void LuaSetIntField(lua_State *L, const char *k, lua_Integer v) { lua_pushinteger(L, v); lua_setfield(L, -2, k); @@ -5194,6 +5220,7 @@ static bool LuaRun(const char *path, bool mandatory) { } else { WARNF("%s %s", path, lua_tostring(L, -1)); } + lua_pop(L, 1); } free(code); } @@ -5236,6 +5263,7 @@ static const luaL_Reg kLuaFuncs[] = { {"GetHeader", LuaGetHeader}, // {"GetHeaders", LuaGetHeaders}, // {"GetHost", LuaGetHost}, // + {"GetHostOs", LuaGetHostOs}, // {"GetHttpReason", LuaGetHttpReason}, // {"GetHttpVersion", LuaGetHttpVersion}, // {"GetLastModifiedTime", LuaGetLastModifiedTime}, // diff --git a/tool/viz/dumphexc.c b/tool/viz/dumphexc.c index e2d0ab94f..13d558121 100644 --- a/tool/viz/dumphexc.c +++ b/tool/viz/dumphexc.c @@ -18,75 +18,30 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/fmt/fmt.h" #include "libc/macros.internal.h" +#include "libc/stdio/append.internal.h" +#include "libc/stdio/hex.internal.h" #include "libc/stdio/stdio.h" -struct Append { - size_t i, n; - char *p; -}; - -int AppendFmt(struct Append *b, const char *fmt, ...) { - int n; - char *p; - va_list va, vb; - va_start(va, fmt); - va_copy(vb, va); - n = vsnprintf(b->p + b->i, b->n - b->i, fmt, va); - if (n >= b->n - b->i) { - do { - if (b->n) { - b->n += b->n >> 1; /* this is the important line */ - } else { - b->n = 16; - } - } while (b->i + n + 1 > b->n); - b->p = realloc(b->p, b->n); - vsnprintf(b->p + b->i, b->n - b->i, fmt, vb); - } - va_end(vb); - va_end(va); - b->i += n; - return n; -} - -char *DumpHexc(const char *data, size_t size, size_t *z) { - long o; - int b, i, n; - char A[128], *p; - struct Append buf; - if (size == -1) size = data ? strlen(data) : 0; - buf.i = 0; - buf.n = 256; - buf.p = calloc(1, 256); - AppendFmt(&buf, "\"\\\n"); - for (b = o = 0; (n = MIN(16, size)); data += n, size -= n) { - p = A; - for (i = 0; i < n; ++i) { - *p++ = '\\'; - *p++ = 'x'; - *p++ = "0123456789abcdef"[(data[i] & 0xF0) >> 4]; - *p++ = "0123456789abcdef"[(data[i] & 0x0F) >> 0]; - } - if (o) AppendFmt(&buf, "\\\n"); - AppendFmt(&buf, "%.*s", p - A, A); - o += n; - } - AppendFmt(&buf, "\""); - if (z) *z = buf.i; - return buf.p; -} +/** + * @fileoverview Hex String Literal Converter, e.g. + * + * $ echo hello | o/tool/viz/dumphexc.com + * "\ + * \x68\x65\x6c\x6c\x6f\x0a" + */ int main(int argc, char *argv[]) { char *p; size_t n, g; + char *b = 0; char buf[512]; - struct Append b = {0}; while ((g = fread(buf, 1, sizeof(buf), stdin))) { - AppendFmt(&b, "%.*s", g, buf); + appendd(&b, buf, g); } if (!ferror(stdin)) { - p = DumpHexc(b.p, b.i, &n); + p = DumpHexc(b, appendz(b).i, &n); fwrite(p, 1, n, stdout); + free(p); } printf("\n"); return ferror(stdin) || ferror(stdout) ? 1 : 0;