summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMitya Selivanov <automainint@guattari.tech>2024-08-23 01:11:27 +0200
committerMitya Selivanov <automainint@guattari.tech>2024-08-23 01:11:27 +0200
commit40c43d19bb65542c8406207775def43ee875462d (patch)
tree1ce2e031d90b799d7db5c44839ea739c6216e479
parent7161b6990a8e73d9e79c1bdddacf22ce4068a72d (diff)
downloadreduced_system_layer-40c43d19bb65542c8406207775def43ee875462d.zip
rf64.c, test.c (incomplete)
-rwxr-xr-xexamples/rf64.c252
-rw-r--r--examples/test.c989
-rwxr-xr-xreduced_system_layer.c48
3 files changed, 1269 insertions, 20 deletions
diff --git a/examples/rf64.c b/examples/rf64.c
new file mode 100755
index 0000000..aeba85a
--- /dev/null
+++ b/examples/rf64.c
@@ -0,0 +1,252 @@
+#if 0 /*
+#/ ================================================================
+#/
+#/ rf64.c
+#/
+#/ Probabalistic floating-point numbers
+#/
+#/ ----------------------------------------------------------------
+#/
+#/ (C) 2024 Mitya Selivanov <guattari.tech>, MIT License
+#/
+#/ ================================================================
+#/
+#/ Self-compilation shell script
+#/
+SRC=${0##*./}
+BIN=${SRC%.*}
+gcc \
+ -Wall -Wextra -Werror -pedantic \
+ -Wno-old-style-declaration \
+ -Wno-missing-braces \
+ -Wno-unused-variable \
+ -Wno-unused-but-set-variable \
+ -Wno-unused-parameter \
+ -Wno-overlength-strings \
+ -O3 \
+ -fsanitize=undefined,address,leak -mshstk \
+ -D RF64_TESTS \
+ -o $BIN $SRC && \
+ ./$BIN $@ && rm $BIN
+exit $? # */
+#endif
+
+// ================================================================
+
+#ifndef TYPES_HEADER_GUARD_
+#define TYPES_HEADER_GUARD_
+
+typedef signed char i8;
+typedef signed short i16;
+typedef signed i32;
+typedef signed long long i64;
+typedef unsigned char u8;
+typedef unsigned short u16;
+typedef unsigned u32;
+typedef unsigned long long u64;
+typedef char c8;
+typedef int c32;
+typedef signed char b8;
+typedef float f32;
+typedef double f64;
+
+#endif // TYPES_HEADER_GUARD_
+
+// ================================================================
+
+#ifndef RF64_HEADER_GUARD_
+#define RF64_HEADER_GUARD_
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include <assert.h>
+#include <stdlib.h>
+
+#ifndef RF64_RAND
+static i32 rf64_rand(i32 min, i32 max) {
+ if (max <= min)
+ return min;
+ return min + (rand() % (max - min + 1));
+}
+
+#define RF64_RAND(min, max) rf64_rand((min), (max))
+#endif
+
+#define RF64_EXPONENT_NUM_BITS 30
+#define RF64_MANTISSA_NUM_BITS 32
+#define RF64_EXPONENT_ZERO (1 << (RF64_EXPONENT_NUM_BITS / 2))
+#define RF64_EXPONENT_MAX ((1 << RF64_EXPONENT_NUM_BITS) - 1)
+#define RF64_MANTISSA_MAX 0xffffffffu
+
+typedef struct {
+ u32 infinity : 1;
+ u32 sign : 1;
+ i32 exponent : RF64_EXPONENT_NUM_BITS;
+ u32 mantissa;
+} rf64;
+
+rf64 rf64_from_i64(i64 x);
+i64 i64_from_rf64(rf64 x);
+void rf64_inc(rf64 *x);
+void rf64_dec(rf64 *x);
+void rf64_add(rf64 *x, rf64 y);
+void rf64_sub(rf64 *x, rf64 y);
+void rf64_mul(rf64 *x, rf64 y);
+void rf64_div(rf64 *x, rf64 y);
+
+#endif // RF64_HEADER_GUARD_
+
+#ifndef RF64_HEADER
+#ifndef RF64_IMPL_GUARD_
+#define RF64_IMPL_GUARD_
+
+rf64 rf64_from_i64(i64 x) {
+ rf64 z = {
+ .sign = x < 0,
+ .exponent = RF64_EXPONENT_ZERO,
+ };
+
+ u64 a = z.sign ? -x : x;
+
+ while (a > RF64_MANTISSA_MAX) {
+ u64 loss_comp = (a & 1) == 1 ? RF64_RAND(0, 1) : 0;
+
+ z.exponent += 1;
+
+ a /= 2;
+ a += loss_comp;
+ }
+
+ z.mantissa = (u32) a;
+
+ return z;
+}
+
+i64 i64_from_rf64(rf64 x) {
+ assert(0);
+}
+
+void rf64_inc(rf64 *x) {
+ assert(0);
+}
+
+void rf64_dec(rf64 *x) {
+ assert(x != NULL);
+ if (x == NULL) return;
+
+ assert(0);
+}
+
+void rf64_add(rf64 *x, rf64 y) {
+ assert(x != NULL);
+ if (x == NULL) return;
+
+ assert(0);
+}
+
+void rf64_sub(rf64 *x, rf64 y) {
+ assert(x != NULL);
+ if (x == NULL) return;
+
+ assert(0);
+}
+
+void rf64_mul(rf64 *x, rf64 y) {
+ assert(x != NULL);
+ if (x == NULL) return;
+
+ assert(0);
+}
+
+void rf64_div(rf64 *x, rf64 y) {
+ assert(x != NULL);
+ if (x == NULL) return;
+
+ assert(0);
+}
+
+#ifdef RF64_TESTS
+
+#include <stdio.h>
+#include <string.h>
+
+void report_test(c8 *name, b8 success) {
+ i32 l = 20 - (i32) strlen(name);
+
+ printf("%s %.*s %s\n",
+ name,
+ l < 1 ? 1 : l,
+ "....................",
+ success ? "OK" : "FAIL"
+ );
+}
+
+#define TEST_(x) report_test(#x, test_##x())
+
+b8 test_from_i64() {
+ return 1
+ && rf64_from_i64( 0 ).infinity == 0
+ && rf64_from_i64( 0 ).sign == 0
+ && rf64_from_i64( 0 ).exponent == RF64_EXPONENT_ZERO
+ && rf64_from_i64( 0 ).mantissa == 0
+ && rf64_from_i64( 42 ).infinity == 0
+ && rf64_from_i64( 42 ).sign == 0
+ && rf64_from_i64( 42 ).exponent == RF64_EXPONENT_ZERO
+ && rf64_from_i64( 42 ).mantissa == 42
+ && rf64_from_i64(-42 ).infinity == 0
+ && rf64_from_i64(-42 ).sign == 1
+ && rf64_from_i64(-42 ).exponent == RF64_EXPONENT_ZERO
+ && rf64_from_i64(-42 ).mantissa == 42
+ && rf64_from_i64( 0xffffffff ).infinity == 0
+ && rf64_from_i64( 0xffffffff ).sign == 0
+ && rf64_from_i64( 0xffffffff ).exponent == RF64_EXPONENT_ZERO
+ && rf64_from_i64( 0xffffffff ).mantissa == 0xffffffff
+ && rf64_from_i64(-0xffffffffll ).infinity == 0
+ && rf64_from_i64(-0xffffffffll ).sign == 1
+ && rf64_from_i64(-0xffffffffll ).exponent == RF64_EXPONENT_ZERO
+ && rf64_from_i64(-0xffffffffll ).mantissa == 0xffffffff
+ && rf64_from_i64( 0x100000000ll).infinity == 0
+ && rf64_from_i64( 0x100000000ll).sign == 0
+ && rf64_from_i64( 0x100000000ll).exponent == RF64_EXPONENT_ZERO + 1
+ && rf64_from_i64( 0x100000000ll).mantissa == 0x80000000u
+ && rf64_from_i64(-0x100000000ll).infinity == 0
+ && rf64_from_i64(-0x100000000ll).sign == 1
+ && rf64_from_i64(-0x100000000ll).exponent == RF64_EXPONENT_ZERO + 1
+ && rf64_from_i64(-0x100000000ll).mantissa == 0x80000000u
+ && 1;
+}
+
+b8 test_to_i64() {
+ return 1
+ && i64_from_rf64(rf64_from_i64( 0 )) == 0
+ && i64_from_rf64(rf64_from_i64( 1 )) == 1
+ && i64_from_rf64(rf64_from_i64(-1 )) == -1
+ && i64_from_rf64(rf64_from_i64( 42 )) == 42
+ && i64_from_rf64(rf64_from_i64(-42 )) == -42
+ && i64_from_rf64(rf64_from_i64( 0xffffffff )) == 0xffffffff
+ && i64_from_rf64(rf64_from_i64(-0xffffffff )) == -0xffffffff
+ && i64_from_rf64(rf64_from_i64( 0x100000000ll )) == 0x100000000ll
+ && i64_from_rf64(rf64_from_i64(-0x100000000ll )) == -0x100000000ll
+ && i64_from_rf64(rf64_from_i64( 0x42000000000ll)) == 0x42000000000ll
+ && i64_from_rf64(rf64_from_i64(-0x42000000000ll)) == -0x42000000000ll
+ && 1;
+}
+
+i32 main(i32 argc, c8 **argv) {
+ (void) argc;
+ (void) argv;
+
+ TEST_(from_i64);
+ TEST_(to_i64);
+
+ return 0;
+}
+
+#undef TEST_
+
+#endif // RF64_TESTS
+
+#endif // RF64_IMPL_GUARD_
+#endif // RF64_HEADER
diff --git a/examples/test.c b/examples/test.c
new file mode 100644
index 0000000..c5e17bd
--- /dev/null
+++ b/examples/test.c
@@ -0,0 +1,989 @@
+// ================================================================
+//
+// test.c
+//
+// Unit-testing and microbenchmarks modular library for C
+//
+// ----------------------------------------------------------------
+//
+// Optional settings
+//
+// - MAX_NUM_TESTS
+// - MAX_NUM_TEST_ASSECTIONS
+// - MAX_NUM_BENCHS
+// - MAX_NUM_BENCH_REPEATS
+// - MAX_NUM_BENCH_CYCLES
+// - NUM_BENCH_REPEATS_DEFAULT_1
+// - NUM_BENCH_REPEATS_DEFAULT_2
+//
+// ----------------------------------------------------------------
+//
+// Usage
+//
+// - Define a unique TEST_FILE for each file to avoid name
+// collisions.
+//
+// Example
+//
+// // foo.test.c
+// #define TEST_FILE foo // This is required if you want to
+// // use multiple test files.
+// #define TEST_HEADER // Do not include the implementation.
+// #include "test.c"
+// TEST("foo") {
+// REQUIRE(1);
+// REQUIRE_EQ(2 + 2, 4);
+// }
+//
+// // main.c
+// #include "test.c"
+// int main(int argc, char **argv) {
+// return run_tests(argc, argv);
+// }
+//
+// ----------------------------------------------------------------
+//
+// (C) 2024 Mitya Selivanov <guattari.tech>, MIT License
+//
+// ================================================================
+
+#ifndef TYPES_HEADER_GUARD_
+#define TYPES_HEADER_GUARD_
+
+typedef signed char i8;
+typedef signed short i16;
+typedef signed i32;
+typedef signed long long i64;
+typedef unsigned char u8;
+typedef unsigned short u16;
+typedef unsigned u32;
+typedef unsigned long long u64;
+typedef char c8;
+typedef int c32;
+typedef signed char b8;
+typedef float f32;
+typedef double f64;
+
+#endif // TYPES_HEADER_GUARD_
+
+// ================================================================
+
+#ifndef TEST_HEADER_GUARD_
+#define TEST_HEADER_GUARD_
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#ifdef __GNUC__
+# pragma GCC diagnostic ignored "-Wunused-value"
+# pragma GCC diagnostic ignored "-Wunused-parameter"
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stddef.h>
+#include <stdint.h>
+
+#ifndef TEST_FILE
+#define TEST_FILE test
+#endif
+
+#ifndef MAX_NUM_TESTS
+#define MAX_NUM_TESTS 0x1000
+#endif
+
+#ifndef MAX_NUM_TEST_ASSECTIONS
+#define MAX_NUM_TEST_ASSECTIONS 0x50
+#endif
+
+#ifndef MAX_NUM_BENCHS
+#define MAX_NUM_BENCHS 200
+#endif
+
+#ifndef MAX_NUM_BENCH_REPEATS
+#define MAX_NUM_BENCH_REPEATS 4000
+#endif
+
+#ifndef MAX_NUM_BENCH_CYCLES
+#define MAX_NUM_BENCH_CYCLES 40
+#endif
+
+#ifndef NUM_BENCH_REPEATS_DEFAULT_1
+#define NUM_BENCH_REPEATS_DEFAULT_1 40
+#endif
+
+#ifndef NUM_BENCH_REPEATS_DEFAULT_2
+#define NUM_BENCH_REPEATS_DEFAULT_2 400
+#endif
+
+typedef void (*test_report_fn)(i32 test_index, i32 line, i64 value, i64 expected);
+typedef void (*test_run_fn) (i32 test_index_, test_report_fn test_report_fn_);
+
+typedef struct {
+ c8 const *test_name;
+ c8 const *test_file;
+ test_run_fn test_fn;
+ i32 assertions;
+ i32 line [MAX_NUM_TEST_ASSECTIONS];
+ i32 status [MAX_NUM_TEST_ASSECTIONS];
+ i64 value [MAX_NUM_TEST_ASSECTIONS];
+ i64 expected[MAX_NUM_TEST_ASSECTIONS];
+ i32 signal;
+} Test_Case;
+
+typedef struct {
+ i32 size;
+ Test_Case v[MAX_NUM_TESTS];
+} Tests_List;
+
+extern Tests_List tests_list;
+
+#define TEST_CONCAT4_(a, b, c, d) a##b##c##d
+#define TEST_CONCAT3_(a, b, c) TEST_CONCAT4_(a, b, _, c)
+
+#ifdef __cplusplus
+# define TEST_ON_START_(f) \
+ static void f(void); \
+ static i32 TEST_CONCAT3_(_test_init_, __LINE__, f) = (f(), 0); \
+ static void f(void)
+#else
+# ifdef _MSC_VER
+# pragma section(".CRT$XCU", read)
+# define TEST_ON_START_2_(f, p) \
+ static void f(void); \
+ __declspec(allocate(".CRT$XCU")) void (*f##_)(void) = f; \
+ __pragma(comment(linker, "/include:" p #f "_")) static void f(void)
+# ifdef _WIN64
+# define TEST_ON_START_(f) TEST_ON_START_2_(f, "")
+# else
+# define TEST_ON_START_(f) TEST_ON_START_2_(f, "_")
+# endif
+# else
+# define TEST_ON_START_(f) \
+ static void f(void) __attribute__((constructor)); \
+ static void f(void)
+# endif
+#endif
+
+void test_register(c8 const *name, c8 const *file, test_run_fn fn);
+
+#define TEST(name) \
+ static void TEST_CONCAT3_(test_run_, TEST_FILE, __LINE__)(i32, test_report_fn); \
+ TEST_ON_START_(TEST_CONCAT3_(test_case_, TEST_FILE, __LINE__)) { \
+ test_register(name, __FILE__, TEST_CONCAT3_(test_run_, TEST_FILE, __LINE__)); \
+ } \
+ static void TEST_CONCAT3_(test_run_, TEST_FILE, __LINE__) \
+ (i32 test_index_, test_report_fn test_report_fn_)
+
+#define REQUIRE(...) test_report_fn_(test_index_, __LINE__, (__VA_ARGS__), 1)
+#define REQUIRE_EQ(...) test_report_fn_(test_index_, __LINE__, __VA_ARGS__)
+
+i32 run_tests(i32 argc, c8 **argv);
+
+// ================================================================
+
+typedef void (*bench_set_repeats_limit_fn)(i32 bench_index, i32 repeats_limit);
+typedef i32 (*bench_loop_fn) (i32 bench_index);
+typedef void (*bench_begin_fn) (i32 bench_index);
+typedef void (*bench_end_fn) (i32 bench_index);
+
+typedef void (*bench_run_fn)(
+ i32 bench_index_,
+ bench_set_repeats_limit_fn bench_set_repeats_limit_,
+ bench_loop_fn bench_loop_,
+ bench_begin_fn bench_begin_,
+ bench_end_fn bench_end_
+);
+
+typedef struct {
+ c8 const *bench_name;
+ c8 const *bench_file;
+ bench_run_fn bench_fn;
+ i64 sec [MAX_NUM_BENCH_REPEATS];
+ i32 nsec [MAX_NUM_BENCH_REPEATS];
+ i64 duration_nsec [MAX_NUM_BENCH_REPEATS];
+ i64 duration_sorted_nsec[MAX_NUM_BENCH_REPEATS];
+ i32 repeats;
+ i32 cycles_size;
+ i32 cycles[MAX_NUM_BENCH_CYCLES];
+ i32 cycle;
+ i32 signal;
+ i32 ready;
+} Benchmark;
+
+typedef struct {
+ i32 size;
+ Benchmark v[MAX_NUM_BENCHS];
+} Benchs_List;
+
+extern Benchs_List benchs_list;
+
+void bench_register(c8 const *name, c8 const *file, bench_run_fn fn);
+
+#define BENCHMARK(name) \
+ static void TEST_CONCAT3_(bench_run_, TEST_FILE, __LINE__) \
+ (i32, bench_set_repeats_limit_fn, bench_loop_fn, bench_begin_fn, bench_end_fn); \
+ TEST_ON_START_(TEST_CONCAT3_(benchmark_, TEST_FILE, __LINE__)) { \
+ bench_register(name, __FILE__, TEST_CONCAT3_(bench_run_, TEST_FILE, __LINE__)); \
+ } \
+ static void TEST_CONCAT3_(bench_run_, TEST_FILE, __LINE__)( \
+ i32 bench_index_, \
+ bench_set_repeats_limit_fn bench_set_repeats_limit_, \
+ bench_loop_fn bench_loop_, \
+ bench_begin_fn bench_begin_, \
+ bench_end_fn bench_end_ \
+ )
+
+#define BENCHMARK_REPEAT(repeats_limit_) \
+ bench_set_repeats_limit_(bench_index_, repeats_limit_)
+
+#define BENCHMARK_BEGIN \
+ while (bench_loop_(bench_index_)) { \
+ bench_begin_(bench_index_); {
+
+#define BENCHMARK_END \
+ } bench_end_(bench_index_); }
+
+// FIXME
+// Does this work reliably?
+//
+#define DO_NOT_OPTIMIZE(x) \
+ do { \
+ volatile void *bench_ptr_ = &(x); \
+ (void) bench_ptr_; \
+ } while (0)
+
+i32 run_benchmarks(i32 argc, c8 **argv);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // TEST_HEADER_GUARD_
+
+// ================================================================
+//
+// Implementation
+//
+// ================================================================
+
+#ifndef TEST_HEADER
+#ifndef TEST_IMPL_GUARD_
+#define TEST_IMPL_GUARD_
+
+#include <setjmp.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+enum {
+ white_,
+ blue_,
+ light_,
+ yellow_,
+ red_,
+ green_
+};
+
+static c8 const *color_codes_[] = {
+ "\x1b[38m", "\x1b[34m",
+ "\x1b[37m", "\x1b[33m",
+ "\x1b[31m", "\x1b[32m"
+};
+
+static i32 print_color_(i32 c) {
+ return printf("%s", color_codes_[c]);
+}
+
+static i32 signums_[] = { SIGINT, SIGILL, SIGABRT, SIGFPE, SIGSEGV, SIGTERM };
+
+static c8 const *signames_[64];
+
+static void signames_init_(void) {
+ memset(signames_, 0, sizeof signames_);
+ signames_[SIGINT] = "Interactive attention signal";
+ signames_[SIGILL] = "Illegal instruction";
+ signames_[SIGABRT] = "Abnormal termination";
+ signames_[SIGFPE] = "Erroneous arithmetic operation";
+ signames_[SIGSEGV] = "Invalid access to storage";
+ signames_[SIGTERM] = "Termination request";
+}
+
+Tests_List tests_list = { 0 };
+
+static void report_(i32 i, i32 line, i64 value, i64 expected) {
+ i32 n = tests_list.v[i].assertions++;
+
+ if (n >= MAX_NUM_TEST_ASSECTIONS)
+ return;
+
+ tests_list.v[i].line[n] = line;
+ tests_list.v[i].status[n] = value == expected;
+ tests_list.v[i].value[n] = value;
+ tests_list.v[i].expected[n] = expected;
+}
+
+static i64 ns_to_ms(i64 ns) {
+ return (ns + 500000) / 1000000;
+}
+
+static i64 sec_to_ms(int64_t sec) {
+ return 1000 * sec;
+}
+
+void test_register(c8 const *name, c8 const *file, test_run_fn fn) {
+ i32 n = tests_list.size++;
+ if (n < MAX_NUM_TESTS) {
+ tests_list.v[n].test_fn = fn;
+ tests_list.v[n].test_name = name;
+ tests_list.v[n].test_file = file;
+ tests_list.v[n].assertions = 0;
+ }
+}
+
+static jmp_buf test_restore_execution;
+
+static void test_handle_signal(i32 signum) {
+ longjmp(test_restore_execution, signum);
+}
+
+static void test_setup_signals() {
+ for (u32 i = 0; i < sizeof signums_ / sizeof *signums_; i++)
+ signal(signums_[i], test_handle_signal);
+}
+
+static i32 run_test_(volatile i32 i) {
+ i32 signum = setjmp(test_restore_execution);
+
+ if (signum != 0) {
+ tests_list.v[i].signal = signum;
+ return 0;
+ }
+
+ tests_list.v[i].test_fn(i, report_);
+ return 1;
+}
+
+i32 run_tests(i32 argc, c8 **argv) {
+ signames_init_();
+
+ i32 success_count = 0;
+ i32 fail_assertion_count = 0;
+ i32 total_assertion_count = 0;
+ i32 status = 0;
+ i32 quiet = 0;
+ i32 no_color = 0;
+ i32 line_width = 20;
+ i32 carriage_return = 1;
+
+ i32 i, j;
+
+ c8 const *specific_test = NULL;
+
+ test_setup_signals();
+
+ for (i = 0; i < argc; i++)
+ if (strcmp("--no-term-color", argv[i]) == 0)
+ no_color = 1;
+ else if (strcmp("--no-carriage-return", argv[i]) == 0)
+ carriage_return = 0;
+ else if (strcmp("--quiet", argv[i]) == 0)
+ quiet = 1;
+ else if (strcmp("--match", argv[i]) == 0)
+ specific_test = argv[++i];
+
+ quiet && (no_color = 1);
+
+ if (specific_test != NULL) {
+ no_color || print_color_(light_);
+ quiet || printf("Run tests matching ");
+ no_color || print_color_(white_);
+ quiet || printf("*%s*", specific_test);
+ no_color || print_color_(light_);
+ quiet || printf("\n\n");
+ }
+
+ c8 const *file = NULL;
+ i64 file_root = -1;
+ i32 tests_total = 0;
+
+ for (i = 0; i < tests_list.size && i < MAX_NUM_TESTS;
+ i++) {
+ if (specific_test != NULL &&
+ strstr(tests_list.v[i].test_name, specific_test) == NULL)
+ continue;
+ tests_total++;
+ i32 l = 2 + (int) strlen(tests_list.v[i].test_name);
+ if (line_width < l)
+ line_width = l;
+ }
+
+ if (tests_total > 0) {
+ c8 const *s = tests_list.v[0].test_file;
+
+ for (j = 1; j < tests_list.size && j < MAX_NUM_TESTS;
+ j++) {
+ if (specific_test != NULL &&
+ strstr(tests_list.v[j].test_name, specific_test) ==
+ NULL)
+ continue;
+ if (strcmp(s, tests_list.v[j].test_file) == 0)
+ continue;
+ i32 k = 0;
+ for (;
+ s[k] != '\0' && tests_list.v[j].test_file[k] != '\0' &&
+ s[k] == tests_list.v[j].test_file[k];
+ k++) { }
+ if (file_root == -1 || file_root > k)
+ file_root = k;
+ }
+
+ if (file_root == -1) {
+ for (i = 0; s[i] != '\0'; i++)
+ if (s[i] == '/' || s[i] == '\\')
+ file_root = i + 1;
+ }
+ }
+
+ for (i = 0; i < tests_list.size && i < MAX_NUM_TESTS; i++) {
+ if (specific_test != NULL && strstr(tests_list.v[i].test_name, specific_test) == NULL)
+ continue;
+ if (file == NULL || strcmp(file, tests_list.v[i].test_file) != 0) {
+ if (file != NULL)
+ quiet || printf("\n");
+ file = tests_list.v[i].test_file;
+ no_color || print_color_(blue_);
+ quiet || printf("* ");
+ no_color || print_color_(white_);
+ quiet || printf("%s\n", file + file_root);
+ }
+
+ !carriage_return || no_color || print_color_(yellow_);
+ carriage_return || no_color || print_color_(light_);
+ quiet || printf("` %s ", tests_list.v[i].test_name);
+ !carriage_return || quiet || printf("\r");
+ quiet || fflush(stdout);
+
+ struct timespec begin, end;
+ timespec_get(&begin, TIME_UTC);
+
+ i32 test_status = run_test_(i);
+
+ timespec_get(&end, TIME_UTC);
+ i32 duration = (i32) (ns_to_ms(end.tv_nsec - begin.tv_nsec) + sec_to_ms(end.tv_sec - begin.tv_sec));
+
+ for (j = 0; j < tests_list.v[i].assertions && j < MAX_NUM_TEST_ASSECTIONS; j++)
+ if (tests_list.v[i].status[j] == 0) {
+ fail_assertion_count++;
+ test_status = 0;
+ }
+
+ if (tests_list.v[i].assertions > MAX_NUM_TEST_ASSECTIONS)
+ test_status = 0;
+
+ total_assertion_count += tests_list.v[i].assertions;
+
+ !carriage_return || no_color || print_color_(light_);
+ !carriage_return || quiet || printf("` %s ", tests_list.v[i].test_name);
+
+ i32 l = (i32) strlen(tests_list.v[i].test_name);
+ quiet || printf("%*c", line_width - l, ' ');
+
+ if (test_status == 0) {
+ no_color || print_color_(red_);
+ quiet || printf("FAIL");
+ no_color || print_color_(light_);
+ duration == 0 || quiet || printf(" %d ms", duration);
+ quiet || printf("\n");
+ status = 1;
+ } else {
+ no_color || print_color_(green_);
+ quiet || printf("OK");
+ no_color || print_color_(light_);
+ duration == 0 || quiet || printf(" %d ms", duration);
+ quiet || printf("\n");
+ success_count++;
+ }
+
+ quiet || fflush(stdout);
+ }
+
+ no_color || print_color_(white_);
+ quiet || printf("\n%d of %d tests passed.\n", success_count, tests_total);
+ quiet || printf("%d of %d assertions passed.\n\n", total_assertion_count - fail_assertion_count, total_assertion_count);
+ no_color || print_color_(light_);
+
+ if (!quiet && status != 0) {
+ i32 have_report_s = 0;
+
+ for (i = 0; i < tests_list.size && i < MAX_NUM_TESTS; i++) {
+ if (specific_test != NULL && strstr(tests_list.v[i].test_name, specific_test) == NULL)
+ continue;
+ if (tests_list.v[i].signal != 0) {
+ i32 signum = tests_list.v[i].signal;
+ if (signum >= 0 && signum < (int) (sizeof signames_ / sizeof *signames_) && signames_[signum] != NULL) {
+ no_color || print_color_(light_);
+ printf("Signal \"%s\" (%d) for \"", signames_[signum], signum);
+ no_color || print_color_(white_);
+ printf("%s", tests_list.v[i].test_name);
+ no_color || print_color_(light_);
+ printf("\" in \"");
+ no_color || print_color_(white_);
+
+ printf("%s", tests_list.v[i].test_file + file_root);
+ no_color || print_color_(light_);
+ printf("\"!\n");
+ } else {
+ no_color || print_color_(light_);
+ printf("Unknown signal (%d) for \"", signum);
+ no_color || print_color_(white_);
+ printf("%s", tests_list.v[i].test_name);
+ no_color || print_color_(light_);
+ printf("\" in \"");
+ no_color || print_color_(white_);
+ printf("%s", tests_list.v[i].test_file + file_root);
+ no_color || print_color_(light_);
+ printf("\"!\n");
+ }
+ have_report_s = 1;
+ }
+ if (tests_list.v[i].assertions >
+ MAX_NUM_TEST_ASSECTIONS) {
+ no_color || print_color_(light_);
+ printf("Too many assertions for \"");
+ no_color || print_color_(white_);
+ printf("%s", tests_list.v[i].test_name);
+ no_color || print_color_(light_);
+ printf("\" in \"");
+ no_color || print_color_(white_);
+ printf("%s", tests_list.v[i].test_file + file_root);
+ no_color || print_color_(light_);
+ printf("\"!\n");
+ have_report_s = 1;
+ }
+ }
+
+ have_report_s && printf("\n");
+ }
+
+ if (!quiet && status != 0) {
+ for (i = 0; i < tests_list.size && i < MAX_NUM_TESTS; i++) {
+ if (specific_test != NULL && strstr(tests_list.v[i].test_name, specific_test) == NULL)
+ continue;
+
+ if (tests_list.v[i].assertions <= MAX_NUM_TEST_ASSECTIONS)
+ for (j = 0; j < tests_list.v[i].assertions; j++)
+ if (!tests_list.v[i].status[j]) {
+ no_color || print_color_(light_);
+ printf("Assertion on line ");
+ no_color || print_color_(white_);
+ printf("%d", tests_list.v[i].line[j]);
+ no_color || print_color_(light_);
+ printf(" in \"");
+ no_color || print_color_(white_);
+ printf("%s", tests_list.v[i].test_file + file_root);
+ no_color || print_color_(light_);
+ printf("\" failed.\n");
+ no_color || print_color_(red_);
+ printf(" -> ");
+ no_color || print_color_(light_);
+ printf("Got wrong value ");
+ no_color || print_color_(white_);
+ printf("%10lld", (i64) tests_list.v[i].value[j]);
+ no_color || print_color_(light_);
+ printf(" (");
+ no_color || print_color_(white_);
+ printf("0x%08llx", (u64) tests_list.v[i].value[j]);
+ no_color || print_color_(light_);
+ printf(")\n");
+ no_color || print_color_(green_);
+ printf(" -> ");
+ no_color || print_color_(light_);
+ printf("Expected value ");
+ no_color || print_color_(white_);
+ printf("%10lld", (i64) tests_list.v[i].expected[j]);
+ no_color || print_color_(light_);
+ printf(" (");
+ no_color || print_color_(white_);
+ printf("0x%08llx", (u64) tests_list.v[i].expected[j]);
+ no_color || print_color_(light_);
+ printf(")\n\n");
+ }
+ }
+ }
+
+ if (tests_list.size > MAX_NUM_TESTS) {
+ no_color || print_color_(light_);
+ quiet || printf("Too many tests!\n\n");
+ status = 1;
+ }
+
+ if (status == 0) {
+ no_color || print_color_(green_);
+ quiet || printf("OK\n");
+ } else {
+ no_color || print_color_(red_);
+ quiet || printf("FAILED\n");
+ }
+
+ no_color || print_color_(light_);
+ quiet || printf("\n");
+ return status;
+}
+
+// ================================================================
+
+Benchs_List benchs_list = { 0 };
+
+static void bench_set_repeats_limit(i32 i, i32 repeats_limit) {
+ if (benchs_list.v[i].ready)
+ return;
+ if (benchs_list.v[i].cycles_size >= MAX_NUM_BENCH_CYCLES)
+ return;
+ benchs_list.v[i].cycles[benchs_list.v[i].cycles_size] = repeats_limit;
+ benchs_list.v[i].cycles_size++;
+}
+
+static i32 bench_loop(i32 i) {
+ if (!benchs_list.v[i].ready)
+ return 0;
+ return benchs_list.v[i].repeats < benchs_list.v[i].cycles[benchs_list.v[i].cycle];
+}
+
+static void bench_begin(i32 i) {
+ i32 n = benchs_list.v[i].repeats++;
+
+ if (n >= MAX_NUM_BENCH_REPEATS)
+ return;
+
+ struct timespec tv;
+ timespec_get(&tv, TIME_UTC);
+
+ benchs_list.v[i].sec[n] = (int64_t) tv.tv_sec;
+ benchs_list.v[i].nsec[n] = (int64_t) tv.tv_nsec;
+}
+
+static void bench_end(i32 i) {
+ i32 n = benchs_list.v[i].repeats - 1;
+
+ if (n < 0 || n >= MAX_NUM_BENCH_REPEATS)
+ return;
+
+ struct timespec tv;
+ timespec_get(&tv, TIME_UTC);
+
+ i64 sec = ((i64) tv.tv_sec) - benchs_list.v[i].sec[n];
+ i64 nsec = ((i64) tv.tv_nsec) - benchs_list.v[i].nsec[n];
+
+ benchs_list.v[i].duration_nsec[n] = sec * 1000000000 + nsec;
+}
+
+void bench_register(c8 const *name, c8 const *file, bench_run_fn fn) {
+ i32 n = benchs_list.size++;
+ if (n < MAX_NUM_BENCHS) {
+ Benchmark *bench = benchs_list.v + n;
+
+ bench->bench_fn = fn;
+ bench->bench_name = name;
+ bench->bench_file = file;
+ bench->cycles_size = 0;
+ bench->ready = 0;
+ }
+}
+
+static void bench_setup_signals() {
+ for (u32 i = 0; i < sizeof signums_ / sizeof *signums_; i++)
+ signal(signums_[i], test_handle_signal);
+}
+
+static i32 run_bench_(volatile i32 i) {
+ i32 signum = setjmp(test_restore_execution);
+
+ if (signum != 0) {
+ benchs_list.v[i].signal = signum;
+ return 0;
+ }
+
+ benchs_list.v[i].bench_fn(i, bench_set_repeats_limit, bench_loop, bench_begin, bench_end);
+ return 1;
+}
+
+static i32 compare_64_(void const *x_, void const *y_) {
+ i64 const *x = (i64 const *) x_;
+ i64 const *y = (i64 const *) y_;
+ return *x - *y;
+}
+
+static i32 compare_32_(void const *x_, void const *y_) {
+ i32 const *x = (i32 const *) x_;
+ i32 const *y = (i32 const *) y_;
+ return *x - *y;
+}
+
+i32 run_benchmarks(i32 argc, c8 **argv) {
+ i32 success_count = 0;
+ i32 status = 0;
+ i32 no_color = 0;
+ i32 line_width = 20;
+ i32 carriage_return = 1;
+
+ c8 const *specific_bench = NULL;
+
+ bench_setup_signals();
+
+ for (i32 i = 0; i < argc; i++)
+ if (strcmp("--no-term-color", argv[i]) == 0)
+ no_color = 1;
+ else if (strcmp("--no-carriage-return", argv[i]) == 0)
+ carriage_return = 0;
+ else if (strcmp("--match", argv[i]) == 0)
+ specific_bench = argv[++i];
+
+ if (specific_bench != NULL) {
+ no_color || print_color_(light_);
+ printf("Run benchmarks matching ");
+ no_color || print_color_(white_);
+ printf("*%s*", specific_bench);
+ no_color || print_color_(light_);
+ printf("\n\n");
+ }
+
+ c8 const *file = NULL;
+ i64 file_root = -1;
+ i32 benchs_total = 0;
+
+ for (i32 i = 0; i < benchs_list.size && i < MAX_NUM_BENCHS; i++) {
+ if (specific_bench != NULL && strstr(benchs_list.v[i].bench_name, specific_bench) == NULL)
+ continue;
+ benchs_total++;
+ i32 l = 2 + (int) strlen(benchs_list.v[i].bench_name);
+ if (line_width < l)
+ line_width = l;
+ }
+
+ if (benchs_total > 0) {
+ c8 const *s = benchs_list.v[0].bench_file;
+
+ for (i32 j = 1; j < benchs_list.size && j < MAX_NUM_BENCHS; j++) {
+ Benchmark *bench = benchs_list.v + j;
+
+ if (specific_bench != NULL && strstr(bench->bench_name, specific_bench) == NULL)
+ continue;
+ if (strcmp(s, bench->bench_file) == 0)
+ continue;
+ i32 k = 0;
+ for (; s[k] != '\0' && bench->bench_file[k] != '\0' && s[k] == bench->bench_file[k]; k++) { }
+ if (file_root == -1 || file_root > k)
+ file_root = k;
+ }
+
+ if (file_root == -1) {
+ for (i32 i = 0; s[i] != '\0'; i++)
+ if (s[i] == '/' || s[i] == '\\')
+ file_root = i + 1;
+ }
+ }
+
+ no_color || print_color_(blue_);
+ printf("# ");
+ no_color || print_color_(light_);
+ printf("BENCHMARK");
+ printf("%*c", line_width - 9, ' ');
+ no_color || print_color_(green_);
+ printf(" LOW ");
+ no_color || print_color_(light_);
+ printf("|");
+ no_color || print_color_(blue_);
+ printf(" MEDIAN ");
+ no_color || print_color_(light_);
+ printf("|");
+ no_color || print_color_(yellow_);
+ printf(" HIGH\n");
+ no_color || print_color_(light_);
+ printf(" (in microseconds)\n\n");
+
+ // Prepare cycles.
+ //
+
+ for (i32 i = 0; i < benchs_list.size && i < MAX_NUM_BENCHS; i++) {
+ Benchmark *bench = benchs_list.v + i;
+
+ if (specific_bench != NULL && strstr(bench->bench_name, specific_bench) == NULL)
+ continue;
+
+ run_bench_(i);
+
+ if (bench->cycles_size == 0) {
+ bench->cycles_size = 2;
+ bench->cycles[0] = NUM_BENCH_REPEATS_DEFAULT_1;
+ bench->cycles[1] = NUM_BENCH_REPEATS_DEFAULT_2;
+ }
+
+ qsort(bench->cycles, bench->cycles_size, sizeof *bench->cycles, compare_32_);
+
+ benchs_list.v[i].ready = 1;
+ }
+
+ // Run cycles.
+ //
+
+ for (i32 cycle = 0; cycle < MAX_NUM_BENCH_CYCLES; cycle++) {
+ // Prepare cycle.
+ //
+
+ i32 cycles_done = 1;
+
+ for (i32 i = 0; i < benchs_list.size && i < MAX_NUM_BENCHS; i++) {
+ Benchmark *bench = benchs_list.v + i;
+
+ if (specific_bench != NULL && strstr(bench->bench_name, specific_bench) == NULL)
+ continue;
+ if (cycle >= bench->cycles_size)
+ continue;
+
+ bench->repeats = 0;
+ bench->cycle = cycle;
+ cycles_done = 0;
+ }
+
+ if (cycles_done)
+ break;
+
+ // Run benchmarks.
+ //
+
+ for (i32 i = 0; i < benchs_list.size && i < MAX_NUM_BENCHS; i++) {
+ Benchmark *bench = benchs_list.v + i;
+
+ if (specific_bench != NULL && strstr(bench->bench_name, specific_bench) == NULL)
+ continue;
+ if (cycle >= bench->cycles_size)
+ continue;
+
+ if (file == NULL || strcmp(file, bench->bench_file) != 0) {
+ if (file != NULL)
+ printf("\n");
+ file = bench->bench_file;
+ no_color || print_color_(blue_);
+ printf("* ");
+ no_color || print_color_(white_);
+ printf("%s\n", file + file_root);
+ }
+
+ !carriage_return || no_color || print_color_(yellow_);
+ carriage_return || no_color || print_color_(light_);
+ printf("` %s ", bench->bench_name);
+ !carriage_return || printf("\r");
+ fflush(stdout);
+
+ i32 bench_status = run_bench_(i);
+
+ if (bench->repeats > MAX_NUM_BENCH_REPEATS)
+ bench_status = 0;
+
+ !carriage_return || no_color || print_color_(light_);
+ !carriage_return || printf("` %s ", bench->bench_name);
+
+ i32 l = (i32) strlen(bench->bench_name);
+ printf("%*c", line_width - l, ' ');
+
+ if (bench->repeats <= 0) {
+ no_color || print_color_(yellow_);
+ printf(" 0 runs\n");
+ success_count++;
+ } else if (bench_status == 0) {
+ no_color || print_color_(red_);
+ printf(" FAIL\n");
+ status = 1;
+ } else {
+ i32 repeats = bench->repeats;
+
+ memcpy(bench->duration_sorted_nsec, bench->duration_nsec, repeats * sizeof *bench->duration_sorted_nsec);
+ qsort(bench->duration_sorted_nsec, repeats, sizeof *bench->duration_sorted_nsec, compare_64_);
+
+ i64 average = bench->duration_sorted_nsec[repeats / 2];
+ i64 floor = bench->duration_sorted_nsec[repeats / 20];
+ i64 roof = bench->duration_sorted_nsec[repeats - repeats / 20 - 1];
+
+ no_color || print_color_(white_);
+ printf("%-9g", (f64) floor * 0.001);
+ no_color || print_color_(light_);
+ printf("| ");
+ no_color || print_color_(white_);
+ printf("%-9g", (f64) average * 0.001);
+ no_color || print_color_(light_);
+ printf("| ");
+ no_color || print_color_(white_);
+ printf("%-9g", (f64) roof * 0.001);
+ no_color || print_color_(light_);
+ printf(" %d runs\n", repeats);
+ success_count++;
+ }
+ }
+ }
+
+ printf("\n");
+
+ if (status != 0) {
+ for (i32 i = 0; i < benchs_list.size && i < MAX_NUM_BENCHS; i++) {
+ Benchmark *bench = benchs_list.v + i;
+
+ if (specific_bench != NULL && strstr(bench->bench_name, specific_bench) == NULL)
+ continue;
+ if (bench->signal != 0) {
+ i32 signum = bench->signal;
+ if (signum >= 0 && signum < (i32) (sizeof signames_ / sizeof *signames_) && signames_[signum] != NULL) {
+ no_color || print_color_(light_);
+ printf("Signal \"%s\" (%d) for \"", signames_[signum], signum);
+ no_color || print_color_(white_);
+ printf("%s", bench->bench_name);
+ no_color || print_color_(light_);
+ printf("\" in \"");
+ no_color || print_color_(white_);
+ printf("%s", bench->bench_file + file_root);
+ no_color || print_color_(light_);
+ printf("\"!.\n");
+ } else {
+ no_color || print_color_(light_);
+ printf("Unknown signal (%d) for \"", signum);
+ no_color || print_color_(white_);
+ printf("%s", bench->bench_name);
+ no_color || print_color_(light_);
+ printf("\" in \"");
+ no_color || print_color_(white_);
+ printf("%s", bench->bench_file + file_root);
+ no_color || print_color_(light_);
+ printf("\"!.\n");
+ }
+ }
+ }
+
+ printf("\n");
+ }
+
+ if (benchs_list.size > MAX_NUM_BENCHS) {
+ no_color || print_color_(light_);
+ printf("Too many benchmarks!\n\n");
+ status = 1;
+ }
+
+ if (status == 0) {
+ no_color || print_color_(green_);
+ printf("DONE\n");
+ } else {
+ no_color || print_color_(red_);
+ printf("DONE WITH ERRORS\n");
+ }
+
+ no_color || print_color_(light_);
+ printf("\n");
+ return status;
+}
+
+#endif // TEST_IMPL_GUARD_
+#endif // TEST_HEADER
diff --git a/reduced_system_layer.c b/reduced_system_layer.c
index 9318098..f81266d 100755
--- a/reduced_system_layer.c
+++ b/reduced_system_layer.c
@@ -66,29 +66,15 @@ gcc \
./$BIN $@ && rm $BIN
exit $? # */
#endif
-// ================================================================
-//
-// Include guards
-//
-// ================================================================
-
-#ifndef REDUCED_SYSTEM_LAYER_HEADER_GUARD_
-#define REDUCED_SYSTEM_LAYER_HEADER_GUARD_
// ================================================================
//
-// Basic declarations
+// Types
//
// ================================================================
-#define _GNU_SOURCE
-
-#include <time.h>
-#include <math.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <assert.h>
+#ifndef TYPES_HEADER_GUARD_
+#define TYPES_HEADER_GUARD_
typedef signed char i8;
typedef signed short i16;
@@ -104,6 +90,28 @@ typedef signed char b8;
typedef float f32;
typedef double f64;
+#endif // TYPES_HEADER_GUARD_
+
+// ================================================================
+//
+// Basic declarations
+//
+// ================================================================
+
+#ifndef REDUCED_SYSTEM_LAYER_HEADER_GUARD_
+#define REDUCED_SYSTEM_LAYER_HEADER_GUARD_
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include <time.h>
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
// ================================================================
//
// PLATFORM API
@@ -271,8 +279,8 @@ i32 main(i32 argc, c8 **argv) {
// ================================================================
#ifndef REDUCED_SYSTEM_LAYER_HEADER
-#ifndef REDUCED_SYSTEM_LAYER_IMPLEMENTATION_GUARD_
-#define REDUCED_SYSTEM_LAYER_IMPLEMENTATION_GUARD_
+#ifndef REDUCED_SYSTEM_LAYER_IMPL_GUARD_
+#define REDUCED_SYSTEM_LAYER_IMPL_GUARD_
Platform platform = {0};
@@ -1155,5 +1163,5 @@ void p_queue_sound(i64 delay, i64 num_samples, f32 *samples) {
// ================================================================
-#endif // REDUCED_SYSTEM_LAYER_IMPLEMENTATION_GUARD_
+#endif // REDUCED_SYSTEM_LAYER_IMPL_GUARD_
#endif // REDUCED_SYSTEM_LAYER_HEADER