Add fixes performance and static web server

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

View File

@ -45,6 +45,7 @@ COUNTERMAND=
# The GNU Compiler Collection passes a lot of CFLAGS to the preprocessor
# (which we call CCFLAGS) but it should pass more; and we do just that.
NOPG=0
FIRST=1
OUTARG=0
INVISIBLE=0
@ -65,13 +66,16 @@ for x; do
-w)
set -- "$@" "$x" -D__W__
;;
-x-no-pg)
NOPG=1
;;
-pg)
if [ $INVISIBLE -eq 0 ]; then
if [ $NOPG -eq 0 ] && [ $INVISIBLE -eq 0 ]; then
set -- "$@" "$x" -D__PG__ # @see libc/macros.h
fi
;;
-mfentry)
if [ $INVISIBLE -eq 0 ]; then
if [ $NOPG -eq 0 ] && [ $INVISIBLE -eq 0 ]; then
set -- "$@" "$x" -D__MFENTRY__
fi
;;

View File

@ -63,6 +63,7 @@ FC = gfortran #/opt/cross9f/bin/x86_64-linux-musl-gfortran
AS = o/third_party/gcc/bin/x86_64-linux-musl-as
CC = o/third_party/gcc/bin/x86_64-linux-musl-gcc
CXX = o/third_party/gcc/bin/x86_64-linux-musl-g++
CXXFILT = o/third_party/gcc/bin/x86_64-linux-musl-c++filt
LD = o/third_party/gcc/bin/x86_64-linux-musl-ld.bfd
AR = o/third_party/gcc/bin/x86_64-linux-musl-ar
NM = o/third_party/gcc/bin/x86_64-linux-musl-nm
@ -319,6 +320,7 @@ OBJECTIFY.c2x.c = $(CC) $(OBJECTIFY.c.flags) -std=c2x -Wextra -Werror -pedantic-
OBJECTIFY.real.c = \
$(GCC) \
-x-no-pg \
$(OBJECTIFY.c.flags) \
-wrapper build/realify.sh \
-D__REAL_MODE__ \
@ -330,7 +332,12 @@ OBJECTIFY.real.c = \
-ffixed-r13 \
-ffixed-r14 \
-ffixed-r15 \
-mno-red-zone \
-fcall-used-rbx \
-fno-jump-tables \
-fno-shrink-wrap \
-fno-schedule-insns2 \
-flive-range-shrinkage \
-fno-omit-frame-pointer \
-momit-leaf-frame-pointer \
-mpreferred-stack-boundary=3 \

View File

@ -15,17 +15,6 @@
# remove comments
s/[ \t][ \t]*#.*//
# preserve hardcoded stack offsets
# bloats code size 13% compared to recomputing stack solution
s/leave\(q\|\)/leavew\n\tadd\t$6,%sp/
s/call\(q\|\)\t/sub\t$6,%sp\n\tcallw\t/
s/ret\(q\|\)/retw\t$6/
s/pushq\t\(.*\)/sub\t$6,%sp\n\tpush\t\1/
s/popq\t\(.*\)/pop\t\1\n\tadd\t$6,%sp/
# can be used instead if
# 1. functions have 6 args or fewer
# 2. long double parameters forceinline
#s/leave\(q\|\)/leavew/
#s/call\(q\|\)/callw/
#s/ret\(q\|\)/retw/
@ -34,6 +23,14 @@ s/popq\t\(.*\)/pop\t\1\n\tadd\t$6,%sp/
#s/pushq\t\(.*\)/sub $6,%sp\n\tpush \1/
#s/popq\t\(.*\)/pop \1\n\tadd $6,%sp/
# preserve hardcoded stack offsets
# bloats code size 13%
s/leave\(q\|\)/leavew\n\tadd\t$6,%sp/
s/call\(q\|\)\t/sub\t$6,%sp\n\tcallw\t/
s/ret\(q\|\)/retw\t$6/
s/pushq\t\(.*\)/sub\t$6,%sp\n\tpush\t\1/
s/popq\t\(.*\)/pop\t\1\n\tadd\t$6,%sp/
s/, /,/g
# 32-bitify

View File

