diff --git a/examples/hello3.c b/examples/hello3.c
index 29a69466..64fbdbbb 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 d523e8e4..4c557186 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 291af2b4..3c00291f 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 882dd4ff..cf4e92fd 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 85af9eff..c1b86340 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 3bacd1d4..fda6ed39 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 4336f7bc..c56f8865 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 25757d6c..508113de 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 e6d50dcd..c489c745 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 04ae8de4..d189dffa 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 72d158b9..762684db 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 4e77c77b..6d920897 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 7d302ca6..7d2d597b 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 040f1119..f74ca425 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 3ce5685e..b7623117 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 750783f7..7890f980 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 b8296a37..942b01c1 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 1ccf2e9c..0fa7538f 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 00000000..cd6af1c9
--- /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 96863de5..1f230596 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 37b0a85d..395f0d41 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 cd2c0a2f..96b35401 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 df67d6ee..00000000
--- 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 7cb70697..00000000
--- 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 a0263673..b41b9340 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 0b520818..1cf3915d 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 b1c5bb1a..71321b41 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 51ec473b..df72e6e3 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 38d48725..8d45d2fd 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 70e9c911..f9e2faa9 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 5869fc90..fa65167a 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 06bc8d5e..6fea1c1f 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 970e87ea..c9387aa0 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 f1b65ead..a9b5750b 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 6e98cb5d..27984f7d 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 28cb2fad..d212afd2 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 87e0e66c..46a53a7e 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 082d9c7a..34dc1fa7 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\
\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");
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 95eaac08..41727d0e 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('