Add fixes performance and static web server

This commit is contained in:
Justine Tunney
2020-10-05 23:11:49 -07:00
parent b6793d42d5
commit c45e46f871
108 changed files with 2927 additions and 819 deletions

128
libc/fmt/fcvt.c Normal file
View File

@@ -0,0 +1,128 @@
/*-*- 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 2009 Ian Piumarta │
│ │
│ Permission is hereby granted, free of charge, to any person obtaining a copy │
│ of this software and associated documentation files (the 'Software'), to │
│ deal in the Software without restriction, including without limitation the │
│ rights to use, copy, modify, merge, publish, distribute, and/or sell copies │
│ of the Software, and to permit persons to whom the Software is furnished to │
│ do so, provided that the above copyright notice(s) and this permission │
│ notice appear in all copies of the Software. Inclusion of the above │
│ copyright notice(s) and this permission notice in supporting documentation │
│ would be appreciated but is not required. │
│ │
│ THE SOFTWARE IS PROVIDED 'AS IS'. USE ENTIRELY AT YOUR OWN RISK. │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/assert.h"
#include "libc/fmt/fmt.h"
#include "libc/macros.h"
#include "libc/math.h"
#include "libc/runtime/runtime.h"
#include "libc/str/str.h"
asm(".ident\t\"\\n\\n\
ecvt, fcvt (MIT License)\\n\
Copyright 2009 Ian Piumarta\"");
/**
* @fileoverview Replacements for the functions ecvt() and fcvt()
*
* These functions were recently deprecated in POSIX. The interface and
* behaviour is identical to the functions that they replace and faster.
*
* For details on the use of these functions, see your ecvt(3) manual
* page. If you don't have one handy, there might still be one available
* here: http://opengroup.org/onlinepubs/007908799/xsh/ecvt.html
*
* @see https://www.piumarta.com/software/fcvt/
*/
static char *Fcvt(double value, int ndigit, int *decpt, int *sign, int fflag) {
static char buf[128];
double i;
uint64_t l, mant;
int exp2, exp10, ptr;
memcpy(&l, &value, 8);
exp2 = (0x7ff & (l >> 52)) - 1023;
mant = l & 0x000fffffffffffffULL;
if ((*sign = l >> 63)) value = -value;
if (exp2 == 0x400) {
*decpt = 0;
return mant ? "nan" : "inf";
}
exp10 = (value == 0) ? !fflag : (int)ceil(log10(value));
if (exp10 < -307) exp10 = -307; /* otherwise overflow in pow() */
value *= pow(10.0, -exp10);
if (value) {
while (value < 0.1) {
value *= 10;
--exp10;
}
while (value >= 1.0) {
value /= 10;
++exp10;
}
}
assert(value == 0 || (0.1 <= value && value < 1.0));
if (fflag) {
if (ndigit + exp10 < 0) {
*decpt = -ndigit;
return "";
}
ndigit += exp10;
}
*decpt = exp10;
if (ARRAYLEN(buf) < ndigit + 2) abort();
ptr = 1;
#if 0 /* slow and safe (and dreadfully boring) */
while (ptr <= ndigit) {
i;
value = modf(value * 10, &i);
buf[ptr++] = '0' + (int)i;
}
if (value >= 0.5) {
while (--ptr && ++buf[ptr] > '9') {
buf[ptr] = '0';
}
}
#else /* faster */
memcpy(&l, &value, 8);
exp2 = (0x7ff & (l >> 52)) - 1023;
assert(value == 0 || (-4 <= exp2 && exp2 <= -1));
mant = l & 0x000fffffffffffffULL;
if (exp2 == -1023) {
++exp2;
} else {
mant |= 0x0010000000000000ULL;
}
mant <<= (exp2 + 4); /* 56-bit denormalised signifier */
while (ptr <= ndigit) {
mant &= 0x00ffffffffffffffULL; /* mod 1.0 */
mant = (mant << 1) + (mant << 3);
buf[ptr++] = '0' + (mant >> 56);
}
if (mant & 0x0080000000000000ULL) /* 1/2 << 56 */
while (--ptr && ++buf[ptr] > '9') buf[ptr] = '0';
#endif
if (ptr) {
buf[ndigit + 1] = 0;
return buf + 1;
}
if (fflag) {
++ndigit;
++*decpt;
}
buf[0] = '1';
buf[ndigit] = 0;
return buf;
}
char *ecvt(double value, int ndigit, int *decpt, int *sign) {
return Fcvt(value, ndigit, decpt, sign, 0);
}
char *fcvt(double value, int ndigit, int *decpt, int *sign) {
return Fcvt(value, ndigit, decpt, sign, 1);
}

