kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

commit e87c69019725a0c08d8a1329614ac3e519758d9e
parent 85ca53b7d3dfe775399b30f30a1531c64d6c4726
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Fri,  5 Jun 2026 19:39:59 -0700

Add freestanding runtime ctype

Diffstat:
Mdriver/lib/runtime.c | 25+++++++++++++++----------
Mmk/rt.mk | 1+
Art/include/ctype.h | 20++++++++++++++++++++
Art/lib/ctype/ctype.c | 79+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/rt/cases/ctype_runtime.c | 56++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mtest/rt/smoke.c | 11+++++++++++
6 files changed, 182 insertions(+), 10 deletions(-)

diff --git a/driver/lib/runtime.c b/driver/lib/runtime.c @@ -37,8 +37,9 @@ static const char* const kRtSrcX64[] = { "assert/assert.c", "int/int.c", "int/si_div.c", "fp/fp.c", "mem/mem.c", "string/string.c", - "stdlib/stdlib.c", "stdlib/qsort.c", - "stdio/printf.c", "atomic/atomic_freestanding.c", + "ctype/ctype.c", "stdlib/stdlib.c", + "stdlib/qsort.c", "stdio/printf.c", + "atomic/atomic_freestanding.c", "cache/clear_cache.c", "kit/ifunc_init.c", "int64/int64.c", "coro/x86_64.c", "coro/coro.c", "stack/backtrace.c", @@ -62,8 +63,9 @@ static const char* const kRtSrcAarch64Elf[] = { "assert/assert.c", "int/int.c", "int/si_div.c", "fp/fp.c", "mem/mem.c", "string/string.c", - "stdlib/stdlib.c", "stdlib/qsort.c", - "stdio/printf.c", "atomic/atomic_freestanding.c", + "ctype/ctype.c", "stdlib/stdlib.c", + "stdlib/qsort.c", "stdio/printf.c", + "atomic/atomic_freestanding.c", "cache/clear_cache.c", "kit/ifunc_init.c", "int64/int64.c", "coro/aarch64.c", "coro/coro.c", "fp_tf/fp_tf.c", @@ -75,8 +77,9 @@ static const char* const kRtSrcAarch64Darwin[] = { "assert/assert.c", "int/int.c", "int/si_div.c", "fp/fp.c", "mem/mem.c", "string/string.c", - "stdlib/stdlib.c", "stdlib/qsort.c", - "stdio/printf.c", "atomic/atomic_freestanding.c", + "ctype/ctype.c", "stdlib/stdlib.c", + "stdlib/qsort.c", "stdio/printf.c", + "atomic/atomic_freestanding.c", "cache/clear_cache.c", "kit/ifunc_init.c", "int64/int64.c", "coro/aarch64.c", "coro/coro.c", "coro/aarch64_macho.s", @@ -99,8 +102,9 @@ static const char* const kRtSrcRv64Elf[] = { "assert/assert.c", "int/int.c", "int/si_div.c", "fp/fp.c", "mem/mem.c", "string/string.c", - "stdlib/stdlib.c", "stdlib/qsort.c", - "stdio/printf.c", "atomic/atomic_freestanding.c", + "ctype/ctype.c", "stdlib/stdlib.c", + "stdlib/qsort.c", "stdio/printf.c", + "atomic/atomic_freestanding.c", "cache/clear_cache.c", "kit/ifunc_init.c", "int64/int64.c", "coro/riscv64.c", "coro/coro.c", "fp_tf/fp_tf.c", @@ -117,8 +121,9 @@ static const char* const kRtSrcRv32Elf[] = { "assert/assert.c", "int/int.c", "int/si_div.c", "fp/fp.c", "mem/mem.c", "string/string.c", - "stdlib/stdlib.c", "stdlib/qsort.c", - "stdio/printf.c", "atomic/atomic_freestanding.c", + "ctype/ctype.c", "stdlib/stdlib.c", + "stdlib/qsort.c", "stdio/printf.c", + "atomic/atomic_freestanding.c", "cache/clear_cache.c", "kit/ifunc_init.c", "int32/int32.c", "coro/riscv32.c", "coro/coro.c", "stack/backtrace.c", diff --git a/mk/rt.mk b/mk/rt.mk @@ -226,6 +226,7 @@ RT_BASE_SRCS = \ rt/lib/fp/fp.c \ rt/lib/mem/mem.c \ rt/lib/string/string.c \ + rt/lib/ctype/ctype.c \ rt/lib/stdlib/stdlib.c \ rt/lib/stdlib/qsort.c \ rt/lib/stdio/printf.c \ diff --git a/rt/include/ctype.h b/rt/include/ctype.h @@ -0,0 +1,20 @@ +/* ctype.h -- minimal freestanding character classification */ +#ifndef KIT_CTYPE_H +#define KIT_CTYPE_H + +int isalnum(int c); +int isalpha(int c); +int isblank(int c); +int iscntrl(int c); +int isdigit(int c); +int isgraph(int c); +int islower(int c); +int isprint(int c); +int ispunct(int c); +int isspace(int c); +int isupper(int c); +int isxdigit(int c); +int tolower(int c); +int toupper(int c); + +#endif diff --git a/rt/lib/ctype/ctype.c b/rt/lib/ctype/ctype.c @@ -0,0 +1,79 @@ +//===-- ctype.c - kit freestanding ctype primitives ----------------------===// +// +// SPDX-License-Identifier: 0BSD +//===----------------------------------------------------------------------===// + +static unsigned int kit_ctype_byte(int c) { + return (unsigned int)(unsigned char)c; +} + +static int kit_isupper(unsigned int c) { return c >= 'A' && c <= 'Z'; } + +static int kit_islower(unsigned int c) { return c >= 'a' && c <= 'z'; } + +__attribute__((weak)) int isdigit(int c) { + unsigned int ch = kit_ctype_byte(c); + return ch >= '0' && ch <= '9'; +} + +__attribute__((weak)) int isupper(int c) { + return kit_isupper(kit_ctype_byte(c)); +} + +__attribute__((weak)) int islower(int c) { + return kit_islower(kit_ctype_byte(c)); +} + +__attribute__((weak)) int isalpha(int c) { + unsigned int ch = kit_ctype_byte(c); + return kit_isupper(ch) || kit_islower(ch); +} + +__attribute__((weak)) int isalnum(int c) { return isalpha(c) || isdigit(c); } + +__attribute__((weak)) int isblank(int c) { + unsigned int ch = kit_ctype_byte(c); + return ch == ' ' || ch == '\t'; +} + +__attribute__((weak)) int iscntrl(int c) { + unsigned int ch = kit_ctype_byte(c); + return ch <= 0x1f || ch == 0x7f; +} + +__attribute__((weak)) int isgraph(int c) { + unsigned int ch = kit_ctype_byte(c); + return ch >= 0x21 && ch <= 0x7e; +} + +__attribute__((weak)) int isprint(int c) { + unsigned int ch = kit_ctype_byte(c); + return ch >= 0x20 && ch <= 0x7e; +} + +__attribute__((weak)) int ispunct(int c) { return isgraph(c) && !isalnum(c); } + +__attribute__((weak)) int isspace(int c) { + unsigned int ch = kit_ctype_byte(c); + return ch == ' ' || (ch >= '\t' && ch <= '\r'); +} + +__attribute__((weak)) int isxdigit(int c) { + unsigned int ch = kit_ctype_byte(c); + return isdigit((int)ch) || (ch >= 'A' && ch <= 'F') || + (ch >= 'a' && ch <= 'f'); +} + +__attribute__((weak)) int tolower(int c) { + if (c == -1) return c; + unsigned int ch = kit_ctype_byte(c); + if (kit_isupper(ch)) return (int)(ch - 'A' + 'a'); + return c; +} + +__attribute__((weak)) int toupper(int c) { + if (c == -1) return c; + unsigned int ch = kit_ctype_byte(c); + if (kit_islower(ch)) return (int)(ch - 'a' + 'A'); + return c; +} diff --git a/test/rt/cases/ctype_runtime.c b/test/rt/cases/ctype_runtime.c @@ -0,0 +1,56 @@ +#include <ctype.h> + +static int same_truth(int a, int b) { return (!!a) == (!!b); } + +static int check_class(int c, int alnum, int alpha, int blank, int cntrl, + int digit, int graph, int lower, int print, int punct, + int space, int upper, int xdigit) { + return same_truth(isalnum(c), alnum) && same_truth(isalpha(c), alpha) && + same_truth(isblank(c), blank) && same_truth(iscntrl(c), cntrl) && + same_truth(isdigit(c), digit) && same_truth(isgraph(c), graph) && + same_truth(islower(c), lower) && same_truth(isprint(c), print) && + same_truth(ispunct(c), punct) && same_truth(isspace(c), space) && + same_truth(isupper(c), upper) && same_truth(isxdigit(c), xdigit); +} + +static int classify_ok(void) { + if (!check_class('A', 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1)) return 1; + if (!check_class('G', 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0)) return 2; + if (!check_class('f', 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1)) return 3; + if (!check_class('x', 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0)) return 4; + if (!check_class('7', 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1)) return 5; + if (!check_class(' ', 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0)) return 6; + if (!check_class('\t', 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0)) return 7; + if (!check_class('\n', 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0)) return 8; + if (!check_class('\v', 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0)) return 9; + if (!check_class('\f', 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0)) return 10; + if (!check_class('\r', 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0)) return 11; + if (!check_class(0x7f, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0)) return 12; + if (!check_class('!', 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0)) return 13; + if (!check_class('~', 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0)) return 14; + if (!check_class(0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)) return 15; + if (!check_class(-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)) return 16; + return 0; +} + +static int case_ok(void) { + if (tolower('A') != 'a') return 21; + if (tolower('Z') != 'z') return 22; + if (tolower('a') != 'a') return 23; + if (tolower('7') != '7') return 24; + if (tolower(-1) != -1) return 25; + if (toupper('a') != 'A') return 26; + if (toupper('z') != 'Z') return 27; + if (toupper('Z') != 'Z') return 28; + if (toupper('#') != '#') return 29; + if (toupper(-1) != -1) return 30; + return 0; +} + +int test_main(void) { + int rc = classify_ok(); + if (rc != 0) return rc; + rc = case_ok(); + if (rc != 0) return rc; + return 42; +} diff --git a/test/rt/smoke.c b/test/rt/smoke.c @@ -26,6 +26,7 @@ #define __ATOMIC_POINTER_LOCK_FREE __GCC_ATOMIC_POINTER_LOCK_FREE #endif +#include <ctype.h> #include <float.h> #include <iso646.h> #include <kit/backtrace.h> @@ -213,6 +214,15 @@ static int kit_atomic_ok(void) { return 1; } +/* ctype: header must expose classifiers and case conversion. Compile-only -- + runtime behavior is covered by test/rt/cases/ctype_runtime.c. */ +static int kit_ctype_compiles(void) { + return isalnum('A') && isalpha('z') && isblank('\t') && iscntrl('\n') && + isdigit('7') && isgraph('!') && islower('q') && isprint(' ') && + ispunct('#') && isspace('\r') && isupper('Q') && isxdigit('f') && + tolower('Z') == 'z' && toupper('z') == 'Z'; +} + /* kit/backtrace: the capture/print surface compiles and resolves. Compile-only -- smoke.c never links against a libkit_rt, so the actual walk never runs. */ static int kit_backtrace_compiles(void) { @@ -229,5 +239,6 @@ int kit_smoke_ok(void) { if (0) (void)kit_setjmp_compiles(0); if (0) (void)kit_coro_compiles(); if (0) (void)kit_backtrace_compiles(); + if (0) (void)kit_ctype_compiles(); return sum_n(3, 1, 2, 3) == 6 && kit_atomic_ok(); }