Improve performance of printf functions

This commit is contained in:
Justine Tunney 2021-04-24 13:58:34 -07:00
parent b107d2709f
commit dc6d11a031
39 changed files with 577 additions and 650 deletions

View file

@ -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;
}

View file

@ -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

View file

@ -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));
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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:

View file

@ -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_

View file

@ -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) {

View file

@ -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;
}

View file

@ -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;

View file

@ -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;
}

View file

@ -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;

View file

@ -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");
}

View file

@ -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;
}

View file

@ -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

View file

@ -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);

View file

@ -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

View file

@ -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;
}

56
libc/runtime/ftraceinit.c Normal file
View file

@ -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;
}

View file

@ -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) */

View file

@ -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;
}

View file

@ -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) {

View file

@ -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
*/

View file

@ -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
*/

View file

@ -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;
}

View file

@ -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);

View file

@ -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;

View file

@ -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[] =

View file

@ -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"

View file

@ -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;
}

View file

@ -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);
}

View file

@ -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.
*

View file

@ -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));

View file

@ -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));

View file

@ -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));
}

View file

@ -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));
}

View file

@ -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 \

View file

@ -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("<img src=\"data:image/png;base64,");
Append("<img src=\"data:image/png;base64,");
AppendData(q, 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("\
<!doctype html>\r\n\
<title>");
AppendFmt("%d %s", code, reason);
AppendString("\
Append("%d %s", code, reason);
Append("\
</title>\r\n\
<style>\r\n\
html { color: #111; font-family: sans-serif; }\r\n\
@ -1723,8 +1721,8 @@ img { vertical-align: middle; }\r\n\
</style>\r\n\
<h1>\r\n");
AppendLogo();
AppendFmt("%d %s\r\n", code, reason);
AppendString("</h1>\r\n");
Append("%d %s\r\n", code, reason);
Append("</h1>\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("\
<!doctype html>\r\n\
<meta charset=\"utf-8\">\r\n\
<title>redbean zip listing</title>\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("</h1><hr></header><pre>\r\n");
Append("</h1><hr></header><pre>\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("<a href=\"%.*s\">%-*.*s</a> %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("<a href=\"%.*s\">%-*.*s</a> %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("</pre><footer><hr>\r\n");
AppendString("<table border=\"0\"><tr><td valign=\"top\">\r\n");
AppendString("<a href=\"/statusz\">/statusz</a> says your redbean<br>\r\n");
Append("</pre><footer><hr>\r\n");
Append("<table border=\"0\"><tr><td valign=\"top\">\r\n");
Append("<a href=\"/statusz\">/statusz</a> says your redbean<br>\r\n");
AppendResourceReport(&shared->children, "<br>\r\n");
AppendString("<td valign=\"top\">\r\n");
Append("<td valign=\"top\">\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<br>\r\n", and, y.rem,
y.rem == 1 ? "" : "s");
Append("%s%,ld second%s of operation<br>\r\n", and, y.rem,
y.rem == 1 ? "" : "s");
x = shared->messageshandled;
AppendFmt("%,ld message%s handled<br>\r\n", x, x == 1 ? "" : "s");
Append("%,ld message%s handled<br>\r\n", x, x == 1 ? "" : "s");
x = shared->connectionshandled;
AppendFmt("%,ld connection%s handled<br>\r\n", x, x == 1 ? "" : "s");
Append("%,ld connection%s handled<br>\r\n", x, x == 1 ? "" : "s");
x = shared->workers;
AppendFmt("%,ld connection%s active<br>\r\n", x, x == 1 ? "" : "s");
AppendString("</table>\r\n");
AppendString("</footer>\r\n");
Append("%,ld connection%s active<br>\r\n", x, x == 1 ? "" : "s");
Append("</table>\r\n");
Append("</footer>\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;
}

View file

@ -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('<pre>\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('</pre>\r\n')
Write('<dl>\r\n')