Make more functions friendly to Address Sanitizer

This commit is contained in:
Justine Tunney 2021-02-02 03:45:31 -08:00
parent 3ab76b2312
commit cbfd4ccd1e
70 changed files with 1267 additions and 291 deletions

View file

@ -8,6 +8,7 @@ LIBC_ALG = $(LIBC_ALG_A_DEPS) $(LIBC_ALG_A)
LIBC_ALG_A = o/$(MODE)/libc/alg/alg.a
LIBC_ALG_A_FILES := $(wildcard libc/alg/*)
LIBC_ALG_A_HDRS = $(filter %.h,$(LIBC_ALG_A_FILES))
LIBC_ALG_A_INCS = $(filter %.inc,$(LIBC_ALG_A_FILES))
LIBC_ALG_A_SRCS_S = $(filter %.S,$(LIBC_ALG_A_FILES))
LIBC_ALG_A_SRCS_C = $(filter %.c,$(LIBC_ALG_A_FILES))
@ -51,6 +52,7 @@ o/$(MODE)/libc/alg/critbit0.o: \
LIBC_ALG_LIBS = $(foreach x,$(LIBC_ALG_ARTIFACTS),$($(x)))
LIBC_ALG_SRCS = $(foreach x,$(LIBC_ALG_ARTIFACTS),$($(x)_SRCS))
LIBC_ALG_HDRS = $(foreach x,$(LIBC_ALG_ARTIFACTS),$($(x)_HDRS))
LIBC_ALG_INCS = $(foreach x,$(LIBC_ALG_ARTIFACTS),$($(x)_INCS))
LIBC_ALG_CHECKS = $(foreach x,$(LIBC_ALG_ARTIFACTS),$($(x)_CHECKS))
LIBC_ALG_OBJS = $(foreach x,$(LIBC_ALG_ARTIFACTS),$($(x)_OBJS))
$(LIBC_ALG_OBJS): $(BUILD_FILES) libc/alg/alg.mk

View file

@ -21,6 +21,7 @@ LIBC_CALLS_A_FILES := \
$(wildcard libc/calls/struct/*) \
$(wildcard libc/calls/*)
LIBC_CALLS_A_HDRS = $(filter %.h,$(LIBC_CALLS_A_FILES))
LIBC_CALLS_A_INCS = $(filter %.inc,$(LIBC_CALLS_A_FILES))
LIBC_CALLS_A_SRCS_S = $(filter %.S,$(LIBC_CALLS_A_FILES))
LIBC_CALLS_A_SRCS_C = $(filter %.c,$(LIBC_CALLS_A_FILES))
@ -86,6 +87,7 @@ o/$(MODE)/libc/calls/mkntenvblock.o: \
LIBC_CALLS_LIBS = $(foreach x,$(LIBC_CALLS_ARTIFACTS),$($(x)))
LIBC_CALLS_SRCS = $(foreach x,$(LIBC_CALLS_ARTIFACTS),$($(x)_SRCS))
LIBC_CALLS_HDRS = $(foreach x,$(LIBC_CALLS_ARTIFACTS),$($(x)_HDRS))
LIBC_CALLS_INCS = $(foreach x,$(LIBC_CALLS_ARTIFACTS),$($(x)_INCS))
LIBC_CALLS_BINS = $(foreach x,$(LIBC_CALLS_ARTIFACTS),$($(x)_BINS))
LIBC_CALLS_CHECKS = $(foreach x,$(LIBC_CALLS_ARTIFACTS),$($(x)_CHECKS))
LIBC_CALLS_OBJS = $(foreach x,$(LIBC_CALLS_ARTIFACTS),$($(x)_OBJS))

View file

@ -25,13 +25,14 @@ int32_t dup3$sysv(int32_t oldfd, int32_t newfd, int flags) {
static bool once, demodernize;
int olderr, fd;
if (!once) {
once = true;
olderr = errno;
fd = __dup3$sysv(oldfd, newfd, flags);
if ((fd == -1 && errno == ENOSYS) || fd == __NR_dup3_linux) {
demodernize = true;
once = true;
errno = olderr;
} else {
once = true;
return fd;
}
} else if (!demodernize) {

View file

@ -29,9 +29,9 @@ forceinline typeof(PrefetchVirtualMemory) *GetPrefetchVirtualMemory(void) {
static bool once;
static typeof(PrefetchVirtualMemory) *PrefetchVirtualMemory_;
if (!once) {
once = true;
PrefetchVirtualMemory_ = /* win8.1+ */
GetProcAddressModule("Kernel32.dll", "PrefetchVirtualMemory");
once = true;
}
return PrefetchVirtualMemory_;
}
@ -40,9 +40,9 @@ forceinline typeof(OfferVirtualMemory) *GetOfferVirtualMemory(void) {
static bool once;
static typeof(OfferVirtualMemory) *OfferVirtualMemory_;
if (!once) {
once = true;
OfferVirtualMemory_ = /* win8.1+ */
GetProcAddressModule("Kernel32.dll", "OfferVirtualMemory");
once = true;
}
return OfferVirtualMemory_;
}

View file

