From b3e9e62599532050fc776c5e8f076915b56c2235 Mon Sep 17 00:00:00 2001 From: David Faulkner Date: Fri, 7 Aug 2026 23:40:47 -0500 Subject: Import official C23 code examples for Modern C (Jens Gustedt, 2024) - Add official C source files, Makefile, c23-fallback.h, and LICENSE - Update README.md with study mirror notice --- AUTHORS | 1 + B9-detach.c | 234 +++++++++ B9.c | 224 +++++++++ LICENSE | 27 + Makefile | 249 ++++++++++ README.md | 29 ++ Unicode-test.c | 32 ++ analyze-utf8.c | 57 +++ anonymous_function.c | 96 ++++ array_or_struct.c | 30 ++ bad.c | 23 + basic_blocks.c | 203 ++++++++ basic_blocks2.c | 233 +++++++++ bitint.c | 48 ++ build-musl | 120 +++++ c23-fallback.h | 1325 ++++++++++++++++++++++++++++++++++++++++++++++++++ cat.c | 26 + circular.c | 174 +++++++ circular.h | 123 +++++ constexpr.c | 29 ++ crash.c | 33 ++ crash.h | 37 ++ decimal-binary.c | 38 ++ embed.c | 57 +++ endianness.c | 19 + enum.c | 93 ++++ esc.c | 12 + esc.h | 156 ++++++ euclid.c | 15 + euclid.h | 24 + extern.c | 13 + fibonacci.c | 19 + fibonacci2.c | 26 + fibonacci3.c | 27 + fibonacci4.c | 30 ++ fibonacci5.c | 30 ++ fibonacci6.c | 29 ++ fibonacciCache.c | 50 ++ fibonacciRet.c | 46 ++ fp_except.c | 88 ++++ generic.c | 62 +++ generic.h | 360 ++++++++++++++ getting-started.c | 24 + heron-expanded.c | 37 ++ heron.c | 36 ++ heron_k.c | 231 +++++++++ heron_k.h | 133 +++++ life.c | 264 ++++++++++ life.h | 83 ++++ lifetime.c | 89 ++++ literals.c | 7 + locale.c | 22 + macro_trace.c | 40 ++ macro_trace.h | 302 ++++++++++++ mbstrings-main.c | 206 ++++++++ mbstrings.c | 346 +++++++++++++ mbstrings.h | 606 +++++++++++++++++++++++ numberline.c | 276 +++++++++++ precision.c | 8 + rationals.c | 218 +++++++++ rationals.h | 41 ++ reorder.h | 245 ++++++++++ sequence_point.c | 10 + shadow.c | 20 + sighandler.c | 99 ++++ sighandler.h | 101 ++++ sighandler_test.c | 70 +++ stats.c | 17 + stats.h | 213 ++++++++ strcpy.c | 17 + strtoul.c | 149 ++++++ swap.c | 144 ++++++ tendots.c | 25 + termin.c | 97 ++++ termin.h | 54 ++ terran.c | 122 +++++ test-c8.c | 54 ++ test-features.c | 368 ++++++++++++++ test-lib.c | 46 ++ test-printf.c | 341 +++++++++++++ test-reorder.c | 2 + test-strfrom.c | 26 + test_circular.c | 22 + tgstring.h | 485 ++++++++++++++++++ tgstring_test.c | 53 ++ timespec.c | 118 +++++ va_arg.c | 52 ++ volatile_atomic.c | 162 ++++++ yday.c | 41 ++ 89 files changed, 10669 insertions(+) create mode 100644 AUTHORS create mode 100644 B9-detach.c create mode 100644 B9.c create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 README.md create mode 100644 Unicode-test.c create mode 100644 analyze-utf8.c create mode 100644 anonymous_function.c create mode 100644 array_or_struct.c create mode 100644 bad.c create mode 100644 basic_blocks.c create mode 100644 basic_blocks2.c create mode 100644 bitint.c create mode 100644 build-musl create mode 100644 c23-fallback.h create mode 100644 cat.c create mode 100644 circular.c create mode 100644 circular.h create mode 100644 constexpr.c create mode 100644 crash.c create mode 100644 crash.h create mode 100644 decimal-binary.c create mode 100644 embed.c create mode 100644 endianness.c create mode 100644 enum.c create mode 100644 esc.c create mode 100644 esc.h create mode 100644 euclid.c create mode 100644 euclid.h create mode 100644 extern.c create mode 100644 fibonacci.c create mode 100644 fibonacci2.c create mode 100644 fibonacci3.c create mode 100644 fibonacci4.c create mode 100644 fibonacci5.c create mode 100644 fibonacci6.c create mode 100644 fibonacciCache.c create mode 100644 fibonacciRet.c create mode 100644 fp_except.c create mode 100644 generic.c create mode 100644 generic.h create mode 100644 getting-started.c create mode 100644 heron-expanded.c create mode 100644 heron.c create mode 100644 heron_k.c create mode 100644 heron_k.h create mode 100644 life.c create mode 100644 life.h create mode 100644 lifetime.c create mode 100644 literals.c create mode 100644 locale.c create mode 100644 macro_trace.c create mode 100644 macro_trace.h create mode 100644 mbstrings-main.c create mode 100644 mbstrings.c create mode 100644 mbstrings.h create mode 100644 numberline.c create mode 100644 precision.c create mode 100644 rationals.c create mode 100644 rationals.h create mode 100644 reorder.h create mode 100644 sequence_point.c create mode 100644 shadow.c create mode 100644 sighandler.c create mode 100644 sighandler.h create mode 100644 sighandler_test.c create mode 100644 stats.c create mode 100644 stats.h create mode 100644 strcpy.c create mode 100644 strtoul.c create mode 100644 swap.c create mode 100644 tendots.c create mode 100644 termin.c create mode 100644 termin.h create mode 100644 terran.c create mode 100644 test-c8.c create mode 100644 test-features.c create mode 100644 test-lib.c create mode 100644 test-printf.c create mode 100644 test-reorder.c create mode 100644 test-strfrom.c create mode 100644 test_circular.c create mode 100644 tgstring.h create mode 100644 tgstring_test.c create mode 100644 timespec.c create mode 100644 va_arg.c create mode 100644 volatile_atomic.c create mode 100644 yday.c diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..0a09af9 --- /dev/null +++ b/AUTHORS @@ -0,0 +1 @@ +Jens Gustedt diff --git a/B9-detach.c b/B9-detach.c new file mode 100644 index 0000000..d6ecfb6 --- /dev/null +++ b/B9-detach.c @@ -0,0 +1,234 @@ +#include "life.h" +//#include +#include +#include +//#include +#include "termin.h" +//#include "c23-fallback.h" +//#include + +// The keys that are used for cursor movement. +// Other keys that are use are +// b, B, space for birth9 +// -, + to slow down and accelerate +// q, Q to quit +#define GO_UP 'k' +#define GO_DOWN 'l' +#define GO_RIGHT ';' +#define GO_LEFT 'j' +#define GO_HOME 'h' + +// We translate escape sequences that we may receive to these standard +// characters for further processing. + +#define ESCAPE '\e' + +char const*const termin_trans[UCHAR_MAX+1] = { + [GO_UP] = ESC_UP, + [GO_DOWN] = ESC_DOWN, + [GO_RIGHT] = ESC_FRWD, + [GO_LEFT] = ESC_BKWD, + [GO_HOME] = ESC_HOME, +}; + +static int account_thread(void*); + +static +int update_thread(void* Lv) { + /* Nobody should ever wait for this thread. */ + thrd_detach(thrd_current()); + /* Delegates part of our job to an auxiliary thread */ + thrd_create(&(thrd_t){0}, account_thread, Lv); + life*restrict L = Lv; + size_t changed = 1; + size_t birth9 = 0; + while (!L->finished && changed) { + // block until there is work + mtx_lock(&L->mtx); + while (!L->finished && (L->accounted < L->iteration)) + life_wait(&L->upda, &L->mtx); + + // VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV + if (birth9 != L->birth9) life_torus(L); + life_count(L); + changed = life_update(L); + life_torus(L); + birth9 = L->birth9; + if (L->iteration != SIZE_MAX) L->iteration++; + else L->finished = true; + // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + cnd_signal(&L->acco); + cnd_signal(&L->draw); + mtx_unlock(&L->mtx); + life_sleep(1.0/L->frames); + } + /* Tells everybody that the game is over */ + L->finished = true; + ungetc('q', stdin); + return 0; +} + +static +int draw_thread(void* Lv) { + /* Nobody should ever wait for this thread. */ + thrd_detach(thrd_current()); + life*restrict L = Lv; + size_t x0 = 0; + size_t x1 = 0; + fputs(ESC_CLEAR ESC_CLRSCR, stdout); + while (!L->finished) { + // Blocks until there is work + mtx_lock(&L->mtx); + while (!L->finished + && (L->iteration <= L->drawn) + && (x0 == L->x0) + && (x1 == L->x1)) + life_wait(&L->draw, &L->mtx); + + // VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV + if (L->n0 <= 30) life_draw(L); + else life_draw4(L); + L->drawn++; + // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + mtx_unlock(&L->mtx); + + x0 = L->x0; + x1 = L->x1; + // no need to draw too quickly + life_sleep(1.0/40); + } + return 0; +} + +// This number of consecutive states that are already known (that is, +// hashed in) decides that this sequence of states is cyclic. +constexpr unsigned repetition = 10; + +static +int account_thread(void* Lv) { + /* Nobody should ever wait for this thread. */ + thrd_detach(thrd_current()); + life*restrict L = Lv; + while (!L->finished) { + // Blocks until there is work + mtx_lock(&L->mtx); + while (!L->finished && (L->accounted == L->iteration)) + life_wait(&L->acco, &L->mtx); + + // VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV + life_account(L); + if ((L->last + repetition) < L->accounted) { + L->finished = true; + } + // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + cnd_signal(&L->upda); + mtx_unlock(&L->mtx); + } + return 0; +} + +static +int input_thread(void* Lv) { + /* Nobody should ever wait for this thread. */ + thrd_detach(thrd_current()); + termin_unbuffered(); + life*restrict L = Lv; + constexpr size_t len = 32; + char command[len]; + do { + auto c = getchar(); + command[0] = c; + switch(c) { + case GO_LEFT : life_advance(L, 0, -1); break; + case GO_RIGHT: life_advance(L, 0, +1); break; + case GO_UP : life_advance(L, -1, 0); break; + case GO_DOWN : life_advance(L, +1, 0); break; + case GO_HOME : L->x0 = 1; L->x1 = 1; break; + case ESCAPE : + ungetc(termin_translate(termin_read_esc(len, command)), stdin); + continue; + case '+': if (L->frames < 128) L->frames++; continue; + case '-': if (L->frames > 1) L->frames--; continue; + case ' ': + case 'b': + case 'B': + mtx_lock(&L->mtx); + // VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV + life_birth9(L); + // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + mtx_unlock(&L->mtx); + break; + case 'q': + case 'Q': + case EOF: goto FINISH; + } + cnd_signal(&L->draw); + } while (!(L->finished || feof(stdin))); + FINISH: + L->finished = true; + return 0; +} + +enum { n = 60, m = 160 }; + +bool M[n][m] = { + { }, + { }, + { 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0}, + { 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0}, + { 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0}, + { 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0}, + { 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0}, + { }, + { }, + { 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0 }, + { 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0}, + { 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, }, + { 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, }, + { 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0 }, + { 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0}, + { 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0}, + { 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0}, + { 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0}, + { 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0 }, + { }, + { }, + { 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0}, + { 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0}, + { 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0 }, + { 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0 }, + { 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0 }, + { }, +}; + +life L = LIFE_INITIALIZER; + +void B9_atexit(void) { + /* Puts the board in a nice final picture */ + L.iteration = L.last; + life_draw(&L); + life_destroy(&L); +} + +int main(int argc, char* argv[argc+1]) { + /* Uses command-line arguments for the size of the board */ + size_t n0 = 30; + size_t n1 = 80; + if (argc > 1) n0 = strtoull(argv[1], nullptr, 0); + if (argc > 2) n1 = strtoull(argv[2], nullptr, 0); + /* Create an object that holds the game's data. */ + life_init(&L, n0, n1, M); + atexit(B9_atexit); + /* Creates four threads that operate on the same object and + discards their IDs */ + thrd_create(&(thrd_t){0}, update_thread, &L); + thrd_create(&(thrd_t){0}, draw_thread, &L); + thrd_create(&(thrd_t){0}, input_thread, &L); + /* Ends this thread nicely and lets the threads go on nicely */ + thrd_exit(0); +} diff --git a/B9.c b/B9.c new file mode 100644 index 0000000..c0ef46e --- /dev/null +++ b/B9.c @@ -0,0 +1,224 @@ +#include "life.h" +//#include +#include +#include +//#include +#include "termin.h" +//#include "c23-fallback.h" +//#include + +// The keys that are used for cursor movement +// Other keys that are used are: +// b, B, space for birth9 +// -, + to slow down and accelerate +// q, Q to quit +#define GO_UP 'k' +#define GO_DOWN 'l' +#define GO_RIGHT ';' +#define GO_LEFT 'j' +#define GO_HOME 'h' + +// We translate escape sequences that we may receive to these standard +// characters for further processing. + +#define ESCAPE '\e' + +char const*const termin_trans[UCHAR_MAX+1] = { + [GO_UP] = ESC_UP, + [GO_DOWN] = ESC_DOWN, + [GO_RIGHT] = ESC_FRWD, + [GO_LEFT] = ESC_BKWD, + [GO_HOME] = ESC_HOME, +}; + +static +int update_thread(void* Lv) { + life*restrict L = Lv; + size_t changed = 1; + size_t birth9 = 0; + while (!L->finished && changed) { + // Blocks until there is work + mtx_lock(&L->mtx); + while (!L->finished && (L->accounted < L->iteration)) + life_wait(&L->upda, &L->mtx); + + // VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV + if (birth9 != L->birth9) life_torus(L); + life_count(L); + changed = life_update(L); + life_torus(L); + birth9 = L->birth9; + if (L->iteration != SIZE_MAX) L->iteration++; + else L->finished = true; + // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + cnd_signal(&L->acco); + cnd_signal(&L->draw); + mtx_unlock(&L->mtx); + life_sleep(1.0/L->frames); + } + return 0; +} + +static +int draw_thread(void* Lv) { + life*restrict L = Lv; + size_t x0 = 0; + size_t x1 = 0; + fputs(ESC_CLEAR ESC_CLRSCR, stdout); + while (!L->finished) { + // Blocks until there is work + mtx_lock(&L->mtx); + while (!L->finished + && (L->iteration <= L->drawn) + && (x0 == L->x0) + && (x1 == L->x1)) { + life_wait(&L->draw, &L->mtx); + } + // VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV + if (L->n0 <= 30) life_draw(L); + else life_draw4(L); + L->drawn++; + // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + mtx_unlock(&L->mtx); + + x0 = L->x0; + x1 = L->x1; + // No need to draw too quickly + life_sleep(1.0/40); + } + return 0; +} + +// This number of consecutive states that are already known (that is, +// hashed in) decides that this sequence of states is cyclic +constexpr unsigned repetition = 10u; + +static +int account_thread(void* Lv) { + life*restrict L = Lv; + while (!L->finished) { + // Blocks until there is work + mtx_lock(&L->mtx); + while (!L->finished && (L->accounted == L->iteration)) + life_wait(&L->acco, &L->mtx); + + // VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV + life_account(L); + if ((L->last + repetition) < L->accounted) { + L->finished = true; + } + // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + cnd_signal(&L->upda); + mtx_unlock(&L->mtx); + } + return 0; +} + +static +int input_thread(void* Lv) { + termin_unbuffered(); + life*restrict L = Lv; + constexpr size_t len = 32; + char command[len]; + do { + auto c = getchar(); + command[0] = c; + switch(c) { + case GO_LEFT : life_advance(L, 0, -1); break; + case GO_RIGHT: life_advance(L, 0, +1); break; + case GO_UP : life_advance(L, -1, 0); break; + case GO_DOWN : life_advance(L, +1, 0); break; + case GO_HOME : L->x0 = 1; L->x1 = 1; break; + case ESCAPE : + ungetc(termin_translate(termin_read_esc(len, command)), stdin); + continue; + case '+': if (L->frames < 128) L->frames++; continue; + case '-': if (L->frames > 1) L->frames--; continue; + case ' ': + case 'b': + case 'B': + mtx_lock(&L->mtx); + // VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV + life_birth9(L); /*@\label{lab:birth9}*/ + // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + cnd_signal(&L->draw); + mtx_unlock(&L->mtx); + continue; + case 'q': + case 'Q': + case EOF: goto FINISH; + } + cnd_signal(&L->draw); /*@\label{lab:signal}*/ + } while (!(L->finished || feof(stdin))); + FINISH: + L->finished = true; + return 0; +} + +enum { n = 60, m = 160 }; + +bool M[n][m] = { + { }, + { }, + { 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0}, + { 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0}, + { 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0}, + { 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0}, + { 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0}, + { }, + { }, + { 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0 }, + { 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0}, + { 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, }, + { 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, }, + { 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0 }, + { 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0}, + { 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0}, + { 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0}, + { 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0}, + { 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0 }, + { }, + { }, + { 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0}, + { 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0}, + { 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0 }, + { 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0 }, + { 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0 }, + { }, +}; + +int main(int argc, char* argv[argc+1]) { + /* Uses command-line arguments for the size of the board */ + size_t n0 = 30; + size_t n1 = 80; + if (argc > 1) n0 = strtoull(argv[1], nullptr, 0); + if (argc > 2) n1 = strtoull(argv[2], nullptr, 0); + /* Create an object that holds the game's data. */ + life L = LIFE_INITIALIZER; + life_init(&L, n0, n1, M); + /* Creates four threads that all operate on that same object + and collects their IDs in "thrd" */ + thrd_t thrd[4]; + thrd_create(&thrd[0], update_thread, &L); + thrd_create(&thrd[1], draw_thread, &L); + thrd_create(&thrd[2], input_thread, &L); + thrd_create(&thrd[3], account_thread, &L); + /* Waits for the update thread to terminate */ + thrd_join(thrd[0], nullptr); + /* Tells everybody that the game is over */ + L.finished = true; + ungetc('q', stdin); + /* Waits for the other threads */ + thrd_join(thrd[1], nullptr); + thrd_join(thrd[2], nullptr); + thrd_join(thrd[3], nullptr); + /* Puts the board in a nice final picture */ + L.iteration = L.last; + life_draw(&L); + life_destroy(&L); +} diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..40e45ce --- /dev/null +++ b/LICENSE @@ -0,0 +1,27 @@ +The C source code provided with this book is licensed under the +following standard MIT license: + +---------------------------------------------------------------------- +Copyright © 2015-2024 Jens Gustedt + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. +---------------------------------------------------------------------- diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..2cd989c --- /dev/null +++ b/Makefile @@ -0,0 +1,249 @@ + +# A file to describe dependencies for compilations used by the POSIX +# "make" utility, see http://pubs.opengroup.org/onlinepubs/9699919799/ + +# Ensure that we use strict POSIX make rules. +.POSIX : + +# empty the list of suffixes +.SUFFIXES : +# C files, sources +.SUFFIXES : .c .h +# products, object files, make dependencies, assembler +.SUFFIXES : .o .d .s + +# The choice of the compiler. Commonly available on POSIX systems are +# compilers called clang, gcc, icc or c99. Try them in this order if +# the following does not work for you. +# +# Here we used a "wrapper" for gcc that comes with the "musl" C +# library. This has the advatage to provide full support for C11 +# threads that we need for the "experience" level. On some Linux +# distributions musl is available as packages 'musl', 'musl-dev', +# 'musl-tools'. Other Linux distributions such as Alpine directly ship +# with musl as their C library. +CC ?= /usr/local/musl/bin/musl-gcc +#CC = clang +#CC = gcc +#CC = icc +#CC = c99 + +# A collection of typical flags on a Linux box with gcc or clang + +# First a choice of a C standard to which your code is conforming. The +# following should be the default for a recent compiler (2023). Other +# options could be -std=c11 or -std=gnu11. +STD = -std=c2x + +# Warning options. It is important that the compiler gives you all +# that he knows. +WAR = -Wall + +# Optimization options. The ones here are my favorite, maximal +# optimization for the machine on which we are compiling and maximal +# separation of functions and data into different sections. +OPT ?= -O3 -march=native -fdata-sections -ffunction-sections + +# For the gcc and friends the addition of this flag produces a make +# dependency file with suffix .d +DEP = -MD + +CFLAGS = ${SILENT} ${OPT} ${WAR} ${STD} ${INC} ${DEP} + +# The linker flags. Here this corresponds to eliminate all of the +# collected sections that are finally not needed in the executable. +LDFLAGS = -Wl,--gc-sections +# in the link step we need our own library +LDLIBS = -L. -lmodernC +# sometime we will need the "math" library and also "real time" +LDLIBS += -lm -lrt + +ifdef CEDRO +export CEDRO_CC := ${CC} -D__CEDRO__ -std=c2x -x c - +export CCC := ${CEDRO}/bin/cedrocc +else +export CCC := ${CC} +endif + +# C sources that produce an executable +ROURCES = \ + basic_blocks.c \ + basic_blocks2.c \ + bitint.c \ + cat.c \ + crash.c \ + endianness.c \ + euclid.c \ + extern.c \ + embed.c \ + enum.c \ + fibonacci2.c \ + fibonacci3.c \ + fibonacci4.c \ + fibonacci5.c \ + fibonacci6.c \ + fibonacci.c \ + fibonacciCache.c \ + fibonacciRet.c \ + fp_except.c \ + generic.c \ + getting-started.c \ + heron.c \ + heron-expanded.c \ + heron_k.c \ + lifetime.c \ + locale.c \ + macro_trace.c \ + mbstrings-main.c \ + numberline.c \ + precision.c \ + rationals.c \ + shadow.c \ + sighandler_test.c \ + strcpy.c \ + strtoul.c \ + tendots.c \ + terran.c \ + test_circular.c \ + test-features.c \ + test-printf.c \ + timespec.c \ + va_arg.c \ + yday.c + + +# Comment this out if your platform does not support the C11 thread model +TOURCES = B9.c B9-detach.c + +SOURCES = ${ROURCES} ${TOURCES} + +# Translation units (TU) that provide functionality that can be used +# by others. These are collected into the library libmodernC.a +RSOURCES = \ + circular.c \ + literals.c \ + mbstrings.c \ + sighandler.c \ + stats.c + +# Comment this out if your platform does not support the C11 thread model +TSOURCES = esc.c life.c termin.c + +LSOURCES = ${RSOURCES} ${TSOURCES} + +# TU that just prove a point. +OSOURCES = \ + anonymous_function.c \ + atomic_or_struct.c \ + constexpr.c \ + volatile_atomic.c + +# Examples of bad TU that produce diagnostics when compiled +BSOURCES = \ + getting-started-badly.c \ + tgstring_test.c + +# The following lines use the above to create new lists of files where +# the .c suffixes are replaced. +OBJECTS = ${SOURCES:.c=.o} + +LOBJECTS = ${LSOURCES:.c=.o} + +BOBJECTS = ${BSOURCES:.c=.o} + +OOBJECTS = ${OSOURCES:.c=.o} + +BINARIES = ${SOURCES:.c=} + +DEPENDS = ${SOURCES:.c=.d} ${LSOURCES:.c=.d} + +# This implicit rule tells how make is supposed to produce an object +# file (.o suffix) from a C file (.c suffix). This is needed to +# produce an executable file from your program. +# +# Here the make-special variable $< expands and to the dependency (the +# .o object file). +.c.o : + ${CCC} ${CFLAGS} -o $@ -c $< + +# This implicit rule tells how make is supposed to produce an assembly +# file (.s suffix) from a C file (.c suffix). This is only needed if +# you want to inspect what your compiler produces from your code. +.c.s : + ${CC} ${CFLAGS} -o $@ -S $< + +# This implicit rule tells make that the dependency file (.d suffix) +# itself depends on the object file. This is a bit of a lie, because +# we will produce them along (see DEP above) when normally compiling. +.d.o : + +# This implicit rule tells make that the final executable file (no +# suffix) depends on the object file (.o suffix). If your platform has +# a suffix for executables, e.g .exe, you'd have to add it here. On +# POSIX systems it is common to use the same compiler command (here +# ${CC}) also for linking. +# +# The additional make-special variable $@ expands to the target name +# (the executable). +.o : + ${CC} -o $@ ${LDFLAGS} $< ${LDLIBS} + +# This is the first explicit target, so per default make will try to +# produce it. It is only here for the dependencies to all our +# executables, so all of these will be created, if possible. Since +# this target has no rule of its own, a file named "binaries" will +# never be produced. +binaries : ${BINARIES} + +# This rule tells make that all our executable files also depend on +# the archive 'libmodernC.a'. +${BINARIES} : libmodernC.a + +# That archive depends on all the "LOBJECTS" +libmodernC.a : ${LOBJECTS} + ${AR} crf $@ $^ + +# These target need some special command line arguments so they have +# their own production line. +tgstring.o : tgstring.h + ${CC} -D_TG_INLINE= -c -x c ${CFLAGS} -o tgstring.o tgstring.h + +embed : embed.o + ${CC} ${CFLAGS} -o $@ ${LDFLAGS} $< ${LDLIBS} + +# An alternative target that just has all sources compiled. +objects : ${OBJECTS} ${LOBJECTS} ${OOBJECTS} + + +# Compilation of the "bad" objects will produce a lot of +# diagnostics. These are for demonstrative purposes, only. +bad : ${BOBJECTS} + +# Linkage of the "bad" objects may produce executables that have +# unforseeable effects if they are run. +# +# ***** DO NOT USE THESE **** +# +# These are for demonstrative purposes, only. +verybad : ${BOBJECTS:.o=} + +# They also depend on the library. +${BOBJECTS:.o=} : libmodernC.a + +# Cleanup the directory from all files that we produce +clean : + rm -f libmodernC.a ${DEPENDS} ${OBJECTS} ${LOBJECTS} ${OOBJECTS} ${BOBJECTS} ${BINARIES} ${BOBJECTS:.o=} + +# Include all the produced dependency files. In POSIX, it is +# unspecified if "include" works with more than one file, but usually +# it does. +# +# The "-" in front tells make not to complain, if the dependency files +# to not exist, yet. If they are missing, make looks into its rules to +# see how to produce them, produces them and reads them. +# +# If the initial generation of make dependency files does not work for +# you, you could launch a compilation of all objects with +# +# make -i +-include ${DEPENDS} diff --git a/README.md b/README.md new file mode 100644 index 0000000..a7e5989 --- /dev/null +++ b/README.md @@ -0,0 +1,29 @@ +# Modern C (C23 Edition) — Code & Study Mirror + +This repository contains the official code examples accompanying Jens Gustedt's *Modern C* (C23 Edition, Manning, 2024). I am currently studying this text, and custom build configurations, notes, or experimental branches may be added over time. + +--- + +# Code examples for the book Modern C + +This is a collection of code snippets that come with the book + + Jens Gustedt, /Modern C/, C23 edition, Manning, 2024 + +They have probably not much value outside that context. + +The following files that come with this distribution have more +information: + + - Makefile, for hints about how to compile the code + - c23-fallback.h for the transition to C23 + - LICENSE, for the specific license of this code + +You may find additional resources for the book at + +https://gustedt.gitlabpages.inria.fr/modern-c/ + +If you cannot or do not want to purchase a copy of the book, you may +obtain a CC licensed version of the C17 edition at + +https://hal.inria.fr/hal-02383654 diff --git a/Unicode-test.c b/Unicode-test.c new file mode 100644 index 0000000..5df61e3 --- /dev/null +++ b/Unicode-test.c @@ -0,0 +1,32 @@ +/** + ** @file Test handling of different Unicode letters in identifiers. + **/ + +#include "c23-fallback.h" + +double Å = 1; // error, Ångström not normalized +double Å = 2; // ok, A with circle +double A\u030A = 3; // error, combination not normalized +double Ω = 1; // error, Ohm not normalized +double Ω = 2; // ok, Greek capital omega +double K = 1; // error, Kelvin not normalized +double K = 2; +double fi = 1; // valid, fi does not decompose into f and i +double fi = 2; // valid, different identifier +typedef double ℝ; // valid +typedef unsigned long long ℕ; // valid +#define ℜ(Z) real(Z) // valid +#define ℑ(Z) imag(Z) // valid +ℕ №; // error: Numero sign is not a letter +ℕ ℓ; // valid, ell sign is a letter +double ʃ(double a, double b, double φ(double)); // IPA is also acceptable +typedef double 각; // valid, Hangul generally maps to itself +각 α = 1; +enum : bool { 응 = true, wɛ = true, }; +char const 얜읐[] = "얜읐"; +int a١ = 1; // valid, Arabic digits are considered different +int a1 = 2; +int a⁰ = 1; // non-portable, gcc and clang extension +int a𝟬 = 1; // valid, but not a good idea +int a0 = 2; +unsigned Ⅾ; // valid, roman digit 500, really a bad idea diff --git a/analyze-utf8.c b/analyze-utf8.c new file mode 100644 index 0000000..8e8f881 --- /dev/null +++ b/analyze-utf8.c @@ -0,0 +1,57 @@ +#include "mbstrings.h" +#include +#include +#include + +int main(void) { + // Make sure to have the platform's mb encoding on input. + setlocale(LC_CTYPE, ""); + // Holds the state of input/output buffering. + mbstate_t st = { }; + // collects the input mb sequence + char ib[23]; + // collects the current UTF-8 mb sequence + char8_t ob[5] = { }; + // the number of input characters for the current code point + size_t in = 0; + while (fgets(ib, sizeof(ib), stdin)) { + // Run through the current line. The last character is + // always reserved for the string terminator. + for (char* p = ib; (p-ib) < sizeof(ib)-1;) { + size_t const n = sizeof(ib)-1-(p-ib); + size_t const r = mbrtoc8(ob, p, n, &st); + switch (r) { // Handle the special cases. + case mbincomplete: p += n; in += n; continue; + case 0: case mbstored: case mbinvalid: goto INVAL; + } + p += r; in += r; + char8_t* cont = ob+1; // first character is already stored + while (mbrtoc8(cont, "", 1, &st) == mbstored) { + cont++; + } + // Now we have the whole UTF-8. Analyze the result. + printf("%s", ((cont-ob) == 1) ? "ASCII\t" : "UTF-8\t"); + for (char8_t* o = ob; o < cont; ++o) { + printf("|%02hhx", *o); + } + // fgets stopped at an end of a line + if (*ob == u8'\n') { + puts("|\t~ eol"); + in = 0; + break; + } else if (in == (cont-ob)) { + printf("|\t~ '%s'\n", ob); + } else { + printf("|\t%zu→%tu\n", in, (cont-ob)); + } + in = 0; + } + if (*ob != u8'\n') { + fputs("incomplete line\n", stderr); + } + } + return EXIT_SUCCESS; + INVAL: + fputs("input error, exiting\n", stderr); + return EXIT_FAILURE; +} diff --git a/anonymous_function.c b/anonymous_function.c new file mode 100644 index 0000000..8144e6f --- /dev/null +++ b/anonymous_function.c @@ -0,0 +1,96 @@ +#include + +/** + ** @file + ** + ** This plays with gcc features to define a weird macro that emulates + ** anonymous functions that can be used as closures. Don't use that + ** seriously, this is only meant to prove that this is syntactically + ** not a big deal, and that the ideas necessary for it can be found + ** in modern compilers. + ** + **/ + +/** + ** @def ANONYMOUS(RET) + ** @brief Use this to define an anonymous function with return type @a RET. + ** + ** The @c ... argument list is the parameter list of the + ** function. This the expects the code of the function in ({ + ** }) parenthesis. A simple example would be + ** + ** @code + ** // Function header + ** ANONYMOUS(int) (double a, unsigned b) + ** // Function body + ** ({ + ** return printf("we found %g for %u, average %g\n", a, b, a/b); + ** }) + ** // Function arguments + ** (x, 23); + ** @endcode + ** + ** Of course you would usually put such things inside a macro. See + ** MAX() for a more complicated example. + **/ +#ifdef __clang__ +# define ANONYMOUS(RET) (^ RET ANON_PART2 +# define ANON_PART2(...) (__VA_ARGS__) ANON_PART3 +# define ANON_PART3(...) __VA_ARGS__) +#else +# define ANONYMOUS(RET) ({ RET __anon_func ANON_PART2 +# define ANON_PART2(...) (__VA_ARGS__) ANON_PART3 +# define ANON_PART3(...) __VA_ARGS__ __anon_func; }) +#endif + +#define MAXU(A, B) \ +ANONYMOUS(uintmax_t) (uintmax_t a, uintmax_t b) \ +({ \ + if (a < b) return b; \ + else return a; \ +}) \ +((A), (B)) + +#define MAX(A, B) \ +/* function header */ \ + /* return type */ \ +ANONYMOUS(__typeof__(1 ? (A) : (B))) \ + /*and then argument list */ \ +(__typeof__(A) a, __typeof__(B) b) \ +/* function body */ \ +({ \ + if (a < b) return b; \ + else return a; \ + }) \ +/* function call */ \ +((A), (B)) + +/** + ** Test to see if that even works when such functions are nested. + **/ + +typedef void df(unsigned); + +df* Df[5] = { }; + +void g(unsigned level, unsigned depth) { + Df[depth%5] = + ANONYMOUS(void) (register unsigned a) + ({ + printf("from level %u, depth %u found in position %u\n", + level, depth, + MAX(2*a, depth) + ); + return; + }); + if (level) + g(level-1, depth+1); + else + for (unsigned i = 0; i < 5; ++i) + if (Df[i]) Df[i](i); +} + + +int main(int argc, [[maybe_unused]] char* argv[]) { + g(argc, 0); +} diff --git a/array_or_struct.c b/array_or_struct.c new file mode 100644 index 0000000..3190fe1 --- /dev/null +++ b/array_or_struct.c @@ -0,0 +1,30 @@ +#include + +#ifdef USE_STRUCT + typedef struct two two; + struct two { double e1; double e2; }; +# define FRST(X) ((X).e1) +# define SEC(X) ((X).e2) +#else + typedef double two[2]; +# define FRST(X) ((X)[0]) +# define SEC(X) ((X)[1]) +#endif + +void func0(void) { + for (two sp = { 1, 1, }; + FRST(sp) + SEC(sp) < 10; + FRST(sp) += SEC(sp)) { + printf("two values %g %g\n", FRST(sp), SEC(sp)); + SEC(sp) += (FRST(sp) + SEC(sp))/2; + } +} + +void func1(void) { + for (register two sp = { 1, 1, }; + FRST(sp) + SEC(sp) < 10; + FRST(sp) += SEC(sp)) { + printf("two values %g %g\n", FRST(sp), SEC(sp)); + SEC(sp) += (FRST(sp) + SEC(sp))/2; + } +} diff --git a/bad.c b/bad.c new file mode 100644 index 0000000..7253662 --- /dev/null +++ b/bad.c @@ -0,0 +1,23 @@ +/* This may look like nonsense, but really is -*- mode: C -*- */ + +/* The main thing that this program does. */ +void main() { + // Declarations + int i; + double A[5] = { + 9.0, + 2.9, + 3.E+25, + .00007, + }; + + // Doing some work + for (i = 0; i < 5; ++i) { + printf("element %d is %g, \tits square is %g\n", /*@\label{printf-start-badly}*/ + i, + A[i], + A[i]*A[i]); /*@\label{printf-end-badly}*/ + } + + return 0; /*@\label{main-return-badly}*/ +} diff --git a/basic_blocks.c b/basic_blocks.c new file mode 100644 index 0000000..e0fcb05 --- /dev/null +++ b/basic_blocks.c @@ -0,0 +1,203 @@ +#include "sighandler.h" +#include +#include + +/* + * Because the parser is implemented here, and doesn't take care of + * strings, character constants, or comments, we hide the { } with + * compile time constants. + */ +constexpr char left = '{'; +constexpr char right = '}'; + +/** + ** @brief Initial string to indent a line + **/ +constexpr char head[] = ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>| "; + +/** + ** @brief Skips any number of spaces, tabs, or newlines in @a s + **/ +static +char const* skipspace(char const s[static 1]) { + while (isspace(s[0])) { + ++s; + } + return s; +} + +/** + ** @brief Exceptional states of the parse algorithm + **/ +enum state { + execution = 0, //!< Normal execution + plusL, //!< Too many left braces + plusR, //!< Too many right braces + tooDeep, //!< Nesting too deep to handle + eofOut, //!< End of output + interrupted, //!< Interrupted by a signal +}; + +/** + ** @brief Outputs end of line and eats all subsequent spaces on input + **/ +static +char const* end_line(char const s[static 1], jmp_buf jmpTarget) { + if (putchar('\n') == EOF) longjmp(jmpTarget, eofOut); + return skipspace(s); +} + + + +/** + ** @brief Keeps track of the last signal that interrupted us + **/ +static sig_atomic_t volatile interrupt = 0; + +static +char const* descend(char const* act, + unsigned dp[restrict static 1], // Bad + size_t len, char buffer[static len], + jmp_buf jmpTarget) { + if (dp[0]+3 > sizeof head) longjmp(jmpTarget, tooDeep); + ++dp[0];/*@\label{lab:incr-rec}*/ + NEW_LINE: // Loops on output + while (!act || !act[0]) { // Loops for input + if (interrupt) longjmp(jmpTarget, interrupted); + act = skipspace(fgets(buffer, len, stdin)); + if (!act) { // End of stream + if (dp[0] != 1) longjmp(jmpTarget, plusL); + else goto ASCEND; + } + } + fputs(&head[sizeof head - (dp[0] + 2)], stdout); // Header + + for (; act && act[0]; ++act) { // Remainder of the line + switch (act[0]) { /*@\label{lab:switch-char}*/ + case left: // Descends on left brace + act = end_line(act+1, jmpTarget); + act = descend(act, dp, len, buffer, jmpTarget);/*@\label{lab:descend}*/ + act = end_line(act+1, jmpTarget); + goto NEW_LINE; + case right: // Returns on right brace + if (dp[0] == 1) longjmp(jmpTarget, plusR); + else goto ASCEND; + default: // Prints char and goes on + putchar(act[0]); + } + } + goto NEW_LINE; + ASCEND: + --dp[0];/*@\label{lab:decr-rec}*/ + return act; +} + +constexpr unsigned maxline = 256; + +void basic_blocks(void) { + char buffer[maxline]; + unsigned depth = 0; + char const* format = + "All matching %0.0d'%c' '%c' pairs have been closed correctly\n"; + jmp_buf jmpTarget; + switch (setjmp(jmpTarget)) { + case 0: + descend(nullptr, &depth, maxline, buffer, jmpTarget); + break; + case plusL: + format = + "Warning: %d '%c' have not been closed properly (expected '%c')\n"; + break; + case plusR: + format = + "Error: closing too many (%d) '%c' parenthesis with additional '%c'\n"; + break; + case tooDeep: + format = + "Error: nesting (%d) of '%c' '%c' constructs is too deep\n"; + break; + case eofOut: + format = + "Error: EOF for stdout at %d open '%c', expecting same amount of '%c'\n"; + break; + case interrupted: + format = + "Interrupted at level %d of '%c' '%c' nesting\n"; + break; + default:; + format = + "Error: unknown error within (%d) '%c' '%c' constructs\n"; + } + fflush(stdout); + fprintf(stderr, format, depth, left, right); + if (interrupt) { + SH_PRINT(stderr, interrupt, + "is somebody trying to kill us?"); + raise(interrupt); + } +} + +/** + ** @brief A minimal signal handler + ** + ** After updating the signal count, for most signals this + ** simply stores the signal value in "interrupt" and returns. + **/ +static void signal_handler(int sig) { + sh_count(sig); + switch (sig) { + case SIGTERM: quick_exit(EXIT_FAILURE); + case SIGABRT: _Exit(EXIT_FAILURE); +#ifdef SIGCONT + // continue normal operation + case SIGCONT: return; +#endif + default: + /* reset the handling to its default */ + signal(sig, SIG_DFL); + interrupt = sig; + return; + } +} + +// Will point to the command-line arguments +static char** lastOpen = nullptr; + +// Checks if we were in the middle of an operation +void doAtExit(void) { + if (lastOpen && lastOpen[0]) { + fprintf(stderr, "\n***********\nabnormal exit, last open file was %s\n", lastOpen[0]); + } +} + +int main(int argc, char* argv[argc+1]) { + // Ensures that stdout is line buffered + if (setvbuf(stdout, nullptr, _IOLBF, maxline + sizeof head + 2)) { + fputs("we could not establish line buffering for stdout, terminating.", stderr); + return EXIT_FAILURE; + } + + // Establishes exit handlers + atexit(doAtExit); + at_quick_exit(doAtExit); + + // Establishes signal handlers + for (unsigned i = 1; i < sh_known; ++i) + sh_enable(i, signal_handler); + + // If there are no command-line arguments, reads from stdin + lastOpen = argv; + if (argc < 2) goto RUN; + + // Runs basic_blocks for each command-line argument + for (++lastOpen; lastOpen[0]; ++lastOpen) { + if (!freopen(lastOpen[0], "r", stdin)) { + perror(lastOpen[0]); + return EXIT_FAILURE; + } + printf("++++++++++ %s +++++++++++\n", lastOpen[0]); + RUN: + basic_blocks(); + } + return EXIT_SUCCESS; +} diff --git a/basic_blocks2.c b/basic_blocks2.c new file mode 100644 index 0000000..b47a227 --- /dev/null +++ b/basic_blocks2.c @@ -0,0 +1,233 @@ +#include "sighandler.h" +#include +#include +#include +#include +#include + +/* + * Because the parser is implemented here, and doesn't take care of + * strings, character constants, or comments, we hide the { } with + * compile time constants. + */ +constexpr char left = '{'; +constexpr char right = '}'; + +/** + ** @brief Initial string to indent a line + **/ +constexpr char head[] = ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>| "; + +/** + ** @brief Skips any number of spaces, tabs, or newlines in @a s + **/ +static +char const* skipspace(char const s[static 1]) { + while (isspace(s[0])) { + ++s; + } + return s; +} + +/** + ** @brief Exceptional states of the parse algorithm + **/ +enum { + execution = 0, //!< Normal execution + plusL, //!< Too many left braces + plusR, //!< Too many right braces + tooDeep, //!< Nesting too deep to handle + eofOut, //!< End of output + interrupted, //!< Interrupted by a signal +}; + +/** + ** @brief Outputs end of line and eats all subsequent spaces on input + **/ +static +char const* end_line(char const s[static 1], jmp_buf jmpTarget) { + if (putchar('\n') == EOF) longjmp(jmpTarget, eofOut); + return skipspace(s); +} + +/** + ** @brief Keeps track of the last signal that interrupted us + **/ +static sig_atomic_t volatile interrupt = 0; + +/** + ** @brief Keep track of the depth at which an error was detected. + **/ +static unsigned volatile deepest = 0; + +static +char const* descend1(char const act[static 1], + unsigned depth, + size_t len, char buffer[restrict static len], + jmp_buf jmpTarget) { + if (depth+2 > sizeof head) longjmp(jmpTarget, tooDeep); + NEW_LINE: // loop on output + while (!act[0]) { // loop for input + if (interrupt) longjmp(jmpTarget, interrupted); + act = fgets(buffer, len, stdin); + if (!act) { // end of stream + deepest = depth; + longjmp(jmpTarget, plusL); + } + act = skipspace(act); + } + // act is never 0 from here on + fputs(&head[sizeof head - (depth + 3)], stdout); // header + for (; act[0]; ++act) { // remainder of the line + switch (act[0]) { + case left: // descend on left brace + act = end_line(act+1, jmpTarget); + act = descend1(act, depth+1, len, buffer, jmpTarget); + act = end_line(act+1, jmpTarget); + goto NEW_LINE; + case right: // return on right brace + return act; + default: // print char and go on + putchar(act[0]); + } + } + goto NEW_LINE; +} + +static +void descend0(size_t len, char buffer[static len], jmp_buf jmpTarget) { + char const* act = nullptr; + NEW_LINE: // loop on output + do { // loop for input + if (interrupt) longjmp(jmpTarget, interrupted); + act = fgets(buffer, len, stdin); + if (!act) return; + act = skipspace(act); + } while (!act[0]); + // act is never 0 from here on + fputs(&head[sizeof head - 3], stdout); // header + for (; act[0]; ++act) { // remainder of the line + switch (act[0]) { + case left: // descend on left brace + act = end_line(act+1, jmpTarget); + act = descend1(act, 1, len, buffer, jmpTarget); + act = end_line(act+1, jmpTarget); + goto NEW_LINE; + case right: // right brace is an error + longjmp(jmpTarget, plusR); + default: // print char and go on + if (putchar(act[0]) == EOF) longjmp(jmpTarget, eofOut); + } + } + goto NEW_LINE; +} + +enum { maxline = 256 }; + +void basic_blocks(void) { + char buffer[maxline]; + char const* format = + "All matching %0.0d'%c' '%c' pairs have been closed correctly\n"; + jmp_buf jmpTarget; + switch (setjmp(jmpTarget)) { + case 0: + deepest = 0; + descend0(maxline, buffer, jmpTarget); + break; + case plusL: + format = + "Warning: %d '%c' have not been closed properly (expected '%c')\n"; + break; + case plusR: + format = + "Error: closing too many (%d) '%c' parenthesis with additional '%c'\n"; + break; + case tooDeep: + format = + "Error: nesting (%d) of '%c' '%c' constructs is too deep\n"; + break; + case eofOut: + format = + "Error: EOF for stdout at %d open '%c', expecting same amount of '%c'\n"; + break; + case interrupted: + format = + "Interrupted at level %d of '%c' '%c' nesting\n"; + break; + default:; + format = + "Error: unknown error within (%d) '%c' '%c' constructs\n"; + } + fflush(stdout); + fprintf(stderr, format, deepest, left, right); + if (interrupt) { + SH_PRINT(stderr, interrupt, + "is somebody trying to kill us?"); + raise(interrupt); + } +} + +/** + ** @brief A minimal signal handler + ** + ** After updating the signal count, for most signals this + ** simply stores the signal value in "interrupt" and returns. + **/ +static void signal_handler(int sig) { + sh_count(sig); + switch (sig) { + case SIGTERM: quick_exit(EXIT_FAILURE); + case SIGABRT: _Exit(EXIT_FAILURE); +#ifdef SIGCONT + // continue normal operation + case SIGCONT: return; +#endif + default: + /* reset the handling to its default */ + signal(sig, SIG_DFL); + interrupt = sig; + return; + } +} + +// Will point to the command-line arguments +static char** lastOpen = nullptr; + +// Checks if we were in the middle of an operation +void doAtExit(void) { + if (lastOpen && lastOpen[0]) { + fprintf(stderr, "\n***********\nabnormal exit, last open file was %s\n", lastOpen[0]); + } +} + +int main(int argc, char* argv[argc+1]) { + // Ensures that stdout is line buffered + if (setvbuf(stdout, nullptr, _IOLBF, maxline + sizeof head + 2)) { + fputs("we could not establish line buffering for stdout, terminating.", stderr); + return EXIT_FAILURE; + } + + // Establishes exit handlers + atexit(doAtExit); + at_quick_exit(doAtExit); + + // Establishes signal handlers + for (unsigned i = 1; i < sh_known; ++i) + sh_enable(i, signal_handler); + + // If there are no command-line arguments, reads from stdin + lastOpen = argv; + if (argc < 2) goto RUN; + + // Runs basic_blocks for each command-line argument + for (++lastOpen; lastOpen[0]; ++lastOpen) { + if (!freopen(lastOpen[0], "r", stdin)) { + perror(lastOpen[0]); + return EXIT_FAILURE; + } + printf("++++++++++ %s +++++++++++\n", lastOpen[0]); + RUN: + basic_blocks(); + } + return EXIT_SUCCESS; +} diff --git a/bitint.c b/bitint.c new file mode 100644 index 0000000..bf2b559 --- /dev/null +++ b/bitint.c @@ -0,0 +1,48 @@ +#include +#include "c23-fallback.h" + +#ifndef BITINT_MAXWIDTH +# error "no _BitInt support found, aborting compilation" +// shut off some subsequent error messages +# include "no _BitInt support found, aborting compilation" +#endif + +int main (int argc, char* argv[static argc+1]) { + // this doesn't fit, diagnose? + _BitInt(15) s15 = 0b1010101'01010101wb; + unsigned _BitInt(15) u15 = 0b1010101'01010101wbu; + // this doesn't fit, diagnose? + _BitInt(15) su15 = u15; + + auto u65 = 0x1'FFFF'FFFF'FFFF'FFFFwbu; + // wrap around arithmetic is fine. + auto oa65 = 0x1'FFFF'FFFF'FFFF'FFFFwbu + 1wbu; + // constant too wide, diagnostic? + typeof(u65) o65 = 0x2'FFFF'FFFF'FFFF'FFFFwbu; + + auto s65 = 0xFFFF'FFFF'FFFF'FFFFwb; + // signed arithmetic overflows, diagnostic? + auto sa65 = 0xFFFF'FFFF'FFFF'FFFFwb + 1wb; + // constant too wide, diagnostic? + typeof(s65) os65 = 0x1'FFFF'FFFF'FFFF'FFFFwb; + + unsigned _BitInt(128) max128s = 0x7FFF'FFFF'FFFF'FFFF'FFFF'FFFF'FFFF'FFFFwb; + unsigned _BitInt(128) min128s = -1wb -max128s; + + unsigned _BitInt(3) max3u = -1; + unsigned _BitInt(4) max4u = -1; + unsigned _BitInt(4) one4u = max4u - max3u; + signed _BitInt(4) max4s = max3u; + signed _BitInt(4) min4s = ~max4s; + + printf("15 bits, signed:\t%d\n", (signed)s15); + printf("15 bits, unsigned:\t%u\n", (unsigned)u15); + printf("15 bits, u → s:\t%d\n", (signed)su15); + printf("4 bits:\t%u\n", (unsigned)max4u); + printf("4 bits:\t%u\n", (unsigned)one4u); + printf("4 bits:\t%d\n", (signed)max4s); + printf("4 bits:\t%d\n", (signed)min4s); + printf("4 bit arithm:\t%u\n", (unsigned)(max4u + 1wbu)); + printf("int arithm:\t%u\n", (unsigned)(max4u + 1)); + return s15 + su15 + u15 + u65 + oa65 + o65 + s65 + sa65 + os65 + !max128s + !min128s; +} diff --git a/build-musl b/build-musl new file mode 100644 index 0000000..a3f6c58 --- /dev/null +++ b/build-musl @@ -0,0 +1,120 @@ +#!/bin/sh + +# A script to configure the musl C library + +# This is intended to provide you with a way to use the musl C library +# with modern compilers such that you will be able to use most C23 +# features that such compilers already provide. +# +# Note that this script not suited for cross compilation. +# +# You need +# +# - a native Linux system, with musl or glibc, regardless +# +# - a modern C compiler with good support for C23 features such as gcc +# (starting with version 13) or clang (starting with 17). + +# - a directory (denoted by sourceDir in the following) with the musl +# source plus C23 patches as provided here: +# +# https://forge.icube.unistra.fr/icps/musl/-/branches +# +# As these patches will hopefully be integrated more and more into +# musl's main line this is a moving target and you should make sure +# that you always grab the latest c23 branch. As of this writing +# this is c23-v11. +# +# - Root rights (so-called sudoer rights in Linux slang) to be able to +# install the compiled library and wrappers in a system wide +# directory, "/usr/local" per default. +# +# To configure, compile and install +# +# cd sourceDir +# path/to/this/directory/build-musl COMPILER [FLAG] [LOGFILE] +# +# where +# +# - COMPILER is the name of the compiler, for example "gcc-13" or +# "clang-17". +# +# (FLAG and LOGFILE are optional and not interesting in general.) +# +# For example with +# +# path/to/this/directory/build-musl clang-18 +# +# the LOGFILE is musl-log-clang-18.txt in sourceDir, and the +# installation is in /usr/local/musl-clang-18. Note that to be able to +# install, it is usually necessary to collect your password, which is +# done by a program referred to by the SUDO_ASKPASS variable +# below. Per default this will result in a popup at the end of +# compilation that blocks your screen. + +# The interface that you then should use is bin/cc, for the example +# above that would be +# +# /usr/local/musl-clang-18/bin/cc +# +# You should be able to use this just as any other compiler +# interface. To use this interface to compile in C23 mode you probably +# have to add an option "-std=c23" or "-std=c2x". + + +PREFIXDIR="${PREFIXDIR:-/usr/local}" +LOGFILE="${LOGFILE:-musl-log}" +SUDO_ASKPASS="${SUDO_ASKPASS:-/usr/bin/ssh-askpass}" + +export SUDO_ASKPASS + +COMMANDLINE="$*" + +if [ $# -gt 0 ] ; then + export CC="$1" + PREFIXDIR="${PREFIXDIR}/musl-$1" + LOGFILE="${LOGFILE}-$1" + shift +fi + +if [ $# -gt 0 ] ; then + export CFLAGS="-std=$1" + PREFIXDIR="${PREFIXDIR}-$1" + LOGFILE="${LOGFILE}-$1" + shift +fi + +if [ $# -gt 0 ] ; then + export CFLAGS="${CFLAGS} $*" + PREFIXDIR="${PREFIXDIR}-$1" + LOGFILE="${LOGFILE}-$1" +fi + +LOGFILE="${LOGFILE}.txt" + +echo > "${LOGFILE}" + +echo "command line: ${COMMANDLINE}" | tee -a "${LOGFILE}" +echo "building musl with prefix '${PREFIXDIR}', ask-pass '${SUDO_ASKPASS}'" | tee -a "${LOGFILE}" +echo " compiler '${CC}' and flags '${CFLAGS}'" | tee -a "${LOGFILE}" +echo "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" | tee -a "${LOGFILE}" +./configure "--prefix=${PREFIXDIR}" 2>&1 1>>"${LOGFILE}" +if [ $? -ne 0 ] ; then + echo "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" | tee -a "${LOGFILE}" + echo "configuration failed" | tee -a "${LOGFILE}" + cat "${LOGFILE}" + exit 1 +fi +echo "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" | tee -a "${LOGFILE}" +make clean 2>&1 | tee -a "${LOGFILE}" +echo "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" | tee -a "${LOGFILE}" +make -j 8 2>&1 | tee -a "${LOGFILE}" +echo "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" | tee -a "${LOGFILE}" +sudo -A make install 2>&1 | tee -a "${LOGFILE}" +echo "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" | tee -a "${LOGFILE}" + +if [ -x "${PREFIXDIR}/bin/musl-gcc" ] ; then + sudo -A ln -sf "musl-gcc" "${PREFIXDIR}/bin/cc" 2>&1 | tee -a "${LOGFILE}" +elif [ -x "${PREFIXDIR}/bin/musl-clang" ] ; then + sudo -A ln -sf "musl-clang" "${PREFIXDIR}/bin/cc" 2>&1 | tee -a "${LOGFILE}" +fi diff --git a/c23-fallback.h b/c23-fallback.h new file mode 100644 index 0000000..3331631 --- /dev/null +++ b/c23-fallback.h @@ -0,0 +1,1325 @@ +#ifndef C23_FALLBACK_H +#define C23_FALLBACK_H + +/** @file + ** @brief A header file to augment compatibility of older compilers with C23. + ** + ** This has only been tested with the open source compilers + ** + ** - gcc + ** - clang + ** + ** and the C libraries + ** + ** - glibc + ** - musl + ** + ** The newer the version of these compilers or C libraries you can + ** get, the better. If you use another compiler or C library and have + ** adjustments to make to this file, please share. I will try to + ** include such changes in the future. + ** + ** Commonly supported C23 features that are required are + ** - digit separators such as in `0xAB'CD` + ** - binary integer literals such as `0b1010` or `0B0101` + ** - the new attribute syntax such as `[[deprecated]]` + ** + ** The first two are not easy to circumvent, so probably you should + ** not even try to use C23 enabled code without having support for + ** them. + ** + ** For the attribute syntax there is a feature test + ** __has_c_attribute. It can be used with define (or #ifdef) to test + ** for the syntax itself and with #if and an argument to test for an + ** individual attribute, see below. + ** + ** Another feature test is __has_include, which similarly can be used + ** to test for the preprocessor feature itself and if that is + ** available to test for the availability of a specific header. This + ** can be used to + ** + ** - test for optional headers such as , , or + ** . This now allows to use the corresponding feature + ** test macro (__STDC_NO_COMPLEX__, __STDC_NO_ATOMICS__, + ** __STDC_NO_THREADS__) to test for compiler support of these + ** features, separately from the availability of library + ** interfaces. + ** + ** - test for new headers that come with C23, namely + ** and . Even if your C library does not support these, + ** their functionality should mostly be available through + ** type-generic macros that we emulate. Prefer them over function + ** symbols, those may depend on linker symbols that are not yet + ** available. + ** + ** This header here unconditionally includes a bunch of C library + ** headers (see below) and augments them with C23 features if + ** possible. You should not include them yourself, such that we are + ** sure in what we get. This also concerns the new C23 header + ** . + ** + ** Other headers also may be included conditionally, namely + ** and . + ** + ** When emulating the new features, there may be spurious warnings + ** that some attributes are misplaced or ignored. This obviously + ** means that they are not completely taken into account and that the + ** analysis they are expected to provide is not yet fully implemented + ** by your compiler. If you are flooded by these, you might switch + ** some of the diagnostics off by defining the macro + ** `C23_FALLBACK_SILENT` for example by providing the command line + ** argument `-DC23_FALLBACK_SILENT`. + ** + ** But note also, that some errors that are found are actually real + ** errors. These are meant as bad code examples and also as tests if + ** compilers are able to detect these kind of things. If you stumble + ** into these, there should be a code comment close by that explains + ** the particular example. Compilers vary widely in what they are + ** able to detect, so here the more you see, the better. + ** + ** And then, ..., there are the bugs. Currently, the code triggers + ** one particular bug of gcc-13 in `endianness.c`. + ** + ** As of March 2024, gcc-14 and clang-18 work with most of the new + ** C23 features. Current restrictions are + ** + ** - clang-18: `constexpr` is not yet implemented + ** - both: `#embed` is not yet implemented + ** + ** As indicated above, some short comings of your C library + ** implementation can be worked around, but unfortunately not all. As + ** of this writing + ** + ** - glibc: `printf` and `scanf` support only includes the new %b + ** specifier for binary representation, but not the %w length + ** modifiers. + ** + ** - musl: in the current distribution there is no support for C23 + ** features, so in particular `printf` etc will not work with the + ** new formats, yet. + ** + ** We are currently working to have patches included to musl whence + ** the new edition of Modern C and the new standard appear. In the + ** mean time I will try to post a patch set and build recipe such + ** that you may easily build such modified version of musl with + ** complete C23 support on your own. Stay tuned. + **/ + +#include + +/* Needed for musl */ +/* #ifdef __has_include */ +/* # if __has_include() */ +/* # include */ +/* # endif */ +/* #endif */ + + +#if (__STDC_UTF_16__ != 1) || (__STDC_UTF_32__ != 1) +# ifndef C23_FALLBACK_SILENT +# warning "C23 imposes that char16_t and char32_t use UTF-16 and UTF-32 encodings" +# endif +#endif + +/** + ** @brief A preprocessor test for the `__VA_OPT__` feature. + ** + ** Compilers before C23 might not implement `__VA_OPT__`. + ** @return This macro should always evaluate to `0` or `1`. + **/ +#define __has_va_opt C23_HAS_VA_OPT_I(C23) +#define C23_HAS_VA_OPT_I(...) \ + C23_HAS_VA_OPT_IIplus(__VA_OPT__(,) 1, 0,) +#define C23_HAS_VA_OPT_IIplus(_0, _1, ...) _1 + +#if !__has_va_opt +# ifndef C23_FALLBACK_SILENT +# warning "no support of C23 __VA_OPT__ found" +# endif +#endif + +#ifndef BITINT_MAXWIDTH +# ifdef __BITINT_MAXWIDTH__ +# define BITINT_MAXWIDTH __BITINT_MAXWIDTH__ +# else +# ifndef C23_FALLBACK_SILENT +# warning "no support for _BitInt types found" +# endif +# endif +#endif + +/* A simple type-generic conditional. */ +#define GENERIC_IF(COND, A, B) \ + _Generic((char(*)[1+!!(COND)])0, \ + char(*)[2]: (A), \ + default: (B)) + +/* clang has a set of feature tests that are quite comfortable, + + __has_feature, to test if a specific feature is implemented + + __has_extension, to test if a specific feature is implemented + + __is_identifier, to test if a word is an identifier or a keyword. + + In the following we construct similar tests for gcc such that we + may use these features independently from the C standard version + that is supported. */ +# define __tester2(_0, _1, ...) _1 +# define __tester1(...) __tester2(__VA_ARGS__) +#ifndef __has_feature +# define __has_feature(X) __tester1(__has_feature_ ## X 1, 0, ) +#endif +#ifndef __has_extension +# define __has_extension(X) __tester1(__has_extension_ ## X 1, 0, ) +#endif + +#ifndef __is_identifier +# define __is_identifier(X) __tester1(__is_special_ ## X 0, 1, ) +# define __is_special_auto , +# define __is_special_break , +# define __is_special_case , +# define __is_special_char , +# define __is_special_const , +# define __is_special_continue , +# define __is_special_default , +# define __is_special_do , +# define __is_special_double , +# define __is_special_else , +# define __is_special_enum , +# define __is_special_extern , +# define __is_special_float , +# define __is_special_for , +# define __is_special_goto , +# define __is_special_if , +# define __is_special_inline , +# define __is_special_int , +# define __is_special_long , +# define __is_special_register , +# define __is_special_restrict , +# define __is_special_return , +# define __is_special_short , +# define __is_special_signed , +# define __is_special_sizeof , +# define __is_special_static , +# define __is_special_struct , +# define __is_special_switch , +# define __is_special_typedef , +# define __is_special_union , +# define __is_special_unsigned , +# define __is_special_void , +# define __is_special_volatile , +# define __is_special_while , +# define __is_special__Atomic , +# define __is_special__Complex , +# define __is_special__Decimal128 , +# define __is_special__Decimal32 , +# define __is_special__Decimal64 , +# define __is_special__Generic , +# define __is_special__Imaginary , +# define __is_special__Noreturn , +# if __GNUC__ > 12 || __STDC_VERSION__ >= 202311L +# define __is_special_nullptr , +# define __is_special_static_assert , +# define __is_special_thread_local , +# define __is_special_constexpr , +# define __is_special_typeof , +# define __is_special_typeof_unqual , +# endif +# if __GNUC__ > 13 || __STDC_VERSION__ >= 202311L +# define __is_special__BitInt , +# define __is_special_alignas , +# define __is_special_alignof , +# define __is_special_bool , +# define __is_special_false , +# define __is_special_true , +# endif +#endif +#if __GNUC__ > 12 || __STDC_VERSION__ >= 202311L +# define __has_feature_c_fixed_enum , +#endif + +// The feature for fixed underlying types for enums has been so long +// in clang, that they don't even seem to have a feature test for it. +#if !__has_feature(c_fixed_enum) && defined(__clang_major__) && (__clang_major__ < 8) +# ifndef C23_FALLBACK_SILENT +# warning "syntax for fixed underlying integer type of enumerations is not supported" +# endif +#endif + +#if __is_identifier(constexpr) +# ifndef C23_FALLBACK_SILENT +# warning "constexpr keyword is not supported, emulating as static const" +# endif +# define constexpr static const +#endif + +#if __is_identifier(bool) +# ifndef C23_FALLBACK_SILENT +# warning "bool keyword is not supported, emulating as macro" +# endif +# define bool _Bool +#endif +#if __is_identifier(false) +# ifndef C23_FALLBACK_SILENT +# warning "false keyword is not supported, emulating as macro" +# endif +# undef false +# define false ((bool)+0U) +#endif +#if __is_identifier(true) +# ifndef C23_FALLBACK_SILENT +# warning "true keyword is not supported, emulating as macro" +# endif +# undef true +# define true ((bool)+1U) +#endif + +#if __is_identifier(thread_local) +// Maybe this is also provided by the C library implementation, but a +// redefinition makes no harm. +#define thread_local _Thread_local +#endif + +#if __is_identifier(alignof) +#undef alignof +#define alignof _Alignof +#endif + +#if __is_identifier(alignas) +#undef alignas +#define alignas _Alignas +#endif + +#if __is_identifier(static_assert) +#undef static_assert +#define static_assert(...) __static_assert(__VA_ARGS__,) +#define __static_assert(E, ...) _Static_assert(E) +#endif + +#if __is_identifier(typeof) +# if __GNUC__ +# undef typeof +# define typeof __typeof__ +# else +# warning "typeof operator only comes with C23" +# endif +#endif + +#if __STDC_VERSION__ < 202311L +# if __GNUC__ +# if __GNUC__ < 13 +# define auto __auto_type +# endif +# else +# warning "auto type inference only comes with C23" +# endif +#endif + +#ifndef __has_c_attribute +# define __has_c_attribute(X) 0 +#endif + +// C23 has nullptr, use a fallback if that is not available. Here we +// use an enumeration type and constant to do that. The idea is that +// such an enumeration may then be used in _Generic as long the cases +// otherwise only have pointer types. The first choice is to use a +// _BitInt type for that, if that is already available, in an effort +// to not clash with standard integer types. If that is not found, we +// use a standard integer type that has the right width. +// +// If possible, we also use an attribute that forces warnings if any +// other value than a null or 0 is assigned to a variable of type +// nullptr_t. +#if !__is_identifier(nullptr) +// Maybe this is also provided by the C library implementation, but a +// redefinition makes no harm. +typedef typeof(nullptr) nullptr_t; +#elif (__STC_VERSION__ < 202311L) +# ifdef BITINT_MAXWIDTH + +# ifndef C23_FALLBACK_SILENT +# warning "using fallback for nullptr to enumeration constant based on _BitInt type" +# else +# pragma GCC diagnostic ignored "-Wnon-literal-null-conversion" +# endif + +enum +# if __has_c_attribute(clang::enum_extensibility) +[[__clang__::__enum_extensibility__(closed)]] +# endif +nullptr_t : +__typeof__((unsigned _BitInt(sizeof(void*)*CHAR_BIT))0) +{ + nullptr, +}; + +# else + +# ifndef C23_FALLBACK_SILENT +# warning "using fallback for nullptr to enumeration constant based on standard integer type" +# endif + +enum +# if __has_c_attribute(clang::enum_extensibility) +[[__clang__::__enum_extensibility__(closed)]] +# endif +nullptr_t { + nullptr, + __nullptr_max = + GENERIC_IF((sizeof(void*)==sizeof(int)), + -1U, + GENERIC_IF((sizeof(void*)==sizeof(long)), + -1UL, + -1ULL)), +}; + +// Unfortunately such an enumeration type is not yet good enough for +// all compilers. In particular some gcc version has nullptr and +// __nullptr_max as different types. Force an expression that is an +// ICE of value 0 and of the enumeration type. +# define nullptr (1 ? nullptr : __nullptr_max) +# endif +#endif +typedef typeof(nullptr) nullptr_t; + +static_assert(_Generic(nullptr, nullptr_t: true)); +static_assert(sizeof(void*)==sizeof(nullptr)); +static_assert(sizeof(void*)==sizeof(nullptr_t)); + +/**********************************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/**********************************************************************************************/ + +// for call_once +#ifndef ONCE_FLAG_INIT +# ifdef __has_include +# if __has_include() +# ifndef C23_FALLBACK_SILENT +# warning "including call_once function from instead of " +# endif +# include +# endif +# endif +#endif + +// If call_once has not been found we construct a function that is +// based on atomics. +#ifndef ONCE_FLAG_INIT +# ifdef __has_include +# if __has_include() +# include +# ifndef C23_FALLBACK_SILENT +# warning "emulating call_once function by using atomics" +# endif +enum { once_initial, once_inter, once_final, }; +# define ONCE_FLAG_INIT once_initial + +typedef _Atomic(unsigned) once_flag; + +# if __has_c_attribute(__gnu__::__noinline__) +[[__gnu__::__noinline__]] +# endif +static inline +void call_once(once_flag* flag, void callback(void)) { + if (*flag != once_final) { + unsigned expected = once_initial; + if (atomic_compare_exchange_strong(flag, &expected, once_inter)) { + callback(); + *flag = once_final; + } else { + while (*flag != once_final) { + // + } + } + } else { + // We know already that the implementation does not support threads, + // so the only available fence is this: + atomic_signal_fence(memory_order_seq_cst); + } +} +# endif +# endif +#endif + +/**********************************************************************************************/ + +// We assume that the compiler is already able to handle the new +// attribute syntax such as [[deprecated]]. Nevertheless, maybe +// certain attributes themselves might not yet be implemented. We +// provide fallback macros to corresponding gnu attributes. Here we +// use the "safe" names for these features with double underscores, so +// they don't interfere with application names. + +#if !__has_c_attribute(__unsequenced__) +# ifndef C23_FALLBACK_SILENT +# warning "unsequenced attribute not yet implemented" +# endif +# define __unsequenced__ +# if __has_c_attribute(__gnu__::__const__) +# ifndef C23_FALLBACK_SILENT +# warning "unsequenced attribute could be emulated via gnu attribute by using c23_unsequenced" +# endif +# define c23_unsequenced __attribute__((__const__)) +# endif +#endif + +#if !__has_c_attribute(__reproducible__) +# ifndef C23_FALLBACK_SILENT +# warning "reproducible attribute not yet implemented" +# endif +# define __reproducible__ +# if __has_c_attribute(__gnu__::__pure__) +# ifndef C23_FALLBACK_SILENT +# warning "reproducible attribute could be emulated via gnu attribute by using c23_reproducible" +# endif +# define c23_reproducible __attribute__((__pure__)) +# endif +#endif + +#if !__has_c_attribute(__deprecated__) && __has_c_attribute(__gnu__::__deprecated__) +# ifndef C23_FALLBACK_SILENT +# warning "emulating deprecated attribute via gnu attribute" +# endif +# define __deprecated__ __gnu__::__deprecated__ +#endif + +#if !__has_c_attribute(__nodiscard__) && __has_c_attribute(__gnu__::__warn_unused_result__) +# ifndef C23_FALLBACK_SILENT +# warning "emulating nodiscard attribute via gnu attribute" +# endif +# define __nodiscard__ __gnu__::__warn_unused_result__ +#endif + +#if !__has_c_attribute(__maybe_unused__) && __has_c_attribute(__gnu__::__unused__) +# ifndef C23_FALLBACK_SILENT +# warning "emulating maybe_unused attribute via gnu attribute" +# endif +# define __maybe_unused__ __gnu__::__unused__ +#endif + +#if !__has_c_attribute(noreturn) && __has_c_attribute(__gnu__::__noreturn__) +# ifndef C23_FALLBACK_SILENT +# warning "emulating noreturn attribute via gnu attribute" +# endif +# define noreturn __gnu__::__noreturn__ +#endif + +#if !__has_c_attribute(__fallthrough__) && __has_c_attribute(__gnu__::__fallthrough__) +# warning "emulating fallthrough attribute via gnu attribute" +# define __fallthrough__ __gnu__::__fallthrough__ +#endif + +#if __has_c_attribute(__deprecated__) || defined(__deprecated__) +// Deprecate two interfaces in +[[__deprecated__]] char *asctime(const struct tm*); +[[__deprecated__]] char *ctime(const time_t*); +#endif + +// Gnu compilers have a malloc attribute that comes in two different +// forms. Without argument it just certifies that a function returns a +// pointer value that has not been seen before, and thus the pointer +// target will not alias with anything known. The second form receives +// one or two arguments and specifies a function that should be used +// analogous to free. Unfortunately, clang didn't follow that second +// development, yet. But we suppose that there will be at least a +// partial solution in clang-18 final. +#if __has_c_attribute(__gnu__::__malloc__) && !(defined(__clang_major__) && __clang_major__ <= 19) +# define __gnu_free__(...) __gnu__::__malloc__(__VA_ARGS__) +#else +# define __gnu_free__(...) +#endif + +/**********************************************************************************************/ + +/* C23 allows to omit the second argument of va_start */ +#if __STDC_VERSION_STDARG_H__ < 202311L +#undef va_start +#define va_start(...) __va_start1(__VA_ARGS__ , 0, ) +#define __va_start1(...) __va_start2(__VA_ARGS__) +#define __va_start2(v, l, ...) __builtin_va_start(v, l) +#endif + +/**********************************************************************************************/ + +/* C23 has three new tg interfaces in the new header. +They are modeled after similar gcc features. They are meant to do +arithmetic with overflow check by using everything the compiler can +get from instruction flags that already exist on most CPU. +*/ + +#ifdef __has_include +# if __has_include() +# include +# endif +#endif + +/* If we don't have the header, yet, we may easily emulate it if we +are on a compiler claiming compatibility with gcc. */ +#ifndef ckd_add +# ifdef __GNUC__ +# ifndef C23_FALLBACK_SILENT +# warning "emulating stdckdint.h interfaces via gnu builtins" +# endif +# define ckd_add(R, A, B) __builtin_add_overflow ((A), (B), (R)) +# define ckd_sub(R, A, B) __builtin_sub_overflow ((A), (B), (R)) +# define ckd_mul(R, A, B) __builtin_mul_overflow ((A), (B), (R)) +# else +# warning "checked integer arithmetic as of C23 is not yet implemented" +# endif +#endif + +/**********************************************************************************************/ + +// C23 has the unreachable macro in , use a fallback if that +// is not available +#ifndef unreachable +# ifndef C23_FALLBACK_SILENT +# warning "using fallback for unreachable macro" +# endif +# define unreachable() __builtin_unreachable() +#endif + +/**********************************************************************************************/ + +// C23 provides macros for the width of integer types. Provide a +// fallback if they are not available + +#ifndef BOOL_WIDTH +# define BOOL_WIDTH 1 +#endif + +#ifndef CHAR_WIDTH +# define CHAR_WIDTH CHAR_BIT +#endif + +#ifndef UCHAR_WIDTH +# define UCHAR_WIDTH CHAR_BIT +#endif + +#ifndef SCHAR_WIDTH +# define SCHAR_WIDTH CHAR_BIT +#endif + +#ifndef USHRT_WIDTH +# if USHRT_MAX <= 0xFFFFU +# define USHRT_WIDTH 16 +# define SHRT_WIDTH 16 +# else +# if USHRT_MAX <= 0xFFFFFFFFU +# define USHRT_WIDTH 32 +# define SHRT_WIDTH 32 +# else +# if USHRT_MAX <= 0xFFFFFFFFFFFFFFFFU +# define USHRT_WIDTH 64 +# define SHRT_WIDTH 64 +# else +// This doesn't use #elif but nested #if/#else because the value might +// be too large for the preprocessor +# if USHRT_MAX <= 0xFFFFFFFFFFFFFFFFFFFFFFFFU +# define USHRT_WIDTH 96 +# define SHRT_WIDTH 96 +# else +// This doesn't use #elif but nested #if/#else because the value might +// be too large for the preprocessor +# if USHRT_MAX <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFU +# define USHRT_WIDTH 128 +# define SHRT_WIDTH 128 +# endif +# endif +# endif +# endif +# endif +#endif + +// unsigned int has a width of at least 16. +#ifndef UINT_WIDTH +# if UINT_MAX <= 0xFFFFU +# define UINT_WIDTH 16 +# define INT_WIDTH 16 +# else +# if UINT_MAX <= 0xFFFFFFFFU +# define UINT_WIDTH 32 +# define INT_WIDTH 32 +# else +# if UINT_MAX <= 0xFFFFFFFFFFFFFFFFU +# define UINT_WIDTH 64 +# define INT_WIDTH 64 +# else +// This doesn't use #elif but nested #if/#else because the value might +// be too large for the preprocessor +# if UINT_MAX <= 0xFFFFFFFFFFFFFFFFFFFFFFFFU +# define UINT_WIDTH 96 +# define INT_WIDTH 96 +# else +// This doesn't use #elif but nested #if/#else because the value might +// be too large for the preprocessor +# if UINT_MAX <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFU +# define UINT_WIDTH 128 +# define INT_WIDTH 128 +# endif +# endif +# endif +# endif +# endif +#endif + +// size_t has a width of at least 16. +#ifndef SIZE_WIDTH +# if SIZE_MAX <= 0xFFFFU +# define SIZE_WIDTH 16 +# else +# if SIZE_MAX <= 0xFFFFFFFFU +# define SIZE_WIDTH 32 +# else +# if SIZE_MAX <= 0xFFFFFFFFFFFFFFFFU +# define SIZE_WIDTH 64 +# else +// This doesn't use #elif but nested #if/#else because the value might +// be too large for the preprocessor +# if SIZE_MAX <= 0xFFFFFFFFFFFFFFFFFFFFFFFFU +# define SIZE_WIDTH 96 +# else +// This doesn't use #elif but nested #if/#else because the value might +// be too large for the preprocessor +# if SIZE_MAX <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFU +# define SIZE_WIDTH 128 +# endif +# endif +# endif +# endif +# endif +#endif + +// unsigned long has a width of at least 32 +#ifndef ULONG_WIDTH +# if ULONG_MAX <= 0xFFFFFFFFUL +# define ULONG_WIDTH 32 +# define LONG_WIDTH 32 +# else +# if ULONG_MAX <= 0xFFFFFFFFFFFFFFFFUL +# define ULONG_WIDTH 64 +# define LONG_WIDTH 64 +# else +// This doesn't use #elif but nested #if/#else because the value might +// be too large for the preprocessor +# if ULONG_MAX <= 0xFFFFFFFFFFFFFFFFFFFFFFFFUL +# define ULONG_WIDTH 96 +# define LONG_WIDTH 96 +# else +// This doesn't use #elif but nested #if/#else because the value might +// be too large for the preprocessor +# if ULONG_MAX <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFUL +# define ULONG_WIDTH 128 +# define LONG_WIDTH 128 +# endif +# endif +# endif +# endif +#endif + +// unsigned long long has a width of at least 64. +#ifndef ULLONG_WIDTH +# if ULLONG_MAX <= 0xFFFFFFFFFFFFFFFFULL +# define ULLONG_WIDTH 64 +# define LLONG_WIDTH 64 +# else +// This doesn't use #elif but nested #if/#else because the value might +// be too large for the preprocessor +# if ULLONG_MAX <= 0xFFFFFFFFFFFFFFFFFFFFFFFFULL +# define ULLONG_WIDTH 96 +# define LLONG_WIDTH 96 +# else +// This doesn't use #elif but nested #if/#else because the value might +// be too large for the preprocessor +# if ULLONG_MAX <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFULL +# define ULLONG_WIDTH 128 +# define LLONG_WIDTH 128 +# endif +# endif +# endif +#endif + +// In most cases the [u]intmax_t types will just be the long long +// types. +#ifndef UINTMAX_WIDTH +# if UINTMAX_MAX == ULLONG_MAX +# define UINTMAX_WIDTH ULLONG_WIDTH +# define INTMAX_WIDTH LLONG_WIDTH +# endif +#endif + +#ifndef UINT128_MAX +# ifdef __SIZEOF_INT128__ +# if defined(__clang_major__) && (__clang_major__ < 18) +# ifndef C23_FALLBACK_SILENT +# warning "not using compiler intrinsic for int128_t, please upgrade to clang version ≥ 18" +# endif +# else +# define __has_int128 +# endif +# endif +#endif + +#ifndef UINT256_MAX +# ifdef __SIZEOF_INT256__ +# if defined(__clang_major__) && (__clang_major__ < 18) +# ifndef C23_FALLBACK_SILENT +# warning "not using compiler intrinsic for int256_t, please upgrade to clang version ≥ 18" +# endif +# else +# define __has_int256 +# endif +# endif +#endif + +// In C23 uint128_t may exist, even if it is wider than +// uintmax_t. Provide it if the compiler supports it. +#ifndef UINT128_MAX +# ifdef __has_int128 +typedef signed __int128 int128_t; +typedef unsigned __int128 uint128_t; +# define UINT128_WIDTH 128 +# define INT128_WIDTH 128 +# define UINT128_MAX ((uint128_t)-1) +# define INT128_MAX ((int128_t)(((uint128_t)-1)>>1)) +# define INT128_MIN (-INT128_MAX-1) +# if BITINT_MAXWIDTH >= 128 +# define UINT128_C(X) ((uint128_t)+(X ## wbu)) +# define INT128_C(X) ((int128_t)+(X ## wb)) +# endif +# endif +#endif + +// In C23 uint256_t may exist, even if it is wider than +// uintmax_t. Provide it if the compiler supports it. +#ifndef UINT256_MAX +# ifdef __has_int256 +typedef signed __int256 int256_t; +typedef unsigned __int256 uint256_t; +# define UINT256_WIDTH 256 +# define INT256_WIDTH 256 +# define UINT256_MAX ((uint256_t)-1) +# define INT256_MAX ((int256_t)(((uint256_t)-1)>>1)) +# define INT256_MIN (-INT256_MAX-1) +# if BITINT_MAXWIDTH >= 256 +# define UINT256_C(X) ((uint256_t)+(X ## wbu)) +# define INT256_C(X) ((int256_t)+(X ## wb)) +# endif +# endif +#endif + +/**********************************************************************************************/ + +#if __STDC_VERSION_INTTYPES_H__ < 202311L +# ifndef C23_FALLBACK_SILENT +# warning "native C23 library support for %b printf or scanf formats may be missing" +# endif +# ifndef PRIb32 +# if __GLIBC__ > 1 && defined(__PRI64_PREFIX) +# ifndef C23_FALLBACK_SILENT +# warning "no C23 support for %b printf formats found, emulating with glibc features" +# endif +# define PRIB16 "B" +# define PRIB32 "B" +# define PRIB64 __PRI64_PREFIX "B" +# define PRIB8 "B" +# define PRIBFAST16 __PRIPTR_PREFIX "B" +# define PRIBFAST32 __PRIPTR_PREFIX "B" +# define PRIBFAST64 __PRI64_PREFIX "B" +# define PRIBFAST8 "B" +# define PRIBLEAST16 "B" +# define PRIBLEAST32 "B" +# define PRIBLEAST64 __PRI64_PREFIX "B" +# define PRIBLEAST8 "B" +# define PRIBMAX __PRI64_PREFIX "B" +# define PRIBPTR __PRIPTR_PREFIX "B" +# define PRIb16 "b" +# define PRIb32 "b" +# define PRIb64 __PRI64_PREFIX "b" +# define PRIb8 "b" +# define PRIbFAST16 __PRIPTR_PREFIX "b" +# define PRIbFAST32 __PRIPTR_PREFIX "b" +# define PRIbFAST64 __PRI64_PREFIX "b" +# define PRIbFAST8 "b" +# define PRIbLEAST16 "b" +# define PRIbLEAST32 "b" +# define PRIbLEAST64 __PRI64_PREFIX "b" +# define PRIbLEAST8 "b" +# define PRIbMAX __PRI64_PREFIX "b" +# define PRIbPTR __PRIPTR_PREFIX "b" +# define SCNb16 "hb" +# define SCNb32 "b" +# define SCNb64 __PRI64_PREFIX "b" +# define SCNb8 "hhb" +# define SCNbFAST16 __PRIPTR_PREFIX "b" +# define SCNbFAST32 __PRIPTR_PREFIX "b" +# define SCNbFAST64 __PRI64_PREFIX "b" +# define SCNbFAST8 "hhb" +# define SCNbLEAST16 "hb" +# define SCNbLEAST32 "b" +# define SCNbLEAST64 __PRI64_PREFIX "b" +# define SCNbLEAST8 "hhb" +# define SCNbMAX __PRI64_PREFIX "b" +# define SCNbPTR __PRIPTR_PREFIX "b" +# else +# ifndef C23_FALLBACK_SILENT +# warning "no C23 support for %b printf formats found" +# endif +# endif +# endif +#endif + +/**********************************************************************************************/ + +// Complex arithmetic without header +#ifndef __STDC_NO_COMPLEX__ + +// gcc and related compilers implement an "I" or "i" suffix for +// floating point constants since ages. +# ifdef __GNUC__ +# define I (__extension__ 1.0IF) +# define _COMPLEX_I (0.0F + I) + +// Otherwise use the new constexpr construct to produce a named +// constant of complex type. +# else +typedef union _C23_complex _C23_complex; +union _C23_complex { + // These two are guaranteed to have the same representation. + float _C23_v[2]; + _Complex float _C23_c; +}; +// Use static const. Best would be to have constexpr, but using a +// different member than was initialized is not defined for them, +// unfortunately. +static _C23_complex const _C23_I = { { 0.0F, 1.0F, }, }; +# define _COMPLEX_I (_C23_I._C23_c) +# endif + +#define iscomplex(X) (sizeof(1.0F*(X)) == sizeof(1.0F*(X)+_COMPLEX_I)) +#define isimaginary(X) (sizeof(1.0F*(X)+0.0F) == 2*sizeof(1.0F*(X))) + +# define _C23_float_Complex_CASE float _Complex: true, +# define _C23_double_Complex_CASE double _Complex: true, +# define _C23_long_double_Complex_CASE long double _Complex: true, +#else +# define _C23_float_Complex_CASE +# define _C23_double_Complex_CASE +# define _C23_long_double_Complex_CASE +#endif + +/**********************************************************************************************/ + +// Testing for presence of decimal floating types + +#if defined(__DEC32_MAX__) +# define _C23_Decimal32_CASE _Decimal32: true, +#else +# define _C23_Decimal32_CASE +#endif + +#if defined(__DEC64_MAX__) +# define _C23_Decimal64_CASE _Decimal64: true, +#else +# define _C23_Decimal64_CASE +#endif + +#if defined(__DEC128_MAX__) +# define _C23_Decimal128_CASE _Decimal128: true, +#else +# define _C23_Decimal128_CASE +#endif + +#define isdecimalfloating(...) (_Generic((__VA_ARGS__)+0, _C23_Decimal32_CASE _C23_Decimal64_CASE _C23_Decimal128_CASE default: false)) +#ifndef iscomplex +# define iscomplex(...) (_Generic((__VA_ARGS__)+0, _C23_float_Complex_CASE _C23_double_Complex_CASE _C23_long_double_Complex_CASE default: false)) +#endif +#define isstandardrealfloating(...) (_Generic((__VA_ARGS__)+0, float: true, double: true, long double: true, default: false)) +#define isstandardfloating(...) ((bool)(isstandardrealfloating(__VA_ARGS__)||iscomplex(__VA_ARGS__))) +#ifndef isfloating +# define isfloating(...) ((bool)(isstandardfloating(__VA_ARGS__)||isdecimalfloating(__VA_ARGS__))) +#endif + +/**********************************************************************************************/ + +// various tg-conversions and type traits + +#ifndef totype +# define totype(Y, ...) ((typeof(__VA_ARGS__))Y) +#endif +#ifndef tominusone +# define tominusone(...) totype(-1, __VA_ARGS__) +#endif +#ifndef tozero +# define tozero(...) totype(0, __VA_ARGS__) +#endif +#ifndef toone +# define toone(...) totype(1, __VA_ARGS__) +#endif +#ifndef iscompatible +# define iscompatible(X, Y) (_Generic((X), typeof(Y): true, default: false)) +#endif +#ifndef is_potentially_negative +# define is_potentially_negative(...) (tominusone(__VA_ARGS__) < 0) +#endif +#ifndef is_const_target +# define is_const_target(...) (_Generic((1 ? (__VA_ARGS__) : (void*)1), void const*: true, void const volatile*: true, default: false)) +#endif +#ifndef is_volatile_target +# define is_volatile_target(...) (_Generic((1 ? (__VA_ARGS__) : (void*)1), void volatile*: true, void const volatile*: true, default: false)) +#endif +#ifndef is_const +# define is_const(...) is_const_target(&(typeof(__VA_ARGS__)){ 0 }) +#endif +#ifndef is_volatile +# define is_volatile(...) is_volatile_target(&(typeof(__VA_ARGS__)){ 0 }) +#endif +#ifndef is_null_pointer_constant +struct do_not_use_this_otherwise; +# define is_null_pointer_constant(...) \ + (_Generic((1 ? (struct do_not_use_this_otherwise*)nullptr : (__VA_ARGS__)), \ + struct do_not_use_this_otherwise*: true, \ + default: false)) +#endif +#ifndef is_zero_ice +# define is_zero_ice(...) is_null_pointer_constant((void*)(uintptr_t)(!!(__VA_ARGS__))) +#endif +#ifndef isinteger +# define isinteger(...) is_zero_ice(tozero(__VA_ARGS__)) +#endif +#ifndef issigned +# define issigned(...) ((bool)(is_potentially_negative(__VA_ARGS__) && isinteger(__VA_ARGS__) && !iscompatible(__VA_ARGS__, char))) +#endif +#ifndef isunsigned +# define isunsigned(...) ((bool)(!is_potentially_negative(__VA_ARGS__) && isinteger(__VA_ARGS__) && !iscompatible(__VA_ARGS__, char))) +#endif +#ifndef isice +# define isice(...) is_zero_ice(!((__VA_ARGS__) || 1)) +#endif +#ifndef isvla +# define isvla(...) ((bool)!isice(sizeof(__VA_ARGS__))) +#endif +#ifndef isxwide +# define isxwide(...) \ + ((bool)( \ + isinteger(__VA_ARGS__) \ + &&_Generic((__VA_ARGS__)+0ULL, \ + unsigned long long: false, \ + default: true))) +#endif + +#ifndef is_pointer +struct do_not_use_this_otherwise { char c; }; +# define get_fla(...) \ + GENERIC_IF(isvla(__VA_ARGS__), \ + (struct do_not_use_this_otherwise[1]){ 0 }, \ + (__VA_ARGS__)) +# define is_pointer_nvla(...) \ + (_Generic((typeof(__VA_ARGS__)*)0, \ + typeof(get_fla(*(__VA_ARGS__)))**: true, \ + default: false)) +# define is_pointer_vla(...) \ + (_Generic((typeof(get_fla(*(__VA_ARGS__)))*)0, \ + typeof(struct do_not_use_this_otherwise[1])*: true, \ + default: false)) +# define is_pointer(...) ((bool)(is_pointer_nvla(__VA_ARGS__)||is_pointer_vla(__VA_ARGS__))) +#endif +#ifndef is_array +# define is_array(...) ((bool)!is_pointer(__VA_ARGS__)) +#endif +#ifndef is_fla +# define is_fla(...) ((bool)(is_array(__VA_ARGS__)&&!isvla(__VA_ARGS__))) +#endif + +#ifndef is_void_pointer +# define is_void_pointer(...) \ + _Generic((typeof(*(__VA_ARGS__))const volatile*)nullptr, \ + void const volatile*: true, \ + default: false) +#endif + +/**********************************************************************************************/ + +/* Const preserving functions as of C23. */ + +/* These are not exactly according to the book, because the return + type of the string functions could sometimes be a pointer to a void + type and not a character type.*/ + +#if __STDC_VERSION_STRING_H__ < 202311L + +//QVoid *memchr(QVoid *s, int c, size_t n); +# ifndef memchr +# define memchr(S, C, N) ((typeof(1 ? S : (void*)1))memchr((S), (C), (N))) +# endif + +// QChar *strchr(QChar *s, int c); +# ifndef strchr +# define strchr(S, C) ((typeof(S))strchr((S), (C))) +# endif + +// QChar *strpbrk(QChar *s1, const char *s2); +# ifndef strpbrk +# define strpbrk(S1, S2) ((typeof(S1))strpbrk((S1), (S2))) +# endif + +// QChar *strrchr(QChar *s, int c); +# ifndef strrchr +# define strrchr(S, C) ((typeof(S))strrchr((S), (C))) +# endif + +// QChar *strstr(QChar *s1, const char *s2); +# ifndef strstr +# define strstr(S1, S2) ((typeof(S1))strstr((S1), (S2))) +# endif + +#endif + +#if __STDC_VERSION_WCHAR_H__ < 202311L + +//QWchar *wmemchr(QWchar *s, int c, size_t n); +# ifndef wmemchr +# define wmemchr(S, C, N) ((typeof(S))wmemchr((S), (C), (N))) +# endif + +// QWchar *wcschr(QWchar *s, wchar_t c); +# ifndef wcschr +# define wcschr(S, C) ((typeof(S))wcschr((S), (C))) +# endif + +// QWchar *wcspbrk(QWchar *s1, const wchar_t *s2); +# ifndef wcspbrk +# define wcspbrk(S1, S2) ((typeof(S1))wcspbrk((S1), (S2))) +# endif + +// QWchar *wcsrchr(QWchar *s, wchar_t c); +# ifndef wcsrchr +# define wcsrchr(S, C) ((typeof(S))wcsrchr((S), (C))) +# endif + +// QWchar *wcswcs(QWchar *s1, const wchar_t *s2); +# ifndef wcsstr +# define wcsstr(S1, S2) ((typeof(S1))wcsstr((S1), (S2))) +# endif + +#endif + +#if __STDC_VERSION_STDLIB_H__ < 202311L + +// QVoid *bsearch(const void *key, QVoid *base, size_t nmemb, size_t size, +// int (*compar)(const void *, const void *)); +# ifndef bsearch +# define bsearch(KEY, BASE, NMEMB, SIZE, COMPAR) ((typeof(1 ? (BASE) : (void*)1))bsearch((KEY), (BASE), (NMEMB), (SIZE), (COMPAR))) +# endif + +#endif + +#endif + +/**********************************************************************************************/ + +#ifdef __has_include +# if __has_include() +# include +# define __has_feature_c_stdbit_h , +# endif +#endif + +#ifndef __STDC_ENDIAN_LITTLE__ +# ifdef __GNUC__ +# define __STDC_ENDIAN_LITTLE__ __ORDER_LITTLE_ENDIAN__ +# endif +#endif + +#ifndef __STDC_ENDIAN_BIG__ +# ifdef __GNUC__ +# define __STDC_ENDIAN_BIG__ __ORDER_BIG_ENDIAN__ +# endif +#endif + +#ifndef __STDC_ENDIAN_NATIVE__ +# ifdef __GNUC__ +# define __STDC_ENDIAN_NATIVE__ __BYTE_ORDER__ +# else +# ifndef C23_FALLBACK_SILENT +# warning "native endian could not be determined" +# endif +# endif +#endif + +/**********************************************************************************************/ + +/* This adds the 14 type-generic bit interfaces that are added by + C23. The use of the other type-specific interfaces is of less + interest. + +generic_return_type stdc_leading_zeros(generic_value_type value); +generic_return_type stdc_leading_ones(generic_value_type value); +generic_return_type stdc_trailing_zeros(generic_value_type value); +generic_return_type stdc_trailing_ones(generic_value_type value); +generic_return_type stdc_first_leading_zero(generic_value_type value); +generic_return_type stdc_first_leading_one(generic_value_type value); +generic_return_type stdc_first_trailing_zero(generic_value_type value); +generic_return_type stdc_count_zeros(generic_value_type value); +generic_return_type stdc_count_ones(generic_value_type value); +bool stdc_has_single_bit(generic_value_type value); +generic_return_type stdc_bit_width(generic_value_type value); +generic_value_type stdc_bit_floor(generic_value_type value); +generic_value_type stdc_bit_ceil(generic_value_type value); + +*/ + +/* + An auxiliary macro that shifts right by ULLONG_WIDTH, regardless + whether the type has a width smaller than that. For most types this + will just be a zero of the same type. + */ + +#define shift_xright(...) \ + GENERIC_IF(isxwide(__VA_ARGS__), \ + (__VA_ARGS__)>>GENERIC_IF(isxwide(__VA_ARGS__), \ + ULLONG_WIDTH, \ + 0), \ + tozero(__VA_ARGS__)) + +#ifndef stdc_count_ones +# ifdef __GNUC__ + +# define stdc_trailing_zeros(...) \ + ({ \ + auto s_t_z_x = (__VA_ARGS__); \ + static_assert(isunsigned(s_t_z_x), "bit operation needs unsigned type"); \ + (s_t_z_x) \ + ? _Generic((s_t_z_x), \ + bool: __builtin_ctz((unsigned)s_t_z_x), \ + unsigned char: __builtin_ctz((unsigned)s_t_z_x), \ + unsigned short: __builtin_ctz((unsigned)s_t_z_x), \ + unsigned: __builtin_ctz(s_t_z_x), \ + unsigned long: __builtin_ctzl(s_t_z_x), \ + unsigned long long: __builtin_ctzll(s_t_z_x), \ + default: ({ \ + size_t s_t_z_ret = 0; \ + while (s_t_z_x) { \ + unsigned long long s_t_z_z = s_t_z_x; \ + if (s_t_z_z) { \ + s_t_z_ret += __builtin_ctzll(s_t_z_z); \ + s_t_z_x = 0; \ + } else { \ + s_t_z_ret += ULLONG_WIDTH; \ + s_t_z_x = shift_xright(s_t_z_x); \ + } \ + } \ + s_t_z_ret; \ + })) \ + : stdc_count_ones(tominusone(s_t_z_x)); \ + }) + +/* This counts the least significant 1-bits of the specific type of the + argument. Therefor we complement all bits. To take narrow types + into account, that result has to be cast back to the original + type. */ +# define stdc_trailing_ones(...) stdc_trailing_zeros((typeof(__VA_ARGS__))~(__VA_ARGS__)) + +# define stdc_leading_zeros(...) \ + ({ \ + auto s_l_o_x = (__VA_ARGS__); \ + static_assert(isunsigned(s_l_o_x), "bit operation needs unsigned type"); \ + (s_l_o_x) \ + ? _Generic((s_l_o_x), \ + unsigned char: __builtin_clz((unsigned)s_l_o_x) - __builtin_clz((unsigned)tominusone(s_l_o_x)), \ + unsigned short: __builtin_clz((unsigned)s_l_o_x) - __builtin_clz((unsigned)tominusone(s_l_o_x)), \ + unsigned: __builtin_clz(s_l_o_x), \ + unsigned long: __builtin_clzl(s_l_o_x), \ + unsigned long long: __builtin_clzll(s_l_o_x), \ + default: \ + ((!isxwide(s_l_o_x)) \ + /* Should only trigger for _BitInt types. */ \ + ? (__builtin_clzll(s_l_o_x) - __builtin_clzll(tominusone(s_l_o_x))) \ + /* determine the long long word with highest 1-bit . */ \ + : ({ \ + size_t s_l_o_w = stdc_count_ones(tominusone(s_l_o_x)) - ULLONG_WIDTH; \ + while ((s_l_o_x+0UL) > ULLONG_MAX) { \ + s_l_o_x = shift_xright(s_l_o_x); \ + s_l_o_w -= ULLONG_WIDTH; \ + } \ + __builtin_clzll(s_l_o_x) + s_l_o_w; \ + }))) \ + /* This should resolve to a compile time constant. */ \ + : stdc_count_ones(tominusone(s_l_o_x)); \ + }) + + +/* This counts the most significant 1-bits of the specific type of the + argument. Therefor we complement all bits. To take narrow types + into account, that result has to be cast back to the original + type. */ +# define stdc_leading_ones(...) stdc_leading_zeros((typeof(__VA_ARGS__))~(__VA_ARGS__)) + + +# define stdc_count_ones(...) \ + _Generic((__VA_ARGS__), \ + bool: __builtin_popcount((unsigned)(__VA_ARGS__)), \ + unsigned char: __builtin_popcount((unsigned)(__VA_ARGS__)), \ + unsigned short: __builtin_popcount((unsigned)(__VA_ARGS__)), \ + unsigned: __builtin_popcount((unsigned)(__VA_ARGS__)), \ + unsigned long: __builtin_popcountl((unsigned long)(__VA_ARGS__)), \ + unsigned long long: __builtin_popcountll((unsigned long long)(__VA_ARGS__)), \ + default: ({ \ + auto s_c_o_x = (__VA_ARGS__); \ + static_assert(isunsigned(s_c_o_x), "bit operation needs unsigned type"); \ + size_t s_c_o_ret = 0; \ + while (s_c_o_x) { \ + s_c_o_ret += __builtin_popcountll((unsigned long long)s_c_o_x); \ + s_c_o_x = shift_xright(s_c_o_x); \ + } \ + s_c_o_ret; \ + })) + + +/* This counts the 0-bits of the specific type of the + argument. Therefor we complement all bits. To take narrow types + into account, that result has to be cast back to the original + type. */ +# define stdc_count_zeros(...) stdc_count_ones((typeof(__VA_ARGS__))~(__VA_ARGS__)) + +# define stdc_has_single_bit(...) \ + ({ \ + auto s_h_s_b_x = (__VA_ARGS__); \ + static_assert(isunsigned(s_h_s_b_x), "bit operation needs unsigned type"); \ + (bool)(s_h_s_b_x && !((s_h_s_b_x != 1) && (s_h_s_b_x & (s_h_s_b_x - 1)))); \ + }) + +# define stdc_bit_width(...) \ + ({ \ + auto s_b_w_x = (__VA_ARGS__); \ + static_assert(isunsigned(s_b_w_x), "bit operation needs unsigned type"); \ + stdc_count_ones(tominusone(s_b_w_x))-stdc_leading_zeros(s_b_w_x); \ + }) + +# define stdc_bit_floor(...) \ + ({ \ + auto s_b_f_x = (__VA_ARGS__); \ + static_assert(isunsigned(s_b_f_x), "bit operation needs unsigned type"); \ + (!s_b_f_x) \ + ? 0 \ + : (toone(s_b_f_x)<<(stdc_bit_width(s_b_f_x)-1)); \ + }) + +# define stdc_bit_ceil(...) \ + (toone(__VA_ARGS__)< +#include +#include + +enum { buf_max = 32, }; + +int main(int argc, char* argv[argc+1]) { + int ret = EXIT_FAILURE; + char buffer[buf_max] = { }; + for (int i = 1; i < argc; ++i) { // Processes args + FILE* instream = fopen(argv[i], "r"); // as filenames + if (instream) { + while (fgets(buffer, buf_max, instream)) { + fputs(buffer, stdout); + } + fclose(instream); + ret = EXIT_SUCCESS; + } else { + /* Provides some error diagnostic. */ + fprintf(stderr, "Could not open %s: ", argv[i]); + perror(0); + errno = 0; // Resets the error code + } + } + return ret; +} diff --git a/circular.c b/circular.c new file mode 100644 index 0000000..e69bc77 --- /dev/null +++ b/circular.c @@ -0,0 +1,174 @@ +#include +#include +#include "circular.h" + +circular* circular_new(size_t len); +void circular_delete(circular* c); + +#if __GNUC__ > 4 && __GNUC__ <= 14 +# pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#endif + +[[deprecated("implementation")]] +circular* circular_init(circular* c, size_t cap) { + if (c) { + if (cap) { + *c = (circular){ + .cap = cap, + .tab = malloc(sizeof(double[cap])), + }; + // Allocation failed. + if (!c->tab) c->cap = 0; + } else { + *c = (circular){ }; + } + } + return c; +} + +[[deprecated("implementation")]] +void circular_destroy(circular* c) { + if (c) { + free(c->tab); + circular_init(c, 0); + } +} + +[[deprecated("implementation")]] +size_t circular_getlength(circular const* c) { + return c ? c->len : 0; +} + +[[deprecated("implementation")]] +static size_t circular_getpos(circular const c[static 1], size_t pos) { + if (c->cap) { + pos += c->start; + pos %= c->cap; + } + return pos; +} + +[[deprecated("implementation")]] +circular* circular_append(circular* c, double value) { + if (c) { + double* where = circular_element(c, c->len); + if (where) { + *where = value; + ++c->len; + return c; + } + } + return nullptr; +} + +[[deprecated("implementation")]] +double* circular_element(circular const* c, size_t pos) { + double* ret = nullptr; + if (c) { + if (pos < c->cap) { + pos = circular_getpos(c, pos); + ret = &c->tab[pos]; + } + } + return ret; +} + +[[deprecated("implementation")]] +double circular_pop(circular* c) { + double ret = 0.0; + if (c && c->len) { + double* p = circular_element(c, 0); + if (p) ret = *p; + ++c->start; + --c->len; + } + return ret; +} + +[[nodiscard("returned pointer replaces function argument")]] +[[deprecated("implementation")]] +circular* circular_resize(circular* c, size_t nlen) { + if (c) { + size_t len = c->len; + if (len > nlen) return nullptr; + size_t olen = c->cap; + if (nlen != olen) { + size_t ostart = circular_getpos(c, 0); + size_t nstart = ostart; + double* otab = c->tab; + double* ntab; + if (nlen > olen) { + ntab = realloc(c->tab, sizeof(double[nlen])); + if (!ntab) return nullptr; + // non-empty, and there were already two separate chunks + if (ostart+len > olen) { + size_t ulen = olen - ostart; + size_t llen = len - ulen; + if (llen <= (nlen - olen)) { + /* Copy the lower one up after the old end. */ /*@\label{cpy-lowchunk}*/ + memcpy(ntab + olen, ntab, + llen*sizeof(double)); + } else { + /* Move the upper one up to the new end. */ /*@\label{cpy-hichunk}*/ + nstart = nlen - ulen; + memmove(ntab + nstart, ntab + ostart, + ulen*sizeof(double)); + } + } + } else { + if (ostart+len > olen) { + // Two separate chunks; mv the upper one down to the new end. + size_t ulen = olen - ostart; + nstart = nlen - ulen; + memmove(otab + nstart, otab + ostart, ulen*sizeof(double)); + } else { + // A single chunk + if (ostart + len > nlen) { + // Reallocation cuts the existing chunk in two. + memmove(otab, otab + ostart, len*sizeof(double)); + nstart = 0; + } + } + if (nlen) { + // Now all data is saved in the conserved part of the array. + ntab = realloc(c->tab, sizeof(double[nlen])); + // If realloc fails in this case (would be weird), just overrule it. + if (!ntab) ntab = otab; + } else { + // There had been no elements and we were asked to reduce + // the buffer to 0 elements. + free(c->tab); + ntab = nullptr; + nstart = 0; + } + } + *c = (circular){ + .cap = nlen, + .start = nstart, + .len = len, + .tab = ntab, + }; + } + } + return c; +} + +[[deprecated("implementation")]] +void circular_fput(circular* c, FILE* s) { + if (c) { + size_t len = circular_getlength(c); + double* tab = c->tab; + if (tab) { + fprintf(s, "%p+%zu (%zu+%zu):", (void*)tab, c->cap, c->start, len); + for (size_t i = 0; i < len; ++i) { + double* elp = circular_element(c, i); + // We know that c is not null, and so elp never will be. + if (!elp) unreachable(); + fprintf(s, "\t%g", *elp); + } + fputc('\n', s); + return; + } + } + fputs("invalid circular\n", s); +} diff --git a/circular.h b/circular.h new file mode 100644 index 0000000..9631ed3 --- /dev/null +++ b/circular.h @@ -0,0 +1,123 @@ +#ifndef CIRCULAR_H +# define CIRCULAR_H 1 + +# include "c23-fallback.h" +# include + +typedef struct circular circular; + +/** @brief a type for a circular buffer for @c double values + ** + ** This data structure allows to add @c double values in rear and to + ** take them out in front. Each such structure has a maximal amount + ** of elements that can be stored in it. + ** + **/ +/* + * + * Direct access to all the elements is deprecated. + */ +struct circular { + size_t start [[deprecated("privat")]]; /* First element */ + size_t len [[deprecated("privat")]]; /* Number of elements*/ + size_t cap [[deprecated("privat")]]; /* Maximum capacity */ + double* tab [[deprecated("privat")]]; /* Data array */ +}; + +/** + ** @name Initialization and destruction + ** @{ + **/ + +/** @brief Initialize a circular buffer @a c with maximally @a + ** cap elements. + ** + ** Only use this function on an uninitialized buffer. + ** + ** Each buffer that is initialized with this function must be + ** destroyed with a call to ::circular_destroy. + **/ +circular* circular_init(circular* c, size_t cap); + +/** @brief Destroy circular buffer @a c + ** + ** @a c must have been initialized with a call to ::circular_init + **/ +void circular_destroy(circular* c); + +/** @brief Delete circular buffer @a c + ** + ** @a c must have been allocated with a call to ::circular_new + **/ +inline +void circular_delete(circular* c) { + circular_destroy(c); + free(c); +} +/** + ** @} + **/ + +/** @brief Allocate and initialize a circular buffer with maximally + ** @a len elements. + ** + ** Each buffer that is allocated with this function must be deleted + ** with a call to ::circular_delete. + **/ +[[nodiscard("pointer to allocated data dropped")]] +[[__gnu__::__malloc__, __gnu_free__(circular_delete)]] +inline +circular* circular_new(size_t len) { + return circular_init(malloc(sizeof(circular)), len); +} + +/** + ** @name Using elements of a circular + ** @{ + **/ + +/** @brief Append a new element with value @a value to the buffer @a c + ** + ** @return c if the new element could be appended, null otherwise. + **/ +circular* circular_append(circular* c, double value); + +/** @brief Remove the oldest element from @a c and return its value + ** + ** @return the removed element if it exists, @c 0.0 otherwise. + **/ +double circular_pop(circular* c); + +/** @brief Return a pointer to position @a pos in buffer @a c + ** + ** @return a pointer to element @a pos of the buffer, null otherwise. + **/ +double* circular_element(circular const* c, size_t pos); + + +/** + ** @} + **/ + +/** + ** @name Maintain a circular + ** @{ + **/ + +/** @brief Return the number of elements stored. */ +size_t circular_getlength(circular const* c); + +/** @brief Resize to capacity @a cap. */ +[[nodiscard("returned pointer replaces function argument")]] +circular* circular_resize(circular* c, size_t cap); + +/** @brief Print the buffer values to stream @a s. */ +void circular_fput(circular* c, FILE* s); + +/** + ** @} + **/ + + + +#endif diff --git a/constexpr.c b/constexpr.c new file mode 100644 index 0000000..81bd929 --- /dev/null +++ b/constexpr.c @@ -0,0 +1,29 @@ +#include "c23-fallback.h" +#include +#include + +#if __is_identifier(constexpr) +# error "constexpr is needed for this test" +// really stop it +# include "really stop useless compilation right now" +#endif + + +#define eatit(P) printf("" #P ": %p\n", &(P)) + +[[__maybe_unused__]] static constexpr size_t size_max = (size_t)-1; + +[[__maybe_unused__]] constexpr long double π = 3.141'592'653'589'793'238'46; +[[__maybe_unused__]] constexpr long double π² = π*π; + +[[__maybe_unused__]] constexpr div_t one = { .quot = 1, .rem = 0, }; + +[[__maybe_unused__]] constexpr double E𝟏[2][2] = { {1, 0,}, {0, 1,}, }; + +int main(void) { + eatit(size_max); + eatit(π); + eatit(π²); + eatit(one); + eatit(E𝟏); +} diff --git a/crash.c b/crash.c new file mode 100644 index 0000000..3a99d9e --- /dev/null +++ b/crash.c @@ -0,0 +1,33 @@ +#include "c23-fallback.h" +#include +#include +#include +#include +#include "crash.h" + +void enable_alignment_check(void); +typedef complex double cdbl; + +int main(void) { + enable_alignment_check(); + /* An overlay of complex values and bytes. */ + union { + cdbl val[2]; + unsigned char buf[sizeof(cdbl[2])]; + } toocomplex = { + .val = { 0.5 + 0.5*I, 0.75 + 0.75*I, }, + }; + printf("size/alignment: %zu/%zu\n", + sizeof(cdbl), alignof(cdbl)); + /* Run over all offsets, and crash on misalignment. */ + for (size_t offset = sizeof(cdbl); offset; offset /=2) { + printf("offset\t%zu:\t", offset); + fflush(stdout); + cdbl* bp = (cdbl*)(&toocomplex.buf[offset]); // align! + printf("%g\t+%gI\t", creal(*bp), cimag(*bp)); + fflush(stdout); + *bp *= *bp; + printf("%g\t+%gI", creal(*bp), cimag(*bp)); + fputc('\n', stdout); + } +} diff --git a/crash.h b/crash.h new file mode 100644 index 0000000..6f522b8 --- /dev/null +++ b/crash.h @@ -0,0 +1,37 @@ +#ifndef CRASH_H +#define CRASH_H 1 + +/** + ** @brief enable alignment check for i386 processors + ** + ** Intel's i386 processor family is quite tolerant in accepting + ** misalignment of data. This can lead to irritating bugs when ported + ** to other architectures that are not as tolerant. + ** + ** This function enables a check for this problem also for this + ** family or processors, such that you can be sure to detect this + ** problem early. + ** + ** I found that code on Ygdrasil's blog: + ** http://orchistro.tistory.com/206 + **/ + +inline +void enable_alignment_check(void) { +#if defined(__GNUC__) +# if defined(__x86_64__) + __asm__("pushf\n" + "\torl $0x40000, (%%rsp)\n" + "\tpopf" + : : : "cc"); +# elif defined(__i386__) + __asm__("pushf\n" + "\torl $0x40000, (%%esp)\n" + "\tpopf" + : : : "cc"); +# endif +#endif +} + + +#endif diff --git a/decimal-binary.c b/decimal-binary.c new file mode 100644 index 0000000..921e9d7 --- /dev/null +++ b/decimal-binary.c @@ -0,0 +1,38 @@ +#include +#include + +int main(int argc, [[maybe_unused]] char* argv[argc+1]) { + // This 5²¹ + unsigned long long m10 = 476837158203125; + _Decimal64 f10 = m10; + // Because many of the 5's can be canceled by the 10⁻¹⁵, this is + // exact + _Decimal64 a10 = 2.097'152E-15DD; + _Decimal64 p10 = a10*f10; + double f2 = m10; + // This is not an exact representation in binary floating types + double a2u = 2.097'152E-15; + double p2 = a2u*f2; + printf("value of (double)5²¹ as binary\t\t\t\t%a\n", f2); + printf("value of (double)5⁻²¹ as binary\t\t\t\t%a\n", a2u); + printf("value of (double)5²¹⋅(double)5⁻²¹ as binary\t\t%a\n", p2); + printf("value of 5²¹ as decimal integer\t\t\t\t%llu\n", m10); + printf("value of (double)5²¹ as decimal\t\t\t\t%.26f\n", f2); + printf("value of (_Decimal64)5²¹ as decimal\t\t\t476837158203125.00000000000000000000000000\n"); + printf("value of (double)5⁻²¹ as decimal\t\t\t%.40e\n", a2u); + printf("value of (_Decimal64)5⁻²¹ as decimal\t\t\t2.0971520000000000000000000000000000000000e-15\n"); + printf("value of (double)5²¹⋅(double)5⁻²¹ as decimal\t\t%.40e\n", p2); + printf("value of (_Decimal64)5²¹⋅(_Decimal64)5⁻²¹ as decimal\t%.40e\n", (double)p10); + printf("equality of roundtrip (_Decimal64)5⁻²¹ through binary:\t\t\t\t\t%s\n", ((_Decimal64)(double)a10) == a10 ? "yes" : "no"); + printf("equality of roundtrip (double)5⁻²¹ through decimal:\t\t\t\t\t%s\n", ((double)(_Decimal64)a2u) == a2u ? "yes" : "no"); + printf("equality of roundtrip (_Decimal64)5²¹ through binary:\t\t\t\t\t%s\n", ((_Decimal64)(double)f10) == f10 ? "yes" : "no"); + printf("equality of roundtrip (double)5²¹ through decimal:\t\t\t\t\t%s\n", ((double)(_Decimal64)f2) == f2 ? "yes" : "no"); + printf("equality of roundtrip (_Decimal64)5²¹⋅(_Decimal64)5⁻²¹ through binary:\t\t\t%s\n", ((_Decimal64)(double)p10) == p10 ? "yes" : "no"); + printf("equality of roundtrip (double)5²¹⋅(double)5⁻²¹ through decimal:\t\t\t\t%s\n", ((double)(_Decimal64)p2) == p2 ? "yes" : "no"); + printf("equality (_Decimal64)5⁻²¹ and (double)5⁻²¹ in binary:\t\t\t\t\t%s\n", ((double)a10) == a2u ? "yes" : "no"); + printf("equality (_Decimal64)5⁻²¹ and (double)5⁻²¹ in decimal:\t\t\t\t\t%s\n", a10 == ((_Decimal64)a2u) ? "yes" : "no"); + printf("equality (_Decimal64)5²¹ and (double)5²¹ in binary:\t\t\t\t\t%s\n", ((double)f10) == f2 ? "yes" : "no"); + printf("equality (_Decimal64)5²¹ and (double)5²¹ in decimal:\t\t\t\t\t%s\n", f10 == ((_Decimal64)f2) ? "yes" : "no"); + printf("equality (_Decimal64)5²¹⋅(_Decimal64)5⁻²¹ and (double)5²¹⋅(double)5⁻²¹ in binary:\t%s\n", ((double)p10) == p2 ? "yes" : "no"); + printf("equality (_Decimal64)5²¹⋅(_Decimal64)5⁻²¹ and (double)5²¹⋅(double)5⁻²¹ in decimal:\t%s\n", p10 == ((_Decimal64)p2) ? "yes" : "no"); +} diff --git a/embed.c b/embed.c new file mode 100644 index 0000000..e874762 --- /dev/null +++ b/embed.c @@ -0,0 +1,57 @@ +/** + ** @brief A toy example for a usage of the #embed directive. + ** + ** We just embed the source of this program into the executable and + ** print it. + ** + ** If the #embed directive is entirely supported, the preprocessor + ** should do macro expansion of `__FILE__`. If the directory of the + ** source file is in the include path for embed.c, the source should + ** just be included like that. + ** + ** If the directive is not supported you could try to compile this + ** anyhow by using https://sentido-labs.com/en/library/cedro/. Cedro + ** does not allow to do macro expansion for the argument of the + ** directive, so we have to give this explicitly. + ** + ** For the example itself to output properly, the source and + ** execution encoding should not be too different. + **/ + +#include "c23-fallback.h" +#include +#include + +// define a character array that will contain the entire +// source file +static char const here[] = { + +// Cedro does not work with blanks between the # and any directive +#pragma Cedro 1.0 embed +#embed "embed.c" + +}; + +// define another character array that has the same size +static char there[sizeof here]; + +int main(int argc, char* argv[static argc+1]) { + size_t ibytes = 0; + int cmp = 1000; + // Open the file in binary mode. + FILE* inp = fopen(__FILE__, "rb"); + if (inp) { + // read the file as binary + ibytes = fread(there, 1, sizeof there, inp); + cmp = memcmp(here, there, sizeof here); + fclose(inp); + } else { + printf("could not open %s\n", __FILE__); + printf("+++++++++++++++++++++++++++++++++++++++++++++++\n"); + } + size_t obytes = fwrite(here, 1, sizeof here, stdout); + printf("+++++++++++++++++++++++++++++++++++++++++++++++\n"); + printf("in %zu, out %zu, bytes are %s\n", + ibytes, obytes, + cmp < 0 ? "smaller" : (cmp > 0 ? "greater" : "equal")); +} diff --git a/endianness.c b/endianness.c new file mode 100644 index 0000000..ec589d7 --- /dev/null +++ b/endianness.c @@ -0,0 +1,19 @@ +#include +#include + +typedef union unsignedInspect unsignedInspect; +union unsignedInspect { + unsigned val; + unsigned char bytes[sizeof(unsigned)]; +}; +unsignedInspect twofold = { .val = 0xAABBCCDD, }; + +int main(void) { + printf("value is 0x%.08X\n", twofold.val); + for (size_t i = 0; i < sizeof twofold.bytes; ++i) + printf("byte[%zu]: 0x%.02hhX\n", i, twofold.bytes[i]); + unsigned val = 0xAABBCCDD; + unsigned char* valp = (unsigned char*)&val; + for (size_t i = 0; i < sizeof val; ++i) + printf("byte[%zu]: 0x%.02hhX\n", i, valp[i]); +} diff --git a/enum.c b/enum.c new file mode 100644 index 0000000..73c21a4 --- /dev/null +++ b/enum.c @@ -0,0 +1,93 @@ +#include "c23-fallback.h" +#include + +typedef enum echar echar; +enum echar { + mchar = UCHAR_MAX, +}; + +typedef enum eshort eshort; +enum eshort { + mshort = USHRT_MAX, +}; + +typedef enum eint eint; +enum eint { + mint = UINT_MAX, +}; + +typedef enum elong elong; +enum elong { + mlong = ULONG_MAX, +}; + +typedef enum ellong ellong; +enum ellong { + mllong = ULLONG_MAX, +}; + +#ifdef UINT128_MAX +typedef enum e128 e128; +enum e128 { + m128 = UINT128_MAX, +}; +#endif + +#if __has_feature(c_fixed_enum) || (__clang_major__ > 7) + +enum fchar : unsigned char { + nchar = UCHAR_MAX, +}; +typedef enum fchar fchar; + +enum fshort : unsigned short { + nshort = USHRT_MAX, +}; +typedef enum fshort fshort; + +enum fint : unsigned { + nint = UINT_MAX, +}; +typedef enum fint fint; + +enum flong : unsigned long { + nlong = ULONG_MAX, +}; +typedef enum flong flong; + +enum fllong : unsigned long long { + nllong = ULLONG_MAX, +}; +typedef enum fllong fllong; + +#ifdef UINT128_MAX +enum f128 : uint128_t { + n128 = UINT128_MAX, +}; +typedef enum f128 f128; +#endif +#else +# warning "test for fixed underlying integer type skipped" +#endif + +int main(void) { + printf("echar:\t%zu\t%zu\n", sizeof(echar), sizeof(mchar)); + printf("eshort:\t%zu\t%zu\n", sizeof(eshort), sizeof(mshort)); + printf("eint:\t%zu\t%zu\n", sizeof(eint), sizeof(mint)); + printf("elong:\t%zu\t%zu\n", sizeof(elong), sizeof(mlong)); + printf("ellong:\t%zu\t%zu\n", sizeof(ellong), sizeof(mllong)); +#ifdef UINT128_MAX + printf("e128:\t%zu\t%zu\n", sizeof(e128), sizeof(m128)); +#endif + +#if __has_feature(c_fixed_enum) || (__clang_major__ > 7) + printf("fchar:\t%zu\t%zu\n", sizeof(fchar), sizeof(nchar)); + printf("fshort:\t%zu\t%zu\n", sizeof(fshort), sizeof(nshort)); + printf("fint:\t%zu\t%zu\n", sizeof(fint), sizeof(nint)); + printf("flong:\t%zu\t%zu\n", sizeof(flong), sizeof(nlong)); + printf("fllong:\t%zu\t%zu\n", sizeof(fllong), sizeof(nllong)); +# ifdef UINT128_MAX + printf("f128:\t%zu\t%zu\n", sizeof(f128), sizeof(n128)); +# endif +#endif +} diff --git a/esc.c b/esc.c new file mode 100644 index 0000000..717e556 --- /dev/null +++ b/esc.c @@ -0,0 +1,12 @@ +#include "esc.h" + +void esc_move(FILE* f, int vert, int hori) { + if (vert > 0) fprintf(f, ESC_CSI "%dB", vert); + else if (vert < 0) fprintf(f, ESC_CSI "%dA", -vert); + if (hori > 0) fprintf(f, ESC_CSI "%dC", hori); + else if (hori < 0) fprintf(f, ESC_CSI "%dD", -hori); +} + +void esc_goto(FILE* f, unsigned vert, unsigned hori) { + fprintf(f, ESC_CSI "%u;%uH", vert, hori); +} diff --git a/esc.h b/esc.h new file mode 100644 index 0000000..d355c90 --- /dev/null +++ b/esc.h @@ -0,0 +1,156 @@ +#ifndef ESCAPE_H +#define ESCAPE_H 1 + +#include + +#define ESC_CSI "\e[" +#define ESC_SS2 "\eN" +#define ESC_SS3 "\eO" +#define ESC_SCI "\eZ" +#define ESC_ROI "\e%" + +#define ESC_SAVE ESC_CSI "s" +#define ESC_RESTORE ESC_CSI "u" +#define ESC_HOME ESC_CSI "H" +#define ESC_CLRSCR ESC_CSI "1J" +#define ESC_CLEAR ESC_CSI "2K" +#define ESC_UP ESC_CSI "A" +#define ESC_DOWN ESC_CSI "B" +#define ESC_FRWD ESC_CSI "C" +#define ESC_BKWD ESC_CSI "D" +#define ESC_END ESC_CSI "F" +#define ESC_INSERT ESC_CSI "2~" +#define ESC_DELETE ESC_CSI "3~" +#define ESC_PGUP ESC_CSI "5~" +#define ESC_PGDOWN ESC_CSI "6~" + +// F1 to F4 are special +#define ESC_F1 ESC_SS3 "P" +#define ESC_F2 ESC_SS3 "Q" +#define ESC_F3 ESC_SS3 "R" +#define ESC_F4 ESC_SS3 "S" +#define ESC_S_F1 ESC_CSI "1;2P" +#define ESC_S_F2 ESC_CSI "1;2Q" +#define ESC_S_F3 ESC_CSI "1;2R" +#define ESC_S_F4 ESC_CSI "1;2S" +#define ESC_C_F1 ESC_CSI "1;5P" +#define ESC_C_F2 ESC_CSI "1;5Q" +#define ESC_C_F3 ESC_CSI "1;5R" +#define ESC_C_F4 ESC_CSI "1;5S" + +#define ESC_F5 ESC_CSI "15~" +#define ESC_F6 ESC_CSI "17~" +#define ESC_F7 ESC_CSI "18~" +#define ESC_F8 ESC_CSI "19~" +#define ESC_F9 ESC_CSI "20~" +#define ESC_F10 ESC_CSI "21~" +#define ESC_F11 ESC_CSI "23~" +#define ESC_F12 ESC_CSI "24~" + +#define ESC_S_F5 ESC_CSI "15;2~" +#define ESC_S_F6 ESC_CSI "17;2~" +#define ESC_S_F7 ESC_CSI "18;2~" +#define ESC_S_F8 ESC_CSI "19;2~" +#define ESC_S_F9 ESC_CSI "20;2~" +#define ESC_S_F10 ESC_CSI "21;2~" +#define ESC_S_F11 ESC_CSI "23;2~" +#define ESC_S_F12 ESC_CSI "24;2~" + +#define ESC_C_F5 ESC_CSI "15;5~" +#define ESC_C_F6 ESC_CSI "17;5~" +#define ESC_C_F7 ESC_CSI "18;5~" +#define ESC_C_F8 ESC_CSI "19;5~" +#define ESC_C_F9 ESC_CSI "20;5~" +#define ESC_C_F10 ESC_CSI "21;5~" +#define ESC_C_F11 ESC_CSI "23;5~" +#define ESC_C_F12 ESC_CSI "24;5~" + +#define ESC_HIDE ESC_CSI "?25l" +#define ESC_SHOW ESC_CSI "?25h" + +#define ESC_NORMAL ESC_CSI "0m" +#define ESC_BOLD ESC_CSI "1m" +#define ESC_UNDERL ESC_CSI "4m" +#define ESC_BLINK ESC_CSI "5m" +#define ESC_INVERS ESC_CSI "7m" +#define ESC_FRAMED ESC_CSI "51m" +#define ESC_CIRCLED ESC_CSI "52m" +#define ESC_OVERL ESC_CSI "53m" + +#define ESC_TBLACK ESC_CSI "30m" +#define ESC_TRED ESC_CSI "38;2;255;0;0m" +#define ESC_TGREEN ESC_CSI "38;2;0;255;0m" +#define ESC_TYELLOW ESC_CSI "38;2;255;255;0m" +#define ESC_TBLUE ESC_CSI "38;2;0;0;255m" +#define ESC_TMAGENTA ESC_CSI "38;2;255;0;255m" +#define ESC_TCYAN ESC_CSI "38;2;0;255;255m" +#define ESC_TWHITE ESC_CSI "37m" + +#define ESC_BBLACK ESC_CSI "40m" +#define ESC_BRED ESC_CSI "48;2;255;0;0m" +#define ESC_BGREEN ESC_CSI "48;2;0;255;0m" +#define ESC_BYELLOW ESC_CSI "48;2;255;255;0m" +#define ESC_BBLUE ESC_CSI "48;2;0;0;255m" +#define ESC_BMAGENTA ESC_CSI "48;2;255;0;255m" +#define ESC_BCYAN ESC_CSI "48;2;0;255;255m" +#define ESC_BWHITE ESC_CSI "47m" + +#define ESC_BLIGHTGREY ESC_CSI "48;2;220;220;220m" +#define ESC_BGREY ESC_CSI "48;2;128;128;128m" + + +enum { esc_top = 1, esc_bottom = 2, esc_left = 4, esc_right = 8, }; + +#define ESC_BORDER \ +((char const*const[]){ \ +[0] = " ", \ + [esc_top] = "━", \ + [esc_bottom] = "━", \ + [esc_bottom | esc_top] = "━", \ + [esc_left] = "┃", \ + [esc_left | esc_top] = "┏", \ + [esc_left | esc_bottom] = "┗", \ + [esc_left | esc_bottom | esc_top] = "┣", \ + [esc_right] = "┃", \ + [esc_right | esc_top] = "┓", \ + [esc_right | esc_bottom] = "┛", \ + [esc_right | esc_bottom | esc_top] = "┫", \ + [esc_right | esc_left] = "┃", \ + [esc_right | esc_left | esc_top] = "┳", \ + [esc_right | esc_left | esc_bottom] = "┻", \ + [esc_right | esc_left | esc_bottom | esc_top] = "╋", \ + }) + +enum { esc_nw = 1, esc_ne = 2, esc_sw = 4, esc_se = 8, }; + +#define ESC_BLOCK \ +((char const*const[]){ \ +[0] = " ", \ + [esc_nw] = "▘", \ + [esc_ne] = "▝", \ + [esc_ne | esc_nw] = "▀", \ + [esc_sw] = "▖", \ + [esc_sw | esc_nw] = "▌", \ + [esc_sw | esc_ne] = "▞", \ + [esc_sw | esc_ne | esc_nw] = "▛", \ + [esc_se] = "▗", \ + [esc_se | esc_nw] = "▚", \ + [esc_se | esc_ne] = "▐", \ + [esc_se | esc_ne | esc_nw] = "▜", \ + [esc_se | esc_sw] = "▄", \ + [esc_se | esc_sw | esc_nw] = "▙", \ + [esc_se | esc_sw | esc_ne] = "▟", \ + [esc_se | esc_sw | esc_ne | esc_nw] = "█", \ + }) + +/** + ** @brief relative cursor movement + **/ +void esc_move(FILE* f, int vert, int hori); + +/** + ** @brief asolute cursor movement + **/ +void esc_goto(FILE* f, unsigned vert, unsigned hori); + +#endif diff --git a/euclid.c b/euclid.c new file mode 100644 index 0000000..72d7648 --- /dev/null +++ b/euclid.c @@ -0,0 +1,15 @@ +#include "euclid.h" + +size_t gcd(size_t a, size_t b) [[__unsequenced__]]; + +int main(int argc, char* argv[argc+1]) { + size_t prev = 0; + for (int i = 1; i < argc; ++i) { // Processes args + size_t act = strtoull(argv[i], nullptr, 0); // arg -> unsigned long long + if (prev) + printf("gcd(%zu, %zu) is %zu\n", + prev, act, gcd(prev, act)); + prev = act; + } + return EXIT_SUCCESS; +} diff --git a/euclid.h b/euclid.h new file mode 100644 index 0000000..82fca4e --- /dev/null +++ b/euclid.h @@ -0,0 +1,24 @@ +#ifndef EUCLID_H +# define EUCLID_H 1 + +# include "c23-fallback.h" +# include +# include + +inline size_t gcd2(size_t a, size_t b) [[__unsequenced__]] { + assert(a <= b); /*@\label{gcd2-precondition}*/ + if (!a) return b; /*@\label{gcd2-bottom}*/ + size_t rem = b % a; /*@\label{gcd2-remainder}*/ + return gcd2(rem, a); /*@\label{gcd2-recurse}*/ +} + +inline size_t gcd(size_t a, size_t b) [[__unsequenced__]] { + assert(a); + assert(b); + if (a < b) + return gcd2(a, b); + else + return gcd2(b, a); +} + +#endif diff --git a/extern.c b/extern.c new file mode 100644 index 0000000..186596d --- /dev/null +++ b/extern.c @@ -0,0 +1,13 @@ +#include + +unsigned i = 1; /*@\label{line:decl-i-1}*/ + +int main(void) { + unsigned i = 2; /* A new object *//*@\label{line:decl-i-2}*/ + if (i) { + extern unsigned i; /* An existing object *//*@\label{line:decl-i-3}*/ + printf("%u\n", i); /* prints 1 */ + } else { + printf("%u\n", i); /* prints 2 */ + } +} diff --git a/fibonacci.c b/fibonacci.c new file mode 100644 index 0000000..640304e --- /dev/null +++ b/fibonacci.c @@ -0,0 +1,19 @@ +#include +#include "c23-fallback.h" + +size_t fib(size_t n) [[__unsequenced__]] { + if (n < 3) + return 1; + else + return fib(n-1) + fib(n-2); +} + + +int main(int argc, char *argv[]) { + for (int i = 1; i < argc; ++i) { // Processes args + size_t const n = strtoull(argv[i], nullptr, 0); // arg -> size_t + printf("fib(%zu) is %zu\n", + n, fib(n)); + } + return EXIT_SUCCESS; +} diff --git a/fibonacci2.c b/fibonacci2.c new file mode 100644 index 0000000..84d3582 --- /dev/null +++ b/fibonacci2.c @@ -0,0 +1,26 @@ +#include +#include "c23-fallback.h" + +void fib2rec(size_t n, size_t buf[static 2]) [[__unsequenced__]] { + if (n > 2) { + size_t res = buf[0] + buf[1]; + buf[1] = buf[0]; + buf[0] = res; + fib2rec(n-1, buf); + } +} + +size_t fib2(size_t n) [[__unsequenced__]] { + size_t res[2] = { 1, 1, }; + fib2rec(n, res); + return res[0]; +} + +int main(int argc, char* argv[argc+1]) { + for (int i = 1; i < argc; ++i) { // Processes args + size_t const n = strtoull(argv[i], nullptr, 0); // arg -> size_t + printf("fib(%zu) is %zu\n", + n, fib2(n)); + } + return EXIT_SUCCESS; +} diff --git a/fibonacci3.c b/fibonacci3.c new file mode 100644 index 0000000..2ed77ef --- /dev/null +++ b/fibonacci3.c @@ -0,0 +1,27 @@ +#include "c23-fallback.h" +#include + +void fib2rec(size_t n, size_t buf[2]) { + if (n) { + size_t res = buf[0] + buf[1]; + buf[1] = buf[0]; + buf[0] = res; + fib2rec(n-1, buf); + } +} + + +size_t fib2(size_t n) { + size_t res[2] = { 1, 1, }; + if (n > 2) fib2rec(n - 2, res); + return res[0]; +} + +int main(int argc, char* argv[argc+1]) { + for (int i = 1; i < argc; ++i) { // process args + size_t const n = strtoull(argv[i], nullptr, 0); // arg -> size_t + printf("fib(%zu) is %zu\n", + n, fib2(n)); + } + return EXIT_SUCCESS; +} diff --git a/fibonacci4.c b/fibonacci4.c new file mode 100644 index 0000000..ff250c7 --- /dev/null +++ b/fibonacci4.c @@ -0,0 +1,30 @@ +#include "c23-fallback.h" +#include + +/** + ** Rewrite the recursive Fibonacci such that it alternates the use of + ** the buffers. + **/ + +void fib2rec(size_t n, size_t buf[2]) { + if (n) { + buf[n%2] += buf[!(n%2)]; + fib2rec(n-1, buf); + } +} + + +size_t fib2(size_t n) { + size_t res[2] = { 1, 1, }; + if (n > 2) fib2rec(n - 2, res); + return res[1]; +} + +int main(int argc, char* argv[argc+1]) { + for (int i = 1; i < argc; ++i) { // process args + size_t const n = strtoull(argv[i], nullptr, 0); // arg -> size_t + printf("fib(%zu) is %zu\n", + n, fib2(n)); + } + return EXIT_SUCCESS; +} diff --git a/fibonacci5.c b/fibonacci5.c new file mode 100644 index 0000000..c642aa5 --- /dev/null +++ b/fibonacci5.c @@ -0,0 +1,30 @@ +#include "c23-fallback.h" +#include + +/** + ** Rewrite the recursive Fibonacci such that it unrolls two + ** successive recursive calls. + **/ + +void fib2rec(size_t n, size_t buf[2]) { + if (n > 1) { + buf[0] += buf[1]; + buf[1] += buf[0]; + fib2rec(n-2, buf); + } +} + +size_t fib2(size_t n) { + size_t res[2] = { 1, 1, }; + if (n > 2) fib2rec(n-1, res); + return res[!(n%2)]; +} + +int main(int argc, char* argv[argc+1]) { + for (int i = 1; i < argc; ++i) { // process args + size_t const n = strtoull(argv[i], nullptr, 0); // arg -> size_t + printf("fib(%zu) is %zu\n", + n, fib2(n)); + } + return EXIT_SUCCESS; +} diff --git a/fibonacci6.c b/fibonacci6.c new file mode 100644 index 0000000..2090ccd --- /dev/null +++ b/fibonacci6.c @@ -0,0 +1,29 @@ +#include "c23-fallback.h" +#include + +/** + ** Rewrite Fibonacci iteratively such that is proceeds in pairs of + ** values and hopefully doesn't spill any of the variables to memory. + **/ + +size_t fib2(size_t n) { + register size_t x1 = 1; // F(x1) for x1 = (n+1)%2 + 1 + register size_t x2 = n%2 ? 1 : 2; // F(x1+1) + for (register size_t i = (n-1)/2; i; --i) { + x1 += x2; + x2 += x1; + } + return x1; // F(y) with y = x1 + 2*((n-1)/2) + // = x1 + ((n-1)-((n+1)%2)) + // = ((n+1)%2 + 1)+((n-1)-((n+1)%2)) + // = n +} + +int main(int argc, char* argv[argc+1]) { + for (int i = 1; i < argc; ++i) { // process args + size_t const n = strtoull(argv[i], nullptr, 0); // arg -> size_t + printf("fib(%zu) is %zu\n", + n, fib2(n)); + } + return EXIT_SUCCESS; +} diff --git a/fibonacciCache.c b/fibonacciCache.c new file mode 100644 index 0000000..b095578 --- /dev/null +++ b/fibonacciCache.c @@ -0,0 +1,50 @@ +#include "c23-fallback.h" +#include +#include + +/* Compute Fibonacci number n with the help of a cache that may + hold previously computed values. */ +size_t fibCacheRec(size_t n, size_t cache[static n]) { + if (!cache[n-1]) { + cache[n-1] + = fibCacheRec(n-1, cache) + fibCacheRec(n-2, cache); + } + return cache[n-1]; +} + +size_t fibCache(size_t n) { + if (n+1 <= 3) return 1; + /* Set up a VLA to cache the values. */ +#if __STDC_VERSION__ > 202311L + /* Since C23, VLA can be default initialized. */ + size_t cache[n] = { }; +#else + size_t cache[n]; memset(cache, 0, n*sizeof(*cache)); +#endif + /* Non-trivial initialization is replaced by assignment. */ + cache[0] = 1; cache[1] = 1; + /* Call the recursive function. */ + return fibCacheRec(n, cache); +} + +double goldenRatio(size_t n) { + if (n+1 <= 3) return 1; + size_t cache[n]; + cache[0] = 1; + cache[1] = 1; + for (size_t i = 2; i < n; ++i) + cache[i] = 0; + double ret = fibCacheRec(n, cache); + return ret/fibCacheRec(n-1, cache); +} + +int main(int argc, char* argv[argc+1]) { + for (int i = 1; i < argc; ++i) { // Processes args + size_t const n = strtoull(argv[i], nullptr, 0); // arg -> size_t + double golden = goldenRatio(n); + double control = golden*2.0-1.0; + printf("fib(%zu) is %zu, golden ratio %.20g, control %.20g\n", + n, fibCache(n), golden, control*control); + } + return EXIT_SUCCESS; +} diff --git a/fibonacciRet.c b/fibonacciRet.c new file mode 100644 index 0000000..bfefa80 --- /dev/null +++ b/fibonacciRet.c @@ -0,0 +1,46 @@ +#include "c23-fallback.h" +#include + +typedef struct fibonaccipair fibonaccipair; + +struct fibonaccipair { + size_t prev; + size_t act; +}; + +fibonaccipair fiboNext(fibonaccipair ret) { + size_t res = ret.act + ret.prev; + ret.prev = ret.act; + ret.act = res; + return ret; +} + +fibonaccipair fibonacci2(size_t n, fibonaccipair ret) { + if (n) { + return fibonacci2(n-1, fiboNext(ret)); + } else + return ret; +} + +fibonaccipair fibonacci3(size_t n, fibonaccipair ret) { + while (n --> 0) { + ret = fiboNext(ret); + } + return ret; +} + +size_t fibonacci(size_t n) { + register fibonaccipair res = { 1, 1, }; + if (n > 2) res = fibonacci2(n - 2, res); + return res.act; +} + + +int main(int argc, char* argv[argc+1]) { + for (int i = 1; i < argc; ++i) { // process args + size_t const n = strtoull(argv[i], nullptr, 0); // arg -> size_t + printf("fibonacci(%zu) is %zu\n", + n, fibonacci(n)); + } + return EXIT_SUCCESS; +} diff --git a/fp_except.c b/fp_except.c new file mode 100644 index 0000000..5dc63ec --- /dev/null +++ b/fp_except.c @@ -0,0 +1,88 @@ +#include "c23-fallback.h" +#include +#include +#include +#ifndef INFINITY +#include +#endif + +#if !defined(__STDC_IEC_559__) && !defined(__STDC_IEC_60559_BFP__) && !FE_ALL_EXCEPT +#error "floating-point arithmetic is too weird" +#endif + +static_assert(FE_ALL_EXCEPT, "floating point exceptions are not supported"); + +#ifdef FENV_OFF +#warning "switching FENV_ACCESS off" +#pragma STDC FENV_ACCESS OFF +#else +#pragma STDC FENV_ACCESS ON +#endif + +bool const has_inf = +#ifdef INFINITY + (1.0/0.0 == INFINITY) +#else + false +#endif + ; + +int excepts[] = { +#ifdef FE_DIVBYZERO + FE_DIVBYZERO, +#endif +#ifdef FE_INEXACT + FE_INEXACT, +#endif +#ifdef FE_INVALID + FE_INVALID, +#endif +#ifdef FE_OVERFLOW + FE_OVERFLOW, +#endif +#ifdef FE_UNDERFLOW + FE_UNDERFLOW, +#endif +}; + +void printexcept(void) { + char const* name[] = { +#ifdef FE_DIVBYZERO + "divbyzero", +#endif +#ifdef FE_INEXACT + "inexact", +#endif +#ifdef FE_INVALID + "invalid", +#endif +#ifdef FE_OVERFLOW + "overflow", +#endif +#ifdef FE_UNDERFLOW + "underflow", +#endif + }; + int except = fetestexcept(FE_ALL_EXCEPT); + if (except) { + printf("["); + for (unsigned j = 0; except; except &= ~excepts[j], ++j) + if (excepts[j] & except) + printf("%s ", name[j]); + printf("]"); + } +} + +int main (int argc, char* argv[static argc+1]) { + printf("division by zero is %sequal to INFINITY\n", has_inf ? "" : "un"); + for (unsigned i = 1; i < argc; i++) { + feclearexcept(FE_ALL_EXCEPT); + double x = strtod(argv[i], nullptr); + printf("%g ", x); + printexcept(); + feclearexcept(FE_ALL_EXCEPT); + printf(": %g ", 1.0/x); + printexcept(); + puts(""); + } +} diff --git a/generic.c b/generic.c new file mode 100644 index 0000000..fcfa94d --- /dev/null +++ b/generic.c @@ -0,0 +1,62 @@ +#include "generic.h" + +/* "Instantiate" the inline functions of the "generic.h" header. */ +signed (maxs)(signed a, signed b) [[__unsequenced__]]; +unsigned (maxu)(unsigned a, unsigned b) [[__unsequenced__]]; +unsigned (maxus)(unsigned a, signed b) [[__unsequenced__]]; +unsigned (maxsu)(signed a, unsigned b) [[__unsequenced__]]; +signed long (maxsl)(signed long a, signed long b) [[__unsequenced__]]; +unsigned long (maxul)(unsigned long a, unsigned long b) [[__unsequenced__]]; +unsigned long (maxusl)(unsigned long a, signed long b) [[__unsequenced__]]; +unsigned long (maxsul)(signed long a, unsigned long b) [[__unsequenced__]]; +signed long long (maxsll)(signed long long a, signed long long b) [[__unsequenced__]]; +unsigned long long (maxull)(unsigned long long a, unsigned long long b) [[__unsequenced__]]; +unsigned long long (maxusll)(unsigned long long a, signed long long b) [[__unsequenced__]]; +unsigned long long (maxsull)(signed long long a, unsigned long long b) [[__unsequenced__]]; + +int main(int argc, char* argv[argc+1]){ + if (argc > 1) { + printf("testing strto functions: base 10 = %lu, general base = %llu\n", + strtoul10(argv[2]), strtoull(argv[2], nullptr)); + long double a = strtold(argv[1]); + printf("minimum of %Lg and 1.0f is %Lg\n", + a, min(a, 1.0F)); + printf("minimum of %g and argc is %g\n", + 3.0, min(3.0, argc)); + } + printf("maxof(1) = %d\n", maxof(1)); + printf("maxof(1.0) = %g\n", maxof(1.0)); + printf("maxof(long double) = %Lg\n", maxof(long double)); + printf("minof(1ul) = %lu\n", minof(1ul)); + printf("minof(1.0) = %g\n", minof(1.0)); + printf("minof(long double) = %Lg\n", minof(long double)); + printf("mix(1u, -1l) = %luul\n", mix(1u, -1l)); + printf("mix(-1, 1ul) = %luul\n", mix(-1, 1ul)); + printf("mix(-1, 1l) = %ldl\n", mix(-1, 1l)); + printf("mix(1u, 2ul) = %luul\n", mix(1u, 2ul)); + puts((memcpy)((char[6]){ 0 }, "hello", 6)); + // Erroneous target buffer, should not be qualified. diagnostic? + puts((memcpy)((char volatile[6]){ 0 }, "he1lo", 6)); + // Erroneous use of target buffer, using 6 where there are only 5. diagnostic? + puts((memcpy)((char[5]){ 0 }, "he2lo", 6)); + // Erroneous use of source buffer, using 7 where there are only 6. diagnostic? + puts((memcpy)((char[7]){ 0 }, "he3lo", 7)); + puts(memcpy((char[6]){ 0 }, "hello", 6)); + // Erroneous target buffer, should not be qualified. diagnostic? + puts(memcpy((char volatile[6]){ 0 }, "he1lo", 6)); + // Erroneous use of target buffer, using 6 where there are only 5. diagnostic? + puts(memcpy((char[5]){ 0 }, "he2lo", 6)); + // Erroneous use of source buffer, using 7 where there are only 6. diagnostic? + puts(memcpy((char[7]){ 0 }, "he3lo", 7)); + printf("snprintf(nullptr, 0, \"%%g\", 6.555): %d\n", snprintf(nullptr, 0, "%g", 6.555)); + printf("snprintf(nullptr, 0, \"hoho\"): %d\n", snprintf(nullptr, 0, "hoho")); + // Erroneous use of nullptr when size is not zero. diagnostic? + printf("snprintf(nullptr, 0, \"%%g\", 6.555): %d\n", snprintf(nullptr, 20, "%g", 6.555)); + printf("snprintf(nullptr, 0, \"hoho\"): %d\n", snprintf(nullptr, 20, "hoho")); + // Erroneous use of buffer, claiming 20 only providing 16. diagnostic? + printf("snprintf(something, 20, \"%%g\", 6.555): %d\n", snprintf((char[16]){ 0 }, 20, "%g", 6.555)); + // Erroneous use of buffer, claiming 20 only providing 16. diagnostic? + printf("snprintf(something, 20, \"hoho\"): %d\n", snprintf((char[16]){ 0 }, 20, "hoho")); + return EXIT_SUCCESS; + SIZE_WIDTH; +} diff --git a/generic.h b/generic.h new file mode 100644 index 0000000..c7c5174 --- /dev/null +++ b/generic.h @@ -0,0 +1,360 @@ +#ifndef GENERIC_H +#define GENERIC_H 1 + +#include +#include +#include "c23-fallback.h" + +#if !__has_va_opt +# error "no __VA_OPT__ support found, aborting compilation" +// shut off some subsequent error messages +# include "no __VA_OPT__ support found, aborting compilation" +#endif + +/** + ** @brief Type-generic minimum for floating-point values + **/ +#define min(A, B) \ +_Generic((A)+(B), \ + float: minf, \ + long double: minl, \ + default: mind)((A), (B)) + +static inline +double mind(double a, double b) [[__unsequenced__]] { + return a < b ? a : b; +} + +static inline +long double minl(long double a, long double b) [[__unsequenced__]] { + return a < b ? a : b; +} + +static inline +float minf(float a, float b) [[__unsequenced__]] { + return a < b ? a : b; +} + +/** + ** @brief The maximum value for the type of @a X + **/ +#define MAXVAL(X) \ +_Generic((X), \ + bool: (bool)+1, \ + char: (char)+CHAR_MAX, \ + signed char: (signed char)+SCHAR_MAX, \ + unsigned char: (unsigned char)+UCHAR_MAX, \ + signed short: (signed short)+SHRT_MAX, \ + unsigned short: (unsigned short)+USHRT_MAX, \ + signed: INT_MAX, \ + unsigned: UINT_MAX, \ + signed long: LONG_MAX, \ + unsigned long: ULONG_MAX, \ + signed long long: LLONG_MAX, \ + unsigned long long: ULLONG_MAX, \ + float: FLT_MAX, \ + double: DBL_MAX, \ + long double: LDBL_MAX) + +/** + ** @brief The maximum promoted value for @a XT, where @a XT + ** can be an expression or a type name + ** + ** So this is the maximum value when fed to an arithmetic + ** operation such as @c +. + ** + ** @remark Narrow types are promoted, usually to @c signed, + ** or maybe to @c unsigned on rare architectures. + **/ +#define maxof(XT) \ +_Generic(0+(XT)+0, \ + signed: INT_MAX, \ + unsigned: UINT_MAX, \ + signed long: LONG_MAX, \ + unsigned long: ULONG_MAX, \ + signed long long: LLONG_MAX, \ + unsigned long long: ULLONG_MAX, \ + float: FLT_MAX, \ + double: DBL_MAX, \ + long double: LDBL_MAX) + +#define MINVAL(X) \ +_Generic((X), \ + bool: (bool)+0, \ + char: (char)+CHAR_MIN, \ + signed char: (signed char)+SCHAR_MIN, \ + unsigned char: (unsigned char)+0, \ + signed short: (signed short)+SHRT_MIN, \ + unsigned short: (unsigned short)+0, \ + signed: INT_MIN, \ + unsigned: 0U, \ + signed long: LONG_MIN, \ + unsigned long: 0UL, \ + signed long long: LLONG_MIN, \ + unsigned long long: 0ULL, \ + float: -FLT_MAX, \ + double: -DBL_MAX, \ + long double: -LDBL_MAX) + +#define minof(XT) \ +_Generic(0+(XT)+0, \ + signed: INT_MIN, \ + unsigned: 0U, \ + signed long: LONG_MIN, \ + unsigned long: 0UL, \ + signed long long: LLONG_MIN, \ + unsigned long long: 0ULL, \ + float: -FLT_MAX, \ + double: -DBL_MAX, \ + long double: -LDBL_MAX) + +#define strtod(NPTR, ...) \ + STRTOD_I __VA_OPT__(I) (NPTR __VA_OPT__(,) __VA_ARGS__) +#define STRTOD_I(NPTR) strtod(NPTR, nullptr) +#define STRTOD_II(NPTR, ENDPTR) strtod(NPTR, ENDPTR) +#define strtof(NPTR, ...) \ + STRTOF_I __VA_OPT__(I) (NPTR __VA_OPT__(,) __VA_ARGS__) +#define STRTOF_I(NPTR) strtof(NPTR, nullptr) +#define STRTOF_II(NPTR, ENDPTR) strtof(NPTR, ENDPTR) +#define strtold(NPTR, ...) \ + STRTOLD_I __VA_OPT__(I) (NPTR __VA_OPT__(,) __VA_ARGS__) +#define STRTOLD_I(NPTR) strtold(NPTR, nullptr) +#define STRTOLD_II(NPTR, ENDPTR) strtold(NPTR, ENDPTR) + +/** @brief Output the second argument, if any, or `DEF0` as a default. + ** + ** Error if more than two arguments are given. + **/ +#define DEFAULT1(DEF0, ...) \ + ID_I ## __VA_OPT__(Iplus_DEFAULT) \ + (DEF0 __VA_OPT__(,) __VA_ARGS__) +#define ID_IIplus_DEFAULT(_01, ...) ID_I(__VA_ARGS__) + +#define ID_() +#define ID_I(_01) _01 +#define ID_II(_01, _2) _01, _2 +#define ID_III(_01, _2, _3) _01, _2, _3 + +/** @brief Output comma-separated arguments from the third position + ** onward, fill with defaults from the first two. + ** + ** Error if more than four arguments are given. + **/ +#define DEFAULT2(DEF0, DEF1, ...) \ + ID_II ## __VA_OPT__(Iplus_DEFAULT) \ + (DEF0, DEF1 __VA_OPT__(,) __VA_ARGS__) +#define ID_IIIplus_DEFAULT(DEF0, DEF1, _01, ...) \ + DEFAULT1(DEF0, _01), DEFAULT1(DEF1, __VA_ARGS__) + +/** @brief Output comma-separated arguments from the fourth position + ** onward, fill with defaults from the first three. + ** + ** Error if more than six arguments are given. + **/ +#define DEFAULT3(DEF0, DEF1, DEF2, ...) \ + ID_III ## __VA_OPT__(Iplus_DEFAULT) \ + (DEF0, DEF1, DEF2 __VA_OPT__(,) __VA_ARGS__) +#define ID_IIIIplus_DEFAULT(DEF0, DEF1, DEF2, _01, ...) \ + DEFAULT1(DEF0, _01), DEFAULT2(DEF1, DEF2, __VA_ARGS__) + +#define CALL1(FUNC, DEF0, ...) \ + FUNC(DEFAULT1(DEF0, __VA_ARGS__)) +#define CALL2(FUNC, DEF0, DEF1, ...) \ + FUNC(DEFAULT2(DEF0, DEF1, __VA_ARGS__)) +#define CALL3(FUNC, DEF0, DEF1, DEF2, ...) \ + FUNC(DEFAULT3(DEF0, DEF1, DEF2, __VA_ARGS__)) + +#define strtoul(...) \ + CALL3(strtoul, "0", nullptr, 0, __VA_ARGS__) +#define strtoull(...) \ + CALL3(strtoull, "0", nullptr, 0, __VA_ARGS__) +#define strtol(...) \ + CALL3(strtol, "0", nullptr, 0, __VA_ARGS__) +#define strtoll(...) \ + CALL3(strtoll, "0", nullptr, 0, __VA_ARGS__) + +/** + ** @brief Calls a three-parameter function with default arguments set to + ** specific values + ** + ** @see strtoull10 for an example + **/ +#define strtoul10(NPTR, ...) CALL3((strtoul), "0", nullptr, 10, NPTR __VA_OPT__(,) __VA_ARGS__) +#define strtoull10(NPTR, ...) CALL3((strtoull), "0", nullptr, 10, NPTR __VA_OPT__(,) __VA_ARGS__) +#define strtol10(NPTR, ...) CALL3((strtol), "0", nullptr, 10, NPTR __VA_OPT__(,) __VA_ARGS__) +#define strtoll10(NPTR, ...) CALL3((strtoll), "0", nullptr, 10, NPTR __VA_OPT__(,) __VA_ARGS__) + +#define PROMOTE(XT, A) \ + _Generic(0+(XT)+0, \ + signed: (signed)(A), \ + unsigned: (unsigned)(A), \ + signed long: (signed long)(A), \ + unsigned long: (unsigned long)(A), \ + signed long long: (signed long long)(A), \ + unsigned long long: (unsigned long long)(A), \ + float: (float)(A), \ + double: (double)(A), \ + long double: (long double)(A)) + +#define SIGNEDNESS(XT) (PROMOTE(XT, -1) < PROMOTE(XT, 0)) + +inline +signed maxs(signed a, signed b) [[__unsequenced__]] { + return a < b ? b : a; +} + +inline +unsigned maxu(unsigned a, unsigned b) [[__unsequenced__]] { + return a < b ? b : a; +} + +inline +unsigned maxus(unsigned a, signed b) [[__unsequenced__]] { + return b < 0 ? a : maxu(a, b); +} + +inline +unsigned maxsu(signed a, unsigned b) [[__unsequenced__]] { + return a < 0 ? b : maxu(a, b); +} + +inline +signed long maxsl(signed long a, signed long b) [[__unsequenced__]] { + return a < b ? b : a; +} + +inline +unsigned long maxul(unsigned long a, unsigned long b) [[__unsequenced__]] { + return a < b ? b : a; +} + +inline +unsigned long maxusl(unsigned long a, signed long b) [[__unsequenced__]] { + return b < 0 ? a : maxul(a, b); +} + +inline +unsigned long maxsul(signed long a, unsigned long b) [[__unsequenced__]] { + return a < 0 ? b : maxul(a, b); +} + +inline +signed long long maxsll(signed long long a, signed long long b) [[__unsequenced__]] { + return a < b ? b : a; +} + +inline +unsigned long long maxull(unsigned long long a, unsigned long long b) [[__unsequenced__]] { + return a < b ? b : a; +} + +inline +unsigned long long maxusll(unsigned long long a, signed long long b) [[__unsequenced__]] { + return b < 0 ? a : maxull(a, b); +} + +inline +unsigned long long maxsull(signed long long a, unsigned long long b) [[__unsequenced__]] { + return a < 0 ? b : maxull(a, b); +} + +#define MAX(X, Y) \ +_Generic((X)+(Y), \ + signed: maxs, \ + unsigned: maxu, \ + signed long: maxsl, \ + unsigned long: maxul, \ + signed long long: maxsll, \ + unsigned long long: maxull)((X), (Y)) + +#define MAXUS(X, Y) \ +_Generic((X)+(Y), \ + signed: maxus, \ + unsigned: maxus, \ + signed long: maxusl, \ + unsigned long: maxusl, \ + signed long long: maxusll, \ + unsigned long long: maxusll)((X), (Y)) + +#define MAXSU(X, Y) \ +_Generic((X)+(Y), \ + signed: maxsu, \ + unsigned: maxsu, \ + signed long: maxsul, \ + unsigned long: maxsul, \ + signed long long: maxsull, \ + unsigned long long: maxsull)((X), (Y)) + +#define mix(X, Y) \ +_Generic(&(char[SIGNEDNESS(X)-SIGNEDNESS(Y)+2]){ }, \ + char(*)[1]: MAXUS((X), (Y)), \ + char(*)[2]: MAX((X), (Y)), \ + char(*)[3]: MAXSU((X), (Y))) + +/** + ** @brief Similar to `snprintf` but checks the buffer argument for + ** the size. + ** + ** To be able to do that check, the function has the size argument + ** first and then the buffer, so the buffer can be specified as array + ** parameter that depends on the that size. + **/ +[[maybe_unused, __gnu__::__format__(__printf__, 3, 4)]] +static inline +int snprintf_swapped(size_t n, char s[restrict static n], + char const* restrict form, ...) { + va_list ap; + va_start(ap); + int ret = vsnprintf(s, n, form, ap); + va_end(ap); + return ret; +} + +/** + ** @brief A type-generic interface to `snprintf` that checks arguments. + ** + ** We distinguish two cases. First if the first argument is `nullptr` + ** the second argument is forced to `0` and the function `snprintf` + ** is called. If it is not `nullptr` a function `snprintf_swapped` + ** that just swaps the first two arguments is called. Because + ** that function has the size argument first and then the buffer, the + ** buffer can be specified as array parameter that depends on the + ** that size. + **/ +#define snprintf(S, N, F, ...) \ +_Generic \ + ((S), \ + nullptr_t: \ + (snprintf)(nullptr, GENERIC_IF(isice(N), (N), 0), \ + F __VA_OPT__(,) __VA_ARGS__), \ + default: \ + snprintf_swapped(_Generic((S), \ + nullptr_t: 1, \ + default: (N)), \ + _Generic((S), \ + nullptr_t: \ + (char[1]){ 0 }, \ + default: (S)), \ + (F) __VA_OPT__(,) __VA_ARGS__)) + +#define pow(X, Y) \ + _Generic \ + ( \ + (void (*)(typeof((X)+(Y)+0ULL), typeof((Y)+0ULL)))nullptr, \ + /* Second argument is an integer. */ \ + void (*)(float, unsigned long long): pownf, \ + void (*)(double, unsigned long long): pown, \ + void (*)(unsigned long long, unsigned long long): pown, \ + void (*)(long double, unsigned long long): pownl, \ + /* Second argument is a floating point. */ \ + void (*)(float, float): powf, \ + void (*)(long double, float): powl, \ + void (*)(long double, double): powl, \ + void (*)(long double, long double): powl, \ + /* Second argument is a floating-point, and first */ \ + /* is double or integer. */ \ + default: pow) \ + ((X), (Y)) + + +#endif diff --git a/getting-started.c b/getting-started.c new file mode 100644 index 0000000..ba43f92 --- /dev/null +++ b/getting-started.c @@ -0,0 +1,24 @@ +/* This may look like nonsense, but really is -*- mode: C -*- */ +#include /*@\label{include-stdlib}*/ +#include /*@\label{include-stdio}*/ + +/* The main thing that this program does. */ /*@\label{C-comment}*/ +int main(int argc, [[maybe_unused]] char* argv[argc+1]) { /*@\label{main-start}*/ + // Declarations + double A[5] = { /*@\label{array-declaration}*/ + [0] = 9.0, /*@\label{designated-init}*/ + [1] = 2.9, + [4] = 3.E+25, /*@\label{scientific-notation}*/ + [3] = .00007, /*@\label{comma-terminate}*/ + }; + + // Doing some work /*@\label{CPP-comment}*/ + for (size_t i = 0; i < 5; ++i) { /*@\label{for-loop}*/ + printf("element %zu is %g, \tits square is %g\n", /*@\label{printf-start}*/ + i, + A[i], + A[i]*A[i]); /*@\label{printf-end}*/ + } /*@\label{for-end}*/ + + return EXIT_SUCCESS; /*@\label{main-return}*/ +} /*@\label{main-end}*/ diff --git a/heron-expanded.c b/heron-expanded.c new file mode 100644 index 0000000..bd5d77f --- /dev/null +++ b/heron-expanded.c @@ -0,0 +1,37 @@ +#include "c23-fallback.h" +#include + +int main(int argc, char* argv[argc+1]) { + constexpr double epsP1 = 1.0 + 1E-9; + constexpr double epsM1 = 1.0 - 1E-9; + for (int i = 1; i < argc; ++i) { + /* Read one command line argument as a double. */ + double const a = strtod(argv[i], nullptr); + /* Compute some local constants. */ + double fact; + double alow; + double ahig; + if (a < 1.0) { + fact = 2.0; + alow = a; + ahig = 1.0; + } else { + fact = 0.5; + alow = -a; + ahig = -1.0; + } + /* A first low quality estimate for the inverse. */ + double x = 1.0; + /* Adapt x until it has the right magnitude. */ + while (alow*x < ahig) x *= fact; + /* We are close, correct with the Heron factor. */ + for (double prod = a*x; + ((prod < epsM1) || (epsP1 < prod)); + prod = a*x) { + x *= (2.0 - prod); + } + printf("heron: a=%.5e,\tx=%.5e,\ta*x=%.12f\n", + a, x, a*x); + } + return EXIT_SUCCESS; +} diff --git a/heron.c b/heron.c new file mode 100644 index 0000000..4dae846 --- /dev/null +++ b/heron.c @@ -0,0 +1,36 @@ +#include "c23-fallback.h" +#include + +/* lower and upper iteration limits centered around 1.0 */ +constexpr double eps1m01 = 1.0 - 0x1P-01; +constexpr double eps1p01 = 1.0 + 0x1P-01; +constexpr double eps1m24 = 1.0 - 0x1P-24; +constexpr double eps1p24 = 1.0 + 0x1P-24; + +int main(int argc, char* argv[argc+1]) { + for (int i = 1; i < argc; ++i) { // process args + double const a = strtod(argv[i], nullptr); // arg -> double + double x = 1.0; + for (;;) { // by powers of 2 + double prod = a*x; + if (prod < eps1m01) { + x *= 2.0; + } else if (eps1p01 < prod) { + x *= 0.5; + } else { + break; + } + } + for (;;) { // Heron approximation + double prod = a*x; + if ((prod < eps1m24) || (eps1p24 < prod)) { + x *= (2.0 - prod); + } else { + break; + } + } + printf("heron: a=%.5e,\tx=%.5e,\ta*x=%.12f\n", + a, x, a*x); + } + return EXIT_SUCCESS; +} diff --git a/heron_k.c b/heron_k.c new file mode 100644 index 0000000..2328a57 --- /dev/null +++ b/heron_k.c @@ -0,0 +1,231 @@ +#include "heron_k.h" + +/** + ** Simply works by repeated squaring. + **/ +double expk2(double a, unsigned k) [[__unsequenced__]] { + for (;;) { + k /= 2u; + if (!k) break; + a *= a; + } + return a; +} + +double expk_rec(double a, unsigned k) [[__unsequenced__]] { + switch (k) + default: { + if (k % 2u) + case 3: { + double ret = expk_rec(a, k/2u); + ret *= ret; + a *= ret; + } else { + /* If k is a power of 2, do something special. */ + if (k == (k & -k)) { + [[fallthrough]]; + case 4: a = expk2(a, k); + } else { + a = expk_rec(a, k/2u); + [[fallthrough]]; + case 2: + a *= a; + } + } + case 1: // only possible with C23 + } + return a; +} + +double frexp_np(double x, signed exp[static 1]) [[__unsequenced__]] { + if (x < 0.0) return -frexp_np(-x, exp); + signed ret = 0; + if (x > 0.0) { + if (x < 0.5) { + do { + x *= 2.0; + --ret; + } while (x < 0.5); + } else { + while (x >= 1.0) { + x *= 0.5; + ++ret; + } + } + } + exp[0] = ret; + return x; +} + +double heron1_estimate(double a) [[__unsequenced__]] { + /* A first low quality estimate for the inverse. */ + signed e0; frexp_np(a, &e0); + return + (e0 <= 0 + ? expk_rec(2.0, 1-e0) + : expk_rec(0.5, e0)); +} + +double heron1_estimate_dir_05(double a) [[__unsequenced__]] { + /* A table of double powers of 2. */ + /*Something like (2 << (2 << k)) if only that would be representable + in an integer type. */ + double const tab[] = { + 0x1P+000, + 0x1P+001, + 0x1P+002, + 0x1P+004, + 0x1P+008, + 0x1P+016, + 0x1P+032, + 0x1P+064, +#if (DBL_MAX_EXP*FLT_RDXRDX) > 128 + 0x1P+128, +#endif +#if (DBL_MAX_EXP*FLT_RDXRDX) > 256 + 0x1P+256, +#endif +#if (DBL_MAX_EXP*FLT_RDXRDX) > 512 + 0x1P+512, +#endif +#if (DBL_MAX_EXP*FLT_RDXRDX) > 1024 + 0x1P+1024, +#endif +#if (DBL_MAX_EXP*FLT_RDXRDX) > 2048 + 0x1P+2048, +#endif +#if (DBL_MAX_EXP*FLT_RDXRDX) > 4096 + 0x1P+4096, +#endif + }; + /* First estimate log2(log2(a)). This will be the most significant + bit of the exponent that we are looking for. */ + constexpr size_t k_max = sizeof tab/sizeof tab[0]; + unsigned k = 1u; + while ((k < k_max) && (tab[k]*a < 1.0)) + ++k; + double x = tab[k-1]; + a *= x; + k -= 2u; + /* Now test all the smaller bit positions in the exponent. */ + for (;k;--k) { + if (tab[k]*a < 1.0) { + a *= tab[k]; + x *= tab[k]; + } + } + return x; +} + +double heron1_estimate_dir_10(double a) [[__unsequenced__]] { + /* A table of inverses of double powers of 2. */ + /* Something like 1.0/(2 << (2 << k)) if only that would be + representable in an integer type. */ + double const tab[] = { + 0x1P-000, + 0x1P-001, + 0x1P-002, + 0x1P-004, + 0x1P-008, + 0x1P-016, + 0x1P-032, + 0x1P-064, +#if (DBL_MIN_EXP*FLT_RDXRDX) < -128 + 0x1P-128, +#endif +#if (DBL_MIN_EXP*FLT_RDXRDX) < -256 + 0x1P-256, +#endif +#if (DBL_MIN_EXP*FLT_RDXRDX) < -512 + 0x1P-512, +#endif +#if (DBL_MIN_EXP*FLT_RDXRDX) < -1024 + 0x1P-1024, +#endif +#if (DBL_MIN_EXP*FLT_RDXRDX) < -2048 + 0x1P-2048, +#endif +#if (DBL_MIN_EXP*FLT_RDXRDX) < -4096 + 0x1P-4096, +#endif + }; + /* First estimate -log2(log2(a)). This will be the most significant + bit of the exponent that we are looking for. */ + constexpr size_t k_max = sizeof tab/sizeof tab[0]; + unsigned k = 1u; + while ((k < k_max) && (tab[k]*a >= 0.5)) + ++k; + double x = tab[k-1]; + a *= x; + k -= 2u; + /* Now test all the smaller bit positions in the exponent. */ + for (;k;--k) { + if (tab[k]*a >= 0.5) { + a *= tab[k]; + x *= tab[k]; + } + } + return x; +} + +double heron1_estimate_dir(double a) [[__unsequenced__]] { + if (a < 0.5) return heron1_estimate_dir_05(a); + else if (a >= 1.0) return heron1_estimate_dir_10(a); + else return 1.0; +} + +double heron1(double a) [[__unsequenced__]] { + constexpr double epsP1 = 1.0 + 1E-9; + constexpr double epsM1 = 1.0 - 1E-9; + double x = heron1_estimate_dir(a); + /* We are close, correct with the Heron factor. */ + for (double prod = a*x; + ((prod < epsM1) || (epsP1 < prod)); + prod = a*x) { + x *= (2.0 - prod); + } + return x; +} + +double expk(double a, signed k) [[__unsequenced__]] { + if (k < 0) return heron1(expk_rec(a, -k)); + if (k) return expk_rec(a, k); + else return 1.0; +} + +double heron(double a, signed k) [[__unsequenced__]] { + switch (k) { + case 0: return 1.0; + case 1: return a; + } + if (k < 0) return heron1(heron(a, -k)); + if (a <= 0.0) return 0.0; + constexpr double eps = 1E-12; + double const k1 = heron1(k); + double const a1 = heron1(a); + double x = (1.0 + a)*k1; + for (;;) { + double xk1 = expk(x, k-1); + double axk1 = a*heron1(xk1); + if (fabs(1.0 - x*xk1*a1) < eps) break; + x = ((k-1)*x + axk1)*k1; + } + return x; +} + +/* A normal project would place the `main` in a different compilation + unit. */ +int main(int argc, char* argv[argc+1]) { + /* Read the first command line argument for the exponent. */ + int k = strtol(argv[1], 0, 0); + for (int i = 2; i < argc; ++i) { + /* Read one command line argument as a double. */ + double const a = strtod(argv[i], 0); + double const x = heron(a, k); + signed e0; double m0 = frexp_np(x, &e0); + signed e1; double m1 = frexp(x, &e1); + printf("heron: a=%.5e,\tx=%.5e,\tx**k=%.20e, %gT%+d, %gT%+d\n", + a, x, expk(x, k), m0, e0, m1, e1); + } + return EXIT_SUCCESS; +} diff --git a/heron_k.h b/heron_k.h new file mode 100644 index 0000000..7a2c9fd --- /dev/null +++ b/heron_k.h @@ -0,0 +1,133 @@ +#ifndef HERON_K_H +#define HERON_K_H 1 + +#include +/* Math functions usually need "-lm" as compiler argument: */ +#include +//#include +#include +#include "c23-fallback.h" + + +/** + ** @file + ** @brief Implement a Heron process to approximate powers of `1/k` + **/ + + +/** + ** @def FLT_RDXRDX + ** @brief the radix base 2 of @c FLT_RADIX + ** + ** This is needed internally for some of the code below. + **/ +#if FLT_RADIX == 2 +# define FLT_RDXRDX 1 +#elif FLT_RADIX == 4 +# define FLT_RDXRDX 2 +#elif FLT_RADIX == 8 +# define FLT_RDXRDX 3 +#elif FLT_RADIX == 16 +# define FLT_RDXRDX 4 +#else +# error "encoutered platform with unusual FLT_RADIX, please report" +/** + ** @def FLT_RDXRDX + ** @brief the radix base 2 of @c FLT_RADIX + ** + ** This is needed internally for some of the code below. + **/ +# define FLT_RDXRDX something +#endif + +/** + ** @brief raise @a to the power of @a k + ** + ** @warning manual implementation as an exercise, never use in + ** production code + ** + ** @pre @a k must be a power of 2.0 + **/ +double expk2(double a, unsigned k) [[__unsequenced__]]; + +/** + ** @brief raise @a to the power of @a k + ** + ** @warning manual implementation as an exercise, never use in + ** production code + ** + ** @pre @a a must be strictly greater than 0. + **/ +double expk_rec(double a, unsigned k) [[__unsequenced__]]; + +/** + ** @brief convert floating-point number to fractional and integral components + ** + ** @warning manual implementation as an exercise, never use in + ** production code + ** + ** This implementation just repeatedly multiplies the input value by + ** 2.0 or 0.5. + **/ +double frexp_np(double x, signed exp[static 1]) [[__unsequenced__]]; + +/** + ** @brief compute a good estimate for the Heron process for k=-1 + ** @return x is a power of 2 such that 0.5 <= x*a < 1.0 + ** @pre Supposes that a > 0 + **/ +double heron1_estimate(double a) [[__unsequenced__]]; + +/** + ** @brief compute a good estimate for the Heron process for k=-1 + ** @return x is a power of 2 such that 0.5 <= x*a < 1.0 + ** @pre Supposes that 0 < a < 0.5 + **/ +double heron1_estimate_dir_05(double a) [[__unsequenced__]]; + +/** + ** @brief compute a good estimate for the Heron process for k=-1 + ** @return x is a power of 2 such that 0.5 <= x*a < 1.0 + ** @pre Supposes that a > 1.0 + **/ +double heron1_estimate_dir_10(double a) [[__unsequenced__]]; + +/** + ** @brief compute a good estimate for the Heron process for k=-1 + ** @return x is a power of 2 such that 0.5 <= x*a < 1.0 + ** @pre Supposes that a > 0.0 + **/ +double heron1_estimate_dir(double a) [[__unsequenced__]]; + +/** + ** @brief use the Heron process to approximate 1.0/a + ** @pre Supposes that a > 0.0 + **/ +double heron1(double a) [[__unsequenced__]]; + +/** + ** @brief raise @a to the power of @a k + ** + ** @warning manual implementation as an exercise, never use in + ** production code + **/ +double expk(double a, signed k) [[__unsequenced__]]; + +/** + ** @brief use the Heron process to approximate @a a to the + ** power of `1/k` + ** + ** Or in other words this computes the @f$k^{th}@f$ root of @a a. + ** As a special feature, if @a k is `-1` it computes the + ** multiplicative inverse of @a a. + ** + ** @param a must be greater than `0.0` + ** @param k should not be `0` and otherwise be between + ** `DBL_MIN_EXP*FLT_RDXRDX` and + ** `DBL_MAX_EXP*FLT_RDXRDX`. + ** + ** @see FLT_RDXRDX + **/ +double heron(double a, signed k) [[__unsequenced__]]; + +#endif diff --git a/life.c b/life.c new file mode 100644 index 0000000..157cad1 --- /dev/null +++ b/life.c @@ -0,0 +1,264 @@ +#include "life.h" +#include +#include +#include "termin.h" + + +void life_sleep(double s) { + struct timespec duration = { + .tv_sec = s, + .tv_nsec = (s-(size_t)s)*1E9, + }; + while (thrd_sleep(&duration, &duration)) { + // Empty + } +} + +int life_wait(cnd_t* cnd, mtx_t* mtx) { + struct timespec now; + timespec_get(&now, TIME_UTC); + now.tv_sec += 1; + return cnd_timedwait(cnd, mtx, &now); +} + +void life_advance(life* L, signed t0, signed t1); + +static +bool life_alive(bool l, unsigned x) { + return x == 3 || (l && x ==2); +} + +// Computes the number of neighbors and stores them in B +void life_count(life* L) { + size_t const n1 = L->n1; + bool (*const restrict M)[n1] = (void*)L->Mv; + size_t const off0 = L->off0; + size_t const off1 = L->off1; + size_t const len0 = L->len0; + size_t const len1 = L->len1; + if (!L->Bv) L->Bv = malloc(sizeof(unsigned char[len0][len1])); + unsigned char (*restrict B)[len1] = L->Bv; + for (size_t j0 = 0; j0 < len0; ++j0) { + size_t i0 = off0 + j0; + for (size_t j1 = 0; j1 < len1; ++j1) { + size_t i1 = off1 + j1; + B[j0][j1] = + + M[i0-1][i1-1] + M[i0-1][i1] + M[i0-1][i1+1] + + M[i0 ][i1-1] + 0 + M[i0 ][i1+1] + + M[i0+1][i1-1] + M[i0+1][i1] + M[i0+1][i1+1]; + } + } +} + +size_t life_update(life* L) { + size_t const n1 = L->n1; + bool (*const restrict M)[n1] = (void*)L->Mv; + size_t const off0 = L->off0; + size_t const off1 = L->off1; + size_t const len0 = L->len0; + size_t const len1 = L->len1; + unsigned char (*restrict B)[len1] = L->Bv; + size_t changed = 0; + if (B) { + for (size_t j0 = 0; j0 < len0; ++j0) { + size_t i0 = off0 + j0; + for (size_t j1 = 0; j1 < len1; ++j1) { + size_t i1 = off1 + j1; + bool newval = life_alive(M[i0][i1], B[j0][j1]); + changed += (M[i0][i1] != newval); + M[i0][i1] = newval; + } + } + } + L->Bv = nullptr; + free(B); + return changed; +} + +void life_torus(life* L) { + size_t const n0 = L->n0; + size_t const n1 = L->n1; + bool (*const restrict M)[n1] = L->Mv; + for (size_t i0 = 1; i0 < n0; ++i0) { + M[i0][0] = M[i0][n1-2]; + M[i0][n1-1] = M[i0][1]; + } + for (size_t i1 = 1; i1 < n1; ++i1) { + M[0] [i1] = M[n0-2][i1]; + M[n0-1][i1] = M[1] [i1]; + } + M[0] [0] = M[n0-2][n1-2]; + M[n0-1][0] = M[1] [n1-2]; + M[0] [n1-1] = M[n0-2][1]; + M[n0-1][n1-1] = M[1] [1]; +} + + + +#define DOT "⦿" +#define SPACE " " +#define WROOK ESC_TBLUE "♖" ESC_NORMAL +#define BROOK ESC_TBLUE "♜" ESC_NORMAL + +static +void border(size_t m, int b) { + fputs(ESC_TRED, stdout); + fputs(ESC_BORDER[b & ~esc_right], stdout); + for (size_t j = 0; j < m; ++j) + fputs("━", stdout); + fputs(ESC_BORDER[b & ~esc_left], stdout); + fputs(ESC_NORMAL, stdout); + esc_move(stdout, 1, -(m+2)); +} + +void life_draw(life* L) { + size_t const n1 = L->n1; + bool (*const restrict M)[n1] = L->Mv; + size_t const x0 = L->x0; + size_t const x1 = L->x1; + size_t const off0 = L->off0; + size_t const off1 = L->off1; + size_t const len0 = L->len0; + size_t const len1 = L->len1; + + fputs(ESC_SAVE ESC_HOME ESC_HIDE, stdout); + border(len1, esc_top|esc_right|esc_left); + for (size_t i0 = off0; i0 < off0+len0; ++i0) { + fputs(ESC_TRED, stdout); + fputs(ESC_BORDER[esc_left], stdout); + fputs(ESC_NORMAL, stdout); + for (size_t i1 = off1; i1 < off1+len1; ++i1) { + if (i1 == x1 && x0 == i0) fputs(M[i0][i1] ? WROOK : BROOK, stdout); + else fputs(M[i0][i1] ? DOT : SPACE, stdout); + } + fputs(ESC_TRED, stdout); + fputs(ESC_BORDER[esc_right], stdout); + fputs(ESC_NORMAL, stdout); + esc_move(stdout, 1, -(len1+2)); + } + border(len1, esc_bottom|esc_right|esc_left); + esc_goto(stdout, len0+3, 1); + fprintf(stdout, ESC_CLEAR "%3zu FPS, %5zu iterations, %5zu birth9, %5zu constellations, " ESC_BOLD "%6.2f" ESC_NORMAL " quotient", + L->frames, L->iteration, L->birth9, L->constellations, L->constellations*1.0/L->birth9); + fputs(ESC_RESTORE ESC_SHOW, stdout); + esc_goto(stdout, len0+4, 1); +} + +void life_draw4(life* L) { + size_t const n1 = L->n1; + bool (*const restrict M)[n1] = L->Mv; + size_t const x0 = L->x0; + size_t const x1 = L->x1; + size_t const off0 = L->off0; + size_t const off1 = L->off1; + size_t const len0 = L->len0; + size_t const len1 = L->len1; + + fputs(ESC_SAVE ESC_HOME ESC_HIDE, stdout); + border(len1/2, esc_top|esc_right|esc_left); + for (size_t i0 = off0; i0 < off0+len0; i0 += 2) { + fputs(ESC_TRED, stdout); + fputs(ESC_BORDER[esc_left], stdout); + fputs(ESC_NORMAL, stdout); + for (size_t i1 = off1; i1 < off1+len1; i1 += 2) { + char const* str = nullptr; + if ((i1 == x1 || i1+1 == x1) && (x0 == i0 || x0 == i0+1)) { + // The cursor is a blue dot in the correct position, but the + // three cell positions are also hidden. We grey them out. + static char const*const corner[4] = { + [0] = ESC_TBLUE ESC_BGREY "▗" ESC_NORMAL, + [1] = ESC_TBLUE ESC_BGREY "▖" ESC_NORMAL, + [2] = ESC_TBLUE ESC_BGREY "▝" ESC_NORMAL, + [3] = ESC_TBLUE ESC_BGREY "▘" ESC_NORMAL, + }; + unsigned ty = (x0%2)<<1 | (x1%2); + str = corner[ty]; + } else { + unsigned val = M[i0][i1] + | (M[i0][i1+1] << 1) + | (M[i0+1][i1] << 2) + | (M[i0+1][i1+1] << 3); + str = ESC_BLOCK[val]; + } + fputs(str, stdout); + } + fputs(ESC_TRED, stdout); + fputs(ESC_BORDER[esc_right], stdout); + fputs(ESC_NORMAL, stdout); + esc_move(stdout, 1, -(len1/2+2)); + } + border(len1/2, esc_bottom|esc_right|esc_left); + esc_goto(stdout, len0/2+3, 1); + fprintf(stdout, ESC_CLEAR "%3zu FPS, %5zu iterations, %5zu birth9, %5zu constellations, " ESC_BOLD "%6.2f" ESC_NORMAL " quotient", + L->frames, L->iteration, L->birth9, L->constellations, L->constellations*1.0/L->birth9); + fputs(ESC_RESTORE ESC_SHOW, stdout); + esc_goto(stdout, len0+4, 1); +} + +static +bool hashin(bool (*visited)[life_maxit], size_t hash) { + bool ret = false; + hash %= life_maxit; + if (!(*visited)[hash]) { + (*visited)[hash] = true; + ret = true; + } + return ret; +} + +void life_account(life* L) { + size_t const n = L->n0; + size_t const m = L->n1; + bool (*const restrict M)[m] = L->Mv; + // Computes a hash of the new state + size_t hash = 0; + for (size_t i = 1; i < n-2; ++i) { + for (size_t j = 1; j < m-2; ++j) { + hash = 37*hash + M[i][j] + 7; + } + } + bool is_new = hashin(L->visited, hash); + if (is_new) { + L->constellations++; + L->last = L->accounted; + } + L->accounted++; +} + +void life_birth9(life* L) { + size_t const n0 = L->n0; + size_t const n1 = L->n1; + size_t const x = L->x0; + size_t const y = L->x1; + bool (*const restrict M)[n1] = L->Mv; + for (size_t i = n0-4; i < n0-1; ++i) { + size_t xi = ((x+i)%(n0-2)) + 1; + for (int j = n1-4; j < n1-1; ++j) { + size_t yj = ((y+j)%(n1-2)) + 1; + M[xi][yj] = true; + } + } + L->birth9++; +} + +life* life_init(life* L, size_t n, size_t m, bool mat[static n][m]) { + if (L) { + L->n0 = n; + L->n1 = m; + L->off0 = 1; + L->len0 = n-2; + L->off1 = 1; + L->len1 = m-2; + L->visited = calloc(sizeof(bool), life_maxit); + L->Mv = mat; + mtx_init(&L->mtx, mtx_plain); + cnd_init(&L->draw); + cnd_init(&L->acco); + cnd_init(&L->upda); + } + return L; +} + +void life_destroy(life* L) { + if (L) free(L->visited); +} diff --git a/life.h b/life.h new file mode 100644 index 0000000..d5f7eec --- /dev/null +++ b/life.h @@ -0,0 +1,83 @@ +#include "c23-fallback.h" +#include +#ifndef __STDC_NO_THREADS__ +# include +#else +# error This needs C11 threads, aborting. +#endif +#include + + +enum { life_maxit = 1ull << 23, }; + +typedef struct life life; +struct life { + mtx_t mtx; //< Mutex that protects Mv + cnd_t draw; //< cnd that controls drawing + cnd_t acco; //< cnd that controls accounting + cnd_t upda; //< cnd that controls updating + + void*restrict Mv; //< bool M[n0][n1]; + bool (*visited)[life_maxit]; //< Hashing constellations + + // A bunch of parameters that are not subject to change + size_t n0; //< Number of rows + size_t n1; //< Number of columns + size_t off0; //< Start row + size_t len0; //< Number of rows to be handled + size_t off1; //< Start column + size_t len1; //< Number of columns to be handled + void* restrict Bv; //< Unsigned char B[len0][len1]; + + + // Parameters that are updated but that are protected by the mutex + size_t iteration; //< Current iteration + size_t accounted; //< Last accounted iteration + size_t drawn; //< Last drawn iteration + size_t last; //< Last iteration with new state + size_t birth9; //< Number of birth9 calls + + // Parameters that will dynamically be changed by + // different threads + _Atomic(size_t) constellations; //< Constellations visited + _Atomic(size_t) x0; //< Cursor position, row + _Atomic(size_t) x1; //< Cursor position, column + _Atomic(size_t) frames; //< FPS for display + _Atomic(bool) finished; //< This game is finished. +}; + +inline +void life_advance(life* L, signed t0, signed t1) { + if (t0) { + size_t n = L->n0-2; + L->x0 = ((L->x0-1)+n+t0)%n + 1; + } + if (t1) { + size_t n = L->n1-2; + L->x1 = ((L->x1-1)+n+t1)%n + 1; + } +} + +int life_wait(cnd_t*, mtx_t*); +void life_sleep(double s); + +void life_birth9(life*); +size_t life_update(life*restrict L); +void life_count(life*restrict L); +void life_draw(life*); +void life_draw4(life*); +void life_account(life*); +life* life_init(life*, size_t _n, size_t _m, bool[_n][_m]); +void life_destroy(life* L); +void life_torus(life*); + +#define LIFE_INITIALIZER \ +{ \ + .visited = 0, \ + .birth9 = 1, \ + .x0 = 1, \ + .x1 = 1, \ + .frames = 20, \ + .finished = false, \ + .constellations = 1, \ +} diff --git a/lifetime.c b/lifetime.c new file mode 100644 index 0000000..ed12c10 --- /dev/null +++ b/lifetime.c @@ -0,0 +1,89 @@ +#include +#include "c23-fallback.h" + +void fgoto(unsigned n) { + unsigned j = 0; + unsigned* p = nullptr; + unsigned* q; + AGAIN: + if (p) printf("%u: p and q are %s, *p is %u\n", + j, + (q == p) ? "equal" : "unequal", + *p); + q = p; + p = &((unsigned){ j, }); /*@\label{lifetime_compound_literal}*/ + ++j; + if (j <= n) goto AGAIN; +} + +void fgotoblock(unsigned n) { + unsigned j = 0; + unsigned* p = nullptr; + unsigned* q; + AGAIN: // Using a compound statement creates a new object at each iteration. + { // A good modern compiler should complain that it is uninitialized. + if (p) printf("%u: p and q are %s, *p is %u\n", + j, + (q == p) ? "equal" : "unequal", + *p); + q = p; + p = &((unsigned){ j, }); + ++j; + if (j <= n) goto AGAIN; + } +} + +__attribute__((noinline)) +void ffor1(void) { + unsigned j = 1; + printf("%u: p and q are %s, *p is %u\n", + j, + "unequal", + j-1); +} + +__attribute__((noinline)) +void fforn(unsigned n) { + ffor1(); + for (unsigned j = 2; j <= n; ++j) { + printf("%u: p and q are %s, *p is %u\n", + j, + "equal", + j-1); + } +} + +void ffor(unsigned n) { + switch (n) { + case 0: break; + case 1: ffor1(); break; + default: fforn(n); break; + } +} + +void fVLA(unsigned n) { + unsigned volatile j = 0; + unsigned* p = nullptr; + unsigned* q; + AGAIN: + { + if (p) printf("%u: p and q are %s\n", + j, + (q == p) ? "equal" : "unequal"); + q = p; + unsigned VLA[j+1]; + for (unsigned i = 0; i <= j; ++i) + VLA[i] = j; + p = VLA; + ++j; + if (j <= n) goto AGAIN; + } +} + + + +int main(int argc, [[maybe_unused]] char* argv[]) { + fgoto(argc+1); + fgotoblock(argc+1); + ffor(argc+1); +} diff --git a/literals.c b/literals.c new file mode 100644 index 0000000..f2c577e --- /dev/null +++ b/literals.c @@ -0,0 +1,7 @@ +char const A[] = { 'e', 'n', 'd', '\0', }; +char const B[] = { 'e', 'n', 'd', '\0', }; +char const* c = "end"; +char const* d = "end"; +char const* e = "friend"; +char const* f = (char const[]){ 'e', 'n', 'd', '\0', }; +char const* g = (char const[]){ 'e', 'n', 'd', '\0', }; diff --git a/locale.c b/locale.c new file mode 100644 index 0000000..8e258f2 --- /dev/null +++ b/locale.c @@ -0,0 +1,22 @@ +#include +#include + +/* You may have to unset the LANGUAGE environment variable for this to + work properly. */ + +int main(int argc, char* argv[argc+1]) { + perror("With C locale"); + if (!setlocale(LC_ALL, "")) + perror("couldn't set locale"); + else + perror("With default locale"); + if (!setlocale(LC_MESSAGES, "C")) + perror("couldn't set locale"); + else + perror("C locale, again"); + if (argv[1] && !setlocale(LC_MESSAGES, argv[1])) + perror("couldn't set locale"); + else + perror("new locale"); + printf("commandline argument was '%s'\n", argv[1]); +} diff --git a/macro_trace.c b/macro_trace.c new file mode 100644 index 0000000..1ecda7a --- /dev/null +++ b/macro_trace.c @@ -0,0 +1,40 @@ +#include "macro_trace.h" +#include +#include + +char const* trace_skip(char const expr[static 1]); + +void trace_values(FILE* s, + char const func[static 1], + char const line[static 1], + char const expr[static 1], + char const head[static 1], + size_t len, long double const arr[len]); + + +int main(int argc, char* argv[]) { + double sum = argc > 1 ? strtod(argv[1], 0) : 0x1P-1; + TRACE_PRINT0("my favorite variable: %g\n", sum); + TRACE_PRINT1("my favorite variable: %g", sum); + TRACE_PRINT2("my favorite variable: %g", sum); + TRACE_VALUE0("my favorite value:", sum); + TRACE_PTR0("my favorite pointer:", argv); + TRACE_PTR1("my favorite pointer:", argv); + TRACE_PRINT3("my favorite variable: %g", sum); + TRACE_PRINT4("my favorite variable: %g", sum); + TRACE_PRINT5("my favorite variable: %g", sum); + TRACE_PRINT5("a good expression: %g", sum*argc); + TRACE_PRINT6("a collection: %g, %i", sum, argc); + TRACE_PRINT7("a string"); + TRACE_PRINT8("a collection: %g, %i", sum, argc); + TRACE_PRINT8("another string"); + TRACE_PRINT9("a collection: %g, %i", sum*acos(0), argc); + TRACE_VALUES("an untyped collection:", sum, argc, acos(0)*2); + TRACE_VALUES("an untyped \"collection\":", sum, argc, acos(0)*2); + TRACE_VALUES("just one element:", acos(0)*2); + TRACE_VALUES("empty"); + TRACE_VALUES("\"empty\""); + TRACE_VALUE1("my favorite value:", sum); + TRACE_VALUE1("another value:", 0x1111111111111); + TRACE_VALUE1("my favorite pointer:", argv); +} diff --git a/macro_trace.h b/macro_trace.h new file mode 100644 index 0000000..2125f50 --- /dev/null +++ b/macro_trace.h @@ -0,0 +1,302 @@ +#ifndef MACRO_TRACE_H +#define MACRO_TRACE_H 1 +#include +#include "c23-fallback.h" + +/** + ** @file + ** + ** @brief A sequence of macros for tracing execution + **/ + +#if !__has_va_opt +# error "no __VA_OPT__ support found, aborting compilation" +// shut off some subsequent error messages +# include "no __VA_OPT__ support found, aborting compilation" +#endif + +/** + ** @brief A simple version of the macro that just does + ** a @c fprintf or nothing + **/ +#if NDEBUG +# define TRACE_PRINT0(F, X) do { /* nothing */ } while (false) +#else +# define TRACE_PRINT0(F, X) fprintf(stderr, F, X) +#endif + +/** + ** @brief A simple version of the macro that ensures that the @c + ** fprintf format is a string literal + ** + ** As an extra, it also adds a newline to the printout, so + ** the user doesn't have to specify it each time. + **/ +#if NDEBUG +# define TRACE_PRINT1(F, X) do { /* nothing */ } while (false) +#else +# define TRACE_PRINT1(F, X) fprintf(stderr, "" F "\n", X) +#endif + +/** + ** @brief A macro that resolves to @c 0 or @c 1 according to @c + ** NDEBUG being set + **/ +#ifdef NDEBUG +# define TRACE_ON 0 +#else +# define TRACE_ON 1 +#endif + +/** + ** @brief A simple version of the macro that ensures that the @c fprintf call is always evaluated + **/ +#define TRACE_PRINT2(F, X) \ +do { if (TRACE_ON) fprintf(stderr, "" F "\n", X); } \ + while (false) + +/** + ** @brief Traces a value without having to specify a format + **/ +#define TRACE_VALUE0(HEAD, X) TRACE_PRINT2(HEAD " %Lg", (X)+0.0L) + +/** + ** @brief Traces a pointer without having to specify a format + ** + ** @warning Uses a cast of @a X to @c void* + **/ +#define TRACE_PTR0(HEAD, X) TRACE_PRINT2(HEAD " %p", (void*)(X)) + +/** + ** @brief Traces a pointer without specifying a format + **/ +#define TRACE_PTR1(HEAD, X) \ + TRACE_PRINT2(HEAD " %p", ((void*){ (X) })) + +/** + ** @brief Adds the current line number to the trace + **/ +#define TRACE_PRINT3(F, X) \ +do { \ + if (TRACE_ON) \ + fprintf(stderr, "%lu: " F "\n", __LINE__+0UL, X); \ +} while (false) + +/** + ** @brief Adds the name of the current function to the trace + **/ +#define TRACE_PRINT4(F, X) \ +do { \ + if (TRACE_ON) \ + fprintf(stderr, "%s:%lu: " F "\n", \ + __func__, __LINE__+0UL, X); \ +} while (false) + +#define STRINGIFY(X) #X +#define STRGY(X) STRINGIFY(X) +/** @brief Adds a textual version of the expression that is evaluated + **/ +#define TRACE_PRINT5(F, X) \ +do { \ + if (TRACE_ON) \ + fprintf(stderr, \ + "%s:" STRGY(__LINE__) ":(" #X "): " F "\n", \ + __func__, X); \ +} while (false) + +/** + ** @brief Allows multiple arguments to be printed in the + ** same trace + **/ +#define TRACE_PRINT6(F, ...) \ +do { \ + if (TRACE_ON) \ + fprintf(stderr, "%s:" STRGY(__LINE__) ": " F "\n", \ + __func__, __VA_ARGS__); \ +} while (false) + +/** @brief Only traces with a text message; no values printed + **/ +#define TRACE_PRINT7(...) \ +do { \ + if (TRACE_ON) \ + fprintf(stderr, \ + "%s:" STRGY(__LINE__) ": " __VA_ARGS__ "\n", \ + __func__); \ +} while (false) + +/** + ** @brief Extracts the first argument from a list of arguments + **/ +#define TRACE_FIRST(...) TRACE_FIRST0(__VA_ARGS__, 0) +#define TRACE_FIRST0(_0, ...) _0 + +/** + ** @brief Removes the first argument from a list of arguments + ** + ** @remark This is only suitable in our context, + ** since this adds an artificial last argument. + **/ +#define TRACE_LAST(...) TRACE_LAST0(__VA_ARGS__, 0) +#define TRACE_LAST0(_0, ...) __VA_ARGS__ + +/** + ** @brief Traces with or without values + ** + ** This implementation uses the C23 feature + ** `__VA_OPT__` to add a possibly empty variable + ** list at the end of the `fprintf` arguments. + **/ +#define TRACE_PRINT8(F, ...) \ +do { \ + if (TRACE_ON) \ + fprintf(stderr, "%s:" STRGY(__LINE__) ": " F "\n", \ + __func__ __VA_OPT__(,) __VA_ARGS__); \ +} while (false) + +/** + ** @brief Traces by first giving a textual representation of the + ** arguments + **/ +#define TRACE_PRINT9(F, ...) \ +TRACE_PRINT8("(" #__VA_ARGS__ ") " F __VA_OPT__(,) __VA_ARGS__) + +inline +char const* trace_skip(char const expr[static 1]){ + for (char const* p = expr+1; *p; ++p) { + // at the end of head we expect the 3 chars ``", '' + if (p[0] == '"') return p+3; + // watch out for escaped " characters + if (p[0] == '\\' && p[1] == '"') ++p; + } + return 0; +} + +/** + ** @brief A function to print a list of values + ** + ** @remark Only call this through the macro ::TRACE_VALUES, + ** which will provide the necessary contextual information. + **/ +inline +void trace_values(FILE* s, + char const func[static 1], + char const line[static 1], + char const expr[static 1], + char const head[static 1], + size_t len, long double const arr[len]) { + fprintf(s, "%s:%s:(%s) %s %Lg", func, line, + trace_skip(expr), head, arr[0]); + for (size_t i = 1; i < len-1; ++i) + fprintf(s, ", %Lg", arr[i]); + fputc('\n', s); +} + +/** + ** @brief Returns the number of arguments in the variable list + ** + ** This version works for lists with up to 31 elements. + ** + ** @remark An empty argument list is taken as one (empty) argument. + **/ +#define ALEN(...) ALEN0(__VA_ARGS__, \ + 0x1E, 0x1F, 0x1D, 0x1C, 0x1B, 0x1A, 0x19, 0x18, \ + 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, \ + 0x0E, 0x0F, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08, \ + 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00) + +#define ALEN0(_00, _01, _02, _03, _04, _05, _06, _07, _08, \ + _09, _0A, _0B, _0C, _0D, _0F, _0E, _10, _11, \ + _12, _13, _14, _15, _16, _17, _18, _19, _1A, \ + _1B, _1C, _1D, _1F, _1E, ...) _1E + +/** + ** @brief Traces a list of arguments without having to specify + ** the type of each argument + ** + ** @remark This constructs a temporary array with the arguments + ** all converted to @c long double. Thereby implicit conversion + ** to that type is always guaranteed. + **/ +#define TRACE_VALUES(...) \ +TRACE_VALUES0(ALEN(__VA_ARGS__), \ + #__VA_ARGS__, \ + __VA_ARGS__ \ + ) + +#define TRACE_VALUES0(NARGS, EXPR, HEAD, ...) \ +do { \ + if (TRACE_ON) { \ + if (NARGS > 1) \ + trace_values(stderr, __func__, STRGY(__LINE__), \ + "" EXPR "", "" HEAD "", NARGS, \ + (long double const[NARGS]){ __VA_ARGS__ }); \ + else \ + fprintf(stderr, "%s:" STRGY(__LINE__) ": %s\n", \ + __func__, HEAD); \ + } \ + } while (false) + +/** + ** @brief Returns a format that is suitable for @c fprintf + ** + ** @return The argument @a F must be a string literal, + ** so the return value will also be one. + ** + **/ +#define TRACE_FORMAT(F, X) \ +_Generic((X)+0LL, \ + unsigned long long: "" F " %llu\n", \ + long long: "" F " %lld\n", \ + float: "" F " %.8f\n", \ + double: "" F " %.12f\n", \ + long double: "" F " %.20Lf\n", \ + default: "" F " %p\n") + +/** + ** @brief Returns a value that forcibly can be interpreted as + ** pointer value + ** + ** That is, any pointer will be returned as such, but other + ** arithmetic values will result in a @c nullptr. + **/ +#define TRACE_POINTER(X) \ +_Generic((X)+0LL, \ + unsigned long long: nullptr, \ + long long: nullptr, \ + float: nullptr, \ + double: nullptr, \ + long double: nullptr, \ + default: (X)) + +/** + ** @brief Returns a value that is promoted either to a wide + ** integer, to a floating point, or to a @c void* if @a X is a + ** pointer + **/ +#define TRACE_CONVERT(X) \ +_Generic((X)+0LL, \ + unsigned long long: (X)+0LL, \ + long long: (X)+0LL, \ + float: (X)+0LL, \ + double: (X)+0LL, \ + long double: (X)+0LL, \ + default: ((void*){ nullptr } = TRACE_POINTER(X))) + +/** + ** @brief Traces a value without having to specify a format + ** + ** This variant works correctly with non-void pointers. + ** + ** The formats are tunable by changing the specifiers in + ** ::TRACE_FORMAT. + **/ +#define TRACE_VALUE1(F, X) \ +do { \ + if (TRACE_ON) \ + fprintf(stderr, \ + TRACE_FORMAT("%s:" STRGY(__LINE__) ": " F, X), \ + __func__, TRACE_CONVERT(X)); \ + } while (false) + +#endif diff --git a/mbstrings-main.c b/mbstrings-main.c new file mode 100644 index 0000000..56a3838 --- /dev/null +++ b/mbstrings-main.c @@ -0,0 +1,206 @@ +#include +#include +#include +#include +#include + +#include "mbstrings.h" + +enum { + g_l = 1, + g_r = 2, + g_u = 4, + g_d = 8, +}; + +#define MONTH(X) \ + (char const*[]){ \ + [1] = "Jan", \ + [2] = "Feb", \ + [3] = "Mar", \ + [4] = "Apr", \ + [5] = "Mai", \ + [6] = "Jun", \ + [7] = "Jul", \ + [8] = "Aug", \ + [9] = "Sep", \ + [10] = "Oct", \ + [11] = "Nov", \ + [12] = "Dec", \ + }[(X)%100] + +#define STRINGIFY_(X) #X +#define STRINGIFY(X) STRINGIFY_(X) + +#define YEAR(X) \ +(char const[5]){ \ + STRINGIFY(X)[0], \ + STRINGIFY(X)[1], \ + STRINGIFY(X)[2], \ + STRINGIFY(X)[3], \ + } + +#define VBAR "\u2502" /**< a vertical bar character */ +#define HBAR "\u2500" /**< a horizontal bar character */ +#define TOPLEFT "\u250c" /**< topleft corner character */ +#define TOPRIGHT "\u2510" /**< topright corner character */ + +/** + ** @brief Draw multibyte strings @a start and @a end separated + ** by a horizontal line. + **/ +void draw_sep(char const start[static 1], + char const end[static 1]) { + fputs(start, stdout); + size_t slen = mbsrlen(start, 0); + size_t elen = 90 - mbsrlen(end, 0); + for (size_t i = slen; i < elen; ++i) fputs(HBAR, stdout); + fputs(end, stdout); + fputc('\n', stdout); +} + +char const* g_table[] = { + [0] = " ", + [g_l] = "\u2574", + [g_r] = "\u2576", + [g_l|g_r] = "\u2500", + [g_u] = "\u2575", + [g_l|g_u] = "\u2518", + [g_r|g_u] = "\u2514", + [g_l|g_r|g_u] = "\u2534", + [g_d] = "\u2577", + [g_l|g_d] = "\u2510", + [g_r|g_d] = "\u250c", + [g_l|g_r|g_d] = "\u252c", + [g_u|g_d] = "\u2502", + [g_l|g_u|g_d] = "\u2524", + [g_r|g_u|g_d] = "\u251c", + [g_l|g_r|g_u|g_d] = "\u253c", +}; + +int main(void) { + /* The first action of the executable should be locale switch. */ + char const* locC_ = setlocale(LC_CTYPE, 0); + char locC[strlen(locC_)+1]; + strcpy(locC, locC_); + + setlocale(LC_ALL, ""); + /* Multibyte character printing only works after the locale + has been switched. */ + draw_sep(TOPLEFT " © 2014 jɛnz ˈgʊzˌtɛt ", TOPRIGHT); + + char const* locLoc_ = setlocale(LC_CTYPE, 0); + char locLoc[strlen(locLoc_)+1]; + strcpy(locLoc, locLoc_); + char const strhello[] = "Hä\u0131łłö"; + char const u8hello[] = u8"Hä\u0131łłö"; + wchar_t const wcshello[] = L"Hä\u0131łłö"; + char const strvowel[] = "aeiouäëïöüáéíóúàèìòùæœ\u0131"; + char const u8vowel[] = u8"aeiouäëïöüáéíóúàèìòùæœ\u0131"; + wchar_t const wcsvowel[] = L"aeiouäëïöüáéíóúàèìòùæœ\u0131"; + char16_t c16butterfly[] = u"\U000272CA"; + char32_t c32butterfly[] = U"\U000272CA"; + wchar_t wbutterfly[] = L"\U000272CA"; + char u8butterfly[] = u8"\U000272CA"; + bool mbs_u8 = (sizeof strvowel != sizeof u8vowel) + ? false : + (!strcmp(strvowel, u8vowel)) + && (!strcmp("\U000272CA", (char const*)u8"\U000272CA")); + printf(VBAR "%ls world: switched \"%s\" \u21E8 \"%s\", multibyte strings are%s utf8, state%s\n", + wcshello, locC, locLoc, + mbs_u8 ? "" : " not", + mblen(nullptr, 0) ? "full" : "less"); + printf(VBAR "wchar_t (%ssigned) is %zu bit, wint_t (%ssigned) is %zu bit, WEOF is ", + (WCHAR_MIN ? "" : "un"), sizeof(wchar_t)*CHAR_BIT, + (WINT_MIN ? "" : "un"), sizeof(wint_t)*CHAR_BIT); + if (WINT_MIN) printf("%lld\n", (long long)WEOF); + else printf("%#llx\n", (unsigned long long)WEOF); + printf(VBAR "wchar_t are Unicode code points as of %s %s, ", +# ifdef __STDC_ISO_10646__ + MONTH(__STDC_ISO_10646__), + YEAR(__STDC_ISO_10646__) +# else + "", + "" +# endif + ); +# ifdef __STDC_MB_MIGHT_NEQ_WC__ + fputs("the basic character set is not extended to wchar_t\n", stdout); +# else + fputs("wchar_t extends the basic character set\n", stdout); +# endif +#ifdef __STDC_UTF_16__ + fputs(VBAR "char16_t is UTF-16, ", stdout); +#else + fputs("char16_t is not UTF-16, ", stdout); +#endif +#ifdef __STDC_UTF_32__ + fputs("char32_t is UTF-32\n", stdout); +#else + fputs(VBAR "char32_t is not UTF-32\n", stdout); +#endif + draw_sep(g_table[g_u|g_d|g_r], g_table[g_l|g_u|g_d]); + printf(VBAR "testing character %ls, codepoint U+%lX, utf8 %hhx %hhx %hhx %hhx %hhx:\n", + wbutterfly, 0x272CALU, + u8butterfly[0], u8butterfly[1], u8butterfly[2], u8butterfly[3], u8butterfly[4]); + printf(VBAR "\twchar_t,\tlength %zu,\t%s\n", + (sizeof(wbutterfly)/sizeof(wbutterfly[0])) - 1, + (sizeof(wbutterfly)/sizeof(wbutterfly[0])) == 2 + ? "one word encoding" : "surrogate encoding"); + printf(VBAR "\tchar32_t,\tlength %zu,\t%s\n", + (sizeof(c32butterfly)/sizeof(c32butterfly[0])) - 1, + (sizeof(c32butterfly)/sizeof(c32butterfly[0])) == 2 + ? "one word encoding" : "surrogate encoding"); + printf(VBAR "\tchar16_t,\tlength %zu,\t%s\n", + (sizeof(c16butterfly)/sizeof(c16butterfly[0])) - 1, + (sizeof(c16butterfly)/sizeof(c16butterfly[0])) == 2 + ? "one word encoding" : "surrogate encoding"); + if (sizeof(wchar_t) == sizeof(char32_t)) + printf(VBAR "\twchar_t and char32_t encoding %s\n", + (sizeof(c32butterfly) == sizeof(wbutterfly) + && c32butterfly[0] == wbutterfly[0] + && c32butterfly[1] == wbutterfly[1]) ? "are equal" : "differ"); + else if (sizeof(wchar_t) == sizeof(char16_t)) + printf(VBAR "\twchar_t and char16_t encoding %s\n", + (sizeof(c16butterfly) == sizeof(wbutterfly) + && c16butterfly[0] == wbutterfly[0] + && c16butterfly[1] == wbutterfly[1]) ? "are equal" : "differ"); + else + printf(VBAR "\twchar_t, char32_t and char16_t types and encodings all differ\n"); + char const mbs0[] = u8"ł"; + char const* pos0 = mbsrmb(u8hello, nullptr, mbs0, 0); + draw_sep(g_table[g_u|g_d|g_r], g_table[g_l|g_u|g_d]); + printf(VBAR "search first:\tfound \"%s\",\tposition %td, %s\n", mbs0, pos0-u8hello, pos0); + pos0 = mbsrmb(u8hello, nullptr, mbs0, 1); + printf(VBAR "search second:\tfound \"%s\",\tposition %td, %s\n", mbs0, pos0-u8hello, pos0); + char const mbs1[] = u8"ł"; + char const* pos1 = mbsrrmb(u8hello, nullptr, mbs1); + printf(VBAR "search last:\tfound \"%s\",\tposition %td, %s\n", mbs1, pos1-u8hello, pos1); + char const mbs2[] = u8"łö"; + char const* pos2 = mbsrmbs(u8hello, nullptr, mbs2); + printf(VBAR "search needle:\tfound \"%s\",\tposition %td, %s\n", mbs2, pos2-u8hello, pos2); + char const mbs3[] = u8" \u2003\u2002—"; + size_t pos3 = mbsspn_class(mbs3, iswspace); + printf(VBAR "skip space:\tfound \"%s\",\tposition %zu, %s\n", mbs3, pos3, mbs3+pos3); + draw_sep(g_table[g_u|g_d|g_r], g_table[g_l|g_u|g_d]); + printf(VBAR "floats (locale):\t%g (C with .)\t%g (C with ,)\t%g (with .)\t%g (with ,)\n", + strtod("9.3", nullptr), strtod("9,3", nullptr), + mbsrtod("\u20039.3", nullptr, nullptr), mbsrtod("\u20039,3", nullptr, nullptr)); + draw_sep(g_table[g_u|g_d|g_r], g_table[g_l|g_u|g_d]); + printf(VBAR "vowels\t%ls\n", wcsvowel); + char u8vowel2[MB_LEN_MAX + sizeof u8vowel]; + char* partial = mbsrncpy(sizeof u8vowel-2, u8vowel2, nullptr, u8vowel, nullptr); + if (partial[0]) partial[0] = 0; + printf(VBAR "partial\t%s\n", u8vowel2); + for (char const* s = strhello; s && s[0];) { + s += mbscspn(s, strvowel); + int vowels = mbsspn(s, strvowel); + if (!vowels) { + printf("bizarre, no vowels in remaining string %s", s); + break; + } + printf(VBAR "%d bytes for vowel(s) %.*s\n", vowels, vowels, s); + s += vowels; + } + draw_sep(g_table[g_u|g_r], g_table[g_l|g_u]); +} diff --git a/mbstrings.c b/mbstrings.c new file mode 100644 index 0000000..15d681c --- /dev/null +++ b/mbstrings.c @@ -0,0 +1,346 @@ +#include +//#include +#include +#include + +#include "mbstrings.h" +//#include "c23-fallback.h" + +/** + ** @file + ** @brief implement multibyte character string helpers + **/ + +size_t mbrtow(wchar_t*restrict C, char const c[restrict static 1], + mbstate_t*restrict state) { + if (!state) state = MBSTATE; + size_t len = -2; + for (size_t maxlen = MB_LEN_MAX; len >= mbincomplete; maxlen *= 2) + len = mbrtowc(C, c, maxlen, state); + if (len == mbinvalid) errno = 0; + return len; +} + +wint_t mbtow(char const*c) { + wchar_t C = 0; + size_t len = mbrtow(&C, c, MBSTATE); + return (len == mbinvalid) ? WEOF : C; +} + +size_t mbsrlen(char const*s, mbstate_t const*restrict state) { + state = state ? state : MBSTATE; + mbstate_t st = *state; + size_t mblen = mbsrtowcs(nullptr, &s, 0, &st); + if (mblen == mbinvalid) errno = 0; + return mblen; +} + +wchar_t* mbsrdup(char const*s, mbstate_t*restrict state) { + mbstate_t st = state ? *state : *MBSTATE; + size_t mblen = mbsrlen(s, &st); + if (mblen == mbinvalid) return nullptr; + wchar_t* S = malloc(sizeof(wchar_t[mblen+1])); + /* We know that s converts well, so no error check */ + if (S) mbsrtowcs(S, &s, mblen+1, state); + return S; +} + +#define SURROG0 0xD800L +#define SURROG1 (SURROG0 + 1024) +#define SURROG2 (SURROG1 + 1024) + +int iswhighsurrogate(wint_t x) { + return (SURROG0 <= x) && (x < SURROG1); +} + +int iswlowsurrogate(wint_t x) { + return (SURROG1 <= x) && (x < SURROG2); +} + +int iswsurrogate(wint_t x) { + return (SURROG0 <= x) && (x < SURROG2); +} + +int iswvalid(wint_t x) { + return x && x != WEOF && !iswsurrogate(x); +} + +char const* mbsrwc(char const s[restrict static 1], mbstate_t*restrict state, + wchar_t C, size_t occurrence) { + if (!C || C == WEOF) return nullptr; + state = state ? state : MBSTATE; + char const* ret = nullptr; + + mbstate_t st = *state; + for (size_t len = 0; s[0]; s += len) { + mbstate_t backup = st; + wchar_t S = 0; + len = mbrtow(&S, s, &st); + if (!S) break; + if (C == S) { + *state = backup; + ret = s; + if (!occurrence) break; + --occurrence; + } + } + return ret; +} + +char const* mbsrmb(char const s[static 1], mbstate_t*restrict state, + char const c[static 1], size_t occurrence) { + state = state ? state : MBSTATE; + wint_t C = mbtow(c); + return mbsrwc(s, state, C, occurrence); +} + +char const* mbsrrwc(char const s[restrict static 1], mbstate_t*restrict state, + wchar_t C) { + return mbsrwc(s, state, C, SIZE_MAX); +} + +char const* mbsrrmb(char const s[static 1], mbstate_t*restrict state, + char const c[static 1]) { + return mbsrmb(s, state, c, SIZE_MAX); +} + +char const* mbsrwcjump(char const s1[static 1], mbstate_t*restrict state, + size_t S2len, wchar_t const S2[S2len]) { + state = state ? state : MBSTATE; + mbstate_t st = *state; + for (size_t i = 0; i < S2len; ++i) { + wchar_t S1 = 0; + s1 += mbrtow(&S1, s1, &st); + if (S1 != S2[i]) return nullptr; + } + *state = st; + return s1; +} + + +char const* mbsrwcs(char const s1[static 1], mbstate_t*restrict state, + wchar_t const* S2) { + if (!S2) return nullptr; + state = state ? state : MBSTATE; + size_t S2len = wcslen(S2); + switch (S2len) { + case 0: return nullptr; + case 1: return mbsrwc(s1, state, S2[0], 0); + default:; + /* Don't modify shift state until we found it. */ + mbstate_t rstate = *state; + while (s1 && s1[0]) { + s1 = mbsrwc(s1, &rstate, S2[0], 0); + if (s1 && s1[0]) { + /* s1 now is at a potential starting point */ + char const* ret = s1; + mbstate_t tstate = rstate; + if (mbsrwcjump(s1, &tstate, S2len, S2)) { + *state = rstate; + return ret; + } + /* Advance s1 to the next mb character. */ + s1 += mbrtow(nullptr, s1, &rstate); + } + } + } + return nullptr; +} + +char const* mbsrmbs(char const s1[static 1], mbstate_t*restrict state, + char const* s2) { + state = state ? state : MBSTATE; + wchar_t*restrict S2 = mbsrdup(s2, nullptr); + if (!S2 || !S2[0]) return nullptr; + s1 = mbsrwcs(s1, state, S2); + free(S2); + return s1; +} + +char const* mbsrwcsskip(char const s1[static 1], mbstate_t*restrict state, + wchar_t const* S2){ + if (S2) { + state = state ? state : MBSTATE; + mbstate_t st = *state; + for (size_t len; s1[0]; *state = st, s1 += len) { + wchar_t S1[3] = { }; + len = mbrtow(&S1[0], s1, &st); + if (!S1[0]) break; + if (!iswlowsurrogate(S1[0])) { + if (!wcschr(S2, S1[0])) break; + } else { + len += mbrtow(&S1[1], s1, &st); + if (!wcsstr(S2, S1)) break; + } + } + *state = st; + } + return s1; +} + +char const* mbsrskip(char const s1[static 1], mbstate_t*restrict state, + char const* s2) { + state = state ? state : MBSTATE; + wchar_t*restrict S2 = mbsrdup(s2, nullptr); + s1 = mbsrwcsskip(s1, state, S2); + free(S2); + return s1; +} + +size_t mbsspn(char const* s1, char const* s2) { + return s1 ? mbsrskip(s1, 0, s2)-s1 : 0; +} + +char const* mbsrskip_class(char const s1[static 1], mbstate_t*restrict state, + wcclass_t func) { + state = state ? state : MBSTATE; + for (size_t len; s1[0]; s1 += len) { + wchar_t S1 = 0; + len = mbrtow(&S1, s1, state); + if (!S1 || !func(S1)) break; + } + return s1; +} + +size_t mbsspn_class(char const* s1, wcclass_t func) { + return s1 ? mbsrskip_class(s1, 0, func)-s1 : 0; +} + +char const* mbsrskip_type(char const s1[static 1], mbstate_t*restrict state, wctype_t type) { + state = state ? state : MBSTATE; + for (size_t len; s1[0]; s1 += len) { + wchar_t S1 = 0; + len = mbrtow(&S1, s1, state); + if (!S1 || !iswctype(S1, type)) break; + } + return s1; +} + +size_t mbsspn_type(char const* s1, wctype_t type) { + return s1 ? mbsrskip_type(s1, 0, type)-s1 : 0; +} + +size_t mbsspn_name(char const* s1, char const name[static 1]) { + return mbsspn_type(s1, wctype(name)); +} + +char const* mbsrwcscskip(char const s1[static 1], mbstate_t*restrict state, + wchar_t const* S2) { + state = state ? state : MBSTATE; + if (S2) { + mbstate_t st = *state; + for (size_t len; s1[0]; s1 += len) { + wchar_t S1[3] = { }; + len = mbrtow(&S1[0], s1, &st); + if (!S1[0]) break; + if (!iswlowsurrogate(S1[0])) { + if (wcschr(S2, S1[0])) break; + } else { + len += mbrtow(&S1[1], s1, &st); + if (wcsstr(S2, S1)) break; + } + } + *state = st; + } + return s1; +} + +char const* mbsrcskip(char const* s1, mbstate_t*restrict state, char const* s2) { + state = state ? state : MBSTATE; + wchar_t*restrict S2 = mbsrdup(s2, nullptr); + s1 = mbsrwcscskip(s1, state, S2); + free(S2); + return s1; +} + +size_t mbscspn(char const* s1, char const* s2) { + return (s1 && s2) ? mbsrcskip(s1, 0, s2)-s1 : 0; +} + + +double mbsrtod(char const*restrict s1, mbstate_t*restrict state, + char**restrict endptr) { + state = state ? state : MBSTATE; + return strtod(mbsrskip_class(s1, state, iswspace), endptr); +} + +long double mbsrtold(char const*restrict s1, mbstate_t*restrict state, char**restrict endptr) { + state = state ? state : MBSTATE; + return strtold(mbsrskip_class(s1, state, iswspace), endptr); +} + +long mbsrtol(char const*restrict s1, mbstate_t*restrict state, char**restrict endptr, int base) { + state = state ? state : MBSTATE; + return strtol(mbsrskip_class(s1, state, iswspace), endptr, base); +} + +long long mbsrtoll(char const*restrict s1, mbstate_t*restrict state, char**restrict endptr, int base) { + state = state ? state : MBSTATE; + return strtoll(mbsrskip_class(s1, state, iswspace), endptr, base); +} + +unsigned long mbsrtoul(char const*restrict s1, mbstate_t*restrict state, char**restrict endptr, int base) { + state = state ? state : MBSTATE; + return strtoul(mbsrskip_class(s1, state, iswspace), endptr, base); +} + +unsigned long long mbsrtoull(char const*restrict s1, mbstate_t*restrict state, char**restrict endptr, int base) { + state = state ? state : MBSTATE; + return strtoull(mbsrskip_class(s1, state, iswspace), endptr, base); +} + + + +char* mbsrmbsncpy(size_t n, char t[restrict n], mbstate_t const*restrict state, + char const s [restrict static 1]) { + /* Don't do anything if s wouldn't fit entirely into t */ + char const* ep = memchr(s, 0, n); + if (!ep) return nullptr; + size_t slen = ep - s; + /* If the target is in non-initial state, try to reset it. */ + if (!mbsinit(state)) { + /* Now state is known to be non-null. */ + char buf[2*MB_LEN_MAX]; + mbstate_t st = *state; + size_t len = wcrtomb(buf, 0, &st); + /* See if shift characters plus s will fit into t. */ + if (slen+len >= n) return nullptr; + /* write the bytes that end the shift state */ + memcpy(t, buf, len); + t += len-1; + n -= len-1; + } + if (slen >= n) return nullptr; + memcpy(t, s, slen+1); + return t + slen; +} + +char* mbsrncpy(size_t n, char t[restrict n], mbstate_t*restrict tstate, + char const s [restrict static 1], mbstate_t const*restrict sstate) { + /* First check if the bytes can just be copied over. */ + if (mbsinit(sstate)) { + char* ret = mbsrmbsncpy(n, t, tstate, s); + if (ret) return ret; + } + char* tt = t; + tstate = tstate ? tstate : MBSTATE; + sstate = sstate ? sstate : MBSTATE; + mbstate_t sst = *sstate; + char buf[2*MB_LEN_MAX]; + for (size_t slen = 0; s[0] && n; s += slen) { + wchar_t S[2] = { }; + slen = mbrtowc(S, s, n, &sst); + /* Here, wcsrtombs can't fail, but because of the different state + of t, tlen may be larger than slen. */ + wchar_t const* Sp = S; + mbstate_t tst = *tstate; + size_t tlen = wcsrtombs(buf, &Sp, n, &tst); + if (tlen > n) break; + *tstate = tst; + memcpy(tt, buf, tlen); + if (!S[0]) break; + tt += tlen; + n -= tlen; + } + return tt; +} diff --git a/mbstrings.h b/mbstrings.h new file mode 100644 index 0000000..221720b --- /dev/null +++ b/mbstrings.h @@ -0,0 +1,606 @@ +#include "c23-fallback.h" +#include +#include + +#if (__STDC_UTF_16__ != 1) || (__STDC_UTF_32__ != 1) +# error "wide character strings should use UTF encodings" +#endif + +/** + ** @file + ** @brief Helper functions for multibyte strings. + ** + ** These functions try to ease the use of multibyte strings. Most of + ** the time they pass through wide character functions to achieve + ** their goal. This has only use that is somewhat limited if on + ** platform wide characters are not of fixed width but use + ** surrogates. For such surrogate characters the classification + ** functions will fail erratically. + ** + ** @warning this implementations supposes that @c wchar_t supports + ** some form of Unicode. + **/ + +#ifndef __STDC_ISO_10646__ +# warning "wchar_t wide characters have to be Unicode code points" +#endif +#ifdef __STDC_MB_MIGHT_NEQ_WC__ +# warning "basic character codes must agree on char and wchar_t" +#endif + +/** + ** @brief A type for 16 bit characters as provided by "uchar.h" + ** + ** This is the base type of strings that are prefixed with @c u, such + ** as in u"string". + ** + ** There are good chances that on most platforms this is the UCS-2 + ** encoding, thus an encoding that has surrogates. + ** + ** @remark This should be provided by the header file, but the type + ** is fixed as is given here, and C11 compilers should tolerate a + ** repetition of a @c typedef, as long as it is consistent. + **/ +typedef uint_least16_t char16_t; + +/** + ** @brief A type for 32 bit characters as provided by "uchar.h" + ** + ** This is the base type of strings that are prefixed with @c U, such + ** as in U"string". + ** + ** There are good chances that on most platforms this is the UCS-4 + ** encoding, thus an encoding without surrogates. + ** + ** @remark This should be provided by the header file, but the type + ** is fixed as is given here, and C11 compilers should tolerate a + ** repetition of a @c typedef, as long as it is consistent. + **/ +typedef uint_least32_t char32_t; + +/** + ** @brief A helper type to ensure proper initialization of @c + ** mbstate_t variables + ** + ** Not to be used directly + ** @see MBSTATE + **/ +typedef union mbstate_t_ { + unsigned char bytes[sizeof(mbstate_t)]; + mbstate_t state; +} mbstate_t_; + +/** + ** @brief Provide a pointer to a properly initialized @c mbstate_t + ** + ** This uses the union type to ensure that the state is initialized + ** with all bytes set to @c 0. + **/ +#define MBSTATE (&(union mbstate_t_){ .bytes = { }}.state) + +/** + ** @brief For functions that receive a wide character classification + ** function as an argument. + **/ +typedef int (*wcclass_t)(wint_t); + +/** + ** @brief Interpret a sequence of bytes in @a c as mb character + ** and return that as wide character through @a C + ** + ** @return the length of the mb character or @c mbinvalid if an + ** encoding error occured. + ** + ** This function can be integrated into a sequence of such + ** searches through a string, as long as the same @a state + ** argument is passed to all calls to this or similar functions. + ** + ** @remark @a state of null indicates that @a c can be scanned + ** without considering any context. + **/ +size_t mbrtow(wchar_t*restrict C, char const c[restrict static 1], + mbstate_t*restrict state); + +/** + ** @brief Interpret a sequence of bytes in @a c as mb character and + ** return that as wide character. + ** + ** @return the wide character converted to @c wint_t or @c WEOF if an + ** encoding error occurred. + ** + ** This function should only be used for strings containing one + ** single mb character. It cannot be integrated into a sequence of + ** searches through a string with more characters since the @c + ** mbstate_t is not kept. + **/ +wint_t mbtow(char const*c); + +/** + ** @brief Interpret a mb string in @a mbs and return its + ** length when interpreted as a wide character string + ** + ** @return the length of the mb string or @c mbinvalid if an + ** encoding error occured. + ** + ** This function can be integrated into a sequence of searches + ** through a string, as long as a @a state argument is passed to + ** this function that is consistent with the mb character + ** starting in @a mbs. The state itself is not modified by this + ** function. + ** + ** @remark @a state of null indicates that @a mbs can be scanned + ** without considering any context. + **/ +size_t mbsrlen(char const*restrict mbs, + mbstate_t const*restrict state); + +/** + ** @brief Interpret a sequence of bytes in @a s as mb string and + ** convert it to a wide character string. + ** + ** @return a newly malloc'ed wide character string of the + ** appropriate length, null if an encoding error occurred. + ** + ** @remark This function can be integrated into a sequence of + ** such searches through a string, as long as a @a state + ** argument is passed to this function that is consistent with + ** the mb character starting in @a c. The state itself is not + ** modified by this function. + ** + ** @remark @a state of null indicates that @a s can be scanned + ** without considering any context. + **/ +wchar_t* mbsrdup(char const*s, mbstate_t*restrict state); + +/** + ** @brief Copy at most @a n bytes of the mb string @a s to @a t. + ** + ** This is meant to be an efficient variant of ::mbsrncpy for + ** the case the copy operation can be done with ::memcpy. + ** + ** Mb string @a s is supposed to be a complete mb string in + ** initial shift state. If it has no @c 0 character in the first + ** @a n bytes, the copy operation fails and the function returns + ** null. + ** + ** The mb string that is produced in @a t may be the + ** continuation of an existing prefix who's conversion state is + ** described by @a state. + ** + ** If @a s can not completely be copied because the adjustment + ** of the shift state would leave less than + ** strlen(s)+1 space in @a t, the copy operation is + ** aborted and null is returned. + ** + ** @return the address of the terminating null character in @a t + ** if the copy operation succeeded, null otherwise. In case of + ** success, @a t is in an initial shift state for the returned + ** position. + **/ +char* mbsrmbsncpy(size_t n, char t[restrict n], mbstate_t const*restrict state, + char const s [restrict static 1]); + + +/** + ** @brief Copy at most @a n bytes of the mb string @a s to @a t. + ** + ** This is a safe variant of ::mbsrmbsncpy that covers all + ** cases. + ** + ** Mb string @a s is supposed to be a mb string in state @a + ** sstate. If it has no @c 0 character in the first @a n bytes, + ** the copy operation will be partial up to that point. + ** + ** The mb string that is produced in @a t may be the continuation of + ** an existing prefix who's conversion state is described by @a + ** sstate. + ** + ** This function copies complete mb characters as long as they + ** fit into the target array. To be able to do so, the + ** conversion state of @a t might have to be adapted to be in + ** sync with @a s. By that the copied part may be longer than + ** strlen(s)+1. + ** + ** @return The address of the first byte in @a t after the copy + ** is returned; that byte is null if the whole @a s could be + ** copied. If nothing could be copied, @a t is returned. In that + ** case @c *t is not written to and @a tstate is unchanged. + **/ +char* mbsrncpy(size_t n, char t[restrict n], mbstate_t*restrict tstate, + char const s [restrict static 1], mbstate_t const*restrict sstate); + + +/** + ** @brief Interpret a sequence of bytes in @a s as mb string and + ** search for wide character @a C + ** + ** @return the @a occurrence'th position in @a s that starts a + ** mb sequence corresponding to @a C or null if an encoding + ** error occurred. + ** + ** If the number of occurrences is less than @a occurrence the + ** last such position is returned. So in particular using @c + ** SIZE_MAX (or @c -1) will always return the last occurrence. + ** + ** @remark This function can be integrated into a sequence of + ** such searches through a string, as long as the same @a state + ** argument passed to all calls to this or similar functions and + ** as long as the continuation of the search starts at the + ** position that is returned by this function. + ** + ** @remark @a state of null indicates that @a s can be scanned + ** without considering any context. + **/ +char const* mbsrwc(char const s[restrict static 1], + mbstate_t*restrict state, + wchar_t C, size_t occurrence); + +/** + ** @brief Interpret a sequence of bytes in @a s as mb string and + ** search for mb character represented by @a c + ** + ** @return the @a occurrence'th position in @a s that starts a + ** mb sequence corresponding to @a c or null if an encoding + ** error occurred. + ** + ** If the number of occurrences is less than @a occurrence the + ** last such position is returned. So in particular using @c + ** SIZE_MAX (or @c -1) will always return the last occurrence. + ** + ** @remark This function can be integrated into a sequence of + ** such searches through a string, as long as the same @a state + ** argument passed to all calls to this or similar functions and + ** as long as the continuation of the search starts at the + ** position that is returned by this function. + ** + ** @remark @a state of null indicates that @a s can be scanned + ** without considering any context. + **/ +char const* mbsrmb(char const s[static 1], mbstate_t*restrict state, + char const c[static 1], size_t occurence); + +/** + ** @brief Interpret a sequence of bytes in @a s as mb string and + ** and reverse search for wide character @a C + ** + ** @return the maximal position in @a s that starts a mb + ** sequence corresponding to @a C or null if an encoding error + ** occured. + ** + ** @remark This function can be integrated into a sequence of + ** such searches through a string, as long as the same @a state + ** argument passed to all calls to this or similar functions and + ** as long as the continuation of the search starts at the + ** position that is returned by this function. + ** + ** @remark @a state of null indicates that @a s can be scanned + ** without considering any context. + **/ +char const* mbsrrwc(char const s[restrict static 1], mbstate_t*restrict state, + wchar_t C); + +/** + ** @brief Interpret a sequence of bytes in @a s as mb string and + ** reverse search for mb character represented by @a c + ** + ** @return the maximal position in @a s that starts a mb + ** sequence corresponding to @a c or null if an encoding error + ** occured. + ** + ** @remark This function can be integrated into a sequence of + ** such searches through a string, as long as the same @a state + ** argument passed to all calls to this or similar functions and + ** as long as the continuation of the search starts at the + ** position that is returned by this function. + ** + ** @remark @a state of null indicates that @a s can be scanned + ** without considering any context. + **/ +char const* mbsrrmb(char const s[static 1], mbstate_t*restrict state, + char const c[static 1]); + +/** + ** @brief In mb string @a s1 jump over the initial segment + ** corresponding to wide character string @a S2. + ** + ** @return the position in @a s1 after the initial segment if @a + ** S2 is found or null otherwise. + ** + ** @remark This function can be integrated into a sequence of + ** such searches through a string, as long as the same @a state + ** argument passed to all calls to this or similar functions and + ** as long as the continuation of the search starts at the + ** position that is returned by this function. + ** + ** @remark A @a state that is null indicates that @a s1 can be scanned + ** without considering any context. + **/ +char const* mbsrwcjump(char const s1[static 1], mbstate_t*restrict state, + size_t S2len, wchar_t const S2[S2len]); + +/** + ** @brief In mb string @a s1 find a segment corresponding to + ** wide character string @a S2. + ** + ** @return the position in @a s1 that starts the first such + ** segment if @a S2 is found or null otherwise. + ** + ** @remark This function can be integrated into a sequence of + ** such searches through a string, as long as the same @a state + ** argument passed to all calls to this or similar functions and + ** as long as the continuation of the search starts at the + ** position that is returned by this function. + ** + ** @remark A @a state of null indicates that @a s can be scanned + ** without considering any context. + **/ +char const* mbsrwcs(char const s1[static 1], mbstate_t*restrict state, + wchar_t const* S2); + +/** + ** @brief In mb string @a s1 find a segment corresponding to mb + ** string @a s2. + ** + ** @return the position in @a s1 that starts the first such + ** segment if @a s2 is found or null otherwise. + ** + ** @remark This function can be integrated into a sequence of + ** such searches through a string, as long as the same @a state + ** argument passed to all calls to this or similar functions and + ** as long as the continuation of the search starts at the + ** position that is returned by this function. + ** + ** @remark A @a state of null indicates that @a s can be scanned + ** without considering any context. + **/ +char const* mbsrmbs(char const s1[static 1], mbstate_t*restrict state, + char const* s2); + +/** + ** @brief In mb string @a s1 skip over the initial part of the + ** string that corresponds to wide characters in string @a S2. + ** + ** @return the position in @a s1 that corresponds to the the + ** first non-matching wide character in @a S2, which may be the + ** terminating @c 0 character or a position that produces an + ** encoding error. + ** + ** @a state is updated to correspond to the returned position. + ** + ** @remark This function can be integrated into a sequence of + ** such searches through a string, as long as the same @a state + ** argument passed to all calls to this or similar functions and + ** as long as the continuation of the search starts at the + ** position that is returned by this function. + ** + ** @remark A @a state of null indicates that @a s can be scanned + ** without considering any context. + **/ +char const* mbsrwcsskip(char const s1[static 1], mbstate_t*restrict state, + wchar_t const* S2); + +/** + ** @brief In mb string @a s1 skip over the initial part of the + ** string that corresponds to mb characters in string @a s2. + ** + ** @return the position in @a s1 that corresponds to the the + ** first non-matching mb character in @a s2, which may be the + ** terminating @c 0 character or a position that produces an + ** encoding error. + ** + ** @a state is updated to correspond to the returned position. + ** + ** @remark This function can be integrated into a sequence of + ** such searches through a string, as long as the same @a state + ** argument passed to all calls to this or similar functions and + ** as long as the continuation of the search starts at the + ** position that is returned by this function. + ** + ** @remark A @a state of null indicates that @a s can be scanned + ** without considering any context. + **/ +char const* mbsrskip(char const s1[static 1], mbstate_t*restrict state, + char const* s2); + +/** + ** @brief In mb string @a s1 return the length of the initial + ** part of the string that corresponds to mb characters in + ** string @a s2. + ** + ** @return the position in @a s1 that corresponds to the the + ** first non-matching mb character in @a s2, which may be the + ** terminating @c 0 character or a position that produces an + ** encoding error. + ** + ** @remark This function supposes that @a s1 can be scanned + ** without context, that is it starts in an initial shift state. + **/ +size_t mbsspn(char const* s1, char const* s2); + +/** + ** @brief In mb string @a s1 skip over the initial part of the + ** string that verifies classification @a func. + ** + ** @return the position in @a s1 that corresponds to the the + ** first non-matching mb character in @a s2, which may be the + ** terminating @c 0 character or a position that produces an + ** encoding error. + ** + ** @a state is updated to correspond to the returned position. + ** + ** @remark This function can be integrated into a sequence of + ** such searches through a string, as long as the same @a state + ** argument passed to all calls to this or similar functions and + ** as long as the continuation of the search starts at the + ** position that is returned by this function. + ** + ** @remark A @a state of null indicates that @a s can be scanned + ** without considering any context. + **/ +char const* mbsrskip_class(char const s1[static 1], mbstate_t*restrict state, + wcclass_t func); + +/** + ** @brief In mb string @a s1 return the length of the initial + ** part of the string that verifies classification @a func. + ** + ** @return the position in @a s1 that corresponds to the the + ** first non-matching mb character in @a s2, which may be the + ** terminating @c 0 character or a position that produces an + ** encoding error. + ** + ** @remark This function supposes that @a s1 can be scanned + ** without context, that is it starts in an initial shift state. + **/ +size_t mbsspn_class(char const* s1, wcclass_t func); + +/** + ** @brief In mb string @a s1 skip over the initial part of the + ** string that verifies type @a type. + ** + ** @return the position in @a s1 that corresponds to the the + ** first non-matching mb character in @a s2, which may be the + ** terminating @c 0 character or a position that produces an + ** encoding error. + ** + ** @a state is updated to correspond to the returned position. + ** + ** @remark This function can be integrated into a sequence of + ** such searches through a string, as long as the same @a state + ** argument passed to all calls to this or similar functions and + ** as long as the continuation of the search starts at the + ** position that is returned by this function. + ** + ** @remark A @a state of null indicates that @a s can be scanned + ** without considering any context. + **/ +char const* mbsrskip_type(char const s1[static 1], mbstate_t*restrict state, wctype_t type); + +/** + ** @brief In mb string @a s1 return the length of the initial + ** part of the string that verifies type @a type. + ** + ** @return the position in @a s1 that corresponds to the the + ** first non-matching mb character in @a s2, which may be the + ** terminating @c 0 character or a position that produces an + ** encoding error. + ** + ** @remark This function supposes that @a s1 can be scanned + ** without context, that is it starts in an initial shift state. + **/ +size_t mbsspn_type(char const* s1, wctype_t type); + +/** + ** @brief In mb string @a s1 return the length of the initial + ** part of the string who's type corresponds to category @a + ** name. + ** + ** Predefined standard names are @c "alnum", @c "alpha", @c + ** "blank", @c "cntrl", @c "digit", @c "graph", @c "lower", @c + ** "print", @c "punct", @c "space", @c "upper", and @c + ** "xdigit". Others may be provided by your platform. + ** + ** @return the position in @a s1 that corresponds to the the + ** first non-matching mb character in @a s2, which may be the + ** terminating @c 0 character or a position that produces an + ** encoding error. + ** + ** @remark This function supposes that @a s1 can be scanned + ** without context, that is it starts in an initial shift state. + **/ +size_t mbsspn_name(char const* s1, char const name[static 1]); + +/** + ** @brief In mb string @a s1 skip over the initial part of the + ** string that doesn't correspond to wide characters in string + ** @a S2. + ** + ** @see mbsrwcsskip + **/ +char const* mbsrwcscskip(char const s1[static 1], mbstate_t*restrict state, wchar_t const* S2); + +/** + ** @brief In mb string @a s1 skip over the initial part of the + ** string that doesn't correspond to mb characters in string @a + ** s2. + ** + ** @see mbsrskip + **/ +char const* mbsrcskip(char const* s1, mbstate_t*restrict state, char const* s2); + +/** + ** @brief In mb string @a s1 skip over the initial part of the + ** string that doesn't correspond to mb characters in string @a + ** s2. + ** + ** @see mbsrskip + **/ +size_t mbscspn(char const* s1, char const* s2); + +/** + ** @brief Convert mb string @a s1 to @c double. + ** + ** @see strtod + **/ +double mbsrtod(char const*restrict s1, mbstate_t*restrict state, char**restrict endptr); + +/** + ** @brief Convert mb string @a s1 to long double. + ** + ** @see strtold + **/ +long double mbsrtold(char const*restrict s1, mbstate_t*restrict state, char**restrict endptr); + +/** + ** @brief Convert mb string @a s1 to long according + ** to base @a base. + ** + ** @see strtol + **/ +long mbsrtol(char const*restrict s1, mbstate_t*restrict state, char**restrict endptr, int base); + +/** + ** @brief Convert mb string @a s1 to long long according + ** to base @a base. + ** + ** @see strtoll + **/ +long long mbsrtoll(char const*restrict s1, mbstate_t*restrict state, char**restrict endptr, int base); + +/** + ** @brief Convert mb string @a s1 to unsigned long + ** according to base @a base. + ** + ** @see strtoul + **/ +unsigned long mbsrtoul(char const*restrict s1, mbstate_t*restrict state, char**restrict endptr, int base); + +/** + ** @brief Convert mb string @a s1 to unsigned long + ** long according to base @a base. + ** + ** @see strtoull + **/ +unsigned long long mbsrtoull(char const*restrict s1, mbstate_t*restrict state, char**restrict endptr, int base); + +/** + ** @brief The codes returned by multi-byte conversion functions. + ** + ** In general these functions return a value of type + ** `size_t`. Therefore the special codes have to be at the upper end + ** of the range of that type. + ** + ** This provides names for these constants as enumeration type. This + ** is only possible since C23, because previously enumeration + ** constants had been limited to `signed int`. + **/ +enum mbcode { + //! An invalid encoding was encountered. + mbinvalid = (size_t)-1, + //! The encoding was incomplete. + mbincomplete = (size_t)-2, + //! A previously detect output character was stored. + mbstored = (size_t)-3, +}; + +static_assert(mbinvalid == SIZE_MAX, "enum mbcode should have size_t as underlying integer type"); diff --git a/numberline.c b/numberline.c new file mode 100644 index 0000000..9f8f7fe --- /dev/null +++ b/numberline.c @@ -0,0 +1,276 @@ +#include "c23-fallback.h" +//#include +#include +//#include + +/** + ** @brief interpret string @a lbuf as a sequence of numbers + ** represented with @a base + ** + ** @return a newly allocated array of numbers as found in @a lbuf + ** @param lbuf is supposed to be a string + ** @param np if non-null, the count of numbers is stored in + ** @c *np + ** @param base value from 0 to 36, with the same interpretation + ** as for @c strtoul and similar functions + ** + ** @remark The caller of this function is responsible to @c + ** free the array that is returned. + **/ +size_t* numberline(size_t size, + char const lbuf[restrict static size], + size_t*restrict np, int base); + +/** + ** @brief read one text line of at most size-1 + ** bytes. The '\n' character is replaced by @c 0. + ** + ** @return s if an entire line was read + ** successfully. Otherwise, @c 0 is returned and @a s contains + ** a maximal partial line that could be read. @a s is null + ** terminated. + **/ +char* fgetline(size_t size, + char s[restrict size], + FILE*restrict stream); + + +#include +#include +#ifndef EFAULT +# define EFAULT EDOM +#endif +#ifndef EOVERFLOW +# define EOVERFLOW (EFAULT-EOF) +# if EOVERFLOW > INT_MAX +# error EOVERFLOW constant is too large +# endif +#endif +#ifndef ENOMEM +# define ENOMEM (EOVERFLOW+EFAULT-EOF) +# if ENOMEM > INT_MAX +# error ENOMEM constant is too large +# endif +#endif + +/** + ** @brief print a series of numbers @a nums on @a stream, using + ** @c printf format @a form, separated by @a sep characters and + ** terminated with a newline character. + ** + ** @return the number of characters printed to @a stream, or a + ** negative error value on error. + ** + ** If @a len is @c 0, an empty line is printed and @c 1 is + ** returned. + ** + ** Possible error returns are: + ** - EOF (which is negative) if @a stream was not + ** ready to be written to + ** - -EOVERFLOW if more than INT_MAX + ** characters would have to be written, including the case + ** that @a len is greater than INT_MAX. + ** - -EFAULT if @a stream is a null pointer + ** - -EFAULT if @a numb is a null pointer and @a len is not zero + ** - -ENOMEM if a memory error occurred + ** + ** This function leaves errno to the same value as + ** occurred on entry. + **/ +int fprintnumbers(FILE*restrict stream, + char const form[restrict static 1], + char const sep[restrict static 1], + size_t len, size_t numb[restrict len]); + +/** + ** @brief print a series of numbers @a nums in @a buf, using @c + ** printf format @a form, separated by @a sep characters and + ** terminated with a newline character. + ** + ** @return the number of characters printed to @a buf. + ** + ** This supposes that @a tot and @a buf are big enough and + ** that @a form is a format suitable to print @c size_t. + **/ +int sprintnumbers(size_t tot, char buf[restrict tot], + char const form[restrict static 1], + char const sep[restrict static 1], + size_t len, size_t nums[restrict static len]); + +static +size_t numberline_inner(char const*restrict act, + size_t numb[restrict static 1], + int base){ + size_t n = 0; + for (char* next = nullptr; act[0]; act = next) { + numb[n] = strtoull(act, &next, base); + if (act == next) break; + ++n; + } + return n; +} + +size_t* numberline(size_t size, + char const lbuf[restrict static size], + size_t*restrict np, int base){ + size_t* ret = nullptr; + size_t n = 0; + /* Check for validity of the string, first. */ + if (memchr(lbuf, 0, size)) { + /* The maximum number of integers encoded. + To see that this may be as much look at + the sequence 08 08 08 08 ... and suppose + that base is 0. */ + ret = malloc(sizeof(size_t[1+(2*size)/3])); + if (!ret) return nullptr; + n = numberline_inner(lbuf, ret, base); + + size_t len = n ? n : 1; + size_t* ret2 = realloc(ret, sizeof(size_t[len])); + if (ret2) ret = ret2; + } + if (np) *np = n; + return ret; +} + +char* fgetline(size_t size, + char s[restrict size], + FILE*restrict stream){ + s[0] = 0; + char* ret = fgets(s, size, stream); + if (ret) { + /* Uses non-const variant of strchr */ + char* pos = strchr(s, '\n'); + if (pos) *pos = 0; + else ret = nullptr; + } + return ret; +} + +static inline int error_cleanup(int err, int prev) { + errno = prev; + return -err; +} + +int sprintnumbers(size_t tot, char buf[restrict tot], + char const form[restrict static 1], + char const sep[restrict static 1], + size_t len, size_t nums[const restrict static len]) { + char* p = buf; /* next position in buf */ + size_t const seplen = strlen(sep); + if (len) { + size_t i = 0; + for (; i < len;) { + p += sprintf(p, form, nums[i]); + ++i; + if (i >= len) break; + memcpy(p, sep, seplen); + p += seplen; + } + } + memcpy(p, "\n", 2); + return (p-buf)+1; +} + +int fprintnumbers(FILE*restrict stream, + char const form[restrict static 1], + char const sep[restrict static 1], + size_t len, size_t nums[restrict len]) { + if (!stream) return -EFAULT; + if (len && !nums) return -EFAULT; + if (len > INT_MAX) return -EOVERFLOW; + + size_t tot = (len ? len : 1)*strlen(sep); + int err = errno; + char* buf = nullptr; + + if (len) { + /* Count the chars for the numbers. */ + for (size_t i = 0; i < len; ++i) + tot += snprintf(nullptr, 0, form, nums[i]); + /* We return int so we have to constrain the max size. */ + if (tot > INT_MAX) return error_cleanup(EOVERFLOW, err); + } + + buf = malloc(tot+1); + if (!buf) return error_cleanup(ENOMEM, err); + + sprintnumbers(tot, buf, form, sep, len, nums); + /* print whole line in one go */ + if (fputs(buf, stream) == EOF) tot = EOF; + free(buf); + return tot; +} + +int fprintnumbers_opt(FILE*restrict stream, + char const form[restrict static 1], + char const sep[restrict static 1], + size_t len, size_t nums[restrict static len]) { + if (!stream) return -EFAULT; + if (len && !nums) return -EFAULT; + if (len > INT_MAX) return -EOVERFLOW; + + int err = errno; + size_t const seplen = strlen(sep); + + size_t tot = 0; + size_t mtot = len*(seplen+10); + char* buf = malloc(mtot); + + if (!buf) return error_cleanup(ENOMEM, err); + + for (size_t i = 0; i < len; ++i) { + tot += sprintf(&buf[tot], form, nums[i]); + ++i; + if (i >= len) break; + if (tot > mtot-20) { + mtot *= 2; + char* nbuf = realloc(buf, mtot); + if (nbuf) { + buf = nbuf; + } else { + tot = error_cleanup(ENOMEM, err); + goto CLEANUP; + } + } + memcpy(&buf[tot], sep, seplen); + tot += seplen; + if (tot > INT_MAX) { + tot = error_cleanup(EOVERFLOW, err); + goto CLEANUP; + } + } + buf[tot] = 0; + + /* print whole line in one go */ + if (fputs(buf, stream) == EOF) tot = EOF; + CLEANUP: + free(buf); + return tot; +} + +int main(void) { + char lbuf[256]; + for (;;) { + if (fgetline(sizeof lbuf, lbuf, stdin)) { + size_t n; + size_t* nums = numberline(strlen(lbuf)+1, lbuf, &n, 0); + if (nums) { + int ret = fprintnumbers(stdout, "%#zX", ",\t", n, nums); + if (ret < 0) return EXIT_FAILURE; + free(nums); + } + } else { + if (lbuf[0]) { /* a partial line has been read */ + for (;;) { + int c = getc(stdin); + if (c == EOF) return EXIT_FAILURE; + if (c == '\n') { + fprintf(stderr, "line too long: %s\n", lbuf); + break; + } + } + } else break; /* regular end of input */ + } + } +} diff --git a/precision.c b/precision.c new file mode 100644 index 0000000..efea85b --- /dev/null +++ b/precision.c @@ -0,0 +1,8 @@ +#include + +int main(void) { + printf("%.20e %.20e\n", + ((1.0E-13 + 1.0E-13)), + (1.0E-13 + (1.0E-13 + 1.0)) - 1.0 + ); +} diff --git a/rationals.c b/rationals.c new file mode 100644 index 0000000..71beb7e --- /dev/null +++ b/rationals.c @@ -0,0 +1,218 @@ +#include "rationals.h" + +size_t gcd(size_t a, size_t b) [[__unsequenced__]]; + +rat rat_get(signed sign, size_t num, size_t denom) [[__unsequenced__]] { + rat ret = { + .sign = (sign < 0), + .num = num, + .denom = denom, + }; + return ret; +} + +rat rat_get_normal(rat x) [[__unsequenced__]] { + size_t c = gcd(x.num, x.denom); + x.num /= c; + x.denom /= c; + return x; +} + +rat rat_get_extended(rat x, size_t f) [[__unsequenced__]] { + x.num *= f; + x.denom *= f; + return x; +} + +rat rat_get_prod(rat x, rat y) [[__unsequenced__]] { + rat ret = { + .sign = (x.sign != y.sign), + .num = x.num * y.num, + .denom = x.denom * y.denom, + }; + return rat_get_normal(ret); +} + +rat rat_get_sum(rat x, rat y) [[__unsequenced__]] { + size_t c = gcd(x.denom, y.denom); + size_t ax = y.denom/c; + size_t bx = x.denom/c; + x = rat_get_extended(x, ax); /*@\label{rat_get_extended_x}*/ + y = rat_get_extended(y, bx); + assert(x.denom == y.denom); + + if (x.sign == y.sign) { + x.num += y.num; + } else if (x.num > y.num) { + x.num -= y.num; + } else { + x.num = y.num - x.num; + x.sign = !x.sign; + } + return rat_get_normal(x); +} + +rat rat_get_sum2(rat x, rat y) [[__unsequenced__]] { + size_t c = gcd(x.denom, y.denom); + size_t denom = (x.denom/c)*y.denom; + size_t num; + bool sign; + + size_t ab = x.num*(y.denom/c); + size_t ba = y.num*(x.denom/c); + if (x.sign == y.sign) { + num = ab + ba; + sign = x.sign; + } else { + if (ab < ba) { + num = ba - ab; + sign = y.sign; + } else { + num = ab - ba; + sign = x.sign; + } + } + rat ret = { + .sign = sign, + .num = num, + .denom = denom, + }; + return rat_get_normal(ret); +} + +rat rat_get_prod2(rat x, rat y) [[__unsequenced__]] { + size_t tmp = y.denom; + y.denom = x.denom; + x.denom = tmp; + x = rat_get_normal(x); + y = rat_get_normal(y); + rat ret = { + .sign = (x.sign != y.sign), + .num = x.num * y.num, + .denom = x.denom * y.denom, + }; + return ret; +} + +void rat_destroy(rat* rp) [[__unsequenced__]] { + if (rp) *rp = (rat){ }; +} + +rat* rat_init(rat* rp, + signed sign, + size_t num, + size_t denom) [[__unsequenced__]] { + if (rp) *rp = rat_get(sign, num, denom); + return rp; +} + +rat* rat_normalize(rat* rp) [[__unsequenced__]] { + if (rp) *rp = rat_get_normal(*rp); + return rp; +} + +rat* rat_extend(rat* rp, size_t f) [[__unsequenced__]] { + if (rp) *rp = rat_get_extended(*rp, f); + return rp; +} + +rat* rat_sumup(rat* rp, rat y) [[__unsequenced__]] { + size_t c = gcd(rp->denom, y.denom); + size_t ax = y.denom/c; + size_t bx = rp->denom/c; + rat_extend(rp, ax); /*@\label{rat_extend_x}*/ + y = rat_get_extended(y, bx); + assert(rp->denom == y.denom); + + if (rp->sign == y.sign) { + rp->num += y.num; + } else if (rp->num > y.num) { + rp->num -= y.num; + } else { + rp->num = y.num - rp->num; + rp->sign = !rp->sign; + } + return rat_normalize(rp); /*@\label{rat_normalize_x}*/ +} + +rat* rat_rma(rat* rp, rat x, rat y) [[__unsequenced__]] { + return rat_sumup(rp, rat_get_prod(x, y)); +} + +char const* rat_print(size_t len, char tmp[len], rat const* x) { + if (x) { + snprintf(tmp, len, "%c%zu/%zu", (x->sign ? '-' : '+'), x->num, x->denom); + } else { + tmp[0] = 0; + } + return tmp; +} + +#define RAT_PRINT_MAX 256 + +#define RAT_PRINT(X) rat_print(RAT_PRINT_MAX, (char[RAT_PRINT_MAX]){ }, (rat[1]){ (X) }) + +int main(void) { + rat third = rat_get(+1, 1, 3); + rat forth = rat_get(-1, 1, 4); + printf("%s, prod is %s, 3x %s\n", + RAT_PRINT(third), + RAT_PRINT(rat_get_prod(third, third)), + RAT_PRINT(rat_get_prod(third, rat_get_prod(third, third))) + ); + printf("%s, prod is %s, 3x %s\n", + RAT_PRINT(forth), + RAT_PRINT(rat_get_prod(forth, forth)), + RAT_PRINT(rat_get_prod(forth, rat_get_prod(forth, forth))) + ); + printf("%s, prod is %s, 3x %s\n", + RAT_PRINT(forth), + RAT_PRINT(rat_get_prod(third, forth)), + RAT_PRINT(rat_get_prod(forth, rat_get_prod(third, forth))) + ); + printf("%s, prod is %s, 3x %s\n", + RAT_PRINT(third), + RAT_PRINT(rat_get_prod2(third, third)), + RAT_PRINT(rat_get_prod2(third, rat_get_prod2(third, third))) + ); + printf("%s, prod is %s, 3x %s\n", + RAT_PRINT(forth), + RAT_PRINT(rat_get_prod2(forth, forth)), + RAT_PRINT(rat_get_prod2(forth, rat_get_prod2(forth, forth))) + ); + printf("%s, prod is %s, 3x %s\n", + RAT_PRINT(forth), + RAT_PRINT(rat_get_prod2(third, forth)), + RAT_PRINT(rat_get_prod2(forth, rat_get_prod2(third, forth))) + ); + printf("%s, sum is %s, 3x %s\n", + RAT_PRINT(third), + RAT_PRINT(rat_get_sum(third, third)), + RAT_PRINT(rat_get_sum(third, rat_get_sum(third, third))) + ); + printf("%s, sum is %s, 3x %s\n", + RAT_PRINT(forth), + RAT_PRINT(rat_get_sum(forth, forth)), + RAT_PRINT(rat_get_sum(forth, rat_get_sum(forth, forth))) + ); + printf("%s, sum is %s, 3x %s\n", + RAT_PRINT(forth), + RAT_PRINT(rat_get_sum(third, forth)), + RAT_PRINT(rat_get_sum(forth, rat_get_sum(third, forth))) + ); + printf("%s, sum is %s, 3x %s\n", + RAT_PRINT(third), + RAT_PRINT(rat_get_sum2(third, third)), + RAT_PRINT(rat_get_sum2(third, rat_get_sum2(third, third))) + ); + printf("%s, sum is %s, 3x %s\n", + RAT_PRINT(forth), + RAT_PRINT(rat_get_sum2(forth, forth)), + RAT_PRINT(rat_get_sum2(forth, rat_get_sum2(forth, forth))) + ); + printf("%s, sum is %s, 3x %s\n", + RAT_PRINT(forth), + RAT_PRINT(rat_get_sum2(third, forth)), + RAT_PRINT(rat_get_sum2(forth, rat_get_sum2(third, forth))) + ); +} diff --git a/rationals.h b/rationals.h new file mode 100644 index 0000000..d69b515 --- /dev/null +++ b/rationals.h @@ -0,0 +1,41 @@ +#ifndef RATIONALS_H +# define RATIONALS_H 1 +# include +# include "euclid.h" + +typedef struct rat rat; + +struct rat { + bool sign; + size_t num; + size_t denom; +}; + +/* Functions that return a value of type rat. */ +rat rat_get(signed sign, size_t num, size_t denom) [[__unsequenced__]]; +rat rat_get_normal(rat x) [[__unsequenced__]]; +rat rat_get_extended(rat x, size_t f) [[__unsequenced__]]; +rat rat_get_prod(rat x, rat y) [[__unsequenced__]]; +rat rat_get_sum(rat x, rat y) [[__unsequenced__]]; + + +/* Functions that operate on pointers to rat. */ +void rat_destroy(rat* rp) [[__unsequenced__]]; +rat* rat_init(rat* rp, + signed sign, + size_t num, size_t denom) [[__unsequenced__]]; +rat* rat_normalize(rat* rp) [[__unsequenced__]]; +rat* rat_extend(rat* rp, size_t f) [[__unsequenced__]]; +rat* rat_sumup(rat* rp, rat y) [[__unsequenced__]]; +rat* rat_rma(rat* rp, rat x, rat y) [[__unsequenced__]]; + +/* Functions that are implemented as exercises. */ +/** @brief Print @a x into @a tmp and return tmp. **/ +char const* rat_print(size_t len, char tmp[len], rat const* x); +/** @brief Print @a x normalize and print. **/ +char const* rat_normalize_print(size_t len, char tmp[len], + rat const* x); +rat* rat_dotproduct(rat rp[static 1], size_t n, + rat const A[n], rat const B[n]); + +#endif diff --git a/reorder.h b/reorder.h new file mode 100644 index 0000000..5cb546b --- /dev/null +++ b/reorder.h @@ -0,0 +1,245 @@ +#ifndef __REORDER_H__ +#define __REORDER_H__ + +#include +#include + +#define rodr_convert(T, ...) (T){ (__VA_ARGS__), } + +[[__maybe_unused__]] +static inline +void* rodr_memcpy(size_t n, + unsigned char s1[restrict static n], + unsigned char const s2[restrict static n]) + [[__unsequenced__]]; + +[[__maybe_unused__]] +static inline +void* rodr_memccpy(size_t n, + unsigned char s1[restrict static n], + unsigned char const s2[restrict static n], + int c) + [[__unsequenced__]]; + +[[__maybe_unused__]] +static inline +void* rodr_memmove(size_t n, + unsigned char s1[static n], + unsigned char const s2[static n]); + +[[__maybe_unused__]] +static inline +int rodr_memcmp(size_t n, + unsigned char s1[restrict static n], + unsigned char const s2[restrict static n]) + [[__unsequenced__]]; + +[[__maybe_unused__]] +static inline +void* rodr_memchr(size_t n, unsigned char const s[static n], int c) + [[__unsequenced__]]; + +[[__maybe_unused__]] +static inline +void* rodr_memset(size_t n, unsigned char s[static n], int c) + [[__unsequenced__]]; + +[[__maybe_unused__]] +static inline +void rodr_qsort(size_t nmemb, size_t size, + unsigned char base[static nmemb*size], + int (*compar)(const void *, const void *)); + +[[__maybe_unused__]] +static inline +void* rodr_bsearch(size_t nmemb, size_t size, + unsigned char const key[static 1], + unsigned char const base[static nmemb*size], + int (*compar)(const void *, const void *)); + +/** + ** @brief Similar to `memcpy` but checks the buffer argument for the + ** size. + ** + ** To be able to do that check, the function has the size argument + ** first and then the buffer, so the buffer can be specified as array + ** parameter that depends on the that size. + ** + ** Here there is an additional complication, namely that the original + ** functions has `void` pointers. We go around this by using + ** `unsigned char` (which then can be the base of an array). Then, in + ** the macro interface we have to convert the arguments to `void` + ** pointers such that this function here can be called without + ** troubles. + **/ +static inline +void* rodr_memcpy(size_t n, + unsigned char s1[restrict static n], + unsigned char const s2[restrict static n]) { + // This captures a possible pre-existing macro for memcpy + return memcpy(s1, s2, n); +} + +static inline +void* rodr_memccpy(size_t n, + unsigned char s1[restrict static n], + unsigned char const s2[restrict static n], + int c) { + // This captures a possible pre-existing macro for memccpy + return memccpy(s1, s2, c, n); +} + +static inline +void* rodr_memmove(size_t n, + unsigned char s1[static n], + unsigned char const s2[static n]) { + // This captures a possible pre-existing macro for memmove + return memmove(s1, s2, n); +} + +static inline +int rodr_memcmp(size_t n, + unsigned char s1[restrict static n], + unsigned char const s2[restrict static n]) { + // This captures a possible pre-existing macro for memcmp + return memcmp(s1, s2, n); +} + + +static inline +void* rodr_memchr(size_t n, unsigned char const s[static n], int c) { + // This avoids a possible pre-existing macro for memchr + return (memchr)(s, c, n); +} + +static inline +void* rodr_memset(size_t n, unsigned char s[static n], int c) { + // This captures a possible pre-existing macro for memset + return memset(s, c, n); +} + + +static inline +void rodr_qsort(size_t nmemb, size_t size, + unsigned char base[static nmemb*size], + int (*compar)(const void *, const void *)) { + // This captures a possible pre-existing macro for qsort + qsort(base, nmemb, size, compar); +} + +static inline +void* rodr_bsearch(size_t nmemb, size_t size, + unsigned char const key[static 1], + unsigned char const base[static nmemb*size], + int (*compar)(const void *, const void *)) { + // This avoids a possible pre-existing macro for bsearch + return (bsearch)(key, base, nmemb, size, compar); +} + +#undef memcpy +/** + ** @brief A macro replacement for `memcpy` that also propagates the + ** requirements for the size of the buffer arguments. + ** + ** There is a complication, namely that the original functions has + ** `void` pointers. We go around this by using `unsigned char` (which + ** then can be the base of an array) in the `rodr_memcpy` + ** interface. So here we have to convert the arguments to `void` + ** pointers such that this function can be called without troubles. + ** + ** We don't want to use simple casts to `void*`, for example, because + ** that would cast away all other type checks that we still want to + ** maintain. Therefore we use compound literals where the arguments + ** are initializers. By that, only an argument type that has an + ** implicit conversion to the corresponding `void` pointer type can + ** be used, all others will be diagnosed. + **/ +#define memcpy(S1, S2, N) \ +rodr_memcpy((N), \ + /* Make sure no qualification gets lost */ \ + rodr_convert(void*, S1), \ + /* Make sure no volatile gets lost */ \ + rodr_convert(void const*, S2)) + +#undef memccpy +#define memccpy(S1, S2, C, N) \ + rodr_memccpy((N), \ + /* Make sure no qualification gets lost */ \ + rodr_convert(void*, S1), \ + /* Make sure no volatile gets lost */ \ + rodr_convert(void const*, S2), \ + (C)) + +#define memmove(S1, S2, N) \ + rodr_memmove((N), \ + /* Make sure no qualification gets lost */ \ + rodr_convert(void*, S1), \ + /* Make sure no volatile gets lost */ \ + rodr_convert(void const*, S2)) + +#define memcmp(S1, S2, N) \ + rodr_memcmp((N), \ + /* Make sure no qualification gets lost */ \ + rodr_convert(void*, S1), \ + /* Make sure no volatile gets lost */ \ + rodr_convert(void const*, S2)) + + +#undef memchr +#define memchr(S, C, N) \ + ((typeof(1 ? (S) : (void*)1)) \ + rodr_memchr((N), \ + /* Make sure no volatile gets lost */ \ + rodr_convert(void const*, S), \ + (C))) + +#undef memset +#define memset(S, C, N) \ + rodr_memset((N), \ + /* Make sure no qualification gets lost */ \ + rodr_convert(void*, S), \ + (C)) + +#undef qsort +#define qsort(BASE, NMEMB, SIZE, COMPAR) \ + rodr_qsort((NMEMB), \ + (SIZE), \ + /* Make sure no qualification gets lost */ \ + rodr_convert(void*, BASE), \ + (COMPAR)) + +#undef bsearch +#define bsearch(KEY, BASE, NMEMB, SIZE, COMPAR) \ + ((typeof(1 ? (BASE) : (void*)1)) \ + rodr_bsearch((NMEMB), \ + (SIZE), \ + /* Make sure no volatile gets lost */ \ + rodr_convert(void const*, KEY), \ + /* Make sure no volatile gets lost */ \ + rodr_convert(void const*, BASE), \ + (COMPAR))) + +#if __STDC_VERSION__ >= 202311L + +[[__maybe_unused__]] +static inline +void* rodr_memset_explicit(size_t n, unsigned char s[static n], int c) + [[__unsequenced__]]; + +static inline +void* rodr_memset_explicit(size_t n, unsigned char s[static n], int c) { + // This captures a possible pre-existing macro for memset_explicit + return memset_explicit(s, c, n); +} + +#undef memset_explicit +#define memset_explicit(S, C, N) \ + rodr_memset_explicit((N), \ + /* Make sure no qualification gets lost */ \ + rodr_convert(void*, S), \ + (C)) + +#endif + + +#endif diff --git a/sequence_point.c b/sequence_point.c new file mode 100644 index 0000000..6048cb6 --- /dev/null +++ b/sequence_point.c @@ -0,0 +1,10 @@ +#include + +unsigned add(unsigned* x, unsigned const* y) [[unsequenced]] { + return *x += *y; +} +int main(void) { + unsigned a = 3; + unsigned b = 5; + printf("a = %u, b = %u\n", add(&a, &b), add(&b, &a)); +} diff --git a/shadow.c b/shadow.c new file mode 100644 index 0000000..f996fc8 --- /dev/null +++ b/shadow.c @@ -0,0 +1,20 @@ +#include "c23-fallback.h" + +void squareIt(double* p) [[__unsequenced__]] { + *p *= *p; +} +int main(void) { + double x = 35.0; /*@\label{line:decl-x}*/ + double* xp = &x; /*@\label{line:decl-xp}*/ + { + squareIt(&x); /* Refers to double x *//*@\label{line:use-x-1}*/ + /*@…*/ + [[maybe_unused]] int x = 0; /* Shadow double x *//*@\label{line:decl-x-2}*/ + /*@…*/ + squareIt(xp); /* Valid use of double x *//*@\label{line:use-xp}*/ + /*@…*/ + } /*@\label{line:end-x-2}*/ + /*@…*/ + squareIt(&x); /* Refers to double x *//*@\label{line:use-x-2}*/ + /*@…*/ +} diff --git a/sighandler.c b/sighandler.c new file mode 100644 index 0000000..bb8fd11 --- /dev/null +++ b/sighandler.c @@ -0,0 +1,99 @@ +#include +#include +#include +#include +#include "sighandler.h" + +#define SH_PAIR(X, D) [X] = { .name = #X, .desc = "" D "", } + +sh_pair const sh_pairs[] = { + /* Execution errors */ + SH_PAIR(SIGFPE, "erroneous arithmetic operation"), + SH_PAIR(SIGILL, "invalid instruction"), + SH_PAIR(SIGSEGV, "invalid access to storage"), +#ifdef SIGBUS + SH_PAIR(SIGBUS, "bad address"), +#endif + /* Job control */ + SH_PAIR(SIGABRT, "abnormal termination"), + SH_PAIR(SIGINT, "interactive attention signal"), + SH_PAIR(SIGTERM, "termination request"), +#ifdef SIGKILL + SH_PAIR(SIGKILL, "kill signal"), +#endif +#ifdef SIGQUIT + SH_PAIR(SIGQUIT, "keyboard quit"), +#endif +#ifdef SIGSTOP + SH_PAIR(SIGSTOP, "stop process"), +#endif +#ifdef SIGCONT + SH_PAIR(SIGCONT, "continue if stopped"), +#endif +#ifdef SIGINFO + SH_PAIR(SIGINFO, "status information request"), +#endif +}; + +size_t const sh_known = (sizeof sh_pairs/sizeof sh_pairs[0]); + +#if ATOMIC_LONG_LOCK_FREE > 1 +_Atomic(unsigned long) sh_counts[sizeof sh_pairs/sizeof sh_pairs[0]]; +# define SH_COU " (%lu times)," +#else +# define SH_COU "%0.0lu," +#endif + +void sh_count(int); +unsigned long sh_counted(int); + +#define SH_HEAD "\r%s:%zu: " +#define SH_DOC "\t%s,\t%s" + + +void sh_print(FILE* io, int sig, + char const* filename, size_t line, + char const* string) { + char const* doc = + (sig < sh_known && sh_pairs[sig].name) + ? sh_pairs[sig].desc + : "unknown signal number"; + if (errno) { + char const* err = strerror(errno); + errno = 0; + if (!sig) + fprintf(io, SH_HEAD "\t%s:\t%s\n", filename, line, + string, err); + else if (sig < sh_known && sh_pairs[sig].name) + fprintf(io, SH_HEAD "%s" SH_COU SH_DOC ":\t%s\n", filename, line, + sh_pairs[sig].name, sh_counted(sig), doc, string, err); + else + fprintf(io, SH_HEAD "#%d" SH_COU SH_DOC ":\t%s\n", filename, line, + sig, sh_counted(sig), doc, string, err); + } else { + if (!sig) + fprintf(io, SH_HEAD "\t%s\n", filename, line, + string); + else if (sig < sh_known && sh_pairs[sig].name) + fprintf(io, SH_HEAD "%s" SH_COU SH_DOC "\n", filename, line, + sh_pairs[sig].name, sh_counted(sig), doc, string); + else + fprintf(io, SH_HEAD "#%d" SH_COU SH_DOC "\n", filename, line, + sig, sh_counted(sig), doc, string); + } +} + +sh_handler* sh_enable(int sig, sh_handler* hnd) { + sh_handler* ret = signal(sig, hnd); + if (ret == SIG_ERR) { + SH_PRINT(stderr, sig, "failed"); + errno = 0; + } else if (ret == SIG_IGN) { + SH_PRINT(stderr, sig, "previously ignored"); + } else if (ret && ret != SIG_DFL) { + SH_PRINT(stderr, sig, "previously set otherwise"); + } else { + SH_PRINT(stderr, sig, "ok"); + } + return ret; +} diff --git a/sighandler.h b/sighandler.h new file mode 100644 index 0000000..4b7768d --- /dev/null +++ b/sighandler.h @@ -0,0 +1,101 @@ +#ifndef SIGHANDLER_H +#define SIGHANDLER_H 1 + +#include "c23-fallback.h" +#include +#ifndef __STDC_NO_ATOMIC__ +# include +#endif + +/** + ** @brief A pair of strings to hold signal information + **/ +struct sh_pair { + char const* name; + char const* desc; +}; +typedef struct sh_pair sh_pair; + +/** + ** @brief For each possible signal, this holds a pair of strings with + ** signal information. + ** + ** The size of this array can be queried with `sh_known`. + ** + ** @see SH_PRINT to use that information. + ** + ** Conditionally, this also adds some commonly used extensions. + **/ +extern sh_pair const sh_pairs[]; + +extern size_t const sh_known; + +#if ATOMIC_LONG_LOCK_FREE > 1 +/** + ** @brief Keep track of the number of calls into a + ** signal handler for each possible signal. + ** + ** Don't use this array directly. + ** + ** @see sh_count to update this information. + ** @see SH_PRINT to use that information. + **/ +extern _Atomic(unsigned long) sh_counts[]; + +/** + ** @brief Use this in your signal handler to keep track of the + ** number of calls to the signal @a sig. + ** + ** @see sh_counted to use that information. + **/ +inline +void sh_count(int sig) { + if (sig < sh_known) ++sh_counts[sig]; +} + +inline +unsigned long sh_counted(int sig){ + return (sig < sh_known) ? sh_counts[sig] : 0; +} + +#else +inline +void sh_count(int sig) { + // empty +} + +inline +unsigned long sh_counted(int sig){ + return 0; +} +#endif + +/** + ** @brief Prototype of signal handlers + **/ +typedef void sh_handler(int); + +/** + ** @brief Pretty-prints signal information + ** + ** @see PRINT_SIG for a convenient user interface + **/ +void sh_print(FILE* io, int sig, + char const* filename, size_t line, + char const* string); + +/** + ** @brief Pretty-prints signal information + ** + ** This prints signal information @a STRING for signal @a SIG to + ** stream @a IO. + **/ +#define SH_PRINT(IO, SIG, STRING) sh_print(IO, SIG, __FILE__, __LINE__, STRING) + +/** + ** @brief Enables a signal handler and catches the errors + **/ +sh_handler* sh_enable(int sig, sh_handler* hnd); + + +#endif diff --git a/sighandler_test.c b/sighandler_test.c new file mode 100644 index 0000000..431ff9a --- /dev/null +++ b/sighandler_test.c @@ -0,0 +1,70 @@ +#include +#include +#include +#include +#include "sighandler.h" + +#ifndef SIGNALS +#define SIGNALS sh_known +#endif + +/** + ** @brief A simple signal handler mechanism. + ** + ** This just raises a flag when a signal is caught. + ** + ** All real processing of the signal has to be done + ** in an event loop. + **/ +static sig_atomic_t volatile received; + +static void signal_handler(int sig) { + received = sig; + switch (sig) { +#ifdef SIGQUIT + case SIGQUIT: exit(EXIT_FAILURE); +#endif + case SIGTERM: quick_exit(EXIT_FAILURE); + case SIGABRT: _Exit(EXIT_FAILURE); + default: + /* reset the handling to its default */ + signal(sig, SIG_DFL); + return; + case SIGINT: return; + } +} + +static +void signal_atexit(void) { + SH_PRINT(stderr, received, "atexit handler"); +} + +static +void signal_at_quick_exit(void) { + SH_PRINT(stderr, received, "at_quick_exit handler"); +} + +int main(int argc, [[maybe_unused]] char* argv[argc+1]) { + atexit(signal_atexit); + at_quick_exit(signal_at_quick_exit); + for (unsigned i = 1; i < SIGNALS; ++i) + sh_enable(i, signal_handler); + SH_PRINT(stderr, SIGNALS-1, "highest known signal number"); + + size_t const Gi = (1ull<<30); + for(size_t step = 0; step < 10*Gi; ++step) { + if (!(step%Gi)) printf("step %zu\n", step); + switch (received) { + case SIGINT: + fprintf(stderr, "\ryou called?\n"); + received = 0; + case 0: + continue; + } + SH_PRINT(stderr, received, "is somebody trying to kill us?"); + raise(received); + break; + } + SH_PRINT(stderr, received, "we survived"); + return EXIT_SUCCESS; +} diff --git a/stats.c b/stats.c new file mode 100644 index 0000000..82afd7e --- /dev/null +++ b/stats.c @@ -0,0 +1,17 @@ +#include "stats.h" + +#define EXTERNAL(NAME) extern typeof(NAME) NAME + +EXTERNAL(stats_collect); +EXTERNAL(stats_collect0); +EXTERNAL(stats_collect1); +EXTERNAL(stats_collect2); +EXTERNAL(stats_collect3); +EXTERNAL(stats_samples); +EXTERNAL(stats_mean); +EXTERNAL(stats_sdev); +EXTERNAL(stats_rsdev); +EXTERNAL(stats_skew); +EXTERNAL(stats_var_unbiased); +EXTERNAL(stats_sdev_unbiased); +EXTERNAL(stats_rsdev_unbiased); diff --git a/stats.h b/stats.h new file mode 100644 index 0000000..c70b4ba --- /dev/null +++ b/stats.h @@ -0,0 +1,213 @@ +#include "c23-fallback.h" +#include +#include + +/** + ** @file + ** @brief Collect some simple statistics online as we go. + ** + ** This uses a generalization of Welford's trick to compute running + ** mean and variance. See + ** + ** Philippe Pébay. Formulas for robust, one-pass parallel computation + ** of covariances and arbitrary-order statistical moments. Technical + ** Report SAND2008-6212, SANDIA, 2008. URL + ** http://prod.sandia.gov/techlib/access-control.cgi/2008/086212.pdf. + **/ + +/** + ** @brief A simple data structure to collect the 0th to 3rd moment of + ** a statistic. + ** + ** @warning Since this also uses a @c double for the number of + ** samples, the validity of all this is restricted to about + ** @f$2^{50} \approx 10^{15}@f$ samples. + **/ +struct stats { + double moment[4]; +}; +typedef struct stats stats; + + +/** + ** @brief Return the number of samples that had been entered into the + ** statistic @a c. + **/ +inline +double stats_samples(stats c[static 1]) [[__unsequenced__]] { + return c->moment[0]; +} + +/** + ** @brief Return the mean value of the samples that had been entered + ** into the statistic @a c. + **/ +inline +double stats_mean(stats c[static 1]) [[__unsequenced__]] { + return c->moment[1]; +} + +/** + ** @brief Return the variance of the samples that had been entered + ** into the statistic @a c. + **/ +inline +double stats_var(stats c[static 1]) [[__reproducible__]] { + return c->moment[2]/stats_samples(c); +} + +// Clang has this notorious design flaw in where they +// declare interfaces as being static. In some cases this results in +// an avalanche of confusing messages of static functions that would +// be used in an inline function. +#if __clang_major__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wstatic-in-inline" +#endif + +/** + ** @brief Return the standard deviation of the samples that had been + ** entered into the statistic @a c. + **/ +inline +double stats_sdev(stats c[static 1]) [[__reproducible__]] { + return sqrt(stats_var(c)); +} + +/** + ** @brief Return the relative standard deviation of the samples that + ** had been entered into the statistic @a c. + **/ +inline +double stats_rsdev(stats c[static 1]) [[__reproducible__]] { + return sqrt(stats_var(c))/stats_mean(c); +} + +/** + ** @brief Return the normalized skew of the samples that had been + ** entered into the statistic @a c. + **/ +inline +double stats_skew(stats c[static 1]) [[__reproducible__]] { + double var = stats_var(c); + return (c->moment[3]/pow(var, 1.5))/stats_samples(c); +} + +/** + ** @brief Return the unbiased variance of the samples that had been + ** entered into the statistic @a c. + ** + ** Use Bessel's correction to have an estimation of the unbiased + ** variance of the overall population. + **/ +inline +double stats_var_unbiased(stats c[static 1]) [[__reproducible__]] { + return c->moment[2]/(stats_samples(c)-1); +} + +/** + ** @brief Return the unbiased standard deviation of the samples that + ** had been entered into the statistic @a c. + ** + ** Use Bessel's correction to have an less biased estimation of the + ** variance of the overall population. + **/ +inline +double stats_sdev_unbiased(stats c[static 1]) [[__reproducible__]] { + return sqrt(stats_var_unbiased(c)); +} + +#if __clang_major__ +#pragma clang diagnostic pop +#endif + + + +/** + ** @brief Return the unbiased relative standard deviation of the + ** samples that had been entered into the statistic @a c. + **/ +inline +double stats_rsdev_unbiased(stats c[static 1]) [[__reproducible__]] { + return stats_rsdev(c)*(1+1/(4*stats_samples(c))); +} + +/** + ** @brief Add value @a val to the statistic @a c. + ** + ** @c moments is the number of statistic moments that is collected, + ** it has to be between `0` and `3`, including. + **/ +inline +void stats_collect(stats c[static 1], + double val, unsigned moments) + [[__reproducible__]] { + double n = stats_samples(c); + double n0 = n-1; + double n1 = n+1; + double delta0 = 1; + double delta = val - stats_mean(c); + double delta1 = delta/n1; + double delta2 = delta1*delta*n; + switch (moments) { + default: + c->moment[3] += (delta2*n0 - 3*c->moment[2])*delta1; + [[__fallthrough__]]; + case 2: + c->moment[2] += delta2; + [[__fallthrough__]]; + case 1: + c->moment[1] += delta1; + [[__fallthrough__]]; + case 0: + c->moment[0] += delta0; + } +} + +/** + ** @brief Add value @a val to the statistic @a c. + ** + ** Only the number of samples is collected. + **/ +inline +void stats_collect0(stats c[static 1], + double val) + [[__reproducible__]] { + stats_collect(c, val, 0); +} + +/** + ** @brief Add value @a val to the statistic @a c. + ** + ** Only the number of samples and mean is collected. + **/ +inline +void stats_collect1(stats c[static 1], + double val) + [[__reproducible__]] { + stats_collect(c, val, 1); +} + +/** + ** @brief Add value @a val to the statistic @a c. + ** + ** The number of samples, mean and standard deviation is collected. + **/ +inline +void stats_collect2(stats c[static 1], + double val) + [[__reproducible__]] { + stats_collect(c, val, 2); +} + +/** + ** @brief Add value @a val to the statistic @a c. + ** + ** All statistics up to the skew are collected. + **/ +inline +void stats_collect3(stats c[static 1], + double val) + [[__reproducible__]] { + stats_collect(c, val, 3); +} diff --git a/strcpy.c b/strcpy.c new file mode 100644 index 0000000..a34dd71 --- /dev/null +++ b/strcpy.c @@ -0,0 +1,17 @@ +#include +#include +int main(int argc, char* argv[argc+1]) { + size_t const len = strlen(argv[0]); // Computes the length + // Initialized VLA, /*@\C{23}*/ + // terminates array with 0 character + char name[len+1] = { }; + // Copies the program name + memcpy(name, argv[0], len); + if (!strcmp(name, argv[0])) { + printf("program name \"%s\" successfully copied\n", + name); + } else { + printf("copying %s leads to different string %s\n", + argv[0], name); + } +} diff --git a/strtoul.c b/strtoul.c new file mode 100644 index 0000000..9306822 --- /dev/null +++ b/strtoul.c @@ -0,0 +1,149 @@ +#include "c23-fallback.h" +#include +//#include +#include +//#include +//#include +#include +/* Supposes that lowercase characters are contiguous. */ +static_assert('z'-'a' == 25, + "alphabetic characters not contiguous"); +#include +/* Converts an alphanumeric digit to an unsigned */ +/* '0' ... '9' => 0 .. 9u */ +/* 'A' ... 'Z' => 10 .. 35u */ +/* 'a' ... 'z' => 10 .. 35u */ +/* Other values => Greater */ +unsigned hexatridecimal(int a) { + if (isdigit(a)) { + /* This is guaranteed to work: decimal digits + are consecutive, and isdigit is not + locale dependent. */ + return a - '0'; + } else { + /* Leaves a unchanged if it is not lowercase */ + a = toupper(a); + /* Returns value >= 36 if not Latin uppercase */ + return (isupper(a)) ? 10 + (a - 'A') : -1; + } +} + +unsigned long Strtoul_inner(char const s[static 1], + size_t i, + unsigned base) { + unsigned long ret = 0; + while (s[i]) { + unsigned c = hexatridecimal(s[i]); + if (c >= base) break; + /* Maximal representable value for 64 bit is + 3w5e11264sgsf in base 36 */ + if (ULONG_MAX/base < ret) { + ret = ULONG_MAX; + errno = ERANGE; + break; + } + ret *= base; + ret += c; + ++i; + } + return ret; +} + +static unsigned find_prefix(char const s[static 1], size_t i, char const t[static 1]) { + size_t ret = 0; + while (s[i+ret] && (tolower(s[i+ret]) == tolower(t[ret]))) { + ++ret; + } + return ret; +} + +unsigned long Strtoul(char const s[static 1], unsigned base) { + if (base > 36u) { /* Tests if base */ + errno = EINVAL; /* Extends the specification */ + return ULONG_MAX; + } + size_t i = strspn(s, " \f\n\r\t\v"); /* Skips spaces */ + bool switchsign = false; /* Looks for a sign */ + switch (s[i]) { + case '-' : + switchsign = true; + [[fallthrough]]; + case '+' : + ++i; + } + /* Test if there is anything left in the string. */ + if (!s[i]) return 0; + if (!base || base == 16 || base == 2) { /* use prefix */ + size_t adj = find_prefix(s, i, "0x"); + switch (adj) { + case 2: // There is a 0x or a 0X prefix + if (!base || base == 16) base = 16; + // If we are looking for another base, the x is the end + else return 0; + break; + case 1: // There is a 0 prefix + adj = find_prefix(s, i, "0b"); + switch (adj) { + case 1: // There is only a 0 prefix + if (!base) base = 8; + break; + default: // There is a 0b or a 0B prefix + if (!base || base == 2) base = 2; + // If we are looking for another base, the b is the end + else return 0; + break; + } + break; + default: + if (!base) base = 10; + break; + } + i += adj; + } + /* Test again, maybe the prefix 0 was the only digit. */ + if (!s[i]) return 0; + /* Now, starts the real conversion */ + unsigned long ret = Strtoul_inner(s, i, base); + return (switchsign) ? -ret : ret; +} + +char* char36(unsigned long n, unsigned base, char buf[static 256]) { + if (base > 36) { + strcpy(buf, "EINVAL"); + } else { + unsigned i = 256; + while (n) { + --i; + buf[i] = n % base; + n /= base; + if (buf[i] < 10) { + buf[i] += '0'; + } else { + buf[i] += 'a' - 10; + } + } + memmove(buf, &buf[i], 256-i); + } + return buf; +} + +int main(int argc, char* argv[argc+1]) { + static unsigned const code[] = { 0, 8, 10, 16, 36, 37, }; + for (unsigned j = 0; j < sizeof code/ sizeof code[0]; ++j) { + printf("%u:", code[j]); + for (int i = 1; i < argc; ++i) { + unsigned long res = Strtoul(argv[i], code[j]); + if (res == ULONG_MAX && errno) { + puts(""); + fprintf(stderr, "invalid conversion with base %u and \"%s\"\n", code[j], argv[i]); + perror("error"); + errno = 0; + } else { + printf("\t%lu", res); + } + } + puts(""); + if (code[j]) printf("%u:\tULONG_MAX %s\n", code[j], char36(ULONG_MAX, code[j], (char[256]){ })); + } + return EXIT_SUCCESS; +} diff --git a/swap.c b/swap.c new file mode 100644 index 0000000..1f006d7 --- /dev/null +++ b/swap.c @@ -0,0 +1,144 @@ +#include "c23-fallback.h" +#include + +#define static_assert_compatible(A, B, REASON) \ + static_assert(_Generic((typeof(A)*)nullptr, \ + typeof(B)*: true, \ + default: false), \ + "expected compatible types: " REASON ", have " #A " and " #B "") + +#ifndef __GNUC__ +#define SWAP(X, Y) \ + do { \ + /* These two variables have the role of function \ + parameters. They ensure to evaluate the \ + expression X and Y once, since these could be \ + complicated lvalue expressions with evaluated \ + subexpressions that have side effects. */ \ + auto const swap_p1 = &(X); \ + auto const swap_p2 = &(Y); \ + static_assert_compatible(*swap_p1, *swap_p2, \ + "to exchange values, '" \ + #X "' and '" #Y \ + "' must have compatible types"); \ + auto swap_tmp = *swap_p1; \ + *swap_p1 = *swap_p2; \ + *swap_p2 = swap_tmp; \ + } while (false) +#else +#define SWAP(X, Y) \ + /* This starts the compound expression construct. */ \ + ({ \ + /* These two variables have the role of function \ + parameters. They ensure to evaluate the \ + expression X and Y once, since these could be \ + complicated lvalue expressions with evaluated \ + subexpressions that have side effects. */ \ + auto const swap_p1 = &(X); \ + auto const swap_p2 = &(Y); \ + static_assert_compatible(*swap_p1, *swap_p2, \ + "to exchange values, '" \ + #X "' and '" #Y \ + "' must have compatible types"); \ + auto swap_tmp = *swap_p1; \ + *swap_p1 = *swap_p2; \ + *swap_p2 = swap_tmp; \ + /* ensure that the type of the expression is void */ \ + (void)0; \ + }) +#endif + + +#define isnegative(X) ((X) < 0) + +#define MAX_EQSIGN(X, Y) \ + /* This starts the compound expression construct. */ \ + ({ \ + /* These two captures play the role of function \ + parameters and read X and Y ... */ \ + auto const max_x = (X); \ + auto const max_y = (Y); \ + /* now the body starts */ \ + /* types need to have the same signedness */ \ + (max_x < max_y) ? max_y : max_x; \ + }) + +#define MAX(X, Y) \ + /* This starts the compound expression construct. */ \ + ({ \ + auto const max_x = (X); \ + auto const max_y = (Y); \ + /* now the body starts */ \ + ((isnegative(max_x) && !isnegative(max_y)) \ + ? max_y \ + : ((isnegative(max_y) && !isnegative(max_x)) \ + ? max_x \ + : /* both have the same sign */ \ + ((max_x < max_y) ? max_y : max_x))); \ + }) + + + +#define mincharacteristic(X, Y) ((issigned(X)<<2)|(issigned(Y)<<1)|1) + +#define minunsigned(X, Y) (_Generic(tozero(X)+tozero(Y), typeof(X): tozero(Y), default: tozero(X))) + +#define minreturn(X, Y) \ + _Generic( \ + (char(*)[mincharacteristic(X, Y)]){ 0 }, \ + /* both signed, arithmetic conversion */ \ + char(*)[4|2|1]: tozero(X)+tozero(Y), \ + /* only one signed, use it */ \ + char(*)[0|2|1]: tozero(Y), \ + char(*)[4|0|1]: tozero(X), \ + /* both unsigned, use narrower */ \ + char(*)[0|0|1]: minunsigned(X, Y) \ +) + +#define MIN(X, Y) \ + /* This starts the compound expression construct. */ \ + ({ \ + /* These two captures play the role of function \ + parameters and read X and Y ... */ \ + auto const min_x = (X); \ + auto const min_y = (Y); \ + /* now the body starts */ \ + typedef typeof(minreturn(min_x, min_y)) min_type; \ + ((isnegative(min_x) && !issigned(min_y)) \ + ? (min_type)min_x \ + : ((isnegative(min_y) && !issigned(min_x)) \ + ? (min_type)min_y \ + : ( \ + /* both have the same signedness or are both positive */ \ + (min_x < min_y) \ + ? (min_type)min_x \ + : (min_type)min_y))); \ + }) + + +int main (int argc, char* argv[static argc + 1]) { + size_t i = argc; + size_t j = argc+1; + printf("%s, %s\n", argv[1], argv[2]); + // Works even with side effects, but don't do that + SWAP(argv[1+(i++)%2], argv[1+(j++)%2]); // don't do that + printf("%s, %s\n", argv[1], argv[2]); + // Works even with side effects, but don't do that + SWAP(argv[1+(i++)%2], argv[1+(j++)%2]); // don't do that + printf("%s, %s\n", argv[1], argv[2]); + printf("%d\t%d\t→\t%d\n", -1, -2, MIN(-1, -2)); + printf("%d\t%ldl\t→\t%ldl\n", -2, -1L, MIN(-2, -1L)); + printf("%d\t%d\t→\t%d\n", -1, 2, MIN(-1, 2)); + printf("%d\t%ldl\t→\t%ldl\n", -1, 2L, MIN(-1, 2L)); + printf("%d\t%uu\t→\t%d\n", -1, 2u, MIN(-1, 2u)); + printf("%d\t%luul\t→\t%d\n", -1, 2UL, MIN(-1, 2UL)); + printf("%uu\t%uu\t→\t%uu\n", -1u, -2u, MIN(-1u, -2u)); + printf("%uu\t%luul\t→\t%uu\n", -1u, -2UL, MIN(-1u, -2UL)); + static_assert_compatible(1, 0, "both int"); + //static_assert_compatible(1, 0L, "both int"); + //static_assert_compatible(1L, 0, "both int"); + static_assert_compatible(1L, 0L, "both int"); + enum A : unsigned long { A = -1UL, }; + static_assert_compatible(A, 0UL, "both int"); + static_assert_compatible(int[1], int[true], "both int"); +} diff --git a/tendots.c b/tendots.c new file mode 100644 index 0000000..0733747 --- /dev/null +++ b/tendots.c @@ -0,0 +1,25 @@ +#include + +/* delay execution with some crude code, + should use thrd_sleep, once we have that */ +void delay(double secs) { + double const magic = 4E8; // works just on my machine + unsigned long long const nano = secs * magic; + for (unsigned long volatile count = 0; + count < nano; + ++count) { + /* nothing here */ + } +} + +int main(int argc, [[maybe_unused]] char* argv[argc+1]) { + fputs("waiting 10 seconds for you to stop me", stdout); + if (argc < 3) fflush(stdout); + for (unsigned i = 0; i < 10; ++i) { + fputc('.', stdout); + if (argc < 2) fflush(stdout); + delay(1.0); + } + fputs("\n", stdout); + fputs("You did ignore me, so bye bye\n", stdout); +} diff --git a/termin.c b/termin.c new file mode 100644 index 0000000..5de89da --- /dev/null +++ b/termin.c @@ -0,0 +1,97 @@ +#include "termin.h" +#include "c23-fallback.h" +#include +#include +#include +#ifndef __STDC_NO_THREADS__ +# include +#endif +#include +#include + +/* Store the flags as they were set before termin_unbuffered. */ +static tcflag_t termin_echo; + +/* The flags that we manipulate. */ +#define TERMIN_FLAGS (ECHO | ECHONL | ICANON) + +void termin_reset(void) { + struct termios term; + tcgetattr(0, &term); + term.c_lflag |= termin_echo; + tcsetattr(0, TCSAFLUSH, &term); +} + +void termin_unbuffered(void) { + // have stdin unbuffered on stdio level + setvbuf(stdin, 0, _IONBF, 0); + // store the terminal settings with respect to the flags + struct termios term; + tcgetattr(0, &term); + termin_echo = term.c_lflag & TERMIN_FLAGS; + // install exit handlers + atexit(termin_reset); + at_quick_exit(termin_reset); + // change the terminal settings to raw mode + term.c_lflag &= ~TERMIN_FLAGS; + tcsetattr(0, TCSAFLUSH, &term); +} + +static unsigned minc = 0; +static unsigned maxc = UCHAR_MAX; + +static +void term_init(void) { + while (!termin_trans[minc]) ++minc; + while (!termin_trans[maxc]) --maxc; +} + +#ifndef __STDC_NO_THREADS__ +static once_flag term_once = ONCE_FLAG_INIT; +#endif + +static +bool termin_read_csi(size_t len, char command[len]) { + for (char *p = command, *stop = command+len-1; p < stop; ++p) { + int c = getchar(); + // catch all: EOF, 0, control characters + if (c < ' ') return false; + p[0] = c; + if ('@' <= c && c <= '~') { + p[1] = 0; + return true; + } + } + return false; +} + +char const* termin_read_esc(size_t len, char command[len]) { + command[1] = getchar(); + switch (command[1]) { + case '[': + return + termin_read_csi(len-2, command+2) + ? command + : 0; + case 'N': + case 'O': + case 'Z': + case '%': + command[2] = getchar(); + command[3] = 0; + return command; + default: + ungetc(command[1], stdin); + return nullptr; + } +} + +char termin_translate(char const* command) { + call_once(&term_once, term_init); + for (unsigned c = minc; c <= maxc; ++c) { + if (termin_trans[c] && !strcmp(command, termin_trans[c])) { + return c; + } + } + return 0; +} diff --git a/termin.h b/termin.h new file mode 100644 index 0000000..c6ae98d --- /dev/null +++ b/termin.h @@ -0,0 +1,54 @@ +#ifndef TERMIN_H +#define TERMIN_H 1 + +#include "c23-fallback.h" +#include "esc.h" + +/** + ** @brief Set a terminal on stdin to raw mode. + ** + ** This uses termios.h underneath, so it probably only works on POSIX + ** systems. + ** + ** The terminal is automatically reset to its original state on @c + ** exit or @c quick_exit by means of ::termin_reset. + **/ +void termin_unbuffered(void); + +/** + ** @brief Reset the terminal to its original state. + **/ +void termin_reset(void); + +/** + ** @brief The translation table for escape sequences. + ** + ** An application that wants to use this module has to provide this + ** symbol. + ** + ** @see ::termin_translate + **/ +extern char const*const termin_trans[UCHAR_MAX+1]; + +/** + ** @brief Translate and escape sequence to a simple character. + ** + ** This supposes that you assign a simple character for each escape + ** sequence that you want to translate. + ** + ** If an exact match for an escape sequence is fount in + ** ::termin_trans, its position in the table is the translation. + ** + ** @see ::termin_trans + **/ +char termin_translate(char const* command); + +/** + ** @brief Read an escape sequence into @a command. + ** + ** This reads from @c stdin and supposes that the escape character is + ** already stored in @c command[0]. + **/ +char const* termin_read_esc(size_t len, char command[len]); + +#endif diff --git a/terran.c b/terran.c new file mode 100644 index 0000000..e604cd9 --- /dev/null +++ b/terran.c @@ -0,0 +1,122 @@ +#include +#include +#include +#include +#include + +/* + terran multiplicative factors between units that are fixed for + Lunar, Day, Hour, Minute, Second +*/ +enum { dl = 28, hd = 24, mh = 60, sm = 60, sh = mh*sm, sd = hd * mh * sm, }; + +/* + There is a long term period of 128 years, that has exactly 31 leap + years. This amounts to 46751 days. +*/ +enum { dy = 365, yp = 128, dp = yp*dy + 31, }; + +/* + Then, periods of four years are grouped together, that have either + 0 or 1 leap year. +*/ +enum { yq = 4, dq0 = yq*dy, dq1 = dq0 + 1, }; + +time_t isotime(char const t[static 1]) { + struct tm date[1] = { + [0] = { .tm_year = 1900,.tm_mon = 1, }, + }; + int retscan = sscanf(t, "%i-%i-%i %i:%i:%i", + &date->tm_year, &date->tm_mon, &date->tm_mday, + &date->tm_hour, &date->tm_min, &date->tm_sec); + if (retscan <= 0) { + if (errno) perror("can't scan"); + return 0; + } + date->tm_year -= 1900; + date->tm_mon -= 1; + return mktime(date); + /* gmtime_r(&ret, date); */ + /* return mktime(date); */ +} + +void time2terran(time_t t) { + /* Provide us with the terran epoch. This is not as easy as it may + appear as a first site, since it is difficult to express a UTC + timestamp in a struct tm: we have convert back and forth with + mktime and gmtime to obtain the correct timestamp at 1969-12-22 0:0:0. + */ + struct tm terranEpoch[1] = { + { + .tm_year = 69, // year starts at 1900 + .tm_mon = 11, // mon starts at 0 + .tm_mday = 23, // mday starts at 1 + }, + }; + time_t epoch = mktime(terranEpoch); + struct tm terranEpoch2[1]; + gmtime_r(&epoch, terranEpoch2); + time_t epoch2 = mktime(terranEpoch2); + terranEpoch[0].tm_sec += difftime(epoch2, epoch); + epoch = mktime(terranEpoch); + + /* Now that we have the appropriate epoch, look how many seconds + have passed since then. + */ + long secs = difftime(t, epoch); + long days = secs/sd; + secs -= days*sd; + + /* + adjust with respect to the 128 year periods + */ + long per = days / dp; + if (days < 0) { + per -= 1; + } + long year = per * yp; + days -= per*dp; + + // the first four years aren't leap years + if (days < dq0) { + year += (days / dy); + days -= (days / dy) * dy; + } else { + // correct for the first 4 years + year += yq; + days -= dq0; + /* + In the remaining period every fourth year starting with year 0 + is a leap year. Compute the amount of such 4 year periods and + adjust + */ + long const qer = days / dq1; + year += qer*yq; + days %= dq1; + // the first year is a leap year + if (days > dy+1) { + // adjust for that leap year + year += 1; + days -= (dy+1); + // compute the remaining days + year += (days / dy); + days %= dy; + } + } + size_t const lunar = days / dl; + days %= dl; + while (secs < 0) secs += sd; + size_t hour = secs / sh; + secs -= hour*sh; + size_t min = secs / sm; + secs %= sm; + printf("%ld.%ld.%ld,%ld:%ld:%ld TC\n", year, lunar, days, hour, min, secs); +} + +int main (int argc, char* argv[argc+1]) { + time_t now = time(0); + time2terran(now); + for (int i = 1; i < argc; ++i) + time2terran(isotime(argv[i])); + return EXIT_SUCCESS; +} diff --git a/test-c8.c b/test-c8.c new file mode 100644 index 0000000..204841b --- /dev/null +++ b/test-c8.c @@ -0,0 +1,54 @@ +#include +#include +#include +#include + +void print_string(char const*s) { + char const*str = s; + size_t n = strlen(str)+1; + char8_t buffer[n+1]; + printf("mb→utf8, source: '%s', sliced: ", str); + mbstate_t mb = { 0 }; + for (char8_t *p = buffer; ; p++) { + size_t res = mbrtoc8(p, str, n, &mb); + if (res > -3) { + printf("!error:%zu!\n", -res); + return; + } else if (res == -3) { + printf(",%hhx", *p); + } else if (res) { + printf("|%hhx", *p); + str += res; + } else { + printf("(%hhx)", *p); + str++; + break; + } + } + printf("; target: %s\n", buffer); + memset(buffer, 0, sizeof buffer); + char *p = (char*)buffer; + printf("utf8→mb, source: '%s', ", s); + for (char const *q = s; ; q++) { + size_t res = c8rtomb(p, *q, &mb); + if (res) { + switch (res) { + default: printf("∞"); res = -1; break; + case 4: printf("<-->"); break; + case 3: printf("<->"); break; + case 2: printf("<>"); break; + case 1: printf("x"); break; + } + if (!*p || (res == -1)) break; + else p += res; + } + } + printf("; target: %s\n", buffer); +} + +int main(int argc, char* argv[static argc+1]) { + if (argc > 1) + setlocale(LC_ALL, ""); + print_string(u8"abcdäα⌫"); + print_string(u8"→😇"); +} diff --git a/test-features.c b/test-features.c new file mode 100644 index 0000000..2224f9b --- /dev/null +++ b/test-features.c @@ -0,0 +1,368 @@ +// test for features + +// Compiler versions know to work with this + +// gcc-?? +// clang-?? + +#include "c23-fallback.h" +#include + +#define test(...) printf("Testing " #__VA_ARGS__ ": %lld\n", (long long)(__VA_ARGS__)) + +int main(int argc, char* argv[argc+1]) { + test(iscomplex(0.0)); + test(isimaginary(0.0)); + test(isfloating(0.0)); + test(iscomplex(I)); + test(isimaginary(I)); + test(isfloating(I)); + test(iscomplex(0U)); + test(isimaginary(0U)); + test(isfloating(0U)); + test(isinteger(0.0)); + test(isinteger(0U)); + test(isinteger((char)'A')); + test(isunsigned((char)'A')); + test(issigned((char)'A')); + test(isunsigned((unsigned char)'A')); + test(issigned((unsigned char)'A')); + test(tominusone((char)'A')); + test(tominusone((unsigned char)'A')); + test(is_const_target((char*)nullptr)); + test(is_const_target((char const*)nullptr)); + test(is_const_target((char volatile*)nullptr)); + test(is_const_target((char const volatile*)nullptr)); + test(is_volatile_target((char*)nullptr)); + test(is_volatile_target((char const*)nullptr)); + test(is_volatile_target((char volatile*)nullptr)); + test(is_volatile_target((char const volatile*)nullptr)); + test(is_const((char){ 0 })); + test(is_const((char const){ 0 })); + test(is_const((char volatile){ 0 })); + test(is_const((char const volatile){ 0 })); + test(is_volatile((char){ 0 })); + test(is_volatile((char const){ 0 })); + test(is_volatile((char volatile){ 0 })); + test(is_volatile((char const volatile){ 0 })); + test(is_null_pointer_constant(0)); + test(is_null_pointer_constant(nullptr)); + test(is_null_pointer_constant((void*)0)); + test(is_null_pointer_constant((void*)1)); + test(is_null_pointer_constant((void*)0LL)); + test(is_null_pointer_constant((void*)(long long){ 0 })); + test(is_zero_ice(0)); + test(is_zero_ice(1)); + test(is_zero_ice((long long){ 0 })); + test(is_zero_ice(0.0)); + test(is_zero_ice(((void)0, 0))); + test(isice(0)); + test(isice(1)); + test(isice((long long){ 0 })); + test(isice(0.0)); + test(isvla(0)); + test(isvla((char[]){ 0 })); + char fla[1][1]; + test(isvla(fla)); + test(isvla(fla[0])); + test(isvla(&fla)); + test(isvla(*(&fla))); + char vla[(int){ 1 }][1]; + test(isvla(vla)); + test(isvla(vla[0])); + test(isvla(&vla)); + test(isvla(*(&vla))); + test(isvla(char[1][1])); + test(isvla(char[(int){ 1 }][1])); + test(isvla(char(*)[1][1])); + test(isvla(char(*)[(int){ 1 }][1])); + test(is_pointer_nvla((char const volatile[]){ 0 })); + test(is_pointer_nvla((char const volatile*){ 0 })); + test(is_pointer_nvla(fla)); + test(is_pointer_nvla(fla[0])); + test(is_pointer_nvla(&fla)); + test(is_pointer_nvla(*(&fla))); + test(is_pointer_nvla(vla)); + test(is_pointer_nvla(vla[0])); + test(is_pointer_nvla(&vla)); + test(is_pointer_nvla(*(&vla))); + test(is_pointer_vla(fla)); + test(is_pointer_vla(fla[0])); + test(is_pointer_vla(&fla)); + test(is_pointer_vla(*(&fla))); + test(is_pointer_vla(vla)); + test(is_pointer_vla(vla[0])); + test(is_pointer_vla(&vla)); + test(is_pointer_vla(*(&vla))); + test(is_pointer((char const volatile[]){ 0 })); + test(is_pointer((char const volatile*){ nullptr })); + test(is_pointer((char*)nullptr)); + test(is_pointer((void*)nullptr)); + test(is_pointer((char const volatile*)nullptr)); + test(is_pointer(fla)); + test(is_pointer(fla[0])); + test(is_pointer(&fla)); + test(is_pointer(*(&fla))); + test(is_pointer(vla)); + test(is_pointer(vla[0])); + test(is_pointer(&vla)); + test(is_pointer(*(&vla))); + test(is_fla((char const volatile[]){ 0 })); + test(is_fla((char const volatile*){ 0 })); + test(is_fla(fla)); + test(is_fla(fla[0])); + test(is_fla(&fla)); + test(is_fla(*(&fla))); + test(is_fla(vla)); + test(is_fla(vla[0])); + test(is_fla(&vla)); + test(is_fla(*(&vla))); + test(is_array((char const volatile[]){ 0 })); + test(is_array((char const volatile*){ 0 })); + test(is_array(fla)); + test(is_array(fla[0])); + test(is_array(&fla)); + test(is_array(*(&fla))); + test(is_array(vla)); + test(is_array(vla[0])); + test(is_array(&vla)); + test(is_array(*(&vla))); + /* test(is_array(char[1][1])); */ + /* test(is_array(char[(int){ 1 }][1])); */ + /* test(is_array(char(*)[1][1])); */ + /* test(is_array(char(*)[(int){ 1 }][1])); */ +#if __has_feature(c_stdbit_h) + test(stdc_count_ones_uc(0)); + test(stdc_count_ones_us(0)); + test(stdc_count_ones_ui(0)); + test(stdc_count_ones_ul(0)); + test(stdc_count_ones_ull(0)); + test(stdc_count_ones(0U)); + test(stdc_count_ones_uc(-1)); + test(stdc_count_ones_us(-1)); + test(stdc_count_ones_ui(-1)); + test(stdc_count_ones_ul(-1)); + test(stdc_count_ones_ul(-1)); + test(stdc_count_ones_uc(u'a')); + test(stdc_count_ones_us(u'a')); + test(stdc_count_ones_ui(u'a')); + test(stdc_count_ones_ul(u'a')); + test(stdc_count_ones_ull(u'a')); +#endif + test(stdc_count_ones(u'a')); + test(stdc_count_ones(0b01010101U)); + test(stdc_count_ones(0b01010101UL)); + test(stdc_count_ones(0b01010101ULL)); +#if __has_feature(c_stdbit_h) + test(stdc_count_zeros_uc(0)); + test(stdc_count_zeros_us(0)); + test(stdc_count_zeros_ui(0)); + test(stdc_count_zeros_ul(0)); + test(stdc_count_zeros_ull(0)); + test(stdc_count_zeros_uc(-1)); + test(stdc_count_zeros_us(-1)); + test(stdc_count_zeros_ui(-1)); + test(stdc_count_zeros_ul(-1)); + test(stdc_count_zeros_ul(-1)); +#endif + test(stdc_count_zeros(u'a')); + test(stdc_count_zeros(0b01010101U)); + test(stdc_count_zeros(0b01010101UL)); + test(stdc_count_zeros(0b01010101ULL)); +#if __has_feature(c_stdbit_h) + test(stdc_leading_zeros_uc(0U)); + test(stdc_leading_zeros_us(0U)); + test(stdc_leading_zeros_ui(0U)); + test(stdc_leading_zeros_ul(0U)); + test(stdc_leading_zeros_ull(0U)); +#endif + test(stdc_leading_zeros(0U)); + test(stdc_leading_zeros(1U)); + test(stdc_leading_zeros(0b01010101U)); + test(stdc_leading_zeros(0b01010101UL)); + test(stdc_leading_zeros(0b01010101ULL)); + test(stdc_trailing_zeros(0U)); + test(stdc_trailing_zeros(1U)); + test(stdc_trailing_zeros(0b0101010100U)); + test(stdc_trailing_zeros(0b0101010100UL)); + test(stdc_trailing_zeros(0b0101010100ULL)); + test(stdc_trailing_zeros(u'\0')); + test(stdc_trailing_zeros(0U)); + test(stdc_trailing_zeros(0UL)); + test(stdc_trailing_zeros(0ULL)); +#if __has_feature(c_stdbit_h) + test(stdc_leading_ones_ui(UINT_MAX)); +#endif + test(stdc_leading_ones(UINT_MAX)); + test(stdc_leading_ones(0U+INT_MIN)); + test(stdc_leading_ones(1U+INT_MIN)); + test(stdc_leading_ones(ULLONG_MAX ^ UINT_MAX)); + test(stdc_trailing_ones(0U)); + test(stdc_trailing_ones(1U)); + test(stdc_trailing_ones(0b01010101U)); + test(stdc_trailing_ones(0b01010101UL)); + test(stdc_trailing_ones(0b01010101ULL)); + test(stdc_trailing_ones(u'\0')); + test(stdc_trailing_ones(UINT_MAX>>1)); + test(stdc_trailing_ones(ULONG_MAX>>1)); + test(stdc_trailing_ones(ULONG_MAX>>1)); + // argument must be an unsigned type + // test(stdc_leading_ones(1)); + /* test(stdc_leading_zeros(true)); */ + /* test(stdc_leading_zeros(false)); */ + /* test(stdc_leading_zeros((bool)1)); */ + test(stdc_leading_zeros((unsigned char)1)); +#if __has_feature(c_stdbit_h) + test(stdc_leading_zeros_uc(u'a')); + test(stdc_leading_zeros_us(u'a')); + test(stdc_leading_zeros_ui(u'a')); + test(stdc_leading_zeros_ul(u'a')); + test(stdc_leading_zeros_ull(u'a')); +#endif + test(stdc_leading_zeros(u'a')); + test(stdc_has_single_bit(u'a')); + test(stdc_has_single_bit(u'\2')); + /* test(stdc_has_single_bit(true)); */ + /* test(stdc_has_single_bit(false)); */ +#if __has_feature(c_stdbit_h) + test(stdc_has_single_bit_uc(0U)); + test(stdc_has_single_bit_uc(1U)); + test(stdc_has_single_bit_uc(2U)); + test(stdc_has_single_bit_uc(3U)); + test(stdc_has_single_bit_uc(4U)); +#endif + test(stdc_has_single_bit(0U)); + test(stdc_has_single_bit(1U)); + test(stdc_has_single_bit(2U)); + test(stdc_has_single_bit(3U)); + test(stdc_has_single_bit(4U)); + test(stdc_has_single_bit(1UL)); + test(stdc_has_single_bit(2UL)); + test(stdc_has_single_bit(3UL)); + test(stdc_has_single_bit(1ULL)); + test(stdc_has_single_bit(2ULL)); + test(stdc_has_single_bit(3ULL)); +#if __has_feature(c_stdbit_h) + test(stdc_bit_width_uc(u'a')); + test(stdc_bit_width_us(u'a')); + test(stdc_bit_width_ui(u'a')); + test(stdc_bit_width_ul(u'a')); + test(stdc_bit_width_ull(u'a')); + test(stdc_bit_width(u'a')); +#endif + /* test(stdc_bit_width(true)); */ + /* test(stdc_bit_width(false)); */ + test(stdc_bit_width(1U)); + test(stdc_bit_width(2U)); + test(stdc_bit_width(3U)); + test(stdc_bit_width(1UL)); + test(stdc_bit_width(2UL)); + test(stdc_bit_width(3UL)); + test(stdc_bit_width(1ULL)); + test(stdc_bit_width(2ULL)); + test(stdc_bit_width(3ULL)); + /* test(stdc_bit_floor(true)); */ + /* test(stdc_bit_floor(false)); */ + test(stdc_bit_floor(0U)); + test(stdc_bit_floor(1U)); + test(stdc_bit_floor(2U)); + test(stdc_bit_floor(3U)); + test(stdc_bit_floor(4U)); + test(stdc_bit_floor(1UL)); + test(stdc_bit_floor(2UL)); + test(stdc_bit_floor(3UL)); + test(stdc_bit_floor(1ULL)); + test(stdc_bit_floor(2ULL)); + test(stdc_bit_floor(3ULL)); + /* test(stdc_bit_ceil(true)); */ + /* test(stdc_bit_ceil(false)); */ + test(stdc_bit_ceil(0U)); + test(stdc_bit_ceil(1U)); + test(stdc_bit_ceil(2U)); + test(stdc_bit_ceil(3U)); + test(stdc_bit_ceil(4U)); + test(stdc_bit_ceil(1UL)); + test(stdc_bit_ceil(2UL)); + test(stdc_bit_ceil(3UL)); + test(stdc_bit_ceil(1ULL)); + test(stdc_bit_ceil(2ULL)); + test(stdc_bit_ceil(3ULL)); +#ifdef UINT128_MAX + test(stdc_leading_zeros(UINT128_MAX)); + test(stdc_leading_zeros(UINT128_MAX>>63)); + test(stdc_leading_zeros(UINT128_MAX>>64)); + test(stdc_leading_zeros(UINT128_MAX>>65)); + test(stdc_leading_zeros(UINT128_MAX ^ INT128_MIN)); + test(stdc_leading_zeros((UINT128_MAX ^ INT128_MIN)>>3)); + test(stdc_leading_zeros((UINT128_MAX ^ INT128_MIN)>>63)); + test(stdc_count_ones(UINT128_MAX ^ 0b01010101U)); + test(stdc_count_zeros(UINT128_MAX ^ 0b01010101U)); + test(stdc_trailing_zeros(~UINT128_MAX)); + test(stdc_trailing_zeros(UINT128_MAX - 1)); + test(stdc_trailing_zeros(UINT128_MAX<<63)); + test(stdc_trailing_zeros(UINT128_MAX<<64)); + test(stdc_trailing_zeros(UINT128_MAX<<65)); + test(stdc_trailing_zeros(UINT128_MAX & INT128_MIN)); + test(stdc_trailing_zeros((UINT128_MAX & INT128_MIN)>>3)); + test(stdc_trailing_zeros((UINT128_MAX & INT128_MIN)>>63)); + test(stdc_leading_ones(UINT128_MAX)); + test(stdc_leading_ones(UINT128_MAX ^ INT128_MIN)); + test(stdc_leading_ones(UINT128_MAX ^ UINT_MAX)); + test(stdc_leading_ones(UINT128_MAX ^ (UINT_MAX>>3))); + test(stdc_trailing_ones(UINT128_MAX)); + test(stdc_trailing_ones(UINT128_MAX - 1)); + test(stdc_trailing_ones(UINT128_MAX>>63)); + test(stdc_trailing_ones(UINT128_MAX>>64)); + test(stdc_trailing_ones(UINT128_MAX>>65)); + test(stdc_trailing_ones(UINT128_MAX ^ INT128_MIN)); + test(stdc_trailing_ones((UINT128_MAX ^ INT128_MIN)>>3)); + test(stdc_trailing_ones((UINT128_MAX ^ INT128_MIN)>>63)); + test(stdc_has_single_bit(UINT128_MAX ^ INT128_MIN)); + test(stdc_has_single_bit(UINT128_MAX & INT128_MIN)); + test(stdc_bit_width(UINT128_MAX ^ INT128_MIN)); + test(stdc_bit_width(UINT128_MAX & INT128_MIN)); + test(stdc_bit_floor(UINT128_MAX & INT128_MIN) == INT128_MIN); + test(stdc_bit_ceil(UINT128_MAX>>1) == INT128_MIN); + test(stdc_leading_zeros(argc+tozero(UINT128_MAX))); +#endif +#if BITINT_MAXWIDTH >= 128 + unsigned _BitInt(128) const ubitint128_max = -1; + signed _BitInt(128) const bitint128_min = ubitint128_max/2; + test(stdc_leading_zeros(ubitint128_max)); + test(stdc_leading_zeros(ubitint128_max>>63)); + test(stdc_leading_zeros(ubitint128_max>>64)); + test(stdc_leading_zeros(ubitint128_max>>65)); + test(stdc_leading_zeros(ubitint128_max ^ bitint128_min)); + test(stdc_leading_zeros((ubitint128_max ^ bitint128_min)>>3)); + test(stdc_leading_zeros((ubitint128_max ^ bitint128_min)>>63)); + test(stdc_count_ones(ubitint128_max ^ 0b01010101U)); + test(stdc_count_zeros(ubitint128_max ^ 0b01010101U)); + test(stdc_trailing_zeros(~ubitint128_max)); + test(stdc_trailing_zeros(ubitint128_max - 1)); + test(stdc_trailing_zeros(ubitint128_max<<63)); + test(stdc_trailing_zeros(ubitint128_max<<64)); + test(stdc_trailing_zeros(ubitint128_max<<65)); + test(stdc_trailing_zeros(ubitint128_max & bitint128_min)); + test(stdc_trailing_zeros((ubitint128_max & bitint128_min)>>3)); + test(stdc_trailing_zeros((ubitint128_max & bitint128_min)>>63)); + test(stdc_leading_ones(ubitint128_max)); + test(stdc_leading_ones(ubitint128_max ^ bitint128_min)); + test(stdc_leading_ones(ubitint128_max ^ UINT_MAX)); + test(stdc_leading_ones(ubitint128_max ^ (UINT_MAX>>3))); + test(stdc_trailing_ones(ubitint128_max)); + test(stdc_trailing_ones(ubitint128_max - 1)); + test(stdc_trailing_ones(ubitint128_max>>63)); + test(stdc_trailing_ones(ubitint128_max>>64)); + test(stdc_trailing_ones(ubitint128_max>>65)); + test(stdc_trailing_ones(ubitint128_max ^ bitint128_min)); + test(stdc_trailing_ones((ubitint128_max ^ bitint128_min)>>3)); + test(stdc_trailing_ones((ubitint128_max ^ bitint128_min)>>63)); + test(stdc_has_single_bit(ubitint128_max ^ bitint128_min)); + test(stdc_has_single_bit(ubitint128_max & bitint128_min)); + test(stdc_bit_width(ubitint128_max ^ bitint128_min)); + test(stdc_bit_width(ubitint128_max & bitint128_min)); + test(stdc_bit_floor(ubitint128_max & bitint128_min) == bitint128_min); + test(stdc_bit_ceil(ubitint128_max>>1) == bitint128_min); + test(stdc_leading_zeros(argc+tozero(ubitint128_max))); +#endif +} diff --git a/test-lib.c b/test-lib.c new file mode 100644 index 0000000..9430df0 --- /dev/null +++ b/test-lib.c @@ -0,0 +1,46 @@ +#include "c23-fallback.h" +#include + +typedef char const* key; + + +int comp_key(key const*a, key const*b) { + key A = *a; + key B = *b; + return strcmp(A, B); +} + +int comp(void const*a, void const*b) { + key const*A = a; + key const*B = b; + return comp_key(A, B); +} + +int main() { + static key data[] = { + "Tina", + "Albert", + "Xaver", + "Albertina", + }; + + qsort(data, (sizeof data)/(sizeof data[0]), sizeof data[0], comp); + for (size_t i = 0; i < (sizeof data)/(sizeof data[0]); ++i) + printf("%zu: \"%s\"\n", i, data[i]); + key k = "Tina"; + key* n1 = bsearch(&k, data, (sizeof data)/(sizeof data[0]), sizeof data[0], comp); + printf("found: \"%s\"\n", n1 ? *n1 : "unknown"); + key const* d = data; + key const* n2 = bsearch(&k, d, (sizeof data)/(sizeof data[0]), sizeof data[0], comp); + printf("found: \"%s\"\n", n2 ? *n2 : "unknown"); + char buf[25] = { 0 }; + strfroml(buf, 25, "%.10g", 3744.0083383L); + printf("strfroml %.10LG → \"%s\"\n", 3744.0083383L, buf); + strfroml(buf, 25, "%.10Lg", 3744.0083383L); + printf("invalid format leads to \"%s\"\n", buf); + for (char const* p = buf; p < buf+16; ++p) + printf("alignment %p: %zu, suitable for int*: %d\n", p, memalignment(p), memalignment(p)>=alignof(int)); + struct two {int a; int b; }; + assert((struct two){ .a = 0, .b = 1, }.b); + assert((struct two){ .a = 0, .b = 1, }.a); +} diff --git a/test-printf.c b/test-printf.c new file mode 100644 index 0000000..e6a544e --- /dev/null +++ b/test-printf.c @@ -0,0 +1,341 @@ +#include "c23-fallback.h" +#include +#include +#include + +#ifndef PRIb8 +# error "library support of binary number representations for input and output are required for this test" +// really stop it +# include "really stop useless compilation right now" +#endif + +#define PS(S, B) \ +do { \ + errno = 0; \ + unsigned long long res = strtoull(S, nullptr, B); \ + if (errno) { \ + perror("strtoull error " S); \ + errno = 0; \ + } else { \ + printf("base %d,\t\"%s\" →\t%llu\n", B, S, res); \ + if (errno) perror("printf error"); \ + errno = 0; \ + } \ + res = wcstoull(L ## S, nullptr, B); \ + if (errno) { \ + perror("wcstoull error " S); \ + errno = 0; \ + } else { \ + printf("base %d,\tL\"%s\" →\t%llu\n", B, S, res); \ + if (errno) perror("printf error"); \ + errno = 0; \ + } \ + } while (false) + +#define PSCAN(F, S, X) \ +do { \ + sscanf(S, F, &(X)); \ + if (errno) { \ + perror("sscanf error \"" S "\" format \"" F "\""); \ + errno = 0; \ + } else { \ + printf("%s: \"%s\" → " F "\n", F, S, X/*+0ULL*/); \ + if (errno) perror("printf error " F); \ + errno = 0; \ + } \ + swscanf(L ## S, L ## F, &(X)); \ + if (errno) { \ + perror("swscanf error L\"" S "\" format L\"" F "\""); \ + errno = 0; \ + } else { \ + printf("%s: L\"%s\" → " F "\n", F, S, X/*+0ULL*/); \ + if (errno) perror("printf error " F); \ + errno = 0; \ + } \ + } while(false) + +int main(int argc, char* argv[static argc+1]) { + printf("%b\n", 35u); + printf("%#b\n", 35u); + printf("%B\n", 35u); + printf("%#B\n", 35u); + if (argc > 1) { + unsigned long b = strtoul(argv[1], nullptr, 0); + printf("%lb\n", b); + printf("%#lb\n", b); + printf("%lB\n", b); + printf("%#lB\n", b); + } + perror("printf error"); errno = 0; + uint8_t u8 = -1; + uint16_t u16 = -1; + uint32_t u32 = -1; + uint64_t u64 = -1; + printf("PRIb8:\t%" PRIb8 "\n", u8); + printf("PRIb16:\t%" PRIb16 "\n", u16); + printf("PRIb32:\t%" PRIb32 "\n", u32); + printf("PRIb64:\t%" PRIb64 "\n", u64); + printf("PRIB8:\t%" PRIB8 "\n", u8); + printf("PRIB16:\t%" PRIB16 "\n", u16); + printf("PRIB32:\t%" PRIB32 "\n", u32); + printf("PRIB64:\t%" PRIB64 "\n", u64); + printf("PRIb8:\t%#" PRIb8 "\n", u8); + printf("PRIb16:\t%#" PRIb16 "\n", u16); + printf("PRIb32:\t%#" PRIb32 "\n", u32); + printf("PRIb64:\t%#" PRIb64 "\n", u64); + printf("PRIB8:\t%#" PRIB8 "\n", u8); + printf("PRIB16:\t%#" PRIB16 "\n", u16); + printf("PRIB32:\t%#" PRIB32 "\n", u32); + printf("PRIB64:\t%#" PRIB64 "\n", u64); + perror("printf error"); errno = 0; +#if __STDC_VERSION_INTTYPES_H__ < 201800L +# ifndef C23_FALLBACK_SILENT +# warning "no C23 printf support found, skipping tests for %w length modifiers" +# endif +#else + printf("11:\t%w11x\n", u8); + perror("printf error"); errno = 0; + printf("8:\t%w8x\n", u8); + printf("16:\t%w16x\n", u16); + printf("32:\t%w32x\n", u32); + printf("64:\t%w64x\n", u64); + perror("printf error"); errno = 0; + printf("11:\t%w11x\n", u8); + perror("printf error"); errno = 0; + printf("8:\t%w8u\n", u8); + printf("16:\t%w16u\n", u16); + printf("32:\t%w32u\n", u32); + printf("64:\t%w64u\n", u64); + perror("printf error"); errno = 0; + printf("11:\t%w11x\n", u8); + perror("printf error"); errno = 0; + printf("8:\t%w8d\n", u8); + printf("16:\t%w16d\n", u16); + printf("32:\t%w32d\n", u32); + printf("64:\t%w64d\n", u64); + perror("printf error"); errno = 0; + int8_t i8 = INT8_MAX; + int16_t i16 = INT16_MAX; + int32_t i32 = INT32_MAX; + int64_t i64 = INT64_MAX; + printf("11:\t%w11x\n", i8); + perror("printf error"); errno = 0; + printf("8:\t%w8x\n", i8); + printf("16:\t%w16x\n", i16); + printf("32:\t%w32x\n", i32); + printf("64:\t%w64x\n", i64); + perror("printf error"); errno = 0; + printf("11:\t%w11x\n", i8); + perror("printf error"); errno = 0; + printf("8:\t%w8u\n", i8); + printf("16:\t%w16u\n", i16); + printf("32:\t%w32u\n", i32); + printf("64:\t%w64u\n", i64); + perror("printf error"); errno = 0; + printf("11:\t%w11x\n", i8); + perror("printf error"); errno = 0; + printf("8:\t%w8d\n", i8); + printf("16:\t%w16d\n", i16); + printf("32:\t%w32d\n", i32); + printf("64:\t%w64d\n", i64); + perror("printf error"); errno = 0; + uint_fast8_t uf8 = -1; + uint_fast16_t uf16 = -1; + uint_fast32_t uf32 = -1; + uint_fast64_t uf64 = -1; + printf("11 fast:\t%wf11x\n", uf8); + perror("printf error"); errno = 0; + printf("8 fast:\t%wf8x\n", uf8); + printf("16 fast:\t%wf16x\n", uf16); + printf("32 fast:\t%wf32x\n", uf32); + printf("64 fast:\t%wf64x\n", uf64); + perror("printf error"); errno = 0; + printf("11 fast:\t%wf11x\n", uf8); + perror("printf error"); errno = 0; + printf("8 fast:\t%wf8u\n", uf8); + printf("16 fast:\t%wf16u\n", uf16); + printf("32 fast:\t%wf32u\n", uf32); + printf("64 fast:\t%wf64u\n", uf64); + perror("printf error"); errno = 0; + printf("11 fast:\t%wf11x\n", uf8); + perror("printf error"); errno = 0; + printf("8 fast:\t%wf8d\n", uf8); + printf("16 fast:\t%wf16d\n", uf16); + printf("32 fast:\t%wf32d\n", uf32); + printf("64 fast:\t%wf64d\n", uf64); + perror("printf error"); errno = 0; + int_fast8_t if8 = INT_FAST8_MAX; + int_fast16_t if16 = INT_FAST16_MAX; + int_fast32_t if32 = INT_FAST32_MAX; + int_fast64_t if64 = INT_FAST64_MAX; + printf("11 fast:\t%wf11x\n", if8); + perror("printf error"); errno = 0; + printf("8 fast:\t%wf8x\n", if8); + printf("16 fast:\t%wf16x\n", if16); + printf("32 fast:\t%wf32x\n", if32); + printf("64 fast:\t%wf64x\n", if64); + perror("printf error"); errno = 0; + printf("11 fast:\t%wf11x\n", if8); + perror("printf error"); errno = 0; + printf("8 fast:\t%wf8u\n", if8); + printf("16 fast:\t%wf16u\n", if16); + printf("32 fast:\t%wf32u\n", if32); + printf("64 fast:\t%wf64u\n", if64); + perror("printf error"); errno = 0; + printf("11 fast:\t%wf11x\n", if8); + perror("printf error"); errno = 0; + printf("8 fast:\t%wf8d\n", if8); + printf("16 fast:\t%wf16d\n", if16); + printf("32 fast:\t%wf32d\n", if32); + printf("64 fast:\t%wf64d\n", if64); + perror("printf error"); errno = 0; +#if UINT128_WIDTH || __SIZEOF_INT128__ +#if UINT128_WIDTH + puts("------------ found standard uint128_t -------------"); + uint128_t u128 = -1; + int128_t i128 = u128/2; +#elif __SIZEOF_INT128__ + puts("------------ found non-standard __uint128_t -------------"); + __uint128_t u128 = -1; + __int128_t i128 = u128/2; +#endif + printf("128u:\t%w128u\n", (__uint128_t)0); + printf("128u:\t%w128u\n", u128); + printf("128d:\t%w128d\n", u128); + printf("128d:\t%w128u\n", (__int128_t)0); + printf("128x:\t%w128x\n", i128); + printf("128d:\t%w128d\n", i128); + printf("128b:\t%#w128b\n", i128); +#endif + perror("printf error"); errno = 0; +#endif + puts("-------------- scanf functions ------------"); + PS("2147483647", 0); + PS("2147483647", 2); + PS("2147483647", 8); + PS("2147483647", 10); + PS("2147483647", 16); + PS("-1", 0); + PS("-1", 2); + PS("-1", 8); + PS("-1", 10); + PS("-1", 16); + PS("0b1", 0); + PS("0b1", 2); + PS("0b1", 8); + PS("0b1", 10); + PS("0b1", 16); + PS("0B1", 0); + PS("0B1", 2); + PS("0B1", 8); + PS("0B1", 10); + PS("0B1", 16); + PS("0x1", 0); + PS("0x1", 2); + PS("0x1", 8); + PS("0x1", 10); + PS("0x1", 16); + PS("0b1111111111111111111111111111111111111111111111111111111111111111", 0); + PS("0b1111111111111111111111111111111111111111111111111111111111111111", 2); + PS("0b1111111111111111111111111111111111111111111111111111111111111111", 8); + PS("0b1111111111111111111111111111111111111111111111111111111111111111", 10); + PS("0b1111111111111111111111111111111111111111111111111111111111111111", 16); + PS("0B1111111111111111111111111111111111111111111111111111111111111111", 0); + PS("0B1111111111111111111111111111111111111111111111111111111111111111", 2); + PS("0B1111111111111111111111111111111111111111111111111111111111111111", 8); + PS("0B1111111111111111111111111111111111111111111111111111111111111111", 10); + PS("0B1111111111111111111111111111111111111111111111111111111111111111", 16); + PS("0xffffffffffffffff", 0); + PS("0xffffffffffffffff", 2); + PS("0xffffffffffffffff", 8); + PS("0xffffffffffffffff", 10); + PS("0xffffffffffffffff", 16); + PSCAN("%" SCNb8, "0b1", u8); + PSCAN("%" SCNu8, "0b1", u8); + PSCAN("%" SCNx8, "0b1", u8); + PSCAN("%" SCNb8, "0x1", u8); + PSCAN("%" SCNu8, "0x1", u8); + PSCAN("%" SCNx8, "0x1", u8); + PSCAN("%" SCNb16, "0b1", u16); + PSCAN("%" SCNu16, "0b1", u16); + PSCAN("%" SCNx16, "0b1", u16); + PSCAN("%" SCNb16, "0x1", u16); + PSCAN("%" SCNu16, "0x1", u16); + PSCAN("%" SCNx16, "0x1", u16); + PSCAN("%" SCNb32, "0b1", u32); + PSCAN("%" SCNu32, "0b1", u32); + PSCAN("%" SCNx32, "0b1", u32); + PSCAN("%" SCNb32, "0x1", u32); + PSCAN("%" SCNu32, "0x1", u32); + PSCAN("%" SCNx32, "0x1", u32); + PSCAN("%" SCNb64, "0b1", u64); + PSCAN("%" SCNu64, "0b1", u64); + PSCAN("%" SCNx64, "0b1", u64); + PSCAN("%" SCNb64, "0x1", u64); + PSCAN("%" SCNu64, "0x1", u64); + PSCAN("%" SCNx64, "0x1", u64); +#if __STDC_VERSION_INTTYPES_H__ < 201800L +# ifndef C23_FALLBACK_SILENT +# warning "no C23 scanf support found, skipping tests for %w length modifiers" +# endif +#else + PSCAN("%w8b", "0b1", u8); + PSCAN("%w8u", "0b1", u8); + PSCAN("%w8x", "0b1", u8); + PSCAN("%w8b", "0x1", u8); + PSCAN("%w8u", "0x1", u8); + PSCAN("%w8x", "0x1", u8); + PSCAN("%w16b", "0b1", u16); + PSCAN("%w16u", "0b1", u16); + PSCAN("%w16x", "0b1", u16); + PSCAN("%w16b", "0x1", u16); + PSCAN("%w16u", "0x1", u16); + PSCAN("%w16x", "0x1", u16); + PSCAN("%w32b", "0b1", u32); + PSCAN("%w32u", "0b1", u32); + PSCAN("%w32x", "0b1", u32); + PSCAN("%w32b", "0x1", u32); + PSCAN("%w32u", "0x1", u32); + PSCAN("%w32x", "0x1", u32); + PSCAN("%w64b", "0b1", u64); + PSCAN("%w64u", "0b1", u64); + PSCAN("%w64x", "0b1", u64); + PSCAN("%w64b", "0x1", u64); + PSCAN("%w64u", "0x1", u64); + PSCAN("%w64x", "0x1", u64); +#if UINT128_WIDTH || __SIZEOF_INT128__ + PSCAN("%w128b", "0b1", u128); + PSCAN("%w128u", "0b1", u128); + PSCAN("%w128x", "0b1", u128); + PSCAN("%w128b", "0x1", u128); + PSCAN("%w128u", "0x1", u128); + PSCAN("%w128x", "0x1", u128); + + PSCAN("%w128b", "0b11111111111111111111111111111111111111111111111111111111111111111", u128); + PSCAN("%w128u", "0b111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111", u128); + PSCAN("%w128x", "0b111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111", u128); + PSCAN("%w128b", "0x7ffffffffffffffffffffffffffffffe", u128); + PSCAN("%w128u", "0x7ffffffffffffffffffffffffffffffe", u128); + PSCAN("%w128x", "0x7ffffffffffffffffffffffffffffffe", u128); + + PSCAN("%w128b", "0b1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111", u128); + PSCAN("%w128u", "0b1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111", u128); + PSCAN("%w128x", "0b1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111", u128); + PSCAN("%w128b", "0x7fffffffffffffffffffffffffffffff", u128); + PSCAN("%w128u", "0x7fffffffffffffffffffffffffffffff", u128); + PSCAN("%w128x", "0x7fffffffffffffffffffffffffffffff", u128); + PSCAN("%w128b", "0xffffffffffffffffffffffffffffffff", u128); + PSCAN("%w128u", "0xffffffffffffffffffffffffffffffff", u128); + PSCAN("%w128x", "0xffffffffffffffffffffffffffffffff", u128); +#ifdef SCNb128 + puts("-------------- SCN macros ------------"); + PSCAN("%" SCNb128, "0b1", u128); + PSCAN("%" SCNu128, "0b1", u128); + PSCAN("%" SCNx128, "0b1", u128); + PSCAN("%" SCNb128, "0x1", u128); + PSCAN("%" SCNu128, "0x1", u128); + PSCAN("%" SCNx128, "0x1", u128); +#endif +#endif +#endif +} diff --git a/test-reorder.c b/test-reorder.c new file mode 100644 index 0000000..d9c16e6 --- /dev/null +++ b/test-reorder.c @@ -0,0 +1,2 @@ +#include "c23-fallback.h" +#include "reorder.h" diff --git a/test-strfrom.c b/test-strfrom.c new file mode 100644 index 0000000..7c5316b --- /dev/null +++ b/test-strfrom.c @@ -0,0 +1,26 @@ +#include +#include + +#define testIt(FUNC, F, V) \ + do { \ + char buf[256]; \ + int ret = FUNC(buf, 256, (F), (V)); \ + printf("%d: %s\n", ret, buf); \ + } while (0) + +int main(void) { + testIt(strfromd, "%g", 1.0/7); + testIt(strfromd, "%.6766g", 1.0/7); + testIt(strfromd, "%.0000000000000000000000000000000000000000000006766g", 1.0/7); + testIt(strfroml, "%g", 1.0L/7); + testIt(strfroml, "%.6766g", 1.0L/7); + testIt(strfroml, "%.0000000000000000000000000000000000000000000006766g", 1.0L/7); + testIt(strfroml, "%.547996766g", 1.0L/7); + testIt(strfromd, "%a", 1.0/7); + testIt(strfromd, "%.6766a", 1.0/7); + testIt(strfromd, "%.0000000000000000000000000000000000000000000006766a", 1.0/7); + testIt(strfroml, "%a", 1.0L/7); + testIt(strfroml, "%.6766a", 1.0L/7); + testIt(strfroml, "%.0000000000000000000000000000000000000000000006766a", 1.0L/7); + testIt(strfroml, "%.547996766a", 1.0L/7); +} diff --git a/test_circular.c b/test_circular.c new file mode 100644 index 0000000..338e16a --- /dev/null +++ b/test_circular.c @@ -0,0 +1,22 @@ +#include +#include "circular.h" + +int main(int argc, char* argv[]) { + size_t len = argc > 1 ? strtoull(argv[1], nullptr, 0) : 10; + fprintf(stderr, "starting with %zu elements in total\n", len); + circular* c = circular_new(len); + circular_fput(c, stderr); + for (size_t i = 0; i < len-1; ++i) { + if (!circular_append(c, i)) break; + } + circular_fput(c, stderr); + for (size_t i = 0; i < len/2; ++i) { + if (!circular_append(c, circular_pop(c))) break; + } + circular_fput(c, stderr); + c = circular_resize(c, 2*len); + circular_fput(c, stderr); + c = circular_resize(c, len); + circular_fput(c, stderr); + circular_delete(c); +} diff --git a/tgstring.h b/tgstring.h new file mode 100644 index 0000000..e58a6cd --- /dev/null +++ b/tgstring.h @@ -0,0 +1,485 @@ +#ifndef __STDC_TGSTRING__ +# define __STDC_TGSTRING__ 201610L + +#include "c23-fallback.h" + +/** + ** @file + ** @brief Type generic string interfaces + ** + ** @copyright 2016 Jens Gustedt + ** + ** @remark This code is distributed under a Creative Commons + ** Attribution 4.0 International License. + ** + ** @warning This code is provided as is, use at your own risk. + ** + ** This header file provides type generic replacements for the C + ** library string functions that are "const-aware" and that chose + ** the right alternative between narrow and wide string + ** functions. + ** + ** Generally this interfaces (and replaces) all the C standard + ** functions with names starting with `str`. These are + ** + ** - `strcat` + ** - `strchr` + ** - `strcmp` + ** - `strcoll` + ** - `strcpy` + ** - `strcspn` + ** - `strlen` + ** - `strncat` + ** - `strncmp` + ** - `strncpy` + ** - `strntok` + ** - `strpbrk` + ** - `strrchr` + ** - `strspn` + ** - `strstr` + ** - `strtod` + ** - `strtof` + ** - `strtoimax` + ** - `strtol` + ** - `strtold` + ** - `strtoll` + ** - `strtoul` + ** - `strtoull` + ** - `strtoumax` + ** - `strxfrm` + ** + ** The interfaces that are prefixed with `mem` are more difficult + ** to match, since the types of parameters of the existing `mem` + ** and `wmem` functions are not consistent: the `mem` functions + ** work with `void*`, thus on @b bytes, whereas the `wmem` + ** functions work with `wchar_t`, thus on wide characters. The + ** first can receive any pointer argument whereas the second can + ** only receive wide character arrays. Generally the semantics of + ** a type generic interface that mixes bytes and wide characters + ** would not be clear since also the interpretation of a length + ** parameter would change. + ** + ** Out of these functions only the functions `memchr` and + ** `wmemchr` have the const-awareness problem, so we provide + ** these. Because for `wmemchr` the parameter is fixed to be + ** derived from `wchar_t`, this one is not very difficult. + ** + ** `memchr` is a bit tricky, because the original receives a + ** `void const*` that might come from any data pointer. We have + ** to do some black magic to decide if the target type is + ** `const`-qualified. + ** + ** This uses a lot of small stub functions that usually will be + ** inlined. If you are a C library provider that wants to + ** integrate this, see the discussion of ::_TG_INLINE. + **/ + +# if __STDC_VERSION__ < 201112L +# error "tgstring.h needs a C11 conforming compiler" +# endif + +# include +# include +# include +# include +# include + +/* We define a bunch of inline functions that have the only + purpose of providing conversions to the their arguments and + then to pass them on to the corresponding standard function. We + use the same names as the standard function and append "_const" + to them. This use is covered by the standard since all of the + "str" and "wcs" names are reserved, anyhow. */ + +/** + ** @def _TG_INLINE + ** + ** We would like to have all the stubs as `inline` functions. The + ** problem is, that if we do so, and the compiler decides not to + ** inline the function (because of command line arguments, + ** whatever), the symbol would be missing and the program would + ** not link. + ** + ** The alternatives would be + ** + ** - `static inline`, but then the functions could not be + ** included in other `inline` functions that are themselves not + ** `static`. + ** + ** - `inline` and provide all stubs in the C library. + ** + ** - compiler magic similar to the gcc attribute as shown below + ** + ** If you have such specific magic, I'd be happy to integrate + ** your two lines, here. If you don't, you'd have to compile + ** this file with something equivalent to + ** @code + ** cc -D_TG_INLINE= -x c -o tgstring.o tgstring.h + ** @endcode + ** to produce an object file `tgstring.o` that you add to your C + ** library. + **/ +#ifndef _TG_INLINE +# if __GNUC__ +# define _TG_INLINE __attribute__((always_inline)) inline +/* Fall back to inline, if none of that worked */ +# else +# define _TG_INLINE inline +# endif +#endif + +// void *memchr(void const *s, int c, size_t n); + +_TG_INLINE +void const*memchr_const(void const* _s, + int _c, size_t _n) { + return memchr(_s, _c, _n); +} + +# undef memchr +/** + ** Because `wchar_t` is only a `typedef`, we can't map to the + ** wide character version. Since the original C library interface + ** has a `void const*` parameter, all types must be valid. + ** + ** This uses black magic to decide if the pointer target of the + ** first argument is `const`-qualified. Namely a conditional + ** expression is used that aggregates type information from @a _0 + ** and a `void*`. The resulting type then is a pointer to `void` + ** with the collection of the qualifications of the two. The + ** alternative part of the conditional expression must not be + ** `(void*)0`, because that would be a null pointer constant and + ** so the resulting type would be the original type of `_0`. + **/ +# define memchr(_0, _1, _2) \ +_Generic(1 ? (_0) : (void*)1, \ + void*: memchr, \ + void const*: memchr_const) \ +((_0), (_1), (_2)) + + +/** + ** @brief An internal macro to select a const conserving string + ** function. + ** + ** This macro simply selects the function that corresponds to the + ** type of argument @a X. It is not meant to be used directly but + ** to be integrated in specific macros that overload C library + ** functions. + ** + ** The argument @a X must correspond to an array or pointer, + ** otherwise an error is triggered in the controlling expression. + ** + ** If any of the 4 cases makes no sense for the type generic + ** interface in question, that case can be switched off by + ** passing `nullptr` instead of a function. A use of that case in user + ** code then results in a compile time error. + ** + ** @see strchr on how to integrate this into a type generic user + ** interface + **/ +# define _TG_TO_CONST(SN, SC, WN, WC, X, ...) \ +_Generic(&(X[0]), \ + char*: SN, \ + char const*: SC, \ + wchar_t*: WN, \ + wchar_t const*: WC \ + )((X), __VA_ARGS__) + +_TG_INLINE +wchar_t const*wmemchr_const(wchar_t const _s[static 1], + wchar_t _c, size_t _n) { + return wmemchr(_s, _c, _n); +} + + +# undef wmemchr +/** + ** This function only makes sense if it receives a `wchar_t` + ** argument. + **/ +# define wmemchr(...) \ + _TG_TO_CONST(nullptr, /* char*: error */ \ + nullptr, /* const char*:error */ \ + wmemchr, /* wchar_t* */ \ + wmemchr_const, /* const wchar_t* */ \ + __VA_ARGS__) + +// char *strchr(char const *s, int c); + +_TG_INLINE +char const*strchr_const(char const *_s, int _c) { + return strchr(_s, _c); +} + +_TG_INLINE +wchar_t const*wcschr_const(wchar_t const *_s, wchar_t _c) { + return wcschr(_s, _c); +} + +# undef strchr +# define strchr(...) \ + _TG_TO_CONST(strchr, strchr_const, wcschr, wcschr_const, \ + __VA_ARGS__) + +// char *strpbrk(char const *_s, char const *_t); + +_TG_INLINE +char const*strpbrk_const(char const *_s, char const *_t) { + return strpbrk(_s, _t); +} + +_TG_INLINE +wchar_t const*wcspbrk_const(wchar_t const *_s, wchar_t const *_t) { + return wcspbrk(_s, _t); +} + +# undef strpbrk +# define strpbrk(...) \ + _TG_TO_CONST(strpbrk, strpbrk_const, wcspbrk, wcspbrk_const, \ + __VA_ARGS__) + +// char *strrchr(char const *s, int c); + +_TG_INLINE +char const* strrchr_const(char const *_s, int _c) { + return strrchr(_s, _c); +} + +_TG_INLINE +wchar_t const* wcsrchr_const(wchar_t const *_s, wchar_t _c) { + return wcsrchr(_s, _c); +} + +# undef strrchr +# define strrchr(...) \ + _TG_TO_CONST(strrchr, strrchr_const, wcsrchr, wcsrchr_const, \ + __VA_ARGS__) + + +// char *strstr(char const *_s, const char *_t); + +_TG_INLINE +char const*strstr_const(char const *_s, char const *_t) { + return strstr(_s, _t); +} + +_TG_INLINE +wchar_t const*wcsstr_const(wchar_t const *_s, wchar_t const *_t) { + return wcsstr(_s, _t); +} + +# undef strstr +# define strstr(...) \ + _TG_TO_CONST(strstr, /* char* */ \ + strstr_const, /* const char* */ \ + wcsstr, /* wchar_t* */ \ + wcsstr_const, /* const wchar_t* */ \ + __VA_ARGS__) + +/** + ** @brief An internal macro to select a string function according + ** to the base type `char` or `wchar_t`. + ** + ** This macro simply adds the "wcs" prefix to the name of the + ** function, if a `wchar_t` function is to be called. Traditional + ** `char` strings are mapped to `char`. + ** + ** There is no default case, because these functions need a + ** string and not some arbitrary bytes. + ** + ** The argument @a X must correspond to an array or pointer, + ** otherwise an error is triggered in the controlling expression. + ** + ** No precaution is made concerning `const`. If a `const` + ** qualified pointer type is passed in and the function that is + ** selected does not accept such an argument, an error is + ** diagnosed as if the corresponding function would have been + ** used directly. + ** + ** @see strcpy on how to integrate this into a type generic user + ** interface + **/ +# define _TG_STR(NAME, X, ...) \ +_Generic(&(X[0]), \ + char*: str ## NAME, \ + char const*: str ## NAME, \ + wchar_t*: wcs ## NAME, \ + wchar_t const*: wcs ## NAME \ + )((X), __VA_ARGS__) + +# undef strcpy +# define strcpy(...) _TG_STR(cpy, __VA_ARGS__) +# undef strncpy +# define strncpy(...) _TG_STR(ncpy, __VA_ARGS__) +# undef strcat +# define strcat(...) _TG_STR(cat, __VA_ARGS__) +# undef strncat +# define strncat(...) _TG_STR(ncat, __VA_ARGS__) +# undef strcmp +# define strcmp(...) _TG_STR(cmp, __VA_ARGS__) +# undef strcoll +# define strcoll(...) _TG_STR(coll, __VA_ARGS__) +# undef strncmp +# define strncmp(...) _TG_STR(ncmp, __VA_ARGS__) +# undef strxfrm +# define strxfrm(...) _TG_STR(xfrm, __VA_ARGS__) +# undef strcspn +# define strcspn(...) _TG_STR(cspn, __VA_ARGS__) +# undef strspn +# define strspn(...) _TG_STR(spn, __VA_ARGS__) +# undef strntok +# define strntok(...) _TG_STR(ntok, __VA_ARGS__) +# undef strlen +# define strlen(...) _TG_STR(len, __VA_ARGS__) + + +/** + ** @brief An internal Xmacro to generate const conserving string + ** to floating point conversion function. + ** + ** In the case of the string to number functions the original C + ** library functions cannot be used directly, because their second + ** argument is an unqualified pointer to pointer. The return type of + ** these functions is deduced from the name of the function by using + ** `typeof`. + ** + ** @param C is the base type of the string, so `char` or `wchar_t` + ** @param NAME is the base name for the function + **/ +#define _TG_STR2F_FUNC_(C, NAME) \ +_TG_INLINE \ +typeof(NAME((C[1]){ 0 }, nullptr)) _TG_ ## NAME ## _c(C const* _p, C const** _e) { \ + return NAME(_p, (C**)_e); \ +} \ +_TG_INLINE \ +typeof(NAME((C[1]){ 0 }, nullptr)) _TG_ ## NAME ## _nc(C* _p, C** _e) { \ + return NAME(_p, _e); \ +} \ +typedef typeof(NAME((C[1]){ 0 }, nullptr)) _TG_ ## NAME ## _c_ftype(C const*, C const**); \ +typedef typeof(NAME((C[1]){ 0 }, nullptr)) _TG_ ## NAME ## _nc_ftype(C*, C**) + +/** + ** @brief An internal Xmacro to generate const conserving string + ** to floating point conversion function. + ** + ** This macro just assembles _TG_STR2F_FUNC_() for the two cases + ** of @a C being `char` or `wchar_t`. + ** + ** @param T is the target type of the conversion + ** @param NAME is the base name for the function + **/ +#define _TG_STR2F_FUNC(NAME) \ + _TG_STR2F_FUNC_(char, str ## NAME); \ + _TG_STR2F_FUNC_(wchar_t, wcs ## NAME) + + +/* generate the three groups of floating point conversion functions */ +_TG_STR2F_FUNC(tof); +_TG_STR2F_FUNC(tod); +_TG_STR2F_FUNC(told); + + + +/** + ** @brief An internal Xmacro to generate const conserving string + ** to integer conversion function. + **/ +#define _TG_STR2I_FUNC_(C, NAME) \ +_TG_INLINE \ +typeof(NAME((C[1]){ 0 }, nullptr, 0)) _TG_ ## NAME ## _c(C const* _p, C const** _e, int _b) { \ + return NAME(_p, (C**)_e, _b); \ +} \ +_TG_INLINE \ +typeof(NAME((C[1]){ 0 }, nullptr, 0)) _TG_ ## NAME ## _nc(C* _p, C** _e, int _b) { \ + return NAME(_p, _e, _b); \ +} \ +typedef typeof(NAME((C[1]){ 0 }, nullptr, 0)) _TG_ ## NAME ## _c_ftype(C const*, C const**, int); \ +typedef typeof(NAME((C[1]){ 0 }, nullptr, 0)) _TG_ ## NAME ## _nc_ftype(C*, C**, int) + + +/** + ** @brief An internal Xmacro to generate const conserving string + ** to integer conversion function. + ** + ** This macro just assembles _TG_STR2I_FUNC_() for the two case of + ** `being` `char` or `wchar_t`. The return type of these functions is + ** deduced from the name of the function by using `typeof`. + ** + ** @param NAME is the base name for the function + **/ +#define _TG_STR2I_FUNC(NAME) \ + _TG_STR2I_FUNC_(char, str ## NAME); \ + _TG_STR2I_FUNC_(wchar_t, wcs ## NAME) + + +/* generate the six groups of integer conversion functions */ +_TG_STR2I_FUNC(tol); +_TG_STR2I_FUNC(toll); +_TG_STR2I_FUNC(toul); +_TG_STR2I_FUNC(toull); +_TG_STR2I_FUNC(toimax); +_TG_STR2I_FUNC(toumax); + +/** + ** @brief An internal macro to select a const conserving string + ** to integer conversion function. + ** + ** This macro simply selects the function that corresponds to + ** the type of argument @a X. It is not meant to be used + ** directly but to be integrated in specific macros that + ** overload C library functions. + ** + ** The argument @a X must correspond to an array or pointer, + ** otherwise an error is triggered in the controlling + ** expression. + ** + ** There is no default case, because these functions need a + ** string and not some arbitrary bytes. + ** + ** @see strtol on how to integrate this into a type generic user + ** interface + **/ +# define _TG_STR_FUNC3(NAME, X, P, B, ...) \ +_Generic(&((*P)[0]), \ + char*: _TG_str ## NAME ## _nc, \ + char const*: _TG_str ## NAME ## _c, \ + wchar_t*: _TG_wcs ## NAME ## _nc, \ + wchar_t const*: _TG_wcs ## NAME ## _c \ + )((X), (P), (B)) + +/** + ** @brief An internal macro to select a const conserving string + ** to floating point conversion function. + ** + ** @see _TG_STR_FUNC3 for a complete description of the strategy + **/ +# define _TG_STR_FUNC2(NAME, X, P, ...) \ +_Generic(&((*P)[0]), \ + char*: _TG_str ## NAME ## _nc, \ + char const*: _TG_str ## NAME ## _c, \ + wchar_t*: _TG_wcs ## NAME ## _nc, \ + wchar_t const*: _TG_wcs ## NAME ## _c \ + )((X), (P)) + +# undef strtod +# define strtod(...) _TG_STR_FUNC2(tod, __VA_ARGS__, 0, 0, 0) +# undef strtof +# define strtof(...) _TG_STR_FUNC2(tof, __VA_ARGS__, 0, 0, 0) +# undef strtold +# define strtold(...) _TG_STR_FUNC2(told, __VA_ARGS__, 0, 0, 0) +# undef strtol +# define strtol(...) _TG_STR_FUNC3(tol, __VA_ARGS__, 0, 0, 0) +# undef strtoll +# define strtoll(...) _TG_STR_FUNC3(toll, __VA_ARGS__, 0, 0, 0) +# undef strtoul +# define strtoul(...) _TG_STR_FUNC3(toul, __VA_ARGS__, 0, 0, 0) +# undef strtoull +# define strtoull(...) _TG_STR_FUNC3(toull, __VA_ARGS__, 0, 0, 0) +# undef strtoimax +# define strtoimax(...) _TG_STR_FUNC3(toimax, __VA_ARGS__, 0, 0, 0) +# undef strtoumax +# define strtoumax(...) _TG_STR_FUNC3(toumax, __VA_ARGS__, 0, 0, 0) + + +#endif diff --git a/tgstring_test.c b/tgstring_test.c new file mode 100644 index 0000000..18d8929 --- /dev/null +++ b/tgstring_test.c @@ -0,0 +1,53 @@ +#include "tgstring.h" +#include + +#define putws(S) do { printf("%ls\n", S); } while(0) + +int main(void) { + char const carray[] = "some characters"; + // this should produce a diagnostic + char * capnc = strchr(carray, 'c'); + capnc = memchr(carray, 'c', sizeof carray); + // this shouldn't produce a diagnostic + char const* capc = strchr(carray, 't'); + capc = memchr(carray, 't', sizeof carray); + printf("%s | %s\n", capnc, capc); + // this shouldn't produce a diagnostic + char cannc[] = "90 first text"; + char* cnextnc = memchr(cannc, 't', sizeof cannc); + printf("reading value %lu, followed by", strtoul(cannc, &cnextnc)); puts(cnextnc); + printf("reading value %g, followed by", strtod(cannc, &cnextnc)); puts(cnextnc); + // this shouldn't produce a diagnostic + char const canc[] = "11 second text"; + char const* cnextc = memchr(canc, 't', sizeof canc); + printf("reading value %jd, followed by", strtoimax(canc, &cnextc)); puts(cnextc); + printf("reading value %Lg, followed by", strtold(canc, &cnextc)); puts(cnextc); + printf("reading value %lu, followed by", strtoul(cannc, &cnextc)); puts(cnextc); + printf("reading value %g, followed by", strtod(cannc, &cnextc)); puts(cnextc); + // these two should produce diagnostics + printf("reading value %ju, followed by", strtoumax(canc, &cnextnc)); puts(cnextnc); + printf("reading value %Lg, followed by", strtold(canc, &cnextnc)); puts(cnextnc); + wchar_t const warray[] = L"some wide characters"; + // this should produce a diagnostic + wchar_t * wapnc = strchr(warray, L'c'); + wapnc = wmemchr(warray, L'c', sizeof warray/sizeof warray[0]); + // this shouldn't produce a diagnostic + wchar_t const* wapc = strchr(warray, L't'); + wapc = wmemchr(warray, L't', sizeof warray/sizeof warray[0]); + printf("%ls | %ls\n", wapnc, wapc); + // this shouldn't produce a diagnostic + wchar_t wcnnc[] = L"5 first text"; + wchar_t* wnextnc = 0; + printf("reading value %lu, followed by", wcstoul(wcnnc, &wnextnc, 0)); putws(wnextnc); + printf("reading value %g, followed by", wcstod(wcnnc, &wnextnc)); putws(wnextnc); + // this shouldn't produce a diagnostic + wchar_t const wcnc[] = L"37 second text"; + wchar_t const* wnextc = nullptr; + printf("reading value %jd, followed by", strtoimax(wcnc, &wnextc)); putws(wnextc); + printf("reading value %Lg, followed by", strtold(wcnc, &wnextc)); putws(wnextc); + printf("reading value %lu, followed by", strtoul(wcnnc, &wnextc)); putws(wnextc); + printf("reading value %g, followed by", strtod(wcnnc, &wnextc)); putws(wnextc); + // these two should produce diagnostics + printf("reading value %ju, followed by", strtoumax(wcnc, &wnextnc)); putws(wnextnc); + printf("reading value %Lg, followed by", strtold(wcnc, &wnextnc)); putws(wnextnc); +} diff --git a/timespec.c b/timespec.c new file mode 100644 index 0000000..597e932 --- /dev/null +++ b/timespec.c @@ -0,0 +1,118 @@ +#include "c23-fallback.h" +#include +#include +#include +#include +#include + +#include "stats.h" + +/** + ** @brief compute a time difference + ** + ** This uses a @c double to compute the time. If we want to + ** be able to track times without further loss of precision + ** and have @c double with 52 bit mantissa, this + ** corresponds to a maximal time difference of about 4.5E6 + ** seconds, or 52 days. + ** + **/ +double timespec_diff(struct timespec const* later, + struct timespec const* sooner){ + /* Be careful: tv_sec could be an unsigned type */ + if (later->tv_sec < sooner->tv_sec) + return -timespec_diff(sooner, later); + else + return + (later->tv_sec - sooner->tv_sec) + /* tv_nsec is known to be a signed type. */ + + (later->tv_nsec - sooner->tv_nsec) * 1E-9; +} + +uint64_t volatile s; + +int main(int argc, char* argv[argc+1]) { + if (argc < 2) { + fprintf(stderr, "Usage: %s NNN, NNN iterations\n", + argv[0]); + return EXIT_FAILURE; + } + enum { probes = 10, loops = 7, }; + uint64_t iterations = strtoull(argv[1], nullptr, 0); + uint64_t upper = iterations*iterations; + + stats statistic[loops] = { }; + + struct timespec tdummy; + stats sdummy[4] = { }; + + for (unsigned probe = 0; probe < probes; ++probe) { + uint64_t accu0 = 0; + uint64_t accu1 = 0; + struct timespec t[loops+1] = { }; + timespec_get(&t[0], TIME_UTC); + /* Volatile for i ensures that the loop is effected */ + for (uint64_t volatile i = 0; i < iterations; ++i) { + /* do nothing */ + } + timespec_get(&t[1], TIME_UTC); + /* s must be volatile to ensure that the loop is effected */ + for (uint64_t i = 0; i < iterations; ++i) { + s = i; + } + timespec_get(&t[2], TIME_UTC); + /* Opaque computation ensures that the loop is effected */ + for (uint64_t i = 1; accu0 < upper; i += 2) { + accu0 += i; + } + timespec_get(&t[3], TIME_UTC); + /* A function call can usually not be optimized out. */ + for (uint64_t i = 0; i < iterations; ++i) {/*@\label{ln:accumulate}*/ + timespec_get(&tdummy, TIME_UTC); + accu1 += tdummy.tv_nsec; + } + timespec_get(&t[4], TIME_UTC); + /* A function call can usually not be optimized out, but + an inline function can. */ + for (uint64_t i = 0; i < iterations; ++i) {/*@\label{ln:collect1}*/ + timespec_get(&tdummy, TIME_UTC); + stats_collect1(&sdummy[1], tdummy.tv_nsec); + } + timespec_get(&t[5], TIME_UTC); + for (uint64_t i = 0; i < iterations; ++i) {/*@\label{ln:collect2}*/ + timespec_get(&tdummy, TIME_UTC); + stats_collect2(&sdummy[2], tdummy.tv_nsec); + } + timespec_get(&t[6], TIME_UTC); + for (uint64_t i = 0; i < iterations; ++i) {/*@\label{ln:collect3}*/ + timespec_get(&tdummy, TIME_UTC); + stats_collect3(&sdummy[3], tdummy.tv_nsec); + } + timespec_get(&t[7], TIME_UTC); + /* make sure to use values of volatiles after the loop */ + printf("s, %zu iterations, accu0 %zx, accu1 %zx\n", s, accu0, accu1); + for (size_t moment = 1; moment < 4; ++moment) { + double mean = stats_mean(&sdummy[moment]); + double sdev = stats_rsdev_unbiased(&sdummy[moment]); + double skew = stats_skew(&sdummy[moment]); + printf("%zu moments: average value of nsec\t%5.4g\t± %g%% (%g skew)\n", + moment, mean, 100.0*sdev, skew); + } + + for (unsigned i = 0; i < loops; i++) { + double diff = timespec_diff(&t[i+1], &t[i]); + stats_collect2(&statistic[i], diff); + } + + } + + for (unsigned i = 0; i < loops; i++) { + double mean = stats_mean(&statistic[i]); + double rsdev = stats_rsdev_unbiased(&statistic[i]); + printf("loop %u: E(t) (sec):\t%5.2e ± %4.02f%%,\tloop body %5.2e\n", + i, mean, 100.0*rsdev, mean/iterations); + } + + + return EXIT_SUCCESS; +} diff --git a/va_arg.c b/va_arg.c new file mode 100644 index 0000000..1b2c0a8 --- /dev/null +++ b/va_arg.c @@ -0,0 +1,52 @@ +#include "c23-fallback.h" +#include +#include +//#include + +/** + ** @brief A small, useless function to show how variadic + ** functions work + **/ +double sumIt(size_t n, ...) { + double ret = 0.0; + va_list va; + va_start(va); + for (size_t i = 0; i < n; ++i) + ret += va_arg(va, double); + va_end(va); + return ret; +} + +/** + ** @brief A simple debug stream + ** + ** Per convention no output is produced when this is null. + ** + ** This can be set local to the current thread, such that threads may + ** print their debug messages to different files or streams. + **/ +thread_local FILE* iodebug = nullptr; + +/** + ** @brief Prints to the debug stream @c iodebug + **/ +[[gnu::format(printf, 1, 2)]] +int printf_debug(const char *format, ...) { + int ret = 0; + if (iodebug) { + va_list va; + va_start(va); + ret = vfprintf(iodebug, format, va); + va_end(va); + } + return ret; +} + + + +int main(int argc, char* argv[argc+1]) { + if (argc < 4) return EXIT_FAILURE; + iodebug = stderr; + printf_debug("%g\n", sumIt(3, strtod(argv[1], nullptr), strtod(argv[2], nullptr), strtod(argv[3], nullptr))); + return EXIT_SUCCESS; +} diff --git a/volatile_atomic.c b/volatile_atomic.c new file mode 100644 index 0000000..ecff3c5 --- /dev/null +++ b/volatile_atomic.c @@ -0,0 +1,162 @@ +#include + +/** + ** @brief A simple accumulation in a loop + ** + ** Any decent optimizer should be able to figure out that the return + ** value is @c 100 and that the only side effect is to store the + ** value @c 10 into @c *count. + **/ +unsigned accu(unsigned*restrict count) { + unsigned ret = 0; + unsigned tmp = 0; + (*count) = 0; + while (tmp < 9) { + tmp = (*count)++; + ret += 2*tmp +1; + } + return ret; +} + +/** + ** @brief @c volatile qualification + ** + ** @c volatile forces all stores to be effected. So this has side + ** effects of storing @f$0, \ldots, 10@f$ into @c *count. + **/ +unsigned accuv(unsigned volatile*restrict count) { + unsigned ret = 0; + unsigned tmp = 0; + (*count) = 0; + while (tmp < 9) { + tmp = (*count)++; + ret += 2*tmp +1; + } + return ret; +} + +/** + ** @brief @c _Atomic specification + ** + ** Here, because it is not @c volatile, none of the side effects + ** would have to be made visible before returning. Only there is an + ** ambiguous phrase in the C standard: + ** + ** + ** Atomic read-modify-write operations shall always read the last + ** value (in the modification order) stored before the write + ** associated with the read-modify-write operation. + ** + ** + ** This can be read that for read-modify-write operations the read + ** and write operations *must* be effected. + **/ +unsigned accua(_Atomic(unsigned)*restrict count) { + unsigned ret = 0; + unsigned tmp = 0; + (*count) = 0; + while (tmp < 9) { + tmp = (*count)++; + ret += 2*tmp +1; + } + return ret; +} + +unsigned accuav(_Atomic(unsigned) volatile*restrict count) { + unsigned ret = 0; + unsigned tmp = 0; + (*count) = 0; + while (tmp < 9) { + tmp = (*count)++; + ret += 2*tmp +1; + } + return ret; +} + +unsigned accuar(_Atomic(unsigned)*restrict count) { + unsigned ret = 0; + unsigned tmp = 0; + atomic_store_explicit(count, 0, memory_order_relaxed); + while (tmp < 9) { + tmp = atomic_fetch_add_explicit(count, 1u, memory_order_relaxed); + ret += 2*tmp +1; + } + return ret; +} + +unsigned accuavr(_Atomic(unsigned) volatile*restrict count) { + unsigned ret = 0; + unsigned tmp = 0; + atomic_store_explicit(count, 0, memory_order_relaxed); + while (tmp < 9) { + tmp = atomic_fetch_add_explicit(count, 1u, memory_order_relaxed); + ret += 2*tmp +1; + } + return ret; +} + +unsigned accut(unsigned*restrict count) { + unsigned ret = 0; + unsigned tmp = 0; + (*count) = 0; + while (tmp < 10) { + ret += 2*tmp +1; + (*count) = ++tmp; + } + return ret; +} + +unsigned accutv(unsigned volatile*restrict count) { + unsigned ret = 0; + unsigned tmp = 0; + (*count) = 0; + while (tmp < 10) { + ret += 2*tmp +1; + (*count) = ++tmp; + } + return ret; +} + +unsigned accuta(_Atomic(unsigned)*restrict count) { + unsigned ret = 0; + unsigned tmp = 0; + (*count) = 0; + while (tmp < 10) { + ret += 2*tmp +1; + (*count) = ++tmp; + } + return ret; +} + +unsigned accutav(_Atomic(unsigned) volatile*restrict count) { + unsigned ret = 0; + unsigned tmp = 0; + (*count) = 0; + while (tmp < 10) { + ret += 2*tmp +1; + (*count) = ++tmp; + } + return ret; +} + +unsigned accutar(_Atomic(unsigned)*restrict count) { + unsigned ret = 0; + unsigned tmp = 0; + atomic_store_explicit(count, 0, memory_order_relaxed); + while (tmp < 10) { + ret += 2*tmp +1; + atomic_store_explicit(count, ++tmp, memory_order_relaxed); + } + return ret; +} + +unsigned accutavr(_Atomic(unsigned) volatile*restrict count) { + unsigned ret = 0; + unsigned tmp = 0; + atomic_store_explicit(count, 0, memory_order_relaxed); + while (tmp < 10) { + ret += 2*tmp +1; + atomic_store_explicit(count, ++tmp, memory_order_relaxed); + } + return ret; +} diff --git a/yday.c b/yday.c new file mode 100644 index 0000000..d8b169f --- /dev/null +++ b/yday.c @@ -0,0 +1,41 @@ +#include +#include +#include + +bool leapyear(unsigned year) { + /* All years that are divisible by 4 are leap years, + unless they start a new century, provided they + are not divisible by 400. */ + return !(year % 4) && ((year % 100) || !(year % 400)); +} + +#define DAYS_BEFORE \ +(int const[12]){ \ + [0] = 0, [1] = 31, [2] = 59, [3] = 90, \ + [4] = 120, [5] = 151, [6] = 181, [7] = 212, \ + [8] = 243, [9] = 273, [10] = 304, [11] = 334, \ +} + +struct tm time_set_yday(struct tm t) { + // tm_mdays starts at 1. + t.tm_yday += DAYS_BEFORE[t.tm_mon] + t.tm_mday - 1; + // Takes care of leap years + if ((t.tm_mon > 1) && leapyear(t.tm_year+1900)) + ++t.tm_yday; + return t; +} + +int main(void) { + struct tm today = { + .tm_year = 2019-1900, + .tm_mon = 4-1, + .tm_mday = 3, + .tm_hour = 10, + .tm_min = 0, + .tm_sec = 47, + }; + printf("this year is %d, next year will be %d\n", + today.tm_year+1900, today.tm_year+1900+1); + today = time_set_yday(today); + printf("day of the year is %d\n", today.tm_yday); +} -- cgit v1.2.3