Improve redbean

- Improve serialization
- Add Benchmark() API to redbean
- Refactor UNIX API to be assert() friendly
- Make the redbean Lua REPL print data structures
- Fix recent regressions in linenoise reverse search
- Add -i flag so redbean can be a language interpreter
This commit is contained in:
Justine Tunney 2022-04-25 08:30:14 -07:00
parent 2046c0d2ae
commit 451e3f73d9
74 changed files with 1781 additions and 1024 deletions

View file

@ -119,6 +119,9 @@ int fsync(int);
int ftruncate(int, int64_t);
int getdents(unsigned, void *, unsigned, long *);
int getdomainname(char *, size_t);
int getegid(void) nosideeffect;
int geteuid(void) nosideeffect;
int getgid(void) nosideeffect;
int gethostname(char *, size_t);
int getloadavg(double *, int);
int getpgid(int);
@ -130,6 +133,7 @@ int getrlimit(int, struct rlimit *);
int getrusage(int, struct rusage *);
int getsid(int) nosideeffect;
int gettid(void);
int getuid(void) nosideeffect;
int kill(int, int);
int killpg(int, int);
int link(const char *, const char *) dontthrow;
@ -196,6 +200,7 @@ int sysinfo(struct sysinfo *);
int touch(const char *, uint32_t);
int truncate(const char *, uint64_t);
int ttyname_r(int, char *, size_t);
int umask(int);
int uname(struct utsname *);
int unlink(const char *);
int unlink_s(const char **);
@ -226,11 +231,6 @@ ssize_t splice(int, int64_t *, int, int64_t *, size_t, uint32_t);
ssize_t vmsplice(int, const struct iovec *, int64_t, uint32_t);
ssize_t write(int, const void *, size_t);
struct dirent *readdir(DIR *);
uint32_t getegid(void) nosideeffect;
uint32_t geteuid(void) nosideeffect;
uint32_t getgid(void) nosideeffect;
uint32_t getuid(void) nosideeffect;
uint32_t umask(uint32_t);
void rewinddir(DIR *);
void sync(void);

View file

@ -111,6 +111,7 @@ o/$(MODE)/libc/calls/execlp.o \
o/$(MODE)/libc/calls/execve-nt.o \
o/$(MODE)/libc/calls/execve-sysv.o \
o/$(MODE)/libc/calls/readlinkat-nt.o \
o/$(MODE)/libc/calls/describeopenflags.greg.o \
o/$(MODE)/libc/calls/mkntenvblock.o: \
OVERRIDE_CPPFLAGS += \
-DSTACK_FRAME_UNLIMITED

View file

@ -0,0 +1,38 @@
/*-*- 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 2022 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/fmt/itoa.h"
#include "libc/fmt/magnumstrs.internal.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/sol.h"
/**
* Describes clock_gettime() clock argument.
*/
char *DescribeClockName(int x) {
int i;
char *s;
_Alignas(char) static char buf[32];
if ((s = GetMagnumStr(kClockNames, x))) {
stpcpy(stpcpy(buf, "CLOCK_"), s);
return buf;
} else {
FormatInt32(buf, x);
return buf;
}
}

View file

@ -0,0 +1,41 @@
/*-*- 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 2022 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/fmt/itoa.h"
#include "libc/fmt/magnumstrs.internal.h"
#include "libc/intrin/describeflags.internal.h"
#include "libc/mem/alloca.h"
#include "libc/sysv/consts/sol.h"
/**
* Describes clock_gettime() clock argument.
*/
char *DescribeOpenFlags(int x) {
char *s;
int i, n;
struct DescribeFlags *d;
_Alignas(char) static char openflags[128];
// TODO(jart): unify DescribeFlags and MagnumStr data structures
for (n = 0; kOpenFlags[n].x != MAGNUM_TERMINATOR;) ++n;
d = alloca(n * sizeof(struct DescribeFlags));
for (i = 0; i < n; ++i) {
d[i].flag = MAGNUM_NUMBER(kOpenFlags, i);
d[i].name = MAGNUM_STRING(kOpenFlags, i);
}
return DescribeFlags(openflags, sizeof(openflags), d, n, "O_", x);
}

View file