@ -23,6 +23,7 @@
#include "libc/dce.h"
#include "libc/nexgen32e/rdtsc.h"
#include "libc/nexgen32e/x86feature.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/clock.h"
#include "libc/time/time.h"
@ -55,19 +56,21 @@ static long double MeasureNanosPerCycle(void) {
}
static void InitTime(void) {
g_now.cpn = MeasureNanosPerCycle();
g_now.r0 = dtime(CLOCK_REALTIME);
g_now.k0 = rdtsc();
g_now.once = true;
struct Now now;
now.cpn = MeasureNanosPerCycle();
now.r0 = dtime(CLOCK_REALTIME);
now.k0 = rdtsc();
now.once = true;
memcpy(&g_now, &now, sizeof(now));
}
long double converttickstonanos(uint64_t ticks) {
long double ConvertTicksToNanos(uint64_t ticks) {
if (!g_now.once) InitTime();
return ticks * g_now.cpn; /* pico scale */
}
long double converttickstoseconds(uint64_t ticks) {
return 1 / 1e9 * converttickstonanos(ticks);
static long double ConvertTicksToSeconds(uint64_t ticks) {
return 1 / 1e9 * ConvertTicksToNanos(ticks);
}
long double nowl$sys(void) {
@ -78,5 +81,5 @@ long double nowl$art(void) {
uint64_t ticks;
if (!g_now.once) InitTime();
ticks = unsignedsubtract(rdtsc(), g_now.k0);
return g_now.r0 + converttickstoseconds(ticks);
return g_now.r0 + ConvertTicksToSeconds(ticks);
}

View file

@ -17,6 +17,7 @@ LIBC_FMT = $(LIBC_FMT_A_DEPS) $(LIBC_FMT_A)
LIBC_FMT_A = o/$(MODE)/libc/fmt/fmt.a
LIBC_FMT_A_FILES := $(wildcard libc/fmt/*)
LIBC_FMT_A_HDRS = $(filter %.h,$(LIBC_FMT_A_FILES))
LIBC_FMT_A_INCS = $(filter %.inc,$(LIBC_FMT_A_FILES))
LIBC_FMT_A_SRCS_S = $(filter %.S,$(LIBC_FMT_A_FILES))
LIBC_FMT_A_SRCS_C = $(filter %.c,$(LIBC_FMT_A_FILES))
@ -75,6 +76,7 @@ o/$(MODE)/libc/fmt/itoa128radix10.greg.o: \
LIBC_FMT_LIBS = $(foreach x,$(LIBC_FMT_ARTIFACTS),$($(x)))
LIBC_FMT_SRCS = $(foreach x,$(LIBC_FMT_ARTIFACTS),$($(x)_SRCS))
LIBC_FMT_HDRS = $(foreach x,$(LIBC_FMT_ARTIFACTS),$($(x)_HDRS))
LIBC_FMT_INCS = $(foreach x,$(LIBC_FMT_ARTIFACTS),$($(x)_INCS))
LIBC_FMT_CHECKS = $(foreach x,$(LIBC_FMT_ARTIFACTS),$($(x)_CHECKS))
LIBC_FMT_OBJS = $(foreach x,$(LIBC_FMT_ARTIFACTS),$($(x)_OBJS))
$(LIBC_FMT_OBJS): $(BUILD_FILES) libc/fmt/fmt.mk

View file

@ -331,7 +331,7 @@ typedef uint64_t uintmax_t;
#ifndef nullterminated
#if !defined(__STRICT_ANSI__) && \
(__has_attribute(__sentinel__) || __GNUC__ >= 4)
(__has_attribute(__sentinel__) || __GNUC__ + 0 >= 4)
#define nullterminated(x) __attribute__((__sentinel__ x))
#else
#define nullterminated(x)

View file

@ -154,6 +154,13 @@ static void *__asan_repstosb(void *di, int al, size_t cx) {
return di;
}
static void *__asan_repmovsb(void *di, void *si, size_t cx) {
asm("rep movsb"
: "=D"(di), "=S"(si), "=c"(cx), "=m"(*(char(*)[cx])di)
: "0"(di), "1"(si), "2"(cx), "m"(*(char(*)[cx])si));
return di;
}
static void *__asan_memset(void *p, int c, size_t n) {
char *b;
size_t i;
@ -264,14 +271,18 @@ static void *__asan_mempcpy(void *dst, const void *src, size_t n) {
__builtin_memcpy(d + n - 8, &b, 8);
return d + n;
default:
i = 0;
do {
__builtin_memcpy(&a, s + i, 8);
asm volatile("" ::: "memory");
__builtin_memcpy(d + i, &a, 8);
} while ((i += 8) + 8 <= n);
for (; i < n; ++i) d[i] = s[i];
return d + i;
if (n <= 64) {
i = 0;
do {
__builtin_memcpy(&a, s + i, 8);
asm volatile("" ::: "memory");
__builtin_memcpy(d + i, &a, 8);
} while ((i += 8) + 8 <= n);
for (; i < n; ++i) d[i] = s[i];
return d + i;
} else {
return __asan_repmovsb(d, s, n);
}
}
}
@ -280,16 +291,6 @@ static void *__asan_memcpy(void *dst, const void *src, size_t n) {
return dst;
}
static void *__asan_memrchr(void *p, int c, size_t n) {
uint8_t *b;
for (c &= 0xff, b = p; n--;) {
if (b[n] == c) {
return b + n;
}
}
return NULL;
}
static size_t __asan_int2hex(uint64_t x, char b[17], uint8_t k) {
int i;
char *p;
@ -455,8 +456,6 @@ static wontreturn void __asan_report_heap_fault(void *addr, long c) {
char *p, ibuf[21], buf[256];
p = __asan_report_start(buf);
p = __asan_stpcpy(p, __asan_dscribe_heap_poison(c));
p = __asan_stpcpy(p, " ");
p = __asan_mempcpy(p, ibuf, __asan_int2str(c, ibuf));
p = __asan_stpcpy(p, " at 0x");
p = __asan_mempcpy(p, ibuf, __asan_int2hex((intptr_t)addr, ibuf, 48));
p = __asan_stpcpy(p, " shadow 0x");
@ -709,7 +708,9 @@ void __asan_map_shadow(uintptr_t p, size_t n) {
int i, x, a, b;
struct DirectMap sm;
struct MemoryIntervals *m;
if (0x7fff8000 <= p && p < 0x100080000000) {
if ((0x7fff8000 <= p && p < 0x100080000000) ||
(0x7fff8000 <= p + n && p + n < 0x100080000000) ||
(p < 0x7fff8000 && 0x100080000000 <= p + n)) {
__asan_die("asan error: mmap can't shadow a shadow\r\n");
}
m = weaken(_mmi);

View file

@ -46,7 +46,8 @@ $(LIBC_INTRIN_A_OBJS): \
o/$(MODE)/libc/intrin/asan.o: \
OVERRIDE_CFLAGS += \
-mgeneral-regs-only
-mgeneral-regs-only \
-O2
LIBC_INTRIN_LIBS = $(foreach x,$(LIBC_INTRIN_ARTIFACTS),$($(x)))
LIBC_INTRIN_HDRS = $(foreach x,$(LIBC_INTRIN_ARTIFACTS),$($(x)_HDRS))

View file

@ -29,6 +29,6 @@
void(psrldq)(uint8_t b[16], const uint8_t a[16], unsigned long n) {
unsigned i;
if (n > 16) n = 16;
memcpy(b, a + n, 16 - n);
memset(b + (16 - n), 0, n);
__builtin_memcpy(b, a + n, 16 - n);
__builtin_memset(b + (16 - n), 0, n);
}

View file

@ -4,6 +4,7 @@
PKGS += LIBC
LIBC_HDRS = $(filter %.h,$(LIBC_FILES))
LIBC_INCS = $(filter %.inc,$(LIBC_FILES))
LIBC_FILES := $(wildcard libc/*)
LIBC_CHECKS = $(LIBC_HDRS:%=o/$(MODE)/%.ok)

View file

@ -18,8 +18,8 @@
*/
#include "libc/macros.h"
/ Half size of level 3 cache in bytes.
.initbss 202,_init_kHalfCache3
/ Half size of level 3 cache in bytes.
kHalfCache3:
.quad 0
.endobj kHalfCache3,globl

View file

@ -3,6 +3,8 @@
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
extern long kHalfCache3;
void imapxlatab(void *);
void insertionsort(int32_t *, size_t);

View file

@ -8,6 +8,7 @@ LIBC_NEXGEN32E = $(LIBC_NEXGEN32E_A_DEPS) $(LIBC_NEXGEN32E_A)
LIBC_NEXGEN32E_A = o/$(MODE)/libc/nexgen32e/nexgen32e.a
LIBC_NEXGEN32E_A_FILES := $(wildcard libc/nexgen32e/*)
LIBC_NEXGEN32E_A_HDRS = $(filter %.h,$(LIBC_NEXGEN32E_A_FILES))
LIBC_NEXGEN32E_A_INCS = $(filter %.inc,$(LIBC_NEXGEN32E_A_FILES))
LIBC_NEXGEN32E_A_SRCS_A = $(filter %.s,$(LIBC_NEXGEN32E_A_FILES))
LIBC_NEXGEN32E_A_SRCS_S = $(filter %.S,$(LIBC_NEXGEN32E_A_FILES))
LIBC_NEXGEN32E_A_SRCS_C = $(filter %.c,$(LIBC_NEXGEN32E_A_FILES))
@ -49,6 +50,7 @@ o/$(MODE)/libc/nexgen32e/tinystrncmp.ncabi.o: \
LIBC_NEXGEN32E_LIBS = $(foreach x,$(LIBC_NEXGEN32E_ARTIFACTS),$($(x)))
LIBC_NEXGEN32E_SRCS = $(foreach x,$(LIBC_NEXGEN32E_ARTIFACTS),$($(x)_SRCS))
LIBC_NEXGEN32E_HDRS = $(foreach x,$(LIBC_NEXGEN32E_ARTIFACTS),$($(x)_HDRS))
LIBC_NEXGEN32E_INCS = $(foreach x,$(LIBC_NEXGEN32E_ARTIFACTS),$($(x)_INCS))
LIBC_NEXGEN32E_CHECKS = $(foreach x,$(LIBC_NEXGEN32E_ARTIFACTS),$($(x)_CHECKS))
LIBC_NEXGEN32E_OBJS = $(foreach x,$(LIBC_NEXGEN32E_ARTIFACTS),$($(x)_OBJS))
$(LIBC_NEXGEN32E_OBJS): $(BUILD_FILES) libc/nexgen32e/nexgen32e.mk

View file

@ -21,71 +21,6 @@
#include "libc/macros.h"
.source __FILE__
/ Returns pointer to first instance of character.
/
/ @param rdi is a non-null NUL-terminated string pointer
/ @param esi is the search byte
/ @return rax points to character, or to NUL byte if not found
/ @note this won't return NULL if search character is NUL
strchrnul:
.leafprologue
.profilable
or $-1,%r9
jmp 0f
.endfn strchrnul,globl
/ Returns pointer to first instance of character, the BSD way.
/
/ @param rdi is a non-null NUL-terminated string pointer
/ @param esi is the search byte
/ @return rax points to first result, or NULL if not found
/ @note this won't return NULL if search character is NUL
index: nop
/ 𝑠𝑙𝑖𝑑𝑒
.endfn index,globl
/ Returns pointer to first instance of character.
/
/ @param rdi is a non-null NUL-terminated string pointer
/ @param esi is the search byte
/ @return rax points to first result, or NULL if not found
/ @note this won't return NULL if search character is NUL
/ @asyncsignalsafe
strchr: .leafprologue
.profilable
xor %r9d,%r9d
0: movzbl %sil,%edx
or $-1,%rsi
xor %r8,%r8
jmp strsak
.endfn strchr,globl
/ Returns pointer to first instance of character in range.
/
/ @param rdi is a non-null pointer to memory
/ @param esi is the search byte
/ @return rax points to byte if found, or else undefined behavior
rawmemchr:
or $-1,%rdx
/ 𝑠𝑙𝑖𝑑𝑒
.endfn rawmemchr,globl
/ Returns pointer to first instance of character in range.
/
/ @param rdi is a non-null pointer to memory
/ @param esi is the search byte
/ @param rdx is length of memory in bytes
/ @return rax points to byte if found or NULL
/ @asyncsignalsafe
memchr: .leafprologue
.profilable
xchg %rsi,%rdx
mov %dl,%dh
xor %r8,%r8
xor %r10,%r10
jmp strsak
.endfn memchr,globl
/ Returns length of NUL-terminated string w/ security blankets.
/
/ This is like strnlen() except it'll return 0 if (1) RDI is NULL
@ -102,24 +37,13 @@ strnlen_s:
test %rdi,%rdi
jnz 0f
.leafepilogue
.endfn strnlen_s,globl
/ Returns length of NUL-terminated memory, with limit.
/
/ @param rdi is non-null memory
/ @param rsi is the maximum number of bytes to consider
/ @return rax is the number of bytes, excluding the NUL
/ @asyncsignalsafe
strnlen:.leafprologue
.profilable
or $-1,%r10
0: xor %edx,%edx
mov %rdi,%r8
/ 𝑠𝑙𝑖𝑑𝑒
.endfn strnlen,globl
.endfn strnlen_s,globl
/ Swiss army knife of string character scanning.
/ Fourteen fast functions in one.
/ Used to be fourteen fast functions in one.
/
/ @param rdi is non-null string memory
/ @param rsi is max number of bytes to consider

View file

@ -5,6 +5,7 @@ PKGS += LIBC_NT
LIBC_NT_LIBS = $(foreach x,$(LIBC_NT_ARTIFACTS),$($(x)))
LIBC_NT_HDRS = $(foreach x,$(LIBC_NT_ARTIFACTS),$($(x)_HDRS))
LIBC_NT_INCS = $(foreach x,$(LIBC_NT_ARTIFACTS),$($(x)_INCS))
LIBC_NT_SRCS = $(foreach x,$(LIBC_NT_ARTIFACTS),$($(x)_SRCS))
LIBC_NT_OBJS = $(foreach x,$(LIBC_NT_ARTIFACTS),$($(x)_OBJS))
LIBC_NT_CHECKS = $(foreach x,$(LIBC_NT_ARTIFACTS),$($(x)_CHECKS))
@ -22,9 +23,8 @@ LIBC_NT_A_FILES := \
$(wildcard libc/nt/nt/*.*) \
$(wildcard libc/nt/*)
LIBC_NT_A_HDRS = \
$(filter %.h,$(LIBC_NT_A_FILES))
LIBC_NT_A_HDRS = $(filter %.h,$(LIBC_NT_A_FILES))
LIBC_NT_A_INCS = $(filter %.inc,$(LIBC_NT_A_FILES))
LIBC_NT_A_CHECKS = $(patsubst %,o/$(MODE)/%.ok,$(filter %.h,$(LIBC_NT_A_HDRS)))
#───────────────────────────────────────────────────────────────────────────────

View file

@ -8,6 +8,7 @@ LIBC_SOCK = $(LIBC_SOCK_A_DEPS) $(LIBC_SOCK_A)
LIBC_SOCK_A = o/$(MODE)/libc/sock/sock.a
LIBC_SOCK_A_FILES := $(wildcard libc/sock/*)
LIBC_SOCK_A_HDRS = $(filter %.h,$(LIBC_SOCK_A_FILES))
LIBC_SOCK_A_INCS = $(filter %.inc,$(LIBC_SOCK_A_FILES))
LIBC_SOCK_A_SRCS = $(filter %.c,$(LIBC_SOCK_A_FILES))
LIBC_SOCK_A_OBJS = \
@ -31,6 +32,7 @@ LIBC_SOCK_A_DIRECTDEPS = \
LIBC_NT_WS2_32 \
LIBC_RUNTIME \
LIBC_STDIO \
LIBC_STR \
LIBC_STUBS \
LIBC_SYSV_CALLS \
LIBC_SYSV
@ -49,6 +51,7 @@ $(LIBC_SOCK_A).pkg: \
LIBC_SOCK_LIBS = $(foreach x,$(LIBC_SOCK_ARTIFACTS),$($(x)))
LIBC_SOCK_SRCS = $(foreach x,$(LIBC_SOCK_ARTIFACTS),$($(x)_SRCS))
LIBC_SOCK_HDRS = $(foreach x,$(LIBC_SOCK_ARTIFACTS),$($(x)_HDRS))
LIBC_SOCK_INCS = $(foreach x,$(LIBC_SOCK_ARTIFACTS),$($(x)_INCS))
LIBC_SOCK_CHECKS = $(foreach x,$(LIBC_SOCK_ARTIFACTS),$($(x)_CHECKS))
LIBC_SOCK_OBJS = $(foreach x,$(LIBC_SOCK_ARTIFACTS),$($(x)_OBJS))
$(LIBC_SOCK_OBJS): $(BUILD_FILES) libc/sock/sock.mk

View file

@ -27,7 +27,7 @@ STATIC_YOINK("_init_g_stderr");
FILE *stderr;
hidden FILE g_stderr;
hidden unsigned char g_stderr_buf[BUFSIZ] forcealign(PAGESIZE);
hidden unsigned char g_stderr_buf[BUFSIZ];
static textstartup void _init_g_stderr2() {
_fflushregister(stderr);

View file

@ -27,7 +27,7 @@ STATIC_YOINK("_init_g_stdin");
FILE *stdin;
hidden FILE g_stdin;
hidden unsigned char g_stdin_buf[BUFSIZ] forcealign(PAGESIZE);
hidden unsigned char g_stdin_buf[BUFSIZ];
static textstartup void g_stdin_init() {
_fflushregister(stdin);

View file

@ -30,7 +30,7 @@ STATIC_YOINK("_init_g_stdout");
FILE *stdout;
hidden FILE g_stdout;
hidden unsigned char g_stdout_buf[BUFSIZ] forcealign(PAGESIZE);
hidden unsigned char g_stdout_buf[BUFSIZ];
static textstartup void _init_g_stdout2() {
struct FILE *sf;

View file

@ -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
@ -18,12 +18,14 @@
*/
#include "libc/str/str.h"
void *tinymemccpy(void *dst, const void *src, int termchar, size_t limit) {
size_t i;
unsigned char *d;
const unsigned char *s;
for (termchar &= 0xff, d = dst, s = src, i = 0; i < limit; ++i) {
if ((d[i] = s[i]) == termchar) return d + i + 1;
}
return NULL;
/**
* Returns pointer to first instance of character, the BSD way.
*
* @param s is a NUL-terminated string
* @param is masked with 255 as byte to search for
* @return is pointer to first instance of c or NULL if not found,
* noting that c being NUL will return a pointer to terminator
*/
char *index(const char *s, int c) {
return strchr(s, c);
}

View file

@ -18,8 +18,14 @@
*/
#include "libc/str/str.h"
static noasan uint64_t UncheckedAlignedRead64(unsigned char *p) {
return (uint64_t)p[7] << 070 | (uint64_t)p[6] << 060 | (uint64_t)p[5] << 050 |
(uint64_t)p[4] << 040 | (uint64_t)p[3] << 030 | (uint64_t)p[2] << 020 |
(uint64_t)p[1] << 010 | (uint64_t)p[0] << 000;
}
/**
* Copies at most 𝑛 bytes from 𝑠 to 𝑑 until 𝑐 is encountered.
* Copies at most N bytes from SRC to DST until 𝑐 is encountered.
*
* This is little-known C Standard Library approach, dating back to the
* Fourth Edition of System Five, for copying a C strings to fixed-width
@ -39,17 +45,46 @@
* char cstrbuf[16];
* snprintf(cstrbuf, sizeof(cstrbuf), "%s", CSTR);
*
* @return 𝑑 + idx(𝑐) + 1, or NULL if 𝑐 𝑠
* @note 𝑑 and 𝑠 can't overlap
* @param c is search character and is masked with 255
* @return DST + idx(c) + 1, or NULL if 𝑐 𝑠
* @note DST and SRC can't overlap
* @asyncsignalsafe
*/
void *memccpy(void *d, const void *s, int c, size_t n) {
const char *p, *pe;
p = s;
if ((pe = memchr(p, c, n))) {
return mempcpy(d, s, pe - p + 1);
} else {
memcpy(d, s, n);
return NULL;
void *memccpy(void *dst, const void *src, int c, size_t n) {
size_t i;
uint64_t v, w;
unsigned char *d;
unsigned char *pd;
const unsigned char *s;
i = 0;
d = dst;
s = src;
c &= 255;
v = 0x0101010101010101 * c;
for (; (uintptr_t)(s + i) & 7; ++i) {
if (i == n) return NULL;
if ((d[i] = s[i]) == c) return d + i + 1;
}
for (; i + 8 <= n; i += 8) {
w = UncheckedAlignedRead64(s + i);
if (~(w ^ v) & ((w ^ v) - 0x0101010101010101) & 0x8080808080808080) {
break;
} else {
pd = d + i;
pd[0] = (w >> 000) & 255;
pd[1] = (w >> 010) & 255;
pd[2] = (w >> 020) & 255;
pd[3] = (w >> 030) & 255;
pd[4] = (w >> 040) & 255;
pd[5] = (w >> 050) & 255;
pd[6] = (w >> 060) & 255;
pd[7] = (w >> 070) & 255;
}
}
for (; i < n; ++i) {
if ((d[i] = s[i]) == c) {
return d + i + 1;
}
}
return NULL;
}

47
libc/str/memchr.c Normal file
View 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/str/str.h"
/**
* Returns pointer to first instance of character.
*
* @param m is memory to search
* @param c is search byte which is masked with 255
* @param n is byte length of p
* @return is pointer to first instance of c or NULL if not found
* @asyncsignalsafe
*/
void *memchr(const void *m, int c, size_t n) {
uint64_t v, w;
const unsigned char *p, *pe;
c &= 255;
v = 0x0101010101010101 * c;
for (p = (const unsigned char *)m, pe = p + n; p + 8 <= pe; p += 8) {
w = (uint64_t)p[7] << 070 | (uint64_t)p[6] << 060 | (uint64_t)p[5] << 050 |
(uint64_t)p[4] << 040 | (uint64_t)p[3] << 030 | (uint64_t)p[2] << 020 |
(uint64_t)p[1] << 010 | (uint64_t)p[0] << 000;
if ((w = ~(w ^ v) & ((w ^ v) - 0x0101010101010101) & 0x8080808080808080)) {
return p + ((unsigned)__builtin_ctzll(w) >> 3);
}
}
for (; p < pe; ++p) {
if (*p == c) return p;
}
return NULL;
}

160
libc/str/memmove-pure.c Normal file
View file

@ -0,0 +1,160 @@
/*-*- 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/str/str.h"
typedef long long xmm_t __attribute__((__vector_size__(16), __aligned__(1)));
/**
* Copies memory.
*
* DST and SRC may overlap.
*
* @param dst is destination
* @param src is memory to copy
* @param n is number of bytes to copy
* @return dst
* @asyncsignalsafe
*/
void *memmove$pure(void *dst, const void *src, size_t n) {
size_t i;
xmm_t v, w;
char *d, *r;
const char *s;
uint64_t a, b;
d = dst;
s = src;
switch (n) {
case 9 ... 15:
__builtin_memcpy(&a, s, 8);
__builtin_memcpy(&b, s + n - 8, 8);
__builtin_memcpy(d, &a, 8);
__builtin_memcpy(d + n - 8, &b, 8);
return d;
case 5 ... 7:
__builtin_memcpy(&a, s, 4);
__builtin_memcpy(&b, s + n - 4, 4);
__builtin_memcpy(d, &a, 4);
__builtin_memcpy(d + n - 4, &b, 4);
return d;
case 17 ... 32:
__builtin_memcpy(&v, s, 16);
__builtin_memcpy(&w, s + n - 16, 16);
__builtin_memcpy(d, &v, 16);
__builtin_memcpy(d + n - 16, &w, 16);
return d;
case 16:
__builtin_memcpy(&v, s, 16);
__builtin_memcpy(d, &v, 16);
return d;
case 0:
return d;
case 1:
*d = *s;
return d;
case 8:
__builtin_memcpy(&a, s, 8);
__builtin_memcpy(d, &a, 8);
return d;
case 4:
__builtin_memcpy(&a, s, 4);
__builtin_memcpy(d, &a, 4);
return d;
case 2:
__builtin_memcpy(&a, s, 2);
__builtin_memcpy(d, &a, 2);
return d;
case 3:
__builtin_memcpy(&a, s, 2);
__builtin_memcpy(&b, s + 1, 2);
__builtin_memcpy(d, &a, 2);
__builtin_memcpy(d + 1, &b, 2);
return d;
default:
r = d;
if (d > s) {
do {
n -= 32;
__builtin_memcpy(&v, s + n, 16);
__builtin_memcpy(&w, s + n + 16, 16);
__builtin_memcpy(d + n, &v, 16);
__builtin_memcpy(d + n + 16, &w, 16);
} while (n >= 32);
} else {
i = 0;
do {
__builtin_memcpy(&v, s + i, 16);
__builtin_memcpy(&w, s + i + 16, 16);
__builtin_memcpy(d + i, &v, 16);
__builtin_memcpy(d + i + 16, &w, 16);
} while ((i += 32) + 32 <= n);
d += i;
s += i;
n -= i;
}
switch (n) {
case 0:
return r;
case 17 ... 31:
__builtin_memcpy(&v, s, 16);
__builtin_memcpy(&w, s + n - 16, 16);
__builtin_memcpy(d, &v, 16);
__builtin_memcpy(d + n - 16, &w, 16);
return r;
case 9 ... 15:
__builtin_memcpy(&a, s, 8);
__builtin_memcpy(&b, s + n - 8, 8);
__builtin_memcpy(d, &a, 8);
__builtin_memcpy(d + n - 8, &b, 8);
return r;
case 5 ... 7:
__builtin_memcpy(&a, s, 4);
__builtin_memcpy(&b, s + n - 4, 4);
__builtin_memcpy(d, &a, 4);
__builtin_memcpy(d + n - 4, &b, 4);
return r;
case 16:
__builtin_memcpy(&v, s, 16);
__builtin_memcpy(d, &v, 16);
return r;
case 8:
__builtin_memcpy(&a, s, 8);
__builtin_memcpy(d, &a, 8);
return r;
case 4:
__builtin_memcpy(&a, s, 4);
__builtin_memcpy(d, &a, 4);
return r;
case 1:
*d = *s;
return r;
case 2:
__builtin_memcpy(&a, s, 2);
__builtin_memcpy(d, &a, 2);
return r;
case 3:
__builtin_memcpy(&a, s, 2);
__builtin_memcpy(&b, s + 1, 2);
__builtin_memcpy(d, &a, 2);
__builtin_memcpy(d + 1, &b, 2);
return r;
default:
unreachable;
}
}
}

100
libc/str/memset-pure.c Normal file
View file

@ -0,0 +1,100 @@
/*-*- 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/str/str.h"
/**
* Sets memory.
*
* @param p is memory address
* @param c is masked with 255 and used as repeated byte
* @param n is byte length
* @return p
* @asyncsignalsafe
*/
void *memset$pure(void *p, int c, size_t n) {
char *b;
uint64_t x;
b = p;
x = 0x0101010101010101 * (c & 0xff);
switch (n) {
case 0:
return p;
case 1:
__builtin_memcpy(b, &x, 1);
return p;
case 2:
__builtin_memcpy(b, &x, 2);
return p;
case 3:
__builtin_memcpy(b, &x, 2);
__builtin_memcpy(b + 1, &x, 2);
return p;
case 4:
__builtin_memcpy(b, &x, 4);
return p;
case 5 ... 7:
__builtin_memcpy(b, &x, 4);
__builtin_memcpy(b + n - 4, &x, 4);
return p;
case 8:
__builtin_memcpy(b, &x, 8);
return p;
case 9 ... 16:
__builtin_memcpy(b, &x, 8);
__builtin_memcpy(b + n - 8, &x, 8);
return p;
default:
do {
n -= 16;
__builtin_memcpy(b + n, &x, 8);
asm volatile("" ::: "memory");
__builtin_memcpy(b + n + 8, &x, 8);
} while (n >= 16);
switch (n) {
case 0:
return p;
case 1:
__builtin_memcpy(b, &x, 1);
return p;
case 2:
__builtin_memcpy(b, &x, 2);
return p;
case 3:
__builtin_memcpy(b, &x, 2);
__builtin_memcpy(b + 1, &x, 2);
return p;
case 4:
__builtin_memcpy(b, &x, 4);
return p;
case 5 ... 7:
__builtin_memcpy(b, &x, 4);
__builtin_memcpy(b + n - 4, &x, 4);
return p;
case 8:
__builtin_memcpy(b, &x, 8);
return p;
case 9 ... 15:
__builtin_memcpy(b, &x, 8);
__builtin_memcpy(b + n - 8, &x, 8);
return p;
default:
unreachable;
}
}
}

53
libc/str/rawmemchr.c Normal file
View file

@ -0,0 +1,53 @@
/*-*- 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/assert.h"
#include "libc/str/str.h"
static noasan uint64_t UncheckedAlignedRead64(unsigned char *p) {
return (uint64_t)p[7] << 070 | (uint64_t)p[6] << 060 | (uint64_t)p[5] << 050 |
(uint64_t)p[4] << 040 | (uint64_t)p[3] << 030 | (uint64_t)p[2] << 020 |
(uint64_t)p[1] << 010 | (uint64_t)p[0] << 000;
}
/**
* Returns pointer to first instance of character.
*
* @param m is memory to search
* @param c is search byte which is masked with 255
* @return is pointer to first instance of c
*/
void *rawmemchr(const void *m, int c) {
uint64_t v, w;
const unsigned char *s;
s = m;
c &= 255;
v = 0x0101010101010101 * c;
for (; (uintptr_t)s & 7; ++s) {
if (*s == c) return s;
}
for (;; s += 8) {
w = UncheckedAlignedRead64(s);
if ((w = ~(w ^ v) & ((w ^ v) - 0x0101010101010101) & 0x8080808080808080)) {
s += (unsigned)__builtin_ctzll(w) >> 3;
break;
}
}
assert(*s == c);
return s;
}

View file

@ -90,7 +90,6 @@ void *memeqmask(void *, const void *, const void *, size_t) memcpyesque;
size_t strlen(const char *) strlenesque;
size_t strnlen(const char *, size_t) strlenesque;
size_t strnlen_s(const char *, size_t);
size_t strlen$pure(const char *) strlenesque;
char *strchr(const char *, int) strlenesque;
char *index(const char *, int) strlenesque;
void *memchr(const void *, int, size_t) strlenesque;
@ -246,8 +245,8 @@ char *strsignal(int) returnsnonnull libcesque;
cosmopolitan § strings » optimizations
*/
#define __memcpy_isgoodsize(SIZE) \
(__builtin_constant_p(SIZE) && ((SIZE) <= __BIGGEST_ALIGNMENT__ * 2 && \
#define __memcpy_isgoodsize(SIZE) \
(__builtin_constant_p(SIZE) && ((SIZE) <= __BIGGEST_ALIGNMENT__ && \
__builtin_popcountl((unsigned)(SIZE)) == 1))
#define __memset_isgoodsize(SIZE) \
@ -361,15 +360,44 @@ char *strsignal(int) returnsnonnull libcesque;
})
#endif /* hosted/sse2/unbloat */
#ifdef __FSANITIZE_ADDRESS__
/*───────────────────────────────────────────────────────────────────────────│─╗
cosmopolitan § strings » address sanitizer
*/
void *memset$pure(void *, int, size_t) memcpyesque;
void *memmove$pure(void *, const void *, size_t) memcpyesque;
size_t strlen$pure(const char *) strlenesque;
size_t strcspn$pure(const char *, const char *) strlenesque;
#if defined(__FSANITIZE_ADDRESS__)
#define strcspn(STR, REJECT) strcspn$pure(STR, REJECT)
#ifdef strlen
#undef strlen
#endif
#define strlen(s) strlen$pure(s)
#define strlen(STR) \
(__builtin_constant_p(STR) ? __builtin_strlen(STR) : strlen$pure(STR))
#undef memset
#define memset(DST, CHAR, SIZE) \
(__memcpy_isgoodsize(SIZE) ? __builtin_memset(DST, CHAR, SIZE) \
: memset$pure(DST, CHAR, SIZE))
#undef memmove
#define memmove(DST, SRC, SIZE) \
(__memcpy_isgoodsize(SIZE) ? __builtin_memmove(DST, SRC, SIZE) \
: memmove$pure(DST, SRC, SIZE))
#undef memcpy
#define memcpy(DST, SRC, SIZE) \
(__memcpy_isgoodsize(SIZE) ? __builtin_memcpy(DST, SRC, SIZE) \
: memmove$pure(DST, SRC, SIZE))
#undef mempcpy
#define mempcpy(DST, SRC, SIZE) \
(__memcpy_isgoodsize(SIZE) ? __builtin_mempcpy(DST, SRC, SIZE) : ({ \
void *DsT = (DST); \
size_t SiZe = (SIZE); \
memmove$pure(DsT, SRC, SiZe); \
(void *)((char *)DsT + SiZe); \
}))
#endif /* __FSANITIZE_ADDRESS__ */
#endif /* __GNUC__ && !__STRICT_ANSI__ */

View file

@ -8,6 +8,7 @@ LIBC_STR = $(LIBC_STR_A_DEPS) $(LIBC_STR_A)
LIBC_STR_A = o/$(MODE)/libc/str/str.a
LIBC_STR_A_FILES := $(wildcard libc/str/*)
LIBC_STR_A_HDRS = $(filter %.h,$(LIBC_STR_A_FILES))
LIBC_STR_A_INCS = $(filter %.inc,$(LIBC_STR_A_FILES))
LIBC_STR_A_SRCS_A = $(filter %.s,$(LIBC_STR_A_FILES))
LIBC_STR_A_SRCS_S = $(filter %.S,$(LIBC_STR_A_FILES))
LIBC_STR_A_SRCS_C = $(filter %.c,$(LIBC_STR_A_FILES))
@ -49,6 +50,7 @@ o/$(MODE)/libc/str/memmem.o: \
LIBC_STR_LIBS = $(foreach x,$(LIBC_STR_ARTIFACTS),$($(x)))
LIBC_STR_SRCS = $(foreach x,$(LIBC_STR_ARTIFACTS),$($(x)_SRCS))
LIBC_STR_HDRS = $(foreach x,$(LIBC_STR_ARTIFACTS),$($(x)_HDRS))
LIBC_STR_INCS = $(foreach x,$(LIBC_STR_ARTIFACTS),$($(x)_INCS))
LIBC_STR_BINS = $(foreach x,$(LIBC_STR_ARTIFACTS),$($(x)_BINS))
LIBC_STR_CHECKS = $(foreach x,$(LIBC_STR_ARTIFACTS),$($(x)_CHECKS))
LIBC_STR_OBJS = $(foreach x,$(LIBC_STR_ARTIFACTS),$($(x)_OBJS))

69
libc/str/strchr.c Normal file
View file

@ -0,0 +1,69 @@
/*-*- 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/assert.h"
#include "libc/str/str.h"
noasan static const unsigned char *strchr$x64(const unsigned char *p,
uint64_t c) {
unsigned a, b;
uint64_t w, x, y;
for (c *= 0x0101010101010101;; p += 8) {
w = (uint64_t)p[7] << 070 | (uint64_t)p[6] << 060 | (uint64_t)p[5] << 050 |
(uint64_t)p[4] << 040 | (uint64_t)p[3] << 030 | (uint64_t)p[2] << 020 |
(uint64_t)p[1] << 010 | (uint64_t)p[0] << 000;
if ((x = ~(w ^ c) & ((w ^ c) - 0x0101010101010101) & 0x8080808080808080) |
(y = ~w & (w - 0x0101010101010101) & 0x8080808080808080)) {
if (x) {
a = __builtin_ctzll(x);
if (y) {
b = __builtin_ctzll(y);
if (a <= b) {
return p + (a >> 3);
} else {
return NULL;
}
} else {
return p + (a >> 3);
}
} else {
return NULL;
}
}
}
}
/**
* Returns pointer to first instance of character.
*
* @param s is a NUL-terminated string
* @param c is masked with 255 as byte to search for
* @return pointer to first instance of c or NULL if not found
* noting that if c is NUL we return pointer to terminator
* @asyncsignalsafe
*/
char *strchr(const char *s, int c) {
char *r;
for (c &= 0xff; (uintptr_t)s & 7; ++s) {
if ((*s & 0xff) == c) return s;
if (!*s) return NULL;
}
r = (char *)strchr$x64((const unsigned char *)s, c);
assert(!r || *r || !c);
return r;
}

69
libc/str/strchrnul.c Normal file
View file

@ -0,0 +1,69 @@
/*-*- 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/assert.h"
#include "libc/str/str.h"
noasan static const unsigned char *strchrnul$x64(const unsigned char *p,
uint64_t c) {
unsigned a, b;
uint64_t w, x, y;
for (c *= 0x0101010101010101;; p += 8) {
w = (uint64_t)p[7] << 070 | (uint64_t)p[6] << 060 | (uint64_t)p[5] << 050 |
(uint64_t)p[4] << 040 | (uint64_t)p[3] << 030 | (uint64_t)p[2] << 020 |
(uint64_t)p[1] << 010 | (uint64_t)p[0] << 000;
if ((x = ~(w ^ c) & ((w ^ c) - 0x0101010101010101) & 0x8080808080808080) |
(y = ~w & (w - 0x0101010101010101) & 0x8080808080808080)) {
if (x) {
a = __builtin_ctzll(x);
if (y) {
b = __builtin_ctzll(y);
if (a <= b) {
return p + (a >> 3);
} else {
return p + (b >> 3);
}
} else {
return p + (a >> 3);
}
} else {
b = __builtin_ctzll(y);
return p + (b >> 3);
}
}
}
}
/**
* Returns pointer to first instance of character.
*
* @param s is a NUL-terminated string
* @param c is masked with 255 as byte to search for
* @return pointer to first instance of c, or pointer to
* NUL terminator if c is not found
*/
char *strchrnul(const char *s, int c) {
char *r;
for (c &= 0xff; (uintptr_t)s & 7; ++s) {
if ((*s & 0xff) == c) return s;
if (!*s) return s;
}
r = (char *)strchrnul$x64((const unsigned char *)s, c);
assert((*r & 0xff) == c || !*r);
return r;
}

View file

@ -18,6 +18,12 @@
*/
#include "libc/str/str.h"
static inline noasan uint64_t UncheckedAlignedRead64(unsigned char *p) {
return (uint64_t)p[7] << 070 | (uint64_t)p[6] << 060 | (uint64_t)p[5] << 050 |
(uint64_t)p[4] << 040 | (uint64_t)p[3] << 030 | (uint64_t)p[2] << 020 |
(uint64_t)p[1] << 010 | (uint64_t)p[0] << 000;
}
/**
* Compares NUL-terminated strings.
*
@ -28,7 +34,25 @@
*/
int strcmp(const char *a, const char *b) {
size_t i = 0;
uint64_t v, w, d;
if (a == b) return 0;
while (a[i] == b[i] && b[i]) ++i;
return (a[i] & 0xff) - (b[i] & 0xff);
if (((uintptr_t)a & 7) == ((uintptr_t)b & 7)) {
for (; (uintptr_t)a & 7; ++i) {
if (a[i] != b[i] || !b[i]) {
return (a[i] & 0xff) - (b[i] & 0xff);
}
}
for (;; i += 8) {
v = UncheckedAlignedRead64(a + i);
w = UncheckedAlignedRead64(b + i);
w = (v ^ w) | (~v & (v - 0x0101010101010101) & 0x8080808080808080);
if (w) {
i += (unsigned)__builtin_ctzll(w) >> 3;
return (a[i] & 0xff) - (b[i] & 0xff);
}
}
} else {
while (a[i] == b[i] && b[i]) ++i;
return (a[i] & 0xff) - (b[i] & 0xff);
}
}

62
libc/str/strcspn-pure.c Normal file
View file

@ -0,0 +1,62 @@
/*-*- 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/intrin/pcmpeqb.h"
#include "libc/intrin/pmovmskb.h"
#include "libc/intrin/pshufd.h"
#include "libc/intrin/punpcklbw.h"
#include "libc/intrin/punpcklwd.h"
#include "libc/nexgen32e/hascharacter.internal.h"
#include "libc/str/str.h"
#define V(p) (void *)(p)
/**
* Returns prefix length, consisting of chars not in reject.
* a.k.a. Return index of first byte that's in charset.
*
* @param reject is nul-terminated character set
* @see strspn(), strtok_r()
* @asyncsignalsafe
*/
size_t strcspn$pure(const char *s, const char *reject) {
size_t i, n;
unsigned m;
char cv[16], sv[16];
if ((n = strlen(reject)) < 16) {
memset(sv, 0, 16);
memcpy(sv, reject, n);
for (i = 0;; ++i) {
cv[0] = s[i];
punpcklbw(V(cv), V(cv), V(cv));
punpcklwd(V(cv), V(cv), V(cv));
pshufd(V(cv), V(cv), 0);
pcmpeqb(V(cv), V(cv), V(sv));
if ((m = pmovmskb(V(cv)))) {
break;
}
}
return i;
}
for (i = 0; s[i]; ++i) {
if (HasCharacter(s[i], reject)) {
break;
}
}
return i;
}

View file

@ -16,35 +16,35 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/assert.h"
#include "libc/str/str.h"
noasan static const char *strlen$pure$x64(const char *p) {
static noasan size_t strlen$pure$x64(const char *s, size_t i) {
uint64_t w;
const unsigned char *p;
for (;;) {
w = *(uint64_t *)p;
if (~w & (w - 0x0101010101010101) & 0x8080808080808080) {
break;
p = (const unsigned char *)s + i;
w = (uint64_t)p[7] << 070 | (uint64_t)p[6] << 060 | (uint64_t)p[5] << 050 |
(uint64_t)p[4] << 040 | (uint64_t)p[3] << 030 | (uint64_t)p[2] << 020 |
(uint64_t)p[1] << 010 | (uint64_t)p[0] << 000;
if ((w = ~w & (w - 0x0101010101010101) & 0x8080808080808080)) {
return i + ((unsigned)__builtin_ctzll(w) >> 3);
} else {
p += 8;
i += 8;
}
}
return p;
}
/**
* Returns length of NUL-terminated string.
*/
size_t strlen$pure(const char *s) {
const char *p;
p = s;
while ((uintptr_t)p & 7) {
if (*p) {
++p;
} else {
return p - s;
}
size_t i;
for (i = 0; (uintptr_t)(s + i) & 7; ++i) {
if (!s[i]) return i;
}
p = strlen$pure$x64(p);
while (*p) ++p;
return p - s;
i = strlen$pure$x64(s, i);
assert(!i || s[0]);
assert(!s[i]);
return i;
}

57
libc/str/strnlen.c Normal file
View file

@ -0,0 +1,57 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 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/assert.h"
#include "libc/str/str.h"
static noasan size_t strnlen$x64(const char *s, size_t n, size_t i) {
uint64_t w;
const unsigned char *p;
for (; i + 8 < n; i += 8) {
p = (const unsigned char *)s + i;
w = (uint64_t)p[7] << 070 | (uint64_t)p[6] << 060 | (uint64_t)p[5] << 050 |
(uint64_t)p[4] << 040 | (uint64_t)p[3] << 030 | (uint64_t)p[2] << 020 |
(uint64_t)p[1] << 010 | (uint64_t)p[0] << 000;
if ((w = ~w & (w - 0x0101010101010101) & 0x8080808080808080)) {
i += (unsigned)__builtin_ctzll(w) >> 3;
break;
}
}
return i;
}
/**
* Returns length of NUL-terminated string w/ limit.
*
* @param s is string
* @param n is max length
* @return byte length
* @asyncsignalsafe
*/
size_t strnlen(const char *s, size_t n) {
size_t i;
for (i = 0; (uintptr_t)(s + i) & 7; ++i) {
if (i == n || !s[i]) return i;
}
i = strnlen$x64(s, n, i);
for (;; ++i) {
if (i == n || !s[i]) break;
}
assert(i == n || (i < n && !s[i]));
return i;
}

View file

@ -5,6 +5,7 @@ PKGS += LIBC_SYSV
LIBC_SYSV_LIBS = $(foreach x,$(LIBC_SYSV_ARTIFACTS),$($(x)_A))
LIBC_SYSV_ARCHIVES = $(foreach x,$(LIBC_SYSV_ARTIFACTS),$($(x)_A))
LIBC_SYSV_HDRS = $(foreach x,$(LIBC_SYSV_ARTIFACTS),$($(x)_HDRS))
LIBC_SYSV_INCS = $(foreach x,$(LIBC_SYSV_ARTIFACTS),$($(x)_INCS))
LIBC_SYSV_BINS = $(foreach x,$(LIBC_SYSV_ARTIFACTS),$($(x)_BINS))
LIBC_SYSV_CHECKS = $(foreach x,$(LIBC_SYSV_ARTIFACTS),$($(x)_CHECKS))
LIBC_SYSV_OBJS = $(foreach x,$(LIBC_SYSV_ARTIFACTS),$($(x)_OBJS))
@ -20,6 +21,7 @@ LIBC_SYSV = \
LIBC_SYSV_ARTIFACTS += LIBC_SYSV_A
LIBC_SYSV_A = o/$(MODE)/libc/sysv/sysv.a
LIBC_SYSV_A_HDRS = $(filter %.h,$(LIBC_SYSV_A_FILES))
LIBC_SYSV_A_INCS = $(filter %.inc,$(LIBC_SYSV_A_FILES))
LIBC_SYSV_A_SRCS_A = $(filter %.s,$(LIBC_SYSV_A_FILES))
LIBC_SYSV_A_SRCS_S = $(filter %.S,$(LIBC_SYSV_A_FILES))
LIBC_SYSV_A_CHECKS = $(LIBC_SYSV_A).pkg

View file

@ -27,8 +27,8 @@ STATIC_YOINK("strnwidth");
void __testlib_ezbenchreport(const char *form, uint64_t c1, uint64_t c2) {
uint64_t ns1, ns2;
ns1 = rintl(converttickstonanos(c1));
ns2 = rintl(converttickstonanos(c2));
ns1 = rintl(ConvertTicksToNanos(c1));
ns2 = rintl(ConvertTicksToNanos(c2));
(fprintf)(stderr,
VEIL("r", "%-30s l: %,10lu𝑐 %,10lu𝑛𝑠 m: %,10lu𝑐 %,10lu𝑛𝑠\n"),
form, c1, ns1, c2, ns2);

View file

@ -60,8 +60,7 @@ int futimesat(int, const char *, const struct timeval[2]);
long double dtime(int);
long double dsleep(long double);
extern long double (*nowl)(void);
long double converttickstonanos(uint64_t);
long double converttickstoseconds(uint64_t);
long double ConvertTicksToNanos(uint64_t);
double difftime(int64_t, int64_t) nothrow pureconst;