Apply even more fixups

- Finish cleaning up the stdio unlocked APIs
- Make __cxa_finalize() properly thread safe
- Don't log locks if threads aren't being used
- Add some more mutex guards to places using _mmi
- Specific lock names now appear in the --ftrace logs
- Fix mkdeps.com generating invalid Makefiles sometimes
- Simplify and fix bugs in the test runner infrastructure
- Fix issue where sometimes some functions wouldn't be logged
This commit is contained in:
Justine Tunney 2022-06-12 11:47:20 -07:00
parent 4ddfc47d6e
commit 8cdec62f5b
87 changed files with 955 additions and 899 deletions

View file

@ -1607,6 +1607,9 @@ ape_pad_text:
.type ape_pad_privileged,@object
.hidden ape_pad_privileged
ape_pad_privileged:
#if !IsTiny()
.align 4096
#endif
.previous
.section .ape.pad.rodata,"a",@progbits

Binary file not shown.

View file

@ -37,7 +37,8 @@ EXAMPLES_COMS = \
EXAMPLES_BINS = \
$(EXAMPLES_COMS) \
$(EXAMPLES_COMS:%=%.dbg) \
o/$(MODE)/examples/life-nomod.com
o/$(MODE)/examples/life-nomod.com \
o/$(MODE)/examples/life-classic.com
EXAMPLES_DIRECTDEPS = \
DSP_CORE \

View file