@ -72,7 +72,7 @@ o/$(MODE)/%.c11.o: %.c11.c; @ACTION=OBJECTIFY.c11 build/compile $(OBJECTIFY.c11.
o/$(MODE)/%.c2x.o: %.c2x.c; @ACTION=OBJECTIFY.c2x build/compile $(OBJECTIFY.c2x.c) $(OUTPUT_OPTION) $<
o/$(MODE)/%.initabi.o: %.initabi.c; @ACTION=OBJECTIFY.init build/compile $(OBJECTIFY.initabi.c) $(OUTPUT_OPTION) $<
o/$(MODE)/%.ncabi.o: %.ncabi.c; @ACTION=OBJECTIFY.nc build/compile $(OBJECTIFY.ncabi.c) $(OUTPUT_OPTION) $<
o/tiny/%.real.o: %.real.c; @ACTION=OBJECTIFY.real build/compile $(OBJECTIFY.real.c) $(OUTPUT_OPTION) $<
o/$(MODE)/%.real.o: %.c; @ACTION=OBJECTIFY.real build/compile $(OBJECTIFY.real.c) $(OUTPUT_OPTION) $<
o/$(MODE)/%.runs: o/$(MODE)/%; @ACTION=CHECK.runs TARGET=$< build/runcom $< $(TESTARGS) && touch $@
o/$(MODE)/%.pkg:; @build/package $(OUTPUT_OPTION) $(addprefix -d,$(filter %.pkg,$^)) $(filter %.o,$^)
o/$(MODE)/%.zip.o: %; @build/zipobj $(OUTPUT_OPTION) $<

View File

@ -49,6 +49,7 @@ EXAMPLES_DIRECTDEPS = \
LIBC_CONV \
LIBC_FMT \
LIBC_LOG \
LIBC_LOG_ASAN \
LIBC_MEM \
LIBC_NEXGEN32E \
LIBC_NT_KERNELBASE \

View File

@ -55,7 +55,7 @@ int main(int argc, char *argv[], char **envp) {
unsigned long val;
char fmt[64], **env;
printf("\nArguments:\n");
for (i = 0; i < argc; ++i) {
for (i = 0; i < g_argc; ++i) {
printf(" ☼ %s\n", argv[i]);
}
printf("\nEnvironment:\n");

View File

@ -71,27 +71,25 @@ static size_t KnuthMorrisPratt(m, T, W, n, S)
* @param needlelen is its character count
* @return pointer to first result or NULL if not found
*/
void *(memmem)(const void *haystack_, size_t haystacklen, const void *needle_,
void *(memmem)(const void *haystackp, size_t haystacklen, const void *needlep,
size_t needlelen) {
long *T;
size_t i;
const char *haystack, *needle, *h;
haystack = haystack_;
needle = needle_;
needle = needlep;
haystack = haystackp;
if (needlelen > haystacklen) return NULL;
if (!needlelen) return (/*unconst*/ void *)haystack;
if (!needlelen) return haystack;
h = memchr(haystack, *needle, haystacklen);
if (!h || needlelen == 1) return (/*unconst*/ void *)h;
if (!h || needlelen == 1) return h;
haystacklen -= h - haystack;
long stacktmp[16];
void *freeme = NULL;
long *T = (needlelen + 1 < ARRAYLEN(stacktmp))
? &stacktmp[0]
: (freeme = malloc((needlelen + 1) * sizeof(long)));
KnuthMorrisPrattInit(needlelen, T, needle);
size_t i = KnuthMorrisPratt(needlelen, T, needle, haystacklen, h);
free(freeme);
if (i < haystacklen) {
return (/*unconst*/ char *)h + i * sizeof(char);
if (needlelen < haystacklen && memcmp(h, needle, needlelen) == 0) {
return h;
} else {
return NULL;
T = malloc((needlelen + 1) * sizeof(long));
KnuthMorrisPrattInit(needlelen, T, needle);
i = KnuthMorrisPratt(needlelen, T, needle, haystacklen, h);
free(T);
return i < haystacklen ? h + i * sizeof(char) : NULL;
}
}

View File

@ -17,10 +17,7 @@
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "libc/bits/bits.h"
#include "libc/calls/internal.h"
#include "libc/macros.h"
#include "libc/runtime/internal.h"
#include "libc/runtime/runtime.h"
#include "libc/sysv/errfuns.h"

View File

@ -77,7 +77,7 @@ int clock_gettime(int clockid, struct timespec *out_ts) {
} else {
struct NtFileTime ft;
GetSystemTimeAsFileTime(&ft);
*out_ts = filetimetotimespec(ft);
*out_ts = FileTimeToTimeSpec(ft);
return 0;
}
}

View File

@ -49,9 +49,9 @@ textwindows int fstat$nt(int64_t handle, struct stat *st) {
: (((filetype == kNtFileTypeDisk) ? S_IFBLK : 0) |
((filetype == kNtFileTypeChar) ? S_IFCHR : 0) |
((filetype == kNtFileTypePipe) ? S_IFIFO : 0))));
st->st_atim = filetimetotimespec(wst.ftLastAccessFileTime);
st->st_mtim = filetimetotimespec(wst.ftLastWriteFileTime);
st->st_ctim = filetimetotimespec(wst.ftCreationFileTime);
st->st_atim = FileTimeToTimeSpec(wst.ftLastAccessFileTime);
st->st_mtim = FileTimeToTimeSpec(wst.ftLastWriteFileTime);
st->st_ctim = FileTimeToTimeSpec(wst.ftCreationFileTime);
st->st_size = (uint64_t)wst.nFileSizeHigh << 32 | wst.nFileSizeLow;
st->st_blksize = PAGESIZE;
st->st_dev = wst.dwVolumeSerialNumber;

View File

@ -28,7 +28,6 @@
static int g_pid;
static void __updatepid(void) {
atfork(__updatepid, NULL);
g_pid = __getpid();
}
@ -47,8 +46,9 @@ int __getpid(void) {
int getpid(void) {
static bool once;
if (!once) {
once = true;
__updatepid();
atfork(__updatepid, NULL);
once = true;
}
return g_pid;
}

View File

@ -36,8 +36,8 @@ textwindows int getrusage$nt(int who, struct rusage *usage) {
if ((who == RUSAGE_SELF ? GetProcessTimes : GetThreadTimes)(
(who == RUSAGE_SELF ? GetCurrentProcess : GetCurrentThread)(),
&CreationFileTime, &ExitFileTime, &KernelFileTime, &UserFileTime)) {
filetimetotimeval(&usage->ru_utime, UserFileTime);
filetimetotimeval(&usage->ru_stime, KernelFileTime);
FileTimeToTimeVal(&usage->ru_utime, UserFileTime);
FileTimeToTimeVal(&usage->ru_stime, KernelFileTime);
return 0;
} else {
return winerr();

View File

@ -29,7 +29,7 @@
int gettimeofday$nt(struct timeval *tv, struct timezone *tz) {
struct NtFileTime ft;
GetSystemTimeAsFileTime(&ft);
filetimetotimeval(tv, ft);
FileTimeToTimeVal(tv, ft);
if (tz) memset(tz, 0, sizeof(*tz));
return 0;
}

View File

@ -35,7 +35,7 @@
* @param mode is an octal user/group/other permission signifier, that's
* ignored if O_CREAT or O_TMPFILE weren't passed
* @return number needing close(), or -1 w/ errno
* @asyncsignalsafe
* @note don't call open() from signal handlers
*/
nodiscard int open(const char *file, int flags, ...) {
va_list va;

View File

@ -155,7 +155,7 @@ int(sigaction)(int sig, const struct sigaction *act, struct sigaction *oldact) {
ap->sa_restorer = &__restore_rt;
}
}
if (rva >= 0) {
if (rva >= kSigactionMinRva) {
ap->sa_sigaction = (sigaction_f)__sigenter;
}
}

View File

@ -63,7 +63,7 @@ textwindows int utimensat$nt(int dirfd, const char *path,
} else if (ts[i].tv_nsec == UTIME_OMIT) {
ftp[i] = NULL;
} else {
ft[i] = timespectofiletime(ts[i]);
ft[i] = TimeSpecToFileTime(ts[i]);
ftp[i] = &ft[i];
}
}

View File

@ -50,8 +50,8 @@ textwindows int wait4$nt(int pid, int *opt_out_wstatus, int options,
memset(opt_out_rusage, 0, sizeof(*opt_out_rusage));
GetProcessTimes(GetCurrentProcess(), &createfiletime, &exitfiletime,
&kernelfiletime, &userfiletime);
filetimetotimeval(&opt_out_rusage->ru_utime, userfiletime);
filetimetotimeval(&opt_out_rusage->ru_stime, kernelfiletime);
FileTimeToTimeVal(&opt_out_rusage->ru_utime, userfiletime);
FileTimeToTimeVal(&opt_out_rusage->ru_stime, kernelfiletime);
}
return pid;
} else if (options & WNOHANG) {

View File

@ -15,8 +15,8 @@
* of the UNIX operation system signalled the end of modernity. Windows
* timestamps are living proof.
*/
#define MODERNITYSECONDS 11644473600
#define HECTONANOSECONDS 10000000
#define MODERNITYSECONDS 11644473600ull
#define HECTONANOSECONDS 10000000ull
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
@ -46,12 +46,13 @@ long strtol(const char *, char **, int)
cosmopolitan § conversion » time
*/
struct timespec filetimetotimespec(struct NtFileTime);
struct NtFileTime timespectofiletime(struct timespec);
struct NtFileTime timetofiletime(int64_t) nothrow pureconst;
int64_t DosDateTimeToUnix(unsigned, unsigned);
struct timespec FileTimeToTimeSpec(struct NtFileTime);
struct NtFileTime TimeSpecToFileTime(struct timespec);
struct NtFileTime TimeToFileTime(int64_t) nothrow pureconst;
int64_t filetimetotime(struct NtFileTime) nothrow pureconst;
void filetimetotimeval(struct timeval *, struct NtFileTime) nothrow;
struct NtFileTime timevaltofiletime(const struct timeval *) nosideeffect;
void FileTimeToTimeVal(struct timeval *, struct NtFileTime) nothrow;
struct NtFileTime TimeValToFileTime(const struct timeval *) nosideeffect;
long convertmicros(const struct timeval *, long) paramsnonnull() nosideeffect;
/*───────────────────────────────────────────────────────────────────────────│─╗

View File

@ -52,6 +52,7 @@ $(LIBC_CONV_A).pkg: \
$(LIBC_CONV_A_OBJS) \
$(foreach x,$(LIBC_CONV_A_DIRECTDEPS),$($(x)_A).pkg)
o/$(MODE)/libc/conv/dosdatetimetounix.o \
o/$(MODE)/libc/conv/itoa64radix10.greg.o \
o/$(MODE)/libc/conv/timetofiletime.o \
o/$(MODE)/libc/conv/filetimetotime.o \

View File

@ -0,0 +1,43 @@
/*-*- 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
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "libc/conv/conv.h"
#include "libc/macros.h"
#include "libc/time/time.h"
/**
* Converts MS-DOS timestamp to UNIX.
*
* @note type signature supports dates greater than 2100
* @see PKZIP, FAT
*/
int64_t DosDateTimeToUnix(unsigned date, unsigned time) {
unsigned weekday, year, month, day, hour, minute, second, yday, leap;
year = ((date & 0xfffffe00) >> 9) + 1980 - 1900;
month = MAX(1, MIN(12, (date & 0x01e0) >> 5));
day = (date & 0x001f) ? (date & 0x001f) - 1 : 0;
hour = (time & 0x0000f800) >> 11;
minute = (time & 0x000007e0) >> 5;
second = (time & 0x0000001f) << 1;
leap = year % 4 == 0 && (year % 100 || year % 400 == 0);
yday = day + kMonthYearDay[leap][month - 1];
return second + minute * 60 + hour * 3600 + yday * 86400 +
(year - 70) * 31536000ull + ((year - 69) / 4) * 86400ull -
((year - 1) / 100) * 86400ull + ((year + 299) / 400) * 86400ull;
}

View File

@ -24,11 +24,11 @@
/**
* Converts Windows COBOL timestamp to UNIX epoch in nanoseconds.
*/
struct timespec filetimetotimespec(struct NtFileTime ft) {
struct timespec FileTimeToTimeSpec(struct NtFileTime ft) {
uint64_t x;
x = ft.dwHighDateTime;
x <<= 32;
x |= ft.dwLowDateTime;
x -= MODERNITYSECONDS;
return (struct timespec){x / HECTONANOSECONDS, x % HECTONANOSECONDS * 100};
return (struct timespec){x / HECTONANOSECONDS - MODERNITYSECONDS,
x % HECTONANOSECONDS * 100};
}

View File

@ -22,9 +22,11 @@
#include "libc/conv/conv.h"
#include "libc/nt/struct/filetime.h"
void filetimetotimeval(struct timeval *tv, struct NtFileTime ft) {
uint64_t t = (uint64_t)ft.dwHighDateTime << 32 | ft.dwLowDateTime;
uint64_t x = t - MODERNITYSECONDS * HECTONANOSECONDS;
tv->tv_sec = x / HECTONANOSECONDS;
void FileTimeToTimeVal(struct timeval *tv, struct NtFileTime ft) {
uint64_t x;
x = ft.dwHighDateTime;
x <<= 32;
x |= ft.dwLowDateTime;
tv->tv_sec = x / HECTONANOSECONDS - MODERNITYSECONDS;
tv->tv_usec = x % HECTONANOSECONDS / 10;
}

View File

@ -0,0 +1,25 @@
/*-*- 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
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "libc/time/time.h"
const unsigned short kMonthYearDay[2][12] = {
{0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334},
{0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335},
};

View File

@ -25,7 +25,7 @@
/**
* Converts UNIX nanosecond timestamp to Windows COBOL timestamp.
*/
struct NtFileTime timespectofiletime(struct timespec ts) {
struct NtFileTime TimeSpecToFileTime(struct timespec ts) {
uint64_t x;
x = MODERNITYSECONDS;
x += ts.tv_sec * HECTONANOSECONDS;

View File

@ -20,7 +20,7 @@
#include "libc/conv/conv.h"
#include "libc/nt/struct/filetime.h"
struct NtFileTime timetofiletime(int64_t t) {
struct NtFileTime TimeToFileTime(int64_t t) {
uint64_t t2 = (t + MODERNITYSECONDS) * HECTONANOSECONDS;
return (struct NtFileTime){(uint32_t)t2, (uint32_t)(t2 >> 32)};
}

View File

@ -22,7 +22,7 @@
#include "libc/nt/struct/filetime.h"
#include "libc/time/time.h"
struct NtFileTime timevaltofiletime(const struct timeval *tv) {
struct NtFileTime TimeValToFileTime(const struct timeval *tv) {
uint64_t t2 = tv->tv_sec * HECTONANOSECONDS + tv->tv_usec * 10 +
MODERNITYSECONDS * HECTONANOSECONDS;
return (struct NtFileTime){(uint32_t)t2, (uint32_t)(t2 >> 32)};

View File

@ -1,5 +1,6 @@
#ifndef COSMOPOLITAN_LIBC_DOS_H_
#define COSMOPOLITAN_LIBC_DOS_H_
#include "libc/macros.h"
/**
* @fileoverview MS-DOS Data Structures.

128
libc/fmt/fcvt.c 100644
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;
}

View File

@ -66,7 +66,7 @@
#endif
#define BIGPAGESIZE 0x200000
#define STACKSIZE 0x80000 /* todo: zlib's fault? */
#define STACKSIZE 0x10000
#define FRAMESIZE 0x10000 /* 8086 */
#define PAGESIZE 0x1000 /* i386+ */
#define BUFSIZ 0x1000 /* best stdio default */

View File

@ -3,7 +3,7 @@
PKGS += LIBC_LINUX
LIBC_LINUX_HDRS = $(filter %.h,$(LIBC_FILES))
LIBC_LINUX_HDRS = $(filter %.h,$(LIBC_LINUX_FILES))
LIBC_LINUX_FILES := $(wildcard libc/linux/*)
LIBC_LINUX_CHECKS = $(LIBC_LINUX_HDRS:%=o/$(MODE)/%.ok)

View File

@ -41,6 +41,8 @@
#include "libc/sysv/consts/prot.h"
#include "third_party/dlmalloc/dlmalloc.h"
STATIC_YOINK("_init_asan");
/**
* @fileoverview Cosmopolitan Address Sanitizer Runtime.
*

View File

@ -31,6 +31,10 @@
#include "libc/str/str.h"
#include "libc/sysv/consts/auxv.h"
STATIC_YOINK("ntoa");
STATIC_YOINK("stoa");
STATIC_YOINK("ftoa");
/**
* Handles failure of CHECK_xx() macros.
*/
@ -47,30 +51,30 @@ relegated void __check_fail(const char *suffix, const char *opstr,
if (!memccpy(sufbuf, suffix, '\0', sizeof(sufbuf))) strcpy(sufbuf, "?");
strtoupper(sufbuf);
fprintf(stderr,
"check failed\n"
"\tCHECK_%s(%s, %s);\n"
"\t\t → %#lx (%s)\n"
"\t\t%s %#lx (%s)\n",
sufbuf, wantstr, gotstr, want, wantstr, opstr, got, gotstr);
(fprintf)(stderr,
"check failed\n"
"\tCHECK_%s(%s, %s);\n"
"\t\t → %#lx (%s)\n"
"\t\t%s %#lx (%s)\n",
sufbuf, wantstr, gotstr, want, wantstr, opstr, got, gotstr);
if (!isempty(fmt)) {
fputc('\t', stderr);
va_start(va, fmt);
vfprintf(stderr, fmt, va);
(vfprintf)(stderr, fmt, va);
va_end(va);
fputc('\n', stderr);
}
fprintf(stderr, "\t%s\n\t%s%s%s%s\n", strerror(lasterr), SUBTLE,
getauxval(AT_EXECFN), g_argc > 1 ? " \\" : "", RESET);
(fprintf)(stderr, "\t%s\n\t%s%s%s%s\n", strerror(lasterr), SUBTLE,
getauxval(AT_EXECFN), g_argc > 1 ? " \\" : "", RESET);
for (i = 1; i < g_argc; ++i) {
fprintf(stderr, "\t\t%s%s\n", g_argv[i], i < g_argc - 1 ? " \\" : "");
(fprintf)(stderr, "\t\t%s%s\n", g_argv[i], i < g_argc - 1 ? " \\" : "");
}
if (!IsTiny() && lasterr == ENOMEM) {
fprintf(stderr, "\n");
(fprintf)(stderr, "\n");
fflush(stderr);
PrintMemoryIntervals(fileno(stderr), &_mmi);
}

View File

@ -15,7 +15,7 @@
* Log level for compile-time DCE.
*/
#ifndef LOGGABLELEVEL
#ifndef NDEBUG
#ifndef TINY
#define LOGGABLELEVEL kLogDebug
/* #elif IsTiny() */
/* #define LOGGABLELEVEL kLogInfo */

View File

@ -65,6 +65,11 @@ $(LIBC_LOG_A_OBJS): \
$(NO_MAGIC) \
-fwrapv
ifeq (,$(MODE))
LIBC_LOG_ASAN = o/$(MODE)/libc/log/asan.o
endif
LIBC_LOG_ASAN_A = o/$(MODE)/libc/log/log.a
LIBC_LOG_LIBS = $(foreach x,$(LIBC_LOG_ARTIFACTS),$($(x)))
LIBC_LOG_SRCS = $(foreach x,$(LIBC_LOG_ARTIFACTS),$($(x)_SRCS))
LIBC_LOG_HDRS = $(foreach x,$(LIBC_LOG_ARTIFACTS),$($(x)_HDRS))

View File

@ -37,6 +37,10 @@
#define kNontrivialSize (8 * 1000 * 1000)
STATIC_YOINK("ntoa");
STATIC_YOINK("stoa");
STATIC_YOINK("ftoa");
static int loglevel2char(unsigned level) {
switch (level) {
case kLogInfo:
@ -47,6 +51,8 @@ static int loglevel2char(unsigned level) {
return 'W';
case kLogFatal:
return 'F';
case kLogVerbose:
return 'V';
default:
return '?';
}
@ -80,8 +86,7 @@ void(vflogf)(unsigned level, const char *file, int line, FILE *f,
long double t2;
const char *prog;
int64_t secs, nsec, dots;
char zonebuf[8], *zonebufp;
char timebuf[24], *timebufp;
char timebuf[32], *timebufp;
if (!f) f = g_logfile;
if (fileno(f) == -1) return;
t2 = nowl();
@ -89,22 +94,19 @@ void(vflogf)(unsigned level, const char *file, int line, FILE *f,
nsec = rem1000000000int64(t2 * 1e9L);
if (secs > ts.tv_sec) {
localtime_r(&secs, &tm);
strftime(timebuf, sizeof(timebuf), "%Y%m%dT%H%M%S", &tm);
strftime(zonebuf, sizeof(zonebuf), "%Z", &tm);
strftime(timebuf, sizeof(timebuf), "%Y-%m-%dT%H:%M:%S.", &tm);
timebufp = timebuf;
zonebufp = zonebuf;
dots = nsec;
} else {
timebufp = "---------------";
zonebufp = "---";
timebufp = "--------------------";
dots = nsec - ts.tv_nsec;
}
ts.tv_sec = secs;
ts.tv_nsec = nsec;
prog = basename(program_invocation_name);
if (fprintf(f, "%c%s%s%06ld:%s:%d:%.*s:%d] ", loglevel2char(level), timebufp,
zonebufp, rem1000000int64(div1000int64(dots)), file, line,
strchrnul(prog, '.') - prog, prog, getpid()) <= 0) {
if ((fprintf)(f, "%c%s%06ld:%s:%d:%.*s:%d] ", loglevel2char(level), timebufp,
rem1000000int64(div1000int64(dots)), file, line,
strchrnul(prog, '.') - prog, prog, getpid()) <= 0) {
vflogf_onfail(f);
}
(vfprintf)(f, fmt, va);
@ -112,7 +114,7 @@ void(vflogf)(unsigned level, const char *file, int line, FILE *f,
fputc('\n', f);
if (level == kLogFatal) {
startfatal(file, line);
fprintf(stderr, "fatal error see logfile\n");
(fprintf)(stderr, "fatal error see logfile\n");
die();
unreachable;
}

View File

@ -1,6 +1,8 @@
#ifndef COSMOPOLITAN_LIBC_NT_WINSOCK_H_
#define COSMOPOLITAN_LIBC_NT_WINSOCK_H_
#include "libc/nt/struct/overlapped.h"
#include "libc/nt/struct/pollfd.h"
#include "libc/sock/sock.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
#if 0
@ -55,10 +57,6 @@ COSMOPOLITAN_C_START_
#define kNtTfUseSystemThread 0x10
#define kNtTfUseKernelApc 0x20
struct sockaddr;
struct sockaddr_in;
struct pollfd$nt;
enum NtWsaEComparator { COMP_EQUAL, COMP_NOTLESS };
enum NtWsaCompletionType {

View File

@ -53,8 +53,7 @@ _executive:
call _setstack
mov %eax,%edi
call exit
9: .endfn _executive,weak,hidden
.endfn _executive,weak,hidden
ud2
#ifdef __PG__

View File

@ -28,9 +28,9 @@
*/
void *_getstack(void) {
char *p;
p = mmap((char *)0x700000000000 - STACKSIZE, STACKSIZE,
PROT_READ | PROT_WRITE, MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1,
0);
p = mmap((char *)0x700000000000 /* IMAGE_BASE_VIRTUAL */ - STACKSIZE,
STACKSIZE, PROT_READ | PROT_WRITE,
MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (p == MAP_FAILED) abort();
return p + STACKSIZE;
}

View File

@ -28,8 +28,11 @@
*/
bool isheap(void *p) {
int x, i;
#if 1
register intptr_t rsp asm("rsp");
if ((intptr_t)p >= rsp) return false;
#endif
if ((intptr_t)p <= (intptr_t)_end) return false;
x = (intptr_t)p >> 16;
i = FindMemoryInterval(&_mmi, x);
return i < _mmi.i && x >= _mmi.p[i].x && x <= _mmi.p[i].y;

View File

@ -17,11 +17,9 @@
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "libc/fmt/fmt.h"
#include "libc/sock/internal.h"
#include "libc/sock/sock.h"
#include "libc/sysv/errfuns.h"
#include "libc/conv/itoa.h"
#include "libc/sysv/consts/af.h"
#include "libc/sysv/errfuns.h"
/**
* Formats internet address to string.
@ -33,11 +31,21 @@
* @return dst on success or NULL w/ errno
*/
const char *inet_ntop(int af, const void *src, char *dst, uint32_t size) {
char *p;
unsigned char *ip4;
if (src) {
if (af == AF_INET) {
unsigned char *p = (unsigned char *)src;
if (snprintf(dst, size, "%hhu.%hhu.%hhu.%hhu", p[0], p[1], p[2], p[3]) <
size) {
if (size >= 16) {
p = dst;
ip4 = src;
p += uint64toarray_radix10(ip4[0], p);
*p++ = '.';
p += uint64toarray_radix10(ip4[1], p);
*p++ = '.';
p += uint64toarray_radix10(ip4[2], p);
*p++ = '.';
p += uint64toarray_radix10(ip4[3], p);
*p = '\0';
return dst;
} else {
enospc();

View File

@ -37,6 +37,7 @@ int fwritebuf(FILE *f) {
unsigned bytes;
bytes = (f->beg < f->end ? f->end : f->size) - f->beg;
if ((put = write(f->fd, &f->buf[f->beg], bytes)) == -1) {
if (errno == EINTR) return 0;
return (int)fseterrno(f);
}
f->beg = (unsigned)((f->beg + put) & (f->size - 1));

View File

@ -19,6 +19,7 @@
*/
#include "libc/errno.h"
#include "libc/str/str.h"
#include "libc/str/utf16.h"
/**
* Decodes UTF-16 character.
@ -30,16 +31,15 @@
*/
forcealignargpointer unsigned(getutf16)(const char16_t *p, wint_t *wc) {
unsigned skip = 0;
while ((p[skip] & UTF16_MASK) == UTF16_CONT) skip++;
if ((p[skip] & UTF16_MASK) != UTF16_MOAR) {
while (IsUtf16Cont(p[skip])) skip++;
if (IsUcs2(p[skip])) {
*wc = p[skip];
return skip + 1;
} else if ((p[skip + 1] & UTF16_MASK) != UTF16_CONT) {
} else if (IsUtf16Cont(p[skip + 1])) {
*wc = INVALID_CODEPOINT;
return -1;
} else {
*wc = 0x10000 + ((((unsigned)p[skip + 0] - 0xd800) << 10) +
((unsigned)p[skip + 1] - 0xdc00));
*wc = MergeUtf16(p[skip], p[skip + 1]);
return skip + 2;
}
}

View File

@ -64,10 +64,6 @@ void *isnotplaintext(const void *, size_t) nothrow nocallback nosideeffect;
#define INVALID_CODEPOINT 0xfffd
#define UTF16_MASK 0b1111110000000000
#define UTF16_MOAR 0b1101100000000000 /* 0xD800..0xDBFF */
#define UTF16_CONT 0b1101110000000000 /* 0xDC00..0xDBFF */
unsigned getutf16(const char16_t *, wint_t *);
int pututf16(char16_t *, size_t, wint_t, bool);
int iswalnum(wint_t);

View File

@ -18,11 +18,14 @@
02110-1301 USA
*/
#include "libc/str/str.h"
#include "libc/str/utf16.h"
/**
* Returns number of characters in UTF-16 or UCS-2 string.
*/
size_t strclen16(const char16_t *s) { return strnclen16(s, -1ull); }
size_t strclen16(const char16_t *s) {
return strnclen16(s, -1ull);
}
noinline size_t strnclen16(const char16_t *p, size_t n) {
size_t l = 0;

View File

@ -2,9 +2,10 @@
#define COSMOPOLITAN_LIBC_STR_THOMPIKE_H_
#include "libc/nexgen32e/bsr.h"
#define ThomPikeCont(x) ((x & 0b11000000) == 0b10000000)
#define ThomPikeByte(x) (x & (((1 << ThomPikeMsb(x)) - 1) | 3))
#define ThomPikeLen(x) (7 - ThomPikeMsb(x))
#define ThomPikeMsb(x) (x < 252 ? bsr(~x & 0xff) : 1)
#define ThomPikeCont(x) (((x)&0b11000000) == 0b10000000)
#define ThomPikeByte(x) ((x) & (((1 << ThomPikeMsb(x)) - 1) | 3))
#define ThomPikeLen(x) (7 - ThomPikeMsb(x))
#define ThomPikeMsb(x) ((x) < 252 ? bsr(~(x)&0xff) : 1)
#define ThomPikeMerge(x, y) ((x) << 6 | (y)&0b00111111)
#endif /* COSMOPOLITAN_LIBC_STR_THOMPIKE_H_ */

12
libc/str/utf16.h 100644
View File

@ -0,0 +1,12 @@
#ifndef COSMOPOLITAN_LIBC_STR_UTF16_H_
#define COSMOPOLITAN_LIBC_STR_UTF16_H_
#define UTF16_MASK 0xfc00
#define UTF16_MOAR 0xd800 /* 0xD800..0xDBFF */
#define UTF16_CONT 0xdc00 /* 0xDC00..0xDBFF */
#define IsUcs2(wc) (((wc)&UTF16_MASK) != UTF16_MOAR)
#define IsUtf16Cont(wc) (((wc)&UTF16_MASK) != UTF16_MOAR)
#define MergeUtf16(lo, hi) ((((lo)-0xD800) << 10) + ((hi)-0xDC00) + 0x10000)
#endif /* COSMOPOLITAN_LIBC_STR_UTF16_H_ */

View File

@ -1421,11 +1421,11 @@ offtime(timep, offset)
** where, to make the math easy, the answer for year zero is defined as zero.
*/
pureconst static int
pureconst optimizespeed static int
leaps_thru_end_of(y)
register const int y;
{
return (y >= 0) ? (y / 4 - div100int64(y) + y / 400) :
return (y >= 0) ? (y / 4 - y / 100 + y / 400) :
-(leaps_thru_end_of(-(y + 1)) + 1);
}
@ -1605,15 +1605,19 @@ timesub(timep, offset, sp, tmp)
** Simplified normalize logic courtesy Paul Eggert.
*/
static int
static inline int
increment_overflow(number, delta)
int * number;
int delta;
{
#ifdef __GNUC__
return __builtin_add_overflow(*number, delta, number);
#else
int number0;
number0 = *number;
*number += delta;
return (*number < number0) != (delta < 0);
#endif
}
static int

View File

@ -12,6 +12,7 @@ extern const char kWeekdayNameShort[7][4];
extern const char kWeekdayName[7][10];
extern const char kMonthNameShort[12][4];
extern const char kMonthName[12][10];
extern const unsigned short kMonthYearDay[2][12];
extern char *tzname[2];
extern long CLOCKS_PER_SEC;

View File

@ -47,9 +47,9 @@ static noinline long times2(struct tms *out_times, struct rusage *ru) {
&KernelTime, &UserTime)) {
return winerr();
}
filetimetotimeval(&tv, UserTime);
FileTimeToTimeVal(&tv, UserTime);
out_times->tms_utime = convertmicros(&tv, tick);
filetimetotimeval(&tv, KernelTime);
FileTimeToTimeVal(&tv, KernelTime);
out_times->tms_stime = convertmicros(&tv, tick);
out_times->tms_cutime = 0;
out_times->tms_cstime = 0;

View File

@ -57,6 +57,7 @@
#define kZipCfileOffsetLastmodifieddate 14
#define kZipCfileOffsetCrc32 16
#define kZipCfileOffsetCompressedsize 20
#define kZipCfileOffsetUncompressedsize 24
#define kZipCfileOffsetExternalattributes 38
#define kZipCfileOffsetOffset 42
@ -71,10 +72,10 @@
#define kZipGflagUtf8 0x800
#define kZipExtraHdrSize 4
#define kZipExtraZip64 0x0001
#define kZipExtraNtfs 0x000a
#define kZipExtraNtfsFiletimes 0x0001
#define kZipExtraHdrSize 4
#define kZipExtraZip64 0x0001
#define kZipExtraNtfs 0x000a
#define kZipExtraExtendedTimestamp 0x5455
#define kZipCfileMagic "PK\001\002"
@ -105,9 +106,10 @@
READ16LE((P) + kZipCfileOffsetLastmodifiedtime) /* @see DOS_TIME() */
#define ZIP_CFILE_LASTMODIFIEDDATE(P) \
READ16LE((P) + kZipCfileOffsetLastmodifieddate) /* @see DOS_DATE() */
#define ZIP_CFILE_CRC32(P) READ32LE((P) + kZipCfileOffsetCrc32)
#define ZIP_CFILE_COMPRESSEDSIZE(P) READ32LE(P + kZipCfileOffsetCompressedsize)
#define ZIP_CFILE_UNCOMPRESSEDSIZE(P) READ32LE((P) + 24)
#define ZIP_CFILE_CRC32(P) READ32LE((P) + kZipCfileOffsetCrc32)
#define ZIP_CFILE_COMPRESSEDSIZE(P) READ32LE(P + kZipCfileOffsetCompressedsize)
#define ZIP_CFILE_UNCOMPRESSEDSIZE(P) \
READ32LE((P) + kZipCfileOffsetUncompressedsize)
#define ZIP_CFILE_NAMESIZE(P) READ16LE((P) + 28)
#define ZIP_CFILE_EXTRASIZE(P) READ16LE((P) + 30)
#define ZIP_CFILE_COMMENTSIZE(P) READ16LE((P) + 32)
@ -124,7 +126,7 @@
(ZIP_CFILE_NAMESIZE(P) + ZIP_CFILE_EXTRASIZE(P) + ZIP_CFILE_COMMENTSIZE(P) + \
kZipCfileHdrMinSize)
/* central directory file header */
/* local file header */
#define ZIP_LFILE_MAGIC(P) READ32LE(P)
#define ZIP_LFILE_VERSIONNEED(P) ((P)[4])
#define ZIP_LFILE_OSNEED(P) ((P)[5])

View File

@ -29,7 +29,7 @@
*/
int __zipos_close(struct ZiposHandle *h) {
if (h) {
munmap(h->map, h->mapsize);
free(h->map);
free(h);
}
return 0;

View File

@ -27,16 +27,14 @@
ssize_t __zipos_find(struct Zipos *zipos, const struct ZiposUri *name) {
size_t i, cf;
if ((zipos = __zipos_get())) {
assert(ZIP_CDIR_MAGIC(zipos->cdir) == kZipCdirHdrMagic);
for (i = 0, cf = ZIP_CDIR_OFFSET(zipos->cdir);
i < ZIP_CDIR_RECORDS(zipos->cdir);
++i, cf += ZIP_CFILE_HDRSIZE(zipos->map + cf)) {
assert(ZIP_CFILE_MAGIC(zipos->map + cf) == kZipCfileHdrMagic);
if (name->len == ZIP_CFILE_NAMESIZE(zipos->map + cf) &&
memcmp(name->path, ZIP_CFILE_NAME(zipos->map + cf), name->len) == 0) {
return cf;
}
assert(ZIP_CDIR_MAGIC(zipos->cdir) == kZipCdirHdrMagic);
for (i = 0, cf = ZIP_CDIR_OFFSET(zipos->cdir);
i < ZIP_CDIR_RECORDS(zipos->cdir);
++i, cf += ZIP_CFILE_HDRSIZE(zipos->map + cf)) {
assert(ZIP_CFILE_MAGIC(zipos->map + cf) == kZipCfileHdrMagic);
if (name->len == ZIP_CFILE_NAMESIZE(zipos->map + cf) &&
memcmp(name->path, ZIP_CFILE_NAME(zipos->map + cf), name->len) == 0) {
return cf;
}
}
return -1;

View File

@ -99,9 +99,9 @@ static int __zipos_load(struct Zipos *zipos, size_t cf, unsigned flags,
if ((h->size = ZIP_LFILE_UNCOMPRESSEDSIZE(zipos->map + lf))) {
if (ZIP_LFILE_COMPRESSIONMETHOD(zipos->map + lf)) {
assert(ZIP_LFILE_COMPRESSEDSIZE(zipos->map + lf));
h->mapsize = ROUNDUP(h->size + FRAMESIZE, FRAMESIZE);
if ((h->map = mapanon(h->mapsize)) != MAP_FAILED) {
h->mem = h->map + FRAMESIZE / 2;
h->mapsize = h->size;
if ((h->map = malloc(h->mapsize)) != MAP_FAILED) {
h->mem = h->map;
if ((IsTiny() ? __zipos_inflate_tiny : __zipos_inflate_fast)(
h, ZIP_LFILE_CONTENT(zipos->map + lf),
ZIP_LFILE_COMPRESSEDSIZE(zipos->map + lf)) == -1) {
@ -132,7 +132,7 @@ static int __zipos_load(struct Zipos *zipos, size_t cf, unsigned flags,
* Loads compressed file from αcτµαlly pδrταblε εxεcµταblε object store.
*
* @param uri is obtained via __zipos_parseuri()
* @asyncsignalsafe
* @note don't call open() from signal handlers
*/
int __zipos_open(const struct ZiposUri *name, unsigned flags, int mode) {
int fd;
@ -140,7 +140,6 @@ int __zipos_open(const struct ZiposUri *name, unsigned flags, int mode) {
sigset_t oldmask;
struct Zipos *zipos;
assert((flags & O_ACCMODE) == O_RDONLY);
sigprocmask(SIG_BLOCK, &kSigsetFull, &oldmask);
if ((zipos = __zipos_get())) {
if ((cf = __zipos_find(zipos, name)) != -1) {
fd = __zipos_load(zipos, cf, flags, mode);
@ -150,6 +149,5 @@ int __zipos_open(const struct ZiposUri *name, unsigned flags, int mode) {
} else {
fd = enoexec();
}
sigprocmask(SIG_SETMASK, &oldmask, NULL);
return fd;
}

View File

@ -0,0 +1,67 @@
/*-*- 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
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "libc/str/str.h"
#include "libc/time/struct/tm.h"
#include "libc/time/time.h"
#include "net/http/http.h"
/**
* Formats HTTP timestamp, e.g.
*
* Sun, 04 Oct 2020 19:50:10 GMT
*
* @param tm must be zulu see gmtime_r() and nowl()
* @see ParseHttpDateTime()
*/
char *FormatHttpDateTime(char p[hasatleast 30], struct tm *tm) {
unsigned i;
p = mempcpy(p, kWeekdayNameShort[tm->tm_wday], 3);
*p++ = ',';
*p++ = ' ';
i = tm->tm_mday;
*p++ = '0' + i / 10;
*p++ = '0' + i % 10;
*p++ = ' ';
p = mempcpy(p, kMonthNameShort[tm->tm_mon], 3);
*p++ = ' ';
i = tm->tm_year + 1900;
*p++ = '0' + i / 1000;
*p++ = '0' + i / 100 % 10;
*p++ = '0' + i / 10 % 10;
*p++ = '0' + i % 10;
*p++ = ' ';
i = tm->tm_hour;
*p++ = '0' + i / 10;
*p++ = '0' + i % 10;
*p++ = ':';
i = tm->tm_min;
*p++ = '0' + i / 10;
*p++ = '0' + i % 10;
*p++ = ':';
i = tm->tm_sec;
*p++ = '0' + i / 10;
*p++ = '0' + i % 10;
*p++ = ' ';
*p++ = 'G';
*p++ = 'M';
*p++ = 'T';
*p = '\0';
return p;
}

View File

@ -58,5 +58,5 @@ Retry-After, kHttpRetryAfter
Server, kHttpServer
Vary, kHttpVary
Warning, kHttpWarning
WWW-Authenticate, kHttpWwwAuthenticate
Www-Authenticate, kHttpWwwAuthenticate
Last-Modified, kHttpLastModified

View File

@ -216,7 +216,7 @@ LookupHttpHeader (register const char *str, register size_t len)
#line 43 "gethttpheader.gperf"
{"Keep-Alive", kHttpKeepAlive},
#line 61 "gethttpheader.gperf"
{"WWW-Authenticate", kHttpWwwAuthenticate},
{"Www-Authenticate", kHttpWwwAuthenticate},
#line 35 "gethttpheader.gperf"
{"Expires", kHttpExpires},
#line 46 "gethttpheader.gperf"

View File

@ -9,7 +9,7 @@
%readonly-tables
%struct-type
%define lookup-function-name LookupHttpMethod
struct HttpMethodSlot { char *name; char code; };
struct HttpMethodSlot { char name[8]; char code; };
%%
DELETE, kHttpDelete
GET, kHttpGet
@ -20,17 +20,11 @@ CONNECT, kHttpConnect
OPTIONS, kHttpOptions
TRACE, kHttpTrace
COPY, kHttpCopy
CHECKOUT, kHttpCheckout
LOCK, kHttpLock
MERGE, kHttpMerge
MKACTIVITY, kHttpMkactivity
MKCOL, kHttpMkcol
MOVE, kHttpMove
NOTIFY, kHttpNotify
PATCH, kHttpPatch
PROPFIND, kHttpPropfind
PROPPATCH, kHttpProppatch
REPORT, kHttpReport
SUBSCRIBE, kHttpSubscribe
UNLOCK, kHttpUnlock
UNSUBSCRIBE, kHttpUnsubscribe

View File

@ -35,14 +35,14 @@
#include "net/http/http.h"
#define GPERF_DOWNCASE
#line 12 "gethttpmethod.gperf"
struct HttpMethodSlot { char *name; char code; };
struct HttpMethodSlot { char name[8]; char code; };
#define TOTAL_KEYWORDS 23
#define TOTAL_KEYWORDS 17
#define MIN_WORD_LENGTH 3
#define MAX_WORD_LENGTH 11
#define MIN_HASH_VALUE 4
#define MAX_HASH_VALUE 34
/* maximum key range = 31, duplicates = 0 */
#define MAX_WORD_LENGTH 7
#define MIN_HASH_VALUE 3
#define MAX_HASH_VALUE 25
/* maximum key range = 23, duplicates = 0 */
#ifndef GPERF_DOWNCASE
#define GPERF_DOWNCASE 1
@ -101,32 +101,32 @@ hash (register const char *str, register size_t len)
{
static const unsigned char asso_values[] =
{
35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
35, 35, 35, 35, 35, 0, 35, 5, 10, 10,
35, 5, 10, 35, 35, 0, 30, 15, 0, 0,
0, 35, 5, 15, 0, 5, 35, 35, 35, 35,
35, 35, 35, 35, 35, 35, 35, 0, 35, 5,
10, 10, 35, 5, 10, 35, 35, 0, 30, 15,
0, 0, 0, 35, 5, 15, 0, 5, 35, 35,
35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
35, 35, 35, 35, 35, 35
26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
26, 26, 26, 26, 26, 0, 26, 5, 15, 0,
26, 5, 0, 26, 26, 10, 15, 10, 0, 5,
0, 26, 10, 26, 5, 0, 26, 26, 26, 26,
26, 26, 26, 26, 26, 26, 26, 0, 26, 5,
15, 0, 26, 5, 0, 26, 26, 10, 15, 10,
0, 5, 0, 26, 10, 26, 5, 0, 26, 26,
26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
26, 26, 26, 26, 26, 26
};
return len + asso_values[(unsigned char)str[1]] + asso_values[(unsigned char)str[0]];
}
@ -136,58 +136,46 @@ LookupHttpMethod (register const char *str, register size_t len)
{
static const struct HttpMethodSlot wordlist[] =
{
{""}, {""}, {""}, {""},
#line 17 "gethttpmethod.gperf"
{"POST", kHttpPost},
#line 30 "gethttpmethod.gperf"
{"PATCH", kHttpPatch},
#line 29 "gethttpmethod.gperf"
{"NOTIFY", kHttpNotify},
#line 20 "gethttpmethod.gperf"
{"OPTIONS", kHttpOptions},
{""}, {""}, {""},
#line 18 "gethttpmethod.gperf"
{"PUT", kHttpPut},
#line 22 "gethttpmethod.gperf"
{"COPY", kHttpCopy},
#line 21 "gethttpmethod.gperf"
{"TRACE", kHttpTrace},
#line 35 "gethttpmethod.gperf"
#line 16 "gethttpmethod.gperf"
{"HEAD", kHttpHead},
#line 28 "gethttpmethod.gperf"
{"PATCH", kHttpPatch},
#line 30 "gethttpmethod.gperf"
{"UNLOCK", kHttpUnlock},
#line 19 "gethttpmethod.gperf"
{"CONNECT", kHttpConnect},
#line 31 "gethttpmethod.gperf"
{"PROPFIND", kHttpPropfind},
#line 32 "gethttpmethod.gperf"
{"PROPPATCH", kHttpProppatch},
{""},
#line 36 "gethttpmethod.gperf"
{"UNSUBSCRIBE", kHttpUnsubscribe},
{""},
#line 15 "gethttpmethod.gperf"
{"GET", kHttpGet},
#line 28 "gethttpmethod.gperf"
{"MOVE", kHttpMove},
#line 27 "gethttpmethod.gperf"
{"MKCOL", kHttpMkcol},
#line 33 "gethttpmethod.gperf"
{"REPORT", kHttpReport},
#line 17 "gethttpmethod.gperf"
{"POST", kHttpPost},
{""},
#line 27 "gethttpmethod.gperf"
{"NOTIFY", kHttpNotify},
#line 20 "gethttpmethod.gperf"
{"OPTIONS", kHttpOptions},
{""},
#line 22 "gethttpmethod.gperf"
{"COPY", kHttpCopy},
#line 24 "gethttpmethod.gperf"
{"MERGE", kHttpMerge},
#line 29 "gethttpmethod.gperf"
{"REPORT", kHttpReport},
#line 19 "gethttpmethod.gperf"
{"CONNECT", kHttpConnect},
{""},
#line 23 "gethttpmethod.gperf"
{"CHECKOUT", kHttpCheckout},
#line 16 "gethttpmethod.gperf"
{"HEAD", kHttpHead},
#line 26 "gethttpmethod.gperf"
{"MKACTIVITY", kHttpMkactivity},
{"MOVE", kHttpMove},
#line 21 "gethttpmethod.gperf"
{"TRACE", kHttpTrace},
#line 14 "gethttpmethod.gperf"
{"DELETE", kHttpDelete},
{""}, {""},
#line 34 "gethttpmethod.gperf"
{"SUBSCRIBE", kHttpSubscribe},
#line 23 "gethttpmethod.gperf"
{"LOCK", kHttpLock},
#line 25 "gethttpmethod.gperf"
{"MERGE", kHttpMerge},
{""}, {""}, {""},
#line 24 "gethttpmethod.gperf"
{"LOCK", kHttpLock}
{"MKCOL", kHttpMkcol}
};
if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH)

View File

@ -1,30 +1,25 @@
#ifndef COSMOPOLITAN_LIBC_HTTP_HTTP_H_
#define COSMOPOLITAN_LIBC_HTTP_HTTP_H_
#include "libc/alg/alg.h"
#include "libc/time/struct/tm.h"
#define kHttpGet 0
#define kHttpHead 1
#define kHttpPost 2
#define kHttpPut 3
#define kHttpDelete 4
#define kHttpConnect 5
#define kHttpOptions 6
#define kHttpTrace 7
#define kHttpCopy 8
#define kHttpCheckout 9
#define kHttpLock 10
#define kHttpMerge 11
#define kHttpMkactivity 12
#define kHttpMkcol 13
#define kHttpMove 14
#define kHttpNotify 15
#define kHttpPatch 16
#define kHttpPropfind 17
#define kHttpProppatch 18
#define kHttpReport 19
#define kHttpSubscribe 20
#define kHttpUnlock 21
#define kHttpUnsubscribe 22
#define kHttpGet 0
#define kHttpHead 1
#define kHttpPost 2
#define kHttpPut 3
#define kHttpDelete 4
#define kHttpConnect 5
#define kHttpOptions 6
#define kHttpTrace 7
#define kHttpCopy 8
#define kHttpLock 9
#define kHttpMerge 10
#define kHttpMkcol 11
#define kHttpMove 12
#define kHttpNotify 13
#define kHttpPatch 14
#define kHttpReport 15
#define kHttpUnlock 16
#define kHttpAccept 0
#define kHttpAcceptCharset 1
@ -86,17 +81,24 @@ struct HttpRequestSlice {
struct HttpRequest {
int method;
int length;
struct HttpRequestSlice uri;
struct HttpRequestSlice version;
struct HttpRequestSlice scratch;
struct HttpRequestSlice headers[kHttpHeadersMax];
};
extern const char kHttpMethod[17][8];
int GetHttpHeader(const char *, size_t);
int GetHttpMethod(const char *, size_t);
int ParseHttpRequest(struct HttpRequest *, const char *, size_t);
int NegotiateHttpRequest(int, const char *, uint32_t *, char *, uint32_t *,
uint32_t *, bool, long double);
char *FormatHttpDateTime(char[hasatleast 30], struct tm *);
bool ParseHttpRange(const char *, size_t, long, long *, long *);
unsigned ParseHttpVersion(const char *, size_t);
int64_t ParseHttpDateTime(const char *, size_t);
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */

View File

@ -12,65 +12,70 @@ NET_HTTP_A_SRCS_S = $(filter %.S,$(NET_HTTP_A_FILES))
NET_HTTP_A_SRCS_C = $(filter %.c,$(NET_HTTP_A_FILES))
NET_HTTP_A_SRCS_R = $(filter %.rl,$(NET_HTTP_A_FILES))
NET_HTTP_A_SRCS = \
$(NET_HTTP_A_SRCS_S) \
$(NET_HTTP_A_SRCS_C) \
NET_HTTP_A_SRCS = \
$(NET_HTTP_A_SRCS_S) \
$(NET_HTTP_A_SRCS_C) \
$(NET_HTTP_A_SRCS_R)
NET_HTTP_A_OBJS = \
$(NET_HTTP_A_SRCS:%=o/$(MODE)/%.zip.o) \
$(NET_HTTP_A_SRCS_S:%.S=o/$(MODE)/%.o) \
$(NET_HTTP_A_SRCS_C:%.c=o/$(MODE)/%.o) \
NET_HTTP_A_OBJS = \
$(NET_HTTP_A_SRCS:%=o/$(MODE)/%.zip.o) \
$(NET_HTTP_A_SRCS_S:%.S=o/$(MODE)/%.o) \
$(NET_HTTP_A_SRCS_C:%.c=o/$(MODE)/%.o) \
$(NET_HTTP_A_SRCS_R:%.rl=o/$(MODE)/%.o)
NET_HTTP_A_CHECKS = \
$(NET_HTTP_A).pkg \
NET_HTTP_A_CHECKS = \
$(NET_HTTP_A).pkg \
$(NET_HTTP_A_HDRS:%=o/$(MODE)/%.ok)
NET_HTTP_A_DIRECTDEPS = \
LIBC_ALG \
LIBC_CALLS \
LIBC_CONV \
LIBC_FMT \
LIBC_LOG \
LIBC_NEXGEN32E \
LIBC_RUNTIME \
LIBC_SOCK \
LIBC_STDIO \
LIBC_STUBS \
LIBC_SYSV \
LIBC_TIME \
NET_HTTP_A_DIRECTDEPS = \
LIBC_ALG \
LIBC_CALLS \
LIBC_CONV \
LIBC_FMT \
LIBC_LOG \
LIBC_NEXGEN32E \
LIBC_RUNTIME \
LIBC_SOCK \
LIBC_STDIO \
LIBC_STUBS \
LIBC_SYSV \
LIBC_TIME \
LIBC_X
NET_HTTP_A_DEPS := \
NET_HTTP_A_DEPS := \
$(call uniq,$(foreach x,$(NET_HTTP_A_DIRECTDEPS),$($(x))))
$(NET_HTTP_A): net/http/ \
$(NET_HTTP_A).pkg \
$(NET_HTTP_A): net/http/ \
$(NET_HTTP_A).pkg \
$(NET_HTTP_A_OBJS)
$(NET_HTTP_A).pkg: \
$(NET_HTTP_A_OBJS) \
$(NET_HTTP_A).pkg: \
$(NET_HTTP_A_OBJS) \
$(foreach x,$(NET_HTTP_A_DIRECTDEPS),$($(x)_A).pkg)
o/$(MODE)/net/http/formathttpdatetime.o: \
OVERRIDE_CFLAGS += \
-O3
ifeq (,$(MODE))
$(NET_HTTP_A_OBJS): \
OVERRIDE_CFLAGS += \
-fsanitize=address
endif
NET_HTTP_LIBS = $(foreach x,$(NET_HTTP_ARTIFACTS),$($(x)))
NET_HTTP_SRCS = $(foreach x,$(NET_HTTP_ARTIFACTS),$($(x)_SRCS))
NET_HTTP_HDRS = $(foreach x,$(NET_HTTP_ARTIFACTS),$($(x)_HDRS))
NET_HTTP_CHECKS = $(foreach x,$(NET_HTTP_ARTIFACTS),$($(x)_CHECKS))
NET_HTTP_OBJS = $(foreach x,$(NET_HTTP_ARTIFACTS),$($(x)_OBJS))
$(NET_HTTP_OBJS): $(BUILD_FILES) net/http/http.mk
.PRECIOUS: \
$(NET_HTTP_A_SRCS_R:%.rl=build/bootstrap/%.c) \
o/$(MODE)/net/http/uricspn.s \
o/$(MODE)/net/http/uriparse.s \
o/$(MODE)/net/http/uricspn.i \
o/$(MODE)/net/http/uriparse.i \
o/$(MODE)/net/http/uriparse.c \
.PRECIOUS: \
$(NET_HTTP_A_SRCS_R:%.rl=o/$(MODE)/%.c) \
o/$(MODE)/net/http/uricspn.s \
o/$(MODE)/net/http/uricspn.i \
o/$(MODE)/net/http/uricspn.c
.PHONY: o/$(MODE)/net/http
o/$(MODE)/net/http: \
$(NET_HTTP_CHECKS) \
o/$(MODE)/net/http: \
$(NET_HTTP_CHECKS) \
$(NET_HTTP_A_SRCS_R:%.rl=%.svgz)

View File

@ -0,0 +1,40 @@
/*-*- 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
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "net/http/http.h"
const char kHttpMethod[17][8] = {
"GET", //
"HEAD", //
"POST", //
"PUT", //
"DELETE", //
"CONNECT", //
"OPTIONS", //
"TRACE", //
"COPY", //
"LOCK", //
"MERGE", //
"MKCOL", //
"MOVE", //
"NOTIFY", //
"PATCH", //
"REPORT", //
"UNLOCK", //
};

View File

@ -0,0 +1,57 @@
/*-*- 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
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "libc/bits/bits.h"
#include "libc/time/time.h"
#include "net/http/http.h"
static unsigned ParseMonth(const char *p) {
int i;
for (i = 0; i < 12; ++i) {
if ((READ32LE(p) & 0x00ffffff) == READ32LE(kMonthNameShort[i])) {
return i + 1;
}
}
return 1;
}
/**
* Parses HTTP timestamp, e.g.
*
* Sun, 04 Oct 2020 19:50:10 GMT
*
* @return seconds from unix epoch
* @see FormatHttpDateTime()
*/
int64_t ParseHttpDateTime(const char *p, size_t n) {
unsigned weekday, year, month, day, hour, minute, second, yday, leap;
if (n != 29) return 0;
day = (p[5] - '0') * 10 + (p[6] - '0') - 1;
month = ParseMonth(p + 8);
year = (p[12] - '0') * 1000 + (p[13] - '0') * 100 + (p[14] - '0') * 10 +
(p[15] - '0') - 1900;
hour = (p[17] - '0') * 10 + (p[18] - '0');
minute = (p[20] - '0') * 10 + (p[21] - '0');
second = (p[23] - '0') * 10 + (p[24] - '0');
leap = year % 4 == 0 && (year % 100 || year % 400 == 0);
yday = day + kMonthYearDay[leap][month - 1];
return second + minute * 60 + hour * 3600 + yday * 86400 +
(year - 70) * 31536000ull + ((year - 69) / 4) * 86400ull -
((year - 1) / 100) * 86400ull + ((year + 299) / 400) * 86400ull;
}

View File

@ -0,0 +1,72 @@
/*-*- 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
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "libc/str/str.h"
#include "net/http/http.h"
/**
* Parses HTTP Range request header.
*/
bool ParseHttpRange(const char *p, size_t n, long resourcelength,
long *out_start, long *out_length) {
long start, length, ending;
*out_start = 0;
*out_length = 0;
if (memchr(p, ',', n)) return false;
if (n < 7 || memcmp(p, "bytes=", 6) != 0) return false;
p += 6, n -= 6;
if (n && *p == '-') {
++p, --n;
length = 0;
while (n && '0' <= *p && *p <= '9') {
if (__builtin_mul_overflow(length, 10, &length)) return false;
if (__builtin_add_overflow(length, *p - '0', &length)) return false;
++p, --n;
}
if (__builtin_sub_overflow(resourcelength, length, &start)) return false;
} else {
start = 0;
while (n && '0' <= *p && *p <= '9') {
if (__builtin_mul_overflow(start, 10, &start)) return false;
if (__builtin_add_overflow(start, *p - '0', &start)) return false;
++p, --n;
}
if (n && *p == '-') {
++p, --n;
length = 0;
while (n && '0' <= *p && *p <= '9') {
if (__builtin_mul_overflow(length, 10, &length)) return false;
if (__builtin_add_overflow(length, *p - '0', &length)) return false;
++p, --n;
}
if (__builtin_add_overflow(length, 1, &length)) return false;
if (__builtin_sub_overflow(length, start, &length)) return false;
} else if (__builtin_sub_overflow(resourcelength, start, &length)) {
return false;
}
}
if (n) return false;
if (start < 0) return false;
if (length < 0) return false;
*out_start = start;
*out_length = length;
if (__builtin_add_overflow(start, length, &ending)) return false;
if (ending > resourcelength) return false;
return true;
}

View File

@ -112,7 +112,10 @@ int ParseHttpRequest(struct HttpRequest *req, const char *p, size_t n) {
}
break;
case LF2:
if (c == '\n') return 0;
if (c == '\n') {
req->length = i + 1;
return 0;
}
return ebadmsg();
default:
unreachable;

View File

@ -0,0 +1,33 @@
/*-*- 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
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "libc/bits/bits.h"
#include "net/http/http.h"
unsigned ParseHttpVersion(const char *p, size_t n) {
unsigned x;
if (n >= 8 && READ32LE(p) == ('H' | 'T' << 8 | 'T' << 16 | 'P' << 24)) {
if (READ32LE(p + 4) == ('/' | '1' << 8 | '.' << 16 | '1' << 24)) {
return 101;
} else if (READ32LE(p + 4) == ('/' | '1' << 8 | '.' << 16 | '0' << 24)) {
return 100;
}
}
return -1;
}

View File

@ -0,0 +1,32 @@
/*-*- 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
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "libc/conv/conv.h"
#include "libc/dos.h"
#include "libc/testlib/testlib.h"
TEST(DosDateTimeToUnix, test) {
EXPECT_EQ(1601929396,
DosDateTimeToUnix(DOS_DATE(2020, 10, 5), DOS_TIME(20, 23, 16)));
}
TEST(DosDateTimeToUnix, testNotLeapYear) {
EXPECT_EQ(4107529396,
DosDateTimeToUnix(DOS_DATE(2100, 2, 28), DOS_TIME(20, 23, 16)));
}

View File

@ -25,6 +25,9 @@ TEST_LIBC_CONV_CHECKS = \
TEST_LIBC_CONV_DIRECTDEPS = \
LIBC_CONV \
LIBC_FMT \
LIBC_LOG \
LIBC_STDIO \
LIBC_NEXGEN32E \
LIBC_STUBS \
LIBC_TINYMATH \

View File

@ -23,11 +23,11 @@
#include "libc/testlib/testlib.h"
#include "libc/time/time.h"
TEST(timevaltofiletime, roundTrip) {
TEST(TimeValToFileTime, roundTrip) {
struct timeval tv1, tv2;
tv1.tv_sec = 31337;
tv1.tv_usec = 1337;
filetimetotimeval(&tv2, timevaltofiletime(&tv1));
FileTimeToTimeVal(&tv2, TimeValToFileTime(&tv1));
EXPECT_EQ(31337, tv2.tv_sec);
EXPECT_EQ(1337, tv2.tv_usec);
}

View File

@ -0,0 +1,32 @@
/*-*- 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
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "libc/fmt/fmt.h"
#include "libc/runtime/gc.h"
#include "libc/testlib/testlib.h"
#include "libc/x/x.h"
TEST(fcvt, test) {
int decpt, sign;
ASSERT_STREQ("3.14159265358979", xasprintf("%.14f", 3.14159265358979323846));
ASSERT_STREQ("3141592653589793",
fcvt(3.14159265358979323846, 15, &decpt, &sign));
ASSERT_EQ(1, decpt);
ASSERT_EQ(0, sign);
}

View File

@ -679,9 +679,12 @@ TEST(snprintf, formatStringLiteral) {
}
BENCH(palandprintf, bench) {
EZBENCH2("snprintf %x", donothing, Format("%x", VEIL("r", INT_MIN)));
EZBENCH2("snprintf %d", donothing, Format("%d", VEIL("r", INT_MIN)));
EZBENCH2("snprintf %s", donothing, Format("%s", VEIL("r", "hi (╯°□°)╯")));
EZBENCH2("23 %x", donothing, Format("%x", VEIL("r", 23)));
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("ascii %s", donothing, Format("%s", VEIL("r", "hiuhcreohucreo")));
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 (╯°□°)╯")));
EZBENCH2("int64toarray", donothing, int64toarray_radix10(-3, buffer));

View File

@ -72,6 +72,22 @@ TEST(SUITE(snprintf), testStringPrecision_showsTrueBinary) {
EXPECT_STREQ("\3\4\0", Format("%.*s", 3, "\3\4\0"));
}
TEST(SUITE(snprintf), testPrecision_usesCodepointCount) {
EXPECT_STREQ("ちゃぶ台返し", Format("%.*s", 6, "ちゃぶ台返し"));
TEST(SUITE(snprintf), testPrecision_usesByteCount) {
EXPECT_STREQ("ちゃ", Format("%.*s", 6, "ちゃぶ台返し"));
}
TEST(SUITE(snprintf), testReprChar16) {
EXPECT_STREQ("u'♥'", Format("%`'hc", u'♥'));
}
TEST(SUITE(snprintf), testReprChar32) {
EXPECT_STREQ("L'♥'", Format("%`'Lc", L'♥'));
}
TEST(SUITE(snprintf), testReprUtf8) {
EXPECT_STREQ("\"\"", Format("%`'s", u8""));
}
TEST(SUITE(snprintf), testReprUtf8Precision_countsBytes) {
EXPECT_STREQ("\"\"", Format("%`'.*s", 3, u8""));
}

View File

@ -54,10 +54,3 @@ TEST(inet_ntop, testNoSpace) {
ASSERT_STREQ("", buf);
tfree(buf);
}
TEST(inet_ntop, testSqueeze) {
char *buf = memcpy(tmalloc(8), "hi", 3);
uint8_t localhost[4] = {0, 0, 0, 0};
ASSERT_STREQ("0.0.0.0", inet_ntop(AF_INET, localhost, buf, 8));
tfree(buf);
}

View File

@ -0,0 +1,48 @@
/*-*- 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
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "libc/str/str.h"
#include "libc/testlib/testlib.h"
#include "libc/time/struct/tm.h"
#include "libc/time/time.h"
#include "net/http/http.h"
#define PARSE(s) ParseHttpDateTime(s, sizeof(s) - 1)
TEST(ParseHttpDateTime, testRoundTrip) {
int64_t t;
struct tm tm;
char b[30], *s = "Mon, 05 Oct 2020 20:23:16 GMT";
t = ParseHttpDateTime(s, strlen(s));
EXPECT_EQ(1601929396, t);
gmtime_r(&t, &tm);
FormatHttpDateTime(b, &tm);
EXPECT_STREQ(s, b);
}
TEST(ParseHttpDateTime, test64bit) {
int64_t t;
struct tm tm;
char b[30], *s = "Tue, 05 Oct 2100 20:23:16 GMT";
t = ParseHttpDateTime(s, strlen(s));
EXPECT_EQ(4126450996, t);
gmtime_r(&t, &tm);
FormatHttpDateTime(b, &tm);
EXPECT_STREQ(s, b);
}

View File

@ -0,0 +1,102 @@
/*-*- 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
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "libc/str/str.h"
#include "libc/testlib/testlib.h"
#include "net/http/http.h"
TEST(ParseHttpRange, testEmptyHack) {
long start, length;
const char *s = "bytes=-0";
EXPECT_TRUE(ParseHttpRange(s, strlen(s), 100, &start, &length));
EXPECT_EQ(100, start);
EXPECT_EQ(0, length);
}
TEST(ParseHttpRange, testEmptyRange_isntEmpty) {
long start, length;
const char *s = "bytes=0-0";
EXPECT_TRUE(ParseHttpRange(s, strlen(s), 100, &start, &length));
EXPECT_EQ(0, start);
EXPECT_EQ(1, length);
}
TEST(ParseHttpRange, testInclusiveIndexing) {
long start, length;
const char *s = "bytes=0-10";
EXPECT_TRUE(ParseHttpRange(s, strlen(s), 100, &start, &length));
EXPECT_EQ(0, start);
EXPECT_EQ(11, length);
}
TEST(ParseHttpRange, testOffset) {
long start, length;
const char *s = "bytes=1-10";
EXPECT_TRUE(ParseHttpRange(s, strlen(s), 100, &start, &length));
EXPECT_EQ(1, start);
EXPECT_EQ(10, length);
}
TEST(ParseHttpRange, testToEnd) {
long start, length;
const char *s = "bytes=40";
EXPECT_TRUE(ParseHttpRange(s, strlen(s), 100, &start, &length));
EXPECT_EQ(40, start);
EXPECT_EQ(60, length);
}
TEST(ParseHttpRange, testFromEnd) {
long start, length;
const char *s = "bytes=-40";
EXPECT_TRUE(ParseHttpRange(s, strlen(s), 100, &start, &length));
EXPECT_EQ(60, start);
EXPECT_EQ(40, length);
}
TEST(ParseHttpRange, testOutOfRange) {
long start, length;
const char *s = "bytes=0-100";
EXPECT_FALSE(ParseHttpRange(s, strlen(s), 100, &start, &length));
EXPECT_EQ(0, start);
EXPECT_EQ(101, length);
}
TEST(ParseHttpRange, testInvalidRange) {
long start, length;
const char *s = "bytes=10-0";
EXPECT_FALSE(ParseHttpRange(s, strlen(s), 100, &start, &length));
EXPECT_EQ(0, start);
EXPECT_EQ(0, length);
}
TEST(ParseHttpRange, testOverflow_duringIntepretation_doesntSetRanges) {
long start, length;
const char *s = "bytes=99-9223372036854775808";
EXPECT_FALSE(ParseHttpRange(s, strlen(s), 100, &start, &length));
EXPECT_EQ(0, start);
EXPECT_EQ(0, length);
}
TEST(ParseHttpRange, testOverflow_duringAddition_setsErrorRange) {
long start, length;
const char *s = "bytes=4611686018427387904-4611686018427387915";
EXPECT_FALSE(ParseHttpRange(s, strlen(s), 100, &start, &length));
EXPECT_EQ(4611686018427387904, start);
EXPECT_EQ(12, length);
}

View File

@ -17,6 +17,10 @@
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "libc/rand/lcg.h"
#include "libc/rand/rand.h"
#include "libc/stdio/stdio.h"
#include "libc/testlib/ezbench.h"
#include "libc/testlib/testlib.h"
#include "tool/build/lib/dis.h"
#include "tool/build/lib/modrm.h"
@ -227,7 +231,7 @@ TEST(DisInst, testOrImmCode16gcc) {
xed_decoded_inst_zero_set_mode(d->xedd, XED_MACHINE_MODE_REAL);
ASSERT_EQ(0, xed_instruction_length_decode(d->xedd, op, sizeof(op)));
DisInst(d, b1, DisSpec(d->xedd, b2));
EXPECT_STREQ("or $0xc00,12(%esp)", b1);
EXPECT_STREQ("orw $0xc00,12(%esp)", b1);
}
TEST(DisInst, testPause) {
@ -269,3 +273,19 @@ TEST(DisInst, testJmpEq) {
DisInst(d, b1, DisSpec(d->xedd, b2));
EXPECT_STREQ("jmp %rax", b1);
}
TEST(DisInst, testMovswSs) {
uint8_t op[] = {0x36, 0xA5};
xed_decoded_inst_zero_set_mode(d->xedd, XED_MACHINE_MODE_REAL);
ASSERT_EQ(0, xed_instruction_length_decode(d->xedd, op, sizeof(op)));
DisInst(d, b1, DisSpec(d->xedd, b2));
EXPECT_STREQ("movs %ss:(%si),(%di)", b1);
}
TEST(DisInst, testRealModrm_sibOverlap_showsNoDisplacement) {
uint8_t op[] = {0x8b, 0b00100101};
xed_decoded_inst_zero_set_mode(d->xedd, XED_MACHINE_MODE_REAL);
ASSERT_EQ(0, xed_instruction_length_decode(d->xedd, op, sizeof(op)));
DisInst(d, b1, DisSpec(d->xedd, b2));
EXPECT_STREQ("mov (%di),%sp", b1);
}

View File

@ -57,6 +57,12 @@ o/$(MODE)/third_party/zlib/adler32.o: \
-ffunction-sections \
-fdata-sections
ifeq (,$(MODE))
$(THIRD_PARTY_ZLIB_A_OBJS): \
OVERRIDE_CFLAGS += \
-fsanitize=address
endif
THIRD_PARTY_ZLIB_LIBS = $(foreach x,$(THIRD_PARTY_ZLIB_ARTIFACTS),$($(x)))
THIRD_PARTY_ZLIB_SRCS = $(foreach x,$(THIRD_PARTY_ZLIB_ARTIFACTS),$($(x)_SRCS))
THIRD_PARTY_ZLIB_HDRS = $(foreach x,$(THIRD_PARTY_ZLIB_ARTIFACTS),$($(x)_HDRS))

View File

@ -35,6 +35,7 @@ TOOL_BUILD_DIRECTDEPS = \
LIBC_ELF \
LIBC_FMT \
LIBC_LOG \
LIBC_LOG_ASAN \
LIBC_TINYMATH \
LIBC_MEM \
LIBC_NEXGEN32E \

View File

@ -27,6 +27,7 @@ TOOL_BUILD_EMUBIN_CHECKS = \
TOOL_BUILD_EMUBIN_DIRECTDEPS = \
LIBC_STUBS \
LIBC_NEXGEN32E \
LIBC_TINYMATH
TOOL_BUILD_EMUBIN_DEPS := \
@ -57,10 +58,10 @@ o/dbg/tool/build/emubin/lisp.real.com.dbg: \
$(APE)
-@$(APELINK)
o/tiny/tool/build/emubin/lisp.bin.dbg: \
o/$(MODE)/tool/build/emubin/lisp.bin.dbg: \
$(TOOL_BUILD_EMUBIN_DEPS) \
o/tiny/tool/build/emubin/lisp.real.o \
o/tiny/tool/build/emubin/lispstart.o \
o/$(MODE)/tool/build/emubin/lisp.real.o \
o/$(MODE)/tool/build/emubin/lispstart.o \
tool/build/emubin/lisp.lds
@$(ELFLINK) -z max-page-size=0x10

View File

@ -3,36 +3,35 @@
Copyright 2020 Justine Alexandra Roberts Tunney
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
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.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
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.
*/
#define TRACE 0
#define ERRORS 1
#define LONG long
#define WORD short
#define WORDS 2048
#define WORDS 8192
/*───────────────────────────────────────────────────────────────────────────│─╗
The LISP Challenge § 8086 PC BIOS / x86_64 Linux System Integration
The LISP Challenge § Impure x86_64 Linux 8086 PC BIOS System Integration
*/
#define ATOM(x) /* a.k.a. !(x&1) */ \
({ \
char IsAtom; \
asm("test%z1\t$1,%1" : "=@ccz"(IsAtom) : "Qm"((char)x)); \
IsAtom; \
#define TYPE(x) /* a.k.a. x&1 */ \
({ \
char IsAtom; \
asm("test%z1\t$1,%1" : "=@ccnz"(IsAtom) : "Qm"((char)x)); \
IsAtom; \
})
#define OBJECT(t, v) /* a.k.a. v<<1|t */ \
@ -57,71 +56,109 @@
c; \
})
#define REAL_READ(BASE, INDEX, DISP) /* a.k.a. b[i] */ \
({ \
__typeof(*(BASE)) Reg; \
if (__builtin_constant_p(INDEX) && !(INDEX)) { \
asm("mov\t%c2(%1),%0" \
: "=Q"(Reg) \
: "bDS"(BASE), "i"((DISP) * sizeof(*(BASE)))); \
} else { \
asm("mov\t%c3(%1,%2),%0" \
: "=Q"(Reg) \
: "b"(BASE), "DS"((long)(INDEX) * sizeof(*(BASE))), \
"i"((DISP) * sizeof(*(BASE)))); \
} \
Reg; \
#define REAL_READ_(REG, BASE, INDEX, DISP) \
({ \
__typeof(*(BASE)) Reg; \
if (__builtin_constant_p(INDEX) && !(INDEX)) { \
asm("mov\t%c2(%1),%0" \
: REG(Reg) \
: "bDS"(BASE), "i"((DISP) * sizeof(*(BASE))), \
"m"(BASE[(INDEX) + (DISP)])); \
} else { \
asm("mov\t%c3(%1,%2),%0" \
: REG(Reg) \
: "b"(BASE), "DS"((long)(INDEX) * sizeof(*(BASE))), \
"i"((DISP) * sizeof(*(BASE))), "m"(BASE[(INDEX) + (DISP)])); \
} \
Reg; \
})
#define REAL_READ_ARRAY_FIELD(OBJECT, MEMBER, INDEX, DISP) /* o->m[i] */ \
/* #ifdef __REAL_MODE__ */
#define REAL_READ(BASE, INDEX, DISP) /* a.k.a. b[i] */ \
(sizeof(*(BASE)) == 1 ? REAL_READ_("=Q", BASE, INDEX, DISP) \
: REAL_READ_("=r", BASE, INDEX, DISP))
/* #else */
/* #define REAL_READ(BASE, INDEX, DISP) BASE[INDEX + DISP] */
/* #endif */
#define REAL_READ_ARRAY_FIELD_(REG, OBJECT, MEMBER, INDEX, DISP) \
({ \
__typeof(*(OBJECT->MEMBER)) Reg; \
if (!(OBJECT)) { \
asm("mov\t%c2(%1),%0" \
: "=Q"(Reg) \
: REG(Reg) \
: "bDS"((long)(INDEX) * sizeof(*(OBJECT->MEMBER))), \
"i"(__builtin_offsetof(__typeof(*(OBJECT)), MEMBER) + \
sizeof(*(OBJECT->MEMBER)) * (DISP))); \
sizeof(*(OBJECT->MEMBER)) * (DISP)), \
"m"(OBJECT->MEMBER)); \
} else { \
asm("mov\t%c3(%1,%2),%0" \
: "=Q"(Reg) \
: REG(Reg) \
: "b"(OBJECT), "DS"((long)(INDEX) * sizeof(*(OBJECT->MEMBER))), \
"i"(__builtin_offsetof(__typeof(*(OBJECT)), MEMBER) + \
sizeof(*(OBJECT->MEMBER)) * (DISP))); \
sizeof(*(OBJECT->MEMBER)) * (DISP)), \
"m"(OBJECT->MEMBER)); \
} \
Reg; \
})
#define REAL_WRITE_ARRAY_FIELD(OBJECT, MEMBER, INDEX, DISP, VALUE) \
do { \
__typeof(*(OBJECT->MEMBER)) Reg; \
if (!(OBJECT)) { \
asm volatile("mov\t%0,%c2(%1)" \
: /* manual output */ \
: "Q"((__typeof(*(OBJECT->MEMBER)))(VALUE)), \
"bDS"((long)(INDEX) * sizeof(*(OBJECT->MEMBER))), \
"i"(__builtin_offsetof(__typeof(*(OBJECT)), MEMBER) + \
sizeof(*(OBJECT->MEMBER)) * (DISP)) \
: "memory"); \
} else { \
asm volatile("mov\t%0,%c3(%1,%2)" \
: /* manual output */ \
: "Q"((__typeof(*(OBJECT->MEMBER)))(VALUE)), "b"(OBJECT), \
"DS"((long)(INDEX) * sizeof(*(OBJECT->MEMBER))), \
"i"(__builtin_offsetof(__typeof(*(OBJECT)), MEMBER) + \
sizeof(*(OBJECT->MEMBER)) * (DISP)) \
: "memory"); \
} \
/* #ifdef __REAL_MODE__ */
#define REAL_READ_ARRAY_FIELD(OBJECT, MEMBER, INDEX, DISP) /* o->m[i] */ \
(sizeof(*(OBJECT->MEMBER)) == 1 \
? REAL_READ_ARRAY_FIELD_("=Q", OBJECT, MEMBER, INDEX, DISP) \
: REAL_READ_ARRAY_FIELD_("=r", OBJECT, MEMBER, INDEX, DISP))
/* #else */
/* #define REAL_READ_ARRAY_FIELD(o, m, i, d) o->m[i + d] */
/* #endif */
#define REAL_WRITE_ARRAY_FIELD_(REG, OBJECT, MEMBER, INDEX, DISP, VALUE) \
do { \
if (!(OBJECT)) { \
asm("mov\t%1,%c3(%2)" \
: "=m"(OBJECT->MEMBER) \
: REG((__typeof(*(OBJECT->MEMBER)))(VALUE)), \
"bDS"((long)(INDEX) * sizeof(*(OBJECT->MEMBER))), \
"i"(__builtin_offsetof(__typeof(*(OBJECT)), MEMBER) + \
sizeof(*(OBJECT->MEMBER)) * (DISP))); \
} else { \
asm("mov\t%1,%c4(%2,%3)" \
: "=m"(OBJECT->MEMBER) \
: REG((__typeof(*(OBJECT->MEMBER)))(VALUE)), "b"(OBJECT), \
"DS"((long)(INDEX) * sizeof(*(OBJECT->MEMBER))), \
"i"(__builtin_offsetof(__typeof(*(OBJECT)), MEMBER) + \
sizeof(*(OBJECT->MEMBER)) * (DISP))); \
} \
} while (0)
static void *SetMemory(void *di, int al, unsigned long cx) {
/* #ifdef __REAL_MODE__ */
#define REAL_WRITE_ARRAY_FIELD(OBJECT, MEMBER, INDEX, DISP, VALUE) \
do { \
__typeof(*(OBJECT->MEMBER)) Reg; \
switch (sizeof(*(OBJECT->MEMBER))) { \
case 1: \
REAL_WRITE_ARRAY_FIELD_("Q", OBJECT, MEMBER, INDEX, DISP, VALUE); \
break; \
default: \
REAL_WRITE_ARRAY_FIELD_("ri", OBJECT, MEMBER, INDEX, DISP, VALUE); \
break; \
} \
} while (0)
/* #else */
/* #define REAL_WRITE_ARRAY_FIELD(o, m, i, d, v) o->m[i + d] = v */
/* #endif */
long jb[8];
int setjmp(void *) __attribute__((__returns_twice__));
int longjmp(void *, int) __attribute__((__noreturn__));
static inline void *SetMemory(void *di, int al, unsigned long cx) {
asm("rep stosb"
: "=D"(di), "=c"(cx), "=m"(*(char(*)[cx])di)
: "0"(di), "1"(cx), "a"(al));
return di;
}
static void *CopyMemory(void *di, void *si, unsigned long cx) {
static inline void *CopyMemory(void *di, void *si, unsigned long cx) {
asm("rep movsb"
: "=D"(di), "=S"(si), "=c"(cx), "=m"(*(char(*)[cx])di)
: "0"(di), "1"(si), "2"(cx));
@ -147,14 +184,13 @@ static void RawMode(void) {
#endif
}
__attribute__((__noinline__)) static int PrintChar(LONG c) {
__attribute__((__noinline__)) static void PrintChar(LONG c) {
#ifdef __REAL_MODE__
asm volatile("mov\t$0x0E,%%ah\n\t"
"int\t$0x10"
: /* no outputs */
: "a"(c), "b"(7)
: "memory");
return 0;
#else
static short buf;
int rc;
@ -163,7 +199,6 @@ __attribute__((__noinline__)) static int PrintChar(LONG c) {
: "=a"(rc)
: "0"(1), "D"(1), "S"(&buf), "d"(1)
: "rcx", "r11", "memory");
return rc;
#endif
}
@ -177,6 +212,7 @@ static void PrintString(char *s) {
}
static int XlatChar(LONG c) {
if (c == 0x7F) return '\b';
if (c >= 'a') {
asm volatile("" ::: "memory");
if (c <= 'z') c -= 'a' - 'A';
@ -185,19 +221,16 @@ static int XlatChar(LONG c) {
}
static int EchoChar(LONG c) {
if (c == '\b' || c == 0x7F) {
PrintString("\b \b");
return '\b';
} else {
if (c != '\b') {
PrintChar(c);
if (c == '\r') {
PrintChar('\n');
}
return c;
}
return c;
}
static noinline int ReadChar(void) {
__attribute__((__noinline__)) static noinline int ReadChar(void) {
int c;
#ifdef __REAL_MODE__
asm volatile("int\t$0x16" : "=a"(c) : "0"(0) : "memory");
@ -213,13 +246,13 @@ static noinline int ReadChar(void) {
}
/*───────────────────────────────────────────────────────────────────────────│─╗
The LISP Challenge § LISP Machine
The LISP Challenge § Pure Original LISP Machine
*/
#define TYPE_ATOM 0
#define TYPE_CONS 1
#define ATOM 0
#define CONS 1
#define ATOM_NIL 0
#define NIL 0
#define ATOM_T 8
#define ATOM_QUOTE 12
#define ATOM_ATOM 24
@ -234,7 +267,7 @@ static noinline int ReadChar(void) {
#define ATOM_DEFUN 110
#define Quote(x) List(ATOM_QUOTE, x)
#define List(x, y) Cons(x, Cons(y, ATOM_NIL))
#define List(x, y) Cons(x, Cons(y, NIL))
#define Caar(x) Car(Car(x)) // ((A B C D) (E F G) H I) → A
#define Cdar(x) Cdr(Car(x)) // ((A B C D) (E F G) H I) → (B C D)
#define Cadar(x) Cadr(Car(x)) // ((A B C D) (E F G) H I) → B
@ -242,22 +275,40 @@ static noinline int ReadChar(void) {
#define Cadr(x) Car(Cdr(x)) // ((A B C D) (E F G) H I) → (E F G)
#define Caddr(x) Cadr(Cdr(x)) // ((A B C D) (E F G) H I) → H
#define BOOL(x) ((x) ? ATOM_T : ATOM_NIL)
#define BOOL(x) ((x) ? ATOM_T : NIL)
#define VALUE(x) ((x) >> 1)
#define PTR(i) ((i) << 1 | CONS)
#define ARRAYLEN(A) \
((sizeof(A) / sizeof(*(A))) / ((unsigned)!(sizeof(A) % sizeof(*(A)))))
struct Lisp {
WORD memory[WORDS];
WORD mem[WORDS];
unsigned char syntax[256];
unsigned char look;
char token[16];
WORD look;
WORD globals;
int index;
WORD index;
char token[128];
char str[WORDS];
};
const char kSymbols[] aligned(1) = "\
_Static_assert(sizeof(struct Lisp) <= 0x7c00 - 0x600,
"LISP Machine too large for real mode");
_Alignas(char) const char kSymbols[] = "\
NIL\0T\0QUOTE\0ATOM\0EQ\0COND\0CAR\0CDR\0CONS\0LABEL\0LAMBDA\0SET\0DEFUN\0";
_Alignas(WORD) const WORD kGlobals[] = {
[0] = PTR(2), // ((T . T) (NIL . NIL))
[1] = PTR(4), //
[2] = ATOM_T, // (T . T)
[3] = ATOM_T, //
[4] = PTR(6), // ((NIL . NIL))
[5] = NIL, //
[6] = NIL, // (NIL . NIL)
[7] = NIL, //
};
#ifdef __REAL_MODE__
static struct Lisp *const q;
#else
@ -281,28 +332,39 @@ static void SetupSyntax(void) {
q->syntax['\''] = '\'';
}
forceinline WORD Car(LONG x) {
return REAL_READ_ARRAY_FIELD(q, memory, VALUE(x), 0);
static inline WORD Car(LONG x) {
return REAL_READ_ARRAY_FIELD(q, mem, VALUE(x), 0);
}
forceinline WORD Cdr(LONG x) {
return REAL_READ_ARRAY_FIELD(q, memory, VALUE(x), 1);
static inline WORD Cdr(LONG x) {
return REAL_READ_ARRAY_FIELD(q, mem, VALUE(x), 1);
}
static WORD Cons(WORD car, WORD cdr) {
int i, c;
#if TRACE
PrintString("CONS->");
Print(car);
PrintString(" ");
Print(cdr);
#endif
int i, cell;
i = q->index;
REAL_WRITE_ARRAY_FIELD(q, memory, i, 0, car);
REAL_WRITE_ARRAY_FIELD(q, memory, i, 1, cdr);
q->index += 2;
c = OBJECT(TYPE_CONS, i);
return c;
REAL_WRITE_ARRAY_FIELD(q, mem, i, 0, car);
REAL_WRITE_ARRAY_FIELD(q, mem, i, 1, cdr);
q->index = i + 2;
cell = OBJECT(CONS, i);
#if TRACE
PrintString("CONS<-");
Print(cell);
#endif
return cell;
}
static void SetupBuiltins(void) {
CopyMemory(q->str, kSymbols, sizeof(kSymbols));
q->globals =
Cons(Cons(ATOM_NIL, ATOM_NIL), Cons(Cons(ATOM_T, ATOM_T), ATOM_NIL));
CopyMemory(q->mem, kGlobals, sizeof(kGlobals));
q->index = ARRAYLEN(kGlobals);
q->globals = PTR(0);
}
static char *StpCpy(char *d, char *s) {
@ -314,7 +376,7 @@ static char *StpCpy(char *d, char *s) {
return d;
}
static WORD Intern(char *s) {
WORD Intern(char *s) {
int j, cx;
char c, *z, *t;
z = q->str;
@ -325,7 +387,7 @@ static WORD Intern(char *s) {
break;
}
if (!c) {
return OBJECT(TYPE_ATOM, z - q->str - j - 1);
return OBJECT(ATOM, z - q->str - j - 1);
}
c = LODS(z);
}
@ -334,11 +396,11 @@ static WORD Intern(char *s) {
}
--z;
StpCpy(z, s);
return OBJECT(TYPE_ATOM, SUB((long)z, q->str));
return OBJECT(ATOM, SUB((long)z, q->str));
}
forceinline unsigned char XlatSyntax(unsigned char b) {
return REAL_READ_ARRAY_FIELD(q, syntax, b, 0); /* a.k.a. q->syntax[b] */
return REAL_READ_ARRAY_FIELD(q, syntax, b, 0);
}
static void GetToken(void) {
@ -357,7 +419,8 @@ static void GetToken(void) {
if (b != '\b') {
STOS(t, b);
} else if (t > q->token) {
--t;
PrintString("\b \b");
if (t > q->token) --t;
}
b = ReadChar();
}
@ -387,7 +450,7 @@ static WORD GetList(void) {
case '\'':
return AddList(GetQuote());
case ')':
return ATOM_NIL;
return NIL;
case '.':
return ConsumeObject();
}
@ -422,7 +485,7 @@ static void PrintList(LONG x) {
PrintChar('(');
PrintObject(Car(x));
while ((x = Cdr(x))) {
if (!ATOM(x)) {
if (TYPE(x) == CONS) {
PrintChar(' ');
PrintObject(Car(x));
} else {
@ -434,7 +497,7 @@ static void PrintList(LONG x) {
}
static void PrintObject(LONG x) {
if (ATOM(x)) {
if (TYPE(x) == ATOM) {
PrintAtom(x);
} else {
PrintList(x);
@ -447,8 +510,7 @@ static void Print(LONG i) {
}
__attribute__((__noreturn__)) static void Reset(void) {
asm volatile("jmp\tRepl");
__builtin_unreachable();
longjmp(jb, 1);
}
__attribute__((__noreturn__)) static void OnUndefined(LONG x) {
@ -472,7 +534,7 @@ __attribute__((__noreturn__)) static void OnArity(void) {
*/
static WORD Atom(LONG x) {
return BOOL(ATOM(x));
return BOOL(TYPE(x) == ATOM);
}
static WORD Null(LONG x) {
@ -480,24 +542,32 @@ static WORD Null(LONG x) {
}
static WORD Eq(LONG x, LONG y) {
return BOOL(x == y); /* undefiled if !ATOM(x)||!ATOM(y) */
return BOOL(x == y); /* undefined if !Atom(x)||!Atom(y) */
}
static WORD Assoc(LONG x, LONG y) {
for (;;) {
if (!y) OnUndefined(x);
if (Eq(Caar(y), x)) break;
y = Cdr(y);
}
return Cdar(y);
if (Null(y)) OnUndefined(x);
if (Eq(Caar(y), x)) return Cdar(y);
return Assoc(x, Cdr(y));
}
static WORD Append(LONG x, LONG y) {
if (x) {
return Cons(Car(x), Append(Cdr(x), y));
#if TRACE
PrintString("APPEND->");
Print(x);
PrintString(" ");
Print(y);
#endif
if (!Null(x)) {
x = Cons(Car(x), Append(Cdr(x), y));
} else {
return y;
x = y;
}
#if TRACE
PrintString("APPEND<-");
Print(x);
#endif
return x;
}
/**
@ -506,26 +576,41 @@ static WORD Append(LONG x, LONG y) {
* @note recoded to make lists in dot notation
* @note it's zip() basically
*/
static WORD Pair(LONG x, LONG y) {
if (!x && !y) {
return ATOM_NIL;
} else if (!ATOM(x) && !ATOM(y)) {
return Cons(Cons(Car(x), Car(y)), Pair(Cdr(x), Cdr(y)));
static WORD Pair_(LONG x, LONG y) {
if (Null(x) && Null(y)) {
return NIL;
} else if (TYPE(x) == CONS && TYPE(y) == CONS) {
return Cons(Cons(Car(x), Car(y)), Pair_(Cdr(x), Cdr(y)));
} else {
OnArity();
}
}
static WORD Pair(LONG x, LONG y) {
#if TRACE
PrintString("PAIR->");
Print(x);
PrintString(" ");
Print(y);
#endif
x = Pair_(x, y);
#if TRACE
PrintString("PAIR<-");
Print(x);
#endif
return x;
}
static WORD Appq(long m) {
if (m) {
return Cons(List(ATOM_QUOTE, Car(m)), Appq(Cdr(m)));
} else {
return ATOM_NIL;
return NIL;
}
}
static WORD Apply(long f, long a) {
return Eval(Cons(f, Appq(a)), ATOM_NIL);
return Eval(Cons(f, Appq(a)), NIL);
}
static WORD Evcon(LONG c, LONG a) {
@ -536,14 +621,29 @@ static WORD Evcon(LONG c, LONG a) {
}
}
static WORD Evlis(LONG m, LONG a) {
static WORD Evlis_(LONG m, LONG a) {
if (m) {
return Cons(Eval(Car(m), a), Evlis(Cdr(m), a));
return Cons(Eval(Car(m), a), Evlis_(Cdr(m), a));
} else {
return ATOM_NIL;
return NIL;
}
}
static WORD Evlis(LONG m, LONG a) {
#if TRACE
PrintString("EVLIS->");
Print(m);
PrintString(" ");
Print(a);
#endif
m = Evlis_(m, a);
#if TRACE
PrintString("EVLIS<-");
Print(m);
#endif
return m;
}
static WORD Set(LONG e) {
WORD name, value;
name = Car(e);
@ -563,9 +663,9 @@ static WORD Defun(LONG e) {
}
static WORD Evaluate(LONG e, LONG a) {
if (ATOM(e)) {
if (Atom(e)) {
return Assoc(e, a);
} else if (ATOM(Car(e))) {
} else if (Atom(Car(e))) {
switch (Car(e)) {
case ATOM_QUOTE:
return Cadr(e);
@ -601,6 +701,8 @@ static WORD Eval(LONG e, LONG a) {
#if TRACE
PrintString("->");
Print(e);
PrintString(" ");
Print(a);
#endif
e = Evaluate(e, a);
#if TRACE
@ -615,6 +717,7 @@ static WORD Eval(LONG e, LONG a) {
*/
void Repl(void) {
setjmp(jb);
for (;;) {
PrintString("* ");
Print(Eval(Read(), q->globals));
@ -622,7 +725,7 @@ void Repl(void) {
}
int main(int argc, char *argv[]) {
RawMode();
/* RawMode(); */
SetupSyntax();
SetupBuiltins();
PrintString("THE LISP CHALLENGE V1\r\n"

View File

@ -29,6 +29,7 @@ SECTIONS {
. = 0x1fe;
SHORT(0xaa55);
*(.text .text.*)
_etext = .;
. = ALIGN(512);
}
@ -43,10 +44,11 @@ SECTIONS {
}
}
syntax = 0x600+2048*2;
look = 0x600+2048*2+256;
token = 0x600+2048*2+256+1;
globals = 0x600+2048*2+256+1+16;
index = 0x600+2048*2+256+1+16+2;
str = 0x600+2048*2+256+1+16+2+4;
v_sectors = SIZEOF(.text) / 512;
boot = 0x7c00;
q.syntax = 8192*2;
q.look = 8192*2+256;
q.globals = 8192*2+256+2;
q.index = 8192*2+256+2+2;
q.token = 8192*2+256+2+2+2;
q.str = 8192*2+256+2+2+2+128;
v_sectors = SIZEOF(.text) / 512;

View File

@ -21,8 +21,8 @@
.code16
.section .start,"ax",@progbits
_start: jmp 1f
1: ljmp $0x600>>4,$2f
2: push %cs
1: ljmp $0x600>>4,$_begin
_begin: push %cs
pop %ds
push %cs
pop %es
@ -50,3 +50,45 @@ _start: jmp 1f
.globl _start
.globl v_sectors
.globl main
setjmp: mov %sp,%ax
stosw # sp
xchg %ax,%si
movsw %ss:(%si),(%di) # ip
mov %bp,%ax
stosw # bp
ret
.type setjmp,@function
.size setjmp,.-setjmp
.globl setjmp
longjmp:
mov (%di),%sp
mov 2(%di),%dx
mov 4(%di),%bp
pop %ax
mov %si,%ax
jmp *%dx
.type longjmp,@function
.size longjmp,.-longjmp
.globl longjmp
.globl q.syntax
.type q.syntax,@function
.globl q.look
.type q.look,@function
.globl q.globals
.type q.globals,@function
.globl q.index
.type q.index,@function
.globl q.token
.type q.token,@function
.globl q.str
.type q.str,@function
.globl boot
.type boot,@function
.globl bss
.type bss,@function
.globl rodata
.type rodata,@function

View File

@ -56,6 +56,7 @@
#include "libc/sysv/consts/sig.h"
#include "libc/sysv/consts/termios.h"
#include "libc/sysv/errfuns.h"
#include "libc/time/time.h"
#include "libc/unicode/unicode.h"
#include "libc/x/x.h"
#include "third_party/dtoa/dtoa.h"
@ -91,8 +92,7 @@ DESCRIPTION\n\
\n\
FLAGS\n\
\n\
-h\n\
-? help\n\
-h help\n\
-v verbosity\n\
-r real mode\n\
-s statistics\n\
@ -137,8 +137,9 @@ COMPLETENESS\n\
#define QUIT 0x200
#define EXIT 0x400
#define CTRL(C) ((C) ^ 0100)
#define ALT(C) (('\e' << 010) | (C))
#define CTRL(C) ((C) ^ 0100)
#define ALT(C) (('\e' << 010) | (C))
#define SEX(x, b) ((x) | ((x) & (1ull << (b)) ? -(1ull << (b)) : 0))
struct Panels {
union {
@ -215,16 +216,6 @@ static struct Breakpoints breakpoints;
static void SetupDraw(void);
static void Redraw(void);
static uint64_t SignExtend(uint64_t x, uint8_t b) {
uint64_t s;
s = 1;
b -= 1;
b &= 63;
s <<= b;
if (x & s) x |= ~(s - 1);
return x;
}
static char *FormatDouble(char *b, double x) {
return g_fmt(b, x);
}
@ -234,16 +225,15 @@ static void SetCarry(bool cf) {
}
static bool IsCall(void) {
return m->xedd->op.map == XED_ILD_MAP0 &&
(m->xedd->op.opcode == 0xE8 ||
(m->xedd->op.opcode == 0xFF && m->xedd->op.reg == 2));
return (m->xedd->op.dispatch == 0x0E8 ||
(m->xedd->op.dispatch == 0x0FF && m->xedd->op.reg == 2));
}
static bool IsLongBranch(void) {
return m->xedd && m->xedd->op.map == XED_ILD_MAP0 &&
(m->xedd->op.opcode == 0xEA || m->xedd->op.opcode == 0x9A ||
(m->xedd->op.opcode == 0xFF && m->xedd->op.reg == 3) ||
(m->xedd->op.opcode == 0xFF && m->xedd->op.reg == 5));
return m->mode != XED_MODE_LONG &&
(m->xedd->op.dispatch == 0x0EA || m->xedd->op.dispatch == 0x09A ||
(m->xedd->op.opcode == 0x0FF && m->xedd->op.reg == 3) ||
(m->xedd->op.opcode == 0x0FF && m->xedd->op.reg == 5));
}
static bool IsDebugBreak(void) {
@ -370,13 +360,16 @@ static void GetTtySize(void) {
static void TuiRejuvinate(void) {
GetTtySize();
ttyhidecursor(STDOUT_FILENO);
ttyraw(0);
ttyraw(kTtySigs);
xsigaction(SIGBUS, OnBusted, SA_NODEFER, 0, NULL);
}
static void OnCtrlC(void) {
LOGF("OnCtrlC");
action |= INT;
if (tuimode) {
action |= INT;
} else {
HaltMachine(m, kMachineExit);
}
}
static void OnQ(void) {
@ -404,7 +397,7 @@ static void OnCont(void) {
static void TuiCleanup(void) {
sigaction(SIGWINCH, oldsig + 0, NULL);
sigaction(SIGCONT, oldsig + 2, NULL);
ttyraw((enum TtyRawFlags)(-1u));
ttyraw(-1);
ttyshowcursor(STDOUT_FILENO);
CHECK_NE(-1, close(ttyfd));
tuimode = false;
@ -447,6 +440,7 @@ void TuiSetup(void) {
CHECK_NE(-1, (ttyfd = open("/dev/tty", O_RDWR)));
xsigaction(SIGWINCH, OnWinch, 0, 0, oldsig + 0);
xsigaction(SIGCONT, OnCont, SA_RESTART, 0, oldsig + 2);
xsigaction(SIGINT, OnCtrlC, 0 /* SA_NODEFER */, 0, oldsig + 3);
memcpy(&m[1], &m[0], sizeof(m[0]));
TuiRejuvinate();
}
@ -660,12 +654,19 @@ static void SetupDraw(void) {
pan.display.right - pan.display.left);
}
static long Disassemble(void) {
long lines, current;
lines = pan.disassembly.bottom - pan.disassembly.top * 2;
CHECK_NE(-1, Dis(dis, m, GetIp(), m->ip, lines));
current = DisFind(dis, GetIp());
CHECK_NE(-1, current);
return current;
}
static long GetDisIndex(void) {
long i;
if ((i = DisFind(dis, GetIp())) == -1 || IsLongBranch()) {
Dis(dis, m, GetIp(), m->ip,
pan.disassembly.bottom - pan.disassembly.top * 2);
CHECK_NE(-1, (i = DisFind(dis, GetIp())));
if ((i = DisFind(dis, GetIp())) == -1) {
i = Disassemble();
}
while (i + 1 < dis->ops.i && !dis->ops.p[i].size) ++i;
return i;
@ -864,7 +865,7 @@ static void DrawXmm(struct Panel *p, long i, long r) {
if (0 && ssewidth == 1 && (040 <= ival && ival < 0200 - 1)) {
sprintf(buf, "%`'c", ival);
} else {
int64toarray_radix10(SignExtend(ival, ssewidth * 8), buf);
int64toarray_radix10(SEX(ival, ssewidth * 8), buf);
}
} else {
uint64toarray_fixed16(ival, buf, ssewidth * 8);
@ -977,7 +978,7 @@ static void DrawBreakpoints(struct Panel *p) {
name = sym != -1 ? dis->syms.stab + dis->syms.p[sym].name : "UNKNOWN";
s = buf;
s += sprintf(s, "%p ", addr);
CHECK_LT(Demangle(s, name), buf + ARRAYLEN(buf));
CHECK_LT(Demangle(s, name, DIS_MAX_SYMBOL_LENGTH), buf + ARRAYLEN(buf));
AppendPanel(p, line, buf);
if (sym != -1 && addr != dis->syms.p[sym].addr) {
snprintf(buf, sizeof(buf), "+%#lx", addr - dis->syms.p[sym].addr);
@ -1015,7 +1016,7 @@ static void DrawTrace(struct Panel *p) {
name = sym != -1 ? dis->syms.stab + dis->syms.p[sym].name : "UNKNOWN";
s = line;
s += sprintf(s, "%p %p ", Read64(m->ss) + bp, rp);
s = Demangle(s, name);
s = Demangle(s, name, DIS_MAX_SYMBOL_LENGTH);
AppendPanel(p, i, line);
if (sym != -1 && rp != dis->syms.p[sym].addr) {
snprintf(line, sizeof(line), "+%#lx", rp - dis->syms.p[sym].addr);
@ -1417,7 +1418,6 @@ static void OnVidyaServiceTeletypeOutput(void) {
char buf[12];
n = FormatCga(m->bx[0], buf);
n += tpencode(buf + n, 6, VidyaServiceXlatTeletype(m->ax[0]), false);
LOGF("teletype output %`'.*s", n, buf);
MachinePtyWrite(pty, buf, n);
}
@ -1520,6 +1520,7 @@ static void OnInt15h(void) {
}
static bool OnHalt(int interrupt) {
LOGF("OnHalt(%d)", interrupt);
ReactiveDraw();
switch (interrupt) {
case 1:
@ -1577,15 +1578,24 @@ static void OnPageDown(void) {
static void OnUpArrow(void) {
if (action & CONTINUE) {
speed = MIN(0x40000000, MAX(1, speed) << 1);
if (speed >= -1) {
speed = MIN(0x40000000, MAX(1, speed) << 1); // 1..40mips skip
} else {
speed >>= 1;
}
} else {
--opstart;
}
LOGF("speed %d", speed);
}
static void OnDownArrow(void) {
if (action & CONTINUE) {
speed >>= 1;
if (speed > 0) {
speed >>= 1;
} else {
speed = MAX(-(5 * 1000), MIN(-10, speed) << 1); // 10ms..5s delay
}
} else {
++opstart;
}
@ -1897,6 +1907,9 @@ static void Tui(void) {
action &= ~WINCHED;
}
interactive = ++tick > speed;
if (interactive && speed < 0) {
dsleep(.001L * -speed);
}
if (!(action & CONTINUE) || interactive) {
tick = 0;
GetDisIndex();
@ -1907,7 +1920,8 @@ static void Tui(void) {
LOGF("TUI FAILURE");
PrintMessageBox(ttyfd, systemfailure, tyn, txn);
ReadKeyboard();
} else if (!IsExecuting() || (interactive && HasPendingKeyboard())) {
} else if (!IsExecuting() || ((interactive || !(action & CONTINUE)) &&
!(action & INT) && HasPendingKeyboard())) {
ReadKeyboard();
}
if (action & INT) {
@ -1961,6 +1975,9 @@ static void Tui(void) {
}
if (!IsDebugBreak()) {
ExecuteInstruction(m);
if (IsLongBranch()) {
Disassemble();
}
} else {
m->ip += m->xedd->length;
action &= ~NEXT;
@ -1997,7 +2014,7 @@ static void Tui(void) {
static void GetOpts(int argc, char *argv[]) {
int opt;
stpcpy(stpcpy(stpcpy(logpath, kTmpPath), basename(argv[0])), ".log");
while ((opt = getopt(argc, argv, "?hvtrRsb:HL:")) != -1) {
while ((opt = getopt(argc, argv, "hvtrRsb:HL:")) != -1) {
switch (opt) {
case 't':
tuimode = true;
@ -2007,6 +2024,7 @@ static void GetOpts(int argc, char *argv[]) {
break;
case 'r':
m->mode = XED_MACHINE_MODE_REAL;
g_disisprog_disable = true;
break;
case 's':
printstats = true;
@ -2024,7 +2042,6 @@ static void GetOpts(int argc, char *argv[]) {
strcpy(logpath, optarg);
break;
case 'h':
case '?':
PrintUsage(EXIT_SUCCESS, stdout);
default:
PrintUsage(EX_USAGE, stderr);

View File

@ -64,13 +64,12 @@ ssize_t WriteBuffer(struct Buffer *b, int fd) {
p = b->p;
n = b->i;
do {
TryAgain:
if ((rc = write(fd, p, n)) != -1) {
wrote = rc;
p += wrote;
n -= wrote;
} else if (errno == EINTR) {
goto TryAgain;
break;
} else {
return -1;
}

View File

@ -17,10 +17,14 @@
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "libc/assert.h"
#include "libc/bits/safemacros.h"
#include "libc/calls/calls.h"
#include "libc/calls/hefty/spawn.h"
#include "libc/calls/struct/iovec.h"
#include "libc/macros.h"
#include "libc/runtime/runtime.h"
#include "libc/sock/sock.h"
#include "libc/str/str.h"
#include "tool/build/lib/demangle.h"
@ -37,12 +41,14 @@ void CloseCxxFilt(void) {
void SpawnCxxFilt(void) {
int pid;
const char *cxxfilt;
char path[PATH_MAX];
if (commandv("c++filt", path)) {
cxxfilt = firstnonnull(getenv("CXXFILT"), "c++filt");
if (commandv(cxxfilt, path)) {
g_cxxfilt.fds[0] = -1;
g_cxxfilt.fds[1] = -1;
g_cxxfilt.fds[2] = 2;
if ((pid = spawnve(0, g_cxxfilt.fds, path, (char *const[]){"c++filt", NULL},
if ((pid = spawnve(0, g_cxxfilt.fds, path, (char *const[]){cxxfilt, NULL},
environ)) != -1) {
atexit(CloseCxxFilt);
}
@ -52,30 +58,50 @@ void SpawnCxxFilt(void) {
g_cxxfilt.pid = pid;
}
char *DemangleCxxFilt(char *p, const char *symbol) {
int n;
char buf[512];
bool iscomplicated;
char *CopySymbol(char *p, size_t pn, const char *s, size_t sn) {
size_t extra;
bool showdots, iscomplicated;
assert(pn >= 1 + 3 + 1 + 1);
iscomplicated = memchr(s, ' ', sn) || memchr(s, '(', sn);
extra = 1;
if (iscomplicated) extra += 2;
if (sn + extra > pn) {
sn = pn - extra - 3;
showdots = true;
} else {
showdots = false;
}
if (iscomplicated) *p++ = '"';
p = mempcpy(p, s, sn);
if (showdots) p = stpcpy(p, "...");
if (iscomplicated) *p++ = '"';
*p = '\0';
return p;
}
char *DemangleCxxFilt(char *p, size_t pn, const char *s, size_t sn) {
ssize_t rc;
size_t got;
struct iovec iov[2];
static char buf[PAGESIZE];
if (!g_cxxfilt.pid) SpawnCxxFilt();
if (g_cxxfilt.pid == -1) return NULL;
if ((n = strlen(symbol)) >= ARRAYLEN(buf)) return NULL;
memcpy(buf, symbol, n);
buf[n] = '\n';
write(g_cxxfilt.fds[0], buf, n + 1);
n = read(g_cxxfilt.fds[1], buf, ARRAYLEN(buf));
if (n > 1 && buf[n - 1] == '\n') {
if (buf[n - 2] == '\r') --n;
--n;
iscomplicated = memchr(buf, ' ', n) || memchr(buf, '(', n);
if (iscomplicated) *p++ = '"';
p = mempcpy(p, buf, n);
if (iscomplicated) *p++ = '"';
*p = '\0';
return p;
} else {
CloseCxxFilt();
return NULL;
buf[0] = '\n';
iov[0].iov_base = s;
iov[0].iov_len = sn;
iov[1].iov_base = buf;
iov[1].iov_len = 1;
writev(g_cxxfilt.fds[0], iov, ARRAYLEN(iov));
if ((rc = read(g_cxxfilt.fds[1], buf, sizeof(buf))) != -1) {
got = rc;
if (got >= 2 && buf[got - 1] == '\n') {
if (buf[got - 2] == '\r') --got;
--got;
return CopySymbol(p, pn, buf, got);
}
}
CloseCxxFilt();
return NULL;
}
/**
@ -85,10 +111,12 @@ char *DemangleCxxFilt(char *p, const char *symbol) {
* x86_64 disassembler. That's just for the GNU encoding scheme. So
* what we'll do, is just offload this work to the c++filt program.
*/
char *Demangle(char *p, const char *symbol) {
char *Demangle(char *p, const char *symbol, size_t n) {
char *r;
size_t sn;
sn = strlen(symbol);
if (startswith(symbol, "_Z")) {
if ((r = DemangleCxxFilt(p, symbol))) return r;
if ((r = DemangleCxxFilt(p, n, symbol, sn))) return r;
}
return stpcpy(p, symbol);
return CopySymbol(p, n, symbol, sn);
}

View File

@ -3,7 +3,7 @@
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
char *Demangle(char *, const char *);
char *Demangle(char *, const char *, size_t);
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */

View File

@ -150,7 +150,7 @@ static char *DisLineData(struct Dis *d, char *p, const uint8_t *b, size_t n) {
static char *DisLabel(struct Dis *d, char *p, const char *name) {
p = DisColumn(DisAddr(d, p), p, ADDRLEN);
p = HighStart(p, g_high.label);
p = Demangle(p, name);
p = Demangle(p, name, DIS_MAX_SYMBOL_LENGTH);
p = HighEnd(p);
*p++ = ':';
*p = '\0';

View File

@ -6,6 +6,8 @@
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
#define DIS_MAX_SYMBOL_LENGTH 32
struct Dis {
struct DisOps {
size_t i, n;
@ -50,6 +52,8 @@ struct Dis {
char buf[512];
};
extern bool g_disisprog_disable;
long Dis(struct Dis *, struct Machine *, uint64_t, uint64_t, int);
long DisFind(struct Dis *, int64_t);
void DisFree(struct Dis *);

View File

@ -143,21 +143,30 @@ static char *DisInt(char *p, int64_t x) {
return p;
}
static char *DisSym(struct Dis *d, char *p, int64_t addr, int64_t ip) {
long sym;
static char *DisSymImpl(struct Dis *d, char *p, int64_t x, long sym) {
int64_t addend;
const char *name;
if ((sym = DisFindSym(d, ip)) != -1 && d->syms.p[sym].name) {
addend = ip - d->syms.p[sym].addr;
name = d->syms.stab + d->syms.p[sym].name;
p = Demangle(p, name);
if (addend) {
*p++ = '+';
p = DisInt(p, addend);
}
return p;
addend = x - d->syms.p[sym].addr;
name = d->syms.stab + d->syms.p[sym].name;
p = Demangle(p, name, DIS_MAX_SYMBOL_LENGTH);
if (addend) {
*p++ = '+';
p = DisInt(p, addend);
}
return p;
}
static char *DisSym(struct Dis *d, char *p, int64_t x1, int64_t x2,
bool isrelative) {
long sym;
if ((sym = DisFindSym(d, x2)) != -1 && d->syms.p[sym].name &&
(d->syms.p[sym].isabs ^ isrelative)) {
return DisSymImpl(d, p, x2, sym);
} else if ((sym = DisFindSym(d, x1)) != -1 && d->syms.p[sym].name &&
(d->syms.p[sym].isabs ^ isrelative)) {
return DisSymImpl(d, p, x1, sym);
} else {
return DisInt(p, addr);
return DisInt(p, x1);
}
}
@ -165,7 +174,7 @@ static char *DisSymLiteral(struct Dis *d, uint32_t rde, char *p, uint64_t addr,
uint64_t ip) {
*p++ = '$';
p = HighStart(p, g_high.literal);
p = DisSym(d, p, addr, ip);
p = DisSym(d, p, addr, addr, false);
p = HighEnd(p);
return p;
}
@ -196,27 +205,36 @@ static bool IsRealModrmAbsolute(uint32_t rde) {
return Eamode(rde) == XED_MODE_REAL && ModrmRm(rde) == 6 && !ModrmMod(rde);
}
static char *DisM(struct Dis *d, uint32_t rde, char *p) {
static char *DisDisp(struct Dis *d, uint32_t rde, char *p) {
bool rela;
int64_t disp;
const char *base, *index, *scale;
p = DisSego(d, rde, p);
base = index = scale = NULL;
if (ModrmMod(rde) == 0b01 || ModrmMod(rde) == 0b10 || IsRipRelative(rde) ||
IsRealModrmAbsolute(rde) ||
(ModrmMod(rde) == 0b00 && ModrmRm(rde) == 0b100 &&
SibBase(d->xedd) == 0b101)) {
(Eamode(rde) != XED_MODE_REAL && ModrmMod(rde) == 0b00 &&
ModrmRm(rde) == 0b100 && SibBase(d->xedd) == 0b101)) {
disp = d->xedd->op.disp;
if (IsRipRelative(rde)) {
if (Mode(rde) == XED_MODE_LONG) {
disp = RipRelative(d, disp);
rela = true;
} else {
disp = Unrelative(rde, disp);
rela = false;
}
} else if (IsRealModrmAbsolute(rde)) {
disp = Unrelative(rde, disp);
rela = false;
} else {
rela = true;
}
p = DisSym(d, p, disp, disp);
p = DisSym(d, p, disp, disp, rela);
}
return p;
}
static char *DisBis(struct Dis *d, uint32_t rde, char *p) {
const char *base, *index, *scale;
base = index = scale = NULL;
if (Eamode(rde) != XED_MODE_REAL) {
if (!SibExists(rde)) {
DCHECK(!d->xedd->op.has_sib);
@ -290,6 +308,13 @@ static char *DisM(struct Dis *d, uint32_t rde, char *p) {
return p;
}
static char *DisM(struct Dis *d, uint32_t rde, char *p) {
p = DisSego(d, rde, p);
p = DisDisp(d, rde, p);
p = DisBis(d, rde, p);
return p;
}
static char *DisRegMem(struct Dis *d, uint32_t rde, char *p,
char *f(struct Dis *, uint32_t, char *)) {
if (IsModrmRegister(rde)) {
@ -452,11 +477,11 @@ static char *DisJb(struct Dis *d, uint32_t rde, char *p) {
static char *DisJvds(struct Dis *d, uint32_t rde, char *p) {
return DisSym(d, p, RipRelative(d, d->xedd->op.disp),
RipRelative(d, d->xedd->op.disp) - Read64(d->m->cs));
RipRelative(d, d->xedd->op.disp) - Read64(d->m->cs), true);
}
static char *DisAbs(struct Dis *d, uint32_t rde, char *p) {
return DisSym(d, p, d->xedd->op.disp, d->xedd->op.disp);
return DisSym(d, p, d->xedd->op.disp, d->xedd->op.disp, false);
}
static char *DisSw(struct Dis *d, uint32_t rde, char *p) {
@ -477,12 +502,12 @@ static char *DisY(struct Dis *d, uint32_t rde, char *p) {
}
static char *DisX(struct Dis *d, uint32_t rde, char *p) {
DisSego(d, rde, p);
p = DisSego(d, rde, p);
return DisSpecialAddr(d, rde, p, 6); // ds:si
}
static char *DisBBb(struct Dis *d, uint32_t rde, char *p) {
DisSego(d, rde, p);
p = DisSego(d, rde, p);
return DisSpecialAddr(d, rde, p, 3); // ds:bx
}

View File

@ -22,9 +22,13 @@
#include "libc/elf/elf.h"
#include "libc/elf/struct/sym.h"
#include "libc/log/check.h"
#include "libc/log/log.h"
#include "libc/macros.h"
#include "libc/str/str.h"
#include "tool/build/lib/dis.h"
bool g_disisprog_disable;
static int DisSymCompare(const struct DisSym *a, const struct DisSym *b) {
if (a->addr != b->addr) {
if (a->addr < b->addr) return -1;
@ -72,6 +76,8 @@ static void DisLoadElfSyms(struct Dis *d, struct Elf *elf) {
if (!st[i].st_name) continue;
if (!(0 <= st[i].st_name && st[i].st_name < stablen)) continue;
if (ELF64_ST_TYPE(st[i].st_info) == STT_SECTION) continue;
if (ELF64_ST_TYPE(st[i].st_info) == STT_FILE) continue;
if (startswith(d->syms.stab + st[i].st_name, "v_")) continue;
isabs = st[i].st_shndx == SHN_ABS;
isweak = ELF64_ST_BIND(st[i].st_info) == STB_WEAK;
islocal = ELF64_ST_BIND(st[i].st_info) == STB_LOCAL;
@ -85,6 +91,7 @@ static void DisLoadElfSyms(struct Dis *d, struct Elf *elf) {
t.addr = st[i].st_value;
t.rank = -islocal + -isweak + -isabs + isprotected + isobject + isfunc;
t.iscode = DisIsText(d, st[i].st_value) ? !isobject : isfunc;
t.isabs = isabs;
APPEND(&d->syms.p, &d->syms.i, &d->syms.n, &t);
}
}
@ -93,6 +100,7 @@ static void DisLoadElfSyms(struct Dis *d, struct Elf *elf) {
bool DisIsProg(struct Dis *d, int64_t addr) {
long i;
if (g_disisprog_disable) return true;
for (i = 0; i < d->loads.i; ++i) {
if (addr >= d->loads.p[i].addr &&
addr < d->loads.p[i].addr + d->loads.p[i].size) {

View File

@ -20,6 +20,7 @@
#include "libc/log/check.h"
#include "libc/nexgen32e/tinystrcmp.h"
#include "libc/str/str.h"
#include "third_party/zlib/zlib.h"
#include "tool/build/lib/dis.h"
#include "tool/build/lib/high.h"
#include "tool/build/lib/modrm.h"
@ -163,7 +164,7 @@ static char *DisName(struct Dis *d, char *bp, const char *name,
} else if (wantsuffix || (ambiguous && !startswith(name, "f") &&
!startswith(name, "set"))) {
if (Osz(rde)) {
if (Mode(rde) != XED_MODE_REAL) {
if (ambiguous || Mode(rde) != XED_MODE_REAL) {
*p++ = 'w';
}
} else if (Rexw(rde)) {
@ -187,8 +188,8 @@ static char *DisName(struct Dis *d, char *bp, const char *name,
*/
char *DisInst(struct Dis *d, char *p, const char *spec) {
long i, n;
char sbuf[300];
char args[4][300];
char sbuf[64];
char args[4][256];
char *s, *name, *state;
bool hasarg, hasmodrm, hasregister, hasmemory;
CHECK_EQ(0, (int)d->xedd->op.error);

View File

@ -48,7 +48,7 @@ COSMOPOLITAN_C_START_
#define SibHasIndex(x) (SibIndex(x) != 4 || Rexx(x))
#define SibHasBase(x, r) (SibBase(x) != 5 || ModrmMod(r))
#define SibIsAbsolute(x, r) (!SibHasBase(x, r) && !SibHasIndex(x))
#define IsRipRelative(x) (ModrmRm(x) == 5 && !ModrmMod(x))
#define IsRipRelative(x) (Eamode(x) && ModrmRm(x) == 5 && !ModrmMod(x))
struct AddrSeg {
int64_t addr;

View File

@ -474,11 +474,8 @@ ssize_t MachinePtyWrite(struct MachinePty *pty, const void *data, size_t n) {
break;
case kMachinePtyUtf8:
if (ThomPikeCont(p[i])) {
pty->u8 <<= 6;
pty->u8 |= p[i] & 0b00111111;
if (--pty->n8) {
break;
}
pty->u8 = ThomPikeMerge(pty->u8, p[i]);
if (--pty->n8) break;
}
SetMachinePtyCell(pty, pty->u8);
pty->state = kMachinePtyAscii;

Some files were not shown because too many files have changed in this diff Show More