From dc6d11a0311aedfbd00be05dc3f6b48200e59a90 Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Sat, 24 Apr 2021 13:58:34 -0700 Subject: [PATCH] Improve performance of printf functions --- examples/hello3.c | 3 +- libc/calls/internal.h | 1 + libc/calls/now.c | 6 +- libc/calls/vdprintf.c | 58 ++-- .../writevuninterruptible.c} | 56 ++-- libc/fmt/fmt.c | 82 ++++- libc/fmt/fmts.h | 13 +- libc/fmt/ntoa.c | 29 +- libc/fmt/pad.c | 5 +- libc/fmt/stoa.c | 86 ++--- libc/fmt/vsnprintf.c | 15 +- libc/log/cancolor.c | 14 +- libc/log/isterminalinarticulate.c | 5 +- libc/mem/vasprintf.c | 16 +- libc/nexgen32e/cescapec.S | 28 +- libc/nexgen32e/crc32.h | 2 + libc/nexgen32e/memrchr.S | 3 +- libc/runtime/ftrace.c | 46 +-- libc/runtime/ftraceinit.c | 56 ++++ libc/runtime/runtime.h | 1 + libc/stdio/fwrite.c | 27 +- libc/stdio/vfprintf.c | 15 +- libc/str/crc32c-pure.c | 79 ----- libc/str/crc32c-sse42.c | 95 ------ libc/str/crc32c.c | 25 +- libc/str/stpcpy.c | 2 +- net/http/categorizeip.c | 6 +- net/http/gethttpheader.inc | 5 +- net/http/gethttpmethod.inc | 9 +- net/http/isacceptablehost.c | 58 ++-- net/http/ispublicip.c | 5 +- net/http/parsehttprequest.c | 2 +- test/libc/fmt/itoa64radix10_test.c | 2 + test/libc/fmt/palandprintf_test.c | 43 +-- test/libc/str/crc32c_test.c | 12 +- test/net/http/isacceptablehost_test.c | 4 +- tool/net/net.mk | 16 +- tool/net/redbean.c | 295 +++++++++--------- tool/net/redbean.lua | 2 +- 39 files changed, 577 insertions(+), 650 deletions(-) rename libc/{str/crc32c.S => calls/writevuninterruptible.c} (65%) create mode 100644 libc/runtime/ftraceinit.c delete mode 100644 libc/str/crc32c-pure.c delete mode 100644 libc/str/crc32c-sse42.c diff --git a/examples/hello3.c b/examples/hello3.c index 29a69466f..64fbdbbb2 100644 --- a/examples/hello3.c +++ b/examples/hello3.c @@ -8,9 +8,10 @@ ╚─────────────────────────────────────────────────────────────────*/ #endif #include "libc/errno.h" +#include "libc/fmt/fmt.h" #include "libc/stdio/stdio.h" int main() { - printf("%s \n", "hello world"); + printf("%`'s\n", "hello\1\2world→→"); return errno; } diff --git a/libc/calls/internal.h b/libc/calls/internal.h index d523e8e44..4c5571864 100644 --- a/libc/calls/internal.h +++ b/libc/calls/internal.h @@ -228,6 +228,7 @@ int gethostname_bsd(char *, size_t) hidden; int gethostname_nt(char *, size_t) hidden; size_t __iovec_size(const struct iovec *, size_t) hidden; void __rusage2linux(struct rusage *) hidden; +ssize_t WritevUninterruptible(int, struct iovec *, int); /*───────────────────────────────────────────────────────────────────────────│─╗ │ cosmopolitan § syscalls » windows nt » veneers ─╬─│┼ diff --git a/libc/calls/now.c b/libc/calls/now.c index 291af2b44..3c00291fc 100644 --- a/libc/calls/now.c +++ b/libc/calls/now.c @@ -70,10 +70,6 @@ long double ConvertTicksToNanos(uint64_t ticks) { return ticks * g_now.cpn; /* pico scale */ } -static long double ConvertTicksToSeconds(uint64_t ticks) { - return 1 / 1e9 * ConvertTicksToNanos(ticks); -} - long double nowl_sys(void) { return dtime(CLOCK_REALTIME); } @@ -82,5 +78,5 @@ long double nowl_art(void) { uint64_t ticks; if (!g_now.once) RefreshTime(); ticks = unsignedsubtract(rdtsc(), g_now.k0); - return g_now.r0 + ConvertTicksToSeconds(ticks); + return g_now.r0 + (1 / 1e9L * (ticks * g_now.cpn)); } diff --git a/libc/calls/vdprintf.c b/libc/calls/vdprintf.c index 882dd4ff4..cf4e92fdd 100644 --- a/libc/calls/vdprintf.c +++ b/libc/calls/vdprintf.c @@ -22,43 +22,55 @@ #include "libc/limits.h" #include "libc/macros.internal.h" #include "libc/nt/files.h" +#include "libc/sock/sock.h" +#include "libc/str/str.h" #include "libc/sysv/errfuns.h" struct VdprintfState { - int n; - int fd; - unsigned char buf[1024]; + int n, t, fd; + char b[1024]; }; -static int vdprintf_flush(struct VdprintfState *df, int n) { - int i, rc; - for (i = 0; i < n; i += rc) { - if ((rc = write(df->fd, df->buf + i, n - i)) == -1) { - return -1; +static int vdprintf_putc(const char *s, struct VdprintfState *t, size_t n) { + struct iovec iov[2]; + if (n) { + if (t->n + n < sizeof(t->b)) { + memcpy(t->b + t->n, s, n); + t->n += n; + } else { + iov[0].iov_base = t->b; + iov[0].iov_len = t->n; + iov[1].iov_base = s; + iov[1].iov_len = n; + if (WritevUninterruptible(t->fd, iov, 2) == -1) { + return -1; + } + t->t += t->n; + t->n = 0; } } return 0; } -static int vdprintf_putc(int c, struct VdprintfState *df) { - df->buf[df->n++ & (ARRAYLEN(df->buf) - 1)] = c & 0xff; - if ((df->n & (ARRAYLEN(df->buf) - 1))) { - return 0; - } else { - return vdprintf_flush(df, ARRAYLEN(df->buf)); - } -} - /** * Formats string directly to system i/o device. * @asyncsignalsafe * @vforksafe */ int(vdprintf)(int fd, const char *fmt, va_list va) { - struct VdprintfState df; - df.n = 0; - df.fd = fd; - if (__fmt(vdprintf_putc, &df, fmt, va) == -1) return -1; - if (vdprintf_flush(&df, df.n & (ARRAYLEN(df.buf) - 1)) == -1) return -1; - return df.n; + struct iovec iov[1]; + struct VdprintfState t; + t.n = 0; + t.t = 0; + t.fd = fd; + if (__fmt(vdprintf_putc, &t, fmt, va) == -1) return -1; + if (t.n) { + iov[0].iov_base = t.b; + iov[0].iov_len = t.n; + if (WritevUninterruptible(t.fd, iov, 1) == -1) { + return -1; + } + t.t += t.n; + } + return t.t; } diff --git a/libc/str/crc32c.S b/libc/calls/writevuninterruptible.c similarity index 65% rename from libc/str/crc32c.S rename to libc/calls/writevuninterruptible.c index 85af9effd..c1b86340d 100644 --- a/libc/str/crc32c.S +++ b/libc/calls/writevuninterruptible.c @@ -1,7 +1,7 @@ -/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ -│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│ +/*-*- 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 2020 Justine Alexandra Roberts Tunney │ +│ 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 │ @@ -16,28 +16,30 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/dce.h" -#include "libc/macros.internal.h" -#include "libc/nexgen32e/x86feature.h" -#include "libc/notice.inc" +#include "libc/calls/internal.h" +#include "libc/errno.h" +#include "libc/sock/sock.h" -// Computes 32-bit Castagnoli Cyclic Redundancy Check. -// -// @param edi is the initial hash value (0 is fine) -// @param rsi points to the data -// @param rdx is the byte size of data -// @return eax is the new hash value -// @note Used by ISCSI, TensorFlow, etc. - .initbss 300,_init_crc32c -crc32c: .quad 0 - .endobj crc32c,globl - .previous - - .init.start 300,_init_crc32c - ezlea crc32c_pure,ax - ezlea crc32c_sse42,cx - testb X86_HAVE(SSE4_2)+kCpuids(%rip) - cmovnz %rcx,%rax - stosq - .init.end 300,_init_crc32c - .source __FILE__ +ssize_t WritevUninterruptible(int fd, struct iovec *iov, int iovlen) { + ssize_t rc; + size_t wrote; + do { + if ((rc = writev(fd, iov, iovlen)) != -1) { + wrote = rc; + do { + if (wrote >= iov->iov_len) { + wrote -= iov->iov_len; + ++iov; + --iovlen; + } else { + iov->iov_base = (char *)iov->iov_base + wrote; + iov->iov_len -= wrote; + wrote = 0; + } + } while (wrote); + } else if (errno != EINTR) { + return -1; + } + } while (iovlen); + return 0; +} diff --git a/libc/fmt/fmt.c b/libc/fmt/fmt.c index 3bacd1d48..fda6ed39d 100644 --- a/libc/fmt/fmt.c +++ b/libc/fmt/fmt.c @@ -23,6 +23,7 @@ #include "libc/fmt/fmt.h" #include "libc/fmt/fmts.h" #include "libc/fmt/internal.h" +#include "libc/fmt/itoa.h" #include "libc/macros.internal.h" #include "libc/mem/mem.h" #include "libc/nexgen32e/bsr.h" @@ -31,11 +32,12 @@ #include "libc/sysv/errfuns.h" #include "third_party/gdtoa/gdtoa.h" -#define PUT(C) \ - do { \ - if (out(C, arg) == -1) { \ - return -1; \ - } \ +#define PUT(C) \ + do { \ + char Buf[1] = {C}; \ + if (out(Buf, arg, 1) == -1) { \ + return -1; \ + } \ } while (0) static const char kSpecialFloats[2][2][4] = {{"INF", "inf"}, {"NAN", "nan"}}; @@ -121,14 +123,18 @@ hidden int __fmt(void *fn, void *arg, const char *format, va_list va) { uint32_t u[2]; uint64_t q; } pun; + long ld; void *p; + unsigned u; + char ibuf[21]; bool longdouble; long double ldbl; + unsigned long lu; wchar_t charbuf[1]; const char *alphabet; - int (*out)(long, void *); + int (*out)(const char *, void *, size_t); unsigned char signbit, log2base; - int c, d, k, w, i1, ui, bw, bex; + int c, d, k, w, n, i1, ui, bw, bex; char *s, *q, *se, qchar, special[8]; int sgn, alt, sign, prec, prec1, flags, width, decpt, lasterr; @@ -136,18 +142,64 @@ hidden int __fmt(void *fn, void *arg, const char *format, va_list va) { out = fn ? fn : (void *)missingno; while (*format) { - /* %[flags][width][.prec][length] */ if (*format != '%') { - /* no */ - PUT(*format); - format++; + for (n = 1; format[n]; ++n) { + if (format[n] == '%') break; + } + if (out(format, arg, n) == -1) return -1; + format += n; continue; - } else { - /* yes, evaluate it */ - format++; } - /* evaluate flags */ + if (!IsTiny()) { + if (format[1] == 's') { /* FAST PATH: PLAIN STRING */ + s = va_arg(va, char *); + if (!s) s = "(null)"; + if (out(s, arg, strlen(s)) == -1) return -1; + format += 2; + continue; + } else if (format[1] == 'd') { /* FAST PATH: PLAIN INTEGER */ + d = va_arg(va, int); + if (out(ibuf, arg, int64toarray_radix10(d, ibuf)) == -1) return -1; + format += 2; + continue; + } else if (format[1] == 'u') { /* FAST PATH: PLAIN UNSIGNED */ + u = va_arg(va, unsigned); + if (out(ibuf, arg, uint64toarray_radix10(u, ibuf)) == -1) return -1; + format += 2; + continue; + } else if (format[1] == 'x') { /* FAST PATH: PLAIN HEX */ + u = va_arg(va, unsigned); + if (out(ibuf, arg, uint64toarray_radix16(u, ibuf)) == -1) return -1; + format += 2; + continue; + } else if (format[1] == 'l' && format[2] == 'x') { + lu = va_arg(va, unsigned long); /* FAST PATH: PLAIN LONG HEX */ + if (out(ibuf, arg, uint64toarray_radix16(lu, ibuf)) == -1) return -1; + format += 3; + continue; + } else if (format[1] == 'l' && format[2] == 'd') { + ld = va_arg(va, long); /* FAST PATH: PLAIN LONG */ + if (out(ibuf, arg, int64toarray_radix10(ld, ibuf)) == -1) return -1; + format += 3; + continue; + } else if (format[1] == 'l' && format[2] == 'u') { + lu = va_arg(va, unsigned long); /* FAST PATH: PLAIN UNSIGNED LONG */ + if (out(ibuf, arg, int64toarray_radix10(lu, ibuf)) == -1) return -1; + format += 3; + continue; + } else if (format[1] == '.' && format[2] == '*' && format[3] == 's') { + n = va_arg(va, unsigned); /* FAST PATH: PRECISION STRING */ + s = va_arg(va, const char *); + if (!s) s = "(null)", n = MIN(6, n); + if (out(s, arg, n) == -1) return -1; + format += 4; + continue; + } + } + + /* GENERAL PATH */ + format++; sign = 0; flags = 0; getflag: diff --git a/libc/fmt/fmts.h b/libc/fmt/fmts.h index 4336f7bc8..c56f88650 100644 --- a/libc/fmt/fmts.h +++ b/libc/fmt/fmts.h @@ -6,13 +6,14 @@ #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ -int __fmt_pad(int (*)(long, void *), void *, unsigned long) hidden; -int __fmt_stoa(int (*)(long, void *), void *, void *, unsigned long, - unsigned long, unsigned long, unsigned char, - unsigned char) hidden; -int __fmt_ntoa(int (*)(long, void *), void *, va_list, unsigned char, +int __fmt_pad(int (*)(const char *, void *, size_t), void *, + unsigned long) hidden; +int __fmt_stoa(int (*)(const char *, void *, size_t), void *, void *, unsigned long, unsigned long, unsigned long, unsigned char, - const char *) hidden; + unsigned char) hidden; +int __fmt_ntoa(int (*)(const char *, void *, size_t), void *, va_list, + unsigned char, unsigned long, unsigned long, unsigned long, + unsigned char, const char *) hidden; char *__fmt_dtoa(double, int, int, int *, int *, char **) hidden; COSMOPOLITAN_C_END_ diff --git a/libc/fmt/ntoa.c b/libc/fmt/ntoa.c index 25757d6c6..508113de7 100644 --- a/libc/fmt/ntoa.c +++ b/libc/fmt/ntoa.c @@ -16,6 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/alg/reverse.internal.h" #include "libc/assert.h" #include "libc/fmt/conv.h" #include "libc/fmt/fmts.h" @@ -25,12 +26,11 @@ uintmax_t __udivmodti4(uintmax_t, uintmax_t, uintmax_t *); -static int __fmt_ntoa_format(int out(long, void *), void *arg, char *buf, - unsigned len, bool negative, unsigned log2base, - unsigned prec, unsigned width, +static int __fmt_ntoa_format(int out(const char *, void *, size_t), void *arg, + char *buf, unsigned len, bool negative, + unsigned log2base, unsigned prec, unsigned width, unsigned char flags) { - unsigned i, idx; - idx = 0; + unsigned i; /* pad leading zeros */ if (!(flags & FLAGS_LEFT)) { @@ -82,24 +82,21 @@ static int __fmt_ntoa_format(int out(long, void *), void *arg, char *buf, } } - /* reverse string */ - for (i = 0U; i < len; i++) { - if (out(buf[len - i - 1], arg) == -1) return -1; - idx++; - } + reverse(buf, len); + if (out(buf, arg, len) == -1) return -1; /* append pad spaces up to given width */ if (flags & FLAGS_LEFT) { - if (idx < width) { - if (__fmt_pad(out, arg, width - idx) == -1) return -1; + if (len < width) { + if (__fmt_pad(out, arg, width - len) == -1) return -1; } } return 0; } -int __fmt_ntoa2(int out(long, void *), void *arg, uintmax_t value, bool neg, - unsigned log2base, unsigned prec, unsigned width, - unsigned flags, const char *alphabet) { +int __fmt_ntoa2(int out(const char *, void *, size_t), void *arg, + uintmax_t value, bool neg, unsigned log2base, unsigned prec, + unsigned width, unsigned flags, const char *alphabet) { uintmax_t remainder; unsigned len, count, digit; char buf[BUFFER_SIZE]; @@ -130,7 +127,7 @@ int __fmt_ntoa2(int out(long, void *), void *arg, uintmax_t value, bool neg, flags); } -int __fmt_ntoa(int out(long, void *), void *arg, va_list va, +int __fmt_ntoa(int out(const char *, void *, size_t), void *arg, va_list va, unsigned char signbit, unsigned long log2base, unsigned long prec, unsigned long width, unsigned char flags, const char *lang) { diff --git a/libc/fmt/pad.c b/libc/fmt/pad.c index e6d50dcd3..c489c745f 100644 --- a/libc/fmt/pad.c +++ b/libc/fmt/pad.c @@ -18,8 +18,9 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/fmt/fmts.h" -int __fmt_pad(int out(long, void *), void *arg, unsigned long n) { +int __fmt_pad(int out(const char *, void *, size_t), void *arg, + unsigned long n) { int i, rc; - for (rc = i = 0; i < n; ++i) rc |= out(' ', arg); + for (rc = i = 0; i < n; ++i) rc |= out(" ", arg, 1); return rc; } diff --git a/libc/fmt/stoa.c b/libc/fmt/stoa.c index 04ae8de42..d189dffa4 100644 --- a/libc/fmt/stoa.c +++ b/libc/fmt/stoa.c @@ -16,9 +16,12 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/bits/bits.h" +#include "libc/bits/safemacros.internal.h" #include "libc/bits/weaken.h" #include "libc/fmt/fmts.h" #include "libc/fmt/internal.h" +#include "libc/nexgen32e/bsr.h" #include "libc/nexgen32e/tinystrlen.internal.h" #include "libc/str/str.h" #include "libc/str/thompike.h" @@ -26,56 +29,50 @@ #include "libc/str/utf16.h" #include "libc/unicode/unicode.h" -typedef int (*emit_f)(int (*)(long, void *), void *, wint_t); +typedef int (*out_f)(const char *, void *, size_t); +typedef int (*emit_f)(out_f, void *, uint64_t); -static noinstrument int __fmt_stoa_byte(int f(long, void *), void *a, - wint_t c) { - return f(c, a); +static int __fmt_stoa_byte(out_f out, void *a, uint64_t c) { + char buf[1] = {c}; + return out(buf, a, 1); } -static noinstrument int __fmt_stoa_word(int f(long, void *), void *a, - uint64_t w) { - do { - if (f(w & 0xff, a) == -1) { - return -1; +static int __fmt_stoa_wide(out_f out, void *a, uint64_t w) { + char buf[8]; + if (!isascii(w)) w = tpenc(w); + WRITE64LE(buf, w); + return out(buf, a, w ? (bsr(w) >> 3) + 1 : 1); +} + +static int __fmt_stoa_bing(out_f out, void *a, uint64_t w) { + char buf[8]; + w = tpenc((*weaken(kCp437))[w & 0xFF]); + WRITE64LE(buf, w); + return out(buf, a, w ? (bsr(w) >> 3) + 1 : 1); +} + +static int __fmt_stoa_quoted(out_f out, void *a, uint64_t w) { + char buf[8]; + if (w <= 0x7F) { + if (w < 0x20 || w == 0x7F) { + w = cescapec(w); } - } while ((w >>= 8)); - return 0; -} - -static noinstrument int __fmt_stoa_wide(int f(long, void *), void *a, - wint_t c) { - if (isascii(c)) { - return f(c, a); } else { - return __fmt_stoa_word(f, a, tpenc(c)); + w = tpenc(w); } + WRITE64LE(buf, w); + return out(buf, a, w ? (bsr(w) >> 3) + 1 : 1); } -static noinstrument int __fmt_stoa_bing(int f(long, void *), void *a, - wint_t c) { - return __fmt_stoa_wide(f, a, (*weaken(kCp437))[c]); -} - -static noinstrument int __fmt_stoa_quoted(int f(long, void *), void *a, - wint_t c) { - if (isascii(c)) { - return __fmt_stoa_word(f, a, cescapec(c)); - } else { - return __fmt_stoa_word(f, a, tpenc(c)); - } -} - -static noinstrument int __fmt_stoa_quote(int out(long, void *), void *arg, - unsigned flags, char ch, - unsigned char signbit) { +static int __fmt_stoa_quote(out_f out, void *arg, unsigned flags, char ch, + unsigned char signbit) { if (flags & FLAGS_REPR) { if (signbit == 63) { - if (out('L', arg) == -1) return -1; + if (out("L", arg, 1) == -1) return -1; } else if (signbit == 15) { - if (out('u', arg) == -1) return -1; + if (out("u", arg, 1) == -1) return -1; } - if (out(ch, arg) == -1) return -1; + if (out(&ch, arg, 1) == -1) return -1; } return 0; } @@ -89,23 +86,25 @@ static noinstrument int __fmt_stoa_quote(int out(long, void *), void *arg, * * @see __fmt() */ -int __fmt_stoa(int out(long, void *), void *arg, void *data, +int __fmt_stoa(int out(const char *, void *, size_t), void *arg, void *data, unsigned long flags, unsigned long precision, unsigned long width, unsigned char signbit, unsigned char qchar) { - char *p; wint_t wc; unsigned n; emit_f emit; + char *p, buf[1]; unsigned w, c, pad; bool justdobytes, ignorenul; p = data; if (!p) { p = ((flags & FLAGS_REPR) ? "NULL" : "(null)"); - flags &= ~FLAGS_PRECISION; - flags |= FLAGS_NOQUOTE; signbit = 0; + flags |= FLAGS_NOQUOTE; + if (flags & FLAGS_PRECISION) { + precision = min(strlen(p), precision); + } } else { if (__fmt_stoa_quote(out, arg, flags, qchar, signbit) == -1) return -1; } @@ -215,7 +214,8 @@ int __fmt_stoa(int out(long, void *), void *arg, void *data, } if (!(flags & FLAGS_NOQUOTE) && (flags & FLAGS_REPR)) { - if (out(qchar, arg) == -1) return -1; + buf[0] = qchar; + if (out(buf, arg, 1) == -1) return -1; } return 0; diff --git a/libc/fmt/vsnprintf.c b/libc/fmt/vsnprintf.c index 72d158b98..762684db8 100644 --- a/libc/fmt/vsnprintf.c +++ b/libc/fmt/vsnprintf.c @@ -16,10 +16,10 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/bits/safemacros.internal.h" #include "libc/dce.h" #include "libc/fmt/fmt.h" #include "libc/limits.h" +#include "libc/macros.internal.h" #include "libc/runtime/runtime.h" struct SprintfStr { @@ -28,10 +28,13 @@ struct SprintfStr { size_t n; }; -static noinstrument int vsnprintfputchar(unsigned char c, - struct SprintfStr *str) { - if (str->i < str->n) str->p[str->i] = c; - str->i++; +static int vsnprintfputchar(const char *s, struct SprintfStr *t, size_t n) { + if (t->i + n <= t->n) { + memcpy(t->p + t->i, s, n); + } else if (t->i < t->n) { + memcpy(t->p + t->i, s, t->n - t->i); + } + t->i += n; return 0; } @@ -51,6 +54,6 @@ static noinstrument int vsnprintfputchar(unsigned char c, int(vsnprintf)(char *buf, size_t size, const char *fmt, va_list va) { struct SprintfStr str = {buf, 0, size}; __fmt(vsnprintfputchar, &str, fmt, va); - if (str.n) str.p[min(str.i, str.n - 1)] = '\0'; + if (str.n) str.p[MIN(str.i, str.n - 1)] = '\0'; return str.i; } diff --git a/libc/log/cancolor.c b/libc/log/cancolor.c index 4e77c77bc..6d920897a 100644 --- a/libc/log/cancolor.c +++ b/libc/log/cancolor.c @@ -16,7 +16,10 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/bits/safemacros.internal.h" #include "libc/dce.h" +#include "libc/log/color.internal.h" +#include "libc/log/log.h" #include "libc/runtime/runtime.h" #include "libc/str/str.h" @@ -43,17 +46,8 @@ bool cancolor(void) { static bool once; static bool result; - const char *term; if (!once) { - if (!result) { - if ((term = getenv("TERM"))) { - /* anything but emacs basically */ - result = strcmp(term, "dumb") != 0; - } else { - /* TODO(jart): Why does Mac bash login shell exec nuke TERM? */ - result = IsXnu(); - } - } + result = !!strcmp(nulltoempty(getenv("DONTANSIMEBRO")), "1"); once = true; } return result; diff --git a/libc/log/isterminalinarticulate.c b/libc/log/isterminalinarticulate.c index 7d302ca6b..7d2d597bc 100644 --- a/libc/log/isterminalinarticulate.c +++ b/libc/log/isterminalinarticulate.c @@ -21,9 +21,6 @@ #include "libc/runtime/runtime.h" #include "libc/str/str.h" -/** - * Checks if we're probably running inside Emacs. - */ bool IsTerminalInarticulate(void) { - return strcmp(nulltoempty(getenv("TERM")), "dumb") == 0; + return !strcmp(nulltoempty(getenv("TERM")), "dumb"); } diff --git a/libc/mem/vasprintf.c b/libc/mem/vasprintf.c index 040f11199..f74ca4251 100644 --- a/libc/mem/vasprintf.c +++ b/libc/mem/vasprintf.c @@ -29,26 +29,26 @@ * @see xasprintf() for a better API */ int(vasprintf)(char **strp, const char *fmt, va_list va) { - int wrote; char *p; size_t size; - va_list va2; + va_list vb; + int wrote, rc = -1; if ((*strp = malloc((size = 512)))) { - va_copy(va2, va); + va_copy(vb, va); wrote = (vsnprintf)(*strp, size, fmt, va); - if (wrote == -1) return -1; if (wrote < size) { if ((p = realloc(*strp, wrote + 1))) *strp = p; - return wrote; + rc = wrote; } else { size = wrote + 1; if ((p = realloc(*strp, size))) { *strp = p; - wrote = (vsnprintf)(*strp, size, fmt, va2); + wrote = (vsnprintf)(*strp, size, fmt, vb); assert(wrote == size - 1); - return wrote; + rc = wrote; } } + va_end(vb); } - return -1; + return rc; } diff --git a/libc/nexgen32e/cescapec.S b/libc/nexgen32e/cescapec.S index 3ce5685e6..b7623117f 100644 --- a/libc/nexgen32e/cescapec.S +++ b/libc/nexgen32e/cescapec.S @@ -27,6 +27,8 @@ // @param dil contains byte to escape // @see libc/nexgen32e/cescapec.c cescapec: + .leafprologue + .profilable movzbl %dil,%edi lea -7(%rdi),%ecx cmp $85,%cl @@ -36,28 +38,28 @@ cescapec: jmp *cescapectab(,%rcx,8) .Lanchorpoint: .LBEL: mov $'a',%ah - ret + .leafepilogue .LBS: mov $'b',%ah - ret + .leafepilogue .LHT: mov $'t',%ah - ret + .leafepilogue .LLF: mov $'n',%ah - ret + .leafepilogue .LVT: mov $'v',%ah - ret + .leafepilogue .LFF: mov $'f',%ah - ret + .leafepilogue .LCR: mov $'r',%ah - ret + .leafepilogue .LDQ: mov $'\"',%ah - ret + .leafepilogue .LSQ: mov $'\'',%ah - ret + .leafepilogue .LBSL: mov $'\\',%ah - ret + .leafepilogue #ifdef __STRICT_ANSI__ .LQM: mov $'?',%ah - ret + .leafepilogue #else .LQM: #endif @@ -65,7 +67,7 @@ cescapec: lea -0x20(%rax),%ecx cmp $0x5E,%ecx ja 2f - ret + .leafepilogue 2: and $-64,%eax mov %edi,%ecx and $56,%ecx @@ -75,7 +77,7 @@ cescapec: or %ecx,%edi lea (%rdi,%rax,4),%eax add $'0'<<030|'0'<<020|'0'<<010|'\\',%eax - ret + .leafepilogue .endfn cescapec,globl .initro 300,_init_cescapec diff --git a/libc/nexgen32e/crc32.h b/libc/nexgen32e/crc32.h index 750783f70..7890f980c 100644 --- a/libc/nexgen32e/crc32.h +++ b/libc/nexgen32e/crc32.h @@ -3,6 +3,8 @@ #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ +extern const uint32_t kCrc32cTab[256]; + void crc32init(uint32_t[hasatleast 256], uint32_t); uint32_t crc32c(uint32_t, const void *, size_t); uint32_t crc32_z(uint32_t, const void *, size_t); diff --git a/libc/nexgen32e/memrchr.S b/libc/nexgen32e/memrchr.S index b8296a37a..942b01c10 100644 --- a/libc/nexgen32e/memrchr.S +++ b/libc/nexgen32e/memrchr.S @@ -28,7 +28,8 @@ // @return rax is address of last %sil in %rdi, or NULL // @note AVX2 requires Haswell (2014+) or Excavator (2015+) // @asyncsignalsafe -memrchr:.leafprologue +memrchr: + .leafprologue .profilable #if !IsTiny() cmp $32,%rdx diff --git a/libc/runtime/ftrace.c b/libc/runtime/ftrace.c index 1ccf2e9c0..0fa7538fe 100644 --- a/libc/runtime/ftrace.c +++ b/libc/runtime/ftrace.c @@ -106,44 +106,12 @@ privileged noasan void ftrace(void) { noreentry = 0; } -/** - * Enables plaintext function tracing if `--ftrace` flag is passed. - * - * The `--ftrace` CLI arg is removed before main() is called. This code - * is intended for diagnostic purposes and assumes binaries are - * trustworthy and stack isn't corrupted. Logging plain text allows - * program structure to easily be visualized and hotspots identified w/ - * `sed | sort | uniq -c | sort`. A compressed trace can be made by - * appending `--ftrace 2>&1 | gzip -4 >trace.gz` to the CLI arguments. - * - * @see libc/runtime/_init.S for documentation - */ -textstartup int ftrace_init(int argc, char *argv[]) { - int i; - bool foundflag; - foundflag = false; - for (i = 1; i <= argc; ++i) { - if (!foundflag) { - if (argv[i]) { - if (strcmp(argv[i], "--ftrace") == 0) { - foundflag = true; - } else if (strcmp(argv[i], "----ftrace") == 0) { - strcpy(argv[i], "--ftrace"); - } - } - } else { - argv[i - 1] = argv[i]; - } +textstartup void ftrace_install(void) { + g_buf[0] = '+'; + g_buf[1] = ' '; + if ((g_symbols = OpenSymbolTable(FindDebugBinary()))) { + __hook(ftrace_hook, g_symbols); + } else { + write(2, "error: --ftrace needs the concomitant .com.dbg binary\n", 54); } - if (foundflag) { - --argc; - g_buf[0] = '+'; - g_buf[1] = ' '; - if ((g_symbols = OpenSymbolTable(FindDebugBinary()))) { - __hook(ftrace_hook, g_symbols); - } else { - write(2, "error: --ftrace needs the concomitant .com.dbg binary\n", 54); - } - } - return argc; } diff --git a/libc/runtime/ftraceinit.c b/libc/runtime/ftraceinit.c new file mode 100644 index 000000000..cd6af1c9f --- /dev/null +++ b/libc/runtime/ftraceinit.c @@ -0,0 +1,56 @@ +/*-*- 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/runtime/runtime.h" +#include "libc/str/str.h" + +/** + * Enables plaintext function tracing if `--ftrace` flag is passed. + * + * The `--ftrace` CLI arg is removed before main() is called. This code + * is intended for diagnostic purposes and assumes binaries are + * trustworthy and stack isn't corrupted. Logging plain text allows + * program structure to easily be visualized and hotspots identified w/ + * `sed | sort | uniq -c | sort`. A compressed trace can be made by + * appending `--ftrace 2>&1 | gzip -4 >trace.gz` to the CLI arguments. + * + * @see libc/runtime/_init.S for documentation + */ +textstartup int ftrace_init(int argc, char *argv[]) { + int i; + bool foundflag; + foundflag = false; + for (i = 1; i <= argc; ++i) { + if (!foundflag) { + if (argv[i]) { + if (strcmp(argv[i], "--ftrace") == 0) { + foundflag = true; + } else if (strcmp(argv[i], "----ftrace") == 0) { + strcpy(argv[i], "--ftrace"); + } + } + } else { + argv[i - 1] = argv[i]; + } + } + if (foundflag) { + --argc; + ftrace_install(); + } + return argc; +} diff --git a/libc/runtime/runtime.h b/libc/runtime/runtime.h index 96863de54..1f230596b 100644 --- a/libc/runtime/runtime.h +++ b/libc/runtime/runtime.h @@ -88,6 +88,7 @@ void _weakfree(void *); void free_s(void *) paramsnonnull() libcesque; int close_s(int *) paramsnonnull() libcesque; int OpenExecutable(void); +void ftrace_install(void); COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/libc/stdio/fwrite.c b/libc/stdio/fwrite.c index 37b0a85db..395f0d41f 100644 --- a/libc/stdio/fwrite.c +++ b/libc/stdio/fwrite.c @@ -17,6 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" +#include "libc/calls/internal.h" #include "libc/calls/struct/iovec.h" #include "libc/errno.h" #include "libc/fmt/conv.h" @@ -28,30 +29,6 @@ #include "libc/str/str.h" #include "libc/sysv/consts/o.h" -static ssize_t WritevAll(int fd, struct iovec *iov, int iovlen) { - ssize_t rc; - size_t wrote; - do { - if ((rc = writev(fd, iov, iovlen)) != -1) { - wrote = rc; - do { - if (wrote >= iov->iov_len) { - wrote -= iov->iov_len; - ++iov; - --iovlen; - } else { - iov->iov_base = (char *)iov->iov_base + wrote; - iov->iov_len -= wrote; - wrote = 0; - } - } while (wrote); - } else if (errno != EINTR) { - return -1; - } - } while (iovlen); - return 0; -} - /** * Writes data to stream. * @@ -102,7 +79,7 @@ size_t fwrite(const void *data, size_t stride, size_t count, FILE *f) { iov[1].iov_base = data; iov[1].iov_len = n; n += f->beg; - if (WritevAll(f->fd, iov, 2) == -1) { + if (WritevUninterruptible(f->fd, iov, 2) == -1) { f->state = errno; return 0; } diff --git a/libc/stdio/vfprintf.c b/libc/stdio/vfprintf.c index cd2c0a2fe..96b354013 100644 --- a/libc/stdio/vfprintf.c +++ b/libc/stdio/vfprintf.c @@ -16,6 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/calls.h" #include "libc/fmt/fmt.h" #include "libc/limits.h" #include "libc/stdio/stdio.h" @@ -26,9 +27,17 @@ struct state { int n; }; -static noinstrument int vfprintfputchar(int c, struct state *st) { - st->n++; - return fputc(c, st->f); +static int vfprintfputchar(const char *s, struct state *t, size_t n) { + if (n) { + if (n == 1 && *s != '\n' && t->f->beg < t->f->size && + t->f->bufmode != _IONBF) { + t->f->buf[t->f->beg++] = *s; + } else if (!fwrite(s, 1, n, t->f)) { + return -1; + } + t->n += n; + } + return 0; } int(vfprintf)(FILE *f, const char *fmt, va_list va) { diff --git a/libc/str/crc32c-pure.c b/libc/str/crc32c-pure.c deleted file mode 100644 index df67d6ee5..000000000 --- a/libc/str/crc32c-pure.c +++ /dev/null @@ -1,79 +0,0 @@ -/*-*- 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 2020 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/nexgen32e/crc32.h" - -extern const uint32_t kCrc32cTab[256]; - -/** - * Computes Castagnoli CRC-32 on old computers. - */ -uint32_t crc32c_pure(uint32_t init, const void *data, size_t size) { - const unsigned char *p = data; - uint32_t h = init ^ 0xffffffff; - unsigned i; - for (i = 0; i < size; i++) { - h = h >> 8 ^ kCrc32cTab[(h & 0xff) ^ p[i]]; - } - return h ^ 0xffffffff; -} - -/* - bench_crc32c_pure for #c per n where c ≈ 0.293ns - N x1 x8 x64 mBps - ------------------------------------------------------------ - 1 4305.000 91.375 44.203 74 - 1 75.000 55.875 44.703 73 - 2 46.500 35.188 24.617 132 - 3 40.333 26.625 19.193 169 - 4 32.250 19.969 16.215 200 - 7 18.429 15.089 12.033 270 - 8 20.625 13.547 11.607 280 - 15 15.667 10.775 9.589 339 - 16 17.562 10.695 9.419 345 - 31 12.226 8.891 8.317 391 - 32 13.219 8.480 8.078 402 - 63 9.571 8.065 7.731 420 - 64 9.672 7.955 7.633 426 - 127 8.433 7.548 7.329 443 - 128 8.492 7.528 7.352 442 - 255 7.557 7.366 7.239 449 - 256 7.699 7.342 7.305 445 - 511 7.376 7.243 7.223 450 - 512 7.408 7.233 7.225 450 - 1023 7.188 7.192 7.098 458 - 1024 7.171 7.194 7.097 458 - 2047 7.130 7.172 7.085 459 - 2048 7.117 7.170 7.169 453 - 4095 7.063 7.076 7.085 459 - 4096 7.078 7.161 7.081 459 - 8191 7.041 7.095 7.055 461 - 8192 7.051 7.098 7.087 459 - 16383 7.039 7.114 7.067 460 - 16384 6.876 6.931 7.133 456 - 32767 7.055 7.108 7.290 446 - 32768 6.868 6.887 6.974 466 - 65535 6.984 6.885 6.967 467 - 65536 6.877 6.924 10.994 296 - 131071 7.166 7.141 7.011 464 - 131072 6.853 6.971 7.694 422 - 262143 6.853 7.213 7.406 439 - 262144 6.852 6.968 7.290 446 - 524287 7.398 7.389 7.166 454 - 524288 6.851 7.094 7.159 454 -*/ diff --git a/libc/str/crc32c-sse42.c b/libc/str/crc32c-sse42.c deleted file mode 100644 index 7cb706974..000000000 --- a/libc/str/crc32c-sse42.c +++ /dev/null @@ -1,95 +0,0 @@ -/*-*- 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 2020 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/str/internal.h" - -/** - * Hashes data with hardware acceleration at 10GBps. - * @note needs Nehalem+ c. 2008 or Bulldozer+ c. 2011 - */ -optimizespeed uint32_t crc32c_sse42(uint32_t init, const void *data, size_t n) { - const unsigned char *p = (const unsigned char *)data; - const unsigned char *pe = (const unsigned char *)data + n; - uint32_t h = init ^ 0xffffffff; - if (n >= 16 + 8) { - while ((uintptr_t)p & 7) asm("crc32b\t%1,%0" : "+r"(h) : "rm"(*p++)); - uint64_t hl = h; - while (p < pe - 16ul) { - asm("crc32q\t%1,%0" : "+r"(hl) : "rm"(*(const uint64_t *)p)); - p += 8; - asm("crc32q\t%1,%0" : "+r"(hl) : "rm"(*(const uint64_t *)p)); - p += 8; - } - h = (uint32_t)hl; - } - while (p < pe) asm("crc32b\t%1,%0" : "+r"(h) : "rm"(*p++)); - return h ^ 0xffffffff; -} - -/* - bench_crc32c_sse42 for #c per n where c ≈ 0.293ns - N x1 x8 x64 mBps - ------------------------------------------------------------ - 1 877.000 43.375 40.359 81 - 1 45.000 39.625 40.484 80 - 2 34.500 27.562 20.461 159 - 3 23.000 16.708 14.245 228 - 4 18.250 13.094 11.449 284 - 7 10.429 8.339 8.185 397 - 8 42.125 8.734 6.850 475 - 15 9.400 5.375 4.884 665 - 16 7.312 5.070 4.882 666 - 31 5.258 2.923 2.680 1213 - 32 3.969 2.676 2.562 1269 - 63 3.095 1.581 1.428 2276 - 64 2.234 1.623 1.478 2199 - 127 1.205 0.901 0.900 3610 - 128 1.164 0.960 0.915 3552 - 255 0.922 0.651 0.618 5260 - 256 0.715 0.650 0.609 5341 - 511 0.558 0.482 0.477 6819 - 512 0.529 0.475 0.469 6932 - 1023 0.425 0.400 0.396 8204 - 1024 0.417 0.392 0.388 8383 - 2047 0.367 0.355 0.353 9199 - 2048 0.374 0.366 0.364 8929 - 4095 0.351 0.338 0.337 9644 - 4096 0.353 0.338 0.338 9624 - 8191 0.335 0.338 0.337 9641 - 8192 0.335 0.329 0.329 9870 - 16383 0.336 0.325 0.325 10011 - 16384 0.336 0.326 0.375 8666 - 32767 0.329 0.323 0.323 10070 - 32768 0.327 0.324 0.323 10062 - 65535 0.322 0.322 0.322 10103 - 65536 0.321 0.322 0.322 10102 - 131071 0.322 0.321 0.321 10125 - 131072 0.321 0.321 0.321 10124 - 262143 0.322 0.321 0.335 9699 - 262144 0.321 0.321 0.321 10134 - 524287 0.321 0.321 0.499 6516 - 524288 0.321 0.321 0.339 9575 - 1048575 0.322 0.321 0.322 10095 - 1048576 0.320 1.001 0.323 10048 - 2097151 0.325 0.321 0.322 10086 - 2097152 0.330 0.320 0.323 10076 - 4194303 0.331 0.322 0.321 10128 - 4194304 0.332 0.321 0.325 10004 - 8388607 0.334 0.332 0.331 9829 - 8388608 0.334 0.329 0.327 9934 -*/ diff --git a/libc/str/crc32c.c b/libc/str/crc32c.c index a02636733..b41b9340e 100644 --- a/libc/str/crc32c.c +++ b/libc/str/crc32c.c @@ -22,16 +22,29 @@ /** * Computes 32-bit Castagnoli Cyclic Redundancy Check. * - * @param h is the initial hash value (0 is fine) - * @param p points to the data - * @param n is the byte size of data + * @param init is the initial hash value + * @param data points to the data + * @param size is the byte size of data * @return eax is the new hash value * @note Used by ISCSI, TensorFlow, etc. */ -uint32_t crc32c(uint32_t h, const void *p, size_t n) { +uint32_t crc32c(uint32_t init, const void *data, size_t size) { + uint64_t h; + const unsigned char *p, *pe; + p = data; + pe = p + size; + h = init ^ 0xffffffff; if (X86_HAVE(SSE4_2)) { - return crc32c_sse42(h, p, n); + for (; p + 8 <= pe; p += 8) { + asm("crc32q\t%1,%0" : "+r"(h) : "rm"(*(const uint64_t *)p)); + } + while (p < pe) { + asm("crc32b\t%1,%0" : "+r"(h) : "rm"(*p++)); + } } else { - return crc32c_pure(h, p, n); + while (p < pe) { + h = h >> 8 ^ kCrc32cTab[(h & 0xff) ^ *p++]; + } } + return h ^ 0xffffffff; } diff --git a/libc/str/stpcpy.c b/libc/str/stpcpy.c index 0b5208189..1cf3915d4 100644 --- a/libc/str/stpcpy.c +++ b/libc/str/stpcpy.c @@ -20,7 +20,7 @@ #include "libc/intrin/pmovmskb.h" #include "libc/str/str.h" -static noasan size_t stpcpy_sse2(char *d, const char *s, size_t i) { +static inline noasan size_t stpcpy_sse2(char *d, const char *s, size_t i) { uint8_t v1[16], v2[16], vz[16]; for (;;) { memset(vz, 0, 16); diff --git a/net/http/categorizeip.c b/net/http/categorizeip.c index b1c5bb1ad..71321b410 100644 --- a/net/http/categorizeip.c +++ b/net/http/categorizeip.c @@ -26,11 +26,11 @@ */ int CategorizeIp(uint32_t x) { int a; - if (IsAnonymousIp(x)) return kIpAnonymous; - if (IsMulticastIp(x)) return kIpMulticast; if (IsLoopbackIp(x)) return kIpLoopback; if (IsPrivateIp(x)) return kIpPrivate; - if (IsTestnetIp(x)) return kIpTestnet; + if (IsMulticastIp(x)) return kIpMulticast; + if (IsAnonymousIp(x)) return kIpAnonymous; /* order matters */ + if (IsTestnetIp(x)) return kIpTestnet; /* order matters */ if (IsAfrinicIp(x)) return kIpAfrinic; if (IsLacnicIp(x)) return kIpLacnic; if (IsApnicIp(x)) return kIpApnic; diff --git a/net/http/gethttpheader.inc b/net/http/gethttpheader.inc index 51ec473b7..df72e6e38 100644 --- a/net/http/gethttpheader.inc +++ b/net/http/gethttpheader.inc @@ -1,6 +1,7 @@ /* ANSI-C code produced by gperf version 3.1 */ /* Command-line: gperf gethttpheader.gperf */ /* Computed positions: -k'3-4,10' */ +/* clang-format off */ #if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \ && ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) \ @@ -71,7 +72,7 @@ static unsigned char gperf_downcase[256] = #ifndef GPERF_CASE_STRNCMP #define GPERF_CASE_STRNCMP 1 -static int +static inline int gperf_case_strncmp (register const char *s1, register const char *s2, register size_t n) { for (; n > 0;) @@ -152,7 +153,7 @@ hash (register const char *str, register size_t len) return hval; } -const struct thatispacked HttpHeaderSlot * +static inline const struct thatispacked HttpHeaderSlot * LookupHttpHeader (register const char *str, register size_t len) { static const struct thatispacked HttpHeaderSlot wordlist[] = diff --git a/net/http/gethttpmethod.inc b/net/http/gethttpmethod.inc index 38d487254..8d45d2fd9 100644 --- a/net/http/gethttpmethod.inc +++ b/net/http/gethttpmethod.inc @@ -1,6 +1,7 @@ /* ANSI-C code produced by gperf version 3.1 */ /* Command-line: gperf gethttpmethod.gperf */ /* Computed positions: -k'1-2' */ +/* clang-format off */ #if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \ && ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) \ @@ -71,7 +72,7 @@ static unsigned char gperf_downcase[256] = #ifndef GPERF_CASE_STRNCMP #define GPERF_CASE_STRNCMP 1 -static int +static inline int gperf_case_strncmp (register const char *s1, register const char *s2, register size_t n) { for (; n > 0;) @@ -131,7 +132,7 @@ hash (register const char *str, register size_t len) return len + asso_values[(unsigned char)str[1]] + asso_values[(unsigned char)str[0]]; } -const struct HttpMethodSlot * +static inline const struct HttpMethodSlot * LookupHttpMethod (register const char *str, register size_t len) { static const struct HttpMethodSlot wordlist[] = @@ -153,7 +154,7 @@ LookupHttpMethod (register const char *str, register size_t len) {""}, #line 27 "gethttpmethod.gperf" {"NOTIFY", kHttpNotify}, -#line 20 "gethttpmethod.gperf" +#line 19 "gethttpmethod.gperf" {"OPTIONS", kHttpOptions}, {""}, #line 22 "gethttpmethod.gperf" @@ -162,7 +163,7 @@ LookupHttpMethod (register const char *str, register size_t len) {"MERGE", kHttpMerge}, #line 29 "gethttpmethod.gperf" {"REPORT", kHttpReport}, -#line 19 "gethttpmethod.gperf" +#line 20 "gethttpmethod.gperf" {"CONNECT", kHttpConnect}, {""}, #line 26 "gethttpmethod.gperf" diff --git a/net/http/isacceptablehost.c b/net/http/isacceptablehost.c index 70e9c9115..f9e2faa9a 100644 --- a/net/http/isacceptablehost.c +++ b/net/http/isacceptablehost.c @@ -19,6 +19,26 @@ #include "libc/str/str.h" #include "net/http/http.h" +// -_0-9A-Za-z +static const char kHostChars[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x00 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x10 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, // 0x20 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, // 0x30 + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x40 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, // 0x50 + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x60 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, // 0x70 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x80 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x90 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xa0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xb0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xc0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xd0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xe0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xf0 +}; + /** * Returns true if host seems legit. * @@ -49,33 +69,35 @@ */ bool IsAcceptableHost(const char *s, size_t n) { size_t i; - bool isip; int c, b, j; if (n == -1) n = s ? strlen(s) : 0; if (!n) return true; - for (isip = true, b = j = i = 0; i < n; ++i) { + for (b = j = i = 0; i < n; ++i) { c = s[i] & 255; - if (c == '.' && (!i || s[i - 1] == '.')) { - return false; - } else if (!(isalnum(c) || c == '-' || c == '_' || c == '.')) { - return false; - } - if (isip) { - if (isdigit(c)) { - b *= 10; - b += c - '0'; - if (b > 255) { + if (isdigit(c)) { + b *= 10; + b += c - '0'; + if (b > 255) { + return false; + } + } else if (c == '.') { + if (!i || s[i - 1] == '.') return false; + b = 0; + ++j; + } else { + for (;;) { + if (!kHostChars[c] && (c != '.' || (!i || s[i - 1] == '.'))) { return false; } - } else if (c == '.') { - b = 0; - ++j; - } else { - isip = false; + if (++i < n) { + c = s[i] & 255; + } else { + return true; + } } } } - if (isip && j != 3) return false; + if (j != 3) return false; if (i && s[i - 1] == '.') return false; return true; } diff --git a/net/http/ispublicip.c b/net/http/ispublicip.c index 5869fc906..fa65167af 100644 --- a/net/http/ispublicip.c +++ b/net/http/ispublicip.c @@ -20,7 +20,10 @@ /** * Returns true if IPv4 address can come from the Internet. + * + * We intentionally omit TEST-NET here which can be used to simulate + * public Internet traffic using non-Internet IPs. */ bool IsPublicIp(uint32_t x) { - return !IsLoopbackIp(x) && !IsPrivateIp(x) && !IsTestnetIp(x); + return !IsLoopbackIp(x) && !IsPrivateIp(x); } diff --git a/net/http/parsehttprequest.c b/net/http/parsehttprequest.c index 06bc8d5e7..6fea1c1fb 100644 --- a/net/http/parsehttprequest.c +++ b/net/http/parsehttprequest.c @@ -75,7 +75,7 @@ void DestroyHttpRequest(struct HttpRequest *r) { * fragmented. If a message is valid but incomplete, this function will * return zero so that it can be resumed as soon as more data arrives. * - * This parser takes about 500 nanoseconds to parse a 403 byte Chrome + * This parser takes about 400 nanoseconds to parse a 403 byte Chrome * HTTP request under MODE=rel on a Core i9 which is about three cycles * per byte or a gigabyte per second of throughput per core. * diff --git a/test/libc/fmt/itoa64radix10_test.c b/test/libc/fmt/itoa64radix10_test.c index 970e87ead..c9387aa0f 100644 --- a/test/libc/fmt/itoa64radix10_test.c +++ b/test/libc/fmt/itoa64radix10_test.c @@ -40,6 +40,8 @@ TEST(uint64toarray_radix10, test) { char buf[21]; EXPECT_EQ(1, uint64toarray_radix10(0, buf)); EXPECT_STREQ("0", buf); + EXPECT_EQ(4, uint64toarray_radix10(1024, buf)); + EXPECT_STREQ("1024", buf); EXPECT_EQ(20, uint64toarray_radix10(UINT64_MAX, buf)); EXPECT_STREQ("18446744073709551615", buf); EXPECT_EQ(19, uint64toarray_radix10(INT64_MIN, buf)); diff --git a/test/libc/fmt/palandprintf_test.c b/test/libc/fmt/palandprintf_test.c index f1b65ead9..a9b5750b5 100644 --- a/test/libc/fmt/palandprintf_test.c +++ b/test/libc/fmt/palandprintf_test.c @@ -38,8 +38,9 @@ #include "libc/testlib/testlib.h" #include "libc/x/x.h" -static char buffer[128]; -#define Format(...) gc(xasprintf(__VA_ARGS__)) +char buffer[1000]; +/* #define Format(...) gc(xasprintf(__VA_ARGS__)) */ +#define Format(...) (snprintf(buffer, sizeof(buffer), __VA_ARGS__), buffer) TEST(sprintf, test_space_flag) { EXPECT_STREQ(" 42", Format("% d", 42)); @@ -593,32 +594,14 @@ TEST(snprintf, testFixedWidthString_wontOverrunInput) { TEST(snprintf, testFixedWidthStringIsNull_wontOverrunBuffer) { int N = 3; char *buf = malloc(N + 1); - EXPECT_EQ(6, snprintf(buf, N + 1, "%.*s", pushpop(N), pushpop(NULL))); - EXPECT_BINEQ(u"(nu ", buf); - EXPECT_EQ(6, snprintf(buf, N + 1, "%#.*s", pushpop(N), pushpop(NULL))); - EXPECT_BINEQ(u"(nu ", buf); - EXPECT_EQ(4, snprintf(buf, N + 1, "%`.*s", pushpop(N), pushpop(NULL))); - EXPECT_BINEQ(u"NUL ", buf); - EXPECT_EQ(4, snprintf(buf, N + 1, "%`#.*s", pushpop(N), pushpop(NULL))); - EXPECT_BINEQ(u"NUL ", buf); - free(buf); -} - -TEST(snprintf, testFixedWidthStringIsNull_wontLeakMemory) { - int N = 16; - char *buf = malloc(N + 1); - memset(buf, 0, N + 1); - EXPECT_EQ(6, snprintf(buf, N + 1, "%.*s", pushpop(N), pushpop(NULL))); - EXPECT_BINEQ(u"(null)           ", buf); - memset(buf, 0, N + 1); - EXPECT_EQ(6, snprintf(buf, N + 1, "%#.*s", pushpop(N), pushpop(NULL))); - EXPECT_BINEQ(u"(null)           ", buf); - memset(buf, 0, N + 1); - EXPECT_EQ(4, snprintf(buf, N + 1, "%`.*s", pushpop(N), pushpop(NULL))); - EXPECT_BINEQ(u"NULL             ", buf); - memset(buf, 0, N + 1); - EXPECT_EQ(4, snprintf(buf, N + 1, "%`#.*s", pushpop(N), pushpop(NULL))); - EXPECT_BINEQ(u"NULL             ", buf); + EXPECT_EQ(3, snprintf(buf, N + 1, "%.*s", pushpop(N), pushpop(NULL))); + EXPECT_STREQ("(nu", buf); + EXPECT_EQ(3, snprintf(buf, N + 1, "%#.*s", pushpop(N), pushpop(NULL))); + EXPECT_STREQ("(nu", buf); + EXPECT_EQ(3, snprintf(buf, N + 1, "%`'.*s", pushpop(N), pushpop(NULL))); + EXPECT_STREQ("NUL", buf); + EXPECT_EQ(3, snprintf(buf, N + 1, "%`#.*s", pushpop(N), pushpop(NULL))); + EXPECT_STREQ("NUL", buf); free(buf); } @@ -640,7 +623,9 @@ TEST(palandprintf, precisionStillRespectsNulTerminatorIfNotEscOrRepr) { } BENCH(palandprintf, bench) { + EZBENCH2("ascii", donothing, Format(VEIL("r", "hiuhcreohucreo"))); EZBENCH2("ascii %s", donothing, Format("%s", VEIL("r", "hiuhcreohucreo"))); + EZBENCH2("ascii %`'s", donothing, Format("%`'s", VEIL("r", "hiuhcreohucre"))); EZBENCH2("utf8 %s", donothing, Format("%s", VEIL("r", "hi (╯°□°)╯"))); EZBENCH2("snprintf %hs", donothing, Format("%hs", VEIL("r", u"hi (╯°□°)╯"))); EZBENCH2("snprintf %ls", donothing, Format("%ls", VEIL("r", L"hi (╯°□°)╯"))); @@ -648,6 +633,8 @@ BENCH(palandprintf, bench) { EZBENCH2("23 %d", donothing, Format("%d", VEIL("r", 23))); EZBENCH2("INT_MIN %x", donothing, Format("%x", VEIL("r", INT_MIN))); EZBENCH2("INT_MIN %d", donothing, Format("%d", VEIL("r", INT_MIN))); + EZBENCH2("LONG_MIN %x", donothing, Format("%lx", VEIL("r", LONG_MIN))); + EZBENCH2("LONG_MIN %d", donothing, Format("%ld", VEIL("r", LONG_MIN))); EZBENCH2("23 int64toarray", donothing, int64toarray_radix10(23, buffer)); EZBENCH2("INT_MIN int64toarray", donothing, int64toarray_radix10(INT_MIN, buffer)); diff --git a/test/libc/str/crc32c_test.c b/test/libc/str/crc32c_test.c index 6e98cb5d4..27984f7d9 100644 --- a/test/libc/str/crc32c_test.c +++ b/test/libc/str/crc32c_test.c @@ -20,6 +20,8 @@ #include "libc/nexgen32e/crc32.h" #include "libc/nexgen32e/x86feature.h" #include "libc/str/str.h" +#include "libc/testlib/ezbench.h" +#include "libc/testlib/hyperion.h" #include "libc/testlib/testlib.h" #define FANATICS "Fanatics" @@ -40,12 +42,6 @@ TEST(crc32c, test) { strlen(hyperion) - strlen(FANATICS))); } -TEST(crc32c_pure, test) { - EXPECT_EQ(0, crc32c_pure(0, "", 0)); - EXPECT_EQ(crc32c_pure(0, "hello", 5), crc32c_pure(0, "hello", 5)); - EXPECT_EQ(0xe3069283, crc32c_pure(0, "123456789", 9)); - EXPECT_EQ(0x6d6eefba, crc32c_pure(0, hyperion, strlen(hyperion))); - EXPECT_EQ(0x6d6eefba, crc32c_pure(crc32c_pure(0, FANATICS, strlen(FANATICS)), - hyperion + strlen(FANATICS), - strlen(hyperion) - strlen(FANATICS))); +BENCH(crc32c, bench) { + EZBENCH2("crc32c", donothing, crc32c(0, kHyperion, kHyperionSize)); } diff --git a/test/net/http/isacceptablehost_test.c b/test/net/http/isacceptablehost_test.c index 28cb2fad1..d212afd20 100644 --- a/test/net/http/isacceptablehost_test.c +++ b/test/net/http/isacceptablehost_test.c @@ -37,7 +37,7 @@ TEST(IsAcceptableHost, test) { EXPECT_FALSE(IsAcceptableHost("hello.example\300\200", -1)); EXPECT_FALSE(IsAcceptableHost(".", -1)); EXPECT_FALSE(IsAcceptableHost(".e", -1)); - EXPECT_FALSE(IsAcceptableHost("e.", -1)); + EXPECT_TRUE(IsAcceptableHost("e.", -1)); EXPECT_FALSE(IsAcceptableHost(".hi.example", -1)); EXPECT_FALSE(IsAcceptableHost("hi..example", -1)); EXPECT_TRUE(IsAcceptableHost("hi-there.example", -1)); @@ -126,4 +126,6 @@ BENCH(IsAcceptableHost, bench) { EZBENCH2("IsAcceptablePort 80", donothing, IsAcceptablePort("80", 2)); EZBENCH2("ParseForwarded 80", donothing, ParseForwarded("203.0.113.42:31337", 20, &ip, &port)); + EZBENCH2("IsAcceptableHost foo.example", donothing, + IsAcceptableHost("foo.example:31337", 17)); } diff --git a/tool/net/net.mk b/tool/net/net.mk index 87e0e66c0..46a53a7e3 100644 --- a/tool/net/net.mk +++ b/tool/net/net.mk @@ -80,8 +80,12 @@ o/$(MODE)/tool/net/redbean.com: \ @$(COMPILE) -ADD -T$@ dd if=$@ of=o/$(MODE)/tool/net/.ape bs=64 count=11 conv=notrunc 2>/dev/null @$(COMPILE) -AZIP -T$@ zip -qj $@ o/$(MODE)/tool/net/.ape tool/net/.init.lua tool/net/.reload.lua tool/net/favicon.ico tool/net/redbean.png +o/$(MODE)/tool/net/redbean-demo.com.dbg: \ + o/$(MODE)/tool/net/redbean.com.dbg + @$(COMPILE) -ACP -T$@ cp $< $@ + o/$(MODE)/tool/net/redbean-demo.com: \ - o/$(MODE)/tool/net/redbean.com.dbg \ + o/$(MODE)/tool/net/redbean-demo.com.dbg \ tool/net/net.mk \ tool/net/.init.lua \ tool/net/.reload.lua \ @@ -114,16 +118,6 @@ o/$(MODE)/tool/net/redbean-static.com: \ @$(COMPILE) -ADD -T$@ dd if=$@ of=o/$(MODE)/tool/net/.ape bs=64 count=11 conv=notrunc 2>/dev/null @$(COMPILE) -AZIP -T$@ zip -qj $@ o/$(MODE)/tool/net/.ape tool/net/favicon.ico tool/net/redbean.png -o/$(MODE)/tool/net/redbean-bench.com.dbg: \ - $(TOOL_NET_DEPS) \ - o/$(MODE)/tool/net/redbean.o \ - o/$(MODE)/tool/net/index.html.zip.o \ - o/$(MODE)/tool/net/redbean.lua.zip.o \ - o/$(MODE)/tool/net/net.pkg \ - $(CRT) \ - $(APE) - @$(APELINK) - o/$(MODE)/tool/net/redbean-static.com.dbg: \ $(TOOL_NET_DEPS) \ o/$(MODE)/tool/net/redbean-static.o \ diff --git a/tool/net/redbean.c b/tool/net/redbean.c index 082d9c7ac..34dc1fa73 100644 --- a/tool/net/redbean.c +++ b/tool/net/redbean.c @@ -33,6 +33,7 @@ #include "libc/nexgen32e/bsr.h" #include "libc/nexgen32e/crc32.h" #include "libc/runtime/clktck.h" +#include "libc/runtime/runtime.h" #include "libc/sock/sock.h" #include "libc/stdio/stdio.h" #include "libc/sysv/consts/af.h" @@ -71,7 +72,11 @@ #define HASH_LOAD_FACTOR /* 1. / */ 4 #define DEFAULT_PORT 8080 +#define read(F, P, N) readv(F, &(struct iovec){P, N}, 1) +#define Hash(P, N) max(1, crc32c(0, P, N)); #define LockInc(P) asm volatile("lock inc%z0\t%0" : "=m"(*(P))) +#define AppendCrlf(P) mempcpy(P, "\r\n", 2) +#define HasHeader(H) (!!msg.headers[H].a) #define HeaderData(H) (inbuf.p + msg.headers[H].a) #define HeaderLength(H) (msg.headers[H].b - msg.headers[H].a) #define HeaderEqualCase(H, S) \ @@ -171,7 +176,7 @@ static const char kRegCode[][9] = { }; struct Buffer { - size_t n; + size_t n, c; char *p; }; @@ -327,6 +332,7 @@ static bool istext; static bool zombied; static bool gzipped; static bool branded; +static bool funtrace; static bool meltdown; static bool heartless; static bool printport; @@ -385,6 +391,7 @@ static struct Buffer effectivepath; static struct Url url; static struct HttpRequest msg; +static char slashpath[PATH_MAX]; static long double startread; static long double lastrefresh; @@ -399,7 +406,7 @@ static wontreturn void PrintUsage(FILE *f, int rc) { fprintf(f, "\ SYNOPSIS\n\ \n\ - %s [-hvduzmba] [-p PORT] [-- SCRIPTARGS...]\n\ + %s [-hvduzmbagf] [-p PORT] [-- SCRIPTARGS...]\n\ \n\ DESCRIPTION\n\ \n\ @@ -415,8 +422,11 @@ FLAGS\n\ -m log messages\n\ -b log message body\n\ -a log resource usage\n\ - -g log handler latency\n\ - -H K:V sets http header globally [repeat]\n\ + -g log handler latency\n" +#ifndef TINY +" -f log worker function calls\n" +#endif +" -H K:V sets http header globally [repeat]\n\ -D DIR serve assets from local directory [repeat]\n\ -t MS tunes read and write timeouts [default 30000]\n\ -c SEC configures static asset cache-control headers\n\ @@ -545,8 +555,6 @@ USAGE\n\ then puts the original back once the program loads. If you want\n\ your redbean to follow the platform-local executable convention\n\ then delete the /.ape file from zip.\n\ -\n\ -LEGAL\n\ \n\ redbean contains software licensed ISC, MIT, BSD-2, BSD-3, zlib\n\ which makes it a permissively licensed gift to anyone who might\n\ @@ -664,38 +672,53 @@ static void UseOutput(void) { contentlength = outbuf.n; outbuf.p = 0; outbuf.n = 0; + outbuf.c = 0; } static void DropOutput(void) { free(outbuf.p); outbuf.p = 0; outbuf.n = 0; + outbuf.c = 0; } static void ClearOutput(void) { outbuf.n = 0; } +static void Grow(size_t n) { + do { + if (outbuf.c) { + outbuf.c += outbuf.c >> 1; + } else { + outbuf.c = 16 * 1024; + } + } while (n > outbuf.c); + outbuf.p = xrealloc(outbuf.p, outbuf.c); +} + static void AppendData(const char *data, size_t size) { - outbuf.p = xrealloc(outbuf.p, outbuf.n + size); + size_t n; + n = outbuf.n + size; + if (n > outbuf.c) Grow(n); memcpy(outbuf.p + outbuf.n, data, size); - outbuf.n += size; + outbuf.n = n; } -static void AppendString(const char *s) { - AppendData(s, strlen(s)); -} - -static void AppendFmt(const char *fmt, ...) { +static void Append(const char *fmt, ...) { int n; char *p; - va_list va; + va_list va, vb; va_start(va, fmt); - n = vasprintf(&p, fmt, va); + va_copy(vb, va); + n = vsnprintf(outbuf.p + outbuf.n, outbuf.c - outbuf.n, fmt, va); + if (n >= outbuf.c - outbuf.n) { + Grow(outbuf.n + n + 1); + vsnprintf(outbuf.p + outbuf.n, outbuf.c - outbuf.n, fmt, vb); + } + va_end(vb); va_end(va); - CHECK_NE(-1, n); - AppendData(p, n); - free(p); + outbuf.n += n; } static char *MergePaths(const char *p, size_t n, const char *q, size_t m, @@ -833,10 +856,6 @@ static void DescribeAddress(char buf[32], uint32_t addr, uint16_t port) { *p++ = '\0'; } -static bool HasHeader(int h) { - return !!msg.headers[h].a; -} - static void GetServerAddr(uint32_t *ip, uint16_t *port) { *ip = ntohl(serveraddr.sin_addr.s_addr); if (port) *port = ntohs(serveraddr.sin_port); @@ -975,7 +994,7 @@ static void ProgramHeader(const char *s) { static void GetOpts(int argc, char *argv[]) { int opt; - while ((opt = getopt(argc, argv, "azhdugvmbl:p:r:R:H:c:L:P:U:G:B:D:t:")) != + while ((opt = getopt(argc, argv, "azhdugvmbfl:p:r:R:H:c:L:P:U:G:B:D:t:")) != -1) { switch (opt) { case 'v': @@ -1002,6 +1021,9 @@ static void GetOpts(int argc, char *argv[]) { case 'z': printport = true; break; + case 'f': + funtrace = true; + break; case 'k': encouragekeepalive = true; break; @@ -1097,53 +1119,53 @@ static void AppendResourceReport(struct rusage *ru, const char *nl) { long utime, stime; long double ticks; if (ru->ru_maxrss) { - AppendFmt("ballooned to %,ldkb in size%s", ru->ru_maxrss, nl); + Append("ballooned to %,ldkb in size%s", ru->ru_maxrss, nl); } if ((utime = ru->ru_utime.tv_sec * 1000000 + ru->ru_utime.tv_usec) | (stime = ru->ru_stime.tv_sec * 1000000 + ru->ru_stime.tv_usec)) { ticks = ceill((long double)(utime + stime) / (1000000.L / CLK_TCK)); - AppendFmt("needed %,ldµs cpu (%d%% kernel)%s", utime + stime, - (int)((long double)stime / (utime + stime) * 100), nl); + Append("needed %,ldµs cpu (%d%% kernel)%s", utime + stime, + (int)((long double)stime / (utime + stime) * 100), nl); if (ru->ru_idrss) { - AppendFmt("needed %,ldkb memory on average%s", - lroundl(ru->ru_idrss / ticks), nl); + Append("needed %,ldkb memory on average%s", lroundl(ru->ru_idrss / ticks), + nl); } if (ru->ru_isrss) { - AppendFmt("needed %,ldkb stack on average%s", - lroundl(ru->ru_isrss / ticks), nl); + Append("needed %,ldkb stack on average%s", lroundl(ru->ru_isrss / ticks), + nl); } if (ru->ru_ixrss) { - AppendFmt("mapped %,ldkb shared on average%s", - lroundl(ru->ru_ixrss / ticks), nl); + Append("mapped %,ldkb shared on average%s", lroundl(ru->ru_ixrss / ticks), + nl); } } if (ru->ru_minflt || ru->ru_majflt) { - AppendFmt("caused %,ld page faults (%d%% memcpy)%s", - ru->ru_minflt + ru->ru_majflt, - (int)((long double)ru->ru_minflt / - (ru->ru_minflt + ru->ru_majflt) * 100), - nl); + Append("caused %,ld page faults (%d%% memcpy)%s", + ru->ru_minflt + ru->ru_majflt, + (int)((long double)ru->ru_minflt / (ru->ru_minflt + ru->ru_majflt) * + 100), + nl); } if (ru->ru_nvcsw + ru->ru_nivcsw > 1) { - AppendFmt( + Append( "%,ld context switches (%d%% consensual)%s", ru->ru_nvcsw + ru->ru_nivcsw, (int)((long double)ru->ru_nvcsw / (ru->ru_nvcsw + ru->ru_nivcsw) * 100), nl); } if (ru->ru_inblock || ru->ru_oublock) { - AppendFmt("performed %,ld read and %,ld write i/o operations%s", - ru->ru_inblock, ru->ru_oublock, nl); + Append("performed %,ld read and %,ld write i/o operations%s", + ru->ru_inblock, ru->ru_oublock, nl); } if (ru->ru_msgrcv || ru->ru_msgsnd) { - AppendFmt("received %,ld message and sent %,ld%s", ru->ru_msgrcv, - ru->ru_msgsnd, nl); + Append("received %,ld message and sent %,ld%s", ru->ru_msgrcv, + ru->ru_msgsnd, nl); } if (ru->ru_nsignals) { - AppendFmt("received %,ld signals%s", ru->ru_nsignals, nl); + Append("received %,ld signals%s", ru->ru_nsignals, nl); } if (ru->ru_nswap) { - AppendFmt("got swapped %,ld times%s", ru->ru_nswap, nl); + Append("got swapped %,ld times%s", ru->ru_nswap, nl); } } @@ -1237,7 +1259,7 @@ static void ReapZombies(void) { } while (!terminated); } -static ssize_t WritevAll(int fd, struct iovec *iov, int iovlen) { +static inline ssize_t WritevAll(int fd, struct iovec *iov, int iovlen) { ssize_t rc; size_t wrote; do { @@ -1266,13 +1288,6 @@ static ssize_t WritevAll(int fd, struct iovec *iov, int iovlen) { return 0; } -static uint32_t Hash(const void *data, size_t size) { - uint32_t h; - h = crc32c(0, data, size); - if (!h) h = 1; - return h; -} - static bool ClientAcceptsGzip(void) { return msg.version >= 10 && /* RFC1945 § 3.5 */ HeaderHas(&msg, inbuf.p, kHttpAcceptEncoding, "gzip", 4); @@ -1453,23 +1468,16 @@ static struct Asset *GetAsset(const char *path, size_t pathlen) { struct Asset *a; if (!(a = GetAssetFile(path, pathlen))) { if (!(a = GetAssetZip(path, pathlen))) { - if (pathlen > 1 && path[pathlen - 1] != '/') { - path2 = xmalloc(pathlen + 1); - memcpy(mempcpy(path2, path, pathlen), "/", 1); - a = GetAssetZip(path2, pathlen + 1); - free(path2); + if (pathlen > 1 && path[pathlen - 1] != '/' && + pathlen + 1 <= sizeof(slashpath)) { + memcpy(mempcpy(slashpath, path, pathlen), "/", 1); + a = GetAssetZip(slashpath, pathlen + 1); } } } return a; } -static char *AppendCrlf(char *p) { - p[0] = '\r'; - p[1] = '\n'; - return p + 2; -} - static bool MustNotIncludeMessageBody(void) { /* RFC2616 § 4.4 */ return msg.method == kHttpHead || (100 <= statuscode && statuscode <= 199) || statuscode == 204 || statuscode == 304; @@ -1523,19 +1531,9 @@ static char *AppendCache(char *p, int64_t seconds) { return AppendExpires(p, (int64_t)shared->nowish + seconds); } -static bool IsPublic(void) { - uint32_t ip; - GetRemoteAddr(&ip, 0); - return IsPublicIp(ip); -} - static char *AppendServer(char *p, const char *s) { p = stpcpy(p, "Server: "); - if (IsPublic()) { - p = mempcpy(p, s, strchrnul(s, '/') - s); - } else { - p = stpcpy(p, s); - } + p = stpcpy(p, s); return AppendCrlf(p); } @@ -1658,15 +1656,15 @@ static void AppendLogo(void) { struct Asset *a; if ((a = GetAsset("/redbean.png", 12)) && (p = LoadAsset(a, &n))) { q = EncodeBase64(p, n, &n); - AppendString("\r\n"); + Append("\">\r\n"); free(q); free(p); } } -static ssize_t Send(struct iovec *iov, int iovlen) { +static inline ssize_t Send(struct iovec *iov, int iovlen) { ssize_t rc; if ((rc = WritevAll(client, iov, iovlen)) == -1) { if (errno == ECONNRESET) { @@ -1711,11 +1709,11 @@ static char *CommitOutput(char *p) { static char *ServeDefaultErrorPage(char *p, unsigned code, const char *reason) { p = AppendContentType(p, "text/html; charset=ISO-8859-1"); reason = FreeLater(EscapeHtml(reason, -1, 0)); - AppendString("\ + Append("\ \r\n\ "); - AppendFmt("%d %s", code, reason); - AppendString("\ + Append("%d %s", code, reason); + Append("\ \r\n\ \r\n\

