diff options
| author | David Faulkner <[email protected]> | 2026-08-07 23:40:47 -0500 |
|---|---|---|
| committer | David Faulkner <[email protected]> | 2026-08-07 23:40:47 -0500 |
| commit | b3e9e62599532050fc776c5e8f076915b56c2235 (patch) | |
| tree | af252346106a61b18cc6fc6fdbd32e962d096f1c | |
Import official C23 code examples for Modern C (Jens Gustedt, 2024)HEADupstream-importmain
- Add official C source files, Makefile, c23-fallback.h, and LICENSE
- Update README.md with study mirror notice
| -rw-r--r-- | AUTHORS | 1 | ||||
| -rw-r--r-- | B9-detach.c | 234 | ||||
| -rw-r--r-- | B9.c | 224 | ||||
| -rw-r--r-- | LICENSE | 27 | ||||
| -rw-r--r-- | Makefile | 249 | ||||
| -rw-r--r-- | README.md | 29 | ||||
| -rw-r--r-- | Unicode-test.c | 32 | ||||
| -rw-r--r-- | analyze-utf8.c | 57 | ||||
| -rw-r--r-- | anonymous_function.c | 96 | ||||
| -rw-r--r-- | array_or_struct.c | 30 | ||||
| -rw-r--r-- | bad.c | 23 | ||||
| -rw-r--r-- | basic_blocks.c | 203 | ||||
| -rw-r--r-- | basic_blocks2.c | 233 | ||||
| -rw-r--r-- | bitint.c | 48 | ||||
| -rw-r--r-- | build-musl | 120 | ||||
| -rw-r--r-- | c23-fallback.h | 1325 | ||||
| -rw-r--r-- | cat.c | 26 | ||||
| -rw-r--r-- | circular.c | 174 | ||||
| -rw-r--r-- | circular.h | 123 | ||||
| -rw-r--r-- | constexpr.c | 29 | ||||
| -rw-r--r-- | crash.c | 33 | ||||
| -rw-r--r-- | crash.h | 37 | ||||
| -rw-r--r-- | decimal-binary.c | 38 | ||||
| -rw-r--r-- | embed.c | 57 | ||||
| -rw-r--r-- | endianness.c | 19 | ||||
| -rw-r--r-- | enum.c | 93 | ||||
| -rw-r--r-- | esc.c | 12 | ||||
| -rw-r--r-- | esc.h | 156 | ||||
| -rw-r--r-- | euclid.c | 15 | ||||
| -rw-r--r-- | euclid.h | 24 | ||||
| -rw-r--r-- | extern.c | 13 | ||||
| -rw-r--r-- | fibonacci.c | 19 | ||||
| -rw-r--r-- | fibonacci2.c | 26 | ||||
| -rw-r--r-- | fibonacci3.c | 27 | ||||
| -rw-r--r-- | fibonacci4.c | 30 | ||||
| -rw-r--r-- | fibonacci5.c | 30 | ||||
| -rw-r--r-- | fibonacci6.c | 29 | ||||
| -rw-r--r-- | fibonacciCache.c | 50 | ||||
| -rw-r--r-- | fibonacciRet.c | 46 | ||||
| -rw-r--r-- | fp_except.c | 88 | ||||
| -rw-r--r-- | generic.c | 62 | ||||
| -rw-r--r-- | generic.h | 360 | ||||
| -rw-r--r-- | getting-started.c | 24 | ||||
| -rw-r--r-- | heron-expanded.c | 37 | ||||
| -rw-r--r-- | heron.c | 36 | ||||
| -rw-r--r-- | heron_k.c | 231 | ||||
| -rw-r--r-- | heron_k.h | 133 | ||||
| -rw-r--r-- | life.c | 264 | ||||
| -rw-r--r-- | life.h | 83 | ||||
| -rw-r--r-- | lifetime.c | 89 | ||||
| -rw-r--r-- | literals.c | 7 | ||||
| -rw-r--r-- | locale.c | 22 | ||||
| -rw-r--r-- | macro_trace.c | 40 | ||||
| -rw-r--r-- | macro_trace.h | 302 | ||||
| -rw-r--r-- | mbstrings-main.c | 206 | ||||
| -rw-r--r-- | mbstrings.c | 346 | ||||
| -rw-r--r-- | mbstrings.h | 606 | ||||
| -rw-r--r-- | numberline.c | 276 | ||||
| -rw-r--r-- | precision.c | 8 | ||||
| -rw-r--r-- | rationals.c | 218 | ||||
| -rw-r--r-- | rationals.h | 41 | ||||
| -rw-r--r-- | reorder.h | 245 | ||||
| -rw-r--r-- | sequence_point.c | 10 | ||||
| -rw-r--r-- | shadow.c | 20 | ||||
| -rw-r--r-- | sighandler.c | 99 | ||||
| -rw-r--r-- | sighandler.h | 101 | ||||
| -rw-r--r-- | sighandler_test.c | 70 | ||||
| -rw-r--r-- | stats.c | 17 | ||||
| -rw-r--r-- | stats.h | 213 | ||||
| -rw-r--r-- | strcpy.c | 17 | ||||
| -rw-r--r-- | strtoul.c | 149 | ||||
| -rw-r--r-- | swap.c | 144 | ||||
| -rw-r--r-- | tendots.c | 25 | ||||
| -rw-r--r-- | termin.c | 97 | ||||
| -rw-r--r-- | termin.h | 54 | ||||
| -rw-r--r-- | terran.c | 122 | ||||
| -rw-r--r-- | test-c8.c | 54 | ||||
| -rw-r--r-- | test-features.c | 368 | ||||
| -rw-r--r-- | test-lib.c | 46 | ||||
| -rw-r--r-- | test-printf.c | 341 | ||||
| -rw-r--r-- | test-reorder.c | 2 | ||||
| -rw-r--r-- | test-strfrom.c | 26 | ||||
| -rw-r--r-- | test_circular.c | 22 | ||||
| -rw-r--r-- | tgstring.h | 485 | ||||
| -rw-r--r-- | tgstring_test.c | 53 | ||||
| -rw-r--r-- | timespec.c | 118 | ||||
| -rw-r--r-- | va_arg.c | 52 | ||||
| -rw-r--r-- | volatile_atomic.c | 162 | ||||
| -rw-r--r-- | yday.c | 41 |
89 files changed, 10669 insertions, 0 deletions
@@ -0,0 +1 @@ +Jens Gustedt <[email protected]> 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 <stdlib.h> +#include <stdio.h> +#include <stdatomic.h> +//#include <limits.h> +#include "termin.h" +//#include "c23-fallback.h" +//#include <unistd.h> + +// 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); +} @@ -0,0 +1,224 @@ +#include "life.h" +//#include <stdlib.h> +#include <stdio.h> +#include <stdatomic.h> +//#include <limits.h> +#include "termin.h" +//#include "c23-fallback.h" +//#include <unistd.h> + +// 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); +} @@ -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 <uchar.h> +#include <stdio.h> +#include <locale.h> + +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 <stdio.h> + +/** + ** @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 <code>({ + ** })</code> 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 <stdio.h> + +#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; + } +} @@ -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 <ctype.h> +#include <stdio.h> + +/* + * 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 <ctype.h> +#include <setjmp.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> + +/* + * 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 <stdio.h> +#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 <complex.h>, <threads.h>, or + ** <stdatomic.h>. 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 <stdckdint.h> + ** and <stdbit.h>. 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 + ** <stdckdint.h>. + ** + ** Other headers also may be included conditionally, namely + ** <threads.h> and <stdatomic.h>. + ** + ** 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 <limits.h> + +/* Needed for musl */ +/* #ifdef __has_include */ +/* # if __has_include(<stdc-predef.h>) */ +/* # include <stdc-predef.h> */ +/* # 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 <inttypes.h> +#include <setjmp.h> +#include <signal.h> +#include <stdalign.h> +#include <stdarg.h> +#include <stddef.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <wchar.h> + +/**********************************************************************************************/ + +// for call_once +#ifndef ONCE_FLAG_INIT +# ifdef __has_include +# if __has_include(<threads.h>) +# ifndef C23_FALLBACK_SILENT +# warning "including call_once function from <threads.h> instead of <stdlib.h>" +# endif +# include <threads.h> +# 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(<stdatomic.h>) +# include <stdatomic.h> +# 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 <time.h> +[[__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 <stdkdint.h> 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(<stdckdint.h>) +# include <stdckdint.h> +# 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 <stdlib.h>, 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 <complex.h> 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(<stdbit.h>) +# include <stdbit.h> +# 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__)<<stdc_bit_width(__VA_ARGS__)) + +# endif +#endif @@ -0,0 +1,26 @@ +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> + +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 <stdlib.h> +#include <string.h> +#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 <stdio.h> + +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 <math.h> +#include <stdio.h> + +#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𝟏); +} @@ -0,0 +1,33 @@ +#include "c23-fallback.h" +#include <stdio.h> +#include <stdalign.h> +#include <inttypes.h> +#include <complex.h> +#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); + } +} @@ -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 <stdio.h> +#include <float.h> + +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"); +} @@ -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 <stdio.h> +#include <string.h> + +// 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 <stdio.h> +#include <inttypes.h> + +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]); +} @@ -0,0 +1,93 @@ +#include "c23-fallback.h" +#include <stdio.h> + +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 +} @@ -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); +} @@ -0,0 +1,156 @@ +#ifndef ESCAPE_H +#define ESCAPE_H 1 + +#include <stdio.h> + +#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 <stdio.h> +# include <assert.h> + +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 <stdio.h> + +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 <stdio.h> +#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 <stdio.h> +#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 <stdio.h> + +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 <stdio.h> + +/** + ** 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 <stdio.h> + +/** + ** 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 <stdio.h> + +/** + ** 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 <stdio.h> +#include <string.h> + +/* 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 <stdio.h> + +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 <stdio.h> +#include <fenv.h> +#include <float.h> +#ifndef INFINITY +#include <math.h> +#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 <float.h> +#include <stdio.h> +#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 <stdlib.h> /*@\label{include-stdlib}*/ +#include <stdio.h> /*@\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 <stdio.h> + +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; +} @@ -0,0 +1,36 @@ +#include "c23-fallback.h" +#include <stdio.h> + +/* 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 <stdio.h> +/* Math functions usually need "-lm" as compiler argument: */ +#include <math.h> +//#include <assert.h> +#include <float.h> +#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 @@ -0,0 +1,264 @@ +#include "life.h" +#include <stdlib.h> +#include <stdio.h> +#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); +} @@ -0,0 +1,83 @@ +#include "c23-fallback.h" +#include <ctype.h> +#ifndef __STDC_NO_THREADS__ +# include <threads.h> +#else +# error This needs C11 threads, aborting. +#endif +#include <stdatomic.h> + + +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 <stdio.h> +#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 <locale.h> +#include <stdio.h> + +/* 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 <tgmath.h> +#include <stdlib.h> + +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 <stdio.h> +#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 <errno.h> +#include <limits.h> +#include <locale.h> +#include <stdbool.h> +#include <stdio.h> + +#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 + "<unknown>", + "<unknown>" +# 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 <errno.h> +//#include <limits.h> +#include <locale.h> +#include <stdio.h> + +#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 <wctype.h> +#include <ctype.h> + +#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 <code>u"string"</code>. + ** + ** 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 <code>U"string"</code>. + ** + ** 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 + ** <code>strlen(s)+1</code> 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 + ** <code>strlen(s)+1</code>. + ** + ** @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 <code>long double</code>. + ** + ** @see strtold + **/ +long double mbsrtold(char const*restrict s1, mbstate_t*restrict state, char**restrict endptr); + +/** + ** @brief Convert mb string @a s1 to <code>long</code> 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 <code>long long</code> 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 <code>unsigned long</code> + ** 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 <code>unsigned long + ** long</code> 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 <stdlib.h> +#include <stdio.h> +//#include <string.h> + +/** + ** @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 <code>size-1</code> + ** bytes. The <code>'\n'</code> 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 <limits.h> +#include <errno.h> +#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: + ** - <code>EOF</code> (which is negative) if @a stream was not + ** ready to be written to + ** - <code>-EOVERFLOW</code> if more than <code>INT_MAX</code> + ** characters would have to be written, including the case + ** that @a len is greater than <code>INT_MAX</code>. + ** - <code>-EFAULT</code> if @a stream is a null pointer + ** - <code>-EFAULT</code> if @a numb is a null pointer and @a len is not zero + ** - <code>-ENOMEM</code> if a memory error occurred + ** + ** This function leaves <code>errno</code> 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 <stdio.h> + +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 <stdbool.h> +# 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 <string.h> +#include <stdlib.h> + +#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<stdio.h> + +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 <signal.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#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 <stdio.h> +#ifndef __STDC_NO_ATOMIC__ +# include <stdatomic.h> +#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 <signal.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#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; +} @@ -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); @@ -0,0 +1,213 @@ +#include "c23-fallback.h" +#include <tgmath.h> +#include <stdio.h> + +/** + ** @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 <tgmath.h> 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 <string.h> +#include <stdio.h> +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 <errno.h> +//#include <limits.h> +#include <stdio.h> +//#include <stdlib.h> +//#include <string.h> +#include <assert.h> +/* Supposes that lowercase characters are contiguous. */ +static_assert('z'-'a' == 25, + "alphabetic characters not contiguous"); +#include <ctype.h> +/* 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; +} @@ -0,0 +1,144 @@ +#include "c23-fallback.h" +#include <stdio.h> + +#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 <stdio.h> + +/* 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 <termios.h> +#include <stdio.h> +#include <stdbool.h> +#ifndef __STDC_NO_THREADS__ +# include <threads.h> +#endif +#include <ctype.h> +#include <string.h> + +/* 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 <time.h> +#include <stdlib.h> +#include <stdio.h> +#include <math.h> +#include <errno.h> + +/* + 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 <uchar.h> +#include <stdio.h> +#include <string.h> +#include <locale.h> + +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 <stdio.h> + +#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 <stdio.h> + +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 <stdio.h> +#include <inttypes.h> +#include <errno.h> + +#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 <stdlib.h> +#include <stdio.h> + +#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 <stdlib.h> +#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 <inttypes.h> +# include <stdlib.h> +# include <stdio.h> +# include <string.h> +# include <wchar.h> + +/* 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 <stdio.h> + +#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 <stdint.h> +#include <stdio.h> +#include <stddef.h> +#include <time.h> +#include <tgmath.h> + +#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 <stdarg.h> +#include <stdio.h> +//#include <stdlib.h> + +/** + ** @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 <stdatomic.h> + +/** + ** @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: + ** + ** <quote> + ** 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. + ** </quote> + ** + ** 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; +} @@ -0,0 +1,41 @@ +#include <time.h> +#include <stdbool.h> +#include <stdio.h> + +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); +} |
