mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-01-31 03:27:39 +00:00
Do code cleanup use duff device linenoise i/o
This commit is contained in:
parent
6ff46ca373
commit
2f56ebfe78
79 changed files with 1393 additions and 1484 deletions
|
@ -14,11 +14,11 @@
|
|||
|
||||
int main(int argc, char *argv[]) {
|
||||
int c, n;
|
||||
char a[22];
|
||||
char a[22], *p;
|
||||
if ((c = GetCpuCount())) {
|
||||
n = int64toarray_radix10(c, a);
|
||||
a[n++] = '\n';
|
||||
return write(1, a, n) == n ? 0 : 1;
|
||||
p = FormatInt64(a, c);
|
||||
*p++ = '\n';
|
||||
return write(1, a, p - a) == p - a ? 0 : 1;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@ int main(int argc, char *argv[]) {
|
|||
}
|
||||
}
|
||||
if (isprime) {
|
||||
int64toarray_radix10(i, buf);
|
||||
FormatInt64(buf, i);
|
||||
fputs(buf, stdout);
|
||||
fputc('\n', stdout);
|
||||
if (k++ % 100 == 0) {
|
||||
|
|
|
@ -47,8 +47,8 @@ void PrintMetric(const char *name, long double d) {
|
|||
mils = fmodl(d * 1000, 1000);
|
||||
mics = fmodl(d * 1000000, 1000);
|
||||
p = stpcpy(p, name), *p++ = '\t';
|
||||
p += int64toarray_radix10(mins, p), *p++ = 'm';
|
||||
p += int64toarray_radix10(secs, p), *p++ = '.';
|
||||
p = FormatInt64(p, mins), *p++ = 'm';
|
||||
p = FormatInt64(p, secs), *p++ = '.';
|
||||
*p++ = '0' + mils / 100;
|
||||
*p++ = '0' + mils / 10 % 10;
|
||||
*p++ = '0' + mils % 10;
|
||||
|
|
|
@ -28,31 +28,30 @@
|
|||
#include "libc/sysv/consts/ok.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
static noasan bool IsExePath(const char *s, size_t n) {
|
||||
static bool IsExePath(const char *s, size_t n) {
|
||||
return n >= 4 && (READ32LE(s + n - 4) == READ32LE(".exe") ||
|
||||
READ32LE(s + n - 4) == READ32LE(".EXE"));
|
||||
}
|
||||
|
||||
static noasan bool IsComPath(const char *s, size_t n) {
|
||||
static bool IsComPath(const char *s, size_t n) {
|
||||
return n >= 4 && (READ32LE(s + n - 4) == READ32LE(".com") ||
|
||||
READ32LE(s + n - 4) == READ32LE(".COM"));
|
||||
}
|
||||
|
||||
static noasan bool IsComDbgPath(const char *s, size_t n) {
|
||||
static bool IsComDbgPath(const char *s, size_t n) {
|
||||
return n >= 8 && (READ64LE(s + n - 8) == READ64LE(".com.dbg") ||
|
||||
READ64LE(s + n - 8) == READ64LE(".COM.DBG"));
|
||||
}
|
||||
|
||||
static noasan bool AccessCommand(const char *name,
|
||||
char path[hasatleast PATH_MAX], size_t namelen,
|
||||
int *err, const char *suffix, size_t pathlen) {
|
||||
static bool AccessCommand(const char *name, char path[hasatleast PATH_MAX],
|
||||
size_t namelen, int *err, const char *suffix,
|
||||
size_t pathlen) {
|
||||
size_t suffixlen;
|
||||
suffixlen = strlen(suffix);
|
||||
if (pathlen + 1 + namelen + suffixlen + 1 > PATH_MAX) return false;
|
||||
if (pathlen && (path[pathlen - 1] != '/' && path[pathlen - 1] != '\\')) {
|
||||
path[pathlen] = !IsWindows() ? '/'
|
||||
: memchr(path, '\\', pathlen) ? '\\'
|
||||
: '/';
|
||||
path[pathlen] =
|
||||
!IsWindows() ? '/' : memchr(path, '\\', pathlen) ? '\\' : '/';
|
||||
pathlen++;
|
||||
}
|
||||
memcpy(path + pathlen, name, namelen);
|
||||
|
@ -62,8 +61,8 @@ static noasan bool AccessCommand(const char *name,
|
|||
return false;
|
||||
}
|
||||
|
||||
static noasan bool SearchPath(const char *name, char path[hasatleast PATH_MAX],
|
||||
size_t namelen, int *err, const char *suffix) {
|
||||
static bool SearchPath(const char *name, char path[hasatleast PATH_MAX],
|
||||
size_t namelen, int *err, const char *suffix) {
|
||||
char sep;
|
||||
size_t i;
|
||||
const char *p;
|
||||
|
@ -87,10 +86,9 @@ static noasan bool SearchPath(const char *name, char path[hasatleast PATH_MAX],
|
|||
return false;
|
||||
}
|
||||
|
||||
static noasan bool FindCommand(const char *name,
|
||||
char pathbuf[hasatleast PATH_MAX],
|
||||
size_t namelen, bool priorityonly,
|
||||
const char *suffix, int *err) {
|
||||
static bool FindCommand(const char *name, char pathbuf[hasatleast PATH_MAX],
|
||||
size_t namelen, bool priorityonly, const char *suffix,
|
||||
int *err) {
|
||||
if (priorityonly &&
|
||||
(memchr(name, '/', namelen) || memchr(name, '\\', namelen))) {
|
||||
pathbuf[0] = 0;
|
||||
|
@ -107,15 +105,13 @@ static noasan bool FindCommand(const char *name,
|
|||
SearchPath(name, pathbuf, namelen, err, suffix);
|
||||
}
|
||||
|
||||
static noasan bool FindVerbatim(const char *name,
|
||||
char pathbuf[hasatleast PATH_MAX],
|
||||
size_t namelen, bool priorityonly, int *err) {
|
||||
static bool FindVerbatim(const char *name, char pathbuf[hasatleast PATH_MAX],
|
||||
size_t namelen, bool priorityonly, int *err) {
|
||||
return FindCommand(name, pathbuf, namelen, priorityonly, "", err);
|
||||
}
|
||||
|
||||
static noasan bool FindSuffixed(const char *name,
|
||||
char pathbuf[hasatleast PATH_MAX],
|
||||
size_t namelen, bool priorityonly, int *err) {
|
||||
static bool FindSuffixed(const char *name, char pathbuf[hasatleast PATH_MAX],
|
||||
size_t namelen, bool priorityonly, int *err) {
|
||||
return !IsExePath(name, namelen) && !IsComPath(name, namelen) &&
|
||||
!IsComDbgPath(name, namelen) &&
|
||||
(FindCommand(name, pathbuf, namelen, priorityonly, ".com", err) ||
|
||||
|
@ -131,7 +127,7 @@ static noasan bool FindSuffixed(const char *name,
|
|||
* @asyncsignalsafe
|
||||
* @vforksafe
|
||||
*/
|
||||
noasan char *commandv(const char *name, char pathbuf[hasatleast PATH_MAX]) {
|
||||
char *commandv(const char *name, char pathbuf[hasatleast PATH_MAX]) {
|
||||
int e, f;
|
||||
char *res;
|
||||
size_t namelen;
|
||||
|
|
|
@ -73,7 +73,7 @@ int ioctl_tcgets(int fd, ...) {
|
|||
tio = va_arg(va, struct termios *);
|
||||
va_end(va);
|
||||
if (fd >= 0) {
|
||||
if (!tio) {
|
||||
if (!tio || (IsAsan() && !__asan_is_valid(tio, sizeof(*tio)))) {
|
||||
rc = efault();
|
||||
} else if (fd < g_fds.n && g_fds.p[fd].kind == kFdZip) {
|
||||
rc = enotty();
|
||||
|
|
|
@ -68,7 +68,7 @@ int ioctl_tcsets(int fd, uint64_t request, ...) {
|
|||
va_start(va, request);
|
||||
tio = va_arg(va, const struct termios *);
|
||||
va_end(va);
|
||||
if (!tio) {
|
||||
if (!tio || (IsAsan() && !__asan_is_valid(tio, sizeof(*tio)))) {
|
||||
rc = efault();
|
||||
} else if (fd >= 0) {
|
||||
if (fd < g_fds.n && g_fds.p[fd].kind == kFdZip) {
|
||||
|
|
|
@ -29,7 +29,7 @@ errno_t ptsname_r(int fd, char *buf, size_t size) {
|
|||
if (size) {
|
||||
if (!buf) return einval();
|
||||
if (ioctl(fd, TIOCGPTN, &pty) == -1) return errno;
|
||||
int64toarray_radix10(pty, stpcpy(tb, "/dev/pts/"));
|
||||
FormatInt32(stpcpy(tb, "/dev/pts/"), pty);
|
||||
if (strlen(tb) + 1 >= size) return (errno = ERANGE);
|
||||
stpcpy(buf, tb);
|
||||
/* TODO(jart): OpenBSD OMG */
|
||||
|
|
|
@ -5,10 +5,10 @@
|
|||
#include "libc/calls/struct/sigaction.h"
|
||||
#include "libc/calls/struct/stat.h"
|
||||
|
||||
#define _KERNTRACE 0 /* not configurable w/ flag yet */
|
||||
#define _POLLTRACE 0 /* not configurable w/ flag yet */
|
||||
#define _KERNTRACE 1 /* not configurable w/ flag yet */
|
||||
#define _POLLTRACE 1 /* not configurable w/ flag yet */
|
||||
#define _DATATRACE 1 /* not configurable w/ flag yet */
|
||||
#define _NTTRACE 0 /* not configurable w/ flag yet */
|
||||
#define _NTTRACE 1 /* not configurable w/ flag yet */
|
||||
|
||||
#define STRACE_PROLOGUE "%rSYS %5P %'18T "
|
||||
|
||||
|
|
|
@ -64,7 +64,7 @@ static int ttyname_linux(int fd, char *buf, size_t size) {
|
|||
struct stat st1, st2;
|
||||
if (!isatty(fd)) return errno;
|
||||
char name[PATH_MAX];
|
||||
int64toarray_radix10(fd, stpcpy(name, "/proc/self/fd/"));
|
||||
FormatInt32(stpcpy(name, "/proc/self/fd/"), fd);
|
||||
ssize_t got;
|
||||
got = readlink(name, buf, size);
|
||||
if (got == -1) return errno;
|
||||
|
|
|
@ -73,10 +73,10 @@ int getnameinfo(const struct sockaddr *addr, socklen_t addrlen, char *name,
|
|||
|
||||
ip = (uint8_t *)&(((struct sockaddr_in *)addr)->sin_addr);
|
||||
p = rdomain;
|
||||
p += int64toarray_radix10(ip[3], p), *p++ = '.';
|
||||
p += int64toarray_radix10(ip[2], p), *p++ = '.';
|
||||
p += int64toarray_radix10(ip[1], p), *p++ = '.';
|
||||
p += int64toarray_radix10(ip[0], p), stpcpy(p, ".in-addr.arpa");
|
||||
p = FormatUint32(p, ip[3]), *p++ = '.';
|
||||
p = FormatUint32(p, ip[2]), *p++ = '.';
|
||||
p = FormatUint32(p, ip[1]), *p++ = '.';
|
||||
p = FormatUint32(p, ip[0]), stpcpy(p, ".in-addr.arpa");
|
||||
info[0] = '\0';
|
||||
if (name != NULL && namelen != 0) {
|
||||
if ((flags & NI_NUMERICHOST) && (flags & NI_NAMEREQD)) return EAI_NONAME;
|
||||
|
|
|
@ -147,12 +147,12 @@ hidden int __fmt(void *fn, void *arg, const char *format, va_list va) {
|
|||
continue;
|
||||
} else if (format[1] == 'd') { /* FAST PATH: PLAIN INTEGER */
|
||||
d = va_arg(va, int);
|
||||
if (out(ibuf, arg, int64toarray_radix10(d, ibuf)) == -1) return -1;
|
||||
if (out(ibuf, arg, FormatInt64(ibuf, d) - ibuf) == -1) return -1;
|
||||
format += 2;
|
||||
continue;
|
||||
} else if (format[1] == 'u') { /* FAST PATH: PLAIN UNSIGNED */
|
||||
u = va_arg(va, unsigned);
|
||||
if (out(ibuf, arg, uint64toarray_radix10(u, ibuf)) == -1) return -1;
|
||||
if (out(ibuf, arg, FormatUint64(ibuf, u) - ibuf) == -1) return -1;
|
||||
format += 2;
|
||||
continue;
|
||||
} else if (format[1] == 'x') { /* FAST PATH: PLAIN HEX */
|
||||
|
@ -167,12 +167,12 @@ hidden int __fmt(void *fn, void *arg, const char *format, va_list va) {
|
|||
continue;
|
||||
} else if (format[1] == 'l' && format[2] == 'd') {
|
||||
ld = va_arg(va, long); /* FAST PATH: PLAIN LONG */
|
||||
if (out(ibuf, arg, int64toarray_radix10(ld, ibuf)) == -1) return -1;
|
||||
if (out(ibuf, arg, FormatInt64(ibuf, ld) - ibuf) == -1) return -1;
|
||||
format += 3;
|
||||
continue;
|
||||
} else if (format[1] == 'l' && format[2] == 'u') {
|
||||
lu = va_arg(va, unsigned long); /* FAST PATH: PLAIN UNSIGNED LONG */
|
||||
if (out(ibuf, arg, uint64toarray_radix10(lu, ibuf)) == -1) return -1;
|
||||
if (out(ibuf, arg, FormatUint64(ibuf, lu) - ibuf) == -1) return -1;
|
||||
format += 3;
|
||||
continue;
|
||||
} else if (format[1] == '.' && format[2] == '*' && format[3] == 's') {
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
* @param p needs at least 12 bytes
|
||||
* @return pointer to nul byte
|
||||
*/
|
||||
dontinline char *FormatUint32(char p[static 12], uint32_t x) {
|
||||
dontinline char *FormatUint32(char p[hasatleast 12], uint32_t x) {
|
||||
char t;
|
||||
size_t i, a, b;
|
||||
i = 0;
|
||||
|
@ -49,7 +49,7 @@ dontinline char *FormatUint32(char p[static 12], uint32_t x) {
|
|||
* @param p needs at least 12 bytes
|
||||
* @return pointer to nul byte
|
||||
*/
|
||||
char *FormatInt32(char p[static 12], int32_t x) {
|
||||
char *FormatInt32(char p[hasatleast 12], int32_t x) {
|
||||
if (x < 0) *p++ = '-', x = -(uint32_t)x;
|
||||
return FormatUint32(p, x);
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
||||
│ Copyright 2021 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
|
@ -16,34 +16,32 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/alg/reverse.internal.h"
|
||||
#include "libc/fmt/conv.h"
|
||||
#include "libc/fmt/itoa.h"
|
||||
#include "libc/limits.h"
|
||||
|
||||
/**
|
||||
* Converts unsigned 64-bit integer to string.
|
||||
* @param a needs at least 21 bytes
|
||||
* @return bytes written w/o nul
|
||||
* Converts unsigned 32-bit integer to octal string.
|
||||
*
|
||||
* @param p needs at least 12 bytes
|
||||
* @param z ensures it starts with zero
|
||||
* @return pointer to nul byte
|
||||
*/
|
||||
dontinline size_t uint64toarray_radix10(uint64_t i, char a[hasatleast 21]) {
|
||||
size_t j = 0;
|
||||
char *FormatOctal32(char p[hasatleast 13], uint32_t x, bool z) {
|
||||
char t;
|
||||
size_t i, a, b;
|
||||
i = 0;
|
||||
z = x && z;
|
||||
do {
|
||||
a[j++] = i % 10 + '0';
|
||||
i = i / 10;
|
||||
} while (i > 0);
|
||||
a[j] = '\0';
|
||||
reverse(a, j);
|
||||
return j;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts signed 64-bit integer to string.
|
||||
* @param a needs at least 21 bytes
|
||||
* @return bytes written w/o nul
|
||||
*/
|
||||
size_t int64toarray_radix10(int64_t i, char a[hasatleast 21]) {
|
||||
if (i >= 0) return uint64toarray_radix10(i, a);
|
||||
*a++ = '-';
|
||||
return 1 + uint64toarray_radix10(-(uint64_t)i, a);
|
||||
p[i++] = x % 8 + '0';
|
||||
x = x / 8;
|
||||
} while (x > 0);
|
||||
if (z) p[i++] = '0';
|
||||
p[i] = '\0';
|
||||
if (i) {
|
||||
for (a = 0, b = i - 1; a < b; ++a, --b) {
|
||||
t = p[a];
|
||||
p[a] = p[b];
|
||||
p[b] = t;
|
||||
}
|
||||
}
|
||||
return p + i;
|
||||
}
|
47
libc/fmt/formatoctal64.c
Normal file
47
libc/fmt/formatoctal64.c
Normal file
|
@ -0,0 +1,47 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2021 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/fmt/itoa.h"
|
||||
|
||||
/**
|
||||
* Converts unsigned 64-bit integer to octal string.
|
||||
*
|
||||
* @param p needs at least 12 bytes
|
||||
* @param z ensures it starts with zero
|
||||
* @return pointer to nul byte
|
||||
*/
|
||||
char *FormatOctal64(char p[hasatleast 24], uint64_t x, bool z) {
|
||||
char t;
|
||||
size_t i, a, b;
|
||||
i = 0;
|
||||
z = x && z;
|
||||
do {
|
||||
p[i++] = x % 8 + '0';
|
||||
x = x / 8;
|
||||
} while (x > 0);
|
||||
if (z) p[i++] = '0';
|
||||
p[i] = '\0';
|
||||
if (i) {
|
||||
for (a = 0, b = i - 1; a < b; ++a, --b) {
|
||||
t = p[a];
|
||||
p[a] = p[b];
|
||||
p[b] = t;
|
||||
}
|
||||
}
|
||||
return p + i;
|
||||
}
|
|
@ -2,19 +2,6 @@
|
|||
#define COSMOPOLITAN_LIBC_FMT_ITOA_H_
|
||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
/*───────────────────────────────────────────────────────────────────────────│─╗
|
||||
│ cosmopolitan § integer conversion ─╬─│┼
|
||||
╚────────────────────────────────────────────────────────────────────────────│─╝
|
||||
FASTEST + TINY
|
||||
|
||||
- uint64toarray_radix10(0x31337, a) l: 68 (20ns) m: 112 (33ns)
|
||||
- int64toarray_radix10(0x31337, a) l: 69 (20ns) m: 134 (39ns)
|
||||
|
||||
FAST + AWESOME
|
||||
|
||||
- snprintf(a, sizeof(a), "%d", 0x31337) l: 199 (58ns) m: 421 (123ns)
|
||||
- uint128toarray_radix10(0x31337, a) l: 93 (27ns) m: 141 (41ns)
|
||||
- int128toarray_radix10(0x31337, a) l: 96 (28ns) m: 173 (51ns) */
|
||||
|
||||
unsigned LengthInt64(int64_t) pureconst;
|
||||
unsigned LengthUint64(uint64_t) pureconst;
|
||||
|
@ -26,8 +13,8 @@ char *FormatInt64(char[hasatleast 21], int64_t);
|
|||
char *FormatUint64(char[hasatleast 21], uint64_t);
|
||||
char *FormatInt64Thousands(char[hasatleast 27], int64_t);
|
||||
char *FormatUint64Thousands(char[hasatleast 27], uint64_t);
|
||||
size_t int64toarray_radix10(int64_t, char[hasatleast 21]);
|
||||
size_t uint64toarray_radix10(uint64_t, char[hasatleast 21]);
|
||||
char *FormatOctal32(char[hasatleast 13], uint32_t, bool);
|
||||
char *FormatOctal64(char[hasatleast 24], uint64_t, bool);
|
||||
size_t uint64toarray_radix16(uint64_t, char[hasatleast 17]);
|
||||
size_t uint64toarray_fixed16(uint64_t, char[hasatleast 17], uint8_t);
|
||||
size_t uint64toarray_radix8(uint64_t, char[hasatleast 24]);
|
||||
|
|
|
@ -722,7 +722,7 @@ static void __asan_report_memory_origin(const unsigned char *addr, int size,
|
|||
if (_base <= addr && addr < _end) {
|
||||
__asan_report_memory_origin_image((intptr_t)addr, size);
|
||||
} else if (IsAutoFrame((intptr_t)addr >> 16)) {
|
||||
__asan_report_memory_origin_heap(addr, size);
|
||||
/* __asan_report_memory_origin_heap(addr, size); */
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1202,8 +1202,9 @@ void __asan_unregister_globals(struct AsanGlobal g[], int n) {
|
|||
void __asan_evil(uint8_t *addr, int size, const char *s1, const char *s2) {
|
||||
struct AsanTrace tr;
|
||||
__asan_rawtrace(&tr, __builtin_frame_address(0));
|
||||
kprintf("WARNING: ASAN error during %s bad %d byte %s at %p bt %p %p %p %p%n",
|
||||
s1, size, s2, addr, tr.p[0], tr.p[1], tr.p[2], tr.p[3]);
|
||||
kprintf(
|
||||
"WARNING: ASAN error during %s bad %d byte %s at %x bt %x %x %x %x %x%n",
|
||||
s1, size, s2, addr, tr.p[0], tr.p[1], tr.p[2], tr.p[3], tr.p[4], tr.p[5]);
|
||||
}
|
||||
|
||||
void __asan_report_load(uint8_t *addr, int size) {
|
||||
|
|
|
@ -149,7 +149,7 @@ void(bzero)(void *p, size_t n) {
|
|||
} while (n);
|
||||
}
|
||||
} else if (IsTiny()) {
|
||||
asm("rep stosb" : "+D"(b), "+c"(n), "=m"(*(char(*)[n])b) : "0"(p), "a"(0));
|
||||
asm("rep stosb" : "+D"(b), "+c"(n), "=m"(*(char(*)[n])b) : "a"(0));
|
||||
return;
|
||||
} else if (X86_HAVE(AVX)) {
|
||||
bzero_avx(b, n);
|
||||
|
|
|
@ -116,7 +116,7 @@ o//libc/intrin/memcmp.o \
|
|||
o//libc/intrin/memset.o \
|
||||
o//libc/intrin/memmove.o: \
|
||||
OVERRIDE_CFLAGS += \
|
||||
-O2
|
||||
-O2 -finline
|
||||
|
||||
o/$(MODE)/libc/intrin/bzero.o \
|
||||
o/$(MODE)/libc/intrin/memcmp.o \
|
||||
|
|
|
@ -56,11 +56,11 @@ relegated int(AttachDebugger)(intptr_t continuetoaddr) {
|
|||
struct StackFrame *bp;
|
||||
char pidstr[11], breakcmd[40];
|
||||
const char *se, *elf, *gdb, *rewind, *layout;
|
||||
__restore_tty();
|
||||
if (IsGenuineCosmo() || !(gdb = GetGdbPath()) || !isatty(0) || !isatty(1) ||
|
||||
(ttyfd = open(_PATH_TTY, O_RDWR | O_CLOEXEC)) == -1) {
|
||||
return -1;
|
||||
}
|
||||
__restore_tty(ttyfd);
|
||||
ksnprintf(pidstr, sizeof(pidstr), "%u", getpid());
|
||||
layout = "layout asm";
|
||||
if ((elf = FindDebugBinary())) {
|
||||
|
|
|
@ -59,11 +59,12 @@ static int PrintBacktraceUsingAddr2line(int fd, const struct StackFrame *bp) {
|
|||
size_t i, j, gi;
|
||||
int ws, pid, pipefds[2];
|
||||
struct Garbages *garbage;
|
||||
sigset_t chldmask, savemask;
|
||||
const struct StackFrame *frame;
|
||||
char *debugbin, *p1, *p2, *p3, *addr2line;
|
||||
char buf[kBacktraceBufSize], *argv[kBacktraceMaxFrames];
|
||||
|
||||
return -1;
|
||||
|
||||
if (!(debugbin = FindDebugBinary())) {
|
||||
return -1;
|
||||
}
|
||||
|
@ -105,19 +106,15 @@ static int PrintBacktraceUsingAddr2line(int fd, const struct StackFrame *bp) {
|
|||
argv[i++] = buf + j;
|
||||
buf[j++] = '0';
|
||||
buf[j++] = 'x';
|
||||
j += uint64toarray_radix16(addr, buf + j) + 1;
|
||||
j += uint64toarray_radix16(addr - 1, buf + j) + 1;
|
||||
}
|
||||
argv[i++] = NULL;
|
||||
sigemptyset(&chldmask);
|
||||
sigaddset(&chldmask, SIGCHLD);
|
||||
sigprocmask(SIG_BLOCK, &chldmask, &savemask);
|
||||
pipe(pipefds);
|
||||
if (!(pid = vfork())) {
|
||||
sigprocmask(SIG_SETMASK, &savemask, NULL);
|
||||
dup2(pipefds[1], 1);
|
||||
if (pipefds[0] != 1) close(pipefds[0]);
|
||||
if (pipefds[1] != 1) close(pipefds[1]);
|
||||
execvp(addr2line, argv);
|
||||
execve(addr2line, argv, environ);
|
||||
_exit(127);
|
||||
}
|
||||
close(pipefds[1]);
|
||||
|
@ -150,7 +147,6 @@ static int PrintBacktraceUsingAddr2line(int fd, const struct StackFrame *bp) {
|
|||
if (errno == EINTR) continue;
|
||||
return -1;
|
||||
}
|
||||
sigprocmask(SIG_SETMASK, &savemask, NULL);
|
||||
if (WIFEXITED(ws) && !WEXITSTATUS(ws)) {
|
||||
return 0;
|
||||
} else {
|
|
@ -35,7 +35,7 @@
|
|||
*/
|
||||
relegated void ___check_fail_ndebug(uint64_t want, uint64_t got,
|
||||
const char *opchar) {
|
||||
__restore_tty(1);
|
||||
__restore_tty();
|
||||
kprintf("%n%serror: %s: check failed: 0x%x %s 0x%x (%s)%n",
|
||||
!__nocolor ? "\e[J" : "", program_invocation_name, want, opchar, got,
|
||||
strerror(errno));
|
||||
|
|
|
@ -35,7 +35,7 @@ relegated wontreturn void __die(void) {
|
|||
int rc;
|
||||
static bool once;
|
||||
if (_lockcmpxchg(&once, false, true)) {
|
||||
__restore_tty(1);
|
||||
__restore_tty();
|
||||
if (IsDebuggerPresent(false)) {
|
||||
DebugBreak();
|
||||
}
|
||||
|
|
|
@ -10,12 +10,11 @@ COSMOPOLITAN_C_START_
|
|||
extern hidden bool __nocolor;
|
||||
extern hidden int kCrashSigs[7];
|
||||
extern hidden bool g_isrunningundermake;
|
||||
extern hidden struct termios g_oldtermios;
|
||||
extern hidden struct sigaction g_oldcrashacts[7];
|
||||
|
||||
void __start_fatal(const char *, int) hidden;
|
||||
void __oncrash(int, struct siginfo *, struct ucontext *) relegated;
|
||||
void __restore_tty(int);
|
||||
void __restore_tty(void);
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
|
|
|
@ -77,15 +77,15 @@ extern unsigned __log_level; /* log level for runtime check */
|
|||
// log a message with the specified log level (not checking if LOGGABLE)
|
||||
#define LOGF(LEVEL, FMT, ...) \
|
||||
do { \
|
||||
++g_ftrace; \
|
||||
flogf(LEVEL, __FILE__, __LINE__, NULL, FMT, ##__VA_ARGS__); \
|
||||
--g_ftrace; \
|
||||
flogf(LEVEL, __FILE__, __LINE__, NULL, FMT, ##__VA_ARGS__); \
|
||||
++g_ftrace; \
|
||||
} while (0)
|
||||
|
||||
// die with an error message without backtrace and debugger invocation
|
||||
#define DIEF(FMT, ...) \
|
||||
do { \
|
||||
++g_ftrace; \
|
||||
--g_ftrace; \
|
||||
flogf(kLogError, __FILE__, __LINE__, NULL, FMT, ##__VA_ARGS__); \
|
||||
exit(1); \
|
||||
unreachable; \
|
||||
|
@ -93,7 +93,7 @@ extern unsigned __log_level; /* log level for runtime check */
|
|||
|
||||
#define FATALF(FMT, ...) \
|
||||
do { \
|
||||
++g_ftrace; \
|
||||
--g_ftrace; \
|
||||
ffatalf(kLogFatal, __FILE__, __LINE__, NULL, FMT, ##__VA_ARGS__); \
|
||||
unreachable; \
|
||||
} while (0)
|
||||
|
@ -101,78 +101,78 @@ extern unsigned __log_level; /* log level for runtime check */
|
|||
#define ERRORF(FMT, ...) \
|
||||
do { \
|
||||
if (LOGGABLE(kLogError)) { \
|
||||
++g_ftrace; \
|
||||
flogf(kLogError, __FILE__, __LINE__, NULL, FMT, ##__VA_ARGS__); \
|
||||
--g_ftrace; \
|
||||
flogf(kLogError, __FILE__, __LINE__, NULL, FMT, ##__VA_ARGS__); \
|
||||
++g_ftrace; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define WARNF(FMT, ...) \
|
||||
do { \
|
||||
if (LOGGABLE(kLogWarn)) { \
|
||||
++g_ftrace; \
|
||||
flogf(kLogWarn, __FILE__, __LINE__, NULL, FMT, ##__VA_ARGS__); \
|
||||
--g_ftrace; \
|
||||
flogf(kLogWarn, __FILE__, __LINE__, NULL, FMT, ##__VA_ARGS__); \
|
||||
++g_ftrace; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define INFOF(FMT, ...) \
|
||||
do { \
|
||||
if (LOGGABLE(kLogInfo)) { \
|
||||
++g_ftrace; \
|
||||
flogf(kLogInfo, __FILE__, __LINE__, NULL, FMT, ##__VA_ARGS__); \
|
||||
--g_ftrace; \
|
||||
flogf(kLogInfo, __FILE__, __LINE__, NULL, FMT, ##__VA_ARGS__); \
|
||||
++g_ftrace; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define VERBOSEF(FMT, ...) \
|
||||
do { \
|
||||
if (LOGGABLE(kLogVerbose)) { \
|
||||
++g_ftrace; \
|
||||
fverbosef(kLogVerbose, __FILE__, __LINE__, NULL, FMT, ##__VA_ARGS__); \
|
||||
--g_ftrace; \
|
||||
fverbosef(kLogVerbose, __FILE__, __LINE__, NULL, FMT, ##__VA_ARGS__); \
|
||||
++g_ftrace; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define DEBUGF(FMT, ...) \
|
||||
do { \
|
||||
if (UNLIKELY(LOGGABLE(kLogDebug))) { \
|
||||
++g_ftrace; \
|
||||
fdebugf(kLogDebug, __FILE__, __LINE__, NULL, FMT, ##__VA_ARGS__); \
|
||||
--g_ftrace; \
|
||||
fdebugf(kLogDebug, __FILE__, __LINE__, NULL, FMT, ##__VA_ARGS__); \
|
||||
++g_ftrace; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define NOISEF(FMT, ...) \
|
||||
do { \
|
||||
if (UNLIKELY(LOGGABLE(kLogNoise))) { \
|
||||
++g_ftrace; \
|
||||
fnoisef(kLogNoise, __FILE__, __LINE__, NULL, FMT, ##__VA_ARGS__); \
|
||||
--g_ftrace; \
|
||||
fnoisef(kLogNoise, __FILE__, __LINE__, NULL, FMT, ##__VA_ARGS__); \
|
||||
++g_ftrace; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define FLOGF(F, FMT, ...) \
|
||||
do { \
|
||||
if (LOGGABLE(kLogInfo)) { \
|
||||
++g_ftrace; \
|
||||
flogf(kLogInfo, __FILE__, __LINE__, F, FMT, ##__VA_ARGS__); \
|
||||
--g_ftrace; \
|
||||
flogf(kLogInfo, __FILE__, __LINE__, F, FMT, ##__VA_ARGS__); \
|
||||
++g_ftrace; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define FWARNF(F, FMT, ...) \
|
||||
do { \
|
||||
if (LOGGABLE(kLogWarn)) { \
|
||||
++g_ftrace; \
|
||||
flogf(kLogWarn, __FILE__, __LINE__, F, FMT, ##__VA_ARGS__); \
|
||||
--g_ftrace; \
|
||||
flogf(kLogWarn, __FILE__, __LINE__, F, FMT, ##__VA_ARGS__); \
|
||||
++g_ftrace; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define FFATALF(F, FMT, ...) \
|
||||
do { \
|
||||
++g_ftrace; \
|
||||
--g_ftrace; \
|
||||
ffatalf(kLogFatal, __FILE__, __LINE__, F, FMT, ##__VA_ARGS__); \
|
||||
unreachable; \
|
||||
} while (0)
|
||||
|
@ -180,18 +180,18 @@ extern unsigned __log_level; /* log level for runtime check */
|
|||
#define FDEBUGF(F, FMT, ...) \
|
||||
do { \
|
||||
if (UNLIKELY(LOGGABLE(kLogDebug))) { \
|
||||
++g_ftrace; \
|
||||
fdebugf(kLogDebug, __FILE__, __LINE__, F, FMT, ##__VA_ARGS__); \
|
||||
--g_ftrace; \
|
||||
fdebugf(kLogDebug, __FILE__, __LINE__, F, FMT, ##__VA_ARGS__); \
|
||||
++g_ftrace; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define FNOISEF(F, FMT, ...) \
|
||||
do { \
|
||||
if (UNLIKELY(LOGGABLE(kLogNoise))) { \
|
||||
++g_ftrace; \
|
||||
fnoisef(kLogNoise, __FILE__, __LINE__, F, FMT, ##__VA_ARGS__); \
|
||||
--g_ftrace; \
|
||||
fnoisef(kLogNoise, __FILE__, __LINE__, F, FMT, ##__VA_ARGS__); \
|
||||
++g_ftrace; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
|
@ -204,9 +204,9 @@ extern unsigned __log_level; /* log level for runtime check */
|
|||
int e = errno; \
|
||||
autotype(FORM) Ax = (FORM); \
|
||||
if (UNLIKELY(Ax == (typeof(Ax))(-1)) && LOGGABLE(kLogWarn)) { \
|
||||
++g_ftrace; \
|
||||
__logerrno(__FILE__, __LINE__, #FORM); \
|
||||
--g_ftrace; \
|
||||
__logerrno(__FILE__, __LINE__, #FORM); \
|
||||
++g_ftrace; \
|
||||
errno = e; \
|
||||
} \
|
||||
Ax; \
|
||||
|
@ -217,9 +217,9 @@ extern unsigned __log_level; /* log level for runtime check */
|
|||
int e = errno; \
|
||||
autotype(FORM) Ax = (FORM); \
|
||||
if (Ax == NULL && LOGGABLE(kLogWarn)) { \
|
||||
++g_ftrace; \
|
||||
__logerrno(__FILE__, __LINE__, #FORM); \
|
||||
--g_ftrace; \
|
||||
__logerrno(__FILE__, __LINE__, #FORM); \
|
||||
++g_ftrace; \
|
||||
errno = e; \
|
||||
} \
|
||||
Ax; \
|
||||
|
|
|
@ -41,6 +41,7 @@ LIBC_LOG_A_DIRECTDEPS = \
|
|||
LIBC_STR \
|
||||
LIBC_STUBS \
|
||||
LIBC_SYSV \
|
||||
LIBC_SYSV_CALLS \
|
||||
LIBC_TIME \
|
||||
LIBC_TINYMATH \
|
||||
LIBC_UNICODE \
|
||||
|
@ -58,8 +59,8 @@ $(LIBC_LOG_A).pkg: \
|
|||
$(LIBC_LOG_A_OBJS) \
|
||||
$(foreach x,$(LIBC_LOG_A_DIRECTDEPS),$($(x)_A).pkg)
|
||||
|
||||
o/$(MODE)/libc/log/backtrace2.o \
|
||||
o/$(MODE)/libc/log/backtrace3.o: \
|
||||
o/$(MODE)/libc/log/backtrace2.greg.o \
|
||||
o/$(MODE)/libc/log/backtrace3.greg.o: \
|
||||
OVERRIDE_CFLAGS += \
|
||||
-fno-sanitize=all
|
||||
|
||||
|
@ -67,6 +68,7 @@ o/$(MODE)/libc/log/checkfail.o: \
|
|||
OVERRIDE_CFLAGS += \
|
||||
-mgeneral-regs-only
|
||||
|
||||
o/$(MODE)/libc/log/restoretty.greg.o \
|
||||
o/$(MODE)/libc/log/attachdebugger.o \
|
||||
o/$(MODE)/libc/log/backtrace2.o \
|
||||
o/$(MODE)/libc/log/backtrace3.o \
|
||||
|
|
|
@ -296,7 +296,7 @@ relegated noinstrument void __oncrash(int sig, struct siginfo *si,
|
|||
: 0);
|
||||
}
|
||||
if (!(gdbpid > 0 && (sig == SIGTRAP || sig == SIGQUIT))) {
|
||||
__restore_tty(1);
|
||||
__restore_tty();
|
||||
ShowCrashReport(err, sig, si, ctx);
|
||||
__restorewintty();
|
||||
_Exit(128 + sig);
|
||||
|
|
|
@ -17,8 +17,11 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/struct/metatermios.internal.h"
|
||||
#include "libc/calls/struct/termios.h"
|
||||
#include "libc/calls/termios.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/log/color.internal.h"
|
||||
#include "libc/log/internal.h"
|
||||
|
@ -26,7 +29,7 @@
|
|||
#include "libc/sysv/consts/termios.h"
|
||||
|
||||
/**
|
||||
* @fileoverview Terminal Restoration Helper
|
||||
* @fileoverview Terminal Restoration Helper for System Five.
|
||||
*
|
||||
* This is used by the crash reporting functions, e.g. __die(), to help
|
||||
* ensure the terminal is in an unborked state after a crash happens.
|
||||
|
@ -37,26 +40,28 @@
|
|||
#define DISABLE_MOUSE "\e[?1000;1002;1015;1006l"
|
||||
#define ANSI_RESTORE RESET_COLOR SHOW_CURSOR DISABLE_MOUSE
|
||||
|
||||
struct termios g_oldtermios;
|
||||
static bool __isrestorable;
|
||||
static union metatermios __oldtermios;
|
||||
|
||||
static textstartup void g_oldtermios_init() {
|
||||
int e = errno;
|
||||
tcgetattr(1, &g_oldtermios);
|
||||
static textstartup void __oldtermios_init() {
|
||||
int e;
|
||||
e = errno;
|
||||
if (sys_ioctl(0, TCGETS, &__oldtermios) != -1) {
|
||||
__isrestorable = true;
|
||||
}
|
||||
errno = e;
|
||||
}
|
||||
|
||||
const void *const g_oldtermios_ctor[] initarray = {
|
||||
g_oldtermios_init,
|
||||
const void *const __oldtermios_ctor[] initarray = {
|
||||
__oldtermios_init,
|
||||
};
|
||||
|
||||
void __restore_tty(int fd) {
|
||||
void __restore_tty(void) {
|
||||
int e;
|
||||
if (!__isworker) {
|
||||
if (__isrestorable && !__isworker && !__nocolor) {
|
||||
e = errno;
|
||||
if (g_oldtermios.c_lflag && !__nocolor && isatty(fd)) {
|
||||
write(fd, ANSI_RESTORE, strlen(ANSI_RESTORE));
|
||||
tcsetattr(fd, TCSAFLUSH, &g_oldtermios);
|
||||
}
|
||||
sys_write(0, ANSI_RESTORE, strlen(ANSI_RESTORE));
|
||||
sys_ioctl(0, TCSETSF, &__oldtermios);
|
||||
errno = e;
|
||||
}
|
||||
}
|
|
@ -27,7 +27,7 @@
|
|||
* @note this is support code for __check_fail(), __assert_fail(), etc.
|
||||
*/
|
||||
relegated void __start_fatal(const char *file, int line) {
|
||||
__restore_tty(1);
|
||||
__restore_tty();
|
||||
kprintf("%r%serror%s:%s:%d:%s%s: ", !__nocolor ? "\e[J\e[30;101m" : "",
|
||||
!__nocolor ? "\e[94;49m" : "", file, line,
|
||||
program_invocation_short_name, !__nocolor ? "\e[0m" : "");
|
||||
|
|
|
@ -249,7 +249,7 @@ textwindows int sys_fork_nt(void) {
|
|||
kNtFileFlagOverlapped, 0);
|
||||
if (pid != -1 && reader != -1 && writer != -1) {
|
||||
p = stpcpy(forkvar, "_FORK=");
|
||||
p += uint64toarray_radix10(reader, p);
|
||||
p = FormatUint64(p, reader);
|
||||
bzero(&startinfo, sizeof(startinfo));
|
||||
startinfo.cb = sizeof(struct NtStartupInfo);
|
||||
startinfo.dwFlags = kNtStartfUsestdhandles;
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/sysv/consts/map.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/time/clockstonanos.internal.h"
|
||||
|
||||
#pragma weak stderr
|
||||
|
||||
|
@ -89,7 +90,7 @@ privileged noinstrument noasan noubsan void ftracer(void) {
|
|||
frame = frame->next;
|
||||
if (frame->addr != g_lastaddr) {
|
||||
kprintf("+ %*s%t %d\r\n", GetNestingLevel(frame) * 2, "", frame->addr,
|
||||
(long)(unsignedsubtract(stamp, g_laststamp) / 3.3));
|
||||
ClocksToNanos(stamp, g_laststamp));
|
||||
g_laststamp = X86_HAVE(RDTSCP) ? rdtscp(0) : rdtsc();
|
||||
g_lastaddr = frame->addr;
|
||||
}
|
||||
|
@ -97,14 +98,15 @@ privileged noinstrument noasan noubsan void ftracer(void) {
|
|||
noreentry = 0;
|
||||
}
|
||||
|
||||
textstartup void ftrace_install(void) {
|
||||
textstartup int ftrace_install(void) {
|
||||
if (GetSymbolTable()) {
|
||||
g_lastaddr = -1;
|
||||
g_laststamp = kStartTsc;
|
||||
g_skew = GetNestingLevelImpl(__builtin_frame_address(0));
|
||||
ftrace_enabled = 1;
|
||||
__hook(ftrace_hook, GetSymbolTable());
|
||||
return __hook(ftrace_hook, GetSymbolTable());
|
||||
} else {
|
||||
kprintf("error: --ftrace failed to open symbol table\r\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
|
31
libc/runtime/ismemtracked.greg.c
Normal file
31
libc/runtime/ismemtracked.greg.c
Normal file
|
@ -0,0 +1,31 @@
|
|||
/*-*- 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/runtime/memtrack.internal.h"
|
||||
|
||||
bool IsMemtracked(int x, int y) {
|
||||
unsigned i;
|
||||
i = FindMemoryInterval(&_mmi, x);
|
||||
if (i == _mmi.i) return false;
|
||||
if (x < _mmi.p[i].x) return false;
|
||||
for (;;) {
|
||||
if (y <= _mmi.p[i].y) return true;
|
||||
if (++i == _mmi.i) return false;
|
||||
if (_mmi.p[i].x != _mmi.p[i - 1].y + 1) return false;
|
||||
}
|
||||
}
|
|
@ -36,8 +36,8 @@
|
|||
#include "libc/sysv/consts/prot.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
static noasan void *MoveMemoryIntervals(struct MemoryInterval *d,
|
||||
const struct MemoryInterval *s, int n) {
|
||||
static void *MoveMemoryIntervals(struct MemoryInterval *d,
|
||||
const struct MemoryInterval *s, int n) {
|
||||
/* asan runtime depends on this function */
|
||||
int i;
|
||||
assert(n >= 0);
|
||||
|
@ -53,8 +53,7 @@ static noasan void *MoveMemoryIntervals(struct MemoryInterval *d,
|
|||
return d;
|
||||
}
|
||||
|
||||
static noasan void RemoveMemoryIntervals(struct MemoryIntervals *mm, int i,
|
||||
int n) {
|
||||
static void RemoveMemoryIntervals(struct MemoryIntervals *mm, int i, int n) {
|
||||
/* asan runtime depends on this function */
|
||||
assert(i >= 0);
|
||||
assert(i + n <= mm->i);
|
||||
|
@ -62,7 +61,7 @@ static noasan void RemoveMemoryIntervals(struct MemoryIntervals *mm, int i,
|
|||
mm->i -= n;
|
||||
}
|
||||
|
||||
static noasan bool ExtendMemoryIntervals(struct MemoryIntervals *mm) {
|
||||
static bool ExtendMemoryIntervals(struct MemoryIntervals *mm) {
|
||||
int prot, flags;
|
||||
char *base, *shad;
|
||||
size_t gran, size;
|
||||
|
@ -99,7 +98,7 @@ static noasan bool ExtendMemoryIntervals(struct MemoryIntervals *mm) {
|
|||
return true;
|
||||
}
|
||||
|
||||
noasan int CreateMemoryInterval(struct MemoryIntervals *mm, int i) {
|
||||
int CreateMemoryInterval(struct MemoryIntervals *mm, int i) {
|
||||
/* asan runtime depends on this function */
|
||||
int rc;
|
||||
rc = 0;
|
||||
|
@ -111,15 +110,15 @@ noasan int CreateMemoryInterval(struct MemoryIntervals *mm, int i) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
static noasan int PunchHole(struct MemoryIntervals *mm, int x, int y, int i) {
|
||||
static int PunchHole(struct MemoryIntervals *mm, int x, int y, int i) {
|
||||
if (CreateMemoryInterval(mm, i) == -1) return -1;
|
||||
mm->p[i].y = x - 1;
|
||||
mm->p[i + 1].x = y + 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
noasan int ReleaseMemoryIntervals(struct MemoryIntervals *mm, int x, int y,
|
||||
void wf(struct MemoryIntervals *, int, int)) {
|
||||
int ReleaseMemoryIntervals(struct MemoryIntervals *mm, int x, int y,
|
||||
void wf(struct MemoryIntervals *, int, int)) {
|
||||
unsigned l, r;
|
||||
assert(y >= x);
|
||||
assert(AreMemoryIntervalsOk(mm));
|
||||
|
@ -158,9 +157,9 @@ noasan int ReleaseMemoryIntervals(struct MemoryIntervals *mm, int x, int y,
|
|||
return 0;
|
||||
}
|
||||
|
||||
noasan int TrackMemoryInterval(struct MemoryIntervals *mm, int x, int y, long h,
|
||||
int prot, int flags, bool readonlyfile,
|
||||
bool iscow, long offset, long size) {
|
||||
int TrackMemoryInterval(struct MemoryIntervals *mm, int x, int y, long h,
|
||||
int prot, int flags, bool readonlyfile, bool iscow,
|
||||
long offset, long size) {
|
||||
/* asan runtime depends on this function */
|
||||
unsigned i;
|
||||
assert(y >= x);
|
||||
|
|
|
@ -50,8 +50,9 @@ struct MemoryIntervals {
|
|||
|
||||
extern hidden struct MemoryIntervals _mmi;
|
||||
|
||||
const char *DescribeFrame(int);
|
||||
bool IsMemtracked(int, int) hidden;
|
||||
void PrintSystemMappings(int) hidden;
|
||||
const char *DescribeFrame(int) hidden;
|
||||
char *DescribeMapping(int, int, char[hasatleast 8]) hidden;
|
||||
bool AreMemoryIntervalsOk(const struct MemoryIntervals *) nosideeffect hidden;
|
||||
void PrintMemoryIntervals(int, const struct MemoryIntervals *) hidden;
|
||||
|
@ -175,18 +176,6 @@ forceinline unsigned FindMemoryInterval(const struct MemoryIntervals *mm,
|
|||
return l;
|
||||
}
|
||||
|
||||
forceinline bool IsMemtracked(int x, int y) {
|
||||
unsigned i;
|
||||
i = FindMemoryInterval(&_mmi, x);
|
||||
if (i == _mmi.i) return false;
|
||||
if (x < _mmi.p[i].x) return false;
|
||||
for (;;) {
|
||||
if (y <= _mmi.p[i].y) return true;
|
||||
if (++i == _mmi.i) return false;
|
||||
if (_mmi.p[i].x != _mmi.p[i - 1].y + 1) return false;
|
||||
}
|
||||
}
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
#endif /* COSMOPOLITAN_LIBC_RUNTIME_MEMTRACK_H_ */
|
||||
|
|
|
@ -203,9 +203,8 @@ textstartup void __printargs(const char *prologue) {
|
|||
PRINT(" L%d%s%s %u-way %,u byte cache w/%s "
|
||||
"%,u sets of %,u byte lines shared across %u threads%s",
|
||||
CPUID4_CACHE_LEVEL,
|
||||
CPUID4_CACHE_TYPE == 1 ? " data"
|
||||
: CPUID4_CACHE_TYPE == 2 ? " code"
|
||||
: "",
|
||||
CPUID4_CACHE_TYPE == 1 ? " data"
|
||||
: CPUID4_CACHE_TYPE == 2 ? " code" : "",
|
||||
CPUID4_IS_FULLY_ASSOCIATIVE ? " fully-associative" : "",
|
||||
CPUID4_WAYS_OF_ASSOCIATIVITY, CPUID4_CACHE_SIZE_IN_BYTES,
|
||||
CPUID4_PHYSICAL_LINE_PARTITIONS > 1 ? " physically partitioned" : "",
|
||||
|
@ -245,8 +244,8 @@ textstartup void __printargs(const char *prologue) {
|
|||
if ((n = poll(pfds, ARRAYLEN(pfds), 0)) != -1) {
|
||||
for (i = 0; i < ARRAYLEN(pfds); ++i) {
|
||||
if (i && (pfds[i].revents & POLLNVAL)) continue;
|
||||
PRINT(" ☼ %d (revents=%#hx F_GETFL=%#x)", i, pfds[i].revents,
|
||||
fcntl(i, F_GETFL));
|
||||
PRINT(" ☼ %d (revents=%#hx fcntl(F_GETFL)=%#x isatty()=%hhhd)", i,
|
||||
pfds[i].revents, fcntl(i, F_GETFL), isatty(i));
|
||||
}
|
||||
} else {
|
||||
PRINT(" poll() returned %d %m", n);
|
||||
|
|
|
@ -98,7 +98,7 @@ void _weakfree(void *);
|
|||
void free_s(void *) paramsnonnull() libcesque;
|
||||
int close_s(int *) paramsnonnull() libcesque;
|
||||
int OpenExecutable(void);
|
||||
void ftrace_install(void);
|
||||
int ftrace_install(void);
|
||||
long GetResourceLimit(int);
|
||||
long GetMaxFd(void);
|
||||
char *GetProgramExecutableName(void);
|
||||
|
|
|
@ -70,8 +70,9 @@ o/$(MODE)/libc/runtime/getdosargv.o \
|
|||
o/$(MODE)/libc/runtime/getdosenviron.o \
|
||||
o/$(MODE)/libc/runtime/hook.greg.o \
|
||||
o/$(MODE)/libc/runtime/isheap.o \
|
||||
o/$(MODE)/libc/runtime/memtrack.o \
|
||||
o/$(MODE)/libc/runtime/memtracknt.o \
|
||||
o/$(MODE)/libc/runtime/memtrack.greg.o \
|
||||
o/$(MODE)/libc/runtime/ismemtracked.greg.o \
|
||||
o/$(MODE)/libc/runtime/metalprintf.greg.o \
|
||||
o/$(MODE)/libc/runtime/printargs.greg.o \
|
||||
o/$(MODE)/libc/runtime/mman.greg.o \
|
||||
|
|
|
@ -25,12 +25,12 @@
|
|||
char *inet_ntoa(struct in_addr in) {
|
||||
static char buf[16];
|
||||
char *p = buf;
|
||||
p += int64toarray_radix10((in.s_addr >> 000) & 255, p);
|
||||
p = FormatUint32(p, (in.s_addr >> 000) & 255);
|
||||
*p++ = '.';
|
||||
p += int64toarray_radix10((in.s_addr >> 010) & 255, p);
|
||||
p = FormatUint32(p, (in.s_addr >> 010) & 255);
|
||||
*p++ = '.';
|
||||
p += int64toarray_radix10((in.s_addr >> 020) & 255, p);
|
||||
p = FormatUint32(p, (in.s_addr >> 020) & 255);
|
||||
*p++ = '.';
|
||||
p += int64toarray_radix10((in.s_addr >> 030) & 255, p);
|
||||
p = FormatUint32(p, (in.s_addr >> 030) & 255);
|
||||
return buf;
|
||||
}
|
||||
|
|
|
@ -53,19 +53,19 @@ static relegated void DieBecauseOfQuota(int rc, const char *message) {
|
|||
}
|
||||
|
||||
static relegated void OnXcpu(int sig) {
|
||||
__restore_tty(2);
|
||||
__restore_tty();
|
||||
DieBecauseOfQuota(23, "\n\nSIGXCPU: ran out of cpu");
|
||||
}
|
||||
|
||||
static relegated void OnXfsz(int sig) {
|
||||
__restore_tty(2);
|
||||
__restore_tty();
|
||||
DieBecauseOfQuota(25, "\n\nSIGXFSZ: exceeded maximum file size");
|
||||
}
|
||||
|
||||
relegated void __oom_hook(size_t request) {
|
||||
int e;
|
||||
uint64_t toto, newlim;
|
||||
__restore_tty(2);
|
||||
__restore_tty();
|
||||
e = errno;
|
||||
toto = CountMappedBytes();
|
||||
kprintf("\n\nWE REQUIRE MORE VESPENE GAS");
|
||||
|
|
|
@ -48,7 +48,7 @@ static char *strftime_secs(char *p, const char *pe, const struct tm *t) {
|
|||
int64_t s;
|
||||
tmp = *t; /* Make a copy, mktime(3) modifies the tm struct. */
|
||||
s = mktime(&tmp);
|
||||
int64toarray_radix10(s, ibuf);
|
||||
FormatInt64(ibuf, s);
|
||||
return strftime_add(p, pe, ibuf);
|
||||
}
|
||||
|
||||
|
@ -329,13 +329,13 @@ static char *strftime_timefmt(char *p, const char *pe, const char *format,
|
|||
if (t->tm_isdst == 0)
|
||||
#ifdef USG_COMPAT
|
||||
diff = -timezone;
|
||||
#else /* !defined USG_COMPAT */
|
||||
#else /* !defined USG_COMPAT */
|
||||
continue;
|
||||
#endif /* !defined USG_COMPAT */
|
||||
else
|
||||
#ifdef ALTZONE
|
||||
diff = -altzone;
|
||||
#else /* !defined ALTZONE */
|
||||
#else /* !defined ALTZONE */
|
||||
continue;
|
||||
#endif /* !defined ALTZONE */
|
||||
#endif /* !defined TM_GMTOFF */
|
||||
|
|
|
@ -46,6 +46,13 @@ TEST(open, eexist) {
|
|||
ASSERT_SYS(EEXIST, -1, open("exists", O_WRONLY | O_CREAT | O_EXCL));
|
||||
}
|
||||
|
||||
TEST(open, enametoolong) {
|
||||
size_t n = 260;
|
||||
char *s = gc(xcalloc(1, n + 1));
|
||||
memset(s, 'J', n);
|
||||
ASSERT_SYS(ENAMETOOLONG, -1, creat(s, 0644));
|
||||
}
|
||||
|
||||
TEST(open, testOpenExistingForWriteOnly_seeksToStart) {
|
||||
char buf[8] = {0};
|
||||
ASSERT_SYS(0, 0, xbarf("hello.txt", "hello", -1));
|
||||
|
|
|
@ -22,29 +22,29 @@
|
|||
#include "libc/testlib/ezbench.h"
|
||||
#include "libc/testlib/testlib.h"
|
||||
|
||||
TEST(int64toarray_radix10, test) {
|
||||
TEST(FormatInt64, test) {
|
||||
char buf[21];
|
||||
EXPECT_EQ(1, int64toarray_radix10(0, buf));
|
||||
EXPECT_EQ(1, FormatInt64(buf, 0) - buf);
|
||||
EXPECT_STREQ("0", buf);
|
||||
EXPECT_EQ(1, int64toarray_radix10(1, buf));
|
||||
EXPECT_EQ(1, FormatInt64(buf, 1) - buf);
|
||||
EXPECT_STREQ("1", buf);
|
||||
EXPECT_EQ(2, int64toarray_radix10(-1, buf));
|
||||
EXPECT_EQ(2, FormatInt64(buf, -1) - buf);
|
||||
EXPECT_STREQ("-1", buf);
|
||||
EXPECT_EQ(19, int64toarray_radix10(INT64_MAX, buf));
|
||||
EXPECT_EQ(19, FormatInt64(buf, INT64_MAX) - buf);
|
||||
EXPECT_STREQ("9223372036854775807", buf);
|
||||
EXPECT_EQ(20, int64toarray_radix10(INT64_MIN, buf));
|
||||
EXPECT_EQ(20, FormatInt64(buf, INT64_MIN) - buf);
|
||||
EXPECT_STREQ("-9223372036854775808", buf);
|
||||
}
|
||||
|
||||
TEST(uint64toarray_radix10, test) {
|
||||
TEST(FormatUint64, test) {
|
||||
char buf[21];
|
||||
EXPECT_EQ(1, uint64toarray_radix10(0, buf));
|
||||
EXPECT_EQ(1, FormatUint64(buf, 0) - buf);
|
||||
EXPECT_STREQ("0", buf);
|
||||
EXPECT_EQ(4, uint64toarray_radix10(1024, buf));
|
||||
EXPECT_EQ(4, FormatUint64(buf, 1024) - buf);
|
||||
EXPECT_STREQ("1024", buf);
|
||||
EXPECT_EQ(20, uint64toarray_radix10(UINT64_MAX, buf));
|
||||
EXPECT_EQ(20, FormatUint64(buf, UINT64_MAX) - buf);
|
||||
EXPECT_STREQ("18446744073709551615", buf);
|
||||
EXPECT_EQ(19, uint64toarray_radix10(INT64_MIN, buf));
|
||||
EXPECT_EQ(19, FormatUint64(buf, INT64_MIN) - buf);
|
||||
EXPECT_STREQ("9223372036854775808", buf);
|
||||
}
|
||||
|
||||
|
@ -70,5 +70,5 @@ TEST(uint128toarray_radix10, test) {
|
|||
|
||||
BENCH(itoa64radix10, bench) {
|
||||
char b[21];
|
||||
EZBENCH2("itoa64radix10", donothing, uint64toarray_radix10(UINT64_MAX, b));
|
||||
EZBENCH2("itoa64radix10", donothing, FormatUint64(b, UINT64_MAX));
|
||||
}
|
|
@ -76,8 +76,6 @@ TEST(FormatInt64Thousands, testNegative) {
|
|||
|
||||
BENCH(FormatInt64Thousands, bench) {
|
||||
char s[27];
|
||||
EZBENCH2("int64toarray_radix10(MAX)", donothing,
|
||||
int64toarray_radix10(INT64_MAX, s));
|
||||
EZBENCH2("FormatInt64Thousands(MAX)", donothing,
|
||||
FormatInt64Thousands(s, INT64_MAX));
|
||||
EZBENCH2("FormatInt64Thousands(MIN)", donothing,
|
||||
|
|
57
test/libc/fmt/formatoctal32_test.c
Normal file
57
test/libc/fmt/formatoctal32_test.c
Normal file
|
@ -0,0 +1,57 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 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/str/str.h"
|
||||
#include "libc/testlib/testlib.h"
|
||||
|
||||
char buf[13];
|
||||
|
||||
void SetUp(void) {
|
||||
memset(buf, 0x55, sizeof(buf));
|
||||
}
|
||||
|
||||
TEST(FormatOctal32, test1) {
|
||||
EXPECT_EQ(1, FormatOctal32(buf, 0, true) - buf);
|
||||
EXPECT_STREQ("0", buf);
|
||||
}
|
||||
|
||||
TEST(FormatOctal32, test2) {
|
||||
EXPECT_EQ(1, FormatOctal32(buf, 0, false) - buf);
|
||||
EXPECT_STREQ("0", buf);
|
||||
}
|
||||
|
||||
TEST(FormatOctal32, test3) {
|
||||
EXPECT_EQ(2, FormatOctal32(buf, 1, true) - buf);
|
||||
EXPECT_STREQ("01", buf);
|
||||
}
|
||||
|
||||
TEST(FormatOctal32, test4) {
|
||||
EXPECT_EQ(1, FormatOctal32(buf, 1, false) - buf);
|
||||
EXPECT_STREQ("1", buf);
|
||||
}
|
||||
|
||||
TEST(FormatOctal32, test5) {
|
||||
EXPECT_EQ(12, FormatOctal32(buf, 037777777777, true) - buf);
|
||||
EXPECT_STREQ("037777777777", buf);
|
||||
}
|
||||
|
||||
TEST(FormatOctal32, test6) {
|
||||
EXPECT_EQ(11, FormatOctal32(buf, 037777777777, false) - buf);
|
||||
EXPECT_STREQ("37777777777", buf);
|
||||
}
|
66
test/libc/fmt/formatoctal64_test.c
Normal file
66
test/libc/fmt/formatoctal64_test.c
Normal file
|
@ -0,0 +1,66 @@
|
|||
/*-*- 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/str/str.h"
|
||||
#include "libc/testlib/ezbench.h"
|
||||
#include "libc/testlib/testlib.h"
|
||||
|
||||
char buf[25];
|
||||
|
||||
void SetUp(void) {
|
||||
memset(buf, 0x55, sizeof(buf));
|
||||
}
|
||||
|
||||
TEST(FormatOctal64, test1) {
|
||||
EXPECT_EQ(1, FormatOctal64(buf, 0, true) - buf);
|
||||
EXPECT_STREQ("0", buf);
|
||||
}
|
||||
|
||||
TEST(FormatOctal64, test2) {
|
||||
EXPECT_EQ(1, FormatOctal64(buf, 0, false) - buf);
|
||||
EXPECT_STREQ("0", buf);
|
||||
}
|
||||
|
||||
TEST(FormatOctal64, test3) {
|
||||
EXPECT_EQ(2, FormatOctal64(buf, 1, true) - buf);
|
||||
EXPECT_STREQ("01", buf);
|
||||
}
|
||||
|
||||
TEST(FormatOctal64, test4) {
|
||||
EXPECT_EQ(1, FormatOctal64(buf, 1, false) - buf);
|
||||
EXPECT_STREQ("1", buf);
|
||||
}
|
||||
|
||||
TEST(FormatOctal64, test5) {
|
||||
EXPECT_EQ(23, FormatOctal64(buf, 01777777777777777777777UL, true) - buf);
|
||||
EXPECT_STREQ("01777777777777777777777", buf);
|
||||
}
|
||||
|
||||
TEST(FormatOctal64, test6) {
|
||||
EXPECT_EQ(22, FormatOctal64(buf, 01777777777777777777777UL, false) - buf);
|
||||
EXPECT_STREQ("1777777777777777777777", buf);
|
||||
}
|
||||
|
||||
BENCH(FormatOctal64, bench) {
|
||||
EZBENCH2("FormatUint64", donothing,
|
||||
FormatUint64(buf, 01777777777777777777777UL));
|
||||
EZBENCH2("FormatOctal64", donothing,
|
||||
FormatOctal64(buf, 01777777777777777777777UL, true));
|
||||
EZBENCH2("FormatOctal32", donothing, FormatOctal32(buf, 037777777777U, true));
|
||||
}
|
|
@ -641,6 +641,7 @@ BENCH(palandprintf, bench) {
|
|||
EZBENCH2("%g M_PI", donothing, Format("%g", VEIL("x", M_PI)));
|
||||
EZBENCH2("%a M_PI", donothing, Format("%a", VEIL("x", M_PI)));
|
||||
EZBENCH2("%e M_PI", donothing, Format("%e", VEIL("x", M_PI)));
|
||||
EZBENCH2("ULONG_MAX %lo", donothing, Format("%lo", VEIL("r", ULONG_MAX)));
|
||||
EZBENCH2("INT_MIN %x", donothing, Format("%x", VEIL("r", INT_MIN)));
|
||||
EZBENCH2("INT_MIN %d", donothing, Format("%d", VEIL("r", INT_MIN)));
|
||||
EZBENCH2("INT_MIN %,d", donothing, Format("%,d", VEIL("r", INT_MIN)));
|
||||
|
@ -649,7 +650,6 @@ BENCH(palandprintf, bench) {
|
|||
EZBENCH2("LONG_MIN %ld", donothing, Format("%ld", VEIL("r", LONG_MIN)));
|
||||
EZBENCH2("INT128_MIN %jjd", donothing, Format("%jjd", INT128_MIN));
|
||||
EZBENCH2("INT128_MIN %jjx", donothing, Format("%jjx", INT128_MIN));
|
||||
EZBENCH2("int64toarray 23", donothing, int64toarray_radix10(23, buffer));
|
||||
EZBENCH2("int64toarray min", donothing,
|
||||
int64toarray_radix10(INT_MIN, buffer));
|
||||
EZBENCH2("int64toarray 23", donothing, FormatInt64(buffer, 23));
|
||||
EZBENCH2("int64toarray min", donothing, FormatInt64(buffer, INT_MIN));
|
||||
}
|
||||
|
|
|
@ -43,7 +43,8 @@
|
|||
*/
|
||||
static uint64_t Rando(void) {
|
||||
uint64_t x;
|
||||
do x = lemur64();
|
||||
do
|
||||
x = lemur64();
|
||||
while (((x ^ READ64LE("!!!!!!!!")) - 0x0101010101010101) &
|
||||
~(x ^ READ64LE("!!!!!!!!")) & 0x8080808080808080);
|
||||
return x;
|
||||
|
@ -375,6 +376,8 @@ BENCH(printf, bench) {
|
|||
kusnprintf(b, 128, "%hs\n", u"𐌰𐌱𐌲𐌳𐌴𐌵𐌶𐌷"));
|
||||
EZBENCH2("snprintf astral", donothing,
|
||||
snprintf_(b, 128, "%hs\n", u"𐌰𐌱𐌲𐌳𐌴𐌵𐌶𐌷"));
|
||||
EZBENCH2("kusnprintf octal", donothing,
|
||||
kusnprintf(b, 128, "%#lo", ULONG_MAX));
|
||||
EZBENCH2("kusnprintf long", donothing, kusnprintf(b, 128, "%ld", LONG_MAX));
|
||||
EZBENCH2("snprintf long", donothing, snprintf_(b, 128, "%ld", LONG_MAX));
|
||||
EZBENCH2("kusnprintf thou", donothing, kusnprintf(b, 128, "%'ld", LONG_MAX));
|
||||
|
|
|
@ -33,6 +33,14 @@ STATIC_YOINK("usr/share/zoneinfo/New_York");
|
|||
|
||||
char testlib_enable_tmp_setup_teardown;
|
||||
|
||||
DIR *dir;
|
||||
struct dirent *ent;
|
||||
|
||||
void SetUp(void) {
|
||||
dir = 0;
|
||||
ent = 0;
|
||||
}
|
||||
|
||||
TEST(opendir, efault) {
|
||||
ASSERT_SYS(EFAULT, NULL, opendir(0));
|
||||
if (!IsAsan()) return; // not possible
|
||||
|
@ -50,10 +58,8 @@ TEST(opendir, enotdir) {
|
|||
}
|
||||
|
||||
TEST(dirstream, testDots) {
|
||||
DIR *dir;
|
||||
int hasdot = 0;
|
||||
int hasdotdot = 0;
|
||||
struct dirent *ent;
|
||||
ASSERT_SYS(0, 0, close(creat("foo", 0644)));
|
||||
ASSERT_NE(NULL, (dir = opendir(".")));
|
||||
while ((ent = readdir(dir))) {
|
||||
|
@ -72,8 +78,6 @@ TEST(dirstream, testDots) {
|
|||
}
|
||||
|
||||
TEST(dirstream, test) {
|
||||
DIR *dir;
|
||||
struct dirent *ent;
|
||||
bool hasfoo = false;
|
||||
bool hasbar = false;
|
||||
char *dpath, *file1, *file2;
|
||||
|
@ -104,21 +108,17 @@ TEST(dirstream, test) {
|
|||
|
||||
TEST(dirstream, zipTest) {
|
||||
bool foundNewYork = false;
|
||||
DIR *d;
|
||||
struct dirent *e;
|
||||
const char *path = "/zip/usr/share/zoneinfo/";
|
||||
ASSERT_NE(0, _gc(xiso8601ts(NULL)));
|
||||
ASSERT_NE(NULL, (d = opendir(path)));
|
||||
while ((e = readdir(d))) {
|
||||
foundNewYork |= !strcmp(e->d_name, "New_York");
|
||||
ASSERT_NE(NULL, (dir = opendir(path)));
|
||||
while ((ent = readdir(dir))) {
|
||||
foundNewYork |= !strcmp(ent->d_name, "New_York");
|
||||
}
|
||||
closedir(d);
|
||||
closedir(dir);
|
||||
EXPECT_TRUE(foundNewYork);
|
||||
}
|
||||
|
||||
TEST(rewinddir, test) {
|
||||
DIR *dir;
|
||||
struct dirent *ent;
|
||||
bool hasfoo = false;
|
||||
bool hasbar = false;
|
||||
char *dpath, *file1, *file2;
|
||||
|
|
|
@ -528,7 +528,7 @@ GET / HTTP/1.1\r\n\
|
|||
X-In-Your-Way-A: a\r\n\
|
||||
X-In-Your-Way-B: b\r\n\
|
||||
X-In-Your-Way-C: b\r\n\
|
||||
Accept-Encoding: deflate\r\n\
|
||||
Accept-Encoding:deflate\r\n\
|
||||
ACCEPT-ENCODING: gzip\r\n\
|
||||
ACCEPT-encoding: bzip2\r\n\
|
||||
\r\n";
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/sigbits.h"
|
||||
#include "libc/fmt/conv.h"
|
||||
#include "libc/log/check.h"
|
||||
#include "libc/runtime/gc.internal.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/sock/goodsocket.internal.h"
|
||||
|
@ -91,7 +90,7 @@ char *SendHttpRequest(const char *s) {
|
|||
bool Matches(const char *regex, const char *str) {
|
||||
bool r;
|
||||
regex_t re;
|
||||
CHECK_EQ(REG_OK, regcomp(&re, regex, 0));
|
||||
EXPECT_EQ(REG_OK, regcomp(&re, regex, 0));
|
||||
r = regexec(&re, str, 0, 0, 0) == REG_OK;
|
||||
regfree(&re);
|
||||
return r;
|
||||
|
@ -103,15 +102,22 @@ TEST(redbean, testOptions) {
|
|||
int pid, pipefds[2];
|
||||
sigset_t chldmask, savemask;
|
||||
sigaddset(&chldmask, SIGCHLD);
|
||||
CHECK_NE(-1, sigprocmask(SIG_BLOCK, &chldmask, &savemask));
|
||||
EXPECT_NE(-1, sigprocmask(SIG_BLOCK, &chldmask, &savemask));
|
||||
ASSERT_NE(-1, pipe(pipefds));
|
||||
ASSERT_NE(-1, (pid = fork()));
|
||||
if (!pid) {
|
||||
setpgrp();
|
||||
close(0);
|
||||
open("/dev/null", O_RDWR);
|
||||
close(1);
|
||||
dup(0);
|
||||
close(2);
|
||||
open("log", O_CREAT | O_TRUNC | O_WRONLY | O_APPEND, 0644);
|
||||
close(pipefds[0]);
|
||||
dup2(pipefds[1], 1);
|
||||
sigprocmask(SIG_SETMASK, &savemask, NULL);
|
||||
execv("bin/redbean.com",
|
||||
(char *const[]){"bin/redbean.com", "-szp0", "-l127.0.0.1", 0});
|
||||
(char *const[]){"bin/redbean.com", "-vvszp0", "-l127.0.0.1", 0});
|
||||
_exit(127);
|
||||
}
|
||||
EXPECT_NE(-1, close(pipefds[1]));
|
||||
|
@ -129,7 +135,8 @@ TEST(redbean, testOptions) {
|
|||
EXPECT_EQ(0, close(pipefds[0]));
|
||||
EXPECT_NE(-1, kill(pid, SIGTERM));
|
||||
EXPECT_NE(-1, wait(0));
|
||||
CHECK_NE(-1, sigprocmask(SIG_SETMASK, &savemask, 0));
|
||||
EXPECT_NE(-1, sigprocmask(SIG_SETMASK, &savemask, 0));
|
||||
if (g_testlib_failed) fputs(gc(xslurp("log", 0)), stderr);
|
||||
}
|
||||
|
||||
TEST(redbean, testPipeline) {
|
||||
|
@ -138,15 +145,20 @@ TEST(redbean, testPipeline) {
|
|||
int pid, pipefds[2];
|
||||
sigset_t chldmask, savemask;
|
||||
sigaddset(&chldmask, SIGCHLD);
|
||||
CHECK_NE(-1, sigprocmask(SIG_BLOCK, &chldmask, &savemask));
|
||||
EXPECT_NE(-1, sigprocmask(SIG_BLOCK, &chldmask, &savemask));
|
||||
ASSERT_NE(-1, pipe(pipefds));
|
||||
ASSERT_NE(-1, (pid = fork()));
|
||||
if (!pid) {
|
||||
setpgrp();
|
||||
close(0);
|
||||
open("/dev/null", O_RDWR);
|
||||
close(2);
|
||||
open("log", O_CREAT | O_TRUNC | O_WRONLY | O_APPEND, 0644);
|
||||
close(pipefds[0]);
|
||||
dup2(pipefds[1], 1);
|
||||
sigprocmask(SIG_SETMASK, &savemask, NULL);
|
||||
execv("bin/redbean.com",
|
||||
(char *const[]){"bin/redbean.com", "-szp0", "-l127.0.0.1", 0});
|
||||
(char *const[]){"bin/redbean.com", "-vvszp0", "-l127.0.0.1", 0});
|
||||
_exit(127);
|
||||
}
|
||||
EXPECT_NE(-1, close(pipefds[1]));
|
||||
|
@ -173,5 +185,6 @@ TEST(redbean, testPipeline) {
|
|||
EXPECT_EQ(0, close(pipefds[0]));
|
||||
EXPECT_NE(-1, kill(pid, SIGTERM));
|
||||
EXPECT_NE(-1, wait(0));
|
||||
CHECK_NE(-1, sigprocmask(SIG_SETMASK, &savemask, 0));
|
||||
EXPECT_NE(-1, sigprocmask(SIG_SETMASK, &savemask, 0));
|
||||
if (g_testlib_failed) fputs(gc(xslurp("log", 0)), stderr);
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include "libc/bits/weaken.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "third_party/dlmalloc/dlmalloc.h"
|
||||
// clang-format off
|
||||
|
||||
#define FOOTERS 0
|
||||
|
@ -100,7 +101,7 @@
|
|||
#define FOOTERS 0
|
||||
#endif /* FOOTERS */
|
||||
#ifndef ABORT
|
||||
#define ABORT abort()
|
||||
#define ABORT dlmalloc_abort()
|
||||
#endif /* ABORT */
|
||||
#ifndef ABORT_ON_ASSERT_FAILURE
|
||||
#define ABORT_ON_ASSERT_FAILURE 1
|
||||
|
@ -215,588 +216,6 @@
|
|||
#define FORCEINLINE forceinline
|
||||
#define NOINLINE dontinline
|
||||
|
||||
#if !ONLY_MSPACES
|
||||
|
||||
/* ------------------- Declarations of public routines ------------------- */
|
||||
|
||||
/*
|
||||
malloc(size_t n)
|
||||
Returns a pointer to a newly allocated chunk of at least n bytes, or
|
||||
null if no space is available, in which case errno is set to ENOMEM
|
||||
on ANSI C systems.
|
||||
|
||||
If n is zero, malloc returns a minimum-sized chunk. (The minimum
|
||||
size is 16 bytes on most 32bit systems, and 32 bytes on 64bit
|
||||
systems.) Note that size_t is an unsigned type, so calls with
|
||||
arguments that would be negative if signed are interpreted as
|
||||
requests for huge amounts of space, which will often fail. The
|
||||
maximum supported value of n differs across systems, but is in all
|
||||
cases less than the maximum representable value of a size_t.
|
||||
*/
|
||||
DLMALLOC_EXPORT void* dlmalloc(size_t);
|
||||
|
||||
/*
|
||||
free(void* p)
|
||||
Releases the chunk of memory pointed to by p, that had been previously
|
||||
allocated using malloc or a related routine such as realloc.
|
||||
It has no effect if p is null. If p was not malloced or already
|
||||
freed, free(p) will by default cause the current program to abort.
|
||||
*/
|
||||
DLMALLOC_EXPORT void dlfree(void*);
|
||||
|
||||
/*
|
||||
calloc(size_t n_elements, size_t element_size);
|
||||
Returns a pointer to n_elements * element_size bytes, with all locations
|
||||
set to zero.
|
||||
*/
|
||||
DLMALLOC_EXPORT void* dlcalloc(size_t, size_t);
|
||||
|
||||
/*
|
||||
realloc(void* p, size_t n)
|
||||
Returns a pointer to a chunk of size n that contains the same data
|
||||
as does chunk p up to the minimum of (n, p's size) bytes, or null
|
||||
if no space is available.
|
||||
|
||||
The returned pointer may or may not be the same as p. The algorithm
|
||||
prefers extending p in most cases when possible, otherwise it
|
||||
employs the equivalent of a malloc-copy-free sequence.
|
||||
|
||||
If p is null, realloc is equivalent to malloc.
|
||||
|
||||
If space is not available, realloc returns null, errno is set (if on
|
||||
ANSI) and p is NOT freed.
|
||||
|
||||
if n is for fewer bytes than already held by p, the newly unused
|
||||
space is lopped off and freed if possible. realloc with a size
|
||||
argument of zero (re)allocates a minimum-sized chunk.
|
||||
|
||||
The old unix realloc convention of allowing the last-free'd chunk
|
||||
to be used as an argument to realloc is not supported.
|
||||
*/
|
||||
DLMALLOC_EXPORT void* dlrealloc(void*, size_t);
|
||||
|
||||
/*
|
||||
realloc_in_place(void* p, size_t n)
|
||||
Resizes the space allocated for p to size n, only if this can be
|
||||
done without moving p (i.e., only if there is adjacent space
|
||||
available if n is greater than p's current allocated size, or n is
|
||||
less than or equal to p's size). This may be used instead of plain
|
||||
realloc if an alternative allocation strategy is needed upon failure
|
||||
to expand space; for example, reallocation of a buffer that must be
|
||||
memory-aligned or cleared. You can use realloc_in_place to trigger
|
||||
these alternatives only when needed.
|
||||
|
||||
Returns p if successful; otherwise null.
|
||||
*/
|
||||
DLMALLOC_EXPORT void* dlrealloc_in_place(void*, size_t);
|
||||
|
||||
/*
|
||||
memalign(size_t alignment, size_t n);
|
||||
Returns a pointer to a newly allocated chunk of n bytes, aligned
|
||||
in accord with the alignment argument.
|
||||
|
||||
The alignment argument should be a power of two. If the argument is
|
||||
not a power of two, the nearest greater power is used.
|
||||
8-byte alignment is guaranteed by normal malloc calls, so don't
|
||||
bother calling memalign with an argument of 8 or less.
|
||||
|
||||
Overreliance on memalign is a sure way to fragment space.
|
||||
*/
|
||||
DLMALLOC_EXPORT void* dlmemalign(size_t, size_t);
|
||||
|
||||
/*
|
||||
int posix_memalign(void** pp, size_t alignment, size_t n);
|
||||
Allocates a chunk of n bytes, aligned in accord with the alignment
|
||||
argument. Differs from memalign only in that it (1) assigns the
|
||||
allocated memory to *pp rather than returning it, (2) fails and
|
||||
returns EINVAL if the alignment is not a power of two (3) fails and
|
||||
returns ENOMEM if memory cannot be allocated.
|
||||
*/
|
||||
DLMALLOC_EXPORT int dlposix_memalign(void**, size_t, size_t);
|
||||
|
||||
/*
|
||||
valloc(size_t n);
|
||||
Equivalent to memalign(pagesize, n), where pagesize is the page
|
||||
size of the system. If the pagesize is unknown, 4096 is used.
|
||||
*/
|
||||
DLMALLOC_EXPORT void* dlvalloc(size_t);
|
||||
|
||||
/*
|
||||
mallopt(int parameter_number, int parameter_value)
|
||||
Sets tunable parameters The format is to provide a
|
||||
(parameter-number, parameter-value) pair. mallopt then sets the
|
||||
corresponding parameter to the argument value if it can (i.e., so
|
||||
long as the value is meaningful), and returns 1 if successful else
|
||||
0. To workaround the fact that mallopt is specified to use int,
|
||||
not size_t parameters, the value -1 is specially treated as the
|
||||
maximum unsigned size_t value.
|
||||
|
||||
SVID/XPG/ANSI defines four standard param numbers for mallopt,
|
||||
normally defined in malloc.h. None of these are use in this malloc,
|
||||
so setting them has no effect. But this malloc also supports other
|
||||
options in mallopt. See below for details. Briefly, supported
|
||||
parameters are as follows (listed defaults are for "typical"
|
||||
configurations).
|
||||
|
||||
Symbol param # default allowed param values
|
||||
M_TRIM_THRESHOLD -1 2*1024*1024 any (-1 disables)
|
||||
M_GRANULARITY -2 page size any power of 2 >= page size
|
||||
M_MMAP_THRESHOLD -3 256*1024 any (or 0 if no MMAP support)
|
||||
*/
|
||||
DLMALLOC_EXPORT int dlmallopt(int, int);
|
||||
|
||||
/*
|
||||
malloc_footprint();
|
||||
Returns the number of bytes obtained from the system. The total
|
||||
number of bytes allocated by malloc, realloc etc., is less than this
|
||||
value. Unlike mallinfo, this function returns only a precomputed
|
||||
result, so can be called frequently to monitor memory consumption.
|
||||
Even if locks are otherwise defined, this function does not use them,
|
||||
so results might not be up to date.
|
||||
*/
|
||||
DLMALLOC_EXPORT size_t dlmalloc_footprint(void);
|
||||
|
||||
/*
|
||||
malloc_max_footprint();
|
||||
Returns the maximum number of bytes obtained from the system. This
|
||||
value will be greater than current footprint if deallocated space
|
||||
has been reclaimed by the system. The peak number of bytes allocated
|
||||
by malloc, realloc etc., is less than this value. Unlike mallinfo,
|
||||
this function returns only a precomputed result, so can be called
|
||||
frequently to monitor memory consumption. Even if locks are
|
||||
otherwise defined, this function does not use them, so results might
|
||||
not be up to date.
|
||||
*/
|
||||
DLMALLOC_EXPORT size_t dlmalloc_max_footprint(void);
|
||||
|
||||
/*
|
||||
malloc_footprint_limit();
|
||||
Returns the number of bytes that the heap is allowed to obtain from
|
||||
the system, returning the last value returned by
|
||||
malloc_set_footprint_limit, or the maximum size_t value if
|
||||
never set. The returned value reflects a permission. There is no
|
||||
guarantee that this number of bytes can actually be obtained from
|
||||
the system.
|
||||
*/
|
||||
DLMALLOC_EXPORT size_t dlmalloc_footprint_limit();
|
||||
|
||||
/*
|
||||
malloc_set_footprint_limit();
|
||||
Sets the maximum number of bytes to obtain from the system, causing
|
||||
failure returns from malloc and related functions upon attempts to
|
||||
exceed this value. The argument value may be subject to page
|
||||
rounding to an enforceable limit; this actual value is returned.
|
||||
Using an argument of the maximum possible size_t effectively
|
||||
disables checks. If the argument is less than or equal to the
|
||||
current malloc_footprint, then all future allocations that require
|
||||
additional system memory will fail. However, invocation cannot
|
||||
retroactively deallocate existing used memory.
|
||||
*/
|
||||
DLMALLOC_EXPORT size_t dlmalloc_set_footprint_limit(size_t bytes);
|
||||
|
||||
#if MALLOC_INSPECT_ALL
|
||||
/*
|
||||
malloc_inspect_all(void(*handler)(void *start,
|
||||
void *end,
|
||||
size_t used_bytes,
|
||||
void* callback_arg),
|
||||
void* arg);
|
||||
Traverses the heap and calls the given handler for each managed
|
||||
region, skipping all bytes that are (or may be) used for bookkeeping
|
||||
purposes. Traversal does not include include chunks that have been
|
||||
directly memory mapped. Each reported region begins at the start
|
||||
address, and continues up to but not including the end address. The
|
||||
first used_bytes of the region contain allocated data. If
|
||||
used_bytes is zero, the region is unallocated. The handler is
|
||||
invoked with the given callback argument. If locks are defined, they
|
||||
are held during the entire traversal. It is a bad idea to invoke
|
||||
other malloc functions from within the handler.
|
||||
|
||||
For example, to count the number of in-use chunks with size greater
|
||||
than 1000, you could write:
|
||||
static int count = 0;
|
||||
void count_chunks(void* start, void* end, size_t used, void* arg) {
|
||||
if (used >= 1000) ++count;
|
||||
}
|
||||
then:
|
||||
malloc_inspect_all(count_chunks, NULL);
|
||||
|
||||
malloc_inspect_all is compiled only if MALLOC_INSPECT_ALL is defined.
|
||||
*/
|
||||
DLMALLOC_EXPORT void dlmalloc_inspect_all(void(*handler)(void*, void *, size_t, void*),
|
||||
void* arg);
|
||||
|
||||
#endif /* MALLOC_INSPECT_ALL */
|
||||
|
||||
/*
|
||||
mallinfo()
|
||||
Returns (by copy) a struct containing various summary statistics:
|
||||
|
||||
arena: current total non-mmapped bytes allocated from system
|
||||
ordblks: the number of free chunks
|
||||
smblks: always zero.
|
||||
hblks: current number of mmapped regions
|
||||
hblkhd: total bytes held in mmapped regions
|
||||
usmblks: the maximum total allocated space. This will be greater
|
||||
than current total if trimming has occurred.
|
||||
fsmblks: always zero
|
||||
uordblks: current total allocated space (normal or mmapped)
|
||||
fordblks: total free space
|
||||
keepcost: the maximum number of bytes that could ideally be released
|
||||
back to system via malloc_trim. ("ideally" means that
|
||||
it ignores page restrictions etc.)
|
||||
|
||||
Because these fields are ints, but internal bookkeeping may
|
||||
be kept as longs, the reported values may wrap around zero and
|
||||
thus be inaccurate.
|
||||
*/
|
||||
DLMALLOC_EXPORT struct mallinfo dlmallinfo(void);
|
||||
|
||||
/*
|
||||
independent_calloc(size_t n_elements, size_t element_size, void* chunks[]);
|
||||
|
||||
independent_calloc is similar to calloc, but instead of returning a
|
||||
single cleared space, it returns an array of pointers to n_elements
|
||||
independent elements that can hold contents of size elem_size, each
|
||||
of which starts out cleared, and can be independently freed,
|
||||
realloc'ed etc. The elements are guaranteed to be adjacently
|
||||
allocated (this is not guaranteed to occur with multiple callocs or
|
||||
mallocs), which may also improve cache locality in some
|
||||
applications.
|
||||
|
||||
The "chunks" argument is optional (i.e., may be null, which is
|
||||
probably the most typical usage). If it is null, the returned array
|
||||
is itself dynamically allocated and should also be freed when it is
|
||||
no longer needed. Otherwise, the chunks array must be of at least
|
||||
n_elements in length. It is filled in with the pointers to the
|
||||
chunks.
|
||||
|
||||
In either case, independent_calloc returns this pointer array, or
|
||||
null if the allocation failed. If n_elements is zero and "chunks"
|
||||
is null, it returns a chunk representing an array with zero elements
|
||||
(which should be freed if not wanted).
|
||||
|
||||
Each element must be freed when it is no longer needed. This can be
|
||||
done all at once using bulk_free.
|
||||
|
||||
independent_calloc simplifies and speeds up implementations of many
|
||||
kinds of pools. It may also be useful when constructing large data
|
||||
structures that initially have a fixed number of fixed-sized nodes,
|
||||
but the number is not known at compile time, and some of the nodes
|
||||
may later need to be freed. For example:
|
||||
|
||||
struct Node { int item; struct Node* next; };
|
||||
|
||||
struct Node* build_list() {
|
||||
struct Node** pool;
|
||||
int n = read_number_of_nodes_needed();
|
||||
if (n <= 0) return 0;
|
||||
pool = (struct Node**)(independent_calloc(n, sizeof(struct Node), 0);
|
||||
if (pool == 0) die();
|
||||
// organize into a linked list...
|
||||
struct Node* first = pool[0];
|
||||
for (i = 0; i < n-1; ++i)
|
||||
pool[i]->next = pool[i+1];
|
||||
free(pool); // Can now free the array (or not, if it is needed later)
|
||||
return first;
|
||||
}
|
||||
*/
|
||||
DLMALLOC_EXPORT void** dlindependent_calloc(size_t, size_t, void**);
|
||||
|
||||
/*
|
||||
independent_comalloc(size_t n_elements, size_t sizes[], void* chunks[]);
|
||||
|
||||
independent_comalloc allocates, all at once, a set of n_elements
|
||||
chunks with sizes indicated in the "sizes" array. It returns
|
||||
an array of pointers to these elements, each of which can be
|
||||
independently freed, realloc'ed etc. The elements are guaranteed to
|
||||
be adjacently allocated (this is not guaranteed to occur with
|
||||
multiple callocs or mallocs), which may also improve cache locality
|
||||
in some applications.
|
||||
|
||||
The "chunks" argument is optional (i.e., may be null). If it is null
|
||||
the returned array is itself dynamically allocated and should also
|
||||
be freed when it is no longer needed. Otherwise, the chunks array
|
||||
must be of at least n_elements in length. It is filled in with the
|
||||
pointers to the chunks.
|
||||
|
||||
In either case, independent_comalloc returns this pointer array, or
|
||||
null if the allocation failed. If n_elements is zero and chunks is
|
||||
null, it returns a chunk representing an array with zero elements
|
||||
(which should be freed if not wanted).
|
||||
|
||||
Each element must be freed when it is no longer needed. This can be
|
||||
done all at once using bulk_free.
|
||||
|
||||
independent_comallac differs from independent_calloc in that each
|
||||
element may have a different size, and also that it does not
|
||||
automatically clear elements.
|
||||
|
||||
independent_comalloc can be used to speed up allocation in cases
|
||||
where several structs or objects must always be allocated at the
|
||||
same time. For example:
|
||||
|
||||
struct Head { ... }
|
||||
struct Foot { ... }
|
||||
|
||||
void send_message(char* msg) {
|
||||
int msglen = strlen(msg);
|
||||
size_t sizes[3] = { sizeof(struct Head), msglen, sizeof(struct Foot) };
|
||||
void* chunks[3];
|
||||
if (independent_comalloc(3, sizes, chunks) == 0)
|
||||
die();
|
||||
struct Head* head = (struct Head*)(chunks[0]);
|
||||
char* body = (char*)(chunks[1]);
|
||||
struct Foot* foot = (struct Foot*)(chunks[2]);
|
||||
// ...
|
||||
}
|
||||
|
||||
In general though, independent_comalloc is worth using only for
|
||||
larger values of n_elements. For small values, you probably won't
|
||||
detect enough difference from series of malloc calls to bother.
|
||||
|
||||
Overuse of independent_comalloc can increase overall memory usage,
|
||||
since it cannot reuse existing noncontiguous small chunks that
|
||||
might be available for some of the elements.
|
||||
*/
|
||||
DLMALLOC_EXPORT void** dlindependent_comalloc(size_t, size_t*, void**);
|
||||
|
||||
/*
|
||||
bulk_free(void* array[], size_t n_elements)
|
||||
Frees and clears (sets to null) each non-null pointer in the given
|
||||
array. This is likely to be faster than freeing them one-by-one.
|
||||
If footers are used, pointers that have been allocated in different
|
||||
mspaces are not freed or cleared, and the count of all such pointers
|
||||
is returned. For large arrays of pointers with poor locality, it
|
||||
may be worthwhile to sort this array before calling bulk_free.
|
||||
*/
|
||||
DLMALLOC_EXPORT size_t dlbulk_free(void**, size_t n_elements);
|
||||
|
||||
/*
|
||||
pvalloc(size_t n);
|
||||
Equivalent to valloc(minimum-page-that-holds(n)), that is,
|
||||
round up n to nearest pagesize.
|
||||
*/
|
||||
DLMALLOC_EXPORT void* dlpvalloc(size_t);
|
||||
|
||||
/*
|
||||
malloc_trim(size_t pad);
|
||||
|
||||
If possible, gives memory back to the system (via negative arguments
|
||||
to sbrk) if there is unused memory at the `high' end of the malloc
|
||||
pool or in unused MMAP segments. You can call this after freeing
|
||||
large blocks of memory to potentially reduce the system-level memory
|
||||
requirements of a program. However, it cannot guarantee to reduce
|
||||
memory. Under some allocation patterns, some large free blocks of
|
||||
memory will be locked between two used chunks, so they cannot be
|
||||
given back to the system.
|
||||
|
||||
The `pad' argument to malloc_trim represents the amount of free
|
||||
trailing space to leave untrimmed. If this argument is zero, only
|
||||
the minimum amount of memory to maintain internal data structures
|
||||
will be left. Non-zero arguments can be supplied to maintain enough
|
||||
trailing space to service future expected allocations without having
|
||||
to re-obtain memory from the system.
|
||||
|
||||
Malloc_trim returns 1 if it actually released any memory, else 0.
|
||||
*/
|
||||
DLMALLOC_EXPORT int dlmalloc_trim(size_t);
|
||||
|
||||
/*
|
||||
malloc_stats();
|
||||
Prints on stderr the amount of space obtained from the system (both
|
||||
via sbrk and mmap), the maximum amount (which may be more than
|
||||
current if malloc_trim and/or munmap got called), and the current
|
||||
number of bytes allocated via malloc (or realloc, etc) but not yet
|
||||
freed. Note that this is the number of bytes allocated, not the
|
||||
number requested. It will be larger than the number requested
|
||||
because of alignment and bookkeeping overhead. Because it includes
|
||||
alignment wastage as being in use, this figure may be greater than
|
||||
zero even when no user-level chunks are allocated.
|
||||
|
||||
The reported current and maximum system memory can be inaccurate if
|
||||
a program makes other calls to system memory allocation functions
|
||||
(normally sbrk) outside of malloc.
|
||||
|
||||
malloc_stats prints only the most commonly interesting statistics.
|
||||
More information can be obtained by calling mallinfo.
|
||||
*/
|
||||
DLMALLOC_EXPORT void dlmalloc_stats(void);
|
||||
|
||||
/*
|
||||
malloc_usable_size(void* p);
|
||||
|
||||
Returns the number of bytes you can actually use in
|
||||
an allocated chunk, which may be more than you requested (although
|
||||
often not) due to alignment and minimum size constraints.
|
||||
You can use this many bytes without worrying about
|
||||
overwriting other allocated objects. This is not a particularly great
|
||||
programming practice. malloc_usable_size can be more useful in
|
||||
debugging and assertions, for example:
|
||||
|
||||
p = malloc(n);
|
||||
assert(malloc_usable_size(p) >= 256);
|
||||
*/
|
||||
size_t dlmalloc_usable_size(void*);
|
||||
|
||||
#endif /* ONLY_MSPACES */
|
||||
|
||||
#if MSPACES
|
||||
|
||||
/*
|
||||
mspace is an opaque type representing an independent
|
||||
region of space that supports mspace_malloc, etc.
|
||||
*/
|
||||
typedef void* mspace;
|
||||
|
||||
/*
|
||||
create_mspace creates and returns a new independent space with the
|
||||
given initial capacity, or, if 0, the default granularity size. It
|
||||
returns null if there is no system memory available to create the
|
||||
space. If argument locked is non-zero, the space uses a separate
|
||||
lock to control access. The capacity of the space will grow
|
||||
dynamically as needed to service mspace_malloc requests. You can
|
||||
control the sizes of incremental increases of this space by
|
||||
compiling with a different DEFAULT_GRANULARITY or dynamically
|
||||
setting with mallopt(M_GRANULARITY, value).
|
||||
*/
|
||||
DLMALLOC_EXPORT mspace create_mspace(size_t capacity, int locked);
|
||||
|
||||
/*
|
||||
destroy_mspace destroys the given space, and attempts to return all
|
||||
of its memory back to the system, returning the total number of
|
||||
bytes freed. After destruction, the results of access to all memory
|
||||
used by the space become undefined.
|
||||
*/
|
||||
DLMALLOC_EXPORT size_t destroy_mspace(mspace msp);
|
||||
|
||||
/*
|
||||
create_mspace_with_base uses the memory supplied as the initial base
|
||||
of a new mspace. Part (less than 128*sizeof(size_t) bytes) of this
|
||||
space is used for bookkeeping, so the capacity must be at least this
|
||||
large. (Otherwise 0 is returned.) When this initial space is
|
||||
exhausted, additional memory will be obtained from the system.
|
||||
Destroying this space will deallocate all additionally allocated
|
||||
space (if possible) but not the initial base.
|
||||
*/
|
||||
DLMALLOC_EXPORT mspace create_mspace_with_base(void* base, size_t capacity, int locked);
|
||||
|
||||
/*
|
||||
mspace_track_large_chunks controls whether requests for large chunks
|
||||
are allocated in their own untracked mmapped regions, separate from
|
||||
others in this mspace. By default large chunks are not tracked,
|
||||
which reduces fragmentation. However, such chunks are not
|
||||
necessarily released to the system upon destroy_mspace. Enabling
|
||||
tracking by setting to true may increase fragmentation, but avoids
|
||||
leakage when relying on destroy_mspace to release all memory
|
||||
allocated using this space. The function returns the previous
|
||||
setting.
|
||||
*/
|
||||
DLMALLOC_EXPORT int mspace_track_large_chunks(mspace msp, int enable);
|
||||
|
||||
|
||||
/*
|
||||
mspace_malloc behaves as malloc, but operates within
|
||||
the given space.
|
||||
*/
|
||||
DLMALLOC_EXPORT void* mspace_malloc(mspace msp, size_t bytes);
|
||||
|
||||
/*
|
||||
mspace_free behaves as free, but operates within
|
||||
the given space.
|
||||
|
||||
If compiled with FOOTERS==1, mspace_free is not actually needed.
|
||||
free may be called instead of mspace_free because freed chunks from
|
||||
any space are handled by their originating spaces.
|
||||
*/
|
||||
DLMALLOC_EXPORT void mspace_free(mspace msp, void* mem);
|
||||
|
||||
/*
|
||||
mspace_realloc behaves as realloc, but operates within
|
||||
the given space.
|
||||
|
||||
If compiled with FOOTERS==1, mspace_realloc is not actually
|
||||
needed. realloc may be called instead of mspace_realloc because
|
||||
realloced chunks from any space are handled by their originating
|
||||
spaces.
|
||||
*/
|
||||
DLMALLOC_EXPORT void* mspace_realloc(mspace msp, void* mem, size_t newsize);
|
||||
|
||||
/*
|
||||
mspace_calloc behaves as calloc, but operates within
|
||||
the given space.
|
||||
*/
|
||||
DLMALLOC_EXPORT void* mspace_calloc(mspace msp, size_t n_elements, size_t elem_size);
|
||||
|
||||
/*
|
||||
mspace_memalign behaves as memalign, but operates within
|
||||
the given space.
|
||||
*/
|
||||
DLMALLOC_EXPORT void* mspace_memalign(mspace msp, size_t alignment, size_t bytes);
|
||||
|
||||
/*
|
||||
mspace_independent_calloc behaves as independent_calloc, but
|
||||
operates within the given space.
|
||||
*/
|
||||
DLMALLOC_EXPORT void** mspace_independent_calloc(mspace msp, size_t n_elements,
|
||||
size_t elem_size, void* chunks[]);
|
||||
|
||||
/*
|
||||
mspace_independent_comalloc behaves as independent_comalloc, but
|
||||
operates within the given space.
|
||||
*/
|
||||
DLMALLOC_EXPORT void** mspace_independent_comalloc(mspace msp, size_t n_elements,
|
||||
size_t sizes[], void* chunks[]);
|
||||
|
||||
/*
|
||||
mspace_footprint() returns the number of bytes obtained from the
|
||||
system for this space.
|
||||
*/
|
||||
DLMALLOC_EXPORT size_t mspace_footprint(mspace msp);
|
||||
|
||||
/*
|
||||
mspace_max_footprint() returns the peak number of bytes obtained from the
|
||||
system for this space.
|
||||
*/
|
||||
DLMALLOC_EXPORT size_t mspace_max_footprint(mspace msp);
|
||||
|
||||
|
||||
#if !NO_MALLINFO
|
||||
/*
|
||||
mspace_mallinfo behaves as mallinfo, but reports properties of
|
||||
the given space.
|
||||
*/
|
||||
DLMALLOC_EXPORT struct mallinfo mspace_mallinfo(mspace msp);
|
||||
#endif /* NO_MALLINFO */
|
||||
|
||||
/*
|
||||
malloc_usable_size(void* p) behaves the same as malloc_usable_size;
|
||||
*/
|
||||
DLMALLOC_EXPORT size_t mspace_usable_size(const void* mem);
|
||||
|
||||
/*
|
||||
mspace_malloc_stats behaves as malloc_stats, but reports
|
||||
properties of the given space.
|
||||
*/
|
||||
DLMALLOC_EXPORT void mspace_malloc_stats(mspace msp);
|
||||
|
||||
/*
|
||||
mspace_trim behaves as malloc_trim, but
|
||||
operates within the given space.
|
||||
*/
|
||||
DLMALLOC_EXPORT int mspace_trim(mspace msp, size_t pad);
|
||||
|
||||
/*
|
||||
An alias for mallopt.
|
||||
*/
|
||||
DLMALLOC_EXPORT int mspace_mallopt(int, int);
|
||||
|
||||
#endif /* MSPACES */
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* end of extern "C" */
|
||||
#endif /* __cplusplus */
|
||||
|
||||
/*
|
||||
========================================================================
|
||||
To make a fully customizable malloc.h header file, cut everything
|
||||
|
@ -4364,7 +3783,7 @@ int dlmallopt(int param_number, int value) {
|
|||
return change_mparam(param_number, value);
|
||||
}
|
||||
|
||||
size_t dlmalloc_usable_size(void* mem) {
|
||||
size_t dlmalloc_usable_size(const void* mem) {
|
||||
mchunkptr p;
|
||||
size_t bytes;
|
||||
if (mem) {
|
2
third_party/dlmalloc/dlmalloc.h
vendored
2
third_party/dlmalloc/dlmalloc.h
vendored
|
@ -505,6 +505,8 @@ void mspace_inspect_all(mspace msp,
|
|||
void (*handler)(void*, void*, size_t, void*),
|
||||
void* arg);
|
||||
|
||||
void dlmalloc_abort(void);
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
#endif /* COSMOPOLITAN_THIRD_PARTY_DLMALLOC_DLMALLOC_H_ */
|
||||
|
|
1
third_party/dlmalloc/dlmalloc.mk
vendored
1
third_party/dlmalloc/dlmalloc.mk
vendored
|
@ -52,6 +52,7 @@ $(THIRD_PARTY_DLMALLOC_A).pkg: \
|
|||
$(THIRD_PARTY_DLMALLOC_A_OBJS): \
|
||||
OVERRIDE_CFLAGS += \
|
||||
$(NO_MAGIC) \
|
||||
-ffreestanding \
|
||||
-ffunction-sections \
|
||||
-fdata-sections
|
||||
|
||||
|
|
29
third_party/dlmalloc/dlmalloc_abort.greg.c
vendored
Normal file
29
third_party/dlmalloc/dlmalloc_abort.greg.c
vendored
Normal file
|
@ -0,0 +1,29 @@
|
|||
/*-*- 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/bits/weaken.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/log/log.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "third_party/dlmalloc/dlmalloc.h"
|
||||
|
||||
void dlmalloc_abort(void) {
|
||||
kprintf("dlmalloc_abort()%n");
|
||||
if (weaken(__die)) weaken(__die)();
|
||||
_Exit(44);
|
||||
}
|
1027
third_party/linenoise/linenoise.c
vendored
1027
third_party/linenoise/linenoise.c
vendored
File diff suppressed because it is too large
Load diff
20
third_party/linenoise/linenoise.h
vendored
20
third_party/linenoise/linenoise.h
vendored
|
@ -1,8 +1,11 @@
|
|||
#ifndef COSMOPOLITAN_THIRD_PARTY_LINENOISE_LINENOISE_H_
|
||||
#define COSMOPOLITAN_THIRD_PARTY_LINENOISE_LINENOISE_H_
|
||||
#include "libc/calls/struct/winsize.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
struct linenoiseState;
|
||||
typedef struct linenoiseCompletions {
|
||||
size_t len;
|
||||
char **cvec;
|
||||
|
@ -13,17 +16,14 @@ typedef char *(linenoiseHintsCallback)(const char *, const char **,
|
|||
const char **);
|
||||
typedef void(linenoiseFreeHintsCallback)(void *);
|
||||
typedef wint_t(linenoiseXlatCallback)(wint_t);
|
||||
typedef int(linenoisePollCallback)(int, int);
|
||||
|
||||
void linenoiseSetCompletionCallback(linenoiseCompletionCallback *);
|
||||
void linenoiseSetHintsCallback(linenoiseHintsCallback *);
|
||||
void linenoiseSetFreeHintsCallback(linenoiseFreeHintsCallback *);
|
||||
void linenoiseAddCompletion(linenoiseCompletions *, const char *);
|
||||
void linenoiseSetXlatCallback(linenoiseXlatCallback *);
|
||||
void linenoiseSetPollCallback(linenoisePollCallback *);
|
||||
|
||||
char *linenoise(const char *) dontdiscard;
|
||||
char *linenoiseRaw(const char *, int, int) dontdiscard;
|
||||
char *linenoiseWithHistory(const char *, const char *) dontdiscard;
|
||||
int linenoiseHistoryAdd(const char *);
|
||||
int linenoiseHistorySave(const char *);
|
||||
|
@ -33,8 +33,22 @@ void linenoiseHistoryFree(void);
|
|||
void linenoiseClearScreen(int);
|
||||
void linenoiseMaskModeEnable(void);
|
||||
void linenoiseMaskModeDisable(void);
|
||||
int linenoiseEnableRawMode(int);
|
||||
void linenoiseDisableRawMode(void);
|
||||
void linenoiseFree(void *);
|
||||
int linenoiseIsTerminal(void);
|
||||
int linenoiseIsTeletype(void);
|
||||
|
||||
char *linenoiseGetHistoryPath(const char *);
|
||||
struct linenoiseState *linenoiseBegin(const char *, int, int);
|
||||
ssize_t linenoiseEdit(struct linenoiseState *, const char *, char **, bool);
|
||||
int linenoiseGetInterrupt(void);
|
||||
void linenoiseEnd(struct linenoiseState *);
|
||||
|
||||
char *linenoiseGetLine(FILE *);
|
||||
struct winsize linenoiseGetTerminalSize(struct winsize, int, int);
|
||||
void linenoiseRefreshLine(struct linenoiseState *);
|
||||
char *linenoiseRaw(const char *, int, int) dontdiscard;
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
|
|
1
third_party/linenoise/linenoise.mk
vendored
1
third_party/linenoise/linenoise.mk
vendored
|
@ -25,6 +25,7 @@ THIRD_PARTY_LINENOISE_A_DIRECTDEPS = \
|
|||
LIBC_SOCK \
|
||||
LIBC_STDIO \
|
||||
LIBC_RUNTIME \
|
||||
LIBC_LOG \
|
||||
LIBC_SYSV_CALLS \
|
||||
LIBC_STR \
|
||||
LIBC_UNICODE \
|
||||
|
|
89
third_party/lua/lrepl.c
vendored
89
third_party/lua/lrepl.c
vendored
|
@ -1,6 +1,9 @@
|
|||
#define lua_c
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/sigbits.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/intrin/nomultics.internal.h"
|
||||
#include "libc/intrin/spinlock.h"
|
||||
#include "libc/log/check.h"
|
||||
#include "libc/runtime/gc.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
|
@ -13,8 +16,13 @@
|
|||
#include "third_party/lua/lualib.h"
|
||||
// clang-format off
|
||||
|
||||
bool lua_repl_blocking;
|
||||
bool lua_repl_isterminal;
|
||||
_Alignas(64) char lualock;
|
||||
struct linenoiseState *lua_repl_linenoise;
|
||||
static lua_State *globalL;
|
||||
static const char *g_progname;
|
||||
static const char *g_historypath;
|
||||
|
||||
/*
|
||||
** {==================================================================
|
||||
|
@ -111,14 +119,35 @@ static int incomplete (lua_State *L, int status) {
|
|||
/*
|
||||
** Prompt the user, read a line, and push it into the Lua stack.
|
||||
*/
|
||||
static int pushline (lua_State *L, int firstline) {
|
||||
static ssize_t pushline (lua_State *L, int firstline) {
|
||||
char *b;
|
||||
size_t l;
|
||||
ssize_t rc;
|
||||
char *prmt;
|
||||
globalL = L;
|
||||
const char *prmt = get_prompt(L, firstline);
|
||||
if (!(b = linenoiseWithHistory(prmt, g_progname)))
|
||||
return 0; /* no input (prompt will be popped by caller) */
|
||||
lua_pop(L, 1); /* remove prompt */
|
||||
if (lua_repl_isterminal) {
|
||||
prmt = strdup(get_prompt(L, firstline));
|
||||
lua_pop(L, 1); /* remove prompt */
|
||||
_spunlock(&lualock);
|
||||
rc = linenoiseEdit(lua_repl_linenoise, prmt, &b, !firstline || lua_repl_blocking);
|
||||
free(prmt);
|
||||
if (rc != -1) {
|
||||
if (b && *b) {
|
||||
linenoiseHistoryLoad(g_historypath);
|
||||
linenoiseHistoryAdd(b);
|
||||
linenoiseHistorySave(g_historypath);
|
||||
}
|
||||
}
|
||||
_spinlock(&lualock);
|
||||
} else {
|
||||
_spunlock(&lualock);
|
||||
b = linenoiseGetLine(stdin);
|
||||
_spinlock(&lualock);
|
||||
rc = b ? 1 : -1;
|
||||
}
|
||||
if (rc == -1 || (!rc && !b)) {
|
||||
return rc;
|
||||
}
|
||||
l = strlen(b);
|
||||
if (l > 0 && b[l-1] == '\n') /* line ends with newline? */
|
||||
b[--l] = '\0'; /* remove it */
|
||||
|
@ -164,9 +193,10 @@ static void lstop (lua_State *L, lua_Debug *ar) {
|
|||
static int multiline (lua_State *L) {
|
||||
for (;;) { /* repeat until gets a complete statement */
|
||||
size_t len;
|
||||
ssize_t rc;
|
||||
const char *line = lua_tolstring(L, 1, &len); /* get what it has */
|
||||
int status = luaL_loadbuffer(L, line, len, "=stdin"); /* try it */
|
||||
if (!incomplete(L, status) || !pushline(L, 0)) {
|
||||
if (!incomplete(L, status) || pushline(L, 0) != 1) {
|
||||
return status; /* cannot or should not try to add continuation line */
|
||||
}
|
||||
lua_pushliteral(L, "\n"); /* add newline... */
|
||||
|
@ -176,11 +206,38 @@ static int multiline (lua_State *L) {
|
|||
}
|
||||
|
||||
|
||||
void lua_initrepl(const char *progname) {
|
||||
void lua_initrepl(lua_State *L, const char *progname) {
|
||||
const char *prompt;
|
||||
_spinlock(&lualock);
|
||||
g_progname = progname;
|
||||
linenoiseSetCompletionCallback(lua_readline_completions);
|
||||
linenoiseSetHintsCallback(lua_readline_hint);
|
||||
linenoiseSetFreeHintsCallback(free);
|
||||
if ((lua_repl_isterminal = linenoiseIsTerminal())) {
|
||||
linenoiseSetCompletionCallback(lua_readline_completions);
|
||||
linenoiseSetHintsCallback(lua_readline_hint);
|
||||
linenoiseSetFreeHintsCallback(free);
|
||||
prompt = get_prompt(L, 1);
|
||||
if ((g_historypath = linenoiseGetHistoryPath(progname))) {
|
||||
if (linenoiseHistoryLoad(g_historypath) == -1) {
|
||||
kprintf("%r%s: failed to load history: %m%n", g_historypath);
|
||||
free(g_historypath);
|
||||
g_historypath = 0;
|
||||
}
|
||||
}
|
||||
lua_repl_linenoise = linenoiseBegin(prompt, 0, 1);
|
||||
lua_pop(L, 1); /* remove prompt */
|
||||
__nomultics = 2;
|
||||
__replmode = true;
|
||||
}
|
||||
_spunlock(&lualock);
|
||||
}
|
||||
|
||||
|
||||
void lua_freerepl(void) {
|
||||
_spinlock(&lualock);
|
||||
__nomultics = false;
|
||||
__replmode = false;
|
||||
linenoiseEnd(lua_repl_linenoise);
|
||||
free(g_historypath);
|
||||
_spunlock(&lualock);
|
||||
}
|
||||
|
||||
|
||||
|
@ -189,16 +246,24 @@ void lua_initrepl(const char *progname) {
|
|||
** adding "return " in front of it) and second as a statement. Return
|
||||
** the final status of load/call with the resulting function (if any)
|
||||
** in the top of the stack.
|
||||
**
|
||||
** returns -1 on eof
|
||||
** returns -2 on error
|
||||
*/
|
||||
int lua_loadline (lua_State *L) {
|
||||
ssize_t rc;
|
||||
int status;
|
||||
lua_settop(L, 0);
|
||||
if (!pushline(L, 1))
|
||||
return -1; /* no input */
|
||||
_spinlock(&lualock);
|
||||
if ((rc = pushline(L, 1)) != 1) {
|
||||
_spunlock(&lualock);
|
||||
return rc - 1; /* eof or error */
|
||||
}
|
||||
if ((status = addreturn(L)) != LUA_OK) /* 'return ...' did not work? */
|
||||
status = multiline(L); /* try as command, maybe with continuation lines */
|
||||
lua_remove(L, 1); /* remove line from the stack */
|
||||
lua_assert(lua_gettop(L) == 1);
|
||||
_spunlock(&lualock);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
|
8
third_party/lua/lrepl.h
vendored
8
third_party/lua/lrepl.h
vendored
|
@ -5,9 +5,15 @@
|
|||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
extern char lualock;
|
||||
extern bool lua_repl_blocking;
|
||||
extern bool lua_repl_isterminal;
|
||||
extern struct linenoiseState *lua_repl_linenoise;
|
||||
|
||||
void lua_freerepl(void);
|
||||
int lua_loadline(lua_State *);
|
||||
void lua_l_print(lua_State *);
|
||||
void lua_initrepl(const char *);
|
||||
void lua_initrepl(lua_State *, const char *);
|
||||
int lua_report(lua_State *, int);
|
||||
int lua_runchunk(lua_State *, int, int);
|
||||
void lua_l_message(const char *, const char *);
|
||||
|
|
31
third_party/lua/lua.main.c
vendored
31
third_party/lua/lua.main.c
vendored
|
@ -10,14 +10,19 @@
|
|||
#include "libc/calls/sigbits.h"
|
||||
#include "libc/calls/struct/sigaction.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/log/log.h"
|
||||
#include "libc/runtime/gc.h"
|
||||
#include "libc/runtime/stack.h"
|
||||
#include "libc/sock/sock.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/exit.h"
|
||||
#include "libc/sysv/consts/poll.h"
|
||||
#include "libc/sysv/consts/sa.h"
|
||||
#include "libc/x/x.h"
|
||||
#include "third_party/linenoise/linenoise.h"
|
||||
#include "third_party/lua/cosmo.h"
|
||||
#include "third_party/lua/lauxlib.h"
|
||||
#include "third_party/lua/lprefix.h"
|
||||
#include "third_party/lua/lrepl.h"
|
||||
|
@ -275,8 +280,27 @@ static void doREPL (lua_State *L) {
|
|||
int status;
|
||||
const char *oldprogname = progname;
|
||||
progname = NULL; /* no 'progname' on errors in interactive mode */
|
||||
lua_initrepl(LUA_PROGNAME);
|
||||
while ((status = lua_loadline(L)) != -1) {
|
||||
lua_initrepl(L, LUA_PROGNAME);
|
||||
for (;;) {
|
||||
linenoiseEnableRawMode(0);
|
||||
TryAgain:
|
||||
status = lua_loadline(L);
|
||||
if (status == -2 && errno == EAGAIN) {
|
||||
errno = 0;
|
||||
poll(&(struct pollfd){0, POLLIN}, 1, -1);
|
||||
goto TryAgain;
|
||||
}
|
||||
linenoiseDisableRawMode();
|
||||
if (status == -1) {
|
||||
break;
|
||||
} else if (status == -2) {
|
||||
lua_pushfstring(L, "read error: %s", strerror(errno));
|
||||
lua_report(L, status);
|
||||
lua_freerepl();
|
||||
progname = oldprogname;
|
||||
return;
|
||||
}
|
||||
lua_writeline();
|
||||
if (status == LUA_OK)
|
||||
status = lua_runchunk(L, 0, LUA_MULTRET);
|
||||
if (status == LUA_OK) {
|
||||
|
@ -285,6 +309,7 @@ static void doREPL (lua_State *L) {
|
|||
lua_report(L, status);
|
||||
}
|
||||
}
|
||||
lua_freerepl();
|
||||
lua_settop(L, 0); /* clear stack */
|
||||
lua_writeline();
|
||||
progname = oldprogname;
|
||||
|
@ -343,7 +368,7 @@ static int pmain (lua_State *L) {
|
|||
int main (int argc, char **argv) {
|
||||
int status, result;
|
||||
lua_State *L;
|
||||
if (!IsModeDbg()) {
|
||||
if (IsModeDbg()) {
|
||||
ShowCrashReports();
|
||||
}
|
||||
/* if (IsModeDbg()) ShowCrashReports(); */
|
||||
|
|
|
@ -103,22 +103,21 @@ static void AppendArg(struct Args *l, char *s) {
|
|||
static void MakeHeader(struct Header *h, const char *name, int ref, int mode,
|
||||
int size) {
|
||||
size_t n;
|
||||
char buf[24];
|
||||
memset(h, ' ', sizeof(*h));
|
||||
n = strlen(name);
|
||||
memcpy(h->name, name, n);
|
||||
if (ref != -1) {
|
||||
memcpy(h->name + n, buf, uint64toarray_radix10(ref, buf));
|
||||
FormatUint32(h->name + n, ref);
|
||||
}
|
||||
if (strcmp(name, "//") != 0) {
|
||||
h->date[0] = '0';
|
||||
h->uid[0] = '0';
|
||||
h->gid[0] = '0';
|
||||
memcpy(h->mode, buf, uint64toarray_radix8(mode & 0777, buf));
|
||||
FormatOctal32(h->mode, mode & 0777, false);
|
||||
}
|
||||
h->fmag[0] = '`';
|
||||
h->fmag[1] = '\n';
|
||||
memcpy(h->size, buf, uint64toarray_radix10(size, buf));
|
||||
FormatUint32(h->size, size);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
|
|
|
@ -1271,7 +1271,7 @@ static void DrawXmm(struct Panel *p, long i, long r) {
|
|||
uint64toarray_fixed16(ival, buf, xmmtype.size[r] * 8);
|
||||
}
|
||||
} else {
|
||||
int64toarray_radix10(SignExtend(ival, xmmtype.size[r] * 8), buf);
|
||||
FormatInt64(buf, SignExtend(ival, xmmtype.size[r] * 8));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
@ -2270,7 +2270,8 @@ 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);
|
||||
}
|
||||
|
|
|
@ -30,9 +30,9 @@ size_t FormatCga(uint8_t bgfg, char buf[hasatleast 11]) {
|
|||
char *p = buf;
|
||||
*p++ = '\e';
|
||||
*p++ = '[';
|
||||
p += uint64toarray_radix10(kCgaToAnsi[(bgfg & 0xF0) >> 4] + 10, p);
|
||||
p = FormatUint32(p, kCgaToAnsi[(bgfg & 0xF0) >> 4] + 10);
|
||||
*p++ = ';';
|
||||
p += uint64toarray_radix10(kCgaToAnsi[bgfg & 0x0F], p);
|
||||
p = FormatUint32(p, kCgaToAnsi[bgfg & 0x0F]);
|
||||
*p++ = 'm';
|
||||
*p = '\0';
|
||||
return p - buf;
|
||||
|
|
|
@ -128,7 +128,7 @@ static char *DisRegisterWord(struct Dis *d, uint32_t rde, char *p, bool g,
|
|||
|
||||
static char *DisInt(char *p, int64_t x) {
|
||||
if (-15 <= x && x <= 15) {
|
||||
p += int64toarray_radix10(x, p);
|
||||
p = FormatInt64(p, x);
|
||||
} else if (x == INT64_MIN) {
|
||||
p = stpcpy(p, "-0x");
|
||||
p += uint64toarray_radix16(INT64_MIN, p);
|
||||
|
@ -319,7 +319,8 @@ static char *DisRegMem(struct Dis *d, uint32_t rde, char *p,
|
|||
}
|
||||
|
||||
static dontinline char *DisE(struct Dis *d, uint32_t rde, char *p,
|
||||
char *f(struct Dis *, uint32_t, char *, bool, int)) {
|
||||
char *f(struct Dis *, uint32_t, char *, bool,
|
||||
int)) {
|
||||
if (IsModrmRegister(rde)) {
|
||||
return f(d, rde, p, Rexb(rde), ModrmRm(rde));
|
||||
} else {
|
||||
|
@ -471,13 +472,13 @@ static char *DisOne(struct Dis *d, uint32_t rde, char *p) {
|
|||
|
||||
static char *DisJbs(struct Dis *d, uint32_t rde, char *p) {
|
||||
if (d->xedd->op.disp > 0) *p++ = '+';
|
||||
p += int64toarray_radix10(d->xedd->op.disp, p);
|
||||
p = FormatInt64(p, d->xedd->op.disp);
|
||||
return p;
|
||||
}
|
||||
|
||||
static char *DisJb(struct Dis *d, uint32_t rde, char *p) {
|
||||
if (d->xedd->op.disp > 0) *p++ = '+';
|
||||
p += uint64toarray_radix10(d->xedd->op.disp & 0xff, p);
|
||||
p = FormatUint32(p, d->xedd->op.disp & 0xff);
|
||||
return p;
|
||||
}
|
||||
|
||||
|
@ -522,7 +523,7 @@ static char *DisXmm(struct Dis *d, uint32_t rde, char *p, const char *s,
|
|||
p = HighStart(p, g_high.reg);
|
||||
*p++ = '%';
|
||||
p = stpcpy(p, s);
|
||||
p += uint64toarray_radix10(reg, p);
|
||||
p = FormatUint32(p, reg);
|
||||
p = HighEnd(p);
|
||||
return p;
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@ struct High g_high;
|
|||
char *HighStart(char *p, int h) {
|
||||
if (h) {
|
||||
p = stpcpy(p, "\e[38;5;");
|
||||
p += uint64toarray_radix10(h, p);
|
||||
p = FormatUint32(p, h);
|
||||
p = stpcpy(p, "m");
|
||||
g_high.active = true;
|
||||
}
|
||||
|
|
|
@ -693,9 +693,9 @@ static void PtyReportCursorPosition(struct Pty *pty) {
|
|||
p = buf;
|
||||
*p++ = '\e';
|
||||
*p++ = '[';
|
||||
p += uint64toarray_radix10((pty->y + 1) & 0x7fff, p);
|
||||
p = FormatInt32(p, (pty->y + 1) & 0x7fff);
|
||||
*p++ = ';';
|
||||
p += uint64toarray_radix10((pty->x + 1) & 0x7fff, p);
|
||||
p = FormatInt32(p, (pty->x + 1) & 0x7fff);
|
||||
*p++ = 'R';
|
||||
PtyWriteInput(pty, buf, p - buf);
|
||||
}
|
||||
|
@ -1178,7 +1178,8 @@ ssize_t PtyWriteInput(struct Pty *pty, const void *data, size_t n) {
|
|||
m = pty->input.n;
|
||||
if (i + n * 2 + 1 > m) {
|
||||
m = MAX(m, 8);
|
||||
do m += m >> 1;
|
||||
do
|
||||
m += m >> 1;
|
||||
while (i + n * 2 + 1 > m);
|
||||
if (!(p = realloc(p, m))) {
|
||||
return -1;
|
||||
|
@ -1229,18 +1230,18 @@ ssize_t PtyRead(struct Pty *pty, void *buf, size_t size) {
|
|||
static char *PtyEncodeRgb(char *p, int rgb) {
|
||||
*p++ = '2';
|
||||
*p++ = ';';
|
||||
p += uint64toarray_radix10((rgb & 0x0000ff) >> 000, p);
|
||||
p = FormatUint32(p, (rgb & 0x0000ff) >> 000);
|
||||
*p++ = ';';
|
||||
p += uint64toarray_radix10((rgb & 0x00ff00) >> 010, p);
|
||||
p = FormatUint32(p, (rgb & 0x00ff00) >> 010);
|
||||
*p++ = ';';
|
||||
p += uint64toarray_radix10((rgb & 0xff0000) >> 020, p);
|
||||
p = FormatUint32(p, (rgb & 0xff0000) >> 020);
|
||||
return p;
|
||||
}
|
||||
|
||||
static char *PtyEncodeXterm256(char *p, int xt) {
|
||||
*p++ = '5';
|
||||
*p++ = ';';
|
||||
p += uint64toarray_radix10(xt, p);
|
||||
p = FormatUint32(p, xt);
|
||||
return p;
|
||||
}
|
||||
|
||||
|
|
|
@ -661,7 +661,7 @@ static const char *GetErrnoName(int x) {
|
|||
const char *s;
|
||||
static char buf[16];
|
||||
if ((s = strerror_short(x))) return s;
|
||||
int64toarray_radix10(x, buf);
|
||||
FormatInt64(buf, x);
|
||||
return buf;
|
||||
}
|
||||
|
||||
|
|
|
@ -58,7 +58,7 @@ void PrintClosure(struct Closure *c, const char *name, int indent, FILE *f) {
|
|||
fputs(": ", f);
|
||||
Print(c->term, 0, GetDepth(c->envp), f);
|
||||
fputs(" +", f);
|
||||
int64toarray_radix10(c->refs, ibuf);
|
||||
FormatInt64(ibuf, c->refs);
|
||||
fputs(ibuf, f);
|
||||
fputc('\n', f);
|
||||
PrintClosure(c->envp, "envp", indent + 1, f);
|
||||
|
|
|
@ -632,7 +632,7 @@ void PrintVar(int i, FILE* f) {
|
|||
char ibuf[22];
|
||||
switch (style) {
|
||||
case 0:
|
||||
int64toarray_radix10(i, ibuf);
|
||||
FormatInt64(ibuf, i);
|
||||
fputs(ibuf, f);
|
||||
break;
|
||||
case 1:
|
||||
|
@ -642,7 +642,7 @@ void PrintVar(int i, FILE* f) {
|
|||
fputwc(FREEBIES[~i], f);
|
||||
} else {
|
||||
ibuf[0] = '?';
|
||||
int64toarray_radix10(i, ibuf + 1);
|
||||
FormatInt64(ibuf + 1, i);
|
||||
fputs(ibuf, f);
|
||||
}
|
||||
break;
|
||||
|
@ -889,7 +889,7 @@ void PrintDebruijn(int x, int head, int depth, FILE* f) {
|
|||
PrintVar(mem[x + 1], f);
|
||||
} else {
|
||||
fputc(L'‼', f);
|
||||
int64toarray_radix10(x, ibuf);
|
||||
FormatInt64(ibuf, x);
|
||||
fputs(ibuf, f);
|
||||
}
|
||||
} else if (mem[x] == IOP) {
|
||||
|
@ -905,19 +905,19 @@ void PrintDebruijn(int x, int head, int depth, FILE* f) {
|
|||
fputs(asciiname ? "gro" : "⋯", f);
|
||||
} else {
|
||||
fputc(L'!', f);
|
||||
int64toarray_radix10(x, ibuf);
|
||||
FormatInt64(ibuf, x);
|
||||
fputs(ibuf, f);
|
||||
}
|
||||
} else {
|
||||
fputc(L'!', f);
|
||||
int64toarray_radix10(x, ibuf);
|
||||
FormatInt64(ibuf, x);
|
||||
fputs(ibuf, f);
|
||||
}
|
||||
return;
|
||||
}
|
||||
Overflow:
|
||||
fputc(L'‼', f);
|
||||
int64toarray_radix10(x, ibuf);
|
||||
FormatInt64(ibuf, x);
|
||||
fputs(ibuf, f);
|
||||
}
|
||||
|
||||
|
@ -1172,7 +1172,7 @@ void PrintLambda(int x, int head, int depth, int apps, FILE* f) {
|
|||
PrintVar(depth - 1 - mem[x + 1], f);
|
||||
} else {
|
||||
fputc(L'‼', f);
|
||||
int64toarray_radix10(x, ibuf);
|
||||
FormatInt64(ibuf, x);
|
||||
fputs(ibuf, f);
|
||||
}
|
||||
} else if (mem[x] == APP) {
|
||||
|
@ -1198,12 +1198,12 @@ void PrintLambda(int x, int head, int depth, int apps, FILE* f) {
|
|||
fputs(asciiname ? "gro" : "⋯", f);
|
||||
} else {
|
||||
fputc(L'!', f);
|
||||
int64toarray_radix10(x, ibuf);
|
||||
FormatInt64(ibuf, x);
|
||||
fputs(ibuf, f);
|
||||
}
|
||||
} else {
|
||||
fputc(L'!', f);
|
||||
int64toarray_radix10(x, ibuf);
|
||||
FormatInt64(ibuf, x);
|
||||
fputs(ibuf, f);
|
||||
}
|
||||
if (close) {
|
||||
|
@ -1213,7 +1213,7 @@ void PrintLambda(int x, int head, int depth, int apps, FILE* f) {
|
|||
}
|
||||
Overflow:
|
||||
fputc(L'‼', f);
|
||||
int64toarray_radix10(x, ibuf);
|
||||
FormatInt64(ibuf, x);
|
||||
fputs(ibuf, f);
|
||||
}
|
||||
|
||||
|
@ -1239,7 +1239,7 @@ void PrintBinary(int x, int head, int depth, FILE* f) {
|
|||
PrintVar(mem[x + 1], f);
|
||||
} else {
|
||||
fputc(L'‼', f);
|
||||
int64toarray_radix10(x, ibuf);
|
||||
FormatInt64(ibuf, x);
|
||||
fputs(ibuf, f);
|
||||
}
|
||||
} else if (mem[x] == APP) {
|
||||
|
@ -1263,14 +1263,14 @@ void PrintBinary(int x, int head, int depth, FILE* f) {
|
|||
fputwc(L'⋯', f);
|
||||
} else {
|
||||
fputc(L'!', f);
|
||||
int64toarray_radix10(x, ibuf);
|
||||
FormatInt64(ibuf, x);
|
||||
fputs(ibuf, f);
|
||||
}
|
||||
return;
|
||||
}
|
||||
Overflow:
|
||||
fputc(L'‼', f);
|
||||
int64toarray_radix10(x, ibuf);
|
||||
FormatInt64(ibuf, x);
|
||||
fputs(ibuf, f);
|
||||
}
|
||||
|
||||
|
|
|
@ -20,17 +20,17 @@ db:exec[[
|
|||
INSERT INTO test (content) VALUES ('Hello Sqlite3');
|
||||
]]
|
||||
|
||||
-- this intercepts all requests if it's defined
|
||||
function OnHttpRequest()
|
||||
if HasParam('magic') then
|
||||
Write('<p>\r\n')
|
||||
Write('OnHttpRequest() has intercepted your request<br>\r\n')
|
||||
Write('because you specified the magic parameter\r\n')
|
||||
Write('<pre>\r\n')
|
||||
Write(EscapeHtml(LoadAsset('/.init.lua')))
|
||||
Write('</pre>\r\n')
|
||||
else
|
||||
Route() -- this asks redbean to do the default thing
|
||||
end
|
||||
SetHeader('Server', 'redbean!')
|
||||
end
|
||||
-- -- this intercepts all requests if it's defined
|
||||
-- function OnHttpRequest()
|
||||
-- if HasParam('magic') then
|
||||
-- Write('<p>\r\n')
|
||||
-- Write('OnHttpRequest() has intercepted your request<br>\r\n')
|
||||
-- Write('because you specified the magic parameter\r\n')
|
||||
-- Write('<pre>\r\n')
|
||||
-- Write(EscapeHtml(LoadAsset('/.init.lua')))
|
||||
-- Write('</pre>\r\n')
|
||||
-- else
|
||||
-- Route() -- this asks redbean to do the default thing
|
||||
-- end
|
||||
-- SetHeader('Server', 'redbean!')
|
||||
-- end
|
||||
|
|
|
@ -90,7 +90,7 @@ o/$(MODE)/tool/net/%.com.dbg: \
|
|||
|
||||
o/$(MODE)/tool/net/redbean.com.dbg: \
|
||||
$(TOOL_NET_DEPS) \
|
||||
o/$(MODE)/tool/net/redbean.o \
|
||||
o/$(MODE)/tool/net/redbean.o \
|
||||
o/$(MODE)/tool/net/lre.o \
|
||||
o/$(MODE)/tool/net/lunix.o \
|
||||
o/$(MODE)/tool/net/lmaxmind.o \
|
||||
|
@ -208,7 +208,7 @@ o/$(MODE)/tool/net/demo/virtualbean.html.zip.o: \
|
|||
|
||||
o/$(MODE)/tool/net/redbean-demo.com.dbg: \
|
||||
$(TOOL_NET_DEPS) \
|
||||
o/$(MODE)/tool/net/redbean.o \
|
||||
o/$(MODE)/tool/net/redbean.o \
|
||||
o/$(MODE)/tool/net/lre.o \
|
||||
o/$(MODE)/tool/net/lunix.o \
|
||||
o/$(MODE)/tool/net/lmaxmind.o \
|
||||
|
|
|
@ -39,6 +39,7 @@
|
|||
#include "libc/fmt/conv.h"
|
||||
#include "libc/fmt/itoa.h"
|
||||
#include "libc/intrin/nomultics.internal.h"
|
||||
#include "libc/intrin/spinlock.h"
|
||||
#include "libc/log/backtrace.internal.h"
|
||||
#include "libc/log/check.h"
|
||||
#include "libc/log/log.h"
|
||||
|
@ -50,10 +51,12 @@
|
|||
#include "libc/nexgen32e/bsf.h"
|
||||
#include "libc/nexgen32e/bsr.h"
|
||||
#include "libc/nexgen32e/crc32.h"
|
||||
#include "libc/nexgen32e/nt2sysv.h"
|
||||
#include "libc/nexgen32e/rdtsc.h"
|
||||
#include "libc/nexgen32e/rdtscp.h"
|
||||
#include "libc/nt/enum/fileflagandattributes.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/nt/thread.h"
|
||||
#include "libc/rand/rand.h"
|
||||
#include "libc/runtime/clktck.h"
|
||||
#include "libc/runtime/directmap.internal.h"
|
||||
|
@ -362,6 +365,7 @@ static bool printport;
|
|||
static bool daemonize;
|
||||
static bool logrusage;
|
||||
static bool logbodies;
|
||||
static bool isterminal;
|
||||
static bool sslcliused;
|
||||
static bool loglatency;
|
||||
static bool terminated;
|
||||
|
@ -835,12 +839,13 @@ static void DescribeAddress(char buf[40], uint32_t addr, uint16_t port) {
|
|||
char *p;
|
||||
const char *s;
|
||||
p = buf;
|
||||
p += uint64toarray_radix10((addr & 0xFF000000) >> 030, p), *p++ = '.';
|
||||
p += uint64toarray_radix10((addr & 0x00FF0000) >> 020, p), *p++ = '.';
|
||||
p += uint64toarray_radix10((addr & 0x0000FF00) >> 010, p), *p++ = '.';
|
||||
p += uint64toarray_radix10((addr & 0x000000FF) >> 000, p), *p++ = ':';
|
||||
p += uint64toarray_radix10(port, p);
|
||||
*p++ = '\0';
|
||||
p = FormatUint64(p, (addr & 0xFF000000) >> 030), *p++ = '.';
|
||||
p = FormatUint64(p, (addr & 0x00FF0000) >> 020), *p++ = '.';
|
||||
p = FormatUint64(p, (addr & 0x0000FF00) >> 010), *p++ = '.';
|
||||
p = FormatUint64(p, (addr & 0x000000FF) >> 000), *p++ = ':';
|
||||
p = FormatUint64(p, port);
|
||||
*p = '\0';
|
||||
assert(p - buf < 40);
|
||||
}
|
||||
|
||||
static inline void GetServerAddr(uint32_t *ip, uint16_t *port) {
|
||||
|
@ -1029,7 +1034,7 @@ static void Daemonize(void) {
|
|||
umask(0);
|
||||
if (pidpath) {
|
||||
fd = open(pidpath, O_CREAT | O_WRONLY, 0644);
|
||||
WRITE(fd, ibuf, uint64toarray_radix10(getpid(), ibuf));
|
||||
WRITE(fd, ibuf, FormatInt32(ibuf, getpid()) - ibuf);
|
||||
close(fd);
|
||||
}
|
||||
if (!logpath) ProgramLogPath("/dev/null");
|
||||
|
@ -1486,6 +1491,7 @@ static void CertsDestroy(void) {
|
|||
|
||||
static void WipeServingKeys(void) {
|
||||
size_t i;
|
||||
long double t = nowl();
|
||||
if (uniprocess) return;
|
||||
mbedtls_ssl_ticket_free(&ssltick);
|
||||
mbedtls_ssl_key_cert_free(conf.key_cert), conf.key_cert = 0;
|
||||
|
@ -2083,7 +2089,7 @@ static char *AppendExpires(char *p, int64_t t) {
|
|||
static char *AppendCache(char *p, int64_t seconds) {
|
||||
if (seconds < 0) return p;
|
||||
p = stpcpy(p, "Cache-Control: max-age=");
|
||||
p += uint64toarray_radix10(seconds, p);
|
||||
p = FormatUint64(p, seconds);
|
||||
if (seconds) {
|
||||
p = stpcpy(p, ", public");
|
||||
} else {
|
||||
|
@ -2095,21 +2101,21 @@ static char *AppendCache(char *p, int64_t seconds) {
|
|||
|
||||
static inline char *AppendContentLength(char *p, size_t n) {
|
||||
p = stpcpy(p, "Content-Length: ");
|
||||
p += uint64toarray_radix10(n, p);
|
||||
p = FormatUint64(p, n);
|
||||
return AppendCrlf(p);
|
||||
}
|
||||
|
||||
static char *AppendContentRange(char *p, long a, long b, long c) {
|
||||
p = stpcpy(p, "Content-Range: bytes ");
|
||||
if (a >= 0 && b > 0) {
|
||||
p += uint64toarray_radix10(a, p);
|
||||
p = FormatUint64(p, a);
|
||||
*p++ = '-';
|
||||
p += uint64toarray_radix10(a + b - 1, p);
|
||||
p = FormatUint64(p, a + b - 1);
|
||||
} else {
|
||||
*p++ = '*';
|
||||
}
|
||||
*p++ = '/';
|
||||
p += uint64toarray_radix10(c, p);
|
||||
p = FormatUint64(p, c);
|
||||
return AppendCrlf(p);
|
||||
}
|
||||
|
||||
|
@ -6396,6 +6402,7 @@ static bool StreamResponse(char *p) {
|
|||
static bool HandleMessageActual(void) {
|
||||
int rc;
|
||||
char *p;
|
||||
long double now;
|
||||
if ((rc = ParseHttpMessage(&msg, inbuf.p, amtread)) != -1) {
|
||||
if (!rc) return false;
|
||||
hdrsize = rc;
|
||||
|
@ -6437,8 +6444,11 @@ static bool HandleMessageActual(void) {
|
|||
LockInc(&shared->c.messageshandled);
|
||||
++messageshandled;
|
||||
if (loglatency || LOGGABLE(kLogDebug)) {
|
||||
LOGF(kLogDebug, "(stat) %`'.*s latency %,ldµs", msg.uri.b - msg.uri.a,
|
||||
inbuf.p + msg.uri.a, (long)((nowl() - startrequest) * 1e6L));
|
||||
now = nowl();
|
||||
LOGF(kLogDebug, "(stat) %`'.*s latency r: %,ldµs c: %,ldµs",
|
||||
msg.uri.b - msg.uri.a, inbuf.p + msg.uri.a,
|
||||
(long)((now - startrequest) * 1e6L),
|
||||
(long)((now - startconnection) * 1e6L));
|
||||
}
|
||||
if (!generator) {
|
||||
return TransmitResponse(p);
|
||||
|
@ -6702,7 +6712,6 @@ static int EnableSandbox(void) {
|
|||
}
|
||||
}
|
||||
|
||||
// returns 0 otherwise -1 if worker needs to unwind stack and exit
|
||||
static int HandleConnection(size_t i) {
|
||||
int pid, rc = 0;
|
||||
clientaddrsize = sizeof(clientaddr);
|
||||
|
@ -6731,10 +6740,16 @@ static int HandleConnection(size_t i) {
|
|||
__kbirth = rdtsc();
|
||||
}
|
||||
if (funtrace) {
|
||||
ftrace_install();
|
||||
if (ftrace_install() != -1) {
|
||||
g_ftrace = 1;
|
||||
} else {
|
||||
WARNF("ftrace failed to install %m");
|
||||
}
|
||||
}
|
||||
}
|
||||
CHECK_NE(-1, EnableSandbox());
|
||||
if (sandboxed) {
|
||||
CHECK_NE(-1, EnableSandbox());
|
||||
}
|
||||
if (hasonworkerstart) {
|
||||
CallSimpleHook("OnWorkerStart");
|
||||
}
|
||||
|
@ -6824,45 +6839,6 @@ static int HandleConnection(size_t i) {
|
|||
return rc;
|
||||
}
|
||||
|
||||
// returns 2 if we should stay in the redbean event loop
|
||||
// returns 1 if poll() says stdin has user input available
|
||||
// returns 0 if poll() timed out after ms
|
||||
// returns -1 if worker is unwinding exit
|
||||
static int HandlePoll(int ms) {
|
||||
size_t i;
|
||||
int nfds;
|
||||
if ((nfds = poll(polls, 1 + servers.n, ms)) != -1) {
|
||||
for (i = 0; i < servers.n; ++i) {
|
||||
if (polls[1 + i].revents) {
|
||||
serveraddr = &servers.p[i].addr;
|
||||
ishandlingconnection = true;
|
||||
if (HandleConnection(i) == -1) return -1;
|
||||
ishandlingconnection = false;
|
||||
}
|
||||
}
|
||||
// are we polling stdin for the repl?
|
||||
if (polls[0].fd >= 0) {
|
||||
if (polls[0].revents) {
|
||||
return 1; // user entered a keystroke
|
||||
} else if (!nfds) {
|
||||
return 0; // let linenoise know it timed out
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (errno == EINTR || errno == EAGAIN) {
|
||||
LockInc(&shared->c.pollinterrupts);
|
||||
} else if (errno == ENOMEM) {
|
||||
LockInc(&shared->c.enomems);
|
||||
WARNF("(srvr) %s ran out of memory");
|
||||
meltdown = true;
|
||||
} else {
|
||||
DIEF("(srvr) poll error: %m");
|
||||
}
|
||||
errno = 0;
|
||||
}
|
||||
return 2;
|
||||
}
|
||||
|
||||
static void RestoreApe(void) {
|
||||
char *p;
|
||||
size_t n;
|
||||
|
@ -6883,6 +6859,97 @@ static void RestoreApe(void) {
|
|||
}
|
||||
}
|
||||
|
||||
static int HandleReadline(void) {
|
||||
int status;
|
||||
for (;;) {
|
||||
status = lua_loadline(GL);
|
||||
if (status < 0) {
|
||||
if (status == -1) {
|
||||
OnTerm(SIGHUP); // eof
|
||||
INFOF("got repl eof");
|
||||
write(1, "\r\n", 2);
|
||||
return -1;
|
||||
} else if (errno == EINTR) {
|
||||
errno = 0;
|
||||
OnInt(SIGINT);
|
||||
INFOF("got repl interrupt");
|
||||
return 0;
|
||||
} else if (errno == EAGAIN) {
|
||||
errno = 0;
|
||||
return 0;
|
||||
} else {
|
||||
OnTerm(SIGIO); // error
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
write(1, "\r\n", 2);
|
||||
linenoiseDisableRawMode();
|
||||
_spinlock(&lualock);
|
||||
if (status == LUA_OK) {
|
||||
status = lua_runchunk(GL, 0, LUA_MULTRET);
|
||||
}
|
||||
if (status == LUA_OK) {
|
||||
lua_l_print(GL);
|
||||
} else {
|
||||
lua_report(GL, status);
|
||||
}
|
||||
_spunlock(&lualock);
|
||||
if (lua_repl_isterminal) {
|
||||
linenoiseEnableRawMode(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int HandlePoll(int ms) {
|
||||
int rc, nfds;
|
||||
size_t pollid, serverid;
|
||||
if ((nfds = poll(polls, 1 + servers.n, ms)) != -1) {
|
||||
if (nfds) {
|
||||
// handle pollid/o events
|
||||
for (pollid = 0; pollid < 1 + servers.n; ++pollid) {
|
||||
if (!polls[pollid].revents) continue;
|
||||
if (polls[pollid].fd < 0) continue;
|
||||
if (polls[pollid].fd) {
|
||||
// handle listen socket
|
||||
serverid = pollid - 1;
|
||||
assert(0 <= serverid && serverid < servers.n);
|
||||
serveraddr = &servers.p[serverid].addr;
|
||||
ishandlingconnection = true;
|
||||
_spinlock(&lualock);
|
||||
rc = HandleConnection(serverid);
|
||||
_spunlock(&lualock);
|
||||
ishandlingconnection = false;
|
||||
if (rc == -1) return -1;
|
||||
} else {
|
||||
// handle standard input
|
||||
rc = HandleReadline();
|
||||
if (rc == -1) return rc;
|
||||
}
|
||||
}
|
||||
} else if (__replmode) {
|
||||
// handle refresh repl line
|
||||
if (!IsWindows()) {
|
||||
rc = HandleReadline();
|
||||
if (rc < 0) return rc;
|
||||
} else {
|
||||
linenoiseRefreshLine(lua_repl_linenoise);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (errno == EINTR || errno == EAGAIN) {
|
||||
LockInc(&shared->c.pollinterrupts);
|
||||
} else if (errno == ENOMEM) {
|
||||
LockInc(&shared->c.enomems);
|
||||
WARNF("(srvr) %s ran out of memory");
|
||||
meltdown = true;
|
||||
} else {
|
||||
DIEF("(srvr) poll error: %m");
|
||||
}
|
||||
errno = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void Listen(void) {
|
||||
char ipbuf[16];
|
||||
size_t i, j, n;
|
||||
|
@ -6960,10 +7027,9 @@ static void HandleShutdown(void) {
|
|||
}
|
||||
|
||||
// this function coroutines with linenoise
|
||||
static int EventLoop(int fd, int ms) {
|
||||
int rc;
|
||||
static int EventLoop(int ms) {
|
||||
long double t;
|
||||
rc = -1;
|
||||
VERBOSEF("EventLoop()");
|
||||
while (!terminated) {
|
||||
errno = 0;
|
||||
if (zombied) {
|
||||
|
@ -6977,54 +7043,47 @@ static int EventLoop(int fd, int ms) {
|
|||
} else if ((t = nowl()) - lastheartbeat > HEARTBEAT / 1000.) {
|
||||
lastheartbeat = t;
|
||||
HandleHeartbeat();
|
||||
} else if ((rc = HandlePoll(ms)) != 2) {
|
||||
break; // return control to linenoise
|
||||
} else if (HandlePoll(ms) == -1) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void ReplEventLoop(void) {
|
||||
int status;
|
||||
long double t;
|
||||
lua_State *L = GL;
|
||||
DEBUGF("ReplEventLoop()");
|
||||
polls[0].fd = 0;
|
||||
__nomultics = 2;
|
||||
__replmode = true;
|
||||
lua_initrepl("redbean");
|
||||
linenoiseSetPollCallback(EventLoop);
|
||||
for (;;) {
|
||||
if ((status = lua_loadline(L)) == -1) {
|
||||
if (errno == EINTR) {
|
||||
LockInc(&shared->c.pollinterrupts);
|
||||
if (terminated) {
|
||||
break;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (status == LUA_OK) {
|
||||
status = lua_runchunk(L, 0, LUA_MULTRET);
|
||||
}
|
||||
if (status == LUA_OK) {
|
||||
lua_l_print(L);
|
||||
} else {
|
||||
lua_report(L, status);
|
||||
}
|
||||
lua_initrepl(GL, "redbean");
|
||||
if (lua_repl_isterminal) {
|
||||
linenoiseEnableRawMode(0);
|
||||
}
|
||||
if (!terminated && !isexitingworker) {
|
||||
OnTerm(SIGHUP); // eof event
|
||||
}
|
||||
lua_settop(L, 0); // clear stack
|
||||
lua_writeline();
|
||||
__replmode = false;
|
||||
__nomultics = 0;
|
||||
EventLoop(100);
|
||||
linenoiseDisableRawMode();
|
||||
lua_freerepl();
|
||||
lua_settop(GL, 0); // clear stack
|
||||
polls[0].fd = -1;
|
||||
}
|
||||
|
||||
static uint32_t WindowsReplThread(void *arg) {
|
||||
DEBUGF("WindowsReplThread()");
|
||||
lua_repl_blocking = true;
|
||||
lua_initrepl(GL, "redbean");
|
||||
if (lua_repl_isterminal) {
|
||||
linenoiseEnableRawMode(0);
|
||||
}
|
||||
for (;;) {
|
||||
if (HandleReadline() == -1) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
linenoiseDisableRawMode();
|
||||
lua_freerepl();
|
||||
_spinlock(&lualock);
|
||||
lua_settop(GL, 0); // clear stack
|
||||
_spunlock(&lualock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void SigInit(void) {
|
||||
xsigaction(SIGINT, OnInt, 0, 0, 0);
|
||||
xsigaction(SIGHUP, OnHup, 0, 0, 0);
|
||||
|
@ -7217,13 +7276,6 @@ void RedBean(int argc, char *argv[]) {
|
|||
if (daemonize) {
|
||||
Daemonize();
|
||||
} else {
|
||||
// xxx: create process group to make it easier to propagate SIGTERM
|
||||
// to children. the downside to doing this seems to be that
|
||||
// ctrl-c isn't propagating as expected when running redbean
|
||||
// underneath strace.com :|
|
||||
if (!IsWindows()) {
|
||||
setpgrp();
|
||||
}
|
||||
if (logpath) {
|
||||
close(2);
|
||||
open(logpath, O_APPEND | O_WRONLY | O_CREAT, 0640);
|
||||
|
@ -7240,14 +7292,18 @@ void RedBean(int argc, char *argv[]) {
|
|||
isinitialized = true;
|
||||
CallSimpleHookIfDefined("OnServerStart");
|
||||
#ifdef STATIC
|
||||
EventLoop(-1, HEARTBEAT);
|
||||
EventLoop(HEARTBEAT);
|
||||
#else
|
||||
GetResolvConf(); // for effect
|
||||
GetHostsTxt(); // for effect
|
||||
if (!IsWindows() && isatty(0)) {
|
||||
ReplEventLoop();
|
||||
GetResolvConf(); // for effect
|
||||
if (daemonize || !linenoiseIsTerminal()) {
|
||||
EventLoop(HEARTBEAT);
|
||||
} else if (IsWindows()) {
|
||||
uint32_t tid;
|
||||
CreateThread(0, 0, NT2SYSV(WindowsReplThread), 0, 0, &tid);
|
||||
EventLoop(100);
|
||||
} else {
|
||||
EventLoop(-1, HEARTBEAT);
|
||||
ReplEventLoop();
|
||||
}
|
||||
#endif
|
||||
if (!isexitingworker) {
|
||||
|
|
|
@ -83,7 +83,7 @@ void bf(int fd) {
|
|||
obuf[n++] = ';';
|
||||
obuf[n++] = '5';
|
||||
obuf[n++] = ';';
|
||||
n += int64toarray_radix10(fg, obuf + n);
|
||||
n = FormatInt64(obuf + n, fg) - (obuf + n);
|
||||
obuf[n++] = 'm';
|
||||
}
|
||||
obuf[n++] = "0123456789abcdef"[c >> 4];
|
||||
|
|
|
@ -79,7 +79,7 @@ static const char *GetStorageSpecifier(const char *type, int *out_width,
|
|||
static void EmitSection(long yn, long xn, int w, int arrayalign, int emit(),
|
||||
void *a) {
|
||||
char alignstr[21];
|
||||
uint64toarray_radix10(arrayalign, alignstr);
|
||||
FormatUint32(alignstr, arrayalign);
|
||||
if (arrayalign <= 8 && yn * xn * w == 8) {
|
||||
emit("\t.rodata.cst", a);
|
||||
emit("8\n", a);
|
||||
|
@ -108,8 +108,8 @@ void *FormatStringTableAsAssembly(long yn, long xn, const char *const T[yn][xn],
|
|||
char ynstr[21], xnstr[21];
|
||||
name = firstnonnull(name, "M");
|
||||
storage = GetStorageSpecifier(firstnonnull(type, "long"), &w, &align);
|
||||
uint64toarray_radix10(yn, ynstr);
|
||||
uint64toarray_radix10(xn, xnstr);
|
||||
FormatUint64(ynstr, yn);
|
||||
FormatUint64(xnstr, xn);
|
||||
EmitSection(yn, xn, w, GetArrayAlignment(yn, xn, w, align), emit, a);
|
||||
emit(name, a);
|
||||
emit(":", a);
|
||||
|
|
|
@ -24,8 +24,8 @@ void *FormatStringTableAsCode(long yn, long xn, const char *const T[yn][xn],
|
|||
int emit(), void *arg, const char *type,
|
||||
const char *name, const char *ignored) {
|
||||
char ynstr[21], xnstr[21];
|
||||
uint64toarray_radix10(yn, ynstr);
|
||||
uint64toarray_radix10(xn, xnstr);
|
||||
FormatUint64(ynstr, yn);
|
||||
FormatUint64(xnstr, xn);
|
||||
emit(type, arg);
|
||||
emit(" ", arg);
|
||||
emit(firstnonnull(name, "M"), arg);
|
||||
|
|
|
@ -341,7 +341,7 @@ static void AppendChar(char c) {
|
|||
|
||||
static void AppendInt(long x) {
|
||||
char ibuf[21];
|
||||
AppendData(ibuf, int64toarray_radix10(x, ibuf));
|
||||
AppendData(ibuf, FormatInt64(ibuf, x) - ibuf);
|
||||
}
|
||||
|
||||
/*───────────────────────────────────────────────────────────────────────────│─╗
|
||||
|
|
|
@ -743,7 +743,7 @@ static void Render(void) {
|
|||
fg = InvertXtermGreyscale(fg);
|
||||
}
|
||||
p = stpcpy(p, "\e[38;5;");
|
||||
p += int64toarray_radix10(fg, p);
|
||||
p = FormatInt64(p, fg);
|
||||
*p++ = 'm';
|
||||
}
|
||||
w = tpenc(kCp437[c]);
|
||||
|
@ -769,14 +769,14 @@ static void Render(void) {
|
|||
}
|
||||
p = stpcpy(p, " memzoom\e[0m ");
|
||||
if (!pid) {
|
||||
p += uint64toarray_radix10(MIN(offset / (long double)size * 100, 100), p);
|
||||
p = FormatUint32(p, MIN(offset / (long double)size * 100, 100));
|
||||
p = stpcpy(p, "%-");
|
||||
p += uint64toarray_radix10(
|
||||
MIN((offset + ((tyn * txn) << zoom)) / (long double)size * 100, 100),
|
||||
p);
|
||||
p = FormatUint32(
|
||||
p,
|
||||
MIN((offset + ((tyn * txn) << zoom)) / (long double)size * 100, 100));
|
||||
p = stpcpy(p, "% ");
|
||||
}
|
||||
p += uint64toarray_radix10(1L << zoom, p);
|
||||
p = FormatUint32(p, 1L << zoom);
|
||||
p = stpcpy(p, "x\e[J");
|
||||
PreventBufferbloat();
|
||||
for (i = 0, n = p - buffer; i < n; i += got) {
|
||||
|
@ -910,10 +910,10 @@ static void GetOpts(int argc, char *argv[]) {
|
|||
}
|
||||
if (pid) {
|
||||
p = stpcpy(path, "/proc/");
|
||||
p += int64toarray_radix10(pid, p);
|
||||
p = FormatInt64(p, pid);
|
||||
stpcpy(p, "/mem");
|
||||
p = stpcpy(mapspath, "/proc/");
|
||||
p += int64toarray_radix10(pid, p);
|
||||
p = FormatInt64(p, pid);
|
||||
stpcpy(p, "/maps");
|
||||
} else {
|
||||
if (optind == argc) {
|
||||
|
|
|
@ -857,8 +857,8 @@ static void OpenVideo(void) {
|
|||
plm_set_video_decode_callback(plm_, OnVideo, NULL);
|
||||
plm_set_audio_decode_callback(plm_, OnAudio, NULL);
|
||||
plm_set_loop(plm_, false);
|
||||
int64toarray_radix10((chans_ = 2), chansstr_);
|
||||
int64toarray_radix10((srate_ = plm_get_samplerate(plm_)), sratestr_);
|
||||
FormatInt64(chansstr_, (chans_ = 2));
|
||||
FormatInt64(sratestr_, (srate_ = plm_get_samplerate(plm_)));
|
||||
if (plm_get_num_audio_streams(plm_) && OpenSpeaker()) {
|
||||
plm_set_audio_enabled(plm_, true, 0);
|
||||
} else {
|
||||
|
|
Loading…
Reference in a new issue