Compare commits
10
Commits
180c1107b6
...
6854c7771d
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6854c7771d | ||
|
|
53c8531eab | ||
|
|
e8fe84e27c | ||
|
|
d5e390e992 | ||
|
|
d23deaa32a | ||
|
|
7d28d7cc9d | ||
|
|
ace9728565 | ||
|
|
060fc03ab7 | ||
|
|
f07b270675 | ||
|
|
a5c16e97d7 |
@@ -85,6 +85,9 @@ Makefile.in
|
|||||||
/build-aux/
|
/build-aux/
|
||||||
/m4/
|
/m4/
|
||||||
|
|
||||||
|
# Generated feature header (from include/vlibc/features.h.in); the template is tracked.
|
||||||
|
/include/vlibc/features.h
|
||||||
|
|
||||||
# Libtool build directories and artifacts.
|
# Libtool build directories and artifacts.
|
||||||
.deps/
|
.deps/
|
||||||
.libs/
|
.libs/
|
||||||
@@ -102,4 +105,5 @@ Makefile.in
|
|||||||
|
|
||||||
# Built benchmark harnesses (benchmarks/bench_* are generated from bench_*.c).
|
# Built benchmark harnesses (benchmarks/bench_* are generated from bench_*.c).
|
||||||
/benchmarks/bench_vlibc
|
/benchmarks/bench_vlibc
|
||||||
|
/benchmarks/bench_string
|
||||||
|
|
||||||
|
|||||||
+16
-2
@@ -6,10 +6,17 @@ AM_CPPFLAGS = -I$(top_srcdir)/include
|
|||||||
AM_CFLAGS = @VLIBC_CFLAGS@
|
AM_CFLAGS = @VLIBC_CFLAGS@
|
||||||
|
|
||||||
# ---- Library -------------------------------------------------------------
|
# ---- Library -------------------------------------------------------------
|
||||||
vlibc_include_HEADERS = include/vlibc.h
|
vlibc_include_HEADERS = include/vlibc.h include/stddef.h include/string.h
|
||||||
vlibc_lib_LTLIBRARIES = libvlibc.la
|
vlibc_lib_LTLIBRARIES = libvlibc.la
|
||||||
|
|
||||||
libvlibc_la_SOURCES = src/vlibc.c
|
libvlibc_la_SOURCES = src/vlibc.c src/string/strlen.c src/string/strcmp.c \
|
||||||
|
src/string/memcpy.c src/string/memmove.c src/string/memset.c
|
||||||
|
if PROFILE_GE_2
|
||||||
|
libvlibc_la_SOURCES += src/string/strlcpy.c src/string/strlcat.c
|
||||||
|
endif
|
||||||
|
if PROFILE_GE_3
|
||||||
|
libvlibc_la_SOURCES += src/string/strcasestr.c
|
||||||
|
endif
|
||||||
libvlibc_la_LDFLAGS = -version-info 0:0:0 -no-undefined
|
libvlibc_la_LDFLAGS = -version-info 0:0:0 -no-undefined
|
||||||
|
|
||||||
# Install location depends on the install method (see configure.ac).
|
# Install location depends on the install method (see configure.ac).
|
||||||
@@ -21,6 +28,13 @@ vlibc_includedir = $(prefix)/lib/vlibc/include
|
|||||||
vlibc_libdir = $(prefix)/lib/vlibc/lib
|
vlibc_libdir = $(prefix)/lib/vlibc/lib
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
# Generated per-profile feature header (see configure.ac); installed under
|
||||||
|
# $(vlibc_includedir)/vlibc/. A dedicated primary is used because
|
||||||
|
# nodist_nobase_vlibc_include_HEADERS is rejected by automake (the "nobase"
|
||||||
|
# prefix is unsupported for this directory) and loses the vlibc/ path.
|
||||||
|
vlibc_featuresdir = $(vlibc_includedir)/vlibc
|
||||||
|
nodist_vlibc_features_HEADERS = include/vlibc/features.h
|
||||||
|
|
||||||
# ---- Compiler drivers ----------------------------------------------------
|
# ---- Compiler drivers ----------------------------------------------------
|
||||||
bin_SCRIPTS = tools/vlibc-gcc tools/vlibc-clang
|
bin_SCRIPTS = tools/vlibc-gcc tools/vlibc-clang
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,13 @@ required capability.
|
|||||||
scripts and programs that rely on long-standing glibc behavior.
|
scripts and programs that rely on long-standing glibc behavior.
|
||||||
5. *(default)* — **vlibc**: glibc-extended, without the spoofing layer, but
|
5. *(default)* — **vlibc**: glibc-extended, without the spoofing layer, but
|
||||||
with extended standard features and high glibc compatibility.
|
with extended standard features and high glibc compatibility.
|
||||||
|
- **Profiles 1-3 are functional.** `--enable-onlyposix`, `--enable-muslmimic`,
|
||||||
|
and `--enable-muslext` build with real build-time profile gating: a generated
|
||||||
|
`include/vlibc/features.h` exposes `VLIBC_LEVEL`, the public `<stddef.h>` and
|
||||||
|
`<string.h>` headers are gated by it, and an initial string slice (`memcpy`,
|
||||||
|
`memmove`, `memset`, `strlen`, `strcmp`, `strlcpy`, `strlcat`,
|
||||||
|
`strcasestr`) is compiled per level. Profiles 4 and 5 (`spoof`, `vlibc`)
|
||||||
|
remain configure-level declarations in this increment.
|
||||||
- **Two install methods** (`--with-install=`):
|
- **Two install methods** (`--with-install=`):
|
||||||
- `alongside` (default) — install next to the system libc, under a
|
- `alongside` (default) — install next to the system libc, under a
|
||||||
vlibc-specific tree; the system libc is left untouched.
|
vlibc-specific tree; the system libc is left untouched.
|
||||||
|
|||||||
@@ -76,6 +76,34 @@ linked binaries (see `docs/overview.md`):
|
|||||||
|
|
||||||
Every public function carries the tightest correct attribute.
|
Every public function carries the tightest correct attribute.
|
||||||
|
|
||||||
|
## Profile gating
|
||||||
|
|
||||||
|
Profile-gated code follows these formatting and naming conventions (the
|
||||||
|
semantic classification of functions into levels lives in
|
||||||
|
`docs/compatibility.md`, not here):
|
||||||
|
|
||||||
|
- Public headers gate declarations with `#if VLIBC_LEVEL >= N`. Prefer the
|
||||||
|
`VLIBC_LEVEL_GE(n)` helper from `include/vlibc/features.h`; fall back to the
|
||||||
|
raw comparison only when the level is a literal constant:
|
||||||
|
|
||||||
|
```c
|
||||||
|
#if VLIBC_LEVEL_GE(2)
|
||||||
|
size_t strlcpy(char *dst, const char *src, size_t n);
|
||||||
|
#endif
|
||||||
|
```
|
||||||
|
|
||||||
|
- Group implementation sources by level in `Makefile.am` using the per-level
|
||||||
|
conditionals `if PROFILE_GE_2` / `if PROFILE_GE_3`, mirroring the
|
||||||
|
`#if VLIBC_LEVEL >= N` rule in headers.
|
||||||
|
- Standard headers use `VLIBC_`-prefixed include guards
|
||||||
|
(`VLIBC_STDDEF_H`, `VLIBC_STRING_H`), never the `_STDDEF_H`-style reserved
|
||||||
|
forms. A leading underscore is reserved for the implementation and the
|
||||||
|
C/POSIX standards (see Naming); the `VLIBC_` prefix stays out of the
|
||||||
|
reserved namespace while remaining unambiguous.
|
||||||
|
- Intent attributes respect the gating: `pure` / `const` are only valid on
|
||||||
|
side-effect-free functions. Never annotate a mutating function (`memcpy`,
|
||||||
|
`memmove`, `memset`, `strlcpy`, `strlcat`) with either.
|
||||||
|
|
||||||
## Error handling
|
## Error handling
|
||||||
|
|
||||||
- No empty blocks; no silent failure.
|
- No empty blocks; no silent failure.
|
||||||
|
|||||||
+28
-2
@@ -5,23 +5,49 @@ AM_CFLAGS = @VLIBC_CFLAGS@
|
|||||||
|
|
||||||
# Built only on demand (via `make bench`), so `make all` does not need the
|
# Built only on demand (via `make bench`), so `make all` does not need the
|
||||||
# library to be built first.
|
# library to be built first.
|
||||||
EXTRA_PROGRAMS = bench_vlibc
|
EXTRA_PROGRAMS = bench_vlibc bench_string
|
||||||
bench_vlibc_SOURCES = bench_vlibc.c
|
bench_vlibc_SOURCES = bench_vlibc.c
|
||||||
|
bench_string_SOURCES = bench_string.c
|
||||||
|
|
||||||
|
# bench_string is a system-headers-only translation unit (it times whichever
|
||||||
|
# libc it is linked against), so it must NOT inherit AM_CPPFLAGS: -I include
|
||||||
|
# would pull vlibc's self-contained <stddef.h>/<string.h> into the same TU as
|
||||||
|
# the system <stdio.h>/<time.h> and double-define size_t/NULL/offsetof.
|
||||||
|
# bench_vlibc keeps the inherited -I include.
|
||||||
|
bench_string_CPPFLAGS =
|
||||||
|
|
||||||
# By default benchmarks link against vlibc itself. --with-libc=glibc links the
|
# By default benchmarks link against vlibc itself. --with-libc=glibc links the
|
||||||
# same harness against the system glibc; --with-libc=musl builds it with
|
# same harness against the system glibc; --with-libc=musl builds it with
|
||||||
# musl-gcc for the musl reference.
|
# musl-gcc for the musl reference.
|
||||||
if BENCH_LINK_VLIBC
|
if BENCH_LINK_VLIBC
|
||||||
bench_vlibc_LDADD = ../libvlibc.la
|
bench_vlibc_LDADD = ../libvlibc.la
|
||||||
|
bench_string_LDADD = ../libvlibc.la
|
||||||
else
|
else
|
||||||
bench_vlibc_LDADD =
|
bench_vlibc_LDADD =
|
||||||
|
bench_string_LDADD =
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if BENCH_LINK_MUSL
|
if BENCH_LINK_MUSL
|
||||||
CC = $(MUSL_CC)
|
CC = $(MUSL_CC)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
bench: bench_vlibc
|
# Relink whenever the configuration changes (autogen.sh reruns configure and
|
||||||
|
# rewrites config.status). --with-libc only affects the link (LDADD), and the
|
||||||
|
# objects are compile-time identical, so make would otherwise reuse a stale
|
||||||
|
# binary from the previous configuration and the wrong libc would be measured.
|
||||||
|
EXTRA_bench_string_DEPENDENCIES = $(top_builddir)/config.status
|
||||||
|
EXTRA_bench_vlibc_DEPENDENCIES = $(top_builddir)/config.status
|
||||||
|
|
||||||
|
# bench_vlibc.c calls vlibc_version(), which exists only in vlibc — under
|
||||||
|
# --with-libc=glibc/musl it cannot link, so it is built and run only in the
|
||||||
|
# vlibc build. bench_string links either way and runs always.
|
||||||
|
if BENCH_LINK_VLIBC
|
||||||
|
bench: bench_vlibc bench_string
|
||||||
./bench_vlibc
|
./bench_vlibc
|
||||||
|
./bench_string
|
||||||
|
else
|
||||||
|
bench: bench_string
|
||||||
|
./bench_string
|
||||||
|
endif
|
||||||
|
|
||||||
.PHONY: bench
|
.PHONY: bench
|
||||||
|
|||||||
@@ -0,0 +1,153 @@
|
|||||||
|
/*
|
||||||
|
* Benchmark harness for the five L1 string functions — strlen, strcmp,
|
||||||
|
* memcpy, memmove, memset (musts/BENCHMARKING.md).
|
||||||
|
*
|
||||||
|
* Deliberately a SYSTEM-headers-only translation unit: it includes no vlibc
|
||||||
|
* header, because vlibc's self-contained <stddef.h>/<string.h> (guard
|
||||||
|
* VLIBC_STDDEF_H) conflicts with the system <stdio.h>/<time.h> (guard
|
||||||
|
* _STDDEF_H) — both define size_t/NULL/offsetof/max_align_t and mixing them
|
||||||
|
* hard-errors. Which libc implements the timed calls is therefore decided
|
||||||
|
* purely at link time (../libvlibc.la vs the system libc).
|
||||||
|
*/
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include <config.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#define ITERATIONS 50000000ULL
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Volatile function pointers defeat both GCC builtin substitution and
|
||||||
|
* pure-attribute LICM hoisting of the timed loops (-fno-builtin alone does
|
||||||
|
* not prevent the latter). The signatures match the system-header
|
||||||
|
* declarations exactly: memcpy has restrict, memmove does not.
|
||||||
|
*/
|
||||||
|
static size_t (*volatile p_strlen)(const char *) = strlen;
|
||||||
|
static int (*volatile p_strcmp)(const char *, const char *) = strcmp;
|
||||||
|
static void *(*volatile p_memcpy)(void *restrict, const void *restrict, size_t) = memcpy;
|
||||||
|
static void *(*volatile p_memmove)(void *, const void *, size_t) = memmove;
|
||||||
|
static void *(*volatile p_memset)(void *, int, size_t) = memset;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* XOR accumulator. Every timed result is folded in here so the compiler
|
||||||
|
* cannot discard the indirect calls; volatile keeps the store itself alive.
|
||||||
|
*/
|
||||||
|
static volatile unsigned long long sink;
|
||||||
|
|
||||||
|
/* Fixed small inputs: 8-byte and 64-byte buffers, sized per the plan. */
|
||||||
|
static char str8[8] = "abcdefg"; /* 7 non-NUL bytes + NUL: full scan */
|
||||||
|
static char str64[64] =
|
||||||
|
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.";
|
||||||
|
static char cmp8a[8] = "abcdefg";
|
||||||
|
static char cmp8b[8] = "abcdefg"; /* equal: strcmp runs the full length */
|
||||||
|
static char cmp64a[64] =
|
||||||
|
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.";
|
||||||
|
static char cmp64b[64] =
|
||||||
|
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.";
|
||||||
|
static char src8[8];
|
||||||
|
static char src64[64];
|
||||||
|
static char dst8[8];
|
||||||
|
static char dst64[64];
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Time one expression executed ITERATIONS times through a volatile function
|
||||||
|
* pointer, then print `name@size ns/call`. The result of each call is XORed
|
||||||
|
* into a local accumulator and folded into `sink` after the loop, so the
|
||||||
|
* calls cannot be eliminated. On clock_gettime failure, returns 1 out of
|
||||||
|
* main.
|
||||||
|
*/
|
||||||
|
#define MEASURE(name, size, call) \
|
||||||
|
do \
|
||||||
|
{ \
|
||||||
|
unsigned long long r = 0; \
|
||||||
|
double seconds; \
|
||||||
|
if (clock_gettime(CLOCK_MONOTONIC, &start) != 0) \
|
||||||
|
{ \
|
||||||
|
return 1; \
|
||||||
|
} \
|
||||||
|
for (unsigned long long i = 0; i < ITERATIONS; i++) \
|
||||||
|
{ \
|
||||||
|
r ^= (unsigned long long)(call); \
|
||||||
|
} \
|
||||||
|
if (clock_gettime(CLOCK_MONOTONIC, &end) != 0) \
|
||||||
|
{ \
|
||||||
|
return 1; \
|
||||||
|
} \
|
||||||
|
sink ^= r; \
|
||||||
|
seconds = (double)(end.tv_sec - start.tv_sec) + \
|
||||||
|
(double)(end.tv_nsec - start.tv_nsec) / 1000000000.0; \
|
||||||
|
printf("%s@%d %.1f\n", name, size, \
|
||||||
|
seconds * 1000000000.0 / (double)ITERATIONS); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
int
|
||||||
|
main(void)
|
||||||
|
{
|
||||||
|
struct timespec start;
|
||||||
|
struct timespec end;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Correctness self-asserts, routed through the SAME volatile function
|
||||||
|
* pointers as the timed loops. A bare literal call (strlen("hello"))
|
||||||
|
* would be constant-folded by GCC even at -O0, and a call through a
|
||||||
|
* non-volatile pointer would not exercise the pointer the timing uses.
|
||||||
|
*/
|
||||||
|
{
|
||||||
|
char scratch[16];
|
||||||
|
|
||||||
|
if (p_strlen("hello") != 5)
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (p_strcmp("a", "b") >= 0)
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (p_strcmp("abc", "abc") != 0)
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (p_memset(scratch, 0, sizeof scratch) != scratch)
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (p_memcpy(scratch, "hello", 6) != scratch)
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (p_strcmp(scratch, "hello") != 0)
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (p_memmove(scratch + 1, scratch, 6) != scratch + 1)
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (p_strcmp(scratch + 1, "hello") != 0)
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MEASURE("strlen", 8, p_strlen(str8));
|
||||||
|
MEASURE("strlen", 64, p_strlen(str64));
|
||||||
|
MEASURE("strcmp", 8, p_strcmp(cmp8a, cmp8b));
|
||||||
|
MEASURE("strcmp", 64, p_strcmp(cmp64a, cmp64b));
|
||||||
|
MEASURE("memcpy", 8, p_memcpy(dst8, src8, sizeof dst8));
|
||||||
|
MEASURE("memcpy", 64, p_memcpy(dst64, src64, sizeof dst64));
|
||||||
|
MEASURE("memmove", 8, p_memmove(dst8, src8, sizeof dst8));
|
||||||
|
MEASURE("memmove", 64, p_memmove(dst64, src64, sizeof dst64));
|
||||||
|
MEASURE("memset", 8, p_memset(dst8, 0x5a, sizeof dst8));
|
||||||
|
MEASURE("memset", 64, p_memset(dst64, 0x5a, sizeof dst64));
|
||||||
|
|
||||||
|
/* Keep the accumulated results observable to the compiler. */
|
||||||
|
if (sink == 0xdeadbeefULL)
|
||||||
|
{
|
||||||
|
printf("%llu\n", sink);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -679,6 +679,11 @@ MUSL_CC
|
|||||||
vlibc_install_mode
|
vlibc_install_mode
|
||||||
INSTALL_OVERWRITE_FALSE
|
INSTALL_OVERWRITE_FALSE
|
||||||
INSTALL_OVERWRITE_TRUE
|
INSTALL_OVERWRITE_TRUE
|
||||||
|
PROFILE_GE_3_FALSE
|
||||||
|
PROFILE_GE_3_TRUE
|
||||||
|
PROFILE_GE_2_FALSE
|
||||||
|
PROFILE_GE_2_TRUE
|
||||||
|
vlibc_level
|
||||||
vlibc_profile
|
vlibc_profile
|
||||||
VLIBC_CFLAGS
|
VLIBC_CFLAGS
|
||||||
am__fastdepCC_FALSE
|
am__fastdepCC_FALSE
|
||||||
@@ -5283,6 +5288,24 @@ esac
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if test "$vlibc_level" -ge 2; then
|
||||||
|
PROFILE_GE_2_TRUE=
|
||||||
|
PROFILE_GE_2_FALSE='#'
|
||||||
|
else
|
||||||
|
PROFILE_GE_2_TRUE='#'
|
||||||
|
PROFILE_GE_2_FALSE=
|
||||||
|
fi
|
||||||
|
|
||||||
|
if test "$vlibc_level" -ge 3; then
|
||||||
|
PROFILE_GE_3_TRUE=
|
||||||
|
PROFILE_GE_3_FALSE='#'
|
||||||
|
else
|
||||||
|
PROFILE_GE_3_TRUE='#'
|
||||||
|
PROFILE_GE_3_FALSE=
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
# ---- Install method ------------------------------------------------------
|
# ---- Install method ------------------------------------------------------
|
||||||
# "alongside" installs vlibc next to the system libc: headers and libraries go
|
# "alongside" installs vlibc next to the system libc: headers and libraries go
|
||||||
# under a vlibc-specific tree, used via the vlibc-gcc / vlibc-clang drivers.
|
# under a vlibc-specific tree, used via the vlibc-gcc / vlibc-clang drivers.
|
||||||
@@ -14467,7 +14490,7 @@ CC=$lt_save_CC
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
ac_config_files="$ac_config_files Makefile benchmarks/Makefile tools/vlibc-gcc tools/vlibc-clang"
|
ac_config_files="$ac_config_files Makefile benchmarks/Makefile include/vlibc/features.h tools/vlibc-gcc tools/vlibc-clang"
|
||||||
|
|
||||||
|
|
||||||
# Make the generated compiler drivers executable. This must run after the
|
# Make the generated compiler drivers executable. This must run after the
|
||||||
@@ -14584,6 +14607,14 @@ if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then
|
|||||||
as_fn_error $? "conditional \"am__fastdepCC\" was never defined.
|
as_fn_error $? "conditional \"am__fastdepCC\" was never defined.
|
||||||
Usually this means the macro was only invoked conditionally." "$LINENO" 5
|
Usually this means the macro was only invoked conditionally." "$LINENO" 5
|
||||||
fi
|
fi
|
||||||
|
if test -z "${PROFILE_GE_2_TRUE}" && test -z "${PROFILE_GE_2_FALSE}"; then
|
||||||
|
as_fn_error $? "conditional \"PROFILE_GE_2\" was never defined.
|
||||||
|
Usually this means the macro was only invoked conditionally." "$LINENO" 5
|
||||||
|
fi
|
||||||
|
if test -z "${PROFILE_GE_3_TRUE}" && test -z "${PROFILE_GE_3_FALSE}"; then
|
||||||
|
as_fn_error $? "conditional \"PROFILE_GE_3\" was never defined.
|
||||||
|
Usually this means the macro was only invoked conditionally." "$LINENO" 5
|
||||||
|
fi
|
||||||
if test -z "${INSTALL_OVERWRITE_TRUE}" && test -z "${INSTALL_OVERWRITE_FALSE}"; then
|
if test -z "${INSTALL_OVERWRITE_TRUE}" && test -z "${INSTALL_OVERWRITE_FALSE}"; then
|
||||||
as_fn_error $? "conditional \"INSTALL_OVERWRITE\" was never defined.
|
as_fn_error $? "conditional \"INSTALL_OVERWRITE\" was never defined.
|
||||||
Usually this means the macro was only invoked conditionally." "$LINENO" 5
|
Usually this means the macro was only invoked conditionally." "$LINENO" 5
|
||||||
@@ -15470,6 +15501,7 @@ do
|
|||||||
"libtool") CONFIG_COMMANDS="$CONFIG_COMMANDS libtool" ;;
|
"libtool") CONFIG_COMMANDS="$CONFIG_COMMANDS libtool" ;;
|
||||||
"Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;;
|
"Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;;
|
||||||
"benchmarks/Makefile") CONFIG_FILES="$CONFIG_FILES benchmarks/Makefile" ;;
|
"benchmarks/Makefile") CONFIG_FILES="$CONFIG_FILES benchmarks/Makefile" ;;
|
||||||
|
"include/vlibc/features.h") CONFIG_FILES="$CONFIG_FILES include/vlibc/features.h" ;;
|
||||||
"tools/vlibc-gcc") CONFIG_FILES="$CONFIG_FILES tools/vlibc-gcc" ;;
|
"tools/vlibc-gcc") CONFIG_FILES="$CONFIG_FILES tools/vlibc-gcc" ;;
|
||||||
"tools/vlibc-clang") CONFIG_FILES="$CONFIG_FILES tools/vlibc-clang" ;;
|
"tools/vlibc-clang") CONFIG_FILES="$CONFIG_FILES tools/vlibc-clang" ;;
|
||||||
"chmod-vlibc-drivers") CONFIG_COMMANDS="$CONFIG_COMMANDS chmod-vlibc-drivers" ;;
|
"chmod-vlibc-drivers") CONFIG_COMMANDS="$CONFIG_COMMANDS chmod-vlibc-drivers" ;;
|
||||||
|
|||||||
@@ -116,6 +116,10 @@ AS_CASE([$vlibc_profile],
|
|||||||
[vlibc], [AC_DEFINE([VLIBC_PROFILE_VLIBC], [1], [vlibc (glibc-ext) profile])])
|
[vlibc], [AC_DEFINE([VLIBC_PROFILE_VLIBC], [1], [vlibc (glibc-ext) profile])])
|
||||||
|
|
||||||
AC_SUBST([vlibc_profile])
|
AC_SUBST([vlibc_profile])
|
||||||
|
AC_SUBST([vlibc_level])
|
||||||
|
|
||||||
|
AM_CONDITIONAL([PROFILE_GE_2], [test "$vlibc_level" -ge 2])
|
||||||
|
AM_CONDITIONAL([PROFILE_GE_3], [test "$vlibc_level" -ge 3])
|
||||||
|
|
||||||
# ---- Install method ------------------------------------------------------
|
# ---- Install method ------------------------------------------------------
|
||||||
# "alongside" installs vlibc next to the system libc: headers and libraries go
|
# "alongside" installs vlibc next to the system libc: headers and libraries go
|
||||||
@@ -168,6 +172,7 @@ LT_INIT
|
|||||||
|
|
||||||
AC_CONFIG_FILES([Makefile
|
AC_CONFIG_FILES([Makefile
|
||||||
benchmarks/Makefile
|
benchmarks/Makefile
|
||||||
|
include/vlibc/features.h
|
||||||
tools/vlibc-gcc
|
tools/vlibc-gcc
|
||||||
tools/vlibc-clang])
|
tools/vlibc-clang])
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,51 @@ mutually exclusive; the default is `vlibc`.
|
|||||||
| 4 | `--enable-spoof` | `spoof` | A glibc replica: emulates glibc for drop-in compatibility, higher than `muslext` or `vlibc` full. |
|
| 4 | `--enable-spoof` | `spoof` | A glibc replica: emulates glibc for drop-in compatibility, higher than `muslext` or `vlibc` full. |
|
||||||
| 5 | *(default)* | `vlibc` | glibc-extended: glibc minus its baggage, plus extended standard features. High (but not spoof-level) glibc compatibility. |
|
| 5 | *(default)* | `vlibc` | glibc-extended: glibc minus its baggage, plus extended standard features. High (but not spoof-level) glibc compatibility. |
|
||||||
|
|
||||||
|
## Profile mechanism
|
||||||
|
|
||||||
|
The profiles map onto a single integer level, `VLIBC_LEVEL`. Levels are
|
||||||
|
**cumulative**: a profile at level N exposes everything whose minimum level is
|
||||||
|
`<= N`.
|
||||||
|
|
||||||
|
| Level | Classification |
|
||||||
|
|-------|-----------------------------------------|
|
||||||
|
| 1 | ISO C + POSIX base |
|
||||||
|
| 2 | + BSD / XSI extensions |
|
||||||
|
| 3 | + GNU extensions |
|
||||||
|
|
||||||
|
Public headers gate declarations with `#if VLIBC_LEVEL >= N`; a function whose
|
||||||
|
minimum level is 2 is only declared when the active profile is level 2 or
|
||||||
|
higher. The build applies the same rule to sources via
|
||||||
|
`AM_CONDITIONAL([PROFILE_GE_2])` and `AM_CONDITIONAL([PROFILE_GE_3])`, so each
|
||||||
|
level's implementation files are only compiled when the selected profile
|
||||||
|
reaches that level.
|
||||||
|
|
||||||
|
The generated installed header `include/vlibc/features.h` exposes the macros
|
||||||
|
the gating is based on:
|
||||||
|
|
||||||
|
- `VLIBC_LEVEL` — the active profile's integer level.
|
||||||
|
- `VLIBC_PROFILE` — the selected profile name.
|
||||||
|
- `VLIBC_LEVEL_GE(n)` — expands to 1 when `VLIBC_LEVEL >= n`, 0 otherwise.
|
||||||
|
|
||||||
|
### Slice manifest
|
||||||
|
|
||||||
|
The initial implementation covers eight string/memory functions, classified as
|
||||||
|
follows:
|
||||||
|
|
||||||
|
| Function | Level | Origin |
|
||||||
|
|--------------|-------|---------|
|
||||||
|
| `memcpy` | L1 | ISO C |
|
||||||
|
| `memmove` | L1 | ISO C |
|
||||||
|
| `memset` | L1 | ISO C |
|
||||||
|
| `strlen` | L1 | ISO C |
|
||||||
|
| `strcmp` | L1 | ISO C |
|
||||||
|
| `strlcpy` | L2 | BSD |
|
||||||
|
| `strlcat` | L2 | BSD |
|
||||||
|
| `strcasestr` | L3 | GNU |
|
||||||
|
|
||||||
|
Profiles 4 and 5 (`spoof`, `vlibc`) build on the same level mechanism but are
|
||||||
|
not implemented in this increment beyond their configure declarations.
|
||||||
|
|
||||||
## Differences from glibc
|
## Differences from glibc
|
||||||
|
|
||||||
- **No legacy baggage.** vlibc targets modern, standard-conforming behavior and
|
- **No legacy baggage.** vlibc targets modern, standard-conforming behavior and
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
#ifndef VLIBC_STDDEF_H
|
||||||
|
#define VLIBC_STDDEF_H
|
||||||
|
|
||||||
|
/*
|
||||||
|
* vlibc — <stddef.h>.
|
||||||
|
*
|
||||||
|
* Self-contained definitions of the common C types and macros (C23). Every
|
||||||
|
* definition here is derived from compiler builtins, so this header never
|
||||||
|
* borrows from a system header.
|
||||||
|
*
|
||||||
|
* The compatibility profile is exposed by include/vlibc/features.h; it is
|
||||||
|
* included here so consumers always see the configured VLIBC_LEVEL.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <vlibc/features.h>
|
||||||
|
|
||||||
|
/* Integer type of the result of the sizeof operator. */
|
||||||
|
typedef __SIZE_TYPE__ size_t;
|
||||||
|
|
||||||
|
/* Signed integer type of the difference of two pointers. */
|
||||||
|
typedef __PTRDIFF_TYPE__ ptrdiff_t;
|
||||||
|
|
||||||
|
/* Wide character type. */
|
||||||
|
typedef __WCHAR_TYPE__ wchar_t;
|
||||||
|
|
||||||
|
/* Type of the null pointer constant nullptr (C23). */
|
||||||
|
typedef typeof(nullptr) nullptr_t;
|
||||||
|
|
||||||
|
/* Null pointer constant. */
|
||||||
|
#define NULL ((void *)0)
|
||||||
|
|
||||||
|
/* Offset in bytes of a member from the start of its enclosing object. */
|
||||||
|
#define offsetof(type, m) __builtin_offsetof(type, m)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* An object type whose alignment is as great as that of any supported
|
||||||
|
* fundamental type; suitable as the storage type for aligned allocation.
|
||||||
|
*/
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
long long vll;
|
||||||
|
long double vld;
|
||||||
|
} max_align_t;
|
||||||
|
|
||||||
|
#endif /* VLIBC_STDDEF_H */
|
||||||
@@ -0,0 +1,102 @@
|
|||||||
|
#ifndef VLIBC_STRING_H
|
||||||
|
#define VLIBC_STRING_H
|
||||||
|
|
||||||
|
/*
|
||||||
|
* vlibc — <string.h>.
|
||||||
|
*
|
||||||
|
* String and memory functions, gated by the active compatibility profile
|
||||||
|
* (see include/vlibc/features.h). Levels are cumulative:
|
||||||
|
*
|
||||||
|
* Level 1 (onlyposix): ISO C core — memcpy, memmove, memset, strlen, strcmp.
|
||||||
|
* Level 2 (muslmimic): BSD extensions — strlcpy, strlcat.
|
||||||
|
* Level 3 (muslext): GNU extensions — strcasestr.
|
||||||
|
*
|
||||||
|
* This header includes <vlibc/features.h> itself, so the gates below always
|
||||||
|
* see the configured VLIBC_LEVEL even when the caller included no vlibc
|
||||||
|
* header first, and <stddef.h> for size_t.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <vlibc/features.h>
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Level 1: ISO C core (always present). */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return the length of s, excluding the terminating NUL.
|
||||||
|
* pure: reads memory, no side effects.
|
||||||
|
*/
|
||||||
|
__attribute__((pure)) size_t
|
||||||
|
strlen(const char *s);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Compare the strings lhs and rhs; return negative, zero, or positive when
|
||||||
|
* lhs is less than, equal to, or greater than rhs.
|
||||||
|
* pure: reads memory, no side effects.
|
||||||
|
*/
|
||||||
|
__attribute__((pure)) int
|
||||||
|
strcmp(const char *lhs, const char *rhs);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copy n bytes from src to dst. The regions must not overlap (restrict).
|
||||||
|
* No intent attribute: it writes memory.
|
||||||
|
*/
|
||||||
|
void *
|
||||||
|
memcpy(void *restrict dst, const void *restrict src, size_t n);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copy n bytes from src to dst. The regions may overlap, so the parameters
|
||||||
|
* are deliberately not restrict.
|
||||||
|
* No intent attribute: it writes memory.
|
||||||
|
*/
|
||||||
|
void *
|
||||||
|
memmove(void *dst, const void *src, size_t n);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Fill n bytes at dst with c (converted to unsigned char).
|
||||||
|
* No intent attribute: it writes memory.
|
||||||
|
*/
|
||||||
|
void *
|
||||||
|
memset(void *dst, int c, size_t n);
|
||||||
|
|
||||||
|
#if VLIBC_LEVEL >= 2
|
||||||
|
/* Level 2 (muslmimic): BSD extensions. */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copy at most size - 1 bytes from src to dst and NUL-terminate the result;
|
||||||
|
* return strlen(src). No NUL is written when size is 0.
|
||||||
|
* No intent attribute: it writes memory.
|
||||||
|
*/
|
||||||
|
size_t
|
||||||
|
strlcpy(char *dst, const char *src, size_t size);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Append src to dst, NUL-terminating within size bytes; return the length of
|
||||||
|
* the string that would have been created without truncation.
|
||||||
|
* No intent attribute: it writes memory.
|
||||||
|
*/
|
||||||
|
size_t
|
||||||
|
strlcat(char *dst, const char *src, size_t size);
|
||||||
|
#endif /* VLIBC_LEVEL >= 2 */
|
||||||
|
|
||||||
|
#if VLIBC_LEVEL >= 3
|
||||||
|
/* Level 3 (muslext): GNU extensions. */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return a pointer to the first case-insensitive (ASCII fold) occurrence of
|
||||||
|
* needle in haystack, or NULL if absent; an empty needle matches haystack.
|
||||||
|
* pure: reads memory, no side effects.
|
||||||
|
*/
|
||||||
|
__attribute__((pure)) char *
|
||||||
|
strcasestr(const char *haystack, const char *needle);
|
||||||
|
#endif /* VLIBC_LEVEL >= 3 */
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* VLIBC_STRING_H */
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
#ifndef VLIBC_FEATURES_H
|
||||||
|
#define VLIBC_FEATURES_H
|
||||||
|
|
||||||
|
/*
|
||||||
|
* vlibc — compatibility feature gate.
|
||||||
|
*
|
||||||
|
* This header is generated by configure from include/vlibc/features.h.in; do
|
||||||
|
* not edit the generated file. It exposes the active compatibility profile as
|
||||||
|
* build-time constants, so headers and sources can gate declarations on the
|
||||||
|
* profile level (see configure.ac and docs/compatibility.md):
|
||||||
|
*
|
||||||
|
* 1 onlyposix pure POSIX, nothing more
|
||||||
|
* 2 muslmimic musl-like, light
|
||||||
|
* 3 muslext musl-extended
|
||||||
|
* 4 spoof glibc replica (drop-in compatibility)
|
||||||
|
* 5 vlibc glibc-extended, the default
|
||||||
|
*
|
||||||
|
* VLIBC_LEVEL is the numeric level (1..5); VLIBC_PROFILE is the profile name.
|
||||||
|
* Both are emitted only when not already defined: the library's own sources
|
||||||
|
* include config.h first (via HAVE_CONFIG_H), whose values win, and this
|
||||||
|
* header skips the redefinition. Consumers of the installed header get the
|
||||||
|
* values baked in at configure time.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* clang-format off */
|
||||||
|
#ifndef VLIBC_LEVEL
|
||||||
|
#define VLIBC_LEVEL @vlibc_level@
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef VLIBC_PROFILE
|
||||||
|
#define VLIBC_PROFILE "@vlibc_profile@"
|
||||||
|
#endif
|
||||||
|
/* clang-format on */
|
||||||
|
|
||||||
|
/* True when the active profile is at least level n. */
|
||||||
|
#define VLIBC_LEVEL_GE(n) (VLIBC_LEVEL >= (n))
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Per-profile feature booleans, derived from VLIBC_LEVEL. Levels are
|
||||||
|
* cumulative: level 3 implies the POSIX and musl feature sets as well. A
|
||||||
|
* macro is defined (as 1) when its feature set is present and left undefined
|
||||||
|
* otherwise, so `#if VLIBC_HAS_MUSL` works in both cases.
|
||||||
|
*/
|
||||||
|
#if VLIBC_LEVEL >= 1
|
||||||
|
#define VLIBC_HAS_POSIX 1
|
||||||
|
#endif
|
||||||
|
#if VLIBC_LEVEL >= 2
|
||||||
|
#define VLIBC_HAS_MUSL 1
|
||||||
|
#endif
|
||||||
|
#if VLIBC_LEVEL >= 3
|
||||||
|
#define VLIBC_HAS_MUSLEXT 1
|
||||||
|
#endif
|
||||||
|
#if VLIBC_LEVEL >= 4
|
||||||
|
#define VLIBC_HAS_GLIBC 1
|
||||||
|
#endif
|
||||||
|
#if VLIBC_LEVEL >= 5
|
||||||
|
#define VLIBC_HAS_VLIBC 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* VLIBC_FEATURES_H */
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include <config.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copy n bytes from src to dst. The regions must not overlap (restrict).
|
||||||
|
* Word-at-a-time: copy whole unsigned long words once dst is aligned, with
|
||||||
|
* a byte head and tail. Word loads and stores go through __builtin_memcpy
|
||||||
|
* so unaligned src is handled without undefined behavior.
|
||||||
|
*
|
||||||
|
* The word-copy loop below is the canonical memcpy idiom; GCC's
|
||||||
|
* -ftree-loop-distribute-patterns (on by default at -O2 and -O3) rewrites
|
||||||
|
* it into a call to memcpy() — this very function — causing infinite
|
||||||
|
* self-recursion and stack overflow. Disable that one transformation for
|
||||||
|
* this function only.
|
||||||
|
*/
|
||||||
|
__attribute__((optimize("no-tree-loop-distribute-patterns"))) void *
|
||||||
|
memcpy(void *restrict dst, const void *restrict src, size_t n) // NOLINT(bugprone-*)
|
||||||
|
{
|
||||||
|
const unsigned long word = sizeof(unsigned long);
|
||||||
|
unsigned char *d = dst;
|
||||||
|
const unsigned char *s = src;
|
||||||
|
|
||||||
|
/* Copy the unaligned head byte-wise. */
|
||||||
|
for (; (unsigned long)d % word != 0 && n != 0; n--)
|
||||||
|
{
|
||||||
|
*d++ = *s++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Copy whole words. */
|
||||||
|
while (n >= word)
|
||||||
|
{
|
||||||
|
unsigned long w;
|
||||||
|
|
||||||
|
__builtin_memcpy(&w, s, sizeof w); // NOLINT(clang-analyzer-security.insecureAPI.*)
|
||||||
|
__builtin_memcpy(d, &w, sizeof w); // NOLINT(clang-analyzer-security.insecureAPI.*)
|
||||||
|
d += word;
|
||||||
|
s += word;
|
||||||
|
n -= word;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Copy the remaining tail byte-wise. */
|
||||||
|
while (n != 0)
|
||||||
|
{
|
||||||
|
*d++ = *s++;
|
||||||
|
n--;
|
||||||
|
}
|
||||||
|
|
||||||
|
return dst;
|
||||||
|
}
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include <config.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copy n bytes from src to dst. The regions may overlap, so the copy
|
||||||
|
* direction is chosen by the relative positions: forward when dst starts
|
||||||
|
* at or before src, or at or after the end of src; backward when dst
|
||||||
|
* starts inside src. Word-at-a-time in both directions, with byte heads
|
||||||
|
* and tails; word loads and stores go through __builtin_memcpy.
|
||||||
|
*/
|
||||||
|
void *
|
||||||
|
memmove(void *dst, const void *src, size_t n) // NOLINT(bugprone-easily-swappable-parameters)
|
||||||
|
{
|
||||||
|
const unsigned long word = sizeof(unsigned long);
|
||||||
|
unsigned char *d = dst;
|
||||||
|
const unsigned char *s = src;
|
||||||
|
|
||||||
|
if (d <= s || d >= s + n)
|
||||||
|
{
|
||||||
|
/* Forward copy. */
|
||||||
|
for (; (unsigned long)d % word != 0 && n != 0; n--)
|
||||||
|
{
|
||||||
|
*d++ = *s++;
|
||||||
|
}
|
||||||
|
while (n >= word)
|
||||||
|
{
|
||||||
|
unsigned long w;
|
||||||
|
|
||||||
|
__builtin_memcpy(&w, s, sizeof w); // NOLINT(clang-analyzer-security.insecureAPI.*)
|
||||||
|
__builtin_memcpy(d, &w, sizeof w); // NOLINT(clang-analyzer-security.insecureAPI.*)
|
||||||
|
d += word;
|
||||||
|
s += word;
|
||||||
|
n -= word;
|
||||||
|
}
|
||||||
|
while (n != 0)
|
||||||
|
{
|
||||||
|
*d++ = *s++;
|
||||||
|
n--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Backward copy, starting from the last byte. */
|
||||||
|
d += n;
|
||||||
|
s += n;
|
||||||
|
for (; (unsigned long)d % word != 0 && n != 0; n--)
|
||||||
|
{
|
||||||
|
*--d = *--s;
|
||||||
|
}
|
||||||
|
while (n >= word)
|
||||||
|
{
|
||||||
|
unsigned long w;
|
||||||
|
|
||||||
|
d -= word;
|
||||||
|
s -= word;
|
||||||
|
__builtin_memcpy(&w, s, sizeof w); // NOLINT(clang-analyzer-security.insecureAPI.*)
|
||||||
|
__builtin_memcpy(d, &w, sizeof w); // NOLINT(clang-analyzer-security.insecureAPI.*)
|
||||||
|
n -= word;
|
||||||
|
}
|
||||||
|
while (n != 0)
|
||||||
|
{
|
||||||
|
*--d = *--s;
|
||||||
|
n--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return dst;
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include <config.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Fill n bytes at dst with c (converted to unsigned char). Word-at-a-time:
|
||||||
|
* broadcast the byte into every byte of an unsigned long, align dst, fill
|
||||||
|
* whole words, then finish with a byte tail. The broadcast multiply cannot
|
||||||
|
* carry between bytes because each byte is at most 0xff.
|
||||||
|
*/
|
||||||
|
void *
|
||||||
|
memset(void *dst, int c, size_t n) // NOLINT(bugprone-easily-swappable-parameters)
|
||||||
|
{
|
||||||
|
const unsigned long word = sizeof(unsigned long);
|
||||||
|
const unsigned char byte = (unsigned char)c;
|
||||||
|
const unsigned long fill = (unsigned long)byte * ((unsigned long)-1 / 0xff);
|
||||||
|
unsigned char *d = dst;
|
||||||
|
|
||||||
|
/* Fill the unaligned head byte-wise. */
|
||||||
|
for (; (unsigned long)d % word != 0 && n != 0; n--)
|
||||||
|
{
|
||||||
|
*d++ = byte;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fill whole words. */
|
||||||
|
while (n >= word)
|
||||||
|
{
|
||||||
|
__builtin_memcpy(d, &fill, sizeof fill); // NOLINT(clang-analyzer-security.insecureAPI.*)
|
||||||
|
d += word;
|
||||||
|
n -= word;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fill the remaining tail byte-wise. */
|
||||||
|
while (n != 0)
|
||||||
|
{
|
||||||
|
*d++ = byte;
|
||||||
|
n--;
|
||||||
|
}
|
||||||
|
|
||||||
|
return dst;
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include <config.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Fold an ASCII uppercase letter to lowercase; every other character
|
||||||
|
* (including NUL) passes through unchanged. Kept local and locale-free:
|
||||||
|
* strcasestr must never depend on ctype tables or the active locale.
|
||||||
|
*/
|
||||||
|
static inline char
|
||||||
|
fold_ascii(char c)
|
||||||
|
{
|
||||||
|
if (c >= 'A' && c <= 'Z')
|
||||||
|
{
|
||||||
|
return (char)(c + ('a' - 'A'));
|
||||||
|
}
|
||||||
|
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return a pointer to the first case-insensitive (ASCII fold) occurrence of
|
||||||
|
* needle in haystack, or NULL if absent. An empty needle matches haystack.
|
||||||
|
*
|
||||||
|
* Naive two-pointer scan: for each position in haystack, compare folded
|
||||||
|
* characters until the needle is exhausted (match) or a mismatch occurs
|
||||||
|
* (advance). The inner loop stops at the NUL terminator of either string,
|
||||||
|
* so the comparison never reads past the end of haystack when needle is
|
||||||
|
* longer than the remaining tail.
|
||||||
|
*/
|
||||||
|
char *
|
||||||
|
strcasestr(const char *haystack, const char *needle) // NOLINT(bugprone-easily-swappable-parameters)
|
||||||
|
{
|
||||||
|
if (*needle == '\0')
|
||||||
|
{
|
||||||
|
return (char *)haystack;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (; *haystack != '\0'; haystack++)
|
||||||
|
{
|
||||||
|
const char *h = haystack;
|
||||||
|
const char *n = needle;
|
||||||
|
|
||||||
|
while (*n != '\0' && fold_ascii(*h) == fold_ascii(*n))
|
||||||
|
{
|
||||||
|
h++;
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*n == '\0')
|
||||||
|
{
|
||||||
|
return (char *)haystack;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include <config.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* True if w contains a zero byte. Subtracting ONES from w propagates a
|
||||||
|
* borrow into the high bit of a byte exactly when that byte is zero;
|
||||||
|
* ANDing with ~w keeps only bytes that were zero and high-bit-clear in w.
|
||||||
|
* ONES and its shifted form are derived from the word width, so this works
|
||||||
|
* for both 32- and 64-bit unsigned long.
|
||||||
|
*/
|
||||||
|
static unsigned long
|
||||||
|
haszero(unsigned long w)
|
||||||
|
{
|
||||||
|
const unsigned long ones = (unsigned long)-1 / 0xff;
|
||||||
|
|
||||||
|
return (w - ones) & ~w & (ones << 7);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Compare the strings lhs and rhs; return negative, zero, or positive when
|
||||||
|
* lhs is less than, equal to, or greater than rhs. Bytes are compared as
|
||||||
|
* unsigned char, so values at or above 0x80 sort above 0x7f.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
strcmp(const char *lhs, const char *rhs)
|
||||||
|
{
|
||||||
|
const unsigned long word = sizeof(unsigned long);
|
||||||
|
const unsigned char *l = (const unsigned char *)lhs;
|
||||||
|
const unsigned char *r = (const unsigned char *)rhs;
|
||||||
|
|
||||||
|
/* Compare the unaligned head, aligning l to a word boundary. */
|
||||||
|
for (; (unsigned long)l % word != 0; l++, r++)
|
||||||
|
{
|
||||||
|
if (*l != *r)
|
||||||
|
{
|
||||||
|
return *l < *r ? -1 : 1;
|
||||||
|
}
|
||||||
|
if (*l == '\0')
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((unsigned long)r % word == 0)
|
||||||
|
{
|
||||||
|
/* Both pointers are word-aligned: scan whole words. */
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
unsigned long wl;
|
||||||
|
unsigned long wr;
|
||||||
|
|
||||||
|
__builtin_memcpy(&wl, l, sizeof wl); // NOLINT(clang-analyzer-security.insecureAPI.*)
|
||||||
|
__builtin_memcpy(&wr, r, sizeof wr); // NOLINT(clang-analyzer-security.insecureAPI.*)
|
||||||
|
if (wl != wr || haszero(wl))
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
l += word;
|
||||||
|
r += word;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Resolve the differing or terminating word byte-wise. */
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
if (*l != *r)
|
||||||
|
{
|
||||||
|
return *l < *r ? -1 : 1;
|
||||||
|
}
|
||||||
|
if (*l == '\0')
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
l++;
|
||||||
|
r++;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include <config.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Append src to dst, NUL-terminating the result within size bytes.
|
||||||
|
* Return dlen + strlen(src), where dlen = min(size, strlen(dst)): the length
|
||||||
|
* the result would have had without truncation. dst is never read past
|
||||||
|
* size bytes. When size is 0 or dlen == size, nothing is written.
|
||||||
|
*/
|
||||||
|
size_t
|
||||||
|
strlcat(char *dst, const char *src, size_t size)
|
||||||
|
{
|
||||||
|
char *d;
|
||||||
|
const char *s;
|
||||||
|
size_t n;
|
||||||
|
size_t dlen;
|
||||||
|
|
||||||
|
d = dst;
|
||||||
|
s = src;
|
||||||
|
n = size;
|
||||||
|
|
||||||
|
/* Find the end of dst, stopping after at most size bytes. */
|
||||||
|
while (n != 0 && *d != '\0')
|
||||||
|
{
|
||||||
|
d++;
|
||||||
|
n--;
|
||||||
|
}
|
||||||
|
dlen = (size_t)(d - dst);
|
||||||
|
n = size - dlen;
|
||||||
|
|
||||||
|
if (n != 0)
|
||||||
|
{
|
||||||
|
/* Append as much of src as fits, leaving room for the NUL. */
|
||||||
|
while (*s != '\0')
|
||||||
|
{
|
||||||
|
if (n != 1)
|
||||||
|
{
|
||||||
|
*d++ = *s;
|
||||||
|
n--;
|
||||||
|
}
|
||||||
|
s++;
|
||||||
|
}
|
||||||
|
*d = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Count the unwritten remainder of src. */
|
||||||
|
while (*s != '\0')
|
||||||
|
{
|
||||||
|
s++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return dlen + (size_t)(s - src);
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include <config.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copy src to dst, NUL-terminating the result, truncated to fit size bytes.
|
||||||
|
* Return strlen(src): the length of the string the call tried to create,
|
||||||
|
* independent of any truncation. When size is 0, nothing is written.
|
||||||
|
*/
|
||||||
|
size_t
|
||||||
|
strlcpy(char *dst, const char *src, size_t size)
|
||||||
|
{
|
||||||
|
const char *orig_src;
|
||||||
|
size_t nleft;
|
||||||
|
|
||||||
|
orig_src = src;
|
||||||
|
nleft = size;
|
||||||
|
|
||||||
|
/* Copy as many bytes as will fit, leaving room for the NUL. */
|
||||||
|
if (nleft != 0)
|
||||||
|
{
|
||||||
|
while (--nleft != 0)
|
||||||
|
{
|
||||||
|
*dst = *src;
|
||||||
|
if (*src == '\0')
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
dst++;
|
||||||
|
src++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* No room for a NUL (or size was 0): NUL-terminate and measure src. */
|
||||||
|
if (nleft == 0)
|
||||||
|
{
|
||||||
|
if (size != 0)
|
||||||
|
{
|
||||||
|
*dst = '\0';
|
||||||
|
}
|
||||||
|
while (*src != '\0')
|
||||||
|
{
|
||||||
|
src++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* src ends at its NUL either way, so this is strlen(original src). */
|
||||||
|
return (size_t)(src - orig_src);
|
||||||
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include <config.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* True if w contains a zero byte. Subtracting ONES from w propagates a
|
||||||
|
* borrow into the high bit of a byte exactly when that byte is zero;
|
||||||
|
* ANDing with ~w keeps only bytes that were zero and high-bit-clear in w.
|
||||||
|
* ONES and its shifted form are derived from the word width, so this works
|
||||||
|
* for both 32- and 64-bit unsigned long.
|
||||||
|
*/
|
||||||
|
static unsigned long
|
||||||
|
haszero(unsigned long w)
|
||||||
|
{
|
||||||
|
const unsigned long ones = (unsigned long)-1 / 0xff;
|
||||||
|
|
||||||
|
return (w - ones) & ~w & (ones << 7);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return the length of s, excluding the terminating NUL.
|
||||||
|
*/
|
||||||
|
size_t
|
||||||
|
strlen(const char *s)
|
||||||
|
{
|
||||||
|
const unsigned long word = sizeof(unsigned long);
|
||||||
|
const char *p = s;
|
||||||
|
|
||||||
|
/* Check the head byte-wise until p is word-aligned. */
|
||||||
|
for (; (unsigned long)p % word != 0; p++)
|
||||||
|
{
|
||||||
|
if (*p == '\0')
|
||||||
|
{
|
||||||
|
return (size_t)(p - s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Scan whole words for a zero byte. */
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
unsigned long w;
|
||||||
|
|
||||||
|
__builtin_memcpy(&w, p, sizeof w); // NOLINT(clang-analyzer-security.insecureAPI.*)
|
||||||
|
if (haszero(w))
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
p += word;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Resolve the terminating word byte-wise. */
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
if (*p == '\0')
|
||||||
|
{
|
||||||
|
return (size_t)(p - s);
|
||||||
|
}
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user