From dec1017527c6a5c18c7c4be7e6d13bcf8e296e9d Mon Sep 17 00:00:00 2001 From: huntedbytheirs Date: Thu, 3 Sep 2026 17:34:16 -0400 Subject: [PATCH] feat(headers): stddef/stdint/stdbool/limits/float/assert + features wiring --- include/assert.h | 45 ++++ include/cpio.h | 48 ++++ include/float.h | 79 ++++++ include/iso646.h | 32 +++ include/limits.h | 70 +++++ include/stdalign.h | 27 ++ include/stdarg.h | 24 ++ include/stdatomic.h | 186 ++++++++++++++ include/stdbit.h | 496 ++++++++++++++++++++++++++++++++++++ include/stdbool.h | 27 ++ include/stdckdint.h | 23 ++ include/stddef.h | 13 +- include/stdint.h | 157 ++++++++++++ include/stdnoreturn.h | 23 ++ include/sys/types.h | 77 ++++++ include/tar.h | 51 ++++ include/tgmath.h | 18 ++ include/threads.h | 190 ++++++++++++++ include/uchar.h | 64 +++++ include/vlibc/features.h.in | 17 ++ src/internal/assert_fail.c | 80 ++++++ tests/test_headers.c | 331 ++++++++++++++++++++++++ 22 files changed, 2076 insertions(+), 2 deletions(-) create mode 100644 include/assert.h create mode 100644 include/cpio.h create mode 100644 include/float.h create mode 100644 include/iso646.h create mode 100644 include/limits.h create mode 100644 include/stdalign.h create mode 100644 include/stdarg.h create mode 100644 include/stdatomic.h create mode 100644 include/stdbit.h create mode 100644 include/stdbool.h create mode 100644 include/stdckdint.h create mode 100644 include/stdint.h create mode 100644 include/stdnoreturn.h create mode 100644 include/sys/types.h create mode 100644 include/tar.h create mode 100644 include/tgmath.h create mode 100644 include/threads.h create mode 100644 include/uchar.h create mode 100644 src/internal/assert_fail.c create mode 100644 tests/test_headers.c diff --git a/include/assert.h b/include/assert.h new file mode 100644 index 0000000..c1f36b3 --- /dev/null +++ b/include/assert.h @@ -0,0 +1,45 @@ +#ifndef VLIBC_ASSERT_H +#define VLIBC_ASSERT_H + +/* + * vlibc — . + * + * The assert diagnostic macro. When NDEBUG is defined the macro expands to + * ((void)0) and the expression is not evaluated; otherwise a false result + * calls the library-internal __vlibc_assert_fail(), which writes the + * diagnostic to fd 2 through the raw syscall layer and terminates the + * process with __builtin_trap(). The helper deliberately does not depend on + * abort(), exit() or stdio: those land in later todos, and an assertion + * failure must work even when the runtime is half-initialized. + * + * static_assert is a C23 keyword; for C17 and older this header maps it onto + * _Static_assert, and _Static_assert itself remains usable in every mode. + * + * This header is ISO C core and is present in every profile. + */ + +#include + +/* NDEBUG: strip the check entirely, matching the standard. */ +#ifdef NDEBUG +#define assert(expr) ((void)0) +#else +/* + * Terminate after writing the failing assertion to fd 2; never returns. + * Implemented in src/internal/assert_fail.c. The name sits in the + * implementation-reserved namespace because it is libc plumbing, not + * public API. + */ +__attribute__((noreturn)) void +__vlibc_assert_fail(const char *expr, const char *file, + int line, // NOLINT(bugprone-reserved-identifier) + const char *func); + +#define assert(expr) ((expr) ? (void)0 : __vlibc_assert_fail(#expr, __FILE__, __LINE__, __func__)) +#endif + +#if !defined(__cplusplus) && (!defined(__STDC_VERSION__) || __STDC_VERSION__ < 202311L) +#define static_assert _Static_assert +#endif + +#endif /* VLIBC_ASSERT_H */ diff --git a/include/cpio.h b/include/cpio.h new file mode 100644 index 0000000..66dfd59 --- /dev/null +++ b/include/cpio.h @@ -0,0 +1,48 @@ +#ifndef VLIBC_CPIO_H +#define VLIBC_CPIO_H + +/* + * vlibc — . + * + * Constants for cpio archive formats (POSIX). Constants only: the file and + * mode bits used in "newc"/odc archive headers, and the octal magic string + * of the odc format. The newc magic ("070701") is not standard and is + * deliberately omitted. + */ + +#include + +#if VLIBC_HAS_HEADER_CPIO_H + +/* Octal magic of the odc ("old binary") archive format. */ +#define MAGIC "070707" + +/* Mode bits (permissions) of an archived entry. */ +#define C_IRUSR 0000400 +#define C_IWUSR 0000200 +#define C_IXUSR 0000100 +#define C_IRGRP 0000040 +#define C_IWGRP 0000020 +#define C_IXGRP 0000010 +#define C_IROTH 0000004 +#define C_IWOTH 0000002 +#define C_IXOTH 0000001 + +/* Set-id and sticky bits of an archived entry. */ +#define C_ISUID 0004000 +#define C_ISGID 0002000 +#define C_ISVTX 0001000 + +/* File type bits of an archived entry. */ +#define C_ISDIR 0040000 +#define C_ISFIFO 0010000 +#define C_ISREG 0100000 +#define C_ISBLK 0060000 +#define C_ISCHR 0020000 +#define C_ISCTG 0110000 +#define C_ISLNK 0120000 +#define C_ISSOCK 0140000 + +#endif /* VLIBC_HAS_HEADER_CPIO_H */ + +#endif /* VLIBC_CPIO_H */ diff --git a/include/float.h b/include/float.h new file mode 100644 index 0000000..f983b21 --- /dev/null +++ b/include/float.h @@ -0,0 +1,79 @@ +#ifndef VLIBC_FLOAT_H +#define VLIBC_FLOAT_H + +/* + * vlibc — . + * + * Characteristics of floating-point types (C23). Every value is derived from + * compiler predefined macros (__FLT_MANT_DIG__, __DBL_MAX__, ...), so this + * header is fully self-contained. The values reflect the x86_64 SSE2 + * environment: FLT_RADIX 2, IEC 60559 semantics for float, double and long + * double (the 80-bit extended type). + * + * FLT_ROUNDS is 1 (round to nearest): vlibc does not implement the floating + * environment yet, so the default rounding mode is always active. The fenv + * todo (#42) is expected to replace this with a live read of the x87/MXCSR + * control word. + * + * This header is ISO C core and is present in every profile. + */ + +#include + +/* Base of all floating-point types. */ +#define FLT_RADIX __FLT_RADIX__ + +/* Current rounding mode (see the header comment). */ +#define FLT_ROUNDS 1 + +/* Evaluation method (x86_64: all operations use the type's own range). */ +#define FLT_EVAL_METHOD __FLT_EVAL_METHOD__ + +/* IEC 60559 conformance of each type (C23). */ +#define FLT_IS_IEC_60559 __FLT_IS_IEC_60559__ +#define DBL_IS_IEC_60559 __DBL_IS_IEC_60559__ +#define LDBL_IS_IEC_60559 __LDBL_IS_IEC_60559__ + +/* float. */ +#define FLT_MANT_DIG __FLT_MANT_DIG__ +#define FLT_DECIMAL_DIG __FLT_DECIMAL_DIG__ +#define FLT_DIG __FLT_DIG__ +#define FLT_MIN_EXP __FLT_MIN_EXP__ +#define FLT_MIN_10_EXP __FLT_MIN_10_EXP__ +#define FLT_MAX_EXP __FLT_MAX_EXP__ +#define FLT_MAX_10_EXP __FLT_MAX_10_EXP__ +#define FLT_MAX __FLT_MAX__ +#define FLT_NORM_MAX __FLT_NORM_MAX__ +#define FLT_EPSILON __FLT_EPSILON__ +#define FLT_MIN __FLT_MIN__ +#define FLT_TRUE_MIN __FLT_DENORM_MIN__ + +/* double. */ +#define DBL_MANT_DIG __DBL_MANT_DIG__ +#define DBL_DECIMAL_DIG __DBL_DECIMAL_DIG__ +#define DBL_DIG __DBL_DIG__ +#define DBL_MIN_EXP __DBL_MIN_EXP__ +#define DBL_MIN_10_EXP __DBL_MIN_10_EXP__ +#define DBL_MAX_EXP __DBL_MAX_EXP__ +#define DBL_MAX_10_EXP __DBL_MAX_10_EXP__ +#define DBL_MAX __DBL_MAX__ +#define DBL_NORM_MAX __DBL_NORM_MAX__ +#define DBL_EPSILON __DBL_EPSILON__ +#define DBL_MIN __DBL_MIN__ +#define DBL_TRUE_MIN __DBL_DENORM_MIN__ + +/* long double (x86_64: 80-bit extended). */ +#define LDBL_MANT_DIG __LDBL_MANT_DIG__ +#define LDBL_DECIMAL_DIG __LDBL_DECIMAL_DIG__ +#define LDBL_DIG __LDBL_DIG__ +#define LDBL_MIN_EXP __LDBL_MIN_EXP__ +#define LDBL_MIN_10_EXP __LDBL_MIN_10_EXP__ +#define LDBL_MAX_EXP __LDBL_MAX_EXP__ +#define LDBL_MAX_10_EXP __LDBL_MAX_10_EXP__ +#define LDBL_MAX __LDBL_MAX__ +#define LDBL_NORM_MAX __LDBL_NORM_MAX__ +#define LDBL_EPSILON __LDBL_EPSILON__ +#define LDBL_MIN __LDBL_MIN__ +#define LDBL_TRUE_MIN __LDBL_DENORM_MIN__ + +#endif /* VLIBC_FLOAT_H */ diff --git a/include/iso646.h b/include/iso646.h new file mode 100644 index 0000000..fa96336 --- /dev/null +++ b/include/iso646.h @@ -0,0 +1,32 @@ +#ifndef VLIBC_ISO646_H +#define VLIBC_ISO646_H + +/* + * vlibc — . + * + * Alternative spellings for the bitwise and logical operators. C23 removed + * them from the language as alternative tokens, so this header provides them + * as macros; in C17 the spellings are lexed as tokens and the macros never + * fire, which makes the definitions harmless in both modes. In C++ they are + * part of the language, so no macros are defined there. + * + * This header is ISO C core and is present in every profile. + */ + +#include + +#ifndef __cplusplus +#define and && +#define and_eq &= +#define bitand & +#define bitor | +#define compl ~ +#define not ! +#define not_eq != +#define or || +#define or_eq |= +#define xor ^ +#define xor_eq ^= +#endif + +#endif /* VLIBC_ISO646_H */ diff --git a/include/limits.h b/include/limits.h new file mode 100644 index 0000000..dbfc1b3 --- /dev/null +++ b/include/limits.h @@ -0,0 +1,70 @@ +#ifndef VLIBC_LIMITS_H +#define VLIBC_LIMITS_H + +/* + * vlibc — . + * + * Sizes of integer types (C23). Every value is derived from compiler + * predefined macros (__CHAR_BIT__, __SCHAR_MAX__, __SIZEOF_INT__, ...), so + * this header is fully self-contained. char is signed on x86_64; the + * __CHAR_UNSIGNED__ branch keeps the header correct if that ever changes. + * + * This header is ISO C core and is present in every profile. + */ + +#include + +/* Number of bits in the smallest addressable object. */ +#define CHAR_BIT __CHAR_BIT__ + +/* Maximum number of bytes in a multibyte character (UTF-8 over 32-bit + * wchar_t needs at most 4). */ +#define MB_LEN_MAX 4 + +/* Limits of the plain char types. */ +#define SCHAR_MIN (-__SCHAR_MAX__ - 1) +#define SCHAR_MAX __SCHAR_MAX__ +#define UCHAR_MAX (__SCHAR_MAX__ * 2 + 1) +#if defined(__CHAR_UNSIGNED__) +#define CHAR_MIN 0 +#define CHAR_MAX UCHAR_MAX +#else +#define CHAR_MIN SCHAR_MIN +#define CHAR_MAX SCHAR_MAX +#endif + +/* Limits of the short types. */ +#define SHRT_MIN (-__SHRT_MAX__ - 1) +#define SHRT_MAX __SHRT_MAX__ +#define USHRT_MAX (__SHRT_MAX__ * 2 + 1) + +/* Limits of the int types. */ +#define INT_MIN (-__INT_MAX__ - 1) +#define INT_MAX __INT_MAX__ +#define UINT_MAX (__INT_MAX__ * 2U + 1U) + +/* Limits of the long types (x86_64 LP64: 64-bit). */ +#define LONG_MIN (-__LONG_MAX__ - 1L) +#define LONG_MAX __LONG_MAX__ +#define ULONG_MAX (__LONG_MAX__ * 2UL + 1UL) + +/* Limits of the long long types. */ +#define LLONG_MIN (-__LONG_LONG_MAX__ - 1LL) +#define LLONG_MAX __LONG_LONG_MAX__ +#define ULLONG_MAX (__LONG_LONG_MAX__ * 2ULL + 1ULL) + +/* Width of each integer type, in bits (C23). */ +#define BOOL_WIDTH 1 +#define CHAR_WIDTH __CHAR_BIT__ +#define SCHAR_WIDTH __CHAR_BIT__ +#define UCHAR_WIDTH __CHAR_BIT__ +#define SHRT_WIDTH (__SIZEOF_SHORT__ * __CHAR_BIT__) +#define USHRT_WIDTH (__SIZEOF_SHORT__ * __CHAR_BIT__) +#define INT_WIDTH (__SIZEOF_INT__ * __CHAR_BIT__) +#define UINT_WIDTH (__SIZEOF_INT__ * __CHAR_BIT__) +#define LONG_WIDTH (__SIZEOF_LONG__ * __CHAR_BIT__) +#define ULONG_WIDTH (__SIZEOF_LONG__ * __CHAR_BIT__) +#define LLONG_WIDTH (__SIZEOF_LONG_LONG__ * __CHAR_BIT__) +#define ULLONG_WIDTH (__SIZEOF_LONG_LONG__ * __CHAR_BIT__) + +#endif /* VLIBC_LIMITS_H */ diff --git a/include/stdalign.h b/include/stdalign.h new file mode 100644 index 0000000..1c9296a --- /dev/null +++ b/include/stdalign.h @@ -0,0 +1,27 @@ +#ifndef VLIBC_STDALIGN_H +#define VLIBC_STDALIGN_H + +/* + * vlibc — . + * + * In C23 alignas and alignof are keywords, so this header defines only the + * feature-test macros __alignas_is_defined and __alignof_is_defined. In C17 + * (and older) it additionally maps them onto the _Alignas/_Alignof keywords. + * In C++ the two names are keywords as well. + * + * This header is ISO C core and is present in every profile. + */ + +#include + +#ifndef __cplusplus +#if !defined(__STDC_VERSION__) || __STDC_VERSION__ < 202311L +#define alignas _Alignas +#define alignof _Alignof +#endif +#endif + +#define __alignas_is_defined 1 +#define __alignof_is_defined 1 + +#endif /* VLIBC_STDALIGN_H */ diff --git a/include/stdarg.h b/include/stdarg.h new file mode 100644 index 0000000..c1b4947 --- /dev/null +++ b/include/stdarg.h @@ -0,0 +1,24 @@ +#ifndef VLIBC_STDARG_H +#define VLIBC_STDARG_H + +/* + * vlibc — . + * + * Variable argument lists. The entire header defers to the compiler's + * builtin support: __builtin_va_list names the ABI's va_list layout and the + * __builtin_va_* entry points manipulate it. Nothing here depends on a + * system header. + * + * This header is ISO C core and is present in every profile. + */ + +#include + +typedef __builtin_va_list va_list; + +#define va_start(ap, last) __builtin_va_start((ap), (last)) +#define va_end(ap) __builtin_va_end((ap)) +#define va_arg(ap, type) __builtin_va_arg((ap), type) +#define va_copy(dst, src) __builtin_va_copy((dst), (src)) + +#endif /* VLIBC_STDARG_H */ diff --git a/include/stdatomic.h b/include/stdatomic.h new file mode 100644 index 0000000..d45f39a --- /dev/null +++ b/include/stdatomic.h @@ -0,0 +1,186 @@ +#ifndef VLIBC_STDATOMIC_H +#define VLIBC_STDATOMIC_H + +/* + * vlibc — . + * + * Atomics (C23). All operations defer to the GCC __atomic_* builtins, the + * same idiom src/internal/atomic.h uses: on x86_64 every standard atomic + * type here is lock-free in hardware, so the builtins inline to plain + * instructions and never call into libatomic. The header is fully + * self-contained (no system stdatomic.h) and compiles as C17-style code too, + * since _Atomic remains a keyword. + * + * The atomic_* operations are function-like macros over the polymorphic + * builtins (C23 allows any atomic operation to be implemented as a macro), + * which keeps them type-generic across every atomic_* type without a + * _Generic dispatch table. + * + * This header is ISO C core and is present in every profile. + */ + +#include + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Memory ordering. The enum values are the GCC __ATOMIC_* constants. */ +typedef enum +{ + memory_order_relaxed = __ATOMIC_RELAXED, + memory_order_consume = __ATOMIC_CONSUME, + memory_order_acquire = __ATOMIC_ACQUIRE, + memory_order_release = __ATOMIC_RELEASE, + memory_order_acq_rel = __ATOMIC_ACQ_REL, + memory_order_seq_cst = __ATOMIC_SEQ_CST +} memory_order; + +/* Lock-free guarantees for each type (0: never, 1: sometimes, 2: always). */ +#define ATOMIC_BOOL_LOCK_FREE __GCC_ATOMIC_BOOL_LOCK_FREE +#define ATOMIC_CHAR_LOCK_FREE __GCC_ATOMIC_CHAR_LOCK_FREE +#define ATOMIC_CHAR16_T_LOCK_FREE __GCC_ATOMIC_CHAR16_T_LOCK_FREE +#define ATOMIC_CHAR32_T_LOCK_FREE __GCC_ATOMIC_CHAR32_T_LOCK_FREE +#define ATOMIC_WCHAR_T_LOCK_FREE __GCC_ATOMIC_WCHAR_T_LOCK_FREE +#define ATOMIC_SHORT_LOCK_FREE __GCC_ATOMIC_SHORT_LOCK_FREE +#define ATOMIC_INT_LOCK_FREE __GCC_ATOMIC_INT_LOCK_FREE +#define ATOMIC_LONG_LOCK_FREE __GCC_ATOMIC_LONG_LOCK_FREE +#define ATOMIC_LLONG_LOCK_FREE __GCC_ATOMIC_LLONG_LOCK_FREE +#define ATOMIC_POINTER_LOCK_FREE __GCC_ATOMIC_POINTER_LOCK_FREE + +/* Atomic scalar types. */ +typedef _Atomic _Bool atomic_bool; +typedef _Atomic char atomic_char; +typedef _Atomic signed char atomic_schar; +typedef _Atomic unsigned char atomic_uchar; +typedef _Atomic short atomic_short; +typedef _Atomic unsigned short atomic_ushort; +typedef _Atomic int atomic_int; +typedef _Atomic unsigned int atomic_uint; +typedef _Atomic long atomic_long; +typedef _Atomic unsigned long atomic_ulong; +typedef _Atomic long long atomic_llong; +typedef _Atomic unsigned long long atomic_ullong; +typedef _Atomic __CHAR16_TYPE__ atomic_char16_t; +typedef _Atomic __CHAR32_TYPE__ atomic_char32_t; +typedef _Atomic __WCHAR_TYPE__ atomic_wchar_t; +typedef _Atomic int_least8_t atomic_int_least8_t; +typedef _Atomic uint_least8_t atomic_uint_least8_t; +typedef _Atomic int_least16_t atomic_int_least16_t; +typedef _Atomic uint_least16_t atomic_uint_least16_t; +typedef _Atomic int_least32_t atomic_int_least32_t; +typedef _Atomic uint_least32_t atomic_uint_least32_t; +typedef _Atomic int_least64_t atomic_int_least64_t; +typedef _Atomic uint_least64_t atomic_uint_least64_t; +typedef _Atomic int_fast8_t atomic_int_fast8_t; +typedef _Atomic uint_fast8_t atomic_uint_fast8_t; +typedef _Atomic int_fast16_t atomic_int_fast16_t; +typedef _Atomic uint_fast16_t atomic_uint_fast16_t; +typedef _Atomic int_fast32_t atomic_int_fast32_t; +typedef _Atomic uint_fast32_t atomic_uint_fast32_t; +typedef _Atomic int_fast64_t atomic_int_fast64_t; +typedef _Atomic uint_fast64_t atomic_uint_fast64_t; +typedef _Atomic intptr_t atomic_intptr_t; +typedef _Atomic uintptr_t atomic_uintptr_t; +typedef _Atomic size_t atomic_size_t; +typedef _Atomic ptrdiff_t atomic_ptrdiff_t; +typedef _Atomic intmax_t atomic_intmax_t; +typedef _Atomic uintmax_t atomic_uintmax_t; + +/* The only type the standard guarantees to be lock-free. */ +typedef _Atomic unsigned int atomic_flag; + +/* Initializer for an atomic_flag object. */ +#define ATOMIC_FLAG_INIT 0 + +/* + * The operations below are macros because the __atomic_* builtins are + * polymorphic: they accept any 1/2/4/8-byte scalar or pointer type, so a + * single macro serves every atomic_* type. The (object) argument is the + * address of the atomic object, matching the standard's function signatures. + */ + +/* Initialize the atomic object to value (a relaxed store). */ +#define atomic_init(object, value) __atomic_store_n((object), (value), __ATOMIC_RELAXED) + +/* Load the current value. */ +#define atomic_load(object) __atomic_load_n((object), __ATOMIC_SEQ_CST) +#define atomic_load_explicit(object, order) __atomic_load_n((object), (order)) + +/* Store value. */ +#define atomic_store(object, value) __atomic_store_n((object), (value), __ATOMIC_SEQ_CST) +#define atomic_store_explicit(object, value, order) __atomic_store_n((object), (value), (order)) + +/* Exchange object's value with desired; returns the previous value. */ +#define atomic_exchange(object, desired) __atomic_exchange_n((object), (desired), __ATOMIC_SEQ_CST) +#define atomic_exchange_explicit(object, desired, order) \ + __atomic_exchange_n((object), (desired), (order)) + +/* + * Compare-and-swap: store desired into object iff *object equals *expected. + * On failure *expected is updated with the actual value and the macro + * returns false. The first memorder governs the read-modify-write on + * success, the second the load on failure. + */ +#define atomic_compare_exchange_strong(object, expected, desired) \ + __atomic_compare_exchange_n((object), (expected), (desired), 0, __ATOMIC_SEQ_CST, \ + __ATOMIC_SEQ_CST) +#define atomic_compare_exchange_strong_explicit(object, expected, desired, success, failure) \ + __atomic_compare_exchange_n((object), (expected), (desired), 0, (success), (failure)) +#define atomic_compare_exchange_weak(object, expected, desired) \ + __atomic_compare_exchange_n((object), (expected), (desired), 1, __ATOMIC_SEQ_CST, \ + __ATOMIC_SEQ_CST) +#define atomic_compare_exchange_weak_explicit(object, expected, desired, success, failure) \ + __atomic_compare_exchange_n((object), (expected), (desired), 1, (success), (failure)) + +/* Fetch and add; returns the previous value. */ +#define atomic_fetch_add(object, operand) __atomic_fetch_add((object), (operand), __ATOMIC_SEQ_CST) +#define atomic_fetch_add_explicit(object, operand, order) \ + __atomic_fetch_add((object), (operand), (order)) + +/* Fetch and subtract; returns the previous value. */ +#define atomic_fetch_sub(object, operand) __atomic_fetch_sub((object), (operand), __ATOMIC_SEQ_CST) +#define atomic_fetch_sub_explicit(object, operand, order) \ + __atomic_fetch_sub((object), (operand), (order)) + +/* Fetch and bitwise-or; returns the previous value. */ +#define atomic_fetch_or(object, operand) __atomic_fetch_or((object), (operand), __ATOMIC_SEQ_CST) +#define atomic_fetch_or_explicit(object, operand, order) \ + __atomic_fetch_or((object), (operand), (order)) + +/* Fetch and bitwise-xor; returns the previous value. */ +#define atomic_fetch_xor(object, operand) __atomic_fetch_xor((object), (operand), __ATOMIC_SEQ_CST) +#define atomic_fetch_xor_explicit(object, operand, order) \ + __atomic_fetch_xor((object), (operand), (order)) + +/* Fetch and bitwise-and; returns the previous value. */ +#define atomic_fetch_and(object, operand) __atomic_fetch_and((object), (operand), __ATOMIC_SEQ_CST) +#define atomic_fetch_and_explicit(object, operand, order) \ + __atomic_fetch_and((object), (operand), (order)) + +/* atomic_flag: test-and-set (returns the previous flag value). */ +#define atomic_flag_test_and_set(object) __atomic_test_and_set((object), __ATOMIC_SEQ_CST) +#define atomic_flag_test_and_set_explicit(object, order) __atomic_test_and_set((object), (order)) + +/* atomic_flag: clear. */ +#define atomic_flag_clear(object) __atomic_clear((object), __ATOMIC_SEQ_CST) +#define atomic_flag_clear_explicit(object, order) __atomic_clear((object), (order)) + +/* Non-zero iff *object is always lock-free. */ +#define atomic_is_lock_free(object) __atomic_is_lock_free(sizeof *(object), (object)) + +/* Fences. */ +#define atomic_thread_fence(order) __atomic_thread_fence((order)) +#define atomic_signal_fence(order) __atomic_signal_fence((order)) + +/* Carry a dependency through a load without a synchronization edge. */ +#define kill_dependency(y) (y) + +#ifdef __cplusplus +} +#endif + +#endif /* VLIBC_STDATOMIC_H */ diff --git a/include/stdbit.h b/include/stdbit.h new file mode 100644 index 0000000..3f6b0eb --- /dev/null +++ b/include/stdbit.h @@ -0,0 +1,496 @@ +#ifndef VLIBC_STDBIT_H +#define VLIBC_STDBIT_H + +/* + * vlibc — . + * + * Bit and byte manipulation (C23 7.18). Every per-width operation is a + * static inline function over the GCC counting builtins + * (__builtin_clz/clzl/clzll, __builtin_ctz/ctzl/ctzll, + * __builtin_popcount/popcountl/popcountll), so no call escapes into the + * library. The type-generic names (stdc_leading_zeros, ...) dispatch through + * _Generic to the per-width functions; signed arguments convert to the + * unsigned type of the same rank, which preserves the bit pattern exactly as + * C23 specifies. Widths match x86_64: int is 32-bit, long is 64-bit. + * + * This header is ISO C core and is present in every profile. + */ + +#include + +#include + +/* stdc_leading_zeros: number of 0 bits before the first 1 bit, or the width. */ +static inline unsigned int +stdc_leading_zeros_uc(unsigned char x) +{ + return x == 0 ? 8u : (unsigned int)(__builtin_clz(x) - 24); +} + +static inline unsigned int +stdc_leading_zeros_us(unsigned short x) +{ + return x == 0 ? 16u : (unsigned int)(__builtin_clz(x) - 16); +} + +static inline unsigned int +stdc_leading_zeros_ui(unsigned int x) +{ + return x == 0 ? 32u : (unsigned int)__builtin_clz(x); +} + +static inline unsigned int +stdc_leading_zeros_ul(unsigned long x) +{ + return x == 0 ? 64u : (unsigned int)__builtin_clzl(x); +} + +static inline unsigned int +stdc_leading_zeros_ull(unsigned long long x) +{ + return x == 0 ? 64u : (unsigned int)__builtin_clzll(x); +} + +/* stdc_leading_ones: number of 1 bits before the first 0 bit, or the width. */ +static inline unsigned int +stdc_leading_ones_uc(unsigned char x) +{ + return x == UCHAR_MAX ? 8u : (unsigned int)(__builtin_clz((unsigned char)~x) - 24); +} + +static inline unsigned int +stdc_leading_ones_us(unsigned short x) +{ + return x == USHRT_MAX ? 16u : (unsigned int)(__builtin_clz((unsigned short)~x) - 16); +} + +static inline unsigned int +stdc_leading_ones_ui(unsigned int x) +{ + return x == UINT_MAX ? 32u : (unsigned int)__builtin_clz(~x); +} + +static inline unsigned int +stdc_leading_ones_ul(unsigned long x) +{ + return x == ULONG_MAX ? 64u : (unsigned int)__builtin_clzl(~x); +} + +static inline unsigned int +stdc_leading_ones_ull(unsigned long long x) +{ + return x == ULLONG_MAX ? 64u : (unsigned int)__builtin_clzll(~x); +} + +/* stdc_trailing_zeros: number of 0 bits after the last 1 bit, or the width. */ +static inline unsigned int +stdc_trailing_zeros_uc(unsigned char x) +{ + return x == 0 ? 8u : (unsigned int)__builtin_ctz(x); +} + +static inline unsigned int +stdc_trailing_zeros_us(unsigned short x) +{ + return x == 0 ? 16u : (unsigned int)__builtin_ctz(x); +} + +static inline unsigned int +stdc_trailing_zeros_ui(unsigned int x) +{ + return x == 0 ? 32u : (unsigned int)__builtin_ctz(x); +} + +static inline unsigned int +stdc_trailing_zeros_ul(unsigned long x) +{ + return x == 0 ? 64u : (unsigned int)__builtin_ctzl(x); +} + +static inline unsigned int +stdc_trailing_zeros_ull(unsigned long long x) +{ + return x == 0 ? 64u : (unsigned int)__builtin_ctzll(x); +} + +/* stdc_trailing_ones: number of 1 bits after the last 0 bit, or the width. */ +static inline unsigned int +stdc_trailing_ones_uc(unsigned char x) +{ + return x == UCHAR_MAX ? 8u : (unsigned int)__builtin_ctz((unsigned char)~x); +} + +static inline unsigned int +stdc_trailing_ones_us(unsigned short x) +{ + return x == USHRT_MAX ? 16u : (unsigned int)__builtin_ctz((unsigned short)~x); +} + +static inline unsigned int +stdc_trailing_ones_ui(unsigned int x) +{ + return x == UINT_MAX ? 32u : (unsigned int)__builtin_ctz(~x); +} + +static inline unsigned int +stdc_trailing_ones_ul(unsigned long x) +{ + return x == ULONG_MAX ? 64u : (unsigned int)__builtin_ctzl(~x); +} + +static inline unsigned int +stdc_trailing_ones_ull(unsigned long long x) +{ + return x == ULLONG_MAX ? 64u : (unsigned int)__builtin_ctzll(~x); +} + +/* + * stdc_first_*: index (from the most significant bit) of the first zero/one + * bit, or the width when no such bit exists. By construction, the index of + * the first zero from the top equals the count of leading ones, and so on. + */ +static inline unsigned int +stdc_first_leading_zero_uc(unsigned char x) +{ + return stdc_leading_ones_uc(x); +} + +static inline unsigned int +stdc_first_leading_zero_us(unsigned short x) +{ + return stdc_leading_ones_us(x); +} + +static inline unsigned int +stdc_first_leading_zero_ui(unsigned int x) +{ + return stdc_leading_ones_ui(x); +} + +static inline unsigned int +stdc_first_leading_zero_ul(unsigned long x) +{ + return stdc_leading_ones_ul(x); +} + +static inline unsigned int +stdc_first_leading_zero_ull(unsigned long long x) +{ + return stdc_leading_ones_ull(x); +} + +static inline unsigned int +stdc_first_leading_one_uc(unsigned char x) +{ + return stdc_leading_zeros_uc(x); +} + +static inline unsigned int +stdc_first_leading_one_us(unsigned short x) +{ + return stdc_leading_zeros_us(x); +} + +static inline unsigned int +stdc_first_leading_one_ui(unsigned int x) +{ + return stdc_leading_zeros_ui(x); +} + +static inline unsigned int +stdc_first_leading_one_ul(unsigned long x) +{ + return stdc_leading_zeros_ul(x); +} + +static inline unsigned int +stdc_first_leading_one_ull(unsigned long long x) +{ + return stdc_leading_zeros_ull(x); +} + +static inline unsigned int +stdc_first_trailing_zero_uc(unsigned char x) +{ + return stdc_trailing_ones_uc(x); +} + +static inline unsigned int +stdc_first_trailing_zero_us(unsigned short x) +{ + return stdc_trailing_ones_us(x); +} + +static inline unsigned int +stdc_first_trailing_zero_ui(unsigned int x) +{ + return stdc_trailing_ones_ui(x); +} + +static inline unsigned int +stdc_first_trailing_zero_ul(unsigned long x) +{ + return stdc_trailing_ones_ul(x); +} + +static inline unsigned int +stdc_first_trailing_zero_ull(unsigned long long x) +{ + return stdc_trailing_ones_ull(x); +} + +static inline unsigned int +stdc_first_trailing_one_uc(unsigned char x) +{ + return stdc_trailing_zeros_uc(x); +} + +static inline unsigned int +stdc_first_trailing_one_us(unsigned short x) +{ + return stdc_trailing_zeros_us(x); +} + +static inline unsigned int +stdc_first_trailing_one_ui(unsigned int x) +{ + return stdc_trailing_zeros_ui(x); +} + +static inline unsigned int +stdc_first_trailing_one_ul(unsigned long x) +{ + return stdc_trailing_zeros_ul(x); +} + +static inline unsigned int +stdc_first_trailing_one_ull(unsigned long long x) +{ + return stdc_trailing_zeros_ull(x); +} + +/* stdc_count_ones: number of 1 bits. */ +static inline unsigned int +stdc_count_ones_uc(unsigned char x) +{ + return (unsigned int)__builtin_popcount(x); +} + +static inline unsigned int +stdc_count_ones_us(unsigned short x) +{ + return (unsigned int)__builtin_popcount(x); +} + +static inline unsigned int +stdc_count_ones_ui(unsigned int x) +{ + return (unsigned int)__builtin_popcount(x); +} + +static inline unsigned int +stdc_count_ones_ul(unsigned long x) +{ + return (unsigned int)__builtin_popcountl(x); +} + +static inline unsigned int +stdc_count_ones_ull(unsigned long long x) +{ + return (unsigned int)__builtin_popcountll(x); +} + +/* stdc_count_zeros: number of 0 bits. */ +static inline unsigned int +stdc_count_zeros_uc(unsigned char x) +{ + return 8u - stdc_count_ones_uc(x); +} + +static inline unsigned int +stdc_count_zeros_us(unsigned short x) +{ + return 16u - stdc_count_ones_us(x); +} + +static inline unsigned int +stdc_count_zeros_ui(unsigned int x) +{ + return 32u - stdc_count_ones_ui(x); +} + +static inline unsigned int +stdc_count_zeros_ul(unsigned long x) +{ + return 64u - stdc_count_ones_ul(x); +} + +static inline unsigned int +stdc_count_zeros_ull(unsigned long long x) +{ + return 64u - stdc_count_ones_ull(x); +} + +/* stdc_has_single_bit: true iff exactly one bit is set. */ +static inline _Bool +stdc_has_single_bit_uc(unsigned char x) +{ + return x != 0 && (x & (x - 1U)) == 0; +} + +static inline _Bool +stdc_has_single_bit_us(unsigned short x) +{ + return x != 0 && (x & (x - 1U)) == 0; +} + +static inline _Bool +stdc_has_single_bit_ui(unsigned int x) +{ + return x != 0 && (x & (x - 1U)) == 0; +} + +static inline _Bool +stdc_has_single_bit_ul(unsigned long x) +{ + return x != 0 && (x & (x - 1UL)) == 0; +} + +static inline _Bool +stdc_has_single_bit_ull(unsigned long long x) +{ + return x != 0 && (x & (x - 1ULL)) == 0; +} + +/* stdc_bit_width: bits needed to represent x (0 for 0, 1 for 1). */ +static inline unsigned int +stdc_bit_width_uc(unsigned char x) +{ + return x == 0 ? 0u : (unsigned int)(32 - __builtin_clz(x)); +} + +static inline unsigned int +stdc_bit_width_us(unsigned short x) +{ + return x == 0 ? 0u : (unsigned int)(32 - __builtin_clz(x)); +} + +static inline unsigned int +stdc_bit_width_ui(unsigned int x) +{ + return x == 0 ? 0u : (unsigned int)(32 - __builtin_clz(x)); +} + +static inline unsigned int +stdc_bit_width_ul(unsigned long x) +{ + return x == 0 ? 0u : (unsigned int)(64 - __builtin_clzl(x)); +} + +static inline unsigned int +stdc_bit_width_ull(unsigned long long x) +{ + return x == 0 ? 0u : (unsigned int)(64 - __builtin_clzll(x)); +} + +/* stdc_bit_floor: largest power of 2 not greater than x (0 for 0). */ +static inline unsigned char +stdc_bit_floor_uc(unsigned char x) +{ + return (unsigned char)(x == 0 ? 0 : (1u << (32 - __builtin_clz(x) - 1))); +} + +static inline unsigned short +stdc_bit_floor_us(unsigned short x) +{ + return (unsigned short)(x == 0 ? 0 : (1u << (32 - __builtin_clz(x) - 1))); +} + +static inline unsigned int +stdc_bit_floor_ui(unsigned int x) +{ + return x == 0 ? 0u : (1u << (32 - __builtin_clz(x) - 1)); +} + +static inline unsigned long +stdc_bit_floor_ul(unsigned long x) +{ + return x == 0 ? 0ul : (1ul << (64 - __builtin_clzl(x) - 1)); +} + +static inline unsigned long long +stdc_bit_floor_ull(unsigned long long x) +{ + return x == 0 ? 0ull : (1ull << (64 - __builtin_clzll(x) - 1)); +} + +/* + * stdc_bit_ceil: smallest power of 2 not less than x. The result is + * undefined when it is not representable in the type of x, per C23. + */ +static inline unsigned char +stdc_bit_ceil_uc(unsigned char x) +{ + return (unsigned char)(x <= 1 ? 1 : (1u << (32 - __builtin_clz(x - 1)))); +} + +static inline unsigned short +stdc_bit_ceil_us(unsigned short x) +{ + return (unsigned short)(x <= 1 ? 1 : (1u << (32 - __builtin_clz(x - 1)))); +} + +static inline unsigned int +stdc_bit_ceil_ui(unsigned int x) +{ + return x <= 1 ? 1u : (1u << (32 - __builtin_clz(x - 1))); +} + +static inline unsigned long +stdc_bit_ceil_ul(unsigned long x) +{ + return x <= 1 ? 1ul : (1ul << (64 - __builtin_clzl(x - 1))); +} + +static inline unsigned long long +stdc_bit_ceil_ull(unsigned long long x) +{ + return x <= 1 ? 1ull : (1ull << (64 - __builtin_clzll(x - 1))); +} + +/* + * Type-generic dispatch: map the (lvalue-converted) argument type to the + * per-width function. Signed types route to the unsigned function of the + * same rank, which preserves the bit pattern as C23 requires. + */ +#define VLIBC_STDBIT_DISPATCH(fn, x) \ + _Generic((x), \ + _Bool: fn##_uc, \ + char: fn##_uc, \ + signed char: fn##_uc, \ + unsigned char: fn##_uc, \ + short: fn##_us, \ + unsigned short: fn##_us, \ + int: fn##_ui, \ + unsigned int: fn##_ui, \ + long: fn##_ul, \ + unsigned long: fn##_ul, \ + long long: fn##_ull, \ + unsigned long long: fn##_ull)(x) + +#define stdc_leading_zeros(x) VLIBC_STDBIT_DISPATCH(stdc_leading_zeros, x) +#define stdc_leading_ones(x) VLIBC_STDBIT_DISPATCH(stdc_leading_ones, x) +#define stdc_trailing_zeros(x) VLIBC_STDBIT_DISPATCH(stdc_trailing_zeros, x) +#define stdc_trailing_ones(x) VLIBC_STDBIT_DISPATCH(stdc_trailing_ones, x) +#define stdc_first_leading_zero(x) VLIBC_STDBIT_DISPATCH(stdc_first_leading_zero, x) +#define stdc_first_leading_one(x) VLIBC_STDBIT_DISPATCH(stdc_first_leading_one, x) +#define stdc_first_trailing_zero(x) VLIBC_STDBIT_DISPATCH(stdc_first_trailing_zero, x) +#define stdc_first_trailing_one(x) VLIBC_STDBIT_DISPATCH(stdc_first_trailing_one, x) +#define stdc_count_zeros(x) VLIBC_STDBIT_DISPATCH(stdc_count_zeros, x) +#define stdc_count_ones(x) VLIBC_STDBIT_DISPATCH(stdc_count_ones, x) +#define stdc_has_single_bit(x) VLIBC_STDBIT_DISPATCH(stdc_has_single_bit, x) +#define stdc_bit_width(x) VLIBC_STDBIT_DISPATCH(stdc_bit_width, x) +#define stdc_bit_floor(x) VLIBC_STDBIT_DISPATCH(stdc_bit_floor, x) +#define stdc_bit_ceil(x) VLIBC_STDBIT_DISPATCH(stdc_bit_ceil, x) + +#endif /* VLIBC_STDBIT_H */ diff --git a/include/stdbool.h b/include/stdbool.h new file mode 100644 index 0000000..8206cae --- /dev/null +++ b/include/stdbool.h @@ -0,0 +1,27 @@ +#ifndef VLIBC_STDBOOL_H +#define VLIBC_STDBOOL_H + +/* + * vlibc — . + * + * In C23 bool, true and false are keywords, so this header defines only the + * guard macro __bool_true_false_are_defined. In C17 (and older) it defines + * the _Bool-based spellings the standard requires. In C++ the three names are + * keywords as well, so only the guard macro is defined there too. + * + * This header is ISO C core and is present in every profile. + */ + +#include + +#ifndef __cplusplus +#if !defined(__STDC_VERSION__) || __STDC_VERSION__ < 202311L +#define bool _Bool +#define true 1 +#define false 0 +#endif +#endif + +#define __bool_true_false_are_defined 1 + +#endif /* VLIBC_STDBOOL_H */ diff --git a/include/stdckdint.h b/include/stdckdint.h new file mode 100644 index 0000000..06fc459 --- /dev/null +++ b/include/stdckdint.h @@ -0,0 +1,23 @@ +#ifndef VLIBC_STDCKDINT_H +#define VLIBC_STDCKDINT_H + +/* + * vlibc — . + * + * Checked integer arithmetic (C23). Each macro computes the operation and + * stores the result through *result; it returns true when the mathematical + * result is not representable in the result type, in which case *result is + * left unmodified. The implementation defers to the GCC + * __builtin_*_overflow family, which is type-generic across every integer + * type, matching the standard's semantics. + * + * This header is ISO C core and is present in every profile. + */ + +#include + +#define ckd_add(result, a, b) __builtin_add_overflow((a), (b), (result)) +#define ckd_sub(result, a, b) __builtin_sub_overflow((a), (b), (result)) +#define ckd_mul(result, a, b) __builtin_mul_overflow((a), (b), (result)) + +#endif /* VLIBC_STDCKDINT_H */ diff --git a/include/stddef.h b/include/stddef.h index 5100109..9440909 100644 --- a/include/stddef.h +++ b/include/stddef.h @@ -20,11 +20,15 @@ typedef __SIZE_TYPE__ size_t; /* Signed integer type of the difference of two pointers. */ typedef __PTRDIFF_TYPE__ ptrdiff_t; -/* Wide character type. */ +/* Wide character type; a C++ keyword there, so no redefinition. */ +#ifndef __cplusplus typedef __WCHAR_TYPE__ wchar_t; +#endif -/* Type of the null pointer constant nullptr (C23). */ +/* Type of the null pointer constant nullptr (C23); a C++ keyword there. */ +#if !defined(__cplusplus) && defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L typedef typeof(nullptr) nullptr_t; +#endif /* Null pointer constant. */ #define NULL ((void *)0) @@ -32,6 +36,11 @@ typedef typeof(nullptr) nullptr_t; /* Offset in bytes of a member from the start of its enclosing object. */ #define offsetof(type, m) __builtin_offsetof(type, m) +/* Mark the following point of the program as unreachable (C23). */ +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L +#define unreachable() __builtin_unreachable() +#endif + /* * An object type whose alignment is as great as that of any supported * fundamental type; suitable as the storage type for aligned allocation. diff --git a/include/stdint.h b/include/stdint.h new file mode 100644 index 0000000..d5e639b --- /dev/null +++ b/include/stdint.h @@ -0,0 +1,157 @@ +#ifndef VLIBC_STDINT_H +#define VLIBC_STDINT_H + +/* + * vlibc — . + * + * Fixed-width integer types and their limit and constant macros (C23). Every + * definition is derived from compiler predefined macros (__INT8_TYPE__, + * __INT64_MAX__, __INT64_C, ...), so this header is fully self-contained and + * never borrows from a system header. Widths follow the x86_64 LP64 model. + * + * The compatibility profile is exposed by include/vlibc/features.h; this + * header is ISO C core and is present in every profile. + */ + +#include + +/* Exact-width types. */ +typedef __INT8_TYPE__ int8_t; +typedef __INT16_TYPE__ int16_t; +typedef __INT32_TYPE__ int32_t; +typedef __INT64_TYPE__ int64_t; +typedef __UINT8_TYPE__ uint8_t; +typedef __UINT16_TYPE__ uint16_t; +typedef __UINT32_TYPE__ uint32_t; +typedef __UINT64_TYPE__ uint64_t; + +/* Types with at least the given width (least-width). */ +typedef __INT_LEAST8_TYPE__ int_least8_t; +typedef __INT_LEAST16_TYPE__ int_least16_t; +typedef __INT_LEAST32_TYPE__ int_least32_t; +typedef __INT_LEAST64_TYPE__ int_least64_t; +typedef __UINT_LEAST8_TYPE__ uint_least8_t; +typedef __UINT_LEAST16_TYPE__ uint_least16_t; +typedef __UINT_LEAST32_TYPE__ uint_least32_t; +typedef __UINT_LEAST64_TYPE__ uint_least64_t; + +/* Fastest types with at least the given width. */ +typedef __INT_FAST8_TYPE__ int_fast8_t; +typedef __INT_FAST16_TYPE__ int_fast16_t; +typedef __INT_FAST32_TYPE__ int_fast32_t; +typedef __INT_FAST64_TYPE__ int_fast64_t; +typedef __UINT_FAST8_TYPE__ uint_fast8_t; +typedef __UINT_FAST16_TYPE__ uint_fast16_t; +typedef __UINT_FAST32_TYPE__ uint_fast32_t; +typedef __UINT_FAST64_TYPE__ uint_fast64_t; + +/* Types wide enough for pointers. */ +typedef __INTPTR_TYPE__ intptr_t; +typedef __UINTPTR_TYPE__ uintptr_t; + +/* Types of maximum width. */ +typedef __INTMAX_TYPE__ intmax_t; +typedef __UINTMAX_TYPE__ uintmax_t; + +/* Limits of the exact-width types. */ +#define INT8_MIN (-__INT8_MAX__ - 1) +#define INT8_MAX __INT8_MAX__ +#define UINT8_MAX __UINT8_MAX__ +#define INT16_MIN (-__INT16_MAX__ - 1) +#define INT16_MAX __INT16_MAX__ +#define UINT16_MAX __UINT16_MAX__ +#define INT32_MIN (-__INT32_MAX__ - 1) +#define INT32_MAX __INT32_MAX__ +#define UINT32_MAX __UINT32_MAX__ +#define INT64_MIN (-__INT64_MAX__ - 1) +#define INT64_MAX __INT64_MAX__ +#define UINT64_MAX __UINT64_MAX__ + +/* Limits of the least-width types. */ +#define INT_LEAST8_MIN (-__INT_LEAST8_MAX__ - 1) +#define INT_LEAST8_MAX __INT_LEAST8_MAX__ +#define UINT_LEAST8_MAX __UINT_LEAST8_MAX__ +#define INT_LEAST16_MIN (-__INT_LEAST16_MAX__ - 1) +#define INT_LEAST16_MAX __INT_LEAST16_MAX__ +#define UINT_LEAST16_MAX __UINT_LEAST16_MAX__ +#define INT_LEAST32_MIN (-__INT_LEAST32_MAX__ - 1) +#define INT_LEAST32_MAX __INT_LEAST32_MAX__ +#define UINT_LEAST32_MAX __UINT_LEAST32_MAX__ +#define INT_LEAST64_MIN (-__INT_LEAST64_MAX__ - 1) +#define INT_LEAST64_MAX __INT_LEAST64_MAX__ +#define UINT_LEAST64_MAX __UINT_LEAST64_MAX__ + +/* Limits of the fastest types. */ +#define INT_FAST8_MIN (-__INT_FAST8_MAX__ - 1) +#define INT_FAST8_MAX __INT_FAST8_MAX__ +#define UINT_FAST8_MAX __UINT_FAST8_MAX__ +#define INT_FAST16_MIN (-__INT_FAST16_MAX__ - 1) +#define INT_FAST16_MAX __INT_FAST16_MAX__ +#define UINT_FAST16_MAX __UINT_FAST16_MAX__ +#define INT_FAST32_MIN (-__INT_FAST32_MAX__ - 1) +#define INT_FAST32_MAX __INT_FAST32_MAX__ +#define UINT_FAST32_MAX __UINT_FAST32_MAX__ +#define INT_FAST64_MIN (-__INT_FAST64_MAX__ - 1) +#define INT_FAST64_MAX __INT_FAST64_MAX__ +#define UINT_FAST64_MAX __UINT_FAST64_MAX__ + +/* Limits of the pointer-width and maximum-width types. */ +#define INTPTR_MIN (-__INTPTR_MAX__ - 1) +#define INTPTR_MAX __INTPTR_MAX__ +#define UINTPTR_MAX __UINTPTR_MAX__ +#define INTMAX_MIN (-__INTMAX_MAX__ - 1) +#define INTMAX_MAX __INTMAX_MAX__ +#define UINTMAX_MAX __UINTMAX_MAX__ + +/* Limits of other standard integer types (x86_64: wchar_t is signed int). */ +#define WCHAR_MIN __WCHAR_MIN__ +#define WCHAR_MAX __WCHAR_MAX__ + +/* Macros for integer constants of the given type. */ +#define INT8_C(c) __INT8_C(c) +#define INT16_C(c) __INT16_C(c) +#define INT32_C(c) __INT32_C(c) +#define INT64_C(c) __INT64_C(c) +#define UINT8_C(c) __UINT8_C(c) +#define UINT16_C(c) __UINT16_C(c) +#define UINT32_C(c) __UINT32_C(c) +#define UINT64_C(c) __UINT64_C(c) +#define INTMAX_C(c) __INTMAX_C(c) +#define UINTMAX_C(c) __UINTMAX_C(c) + +/* Width of each integer type, in bits (C23). */ +#define INT8_WIDTH 8 +#define INT16_WIDTH 16 +#define INT32_WIDTH 32 +#define INT64_WIDTH 64 +#define UINT8_WIDTH 8 +#define UINT16_WIDTH 16 +#define UINT32_WIDTH 32 +#define UINT64_WIDTH 64 +#define INT_LEAST8_WIDTH __INT_LEAST8_WIDTH__ +#define INT_LEAST16_WIDTH __INT_LEAST16_WIDTH__ +#define INT_LEAST32_WIDTH __INT_LEAST32_WIDTH__ +#define INT_LEAST64_WIDTH __INT_LEAST64_WIDTH__ +#define UINT_LEAST8_WIDTH __UINT_LEAST8_WIDTH__ +#define UINT_LEAST16_WIDTH __UINT_LEAST16_WIDTH__ +#define UINT_LEAST32_WIDTH __UINT_LEAST32_WIDTH__ +#define UINT_LEAST64_WIDTH __UINT_LEAST64_WIDTH__ +#define INT_FAST8_WIDTH __INT_FAST8_WIDTH__ +#define INT_FAST16_WIDTH __INT_FAST16_WIDTH__ +#define INT_FAST32_WIDTH __INT_FAST32_WIDTH__ +#define INT_FAST64_WIDTH __INT_FAST64_WIDTH__ +#define UINT_FAST8_WIDTH __UINT_FAST8_WIDTH__ +#define UINT_FAST16_WIDTH __UINT_FAST16_WIDTH__ +#define UINT_FAST32_WIDTH __UINT_FAST32_WIDTH__ +#define UINT_FAST64_WIDTH __UINT_FAST64_WIDTH__ +#define INTPTR_WIDTH __INTPTR_WIDTH__ +#define UINTPTR_WIDTH __UINTPTR_WIDTH__ +#define INTMAX_WIDTH __INTMAX_WIDTH__ +#define UINTMAX_WIDTH __UINTMAX_WIDTH__ +#define PTRDIFF_WIDTH __PTRDIFF_WIDTH__ +#define SIG_ATOMIC_WIDTH __SIG_ATOMIC_WIDTH__ +#define SIZE_WIDTH __SIZE_WIDTH__ +#define WCHAR_WIDTH __WCHAR_WIDTH__ +#define WINT_WIDTH __WINT_WIDTH__ + +#endif /* VLIBC_STDINT_H */ diff --git a/include/stdnoreturn.h b/include/stdnoreturn.h new file mode 100644 index 0000000..e6958c8 --- /dev/null +++ b/include/stdnoreturn.h @@ -0,0 +1,23 @@ +#ifndef VLIBC_STDNORETURN_H +#define VLIBC_STDNORETURN_H + +/* + * vlibc — . + * + * In C23 noreturn is a keyword, so this header has nothing left to define. + * In C17 (and older) it maps noreturn onto the _Noreturn function specifier. + * In C++ [[noreturn]] is an attribute, not a macro, so nothing is defined + * there either. + * + * This header is ISO C core and is present in every profile. + */ + +#include + +#ifndef __cplusplus +#if !defined(__STDC_VERSION__) || __STDC_VERSION__ < 202311L +#define noreturn _Noreturn +#endif +#endif + +#endif /* VLIBC_STDNORETURN_H */ diff --git a/include/sys/types.h b/include/sys/types.h new file mode 100644 index 0000000..9b3bc88 --- /dev/null +++ b/include/sys/types.h @@ -0,0 +1,77 @@ +#ifndef VLIBC_SYS_TYPES_H +#define VLIBC_SYS_TYPES_H + +/* + * vlibc — . + * + * The POSIX base scalar types. This public header is authoritative for + * public consumers; src/internal/types.h mirrors the same x86_64 LP64 + * choices for internal use (both agree today — see the learnings note). + * Widths follow the Linux x86_64 ABI, not the ISO minimums: long is 64-bit. + */ + +#include + +#include + +#if VLIBC_HAS_HEADER_SYS_TYPES_H + +/* Signed type for byte counts and read()/write() results (x86_64: long). */ +typedef __PTRDIFF_TYPE__ ssize_t; + +/* Calendar time in seconds. */ +typedef long time_t; + +/* Clock ticks (times(2)). */ +typedef long clock_t; + +/* Clock id (clock_gettime(2)). */ +typedef int clockid_t; + +/* Timer id (timer_create(2)). */ +typedef void *timer_t; + +/* Process ID. */ +typedef int pid_t; + +/* User and group IDs. */ +typedef unsigned int uid_t; +typedef unsigned int gid_t; + +/* General identifier. */ +typedef unsigned int id_t; + +/* System V IPC key. */ +typedef int key_t; + +/* File offset. */ +typedef long off_t; + +/* File mode bits (and permissions). */ +typedef unsigned int mode_t; + +/* Device and inode numbers. */ +typedef unsigned long long dev_t; +typedef unsigned long long ino_t; + +/* Hard link count. */ +typedef unsigned long long nlink_t; + +/* Block counts and sizes. */ +typedef long blkcnt_t; +typedef long blksize_t; + +/* File-system block and file counts (statvfs). */ +typedef unsigned long fsblkcnt_t; +typedef unsigned long fsfilcnt_t; + +/* Microsecond intervals (timeval). */ +typedef long suseconds_t; +typedef unsigned int useconds_t; + +/* Socket address lengths. */ +typedef unsigned int socklen_t; + +#endif /* VLIBC_HAS_HEADER_SYS_TYPES_H */ + +#endif /* VLIBC_SYS_TYPES_H */ diff --git a/include/tar.h b/include/tar.h new file mode 100644 index 0000000..e65a727 --- /dev/null +++ b/include/tar.h @@ -0,0 +1,51 @@ +#ifndef VLIBC_TAR_H +#define VLIBC_TAR_H + +/* + * vlibc — . + * + * Constants for the ustar tar archive format (POSIX). Constants only: the + * header magic and version strings, the typeflag characters that identify + * entry kinds, and the mode bits stored in each header. + */ + +#include + +#if VLIBC_HAS_HEADER_TAR_H + +/* Header magic (6 bytes) and version (2 bytes). */ +#define TMAGIC "ustar" +#define TMAGLEN 6 +#define TVERSION "00" +#define TVERSLEN 2 + +/* Typeflag characters naming the entry kind. */ +#define REGTYPE '0' +#define AREGTYPE '\0' +#define LNKTYPE '1' +#define SYMTYPE '2' +#define CHRTYPE '3' +#define BLKTYPE '4' +#define DIRTYPE '5' +#define FIFOTYPE '6' +#define CONTTYPE '7' + +/* Set-id and sticky bits. */ +#define TSUID 04000 +#define TSGID 02000 +#define TSVTX 01000 + +/* Mode bits (permissions): user, group, other. */ +#define TUREAD 00400 +#define TUWRITE 00200 +#define TUEXEC 00100 +#define TGREAD 00040 +#define TGWRITE 00020 +#define TGEXEC 00010 +#define TOREAD 00004 +#define TOWRITE 00002 +#define TOEXEC 00001 + +#endif /* VLIBC_HAS_HEADER_TAR_H */ + +#endif /* VLIBC_TAR_H */ diff --git a/include/tgmath.h b/include/tgmath.h new file mode 100644 index 0000000..09744f6 --- /dev/null +++ b/include/tgmath.h @@ -0,0 +1,18 @@ +#ifndef VLIBC_TGMATH_H +#define VLIBC_TGMATH_H + +/* + * vlibc — . + * + * Type-generic math. STUB: the type-generic dispatch over is + * IMPLEMENTED by the first math todo (#39), not here. This stub exists so + * that including alongside the other headers compiles cleanly + * today; it deliberately defines no type-generic macros until #39 lands + * the function inventory it dispatches to. + * + * This header is ISO C core and is present in every profile. + */ + +#include + +#endif /* VLIBC_TGMATH_H */ diff --git a/include/threads.h b/include/threads.h new file mode 100644 index 0000000..7f6346d --- /dev/null +++ b/include/threads.h @@ -0,0 +1,190 @@ +#ifndef VLIBC_THREADS_H +#define VLIBC_THREADS_H + +/* + * vlibc — . + * + * C11 threads. STUB: the header lands here (level-1 gate) but every + * function is implemented by the threads todo (#45), which wraps them as + * thin aliases over the pthread layer. Until then, user code compiles + * against these declarations and linking resolves once #45 lands. + * + * The types are placeholder shapes for now; #45 owns their final form (the + * pthread-backed layout) and may refine them in place. + */ + +#include + +#if VLIBC_HAS_HEADER_THREADS_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* Forward-declared: the real definition lives in (later todo). */ +struct timespec; + +/* Thread identifier (x86_64: matches pthread_t's unsigned long). */ +typedef unsigned long thrd_t; + +/* Start routine of a thread. */ +typedef int (*thrd_start_t)(void *); + +/* Destructor run when a thread exits. */ +typedef void (*tss_dtor_t)(void *); + +/* Mutex. */ +typedef struct +{ + unsigned long opaque; +} mtx_t; + +/* Condition variable. */ +typedef struct +{ + unsigned long opaque; +} cnd_t; + +/* Thread-specific storage key. */ +typedef struct +{ + unsigned long opaque; +} tss_t; + +/* call_once state. */ +typedef struct +{ + unsigned long opaque; +} once_flag; + +/* Initializer for a once_flag object. */ +#define ONCE_FLAG_INIT {0} + +/* Upper bound on tss_dtor_t invocations per key at thread exit. */ +#define TSS_DTOR_ITERATIONS 4 + +/* Mutex kinds for mtx_init. */ +enum +{ + mtx_plain = 0, + mtx_recursive = 1, + mtx_timed = 2 +}; + +/* Result codes shared by the thrd_* and mtx_* functions. */ +enum +{ + thrd_success = 0, + thrd_busy = 1, + thrd_error = 2, + thrd_nomem = 3, + thrd_timedout = 4 +}; + +/* Create a thread running func(arg); stores its id through thr. */ +int +thrd_create(thrd_t *thr, thrd_start_t func, void *arg); + +/* Non-zero iff lhs and rhs name the same thread. */ +int +thrd_equal(thrd_t lhs, thrd_t rhs); + +/* Identifier of the calling thread. */ +thrd_t +thrd_current(void); + +/* Sleep for the interval in *duration; *remaining gets the unslept part. */ +int +thrd_sleep(const struct timespec *duration, struct timespec *remaining); + +/* Yield the processor to other runnable threads. */ +void +thrd_yield(void); + +/* Terminate the calling thread, reporting res. */ +_Noreturn void +thrd_exit(int res); + +/* Detach thr so its resources are released on exit. */ +int +thrd_detach(thrd_t thr); + +/* Wait for thr to exit; stores its result through *res when non-NULL. */ +int +thrd_join(thrd_t thr, int *res); + +/* Initialize *mtx with the given kind. */ +int +mtx_init(mtx_t *mtx, int type); + +/* Lock *mtx, blocking if needed. */ +int +mtx_lock(mtx_t *mtx); + +/* Lock *mtx, blocking at most until *ts. */ +int +mtx_timedlock(mtx_t *restrict mtx, const struct timespec *restrict ts); + +/* Lock *mtx unless another thread holds it (thrd_busy). */ +int +mtx_trylock(mtx_t *mtx); + +/* Unlock *mtx. */ +int +mtx_unlock(mtx_t *mtx); + +/* Release *mtx's resources. */ +void +mtx_destroy(mtx_t *mtx); + +/* Run func exactly once per once_flag object. */ +void +call_once(once_flag *flag, void (*func)(void)); + +/* Initialize *cnd. */ +int +cnd_init(cnd_t *cnd); + +/* Wake one thread waiting on *cnd. */ +int +cnd_signal(cnd_t *cnd); + +/* Wake all threads waiting on *cnd. */ +int +cnd_broadcast(cnd_t *cnd); + +/* Wait on *cnd; *mtx must be locked by the caller. */ +int +cnd_wait(cnd_t *cnd, mtx_t *mtx); + +/* Wait on *cnd at most until *ts. */ +int +cnd_timedwait(cnd_t *restrict cnd, mtx_t *restrict mtx, const struct timespec *restrict ts); + +/* Release *cnd's resources. */ +void +cnd_destroy(cnd_t *cnd); + +/* Create a thread-specific key *key; dtor may be NULL. */ +int +tss_create(tss_t *key, tss_dtor_t dtor); + +/* Value stored for *key in the calling thread (NULL when unset). */ +void * +tss_get(tss_t key); + +/* Store val under *key for the calling thread. */ +int +tss_set(tss_t key, void *val); + +/* Release *key; the key may not be used afterwards. */ +void +tss_delete(tss_t key); + +#ifdef __cplusplus +} +#endif + +#endif /* VLIBC_HAS_HEADER_THREADS_H */ + +#endif /* VLIBC_THREADS_H */ diff --git a/include/uchar.h b/include/uchar.h new file mode 100644 index 0000000..0cb1060 --- /dev/null +++ b/include/uchar.h @@ -0,0 +1,64 @@ +#ifndef VLIBC_UCHAR_H +#define VLIBC_UCHAR_H + +/* + * vlibc — . + * + * Unicode character types and the multibyte conversion declarations (C11, + * gated at level 1 per the plan's header inventory). In C23 char16_t and + * char32_t are keywords, so the typedefs below only fire in C17 (and + * older); the C17 types are the compiler's __CHAR16_TYPE__/__CHAR32_TYPE__, + * which is what the C23 keywords name as well. + * + * The mbrtoc16/mbrtoc32/c16rtomb/c32rtomb functions are DECLARED here but + * implemented by the multibyte todo (#50): user code compiles against this + * header today and linking resolves once #50 lands. + */ + +#include + +#include + +#if VLIBC_HAS_HEADER_UCHAR_H + +/* + * char16_t/char32_t remain typedefs in C23 (unlike bool/alignas/... they were + * NOT promoted to keywords), so they are defined in every C mode from the + * compiler's __CHAR16_TYPE__/__CHAR32_TYPE__; in C++ they are keywords and + * no typedef fires. + */ +#ifndef __cplusplus +typedef __CHAR16_TYPE__ char16_t; +typedef __CHAR32_TYPE__ char32_t; +#endif + +/* + * Conversion state for multibyte conversions. Two opaque words; the real + * layout is owned by the multibyte todo (#50), which may extend this + * definition when it lands (see the learnings note). + */ +typedef struct +{ + unsigned int state[2]; +} mbstate_t; + +/* + * Multibyte <-> UTF-16/UTF-32 conversions (implemented in #50). + * + * mbrtoc16: convert at most n bytes of the multibyte string s to a char16_t + * stored through pc16, consuming the corresponding number of bytes. + * c16rtomb: convert char16_t c16 to at most MB_CUR_MAX bytes at s. + * The char32_t forms are the same for UTF-32. + */ +size_t +mbrtoc16(char16_t *restrict pc16, const char *restrict s, size_t n, mbstate_t *restrict ps); +size_t +c16rtomb(char *restrict s, char16_t c16, mbstate_t *restrict ps); +size_t +mbrtoc32(char32_t *restrict pc32, const char *restrict s, size_t n, mbstate_t *restrict ps); +size_t +c32rtomb(char *restrict s, char32_t c32, mbstate_t *restrict ps); + +#endif /* VLIBC_HAS_HEADER_UCHAR_H */ + +#endif /* VLIBC_UCHAR_H */ diff --git a/include/vlibc/features.h.in b/include/vlibc/features.h.in index 6ef68a8..d3bccb3 100644 --- a/include/vlibc/features.h.in +++ b/include/vlibc/features.h.in @@ -57,4 +57,21 @@ #define VLIBC_HAS_VLIBC 1 #endif +/* + * Per-header availability gates, derived from VLIBC_LEVEL. The ISO C core + * headers (, , , , , + * , , , , , + * , , , ) are present in every + * profile and carry no gate. The POSIX-facing headers below exist only in + * profiles that provide POSIX (level >= 1, i.e. all profiles today); each + * header includes this file and gates its declarations on its own macro. + */ +#if VLIBC_LEVEL >= 1 +#define VLIBC_HAS_HEADER_SYS_TYPES_H 1 +#define VLIBC_HAS_HEADER_UCHAR_H 1 +#define VLIBC_HAS_HEADER_CPIO_H 1 +#define VLIBC_HAS_HEADER_TAR_H 1 +#define VLIBC_HAS_HEADER_THREADS_H 1 +#endif + #endif /* VLIBC_FEATURES_H */ diff --git a/src/internal/assert_fail.c b/src/internal/assert_fail.c new file mode 100644 index 0000000..18a19fd --- /dev/null +++ b/src/internal/assert_fail.c @@ -0,0 +1,80 @@ +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "syscall.h" + +/* + * The failing-assertion sink behind . + * + * Writes the standard-shaped diagnostic to fd 2 through the raw syscall + * layer, then traps. No abort(), exit() or stdio: those land in later todos + * and an assertion failure must be able to fire from a half-initialized + * runtime. __builtin_trap() terminates with SIGILL, which the assert QA + * harness expects (the standard only requires abnormal termination). + * + * Not declared hidden, unlike the other src/internal helpers: + * declares this symbol to consumers, so it is part of the public ABI surface + * and must resolve across the shared-library boundary. + */ + +/* Convert v to decimal digits at p; returns the next free position. */ +static char * +append_dec(char *p, int v) +{ + char tmp[12]; + int i = 0; + + if (v == 0) + { + *p++ = '0'; + return p; + } + while (v > 0) + { + tmp[i++] = (char)('0' + v % 10); + v /= 10; + } + while (i > 0) + { + *p++ = tmp[--i]; + } + return p; +} + +/* Append the string s at p; returns the next free position. */ +static char * +append_str(char *p, const char *s) +{ + while (*s != '\0') + { + *p++ = *s++; + } + return p; +} + +/* + * The __vlibc_assert_fail name sits in the implementation-reserved namespace + * by design (it is the libc's private assert plumbing), so the + * reserved-identifier check is waived. + */ +__attribute__((noreturn)) void +__vlibc_assert_fail(const char *expr, const char *file, int line, + const char *func) // NOLINT(bugprone-reserved-identifier) +{ + char msg[384]; + char *p = msg; + + p = append_str(p, "Assertion failed: "); + p = append_str(p, expr); + p = append_str(p, " (file "); + p = append_str(p, file); + p = append_str(p, ": line "); + p = append_dec(p, line); + p = append_str(p, ", func "); + p = append_str(p, func); + p = append_str(p, ")\n"); + + (void)__syscall3(SYS_write, 2, (long)msg, (long)(p - msg)); + __builtin_trap(); +} diff --git a/tests/test_headers.c b/tests/test_headers.c new file mode 100644 index 0000000..3cb715c --- /dev/null +++ b/tests/test_headers.c @@ -0,0 +1,331 @@ +/* + * vlibc — public header skeleton test (todo 5). + * + * Exercises the full public header inventory in one translation unit: + * + * 1. compile-time: every new header plus the scaffold headers is included + * TWICE, so the include guards are proven to tolerate re-inclusion in + * any order; static_asserts pin the type widths and the standard macro + * values; + * 2. runtime: a happy-path main() checks the type sizes, the C23 keywords + * the headers provide (nullptr, alignas/alignof, iso646 spellings), + * stdatomic, stdbit, stdckdint and stdarg behavior end to end; + * 3. failure mode (-f): assert(0) must write the diagnostic to fd 2 and + * terminate with SIGILL — __vlibc_assert_fail() ends in + * __builtin_trap(), so the harness expects exit status 132 (128 + 4). + * When built with -DNDEBUG the -f run prints that assert was swallowed + * and exits 0 instead. + * + * All output goes through the raw SYS_write layer, like tests/syscall_test.c: + * no host is included, so the TU exercises only vlibc's own + * headers through -Iinclude and stays fully self-contained. + * + * Not part of the library proper; compiled manually for this todo (the + * tests/ + make check wiring is owned by a later todo). + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Second round: the guards must make every re-inclusion a no-op. */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../src/internal/syscall.h" + +/* ---- Compile-time gates (features.h wiring) ---- */ + +#if !VLIBC_HAS_POSIX +#error "VLIBC_HAS_POSIX must be 1 at level 1" +#endif +#if !defined(VLIBC_HAS_HEADER_SYS_TYPES_H) || !defined(VLIBC_HAS_HEADER_UCHAR_H) || \ + !defined(VLIBC_HAS_HEADER_CPIO_H) || !defined(VLIBC_HAS_HEADER_TAR_H) || \ + !defined(VLIBC_HAS_HEADER_THREADS_H) +#error "level-1 header gates missing from features.h" +#endif + +/* ---- Compile-time checks: stdint.h / limits.h / float.h ---- */ + +static_assert(sizeof(int8_t) == 1 && sizeof(uint8_t) == 1, "8-bit exact types"); +static_assert(sizeof(int16_t) == 2 && sizeof(uint16_t) == 2, "16-bit exact types"); +static_assert(sizeof(int32_t) == 4 && sizeof(uint32_t) == 4, "32-bit exact types"); +static_assert(sizeof(int64_t) == 8 && sizeof(uint64_t) == 8, "64-bit exact types"); +static_assert(sizeof(intptr_t) == sizeof(void *) && sizeof(uintptr_t) == sizeof(void *), + "pointer-width types"); +static_assert(sizeof(intmax_t) == 8 && sizeof(uintmax_t) == 8, "max-width types"); +static_assert(INT64_WIDTH == 64 && UINT64_WIDTH == 64, "exact widths"); +static_assert(SIZE_WIDTH == 64 && PTRDIFF_WIDTH == 64, "pointer-class widths"); +static_assert(WCHAR_WIDTH == 32, "wchar_t is 32-bit int on x86_64"); +static_assert(INT32_MAX == 2147483647 && INT32_MIN == (-2147483647 - 1), "int32 limits"); +static_assert(UINT32_MAX == 4294967295U, "uint32 limit"); +static_assert(INT64_MAX == INT64_C(0x7fffffffffffffff), "INT64_C produces long"); +static_assert(UINT64_MAX == UINT64_C(0xffffffffffffffff), "UINT64_C produces unsigned long"); +static_assert(CHAR_BIT == 8 && SCHAR_MIN == -128 && UCHAR_MAX == 255, "char limits"); +static_assert(SHRT_MAX == 32767 && USHRT_MAX == 65535, "short limits"); +static_assert(INT_MAX == 2147483647 && UINT_MAX == 4294967295U, "int limits"); +static_assert(LONG_MAX == 0x7fffffffffffffffL && ULONG_MAX == 0xffffffffffffffffUL, + "long limits (LP64)"); +static_assert(LLONG_MAX == 0x7fffffffffffffffLL && ULLONG_MAX == 0xffffffffffffffffULL, + "long long limits"); +static_assert(FLT_RADIX == 2, "binary floating point"); +static_assert(FLT_MANT_DIG == 24 && DBL_MANT_DIG == 53, "IEC 60559 mantissa widths"); +static_assert(FLT_ROUNDS == 1, "default rounding mode"); +static_assert(FLT_IS_IEC_60559 == 1 && DBL_IS_IEC_60559 == 1, "IEC 60559 types"); + +/* ---- Compile-time checks: keyword / feature-test plumbing ---- */ + +#ifndef __bool_true_false_are_defined +#error "stdbool.h must define __bool_true_false_are_defined" +#endif +#ifndef __alignas_is_defined +#error "stdalign.h must define __alignas_is_defined" +#endif +#ifndef __alignof_is_defined +#error "stdalign.h must define __alignof_is_defined" +#endif +_Static_assert(sizeof(int) == 4, "_Static_assert stays usable alongside static_assert"); + +/* ---- Runtime harness ---- */ + +static int failures; + +/* Write a NUL-terminated string to fd via the raw syscall layer. */ +static void +say(int fd, const char *s) +{ + size_t n = 0; + + while (s[n] != '\0') + { + n++; + } + (void)__syscall3(SYS_write, fd, (long)s, (long)n); +} + +static void +check(int cond, const char *what) +{ + if (!cond) + { + say(2, "FAIL: "); + say(2, what); + say(2, "\n"); + failures++; + } +} + +/* Sum n int arguments via . */ +static int +sum(int n, ...) +{ + va_list ap; + int total = 0; + int i; + + va_start(ap, n); + for (i = 0; i < n; i++) + { + total += va_arg(ap, int); + } + va_end(ap); + return total; +} + +int +main(int argc, char **argv) +{ + if (argc == 2 && argv[1][0] == '-' && argv[1][1] == 'f') + { +#ifdef NDEBUG + say(1, "NDEBUG: assert(0) swallowed\n"); + return 0; +#else + say(1, "about to assert(0): expect SIGILL\n"); + assert(0); + return 1; /* unreachable: __vlibc_assert_fail traps */ +#endif + } + + /* stddef.h: the C23 pieces. */ + { + nullptr_t np = nullptr; + struct of_test + { + char c; + int i; + }; + + check(sizeof(size_t) == 8 && sizeof(ptrdiff_t) == 8, "size_t/ptrdiff_t widths"); + check(sizeof(wchar_t) == 4, "wchar_t width"); + check(sizeof(nullptr_t) == sizeof(void *), "nullptr_t size"); + check(np == nullptr, "nullptr constant round-trip"); + check(offsetof(struct of_test, i) == 4, "offsetof"); + check(NULL == 0, "NULL"); + } + + /* stdalign.h / C23 keywords. */ + { + alignas(16) int aligned_obj; + + check(alignof(double) >= 8, "alignof"); + check(((uintptr_t)&aligned_obj & 15) == 0, "alignas(16) honored"); + } + + /* iso646.h: alternative spellings are macros in C23. */ + { + int alt = (1 and 1) or 0; + + check(alt == 1, "iso646 and/or"); + check(not(alt == 0), "iso646 not"); + } + + /* stdbit.h: type-generic dispatch and per-width spellings. */ + check(stdc_leading_zeros(1u) == 31, "stdc_leading_zeros"); + check(stdc_leading_zeros_uc(0x80u) == 0, "stdc_leading_zeros_uc"); + check(stdc_leading_ones(0xffffffffu) == 32, "stdc_leading_ones full"); + check(stdc_trailing_zeros(0x100u) == 8, "stdc_trailing_zeros"); + check(stdc_trailing_ones(7u) == 3, "stdc_trailing_ones"); + check(stdc_count_ones(0xffu) == 8, "stdc_count_ones"); + check(stdc_count_zeros(0xffu) == 24, "stdc_count_zeros"); + check(stdc_has_single_bit(0x40u) == 1, "stdc_has_single_bit true"); + check(stdc_has_single_bit(0x41u) == 0, "stdc_has_single_bit false"); + check(stdc_bit_width(0u) == 0, "stdc_bit_width(0)"); + check(stdc_bit_width(0x40u) == 7, "stdc_bit_width"); + check(stdc_bit_floor(0x33u) == 0x20u, "stdc_bit_floor"); + check(stdc_bit_ceil(0x33u) == 0x40u, "stdc_bit_ceil"); + check(stdc_first_leading_one((unsigned char)0x80) == 0, "stdc_first_leading_one"); + check(stdc_first_leading_zero(0xfffffffeu) == 31, "stdc_first_leading_zero"); + check(stdc_first_trailing_one(0x80u) == 7, "stdc_first_trailing_one"); + check(stdc_first_trailing_zero(1u) == 1, "stdc_first_trailing_zero"); + + /* stdckdint.h: overflow detection and result preservation. */ + { + unsigned int ures = 0; + int sres = 0; + + check(ckd_add(&ures, 0xffffffffu, 1u) == 1, "ckd_add detects overflow"); + check(ures == 0, "ckd_add leaves result unmodified on overflow"); + check(ckd_add(&ures, 1u, 2u) == 0 && ures == 3u, "ckd_add computes"); + check(ckd_mul(&sres, 100000, 100000) == 1, "ckd_mul detects overflow"); + check(ckd_sub(&ures, 5u, 7u) == 1, "ckd_sub detects underflow"); + } + + /* stdatomic.h: the macro operations over GCC builtins. */ + { + atomic_int counter = 7; + atomic_flag flag = ATOMIC_FLAG_INIT; + int expected; + + check(atomic_load(&counter) == 7, "atomic_load"); + atomic_store(&counter, 5); + check(atomic_load(&counter) == 5, "atomic_store"); + check(atomic_fetch_add(&counter, 2) == 5 && atomic_load(&counter) == 7, "atomic_fetch_add"); + check(atomic_exchange(&counter, 1) == 7, "atomic_exchange"); + expected = 1; + check(atomic_compare_exchange_strong(&counter, &expected, 9) == 1 && + atomic_load(&counter) == 9, + "compare_exchange success"); + expected = 3; + check(atomic_compare_exchange_strong(&counter, &expected, 0) == 0 && expected == 9, + "compare_exchange failure updates expected"); + check(atomic_flag_test_and_set(&flag) == 0, "atomic_flag initially clear"); + check(atomic_flag_test_and_set(&flag) == 1, "atomic_flag now set"); + atomic_flag_clear(&flag); + check(atomic_flag_test_and_set(&flag) == 0, "atomic_flag cleared"); + check(atomic_is_lock_free(&counter) != 0, "atomic_int lock-free"); + atomic_thread_fence(memory_order_seq_cst); + atomic_signal_fence(memory_order_relaxed); + } + + /* stdarg.h: variadic round-trip. */ + check(sum(3, 1, 2, 3) == 6, "stdarg sum"); + check(sum(0) == 0, "stdarg empty call"); + + /* assert.h happy path: a true assertion is a no-op. */ + assert(1); + assert(sizeof(int) == 4); + check(1, "assert(true) no-op"); + + /* uchar.h: C23 keyword types and the declared-only conversions. */ + check(sizeof(char16_t) == 2, "char16_t width"); + check(sizeof(char32_t) == 4, "char32_t width"); + { + mbstate_t mbs; + + mbs.state[0] = 0; + mbs.state[1] = 0; + check(mbs.state[0] == 0 && mbs.state[1] == 0, "mbstate_t usable"); + } + + /* sys/types.h: the POSIX scalar set (x86_64 LP64). */ + static_assert(sizeof(ssize_t) == 8 && sizeof(off_t) == 8, "signed 64-bit types"); + static_assert(sizeof(time_t) == 8 && sizeof(clock_t) == 8, "time types"); + static_assert(sizeof(pid_t) == 4 && sizeof(uid_t) == 4 && sizeof(gid_t) == 4, "id types"); + static_assert(sizeof(mode_t) == 4 && sizeof(id_t) == 4 && sizeof(key_t) == 4, + "mode/id/key types"); + static_assert(sizeof(dev_t) == 8 && sizeof(ino_t) == 8 && sizeof(nlink_t) == 8, + "file identity types"); + static_assert(sizeof(blkcnt_t) == 8 && sizeof(blksize_t) == 8, "block types"); + static_assert(sizeof(suseconds_t) == 8 && sizeof(useconds_t) == 4, "usec types"); + + /* cpio.h / tar.h: the archive constants. */ + check(C_IRUSR == 0400 && C_IRGRP == 0040 && C_IROTH == 0004, "cpio permission bits"); + check(C_ISREG == 0100000 && C_ISDIR == 0040000 && C_ISLNK == 0120000, "cpio type bits"); + check(TMAGIC[0] == 'u' && TMAGIC[1] == 's' && TMAGLEN == 6, "tar magic"); + check(TVERSION[0] == '0' && TVERSLEN == 2, "tar version"); + check(REGTYPE == '0' && AREGTYPE == '\0' && LNKTYPE == '1' && DIRTYPE == '5', "tar typeflags"); + check(TSUID == 04000 && TUREAD == 00400 && TGREAD == 00040 && TOREAD == 00004, "tar mode bits"); + + /* threads.h stub: the types exist; the functions stay unimplemented. */ + { + once_flag of = ONCE_FLAG_INIT; + thrd_t t = 0; + + check(of.opaque == 0 && t == 0, "threads.h stub types"); + check(TSS_DTOR_ITERATIONS == 4, "TSS_DTOR_ITERATIONS"); + } + + if (failures > 0) + { + say(2, "FAILED\n"); + } + return failures == 0 ? 0 : 1; +}