diff options
| author | Berke Güzel <wenekar1@gmail.com> | 2026-10-12 17:49:34 +0300 |
|---|---|---|
| committer | Berke Güzel <wenekar1@gmail.com> | 2026-10-12 17:49:34 +0300 |
| commit | 4384435941b8c7658856b381d071aee2e0035e89 (patch) | |
| tree | 5ece99c3dd1decb8ec3fd404c0ece2b5865a552a /src/compat.h | |
term: move the term core to subprojects/term
The core now builds, tests, and benchmarks alone, as a Meson subproject
with no dependency on the main tree.
- include/term/ holds term.h and the header-only unicode/. src/ holds
the core and its private headers. The main tree gets only
include/term through term_dep, so it cannot reach a private header.
- The term tests, the term benchmarks, the Kitty parser suite, and
tools/amalgamate.py and tools/generate_unicode.py move with it. The
tests run as term:<name>.
- The main build passes its -march (arch_flag) and b_ndebug to the
subproject. A directory configured before this commit needs
meson configure -Dterm:arch_flag=-march=... once.
- The term tests compile with -UNDEBUG. They do work inside assert.
- The subproject exports term_test_dep with term_api.h, for the main
tree's render, thumbnail, color, and selection tests.
- The Kitty bridge used struct vector2i and has not compiled since the
public API redesign. It builds again. 2 of its 14 tests fail: REP
after a tab, and a cursor report after a resize.
- dirgraph.py drops the term and unicode layers. Its self-test uses pty.
- The symbols test ignores the __odr_asan symbols that ASan adds.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'src/compat.h')
| -rw-r--r-- | src/compat.h | 171 |
1 files changed, 171 insertions, 0 deletions
diff --git a/src/compat.h b/src/compat.h new file mode 100644 index 0000000..d1264fe --- /dev/null +++ b/src/compat.h @@ -0,0 +1,171 @@ +#ifndef TERM_COMPAT_H +#define TERM_COMPAT_H + +/* + * Compiler and libc differences, kept in one place. GCC and Clang use their + * builtins. MSVC uses its intrinsics. Any other compiler, and any build with + * TERM_NO_COMPILER_BUILTINS, uses plain C. Plain C gives the same results, + * only slower. + */ + +#include <stdbool.h> +#include <stddef.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> + +#if defined(__GNUC__) || defined(__clang__) +#define TERM_COMPILER_GNU 1 +#elif defined(_MSC_VER) +#define TERM_COMPILER_MSVC 1 +#include <intrin.h> +#endif + +#if !defined(TERM_NO_COMPILER_BUILTINS) && defined(TERM_COMPILER_GNU) +#define TERM_HAVE_BUILTINS 1 +#elif !defined(TERM_NO_COMPILER_BUILTINS) && defined(TERM_COMPILER_MSVC) +#define TERM_HAVE_MSVC_BITOPS 1 +#endif + +#if defined(TERM_COMPILER_GNU) +#define TERM_HOT __attribute__((hot)) +#define TERM_NOINLINE __attribute__((noinline)) +#define TERM_ALWAYS_INLINE __attribute__((always_inline)) +#define TERM_MAYBE_UNUSED __attribute__((unused)) +#elif defined(TERM_COMPILER_MSVC) +#define TERM_HOT +#define TERM_NOINLINE __declspec(noinline) +#define TERM_ALWAYS_INLINE __forceinline +#define TERM_MAYBE_UNUSED +#else +#define TERM_HOT +#define TERM_NOINLINE +#define TERM_ALWAYS_INLINE +#define TERM_MAYBE_UNUSED +#endif + +#if defined(TERM_HAVE_BUILTINS) +#define TERM_EXPECT(x, v) __builtin_expect((x), (v)) +#else +#define TERM_EXPECT(x, v) (x) +#endif + +/* term_ctz32(0) is 32 and term_ctz64(0) is 64 on every path. */ +#if defined(TERM_HAVE_BUILTINS) + +static inline int term_ctz32(uint32_t x) { return x ? __builtin_ctz(x) : 32; } +static inline int term_popcount32(uint32_t x) { return __builtin_popcount(x); } +static inline int term_ctz64(uint64_t x) { return x ? __builtin_ctzll(x) : 64; } +static inline int term_popcount64(uint64_t x) { return __builtin_popcountll(x); } + +#else + +#if defined(TERM_HAVE_MSVC_BITOPS) +static inline int term_ctz32(uint32_t x) +{ + unsigned long i; + return _BitScanForward(&i, x) ? (int)i : 32; +} +static inline int term_popcount32(uint32_t x) { return (int)__popcnt(x); } +#else +static inline int term_ctz32(uint32_t x) +{ + if (!x) + return 32; + int n = 0; + while (!(x & 1u)) { + x >>= 1; + n++; + } + return n; +} +static inline int term_popcount32(uint32_t x) +{ + int n = 0; + for (; x; x &= x - 1) + n++; + return n; +} +#endif + +/* Without builtins, the 64-bit forms are built from the 32-bit ones. */ +static inline int term_ctz64(uint64_t x) +{ + uint32_t lo = (uint32_t)x; + return lo ? term_ctz32(lo) : 32 + term_ctz32((uint32_t)(x >> 32)); +} + +static inline int term_popcount64(uint64_t x) +{ + return term_popcount32((uint32_t)x) + term_popcount32((uint32_t)(x >> 32)); +} + +#endif + +/* A spin-wait hint. Compilers without one get a no-op. */ +static inline void term_cpu_relax(void) +{ +#if defined(TERM_COMPILER_GNU) +#if defined(TERM_HAVE_BUILTINS) && defined(__x86_64__) + __builtin_ia32_pause(); +#elif defined(__aarch64__) + __asm__ __volatile__("yield"); +#endif +#elif defined(TERM_COMPILER_MSVC) +#if defined(_M_X64) || defined(_M_IX86) + _mm_pause(); +#elif defined(_M_ARM64) + __yield(); +#endif +#endif +} + +/* strdup is POSIX, so the core copies strings itself. */ +static inline char *term_strdup(const char *s) +{ + size_t n = strlen(s) + 1; + char *copy = malloc(n); + if (copy) + memcpy(copy, s, n); + return copy; +} + +/* ASCII-only prefix test, as strncasecmp() does in the C locale. */ +static inline bool term_has_prefix_ci(const char *s, const char *prefix) +{ + for (; *prefix; prefix++, s++) { + unsigned char a = (unsigned char)*s; + unsigned char b = (unsigned char)*prefix; + if (a >= 'A' && a <= 'Z') + a += 'a' - 'A'; + if (b >= 'A' && b <= 'Z') + b += 'a' - 'A'; + if (a != b) + return false; + } + return true; +} + +/* Allocates size bytes that start on an align-byte boundary. align must be a + * power of two. The raw pointer is stored just before the block, so + * term_aligned_free() can release it. C11 aligned_alloc() and POSIX + * posix_memalign() are not on every toolchain. */ +static inline void *term_aligned_alloc(size_t align, size_t size) +{ + if (size > SIZE_MAX - align - sizeof(void *)) + return NULL; + void *raw = malloc(size + align + sizeof(void *)); + if (!raw) + return NULL; + uintptr_t p = ((uintptr_t)raw + sizeof(void *) + align - 1) & ~((uintptr_t)align - 1); + ((void **)p)[-1] = raw; + return (void *)p; +} + +static inline void term_aligned_free(void *p) +{ + if (p) + free(((void **)p)[-1]); +} + +#endif |