View File

@@ -29,6 +29,8 @@ char *strerror(int) returnsnonnull nothrow nocallback;
int strerror_r(int, char *, size_t) nothrow nocallback;
int palandprintf(void *, void *, const char *, va_list) hidden;
char *itoa(int, char *, int) compatfn;
char *fcvt(double, int, int *, int *);
char *ecvt(double, int, int *, int *);
/*───────────────────────────────────────────────────────────────────────────│─╗
│ cosmopolitan § string formatting » optimizations ─╬─│┼

View File

@@ -40,7 +40,7 @@
* @see xdtoa() for higher precision at the cost of bloat
* @see palandprintf() which is intended caller
*/
int ftoa(int out(int, void *), void *arg, long double value, int prec,
int ftoa(int out(long, void *), void *arg, long double value, int prec,
unsigned long width, unsigned long flags) {
long whole, frac;
long double tmp, diff;

View File

@@ -31,7 +31,7 @@
uintmax_t __udivmodti4(uintmax_t, uintmax_t, uintmax_t *);
static int ntoaformat(int out(int, void *), void *arg, char *buf, unsigned len,
static int ntoaformat(int out(long, void *), void *arg, char *buf, unsigned len,
bool negative, unsigned log2base, unsigned prec,
unsigned width, unsigned char flags) {
unsigned i, idx;
@@ -103,7 +103,7 @@ static int ntoaformat(int out(int, void *), void *arg, char *buf, unsigned len,
return 0;
}
int ntoa2(int out(int, void *), void *arg, uintmax_t value, bool neg,
int ntoa2(int out(long, void *), void *arg, uintmax_t value, bool neg,
unsigned log2base, unsigned prec, unsigned width, unsigned flags,
const char *alphabet) {
uintmax_t remainder;
@@ -135,7 +135,7 @@ int ntoa2(int out(int, void *), void *arg, uintmax_t value, bool neg,
return ntoaformat(out, arg, buf, len, neg, log2base, prec, width, flags);
}
int ntoa(int out(int, void *), void *arg, va_list va, unsigned char signbit,
int ntoa(int out(long, void *), void *arg, va_list va, unsigned char signbit,
unsigned long log2base, unsigned long prec, unsigned long width,
unsigned char flags, const char *lang) {
bool neg;

View File

@@ -75,7 +75,7 @@ static int ppatoi(const char **str) {
* - `%Lf` long double
* - `%p` pointer (48-bit hexadecimal)
*
* Length Modifiers
* Size Modifiers
*
* - `%hhd` char (8-bit)
* - `%hd` short (16-bit)
@@ -89,7 +89,11 @@ static int ppatoi(const char **str) {
* - `%08d` fixed columns w/ zero leftpadding
* - `%8d` fixed columns w/ space leftpadding
* - `%*s` variable column string (thompson-pike)
* - `%.*s` variable column data (ignore nul terminator)
*
* Precision Modifiers
*
* - `%.8s` supplied character length (ignore nul terminator)
* - `%.*s` supplied character length argument (ignore nul terminator)
*
* Formatting Modifiers
*
@@ -112,9 +116,9 @@ hidden int palandprintf(void *fn, void *arg, const char *format, va_list va) {
long double ldbl;
wchar_t charbuf[3];
const char *alphabet;
int (*out)(int, void *);
int (*out)(long, void *);
unsigned char signbit, log2base;
int w, rc, flags, width, lasterr, precision;
int w, flags, width, lasterr, precision;
lasterr = errno;
out = fn ? fn : (int (*)(int, void *))missingno;
@@ -255,7 +259,8 @@ hidden int palandprintf(void *fn, void *arg, const char *format, va_list va) {
case 'u': {
flags &= ~FLAGS_HASH; /* no hash for dec format */
DoNumber:
if (weaken(ntoa)(out, arg, va, signbit, log2base, precision, width,
if (!weaken(ntoa) ||
weaken(ntoa)(out, arg, va, signbit, log2base, precision, width,
flags, alphabet) == -1) {
return -1;
}
@@ -269,7 +274,8 @@ hidden int palandprintf(void *fn, void *arg, const char *format, va_list va) {
} else {
ldbl = va_arg(va, double);
}
if (weaken(ftoa)(out, arg, ldbl, precision, width, flags) == -1) {
if (!weaken(ftoa) ||
weaken(ftoa)(out, arg, ldbl, precision, width, flags) == -1) {
return -1;
}
break;
@@ -297,8 +303,10 @@ hidden int palandprintf(void *fn, void *arg, const char *format, va_list va) {
case 's':
p = va_arg(va, void *);
showstr:
rc = weaken(stoa)(out, arg, p, flags, precision, width, signbit, qchar);
if (rc == -1) return -1;
if (!weaken(stoa) || weaken(stoa)(out, arg, p, flags, precision, width,
signbit, qchar) == -1) {
return -1;
}
break;
case '%':

View File

@@ -7,12 +7,12 @@
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
int spacepad(int(int, void *), void *, unsigned long) hidden;
int ftoa(int(int, void *), void *, long double, int, unsigned long,
int spacepad(int(long, void *), void *, unsigned long) hidden;
int ftoa(int(long, void *), void *, long double, int, unsigned long,
unsigned long) hidden;
int stoa(int(int, void *), void *, void *, unsigned long, unsigned long,
int stoa(int(long, void *), void *, void *, unsigned long, unsigned long,
unsigned long, unsigned char, unsigned char) hidden;
int ntoa(int(int, void *), void *, va_list, unsigned char, unsigned long,
int ntoa(int(long, void *), void *, va_list, unsigned char, unsigned long,
unsigned long, unsigned long, unsigned char, const char *) hidden;
COSMOPOLITAN_C_END_

View File

@@ -19,7 +19,7 @@
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/fmt/palandprintf.h"
int spacepad(int out(int, void *), void *arg, unsigned long n) {
int spacepad(int out(long, void *), void *arg, unsigned long n) {
int i, rc;
for (rc = i = 0; i < n; ++i) rc |= out(' ', arg);
return rc;

View File

@@ -17,47 +17,54 @@
│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │
│ 02110-1301 USA │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/bits/bits.h"
#include "libc/bits/safemacros.h"
#include "libc/bits/weaken.h"
#include "libc/escape/escape.h"
#include "libc/fmt/fmt.h"
#include "libc/fmt/paland.inc"
#include "libc/fmt/palandprintf.h"
#include "libc/nexgen32e/tinystrlen.h"
#include "libc/str/internal.h"
#include "libc/str/str.h"
#include "libc/str/tpdecode.h"
#include "libc/str/tpencode.h"
#include "libc/str/thompike.h"
#include "libc/str/tpenc.h"
#include "libc/str/utf16.h"
#include "libc/unicode/unicode.h"
forceinline unsigned long tpiencode(wint_t wc) {
char buf[8];
memset(buf, 0, sizeof(buf));
tpencode(buf, sizeof(buf), wc, false);
return read64le(buf);
typedef int (*emit_f)(int (*)(long, void *), void *, wint_t);
static int StoaEmitByte(int f(long, void *), void *a, wint_t c) {
return f(c, a);
}
forceinline int emitwc(int out(int, void *), void *arg, unsigned flags,
wint_t wc) {
unsigned long pending;
if (flags & FLAGS_QUOTE) {
if (wc > 127) {
pending = tpiencode(wc);
} else {
pending = cescapec(wc);
}
} else {
pending = tpiencode(wc);
}
static int StoaEmitWordEncodedString(int f(long, void *), void *a, uint64_t w) {
do {
if (out(pending & 0xff, arg) == -1) return -1;
} while ((pending >>= 8));
if (f(w & 0xff, a) == -1) {
return -1;
}
} while ((w >>= 8));
return 0;
}
forceinline int emitquote(int out(int, void *), void *arg, unsigned flags,
char ch, unsigned char signbit) {
static int StoaEmitUnicode(int f(long, void *), void *a, wint_t c) {
if (0 <= c && c <= 127) {
return f(c, a);
} else {
return StoaEmitWordEncodedString(f, a, tpenc(c));
}
}
static int StoaEmitQuoted(int f(long, void *), void *a, wint_t c) {
if (0 <= c && c <= 127) {
return StoaEmitWordEncodedString(f, a, cescapec(c));
} else {
return StoaEmitWordEncodedString(f, a, tpenc(c));
}
}
static int StoaEmitVisualized(int f(long, void *), void *a, wint_t c) {
return StoaEmitUnicode(f, a, (*weaken(kCp437))[c]);
}
static int StoaEmitQuote(int out(long, void *), void *arg, unsigned flags,
char ch, unsigned char signbit) {
if (flags & FLAGS_REPR) {
if (signbit == 63) {
if (out('L', arg) == -1) return -1;
@@ -78,13 +85,15 @@ forceinline int emitquote(int out(int, void *), void *arg, unsigned flags,
*
* @see palandprintf()
*/
int stoa(int out(int, void *), void *arg, void *data, unsigned long flags,
int stoa(int out(long, void *), 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 w, c;
bool ignorenul;
unsigned n;
emit_f emit;
bool justdobytes;
unsigned w, c, pad;
p = data;
if (!p) {
@@ -93,89 +102,102 @@ int stoa(int out(int, void *), void *arg, void *data, unsigned long flags,
flags |= FLAGS_NOQUOTE;
signbit = 0;
} else {
if (emitquote(out, arg, flags, qchar, signbit) == -1) return -1;
if (StoaEmitQuote(out, arg, flags, qchar, signbit) == -1) return -1;
}
w = precision ? precision : -1;
if (!(flags & FLAGS_PRECISION)) {
if (signbit == 63) {
precision = tinywcsnlen((const wchar_t *)p, -1);
} else if (signbit == 15) {
precision = tinystrnlen16((const char16_t *)p, -1);
} else {
precision = strlen(p);
}
}
pad = 0;
if (width) {
w = precision;
if (signbit == 63) {
if (weaken(wcsnwidth)) {
w = weaken(wcsnwidth)((const wchar_t *)p, w);
} else {
w = tinywcsnlen((const wchar_t *)p, w);
w = weaken(wcsnwidth)((const wchar_t *)p, precision);
}
} else if (signbit == 15) {
if (weaken(strnwidth16)) {
w = weaken(strnwidth16)((const char16_t *)p, w);
} else {
w = tinystrnlen16((const char16_t *)p, w);
w = weaken(strnwidth16)((const char16_t *)p, precision);
}
} else if (weaken(strnwidth)) {
w = weaken(strnwidth)(p, w);
} else {
w = strnlen(p, w);
w = weaken(strnwidth)(p, precision);
}
if (w < width) {
pad = width - w;
}
}
if (flags & FLAGS_PRECISION) {
w = MIN(w, precision);
if (pad && !(flags & FLAGS_LEFT)) {
if (spacepad(out, arg, pad) == -1) return -1;
}
if (w < width && !(flags & FLAGS_LEFT)) {
if (spacepad(out, arg, width - w) == -1) return -1;
justdobytes = false;
if (signbit == 15 || signbit == 63) {
if (flags & FLAGS_QUOTE) {
emit = StoaEmitQuoted;
} else {
emit = StoaEmitUnicode;
}
} else if ((flags & FLAGS_HASH) && weaken(kCp437)) {
justdobytes = true;
emit = StoaEmitVisualized;
} else if (flags & FLAGS_QUOTE) {
emit = StoaEmitQuoted;
} else {
justdobytes = true;
emit = StoaEmitByte;
}
ignorenul = (flags & FLAGS_PRECISION) && (flags & (FLAGS_HASH | FLAGS_QUOTE));
for (; !(flags & FLAGS_PRECISION) || precision; --precision) {
if (signbit == 15) {
if ((wc = *(const char16_t *)p) || ignorenul) {
if ((1 <= wc && wc <= 0xD7FF)) {
if (justdobytes) {
while (precision--) {
wc = *p++ & 0xff;
if (emit(out, arg, wc) == -1) return -1;
}
} else {
while (precision--) {
if (signbit == 15) {
wc = *(const char16_t *)p;
if (IsUcs2(wc)) {
p += sizeof(char16_t);
} else if ((wc & UTF16_MASK) == UTF16_CONT) {
} else if (IsUtf16Cont(wc)) {
p += sizeof(char16_t);
continue;
} else if (!precision) {
break;
} else {
char16_t buf[4] = {wc};
if (!(flags & FLAGS_PRECISION) || precision > 1) {
buf[1] = ((const char16_t *)p)[1];
--precision;
wc = MergeUtf16(wc, *(const char16_t *)p);
}
} else if (signbit == 63) {
wc = *(const wint_t *)p;
p += sizeof(wint_t);
if (!wc) break;
} else {
wc = *p++ & 0xff;
if (!isascii(wc)) {
if (ThomPikeCont(wc)) continue;
n = ThomPikeLen(wc) - 1;
wc = ThomPikeByte(wc);
if (n > precision) break;
precision -= n;
while (n--) {
wc = ThomPikeMerge(wc, *p++);
}
p += max(1, getutf16((const char16_t *)p, &wc)) * sizeof(char16_t);
}
} else {
break;
}
} else if (signbit == 63) {
wc = *(const wint_t *)p;
p += sizeof(wint_t);
if (!wc) break;
} else if (flags & FLAGS_HASH) {
c = *p & 0xff;
if (!c && !ignorenul) break;
wc = (*weaken(kCp437))[c];
p++;
} else {
if ((wc = *p & 0xff) || ignorenul) {
if (1 <= wc && wc <= 0x7f) {
++p;
} else if (iscont(wc & 0xff)) {
++p;
continue;
} else {
char buf[8];
memset(buf, 0, sizeof(buf));
memcpy(buf, p,
!(flags & FLAGS_PRECISION) ? 7 : MIN(7, precision - 1));
p += max(1, tpdecode(p, &wc));
}
} else {
break;
}
if (emit(out, arg, wc) == -1) return -1;
}
if (emitwc(out, arg, flags, wc) == -1) return -1;
}
if (w <= width && (flags & FLAGS_LEFT)) {
if (spacepad(out, arg, width - w) == -1) return -1;
if (pad && (flags & FLAGS_LEFT)) {
if (spacepad(out, arg, pad) == -1) return -1;
}
if (!(flags & FLAGS_NOQUOTE) && (flags & FLAGS_REPR)) {

View File

@@ -25,17 +25,13 @@
struct SprintfStr {
char *p;
size_t i, n;
size_t i;
size_t n;
};
static int vsnprintfputchar(unsigned char c, struct SprintfStr *str) {
if (str->i < str->n) {
if (str->p) str->p[str->i] = c;
str->i++;
} else {
if (!IsTrustworthy() && str->i >= INT_MAX) abort();
str->i++;
}
if (str->i < str->n) str->p[str->i] = c;
str->i++;
return 0;
}
@@ -48,15 +44,11 @@ static int vsnprintfputchar(unsigned char c, struct SprintfStr *str) {
* @return number of bytes written, excluding the NUL terminator; or,
* if the output buffer wasn't passed, or was too short, then the
* number of characters that *would* have been written is returned
* @throw EOVERFLOW when a formatted field exceeds its limit, which can
* be checked by setting errno to 0 before calling
* @see palandprintf() and printf() for detailed documentation
*/
int(vsnprintf)(char *buf, size_t size, const char *fmt, va_list va) {
struct SprintfStr str = {buf, 0, size};
palandprintf(vsnprintfputchar, &str, fmt, va);
if (str.p && 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;
}