@ -28,6 +28,7 @@
*/
int32_t sys_fstatat(int32_t dirfd, const char *path, struct stat *st,
int32_t flags) {
int rc;
void *p;
union metastat ms;
if (IsAsan() && !__asan_is_valid(path, 1)) return efault();

View file

@ -25,7 +25,7 @@
* Returns effective group ID of calling process.
* @return group id
*/
uint32_t getegid(void) {
int getegid(void) {
int rc;
if (!IsWindows()) {
rc = sys_getegid();

View file

@ -24,7 +24,7 @@
* Returns effective user ID of calling process.
* @return user id
*/
uint32_t geteuid(void) {
int geteuid(void) {
int rc;
if (!IsWindows()) {
rc = sys_geteuid();

View file

@ -51,7 +51,7 @@ static textwindows dontinline uint32_t GetUserNameHash(void) {
* @asyncsignalsafe
* @vforksafe
*/
uint32_t getuid(void) {
int getuid(void) {
int rc;
if (!IsWindows()) {
rc = sys_getuid();
@ -71,7 +71,7 @@ uint32_t getuid(void) {
* @asyncsignalsafe
* @vforksafe
*/
uint32_t getgid(void) {
int getgid(void) {
int rc;
if (!IsWindows()) {
rc = sys_getgid();

47
libc/calls/kclocknames.S Normal file
View file

@ -0,0 +1,47 @@
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi
Copyright 2021 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/fmt/magnumstrs.internal.h"
#include "libc/macros.internal.h"
.macro .e e s
.long \e - kClockNames
.long 1f - kClockNames
.rodata.str1.1
1: .string "\s"
.previous
.endm
.section .rodata
.align 4
.underrun
kClockNames:
.e CLOCK_REALTIME,"REALTIME"
.e CLOCK_MONOTONIC,"MONOTONIC"
.e CLOCK_MONOTONIC_RAW,"MONOTONIC_RAW"
.e CLOCK_REALTIME_COARSE,"REALTIME_COARSE"
.e CLOCK_MONOTONIC_COARSE,"MONOTONIC_COARSE"
.e CLOCK_PROCESS_CPUTIME_ID,"PROCESS_CPUTIME_ID"
.e CLOCK_TAI,"TAI"
.e CLOCK_PROF,"PROF"
.e CLOCK_BOOTTIME,"BOOTTIME"
.e CLOCK_REALTIME_ALARM,"REALTIME_ALARM"
.e CLOCK_BOOTTIME_ALARM,"BOOTTIME_ALARM"
.long MAGNUM_TERMINATOR
.endobj kClockNames,globl,hidden
.overrun

64
libc/calls/kopenflags.S Normal file
View file

@ -0,0 +1,64 @@
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi
Copyright 2021 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/fmt/magnumstrs.internal.h"
#include "libc/macros.internal.h"
.macro .e e s
.long \e - kOpenFlags
.long 1f - kOpenFlags
.rodata.str1.1
1: .string "\s"
.previous
.endm
.section .rodata
.align 4
.underrun
kOpenFlags:
.e O_RDWR,"RDWR" // order matters
.e O_RDONLY,"RDONLY" //
.e O_WRONLY,"WRONLY" //
.e O_ACCMODE,"ACCMODE" // mask of prev three
.e O_CREAT,"CREAT" //
.e O_EXCL,"EXCL" //
.e O_TRUNC,"TRUNC" //
.e O_CLOEXEC,"CLOEXEC" //
.e O_DIRECT,"DIRECT" // no-op on xnu/openbsd
.e O_APPEND,"APPEND" // weird on nt
.e O_TMPFILE,"TMPFILE" // linux, windows
.e O_NOFOLLOW,"NOFOLLOW" // unix
.e O_SYNC,"SYNC" // unix
.e O_ASYNC,"ASYNC" // unix
.e O_NOCTTY,"NOCTTY" // unix
.e O_NOATIME,"NOATIME" // linux
.e O_EXEC,"EXEC" // free/openbsd
.e O_SEARCH,"SEARCH" // free/netbsd
.e O_DSYNC,"DSYNC" // linux/xnu/open/netbsd
.e O_RSYNC,"RSYNC" // linux/open/netbsd
.e O_PATH,"PATH" // linux
.e O_VERIFY,"VERIFY" // freebsd
.e O_SHLOCK,"SHLOCK" // bsd
.e O_EXLOCK,"EXLOCK" // bsd
.e O_RANDOM,"RANDOM" // windows
.e O_SEQUENTIAL,"SEQUENTIAL" // windows
.e O_COMPRESSED,"COMPRESSED" // windows
.e O_INDEXED,"INDEXED" // windows
.long MAGNUM_TERMINATOR
.endobj kOpenFlags,globl,hidden
.overrun

View file

@ -21,6 +21,7 @@
#include "libc/calls/internal.h"
#include "libc/calls/strace.internal.h"
#include "libc/dce.h"
#include "libc/fmt/magnumstrs.internal.h"
#include "libc/intrin/asan.internal.h"
#include "libc/log/log.h"
#include "libc/str/str.h"
@ -74,7 +75,8 @@ int openat(int dirfd, const char *file, int flags, ...) {
} else {
rc = efault();
}
STRACE("openat(%s, %#s, %#x, %#o) → %d% m", __strace_dirfd(buf, dirfd), file,
flags, (flags & (O_CREAT | O_TMPFILE)) ? mode : 0, rc);
STRACE("openat(%s, %#s, %s, %#o) → %d% m", __strace_dirfd(buf, dirfd), file,
DescribeOpenFlags(flags), (flags & (O_CREAT | O_TMPFILE)) ? mode : 0,
rc);
return rc;
}

View file

@ -27,8 +27,8 @@
* @return previous mask
* @note always succeeds
*/
unsigned umask(unsigned newmask) {
unsigned oldmask;
int umask(int newmask) {
int oldmask;
if (!IsWindows()) {
oldmask = sys_umask(newmask);
} else {

View file

@ -16,6 +16,7 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/fmt/magnumstrs.internal.h"
#include "libc/macros.internal.h"
.macro .e e s
@ -115,6 +116,6 @@ kErrnoDocs:
.e ENOTRECOVERABLE,"State not recoverable"
.e ENONET,"Machine is not on the network"
.e ERESTART,"Interrupted system call should be restarted"
.long -123
.long MAGNUM_TERMINATOR
.endobj kErrnoDocs,globl,hidden
.overrun

View file

@ -16,6 +16,7 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/fmt/magnumstrs.internal.h"
#include "libc/macros.internal.h"
.macro .e e
@ -27,7 +28,7 @@
.endm
.section .rodata
.align 4
.align 4
.underrun
kErrnoNames:
.e EINVAL
@ -116,6 +117,6 @@ kErrnoNames:
.e ENONET
.e ERESTART
.e ENODATA
.long -123
.long MAGNUM_TERMINATOR
.endobj kErrnoNames,globl,hidden
.overrun

View file

@ -1,22 +1,35 @@
#ifndef COSMOPOLITAN_LIBC_FMT_MAGNUMSTRS_H_
#define COSMOPOLITAN_LIBC_FMT_MAGNUMSTRS_H_
#define MAGNUM_TERMINATOR -123
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
#define MAGNUM_NUMBER(TABLE, INDEX) \
*(const int *)((uintptr_t)TABLE + TABLE[INDEX].x)
#define MAGNUM_STRING(TABLE, INDEX) \
(const char *)((uintptr_t)TABLE + TABLE[INDEX].s)
struct MagnumStr {
int x, s;
};
extern const struct MagnumStr kErrnoDocs[];
extern const struct MagnumStr kErrnoNames[];
extern const struct MagnumStr kIpOptnames[];
extern const struct MagnumStr kSignalNames[];
extern const struct MagnumStr kSockOptnames[];
extern const struct MagnumStr kTcpOptnames[];
hidden extern const struct MagnumStr kClockNames[];
hidden extern const struct MagnumStr kErrnoDocs[];
hidden extern const struct MagnumStr kErrnoNames[];
hidden extern const struct MagnumStr kIpOptnames[];
hidden extern const struct MagnumStr kOpenFlags[];
hidden extern const struct MagnumStr kSignalNames[];
hidden extern const struct MagnumStr kSockOptnames[];
hidden extern const struct MagnumStr kTcpOptnames[];
const char *DescribeSockLevel(int);
const char *DescribeSockOptname(int, int);
const char *GetMagnumStr(const struct MagnumStr *, int);
char *DescribeClockName(int) hidden;
char *DescribeOpenFlags(int) hidden;
char *DescribeSockLevel(int) hidden;
char *DescribeSockOptname(int, int) hidden;
char *GetMagnumStr(const struct MagnumStr *, int) hidden;
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */

View file

@ -23,7 +23,7 @@
* Converts errno value to descriptive sentence.
* @return non-null rodata string or null if not found
*/
const char *strerdoc(int x) {
char *strerdoc(int x) {
if (x) {
return GetMagnumStr(kErrnoDocs, x);
} else {

View file

@ -23,7 +23,7 @@
* Converts errno value to symbolic name.
* @return non-null rodata string or null if not found
*/
const char *strerrno(int x) {
char *strerrno(int x) {
if (x) {
return GetMagnumStr(kErrnoNames, x);
} else {

View file

@ -23,7 +23,7 @@
#include "libc/sysv/consts/prot.h"
const char *DescribeMapFlags(int x) {
static char mapflags[256];
_Alignas(char) static char mapflags[256];
const struct DescribeFlags kMapFlags[] = {
{MAP_ANONYMOUS, "ANONYMOUS"}, //
{MAP_PRIVATE, "PRIVATE"}, //

View file

@ -34,7 +34,7 @@ static const struct DescribeFlags kConsoleModeInputFlags[] = {
};
const char *DescribeNtConsoleModeInputFlags(uint32_t x) {
static char consolemodeinputflags[256];
_Alignas(char) static char consolemodeinputflags[256];
return DescribeFlags(consolemodeinputflags, sizeof(consolemodeinputflags),
kConsoleModeInputFlags, ARRAYLEN(kConsoleModeInputFlags),
"kNtEnable", x);

View file

@ -29,7 +29,7 @@ static const struct DescribeFlags kConsoleModeOutputFlags[] = {
};
const char *DescribeNtConsoleModeOutputFlags(uint32_t x) {
static char consolemodeoutputflags[128];
_Alignas(char) static char consolemodeoutputflags[128];
return DescribeFlags(consolemodeoutputflags, sizeof(consolemodeoutputflags),
kConsoleModeOutputFlags,
ARRAYLEN(kConsoleModeOutputFlags), "kNt", x);

View file

@ -64,7 +64,7 @@ static const struct DescribeFlags kFileAccessflags[] = {
};
const char *DescribeNtFileAccessFlags(uint32_t x) {
static char ntfileaccessflags[512];
_Alignas(char) static char ntfileaccessflags[512];
return DescribeFlags(ntfileaccessflags, sizeof(ntfileaccessflags),
kFileAccessflags, ARRAYLEN(kFileAccessflags), "kNt", x);
}

View file

@ -52,7 +52,7 @@ static const struct DescribeFlags kFileFlags[] = {
};
const char *DescribeNtFileFlagsAndAttributes(uint32_t x) {
static char ntfileflags[256];
_Alignas(char) static char ntfileflags[256];
if (x == -1u) return "-1u";
return DescribeFlags(ntfileflags, sizeof(ntfileflags), kFileFlags,
ARRAYLEN(kFileFlags), "kNtFile", x);

View file

@ -31,7 +31,7 @@ static const struct DescribeFlags kFileMapFlags[] = {
};
const char *DescribeNtFileMapFlags(uint32_t x) {
static char filemapflags[64];
_Alignas(char) static char filemapflags[64];
return DescribeFlags(filemapflags, sizeof(filemapflags), kFileMapFlags,
ARRAYLEN(kFileMapFlags), "kNtFileMap", x);
}

View file

@ -27,7 +27,7 @@ static const struct DescribeFlags kFileShareflags[] = {
};
const char *DescribeNtFileShareFlags(uint32_t x) {
static char ntfileshareflags[64];
_Alignas(char) static char ntfileshareflags[64];
return DescribeFlags(ntfileshareflags, sizeof(ntfileshareflags),
kFileShareflags, ARRAYLEN(kFileShareflags),
"kNtFileShare", x);

View file

@ -29,7 +29,7 @@ static const struct DescribeFlags kFiletypeFlags[] = {
};
const char *DescribeNtFiletypeFlags(uint32_t x) {
static char filetypeflags[64];
_Alignas(char) static char filetypeflags[64];
return DescribeFlags(filetypeflags, sizeof(filetypeflags), kFiletypeFlags,
ARRAYLEN(kFiletypeFlags), "kNtFileType", x);
}

View file

@ -30,7 +30,7 @@ static const struct DescribeFlags kMoveFileInputFlags[] = {
};
const char *DescribeNtMoveFileInputFlags(uint32_t x) {
static char movefileflags[256];
_Alignas(char) static char movefileflags[256];
return DescribeFlags(movefileflags, sizeof(movefileflags),
kMoveFileInputFlags, ARRAYLEN(kMoveFileInputFlags),
"kNtMovefile", x);

View file

@ -42,7 +42,7 @@ static const struct DescribeFlags kPageFlags[] = {
};
const char *DescribeNtPageFlags(uint32_t x) {
static char pageflags[64];
_Alignas(char) static char pageflags[64];
return DescribeFlags(pageflags, sizeof(pageflags), kPageFlags,
ARRAYLEN(kPageFlags), "kNt", x);
}

View file

@ -33,7 +33,7 @@ static const struct DescribeFlags kPipeModeFlags[] = {
};
const char *DescribeNtPipeModeFlags(uint32_t x) {
static char pipemodeflags[64];
_Alignas(char) static char pipemodeflags[64];
return DescribeFlags(pipemodeflags, sizeof(pipemodeflags), kPipeModeFlags,
ARRAYLEN(kPipeModeFlags), "kNtPipe", x);
}

View file

@ -28,7 +28,7 @@ static const struct DescribeFlags kPipeOpenFlags[] = {
};
const char *DescribeNtPipeOpenFlags(uint32_t x) {
static char pipeopenflags[64];
_Alignas(char) static char pipeopenflags[64];
return DescribeFlags(pipeopenflags, sizeof(pipeopenflags), kPipeOpenFlags,
ARRAYLEN(kPipeOpenFlags), "kNtPipeAccess", x);
}

View file

@ -38,7 +38,7 @@ static const struct DescribeFlags kProcessAccessflags[] = {
};
const char *DescribeNtProcessAccessFlags(uint32_t x) {
static char ntprocessaccessflags[256];
_Alignas(char) static char ntprocessaccessflags[256];
return DescribeFlags(ntprocessaccessflags, sizeof(ntprocessaccessflags),
kProcessAccessflags, ARRAYLEN(kProcessAccessflags),
"kNtProcess", x);

View file

@ -39,7 +39,7 @@ static const struct DescribeFlags kNtStartFlags[] = {
};
const char *DescribeNtStartFlags(uint32_t x) {
static char startflags[128];
_Alignas(char) static char startflags[128];
return DescribeFlags(startflags, sizeof(startflags), kNtStartFlags,
ARRAYLEN(kNtStartFlags), "kNtStartf", x);
}

View file

@ -26,7 +26,7 @@ static const struct DescribeFlags kSymbolicLinkflags[] = {
};
const char *DescribeNtSymbolicLinkFlags(uint32_t x) {
static char ntsymboliclinkflags[64];
_Alignas(char) static char ntsymboliclinkflags[64];
return DescribeFlags(ntsymboliclinkflags, sizeof(ntsymboliclinkflags),
kSymbolicLinkflags, ARRAYLEN(kSymbolicLinkflags),
"kNtSymbolicLinkFlag", x);

View file

@ -27,7 +27,7 @@ static const struct DescribeFlags kProtFlags[] = {
};
const char *DescribeProtFlags(int x) {
static char protflags[64];
_Alignas(char) static char protflags[64];
return DescribeFlags(protflags, sizeof(protflags), kProtFlags,
ARRAYLEN(kProtFlags), "PROT_", x);
}

View file

@ -26,7 +26,7 @@ static const struct DescribeFlags kRemapFlags[] = {
};
const char *DescribeRemapFlags(int x) {
static char remapflags[64];
_Alignas(char) static char remapflags[64];
return DescribeFlags(remapflags, sizeof(remapflags), kRemapFlags,
ARRAYLEN(kRemapFlags), "MREMAP_", x);
}

View file

@ -18,11 +18,11 @@
*/
#include "libc/fmt/magnumstrs.internal.h"
const char *GetMagnumStr(const struct MagnumStr *ms, int x) {
char *GetMagnumStr(const struct MagnumStr *ms, int x) {
int i;
for (i = 0; ms[i].x != -123; ++i) {
if (x == *(const int *)((uintptr_t)ms + ms[i].x)) {
return (const char *)((uintptr_t)ms + ms[i].s);
for (i = 0; ms[i].x != MAGNUM_TERMINATOR; ++i) {
if (x == MAGNUM_NUMBER(ms, i)) {
return MAGNUM_STRING(ms, i);
}
}
return 0;

View file

@ -28,6 +28,8 @@
STATIC_YOINK("__get_symbol_by_addr");
#define MAXLEAKS 1000
static bool once;
static bool hasleaks;
@ -46,15 +48,18 @@ static noasan void CheckLeak(void *x, void *y, size_t n, void *a) {
static noasan void OnMemory(void *x, void *y, size_t n, void *a) {
static int i;
if (n) {
if (++i < 20) {
kprintf("%p %,lu bytes [dlmalloc]", x, n);
if (IsAsan()) {
__asan_print_trace(x);
if (MAXLEAKS) {
if (i < MAXLEAKS) {
++i;
kprintf("%p %,lu bytes [dlmalloc]", x, n);
if (IsAsan()) {
__asan_print_trace(x);
}
kprintf("\n");
} else if (i == MAXLEAKS) {
++i;
kprintf("etc. etc.\n");
}
kprintf("\n");
}
if (i == 20) {
kprintf("etc. etc.\n");
}
}
}

13
libc/nt/struct/linger.h Normal file
View file

@ -0,0 +1,13 @@
#ifndef COSMOPOLITAN_LIBC_NT_STRUCT_LINGER_H_
#define COSMOPOLITAN_LIBC_NT_STRUCT_LINGER_H_
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
struct linger_nt {
uint16_t l_onoff; /* on/off */
uint16_t l_linger; /* seconds */
};
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_NT_STRUCT_LINGER_H_ */

View file

@ -22,7 +22,7 @@
/**
* Describes setsockopt() level arguments.
*/
const char *DescribeSockLevel(int x) {
char *DescribeSockLevel(int x) {
static char buf[12];
if (x == SOL_IP) return "SOL_IP";
if (x == SOL_TCP) return "SOL_TCP";

View file

@ -18,26 +18,32 @@
*/
#include "libc/fmt/itoa.h"
#include "libc/fmt/magnumstrs.internal.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/sol.h"
/**
* Describes setsockopt() optname arguments.
*/
const char *DescribeSockOptname(int l, int x) {
char *DescribeSockOptname(int l, int x) {
int i;
static char buf[12], *s;
char *ps, *s;
const struct MagnumStr *ms = 0;
_Alignas(char) static char buf[32];
if (x) {
if (l == SOL_SOCKET) {
ps = "SO_";
ms = kSockOptnames;
} else if (l == SOL_TCP) {
ps = "TCP_";
ms = kTcpOptnames;
} else if (l == SOL_IP) {
ps = "IP_";
ms = kIpOptnames;
}
}
if (ms && (s = GetMagnumStr(ms, x))) {
return s;
stpcpy(stpcpy(buf, ps), s);
return buf;
} else {
FormatInt32(buf, x);
return buf;

View file

@ -20,8 +20,10 @@
#include "libc/bits/bits.h"
#include "libc/calls/internal.h"
#include "libc/calls/struct/timeval.h"
#include "libc/nt/struct/linger.h"
#include "libc/nt/winsock.h"
#include "libc/sock/internal.h"
#include "libc/sock/sock.h"
#include "libc/sock/yoink.inc"
#include "libc/str/str.h"
#include "libc/sysv/consts/so.h"
@ -33,6 +35,7 @@ textwindows int sys_getsockopt_nt(struct Fd *fd, int level, int optname,
uint32_t *inout_optlen) {
uint64_t ms;
uint32_t in_optlen;
struct linger_nt linger;
assert(fd->kind == kFdSocket);
if (out_opt_optval && inout_optlen) {
@ -55,6 +58,11 @@ textwindows int sys_getsockopt_nt(struct Fd *fd, int level, int optname,
((struct timeval *)out_opt_optval)->tv_sec = ms / 1000;
((struct timeval *)out_opt_optval)->tv_usec = ms % 1000 * 1000;
*inout_optlen = sizeof(struct timeval);
} else if (optname == SO_LINGER && in_optlen == sizeof(struct linger)) {
linger = *(struct linger_nt *)out_opt_optval;
((struct linger *)out_opt_optval)->l_onoff = !!linger.l_onoff;
((struct linger *)out_opt_optval)->l_linger = linger.l_linger;
*inout_optlen = sizeof(struct linger);
}
}

View file

@ -16,24 +16,25 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/fmt/magnumstrs.internal.h"
#include "libc/macros.internal.h"
.macro .e e
.macro .e e s
.long \e - kIpOptnames
.long 1f - kIpOptnames
.rodata.str1.1
1: .string "\e"
1: .string "\s"
.previous
.endm
.section .rodata
.align 4
.align 4
.underrun
kIpOptnames:
.e IP_TOS # int
.e IP_MTU # int
.e IP_TTL # int
.e IP_HDRINCL # bool32
.long -123
.e IP_TOS,"TOS" # int
.e IP_MTU,"MTU" # int
.e IP_TTL,"TTL" # int
.e IP_HDRINCL,"HDRINCL" # bool32
.long MAGNUM_TERMINATOR
.endobj kIpOptnames,globl,hidden
.overrun

View file

@ -16,13 +16,14 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/fmt/magnumstrs.internal.h"
#include "libc/macros.internal.h"
.macro .e e
.macro .e e s
.long \e - kSockOptnames
.long 1f - kSockOptnames
.rodata.str1.1
1: .string "\e"
1: .string "\s"
.previous
.endm
@ -30,20 +31,22 @@
.align 4
.underrun
kSockOptnames:
.e SO_DEBUG # bool32
.e SO_BROADCAST # bool32
.e SO_REUSEADDR # bool32
.e SO_REUSEPORT # bool32
.e SO_KEEPALIVE # bool32
.e SO_DONTROUTE # bool32
.e SO_RCVTIMEO # timeval
.e SO_SNDTIMEO # timeval
.e SO_LINGER # linger
.e SO_SNDBUF # int
.e SO_RCVBUF # int
.e SO_RCVLOWAT # int
.e SO_SNDLOWAT # int
.e SO_ERROR # int
.long -123
.e SO_DEBUG,"DEBUG" # bool32
.e SO_ACCEPTCONN,"ACCEPTCONN" # bool32
.e SO_BROADCAST,"BROADCAST" # bool32
.e SO_REUSEADDR,"REUSEADDR" # bool32
.e SO_REUSEPORT,"REUSEPORT" # bool32
.e SO_KEEPALIVE,"KEEPALIVE" # bool32
.e SO_DONTROUTE,"DONTROUTE" # bool32
.e SO_RCVTIMEO,"RCVTIMEO" # timeval
.e SO_SNDTIMEO,"SNDTIMEO" # timeval
.e SO_LINGER,"LINGER" # linger
.e SO_TYPE,"TYPE" # int
.e SO_SNDBUF,"SNDBUF" # int
.e SO_RCVBUF,"RCVBUF" # int
.e SO_RCVLOWAT,"RCVLOWAT" # int
.e SO_SNDLOWAT,"SNDLOWAT" # int
.e SO_ERROR,"ERROR" # int
.long MAGNUM_TERMINATOR
.endobj kSockOptnames,globl,hidden
.overrun

View file

@ -16,33 +16,34 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/fmt/magnumstrs.internal.h"
#include "libc/macros.internal.h"
.macro .e e
.macro .e e s
.long \e - kTcpOptnames
.long 1f - kTcpOptnames
.rodata.str1.1
1: .string "\e"
1: .string "\s"
.previous
.endm
.section .rodata
.align 4
.align 4
.underrun
kTcpOptnames:
.e TCP_NODELAY # bool32
.e TCP_CORK # bool32
.e TCP_QUICKACK # bool32
.e TCP_FASTOPEN_CONNECT # bool32
.e TCP_DEFER_ACCEPT # bool32
.e TCP_KEEPIDLE # int (seconds)
.e TCP_KEEPINTVL # int (seconds)
.e TCP_FASTOPEN # int
.e TCP_KEEPCNT # int
.e TCP_MAXSEG # int
.e TCP_SYNCNT # int
.e TCP_NOTSENT_LOWAT # int
.e TCP_WINDOW_CLAMP # int
.long -123
.e TCP_NODELAY,"NODELAY" # bool32
.e TCP_CORK,"CORK" # bool32
.e TCP_QUICKACK,"QUICKACK" # bool32
.e TCP_FASTOPEN_CONNECT,"FASTOPEN_CONNECT" # bool32
.e TCP_DEFER_ACCEPT,"DEFER_ACCEPT" # bool32
.e TCP_KEEPIDLE,"KEEPIDLE" # int (seconds)
.e TCP_KEEPINTVL,"KEEPINTVL" # int (seconds)
.e TCP_FASTOPEN,"FASTOPEN" # int
.e TCP_KEEPCNT,"KEEPCNT" # int
.e TCP_MAXSEG,"MAXSEG" # int
.e TCP_SYNCNT,"SYNCNT" # int
.e TCP_NOTSENT_LOWAT,"NOTSENT_LOWAT" # int
.e TCP_WINDOW_CLAMP,"WINDOW_CLAMP" # int
.long MAGNUM_TERMINATOR
.endobj kTcpOptnames,globl,hidden
.overrun

View file

@ -19,16 +19,12 @@
#include "libc/calls/struct/timeval.h"
#include "libc/limits.h"
#include "libc/macros.internal.h"
#include "libc/nt/struct/linger.h"
#include "libc/sock/internal.h"
#include "libc/sysv/consts/so.h"
#include "libc/sysv/consts/sol.h"
#include "libc/sysv/errfuns.h"
struct linger_nt { /* Linux+XNU+BSD ABI */
uint16_t l_onoff; /* on/off */
uint16_t l_linger; /* seconds */
};
textwindows int sys_setsockopt_nt(struct Fd *fd, int level, int optname,
const void *optval, uint32_t optlen) {
int64_t ms;

View file

@ -22,6 +22,7 @@
#include "libc/calls/struct/sigaction.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/log/log.h"
#include "libc/paths.h"
#include "libc/runtime/runtime.h"
#include "libc/stdio/stdio.h"

View file

@ -16,6 +16,7 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/fmt/magnumstrs.internal.h"
#include "libc/macros.internal.h"
.macro .e e s
@ -65,6 +66,6 @@ kSignalNames:
.e SIGRTMIN,"RTMIN"
.e SIGEMT,"EMT"
.e SIGPWR,"PWR"
.long -123
.long MAGNUM_TERMINATOR
.endobj kSignalNames,globl,hidden
.overrun

View file

@ -262,8 +262,8 @@ wint_t towctrans(wint_t, wctrans_t);
char *strsignal(int) returnsnonnull libcesque;
char *strerror(int) returnsnonnull dontthrow nocallback;
const char *strerrno(int) nosideeffect libcesque;
const char *strerdoc(int) nosideeffect libcesque;
char *strerrno(int) nosideeffect libcesque;
char *strerdoc(int) nosideeffect libcesque;
int strerror_r(int, char *, size_t) dontthrow nocallback;
int strerror_wr(int, uint32_t, char *, size_t) dontthrow nocallback;

View file

@ -620,7 +620,7 @@ syscon clock CLOCK_MONOTONIC_RAW 4 4 0x4000 0x4000 0x4000 4 # actu
syscon clock CLOCK_REALTIME_COARSE 5 -1 -1 -1 -1 -1 # Linux 2.6.32+; bsd consensus; not available on RHEL5
syscon clock CLOCK_MONOTONIC_COARSE 6 -1 -1 -1 -1 -1 # Linux 2.6.32+; bsd consensus; not available on RHEL5
syscon clock CLOCK_PROF -1 -1 2 -1 2 -1 #
syscon clock CLOCK_BOOTTIME 7 -1 -1 6 6 -1 #
syscon clock CLOCK_BOOTTIME 7 -1 -1 6 -1 -1 #
syscon clock CLOCK_REALTIME_ALARM 8 -1 -1 -1 -1 -1 #
syscon clock CLOCK_BOOTTIME_ALARM 9 -1 -1 -1 -1 -1 #
syscon clock CLOCK_TAI 11 -1 -1 -1 -1 -1 #
@ -669,16 +669,19 @@ syscon epoll EPOLLET 0x80000000 0x80000000 0x80000000 0x80000000 0x80000
# * -1 we define as no-op
#
# group name GNU/Systemd XNU's Not UNIX! FreeBSD OpenBSD NetBSD The New Technology Commentary
syscon so SO_DEBUG 1 1 1 1 1 1 # debugging is enabled; consensus
syscon so SO_TYPE 3 0x1008 0x1008 0x1008 0x1008 0x1008 # bsd consensus
syscon so SO_ERROR 4 0x1007 0x1007 0x1007 0x1007 0x1007 # takes int pointer and stores/clears the pending error code; bsd consensus
syscon so SO_ACCEPTCONN 30 2 2 2 2 2 # takes int pointer and stores boolean indicating if listen() was called on fd; bsd consensus
syscon so SO_REUSEPORT 15 0x0200 0x0200 0x0200 0x0200 4 # bsd consensus (NT calls it SO_REUSEADDR)
syscon so SO_REUSEADDR 2 4 4 4 4 0 # bsd consensus (default behavior on NT)
syscon so SO_REUSEADDR 2 4 4 4 4 4 # bsd consensus (default behavior on NT)
syscon so SO_EXCLUSIVEADDRUSE 0 0 0 0 0 ~4 # bsd consensus (default behavior on NT)
syscon so SO_KEEPALIVE 9 8 8 8 8 8 # bsd consensus
syscon so SO_DONTROUTE 5 0x10 0x10 0x10 0x10 0x10 # bsd consensus
syscon so SO_BROADCAST 6 0x20 0x20 0x20 0x20 0x20 # bsd consensus
syscon so SO_BROADCAST 6 0x20 0x20 0x20 0x20 0x20 # socket is configured for broadcast messages; bsd consensus
syscon so SO_USELOOPBACK 0 0x40 0x40 0x40 0x40 0x40 # bsd consensus
syscon so SO_LINGER 13 0x80 0x80 0x80 0x80 0x80 # takes struct linger; causes close() return value to actually mean something; bsd consensus
syscon so SO_DEBUG 1 1 1 1 1 1 # consensus
syscon so SO_ACCEPTCONN 30 2 2 2 2 2 # takes int pointer and stores boolean indicating if listen() was called on fd; bsd consensus
syscon so SO_ERROR 4 0x1007 0x1007 0x1007 0x1007 0x1007 # takes int pointer and stores/clears the pending error code; bsd consensus
syscon so SO_DONTLINGER 0 0 0 0 0 ~0x80 # disables so_linger on windows
syscon so SO_OOBINLINE 10 0x0100 0x0100 0x0100 0x0100 0x0100 # bsd consensus
syscon so SO_SNDBUF 7 0x1001 0x1001 0x1001 0x1001 0x1001 # bsd consensus
syscon so SO_RCVBUF 8 0x1002 0x1002 0x1002 0x1002 0x1002 # bsd consensus
@ -686,7 +689,6 @@ syscon so SO_RCVTIMEO 20 0x1006 0x1006 0x1006 0x100c 0x1006 # rec
syscon so SO_SNDTIMEO 21 0x1005 0x1005 0x1005 0x100b 0x1005 # send timeout; takes struct timeval; bsd consensus
syscon so SO_RCVLOWAT 18 0x1004 0x1004 0x1004 0x1004 0x1004 # bsd consensus
syscon so SO_SNDLOWAT 19 0x1003 0x1003 0x1003 0x1003 0x1003 # bsd consensus
syscon so SO_TYPE 3 0x1008 0x1008 0x1008 0x1008 0x1008 # bsd consensus
syscon so SO_TIMESTAMP 29 0x0400 0x0400 0x0800 0x2000 0
syscon so SO_SETFIB 0 0 0x1014 0 0 0
syscon so SO_DOMAIN 39 0 0x1019 0x1024 0 0

View file

@ -1,2 +1,2 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon clock,CLOCK_BOOTTIME,7,-1,-1,6,6,-1
.syscon clock,CLOCK_BOOTTIME,7,-1,-1,6,-1,-1

View file

@ -0,0 +1,2 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon so,SO_DONTLINGER,0,0,0,0,0,~0x80

View file

@ -1,2 +1,2 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon so,SO_REUSEADDR,2,4,4,4,4,0
.syscon so,SO_REUSEADDR,2,4,4,4,4,4

View file

@ -1,6 +1,7 @@
#ifndef COSMOPOLITAN_LIBC_TESTLIB_EZBENCH_H_
#define COSMOPOLITAN_LIBC_TESTLIB_EZBENCH_H_
#include "libc/macros.internal.h"
#include "libc/nexgen32e/bench.h"
#include "libc/nexgen32e/x86feature.h"
#include "libc/sysv/consts/clock.h"
#include "libc/testlib/bench.h"
@ -9,151 +10,162 @@
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
#define EZBENCH_COUNT 128
#define EZBENCH_TRIES 10
#define EZBENCH(INIT, EXPR) EZBENCH2(#EXPR, INIT, EXPR)
#define EZBENCH2(NAME, INIT, EXPR) \
do { \
int Core, Tries, Interrupts; \
int64_t Speculative, MemoryStrict; \
Tries = 0; \
do { \
__testlib_yield(); \
Core = __testlib_getcore(); \
Interrupts = __testlib_getinterrupts(); \
INIT; \
EXPR; \
Speculative = BENCHLOOP(__startbench, __endbench, 128, ({ \
INIT; \
polluteregisters(); \
}), \
(EXPR)); \
} while (++Tries < 10 && (__testlib_getcore() != Core && \
__testlib_getinterrupts() > Interrupts)); \
if (Tries == 10) __testlib_ezbenchwarn(" speculative"); \
Tries = 0; \
do { \
__testlib_yield(); \
Core = __testlib_getcore(); \
Interrupts = __testlib_getinterrupts(); \
INIT; \
EXPR; \
MemoryStrict = BENCHLOOP(__startbench_m, __endbench_m, 32, ({ \
INIT; \
thrashcodecache(); \
polluteregisters(); \
}), \
(EXPR)); \
} while (++Tries < 10 && (__testlib_getcore() != Core && \
__testlib_getinterrupts() > Interrupts)); \
if (Tries == 10) __testlib_ezbenchwarn(" memory strict"); \
__testlib_ezbenchreport( \
NAME, MAX(0, Speculative - __testlib_ezbenchcontrol()), \
MAX(0, MemoryStrict - __testlib_ezbenchcontrol())); \
#define EZBENCH2(NAME, INIT, EXPR) \
do { \
int Core, Tries, Interrupts; \
int64_t Speculative, MemoryStrict; \
Tries = 0; \
do { \
__testlib_yield(); \
Core = __testlib_getcore(); \
Interrupts = __testlib_getinterrupts(); \
INIT; \
EXPR; \
Speculative = BENCHLOOP(__startbench, __endbench, EZBENCH_COUNT, ({ \
INIT; \
__polluteregisters(); \
}), \
(EXPR)); \
} while (++Tries < EZBENCH_TRIES && \
(__testlib_getcore() != Core && \
__testlib_getinterrupts() > Interrupts)); \
if (Tries == EZBENCH_TRIES) __testlib_ezbenchwarn(" speculative"); \
Tries = 0; \
do { \
__testlib_yield(); \
Core = __testlib_getcore(); \
Interrupts = __testlib_getinterrupts(); \
INIT; \
EXPR; \
MemoryStrict = BENCHLOOP(__startbench_m, __endbench_m, 32, ({ \
INIT; \
thrashcodecache(); \
__polluteregisters(); \
}), \
(EXPR)); \
} while (++Tries < EZBENCH_TRIES && \
(__testlib_getcore() != Core && \
__testlib_getinterrupts() > Interrupts)); \
if (Tries == EZBENCH_TRIES) __testlib_ezbenchwarn(" memory strict"); \
__testlib_ezbenchreport( \
NAME, MAX(0, Speculative - __testlib_ezbenchcontrol()), \
MAX(0, MemoryStrict - __testlib_ezbenchcontrol())); \
} while (0)
#define EZBENCH3(NAME, NUM, INIT, EXPR) \
do { \
int Core, Tries, Interrupts; \
int64_t Speculative, MemoryStrict; \
Tries = 0; \
do { \
__testlib_yield(); \
Core = __testlib_getcore(); \
Interrupts = __testlib_getinterrupts(); \
INIT; \
EXPR; \
Speculative = BENCHLOOP(__startbench, __endbench, NUM, ({ \
INIT; \
polluteregisters(); \
}), \
(EXPR)); \
} while (++Tries < 10 && (__testlib_getcore() != Core && \
__testlib_getinterrupts() > Interrupts)); \
if (Tries == 10) __testlib_ezbenchwarn(" speculative"); \
Tries = 0; \
do { \
__testlib_yield(); \
Core = __testlib_getcore(); \
Interrupts = __testlib_getinterrupts(); \
INIT; \
EXPR; \
MemoryStrict = BENCHLOOP(__startbench_m, __endbench_m, NUM, ({ \
INIT; \
thrashcodecache(); \
polluteregisters(); \
}), \
(EXPR)); \
} while (++Tries < 10 && (__testlib_getcore() != Core && \
__testlib_getinterrupts() > Interrupts)); \
if (Tries == 10) __testlib_ezbenchwarn(" memory strict"); \
__testlib_ezbenchreport( \
NAME, MAX(0, Speculative - __testlib_ezbenchcontrol()), \
MAX(0, MemoryStrict - __testlib_ezbenchcontrol())); \
#define EZBENCH3(NAME, NUM, INIT, EXPR) \
do { \
int Core, Tries, Interrupts; \
int64_t Speculative, MemoryStrict; \
Tries = 0; \
do { \
__testlib_yield(); \
Core = __testlib_getcore(); \
Interrupts = __testlib_getinterrupts(); \
INIT; \
EXPR; \
Speculative = BENCHLOOP(__startbench, __endbench, NUM, ({ \
INIT; \
__polluteregisters(); \
}), \
(EXPR)); \
} while (++Tries < EZBENCH_TRIES && \
(__testlib_getcore() != Core && \
__testlib_getinterrupts() > Interrupts)); \
if (Tries == EZBENCH_TRIES) __testlib_ezbenchwarn(" speculative"); \
Tries = 0; \
do { \
__testlib_yield(); \
Core = __testlib_getcore(); \
Interrupts = __testlib_getinterrupts(); \
INIT; \
EXPR; \
MemoryStrict = BENCHLOOP(__startbench_m, __endbench_m, NUM, ({ \
INIT; \
thrashcodecache(); \
__polluteregisters(); \
}), \
(EXPR)); \
} while (++Tries < EZBENCH_TRIES && \
(__testlib_getcore() != Core && \
__testlib_getinterrupts() > Interrupts)); \
if (Tries == EZBENCH_TRIES) __testlib_ezbenchwarn(" memory strict"); \
__testlib_ezbenchreport( \
NAME, MAX(0, Speculative - __testlib_ezbenchcontrol()), \
MAX(0, MemoryStrict - __testlib_ezbenchcontrol())); \
} while (0)
#define EZBENCH_C(NAME, CONTROL, EXPR) \
do { \
int Core, Tries, Interrupts; \
int64_t Control, Speculative, MemoryStrict; \
Tries = 0; \
do { \
__testlib_yield(); \
Core = __testlib_getcore(); \
Interrupts = __testlib_getinterrupts(); \
Control = BENCHLOOP(__startbench_m, __endbench_m, 128, ({ \
thrashcodecache(); \
polluteregisters(); \
}), \
(CONTROL)); \
} while (++Tries < 10 && (__testlib_getcore() != Core && \
__testlib_getinterrupts() > Interrupts)); \
if (Tries == 10) __testlib_ezbenchwarn(" control"); \
Tries = 0; \
do { \
__testlib_yield(); \
Core = __testlib_getcore(); \
Interrupts = __testlib_getinterrupts(); \
EXPR; \
Speculative = BENCHLOOP(__startbench, __endbench, 128, \
polluteregisters(), (EXPR)); \
} while (++Tries < 10 && (__testlib_getcore() != Core && \
__testlib_getinterrupts() > Interrupts)); \
if (Tries == 10) __testlib_ezbenchwarn(" speculative"); \
Tries = 0; \
do { \
__testlib_yield(); \
Core = __testlib_getcore(); \
Interrupts = __testlib_getinterrupts(); \
EXPR; \
MemoryStrict = BENCHLOOP(__startbench_m, __endbench_m, 8, ({ \
thrashcodecache(); \
polluteregisters(); \
}), \
(EXPR)); \
} while (++Tries < 10 && (__testlib_getcore() != Core && \
__testlib_getinterrupts() > Interrupts)); \
if (Tries == 10) __testlib_ezbenchwarn(" memory strict"); \
__testlib_ezbenchreport(NAME, MAX(0, Speculative - Control), \
MAX(0, MemoryStrict - Control)); \
#define EZBENCH_C(NAME, CONTROL, EXPR) \
do { \
int Core, Tries, Interrupts; \
int64_t Control, Speculative, MemoryStrict; \
Tries = 0; \
do { \
__testlib_yield(); \
Core = __testlib_getcore(); \
Interrupts = __testlib_getinterrupts(); \
Control = BENCHLOOP(__startbench_m, __endbench_m, EZBENCH_COUNT, ({ \
thrashcodecache(); \
__polluteregisters(); \
}), \
(CONTROL)); \
} while (++Tries < EZBENCH_TRIES && \
(__testlib_getcore() != Core && \
__testlib_getinterrupts() > Interrupts)); \
if (Tries == EZBENCH_TRIES) __testlib_ezbenchwarn(" control"); \
Tries = 0; \
do { \
__testlib_yield(); \
Core = __testlib_getcore(); \
Interrupts = __testlib_getinterrupts(); \
EXPR; \
Speculative = BENCHLOOP(__startbench, __endbench, EZBENCH_COUNT, \
__polluteregisters(), (EXPR)); \
} while (++Tries < EZBENCH_TRIES && \
(__testlib_getcore() != Core && \
__testlib_getinterrupts() > Interrupts)); \
if (Tries == EZBENCH_TRIES) __testlib_ezbenchwarn(" speculative"); \
Tries = 0; \
do { \
__testlib_yield(); \
Core = __testlib_getcore(); \
Interrupts = __testlib_getinterrupts(); \
EXPR; \
MemoryStrict = BENCHLOOP(__startbench_m, __endbench_m, 8, ({ \
thrashcodecache(); \
__polluteregisters(); \
}), \
(EXPR)); \
} while (++Tries < EZBENCH_TRIES && \
(__testlib_getcore() != Core && \
__testlib_getinterrupts() > Interrupts)); \
if (Tries == EZBENCH_TRIES) __testlib_ezbenchwarn(" memory strict"); \
__testlib_ezbenchreport(NAME, MAX(0, Speculative - Control), \
MAX(0, MemoryStrict - Control)); \
} while (0)
#define EZBENCH_N(NAME, N, EXPR) \
do { \
int64_t Speculative, Toto; \
int Core, Tries, Interrupts; \
Tries = 0; \
do { \
__testlib_yield(); \
Core = __testlib_getcore(); \
Interrupts = __testlib_getinterrupts(); \
EXPR; \
Speculative = \
BENCHLOOP(__startbench, __endbench, 32, polluteregisters(), (EXPR)); \
} while (++Tries < 10 && (__testlib_getcore() != Core && \
__testlib_getinterrupts() > Interrupts)); \
if (Tries == 10) __testlib_ezbenchwarn(""); \
__testlib_ezbenchreport_n( \
NAME, 'n', N, MAX(0, Speculative - __testlib_ezbenchcontrol())); \
#define EZBENCH_N(NAME, N, EXPR) \
do { \
int64_t Speculative, Toto; \
int Core, Tries, Interrupts; \
Tries = 0; \
do { \
__testlib_yield(); \
Core = __testlib_getcore(); \
Interrupts = __testlib_getinterrupts(); \
EXPR; \
Speculative = BENCHLOOP(__startbench, __endbench, 32, \
__polluteregisters(), (EXPR)); \
} while (++Tries < EZBENCH_TRIES && \
(__testlib_getcore() != Core && \
__testlib_getinterrupts() > Interrupts)); \
if (Tries == EZBENCH_TRIES) __testlib_ezbenchwarn(""); \
__testlib_ezbenchreport_n( \
NAME, 'n', N, MAX(0, Speculative - __testlib_ezbenchcontrol())); \
} while (0)
#define EZBENCH_K(NAME, K, EXPR) \
@ -164,14 +176,14 @@ COSMOPOLITAN_C_START_
__testlib_yield(); \
Core = __testlib_getcore(); \
EXPR; \
Speculative = \
BENCHLOOP(__startbench, __endbench, 128, donothing, (EXPR)); \
Speculative = BENCHLOOP(__startbench, __endbench, EZBENCH_COUNT, \
donothing, (EXPR)); \
} while (Core != __testlib_getcore()); \
__testlib_ezbenchreport_n( \
NAME, 'k', K, MAX(0, Speculative - __testlib_ezbenchcontrol())); \
} while (0)
void polluteregisters(void);
void __polluteregisters(void);
void __testlib_yield(void);
int __testlib_getcore(void);
int64_t __testlib_getinterrupts(void);

View file

@ -19,15 +19,15 @@
#include "libc/nexgen32e/x86feature.h"
#include "libc/macros.internal.h"
polluteregisters:
__polluteregisters:
.leafprologue
xor %eax,%eax
mov %ecx,%ecx
mov %edx,%edx
mov %r8d,%r8d
mov %r9d,%r9d
mov %r10d,%r10d
mov %r11d,%r11d
xor %ecx,%ecx
xor %edx,%edx
xor %r8d,%r8d
xor %r9d,%r9d
xor %r10d,%r10d
xor %r11d,%r11d
testb X86_HAVE(AVX)+kCpuids(%rip)
jz .Lsse
vpxor %xmm0,%xmm0,%xmm0
@ -48,13 +48,13 @@ polluteregisters:
xorps %xmm6,%xmm6
xorps %xmm7,%xmm7
.leafepilogue
.endfn polluteregisters,globl
.endfn __polluteregisters,globl
.end
// Fill registers with junk data to create false dependencies.
// Which shall create the problem that happens w/o vzeroupper.
// Or the Core Architecture errata regarding BSR/BSF w/ 64bit.
polluteregisters:
__polluteregisters:
.leafprologue
mov $-1,%rax
mov %rax,%rcx
@ -92,4 +92,4 @@ polluteregisters:
punpcklqdq %xmm0,%xmm6
punpcklqdq %xmm0,%xmm7
.leafepilogue
.endfn polluteregisters,globl
.endfn __polluteregisters,globl

View file

@ -17,6 +17,8 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/calls/struct/metastat.internal.h"
#include "libc/calls/struct/stat.h"
#include "libc/dce.h"
#include "libc/errno.h"
@ -24,6 +26,7 @@
#include "libc/runtime/gc.internal.h"
#include "libc/runtime/runtime.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/at.h"
#include "libc/sysv/consts/nr.h"
#include "libc/testlib/ezbench.h"
#include "libc/testlib/testlib.h"
@ -68,18 +71,34 @@ static long Stat(const char *path, struct stat *st) {
return ax;
}
static long Fstatat(const char *path, struct stat *st) {
long ax, di, si, dx;
register long r10 asm("r10") = 0;
asm volatile("syscall"
: "=a"(ax), "=D"(di), "=S"(si), "=d"(dx), "+r"(r10)
: "0"(__NR_fstatat), "1"(AT_FDCWD), "2"(path), "3"(st)
: "rcx", "r8", "r9", "r11", "memory", "cc");
return ax;
}
BENCH(stat, bench) {
struct stat st;
union metastat ms;
EXPECT_SYS(0, 0, makedirs(".python/test", 0755));
EZBENCH2("__stat2cosmo", donothing, __stat2cosmo(&st, &ms));
EXPECT_SYS(0, 0,
touch(".python/test/"
"tokenize_tests-latin1-coding-cookie-and-utf8-bom-sig.txt",
0644));
if (!IsWindows()) {
if (!IsWindows() && !IsFreebsd()) {
EZBENCH2("stat syscall", donothing,
Stat(".python/test/"
"tokenize_tests-latin1-coding-cookie-and-utf8-bom-sig.txt",
&st));
EZBENCH2("fstatat syscall", donothing,
Fstatat(".python/test/"
"tokenize_tests-latin1-coding-cookie-and-utf8-bom-sig.txt",
&st));
}
EZBENCH2("stat() fs", donothing,
stat(".python/test/"

View file

@ -19,8 +19,11 @@
#include "libc/bits/bits.h"
#include "libc/bits/xchg.internal.h"
#include "libc/calls/calls.h"
#include "libc/dce.h"
#include "libc/fmt/fmt.h"
#include "libc/intrin/kprintf.h"
#include "libc/linux/mmap.h"
#include "libc/linux/munmap.h"
#include "libc/log/log.h"
#include "libc/mem/mem.h"
#include "libc/rand/rand.h"
@ -33,6 +36,7 @@
#include "libc/sysv/consts/msync.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/prot.h"
#include "libc/testlib/ezbench.h"
#include "libc/testlib/testlib.h"
#include "libc/x/x.h"
@ -281,3 +285,57 @@ TEST(mmap, sharedFileMapFork) {
EXPECT_NE(-1, close(fd));
EXPECT_NE(-1, unlink(path));
}
////////////////////////////////////////////////////////////////////////////////
// BENCHMARKS
#define N (EZBENCH_COUNT * EZBENCH_TRIES)
int count;
void *ptrs[N];
void BenchUnmap(void) {
int i;
for (i = 0; i < count; ++i) {
if (ptrs[i]) {
ASSERT_EQ(0, munmap(ptrs[i], FRAMESIZE));
}
}
}
void BenchMmapPrivate(void) {
void *p;
p = mmap(0, FRAMESIZE, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE,
-1, 0);
if (p == MAP_FAILED) abort();
ptrs[count++] = p;
}
BENCH(mmap, bench) {
EZBENCH2("mmap", donothing, BenchMmapPrivate());
BenchUnmap();
}
void BenchUnmapLinux(void) {
int i;
for (i = 0; i < count; ++i) {
if (ptrs[i]) {
ASSERT_EQ(0, LinuxMunmap(ptrs[i], FRAMESIZE));
}
}
}
void BenchMmapPrivateLinux(void) {
void *p;
p = (void *)LinuxMmap(0, FRAMESIZE, PROT_READ | PROT_WRITE,
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
if (p == MAP_FAILED) abort();
ptrs[count++] = p;
}
BENCH(mmap, benchLinux) {
void *p;
if (!IsLinux()) return;
EZBENCH2("mmap (linux)", donothing, BenchMmapPrivateLinux());
BenchUnmapLinux();
}

View file

@ -193,13 +193,14 @@ Copyright 2010-2013 Pieter Noordhuis <pcnoordhuis@gmail.com>\"");
#define DEBUG(L, ...) (void)0
#endif
#define DUFF_ROUTINE_LOOP 0
#define DUFF_ROUTINE_START 5
#define DUFF_ROUTINE_LOOP 0
#define DUFF_ROUTINE_SEARCH 1
#define DUFF_ROUTINE_START 5
#define DUFF_ROUTINE_LABEL(STATE) \
case STATE: \
linenoiseRefreshLineForce(l); \
l->state = DUFF_ROUTINE_LOOP
l->state = STATE
#define DUFF_ROUTINE_READ(STATE) \
DUFF_ROUTINE_LABEL(STATE); \
@ -469,11 +470,11 @@ static const char *FindSubstringReverse(const char *p, size_t n, const char *q,
n -= m;
do {
for (i = 0; i < m; ++i) {
if (p[n + i] != q[i]) {
if (kToLower[p[n + i] & 255] != kToLower[q[i] & 255]) {
break;
}
}
if (i == m) {
if (kToLower[i & 255] == kToLower[m & 255]) {
return p + n;
}
} while (n--);
@ -1852,7 +1853,8 @@ ssize_t linenoiseEdit(struct linenoiseState *l, const char *prompt, char **obuf,
char seq[16];
gotint = 0;
if (prompt && (!l->prompt || strcmp(prompt, l->prompt))) {
if (prompt && l->state != DUFF_ROUTINE_SEARCH &&
(!l->prompt || strcmp(prompt, l->prompt))) {
free(l->prompt);
l->prompt = strdup(prompt);
}
@ -1885,7 +1887,7 @@ ssize_t linenoiseEdit(struct linenoiseState *l, const char *prompt, char **obuf,
for (fail = l->matlen = 0;;) {
free(l->prompt);
l->prompt = linenoiseMakeSearchPrompt(fail, l->ab.b, l->matlen);
DUFF_ROUTINE_READ(1);
DUFF_ROUTINE_READ(DUFF_ROUTINE_SEARCH);
fail = 1;
added = 0;
l->j = l->pos;
@ -1906,7 +1908,6 @@ ssize_t linenoiseEdit(struct linenoiseState *l, const char *prompt, char **obuf,
} else if (seq[0] == CTRL('G')) {
linenoiseEditHistoryGoto(l, l->oldindex);
l->pos = l->olderpos;
rc = 0;
break;
} else if (iswcntrl(seq[0])) { // only sees canonical c0
break;
@ -1953,7 +1954,7 @@ ssize_t linenoiseEdit(struct linenoiseState *l, const char *prompt, char **obuf,
rc = 0;
linenoiseFreeCompletions(&l->lc);
i = Backwards(l, l->pos, iswname);
j = Forwards(l, l->pos, iswname);
j = l->pos;
{
char *s = strndup(l->buf + i, j - i);
completionCallback(s, &l->lc);
@ -1966,7 +1967,8 @@ ssize_t linenoiseEdit(struct linenoiseState *l, const char *prompt, char **obuf,
if (linenoiseGrow(l, n + 1)) {
memmove(l->buf + i + m, l->buf + i + j, l->len - j + 1);
memcpy(l->buf + i, l->lc.cvec[0], m);
l->len = l->pos = n;
l->pos = i + m;
l->len = n;
}
continue;
}

View file

@ -8,8 +8,8 @@ COSMOPOLITAN_C_START_
char *LuaFormatStack(lua_State *) dontdiscard;
int LuaCallWithTrace(lua_State *, int, int, lua_State *);
int LuaEncodeJsonData(lua_State *, char **, int, char *);
int LuaEncodeLuaData(lua_State *, char **, int, char *);
int LuaEncodeJsonData(lua_State *, char **, int, char *, int);
int LuaEncodeLuaData(lua_State *, char **, int, char *, int);
int LuaEncodeUrl(lua_State *);
int LuaParseUrl(lua_State *);
int LuaPushHeader(lua_State *, struct HttpMessage *, char *, int);

View file

@ -16,20 +16,25 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/bits/bits.h"
#include "libc/stdio/append.internal.h"
#include "libc/str/str.h"
#include "third_party/lua/cosmo.h"
#include "third_party/lua/lua.h"
void EscapeLuaString(char *s, size_t len, char **buf) {
appendw(buf, '"');
for (size_t i = 0; i < len; i++) {
if (s[i] == '\\' || s[i] == '\"' || s[i] == '\n' || s[i] == '\0' ||
s[i] == '\r') {
if (' ' <= s[i] && s[i] <= 0x7e) {
appendw(buf, s[i]);
} else if (s[i] == '\n') {
appendw(buf, '\\' | 'n' << 8);
} else if (s[i] == '\\' || s[i] == '\'' || s[i] == '\"') {
appendw(buf, '\\' | s[i] << 8);
} else {
appendw(buf, '\\' | 'x' << 010 |
"0123456789abcdef"[(s[i] & 0xF0) >> 4] << 020 |
"0123456789abcdef"[(s[i] & 0x0F) >> 0] << 030);
} else {
appendd(buf, s + i, 1);
}
}
appendw(buf, '"');

View file

@ -32,6 +32,7 @@
#include "libc/intrin/kprintf.h"
#include "libc/intrin/nomultics.internal.h"
#include "libc/log/check.h"
#include "libc/macros.internal.h"
#include "libc/runtime/gc.h"
#include "libc/runtime/runtime.h"
#include "libc/sysv/consts/sa.h"
@ -50,6 +51,17 @@ Copyright 19942021 Lua.org, PUC-Rio.\"");
asm(".include \"libc/disclaimer.inc\"");
static const char *const kKeywordHints[] = {
"else", //
"elseif", //
"function", //
"function", //
"repeat", //
"then", //
"until", //
"while", //
};
bool lua_repl_blocking;
bool lua_repl_isterminal;
linenoiseCompletionCallback *lua_repl_completions_callback;
@ -74,46 +86,59 @@ static const char *g_historypath;
#define LUA_MAXINPUT 512
#endif
static void lua_readline_addcompletion(linenoiseCompletions *c, char *s) {
char **p = c->cvec;
size_t n = c->len + 1;
if ((p = realloc(p, n * sizeof(*p)))) {
p[n - 1] = s;
static void lua_readline_addcompletion (linenoiseCompletions *c, const char *s) {
char **p, *t;
if ((p = realloc(c->cvec, (c->len + 1) * sizeof(*p)))) {
c->cvec = p;
c->len = n;
if ((t = strdup(s))) {
c->cvec[c->len++] = t;
}
}
}
void lua_readline_completions(const char *p, linenoiseCompletions *c) {
void lua_readline_completions (const char *p, linenoiseCompletions *c) {
int i;
lua_State *L;
const char *name;
for (i = 0; i < ARRAYLEN(kKeywordHints); ++i) {
if (startswithi(kKeywordHints[i], p)) {
lua_readline_addcompletion(c, kKeywordHints[i]);
}
}
L = globalL;
lua_pushglobaltable(L);
lua_pushnil(L);
while (lua_next(L, -2) != 0) {
name = lua_tostring(L, -2);
if (startswithi(name, p)) {
lua_readline_addcompletion(c, strdup(name));
lua_readline_addcompletion(c, name);
}
lua_pop(L, 1);
}
lua_pop(L, 1);
lua_repl_completions_callback(p, c);
if (lua_repl_completions_callback) {
lua_repl_completions_callback(p, c);
}
}
char *lua_readline_hint(const char *p, const char **ansi1, const char **ansi2) {
char *h = 0;
char *lua_readline_hint (const char *p, const char **ansi1, const char **ansi2) {
char *h;
linenoiseCompletions c = {0};
lua_readline_completions(p, &c);
if (c.len == 1) h = strdup(c.cvec[0] + strlen(p));
h = c.len == 1 ? strdup(c.cvec[0] + strlen(p)) : 0;
linenoiseFreeCompletions(&c);
return h;
}
static void lua_freeline (lua_State *L, char *b) {
free(b);
}
/*
** Return the string to be used as a prompt by the interpreter. Leave
** the string (or nil, if using the default value) on the stack, to keep
@ -129,6 +154,7 @@ static const char *get_prompt (lua_State *L, int firstline) {
}
}
/* mark in error messages for incomplete statements */
#define EOFMARK "<eof>"
#define marklen (sizeof(EOFMARK)/sizeof(char) - 1)
@ -165,7 +191,7 @@ static ssize_t pushline (lua_State *L, int firstline) {
prmt = strdup(get_prompt(L, firstline));
lua_pop(L, 1); /* remove prompt */
LUA_REPL_UNLOCK;
rc = linenoiseEdit(lua_repl_linenoise, prmt, &b, !firstline || lua_repl_blocking);
rc = linenoiseEdit(lua_repl_linenoise, 0, &b, !firstline || lua_repl_blocking);
free(prmt);
if (rc != -1) {
if (b && *b) {
@ -187,10 +213,11 @@ static ssize_t pushline (lua_State *L, int firstline) {
l = strlen(b);
if (l > 0 && b[l-1] == '\n') /* line ends with newline? */
b[--l] = '\0'; /* remove it */
if (firstline && b[0] == '=') /* for compatibility with 5.2, ... */
if (firstline && b[0] == '=') { /* for compatibility with 5.2, ... */
lua_pushfstring(L, "return %s", b + 1); /* change '=' to 'return' */
else
} else {
lua_pushlstring(L, b, l);
}
lua_freeline(L, b);
return 1;
}

View file

@ -80,6 +80,14 @@ o/$(MODE)/third_party/lua/lua.com: \
@$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -9qj $@ \
o/$(MODE)/third_party/lua/.lua/.symtab
o//third_party/lua/lgc.o: \
OVERRIDE_CFLAGS += \
-O2
o/$(MODE)/third_party/lua/lvm.o: \
OVERRIDE_CFLAGS += \
-fno-gcse
o/$(MODE)/third_party/lua/lauxlib.o: \
OVERRIDE_CFLAGS += \
-DSTACK_FRAME_UNLIMITED
@ -89,6 +97,8 @@ $(THIRD_PARTY_LUA_OBJS): \
-ffunction-sections \
-fdata-sections
$(THIRD_PARTY_LUA_OBJS): third_party/lua/lua.mk
.PHONY: o/$(MODE)/third_party/lua
o/$(MODE)/third_party/lua: \
$(THIRD_PARTY_LUA_BINS) \

View file

@ -16,6 +16,8 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/bits/bits.h"
#include "libc/fmt/itoa.h"
#include "libc/runtime/gc.internal.h"
#include "libc/stdio/append.internal.h"
#include "net/http/escape.h"
@ -23,63 +25,115 @@
#include "third_party/lua/lauxlib.h"
#include "third_party/lua/lua.h"
int LuaEncodeJsonData(lua_State *L, char **buf, int level, char *numformat) {
size_t idx = -1;
size_t tbllen, buflen;
int LuaEncodeJsonData(lua_State *L, char **buf, int level, char *numformat,
int idx) {
char *s;
bool isarray;
int t = lua_type(L, idx);
if (level < 0) return luaL_argerror(L, 1, "too many nested tables");
if (LUA_TSTRING == t) {
appendw(buf, '"');
appends(buf, gc(EscapeJsStringLiteral(lua_tostring(L, idx), -1, 0)));
appendw(buf, '"');
} else if (LUA_TNUMBER == t) {
appendf(buf, numformat, lua_tonumber(L, idx));
} else if (LUA_TBOOLEAN == t) {
appends(buf, lua_toboolean(L, idx) ? "true" : "false");
} else if (LUA_TTABLE == t) {
tbllen = lua_rawlen(L, idx);
// encode tables with numeric indices and empty tables as arrays
isarray = tbllen > 0 || // integer keys present
(lua_pushnil(L), lua_next(L, -2) == 0) || // no non-integer keys
(lua_pop(L, 2), false); // pop key/value pushed by lua_next
appendw(buf, isarray ? '[' : '{');
if (isarray) {
for (int i = 1; i <= tbllen; i++) {
if (i > 1) appendw(buf, ',');
lua_rawgeti(L, -1, i); // table/-2, value/-1
LuaEncodeJsonData(L, buf, level - 1, numformat);
lua_pop(L, 1);
}
} else {
int i = 1;
lua_pushnil(L); // push the first key
while (lua_next(L, -2) != 0) {
if (!lua_isstring(L, -2))
return luaL_argerror(L, 1, "expected number or string as key value");
if (i++ > 1) appendw(buf, ',');
size_t tbllen, z;
char ibuf[21], fmt[] = "%.14g";
if (level > 0) {
switch (lua_type(L, idx)) {
case LUA_TSTRING:
s = lua_tolstring(L, idx, &z);
s = EscapeJsStringLiteral(s, z, &z);
appendw(buf, '"');
if (lua_type(L, -2) == LUA_TSTRING) {
appends(buf, gc(EscapeJsStringLiteral(lua_tostring(L, -2), -1, 0)));
appendd(buf, s, z);
appendw(buf, '"');
free(s);
return 0;
case LUA_TNIL:
appendw(buf, READ32LE("null"));
return 0;
case LUA_TFUNCTION:
appendf(buf, "\"func@%p\"", lua_touserdata(L, idx));
return 0;
case LUA_TUSERDATA:
appendf(buf, "\"udata@%p\"", lua_touserdata(L, idx));
return 0;
case LUA_TLIGHTUSERDATA:
appendf(buf, "\"light@%p\"", lua_touserdata(L, idx));
return 0;
case LUA_TTHREAD:
appendf(buf, "\"thread@%p\"", lua_touserdata(L, idx));
return 0;
case LUA_TNUMBER:
if (lua_isinteger(L, idx)) {
appendd(buf, ibuf,
FormatInt64(ibuf, luaL_checkinteger(L, idx)) - ibuf);
} else {
// we'd still prefer to use lua_tostring on a numeric index, but can't
// use it in-place, as it breaks lua_next (changes numeric key to a
// string)
lua_pushvalue(L, -2); // table/-4, key/-3, value/-2, key/-1
appends(buf, lua_tostring(L, idx)); // use the copy
lua_remove(L, -1); // remove copied key: table/-3, key/-2, value/-1
// TODO(jart): replace this api
while (*numformat == '%' || *numformat == '.' ||
isdigit(*numformat)) {
++numformat;
}
switch (*numformat) {
case 'a':
case 'g':
case 'f':
fmt[4] = *numformat;
break;
default:
return luaL_error(L, "numformat string not allowed");
}
appendf(buf, fmt, lua_tonumber(L, idx));
}
appendw(buf, '"' | ':' << 010);
LuaEncodeJsonData(L, buf, level - 1, numformat);
lua_pop(L, 1); // table/-2, key/-1
}
// stack: table/-1, as the key was popped by lua_next
return 0;
case LUA_TBOOLEAN:
appends(buf, lua_toboolean(L, idx) ? "true" : "false");
return 0;
case LUA_TTABLE:
tbllen = lua_rawlen(L, idx);
// encode tables with numeric indices and empty tables as arrays
isarray =
tbllen > 0 || // integer keys present
(lua_pushnil(L), lua_next(L, -2) == 0) || // no non-integer keys
(lua_pop(L, 2), false); // pop key/value pushed by lua_next
appendw(buf, isarray ? '[' : '{');
if (isarray) {
for (size_t i = 1; i <= tbllen; i++) {
if (i > 1) appendw(buf, ',');
lua_rawgeti(L, -1, i); // table/-2, value/-1
LuaEncodeJsonData(L, buf, level - 1, numformat, -1);
lua_pop(L, 1);
}
} else {
int i = 1;
lua_pushnil(L); // push the first key
while (lua_next(L, -2)) {
if (!lua_isstring(L, -2)) {
luaL_error(L, "expected number or string as key value");
unreachable;
}
if (i++ > 1) appendw(buf, ',');
appendw(buf, '"');
if (lua_type(L, -2) == LUA_TSTRING) {
s = lua_tolstring(L, -2, &z);
s = EscapeJsStringLiteral(s, z, &z);
appendd(buf, s, z);
free(s);
} else {
// we'd still prefer to use lua_tostring on a numeric index, but
// can't use it in-place, as it breaks lua_next (changes numeric
// key to a string)
lua_pushvalue(L, -2); // table/-4, key/-3, value/-2, key/-1
s = lua_tolstring(L, idx, &z);
appendd(buf, s, z); // use the copy
lua_remove(L, -1); // remove copied key: tab/-3, key/-2, val/-1
}
appendw(buf, '"' | ':' << 010);
LuaEncodeJsonData(L, buf, level - 1, numformat, -1);
lua_pop(L, 1); // table/-2, key/-1
}
// stack: table/-1, as the key was popped by lua_next
}
appendw(buf, isarray ? ']' : '}');
return 0;
default:
luaL_error(L, "can't serialize value of this type");
unreachable;
}
appendw(buf, isarray ? ']' : '}');
} else if (LUA_TNIL == t) {
appendd(buf, "null", 4);
} else {
return luaL_argerror(L, 1, "can't serialize value of this type");
luaL_error(L, "too many nested tables");
unreachable;
}
return 0;
}

View file

@ -16,51 +16,97 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/bits/bits.h"
#include "libc/fmt/itoa.h"
#include "libc/math.h"
#include "libc/stdio/append.internal.h"
#include "third_party/lua/cosmo.h"
#include "third_party/lua/lauxlib.h"
#include "third_party/lua/lua.h"
int LuaEncodeLuaData(lua_State *L, char **buf, int level, char *numformat) {
size_t idx = -1;
size_t tbllen, buflen, slen;
int LuaEncodeLuaData(lua_State *L, char **buf, int level, char *numformat,
int idx) {
char *s;
int ktype;
int t = lua_type(L, idx);
if (level < 0) return luaL_argerror(L, 1, "too many nested tables");
if (LUA_TSTRING == t) {
s = lua_tolstring(L, idx, &slen);
EscapeLuaString(s, slen, buf);
} else if (LUA_TNUMBER == t) {
appendf(buf, numformat, lua_tonumber(L, idx));
} else if (LUA_TBOOLEAN == t) {
appends(buf, lua_toboolean(L, idx) ? "true" : "false");
} else if (LUA_TTABLE == t) {
appendw(buf, '{');
int i = 0;
lua_pushnil(L); // push the first key
while (lua_next(L, -2) != 0) {
ktype = lua_type(L, -2);
if (ktype == LUA_TTABLE)
return luaL_argerror(L, 1, "can't serialize key of this type");
if (i++ > 0) appendd(buf, ",", 1);
if (ktype != LUA_TNUMBER || floor(lua_tonumber(L, -2)) != i) {
appendw(buf, '[');
lua_pushvalue(L, -2); // table/-4, key/-3, value/-2, key/-1
LuaEncodeLuaData(L, buf, level, numformat);
lua_remove(L, -1); // remove copied key: table/-3, key/-2, value/-1
appendw(buf, ']' | '=' << 010);
}
LuaEncodeLuaData(L, buf, level - 1, numformat);
lua_pop(L, 1); // table/-2, key/-1
lua_Integer i;
size_t tbllen, buflen, slen;
char ibuf[21], fmt[] = "%.14g";
if (level > 0) {
switch (lua_type(L, idx)) {
case LUA_TNIL:
appendw(buf, READ32LE("nil"));
return 0;
case LUA_TSTRING:
s = lua_tolstring(L, idx, &slen);
EscapeLuaString(s, slen, buf);
return 0;
case LUA_TFUNCTION:
appendf(buf, "\"func@%p\"", lua_touserdata(L, idx));
return 0;
case LUA_TUSERDATA:
appendf(buf, "\"udata@%p\"", lua_touserdata(L, idx));
return 0;
case LUA_TLIGHTUSERDATA:
appendf(buf, "\"light@%p\"", lua_touserdata(L, idx));
return 0;
case LUA_TTHREAD:
appendf(buf, "\"thread@%p\"", lua_touserdata(L, idx));
return 0;
case LUA_TNUMBER:
if (lua_isinteger(L, idx)) {
appendd(buf, ibuf,
FormatInt64(ibuf, luaL_checkinteger(L, idx)) - ibuf);
} else {
// TODO(jart): replace this api
while (*numformat == '%' || *numformat == '.' ||
isdigit(*numformat)) {
++numformat;
}
switch (*numformat) {
case 'a':
case 'g':
case 'f':
fmt[4] = *numformat;
break;
default:
return luaL_error(L, "numformat string not allowed");
}
appendf(buf, fmt, lua_tonumber(L, idx));
}
return 0;
case LUA_TBOOLEAN:
if (lua_toboolean(L, idx)) {
appendw(buf, READ32LE("true"));
} else {
appendw(buf, READ64LE("false\0\0"));
}
return 0;
case LUA_TTABLE:
i = 0;
appendw(buf, '{');
lua_pushnil(L); // push the first key
while (lua_next(L, -2) != 0) {
ktype = lua_type(L, -2);
if (i++ > 0) appendw(buf, ',');
if (ktype != LUA_TNUMBER || lua_tointeger(L, -2) != i) {
appendw(buf, '[');
lua_pushvalue(L, -2); // table/-4, key/-3, value/-2, key/-1
LuaEncodeLuaData(L, buf, level - 1, numformat, -1);
lua_remove(L, -1); // remove copied key: table/-3, key/-2, value/-1
appendw(buf, ']' | '=' << 010);
}
LuaEncodeLuaData(L, buf, level - 1, numformat, -1);
lua_pop(L, 1); // table/-2, key/-1
}
// stack: table/-1, as the key was popped by lua_next
appendw(buf, '}');
return 0;
default:
luaL_error(L, "can't serialize value of this type");
unreachable;
}
// stack: table/-1, as the key was popped by lua_next
appendw(buf, '}');
} else if (LUA_TNIL == t) {
appendd(buf, "nil", 3);
} else {
return luaL_argerror(L, 1, "can't serialize value of this type");
luaL_error(L, "too many nested tables");
unreachable;
}
return 0;
}

View file

@ -18,31 +18,17 @@
*/
#include "libc/stdio/append.internal.h"
#include "third_party/lua/cosmo.h"
#include "third_party/lua/lauxlib.h"
dontdiscard char *LuaFormatStack(lua_State *L) {
size_t l;
int i, top;
char *b = 0;
char *p, *b = 0;
top = lua_gettop(L);
for (i = 1; i <= top; i++) {
if (i > 1) appendw(&b, '\n');
appendf(&b, "\t%d\t%s\t", i, luaL_typename(L, i));
switch (lua_type(L, i)) {
case LUA_TNUMBER:
appendf(&b, "%g", lua_tonumber(L, i));
break;
case LUA_TSTRING:
appends(&b, lua_tostring(L, i));
break;
case LUA_TBOOLEAN:
appends(&b, lua_toboolean(L, i) ? "true" : "false");
break;
case LUA_TNIL:
appends(&b, "nil");
break;
default:
appendf(&b, "%p", lua_topointer(L, i));
break;
}
LuaEncodeLuaData(L, &b, 64, "g", -1);
}
return b;
}

View file

@ -2270,8 +2270,7 @@ static void OnVidyaServiceTeletypeOutput(void) {
char buf[12];
n = 0 /* FormatCga(m->bx[0], buf) */;
w = tpenc(VidyaServiceXlatTeletype(m->ax[0]));
do
buf[n++] = w;
do buf[n++] = w;
while ((w >>= 8));
PtyWrite(pty, buf, n);
}

View file

@ -89,6 +89,7 @@ o/$(MODE)/tool/build/blinkenlights.com.dbg: \
$(APE_NO_MODIFY_SELF)
@$(APELINK)
.PRECIOUS: o/$(MODE)/tool/build/blinkenlights.com
o/$(MODE)/tool/build/blinkenlights.com: \
o/$(MODE)/tool/build/blinkenlights.com.dbg \
o/$(MODE)/third_party/zip/zip.com \

View file

@ -88,6 +88,14 @@ else
Write('<dd>%s\r\n' % {enabled})
end
errno, enabled = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_ACCEPTCONN)
Write('<dt>unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_ACCEPTCONN)\r\n')
if errno then
Write('<dd>%s\r\n' % {EscapeHtml(unix.strerrno(errno))})
else
Write('<dd>%s\r\n' % {enabled})
end
errno, enabled = unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_REUSEADDR)
Write('<dt>unix.getsockopt(GetClientFd(), unix.SOL_SOCKET, unix.SO_REUSEADDR)\r\n')
if errno then

View file

@ -13,9 +13,9 @@ function main()
syscall = 'pipe'
reader, writer, errno = unix.pipe()
if reader then
-- oldint = unix.sigaction(unix.SIGINT, unix.SIG_IGN)
-- oldquit = unix.sigaction(unix.SIGQUIT, unix.SIG_IGN)
-- oldmask = unix.sigprocmask(unix.SIG_BLOCK, unix.SIGCHLD)
oldint = unix.sigaction(unix.SIGINT, unix.SIG_IGN)
oldquit = unix.sigaction(unix.SIGQUIT, unix.SIG_IGN)
oldmask = unix.sigprocmask(unix.SIG_BLOCK, (1 << (unix.SIGCHLD - 1)))
syscall = 'fork'
child, errno = unix.fork()
if child then

View file

@ -42,6 +42,7 @@ FLAGS
-u uniprocess
-z print port
-m log messages
-i interpreter mode
-b log message bodies
-a log resource usage
-g log handler latency
@ -236,9 +237,9 @@ USAGE
REPL
Your redbean displays a REPL that lets you modify the state of the
main server process while your server is running. Any changes will
propagate into forked clients.
Your redbean displays a Read-Eval-Print-Loop that lets you modify the
state of the main server process while your server is running. Any
changes will propagate into forked clients.
Your REPL is displayed only when redbean is run as a non-daemon in a
UNIX terminal or the Windows 10 command prompt or powershell. Since
@ -250,6 +251,20 @@ REPL
A history of your commands is saved to `~/.redbean_history`.
If you love the redbean repl and want to use it as your language
interpreter then you can pass the `-i` flag to put redbean into
interpreter mode.
redbean.com -i binarytrees.lua 15
In this mode redbean won't start a web server and instead functions
like the `lua` command. The first command line argument becomes the
script you want to run. If you don't supply a script, then the repl
without a web server is displayed.
This can be useful for testing, since the redbean extensions and
modules for the Lua language are still made available.
SECURITY
@ -357,12 +372,36 @@ SPECIAL PATHS
GLOBALS
argv: array[str]
arg: array[str]
Array of command line arguments, excluding those parsed by
getopt() in the C code, which stops parsing at the first
non-hyphenated arg. In some cases you can use the magic --
argument to delimit C from Lua arguments.
For example, if you launch your redbean as follows:
redbean.com -v arg1 arg2
Then your `/.init.lua` file will have the `arg` array like:
arg[-1] = '/usr/bin/redbean.com'
arg[ 0] = '/zip/.init.lua'
arg[ 1] = 'arg1'
arg[ 2] = 'arg2'
If you launch redbean in interpreter mode (rather than web
server) mode, then an invocation like this:
./redbean.com -i script.lua arg1 arg2
Would have an `arg` array like this:
arg[-1] = './redbean.com'
arg[ 0] = 'script.lua'
arg[ 1] = 'arg1'
arg[ 2] = 'arg2'
HOOKS
@ -522,7 +561,8 @@ FUNCTIONS
- useoutput: (bool=false) encodes the result directly to the
output buffer and returns `nil` value. This option is
ignored if used outside of request handling code.
- numformat: (string="%.14g") sets numeric format to be used.
- numformat: sets numeric format to be used, which can be 'g',
'f', or 'a' [experimental api]
- maxdepth: (number=64) sets the max number of nested tables.
EncodeLua(value[,options:table]) → json:str
@ -531,7 +571,6 @@ FUNCTIONS
- useoutput: (bool=false) encodes the result directly to the
output buffer and returns `nil` value. This option is
ignored if used outside of request handling code.
- numformat: (string="%.14g") sets numeric format to be used.
- maxdepth: (number=64) sets the max number of nested tables.
EncodeLatin1(utf-8:str[,flags:int]) → iso-8859-1:str
@ -1329,52 +1368,25 @@ MAXMIND MODULE
UNIX MODULE
This module exposes the low-level UNIX system call interface. The way
these functions work is they'll only throw a Lua exception if there's
some kind of error obtaining the required arguments. Once Lua reads
the arguments and the call is delegated to the system call interface,
all further errors won't be raised, but rather returned as errnos,
which should always be checked. For example, most syscalls follow:
This module exposes the low-level UNIX system call interface. This
module works on all supported platforms, including Windows NT.
errno = unix.foo(...)
if errno then
Log(kLogWarn, 'foo() failed: %s' % {unix.strerror(errno)})
end
Any POSIX API that's defined as returning 0 on success or -1 on error
is wrapped here to return nil on success and an integer on error. To
see which errnos are possible for which system calls, please see the
comprehensive index at the bottom of this section.
In cases where POSIX defines an API as returning codes on success we
wrap the APIs as follows:
rc, errno = unix.bar(...)
if rc then
Log(kLogWarn, 'foo() succeeded: %d' % {rc})
else
Log(kLogWarn, 'foo() failed: %s' % {unix.strerror(errno)})
end
If the above code succeeds, `rc` will be non-nil and `errno` will be
`nil`. If the above code fails, `rc` will be nil and `errno` will be
an integer greater than zero.
UNIX FUNCTIONS
unix.read(fd:int[, bufsiz:int, offset:int]) → data:str[, errno:int]
Reads from file descriptor.
unix.write(fd:int, data[, offset:int]) → wrote:int[, errno:int]
Writes to file descriptor.
unix.open(path:str, flags:int[, mode:int]) → fd:int[, errno:int]
unix.open(path:str, flags:int[, mode:int]) → fd:int, unix.Errno
Opens file.
`flags` should have one of `O_RDONLY`, `O_WRONLY`, or `O_RDWR`.
Returns a file descriptor integer that needs to be closed, e.g.
fd = assert(open("/etc/passwd", unix.O_RDONLY))
print(unix.read(fd))
unix.close(fd)
`flags` should have one of:
- `O_RDONLY`: open for reading
- `O_WRONLY`: open for writing
- `O_RDWR`: open for reading and writing
The following values may also be OR'd into `flags`:
- `O_CREAT`: create file if it doesn't exist
@ -1412,10 +1424,18 @@ UNIX MODULE
already. If it does exist then `nil` is returned along with
`errno` set to `EEXIST`.
unix.close(fd:int) → errno:int
unix.close(fd:int) → ok:bool, unix.Errno
Closes file descriptor.
unix.read(fd:int[, bufsiz:int, offset:int]) → data:str, unix.Errno
Reads from file descriptor.
unix.write(fd:int, data[, offset:int]) → wrote:int, unix.Errno
Writes to file descriptor.
unix.exit([exitcode]) → ⊥
Invokes `_Exit(exitcode)` on the process. This will immediately
@ -1443,21 +1463,27 @@ UNIX MODULE
command prompt inserts multiple environment variables with empty
string as keys, for its internal bookkeeping.
unix.fork() → childpid|0:int[, errno:int]
unix.fork() → childpid|0:int, unix.Errno
Creates a new process mitosis style. This returns twice. The
parent process gets the nonzero pid. The child gets zero.
unix.commandv(prog:str) → path:str[, errno:int]
unix.commandv(prog:str) → path:str, unix.Errno
Performs `$PATH` lookup of executable. We automatically suffix
`.com` and `.exe` for all platforms when path searching.
By default, the current directory is not on the path.
If `prog` is an absolute path, then it's returned as-is. If
`prog` contains slashes then it's not path searched either and
will be returned if it exists.
Performs `$PATH` lookup of executable.
unix.execve(prog:str[, args:List<*>[, env:List<*>]]) → errno:int
unix = require "unix"
prog = assert(unix.commandv("ls"))
unix.execve(prog, {prog, "-hal", "."}, {PATH="/bin"})
unix.exit(127)
We automatically suffix `.com` and `.exe` for all platforms when
path searching. By default, the current directory is not on the
path. If `prog` is an absolute path, then it's returned as-is. If
`prog` contains slashes then it's not path searched either and will
be returned if it exists.
unix.execve(prog:str[, args:List<*>[, env:List<*>]]) → false, unix.Errno
Exits current process, replacing it with a new instance of the
specified program. `prog` needs to be an absolute path, see
@ -1490,7 +1516,7 @@ UNIX MODULE
`EAGAIN` is returned if you've enforced a max number of
processes using `setrlimit(RLIMIT_NPROC)`.
unix.dup(oldfd:int[, newfd:int[, flags:int]]) → newfd:int, errno:int
unix.dup(oldfd:int[, newfd:int[, flags:int]]) → newfd:int, unix.Errno
Duplicates file descriptor.
@ -1501,7 +1527,7 @@ UNIX MODULE
`flags` can have `O_CLOEXEC` which means the returned file
descriptors will be automatically closed upon execve().
unix.pipe([flags:int]) → reader:int, writer:int[, errno:int]
unix.pipe([flags:int]) → reader:int, unix.Errno, writer:int
Creates fifo which enables communication between processes.
Returns two file descriptors: one for reading and one for
@ -1537,7 +1563,7 @@ UNIX MODULE
end
unix.wait([pid:int, options:int])
→ pid:int, wstatus:int, nil, errno:int
→ pid:int, unix.Errno, wstatus:int
Waits for subprocess to terminate.
@ -1590,22 +1616,22 @@ UNIX MODULE
This function does not fail.
unix.kill(pid, sig) → errno:int
unix.kill(pid, sig) → ok:bool, unix.Errno
Returns process id of current process.
unix.raise(sig) → rc:int[, errno:int]
unix.raise(sig) → rc:int, unix.Errno
Triggers signal in current process.
This is pretty much the same as `kill(getpid(), sig)`.
unix.access(path:str, how) → errno:int
unix.access(path:str, how) → ok:bool, unix.Errno
Checks if effective user of current process has permission to access
file. `how` can be `R_OK`, `W_OK`, `X_OK`, or `F_OK` to check for
read, write, execute, and existence respectively.
unix.mkdir(path:str, mode) → errno:int
unix.mkdir(path:str, mode) → ok:bool, unix.Errno
Makes directory.
@ -1628,7 +1654,7 @@ UNIX MODULE
Fails with `ENAMETOOLONG` if the path is too long.
unix.makedirs(path:str, mode) → errno:int
unix.makedirs(path:str, mode) → ok:bool, unix.Errno
Makes directories.
@ -1639,48 +1665,48 @@ UNIX MODULE
Unlike mkdir() this convenience wrapper will automatically create
parent parent directories as needed.
unix.chdir(path:str) → errno:int
unix.chdir(path:str) → ok:bool, unix.Errno
Changes current directory to `path`.
unix.unlink(path:str) → errno:int
unix.unlink(path:str) → ok:bool, unix.Errno
Removes file at `path`.
unix.rmdir(path:str) → errno:int
unix.rmdir(path:str) → ok:bool, unix.Errno
Removes empty directory at `path`.
unix.rename(oldpath:str, newpath:str) → errno:int
unix.rename(oldpath:str, newpath:str) → ok:bool, unix.Errno
Renames file or directory.
unix.link(existingpath:str, newpath:str) → errno:int
unix.link(existingpath:str, newpath:str) → ok:bool, unix.Errno
Creates hard link, so your underlying inode has two names.
unix.symlink(target:str, linkpath:str) → errno:int
unix.symlink(target:str, linkpath:str) → ok:bool, unix.Errno
Creates soft link, or a symbolic link.
unix.realpath(filename:str) → abspath:str[, errno:int]
unix.realpath(filename:str) → abspath:str, unix.Errno
Returns absolute path of filename, with `.` and `..` components
removed, and symlinks will be resolved.
unix.chown(path:str, uid, gid) → errno:int
unix.chown(path:str, uid, gid) → ok:bool, unix.Errno
Changes user and gorup on file.
unix.chmod(path:str, mode) → errno:int
unix.chmod(path:str, mode) → ok:bool, unix.Errno
Changes mode bits on file.
unix.getcwd() → path:str[, errno:int]
unix.getcwd() → path:str, unix.Errno
Returns current working directory.
unix.fcntl(fd:int, cmd:int[, arg:int]) → rc:int[, errno:int]
unix.fcntl(fd:int, cmd:int[, arg:int]) → rc:int, unix.Errno
Manipulates file descriptor.
@ -1691,27 +1717,27 @@ UNIX MODULE
POSIX advisory locks can be controlled by setting `cmd` to
`F_UNLCK`, `F_RDLCK`, `F_WRLCK`, `F_SETLK`, or `F_SETLKW`.
unix.getsid(pid:int) → sid:int[, errno:int]
unix.getsid(pid:int) → sid:int, unix.Errno
Gets session id.
unix.getpgrp() → pgid:int[, errno:int]
unix.getpgrp() → pgid:int, unix.Errno
Gets process group id.
unix.setpgrp() → pgid:int[, errno:int]
unix.setpgrp() → pgid:int, unix.Errno
Sets process group id. This is the same as `setpgid(0,0)`.
unix.setpgid(pid:int, pgid:int) → pgid:int[, errno:int]
unix.setpgid(pid:int, pgid:int) → pgid:int, unix.Errno
Sets process group id the modern way.
unix.getpgid(pid) → pgid:int[, errno:int]
unix.getpgid(pid) → pgid:int, unix.Errno
Gets process group id the modern wayp.
unix.setsid() → sid:int[, errno:int]
unix.setsid() → sid:int, unix.Errno
Sets session id.
@ -1756,13 +1782,13 @@ UNIX MODULE
This function does not fail.
unix.chroot(path:str) → errno:int
unix.chroot(path:str) → ok:bool, unix.Errno
Changes root directory.
Returns `ENOSYS` on Windows NT.
unix.setuid(uid:int) → errno:int
unix.setuid(uid:int) → ok:bool, unix.Errno
Sets user id.
@ -1798,13 +1824,13 @@ UNIX MODULE
Returns `ENOSYS` on Windows NT if `uid` isn't `getuid()`.
unix.setgid(gid:int) → errno:int
unix.setgid(gid:int) → ok:bool, unix.Errno
Sets group id.
Returns `ENOSYS` on Windows NT if `gid` isn't `getgid()`.
unix.setresuid(real:int, effective:int, saved:int) → errno:int
unix.setresuid(real:int, effective:int, saved:int) → ok:bool, unix.Errno
Sets real, effective, and saved user ids.
@ -1813,7 +1839,7 @@ UNIX MODULE
Returns `ENOSYS` on Windows NT.
Returns `ENOSYS` on Macintosh and NetBSD if `saved` isn't -1.
unix.setresgid(real:int, effective:int, saved:int) → errno:int
unix.setresgid(real:int, effective:int, saved:int) → ok:bool, unix.Errno
Sets real, effective, and saved group ids.
@ -1854,30 +1880,40 @@ UNIX MODULE
This function currently works on Linux, Windows, and NetBSD. On
WIN32 it uses the ReportEvent() facility.
unix.clock_gettime([clock]) → seconds, nanos, errno:int
unix.clock_gettime([clock:int]) → seconds:int, unix.Errno, nanos:int
Returns nanosecond precision timestamp from the system.
`clock` should be `CLOCK_REALTIME`, `CLOCK_MONOTONIC`, or
`CLOCK_MONOTONIC_RAW` since they work across platforms.
You may also try your luck with `CLOCK_REALTIME_COARSE`,
`CLOCK_MONOTONIC_COARSE`, `CLOCK_PROCESS_CPUTIME_ID`,
`CLOCK_TAI`, `CLOCK_PROF`, `CLOCK_BOOTTIME`,
`CLOCK_REALTIME_ALARM`, and `CLOCK_BOOTTIME_ALARM`,
`clock` can be any one of of:
- `CLOCK_REALTIME`: universally supported
- `CLOCK_MONOTONIC`: universally supported
- `CLOCK_MONOTONIC_RAW`: nearly universally supported
- `CLOCK_PROCESS_CPUTIME_ID`: linux and bsd
- `CLOCK_THREAD_CPUTIME_ID`: linux and bsd
- `CLOCK_REALTIME_COARSE`: : linux and openbsd
- `CLOCK_MONOTONIC_COARSE`: linux
- `CLOCK_PROF`: linux and netbsd
- `CLOCK_BOOTTIME`: linux and openbsd
- `CLOCK_REALTIME_ALARM`: linux-only
- `CLOCK_BOOTTIME_ALARM`: linux-only
- `CLOCK_TAI`: ilnux-only
Returns `EINVAL` if clock isn't supported on platform.
unix.nanosleep(seconds:int[, nanos:int])
→ remseconds:int, remnanos:int[, errno:int]
→ remseconds:int, unix.Errno, remnanos:int
Sleeps with nanosecond precision.
unix.sync()
unix.fsync(fd:int) → errno:int
unix.fdatasync(fd:int) → errno:int
unix.fsync(fd:int) → ok:bool, unix.Errno
unix.fdatasync(fd:int) → ok:bool, unix.Errno
These functions are used to make programs slower by asking the
operating system to flush data to the physical medium.
unix.lseek(fd:int, offset:int, whence:int) → newpos:int[, errno:int]
unix.lseek(fd:int, offset:int, whence:int) → newpos:int, unix.Errno
Seeks to file position.
@ -1889,21 +1925,21 @@ UNIX MODULE
Returns the new position relative to the start of the file.
unix.truncate(path:str[, length:int]) → errno:int
unix.truncate(path:str[, length:int]) → ok:bool, unix.Errno
Reduces or extends underlying physical medium of file.
If file was originally larger, content >length is lost.
`length` defaults to zero.
unix.ftruncate(fd:int[, length:int]) → errno:int
unix.ftruncate(fd:int[, length:int]) → ok:bool, unix.Errno
Reduces or extends underlying physical medium of open file.
If file was originally larger, content >length is lost.
`length` defaults to zero.
unix.socket([family:int[, type:int[, protocol:int]]]) → fd:int[, errno:int]
unix.socket([family:int[, type:int[, protocol:int]]]) → fd:int, unix.Errno
`family` defaults to `AF_INET` and can be:
@ -1929,14 +1965,14 @@ UNIX MODULE
`SOCK_CLOEXEC` may be bitwise or'd into `type`.
unix.socketpair([family:int[, type:int[, protocol:int]]])
→ fd1:int, fd2:int[, errno:int]
→ fd1:int, unix.Errno, fd2:int
`SOCK_CLOEXEC` may be or'd into type
`family` defaults to `AF_INET`
`type` defaults to `SOCK_STREAM`
`protocol` defaults to `IPPROTO_TCP`
unix.bind(fd:int[, ip:uint32, port:uint16]) → errno:int
unix.bind(fd:int[, ip:uint32, port:uint16]) → ok:bool, unix.Errno
Binds socket.
@ -1970,19 +2006,21 @@ UNIX MODULE
Further note that calling `unix.bind(sock)` is equivalent to not
calling bind() at all, since the above behavior is the default.
unix.siocgifconf() → {{name:str,ip:uint32,netmask:uint32}, ...}[, errno:int]
unix.siocgifconf() → {{name:str,ip:uint32,netmask:uint32}, ...}, unix.Errno
Returns list of network adapter addresses.
unix.getsockopt(fd:int, level:int, optname:int) → errno:int, ...
unix.setsockopt(fd:int, level:int, optname:int, ...) → errno:int
unix.getsockopt(fd:int, level:int, optname:int) → ok:bool, unix.Errno, ...
unix.setsockopt(fd:int, level:int, optname:int, ...) → ok:bool, unix.Errno
Tunes networking parameters.
`level` and `optname` may be one of the following. Please note the
type signature for getsockopt() changes depending on these values:
`level` and `optname` may be one of the following. The ellipses type
signature above changes depending on which options are used.
- `SOL_SOCKET` + `SO_TYPE`: bool
- `SOL_SOCKET` + `SO_DEBUG`: bool
- `SOL_SOCKET` + `SO_ACCEPTCONN`: bool
- `SOL_SOCKET` + `SO_BROADCAST`: bool
- `SOL_SOCKET` + `SO_REUSEADDR`: bool
- `SOL_SOCKET` + `SO_REUSEPORT`: bool
@ -2016,15 +2054,8 @@ UNIX MODULE
Returns `ENOSYS` if setting isn't supported by the host o/s.
NOTE: The API for this function diverges from the the norm. `errno`
needs to come first in the results, because otherwise we
wouldn't know the arity of items to push before it. It's
because Cosmopolitan Libc polyfills the magic numbers above as
zero if the host operating system doesn't support them. If
there's no error, then `errno` will be set to nil.
unix.poll({fd:int=events:int, ...}[, timeoutms:int])
→ {fd:int=revents:int, ...}[, errno:int]
→ {fd:int=revents:int, ...}, unix.Errno
Checks for events on a set of file descriptors.
@ -2037,19 +2068,25 @@ UNIX MODULE
then that means block as long as it takes until there's an event or
an interrupt. If the timeout expires, an empty table is returned.
unix.gethostname() → host:str[, errno:int]
unix.gethostname() → host:str, unix.Errno
Returns hostname of system.
unix.listen(fd:int[, backlog]) → errno:int
unix.listen(fd:int[, backlog:int]) → ok:bool, unix.Errno
Begins listening for incoming connections on a socket.
unix.accept(serverfd) → clientfd:int, ip:uint32, port:uint16[, errno:int]
unix.accept(serverfd:int[, flags:int])
→ clientfd:int, unix.Errno, ip:uint32, port:uint16
Accepts new client socket descriptor for a listening tcp socket.
unix.connect(fd:int, ip:uint32, port:uint16) → rc:int[, errno:int]
`flags` can have any of:
- `SOCK_CLOEXEC`
- `SOCK_NONBLOCK`
unix.connect(fd:int, ip:uint32, port:uint16) → ok:bool, unix.Errno
Connects a TCP socket to a remote host.
@ -2057,24 +2094,34 @@ UNIX MODULE
remembers the intended address so that send() or write() may be used
rather than sendto().
unix.getsockname(fd:int) → ip:uint32, port:uint16[, errno:int]
unix.getsockname(fd:int) → ip:uint32, unix.Errno, port:uint16
Retrieves the local address of a socket.
unix.getpeername(fd:int) → ip:uint32, port:uint16[, errno:int]
unix.getpeername(fd:int) → ip:uint32, unix.Errno, port:uint16
Retrieves the remote address of a socket.
unix.recv(fd:int[, bufsiz:int[, flags:int]]) → data:str[, errno:int]
unix.recv(fd:int[, bufsiz:int[, flags:int]]) → data:str, unix.Errno
`flags` can have MSG_{WAITALL,DONTROUTE,PEEK,OOB}, etc.
`flags` can have:
- `MSG_WAITALL`
- `MSG_DONTROUTE`
- `MSG_PEEK`
- `MSG_OOB`
unix.recvfrom(fd:int[, bufsiz:int[, flags:int]])
→ data:str, ip:uint32, port:uint16, errno:int
→ data:str, unix.Errno, ip:uint32, port:uint16
`flags` can have MSG_{WAITALL,DONTROUTE,PEEK,OOB}, etc.
`flags` can have:
unix.send(fd:int, data:str[, flags:int]) → sent:int[, errno:int]
- `MSG_WAITALL`
- `MSG_DONTROUTE`
- `MSG_PEEK`
- `MSG_OOB`
unix.send(fd:int, data:str[, flags:int]) → sent:int, unix.Errno
This is the same as `write` except it has a `flags` argument
that's intended for sockets.
@ -2082,24 +2129,36 @@ UNIX MODULE
`flags` can have `MSG_OOB`, `MSG_DONTROUTE`, or `MSG_NOSIGNAL`.
unix.sendto(fd:int, data:str, ip:int, port:int[, flags:int])
→ sent:int, errno:int
→ sent:int, unix.Errno
This is useful for sending messages over UDP sockets to specific
addresses.
`flags` can have `MSG_OOB`, `MSG_DONTROUTE`, or `MSG_NOSIGNAL`.
unix.shutdown(fd:int, how:int) → errno:int
unix.shutdown(fd:int, how:int) → ok:bool, unix.Errno
Partially closes socket. `how` can be `SHUT_RD`, `SHUT_WR`, or
`SHUT_RDWR`.
unix.sigprocmask(how[, mask]) → oldmask, errno:int
unix.sigprocmask(how:int[, mask:int]) → oldmask:int, unix.Errno
`how` can be `SIG_BLOCK`, `SIG_UNBLOCK`, `SIG_SETMASK`
Manipulates bitset of signals blocked by process.
`how` can be one of:
- `SIG_BLOCK`: bitwise ors `mask` into set of blocked signals
- `SIG_UNBLOCK`: removes bits in `mask` from set of blocked signals
- `SIG_SETMASK`: replaces process signal mask with `mask`
`mask` is a word encoded bitset of signals. Valid signal numbers
start at 1 and vary between platforms. The most famous `SIGKILL`
can't be masked, but if it could, it's assigned the number `9`
across all platforms, so if you wanted to add it to a bitset, you
would say, `1 << 8` or in general terms `1 << (sig - 1)`.
unix.sigaction(sig:int[, handler:func|int[, flags:int[, mask:int]]])
→ oldhandler:func|int, flags:int, mask:int, errno:int
→ oldhandler:func|int, unix.Errno, flags:int, mask:int
`handler` can be `SIG_IGN`, `SIG_DFL`, `intptr_t`, or a Lua
function. `sig` can be `SIGINT`, `SIGQUIT`, `SIGTERM`, etc.
@ -2115,7 +2174,7 @@ UNIX MODULE
It's a good idea to not do too much work in a signal handler.
unix.sigsuspend([mask:int]) → errno:int
unix.sigsuspend([mask]) → false, unix.Errno
Waits for signal to be delivered.
@ -2123,7 +2182,7 @@ UNIX MODULE
system call. `mask` specifies which signals should be blocked.
unix.setitimer(which[, intsec, intmicros, valsec, valmicros])
→ intsec, intns, valsec, valns, errno:int
→ intsec, unix.Errno, intns, valsec, valns
Causes `SIGALRM` signals to be generated at some point(s) in the
future. The `which` parameter should be `ITIMER_REAL`.
@ -2145,17 +2204,17 @@ UNIX MODULE
unix.sigaction(unix.SIGALRM, MyOnSigAlrm, unix.SA_RESETHAND)
unix.setitimer(unix.ITIMER_REAL, 0, 0, 1, 0)
unix.strerrno(errno:int) → str
unix.strerrno(unix.Errno) → str
Turns `errno` code into its symbolic name, e.g. `"EINTR"`. If
`errno` isn't known, this function returns nil.
unix.strerdoc(errno:int) → str
unix.strerdoc(unix.Errno) → str
Turns `errno` code into a descriptive string. If `errno` isn't
known, this function returns nil.
unix.strerror(errno:int) → str
unix.strerror(unix.Errno) → str
Turns `errno` code into longest string describing the error. This
includes the output of both strerrno() and strerror() as well as the
@ -2167,7 +2226,7 @@ UNIX MODULE
Turns platform-specific `sig` code into its name, e.g.
`strsignal(9)` always returns `"SIGKILL"`.
unix.setrlimit(resource:int, soft:int[, hard:int]) → errno:int
unix.setrlimit(resource:int, soft:int[, hard:int]) → ok:bool, unix.Errno
Changes resource limit.
@ -2198,72 +2257,79 @@ UNIX MODULE
127. On most platforms these limits are enforced by the kernel and
as such are inherited by subprocesses.
unix.getrlimit(resource:int) → soft:int, hard:int[, errno:int]
unix.getrlimit(resource:int) → soft:int, unix.Errno, hard:int
Returns information about resource limit.
unix.stat(x) → UnixStat*[, errno:int]
unix.stat(x) → unix.Stat, Errno*
Gets information about file or directory. `x` may be a file or
directory path string, or it may be a file descriptor int that
was made by open().
unix.opendir(path:str) → UnixDir*[, errno:int]
unix.opendir(path:str) → unix.Dir, Errno*
Opens directory for listing its contents.
unix.fdopendir(fd:int) → UnixDir*[, errno:int]
unix.fdopendir(fd:int) → unix.Dir, Errno*
Opens directory for listing its contents, via an fd.
`fd` should be created by `open(path, O_RDONLY|O_DIRECTORY)`. The
returned UnixDir* ownership takes ownership of the file descriptor
returned unix.Dir ownership takes ownership of the file descriptor
and will close it automatically when garbage collected.
UNIX DIR OBJECT
UnixDir* objects are created by opendir() or fdopendir(). The
unix.Dir objects are created by opendir() or fdopendir(). The
following methods are available:
UnixDir:close() → errno:int
unix.Dir:close() → ok:bool, unix.Errno
may be called multiple times
called by the garbage collector too
UnixDir:read() → name:str, kind:int, ino:int, off:int[, errno:int]
unix.Dir:read() → name:str, unix.Errno, kind:int, ino:int, off:int
Returns `nil` if there are no more entries. Or error, `nil` will
be returned and `errno` will be non-nil.
`kind` can be `DT_UNKNOWN`, `DT_REG`, `DT_DIR`, `DT_BLK`,
`DT_LNK`, `DT_CHR`, `DT_FIFO`, or `DT_SOCK`.
`kind` can be any of:
UnixDir:fd() → fd:int[, errno:int]
- `DT_UNKNOWN`
- `DT_REG`
- `DT_DIR`
- `DT_BLK`
- `DT_LNK`
- `DT_CHR`
- `DT_FIFO`
- `DT_SOCK`
unix.Dir:fd() → fd:int, unix.Errno
Returns file descriptor of open directory object.
Returns `EOPNOTSUPP` if using a `/zip/...` path.
Returns `EOPNOTSUPP` if using Windows NT.
UnixDir:tell() → offset:int
unix.Dir:tell() → offset:int
Returns current arbitrary offset into stream.
UnixDir:rewind()
unix.Dir:rewind()
Resets stream back to beginning.
UNIX STAT OBJECT
UnixStat* objects are created by stat() or fstat(). The following
unix.Stat objects are created by stat() or fstat(). The following
methods are available:
UnixStat:size() → bytes:int
unix.Stat:size() → bytes:int
Size of file in bytes.
UnixStat:mode() → mode:int
unix.Stat:mode() → mode:int
Contains file type and permissions.
@ -2280,49 +2346,49 @@ UNIX MODULE
- `(st:mode() & 0170000) == 0120000` means symbolic link
- `(st:mode() & 0170000) == 0140000` means socket
UnixStat:atim() → secs:int, nanos:int
unix.Stat:atim() → secs:int, nanos:int
Size of file in bytes.
UnixStat:uid() → int
unix.Stat:uid() → int
User ID of file owner.
UnixStat:gid() → int
unix.Stat:gid() → int
Group ID of file owner.
UnixStat:mtim() → secs:int, nanos:int
unix.Stat:mtim() → secs:int, nanos:int
Last modified time.
UnixStat:birthtim() → secs:int, nanos:int
unix.Stat:birthtim() → secs:int, nanos:int
Creation time. Note that on Linux this is the mimimum of
atom/mtim/ctim.
UnixStat:ctim() → secs:int, nanos:int
unix.Stat:ctim() → secs:int, nanos:int
Complicated time. Means time file status was last changed on
UNIX. Means creation time on Windows.
UnixStat:blocks() → int
unix.Stat:blocks() → int
Number of blocks used by storage medium.
UnixStat:blksize() → int
unix.Stat:blksize() → int
Block size is usually 4096 for file system files.
UnixStat:dev() → int
unix.Stat:dev() → int
ID of device containing file.
UnixStat:ino() → int
unix.Stat:ino() → int
Inode number.
UnixStat:rdev() → int
unix.Stat:rdev() → int
Device ID (if special file)

View file

@ -531,16 +531,16 @@ luaopen_argon2(lua_State *L)
largon2_push_argon2_variants_table(L);
lua_setfield(L, -2, "variants");
lua_pushstring(L, "3.0.1");
lua_pushliteral(L, "3.0.1");
lua_setfield(L, -2, "_VERSION");
lua_pushstring(L, "Thibault Charbonnier");
lua_pushliteral(L, "Thibault Charbonnier");
lua_setfield(L, -2, "_AUTHOR");
lua_pushstring(L, "MIT");
lua_pushliteral(L, "MIT");
lua_setfield(L, -2, "_LICENSE");
lua_pushstring(L, "https://github.com/thibaultcha/lua-argon2");
lua_pushliteral(L, "https://github.com/thibaultcha/lua-argon2");
lua_setfield(L, -2, "_URL");
return 1;

View file

@ -18,10 +18,14 @@
*/
#include "dsp/scale/cdecimate2xuint8x8.h"
#include "libc/bits/popcnt.h"
#include "libc/calls/calls.h"
#include "libc/calls/struct/rusage.h"
#include "libc/intrin/kprintf.h"
#include "libc/log/check.h"
#include "libc/log/log.h"
#include "libc/macros.internal.h"
#include "libc/mem/mem.h"
#include "libc/nexgen32e/bench.h"
#include "libc/nexgen32e/bsf.h"
#include "libc/nexgen32e/bsr.h"
#include "libc/nexgen32e/crc32.h"
@ -31,6 +35,7 @@
#include "libc/runtime/gc.internal.h"
#include "libc/sock/sock.h"
#include "libc/sysv/consts/af.h"
#include "libc/sysv/consts/rusage.h"
#include "libc/time/time.h"
#include "libc/x/x.h"
#include "net/http/escape.h"
@ -48,6 +53,10 @@
#include "third_party/mbedtls/sha512.h"
#include "tool/net/lfuncs.h"
static int Rdpid(void) {
return rdpid();
}
int LuaGetTime(lua_State *L) {
lua_pushnumber(L, nowl());
return 1;
@ -64,12 +73,12 @@ int LuaRdtsc(lua_State *L) {
}
int LuaGetCpuNode(lua_State *L) {
lua_pushinteger(L, TSC_AUX_NODE(rdpid()));
lua_pushinteger(L, TSC_AUX_NODE(Rdpid()));
return 1;
}
int LuaGetCpuCore(lua_State *L) {
lua_pushinteger(L, TSC_AUX_CORE(rdpid()));
lua_pushinteger(L, TSC_AUX_CORE(Rdpid()));
return 1;
}
@ -560,3 +569,44 @@ void LuaPushUrlView(lua_State *L, struct UrlView *v) {
lua_pushnil(L);
}
}
static int64_t GetInterrupts(void) {
struct rusage ru;
if (!getrusage(RUSAGE_SELF, &ru)) {
return ru.ru_nivcsw;
} else {
return 0;
}
}
int LuaBenchmark(lua_State *L) {
double avgticks;
uint64_t t1, t2;
int64_t interrupts;
int core, iter, count, tries, attempts, maxattempts;
luaL_checktype(L, 1, LUA_TFUNCTION);
count = luaL_optinteger(L, 2, 100);
maxattempts = luaL_optinteger(L, 3, 10);
for (attempts = 0;;) {
sched_yield();
core = TSC_AUX_CORE(Rdpid());
interrupts = GetInterrupts();
for (avgticks = iter = 1; iter < count; ++iter) {
t1 = __startbench();
lua_pushvalue(L, 1);
lua_call(L, 0, 0);
t2 = __endbench();
avgticks += 1. / iter * ((int)(t2 - t1) - avgticks);
}
++attempts;
if (TSC_AUX_CORE(Rdpid()) == core && GetInterrupts() == interrupts) {
break;
} else if (attempts >= maxattempts) {
return luaL_error(L, "system is under too much load to run benchmark");
}
}
lua_pushnumber(L, ConvertTicksToNanos(avgticks));
lua_pushinteger(L, avgticks);
lua_pushinteger(L, attempts);
return 3;
}

View file

@ -11,6 +11,7 @@ int LuaUnix(lua_State *);
int luaopen_argon2(lua_State *);
int luaopen_lsqlite3(lua_State *);
int LuaBenchmark(lua_State *);
int LuaBsf(lua_State *);
int LuaBsr(lua_State *);
int LuaCategorizeIp(lua_State *);

View file

@ -1936,7 +1936,7 @@ static const luaL_Reg sqlitelib[] = {
static void create_meta(lua_State *L, const char *name, const luaL_Reg *lib) {
luaL_newmetatable(L, name);
lua_pushstring(L, "__index");
lua_pushliteral(L, "__index");
lua_pushvalue(L, -2); /* push metatable */
lua_rawset(L, -3); /* metatable.__index = metatable */

File diff suppressed because it is too large Load diff

View file

@ -191,10 +191,10 @@ STATIC_YOINK("zip_uri_support");
#define HeaderEqualCase(H, S) \
SlicesEqualCase(S, strlen(S), HeaderData(H), HeaderLength(H))
// letters not used: EIJNOQWXYinoqwxy
// letters not used: EIJNOQWXYnoqwxy
// digits not used: 0123456789
// puncts not used: !"#$%&'()*+,-./;<=>@[\]^_`{|}~
#define GETOPTS "BSVZabdfghjkmsuvzA:C:D:F:G:H:K:L:M:P:R:T:U:c:e:l:p:r:t:"
#define GETOPTS "BSVZabdfghijkmsuvzA:C:D:F:G:H:K:L:M:P:R:T:U:c:e:l:p:r:t:"
static const uint8_t kGzipHeader[] = {
0x1F, // MAGNUM
@ -379,6 +379,7 @@ static bool checkedmethod;
static bool sslinitialized;
static bool sslfetchverify;
static bool hascontenttype;
static bool interpretermode;
static bool sslclientverify;
static bool connectionclose;
static bool hasonworkerstop;
@ -1185,10 +1186,10 @@ static void ReportWorkerExit(int pid, int ws) {
static void ReportWorkerResources(int pid, struct rusage *ru) {
char *s, *b = 0;
if (logrusage || LOGGABLE(kLogDebug)) {
AppendResourceReport(&b, ru, "\r\n");
AppendResourceReport(&b, ru, "\n");
if (b) {
if ((s = IndentLines(b, appendz(b).i - 1, 0, 1))) {
LOGF(kLogDebug, "(stat) resource report for pid %d\r\n%s", pid, s);
LOGF(kLogDebug, "(stat) resource report for pid %d\n%s", pid, s);
free(s);
}
free(b);
@ -4116,7 +4117,7 @@ static int LuaLog(lua_State *L) {
}
static int LuaEncodeSmth(lua_State *L,
int Encoder(lua_State *, char **, int, char *)) {
int Encoder(lua_State *, char **, int, char *, int)) {
int useoutput = false;
int maxdepth = 64;
char *numformat = "%.14g";
@ -4133,7 +4134,7 @@ static int LuaEncodeSmth(lua_State *L,
numformat = luaL_optstring(L, -1, numformat);
}
lua_settop(L, 1); // keep the passed argument on top
Encoder(L, useoutput ? &outbuf : &p, maxdepth, numformat);
Encoder(L, useoutput ? &outbuf : &p, maxdepth, numformat, -1);
if (useoutput) {
lua_pushnil(L);
} else {
@ -4920,6 +4921,7 @@ static const char *const kDontAutoComplete[] = {
// </SORTED>
static const luaL_Reg kLuaFuncs[] = {
{"Benchmark", LuaBenchmark}, //
{"Bsf", LuaBsf}, //
{"Bsr", LuaBsr}, //
{"CategorizeIp", LuaCategorizeIp}, //
@ -5085,13 +5087,21 @@ static const luaL_Reg kLuaLibs[] = {
};
static void LuaSetArgv(lua_State *L) {
size_t i;
int i, j = -1;
lua_newtable(L);
lua_pushstring(L, __argv[0]);
lua_seti(L, -2, j++);
if (!interpretermode) {
lua_pushstring(L, "/zip/.init.lua");
lua_seti(L, -2, j++);
}
for (i = optind; i < __argc; ++i) {
lua_pushstring(L, __argv[i]);
lua_seti(L, -2, i - optind + 1);
lua_seti(L, -2, j++);
}
lua_setglobal(L, "argv");
lua_pushvalue(L, -1);
lua_setglobal(L, "argv"); // deprecated
lua_setglobal(L, "arg");
}
static void LuaSetConstant(lua_State *L, const char *s, long x) {
@ -5133,10 +5143,111 @@ static void LuaStart(void) {
#endif
}
static bool ShouldAutocomplete(const char *s) {
int c, m, l, r;
l = 0;
r = ARRAYLEN(kDontAutoComplete) - 1;
while (l <= r) {
m = (l + r) >> 1;
c = strcmp(kDontAutoComplete[m], s);
if (c < 0) {
l = m + 1;
} else if (c > 0) {
r = m - 1;
} else {
return false;
}
}
return true;
}
static void HandleCompletions(const char *p, linenoiseCompletions *c) {
size_t i, j;
for (j = i = 0; i < c->len; ++i) {
if (ShouldAutocomplete(c->cvec[i])) {
c->cvec[j++] = c->cvec[i];
} else {
free(c->cvec[i]);
}
}
c->len = j;
}
static void LuaPrint(lua_State *L) {
int i, n;
char *b = 0;
const char *s;
n = lua_gettop(L);
for (i = 1; i <= n; i++) {
if (i > 1) appendw(&b, '\t');
LuaEncodeLuaData(L, &b, 64, "g", i);
}
appendw(&b, '\n');
WRITE(1, b, appendz(b).i);
free(b);
}
static void LuaInterpreter(lua_State *L) {
int i, n, sig, status;
const char *script;
if (optind < __argc) {
script = __argv[optind];
if (!strcmp(script, "-")) script = 0;
if ((status = luaL_loadfile(L, script)) == LUA_OK) {
lua_getglobal(L, "arg");
n = luaL_len(L, -1);
luaL_checkstack(L, n + 3, "too many script args");
for (i = 1; i <= n; i++) lua_rawgeti(L, -i, i);
lua_remove(L, -i); // remove arg table from stack
status = lua_runchunk(L, n, LUA_MULTRET);
}
lua_report(L, status);
} else {
lua_repl_blocking = true;
lua_repl_completions_callback = HandleCompletions;
lua_initrepl(GL, "redbean");
if (lua_repl_isterminal) {
linenoiseEnableRawMode(0);
}
for (;;) {
status = lua_loadline(L);
write(1, "\n", 1);
if (status == -1) break; // eof
if (status == -2) {
if (errno == EINTR) {
if ((sig = linenoiseGetInterrupt())) {
raise(sig);
}
}
fprintf(stderr, "i/o error: %m\n");
exit(1);
}
if (status == LUA_OK) {
status = lua_runchunk(GL, 0, LUA_MULTRET);
}
if (status == LUA_OK) {
LuaPrint(GL);
} else {
lua_report(GL, status);
}
}
linenoiseDisableRawMode();
lua_freerepl();
lua_settop(GL, 0); // clear stack
if ((sig = linenoiseGetInterrupt())) {
raise(sig);
}
}
}
static void LuaInit(void) {
#ifndef STATIC
lua_State *L = GL;
LuaSetArgv(L);
if (interpretermode) {
LuaInterpreter(L);
exit(0);
}
if (LuaRunAsset("/.init.lua", true)) {
hasonhttprequest = IsHookDefined("OnHttpRequest");
hasonclientconnection = IsHookDefined("OnClientConnection");
@ -6369,34 +6480,6 @@ static void RestoreApe(void) {
}
}
static bool ShouldAutocomplete(const char *s) {
int c, m, l, r;
l = 0;
r = ARRAYLEN(kDontAutoComplete) - 1;
while (l <= r) {
m = (l + r) >> 1;
c = strcmp(kDontAutoComplete[m], s);
if (c < 0) {
l = m + 1;
} else if (c > 0) {
r = m - 1;
} else {
return false;
}
}
return true;
}
static void HandleCompletions(const char *p, linenoiseCompletions *c) {
size_t i, j;
for (j = i = 0; i < c->len; ++i) {
if (ShouldAutocomplete(c->cvec[i])) {
c->cvec[j++] = c->cvec[i];
}
}
c->len = j;
}
static int HandleReadline(void) {
int status;
for (;;) {
@ -6405,7 +6488,7 @@ static int HandleReadline(void) {
if (status == -1) {
OnTerm(SIGHUP); // eof
INFOF("got repl eof");
write(1, "\r\n", 2);
write(1, "\n", 1);
return -1;
} else if (errno == EINTR) {
errno = 0;
@ -6419,14 +6502,14 @@ static int HandleReadline(void) {
return -1;
}
}
write(1, "\r\n", 2);
write(1, "\n", 1);
linenoiseDisableRawMode();
LUA_REPL_LOCK;
if (status == LUA_OK) {
status = lua_runchunk(GL, 0, LUA_MULTRET);
}
if (status == LUA_OK) {
lua_l_print(GL);
LuaPrint(GL);
} else {
lua_report(GL, status);
}
@ -6781,6 +6864,7 @@ static void GetOpts(int argc, char *argv[]) {
#ifndef STATIC
CASE('e', LuaEvalCode(optarg));
CASE('F', LuaEvalFile(optarg));
CASE('i', interpretermode = true);
CASE('E', leakcrashreports = true);
CASE('A', storeasset = true; StorePath(optarg));
#endif