@ -61,7 +61,6 @@ textwindows int sys_execve_nt(const char *program, char *const argv[],
size_t i;
uint32_t dwExitCode;
char progbuf[PATH_MAX];
struct MemoryIntervals *mm;
struct NtStartupInfo startinfo;
struct NtProcessInformation procinfo;

View file

@ -16,8 +16,18 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/state.internal.h"
#include "libc/intrin/pthread.h"
unsigned __sighandrvas[NSIG];
unsigned __sighandflags[NSIG];
pthread_mutex_t __sig_lock_obj;
static pthread_mutex_t __sig_lock_obj;
void(__sig_lock)(void) {
pthread_mutex_lock(&__sig_lock_obj);
}
void(__sig_unlock)(void) {
pthread_mutex_unlock(&__sig_lock_obj);
}

View file

@ -1,6 +1,6 @@
#ifndef COSMOPOLITAN_LIBC_CALLS_STATE_INTERNAL_H_
#define COSMOPOLITAN_LIBC_CALLS_STATE_INTERNAL_H_
#include "libc/intrin/pthread.h"
#include "libc/nexgen32e/threaded.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
@ -8,14 +8,17 @@ hidden extern int __vforked;
hidden extern bool __time_critical;
hidden extern unsigned __sighandrvas[NSIG];
hidden extern unsigned __sighandflags[NSIG];
hidden extern pthread_mutex_t __sig_lock_obj;
hidden extern pthread_mutex_t __fds_lock_obj;
hidden extern const struct NtSecurityAttributes kNtIsInheritable;
#define __fds_lock() pthread_mutex_lock(&__fds_lock_obj)
#define __fds_unlock() pthread_mutex_unlock(&__fds_lock_obj)
#define __sig_lock() pthread_mutex_lock(&__sig_lock_obj)
#define __sig_unlock() pthread_mutex_unlock(&__sig_lock_obj)
void __fds_lock(void);
void __fds_unlock(void);
void __sig_lock(void);
void __sig_unlock(void);
#define __fds_lock() (__threaded ? __fds_lock() : 0)
#define __fds_unlock() (__threaded ? __fds_unlock() : 0)
#define __sig_lock() (__threaded ? __sig_lock() : 0)
#define __sig_unlock() (__threaded ? __sig_unlock() : 0)
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */

View file

@ -858,25 +858,25 @@ dontdiscard __asan_die_f *__asan_report_memory_fault(void *addr, int size,
void *__asan_morgue_add(void *p) {
int i;
void *r;
pthread_mutex_lock(&__asan_lock);
if (__threaded) pthread_mutex_lock(&__asan_lock);
i = __asan_morgue.i++ & (ARRAYLEN(__asan_morgue.p) - 1);
r = __asan_morgue.p[i];
__asan_morgue.p[i] = p;
pthread_mutex_unlock(&__asan_lock);
if (__threaded) pthread_mutex_unlock(&__asan_lock);
return r;
}
static void __asan_morgue_flush(void) {
int i;
void *p;
pthread_mutex_lock(&__asan_lock);
if (__threaded) pthread_mutex_lock(&__asan_lock);
for (i = 0; i < ARRAYLEN(__asan_morgue.p); ++i) {
if (__asan_morgue.p[i] && weaken(dlfree)) {
weaken(dlfree)(__asan_morgue.p[i]);
}
__asan_morgue.p[i] = 0;
}
pthread_mutex_unlock(&__asan_lock);
if (__threaded) pthread_mutex_unlock(&__asan_lock);
}
static size_t __asan_user_size(size_t n) {

View file

@ -16,6 +16,15 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/intrin/pthread.h"
#include "libc/runtime/cxaatexit.internal.h"
pthread_mutex_t __cxa_lock_obj;
static pthread_mutex_t __cxa_lock_obj;
void(__cxa_lock)(void) {
pthread_mutex_lock(&__cxa_lock_obj);
}
void(__cxa_unlock)(void) {
pthread_mutex_unlock(&__cxa_lock_obj);
}

View file

@ -27,7 +27,15 @@
STATIC_YOINK("_init_g_fds");
struct Fds g_fds;
pthread_mutex_t __fds_lock_obj;
static pthread_mutex_t __fds_lock_obj;
void(__fds_lock)(void) {
pthread_mutex_lock(&__fds_lock_obj);
}
void(__fds_unlock)(void) {
pthread_mutex_unlock(&__fds_lock_obj);
}
textstartup void InitializeFileDescriptors(void) {
struct Fds *fds;

View file

@ -18,6 +18,7 @@
*/
#include "libc/calls/calls.h"
#include "libc/dce.h"
#include "libc/nexgen32e/gettls.h"
#include "libc/nexgen32e/threaded.h"
#include "libc/nt/thread.h"
#include "libc/nt/thunk/msabi.h"

View file

@ -72,6 +72,21 @@ o/$(MODE)/libc/intrin/kprintf.greg.o: \
-fno-sanitize=all \
-fno-stack-protector
# synchronization primitives are intended to be magic free
o/$(MODE)/libc/intrin/gettid.greg.o \
o/$(MODE)/libc/intrin/pthread_mutex_lock.o \
o/$(MODE)/libc/intrin/pthread_mutex_unlock.o \
o/$(MODE)/libc/intrin/pthread_mutex_trylock.o \
o/$(MODE)/libc/intrin/_trylock_debug_4.o \
o/$(MODE)/libc/intrin/_spinlock_debug_4.o: \
OVERRIDE_CFLAGS += \
-fwrapv \
-x-no-pg \
-mno-fentry \
-ffreestanding \
-fno-sanitize=all \
-fno-stack-protector
o/$(MODE)/libc/intrin/tls.greg.o \
o/$(MODE)/libc/intrin/exit.greg.o \
o/$(MODE)/libc/intrin/exit1.greg.o \

View file

@ -16,8 +16,18 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/intrin/pthread.h"
#include "libc/runtime/memtrack.internal.h"
STATIC_YOINK("_init__mmi");
struct MemoryIntervals _mmi;
static pthread_mutex_t __mmi_lock_obj;
void(__mmi_lock)(void) {
pthread_mutex_lock(&__mmi_lock_obj);
}
void(__mmi_unlock)(void) {
pthread_mutex_unlock(&__mmi_lock_obj);
}

View file

@ -1,6 +1,7 @@
#ifndef COSMOPOLITAN_LIBC_RUNTIME_PTHREAD_H_
#define COSMOPOLITAN_LIBC_RUNTIME_PTHREAD_H_
#include "libc/calls/struct/timespec.h"
#include "libc/intrin/kprintf.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
@ -23,9 +24,9 @@ typedef unsigned long *pthread_t;
typedef int pthread_once_t;
typedef struct {
_Atomic(int) owner;
_Atomic(int) waits;
int reent;
int owner;
int waits;
} pthread_mutex_t;
typedef struct {

View file

@ -18,7 +18,8 @@
*/
#include "libc/bits/atomic.h"
#include "libc/calls/calls.h"
#include "libc/intrin/lockcmpxchgp.h"
#include "libc/dce.h"
#include "libc/intrin/kprintf.h"
#include "libc/intrin/pthread.h"
#include "libc/nexgen32e/threaded.h"
#include "libc/sysv/consts/futex.h"
@ -26,25 +27,24 @@
/**
* Acquires mutex.
*/
noasan noubsan int pthread_mutex_lock(pthread_mutex_t *mutex) {
int pthread_mutex_lock(pthread_mutex_t *mutex) {
int me, owner;
unsigned tries;
if (__threaded) {
for (tries = 0, me = gettid();;) {
owner = 0;
if (_lockcmpxchgp(&mutex->owner, &owner, me) || owner == me) {
break;
}
atomic_fetch_add(&mutex->waits, +1);
if (!IsLinux() || futex((void *)&mutex->owner, FUTEX_WAIT, owner, 0, 0)) {
if (++tries & 7) {
__builtin_ia32_pause();
} else {
sched_yield();
}
}
atomic_fetch_add(&mutex->waits, -1);
for (tries = 0, me = gettid();;) {
owner = 0;
if (atomic_compare_exchange_weak(&mutex->owner, &owner, me) ||
owner == me) {
break;
}
atomic_fetch_add(&mutex->waits, +1);
if (!IsLinux() || futex((void *)&mutex->owner, FUTEX_WAIT, owner, 0, 0)) {
if (++tries & 7) {
__builtin_ia32_pause();
} else {
sched_yield();
}
}
atomic_fetch_add(&mutex->waits, -1);
}
++mutex->reent;
return 0;

View file

@ -16,9 +16,9 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/bits/atomic.h"
#include "libc/calls/calls.h"
#include "libc/errno.h"
#include "libc/intrin/lockcmpxchgp.h"
#include "libc/intrin/pthread.h"
#include "libc/nexgen32e/threaded.h"
@ -26,18 +26,15 @@
* Tries to acquire mutex.
*/
int pthread_mutex_trylock(pthread_mutex_t *mutex) {
int rc, me, owner = 0;
if (__threaded) {
me = gettid();
if (!_lockcmpxchgp(&mutex->owner, &owner, me) && owner == me) {
rc = 0;
++mutex->reent;
} else {
rc = EBUSY;
}
} else {
int rc, me, owner;
me = gettid();
owner = 0;
if (!atomic_compare_exchange_strong(&mutex->owner, &owner, me) &&
owner == me) {
rc = 0;
++mutex->reent;
} else {
rc = EBUSY;
}
return rc;
}

View file

@ -20,6 +20,7 @@
#include "libc/bits/atomic.h"
#include "libc/calls/calls.h"
#include "libc/dce.h"
#include "libc/intrin/kprintf.h"
#include "libc/intrin/pthread.h"
#include "libc/nexgen32e/threaded.h"
#include "libc/sysv/consts/futex.h"
@ -27,19 +28,17 @@
/**
* Releases mutex.
*/
noasan noubsan int pthread_mutex_unlock(pthread_mutex_t *mutex) {
int pthread_mutex_unlock(pthread_mutex_t *mutex) {
int owner;
bool shouldunlock;
assert(mutex->reent > 0);
shouldunlock = --mutex->reent <= 0;
if (__threaded) {
assert(mutex->owner == gettid());
if (shouldunlock) {
atomic_store_explicit(&mutex->owner, 0, memory_order_relaxed);
if (IsLinux() &&
atomic_load_explicit(&mutex->waits, memory_order_acquire)) {
futex((void *)&mutex->owner, FUTEX_WAKE, 1, 0, 0);
}
assert(mutex->owner == gettid());
if (shouldunlock) {
atomic_store_explicit(&mutex->owner, 0, memory_order_relaxed);
if (IsLinux() &&
atomic_load_explicit(&mutex->waits, memory_order_acquire)) {
futex((void *)&mutex->owner, FUTEX_WAKE, 1, 0, 0);
}
}
return 0;

View file

@ -7,6 +7,7 @@
COSMOPOLITAN_C_START_
forceinline pureconst bool IsValidStackFramePointer(struct StackFrame *x) {
/* assumes __mmi_lock() is held */
return IsLegalPointer(x) && !((uintptr_t)x & 15) &&
(IsStaticStackFrame((uintptr_t)x >> 16) ||
IsSigAltStackFrame((uintptr_t)x >> 16) ||

View file

@ -53,6 +53,7 @@ static void ShowHint(const char *s) {
}
static int PrintBacktraceUsingAddr2line(int fd, const struct StackFrame *bp) {
bool ok;
ssize_t got;
intptr_t addr;
size_t i, j, gi;
@ -99,7 +100,10 @@ static int PrintBacktraceUsingAddr2line(int fd, const struct StackFrame *bp) {
garbage = weaken(__garbage);
gi = garbage ? garbage->i : 0;
for (frame = bp; frame && i < kBacktraceMaxFrames - 1; frame = frame->next) {
if (!IsValidStackFramePointer(frame)) {
__mmi_lock();
ok = IsValidStackFramePointer(frame);
__mmi_unlock();
if (!ok) {
return -1;
}
addr = frame->addr;

View file

@ -27,6 +27,7 @@
#include "libc/macros.internal.h"
#include "libc/nexgen32e/gc.internal.h"
#include "libc/nexgen32e/stackframe.h"
#include "libc/runtime/memtrack.internal.h"
#include "libc/runtime/runtime.h"
#include "libc/runtime/symbols.internal.h"
#include "libc/str/str.h"
@ -46,6 +47,7 @@
noinstrument noasan int PrintBacktraceUsingSymbols(int fd,
const struct StackFrame *bp,
struct SymbolTable *st) {
bool ok;
size_t gi;
intptr_t addr;
int i, symbol, addend;
@ -55,7 +57,10 @@ noinstrument noasan int PrintBacktraceUsingSymbols(int fd,
garbage = weaken(__garbage);
gi = garbage ? garbage->i : 0;
for (i = 0, frame = bp; frame; frame = frame->next) {
if (!IsValidStackFramePointer(frame)) {
__mmi_lock();
ok = IsValidStackFramePointer(frame);
__mmi_unlock();
if (!ok) {
kprintf("%p corrupt frame pointer\n", frame);
break;
}

27
libc/nexgen32e/gettls.h Normal file
View file

@ -0,0 +1,27 @@
#ifndef COSMOPOLITAN_LIBC_NEXGEN32E_GETTLS_H_
#define COSMOPOLITAN_LIBC_NEXGEN32E_GETTLS_H_
#include "libc/dce.h"
#include "libc/nexgen32e/threaded.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
char *__get_tls(void) libcesque nosideeffect;
#if defined(__GNUC__) && defined(__x86_64__) && !defined(__STRICT_ANSI__)
static noasan inline char *__get_tls_inline(void) {
char *tib, *lin = (char *)0x30;
if (IsLinux() || IsFreebsd() || IsNetbsd() || IsOpenbsd()) {
asm("mov\t%%fs:(%1),%0" : "=a"(tib) : "r"(lin) : "memory");
} else {
asm("mov\t%%gs:(%1),%0" : "=a"(tib) : "r"(lin) : "memory");
if (IsWindows()) {
tib = *(char **)(tib + 0x1480 + __tls_index * 8);
}
}
return tib;
}
#endif /* GNU x86-64 */
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_NEXGEN32E_GETTLS_H_ */

View file

@ -1,6 +1,5 @@
#ifndef COSMOPOLITAN_LIBC_NEXGEN32E_THREADED_H_
#define COSMOPOLITAN_LIBC_NEXGEN32E_THREADED_H_
#include "libc/dce.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
@ -8,25 +7,9 @@ extern int __threaded;
extern bool __tls_enabled;
extern unsigned __tls_index;
char *__get_tls(void) libcesque nosideeffect;
void *__initialize_tls(char[64]);
void __install_tls(char[64]);
#if defined(__GNUC__) && defined(__x86_64__) && !defined(__STRICT_ANSI__)
static noasan inline char *__get_tls_inline(void) {
char *tib, *lin = (char *)0x30;
if (IsLinux() || IsFreebsd() || IsNetbsd() || IsOpenbsd()) {
asm("mov\t%%fs:(%1),%0" : "=a"(tib) : "r"(lin) : "memory");
} else {
asm("mov\t%%gs:(%1),%0" : "=a"(tib) : "r"(lin) : "memory");
if (IsWindows()) {
tib = *(char **)(tib + 0x1480 + __tls_index * 8);
}
}
return tib;
}
#endif /* GNU x86-64 */
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_NEXGEN32E_THREADED_H_ */

View file

@ -31,7 +31,6 @@
sys_clone_linux:
push %rbp
mov %rsp,%rbp
.profilable
push %rbx
mov %rcx,%r10
mov 16(%rbp),%rbx

View file

@ -1,6 +1,6 @@
#ifndef COSMOPOLITAN_LIBC_RUNTIME_CXAATEXIT_H_
#define COSMOPOLITAN_LIBC_RUNTIME_CXAATEXIT_H_
#include "libc/intrin/pthread.h"
#include "libc/nexgen32e/threaded.h"
#include "libc/stdio/stdio.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
@ -17,13 +17,14 @@ struct CxaAtexitBlocks {
} * p, root;
};
extern pthread_mutex_t __cxa_lock_obj;
extern struct CxaAtexitBlocks __cxa_blocks;
void __cxa_lock(void);
void __cxa_unlock(void);
void __cxa_printexits(FILE *, void *);
#define __cxa_lock() pthread_mutex_lock(&__cxa_lock_obj)
#define __cxa_unlock() pthread_mutex_unlock(&__cxa_lock_obj)
#define __cxa_lock() (__threaded ? __cxa_lock() : 0)
#define __cxa_unlock() (__threaded ? __cxa_unlock() : 0)
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */

View file

@ -20,7 +20,7 @@
#include "libc/fmt/itoa.h"
#include "libc/intrin/cmpxchg.h"
#include "libc/intrin/kprintf.h"
#include "libc/intrin/lockcmpxchgp.h"
#include "libc/intrin/lockcmpxchg.h"
#include "libc/macros.internal.h"
#include "libc/nexgen32e/stackframe.h"
#include "libc/nexgen32e/threaded.h"
@ -39,12 +39,11 @@
void ftrace_hook(void);
_Alignas(64) int ftrace_lock;
static struct Ftrace {
int skew;
int stackdigs;
int64_t lastaddr;
volatile bool busy;
} g_ftrace;
static privileged inline int GetNestingLevelImpl(struct StackFrame *frame) {
@ -65,37 +64,14 @@ static privileged inline int GetNestingLevel(struct StackFrame *frame) {
}
static privileged inline void ReleaseFtraceLock(void) {
int zero = 0;
__atomic_store(&ftrace_lock, &zero, __ATOMIC_RELAXED);
g_ftrace.busy = false;
}
static privileged inline bool AcquireFtraceLock(void) {
int me, owner;
unsigned tries;
if (!__threaded) {
return _cmpxchg(&ftrace_lock, 0, -1);
return _cmpxchg(&g_ftrace.busy, false, true);
} else {
for (tries = 0, me = gettid();;) {
owner = 0;
if (_lockcmpxchgp(&ftrace_lock, &owner, me)) {
return true;
}
if (owner == -1) {
// avoid things getting weird after first clone() call transition
return false;
}
if (owner == me) {
// we ignore re-entry into ftrace. while the code and build config
// is written to make re-entry highly unlikely, it's impossible to
// guarantee. there's also the possibility of asynchronous signals
return false;
}
if (++tries & 7) {
__builtin_ia32_pause();
} else {
sched_yield();
}
}
return _lockcmpxchg(&g_ftrace.busy, false, true);
}
}

View file

@ -3,8 +3,8 @@
#include "libc/assert.h"
#include "libc/bits/midpoint.h"
#include "libc/dce.h"
#include "libc/intrin/pthread.h"
#include "libc/macros.internal.h"
#include "libc/nexgen32e/threaded.h"
#include "libc/nt/version.h"
#include "libc/runtime/stack.h"
#include "libc/sysv/consts/ss.h"
@ -31,9 +31,6 @@ COSMOPOLITAN_C_START_
#define _kMem(NORMAL, WIN7) \
(!IsWindows() || IsAtLeastWindows10() ? NORMAL : WIN7)
#define __mmi_lock() pthread_mutex_lock(&_mmi.lock)
#define __mmi_unlock() pthread_mutex_unlock(&_mmi.lock)
struct MemoryInterval {
int x;
int y;
@ -50,11 +47,12 @@ struct MemoryIntervals {
size_t i, n;
struct MemoryInterval *p;
struct MemoryInterval s[OPEN_MAX];
pthread_mutex_t lock;
};
extern hidden struct MemoryIntervals _mmi;
void __mmi_lock(void) hidden;
void __mmi_unlock(void) hidden;
bool IsMemtracked(int, int) hidden;
void PrintSystemMappings(int) hidden;
const char *DescribeFrame(int) hidden;
@ -69,6 +67,9 @@ void ReleaseMemoryNt(struct MemoryIntervals *, int, int) hidden;
int UntrackMemoryIntervals(void *, size_t) hidden;
size_t GetMemtrackSize(struct MemoryIntervals *);
#define __mmi_lock() (__threaded ? __mmi_lock() : 0)
#define __mmi_unlock() (__threaded ? __mmi_unlock() : 0)
#define IsLegalPointer(p) \
(-0x800000000000 <= (intptr_t)(p) && (intptr_t)(p) <= 0x7fffffffffff)

View file

@ -11,13 +11,17 @@ struct StdioFlushHandles {
};
struct StdioFlush {
pthread_mutex_t lock;
struct StdioFlushHandles handles;
FILE *handles_initmem[8];
};
hidden extern struct StdioFlush __fflush;
void __fflush_lock(void);
void __fflush_unlock(void);
#define __fflush_lock() (__threaded ? __fflush_lock() : 0)
#define __fflush_unlock() (__threaded ? __fflush_unlock() : 0)
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_STDIO_FFLUSH_H_ */

View file

@ -31,6 +31,16 @@
#include "libc/stdio/stdio.h"
#include "libc/sysv/consts/o.h"
static pthread_mutex_t __fflush_lock_obj;
void(__fflush_lock)(void) {
pthread_mutex_lock(&__fflush_lock_obj);
}
void(__fflush_unlock)(void) {
pthread_mutex_unlock(&__fflush_lock_obj);
}
/**
* Blocks until data from stream buffer is written out.
*
@ -41,7 +51,7 @@ int fflush_unlocked(FILE *f) {
int rc = 0;
size_t i;
if (!f) {
pthread_mutex_lock(&__fflush.lock);
__fflush_lock();
for (i = __fflush.handles.i; i; --i) {
if ((f = __fflush.handles.p[i - 1])) {
if (fflush(f) == -1) {
@ -49,7 +59,7 @@ int fflush_unlocked(FILE *f) {
}
}
}
pthread_mutex_unlock(&__fflush.lock);
__fflush_unlock();
} else if (f->fd != -1) {
if (__fflush_impl(f) == -1) {
rc = -1;
@ -64,7 +74,7 @@ textstartup int __fflush_register(FILE *f) {
int rc;
size_t i;
struct StdioFlush *sf;
pthread_mutex_lock(&__fflush.lock);
__fflush_lock();
sf = &__fflush;
if (!sf->handles.p) {
sf->handles.p = sf->handles_initmem;
@ -74,19 +84,19 @@ textstartup int __fflush_register(FILE *f) {
for (i = sf->handles.i; i; --i) {
if (!sf->handles.p[i - 1]) {
sf->handles.p[i - 1] = f;
pthread_mutex_unlock(&__fflush.lock);
__fflush_unlock();
return 0;
}
}
rc = append(&sf->handles, &f);
pthread_mutex_unlock(&__fflush.lock);
__fflush_unlock();
return rc;
}
void __fflush_unregister(FILE *f) {
size_t i;
struct StdioFlush *sf;
pthread_mutex_lock(&__fflush.lock);
__fflush_lock();
sf = &__fflush;
sf = pushpop(sf);
for (i = sf->handles.i; i; --i) {
@ -95,5 +105,5 @@ void __fflush_unregister(FILE *f) {
break;
}
}
pthread_mutex_unlock(&__fflush.lock);
__fflush_unlock();
}

View file

@ -16,26 +16,25 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/assert.h"
#include "libc/errno.h"
#include "libc/stdio/stdio.h"
/**
* Reads UTF-8 content from stream into UTF-32 buffer.
*
* This function is similar to getline() except it'll truncate lines
* exceeding size. The line ending marker is included and may be removed
* using _chomp().
*
* @param s is is nul-terminated string that's non-null
* @param size is byte length of `s`
* @param f is file stream object pointer
* @see fgetws()
* @threadsafe
*/
wchar_t *fgetws_unlocked(wchar_t *s, int size, FILE *f) {
wint_t c;
wchar_t *p = s;
if (size > 0) {
while (--size > 0) {
if ((c = fgetwc_unlocked(f)) == -1) {
if (ferror_unlocked(f) == EINTR) continue;
break;
}
*p++ = c;
if (c == '\n') break;
}
*p = '\0';
}
return (intptr_t)p > (intptr_t)s ? s : NULL;
wchar_t *fgetws(wchar_t *s, int size, FILE *f) {
wchar_t *rc;
flockfile(f);
rc = fgetws_unlocked(s, size, f);
funlockfile(f);
return rc;
}

View file

@ -0,0 +1,50 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/assert.h"
#include "libc/errno.h"
#include "libc/stdio/stdio.h"
/**
* Reads UTF-8 content from stream into UTF-32 buffer.
*
* This function is similar to getline() except it'll truncate lines
* exceeding size. The line ending marker is included and may be removed
* using _chomp().
*
* @param s is is nul-terminated string that's non-null
* @param size is byte length of `s`
* @param f is file stream object pointer
* @see fgetws()
*/
wchar_t *fgetws_unlocked(wchar_t *s, int size, FILE *f) {
wint_t c;
wchar_t *p = s;
if (size > 0) {
while (--size > 0) {
if ((c = fgetwc_unlocked(f)) == -1) {
if (ferror_unlocked(f) == EINTR) continue;
break;
}
*p++ = c;
if (c == '\n') break;
}
*p = '\0';
}
return (intptr_t)p > (intptr_t)s ? s : NULL;
}

View file

@ -21,6 +21,6 @@
/**
* Acquires reentrant lock on stdio object, blocking if needed.
*/
void flockfile(FILE *f) {
void(flockfile)(FILE *f) {
pthread_mutex_lock(&f->lock);
}

View file

@ -29,7 +29,7 @@
void _flushlbf(void) {
int i;
FILE *f;
pthread_mutex_lock(&__fflush.lock);
__fflush_lock();
for (i = 0; i < __fflush.handles.i; ++i) {
if ((f = __fflush.handles.p[i])) {
flockfile(f);
@ -39,5 +39,5 @@ void _flushlbf(void) {
funlockfile(f);
}
}
pthread_mutex_unlock(&__fflush.lock);
__fflush_unlock();
}

View file

@ -17,7 +17,6 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
/**
* Writes string to stream.
@ -29,11 +28,12 @@
* @param s is a NUL-terminated string that's non-NULL
* @param f is an open stream
* @return bytes written, or -1 w/ errno
* @threadsafe
*/
int fputs_unlocked(const char *s, FILE *f) {
size_t n, r;
n = strlen(s);
r = fwrite_unlocked(s, 1, n, f);
if (!r && n) return -1;
return r;
int fputs(const char *s, FILE *f) {
int rc;
flockfile(f);
rc = fputs_unlocked(s, f);
funlockfile(f);
return rc;
}

View file

@ -1,5 +1,5 @@
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=8 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
@ -16,20 +16,24 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/macros.internal.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
// Writes string to stream.
//
// Writing stops at the NUL-terminator, which isn't included in output.
// This function blocks until the full string is written, unless an
// unrecoverable error happens.
//
// @param rdi is nul-terminated string that's non-null
// @param rsi is file object stream pointer
// @return strlen(rdi) on success or -1 w/ errno
// @see fputs_unlocked()
// @threadsafe
fputs: mov %rsi,%r11
ezlea fputs_unlocked,ax
jmp stdio_unlock
.endfn fputs,globl
/**
* Writes string to stream.
*
* Writing stops at the NUL-terminator, which isn't included in output.
* This function blocks until the full string is written, unless an
* unrecoverable error happens.
*
* @param s is a NUL-terminated string that's non-NULL
* @param f is an open stream
* @return bytes written, or -1 w/ errno
*/
int fputs_unlocked(const char *s, FILE *f) {
size_t n, r;
n = strlen(s);
r = fwrite_unlocked(s, 1, n, f);
if (!r && n) return -1;
return r;
}

View file

@ -17,24 +17,19 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/stdio/stdio.h"
#include "libc/str/tpenc.h"
/**
* Writes wide character to stream.
*
* @return wc if written or -1 w/ errno
* @param wc has wide character
* @param f is file object stream pointer
* @return wide character if written or -1 w/ errno
* @threadsafe
*/
wint_t fputwc_unlocked(wchar_t wc, FILE *f) {
uint64_t w;
if (wc != -1) {
w = tpenc(wc);
do {
if (fputc_unlocked(w, f) == -1) {
return -1;
}
} while ((w >>= 8));
return wc;
} else {
return -1;
}
wint_t fputwc(wchar_t wc, FILE *f) {
wint_t rc;
flockfile(f);
rc = fputwc_unlocked(wc, f);
funlockfile(f);
return rc;
}

View file

@ -1,5 +1,5 @@
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
@ -16,18 +16,27 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/macros.internal.h"
#include "libc/stdio/stdio.h"
#include "libc/str/tpenc.h"
// Writes data to stream.
//
// @param rdi has pointer to data to write
// @param rsi stride specifies the size of individual items
// @param rdx count is the number of strides to write
// @param rcx has file object stream pointer
// @return count on success, [0,count) on EOF, 0 on error or count==0
// @see fwrite_unlocked()
// @threadsafe
fwrite: mov %rcx,%r11
ezlea fwrite_unlocked,ax
jmp stdio_unlock
.endfn fwrite,globl
/**
* Writes wide character to stream.
*
* @param wc has wide character
* @param f is file object stream pointer
* @return wide character if written or -1 w/ errno
*/
wint_t fputwc_unlocked(wchar_t wc, FILE *f) {
uint64_t w;
if (wc != -1) {
w = tpenc(wc);
do {
if (fputc_unlocked(w, f) == -1) {
return -1;
}
} while ((w >>= 8));
return wc;
} else {
return -1;
}
}

View file

@ -16,7 +16,6 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/errno.h"
#include "libc/stdio/stdio.h"
/**
@ -29,20 +28,12 @@
* @param s is a NUL-terminated string that's non-NULL
* @param f is an open stream
* @return strlen(s) or -1 w/ errno on error
* @threadsafe
*/
int fputws_unlocked(const wchar_t *s, FILE *f) {
int res = 0;
while (*s) {
if (fputwc_unlocked(*s++, f) == -1) {
if (ferror_unlocked(f) == EINTR) {
continue;
}
if (feof_unlocked(f)) {
errno = f->state = EPIPE;
}
return -1;
}
++res;
}
return ++res;
int fputws(const wchar_t *s, FILE *f) {
int rc;
flockfile(f);
rc = fputws_unlocked(s, f);
funlockfile(f);
return rc;
}

View file

@ -1,5 +1,5 @@
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
@ -16,20 +16,33 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/macros.internal.h"
#include "libc/errno.h"
#include "libc/stdio/stdio.h"
// Writes wide character string to stream.
//
// Writing stops at the NUL-terminator, which isn't included in output.
// This function blocks until the full string is written, unless an
// unrecoverable error happens.
//
// @param rdi is nul-terminated string that's non-null
// @param rsi is file object stream pointer
// @return strlen(rdi) on success or -1 w/ errno
// @see fputws_unlocked()
// @threadsafe
fputws: mov %rsi,%r11
ezlea fputws_unlocked,ax
jmp stdio_unlock
.endfn fputws,globl
/**
* Writes wide character string to stream.
*
* Writing stops at the NUL-terminator, which isn't included in output.
* This function blocks until the full string is written, unless an
* unrecoverable error happens.
*
* @param s is a NUL-terminated string that's non-NULL
* @param f is an open stream
* @return strlen(s) or -1 w/ errno on error
*/
int fputws_unlocked(const wchar_t *s, FILE *f) {
int res = 0;
while (*s) {
if (fputwc_unlocked(*s++, f) == -1) {
if (ferror_unlocked(f) == EINTR) {
continue;
}
if (feof_unlocked(f)) {
errno = f->state = EPIPE;
}
return -1;
}
++res;
}
return ++res;
}

View file

@ -16,19 +16,7 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/calls.h"
#include "libc/calls/struct/iovec.h"
#include "libc/errno.h"
#include "libc/fmt/conv.h"
#include "libc/macros.internal.h"
#include "libc/runtime/runtime.h"
#include "libc/sock/sock.h"
#include "libc/stdio/internal.h"
#include "libc/stdio/stdio.h"
#include "libc/str/internal.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/errfuns.h"
/**
* Reads data from stream.
@ -36,64 +24,12 @@
* @param stride specifies the size of individual items
* @param count is the number of strides to fetch
* @return count on success, [0,count) on eof, or 0 on error or count==0
* @threadsafe
*/
size_t fread_unlocked(void *buf, size_t stride, size_t count, FILE *f) {
char *p;
ssize_t rc;
size_t n, m;
struct iovec iov[2];
if ((f->iomode & O_ACCMODE) == O_WRONLY) {
f->state = errno = EBADF;
return 0;
}
if (f->beg > f->end) {
f->state = errno = EINVAL;
return 0;
}
p = buf;
n = stride * count;
m = f->end - f->beg;
if (MIN(n, m)) memcpy(p, f->buf + f->beg, MIN(n, m));
if (n < m) {
f->beg += n;
return count;
}
if (n == m) {
f->beg = f->end = 0;
return count;
}
if (f->fd == -1) {
f->beg = 0;
f->end = 0;
f->state = -1;
return m / stride;
}
iov[0].iov_base = p + m;
iov[0].iov_len = n - m;
if (f->bufmode != _IONBF && n < f->size) {
iov[1].iov_base = f->buf;
if (f->size > PUSHBACK) {
iov[1].iov_len = f->size - PUSHBACK;
} else {
iov[1].iov_len = f->size;
}
} else {
iov[1].iov_base = NULL;
iov[1].iov_len = 0;
}
if ((rc = readv(f->fd, iov, 2)) == -1) {
f->state = errno;
return 0;
}
n = rc;
f->beg = 0;
f->end = 0;
if (n > iov[0].iov_len) {
f->end += n - iov[0].iov_len;
return count;
} else {
n = (m + n) / stride;
if (n < count) f->state = -1;
return n;
}
size_t fread(void *buf, size_t stride, size_t count, FILE *f) {
size_t rc;
flockfile(f);
rc = fread_unlocked(buf, stride, count, f);
funlockfile(f);
return rc;
}

104
libc/stdio/fread_unlocked.c Normal file
View file

@ -0,0 +1,104 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=8 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/calls.h"
#include "libc/calls/struct/iovec.h"
#include "libc/errno.h"
#include "libc/fmt/conv.h"
#include "libc/macros.internal.h"
#include "libc/runtime/runtime.h"
#include "libc/sock/sock.h"
#include "libc/stdio/internal.h"
#include "libc/stdio/stdio.h"
#include "libc/str/internal.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/errfuns.h"
/**
* Reads data from stream.
*
* @param stride specifies the size of individual items
* @param count is the number of strides to fetch
* @return count on success, [0,count) on eof, or 0 on error or count==0
*/
size_t fread_unlocked(void *buf, size_t stride, size_t count, FILE *f) {
char *p;
ssize_t rc;
size_t n, m;
struct iovec iov[2];
if ((f->iomode & O_ACCMODE) == O_WRONLY) {
f->state = errno = EBADF;
return 0;
}
if (f->beg > f->end) {
f->state = errno = EINVAL;
return 0;
}
if (__builtin_mul_overflow(stride, count, &n)) {
f->state = errno = EOVERFLOW;
return 0;
}
p = buf;
m = f->end - f->beg;
if (MIN(n, m)) {
memcpy(p, f->buf + f->beg, MIN(n, m));
}
if (n < m) {
f->beg += n;
return count;
}
if (n == m) {
f->beg = f->end = 0;
return count;
}
if (f->fd == -1) {
f->beg = 0;
f->end = 0;
f->state = -1;
return m / stride;
}
iov[0].iov_base = p + m;
iov[0].iov_len = n - m;
if (f->bufmode != _IONBF && n < f->size) {
iov[1].iov_base = f->buf;
if (f->size > PUSHBACK) {
iov[1].iov_len = f->size - PUSHBACK;
} else {
iov[1].iov_len = f->size;
}
} else {
iov[1].iov_base = NULL;
iov[1].iov_len = 0;
}
if ((rc = readv(f->fd, iov, 2)) == -1) {
f->state = errno;
return 0;
}
n = rc;
f->beg = 0;
f->end = 0;
if (n > iov[0].iov_len) {
f->end += n - iov[0].iov_len;
return count;
} else {
n = (m + n) / stride;
if (n < count) f->state = -1;
return n;
}
}

View file

@ -16,11 +16,7 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/calls.h"
#include "libc/errno.h"
#include "libc/stdio/internal.h"
#include "libc/stdio/stdio.h"
#include "libc/sysv/consts/o.h"
/**
* Repositions open file stream.
@ -34,46 +30,12 @@
* @param offset is the byte delta
* @param whence can be SEET_SET, SEEK_CUR, or SEEK_END
* @returns 0 on success or -1 on error
* @threadsafe
*/
int fseeko_unlocked(FILE *f, int64_t offset, int whence) {
int res;
ssize_t rc;
int64_t pos;
if (f->fd != -1) {
if (__fflush_impl(f) == -1) return -1;
if (whence == SEEK_CUR && f->beg < f->end) {
offset -= f->end - f->beg;
}
if (lseek(f->fd, offset, whence) != -1) {
f->beg = 0;
f->end = 0;
res = 0;
} else {
f->state = errno == ESPIPE ? EBADF : errno;
res = -1;
}
} else {
switch (whence) {
case SEEK_SET:
pos = offset;
break;
case SEEK_CUR:
pos = f->beg + offset;
break;
case SEEK_END:
pos = f->end + offset;
break;
default:
pos = -1;
break;
}
if (0 <= pos && pos <= f->end) {
f->beg = pos;
res = 0;
} else {
f->state = errno = EINVAL;
res = -1;
}
}
return res;
int fseeko(FILE *f, int64_t offset, int whence) {
int rc;
flockfile(f);
rc = fseeko_unlocked(f, offset, whence);
funlockfile(f);
return rc;
}

View file

@ -0,0 +1,79 @@
/*-*- 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/calls/calls.h"
#include "libc/errno.h"
#include "libc/stdio/internal.h"
#include "libc/stdio/stdio.h"
#include "libc/sysv/consts/o.h"
/**
* Repositions open file stream.
*
* This function flushes the buffer (unless it's currently in the EOF
* state) and then calls lseek() on the underlying file. If the stream
* is in the EOF state, this function can be used to restore it without
* needing to reopen the file.
*
* @param f is a non-null stream handle
* @param offset is the byte delta
* @param whence can be SEET_SET, SEEK_CUR, or SEEK_END
* @returns 0 on success or -1 on error
*/
int fseeko_unlocked(FILE *f, int64_t offset, int whence) {
int res;
ssize_t rc;
int64_t pos;
if (f->fd != -1) {
if (__fflush_impl(f) == -1) return -1;
if (whence == SEEK_CUR && f->beg < f->end) {
offset -= f->end - f->beg;
}
if (lseek(f->fd, offset, whence) != -1) {
f->beg = 0;
f->end = 0;
res = 0;
} else {
f->state = errno == ESPIPE ? EBADF : errno;
res = -1;
}
} else {
switch (whence) {
case SEEK_SET:
pos = offset;
break;
case SEEK_CUR:
pos = f->beg + offset;
break;
case SEEK_END:
pos = f->end + offset;
break;
default:
pos = -1;
break;
}
if (0 <= pos && pos <= f->end) {
f->beg = pos;
res = 0;
} else {
f->state = errno = EINVAL;
res = -1;
}
}
return res;
}

View file

@ -23,7 +23,7 @@
#include "libc/stdio/stdio.h"
#include "libc/sysv/consts/o.h"
static int64_t ftello_unlocked(FILE *f) {
static inline int64_t ftello_unlocked(FILE *f) {
int64_t pos;
uint32_t skew;
if (f->fd != -1) {
@ -45,6 +45,7 @@ static int64_t ftello_unlocked(FILE *f) {
*
* @param stream is a non-null stream handle
* @returns current byte offset from beginning, or -1 w/ errno
* @threadsafe
*/
int64_t ftello(FILE *f) {
int64_t rc;

View file

@ -23,6 +23,6 @@
*
* @return 0 on success, or non-zero if another thread owns the lock
*/
int ftrylockfile(FILE *f) {
int(ftrylockfile)(FILE *f) {
return pthread_mutex_trylock(&f->lock);
}

View file

@ -21,6 +21,6 @@
/**
* Releases lock on stdio object.
*/
void funlockfile(FILE *f) {
void(funlockfile)(FILE *f) {
pthread_mutex_unlock(&f->lock);
}

View file

@ -16,18 +16,7 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/calls/struct/iovec.h"
#include "libc/errno.h"
#include "libc/fmt/conv.h"
#include "libc/macros.internal.h"
#include "libc/runtime/runtime.h"
#include "libc/sock/sock.h"
#include "libc/stdio/internal.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/o.h"
/**
* Writes data to stream.
@ -35,54 +24,12 @@
* @param stride specifies the size of individual items
* @param count is the number of strides to write
* @return count on success, [0,count) on EOF, 0 on error or count==0
* @threadsafe
*/
size_t fwrite_unlocked(const void *data, size_t stride, size_t count, FILE *f) {
ldiv_t d;
ssize_t rc;
size_t n, m;
const char *p;
struct iovec iov[2];
if ((f->iomode & O_ACCMODE) == O_RDONLY) {
f->state = errno = EBADF;
return 0;
}
n = stride * count;
m = f->size - f->beg;
if (n <= m && f->bufmode != _IONBF) {
memcpy(f->buf + f->beg, data, n);
f->beg += n;
if (f->fd != -1 && f->bufmode == _IOLBF &&
(p = memrchr(f->buf, '\n', f->beg))) {
n = p + 1 - f->buf;
if ((rc = write(f->fd, f->buf, n)) == -1) {
if (errno == EINTR || errno == EAGAIN) return count;
f->state = errno;
return 0;
}
n = rc;
memmove(f->buf, f->buf + n, f->beg - n);
f->beg -= n;
}
return count;
}
if (f->fd == -1) {
n = MIN(n, m);
d = ldiv(n, stride);
n -= d.rem;
memcpy(f->buf + f->beg, data, n);
f->beg += n;
f->state = -1;
return d.quot;
}
iov[0].iov_base = f->buf;
iov[0].iov_len = f->beg;
iov[1].iov_base = data;
iov[1].iov_len = n;
n += f->beg;
if (WritevUninterruptible(f->fd, iov, 2) == -1) {
f->state = errno;
return 0;
}
f->beg = 0;
return count;
size_t fwrite(const void *data, size_t stride, size_t count, FILE *f) {
size_t rc;
flockfile(f);
rc = fwrite_unlocked(data, stride, count, f);
funlockfile(f);
return rc;
}

View file

@ -0,0 +1,113 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=8 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/assert.h"
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/calls/struct/iovec.h"
#include "libc/errno.h"
#include "libc/fmt/conv.h"
#include "libc/macros.internal.h"
#include "libc/runtime/runtime.h"
#include "libc/sock/sock.h"
#include "libc/stdio/internal.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/o.h"
/**
* Writes data to stream.
*
* @param stride specifies the size of individual items
* @param count is the number of strides to write
* @return count on success, [0,count) on EOF, 0 on error or count==0
*/
size_t fwrite_unlocked(const void *data, size_t stride, size_t count, FILE *f) {
ldiv_t d;
ssize_t rc;
size_t n, m;
const char *p;
struct iovec iov[2];
if ((f->iomode & O_ACCMODE) == O_RDONLY) {
f->state = errno = EBADF;
return 0;
}
if (__builtin_mul_overflow(stride, count, &n)) {
f->state = errno = EOVERFLOW;
return 0;
}
m = f->size - f->beg;
if (n <= m && f->bufmode != _IONBF) {
// this isn't a fully buffered stream, and
// there's enough room in the buffer for the request
memcpy(f->buf + f->beg, data, n);
f->beg += n;
if (f->fd != -1 && f->bufmode == _IOLBF &&
(p = memrchr(f->buf, '\n', f->beg))) {
// write out as many lines as possible
n = p + 1 - f->buf;
if ((rc = write(f->fd, f->buf, n)) == -1) {
// the write() system call failed
if (errno == EINTR || errno == EAGAIN) {
return count;
} else {
f->state = errno;
return 0;
}
}
// copy backwards last line fragment or uncompleted data
n = rc;
if (f->beg - n) {
memmove(f->buf, f->buf + n, f->beg - n);
}
f->beg -= n;
}
return count;
}
// what's happening is either
// (1) a fully buffered stream, or
// (2) no room in buffer to hold full request
if (f->fd == -1) {
// this is an in-memory stream
// store as much of request as we can hold
n = MIN(n, m);
d = ldiv(n, stride);
n -= d.rem;
if (n) {
memcpy(f->buf + f->beg, data, n);
f->beg += n;
}
// trigger eof condition
f->state = EOF;
return d.quot;
}
// perform a fragmented write
// (1) we avoid needless copies in fully buffered mode
// (2) we avoid need for malloc() when it's out of room
iov[0].iov_base = f->buf;
iov[0].iov_len = f->beg;
iov[1].iov_base = data;
iov[1].iov_len = n;
n += f->beg;
if (WritevUninterruptible(f->fd, iov, 2) == -1) {
f->state = errno;
return 0;
}
f->beg = 0;
return count;
}

View file

@ -21,7 +21,8 @@
/**
* Reads byte from stream.
* @return byte in range 0..255, or -1 w/ errno
* @threadsafe
*/
int(getc_unlocked)(FILE *f) {
return fgetc_unlocked(f);
int(getc)(FILE *f) {
return getc(f);
}

View file

@ -1,5 +1,5 @@
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=8 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
@ -16,16 +16,12 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/macros.internal.h"
#include "libc/stdio/stdio.h"
// Reads character from stdin.
//
// @return byte in range 0..255, or -1 w/ errno
// @see fgetc_unlocked()
// @threadsafe
getchar:
mov stdin(%rip),%rdi
mov %rdi,%r11
ezlea fgetc_unlocked,ax
jmp stdio_unlock
.endfn getchar,globl
/**
* Reads byte from stream.
* @return byte in range 0..255, or -1 w/ errno
*/
int(getc_unlocked)(FILE *f) {
return fgetc_unlocked(f);
}

View file

@ -21,7 +21,8 @@
/**
* Reads byte from stdin.
* @return byte in range 0..255, or -1 w/ errno
* @htreadsafe
*/
int getchar_unlocked(void) {
return fgetc_unlocked(stdin);
int getchar(void) {
return fgetc(stdin);
}

View file

@ -0,0 +1,27 @@
/*-*- 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/stdio/stdio.h"
/**
* Reads byte from stdin.
* @return byte in range 0..255, or -1 w/ errno
*/
int getchar_unlocked(void) {
return fgetc_unlocked(stdin);
}

View file

@ -21,7 +21,8 @@
/**
* Reads UTF-8 character from stream.
* @return wide character or -1 on EOF or error
* @threadsafe
*/
wint_t(getwc_unlocked)(FILE *f) {
return fgetwc_unlocked(f);
wint_t(getwc)(FILE *f) {
return fgetwc(f);
}

View file

@ -1,5 +1,5 @@
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
@ -16,16 +16,12 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/macros.internal.h"
#include "libc/stdio/stdio.h"
// Reads UTF-8 character from stdin.
//
// @return wide character or -1 on EOF or error
// @see fgetwc_unlocked()
// @threadsafe
getwchar:
mov stdin(%rip),%rdi
mov %rdi,%r11
ezlea fgetwc_unlocked,ax
jmp stdio_unlock
.endfn getwchar,globl
/**
* Reads UTF-8 character from stream.
* @return wide character or -1 on EOF or error
*/
wint_t(getwc_unlocked)(FILE *f) {
return fgetwc_unlocked(f);
}

View file

@ -21,7 +21,8 @@
/**
* Reads UTF-8 character from stream.
* @return wide character or -1 on EOF or error
* @threadsafe
*/
wint_t getwchar_unlocked(void) {
return fgetwc_unlocked(stdin);
wint_t getwchar(void) {
return fgetwc(stdin);
}

View file

@ -1,5 +1,5 @@
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
@ -16,15 +16,12 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/macros.internal.h"
#include "libc/stdio/stdio.h"
// Reads character from stream.
//
// @param rdi has file stream object pointer
// @return byte in range 0..255, or -1 w/ errno
// @see fgetc_unlocked()
// @threadsafe
getc: mov %rdi,%r11
ezlea fgetwc_unlocked,ax
jmp stdio_unlock
.endfn getc,globl
/**
* Reads UTF-8 character from stream.
* @return wide character or -1 on EOF or error
*/
wint_t getwchar_unlocked(void) {
return fgetwc_unlocked(stdin);
}

View file

@ -23,7 +23,8 @@
*
* @param c is byte to buffer or write, which is masked
* @return c as unsigned char if written or -1 w/ errno
* @threadsafe
*/
int(putc_unlocked)(int c, FILE *f) {
return fputc_unlocked(c, f);
int(putc)(int c, FILE *f) {
return fputc(c, f);
}

View file

@ -1,5 +1,5 @@
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=8 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
@ -16,15 +16,14 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/macros.internal.h"
#include "libc/stdio/stdio.h"
// Reads UTF-8 character from stream.
//
// @param rdi has file stream object pointer
// @return wide character or -1 on EOF or error
// @see fgetwc_unlocked()
// @threadsafe
getwc: mov %rdi,%r11
ezlea fgetwc_unlocked,ax
jmp stdio_unlock
.endfn getwc,globl
/**
* Writes byte to stream.
*
* @param c is byte to buffer or write, which is masked
* @return c as unsigned char if written or -1 w/ errno
*/
int(putc_unlocked)(int c, FILE *f) {
return fputc_unlocked(c, f);
}

View file

@ -22,7 +22,8 @@
* Writes byte to stdout.
*
* @return c (as unsigned char) if written or -1 w/ errno
* @threadsafe
*/
int putchar_unlocked(int c) {
return fputc_unlocked(c, stdout);
int putchar(int c) {
return fputc(c, stdout);
}

View file

@ -0,0 +1,28 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/stdio/stdio.h"
/**
* Writes byte to stdout.
*
* @return c (as unsigned char) if written or -1 w/ errno
*/
int putchar_unlocked(int c) {
return fputc_unlocked(c, stdout);
}

View file

@ -18,7 +18,7 @@
*/
#include "libc/stdio/stdio.h"
static int PutsImpl(const char *s, FILE *f) {
static inline int PutsImpl(const char *s, FILE *f) {
size_t n, r;
if ((n = strlen(s))) {
r = fwrite_unlocked(s, 1, n, f);

View file

@ -22,7 +22,8 @@
* Writes wide character to stream.
*
* @return wc if written or -1 w/ errno
* @threadsafe
*/
wint_t(putwc_unlocked)(wchar_t wc, FILE *f) {
return fputwc_unlocked(wc, f);
wint_t(putwc)(wchar_t wc, FILE *f) {
return fputwc(wc, f);
}

View file

@ -0,0 +1,28 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/stdio/stdio.h"
/**
* Writes wide character to stream.
*
* @return wc if written or -1 w/ errno
*/
wint_t(putwc_unlocked)(wchar_t wc, FILE *f) {
return fputwc_unlocked(wc, f);
}

View file

@ -21,7 +21,8 @@
/**
* Writes wide character to stdout.
* @return wc if written or -1 w/ errno
* @threadsafe
*/
wint_t putwchar_unlocked(wchar_t wc) {
return fputwc_unlocked(wc, stdout);
wint_t putwchar(wchar_t wc) {
return fputwc(wc, stdout);
}

View file

@ -0,0 +1,27 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/stdio/stdio.h"
/**
* Writes wide character to stdout.
* @return wc if written or -1 w/ errno
*/
wint_t putwchar_unlocked(wchar_t wc) {
return fputwc_unlocked(wc, stdout);
}

View file

@ -2,6 +2,7 @@
#define COSMOPOLITAN_LIBC_STDIO_STDIO_H_
#include "libc/fmt/pflink.h"
#include "libc/intrin/pthread.h"
#include "libc/nexgen32e/threaded.h"
#include "libc/runtime/symbolic.h"
#define FILENAME_MAX PATH_MAX
@ -157,20 +158,24 @@ int fprintf_unlocked(FILE *, const char *, ...) printfesque(2)
int vfprintf_unlocked(FILE *, const char *, va_list)
paramsnonnull() dontthrow nocallback;
#define getc_unlocked(f) fgetc_unlocked(f)
#define getwc_unlocked(f) fgetwc_unlocked(f)
#define putc_unlocked(c, f) fputc_unlocked(c, f)
#define putwc_unlocked(c, f) fputwc_unlocked(c, f)
/*───────────────────────────────────────────────────────────────────────────│─╗
cosmopolitan § standard i/o » optimizations
*/
#define flockfile(f) (__threaded ? flockfile(f) : 0)
#define funlockfile(f) (__threaded ? funlockfile(f) : 0)
#define ftrylockfile(f) (__threaded ? ftrylockfile(f) : 0)
#define getc(f) fgetc(f)
#define getwc(f) fgetwc(f)
#define putc(c, f) fputc(c, f)
#define putwc(c, f) fputwc(c, f)
#define getc_unlocked(f) fgetc_unlocked(f)
#define getwc_unlocked(f) fgetwc_unlocked(f)
#define putc_unlocked(c, f) fputc_unlocked(c, f)
#define putwc_unlocked(c, f) fputwc_unlocked(c, f)
#if defined(__GNUC__) && !defined(__STRICT_ANSI__)
/* clang-format off */
#define printf(FMT, ...) (printf)(PFLINK(FMT), ##__VA_ARGS__)

View file

@ -17,20 +17,15 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
/**
* Pushes byte back to stream.
* @threadsafe
*/
int ungetc_unlocked(int c, FILE *f) {
if (c == -1) return -1;
if (f->beg) {
f->buf[--f->beg] = c;
} else if (f->end < f->size) {
memmove(f->buf + 1, f->buf, f->end++);
f->buf[0] = c;
} else {
return -1;
}
return c & 255;
int ungetc(int c, FILE *f) {
int rc;
flockfile(f);
rc = ungetc_unlocked(c, f);
funlockfile(f);
return rc;
}

View file

@ -1,5 +1,5 @@
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=8 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
@ -16,16 +16,21 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/macros.internal.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
// Writes wide character to stream.
//
// @param rdi has wide character
// @param rsi has file object stream pointer
// @return rax is wide character if written or -1 w/ errno
// @see fputwc_unlocked()
// @threadsafe
fputwc: mov %rsi,%r11
ezlea fputwc_unlocked,ax
jmp stdio_unlock
.endfn fputwc,globl
/**
* Pushes byte back to stream.
*/
int ungetc_unlocked(int c, FILE *f) {
if (c == -1) return -1;
if (f->beg) {
f->buf[--f->beg] = c;
} else if (f->end < f->size) {
memmove(f->buf + 1, f->buf, f->end++);
f->buf[0] = c;
} else {
return -1;
}
return c & 255;
}

View file

@ -17,31 +17,15 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/str/tpenc.h"
/**
* Pushes wide character back to stream.
* @threadsafe
*/
wint_t ungetwc_unlocked(wint_t c, FILE *f) {
char b[6];
unsigned n;
uint64_t w;
if (c == -1) return -1;
n = 0;
w = tpenc(c);
do {
b[n++] = w;
} while ((w >>= 8));
if (f->beg >= n) {
f->beg -= n;
memcpy(f->buf + f->beg, b, n);
} else if (f->beg + f->end + n <= f->size) {
memmove(f->buf + f->beg + n, f->buf + f->beg, f->end - f->beg);
memcpy(f->buf + f->beg, b, n);
f->end += n;
} else {
return -1;
}
return c;
wint_t ungetwc(wint_t c, FILE *f) {
wint_t rc;
flockfile(f);
rc = ungetwc_unlocked(c, f);
funlockfile(f);
return rc;
}

View file

@ -1,5 +1,5 @@
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=8 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
@ -16,20 +16,32 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/macros.internal.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/str/tpenc.h"
// Reads UTF-8 content from stream into UTF-32 buffer.
//
// This function is similar to getline() except it'll truncate lines
// exceeding size. The line ending marker is included and may be removed
// using _chomp().
//
// @param rdi is nul-terminated string that's non-null
// @param rsi is size of rdi buffer
// @param rsi is file stream object pointer
// @see fgetws_unlocked()
// @threadsafe
fgetws: mov %rdx,%r11
ezlea fgetws_unlocked,ax
jmp stdio_unlock
.endfn fgetws,globl
/**
* Pushes wide character back to stream.
*/
wint_t ungetwc_unlocked(wint_t c, FILE *f) {
char b[6];
unsigned n;
uint64_t w;
if (c == -1) return -1;
n = 0;
w = tpenc(c);
do {
b[n++] = w;
} while ((w >>= 8));
if (f->beg >= n) {
f->beg -= n;
memcpy(f->buf + f->beg, b, n);
} else if (f->beg + f->end + n <= f->size) {
memmove(f->buf + f->beg + n, f->buf + f->beg, f->end - f->beg);
memcpy(f->buf + f->beg, b, n);
f->end += n;
} else {
return -1;
}
return c;
}

View file

@ -1,33 +0,0 @@
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/macros.internal.h"
// Reads data from stream.
//
// @param rdi has pointer to data to read
// @param rsi stride specifies the size of individual items
// @param rdx count is the number of strides to read
// @param rcx has file object stream pointer
// @return count on success, [0,count) on EOF, 0 on error or count==0
// @see fread_unlocked()
// @threadsafe
fread: mov %rcx,%r11
ezlea fread_unlocked,ax
jmp stdio_unlock
.endfn fread,globl

View file

@ -1,37 +0,0 @@
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/macros.internal.h"
// Repositions open file stream.
//
// This function flushes the buffer (unless it's currently in the EOF
// state) and then calls lseek() on the underlying file. If the stream
// is in the EOF state, this function can be used to restore it without
// needing to reopen the file.
//
// @param rdi is stream handle
// @param rsi is offset is the byte delta
// @param rdx is whence and can be SEET_SET, SEEK_CUR, or SEEK_END
// @return 0 on success or -1 w/ errno
// @see fflush_unlocked()
// @threadsafe
fseeko: mov %rdi,%r11
ezlea fseeko_unlocked,ax
jmp stdio_unlock
.endfn fseeko,globl

View file

@ -1,31 +0,0 @@
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/macros.internal.h"
// Writes character to stream.
//
// @param rdi c is byte to buffer or write, which is masked
// @param rsi has stream object pointer
// @return c as unsigned char if written or -1 w/ errno
// @see fputc_unlocked()
// @threadsafe
putc: mov %rsi,%r11
ezlea fputc_unlocked,ax
jmp stdio_unlock
.endfn putc,globl

View file

@ -1,32 +0,0 @@
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/macros.internal.h"
// Writes character to stdout.
//
// @param rdi has character
// @return c (as unsigned char) if written or -1 w/ errno
// @see fputc_unlocked()
// @threadsafe
putchar:
mov stdout(%rip),%rsi
mov %rsi,%r11
ezlea fputc_unlocked,ax
jmp stdio_unlock
.endfn putchar,globl

View file

@ -1,31 +0,0 @@
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/macros.internal.h"
// Writes wide character to stream.
//
// @param rdi has wide character
// @param rsi has file object
// @return wc if written or -1 w/ errno
// @see putwc_unlocked()
// @threadsafe
putwc: mov %rsi,%r11
ezlea fputwc_unlocked,ax
jmp stdio_unlock
.endfn putwc,globl

View file

@ -1,32 +0,0 @@
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/macros.internal.h"
// Writes wide character to stdout.
//
// @param rdi has wide character
// @return wc if written or -1 w/ errno
// @see fputwc_unlocked()
// @threadsafe
putwchar:
mov stdout(%rip),%rsi
mov %rsi,%r11
ezlea fputwc_unlocked,ax
jmp stdio_unlock
.endfn putwchar,globl

View file

@ -1,72 +0,0 @@
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi
Copyright 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/macros.internal.h"
// Wrapper for applying locking to stdio functions.
//
// This function is intended to be called by thunks.
//
// @param rax is stdio function pointer
// @param rdi is passed along as an arg
// @param rsi is passed along as an arg
// @param rdx is passed along as an arg
// @param rcx is passed along as an arg
// @param r11 has the FILE* obj pointer
// @return rax is passed along as result
// @return rdx is passed along as result
// @threadsafe
stdio_unlock:
push %rbp
mov %rsp,%rbp
.profilable
// acquires mutex
push %rax
push %rdi
push %rsi
push %rdx
push %rcx
push %r11
mov %r11,%rdi
call flockfile
pop %r11
pop %rcx
pop %rdx
pop %rsi
pop %rdi
pop %rax
// calls delegate
push %r11
push %rsi # align stack
call *%rax
pop %rsi
pop %r11
// releases mutex
push %rax
push %rdx
mov %r11,%rdi
call funlockfile
pop %rdx
pop %rax
pop %rbp
ret
.endfn stdio_unlock,globl

View file

@ -1,31 +0,0 @@
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi
Copyright 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/macros.internal.h"
// Pushes byte back to stream.
//
// @param rdi has character to push
// @param rds has stream object pointer
// @return rax has rdi on success or -1 w/ errno
// @see ungetc_unlocked()
// @threadsafe
ungetc: mov %rsi,%r11
ezlea ungetc_unlocked,ax
jmp stdio_unlock
.endfn ungetc,globl

View file

@ -1,32 +0,0 @@
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi
Copyright 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/macros.internal.h"
// Pushes byte back to stream.
//
// @param rdi has character to push
// @param rds has stream object pointer
// @return rax has rdi on success or -1 w/ errno
// @see ungetwc_unlocked()
// @threadsafe
ungetwc:
mov %rsi,%r11
ezlea ungetwc_unlocked,ax
jmp stdio_unlock
.endfn ungetwc,globl

View file

@ -69,8 +69,10 @@ noasan static inline const unsigned char *memchr_sse(const unsigned char *s,
*/
void *memchr(const void *s, int c, size_t n) {
const void *r;
if (X86_HAVE(SSE)) {
if (IsAsan()) __asan_verify(s, n);
if (!IsTiny() && X86_HAVE(SSE)) {
if (IsAsan()) {
__asan_verify(s, n);
}
r = memchr_sse(s, c, n);
} else {
r = memchr_pure(s, c, n);

View file

@ -17,6 +17,7 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/errno.h"
#include "libc/nexgen32e/gettls.h"
#include "libc/nexgen32e/threaded.h"
/**

View file

@ -16,7 +16,7 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/nexgen32e/threaded.h"
#include "libc/nexgen32e/gettls.h"
/**
* Returns address of thread information block.

View file

@ -16,6 +16,7 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/nexgen32e/gettls.h"
#include "libc/nexgen32e/threaded.h"
#include "libc/thread/thread.h"

View file

@ -7,6 +7,7 @@
#include "libc/intrin/pthread.h"
#include "libc/intrin/spinlock.h"
#include "libc/mem/mem.h"
#include "libc/nexgen32e/threaded.h"
#include "libc/runtime/gc.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/o.h"
@ -45,15 +46,18 @@ STATIC_YOINK("usr/share/zoneinfo/UTC");
static pthread_mutex_t locallock;
static int lock(void) {
static int localtime_lock(void) {
pthread_mutex_lock(&locallock);
return 0;
}
static void unlock(void) {
static void localtime_unlock(void) {
pthread_mutex_unlock(&locallock);
}
#define localtime_lock() (__threaded ? localtime_lock() : 0)
#define localtime_unlock() (__threaded ? localtime_unlock() : 0)
#ifndef TZ_ABBR_MAX_LEN
#define TZ_ABBR_MAX_LEN 16
#endif /* !defined TZ_ABBR_MAX_LEN */
@ -1407,10 +1411,10 @@ localtime_tzset_unlocked(void)
void
tzset(void)
{
if (lock() != 0)
if (localtime_lock() != 0)
return;
localtime_tzset_unlocked();
unlock();
localtime_unlock();
}
static void
@ -1422,7 +1426,7 @@ static void
localtime_gmtcheck(void)
{
static bool gmt_is_set;
if (lock() != 0)
if (localtime_lock() != 0)
return;
if (! gmt_is_set) {
gmtptr = malloc(sizeof *gmtptr);
@ -1431,7 +1435,7 @@ localtime_gmtcheck(void)
gmtload(gmtptr);
gmt_is_set = true;
}
unlock();
localtime_unlock();
}
/*
@ -1535,7 +1539,7 @@ localsub(struct state const *sp, time_t const *timep, int_fast32_t setname,
static struct tm *
localtime_tzset(time_t const *timep, struct tm *tmp, bool setname)
{
int err = lock();
int err = localtime_lock();
if (err) {
errno = err;
return NULL;
@ -1543,7 +1547,7 @@ localtime_tzset(time_t const *timep, struct tm *tmp, bool setname)
if (setname || !lcl_is_set)
localtime_tzset_unlocked();
tmp = localsub(lclptr, timep, setname, tmp);
unlock();
localtime_unlock();
return tmp;
}
@ -2150,14 +2154,14 @@ time_t
mktime(struct tm *tmp)
{
time_t t;
int err = lock();
int err = localtime_lock();
if (err) {
errno = err;
return -1;
}
localtime_tzset_unlocked();
t = mktime_tzname(lclptr, tmp, true);
unlock();
localtime_unlock();
return t;
}

View file

@ -17,6 +17,7 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/errno.h"
#include "libc/nexgen32e/gettls.h"
#include "libc/nexgen32e/threaded.h"
#include "libc/runtime/runtime.h"
#include "libc/testlib/testlib.h"

View file

@ -23,6 +23,7 @@
#include "libc/intrin/spinlock.h"
#include "libc/log/backtrace.internal.h"
#include "libc/mem/mem.h"
#include "libc/nexgen32e/gettls.h"
#include "libc/nexgen32e/nexgen32e.h"
#include "libc/nexgen32e/threaded.h"
#include "libc/runtime/runtime.h"

View file

@ -101,7 +101,7 @@ static dontinline int spin_acquire_lock(int *sl) {
#define MLOCK_T int
#define TRY_LOCK(sl) !CAS_LOCK(sl)
#define RELEASE_LOCK(sl) if (__threaded) CLEAR_LOCK(sl)
#define RELEASE_LOCK(sl) (__threaded && (CLEAR_LOCK(sl), 0))
#define ACQUIRE_LOCK(sl) (__threaded && CAS_LOCK(sl) ? spin_acquire_lock(sl) : 0)
#define INITIAL_LOCK(sl) (*sl = 0)
#define DESTROY_LOCK(sl) (0)

View file

@ -131,7 +131,7 @@ struct state {
* buffer, using shift right, and new bytes are appended to the top of the
* bit buffer, using shift left.
*/
static int bits(struct state *s, int need) {
static noinstrument int bits(struct state *s, int need) {
long val; /* bit accumulator (can use up to 20 bits) */
/* load at least need bits into val */