\r\n"); AppendLogo(); - AppendFmt("%d %s\r\n", code, reason); - AppendString("

\r\n"); + Append("%d %s\r\n", code, reason); + Append("\r\n"); UseOutput(); return p; } @@ -1734,7 +1732,7 @@ static char *ServeErrorImpl(unsigned code, const char *reason) { char *p, *s; struct Asset *a; LockInc(&shared->errors); - DropOutput(); + ClearOutput(); p = SetStatus(code, reason); s = xasprintf("/%d.html", code); a = GetAsset(s, strlen(s)); @@ -1877,7 +1875,15 @@ static char *ServeAsset(struct Asset *a, const char *path, size_t pathlen) { } else { LockInc(&shared->openfails); WARNF("open(%`'s) failed %s", a->file->path, strerror(errno)); - return ServeError(500, "Internal Server Error"); + if (errno == ENFILE) { + LockInc(&shared->enfiles); + return ServeError(503, "Service Unavailable"); + } else if (errno == EMFILE) { + LockInc(&shared->emfiles); + return ServeError(503, "Service Unavailable"); + } else { + return ServeError(500, "Internal Server Error"); + } } } else { content = ""; @@ -3320,30 +3326,27 @@ static char *HandleRedirect(struct Redirect *r) { static void LogMessage(const char *d, const char *s, size_t n) { size_t n2, n3; char *s2, *s3; - if (logmessages) { - while (n && (s[n - 1] == '\r' || s[n - 1] == '\n')) --n; - if ((s2 = DecodeLatin1(s, n, &n2))) { - if ((s3 = IndentLines(s2, n2, &n3, 1))) { - LOGF("%s %,ld byte message\n%.*s", d, n, n3, s3); - free(s3); - } - free(s2); + while (n && (s[n - 1] == '\r' || s[n - 1] == '\n')) --n; + if ((s2 = DecodeLatin1(s, n, &n2))) { + if ((s3 = IndentLines(s2, n2, &n3, 1))) { + LOGF("%s %,ld byte message\n%.*s", d, n, n3, s3); + free(s3); } + free(s2); } } static void LogBody(const char *d, const char *s, size_t n) { char *s2, *s3; size_t n2, n3; - if (n && logbodies) { - while (n && (s[n - 1] == '\r' || s[n - 1] == '\n')) --n; - if ((s2 = VisualizeControlCodes(s, n, &n2))) { - if ((s3 = IndentLines(s2, n2, &n3, 1))) { - LOGF("%s %,ld byte payload\n%.*s", d, n, n3, s3); - free(s3); - } - free(s2); + if (!n) return; + while (n && (s[n - 1] == '\r' || s[n - 1] == '\n')) --n; + if ((s2 = VisualizeControlCodes(s, n, &n2))) { + if ((s3 = IndentLines(s2, n2, &n3, 1))) { + LOGF("%s %,ld byte payload\n%.*s", d, n, n3, s3); + free(s3); } + free(s2); } } @@ -3351,7 +3354,7 @@ static ssize_t SendString(const char *s) { size_t n; ssize_t rc; n = strlen(s); - LogMessage("sending", s, n); + if (logmessages) LogMessage("sending", s, n); for (;;) { if ((rc = write(client, s, n)) != -1 || errno != EINTR) { return rc; @@ -3522,11 +3525,11 @@ static const char *MergeNames(const char *a, const char *b) { } static void AppendLong1(const char *a, long x) { - if (x) AppendFmt("%s: %ld\r\n", a, x); + if (x) Append("%s: %ld\r\n", a, x); } static void AppendLong2(const char *a, const char *b, long x) { - if (x) AppendFmt("%s.%s: %ld\r\n", a, b, x); + if (x) Append("%s.%s: %ld\r\n", a, b, x); } static void AppendTimeval(const char *a, struct timeval *tv) { @@ -3681,7 +3684,7 @@ char *ServeListing(void) { char rb[8], tb[64], *rp[6]; size_t i, n, pathlen, rn[6]; if (msg.method != kHttpGet && msg.method != kHttpHead) return BadMethod(); - AppendString("\ + Append("\ \r\n\ \r\n\ redbean zip listing\r\n\ @@ -3698,7 +3701,7 @@ td { padding-right: 3em; }\r\n\ rp[0] = EscapeHtml(brand, -1, &rn[0]); AppendData(rp[0], rn[0]); free(rp[0]); - AppendString("
\r\n");
+  Append("
\r\n");
   memset(w, 0, sizeof(w));
   n = GetZipCdirRecords(cdir);
   for (cf = GetZipCdirOffset(cdir); n--; cf += ZIP_CFILE_HDRSIZE(zmap + cf)) {
@@ -3733,15 +3736,15 @@ td { padding-right: 3em; }\r\n\
       if (IsCompressionMethodSupported(
               ZIP_LFILE_COMPRESSIONMETHOD(zmap + lf)) &&
           IsAcceptablePath(path, pathlen)) {
-        AppendFmt("%-*.*s %s  %0*o %4s  %,*ld  %'s\r\n",
-                  rn[2], rp[2], w[0], rn[4], rp[4], tb, w[1],
-                  GetZipCfileMode(zmap + cf), DescribeCompressionRatio(rb, lf),
-                  w[2], GetZipLfileUncompressedSize(zmap + lf), rp[5]);
+        Append("%-*.*s %s  %0*o %4s  %,*ld  %'s\r\n",
+               rn[2], rp[2], w[0], rn[4], rp[4], tb, w[1],
+               GetZipCfileMode(zmap + cf), DescribeCompressionRatio(rb, lf),
+               w[2], GetZipLfileUncompressedSize(zmap + lf), rp[5]);
       } else {
-        AppendFmt("%-*.*s %s  %0*o %4s  %,*ld  %'s\r\n", w[0], rn[4], rp[4], tb,
-                  w[1], GetZipCfileMode(zmap + cf),
-                  DescribeCompressionRatio(rb, lf), w[2],
-                  GetZipLfileUncompressedSize(zmap + lf), rp[5]);
+        Append("%-*.*s %s  %0*o %4s  %,*ld  %'s\r\n", w[0], rn[4], rp[4], tb,
+               w[1], GetZipCfileMode(zmap + cf),
+               DescribeCompressionRatio(rb, lf), w[2],
+               GetZipLfileUncompressedSize(zmap + lf), rp[5]);
       }
       free(rp[5]);
       free(rp[4]);
@@ -3752,38 +3755,38 @@ td { padding-right: 3em; }\r\n\
     }
     free(path);
   }
-  AppendString("

\r\n"); - AppendString("
\r\n"); - AppendString("/statusz says your redbean
\r\n"); + Append("

\r\n"); + Append("
\r\n"); + Append("/statusz says your redbean
\r\n"); AppendResourceReport(&shared->children, "
\r\n"); - AppendString("
\r\n"); + Append("\r\n"); and = ""; x = nowl() - startserver; y = ldiv(x, 24L * 60 * 60); if (y.quot) { - AppendFmt("%,ld day%s ", y.quot, y.quot == 1 ? "" : "s"); + Append("%,ld day%s ", y.quot, y.quot == 1 ? "" : "s"); and = "and "; } y = ldiv(y.rem, 60 * 60); if (y.quot) { - AppendFmt("%,ld hour%s ", y.quot, y.quot == 1 ? "" : "s"); + Append("%,ld hour%s ", y.quot, y.quot == 1 ? "" : "s"); and = "and "; } y = ldiv(y.rem, 60); if (y.quot) { - AppendFmt("%,ld minute%s ", y.quot, y.quot == 1 ? "" : "s"); + Append("%,ld minute%s ", y.quot, y.quot == 1 ? "" : "s"); and = "and "; } - AppendFmt("%s%,ld second%s of operation
\r\n", and, y.rem, - y.rem == 1 ? "" : "s"); + Append("%s%,ld second%s of operation
\r\n", and, y.rem, + y.rem == 1 ? "" : "s"); x = shared->messageshandled; - AppendFmt("%,ld message%s handled
\r\n", x, x == 1 ? "" : "s"); + Append("%,ld message%s handled
\r\n", x, x == 1 ? "" : "s"); x = shared->connectionshandled; - AppendFmt("%,ld connection%s handled
\r\n", x, x == 1 ? "" : "s"); + Append("%,ld connection%s handled
\r\n", x, x == 1 ? "" : "s"); x = shared->workers; - AppendFmt("%,ld connection%s active
\r\n", x, x == 1 ? "" : "s"); - AppendString("
\r\n"); - AppendString("
\r\n"); + Append("%,ld connection%s active
\r\n", x, x == 1 ? "" : "s"); + Append("
\r\n"); + Append("
\r\n"); p = SetStatus(200, "OK"); p = AppendContentType(p, "text/html"); p = AppendCache(p, 0); @@ -4014,7 +4017,7 @@ static char *HandleRequest(void) { return ServeFailure(505, "HTTP Version Not Supported"); } if ((p = SynchronizeStream())) return p; - LogBody("received", inbuf.p + hdrsize, msgsize - hdrsize); + if (logbodies) LogBody("received", inbuf.p + hdrsize, msgsize - hdrsize); if (msg.version < 11 || HeaderEqualCase(kHttpConnection, "close")) { connectionclose = true; } @@ -4072,10 +4075,11 @@ static bool HandleMessage(void) { char *p, *s; struct iovec iov[4]; long actualcontentlength; + g_syscount = 0; if ((rc = ParseHttpRequest(&msg, inbuf.p, amtread)) != -1) { if (!rc) return false; hdrsize = rc; - LogMessage("received", inbuf.p, hdrsize); + if (logmessages) LogMessage("received", inbuf.p, hdrsize); RecordNetworkOrigin(); p = HandleRequest(); } else { @@ -4091,12 +4095,12 @@ static bool HandleMessage(void) { LockInc(&shared->synchronizationfailures); DEBUGF("could not synchronize message stream"); } - if (connectionclose) { + if (0 && connectionclose) { LockInc(&shared->shutdowns); shutdown(client, SHUT_RD); } if (msg.version >= 10) { - p = AppendHeader(p, "Date", shared->currentdate); + p = AppendCrlf(stpcpy(stpcpy(p, "Date: "), shared->currentdate)); if (!branded) p = AppendServer(p, serverheader); if (extrahdrs) p = stpcpy(p, extrahdrs); if (connectionclose) { @@ -4112,7 +4116,7 @@ static bool HandleMessage(void) { p = AppendContentLength(p, actualcontentlength); p = AppendCrlf(p); CHECK_LE(p - hdrbuf.p, hdrbuf.n); - LogMessage("sending", hdrbuf.p, p - hdrbuf.p); + if (logmessages) LogMessage("sending", hdrbuf.p, p - hdrbuf.p); iov[0].iov_base = hdrbuf.p; iov[0].iov_len = p - hdrbuf.p; iovlen = 1; @@ -4137,7 +4141,7 @@ static bool HandleMessage(void) { iovlen = 1; } if (loglatency || LOGGABLE(kLogDebug)) { - flogf(kLogDebug, __FILE__, __LINE__, NULL, "%`'.*s handled in %,ldµs", + flogf(kLogDebug, __FILE__, __LINE__, NULL, "%`'.*s latency %,ldµs", msg.uri.b - msg.uri.a, inbuf.p + msg.uri.a, (long)((nowl() - startrequest) * 1e6L)); } @@ -4150,6 +4154,7 @@ static bool HandleMessage(void) { static void InitRequest(void) { frags = 0; msgsize = 0; + outbuf.n = 0; content = NULL; gzipped = false; branded = false; @@ -4272,10 +4277,13 @@ static void HandleConnection(void) { case 0: meltdown = false; connectionclose = false; + if (funtrace && !IsTiny()) { + ftrace_install(); + } break; case -1: FATALF("%s too many processes %s", DescribeServer(), strerror(errno)); - LockInc(&shared->forkerrors); + ++shared->forkerrors; LockInc(&shared->dropped); EnterMeltdownMode(); SendServiceUnavailable(); @@ -4292,8 +4300,7 @@ static void HandleConnection(void) { HandleMessages(); DEBUGF("%s closing after %,ldµs", DescribeClient(), (long)((nowl() - startconnection) * 1e6L)); - if (close(client) != -1) { - } else { + if (close(client) == -1) { LockInc(&shared->closeerrors); WARNF("%s close failed", DescribeClient()); } @@ -4303,7 +4310,7 @@ static void HandleConnection(void) { CollectGarbage(); } } else if (errno == EINTR || errno == EAGAIN) { - LockInc(&shared->acceptinterrupts); + ++shared->acceptinterrupts; } else if (errno == ENFILE) { LockInc(&shared->enfiles); WARNF("%s too many open files", DescribeServer()); @@ -4321,19 +4328,19 @@ static void HandleConnection(void) { WARNF("%s ran out of buffer"); EnterMeltdownMode(); } else if (errno == ENONET) { - LockInc(&shared->enonets); + ++shared->enonets; WARNF("%s network gone", DescribeServer()); sleep(1); } else if (errno == ENETDOWN) { - LockInc(&shared->enetdowns); + ++shared->enetdowns; WARNF("%s network down", DescribeServer()); sleep(1); } else if (errno == ECONNABORTED) { - LockInc(&shared->acceptresets); + ++shared->acceptresets; WARNF("%s connection reset before accept"); } else if (errno == ENETUNREACH || errno == EHOSTUNREACH || errno == EOPNOTSUPP || errno == ENOPROTOOPT || errno == EPROTO) { - LockInc(&shared->accepterrors); + ++shared->accepterrors; WARNF("%s ephemeral accept error %s", DescribeServer(), strerror(errno)); } else { FATALF("%s accept error %s", DescribeServer(), strerror(errno)); @@ -4408,8 +4415,8 @@ void RedBean(int argc, char *argv[], const char *prog) { if (setitimer(ITIMER_REAL, &kHeartbeat, NULL) == -1) { heartless = true; } - CHECK_NE(-1, - (server = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, IPPROTO_TCP))); + server = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, IPPROTO_TCP); + CHECK_NE(-1, server); TuneSockets(); if (bind(server, &serveraddr, sizeof(serveraddr)) == -1) { if (errno == EADDRINUSE) { @@ -4473,7 +4480,7 @@ void RedBean(int argc, char *argv[], const char *prog) { int main(int argc, char *argv[]) { setenv("GDB", "", true); - showcrashreports(); + if (!IsTiny()) showcrashreports(); RedBean(argc, argv, (const char *)getauxval(AT_EXECFN)); return 0; } diff --git a/tool/net/redbean.lua b/tool/net/redbean.lua index 95eaac082..41727d0eb 100644 --- a/tool/net/redbean.lua +++ b/tool/net/redbean.lua @@ -262,7 +262,7 @@ local function main() pat = re.compile([[([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})]]) m,a,b,c,d = pat:search(s) -- m and rest are nil if match not found Write('
\r\n')
-   Write([[pat = re.compile('([0-9]{1,3})\\.([0-9]{1,3})\\.([0-9]{1,3})\\.([0-9]{1,3})', re.EXTENDED)]])
+   Write([[pat = re.compile('([0-9]{1,3})\\.([0-9]{1,3})\\.([0-9]{1,3})\\.([0-9]{1,3})')]])
    Write(string.format('\r\nm,a,b,c,d = pat:search(%q)\r\n', s))
    Write('
\r\n') Write('
\r\n')