commit a30a89727205b1ae2b7956d991903e1cefd6b6af Author: krzosa Date: Sun Dec 29 10:10:09 2024 +0100 reinit repo after broken git diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..918ac43 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +backup +build/ +*.wasm +*.o +MSVC/ +multimedia.c +multimedia.h \ No newline at end of file diff --git a/build.bat b/build.bat new file mode 100644 index 0000000..3c44916 --- /dev/null +++ b/build.bat @@ -0,0 +1,13 @@ +@echo off + +if not exist build\build_tool.exe ( + mkdir build + pushd build + cl ..\src\meta\build_tool.c -Fe:build_tool.exe -Fd:build_tool.pdb /Zi /FC /nologo + rem clang ..\src\meta\build_tool.c -o build_tool.exe -g + popd +) + +build\build_tool.exe + + diff --git a/build_file.c b/build_file.c new file mode 100644 index 0000000..4a7db48 --- /dev/null +++ b/build_file.c @@ -0,0 +1,65 @@ +#include "src/core/core.h" +#include "src/core/core.c" + +#define BUILD_TOOL_LIB +#define S8_String s8_t +#include "src/meta/build_tool.c" +#include "src/meta/parser.c" +#include "src/meta/serialize.c" + +#include "src/app/app.meta.c" + +int main(int argc, char **argv) { + int ok = 0; + + ma_arena_t *arena = ma_create(ma_default_reserve_size); + meta_app(arena); + + bool run_server = false; + bool core_test_target = false; + bool wasm_target = true; + bool win32_target = false; + + if (run_server) { + os_systemf("start /D ..\\package ..\\package\\run_server.bat"); + } + + if (win32_target) { + os_delete_file(s8_lit("win32_app.pdb")); + ok = os_systemf( + "cl ../src/app/app_win32.c -Fe:win32_app.exe -Fd:win32_app.pdb" + " -I ../src" + " /Zi /FC /nologo /Oi" + " /WX /W3 /wd4200 /diagnostics:column" + " /link /incremental:no" + ); + if (ok != 0) return ok; + } + + if (core_test_target) { + os_delete_file(s8_lit("core_test.pdb")); + ok = os_systemf( + "cl ../src/core_test/core_test_entry.c -Fe:core_test.exe -Fd:core_test.pdb" + " -I ../src" + " /Zi /FC /nologo /Oi" + " /WX /W3 /wd4200 /diagnostics:column" + " /link /incremental:no" + ); + if (ok != 0) return ok; + } + + + if (wasm_target) { + ok = os_systemf( + "clang.exe ../src/wasm_app/main.c -o main.wasm" + " -Oz -g -I../src" + " -Wall -Wno-missing-braces" + " -fdiagnostics-absolute-paths -fdiagnostics-format=msvc" + " --target=wasm32 -nostdlib -mbulk-memory -msimd128" + " -Wl,-export-dynamic,--allow-undefined,--import-memory,--no-entry,--initial-memory=131072000,--max-memory=4294967296" + ); + os_copy("main.wasm", "../package/main.wasm", os_copy_overwrite); + if (ok != 0) return ok; + } + return ok; +} \ No newline at end of file diff --git a/package/FiraCode-Regular.ttf b/package/FiraCode-Regular.ttf new file mode 100644 index 0000000..b8a44d2 Binary files /dev/null and b/package/FiraCode-Regular.ttf differ diff --git a/package/index.html b/package/index.html new file mode 100644 index 0000000..b324d59 --- /dev/null +++ b/package/index.html @@ -0,0 +1,153 @@ + + + + + + + + Document + + + + + + + + + \ No newline at end of file diff --git a/package/run_server.bat b/package/run_server.bat new file mode 100644 index 0000000..10f6bc1 --- /dev/null +++ b/package/run_server.bat @@ -0,0 +1 @@ +py.exe -m http.server 8080 \ No newline at end of file diff --git a/src/app/app.c b/src/app/app.c new file mode 100644 index 0000000..4a58c72 --- /dev/null +++ b/src/app/app.c @@ -0,0 +1,2 @@ +#include "app.gen.c" +#include "app_wasm.c" \ No newline at end of file diff --git a/src/app/app.gen.c b/src/app/app.gen.c new file mode 100644 index 0000000..cc552e9 --- /dev/null +++ b/src/app/app.gen.c @@ -0,0 +1,274 @@ + +type_t type__app_key_t = { type_kind_enum, s8_const_lit("app_key_t"), sizeof(app_key_t), + .members = (type_member_t[]){ + {.name = s8_const_lit("app_key_invalid"), .value = app_key_invalid}, + {.name = s8_const_lit("app_key_1"), .value = app_key_1}, + {.name = s8_const_lit("app_key_2"), .value = app_key_2}, + {.name = s8_const_lit("app_key_3"), .value = app_key_3}, + {.name = s8_const_lit("app_key_4"), .value = app_key_4}, + {.name = s8_const_lit("app_key_5"), .value = app_key_5}, + {.name = s8_const_lit("app_key_6"), .value = app_key_6}, + {.name = s8_const_lit("app_key_7"), .value = app_key_7}, + {.name = s8_const_lit("app_key_8"), .value = app_key_8}, + {.name = s8_const_lit("app_key_9"), .value = app_key_9}, + {.name = s8_const_lit("app_key_0"), .value = app_key_0}, + {.name = s8_const_lit("app_key_f1"), .value = app_key_f1}, + {.name = s8_const_lit("app_key_f2"), .value = app_key_f2}, + {.name = s8_const_lit("app_key_f3"), .value = app_key_f3}, + {.name = s8_const_lit("app_key_f4"), .value = app_key_f4}, + {.name = s8_const_lit("app_key_f5"), .value = app_key_f5}, + {.name = s8_const_lit("app_key_f6"), .value = app_key_f6}, + {.name = s8_const_lit("app_key_f7"), .value = app_key_f7}, + {.name = s8_const_lit("app_key_f8"), .value = app_key_f8}, + {.name = s8_const_lit("app_key_f9"), .value = app_key_f9}, + {.name = s8_const_lit("app_key_f10"), .value = app_key_f10}, + {.name = s8_const_lit("app_key_f11"), .value = app_key_f11}, + {.name = s8_const_lit("app_key_f12"), .value = app_key_f12}, + {.name = s8_const_lit("app_key_a"), .value = app_key_a}, + {.name = s8_const_lit("app_key_b"), .value = app_key_b}, + {.name = s8_const_lit("app_key_c"), .value = app_key_c}, + {.name = s8_const_lit("app_key_d"), .value = app_key_d}, + {.name = s8_const_lit("app_key_e"), .value = app_key_e}, + {.name = s8_const_lit("app_key_f"), .value = app_key_f}, + {.name = s8_const_lit("app_key_g"), .value = app_key_g}, + {.name = s8_const_lit("app_key_h"), .value = app_key_h}, + {.name = s8_const_lit("app_key_i"), .value = app_key_i}, + {.name = s8_const_lit("app_key_j"), .value = app_key_j}, + {.name = s8_const_lit("app_key_k"), .value = app_key_k}, + {.name = s8_const_lit("app_key_l"), .value = app_key_l}, + {.name = s8_const_lit("app_key_m"), .value = app_key_m}, + {.name = s8_const_lit("app_key_n"), .value = app_key_n}, + {.name = s8_const_lit("app_key_o"), .value = app_key_o}, + {.name = s8_const_lit("app_key_p"), .value = app_key_p}, + {.name = s8_const_lit("app_key_q"), .value = app_key_q}, + {.name = s8_const_lit("app_key_r"), .value = app_key_r}, + {.name = s8_const_lit("app_key_s"), .value = app_key_s}, + {.name = s8_const_lit("app_key__t"), .value = app_key__t}, + {.name = s8_const_lit("app_key_u"), .value = app_key_u}, + {.name = s8_const_lit("app_key_v"), .value = app_key_v}, + {.name = s8_const_lit("app_key_w"), .value = app_key_w}, + {.name = s8_const_lit("app_key_x"), .value = app_key_x}, + {.name = s8_const_lit("app_key_y"), .value = app_key_y}, + {.name = s8_const_lit("app_key_z"), .value = app_key_z}, + {.name = s8_const_lit("app_key_space"), .value = app_key_space}, + {.name = s8_const_lit("app_key_enter"), .value = app_key_enter}, + {.name = s8_const_lit("app_key_escape"), .value = app_key_escape}, + {.name = s8_const_lit("app_key_left"), .value = app_key_left}, + {.name = s8_const_lit("app_key_up"), .value = app_key_up}, + {.name = s8_const_lit("app_key_right"), .value = app_key_right}, + {.name = s8_const_lit("app_key_down"), .value = app_key_down}, + {.name = s8_const_lit("app_key_tab"), .value = app_key_tab}, + {.name = s8_const_lit("app_key_backspace"), .value = app_key_backspace}, + {.name = s8_const_lit("app_key_control"), .value = app_key_control}, + {.name = s8_const_lit("app_key_shift"), .value = app_key_shift}, + {.name = s8_const_lit("app_key_alt"), .value = app_key_alt}, + {.name = s8_const_lit("app_key_meta"), .value = app_key_meta}, + {.name = s8_const_lit("app_key_caps_lock"), .value = app_key_caps_lock}, + {.name = s8_const_lit("app_key_delete"), .value = app_key_delete}, + {.name = s8_const_lit("app_key_home"), .value = app_key_home}, + {.name = s8_const_lit("app_key_end"), .value = app_key_end}, + {.name = s8_const_lit("app_key_insert"), .value = app_key_insert}, + {.name = s8_const_lit("app_key_page_up"), .value = app_key_page_up}, + {.name = s8_const_lit("app_key_page_down"), .value = app_key_page_down}, + }, + .count = 69, +}; + +#if PLATFORM_WASM +typedef struct { app_key_t key; b32 filter_out; } wasm_key_map_t; +wasm_key_map_t wasm_map_key_string_to_app_key(s8_t key) { + if (0) {} + else if (s8_equal_ex(key, s8_lit("1"), s8_ignore_case)) return (wasm_key_map_t){app_key_1, 0}; + else if (s8_equal_ex(key, s8_lit("2"), s8_ignore_case)) return (wasm_key_map_t){app_key_2, 0}; + else if (s8_equal_ex(key, s8_lit("3"), s8_ignore_case)) return (wasm_key_map_t){app_key_3, 0}; + else if (s8_equal_ex(key, s8_lit("4"), s8_ignore_case)) return (wasm_key_map_t){app_key_4, 0}; + else if (s8_equal_ex(key, s8_lit("5"), s8_ignore_case)) return (wasm_key_map_t){app_key_5, 0}; + else if (s8_equal_ex(key, s8_lit("6"), s8_ignore_case)) return (wasm_key_map_t){app_key_6, 0}; + else if (s8_equal_ex(key, s8_lit("7"), s8_ignore_case)) return (wasm_key_map_t){app_key_7, 0}; + else if (s8_equal_ex(key, s8_lit("8"), s8_ignore_case)) return (wasm_key_map_t){app_key_8, 0}; + else if (s8_equal_ex(key, s8_lit("9"), s8_ignore_case)) return (wasm_key_map_t){app_key_9, 0}; + else if (s8_equal_ex(key, s8_lit("0"), s8_ignore_case)) return (wasm_key_map_t){app_key_0, 0}; + else if (s8_equal_ex(key, s8_lit("F1"), s8_ignore_case)) return (wasm_key_map_t){app_key_f1, 1}; + else if (s8_equal_ex(key, s8_lit("F2"), s8_ignore_case)) return (wasm_key_map_t){app_key_f2, 1}; + else if (s8_equal_ex(key, s8_lit("F3"), s8_ignore_case)) return (wasm_key_map_t){app_key_f3, 1}; + else if (s8_equal_ex(key, s8_lit("F4"), s8_ignore_case)) return (wasm_key_map_t){app_key_f4, 1}; + else if (s8_equal_ex(key, s8_lit("F5"), s8_ignore_case)) return (wasm_key_map_t){app_key_f5, 1}; + else if (s8_equal_ex(key, s8_lit("F6"), s8_ignore_case)) return (wasm_key_map_t){app_key_f6, 1}; + else if (s8_equal_ex(key, s8_lit("F7"), s8_ignore_case)) return (wasm_key_map_t){app_key_f7, 1}; + else if (s8_equal_ex(key, s8_lit("F8"), s8_ignore_case)) return (wasm_key_map_t){app_key_f8, 1}; + else if (s8_equal_ex(key, s8_lit("F9"), s8_ignore_case)) return (wasm_key_map_t){app_key_f9, 1}; + else if (s8_equal_ex(key, s8_lit("F10"), s8_ignore_case)) return (wasm_key_map_t){app_key_f10, 1}; + else if (s8_equal_ex(key, s8_lit("F11"), s8_ignore_case)) return (wasm_key_map_t){app_key_f11, 1}; + else if (s8_equal_ex(key, s8_lit("F12"), s8_ignore_case)) return (wasm_key_map_t){app_key_f12, 1}; + else if (s8_equal_ex(key, s8_lit("a"), s8_ignore_case)) return (wasm_key_map_t){app_key_a, 0}; + else if (s8_equal_ex(key, s8_lit("b"), s8_ignore_case)) return (wasm_key_map_t){app_key_b, 0}; + else if (s8_equal_ex(key, s8_lit("c"), s8_ignore_case)) return (wasm_key_map_t){app_key_c, 0}; + else if (s8_equal_ex(key, s8_lit("d"), s8_ignore_case)) return (wasm_key_map_t){app_key_d, 0}; + else if (s8_equal_ex(key, s8_lit("e"), s8_ignore_case)) return (wasm_key_map_t){app_key_e, 0}; + else if (s8_equal_ex(key, s8_lit("f"), s8_ignore_case)) return (wasm_key_map_t){app_key_f, 0}; + else if (s8_equal_ex(key, s8_lit("g"), s8_ignore_case)) return (wasm_key_map_t){app_key_g, 0}; + else if (s8_equal_ex(key, s8_lit("h"), s8_ignore_case)) return (wasm_key_map_t){app_key_h, 0}; + else if (s8_equal_ex(key, s8_lit("i"), s8_ignore_case)) return (wasm_key_map_t){app_key_i, 0}; + else if (s8_equal_ex(key, s8_lit("j"), s8_ignore_case)) return (wasm_key_map_t){app_key_j, 0}; + else if (s8_equal_ex(key, s8_lit("k"), s8_ignore_case)) return (wasm_key_map_t){app_key_k, 0}; + else if (s8_equal_ex(key, s8_lit("l"), s8_ignore_case)) return (wasm_key_map_t){app_key_l, 0}; + else if (s8_equal_ex(key, s8_lit("m"), s8_ignore_case)) return (wasm_key_map_t){app_key_m, 0}; + else if (s8_equal_ex(key, s8_lit("n"), s8_ignore_case)) return (wasm_key_map_t){app_key_n, 0}; + else if (s8_equal_ex(key, s8_lit("o"), s8_ignore_case)) return (wasm_key_map_t){app_key_o, 0}; + else if (s8_equal_ex(key, s8_lit("p"), s8_ignore_case)) return (wasm_key_map_t){app_key_p, 0}; + else if (s8_equal_ex(key, s8_lit("q"), s8_ignore_case)) return (wasm_key_map_t){app_key_q, 0}; + else if (s8_equal_ex(key, s8_lit("r"), s8_ignore_case)) return (wasm_key_map_t){app_key_r, 0}; + else if (s8_equal_ex(key, s8_lit("s"), s8_ignore_case)) return (wasm_key_map_t){app_key_s, 0}; + else if (s8_equal_ex(key, s8_lit("t"), s8_ignore_case)) return (wasm_key_map_t){app_key__t, 0}; + else if (s8_equal_ex(key, s8_lit("u"), s8_ignore_case)) return (wasm_key_map_t){app_key_u, 0}; + else if (s8_equal_ex(key, s8_lit("v"), s8_ignore_case)) return (wasm_key_map_t){app_key_v, 0}; + else if (s8_equal_ex(key, s8_lit("w"), s8_ignore_case)) return (wasm_key_map_t){app_key_w, 0}; + else if (s8_equal_ex(key, s8_lit("x"), s8_ignore_case)) return (wasm_key_map_t){app_key_x, 0}; + else if (s8_equal_ex(key, s8_lit("y"), s8_ignore_case)) return (wasm_key_map_t){app_key_y, 0}; + else if (s8_equal_ex(key, s8_lit("z"), s8_ignore_case)) return (wasm_key_map_t){app_key_z, 0}; + else if (s8_equal_ex(key, s8_lit(" "), s8_ignore_case)) return (wasm_key_map_t){app_key_space, 0}; + else if (s8_equal_ex(key, s8_lit("Enter"), s8_ignore_case)) return (wasm_key_map_t){app_key_enter, 1}; + else if (s8_equal_ex(key, s8_lit("Escape"), s8_ignore_case)) return (wasm_key_map_t){app_key_escape, 1}; + else if (s8_equal_ex(key, s8_lit("ArrowLeft"), s8_ignore_case)) return (wasm_key_map_t){app_key_left, 1}; + else if (s8_equal_ex(key, s8_lit("ArrowUp"), s8_ignore_case)) return (wasm_key_map_t){app_key_up, 1}; + else if (s8_equal_ex(key, s8_lit("ArrowRight"), s8_ignore_case)) return (wasm_key_map_t){app_key_right, 1}; + else if (s8_equal_ex(key, s8_lit("ArrowDown"), s8_ignore_case)) return (wasm_key_map_t){app_key_down, 1}; + else if (s8_equal_ex(key, s8_lit("Tab"), s8_ignore_case)) return (wasm_key_map_t){app_key_tab, 1}; + else if (s8_equal_ex(key, s8_lit("Backspace"), s8_ignore_case)) return (wasm_key_map_t){app_key_backspace, 1}; + else if (s8_equal_ex(key, s8_lit("Control"), s8_ignore_case)) return (wasm_key_map_t){app_key_control, 1}; + else if (s8_equal_ex(key, s8_lit("Shift"), s8_ignore_case)) return (wasm_key_map_t){app_key_shift, 1}; + else if (s8_equal_ex(key, s8_lit("Alt"), s8_ignore_case)) return (wasm_key_map_t){app_key_alt, 1}; + else if (s8_equal_ex(key, s8_lit("AltGraph"), s8_ignore_case)) return (wasm_key_map_t){app_key_alt, 1}; + else if (s8_equal_ex(key, s8_lit("Meta"), s8_ignore_case)) return (wasm_key_map_t){app_key_meta, 1}; + else if (s8_equal_ex(key, s8_lit("CapsLock"), s8_ignore_case)) return (wasm_key_map_t){app_key_caps_lock, 1}; + else if (s8_equal_ex(key, s8_lit("Delete"), s8_ignore_case)) return (wasm_key_map_t){app_key_delete, 1}; + else if (s8_equal_ex(key, s8_lit("Home"), s8_ignore_case)) return (wasm_key_map_t){app_key_home, 1}; + else if (s8_equal_ex(key, s8_lit("End"), s8_ignore_case)) return (wasm_key_map_t){app_key_end, 1}; + else if (s8_equal_ex(key, s8_lit("Insert"), s8_ignore_case)) return (wasm_key_map_t){app_key_insert, 1}; + else if (s8_equal_ex(key, s8_lit("PageUp"), s8_ignore_case)) return (wasm_key_map_t){app_key_page_up, 1}; + else if (s8_equal_ex(key, s8_lit("PageDown"), s8_ignore_case)) return (wasm_key_map_t){app_key_page_down, 1}; + return (wasm_key_map_t){0}; +} +#endif + +#if PLATFORM_WINDOWS +void w32_key_dispatch(WPARAM wparam, void (*handle_key)(app_key_t)) { + switch(wparam) { + case '1': handle_key(app_key_1); break; + case '2': handle_key(app_key_2); break; + case '3': handle_key(app_key_3); break; + case '4': handle_key(app_key_4); break; + case '5': handle_key(app_key_5); break; + case '6': handle_key(app_key_6); break; + case '7': handle_key(app_key_7); break; + case '8': handle_key(app_key_8); break; + case '9': handle_key(app_key_9); break; + case '0': handle_key(app_key_0); break; + case VK_F1: handle_key(app_key_f1); break; + case VK_F2: handle_key(app_key_f2); break; + case VK_F3: handle_key(app_key_f3); break; + case VK_F4: handle_key(app_key_f4); break; + case VK_F5: handle_key(app_key_f5); break; + case VK_F6: handle_key(app_key_f6); break; + case VK_F7: handle_key(app_key_f7); break; + case VK_F8: handle_key(app_key_f8); break; + case VK_F9: handle_key(app_key_f9); break; + case VK_F10: handle_key(app_key_f10); break; + case VK_F11: handle_key(app_key_f11); break; + case VK_F12: handle_key(app_key_f12); break; + case 'A': handle_key(app_key_a); break; + case 'B': handle_key(app_key_b); break; + case 'C': handle_key(app_key_c); break; + case 'D': handle_key(app_key_d); break; + case 'E': handle_key(app_key_e); break; + case 'F': handle_key(app_key_f); break; + case 'G': handle_key(app_key_g); break; + case 'H': handle_key(app_key_h); break; + case 'I': handle_key(app_key_i); break; + case 'J': handle_key(app_key_j); break; + case 'K': handle_key(app_key_k); break; + case 'L': handle_key(app_key_l); break; + case 'M': handle_key(app_key_m); break; + case 'N': handle_key(app_key_n); break; + case 'O': handle_key(app_key_o); break; + case 'P': handle_key(app_key_p); break; + case 'Q': handle_key(app_key_q); break; + case 'R': handle_key(app_key_r); break; + case 'S': handle_key(app_key_s); break; + case 'T': handle_key(app_key__t); break; + case 'U': handle_key(app_key_u); break; + case 'V': handle_key(app_key_v); break; + case 'W': handle_key(app_key_w); break; + case 'X': handle_key(app_key_x); break; + case 'Y': handle_key(app_key_y); break; + case 'Z': handle_key(app_key_z); break; + case VK_SPACE: handle_key(app_key_space); break; + case VK_RETURN: handle_key(app_key_enter); break; + case VK_ESCAPE: handle_key(app_key_escape); break; + case VK_LEFT: handle_key(app_key_left); break; + case VK_UP: handle_key(app_key_up); break; + case VK_RIGHT: handle_key(app_key_right); break; + case VK_DOWN: handle_key(app_key_down); break; + case VK_TAB: handle_key(app_key_tab); break; + case VK_BACK: handle_key(app_key_backspace); break; + case VK_CONTROL: handle_key(app_key_control); break; + case VK_SHIFT: handle_key(app_key_shift); break; + case VK_LMENU: handle_key(app_key_alt); break; + case VK_RMENU: handle_key(app_key_alt); break; + case VK_LWIN: handle_key(app_key_meta); break; + case VK_RWIN: handle_key(app_key_meta); break; + case VK_CAPITAL: handle_key(app_key_caps_lock); break; + case VK_DELETE: handle_key(app_key_delete); break; + case VK_HOME: handle_key(app_key_home); break; + case VK_END: handle_key(app_key_end); break; + case VK_NEXT: handle_key(app_key_insert); break; + case VK_INSERT: handle_key(app_key_page_up); break; + case VK_PRIOR: handle_key(app_key_page_down); break; + default: {} break; + } +} +#endif/*D:\dev\wasm\src/app/app.meta.c*/ +type_t type__app_mouse_button_t = { type_kind_enum, s8_const_lit("app_mouse_button_t"), sizeof(app_mouse_button_t), + .members = (type_member_t[]){ + {.name = s8_const_lit("app_mouse_button_null"), .value = app_mouse_button_null}, + {.name = s8_const_lit("app_mouse_button_left"), .value = app_mouse_button_left}, + {.name = s8_const_lit("app_mouse_button_middle"), .value = app_mouse_button_middle}, + {.name = s8_const_lit("app_mouse_button_right"), .value = app_mouse_button_right}, + {.name = s8_const_lit("app_mouse_button_count"), .value = app_mouse_button_count}, + }, + .count = 5, +}; +type_t type__app_event_kind_t = { type_kind_enum, s8_const_lit("app_event_kind_t"), sizeof(app_event_kind_t), + .members = (type_member_t[]){ + {.name = s8_const_lit("app_event_kind_invalid"), .value = app_event_kind_invalid}, + {.name = s8_const_lit("app_event_kind_update"), .value = app_event_kind_update}, + {.name = s8_const_lit("app_event_kind_text"), .value = app_event_kind_text}, + {.name = s8_const_lit("app_event_kind_key_down"), .value = app_event_kind_key_down}, + {.name = s8_const_lit("app_event_kind_key_up"), .value = app_event_kind_key_up}, + {.name = s8_const_lit("app_event_kind_mouse_down"), .value = app_event_kind_mouse_down}, + {.name = s8_const_lit("app_event_kind_mouse_up"), .value = app_event_kind_mouse_up}, + {.name = s8_const_lit("app_event_kind_mouse_wheel"), .value = app_event_kind_mouse_wheel}, + {.name = s8_const_lit("app_event_kind_mouse_move"), .value = app_event_kind_mouse_move}, + {.name = s8_const_lit("app_event_kind_count"), .value = app_event_kind_count}, + }, + .count = 10, +}; +type_t type__app_event_t = { type_kind_struct, s8_const_lit("app_event_t"), sizeof(app_event_t), + .members = (type_member_t[]){ + {.name = s8_const_lit("kind"), .type = &type__app_event_kind_t, .offset = offsetof(app_event_t, kind)}, + {.name = s8_const_lit("mouse_button"), .type = &type__app_mouse_button_t, .offset = offsetof(app_event_t, mouse_button)}, + {.name = s8_const_lit("key"), .type = &type__app_key_t, .offset = offsetof(app_event_t, key)}, + {.name = s8_const_lit("text"), .type = &type__s8_t, .offset = offsetof(app_event_t, text)}, + {.name = s8_const_lit("ctrl"), .type = &type__b8, .offset = offsetof(app_event_t, ctrl)}, + {.name = s8_const_lit("shift"), .type = &type__b8, .offset = offsetof(app_event_t, shift)}, + {.name = s8_const_lit("alt"), .type = &type__b8, .offset = offsetof(app_event_t, alt)}, + {.name = s8_const_lit("meta"), .type = &type__b8, .offset = offsetof(app_event_t, meta)}, + {.name = s8_const_lit("time"), .type = &type__f64, .offset = offsetof(app_event_t, time)}, + {.name = s8_const_lit("delta_time"), .type = &type__f64, .offset = offsetof(app_event_t, delta_time)}, + {.name = s8_const_lit("dpr"), .type = &type__f64, .offset = offsetof(app_event_t, dpr)}, + {.name = s8_const_lit("window_size"), .type = &type__v2f32_t, .offset = offsetof(app_event_t, window_size)}, + {.name = s8_const_lit("mouse_pos"), .type = &type__v2f32_t, .offset = offsetof(app_event_t, mouse_pos)}, + {.name = s8_const_lit("mouse_wheel_delta"), .type = &type__v3f32_t, .offset = offsetof(app_event_t, mouse_wheel_delta)}, + }, + .count = 14, +}; \ No newline at end of file diff --git a/src/app/app.gen.h b/src/app/app.gen.h new file mode 100644 index 0000000..3867c6b --- /dev/null +++ b/src/app/app.gen.h @@ -0,0 +1,110 @@ +typedef enum { +app_key_invalid, +app_key_1, +app_key_2, +app_key_3, +app_key_4, +app_key_5, +app_key_6, +app_key_7, +app_key_8, +app_key_9, +app_key_0, +app_key_f1, +app_key_f2, +app_key_f3, +app_key_f4, +app_key_f5, +app_key_f6, +app_key_f7, +app_key_f8, +app_key_f9, +app_key_f10, +app_key_f11, +app_key_f12, +app_key_a, +app_key_b, +app_key_c, +app_key_d, +app_key_e, +app_key_f, +app_key_g, +app_key_h, +app_key_i, +app_key_j, +app_key_k, +app_key_l, +app_key_m, +app_key_n, +app_key_o, +app_key_p, +app_key_q, +app_key_r, +app_key_s, +app_key__t, +app_key_u, +app_key_v, +app_key_w, +app_key_x, +app_key_y, +app_key_z, +app_key_space, +app_key_enter, +app_key_escape, +app_key_left, +app_key_up, +app_key_right, +app_key_down, +app_key_tab, +app_key_backspace, +app_key_control, +app_key_shift, +app_key_alt, +app_key_meta, +app_key_caps_lock, +app_key_delete, +app_key_home, +app_key_end, +app_key_insert, +app_key_page_up, +app_key_page_down, +app_key_count, +} app_key_t; +/*D:\dev\wasm\src/app/app.meta.c*/ +typedef enum { + app_mouse_button_null, + app_mouse_button_left, + app_mouse_button_middle, + app_mouse_button_right, + app_mouse_button_count, +} app_mouse_button_t; +typedef enum { + app_event_kind_invalid, + app_event_kind_update, + app_event_kind_text, + app_event_kind_key_down, + app_event_kind_key_up, + app_event_kind_mouse_down, + app_event_kind_mouse_up, + app_event_kind_mouse_wheel, + app_event_kind_mouse_move, + app_event_kind_count, +} app_event_kind_t; + +typedef struct app_event_t app_event_t; +struct app_event_t { + app_event_kind_t kind; + app_mouse_button_t mouse_button; + app_key_t key; + s8_t text; + b8 ctrl; + b8 shift; + b8 alt; + b8 meta; + f64 time; + f64 delta_time; + f64 dpr; + v2f32_t window_size; + v2f32_t mouse_pos; + v3f32_t mouse_wheel_delta; +}; \ No newline at end of file diff --git a/src/app/app.h b/src/app/app.h new file mode 100644 index 0000000..f3d2aa0 --- /dev/null +++ b/src/app/app.h @@ -0,0 +1,3 @@ +#include "app.gen.h" + + diff --git a/src/app/app.meta.c b/src/app/app.meta.c new file mode 100644 index 0000000..0bcca68 --- /dev/null +++ b/src/app/app.meta.c @@ -0,0 +1,189 @@ +void meta_app(ma_arena_t *arena) { + sb8_t *h = sb8_serial_begin(arena); + sb8_t *c = sb8_serial_begin(arena); + + + ast_t *keys = parse_table(arena, __FILE__, CODE( + // javascript filter out + | name | js1 | js2 | jf | windows1 | windows2 | + | invalid | XXX | XXX | 1 | XXX | XXX | + | 1 | 1 | XXX | 0 | `'1'` | XXX | + | 2 | 2 | XXX | 0 | `'2'` | XXX | + | 3 | 3 | XXX | 0 | `'3'` | XXX | + | 4 | 4 | XXX | 0 | `'4'` | XXX | + | 5 | 5 | XXX | 0 | `'5'` | XXX | + | 6 | 6 | XXX | 0 | `'6'` | XXX | + | 7 | 7 | XXX | 0 | `'7'` | XXX | + | 8 | 8 | XXX | 0 | `'8'` | XXX | + | 9 | 9 | XXX | 0 | `'9'` | XXX | + | 0 | 0 | XXX | 0 | `'0'` | XXX | + | f1 | F1 | XXX | 1 | VK_F1 | XXX | + | f2 | F2 | XXX | 1 | VK_F2 | XXX | + | f3 | F3 | XXX | 1 | VK_F3 | XXX | + | f4 | F4 | XXX | 1 | VK_F4 | XXX | + | f5 | F5 | XXX | 1 | VK_F5 | XXX | + | f6 | F6 | XXX | 1 | VK_F6 | XXX | + | f7 | F7 | XXX | 1 | VK_F7 | XXX | + | f8 | F8 | XXX | 1 | VK_F8 | XXX | + | f9 | F9 | XXX | 1 | VK_F9 | XXX | + | f10 | F10 | XXX | 1 | VK_F10 | XXX | + | f11 | F11 | XXX | 1 | VK_F11 | XXX | + | f12 | F12 | XXX | 1 | VK_F12 | XXX | + | a | a | XXX | 0 | `'A'` | XXX | + | b | b | XXX | 0 | `'B'` | XXX | + | c | c | XXX | 0 | `'C'` | XXX | + | d | d | XXX | 0 | `'D'` | XXX | + | e | e | XXX | 0 | `'E'` | XXX | + | f | f | XXX | 0 | `'F'` | XXX | + | g | g | XXX | 0 | `'G'` | XXX | + | h | h | XXX | 0 | `'H'` | XXX | + | i | i | XXX | 0 | `'I'` | XXX | + | j | j | XXX | 0 | `'J'` | XXX | + | k | k | XXX | 0 | `'K'` | XXX | + | l | l | XXX | 0 | `'L'` | XXX | + | m | m | XXX | 0 | `'M'` | XXX | + | n | n | XXX | 0 | `'N'` | XXX | + | o | o | XXX | 0 | `'O'` | XXX | + | p | p | XXX | 0 | `'P'` | XXX | + | q | q | XXX | 0 | `'Q'` | XXX | + | r | r | XXX | 0 | `'R'` | XXX | + | s | s | XXX | 0 | `'S'` | XXX | + | _t | t | XXX | 0 | `'T'` | XXX | + | u | u | XXX | 0 | `'U'` | XXX | + | v | v | XXX | 0 | `'V'` | XXX | + | w | w | XXX | 0 | `'W'` | XXX | + | x | x | XXX | 0 | `'X'` | XXX | + | y | y | XXX | 0 | `'Y'` | XXX | + | z | z | XXX | 0 | `'Z'` | XXX | + | space | ` ` | XXX | 0 | VK_SPACE | XXX | + | enter | Enter | XXX | 1 | VK_RETURN | XXX | + | escape | Escape | XXX | 1 | VK_ESCAPE | XXX | + | left | ArrowLeft | XXX | 1 | VK_LEFT | XXX | + | up | ArrowUp | XXX | 1 | VK_UP | XXX | + | right | ArrowRight | XXX | 1 | VK_RIGHT | XXX | + | down | ArrowDown | XXX | 1 | VK_DOWN | XXX | + | tab | Tab | XXX | 1 | VK_TAB | XXX | + | backspace | Backspace | XXX | 1 | VK_BACK | XXX | + | control | Control | XXX | 1 | VK_CONTROL | XXX | + | shift | Shift | XXX | 1 | VK_SHIFT | XXX | + | alt | Alt | AltGraph | 1 | VK_LMENU | VK_RMENU | + | meta | Meta | XXX | 1 | VK_LWIN | VK_RWIN | + | caps_lock | CapsLock | XXX | 1 | VK_CAPITAL | XXX | + | delete | Delete | XXX | 1 | VK_DELETE | XXX | + | home | Home | XXX | 1 | VK_HOME | XXX | + | end | End | XXX | 1 | VK_END | XXX | + | insert | Insert | XXX | 1 | VK_NEXT | XXX | + | page_up | PageUp | XXX | 1 | VK_INSERT | XXX | + | page_down | PageDown | XXX | 1 | VK_PRIOR | XXX | + )); + sb8_serial_table_enum(c, h, keys, s8_lit("app_key")); + + // Javascript + { + int name_idx = row_findi(keys->first, "name"); + int js1_idx = row_findi(keys->first, "js1"); + int js2_idx = row_findi(keys->first, "js2"); + int filter_out_idx = row_findi(keys->first, "jf"); + + + sb8_stmtf(c, "\n#if PLATFORM_WASM"); + sb8_stmtf(c, "typedef struct { app_key_t key; b32 filter_out; } wasm_key_map_t;"); + sb8_stmtf(c, "wasm_key_map_t wasm_map_key_string_to_app_key(s8_t key) {"); + c->indent += 1; + sb8_stmtf(c, "if (0) {}"); + for (ast_t *row = keys->first->next; row; row = row->next) { + s8_t name = row_geti(row, name_idx)->string; + i64 filter_out = row_geti(row, filter_out_idx)->integer; + assert(filter_out == 0 || filter_out == 1); + + s8_t js[] = {row_geti(row, js1_idx)->string, row_geti(row, js2_idx)->string}; + for (i32 i = 0; i < lengthof(js); i += 1) { + if (s8_equal(js[i], s8_lit("XXX"))) continue; + sb8_stmtf(c, "else if (s8_equal_ex(key, s8_lit(\"%S\"), s8_ignore_case)) return (wasm_key_map_t){app_key_%S, %d};", js[i], name, (int)filter_out); + } + } + sb8_stmtf(c, "return (wasm_key_map_t){0};"); + c->indent -= 1; + sb8_stmtf(c, "}"); + sb8_stmtf(c, "#endif"); + } + + // Windows + { + int name_idx = row_findi(keys->first, "name"); + int w1i = row_findi(keys->first, "windows1"); + int w2i = row_findi(keys->first, "windows2"); + + sb8_stmtf(c, "\n#if PLATFORM_WINDOWS"); + sb8_stmtf(c, "void w32_key_dispatch(WPARAM wparam, void (*handle_key)(app_key_t)) {"); + c->indent += 1; + sb8_stmtf(c, "switch(wparam) {"); + c->indent += 1; + for (ast_t *row = keys->first->next; row; row = row->next) { + s8_t name = row_geti(row, name_idx)->string; + s8_t w[] = {row_geti(row, w1i)->string, row_geti(row, w2i)->string}; + for (i32 i = 0; i < lengthof(w); i += 1) { + if (s8_equal(w[i], s8_lit("XXX"))) continue; + + sb8_stmtf(c, "case %.*s: handle_key(app_key_%.*s); break;", s8_fmtspec(w[i]), s8_fmtspec(name)); + } + } + sb8_stmtf(c, "default: {} break;"); + c->indent -= 1; + sb8_stmtf(c, "}"); + c->indent -= 1; + sb8_stmtf(c, "}"); + sb8_stmtf(c, "#endif"); + } + + { + ast_t *decls = parse_decls(arena, __FILE__, CODE( + typedef enum { + app_mouse_button_null, + app_mouse_button_left, + app_mouse_button_middle, + app_mouse_button_right, + app_mouse_button_count, + } app_mouse_button_t; + + typedef enum { + app_event_kind_invalid, + app_event_kind_update, + app_event_kind_text, + app_event_kind_key_down, + app_event_kind_key_up, + app_event_kind_mouse_down, + app_event_kind_mouse_up, + app_event_kind_mouse_wheel, + app_event_kind_mouse_move, + app_event_kind_count, + } app_event_kind_t; + + struct app_event_t { + app_event_kind_t kind; + + app_mouse_button_t mouse_button; + app_key_t key; + s8_t text; + b8 ctrl; + b8 shift; + b8 alt; + b8 meta; + + f64 time; + f64 delta_time; + + f64 dpr; + v2f32_t window_size; + v2f32_t mouse_pos; + v3f32_t mouse_wheel_delta; + }; + )); + + sb8_serial_ast_to_code(h, decls); + sb8_serial_ast_to_type_info(c, decls); + } + + os_write_file(gen_c(arena), sb8_merge(c)); + os_write_file(gen_h(arena), sb8_merge(h)); +} \ No newline at end of file diff --git a/src/app/app_wasm.c b/src/app/app_wasm.c new file mode 100644 index 0000000..ac31f5a --- /dev/null +++ b/src/app/app_wasm.c @@ -0,0 +1,212 @@ +#define WASM_EXPORT __attribute__((visibility("default"))) + +f64 wasm_parse_float(isize str, i32 len); +void wasm_write_to_console(isize str, i32 len); +void wasm_draw_text(isize str, i32 len, f64 x, f64 y, isize font_str, i32 font_len, i32 font_size, f32 r, f32 g, f32 b, f32 a); +void wasm_draw_rect(f64 x, f64 y, f64 w, f64 h, f32 r, f32 g, f32 b, f32 a); +f64 wasm_measure_text(isize str, i32 len, isize font_str, i32 font_len, i32 font_size); +f64 wasm_get_font_height(isize font_str, i32 font_len, i32 font_size); +void wasm_set_clip(f64 x, f64 y, f64 w, f64 h); + +void on_update(); +void app_update(app_event_t *events); + +extern char __heap_base; +ma_arena_t wasm_perm_arena; + +WASM_EXPORT char wasm_temp_buff1[128] = {[127] = 0x13}; +WASM_EXPORT i32 wasm_temp_buff1_len = 127; +WASM_EXPORT char wasm_temp_buff2[128] = {[127] = 0x13}; +WASM_EXPORT i32 wasm_temp_buff2_len = 127; + + +char *font_face = "fira"; +i32 font_face_len = 4; + +f64 wasm_dpr; +ma_arena_t *wasm_input_text_arena; +STACK(app_event_t, 64) wasm_events; +b32 wasm_event_failed_to_queue; +f64 wasm_last_time = 0; + +void write_to_console(char *string) { + int len = str_len(string); + wasm_write_to_console((isize)string, len); +} + +f64 s8_deserial_f64(s8_t string) { + return wasm_parse_float((isize)string.str, (i32)string.len); +} + +void clip(r2f64_t rect) { + wasm_set_clip(wasm_dpr * rect.min.x, wasm_dpr * rect.min.y, wasm_dpr * (rect.max.x - rect.min.x), wasm_dpr * (rect.max.y - rect.min.y)); +} + + +f64 get_font_height(void) { + return wasm_get_font_height((isize) font_face, font_face_len, 20*wasm_dpr) / wasm_dpr; +} + +f64 measure_text_ex(char *str, int len) { + return wasm_measure_text((isize)str, len, (isize) font_face, font_face_len, 20*wasm_dpr) / wasm_dpr; +} + +f64 measure_text(char *str) { + return measure_text_ex(str, str_len(str)); +} + +void draw_text(v2f64_t pos, v4f32_t color, char *str, int len) { + wasm_draw_text((isize)str, len, wasm_dpr * pos.x, wasm_dpr * pos.y, (isize) font_face, font_face_len, 20*wasm_dpr, color.r * 255.f, color.g * 255.f, color.b * 255.f, color.a); +} + +void draw_textf(v2f64_t pos, char *str, ...) { + char buff[1024]; + va_list args; + va_start(args, str); + int len = stbsp_vsnprintf(buff, sizeof(buff), str, args); + va_end(args); + + wasm_draw_text((isize)buff, len, wasm_dpr * pos.x, wasm_dpr * pos.y, (isize) font_face, font_face_len, 20*wasm_dpr, 0, 0, 0, 1); +} + +void draw_rect(r2f64_t rect, v4f32_t color) { + wasm_draw_rect(wasm_dpr * rect.min.x, wasm_dpr * rect.min.y, wasm_dpr * (rect.max.x - rect.min.x), wasm_dpr * (rect.max.y - rect.min.y), color.r * 255.f, color.g * 255.f, color.b * 255.f, color.a); +} + +void wasm_add_event(app_event_t event) { + if (wasm_events.len < lengthof(wasm_events.data)) { + STACK_PUSH(wasm_events, event); + } else if (wasm_event_failed_to_queue == false) { + wasm_event_failed_to_queue = true; + debugf("failed to queue event"); + } +} + +WASM_EXPORT void wasm_mouse_move(f64 x, f64 y, b32 ctrl, b32 shift, b32 alt, b32 meta) { + wasm_add_event((app_event_t){ + .kind = app_event_kind_mouse_move, + .mouse_pos = {x, y}, + .ctrl = ctrl, + .shift = shift, + .alt = alt, + .meta = meta, + }); +} + +WASM_EXPORT void wasm_mouse_down(f64 x, f64 y, i32 button, b32 ctrl, b32 shift, b32 alt, b32 meta) { + button += 1; + assert(button >= app_mouse_button_left && button <= app_mouse_button_right); + wasm_add_event((app_event_t){ + .kind = app_event_kind_mouse_down, + .mouse_pos = {x, y}, + .mouse_button = button, + .ctrl = ctrl, + .shift = shift, + .alt = alt, + .meta = meta, + }); +} + +WASM_EXPORT void wasm_mouse_up(f64 x, f64 y, i32 button, b32 ctrl, b32 shift, b32 alt, b32 meta) { + button += 1; + assert(button >= app_mouse_button_left && button <= app_mouse_button_right); + wasm_add_event((app_event_t){ + .kind = app_event_kind_mouse_up, + .mouse_pos = {x, y}, + .mouse_button = button, + .ctrl = ctrl, + .shift = shift, + .alt = alt, + .meta = meta, + }); +} + +WASM_EXPORT void wasm_mouse_wheel(f64 delta_x, f64 delta_y, f64 delta_z, b32 ctrl, b32 shift, b32 alt, b32 meta) { + wasm_add_event((app_event_t){ + .kind = app_event_kind_mouse_wheel, + .mouse_wheel_delta = {delta_x, delta_y, delta_z}, + .ctrl = ctrl, + .shift = shift, + .alt = alt, + .meta = meta, + }); +} + +WASM_EXPORT void wasm_key_down(char *key, b32 ctrl, b32 shift, b32 alt, b32 meta) { + assert(wasm_temp_buff1[127] == 0x13); // make sure we didn't overwrite memory in JS + assert(wasm_temp_buff2[127] == 0x13); + s8_t key8 = s8_from_char(key); + + wasm_key_map_t map = wasm_map_key_string_to_app_key(key8); + if (map.key != app_key_invalid) { + wasm_add_event((app_event_t){ + .kind = app_event_kind_key_down, + .key = map.key, + .ctrl = ctrl, + .shift = shift, + .alt = alt, + .meta = meta, + }); + } + + + if (map.filter_out) { + return; + } + + s8_t text = s8_copy(wasm_input_text_arena, key8); + wasm_add_event((app_event_t){ + .kind = app_event_kind_text, + .text = text, + .ctrl = ctrl, + .shift = shift, + .alt = alt, + .meta = meta, + }); +} + +WASM_EXPORT void wasm_key_up(char *key, b32 ctrl, b32 shift, b32 alt, b32 meta) { + assert(wasm_temp_buff1[127] == 0x13); // make sure we didn't overwrite memory in JS + assert(wasm_temp_buff2[127] == 0x13); + s8_t key8 = s8_from_char(key); + + wasm_key_map_t map = wasm_map_key_string_to_app_key(key8); + if (map.key != app_key_invalid) { + wasm_add_event((app_event_t){ + .kind = app_event_kind_key_down, + .key = map.key, + .ctrl = ctrl, + .shift = shift, + .alt = alt, + .meta = meta, + }); + } +} + +WASM_EXPORT void wasm_update(f64 time, f64 width, f64 height, f64 dpr) { + f64 delta_time = (time - wasm_last_time); + for (i32 i = 0; i < wasm_events.len; i += 1) { + wasm_events.data[i].dpr = dpr; + wasm_events.data[i].window_size = (v2f32_t){width / dpr, height / dpr}; + wasm_events.data[i].time = time; + wasm_events.data[i].delta_time = delta_time; + } + wasm_dpr = dpr; + + on_update(); + + wasm_events.len = 0; + wasm_last_time = time; + ma_set0(wasm_input_text_arena); +} + +WASM_EXPORT void wasm_init(void) { + isize page_size = kib(64); + isize page_count = __builtin_wasm_memory_size(0); + u8 *memory = (u8 *)&__heap_base; + usize memory_size = page_count * (page_size) - (isize)memory; + wasm_perm_arena.data = memory; + wasm_perm_arena.commit = wasm_perm_arena.reserve = memory_size; + wasm_input_text_arena = ma_push_arena(&wasm_perm_arena, kib(1)); + debugf("on_init, __builtin_wasm_memory_size(0) = %d(%d)", page_count, memory_size); +} \ No newline at end of file diff --git a/src/app/app_win32.c b/src/app/app_win32.c new file mode 100644 index 0000000..c1ba73c --- /dev/null +++ b/src/app/app_win32.c @@ -0,0 +1,132 @@ +#include "core/core.h" +#include "app.gen.h" + +#include "core/core.c" +#include "app.gen.c" + +#pragma comment(linker, "/subsystem:windows") +#pragma comment(lib, "gdi32.lib") +#pragma comment(lib, "user32.lib") +#pragma comment(lib, "winmm.lib") + + +b32 w32_good_scheduling = false; +WNDCLASSW w32_wc; +HWND w32_window_handle; +HDC w32_dc; + +void w32_on_key_down(app_key_t key) { +} + +void w32_on_key_up(app_key_t key) { +} + +LRESULT CALLBACK w32_window_proc(HWND wnd, UINT msg, WPARAM wparam, LPARAM lparam) { + switch (msg) { + case WM_CLOSE: PostQuitMessage(0); break; + case WM_KEYUP: w32_key_dispatch(wparam, w32_on_key_up); break; + case WM_KEYDOWN: w32_key_dispatch(wparam, w32_on_key_down); break; + default: return DefWindowProcW(wnd, msg, wparam, lparam); + } + return 0; + +} + +int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) { + typedef enum W32_PROCESS_DPI_AWARENESS { + W32_PROCESS_DPI_UNAWARE = 0, + W32_PROCESS_SYSTEM_DPI_AWARE = 1, + W32_PROCESS_PER_MONITOR_DPI_AWARE = 2 + } W32_PROCESS_DPI_AWARENESS; + + typedef unsigned MU_TimeBeginPeriod(unsigned); + typedef HRESULT MU_SetProcessDpiAwareness(W32_PROCESS_DPI_AWARENESS); + + HMODULE shcore = LoadLibraryA("Shcore.dll"); + if (shcore) { + MU_SetProcessDpiAwareness *set_dpi_awr = (MU_SetProcessDpiAwareness *)GetProcAddress(shcore, "SetProcessDpiAwareness"); + if (set_dpi_awr) { + HRESULT hr = set_dpi_awr(W32_PROCESS_PER_MONITOR_DPI_AWARE); + assert(SUCCEEDED(hr) && "Failed to set dpi awareness"); + } + } + + HMODULE winmm = LoadLibraryA("winmm.dll"); + if (winmm) { + MU_TimeBeginPeriod *timeBeginPeriod = (MU_TimeBeginPeriod *)GetProcAddress(winmm, "timeBeginPeriod"); + if (timeBeginPeriod) { + if (timeBeginPeriod(1) == 0) { + w32_good_scheduling = true; + } + } + } + + WNDCLASSW wc = {0}; + { + wc.lpfnWndProc = w32_window_proc; + wc.hInstance = GetModuleHandleW(NULL); + wc.lpszClassName = L"HelloClassName"; + wc.hCursor = LoadCursor(0, IDC_ARROW); + wc.hIcon = NULL; // LoadIcon(wc.hInstance, IDI_APPLICATION); + wc.style = CS_OWNDC | CS_VREDRAW | CS_HREDRAW; + ATOM result = RegisterClassW(&wc); + assert(result != 0); + w32_wc = wc; + } + + RECT window_rect = {0}; + { + window_rect.left = 0; + window_rect.right = 1280; + window_rect.bottom = 732; + window_rect.top = 12; + AdjustWindowRectEx(&window_rect, WS_OVERLAPPEDWINDOW, false, 0); + } + w32_window_handle = CreateWindowW(w32_wc.lpszClassName, L"Zzz... Window, hello!", WS_OVERLAPPEDWINDOW, window_rect.left, window_rect.top, window_rect.right - window_rect.left, window_rect.bottom - window_rect.top, NULL, NULL, hInstance, NULL); + assert(w32_window_handle); + + w32_dc = GetDC(w32_window_handle); + assert(w32_dc); + ShowWindow(w32_window_handle, SW_SHOW); + + // @todo: rebuild on resize + // Create a writable backbuffer bitmap + uint32_t *mem = 0; + BITMAPINFO bminfo = {0}; + { + bminfo.bmiHeader.biSize = sizeof(bminfo.bmiHeader); + bminfo.bmiHeader.biWidth = (LONG)1280; + bminfo.bmiHeader.biHeight = (LONG)720; + bminfo.bmiHeader.biPlanes = 1; + bminfo.bmiHeader.biBitCount = 32; + bminfo.bmiHeader.biCompression = BI_RGB; // AA RR GG BB + bminfo.bmiHeader.biXPelsPerMeter = 1; + bminfo.bmiHeader.biYPelsPerMeter = 1; + } + HBITMAP dib = CreateDIBSection(w32_dc, &bminfo, DIB_RGB_COLORS, (void **)&mem, 0, 0); + HDC dib_dc = CreateCompatibleDC(w32_dc); + + for (;;) { + MSG msg; + if (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE)) { + if (msg.message == WM_QUIT) { + break; + } + TranslateMessage(&msg); + DispatchMessageW(&msg); + continue; + } + + for (int y = 0; y < 720; y++) { + for (int x = 0; x < 1280; x++) { + mem[x + y * 1280] = 0xFFFF0000; + } + } + + SelectObject(dib_dc, dib); + BitBlt(w32_dc, 0, 0, 1280, 720, dib_dc, 0, 0, SRCCOPY); + Sleep(10); + } + + return 0; +} \ No newline at end of file diff --git a/src/core/arena.c b/src/core/arena.c new file mode 100644 index 0000000..90565a9 --- /dev/null +++ b/src/core/arena.c @@ -0,0 +1,132 @@ +b32 is_pow2(usize x) { + b32 result = (((x) & ((x)-1)) == 0); + return result; +} + +usize get_align_offset(usize size, usize align) { + assert(is_pow2(align)); + if (align == 0) return 0; + + usize mask = align - 1; + usize val = size & mask; + if (val) { + val = align - val; + } + return val; +} + +usize align_up(usize size, usize align) { + usize result = size + get_align_offset(size, align); + return result; +} + +usize align_down(usize size, usize align) { + size += 1; // Make sure when align is 8 doesn't get rounded down to 0 + usize result = size - (align - get_align_offset(size, align)); + return result; +} + +void ma_init(ma_arena_t *arena, usize reserve) { + reserve = align_up(reserve, ma_page_size); + arena->align = ma_default_alignment; + arena->data = (u8 *)vmem_reserve(reserve); + if (arena->data) { + arena->reserve = reserve; + } +} + +ma_arena_t *ma_create(usize reserve) { + ma_arena_t *result = NULL; + + void *data = vmem_reserve(reserve); + if (!data) return result; + + b32 success = vmem_commit(data, ma_page_size); + if (!success) { + vmem_release(data); + return result; + } + + result = (ma_arena_t *)data; + result->data = (u8 *)data; + result->reserve = reserve; + result->commit = ma_page_size; + result->len = result->base_len = sizeof(ma_arena_t); + result->align = ma_default_alignment; + return result; +} + +void *ma_push_size_ex(ma_arena_t *arena, usize size) { + // base_len is used for bootstraping arenas, it denotes the + // space occupied by the arena. If len is smaller then base_len then + // we start to overwrite the arena itself - pure barbarism. + assert(arena->len >= arena->base_len); + + usize align_offset = 0; + if (arena->align) { + align_offset = get_align_offset((usize)arena->data + arena->len, arena->align); + } + usize size_with_alignment = size + align_offset; + usize new_len = arena->len + size_with_alignment; + if (new_len > arena->commit) { + if (arena->reserve == 0) { + ma_init(arena, ma_default_reserve_size); + } + usize new_len_aligned_to_page_size = align_up(new_len, ma_page_size); + usize to_commit = new_len_aligned_to_page_size - arena->commit; + usize to_commit_clamped = CLAMP_TOP(to_commit, arena->reserve); + if (to_commit_clamped > 0) { + b32 success = vmem_commit(arena->data + arena->commit, to_commit_clamped); + if (success) { + MA_ASAN_UNPOISON_MEMORY_REGION(arena->data + arena->commit, to_commit_clamped); + arena->commit += to_commit_clamped; + } + } + if (new_len > arena->commit) { + return NULL; + } + } + u8 *result = arena->data + arena->len + align_offset; + arena->len = new_len; + MA_ASAN_UNPOISON_MEMORY_REGION(result, size); + return (void *)result; +} + +void *ma_push_size(ma_arena_t *arena, usize size) { + void *result = ma_push_size_ex(arena, size); + if (result) memory_zero(result, size); + return result; +} + +ma_arena_t *ma_push_arena(ma_arena_t *allocator, usize size) { + ma_arena_t *result = ma_push_type(allocator, ma_arena_t); + result->data = (u8 *)ma_push_size(allocator, size); + result->reserve = size; + result->commit = size; + result->align = ma_default_alignment; + return result; +} + +void ma_destroy(ma_arena_t *arena) { + if (arena == NULL || arena->data == NULL) return; + b32 zero_memory = (u8 *)arena != arena->data; + vmem_release(arena->data); + if (zero_memory) memory_zero(arena, sizeof(ma_arena_t)); +} + +void ma_set_len(ma_arena_t *arena, usize pos) { + // base_len is used for bootstraping arenas, it denotes the + // space occupied by the arena. If len is smaller then base_len then + // we start to overwrite the arena itself - pure barbarism. + assert(arena->len >= arena->base_len); + + pos = CLAMP(pos, arena->base_len, arena->len); + usize size = arena->len - pos; + arena->len = pos; + MA_ASAN_POISON_MEMORY_REGION(arena->data + arena->len, size); +} + +void ma_pop(ma_arena_t *arena, usize size) { ma_set_len(arena, arena->len - size); } +ma_temp_t ma_begin_temp(ma_arena_t *arena) { return (ma_temp_t){arena, arena->len}; } +void ma_end_temp(ma_temp_t temp) { ma_set_len(temp.arena, temp.len); } +void ma_set0(ma_arena_t *arena) { ma_set_len(arena, 0); } diff --git a/src/core/core.c b/src/core/core.c new file mode 100644 index 0000000..961abc8 --- /dev/null +++ b/src/core/core.c @@ -0,0 +1,41 @@ +#include + +#if PLATFORM_WINDOWS +#define NOMINMAX +#define WIN32_LEAN_AND_MEAN +#include +#endif + +#if PLATFORM_ADDRESS_SANITIZER + #include +#endif + +#if !defined(ASAN_POISON_MEMORY_REGION) + #define MA_ASAN_POISON_MEMORY_REGION(addr, size) ((void)(addr), (void)(size)) + #define MA_ASAN_UNPOISON_MEMORY_REGION(addr, size) ((void)(addr), (void)(size)) +#else + #define MA_ASAN_POISON_MEMORY_REGION(addr, size) ASAN_POISON_MEMORY_REGION(addr, size) + #define MA_ASAN_UNPOISON_MEMORY_REGION(addr, size) ASAN_UNPOISON_MEMORY_REGION(addr, size) +#endif + +#if PLATFORM_CL +#include +#include +#include +#include +#endif + +#include "intrinsics.c" +#include "unicode.c" +#include "mathx.c" +#include "mathx.gen.c" +#include "arena.c" + +#define STB_SPRINTF_IMPLEMENTATION +#include "stb_sprintf.h" +#include "string.c" +#include "string8.c" +#include "scratch.c" +#include "log.c" +#include "lexer.c" +#include "type_info.c" \ No newline at end of file diff --git a/src/core/core.h b/src/core/core.h new file mode 100644 index 0000000..44ff12c --- /dev/null +++ b/src/core/core.h @@ -0,0 +1,5 @@ +#include "platform_defines.h" +#include +#include +#include "types.h" +#include "type_info.h" diff --git a/src/core/intrinsics.c b/src/core/intrinsics.c new file mode 100644 index 0000000..b40fae6 --- /dev/null +++ b/src/core/intrinsics.c @@ -0,0 +1,126 @@ +void memory_copy(void *dst, void *src, size_t n) { +#if PLATFORM_CLANG + __builtin_memcpy(dst, src, n); +#else + memcpy(dst, src, n); +#endif +} + +void memory_move(void *dest, const void *src, size_t n) { +#if PLATFORM_CLANG + __builtin_memmove(dest, src, n); +#else + memmove(dest, src, n); +#endif +} + +void memory_set(void *dst, int c, size_t size) { +#if PLATFORM_CLANG + __builtin_memset(dst, c, size); +#else + memset(dst, c, size); +#endif +} +void memory_zero(void *dst, usize size) { memory_set(dst, 0, size); } + + +#if PLATFORM_CLANG +f32 f32_sqrt(f32 x) { return __builtin_sqrtf(x); } +f64 f64_sqrt(f64 x) { return __builtin_sqrt(x); } +f32 f32_ceil(f32 x) { return __builtin_ceilf(x); } +f64 f64_ceil(f64 x) { return __builtin_ceil(x); } +f32 f32_floor(f32 x) { return __builtin_floorf(x); } +f64 f64_floor(f64 x) { return __builtin_floor(x); } +f32 f32_abs(f32 x) { return __builtin_fabsf(x); } +f64 f64_abs(f64 x) { return __builtin_fabs(x); } +f32 f32_round(f32 x) { return __builtin_roundf(x); } +f64 f64_round(f64 x) { return __builtin_round(x); } +f64 f64_mod(f64 a, f64 b) { return __builtin_fmod(a, b); } +f32 f32_mod(f32 a, f32 b) { return __builtin_fmodf(a, b); } +#else +f32 f32_sqrt(f32 x) { return sqrtf(x); } +f64 f64_sqrt(f64 x) { return sqrt(x); } +f32 f32_ceil(f32 x) { return ceilf(x); } +f64 f64_ceil(f64 x) { return ceil(x); } +f32 f32_floor(f32 x) { return floorf(x); } +f64 f64_floor(f64 x) { return floor(x); } +f32 f32_abs(f32 x) { return fabsf(x); } +f64 f64_abs(f64 x) { return fabs(x); } +f32 f32_round(f32 x) { return roundf(x); } +f64 f64_round(f64 x) { return round(x); } +f64 f64_mod(f64 a, f64 b) { return fmod(a, b); } +f32 f32_mod(f32 a, f32 b) { return fmodf(a, b); } +#endif + +#if PLATFORM_WINDOWS +void *vmem_reserve(size_t size) { + void *result = (uint8_t *)VirtualAlloc(0, size, MEM_RESERVE, PAGE_READWRITE); + return result; +} +b32 vmem_commit(void *p, size_t size) { + void *result = VirtualAlloc(p, size, MEM_COMMIT, PAGE_READWRITE); + return result ? true : false; +} +b32 vmem_release(void *p) { + BOOL result = VirtualFree(p, 0, MEM_RELEASE); + return result ? true : false; +} +b32 vmem_decommit(void *p, size_t size) { + BOOL result = VirtualFree(p, size, MEM_DECOMMIT); + return result ? true : false; +} +#else +void *vmem_reserve(size_t size) { return NULL; } +b32 vmem_commit(void *p, size_t size) { return false; } +b32 vmem_release(void *p) { return true; } +b32 vmem_decommit(void *p, size_t size) { return true; } +#endif + +#if PLATFORM_WASM +f64 s8_deserial_f64(s8_t string); +#else +f64 s8_deserial_f64(s8_t string) { return strtod(string.str, NULL); } +#endif + +#if PLATFORM_WINDOWS +void core_log_message(char *string) { + if (IsDebuggerPresent()) { + OutputDebugStringA(string); + } + printf("%s", string); + fflush(stdout); +} +void core_panic(void) { + if (core_desc.on_exit) core_desc.on_exit(); + ExitProcess(1); +} +#elif PLATFORM_WASM +void write_to_console(char *string); +void core_log_message(char *string) { + write_to_console(string); +} +void core_panic(void) { + if (core_desc.on_exit) core_desc.on_exit(); + debug_break(); +} +#else +void core_log_message(char *string) { + printf("%s"); + fflush(stdout); +} +void core_panic(void) { + if (core_desc.on_exit) core_desc.on_exit(); + debug_break(); +} +#endif + +void core_on_exit(void) { + debugf("program panicked! exiting..."); +} + +THREAD_LOCAL core_desc_t core_desc = { + .print = core_log_message, + .panic = core_panic, + .break_on_panic = true, +}; + diff --git a/src/core/lexer.c b/src/core/lexer.c new file mode 100644 index 0000000..c4a0de5 --- /dev/null +++ b/src/core/lexer.c @@ -0,0 +1,493 @@ +typedef enum lex_kind_t lex_kind_t; +enum lex_kind_t { + #define LEX_KIND_XLIST\ + X(eof, "end of file", "---")\ + X(int, "integer", "---")\ + X(real, "real", "---")\ + X(ident, "identifier", "---")\ + X(string, "string", "---")\ + X(comment, "comment", "---")\ + X(open_brace, "'{' open brace", "{")\ + X(close_brace, "'}' close brace", "}")\ + X(open_paren, "'(' open parenthesis", "(")\ + X(close_paren, "')' close parenthesis", ")")\ + X(open_bracket, "'[' open bracket", "[")\ + X(close_bracket, "']' close bracket", "]")\ + X(plus, "'+' plus", "+")\ + X(minus, "'-' minus", "-")\ + X(divide, "'/' division sign", "/")\ + X(multiply, "'*' multiplication sign", "*")\ + X(modulo, "'%' modulo", "%")\ + X(or, "'||' logical or", "||")\ + X(and, "'&&' logical and", "&&")\ + X(negation, "'!' logical negation", "!")\ + X(bit_negation, "'~' bit negation", "~")\ + X(bit_left_shift, "'<<' bit left shift", "<<")\ + X(bit_right_shift, "'>>' bit right shift", ">>")\ + X(bit_or, "'|' bit or", "|")\ + X(bit_and, "'&' bit and", "&")\ + X(bit_xor, "'^' bit xor", "^")\ + X(decrement, "'--' decrement", "--")\ + X(increment, "'++' increment", "++")\ + X(post_decrement, "'--' post decrement", "--")\ + X(post_increment, "'++' post increment", "++")\ + X(assign, "'=' assignment", "=")\ + X(divide_assign, "'/=' divide assignment", "/=")\ + X(multiply_assign, "'*=' multiply assignment", "*=")\ + X(plus_assign, "'+=' plus assignment", "+=")\ + X(minus_assign, "'-=' minus assignment", "-=")\ + X(modulo_assign, "'%=' modulo assignment", "%=")\ + X(bit_and_assign, "&=", "&=")\ + X(bit_or_assign, "'|=' bit or assignment", "|=")\ + X(bit_xor_assign, "'^=' bit xor assignment", "^=")\ + X(bit_left_shift_assign, "'<<=' bit left shift assignment", "<<=")\ + X(bit_right_shift_assign, "'>>=' bit right shift assignment", ">>=")\ + X(equals, "'==' equals sign", "==")\ + X(not_equals, "'!=' not equals sign", "!=")\ + X(lesser, "'<' lesser then", "<")\ + X(greater, "'>' greater then", ">")\ + X(lesser_or_equal, "'<=' lesser then or equal", "<=")\ + X(greater_or_equal, "'>=' greater then or equal", ">=")\ + X(comma, "',' comma", ",")\ + X(dot, "'.' dot", ".")\ + X(three_dots, "'...' three dots", "...")\ + X(semicolon, "';' semicolon", ";")\ + X(colon, "':' colon", ":")\ + X(arrow, "'->' arrow", "->")\ + X(question, "'?' question mark", "?")\ + + + #define X(KIND, STR, SIMPLE) lex_kind_##KIND, + LEX_KIND_XLIST + #undef X + + lex_kind_count, +}; + +typedef enum lex_suffix_t lex_suffix_t; +enum lex_suffix_t { + #define LEX_SUFFIX_XLIST X(none) X(f) X(d) X(u) X(ul) X(ull) X(l) X(ll) + #define X(KIND) lex_suffix_##KIND, + LEX_SUFFIX_XLIST + #undef X + + lex_suffix_count, +}; + +typedef struct lex_t lex_t; +struct lex_t { + lex_kind_t kind; + lex_suffix_t suffix; + + union { + struct {char *str; i64 len;}; + s8_t s8; + }; + + int line; + int column; + char *file_name; + + union { + u64 integer; + f64 real; + char *error; + }; +}; + +typedef struct lexer_t lexer_t; +struct lexer_t { + char *at; + char *file_name; + int line; + int column; +}; + +typedef struct lex_array_t lex_array_t; +struct lex_array_t { + lex_t *data; + int len; +}; + +void lex_panicf(lex_t *token, const char *str, ...) { + ma_temp_t scratch = ma_begin_scratch(); + S8_FMT(scratch.arena, str, str8); + panicf("%s(%d:%d): error: %S", token->file_name, token->line, token->column, str8); + ma_end_scratch(scratch); +} + +lexer_t lex_make(char *begin, char *file_name) { + lexer_t result = {.at = begin, .file_name = file_name}; + return result; +} + +void lex_advance(lexer_t *lex) { + if (lex->at[0] == 0) return; + if (lex->at[0] == '\n') { lex->column = 0; lex->line += 1; } + lex->column += 1; + lex->at += 1; +} + +b32 lex_match(lexer_t *lex, char c) { + if (lex->at[0] == c) { + lex_advance(lex); + return true; + } + return false; +} + +void lex_eat_whitespace(lexer_t *lex) { + while (char_is_whitespace(lex->at[0])) lex_advance(lex); +} + +u64 lex_map_char_to_int(char c) { + switch (c) { + case '0': return 0; break; + case '1': return 1; break; + case '2': return 2; break; + case '3': return 3; break; + case '4': return 4; break; + case '5': return 5; break; + case '6': return 6; break; + case '7': return 7; break; + case '8': return 8; break; + case '9': return 9; break; + case 'a': + case 'A': return 10; break; + case 'b': + case 'B': return 11; break; + case 'c': + case 'C': return 12; break; + case 'd': + case 'D': return 13; break; + case 'e': + case 'E': return 14; break; + case 'f': + case 'F': return 15; break; + default: return 255; + } +} + +u64 lex_deserial_u64(char *string, i64 len, u64 base) { + assert(base >= 2 && base <= 16); + u64 acc = 0; + for (i64 i = 0; i < len; i++) { + u64 num = lex_map_char_to_int(string[i]); + if (num >= base) { + panicf("invalid number"); + break; + } + acc *= base; + acc += num; + } + return acc; +} + +void lex_eat_number(lexer_t *lex, lex_t *token) { + token->kind = lex_kind_int; + for (;;) { + if (char_is_digit(lex->at[0])) { + lex_advance(lex); + continue; + } + + if (lex_match(lex, '.')) { + if (token->kind == lex_kind_real) { + lex_panicf(token, "multiple '.' periods in floating point number literal"); + } + token->kind = lex_kind_real; + continue; + } + + break; + } + + if (lex_match(lex, 'f')) { + token->kind = lex_kind_real; + token->suffix = lex_suffix_f; + } else if (lex_match(lex, 'd')) { + token->kind = lex_kind_real; + token->suffix = lex_suffix_d; + } else if (token->kind == lex_kind_int && ((lex->at[0] == 'u' && lex->at[1] == 'l' && lex->at[2] == 'l') || (lex->at[0] == 'U' && lex->at[1] == 'L' && lex->at[2] == 'L'))) { + token->suffix = lex_suffix_ull; + lex_advance(lex); lex_advance(lex); lex_advance(lex); + } else if (token->kind == lex_kind_int && ((lex->at[0] == 'u' && lex->at[1] == 'l') || (lex->at[0] == 'U' && lex->at[1] == 'L'))) { + token->suffix = lex_suffix_ul; + lex_advance(lex); lex_advance(lex); + } else if (token->kind == lex_kind_int && (lex->at[0] == 'l' || lex->at[0] == 'L')) { + token->suffix = lex_suffix_l; + lex_advance(lex); + } else if (token->kind == lex_kind_int && ((lex->at[0] == 'l' && lex->at[1] == 'l') || (lex->at[0] == 'L' && lex->at[1] == 'L'))) { + token->suffix = lex_suffix_ll; + lex_advance(lex); lex_advance(lex); + } +} + +void lex_eat_until(lexer_t *lex, char c) { + while (lex->at[0] != c && lex->at[0] != 0) lex_advance(lex); +} + +void lex_eat_string(lexer_t *lex, lex_t *token) { + token->kind = lex_kind_string; + for (;;) { + if (lex_match(lex, token->str[0])) { + break; + } + + if (lex->at[0] == 0) { + lex_panicf(token, "unclosed string"); + } + + lex_advance(lex); + } +} + +#define LEX_CASE3(C1, K1, C2, K2, C3, K3)\ +case C1: {\ + token->kind = K1;\ + if (lex_match(lex, C2)) {\ + lex_advance(lex);\ + token->kind = K2;\ + } else if (lex_match(lex, C3)) {\ + lex_advance(lex);\ + token->kind = K3;\ + }\ +} break + +void lex_token_ex(lexer_t *lex, lex_t *token) { + lex_eat_whitespace(lex); + *token = (lex_t){.str = lex->at, .file_name = lex->file_name, .line = lex->line, .column = lex->column}; + lex_advance(lex); + + switch (token->str[0]) { + case '\0': token->kind = lex_kind_eof; break; + case '{': token->kind = lex_kind_open_brace; break; + case '}': token->kind = lex_kind_close_brace; break; + case '(': token->kind = lex_kind_open_paren; break; + case ')': token->kind = lex_kind_close_paren; break; + case '[': token->kind = lex_kind_open_bracket; break; + case ']': token->kind = lex_kind_close_bracket; break; + case '~': token->kind = lex_kind_bit_negation; break; + case ';': token->kind = lex_kind_semicolon; break; + case ':': token->kind = lex_kind_colon; break; + case ',': token->kind = lex_kind_comma; break; + case '"': lex_eat_string(lex, token); break; + case '`': lex_eat_string(lex, token); break; + case '\'': lex_eat_string(lex, token); break; + + case '.': { + token->kind = lex_kind_dot; + if (lex->at[0] == '.' && lex->at[1] == '.') { + lex_advance(lex); + lex_advance(lex); + token->kind = lex_kind_three_dots; + } + } break; + + case '/': { + token->kind = lex_kind_divide; + if (lex_match(lex, '/')) { + token->kind = lex_kind_comment; + lex_eat_until(lex, '\n'); + } else if (lex_match(lex, '*')) { + token->kind = lex_kind_comment; + for (;;) { + if (lex->at[0] == '*' && lex->at[1] == '/') { + break; + } + if (lex->at[0] == 0) { + lex_panicf(token, "Unclosed block comment"); + return; + } + lex_advance(lex); + } + lex_advance(lex); + lex_advance(lex); + } else if (lex_match(lex, '=')) { + token->kind = lex_kind_divide_assign; + lex_advance(lex); + } + } break; + + LEX_CASE3('^', lex_kind_bit_xor, '=', lex_kind_bit_xor_assign, /*ignored option*/'=', lex_kind_bit_xor_assign); + LEX_CASE3('=', lex_kind_assign, '=', lex_kind_equals, /*ignored option*/'=', lex_kind_equals); + LEX_CASE3('!', lex_kind_negation, '=', lex_kind_not_equals, /*ignored option*/'=', lex_kind_not_equals); + LEX_CASE3('%', lex_kind_modulo, '=', lex_kind_modulo_assign, /*ignored option*/'=', lex_kind_modulo_assign); + LEX_CASE3('*', lex_kind_multiply, '=', lex_kind_multiply_assign, /*ignored option*/'=', lex_kind_multiply_assign); + LEX_CASE3('+', lex_kind_plus, '+', lex_kind_increment, '=', lex_kind_plus_assign); + LEX_CASE3('-', lex_kind_minus, '-', lex_kind_decrement, '=', lex_kind_minus_assign); + LEX_CASE3('&', lex_kind_bit_and, '&', lex_kind_and, '=', lex_kind_bit_and_assign); + LEX_CASE3('|', lex_kind_bit_or, '|', lex_kind_or, '=', lex_kind_bit_or_assign); + + case '>': { + token->kind = lex_kind_greater; + if (lex_match(lex, '=')) { + token->kind = lex_kind_greater_or_equal; + } else if (lex_match(lex, '>')) { + token->kind = lex_kind_bit_right_shift; + if (lex_match(lex, '=')) { + token->kind = lex_kind_bit_right_shift_assign; + } + } + } break; + + case '<': { + token->kind = lex_kind_lesser; + if (lex_match(lex, '=')) { + token->kind = lex_kind_lesser_or_equal; + } + else if (lex_match(lex, '<')) { + token->kind = lex_kind_bit_left_shift; + if (lex_match(lex, '=')) { + token->kind = lex_kind_bit_left_shift_assign; + } + } + } break; + + case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case '0': { + lex_eat_number(lex, token); + } break; + + case 'A': case 'a': case 'B': case 'b': case 'C': case 'c': + case 'D': case 'd': case 'E': case 'e': case 'F': case 'f': + case 'G': case 'g': case 'H': case 'h': case 'I': case 'i': + case 'J': case 'j': case 'K': case 'k': case 'L': case 'l': + case 'M': case 'm': case 'N': case 'n': case 'O': case 'o': + case 'P': case 'p': case 'Q': case 'q': case 'R': case 'r': + case 'S': case 's': case 'T': case 't': case 'U': case 'u': + case 'V': case 'v': case 'W': case 'w': case 'X': case 'x': + case 'Y': case 'y': case 'Z': case 'z': case '_': { + token->kind = lex_kind_ident; + while (char_is_alphanumeric(lex->at[0]) || lex->at[0] == '_') lex_advance(lex); + } break; + + default: { + lex_panicf(token, "found invalid character in the token stream (%d)", token->str[0]); + } + } + + token->len = (int)(lex->at - token->str); + + if (token->kind == lex_kind_int) { + token->integer = lex_deserial_u64(token->str, token->len, 10); + } else if (token->kind == lex_kind_real) { + token->real = s8_deserial_f64(token->s8); + } else if (token->kind == lex_kind_string) { + token->str += 1; + token->len -= 2; + } +} + +lex_t lex_token(lexer_t *lex) { + lex_t result = {0}; + lex_token_ex(lex, &result); + return result; +} + +// @todo: use s8_t instead +lex_array_t lex_tokens(ma_arena_t *arena, char *file_name, char *stream) { + usize align = arena->align; + arena->align = 0; + + lex_array_t token_array = {0}; + lexer_t l = lex_make(stream, file_name); + for (;;) { + lex_t *token = ma_push_type(arena, lex_t); + if (token_array.data == NULL) { + token_array.data = token; + } + token_array.len += 1; + + do { + lex_token_ex(&l, token); + } while (token->kind == lex_kind_comment); + if (token->kind == lex_kind_eof) break; + } + + arena->align = align; + return token_array; +} + +s8_t global_lex_kind_simple_strings[] = { +#define X(KIND, STR, SIMPLE) s8_const_lit(SIMPLE), + LEX_KIND_XLIST +#undef X +}; + +s8_t s8_serial_simple_lex_kind_t(lex_kind_t kind) { + assert(kind >= 0 && kind < lex_kind_count); + return global_lex_kind_simple_strings[kind]; +} + +s8_t global_lex_kind_strings[] = { +#define X(KIND, STR, SIMPLE) s8_const_lit(STR), + LEX_KIND_XLIST +#undef X +}; + +s8_t s8_serial_lex_kind_t(lex_kind_t kind) { + assert(kind >= 0 && kind < lex_kind_count); + return global_lex_kind_strings[kind]; +} + +type_member_t members__lex_kind_t[] = { +#define X(KIND, STR, SIMPLE) {.name = s8_const_lit("lex_kind_" #KIND), .value = lex_kind_##KIND}, + LEX_KIND_XLIST +#undef X +}; +DEFINE_ENUM(lex_kind_t); + +type_member_t members__lex_suffix_t[] = { +#define X(KIND) {.name = s8_const_lit("lex_suffix_" #KIND), .value = lex_suffix_##KIND}, + LEX_SUFFIX_XLIST +#undef X +}; +DEFINE_ENUM(lex_suffix_t); + +// +// +typedef struct parser_t parser_t; +struct parser_t { + ma_arena_t *arena; + lex_t *at; +}; + +#define parser_make(ARENA, TOKEN) &(parser_t){.arena = ARENA, .at = TOKEN} + +lex_t *parser_next(parser_t *par) { + lex_t *result = par->at; + if (result->kind != lex_kind_eof) par->at += 1; + return result; +} + +lex_t *parser_match(parser_t *par, lex_kind_t kind) { + if (par->at->kind == kind) { + return parser_next(par); + } else { + return NULL; + } +} + +lex_t *parser_matchi(parser_t *par, s8_t str) { + if (par->at->kind == lex_kind_ident && s8_equal(par->at->s8, str)) { + return parser_next(par); + } else { + return NULL; + } +} + +lex_t *parser_expect(parser_t *par, lex_kind_t kind) { + lex_t *token = parser_match(par, kind); + if (!token) lex_panicf(par->at, "expected token kind: %S, got instead: %S", s8_serial_lex_kind_t(kind), s8_serial_lex_kind_t(par->at->kind)); + return token; +} + +void parser_eat_until(parser_t *par, lex_kind_t kind) { + while (par->at->kind != kind && par->at->kind != lex_kind_eof) { + parser_next(par); + } +} +void parser_eat_including(parser_t *par, lex_kind_t kind) { + parser_eat_until(par, kind); + parser_next(par); +} \ No newline at end of file diff --git a/src/core/log.c b/src/core/log.c new file mode 100644 index 0000000..690def4 --- /dev/null +++ b/src/core/log.c @@ -0,0 +1,27 @@ +void panicf(const char *_str, ...) { + ma_temp_t scratch = ma_begin_scratch(); + S8_FMT(scratch.arena, _str, str); + core_desc.print(str.str); + core_desc.print("\n"); + if (core_desc.break_on_panic) { + debug_break(); + } else { + core_desc.panic(); + } + ma_end_scratch(scratch); +} + +void debugexf(const char *_str, ...) { + ma_temp_t scratch = ma_begin_scratch(); + S8_FMT(scratch.arena, _str, str); + core_desc.print(str.str); + ma_end_scratch(scratch); +} + +void debugf(const char *_str, ...) { + ma_temp_t scratch = ma_begin_scratch(); + S8_FMT(scratch.arena, _str, str); + core_desc.print(str.str); + core_desc.print("\n"); + ma_end_scratch(scratch); +} \ No newline at end of file diff --git a/src/core/math_gen.py b/src/core/math_gen.py new file mode 100644 index 0000000..0a4ef07 --- /dev/null +++ b/src/core/math_gen.py @@ -0,0 +1,291 @@ +import sys + +op_idx = 0 +opname_idx = 1 +symbols = [ + ["+", "add"], + ["-", "sub"], + ["*", "mul"], + ["/", "div"], +] + +basic_types = [ + "f32", + "f64", + "i32", + "i64", +] + +name_idx = 0 +member_idx = 1 + +vec_types = [ + ["v2", ["x", "y"]], + ["v3", ["x", "y", "z"]], + ["v4", ["x", "y", "z", "w"]], +] + +rect_types = [ + ["r2", ["x0", "y0", "x1", "y1"]], + ["r3", ["x0", "y0", "z0", "x1", "y1", "z1"]], +] + +struct_types = vec_types + rect_types + + +# fd = open("mathx.gen.h", "w") +# sys.stdout = fd + +# print("// auto generated using:", __file__) + +# for bt in basic_types: +# s = """ +# typedef union v2f64_t v2f64_t; +# union v2f64_t { +# struct { f64 x, y; }; +# f64 e[2]; +# }; + +# typedef union v3f64_t v3f64_t; +# union v3f64_t { +# struct { f64 x, y, z; }; +# struct { v2f64_t xy; }; +# struct { f64 _x0; v2f64_t zw; }; +# struct { f64 r, g, b; }; +# struct { f64 h, s, l; }; +# f64 e[3]; +# }; + +# typedef union v4f64_t v4f64_t; +# union v4f64_t { +# struct { f64 x, y, z, w; }; +# struct { v2f64_t xy; v2f64_t zw; }; +# struct { v3f64_t xyz; }; +# struct { f64 _x0; f64 yzw; }; +# struct { f64 r, g, b, a; }; +# struct { f64 h, s, l, _a; }; +# f64 e[4]; +# }; + +# typedef union r1f64_t r1f64_t; +# union r1f64_t { +# struct { f64 min, max; }; +# struct { f64 x0, x1; }; +# f64 e[2]; +# }; + +# typedef union r2f64_t r2f64_t; +# union r2f64_t { +# struct { v2f64_t min, max; }; +# struct { f64 x0, y0, x1, y1; }; +# v4f64_t e4; +# f64 e[4]; +# }; + +# typedef union r3f64_t r3f64_t; +# union r3f64_t { +# struct { v3f64_t min, max; }; +# struct { f64 x0, y0, z0, x1, y1, z1; }; +# f64 e[6]; +# }; +# +# +# type_t type__v2f64_t = { type_kind_struct, s8_const_lit("v2f64_t"), sizeof(v2f64_t), .count = 2, +# .members = (type_member_t[]){ +# {s8_const_lit("x"), &type__f64, .offset = offsetof(v2f64_t, x)}, +# {s8_const_lit("y"), &type__f64, .offset = offsetof(v2f64_t, y)}, +# } +# }; + +# type_t type__v3f64_t = { type_kind_struct, s8_const_lit("v3f64_t"), sizeof(v3f64_t), .count = 3, +# .members = (type_member_t[]){ +# {s8_const_lit("x"), &type__f64, .offset = offsetof(v3f64_t, x)}, +# {s8_const_lit("y"), &type__f64, .offset = offsetof(v3f64_t, y)}, +# {s8_const_lit("z"), &type__f64, .offset = offsetof(v3f64_t, z)}, +# } +# }; + +# type_t type__v4f64_t = { type_kind_struct, s8_const_lit("v4f64_t"), sizeof(v4f64_t), .count = 4, +# .members = (type_member_t[]){ +# {s8_const_lit("x"), &type__f64, .offset = offsetof(v4f64_t, x)}, +# {s8_const_lit("y"), &type__f64, .offset = offsetof(v4f64_t, y)}, +# {s8_const_lit("z"), &type__f64, .offset = offsetof(v4f64_t, z)}, +# {s8_const_lit("w"), &type__f64, .offset = offsetof(v4f64_t, w)}, +# } +# }; + +# type_t type__r3f64_t = { type_kind_struct, s8_const_lit("r3f64_t"), sizeof(r3f64_t), .count = 6, +# .members = (type_member_t[]){ +# {s8_const_lit("x0"), &type__f64, .offset = offsetof(r3f64_t, x0)}, +# {s8_const_lit("y0"), &type__f64, .offset = offsetof(r3f64_t, y0)}, +# {s8_const_lit("x0"), &type__f64, .offset = offsetof(r3f64_t, z0)}, +# {s8_const_lit("x1"), &type__f64, .offset = offsetof(r3f64_t, x1)}, +# {s8_const_lit("y1"), &type__f64, .offset = offsetof(r3f64_t, y1)}, +# {s8_const_lit("x1"), &type__f64, .offset = offsetof(r3f64_t, z1)}, +# } +# }; + +# type_member_t members__r2f64_t[] = { +# {s8_const_lit("x0"), &type__f64, .offset = offsetof(r2f64_t, x0)}, +# {s8_const_lit("y0"), &type__f64, .offset = offsetof(r2f64_t, y0)}, +# {s8_const_lit("x1"), &type__f64, .offset = offsetof(r2f64_t, x1)}, +# {s8_const_lit("y1"), &type__f64, .offset = offsetof(r2f64_t, y1)}, +# }; +# DEFINE_STRUCT(r2f64_t); + +# type_member_t members__r1f64_t[] = { +# {s8_const_lit("x0"), &type__f64, .offset = offsetof(r1f64_t, x0)}, +# {s8_const_lit("x1"), &type__f64, .offset = offsetof(r1f64_t, x1)}, +# }; +# DEFINE_STRUCT(r1f64_t); +# """ +# s = s.replace("f64", bt) +# print(s) + + +fd = open("mathx.gen.c", "w") +sys.stdout = fd + +print("// auto generated using:", __file__) + +for basic_type in basic_types: + for st in struct_types: + type = f"{st[name_idx]}{basic_type}" + typedef = f"{type}_t" + members = st[member_idx] + + def members_as_func_params(members, basic_type): + result = "" + for i, m in enumerate(members): + result += f"{basic_type} {m}" + if i != len(members) - 1: + result += ", " + return result + + print(f"""{type}_t {type}({members_as_func_params(members, basic_type)}) {{ return ({type}_t){{ {members_as_func_params(members, "")} }}; }}""") + + for sym in symbols: + op = sym[op_idx] + opname = sym[opname_idx] + + + def op_str(members, op, skip_right = False): + result = "" + for i in range(len(members)): + mem = members[i] + if skip_right: + result += f"a.{mem} {op} b" + else: + result += f"a.{mem} {op} b.{mem}" + + if i != len(members) - 1: + result += ", " + return result + + + + print(f"{type}_t {type}_{opname}({type}_t a, {type}_t b) {{ return ({type}_t){{ {op_str(members, op)} }}; }}") + print(f"{type}_t {type}_{opname}s({type}_t a, {basic_type} b) {{ return ({type}_t){{ {op_str(members, op, skip_right = True)} }}; }}") + +for basic_type in basic_types: + for i in range(len(rect_types)): + vec_type = vec_types[i] + rect_type = rect_types[i] + + vec_name = vec_type[name_idx] + basic_type + vec_members = vec_type[member_idx] + rect_name = rect_type[name_idx] + basic_type + rect_members = rect_type[member_idx] + + print(f"{rect_name}_t {rect_name}_mindim({vec_name}_t pos, {vec_name}_t size) {{ return ({rect_name}_t){{ pos, {vec_name}_add(pos, size) }}; }}") + print(f"{rect_name}_t {rect_name}_center_halfdim({vec_name}_t center, {vec_name}_t halfdim) {{ return ({rect_name}_t){{ {vec_name}_sub(center, halfdim), {vec_name}_add(center, halfdim) }}; }}") + +for basic_type in basic_types: + s = """ +r2f64_t r2f64_cut_left(r2f64_t *r, f64 value) { + f64 minx = r->min.x; + r->min.x = MIN(r->min.x + value, r->max.x); + return (r2f64_t){ .min = {.x = minx, .y = r->min.y}, .max = {.x = r->min.x, .y =r->max.y} }; +} +r2f64_t r2f64_cut_right(r2f64_t *r, f64 value) { + f64 maxx = r->max.x; + r->max.x = MAX(r->min.x, r->max.x - value); + return (r2f64_t){ .min = {.x = r->max.x, .y = r->min.y}, .max = {.x = maxx, .y = r->max.y} }; +} +r2f64_t r2f64_cut_top(r2f64_t *r, f64 value) { /* Y is down */ + f64 miny = r->min.y; + r->min.y = MIN(r->max.y, r->min.y + value); + return (r2f64_t){ .min = {.x = r->min.x, .y = miny}, .max = {.x = r->max.x, .y = r->min.y} }; +} +r2f64_t r2f64_cut_bottom(r2f64_t *r, f64 value) { /* Y is down */ + f64 maxy = r->max.y; + r->max.y = MAX(r->min.y, r->max.y - value); + return (r2f64_t){ .min = {.x = r->min.x, .y = r->max.y}, .max = {.x = r->max.x, .y = maxy} }; +} + +// get past left +r2f64_t r2f64_getp_left(const r2f64_t *rect, f64 value) { + r2f64_t result = r2f64(rect->min.x - value, rect->min.y, rect->min.x, rect->max.y); + return result; +} +r2f64_t r2f64_getp_right(const r2f64_t *rect, f64 value) { + r2f64_t result = r2f64(rect->max.x, rect->min.y, rect->max.x + value, rect->max.y); + return result; +} +r2f64_t r2f64_getp_bottom(const r2f64_t *rect, f64 value) { + r2f64_t result = r2f64(rect->min.x, rect->max.y, rect->max.x, rect->max.y + value); + return result; +} +r2f64_t r2f64_getp_top(const r2f64_t *rect, f64 value) { + r2f64_t result = r2f64(rect->min.x, rect->min.y - value, rect->max.x, rect->min.y); + return result; +} + +r2f64_t r2f64_get_left(const r2f64_t *r, f64 value) { + f64 minx = r->min.x; + r2f64_t result = (r2f64_t){ .min = {.x = minx, .y = r->min.y}, .max = {.x = MIN(r->min.x + value, r->max.x), .y =r->max.y} }; + return result; +} +r2f64_t r2f64_get_right(const r2f64_t *r, f64 value) { + f64 maxx = r->max.x; + r2f64_t result = (r2f64_t){ .min = {.x = MAX(r->min.x, r->max.x - value), .y = r->min.y}, .max = {.x = maxx, .y = r->max.y} }; + return result; +} +r2f64_t r2f64_get_top(const r2f64_t *r, f64 value) { /* Y is down */ + f64 miny = r->min.y; + r2f64_t result = (r2f64_t){ .min = {.x = r->min.x, .y = miny}, .max = {.x = r->max.x, .y = MIN(r->max.y, r->min.y + value)} }; + return result; +} +r2f64_t r2f64_get_bottom(const r2f64_t *r, f64 value) { /* Y is down */ + f64 maxy = r->max.y; + r2f64_t result = (r2f64_t){ .min = {.x = r->min.x, .y = MAX(r->min.y, r->max.y - value)}, .max = {.x = r->max.x, .y = maxy} }; + return result; +} + +r2f64_t r2f64_shrink(r2f64_t rect, v2f64_t value) { return (r2f64_t){ v2f64_add(rect.min, value), v2f64_sub(rect.max, value) }; } +v2f64_t r2f64_get_size(r2f64_t r) { return (v2f64_t){r.max.x - r.min.x, r.max.y - r.min.y}; } +v2f64_t r2f64_get_mid(r2f64_t r) { return v2f64_add(r.min, v2f64_divs(r2f64_get_size(r), 2)); } +b32 r2f64_contains(r2f64_t rec, v2f64_t point) { return (point.x >= rec.min.x) && (point.x < rec.max.x) && (point.y >= rec.min.y) && (point.y < rec.max.y); } + """ + s = s.replace("r2f64", f"r2{basic_type}") + s = s.replace("f64", basic_type) + s = s.replace("v2f64", f"v2{basic_type}") + print(s) + +for at in basic_types: + for bt in basic_types: + if at == bt: continue + for st in struct_types: + atype = f"{st[name_idx]}{at}" + btype = f"{st[name_idx]}{bt}" + members = st[member_idx] + + def cast_members(members, type): + result = "" + for i in range(len(members)): + result += f"({type})v.{members[i]}" + if i != len(members) - 1: + result += ", " + return result + + print(f"{btype}_t {atype}_to_{btype}({atype}_t v) {{ return ({btype}_t){{ {cast_members(members, bt)} }}; }}") \ No newline at end of file diff --git a/src/core/mathx.c b/src/core/mathx.c new file mode 100644 index 0000000..daa7fac --- /dev/null +++ b/src/core/mathx.c @@ -0,0 +1,520 @@ +#define F64_PI (3.1415926535897932384626433832795028841971693994) +#define F64_DEG2RAD (F64_PI / 180.0); // @Usage: degree * DEG2RAD = radians; +#define F64_RAD2DEG (180.0 / F64_PI); +#define F32_PI ((f32)F64_PI) + +#define rgba_macro_const(r, g, b, a) { r / 255.0f, g / 255.0f, b / 255.0f, a / 255.0f } + +v4f32_t primary_color_global = rgba_macro_const(245, 238, 230, 255); +v4f32_t secondary_color_global = rgba_macro_const(255, 248, 227, 255); +v4f32_t accent1_color_global = rgba_macro_const(243, 215, 202, 255); +v4f32_t accent2_color_global = rgba_macro_const(230, 164, 180, 255); + +v4f32_t white_color_global = rgba_macro_const(255, 255, 255, 255); +v4f32_t black_color_global = rgba_macro_const(0, 0, 0, 255); + +/* + * Converts an RGB color value to HSL. Conversion formula + * adapted from http://en.wikipedia.org/wiki/HSL_color_space. + * also https://gist.github.com/ciembor/1494530 + * alpha is ignored + */ +v3f32_t v3f32_rgb_to_hsl(v3f32_t color) { + v3f32_t result; + f32 max = MAX(MAX(color.r, color.g), color.b); + f32 min = MIN(MIN(color.r, color.g), color.b); + + result.h = result.s = result.l = (max + min) / 2.f; + + if (max == min) { + result.h = result.s = 0.f; // achromatic + } else { + f32 d = max - min; + result.s = (result.l > 0.5f) ? d / (2.f - max - min) : d / (max + min); + + if (max == color.r) { + result.h = (color.g - color.b) / d + (color.g < color.b ? 6.f : 0.f); + } else if (max == color.g) { + result.h = (color.b - color.r) / d + 2.f; + } else if (max == color.b) { + result.h = (color.r - color.g) / d + 4.f; + } + + result.h /= 6.f; + } + + return result; +} + +/* + * Converts an HUE to r, g or b. + * returns f32 in the set [0, 1]. + */ +f32 f32_hue_to_rgb(f32 p, f32 q, f32 t) { + if (t < 0.f) + t += 1.f; + if (t > 1.f) + t -= 1.f; + if (t < 1.f / 6.f) + return p + (q - p) * 6.f * t; + if (t < 1.f / 2.f) + return q; + if (t < 2.f / 3.f) + return p + (q - p) * (2.f / 3.f - t) * 6.f; + return p; +} + +v3f32_t v3f32_hsl_to_rgb(v3f32_t color) { + v3f32_t result; + + if (0 == color.s) { + result.r = result.g = result.b = color.l; // achromatic + } else { + f32 q = color.l < 0.5f ? color.l * (1.f + color.s) + : color.l + color.s - color.l * color.s; + f32 p = 2.f * color.l - q; + result.r = f32_hue_to_rgb(p, q, color.h + 1.f / 3.f); + result.g = f32_hue_to_rgb(p, q, color.h); + result.b = f32_hue_to_rgb(p, q, color.h - 1.f / 3.f); + } + return result; +} + +v4f32_t v4f32_rgba_to_hsla(v4f32_t rgba) { + v3f32_t result = v3f32_rgb_to_hsl(rgba.xyz); + return (v4f32_t){result.r, result.g, result.b, rgba.a}; +} + +v4f32_t v4f32_hsl_to_rgb(v4f32_t color) { + v3f32_t result = v3f32_hsl_to_rgb(color.xyz); + return (v4f32_t){result.r, result.g, result.b, color.a}; +} + +/* +https://git.musl-libc.org/cgit/musl/tree/src/math +https://gitlab.com/nakst/essence/-/blob/master/shared/math.cpp +*/ + + + +f64 f64_lerp(f64 a, f64 b, f64 t) { return (1 - t) * a + t * b; } +f64 f64_smooth_step(f64 t) { return t*t*(3-2*t); } +f64 f64_ease_in_quint(f64 x) { return x * x * x * x * x; } +f64 f64_ease_in_cubic(f64 x) { return x * x * x; } +f64 f64_ping_pong(f64 x) { return 1.0 - f64_abs(1.0 - f64_mod(x,2)); } + +f32 f32_lerp(f32 a, f32 b, f32 t) { return (1 - t) * a + t * b; } +f32 f32_smooth_step(f32 t) { return t*t*(3-2*t); } +f32 f32_ease_in_quint(f32 x) { return x * x * x * x * x; } +f32 f32_ease_in_cubic(f32 x) { return x * x * x; } +f32 f32_ping_pong(f32 x) { return 1.0f - f32_abs(1.0f - f32_mod(x,2)); } + +v4f32_t v4f32_lerp(v4f32_t a, v4f32_t b, f32 t) { + return (v4f32_t){f32_lerp(a.x, a.y, t), f32_lerp(a.y, b.y, t), f32_lerp(a.z, b.z, t), f32_lerp(a.w, b.w, t)}; +} + +#if PLATFORM_CL +#pragma warning(disable: 4116) +#endif + +#define U64ToF64(x) (((union { f64 d; u64 i; }) { .i = (x) }).d) +#define U32ToF32(x) (((union { f32 f; u32 i; }) { .i = (x) }).f) + +f64 _Sine(f64 x) { + // Calculates sin(x) for x in [0, pi/4]. + + f64 x2 = x * x; + + return x * (U64ToF64(0x3FF0000000000000) + x2 * (U64ToF64(0xBFC5555555555540) + x2 * (U64ToF64(0x3F8111111110ED80) + x2 * (U64ToF64(0xBF2A01A019AE6000) + + x2 * (U64ToF64(0x3EC71DE349280000) + x2 * (U64ToF64(0xBE5AE5DC48000000) + x2 * U64ToF64(0x3DE5D68200000000))))))); +} + +f32 _SineFloat(f32 x) { + // Calculates sin(x) for x in [0, pi/4]. + + f32 x2 = x * x; + + return x * (U32ToF32(0x3F800000) + x2 * (U32ToF32(0xBE2AAAA0) + x2 * (U32ToF32(0x3C0882C0) + x2 * U32ToF32(0xB94C6000)))); +} + +f64 _ArcSine(f64 x) { + // Calculates arcsin(x) for x in [0, 0.5]. + + f64 x2 = x * x; + + return x * (U64ToF64(0x3FEFFFFFFFFFFFE6) + x2 * (U64ToF64(0x3FC555555555FE00) + x2 * (U64ToF64(0x3FB333333292DF90) + x2 * (U64ToF64(0x3FA6DB6DFD3693A0) + + x2 * (U64ToF64(0x3F9F1C608DE51900) + x2 * (U64ToF64(0x3F96EA0659B9A080) + x2 * (U64ToF64(0x3F91B4ABF1029100) + + x2 * (U64ToF64(0x3F8DA8DAF31ECD00) + x2 * (U64ToF64(0x3F81C01FD5000C00) + x2 * (U64ToF64(0x3F94BDA038CF6B00) + + x2 * (U64ToF64(0xBF8E849CA75B1E00) + x2 * U64ToF64(0x3FA146C2D37F2C60)))))))))))); +} + +f32 _ArcSineFloat(f32 x) { + // Calculates arcsin(x) for x in [0, 0.5]. + + f32 x2 = x * x; + + return x * (U32ToF32(0x3F800004) + x2 * (U32ToF32(0x3E2AA130) + x2 * (U32ToF32(0x3D9B2C28) + x2 * (U32ToF32(0x3D1C1800) + x2 * U32ToF32(0x3D5A97C0))))); +} + +f64 _ArcTangent(f64 x) { + // Calculates arctan(x) for x in [0, 0.5]. + + f64 x2 = x * x; + + return x * (U64ToF64(0x3FEFFFFFFFFFFFF8) + x2 * (U64ToF64(0xBFD5555555553B44) + x2 * (U64ToF64(0x3FC9999999803988) + x2 * (U64ToF64(0xBFC249248C882E80) + + x2 * (U64ToF64(0x3FBC71C5A4E4C220) + x2 * (U64ToF64(0xBFB745B3B75243F0) + x2 * (U64ToF64(0x3FB3AFAE9A2939E0) + + x2 * (U64ToF64(0xBFB1030C4A4A1B90) + x2 * (U64ToF64(0x3FAD6F65C35579A0) + x2 * (U64ToF64(0xBFA805BCFDAFEDC0) + + x2 * (U64ToF64(0x3F9FC6B5E115F2C0) + x2 * U64ToF64(0xBF87DCA5AB25BF80)))))))))))); +} + +f32 _ArcTangentFloat(f32 x) { + // Calculates arctan(x) for x in [0, 0.5]. + + f32 x2 = x * x; + + return x * (U32ToF32(0x3F7FFFF8) + x2 * (U32ToF32(0xBEAAA53C) + x2 * (U32ToF32(0x3E4BC990) + x2 * (U32ToF32(0xBE084A60) + x2 * U32ToF32(0x3D8864B0))))); +} + +f64 _Cosine(f64 x) { + // Calculates cos(x) for x in [0, pi/4]. + + f64 x2 = x * x; + + return U64ToF64(0x3FF0000000000000) + x2 * (U64ToF64(0xBFDFFFFFFFFFFFA0) + x2 * (U64ToF64(0x3FA555555554F7C0) + x2 * (U64ToF64(0xBF56C16C16475C00) + + x2 * (U64ToF64(0x3EFA019F87490000) + x2 * (U64ToF64(0xBE927DF66B000000) + x2 * U64ToF64(0x3E21B949E0000000)))))); +} + +f32 _CosineFloat(f32 x) { + // Calculates cos(x) for x in [0, pi/4]. + + f32 x2 = x * x; + + return U32ToF32(0x3F800000) + x2 * (U32ToF32(0xBEFFFFDA) + x2 * (U32ToF32(0x3D2A9F60) + x2 * U32ToF32(0xBAB22C00))); +} + +f64 _Tangent(f64 x) { + // Calculates tan(x) for x in [0, pi/4]. + + f64 x2 = x * x; + + return x * (U64ToF64(0x3FEFFFFFFFFFFFE8) + x2 * (U64ToF64(0x3FD5555555558000) + x2 * (U64ToF64(0x3FC1111110FACF90) + x2 * (U64ToF64(0x3FABA1BA266BFD20) + + x2 * (U64ToF64(0x3F9664F30E56E580) + x2 * (U64ToF64(0x3F822703B08BDC00) + x2 * (U64ToF64(0x3F6D698D2E4A4C00) + + x2 * (U64ToF64(0x3F57FF4F23EA4400) + x2 * (U64ToF64(0x3F424F3BEC845800) + x2 * (U64ToF64(0x3F34C78CA9F61000) + + x2 * (U64ToF64(0xBF042089F8510000) + x2 * (U64ToF64(0x3F29D7372D3A8000) + x2 * (U64ToF64(0xBF19D1C5EF6F0000) + + x2 * (U64ToF64(0x3F0980BDF11E8000))))))))))))))); +} + +f32 _TangentFloat(f32 x) { + // Calculates tan(x) for x in [0, pi/4]. + + f32 x2 = x * x; + + return x * (U32ToF32(0x3F800001) + x2 * (U32ToF32(0x3EAAA9AA) + x2 * (U32ToF32(0x3E08ABA8) + x2 * (U32ToF32(0x3D58EC90) + + x2 * (U32ToF32(0x3CD24840) + x2 * (U32ToF32(0x3AC3CA00) + x2 * U32ToF32(0x3C272F00))))))); +} + + +f64 f64_sin(f64 x) { + b32 negate = false; + + // x in -infty, infty + + if (x < 0) { + x = -x; + negate = true; + } + + // x in 0, infty + + x -= 2 * F64_PI * f64_floor(x / (2 * F64_PI)); + + // x in 0, 2*pi + + if (x < F64_PI / 2) { + } else if (x < F64_PI) { + x = F64_PI - x; + } else if (x < 3 * F64_PI / 2) { + x = x - F64_PI; + negate = !negate; + } else { + x = F64_PI * 2 - x; + negate = !negate; + } + + // x in 0, pi/2 + + f64 y = x < F64_PI / 4 ? _Sine(x) : _Cosine(F64_PI / 2 - x); + return negate ? -y : y; +} + + +f32 f32_sin(f32 x) { + b32 negate = false; + + // x in -infty, infty + + if (x < 0) { + x = -x; + negate = true; + } + + // x in 0, infty + + x -= 2 * F32_PI * f32_floor(x / (2 * F32_PI)); + + // x in 0, 2*pi + + if (x < F32_PI / 2) { + } else if (x < F32_PI) { + x = F32_PI - x; + } else if (x < 3 * F32_PI / 2) { + x = x - F32_PI; + negate = !negate; + } else { + x = F32_PI * 2 - x; + negate = !negate; + } + + // x in 0, pi/2 + + f32 y = x < F32_PI / 4 ? _SineFloat(x) : _CosineFloat(F32_PI / 2 - x); + return negate ? -y : y; +} + +f64 f64_cos(f64 x) { + b32 negate = false; + + // x in -infty, infty + + if (x < 0) { + x = -x; + } + + // x in 0, infty + + x -= 2 * F64_PI * f64_floor(x / (2 * F64_PI)); + + // x in 0, 2*pi + + if (x < F64_PI / 2) { + } else if (x < F64_PI) { + x = F64_PI - x; + negate = !negate; + } else if (x < 3 * F64_PI / 2) { + x = x - F64_PI; + negate = !negate; + } else { + x = F64_PI * 2 - x; + } + + // x in 0, pi/2 + + f64 y = x < F64_PI / 4 ? _Cosine(x) : _Sine(F64_PI / 2 - x); + return negate ? -y : y; +} + +f32 f32_cos(f32 x) { + b32 negate = false; + + // x in -infty, infty + + if (x < 0) { + x = -x; + } + + // x in 0, infty + + x -= 2 * F32_PI * f32_floor(x / (2 * F32_PI)); + + // x in 0, 2*pi + + if (x < F32_PI / 2) { + } else if (x < F32_PI) { + x = F32_PI - x; + negate = !negate; + } else if (x < 3 * F32_PI / 2) { + x = x - F32_PI; + negate = !negate; + } else { + x = F32_PI * 2 - x; + } + + // x in 0, pi/2 + + f32 y = x < F32_PI / 4 ? _CosineFloat(x) : _SineFloat(F32_PI / 2 - x); + return negate ? -y : y; +} + +f64 f64_tan(f64 x) { + b32 negate = false; + + // x in -infty, infty + + if (x < 0) { + x = -x; + negate = !negate; + } + + // x in 0, infty + + x -= F64_PI * f64_floor(x / F64_PI); + + // x in 0, pi + + if (x > F64_PI / 2) { + x = F64_PI - x; + negate = !negate; + } + + // x in 0, pi/2 + + f64 y = x < F64_PI / 4 ? _Tangent(x) : (1.0 / _Tangent(F64_PI / 2 - x)); + return negate ? -y : y; +} + +f32 f32_tan(f32 x) { + b32 negate = false; + + // x in -infty, infty + + if (x < 0) { + x = -x; + negate = !negate; + } + + // x in 0, infty + + x -= F32_PI * f32_floor(x / F32_PI); + + // x in 0, pi + + if (x > F32_PI / 2) { + x = F32_PI - x; + negate = !negate; + } + + // x in 0, pi/2 + + f32 y = x < F32_PI / 4 ? _TangentFloat(x) : (1.0f / _TangentFloat(F32_PI / 2 - x)); + return negate ? -y : y; +} + +f64 f64_asin(f64 x) { + b32 negate = false; + + if (x < 0) { + x = -x; + negate = true; + } + + f64 y; + + if (x < 0.5) { + y = _ArcSine(x); + } else { + y = F64_PI / 2 - 2 * _ArcSine(f64_sqrt(0.5 - 0.5 * x)); + } + + return negate ? -y : y; +} + +f32 f32_asin(f32 x) { + b32 negate = false; + + if (x < 0) { + x = -x; + negate = true; + } + + f32 y; + + if (x < 0.5) { + y = _ArcSineFloat(x); + } else { + y = F32_PI / 2 - 2 * _ArcSineFloat(f32_sqrt(0.5f - 0.5f * x)); + } + + return negate ? -y : y; +} + +f64 f64_acos(f64 x) { + return f64_asin(-x) + F64_PI / 2; +} + +f32 f32_acos(f32 x) { + return f32_asin(-x) + F32_PI / 2; +} + +f64 f64_atan(f64 x) { + b32 negate = false; + + if (x < 0) { + x = -x; + negate = true; + } + + b32 reciprocalTaken = false; + + if (x > 1) { + x = 1 / x; + reciprocalTaken = true; + } + + f64 y; + + if (x < 0.5) { + y = _ArcTangent(x); + } else { + y = 0.463647609000806116 + _ArcTangent((2 * x - 1) / (2 + x)); + } + + if (reciprocalTaken) { + y = F64_PI / 2 - y; + } + + return negate ? -y : y; +} + +f32 f32_atan(f32 x) { + b32 negate = false; + + if (x < 0) { + x = -x; + negate = true; + } + + b32 reciprocalTaken = false; + + if (x > 1) { + x = 1 / x; + reciprocalTaken = true; + } + + f32 y; + + if (x < 0.5f) { + y = _ArcTangentFloat(x); + } else { + y = 0.463647609000806116f + _ArcTangentFloat((2 * x - 1) / (2 + x)); + } + + if (reciprocalTaken) { + y = F32_PI / 2 - y; + } + + return negate ? -y : y; +} + +f64 f64_atan2(f64 y, f64 x) { + if (x == 0) return y > 0 ? F64_PI / 2 : -F64_PI / 2; + else if (x > 0) return f64_atan(y / x); + else if (y >= 0) return F64_PI + f64_atan(y / x); + else return -F64_PI + f64_atan(y / x); +} + +f32 f32_atan2(f32 y, f32 x) { + if (x == 0) return y > 0 ? F32_PI / 2 : -F32_PI / 2; + else if (x > 0) return f32_atan(y / x); + else if (y >= 0) return F32_PI + f32_atan(y / x); + else return -F32_PI + f32_atan(y / x); +} diff --git a/src/core/mathx.gen.c b/src/core/mathx.gen.c new file mode 100644 index 0000000..ca25f46 --- /dev/null +++ b/src/core/mathx.gen.c @@ -0,0 +1,519 @@ +// auto generated using: D:\dev\wasm\src\core\math_gen.py +v2f32_t v2f32(f32 x, f32 y) { return (v2f32_t){ x, y }; } +v2f32_t v2f32_add(v2f32_t a, v2f32_t b) { return (v2f32_t){ a.x + b.x, a.y + b.y }; } +v2f32_t v2f32_adds(v2f32_t a, f32 b) { return (v2f32_t){ a.x + b, a.y + b }; } +v2f32_t v2f32_sub(v2f32_t a, v2f32_t b) { return (v2f32_t){ a.x - b.x, a.y - b.y }; } +v2f32_t v2f32_subs(v2f32_t a, f32 b) { return (v2f32_t){ a.x - b, a.y - b }; } +v2f32_t v2f32_mul(v2f32_t a, v2f32_t b) { return (v2f32_t){ a.x * b.x, a.y * b.y }; } +v2f32_t v2f32_muls(v2f32_t a, f32 b) { return (v2f32_t){ a.x * b, a.y * b }; } +v2f32_t v2f32_div(v2f32_t a, v2f32_t b) { return (v2f32_t){ a.x / b.x, a.y / b.y }; } +v2f32_t v2f32_divs(v2f32_t a, f32 b) { return (v2f32_t){ a.x / b, a.y / b }; } +v3f32_t v3f32(f32 x, f32 y, f32 z) { return (v3f32_t){ x, y, z }; } +v3f32_t v3f32_add(v3f32_t a, v3f32_t b) { return (v3f32_t){ a.x + b.x, a.y + b.y, a.z + b.z }; } +v3f32_t v3f32_adds(v3f32_t a, f32 b) { return (v3f32_t){ a.x + b, a.y + b, a.z + b }; } +v3f32_t v3f32_sub(v3f32_t a, v3f32_t b) { return (v3f32_t){ a.x - b.x, a.y - b.y, a.z - b.z }; } +v3f32_t v3f32_subs(v3f32_t a, f32 b) { return (v3f32_t){ a.x - b, a.y - b, a.z - b }; } +v3f32_t v3f32_mul(v3f32_t a, v3f32_t b) { return (v3f32_t){ a.x * b.x, a.y * b.y, a.z * b.z }; } +v3f32_t v3f32_muls(v3f32_t a, f32 b) { return (v3f32_t){ a.x * b, a.y * b, a.z * b }; } +v3f32_t v3f32_div(v3f32_t a, v3f32_t b) { return (v3f32_t){ a.x / b.x, a.y / b.y, a.z / b.z }; } +v3f32_t v3f32_divs(v3f32_t a, f32 b) { return (v3f32_t){ a.x / b, a.y / b, a.z / b }; } +v4f32_t v4f32(f32 x, f32 y, f32 z, f32 w) { return (v4f32_t){ x, y, z, w }; } +v4f32_t v4f32_add(v4f32_t a, v4f32_t b) { return (v4f32_t){ a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w }; } +v4f32_t v4f32_adds(v4f32_t a, f32 b) { return (v4f32_t){ a.x + b, a.y + b, a.z + b, a.w + b }; } +v4f32_t v4f32_sub(v4f32_t a, v4f32_t b) { return (v4f32_t){ a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w }; } +v4f32_t v4f32_subs(v4f32_t a, f32 b) { return (v4f32_t){ a.x - b, a.y - b, a.z - b, a.w - b }; } +v4f32_t v4f32_mul(v4f32_t a, v4f32_t b) { return (v4f32_t){ a.x * b.x, a.y * b.y, a.z * b.z, a.w * b.w }; } +v4f32_t v4f32_muls(v4f32_t a, f32 b) { return (v4f32_t){ a.x * b, a.y * b, a.z * b, a.w * b }; } +v4f32_t v4f32_div(v4f32_t a, v4f32_t b) { return (v4f32_t){ a.x / b.x, a.y / b.y, a.z / b.z, a.w / b.w }; } +v4f32_t v4f32_divs(v4f32_t a, f32 b) { return (v4f32_t){ a.x / b, a.y / b, a.z / b, a.w / b }; } +r2f32_t r2f32(f32 x0, f32 y0, f32 x1, f32 y1) { return (r2f32_t){ x0, y0, x1, y1 }; } +r2f32_t r2f32_add(r2f32_t a, r2f32_t b) { return (r2f32_t){ a.x0 + b.x0, a.y0 + b.y0, a.x1 + b.x1, a.y1 + b.y1 }; } +r2f32_t r2f32_adds(r2f32_t a, f32 b) { return (r2f32_t){ a.x0 + b, a.y0 + b, a.x1 + b, a.y1 + b }; } +r2f32_t r2f32_sub(r2f32_t a, r2f32_t b) { return (r2f32_t){ a.x0 - b.x0, a.y0 - b.y0, a.x1 - b.x1, a.y1 - b.y1 }; } +r2f32_t r2f32_subs(r2f32_t a, f32 b) { return (r2f32_t){ a.x0 - b, a.y0 - b, a.x1 - b, a.y1 - b }; } +r2f32_t r2f32_mul(r2f32_t a, r2f32_t b) { return (r2f32_t){ a.x0 * b.x0, a.y0 * b.y0, a.x1 * b.x1, a.y1 * b.y1 }; } +r2f32_t r2f32_muls(r2f32_t a, f32 b) { return (r2f32_t){ a.x0 * b, a.y0 * b, a.x1 * b, a.y1 * b }; } +r2f32_t r2f32_div(r2f32_t a, r2f32_t b) { return (r2f32_t){ a.x0 / b.x0, a.y0 / b.y0, a.x1 / b.x1, a.y1 / b.y1 }; } +r2f32_t r2f32_divs(r2f32_t a, f32 b) { return (r2f32_t){ a.x0 / b, a.y0 / b, a.x1 / b, a.y1 / b }; } +r3f32_t r3f32(f32 x0, f32 y0, f32 z0, f32 x1, f32 y1, f32 z1) { return (r3f32_t){ x0, y0, z0, x1, y1, z1 }; } +r3f32_t r3f32_add(r3f32_t a, r3f32_t b) { return (r3f32_t){ a.x0 + b.x0, a.y0 + b.y0, a.z0 + b.z0, a.x1 + b.x1, a.y1 + b.y1, a.z1 + b.z1 }; } +r3f32_t r3f32_adds(r3f32_t a, f32 b) { return (r3f32_t){ a.x0 + b, a.y0 + b, a.z0 + b, a.x1 + b, a.y1 + b, a.z1 + b }; } +r3f32_t r3f32_sub(r3f32_t a, r3f32_t b) { return (r3f32_t){ a.x0 - b.x0, a.y0 - b.y0, a.z0 - b.z0, a.x1 - b.x1, a.y1 - b.y1, a.z1 - b.z1 }; } +r3f32_t r3f32_subs(r3f32_t a, f32 b) { return (r3f32_t){ a.x0 - b, a.y0 - b, a.z0 - b, a.x1 - b, a.y1 - b, a.z1 - b }; } +r3f32_t r3f32_mul(r3f32_t a, r3f32_t b) { return (r3f32_t){ a.x0 * b.x0, a.y0 * b.y0, a.z0 * b.z0, a.x1 * b.x1, a.y1 * b.y1, a.z1 * b.z1 }; } +r3f32_t r3f32_muls(r3f32_t a, f32 b) { return (r3f32_t){ a.x0 * b, a.y0 * b, a.z0 * b, a.x1 * b, a.y1 * b, a.z1 * b }; } +r3f32_t r3f32_div(r3f32_t a, r3f32_t b) { return (r3f32_t){ a.x0 / b.x0, a.y0 / b.y0, a.z0 / b.z0, a.x1 / b.x1, a.y1 / b.y1, a.z1 / b.z1 }; } +r3f32_t r3f32_divs(r3f32_t a, f32 b) { return (r3f32_t){ a.x0 / b, a.y0 / b, a.z0 / b, a.x1 / b, a.y1 / b, a.z1 / b }; } +v2f64_t v2f64(f64 x, f64 y) { return (v2f64_t){ x, y }; } +v2f64_t v2f64_add(v2f64_t a, v2f64_t b) { return (v2f64_t){ a.x + b.x, a.y + b.y }; } +v2f64_t v2f64_adds(v2f64_t a, f64 b) { return (v2f64_t){ a.x + b, a.y + b }; } +v2f64_t v2f64_sub(v2f64_t a, v2f64_t b) { return (v2f64_t){ a.x - b.x, a.y - b.y }; } +v2f64_t v2f64_subs(v2f64_t a, f64 b) { return (v2f64_t){ a.x - b, a.y - b }; } +v2f64_t v2f64_mul(v2f64_t a, v2f64_t b) { return (v2f64_t){ a.x * b.x, a.y * b.y }; } +v2f64_t v2f64_muls(v2f64_t a, f64 b) { return (v2f64_t){ a.x * b, a.y * b }; } +v2f64_t v2f64_div(v2f64_t a, v2f64_t b) { return (v2f64_t){ a.x / b.x, a.y / b.y }; } +v2f64_t v2f64_divs(v2f64_t a, f64 b) { return (v2f64_t){ a.x / b, a.y / b }; } +v3f64_t v3f64(f64 x, f64 y, f64 z) { return (v3f64_t){ x, y, z }; } +v3f64_t v3f64_add(v3f64_t a, v3f64_t b) { return (v3f64_t){ a.x + b.x, a.y + b.y, a.z + b.z }; } +v3f64_t v3f64_adds(v3f64_t a, f64 b) { return (v3f64_t){ a.x + b, a.y + b, a.z + b }; } +v3f64_t v3f64_sub(v3f64_t a, v3f64_t b) { return (v3f64_t){ a.x - b.x, a.y - b.y, a.z - b.z }; } +v3f64_t v3f64_subs(v3f64_t a, f64 b) { return (v3f64_t){ a.x - b, a.y - b, a.z - b }; } +v3f64_t v3f64_mul(v3f64_t a, v3f64_t b) { return (v3f64_t){ a.x * b.x, a.y * b.y, a.z * b.z }; } +v3f64_t v3f64_muls(v3f64_t a, f64 b) { return (v3f64_t){ a.x * b, a.y * b, a.z * b }; } +v3f64_t v3f64_div(v3f64_t a, v3f64_t b) { return (v3f64_t){ a.x / b.x, a.y / b.y, a.z / b.z }; } +v3f64_t v3f64_divs(v3f64_t a, f64 b) { return (v3f64_t){ a.x / b, a.y / b, a.z / b }; } +v4f64_t v4f64(f64 x, f64 y, f64 z, f64 w) { return (v4f64_t){ x, y, z, w }; } +v4f64_t v4f64_add(v4f64_t a, v4f64_t b) { return (v4f64_t){ a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w }; } +v4f64_t v4f64_adds(v4f64_t a, f64 b) { return (v4f64_t){ a.x + b, a.y + b, a.z + b, a.w + b }; } +v4f64_t v4f64_sub(v4f64_t a, v4f64_t b) { return (v4f64_t){ a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w }; } +v4f64_t v4f64_subs(v4f64_t a, f64 b) { return (v4f64_t){ a.x - b, a.y - b, a.z - b, a.w - b }; } +v4f64_t v4f64_mul(v4f64_t a, v4f64_t b) { return (v4f64_t){ a.x * b.x, a.y * b.y, a.z * b.z, a.w * b.w }; } +v4f64_t v4f64_muls(v4f64_t a, f64 b) { return (v4f64_t){ a.x * b, a.y * b, a.z * b, a.w * b }; } +v4f64_t v4f64_div(v4f64_t a, v4f64_t b) { return (v4f64_t){ a.x / b.x, a.y / b.y, a.z / b.z, a.w / b.w }; } +v4f64_t v4f64_divs(v4f64_t a, f64 b) { return (v4f64_t){ a.x / b, a.y / b, a.z / b, a.w / b }; } +r2f64_t r2f64(f64 x0, f64 y0, f64 x1, f64 y1) { return (r2f64_t){ x0, y0, x1, y1 }; } +r2f64_t r2f64_add(r2f64_t a, r2f64_t b) { return (r2f64_t){ a.x0 + b.x0, a.y0 + b.y0, a.x1 + b.x1, a.y1 + b.y1 }; } +r2f64_t r2f64_adds(r2f64_t a, f64 b) { return (r2f64_t){ a.x0 + b, a.y0 + b, a.x1 + b, a.y1 + b }; } +r2f64_t r2f64_sub(r2f64_t a, r2f64_t b) { return (r2f64_t){ a.x0 - b.x0, a.y0 - b.y0, a.x1 - b.x1, a.y1 - b.y1 }; } +r2f64_t r2f64_subs(r2f64_t a, f64 b) { return (r2f64_t){ a.x0 - b, a.y0 - b, a.x1 - b, a.y1 - b }; } +r2f64_t r2f64_mul(r2f64_t a, r2f64_t b) { return (r2f64_t){ a.x0 * b.x0, a.y0 * b.y0, a.x1 * b.x1, a.y1 * b.y1 }; } +r2f64_t r2f64_muls(r2f64_t a, f64 b) { return (r2f64_t){ a.x0 * b, a.y0 * b, a.x1 * b, a.y1 * b }; } +r2f64_t r2f64_div(r2f64_t a, r2f64_t b) { return (r2f64_t){ a.x0 / b.x0, a.y0 / b.y0, a.x1 / b.x1, a.y1 / b.y1 }; } +r2f64_t r2f64_divs(r2f64_t a, f64 b) { return (r2f64_t){ a.x0 / b, a.y0 / b, a.x1 / b, a.y1 / b }; } +r3f64_t r3f64(f64 x0, f64 y0, f64 z0, f64 x1, f64 y1, f64 z1) { return (r3f64_t){ x0, y0, z0, x1, y1, z1 }; } +r3f64_t r3f64_add(r3f64_t a, r3f64_t b) { return (r3f64_t){ a.x0 + b.x0, a.y0 + b.y0, a.z0 + b.z0, a.x1 + b.x1, a.y1 + b.y1, a.z1 + b.z1 }; } +r3f64_t r3f64_adds(r3f64_t a, f64 b) { return (r3f64_t){ a.x0 + b, a.y0 + b, a.z0 + b, a.x1 + b, a.y1 + b, a.z1 + b }; } +r3f64_t r3f64_sub(r3f64_t a, r3f64_t b) { return (r3f64_t){ a.x0 - b.x0, a.y0 - b.y0, a.z0 - b.z0, a.x1 - b.x1, a.y1 - b.y1, a.z1 - b.z1 }; } +r3f64_t r3f64_subs(r3f64_t a, f64 b) { return (r3f64_t){ a.x0 - b, a.y0 - b, a.z0 - b, a.x1 - b, a.y1 - b, a.z1 - b }; } +r3f64_t r3f64_mul(r3f64_t a, r3f64_t b) { return (r3f64_t){ a.x0 * b.x0, a.y0 * b.y0, a.z0 * b.z0, a.x1 * b.x1, a.y1 * b.y1, a.z1 * b.z1 }; } +r3f64_t r3f64_muls(r3f64_t a, f64 b) { return (r3f64_t){ a.x0 * b, a.y0 * b, a.z0 * b, a.x1 * b, a.y1 * b, a.z1 * b }; } +r3f64_t r3f64_div(r3f64_t a, r3f64_t b) { return (r3f64_t){ a.x0 / b.x0, a.y0 / b.y0, a.z0 / b.z0, a.x1 / b.x1, a.y1 / b.y1, a.z1 / b.z1 }; } +r3f64_t r3f64_divs(r3f64_t a, f64 b) { return (r3f64_t){ a.x0 / b, a.y0 / b, a.z0 / b, a.x1 / b, a.y1 / b, a.z1 / b }; } +v2i32_t v2i32(i32 x, i32 y) { return (v2i32_t){ x, y }; } +v2i32_t v2i32_add(v2i32_t a, v2i32_t b) { return (v2i32_t){ a.x + b.x, a.y + b.y }; } +v2i32_t v2i32_adds(v2i32_t a, i32 b) { return (v2i32_t){ a.x + b, a.y + b }; } +v2i32_t v2i32_sub(v2i32_t a, v2i32_t b) { return (v2i32_t){ a.x - b.x, a.y - b.y }; } +v2i32_t v2i32_subs(v2i32_t a, i32 b) { return (v2i32_t){ a.x - b, a.y - b }; } +v2i32_t v2i32_mul(v2i32_t a, v2i32_t b) { return (v2i32_t){ a.x * b.x, a.y * b.y }; } +v2i32_t v2i32_muls(v2i32_t a, i32 b) { return (v2i32_t){ a.x * b, a.y * b }; } +v2i32_t v2i32_div(v2i32_t a, v2i32_t b) { return (v2i32_t){ a.x / b.x, a.y / b.y }; } +v2i32_t v2i32_divs(v2i32_t a, i32 b) { return (v2i32_t){ a.x / b, a.y / b }; } +v3i32_t v3i32(i32 x, i32 y, i32 z) { return (v3i32_t){ x, y, z }; } +v3i32_t v3i32_add(v3i32_t a, v3i32_t b) { return (v3i32_t){ a.x + b.x, a.y + b.y, a.z + b.z }; } +v3i32_t v3i32_adds(v3i32_t a, i32 b) { return (v3i32_t){ a.x + b, a.y + b, a.z + b }; } +v3i32_t v3i32_sub(v3i32_t a, v3i32_t b) { return (v3i32_t){ a.x - b.x, a.y - b.y, a.z - b.z }; } +v3i32_t v3i32_subs(v3i32_t a, i32 b) { return (v3i32_t){ a.x - b, a.y - b, a.z - b }; } +v3i32_t v3i32_mul(v3i32_t a, v3i32_t b) { return (v3i32_t){ a.x * b.x, a.y * b.y, a.z * b.z }; } +v3i32_t v3i32_muls(v3i32_t a, i32 b) { return (v3i32_t){ a.x * b, a.y * b, a.z * b }; } +v3i32_t v3i32_div(v3i32_t a, v3i32_t b) { return (v3i32_t){ a.x / b.x, a.y / b.y, a.z / b.z }; } +v3i32_t v3i32_divs(v3i32_t a, i32 b) { return (v3i32_t){ a.x / b, a.y / b, a.z / b }; } +v4i32_t v4i32(i32 x, i32 y, i32 z, i32 w) { return (v4i32_t){ x, y, z, w }; } +v4i32_t v4i32_add(v4i32_t a, v4i32_t b) { return (v4i32_t){ a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w }; } +v4i32_t v4i32_adds(v4i32_t a, i32 b) { return (v4i32_t){ a.x + b, a.y + b, a.z + b, a.w + b }; } +v4i32_t v4i32_sub(v4i32_t a, v4i32_t b) { return (v4i32_t){ a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w }; } +v4i32_t v4i32_subs(v4i32_t a, i32 b) { return (v4i32_t){ a.x - b, a.y - b, a.z - b, a.w - b }; } +v4i32_t v4i32_mul(v4i32_t a, v4i32_t b) { return (v4i32_t){ a.x * b.x, a.y * b.y, a.z * b.z, a.w * b.w }; } +v4i32_t v4i32_muls(v4i32_t a, i32 b) { return (v4i32_t){ a.x * b, a.y * b, a.z * b, a.w * b }; } +v4i32_t v4i32_div(v4i32_t a, v4i32_t b) { return (v4i32_t){ a.x / b.x, a.y / b.y, a.z / b.z, a.w / b.w }; } +v4i32_t v4i32_divs(v4i32_t a, i32 b) { return (v4i32_t){ a.x / b, a.y / b, a.z / b, a.w / b }; } +r2i32_t r2i32(i32 x0, i32 y0, i32 x1, i32 y1) { return (r2i32_t){ x0, y0, x1, y1 }; } +r2i32_t r2i32_add(r2i32_t a, r2i32_t b) { return (r2i32_t){ a.x0 + b.x0, a.y0 + b.y0, a.x1 + b.x1, a.y1 + b.y1 }; } +r2i32_t r2i32_adds(r2i32_t a, i32 b) { return (r2i32_t){ a.x0 + b, a.y0 + b, a.x1 + b, a.y1 + b }; } +r2i32_t r2i32_sub(r2i32_t a, r2i32_t b) { return (r2i32_t){ a.x0 - b.x0, a.y0 - b.y0, a.x1 - b.x1, a.y1 - b.y1 }; } +r2i32_t r2i32_subs(r2i32_t a, i32 b) { return (r2i32_t){ a.x0 - b, a.y0 - b, a.x1 - b, a.y1 - b }; } +r2i32_t r2i32_mul(r2i32_t a, r2i32_t b) { return (r2i32_t){ a.x0 * b.x0, a.y0 * b.y0, a.x1 * b.x1, a.y1 * b.y1 }; } +r2i32_t r2i32_muls(r2i32_t a, i32 b) { return (r2i32_t){ a.x0 * b, a.y0 * b, a.x1 * b, a.y1 * b }; } +r2i32_t r2i32_div(r2i32_t a, r2i32_t b) { return (r2i32_t){ a.x0 / b.x0, a.y0 / b.y0, a.x1 / b.x1, a.y1 / b.y1 }; } +r2i32_t r2i32_divs(r2i32_t a, i32 b) { return (r2i32_t){ a.x0 / b, a.y0 / b, a.x1 / b, a.y1 / b }; } +r3i32_t r3i32(i32 x0, i32 y0, i32 z0, i32 x1, i32 y1, i32 z1) { return (r3i32_t){ x0, y0, z0, x1, y1, z1 }; } +r3i32_t r3i32_add(r3i32_t a, r3i32_t b) { return (r3i32_t){ a.x0 + b.x0, a.y0 + b.y0, a.z0 + b.z0, a.x1 + b.x1, a.y1 + b.y1, a.z1 + b.z1 }; } +r3i32_t r3i32_adds(r3i32_t a, i32 b) { return (r3i32_t){ a.x0 + b, a.y0 + b, a.z0 + b, a.x1 + b, a.y1 + b, a.z1 + b }; } +r3i32_t r3i32_sub(r3i32_t a, r3i32_t b) { return (r3i32_t){ a.x0 - b.x0, a.y0 - b.y0, a.z0 - b.z0, a.x1 - b.x1, a.y1 - b.y1, a.z1 - b.z1 }; } +r3i32_t r3i32_subs(r3i32_t a, i32 b) { return (r3i32_t){ a.x0 - b, a.y0 - b, a.z0 - b, a.x1 - b, a.y1 - b, a.z1 - b }; } +r3i32_t r3i32_mul(r3i32_t a, r3i32_t b) { return (r3i32_t){ a.x0 * b.x0, a.y0 * b.y0, a.z0 * b.z0, a.x1 * b.x1, a.y1 * b.y1, a.z1 * b.z1 }; } +r3i32_t r3i32_muls(r3i32_t a, i32 b) { return (r3i32_t){ a.x0 * b, a.y0 * b, a.z0 * b, a.x1 * b, a.y1 * b, a.z1 * b }; } +r3i32_t r3i32_div(r3i32_t a, r3i32_t b) { return (r3i32_t){ a.x0 / b.x0, a.y0 / b.y0, a.z0 / b.z0, a.x1 / b.x1, a.y1 / b.y1, a.z1 / b.z1 }; } +r3i32_t r3i32_divs(r3i32_t a, i32 b) { return (r3i32_t){ a.x0 / b, a.y0 / b, a.z0 / b, a.x1 / b, a.y1 / b, a.z1 / b }; } +v2i64_t v2i64(i64 x, i64 y) { return (v2i64_t){ x, y }; } +v2i64_t v2i64_add(v2i64_t a, v2i64_t b) { return (v2i64_t){ a.x + b.x, a.y + b.y }; } +v2i64_t v2i64_adds(v2i64_t a, i64 b) { return (v2i64_t){ a.x + b, a.y + b }; } +v2i64_t v2i64_sub(v2i64_t a, v2i64_t b) { return (v2i64_t){ a.x - b.x, a.y - b.y }; } +v2i64_t v2i64_subs(v2i64_t a, i64 b) { return (v2i64_t){ a.x - b, a.y - b }; } +v2i64_t v2i64_mul(v2i64_t a, v2i64_t b) { return (v2i64_t){ a.x * b.x, a.y * b.y }; } +v2i64_t v2i64_muls(v2i64_t a, i64 b) { return (v2i64_t){ a.x * b, a.y * b }; } +v2i64_t v2i64_div(v2i64_t a, v2i64_t b) { return (v2i64_t){ a.x / b.x, a.y / b.y }; } +v2i64_t v2i64_divs(v2i64_t a, i64 b) { return (v2i64_t){ a.x / b, a.y / b }; } +v3i64_t v3i64(i64 x, i64 y, i64 z) { return (v3i64_t){ x, y, z }; } +v3i64_t v3i64_add(v3i64_t a, v3i64_t b) { return (v3i64_t){ a.x + b.x, a.y + b.y, a.z + b.z }; } +v3i64_t v3i64_adds(v3i64_t a, i64 b) { return (v3i64_t){ a.x + b, a.y + b, a.z + b }; } +v3i64_t v3i64_sub(v3i64_t a, v3i64_t b) { return (v3i64_t){ a.x - b.x, a.y - b.y, a.z - b.z }; } +v3i64_t v3i64_subs(v3i64_t a, i64 b) { return (v3i64_t){ a.x - b, a.y - b, a.z - b }; } +v3i64_t v3i64_mul(v3i64_t a, v3i64_t b) { return (v3i64_t){ a.x * b.x, a.y * b.y, a.z * b.z }; } +v3i64_t v3i64_muls(v3i64_t a, i64 b) { return (v3i64_t){ a.x * b, a.y * b, a.z * b }; } +v3i64_t v3i64_div(v3i64_t a, v3i64_t b) { return (v3i64_t){ a.x / b.x, a.y / b.y, a.z / b.z }; } +v3i64_t v3i64_divs(v3i64_t a, i64 b) { return (v3i64_t){ a.x / b, a.y / b, a.z / b }; } +v4i64_t v4i64(i64 x, i64 y, i64 z, i64 w) { return (v4i64_t){ x, y, z, w }; } +v4i64_t v4i64_add(v4i64_t a, v4i64_t b) { return (v4i64_t){ a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w }; } +v4i64_t v4i64_adds(v4i64_t a, i64 b) { return (v4i64_t){ a.x + b, a.y + b, a.z + b, a.w + b }; } +v4i64_t v4i64_sub(v4i64_t a, v4i64_t b) { return (v4i64_t){ a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w }; } +v4i64_t v4i64_subs(v4i64_t a, i64 b) { return (v4i64_t){ a.x - b, a.y - b, a.z - b, a.w - b }; } +v4i64_t v4i64_mul(v4i64_t a, v4i64_t b) { return (v4i64_t){ a.x * b.x, a.y * b.y, a.z * b.z, a.w * b.w }; } +v4i64_t v4i64_muls(v4i64_t a, i64 b) { return (v4i64_t){ a.x * b, a.y * b, a.z * b, a.w * b }; } +v4i64_t v4i64_div(v4i64_t a, v4i64_t b) { return (v4i64_t){ a.x / b.x, a.y / b.y, a.z / b.z, a.w / b.w }; } +v4i64_t v4i64_divs(v4i64_t a, i64 b) { return (v4i64_t){ a.x / b, a.y / b, a.z / b, a.w / b }; } +r2i64_t r2i64(i64 x0, i64 y0, i64 x1, i64 y1) { return (r2i64_t){ x0, y0, x1, y1 }; } +r2i64_t r2i64_add(r2i64_t a, r2i64_t b) { return (r2i64_t){ a.x0 + b.x0, a.y0 + b.y0, a.x1 + b.x1, a.y1 + b.y1 }; } +r2i64_t r2i64_adds(r2i64_t a, i64 b) { return (r2i64_t){ a.x0 + b, a.y0 + b, a.x1 + b, a.y1 + b }; } +r2i64_t r2i64_sub(r2i64_t a, r2i64_t b) { return (r2i64_t){ a.x0 - b.x0, a.y0 - b.y0, a.x1 - b.x1, a.y1 - b.y1 }; } +r2i64_t r2i64_subs(r2i64_t a, i64 b) { return (r2i64_t){ a.x0 - b, a.y0 - b, a.x1 - b, a.y1 - b }; } +r2i64_t r2i64_mul(r2i64_t a, r2i64_t b) { return (r2i64_t){ a.x0 * b.x0, a.y0 * b.y0, a.x1 * b.x1, a.y1 * b.y1 }; } +r2i64_t r2i64_muls(r2i64_t a, i64 b) { return (r2i64_t){ a.x0 * b, a.y0 * b, a.x1 * b, a.y1 * b }; } +r2i64_t r2i64_div(r2i64_t a, r2i64_t b) { return (r2i64_t){ a.x0 / b.x0, a.y0 / b.y0, a.x1 / b.x1, a.y1 / b.y1 }; } +r2i64_t r2i64_divs(r2i64_t a, i64 b) { return (r2i64_t){ a.x0 / b, a.y0 / b, a.x1 / b, a.y1 / b }; } +r3i64_t r3i64(i64 x0, i64 y0, i64 z0, i64 x1, i64 y1, i64 z1) { return (r3i64_t){ x0, y0, z0, x1, y1, z1 }; } +r3i64_t r3i64_add(r3i64_t a, r3i64_t b) { return (r3i64_t){ a.x0 + b.x0, a.y0 + b.y0, a.z0 + b.z0, a.x1 + b.x1, a.y1 + b.y1, a.z1 + b.z1 }; } +r3i64_t r3i64_adds(r3i64_t a, i64 b) { return (r3i64_t){ a.x0 + b, a.y0 + b, a.z0 + b, a.x1 + b, a.y1 + b, a.z1 + b }; } +r3i64_t r3i64_sub(r3i64_t a, r3i64_t b) { return (r3i64_t){ a.x0 - b.x0, a.y0 - b.y0, a.z0 - b.z0, a.x1 - b.x1, a.y1 - b.y1, a.z1 - b.z1 }; } +r3i64_t r3i64_subs(r3i64_t a, i64 b) { return (r3i64_t){ a.x0 - b, a.y0 - b, a.z0 - b, a.x1 - b, a.y1 - b, a.z1 - b }; } +r3i64_t r3i64_mul(r3i64_t a, r3i64_t b) { return (r3i64_t){ a.x0 * b.x0, a.y0 * b.y0, a.z0 * b.z0, a.x1 * b.x1, a.y1 * b.y1, a.z1 * b.z1 }; } +r3i64_t r3i64_muls(r3i64_t a, i64 b) { return (r3i64_t){ a.x0 * b, a.y0 * b, a.z0 * b, a.x1 * b, a.y1 * b, a.z1 * b }; } +r3i64_t r3i64_div(r3i64_t a, r3i64_t b) { return (r3i64_t){ a.x0 / b.x0, a.y0 / b.y0, a.z0 / b.z0, a.x1 / b.x1, a.y1 / b.y1, a.z1 / b.z1 }; } +r3i64_t r3i64_divs(r3i64_t a, i64 b) { return (r3i64_t){ a.x0 / b, a.y0 / b, a.z0 / b, a.x1 / b, a.y1 / b, a.z1 / b }; } +r2f32_t r2f32_mindim(v2f32_t pos, v2f32_t size) { return (r2f32_t){ pos, v2f32_add(pos, size) }; } +r2f32_t r2f32_center_halfdim(v2f32_t center, v2f32_t halfdim) { return (r2f32_t){ v2f32_sub(center, halfdim), v2f32_add(center, halfdim) }; } +r3f32_t r3f32_mindim(v3f32_t pos, v3f32_t size) { return (r3f32_t){ pos, v3f32_add(pos, size) }; } +r3f32_t r3f32_center_halfdim(v3f32_t center, v3f32_t halfdim) { return (r3f32_t){ v3f32_sub(center, halfdim), v3f32_add(center, halfdim) }; } +r2f64_t r2f64_mindim(v2f64_t pos, v2f64_t size) { return (r2f64_t){ pos, v2f64_add(pos, size) }; } +r2f64_t r2f64_center_halfdim(v2f64_t center, v2f64_t halfdim) { return (r2f64_t){ v2f64_sub(center, halfdim), v2f64_add(center, halfdim) }; } +r3f64_t r3f64_mindim(v3f64_t pos, v3f64_t size) { return (r3f64_t){ pos, v3f64_add(pos, size) }; } +r3f64_t r3f64_center_halfdim(v3f64_t center, v3f64_t halfdim) { return (r3f64_t){ v3f64_sub(center, halfdim), v3f64_add(center, halfdim) }; } +r2i32_t r2i32_mindim(v2i32_t pos, v2i32_t size) { return (r2i32_t){ pos, v2i32_add(pos, size) }; } +r2i32_t r2i32_center_halfdim(v2i32_t center, v2i32_t halfdim) { return (r2i32_t){ v2i32_sub(center, halfdim), v2i32_add(center, halfdim) }; } +r3i32_t r3i32_mindim(v3i32_t pos, v3i32_t size) { return (r3i32_t){ pos, v3i32_add(pos, size) }; } +r3i32_t r3i32_center_halfdim(v3i32_t center, v3i32_t halfdim) { return (r3i32_t){ v3i32_sub(center, halfdim), v3i32_add(center, halfdim) }; } +r2i64_t r2i64_mindim(v2i64_t pos, v2i64_t size) { return (r2i64_t){ pos, v2i64_add(pos, size) }; } +r2i64_t r2i64_center_halfdim(v2i64_t center, v2i64_t halfdim) { return (r2i64_t){ v2i64_sub(center, halfdim), v2i64_add(center, halfdim) }; } +r3i64_t r3i64_mindim(v3i64_t pos, v3i64_t size) { return (r3i64_t){ pos, v3i64_add(pos, size) }; } +r3i64_t r3i64_center_halfdim(v3i64_t center, v3i64_t halfdim) { return (r3i64_t){ v3i64_sub(center, halfdim), v3i64_add(center, halfdim) }; } + +r2f32_t r2f32_cut_left(r2f32_t *r, f32 value) { + f32 minx = r->min.x; + r->min.x = MIN(r->min.x + value, r->max.x); + return (r2f32_t){ .min = {.x = minx, .y = r->min.y}, .max = {.x = r->min.x, .y =r->max.y} }; +} +r2f32_t r2f32_cut_right(r2f32_t *r, f32 value) { + f32 maxx = r->max.x; + r->max.x = MAX(r->min.x, r->max.x - value); + return (r2f32_t){ .min = {.x = r->max.x, .y = r->min.y}, .max = {.x = maxx, .y = r->max.y} }; +} +r2f32_t r2f32_cut_top(r2f32_t *r, f32 value) { /* Y is down */ + f32 miny = r->min.y; + r->min.y = MIN(r->max.y, r->min.y + value); + return (r2f32_t){ .min = {.x = r->min.x, .y = miny}, .max = {.x = r->max.x, .y = r->min.y} }; +} +r2f32_t r2f32_cut_bottom(r2f32_t *r, f32 value) { /* Y is down */ + f32 maxy = r->max.y; + r->max.y = MAX(r->min.y, r->max.y - value); + return (r2f32_t){ .min = {.x = r->min.x, .y = r->max.y}, .max = {.x = r->max.x, .y = maxy} }; +} + +// get past left +r2f32_t r2f32_getp_left(const r2f32_t *rect, f32 value) { + r2f32_t result = r2f32(rect->min.x - value, rect->min.y, rect->min.x, rect->max.y); + return result; +} +r2f32_t r2f32_getp_right(const r2f32_t *rect, f32 value) { + r2f32_t result = r2f32(rect->max.x, rect->min.y, rect->max.x + value, rect->max.y); + return result; +} +r2f32_t r2f32_getp_bottom(const r2f32_t *rect, f32 value) { + r2f32_t result = r2f32(rect->min.x, rect->max.y, rect->max.x, rect->max.y + value); + return result; +} +r2f32_t r2f32_getp_top(const r2f32_t *rect, f32 value) { + r2f32_t result = r2f32(rect->min.x, rect->min.y - value, rect->max.x, rect->min.y); + return result; +} + +r2f32_t r2f32_get_left(const r2f32_t *r, f32 value) { + f32 minx = r->min.x; + r2f32_t result = (r2f32_t){ .min = {.x = minx, .y = r->min.y}, .max = {.x = MIN(r->min.x + value, r->max.x), .y =r->max.y} }; + return result; +} +r2f32_t r2f32_get_right(const r2f32_t *r, f32 value) { + f32 maxx = r->max.x; + r2f32_t result = (r2f32_t){ .min = {.x = MAX(r->min.x, r->max.x - value), .y = r->min.y}, .max = {.x = maxx, .y = r->max.y} }; + return result; +} +r2f32_t r2f32_get_top(const r2f32_t *r, f32 value) { /* Y is down */ + f32 miny = r->min.y; + r2f32_t result = (r2f32_t){ .min = {.x = r->min.x, .y = miny}, .max = {.x = r->max.x, .y = MIN(r->max.y, r->min.y + value)} }; + return result; +} +r2f32_t r2f32_get_bottom(const r2f32_t *r, f32 value) { /* Y is down */ + f32 maxy = r->max.y; + r2f32_t result = (r2f32_t){ .min = {.x = r->min.x, .y = MAX(r->min.y, r->max.y - value)}, .max = {.x = r->max.x, .y = maxy} }; + return result; +} + +r2f32_t r2f32_shrink(r2f32_t rect, v2f32_t value) { return (r2f32_t){ v2f32_add(rect.min, value), v2f32_sub(rect.max, value) }; } +v2f32_t r2f32_get_size(r2f32_t r) { return (v2f32_t){r.max.x - r.min.x, r.max.y - r.min.y}; } +v2f32_t r2f32_get_mid(r2f32_t r) { return v2f32_add(r.min, v2f32_divs(r2f32_get_size(r), 2)); } +b32 r2f32_contains(r2f32_t rec, v2f32_t point) { return (point.x >= rec.min.x) && (point.x < rec.max.x) && (point.y >= rec.min.y) && (point.y < rec.max.y); } + +r2f64_t r2f64_cut_left(r2f64_t *r, f64 value) { + f64 minx = r->min.x; + r->min.x = MIN(r->min.x + value, r->max.x); + return (r2f64_t){ .min = {.x = minx, .y = r->min.y}, .max = {.x = r->min.x, .y =r->max.y} }; +} +r2f64_t r2f64_cut_right(r2f64_t *r, f64 value) { + f64 maxx = r->max.x; + r->max.x = MAX(r->min.x, r->max.x - value); + return (r2f64_t){ .min = {.x = r->max.x, .y = r->min.y}, .max = {.x = maxx, .y = r->max.y} }; +} +r2f64_t r2f64_cut_top(r2f64_t *r, f64 value) { /* Y is down */ + f64 miny = r->min.y; + r->min.y = MIN(r->max.y, r->min.y + value); + return (r2f64_t){ .min = {.x = r->min.x, .y = miny}, .max = {.x = r->max.x, .y = r->min.y} }; +} +r2f64_t r2f64_cut_bottom(r2f64_t *r, f64 value) { /* Y is down */ + f64 maxy = r->max.y; + r->max.y = MAX(r->min.y, r->max.y - value); + return (r2f64_t){ .min = {.x = r->min.x, .y = r->max.y}, .max = {.x = r->max.x, .y = maxy} }; +} + +// get past left +r2f64_t r2f64_getp_left(const r2f64_t *rect, f64 value) { + r2f64_t result = r2f64(rect->min.x - value, rect->min.y, rect->min.x, rect->max.y); + return result; +} +r2f64_t r2f64_getp_right(const r2f64_t *rect, f64 value) { + r2f64_t result = r2f64(rect->max.x, rect->min.y, rect->max.x + value, rect->max.y); + return result; +} +r2f64_t r2f64_getp_bottom(const r2f64_t *rect, f64 value) { + r2f64_t result = r2f64(rect->min.x, rect->max.y, rect->max.x, rect->max.y + value); + return result; +} +r2f64_t r2f64_getp_top(const r2f64_t *rect, f64 value) { + r2f64_t result = r2f64(rect->min.x, rect->min.y - value, rect->max.x, rect->min.y); + return result; +} + +r2f64_t r2f64_get_left(const r2f64_t *r, f64 value) { + f64 minx = r->min.x; + r2f64_t result = (r2f64_t){ .min = {.x = minx, .y = r->min.y}, .max = {.x = MIN(r->min.x + value, r->max.x), .y =r->max.y} }; + return result; +} +r2f64_t r2f64_get_right(const r2f64_t *r, f64 value) { + f64 maxx = r->max.x; + r2f64_t result = (r2f64_t){ .min = {.x = MAX(r->min.x, r->max.x - value), .y = r->min.y}, .max = {.x = maxx, .y = r->max.y} }; + return result; +} +r2f64_t r2f64_get_top(const r2f64_t *r, f64 value) { /* Y is down */ + f64 miny = r->min.y; + r2f64_t result = (r2f64_t){ .min = {.x = r->min.x, .y = miny}, .max = {.x = r->max.x, .y = MIN(r->max.y, r->min.y + value)} }; + return result; +} +r2f64_t r2f64_get_bottom(const r2f64_t *r, f64 value) { /* Y is down */ + f64 maxy = r->max.y; + r2f64_t result = (r2f64_t){ .min = {.x = r->min.x, .y = MAX(r->min.y, r->max.y - value)}, .max = {.x = r->max.x, .y = maxy} }; + return result; +} + +r2f64_t r2f64_shrink(r2f64_t rect, v2f64_t value) { return (r2f64_t){ v2f64_add(rect.min, value), v2f64_sub(rect.max, value) }; } +v2f64_t r2f64_get_size(r2f64_t r) { return (v2f64_t){r.max.x - r.min.x, r.max.y - r.min.y}; } +v2f64_t r2f64_get_mid(r2f64_t r) { return v2f64_add(r.min, v2f64_divs(r2f64_get_size(r), 2)); } +b32 r2f64_contains(r2f64_t rec, v2f64_t point) { return (point.x >= rec.min.x) && (point.x < rec.max.x) && (point.y >= rec.min.y) && (point.y < rec.max.y); } + + +r2i32_t r2i32_cut_left(r2i32_t *r, i32 value) { + i32 minx = r->min.x; + r->min.x = MIN(r->min.x + value, r->max.x); + return (r2i32_t){ .min = {.x = minx, .y = r->min.y}, .max = {.x = r->min.x, .y =r->max.y} }; +} +r2i32_t r2i32_cut_right(r2i32_t *r, i32 value) { + i32 maxx = r->max.x; + r->max.x = MAX(r->min.x, r->max.x - value); + return (r2i32_t){ .min = {.x = r->max.x, .y = r->min.y}, .max = {.x = maxx, .y = r->max.y} }; +} +r2i32_t r2i32_cut_top(r2i32_t *r, i32 value) { /* Y is down */ + i32 miny = r->min.y; + r->min.y = MIN(r->max.y, r->min.y + value); + return (r2i32_t){ .min = {.x = r->min.x, .y = miny}, .max = {.x = r->max.x, .y = r->min.y} }; +} +r2i32_t r2i32_cut_bottom(r2i32_t *r, i32 value) { /* Y is down */ + i32 maxy = r->max.y; + r->max.y = MAX(r->min.y, r->max.y - value); + return (r2i32_t){ .min = {.x = r->min.x, .y = r->max.y}, .max = {.x = r->max.x, .y = maxy} }; +} + +// get past left +r2i32_t r2i32_getp_left(const r2i32_t *rect, i32 value) { + r2i32_t result = r2i32(rect->min.x - value, rect->min.y, rect->min.x, rect->max.y); + return result; +} +r2i32_t r2i32_getp_right(const r2i32_t *rect, i32 value) { + r2i32_t result = r2i32(rect->max.x, rect->min.y, rect->max.x + value, rect->max.y); + return result; +} +r2i32_t r2i32_getp_bottom(const r2i32_t *rect, i32 value) { + r2i32_t result = r2i32(rect->min.x, rect->max.y, rect->max.x, rect->max.y + value); + return result; +} +r2i32_t r2i32_getp_top(const r2i32_t *rect, i32 value) { + r2i32_t result = r2i32(rect->min.x, rect->min.y - value, rect->max.x, rect->min.y); + return result; +} + +r2i32_t r2i32_get_left(const r2i32_t *r, i32 value) { + i32 minx = r->min.x; + r2i32_t result = (r2i32_t){ .min = {.x = minx, .y = r->min.y}, .max = {.x = MIN(r->min.x + value, r->max.x), .y =r->max.y} }; + return result; +} +r2i32_t r2i32_get_right(const r2i32_t *r, i32 value) { + i32 maxx = r->max.x; + r2i32_t result = (r2i32_t){ .min = {.x = MAX(r->min.x, r->max.x - value), .y = r->min.y}, .max = {.x = maxx, .y = r->max.y} }; + return result; +} +r2i32_t r2i32_get_top(const r2i32_t *r, i32 value) { /* Y is down */ + i32 miny = r->min.y; + r2i32_t result = (r2i32_t){ .min = {.x = r->min.x, .y = miny}, .max = {.x = r->max.x, .y = MIN(r->max.y, r->min.y + value)} }; + return result; +} +r2i32_t r2i32_get_bottom(const r2i32_t *r, i32 value) { /* Y is down */ + i32 maxy = r->max.y; + r2i32_t result = (r2i32_t){ .min = {.x = r->min.x, .y = MAX(r->min.y, r->max.y - value)}, .max = {.x = r->max.x, .y = maxy} }; + return result; +} + +r2i32_t r2i32_shrink(r2i32_t rect, v2i32_t value) { return (r2i32_t){ v2i32_add(rect.min, value), v2i32_sub(rect.max, value) }; } +v2i32_t r2i32_get_size(r2i32_t r) { return (v2i32_t){r.max.x - r.min.x, r.max.y - r.min.y}; } +v2i32_t r2i32_get_mid(r2i32_t r) { return v2i32_add(r.min, v2i32_divs(r2i32_get_size(r), 2)); } +b32 r2i32_contains(r2i32_t rec, v2i32_t point) { return (point.x >= rec.min.x) && (point.x < rec.max.x) && (point.y >= rec.min.y) && (point.y < rec.max.y); } + +r2i64_t r2i64_cut_left(r2i64_t *r, i64 value) { + i64 minx = r->min.x; + r->min.x = MIN(r->min.x + value, r->max.x); + return (r2i64_t){ .min = {.x = minx, .y = r->min.y}, .max = {.x = r->min.x, .y =r->max.y} }; +} +r2i64_t r2i64_cut_right(r2i64_t *r, i64 value) { + i64 maxx = r->max.x; + r->max.x = MAX(r->min.x, r->max.x - value); + return (r2i64_t){ .min = {.x = r->max.x, .y = r->min.y}, .max = {.x = maxx, .y = r->max.y} }; +} +r2i64_t r2i64_cut_top(r2i64_t *r, i64 value) { /* Y is down */ + i64 miny = r->min.y; + r->min.y = MIN(r->max.y, r->min.y + value); + return (r2i64_t){ .min = {.x = r->min.x, .y = miny}, .max = {.x = r->max.x, .y = r->min.y} }; +} +r2i64_t r2i64_cut_bottom(r2i64_t *r, i64 value) { /* Y is down */ + i64 maxy = r->max.y; + r->max.y = MAX(r->min.y, r->max.y - value); + return (r2i64_t){ .min = {.x = r->min.x, .y = r->max.y}, .max = {.x = r->max.x, .y = maxy} }; +} + +// get past left +r2i64_t r2i64_getp_left(const r2i64_t *rect, i64 value) { + r2i64_t result = r2i64(rect->min.x - value, rect->min.y, rect->min.x, rect->max.y); + return result; +} +r2i64_t r2i64_getp_right(const r2i64_t *rect, i64 value) { + r2i64_t result = r2i64(rect->max.x, rect->min.y, rect->max.x + value, rect->max.y); + return result; +} +r2i64_t r2i64_getp_bottom(const r2i64_t *rect, i64 value) { + r2i64_t result = r2i64(rect->min.x, rect->max.y, rect->max.x, rect->max.y + value); + return result; +} +r2i64_t r2i64_getp_top(const r2i64_t *rect, i64 value) { + r2i64_t result = r2i64(rect->min.x, rect->min.y - value, rect->max.x, rect->min.y); + return result; +} + +r2i64_t r2i64_get_left(const r2i64_t *r, i64 value) { + i64 minx = r->min.x; + r2i64_t result = (r2i64_t){ .min = {.x = minx, .y = r->min.y}, .max = {.x = MIN(r->min.x + value, r->max.x), .y =r->max.y} }; + return result; +} +r2i64_t r2i64_get_right(const r2i64_t *r, i64 value) { + i64 maxx = r->max.x; + r2i64_t result = (r2i64_t){ .min = {.x = MAX(r->min.x, r->max.x - value), .y = r->min.y}, .max = {.x = maxx, .y = r->max.y} }; + return result; +} +r2i64_t r2i64_get_top(const r2i64_t *r, i64 value) { /* Y is down */ + i64 miny = r->min.y; + r2i64_t result = (r2i64_t){ .min = {.x = r->min.x, .y = miny}, .max = {.x = r->max.x, .y = MIN(r->max.y, r->min.y + value)} }; + return result; +} +r2i64_t r2i64_get_bottom(const r2i64_t *r, i64 value) { /* Y is down */ + i64 maxy = r->max.y; + r2i64_t result = (r2i64_t){ .min = {.x = r->min.x, .y = MAX(r->min.y, r->max.y - value)}, .max = {.x = r->max.x, .y = maxy} }; + return result; +} + +r2i64_t r2i64_shrink(r2i64_t rect, v2i64_t value) { return (r2i64_t){ v2i64_add(rect.min, value), v2i64_sub(rect.max, value) }; } +v2i64_t r2i64_get_size(r2i64_t r) { return (v2i64_t){r.max.x - r.min.x, r.max.y - r.min.y}; } +v2i64_t r2i64_get_mid(r2i64_t r) { return v2i64_add(r.min, v2i64_divs(r2i64_get_size(r), 2)); } +b32 r2i64_contains(r2i64_t rec, v2i64_t point) { return (point.x >= rec.min.x) && (point.x < rec.max.x) && (point.y >= rec.min.y) && (point.y < rec.max.y); } + +v2f64_t v2f32_to_v2f64(v2f32_t v) { return (v2f64_t){ (f64)v.x, (f64)v.y }; } +v3f64_t v3f32_to_v3f64(v3f32_t v) { return (v3f64_t){ (f64)v.x, (f64)v.y, (f64)v.z }; } +v4f64_t v4f32_to_v4f64(v4f32_t v) { return (v4f64_t){ (f64)v.x, (f64)v.y, (f64)v.z, (f64)v.w }; } +r2f64_t r2f32_to_r2f64(r2f32_t v) { return (r2f64_t){ (f64)v.x0, (f64)v.y0, (f64)v.x1, (f64)v.y1 }; } +r3f64_t r3f32_to_r3f64(r3f32_t v) { return (r3f64_t){ (f64)v.x0, (f64)v.y0, (f64)v.z0, (f64)v.x1, (f64)v.y1, (f64)v.z1 }; } +v2i32_t v2f32_to_v2i32(v2f32_t v) { return (v2i32_t){ (i32)v.x, (i32)v.y }; } +v3i32_t v3f32_to_v3i32(v3f32_t v) { return (v3i32_t){ (i32)v.x, (i32)v.y, (i32)v.z }; } +v4i32_t v4f32_to_v4i32(v4f32_t v) { return (v4i32_t){ (i32)v.x, (i32)v.y, (i32)v.z, (i32)v.w }; } +r2i32_t r2f32_to_r2i32(r2f32_t v) { return (r2i32_t){ (i32)v.x0, (i32)v.y0, (i32)v.x1, (i32)v.y1 }; } +r3i32_t r3f32_to_r3i32(r3f32_t v) { return (r3i32_t){ (i32)v.x0, (i32)v.y0, (i32)v.z0, (i32)v.x1, (i32)v.y1, (i32)v.z1 }; } +v2i64_t v2f32_to_v2i64(v2f32_t v) { return (v2i64_t){ (i64)v.x, (i64)v.y }; } +v3i64_t v3f32_to_v3i64(v3f32_t v) { return (v3i64_t){ (i64)v.x, (i64)v.y, (i64)v.z }; } +v4i64_t v4f32_to_v4i64(v4f32_t v) { return (v4i64_t){ (i64)v.x, (i64)v.y, (i64)v.z, (i64)v.w }; } +r2i64_t r2f32_to_r2i64(r2f32_t v) { return (r2i64_t){ (i64)v.x0, (i64)v.y0, (i64)v.x1, (i64)v.y1 }; } +r3i64_t r3f32_to_r3i64(r3f32_t v) { return (r3i64_t){ (i64)v.x0, (i64)v.y0, (i64)v.z0, (i64)v.x1, (i64)v.y1, (i64)v.z1 }; } +v2f32_t v2f64_to_v2f32(v2f64_t v) { return (v2f32_t){ (f32)v.x, (f32)v.y }; } +v3f32_t v3f64_to_v3f32(v3f64_t v) { return (v3f32_t){ (f32)v.x, (f32)v.y, (f32)v.z }; } +v4f32_t v4f64_to_v4f32(v4f64_t v) { return (v4f32_t){ (f32)v.x, (f32)v.y, (f32)v.z, (f32)v.w }; } +r2f32_t r2f64_to_r2f32(r2f64_t v) { return (r2f32_t){ (f32)v.x0, (f32)v.y0, (f32)v.x1, (f32)v.y1 }; } +r3f32_t r3f64_to_r3f32(r3f64_t v) { return (r3f32_t){ (f32)v.x0, (f32)v.y0, (f32)v.z0, (f32)v.x1, (f32)v.y1, (f32)v.z1 }; } +v2i32_t v2f64_to_v2i32(v2f64_t v) { return (v2i32_t){ (i32)v.x, (i32)v.y }; } +v3i32_t v3f64_to_v3i32(v3f64_t v) { return (v3i32_t){ (i32)v.x, (i32)v.y, (i32)v.z }; } +v4i32_t v4f64_to_v4i32(v4f64_t v) { return (v4i32_t){ (i32)v.x, (i32)v.y, (i32)v.z, (i32)v.w }; } +r2i32_t r2f64_to_r2i32(r2f64_t v) { return (r2i32_t){ (i32)v.x0, (i32)v.y0, (i32)v.x1, (i32)v.y1 }; } +r3i32_t r3f64_to_r3i32(r3f64_t v) { return (r3i32_t){ (i32)v.x0, (i32)v.y0, (i32)v.z0, (i32)v.x1, (i32)v.y1, (i32)v.z1 }; } +v2i64_t v2f64_to_v2i64(v2f64_t v) { return (v2i64_t){ (i64)v.x, (i64)v.y }; } +v3i64_t v3f64_to_v3i64(v3f64_t v) { return (v3i64_t){ (i64)v.x, (i64)v.y, (i64)v.z }; } +v4i64_t v4f64_to_v4i64(v4f64_t v) { return (v4i64_t){ (i64)v.x, (i64)v.y, (i64)v.z, (i64)v.w }; } +r2i64_t r2f64_to_r2i64(r2f64_t v) { return (r2i64_t){ (i64)v.x0, (i64)v.y0, (i64)v.x1, (i64)v.y1 }; } +r3i64_t r3f64_to_r3i64(r3f64_t v) { return (r3i64_t){ (i64)v.x0, (i64)v.y0, (i64)v.z0, (i64)v.x1, (i64)v.y1, (i64)v.z1 }; } +v2f32_t v2i32_to_v2f32(v2i32_t v) { return (v2f32_t){ (f32)v.x, (f32)v.y }; } +v3f32_t v3i32_to_v3f32(v3i32_t v) { return (v3f32_t){ (f32)v.x, (f32)v.y, (f32)v.z }; } +v4f32_t v4i32_to_v4f32(v4i32_t v) { return (v4f32_t){ (f32)v.x, (f32)v.y, (f32)v.z, (f32)v.w }; } +r2f32_t r2i32_to_r2f32(r2i32_t v) { return (r2f32_t){ (f32)v.x0, (f32)v.y0, (f32)v.x1, (f32)v.y1 }; } +r3f32_t r3i32_to_r3f32(r3i32_t v) { return (r3f32_t){ (f32)v.x0, (f32)v.y0, (f32)v.z0, (f32)v.x1, (f32)v.y1, (f32)v.z1 }; } +v2f64_t v2i32_to_v2f64(v2i32_t v) { return (v2f64_t){ (f64)v.x, (f64)v.y }; } +v3f64_t v3i32_to_v3f64(v3i32_t v) { return (v3f64_t){ (f64)v.x, (f64)v.y, (f64)v.z }; } +v4f64_t v4i32_to_v4f64(v4i32_t v) { return (v4f64_t){ (f64)v.x, (f64)v.y, (f64)v.z, (f64)v.w }; } +r2f64_t r2i32_to_r2f64(r2i32_t v) { return (r2f64_t){ (f64)v.x0, (f64)v.y0, (f64)v.x1, (f64)v.y1 }; } +r3f64_t r3i32_to_r3f64(r3i32_t v) { return (r3f64_t){ (f64)v.x0, (f64)v.y0, (f64)v.z0, (f64)v.x1, (f64)v.y1, (f64)v.z1 }; } +v2i64_t v2i32_to_v2i64(v2i32_t v) { return (v2i64_t){ (i64)v.x, (i64)v.y }; } +v3i64_t v3i32_to_v3i64(v3i32_t v) { return (v3i64_t){ (i64)v.x, (i64)v.y, (i64)v.z }; } +v4i64_t v4i32_to_v4i64(v4i32_t v) { return (v4i64_t){ (i64)v.x, (i64)v.y, (i64)v.z, (i64)v.w }; } +r2i64_t r2i32_to_r2i64(r2i32_t v) { return (r2i64_t){ (i64)v.x0, (i64)v.y0, (i64)v.x1, (i64)v.y1 }; } +r3i64_t r3i32_to_r3i64(r3i32_t v) { return (r3i64_t){ (i64)v.x0, (i64)v.y0, (i64)v.z0, (i64)v.x1, (i64)v.y1, (i64)v.z1 }; } +v2f32_t v2i64_to_v2f32(v2i64_t v) { return (v2f32_t){ (f32)v.x, (f32)v.y }; } +v3f32_t v3i64_to_v3f32(v3i64_t v) { return (v3f32_t){ (f32)v.x, (f32)v.y, (f32)v.z }; } +v4f32_t v4i64_to_v4f32(v4i64_t v) { return (v4f32_t){ (f32)v.x, (f32)v.y, (f32)v.z, (f32)v.w }; } +r2f32_t r2i64_to_r2f32(r2i64_t v) { return (r2f32_t){ (f32)v.x0, (f32)v.y0, (f32)v.x1, (f32)v.y1 }; } +r3f32_t r3i64_to_r3f32(r3i64_t v) { return (r3f32_t){ (f32)v.x0, (f32)v.y0, (f32)v.z0, (f32)v.x1, (f32)v.y1, (f32)v.z1 }; } +v2f64_t v2i64_to_v2f64(v2i64_t v) { return (v2f64_t){ (f64)v.x, (f64)v.y }; } +v3f64_t v3i64_to_v3f64(v3i64_t v) { return (v3f64_t){ (f64)v.x, (f64)v.y, (f64)v.z }; } +v4f64_t v4i64_to_v4f64(v4i64_t v) { return (v4f64_t){ (f64)v.x, (f64)v.y, (f64)v.z, (f64)v.w }; } +r2f64_t r2i64_to_r2f64(r2i64_t v) { return (r2f64_t){ (f64)v.x0, (f64)v.y0, (f64)v.x1, (f64)v.y1 }; } +r3f64_t r3i64_to_r3f64(r3i64_t v) { return (r3f64_t){ (f64)v.x0, (f64)v.y0, (f64)v.z0, (f64)v.x1, (f64)v.y1, (f64)v.z1 }; } +v2i32_t v2i64_to_v2i32(v2i64_t v) { return (v2i32_t){ (i32)v.x, (i32)v.y }; } +v3i32_t v3i64_to_v3i32(v3i64_t v) { return (v3i32_t){ (i32)v.x, (i32)v.y, (i32)v.z }; } +v4i32_t v4i64_to_v4i32(v4i64_t v) { return (v4i32_t){ (i32)v.x, (i32)v.y, (i32)v.z, (i32)v.w }; } +r2i32_t r2i64_to_r2i32(r2i64_t v) { return (r2i32_t){ (i32)v.x0, (i32)v.y0, (i32)v.x1, (i32)v.y1 }; } +r3i32_t r3i64_to_r3i32(r3i64_t v) { return (r3i32_t){ (i32)v.x0, (i32)v.y0, (i32)v.z0, (i32)v.x1, (i32)v.y1, (i32)v.z1 }; } diff --git a/src/core/platform_defines.h b/src/core/platform_defines.h new file mode 100644 index 0000000..35bb87a --- /dev/null +++ b/src/core/platform_defines.h @@ -0,0 +1,69 @@ +#ifndef _CRT_SECURE_NO_WARNINGS + #define _CRT_SECURE_NO_WARNINGS +#endif + +#if defined(__APPLE__) && defined(__MACH__) + #define PLATFORM_MAC_OS 1 + #define PLATFORM_POSIX 1 +#elif defined(_WIN32) + #define PLATFORM_WINDOWS 1 +#elif defined(__linux__) + #define PLATFORM_POSIX 1 + #define PLATFORM_LINUX 1 +#elif defined(__wasm) + #define PLATFORM_WASM 1 +#endif + +#if defined(__clang__) + #define PLATFORM_CLANG 1 +#elif defined(__GNUC__) || defined(__GNUG__) + #define PLATFORM_GCC 1 +#elif defined(_MSC_VER) + #define PLATFORM_CL 1 +#elif defined(__TINYC__) + #define PLATFORM_TCC 1 +#endif + +#ifndef PLATFORM_WASM +#define PLATFORM_WASM 0 +#endif + +#ifndef PLATFORM_WINDOWS +#define PLATFORM_WINDOWS 0 +#endif + +#ifndef PLATFORM_LINUX +#define PLATFORM_LINUX 0 +#endif + +#ifndef PLATFORM_POSIX +#define PLATFORM_POSIX 0 +#endif + +#ifndef PLATFORM_MAC_OS +#define PLATFORM_MAC_OS 0 +#endif + +#ifndef PLATFORM_CLANG +#define PLATFORM_CLANG 0 +#endif + +#ifndef PLATFORM_GCC +#define PLATFORM_GCC 0 +#endif + +#ifndef PLATFORM_CL +#define PLATFORM_CL 0 +#endif + +#ifndef PLATFORM_TCC +#define PLATFORM_TCC 0 +#endif + +#ifndef PLATFORM_ASSERT +#define PLATFORM_ASSERT 1 +#endif + +#ifndef PLATFORM_ADDRESS_SANITIZER +#define PLATFORM_ADDRESS_SANITIZER 0 +#endif diff --git a/src/core/scratch.c b/src/core/scratch.c new file mode 100644 index 0000000..2b0ed0f --- /dev/null +++ b/src/core/scratch.c @@ -0,0 +1,36 @@ +ma_temp_t ma_begin_scratch_ex(ma_arena_t **conflicts, int conflict_count) { + ma_arena_t *unoccupied = 0; + for (int i = 0; i < lengthof(core_desc.scratch); i += 1) { + ma_arena_t *from_pool = core_desc.scratch + i; + unoccupied = from_pool; + for (int conflict_i = 0; conflict_i < conflict_count; conflict_i += 1) { + ma_arena_t *from_conflict = conflicts[conflict_i]; + if (from_pool == from_conflict) { + unoccupied = 0; + break; + } + } + + if (unoccupied) { + break; + } + } + + assertf(unoccupied, "Failed to get free scratch memory, this is a fatal error, this shouldnt happen"); + ma_temp_t result = ma_begin_temp(unoccupied); + return result; +} + +ma_temp_t ma_begin_scratch(void) { + ma_temp_t result = ma_begin_temp(core_desc.scratch + 0); + return result; +} + +ma_temp_t ma_begin_scratch1(ma_arena_t *conflict) { + ma_arena_t *conflicts[] = {conflict}; + return ma_begin_scratch_ex(conflicts, 1); +} + +#define ma_end_scratch(x) ma_end_temp(x) +#define ma_temp_scope(name, InArena) for (ma_temp_t name = ma_begin_temp(InArena); name.arena; (ma_end_temp(name), name.arena = 0)) +#define ma_scratch_scope(x) for (ma_temp_t x = ma_begin_scratch(); x.arena; (ma_end_temp(x), x.arena = 0)) \ No newline at end of file diff --git a/src/core/stb_sprintf.h b/src/core/stb_sprintf.h new file mode 100644 index 0000000..4cc302a --- /dev/null +++ b/src/core/stb_sprintf.h @@ -0,0 +1,1961 @@ +// stb_sprintf - v1.10 - public domain snprintf() implementation +// originally by Jeff Roberts / RAD Game Tools, 2015/10/20 +// http://github.com/nothings/stb +// +// allowed types: sc uidBboXx p AaGgEef n +// lengths : hh h ll j z t I64 I32 I +// +// Contributors: +// Fabian "ryg" Giesen (reformatting) +// github:aganm (attribute format) +// +// Contributors (bugfixes): +// github:d26435 +// github:trex78 +// github:account-login +// Jari Komppa (SI suffixes) +// Rohit Nirmal +// Marcin Wojdyr +// Leonard Ritter +// Stefano Zanotti +// Adam Allison +// Arvid Gerstmann +// Markus Kolb +// +// LICENSE: +// +// See end of file for license information. + +#ifndef STB_SPRINTF_H_INCLUDE +#define STB_SPRINTF_H_INCLUDE + +/* +Single file sprintf replacement. + +Originally written by Jeff Roberts at RAD Game Tools - 2015/10/20. +Hereby placed in public domain. + +This is a full sprintf replacement that supports everything that +the C runtime sprintfs support, including float/double, 64-bit integers, +hex floats, field parameters (%*.*d stuff), length reads backs, etc. + +Why would you need this if sprintf already exists? Well, first off, +it's *much* faster (see below). It's also much smaller than the CRT +versions code-space-wise. We've also added some simple improvements +that are super handy (commas in thousands, callbacks at buffer full, +for example). Finally, the format strings for MSVC and GCC differ +for 64-bit integers (among other small things), so this lets you use +the same format strings in cross platform code. + +It uses the standard single file trick of being both the header file +and the source itself. If you just include it normally, you just get +the header file function definitions. To get the code, you include +it from a C or C++ file and define STB_SPRINTF_IMPLEMENTATION first. + +It only uses va_args macros from the C runtime to do it's work. It +does cast doubles to S64s and shifts and divides U64s, which does +drag in CRT code on most platforms. + +It compiles to roughly 8K with float support, and 4K without. +As a comparison, when using MSVC static libs, calling sprintf drags +in 16K. + +API: +==== +int stbsp_sprintf( char * buf, char const * fmt, ... ) +int stbsp_snprintf( char * buf, int count, char const * fmt, ... ) + Convert an arg list into a buffer. stbsp_snprintf always returns + a zero-terminated string (unlike regular snprintf). + +int stbsp_vsprintf( char * buf, char const * fmt, va_list va ) +int stbsp_vsnprintf( char * buf, int count, char const * fmt, va_list va ) + Convert a va_list arg list into a buffer. stbsp_vsnprintf always returns + a zero-terminated string (unlike regular snprintf). + +int stbsp_vsprintfcb( STBSP_SPRINTFCB * callback, void * user, char * buf, char +const * fmt, va_list va ) typedef char * STBSP_SPRINTFCB( char const * buf, void +* user, int len ); Convert into a buffer, calling back every STB_SPRINTF_MIN +chars. Your callback can then copy the chars out, print them or whatever. This +function is actually the workhorse for everything else. The buffer you pass in +must hold at least STB_SPRINTF_MIN characters. + // you return the next buffer to use or 0 to stop converting + +void stbsp_set_separators( char comma, char period ) + Set the comma and period characters to use. + +FLOATS/DOUBLES: +=============== +This code uses a internal float->ascii conversion method that uses +doubles with error correction (double-doubles, for ~105 bits of +precision). This conversion is round-trip perfect - that is, an atof +of the values output here will give you the bit-exact double back. + +One difference is that our insignificant digits will be different than +with MSVC or GCC (but they don't match each other either). We also +don't attempt to find the minimum length matching float (pre-MSVC15 +doesn't either). + +If you don't need float or doubles at all, define STB_SPRINTF_NOFLOAT +and you'll save 4K of code space. + +64-BIT INTS: +============ +This library also supports 64-bit integers and you can use MSVC style or +GCC style indicators (%I64d or %lld). It supports the C99 specifiers +for size_t and ptr_diff_t (%jd %zd) as well. + +EXTRAS: +======= +Like some GCCs, for integers and floats, you can use a ' (single quote) +specifier and commas will be inserted on the thousands: "%'d" on 12345 +would print 12,345. + +For integers and floats, you can use a "$" specifier and the number +will be converted to float and then divided to get kilo, mega, giga or +tera and then printed, so "%$d" 1000 is "1.0 k", "%$.2d" 2536000 is +"2.53 M", etc. For byte values, use two $:s, like "%$$d" to turn +2536000 to "2.42 Mi". If you prefer JEDEC suffixes to SI ones, use three +$:s: "%$$$d" -> "2.42 M". To remove the space between the number and the +suffix, add "_" specifier: "%_$d" -> "2.53M". + +In addition to octal and hexadecimal conversions, you can print +integers in binary: "%b" for 256 would print 100. + +PERFORMANCE vs MSVC 2008 32-/64-bit (GCC is even slower than MSVC): +=================================================================== +"%d" across all 32-bit ints (4.8x/4.0x faster than 32-/64-bit MSVC) +"%24d" across all 32-bit ints (4.5x/4.2x faster) +"%x" across all 32-bit ints (4.5x/3.8x faster) +"%08x" across all 32-bit ints (4.3x/3.8x faster) +"%f" across e-10 to e+10 floats (7.3x/6.0x faster) +"%e" across e-10 to e+10 floats (8.1x/6.0x faster) +"%g" across e-10 to e+10 floats (10.0x/7.1x faster) +"%f" for values near e-300 (7.9x/6.5x faster) +"%f" for values near e+300 (10.0x/9.1x faster) +"%e" for values near e-300 (10.1x/7.0x faster) +"%e" for values near e+300 (9.2x/6.0x faster) +"%.320f" for values near e-300 (12.6x/11.2x faster) +"%a" for random values (8.6x/4.3x faster) +"%I64d" for 64-bits with 32-bit values (4.8x/3.4x faster) +"%I64d" for 64-bits > 32-bit values (4.9x/5.5x faster) +"%s%s%s" for 64 char strings (7.1x/7.3x faster) +"...512 char string..." ( 35.0x/32.5x faster!) +*/ + +#if defined(__clang__) +#if defined(__has_feature) && defined(__has_attribute) +#if __has_feature(address_sanitizer) +#if __has_attribute(__no_sanitize__) +#define STBSP__ASAN __attribute__((__no_sanitize__("address"))) +#elif __has_attribute(__no_sanitize_address__) +#define STBSP__ASAN __attribute__((__no_sanitize_address__)) +#elif __has_attribute(__no_address_safety_analysis__) +#define STBSP__ASAN __attribute__((__no_address_safety_analysis__)) +#endif +#endif +#endif +#elif defined(__GNUC__) && \ + (__GNUC__ >= 5 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) +#if defined(__SANITIZE_ADDRESS__) && __SANITIZE_ADDRESS__ +#define STBSP__ASAN __attribute__((__no_sanitize_address__)) +#endif +#endif + +#ifndef STBSP__ASAN +#define STBSP__ASAN +#endif + +#ifdef STB_SPRINTF_STATIC +#define STBSP__PUBLICDEC static +#define STBSP__PUBLICDEF static STBSP__ASAN +#else +#ifdef __cplusplus +#define STBSP__PUBLICDEC extern "C" +#define STBSP__PUBLICDEF extern "C" STBSP__ASAN +#else +#define STBSP__PUBLICDEC extern +#define STBSP__PUBLICDEF STBSP__ASAN +#endif +#endif + +#if defined(__has_attribute) +#if __has_attribute(format) +#define STBSP__ATTRIBUTE_FORMAT(fmt, va) \ + __attribute__((format(printf, fmt, va))) +#endif +#endif + +#ifndef STBSP__ATTRIBUTE_FORMAT +#define STBSP__ATTRIBUTE_FORMAT(fmt, va) +#endif + +#ifdef _MSC_VER +#define STBSP__NOTUSED(v) (void)(v) +#else +#define STBSP__NOTUSED(v) (void)sizeof(v) +#endif + +#include // for va_arg(), va_list() +#include // size_t, ptrdiff_t + +#ifndef STB_SPRINTF_MIN +#define STB_SPRINTF_MIN 512 // how many characters per callback +#endif +typedef char *STBSP_SPRINTFCB(const char *buf, void *user, int len); + +#ifndef STB_SPRINTF_DECORATE +#define STB_SPRINTF_DECORATE(name) \ + stbsp_##name // define this before including if you want to change the names +#endif + +STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(vsprintf)(char *buf, char const *fmt, + va_list va); +STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(vsnprintf)(char *buf, int count, + char const *fmt, + va_list va); +STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(sprintf)(char *buf, char const *fmt, + ...) + STBSP__ATTRIBUTE_FORMAT(2, 3); +STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(snprintf)(char *buf, int count, + char const *fmt, ...) + STBSP__ATTRIBUTE_FORMAT(3, 4); + +STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(vsprintfcb)(STBSP_SPRINTFCB *callback, + void *user, char *buf, + char const *fmt, + va_list va); +STBSP__PUBLICDEC void STB_SPRINTF_DECORATE(set_separators)(char comma, + char period); + +#endif // STB_SPRINTF_H_INCLUDE + +#ifdef STB_SPRINTF_IMPLEMENTATION + +#define stbsp__uint32 unsigned int +#define stbsp__int32 signed int + +#ifdef _MSC_VER +#define stbsp__uint64 unsigned __int64 +#define stbsp__int64 signed __int64 +#else +#define stbsp__uint64 unsigned long long +#define stbsp__int64 signed long long +#endif +#define stbsp__uint16 unsigned short + +#ifndef stbsp__uintptr +#if defined(__ppc64__) || defined(__powerpc64__) || defined(__aarch64__) || \ + defined(_M_X64) || defined(__x86_64__) || defined(__x86_64) || \ + defined(__s390x__) +#define stbsp__uintptr stbsp__uint64 +#else +#define stbsp__uintptr stbsp__uint32 +#endif +#endif + +#ifndef STB_SPRINTF_MSVC_MODE // used for MSVC2013 and earlier (MSVC2015 matches + // GCC) +#if defined(_MSC_VER) && (_MSC_VER < 1900) +#define STB_SPRINTF_MSVC_MODE +#endif +#endif + +#ifdef STB_SPRINTF_NOUNALIGNED // define this before inclusion to force + // stbsp_sprintf to always use aligned accesses +#define STBSP__UNALIGNED(code) +#else +#define STBSP__UNALIGNED(code) code +#endif + +#ifndef STB_SPRINTF_NOFLOAT +// internal float utility functions +static stbsp__int32 stbsp__real_to_str(char const **start, stbsp__uint32 *len, + char *out, stbsp__int32 *decimal_pos, + double value, stbsp__uint32 frac_digits); +static stbsp__int32 stbsp__real_to_parts(stbsp__int64 *bits, stbsp__int32 *expo, + double value); +#define STBSP__SPECIAL 0x7000 +#endif + +static char stbsp__period = '.'; +static char stbsp__comma = ','; +static struct { + short temp; // force next field to be 2-byte aligned + char pair[201]; +} stbsp__digitpair = {0, "00010203040506070809101112131415161718192021222324" + "25262728293031323334353637383940414243444546474849" + "50515253545556575859606162636465666768697071727374" + "75767778798081828384858687888990919293949596979899"}; + +STBSP__PUBLICDEF void STB_SPRINTF_DECORATE(set_separators)(char pcomma, + char pperiod) { + stbsp__period = pperiod; + stbsp__comma = pcomma; +} + +#define STBSP__LEFTJUST 1 +#define STBSP__LEADINGPLUS 2 +#define STBSP__LEADINGSPACE 4 +#define STBSP__LEADING_0X 8 +#define STBSP__LEADINGZERO 16 +#define STBSP__INTMAX 32 +#define STBSP__TRIPLET_COMMA 64 +#define STBSP__NEGATIVE 128 +#define STBSP__METRIC_SUFFIX 256 +#define STBSP__HALFWIDTH 512 +#define STBSP__METRIC_NOSPACE 1024 +#define STBSP__METRIC_1024 2048 +#define STBSP__METRIC_JEDEC 4096 + +static void stbsp__lead_sign(stbsp__uint32 fl, char *sign) { + sign[0] = 0; + if (fl & STBSP__NEGATIVE) { + sign[0] = 1; + sign[1] = '-'; + } else if (fl & STBSP__LEADINGSPACE) { + sign[0] = 1; + sign[1] = ' '; + } else if (fl & STBSP__LEADINGPLUS) { + sign[0] = 1; + sign[1] = '+'; + } +} + +static STBSP__ASAN stbsp__uint32 stbsp__strlen_limited(char const *s, + stbsp__uint32 limit) { + char const *sn = s; + + // get up to 4-byte alignment + for (;;) { + if (((stbsp__uintptr)sn & 3) == 0) + break; + + if (!limit || *sn == 0) + return (stbsp__uint32)(sn - s); + + ++sn; + --limit; + } + + // scan over 4 bytes at a time to find terminating 0 + // this will intentionally scan up to 3 bytes past the end of buffers, + // but becase it works 4B aligned, it will never cross page boundaries + // (hence the STBSP__ASAN markup; the over-read here is intentional + // and harmless) + while (limit >= 4) { + stbsp__uint32 v = *(stbsp__uint32 *)sn; + // bit hack to find if there's a 0 byte in there + if ((v - 0x01010101) & (~v) & 0x80808080UL) + break; + + sn += 4; + limit -= 4; + } + + // handle the last few characters to find actual size + while (limit && *sn) { + ++sn; + --limit; + } + + return (stbsp__uint32)(sn - s); +} + +STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsprintfcb)(STBSP_SPRINTFCB *callback, + void *user, char *buf, + char const *fmt, + va_list va) { + static char hex[] = "0123456789abcdefxp"; + static char hexu[] = "0123456789ABCDEFXP"; + char *bf; + char const *f; + int tlen = 0; + + bf = buf; + f = fmt; + for (;;) { + stbsp__int32 fw, pr, tz; + stbsp__uint32 fl; + + // macros for the callback buffer stuff +#define stbsp__chk_cb_bufL(bytes) \ + { \ + int len = (int)(bf - buf); \ + if ((len + (bytes)) >= STB_SPRINTF_MIN) { \ + tlen += len; \ + if (0 == (bf = buf = callback(buf, user, len))) \ + goto done; \ + } \ + } +#define stbsp__chk_cb_buf(bytes) \ + { \ + if (callback) { \ + stbsp__chk_cb_bufL(bytes); \ + } \ + } +#define stbsp__flush_cb() \ + { \ + stbsp__chk_cb_bufL(STB_SPRINTF_MIN - 1); \ + } // flush if there is even one byte in the buffer +#define stbsp__cb_buf_clamp(cl, v) \ + cl = v; \ + if (callback) { \ + int lg = STB_SPRINTF_MIN - (int)(bf - buf); \ + if (cl > lg) \ + cl = lg; \ + } + + // fast copy everything up to the next % (or end of string) + for (;;) { + while (((stbsp__uintptr)f) & 3) { + schk1: + if (f[0] == '%') + goto scandd; + schk2: + if (f[0] == 0) + goto endfmt; + stbsp__chk_cb_buf(1); + *bf++ = f[0]; + ++f; + } + for (;;) { + // Check if the next 4 bytes contain %(0x25) or end of string. + // Using the 'hasless' trick: + // https://graphics.stanford.edu/~seander/bithacks.html#HasLessInWord + stbsp__uint32 v, c; + v = *(stbsp__uint32 *)f; + c = (~v) & 0x80808080; + if (((v ^ 0x25252525) - 0x01010101) & c) + goto schk1; + if ((v - 0x01010101) & c) + goto schk2; + if (callback) + if ((STB_SPRINTF_MIN - (int)(bf - buf)) < 4) + goto schk1; +#ifdef STB_SPRINTF_NOUNALIGNED + if (((stbsp__uintptr)bf) & 3) { + bf[0] = f[0]; + bf[1] = f[1]; + bf[2] = f[2]; + bf[3] = f[3]; + } else +#endif + { + *(stbsp__uint32 *)bf = v; + } + bf += 4; + f += 4; + } + } + scandd: + + ++f; + + // ok, we have a percent, read the modifiers first + fw = 0; + pr = -1; + fl = 0; + tz = 0; + + // flags + for (;;) { + switch (f[0]) { + // if we have left justify + case '-': + fl |= STBSP__LEFTJUST; + ++f; + continue; + // if we have leading plus + case '+': + fl |= STBSP__LEADINGPLUS; + ++f; + continue; + // if we have leading space + case ' ': + fl |= STBSP__LEADINGSPACE; + ++f; + continue; + // if we have leading 0x + case '#': + fl |= STBSP__LEADING_0X; + ++f; + continue; + // if we have thousand commas + case '\'': + fl |= STBSP__TRIPLET_COMMA; + ++f; + continue; + // if we have kilo marker (none->kilo->kibi->jedec) + case '$': + if (fl & STBSP__METRIC_SUFFIX) { + if (fl & STBSP__METRIC_1024) { + fl |= STBSP__METRIC_JEDEC; + } else { + fl |= STBSP__METRIC_1024; + } + } else { + fl |= STBSP__METRIC_SUFFIX; + } + ++f; + continue; + // if we don't want space between metric suffix and number + case '_': + fl |= STBSP__METRIC_NOSPACE; + ++f; + continue; + // if we have leading zero + case '0': + fl |= STBSP__LEADINGZERO; + ++f; + goto flags_done; + default: + goto flags_done; + } + } + flags_done: + + // get the field width + if (f[0] == '*') { + fw = va_arg(va, stbsp__uint32); + ++f; + } else { + while ((f[0] >= '0') && (f[0] <= '9')) { + fw = fw * 10 + f[0] - '0'; + f++; + } + } + // get the precision + if (f[0] == '.') { + ++f; + if (f[0] == '*') { + pr = va_arg(va, stbsp__uint32); + ++f; + } else { + pr = 0; + while ((f[0] >= '0') && (f[0] <= '9')) { + pr = pr * 10 + f[0] - '0'; + f++; + } + } + } + + // handle integer size overrides + switch (f[0]) { + // are we halfwidth? + case 'h': + fl |= STBSP__HALFWIDTH; + ++f; + if (f[0] == 'h') + ++f; // QUARTERWIDTH + break; + // are we 64-bit (unix style) + case 'l': + fl |= ((sizeof(long) == 8) ? STBSP__INTMAX : 0); + ++f; + if (f[0] == 'l') { + fl |= STBSP__INTMAX; + ++f; + } + break; + // are we 64-bit on intmax? (c99) + case 'j': + fl |= (sizeof(size_t) == 8) ? STBSP__INTMAX : 0; + ++f; + break; + // are we 64-bit on size_t or ptrdiff_t? (c99) + case 'z': + fl |= (sizeof(ptrdiff_t) == 8) ? STBSP__INTMAX : 0; + ++f; + break; + case 't': + fl |= (sizeof(ptrdiff_t) == 8) ? STBSP__INTMAX : 0; + ++f; + break; + // are we 64-bit (msft style) + case 'I': + if ((f[1] == '6') && (f[2] == '4')) { + fl |= STBSP__INTMAX; + f += 3; + } else if ((f[1] == '3') && (f[2] == '2')) { + f += 3; + } else { + fl |= ((sizeof(void *) == 8) ? STBSP__INTMAX : 0); + ++f; + } + break; + default: + break; + } + + // handle each replacement + switch (f[0]) { +#define STBSP__NUMSZ 512 // big enough for e308 (with commas) or e-307 + char num[STBSP__NUMSZ]; + char lead[8]; + char tail[8]; + char *s; + char const *h; + stbsp__uint32 l, n, cs; + stbsp__uint64 n64; +#ifndef STB_SPRINTF_NOFLOAT + double fv; +#endif + stbsp__int32 dp; + char const *sn; + struct STB_STRING { char *str; int64_t len; }; + struct STB_STRING str; + + case 'S': + str = va_arg(va, struct STB_STRING); + if (str.str == 0 && str.len != 0) { + str.str = (char *)"null"; + str.len = 4; + } + pr = (int)str.len; + s = (char *)str.str; + l = stbsp__strlen_limited(s, (pr >= 0) ? pr : ~0u); + lead[0] = 0; + tail[0] = 0; + pr = 0; + dp = 0; + cs = 0; + goto scopy; + + case 's': + // get the string + s = va_arg(va, char *); + if (s == 0) + s = (char *)"null"; + // get the length, limited to desired precision + // always limit to ~0u chars since our counts are 32b + l = stbsp__strlen_limited(s, (pr >= 0) ? pr : ~0u); + lead[0] = 0; + tail[0] = 0; + pr = 0; + dp = 0; + cs = 0; + // copy the string in + goto scopy; + + case 'c': // char + // get the character + s = num + STBSP__NUMSZ - 1; + *s = (char)va_arg(va, int); + l = 1; + lead[0] = 0; + tail[0] = 0; + pr = 0; + dp = 0; + cs = 0; + goto scopy; + + case 'n': // weird write-bytes specifier + { + int *d = va_arg(va, int *); + *d = tlen + (int)(bf - buf); + } break; + +#ifdef STB_SPRINTF_NOFLOAT + case 'A': // float + case 'a': // hex float + case 'G': // float + case 'g': // float + case 'E': // float + case 'e': // float + case 'f': // float + va_arg(va, double); // eat it + s = (char *)"No float"; + l = 8; + lead[0] = 0; + tail[0] = 0; + pr = 0; + cs = 0; + STBSP__NOTUSED(dp); + goto scopy; +#else + case 'A': // hex float + case 'a': // hex float + h = (f[0] == 'A') ? hexu : hex; + fv = va_arg(va, double); + if (pr == -1) + pr = 6; // default is 6 + // read the double into a string + if (stbsp__real_to_parts((stbsp__int64 *)&n64, &dp, fv)) + fl |= STBSP__NEGATIVE; + + s = num + 64; + + stbsp__lead_sign(fl, lead); + + if (dp == -1023) + dp = (n64) ? -1022 : 0; + else + n64 |= (((stbsp__uint64)1) << 52); + n64 <<= (64 - 56); + if (pr < 15) + n64 += ((((stbsp__uint64)8) << 56) >> (pr * 4)); + // add leading chars + +#ifdef STB_SPRINTF_MSVC_MODE + *s++ = '0'; + *s++ = 'x'; +#else + lead[1 + lead[0]] = '0'; + lead[2 + lead[0]] = 'x'; + lead[0] += 2; +#endif + *s++ = h[(n64 >> 60) & 15]; + n64 <<= 4; + if (pr) + *s++ = stbsp__period; + sn = s; + + // print the bits + n = pr; + if (n > 13) + n = 13; + if (pr > (stbsp__int32)n) + tz = pr - n; + pr = 0; + while (n--) { + *s++ = h[(n64 >> 60) & 15]; + n64 <<= 4; + } + + // print the expo + tail[1] = h[17]; + if (dp < 0) { + tail[2] = '-'; + dp = -dp; + } else + tail[2] = '+'; + n = (dp >= 1000) ? 6 : ((dp >= 100) ? 5 : ((dp >= 10) ? 4 : 3)); + tail[0] = (char)n; + for (;;) { + tail[n] = '0' + dp % 10; + if (n <= 3) + break; + --n; + dp /= 10; + } + + dp = (int)(s - sn); + l = (int)(s - (num + 64)); + s = num + 64; + cs = 1 + (3 << 24); + goto scopy; + + case 'G': // float + case 'g': // float + h = (f[0] == 'G') ? hexu : hex; + fv = va_arg(va, double); + if (pr == -1) + pr = 6; + else if (pr == 0) + pr = 1; // default is 6 + // read the double into a string + if (stbsp__real_to_str(&sn, &l, num, &dp, fv, (pr - 1) | 0x80000000)) + fl |= STBSP__NEGATIVE; + + // clamp the precision and delete extra zeros after clamp + n = pr; + if (l > (stbsp__uint32)pr) + l = pr; + while ((l > 1) && (pr) && (sn[l - 1] == '0')) { + --pr; + --l; + } + + // should we use %e + if ((dp <= -4) || (dp > (stbsp__int32)n)) { + if (pr > (stbsp__int32)l) + pr = l - 1; + else if (pr) + --pr; // when using %e, there is one digit before the decimal + goto doexpfromg; + } + // this is the insane action to get the pr to match %g semantics for %f + if (dp > 0) { + pr = (dp < (stbsp__int32)l) ? l - dp : 0; + } else { + pr = -dp + ((pr > (stbsp__int32)l) ? (stbsp__int32)l : pr); + } + goto dofloatfromg; + + case 'E': // float + case 'e': // float + h = (f[0] == 'E') ? hexu : hex; + fv = va_arg(va, double); + if (pr == -1) + pr = 6; // default is 6 + // read the double into a string + if (stbsp__real_to_str(&sn, &l, num, &dp, fv, pr | 0x80000000)) + fl |= STBSP__NEGATIVE; + doexpfromg: + tail[0] = 0; + stbsp__lead_sign(fl, lead); + if (dp == STBSP__SPECIAL) { + s = (char *)sn; + cs = 0; + pr = 0; + goto scopy; + } + s = num + 64; + // handle leading chars + *s++ = sn[0]; + + if (pr) + *s++ = stbsp__period; + + // handle after decimal + if ((l - 1) > (stbsp__uint32)pr) + l = pr + 1; + for (n = 1; n < l; n++) + *s++ = sn[n]; + // trailing zeros + tz = pr - (l - 1); + pr = 0; + // dump expo + tail[1] = h[0xe]; + dp -= 1; + if (dp < 0) { + tail[2] = '-'; + dp = -dp; + } else + tail[2] = '+'; +#ifdef STB_SPRINTF_MSVC_MODE + n = 5; +#else + n = (dp >= 100) ? 5 : 4; +#endif + tail[0] = (char)n; + for (;;) { + tail[n] = '0' + dp % 10; + if (n <= 3) + break; + --n; + dp /= 10; + } + cs = 1 + (3 << 24); // how many tens + goto flt_lead; + + case 'f': // float + fv = va_arg(va, double); + doafloat: + // do kilos + if (fl & STBSP__METRIC_SUFFIX) { + double divisor; + divisor = 1000.0f; + if (fl & STBSP__METRIC_1024) + divisor = 1024.0; + while (fl < 0x4000000) { + if ((fv < divisor) && (fv > -divisor)) + break; + fv /= divisor; + fl += 0x1000000; + } + } + if (pr == -1) + pr = 6; // default is 6 + // read the double into a string + if (stbsp__real_to_str(&sn, &l, num, &dp, fv, pr)) + fl |= STBSP__NEGATIVE; + dofloatfromg: + tail[0] = 0; + stbsp__lead_sign(fl, lead); + if (dp == STBSP__SPECIAL) { + s = (char *)sn; + cs = 0; + pr = 0; + goto scopy; + } + s = num + 64; + + // handle the three decimal varieties + if (dp <= 0) { + stbsp__int32 i; + // handle 0.000*000xxxx + *s++ = '0'; + if (pr) + *s++ = stbsp__period; + n = -dp; + if ((stbsp__int32)n > pr) + n = pr; + i = n; + while (i) { + if ((((stbsp__uintptr)s) & 3) == 0) + break; + *s++ = '0'; + --i; + } + while (i >= 4) { + *(stbsp__uint32 *)s = 0x30303030; + s += 4; + i -= 4; + } + while (i) { + *s++ = '0'; + --i; + } + if ((stbsp__int32)(l + n) > pr) + l = pr - n; + i = l; + while (i) { + *s++ = *sn++; + --i; + } + tz = pr - (n + l); + cs = 1 + (3 << 24); // how many tens did we write (for commas below) + } else { + cs = (fl & STBSP__TRIPLET_COMMA) ? ((600 - (stbsp__uint32)dp) % 3) : 0; + if ((stbsp__uint32)dp >= l) { + // handle xxxx000*000.0 + n = 0; + for (;;) { + if ((fl & STBSP__TRIPLET_COMMA) && (++cs == 4)) { + cs = 0; + *s++ = stbsp__comma; + } else { + *s++ = sn[n]; + ++n; + if (n >= l) + break; + } + } + if (n < (stbsp__uint32)dp) { + n = dp - n; + if ((fl & STBSP__TRIPLET_COMMA) == 0) { + while (n) { + if ((((stbsp__uintptr)s) & 3) == 0) + break; + *s++ = '0'; + --n; + } + while (n >= 4) { + *(stbsp__uint32 *)s = 0x30303030; + s += 4; + n -= 4; + } + } + while (n) { + if ((fl & STBSP__TRIPLET_COMMA) && (++cs == 4)) { + cs = 0; + *s++ = stbsp__comma; + } else { + *s++ = '0'; + --n; + } + } + } + cs = (int)(s - (num + 64)) + (3 << 24); // cs is how many tens + if (pr) { + *s++ = stbsp__period; + tz = pr; + } + } else { + // handle xxxxx.xxxx000*000 + n = 0; + for (;;) { + if ((fl & STBSP__TRIPLET_COMMA) && (++cs == 4)) { + cs = 0; + *s++ = stbsp__comma; + } else { + *s++ = sn[n]; + ++n; + if (n >= (stbsp__uint32)dp) + break; + } + } + cs = (int)(s - (num + 64)) + (3 << 24); // cs is how many tens + if (pr) + *s++ = stbsp__period; + if ((l - dp) > (stbsp__uint32)pr) + l = pr + dp; + while (n < l) { + *s++ = sn[n]; + ++n; + } + tz = pr - (l - dp); + } + } + pr = 0; + + // handle k,m,g,t + if (fl & STBSP__METRIC_SUFFIX) { + char idx; + idx = 1; + if (fl & STBSP__METRIC_NOSPACE) + idx = 0; + tail[0] = idx; + tail[1] = ' '; + { + if (fl >> 24) { // SI kilo is 'k', JEDEC and SI kibits are 'K'. + if (fl & STBSP__METRIC_1024) + tail[idx + 1] = "_KMGT"[fl >> 24]; + else + tail[idx + 1] = "_kMGT"[fl >> 24]; + idx++; + // If printing kibits and not in jedec, add the 'i'. + if (fl & STBSP__METRIC_1024 && !(fl & STBSP__METRIC_JEDEC)) { + tail[idx + 1] = 'i'; + idx++; + } + tail[0] = idx; + } + } + }; + + flt_lead: + // get the length that we copied + l = (stbsp__uint32)(s - (num + 64)); + s = num + 64; + goto scopy; +#endif + + case 'B': // upper binary + case 'b': // lower binary + h = (f[0] == 'B') ? hexu : hex; + lead[0] = 0; + if (fl & STBSP__LEADING_0X) { + lead[0] = 2; + lead[1] = '0'; + lead[2] = h[0xb]; + } + l = (8 << 4) | (1 << 8); + goto radixnum; + + case 'o': // octal + h = hexu; + lead[0] = 0; + if (fl & STBSP__LEADING_0X) { + lead[0] = 1; + lead[1] = '0'; + } + l = (3 << 4) | (3 << 8); + goto radixnum; + + case 'p': // pointer + fl |= (sizeof(void *) == 8) ? STBSP__INTMAX : 0; + pr = sizeof(void *) * 2; + fl &= ~STBSP__LEADINGZERO; // 'p' only prints the pointer with zeros + // fall through - to X + + case 'X': // upper hex + case 'x': // lower hex + h = (f[0] == 'X') ? hexu : hex; + l = (4 << 4) | (4 << 8); + lead[0] = 0; + if (fl & STBSP__LEADING_0X) { + lead[0] = 2; + lead[1] = '0'; + lead[2] = h[16]; + } + radixnum: + // get the number + if (fl & STBSP__INTMAX) + n64 = va_arg(va, stbsp__uint64); + else + n64 = va_arg(va, stbsp__uint32); + + s = num + STBSP__NUMSZ; + dp = 0; + // clear tail, and clear leading if value is zero + tail[0] = 0; + if (n64 == 0) { + lead[0] = 0; + if (pr == 0) { + l = 0; + cs = 0; + goto scopy; + } + } + // convert to string + for (;;) { + *--s = h[n64 & ((1 << (l >> 8)) - 1)]; + n64 >>= (l >> 8); + if (!((n64) || ((stbsp__int32)((num + STBSP__NUMSZ) - s) < pr))) + break; + if (fl & STBSP__TRIPLET_COMMA) { + ++l; + if ((l & 15) == ((l >> 4) & 15)) { + l &= ~15; + *--s = stbsp__comma; + } + } + }; + // get the tens and the comma pos + cs = + (stbsp__uint32)((num + STBSP__NUMSZ) - s) + ((((l >> 4) & 15)) << 24); + // get the length that we copied + l = (stbsp__uint32)((num + STBSP__NUMSZ) - s); + // copy it + goto scopy; + + case 'u': // unsigned + case 'i': + case 'd': // integer + // get the integer and abs it + if (fl & STBSP__INTMAX) { + stbsp__int64 i64 = va_arg(va, stbsp__int64); + n64 = (stbsp__uint64)i64; + if ((f[0] != 'u') && (i64 < 0)) { + n64 = (stbsp__uint64)-i64; + fl |= STBSP__NEGATIVE; + } + } else { + stbsp__int32 i = va_arg(va, stbsp__int32); + n64 = (stbsp__uint32)i; + if ((f[0] != 'u') && (i < 0)) { + n64 = (stbsp__uint32)-i; + fl |= STBSP__NEGATIVE; + } + } + +#ifndef STB_SPRINTF_NOFLOAT + if (fl & STBSP__METRIC_SUFFIX) { + if (n64 < 1024) + pr = 0; + else if (pr == -1) + pr = 1; + fv = (double)(stbsp__int64)n64; + goto doafloat; + } +#endif + + // convert to string + s = num + STBSP__NUMSZ; + l = 0; + + for (;;) { + // do in 32-bit chunks (avoid lots of 64-bit divides even with constant + // denominators) + char *o = s - 8; + if (n64 >= 100000000) { + n = (stbsp__uint32)(n64 % 100000000); + n64 /= 100000000; + } else { + n = (stbsp__uint32)n64; + n64 = 0; + } + if ((fl & STBSP__TRIPLET_COMMA) == 0) { + do { + s -= 2; + *(stbsp__uint16 *)s = + *(stbsp__uint16 *)&stbsp__digitpair.pair[(n % 100) * 2]; + n /= 100; + } while (n); + } + while (n) { + if ((fl & STBSP__TRIPLET_COMMA) && (l++ == 3)) { + l = 0; + *--s = stbsp__comma; + --o; + } else { + *--s = (char)(n % 10) + '0'; + n /= 10; + } + } + if (n64 == 0) { + if ((s[0] == '0') && (s != (num + STBSP__NUMSZ))) + ++s; + break; + } + while (s != o) + if ((fl & STBSP__TRIPLET_COMMA) && (l++ == 3)) { + l = 0; + *--s = stbsp__comma; + --o; + } else { + *--s = '0'; + } + } + + tail[0] = 0; + stbsp__lead_sign(fl, lead); + + // get the length that we copied + l = (stbsp__uint32)((num + STBSP__NUMSZ) - s); + if (l == 0) { + *--s = '0'; + l = 1; + } + cs = l + (3 << 24); + if (pr < 0) + pr = 0; + + scopy: + // get fw=leading/trailing space, pr=leading zeros + if (pr < (stbsp__int32)l) + pr = l; + n = pr + lead[0] + tail[0] + tz; + if (fw < (stbsp__int32)n) + fw = n; + fw -= n; + pr -= l; + + // handle right justify and leading zeros + if ((fl & STBSP__LEFTJUST) == 0) { + if (fl & STBSP__LEADINGZERO) // if leading zeros, everything is in pr + { + pr = (fw > pr) ? fw : pr; + fw = 0; + } else { + fl &= ~STBSP__TRIPLET_COMMA; // if no leading zeros, then no commas + } + } + + // copy the spaces and/or zeros + if (fw + pr) { + stbsp__int32 i; + stbsp__uint32 c; + + // copy leading spaces (or when doing %8.4d stuff) + if ((fl & STBSP__LEFTJUST) == 0) + while (fw > 0) { + stbsp__cb_buf_clamp(i, fw); + fw -= i; + while (i) { + if ((((stbsp__uintptr)bf) & 3) == 0) + break; + *bf++ = ' '; + --i; + } + while (i >= 4) { + *(stbsp__uint32 *)bf = 0x20202020; + bf += 4; + i -= 4; + } + while (i) { + *bf++ = ' '; + --i; + } + stbsp__chk_cb_buf(1); + } + + // copy leader + sn = lead + 1; + while (lead[0]) { + stbsp__cb_buf_clamp(i, lead[0]); + lead[0] -= (char)i; + while (i) { + *bf++ = *sn++; + --i; + } + stbsp__chk_cb_buf(1); + } + + // copy leading zeros + c = cs >> 24; + cs &= 0xffffff; + cs = (fl & STBSP__TRIPLET_COMMA) + ? ((stbsp__uint32)(c - ((pr + cs) % (c + 1)))) + : 0; + while (pr > 0) { + stbsp__cb_buf_clamp(i, pr); + pr -= i; + if ((fl & STBSP__TRIPLET_COMMA) == 0) { + while (i) { + if ((((stbsp__uintptr)bf) & 3) == 0) + break; + *bf++ = '0'; + --i; + } + while (i >= 4) { + *(stbsp__uint32 *)bf = 0x30303030; + bf += 4; + i -= 4; + } + } + while (i) { + if ((fl & STBSP__TRIPLET_COMMA) && (cs++ == c)) { + cs = 0; + *bf++ = stbsp__comma; + } else + *bf++ = '0'; + --i; + } + stbsp__chk_cb_buf(1); + } + } + + // copy leader if there is still one + sn = lead + 1; + while (lead[0]) { + stbsp__int32 i; + stbsp__cb_buf_clamp(i, lead[0]); + lead[0] -= (char)i; + while (i) { + *bf++ = *sn++; + --i; + } + stbsp__chk_cb_buf(1); + } + + // copy the string + n = l; + while (n) { + stbsp__int32 i; + stbsp__cb_buf_clamp(i, n); + n -= i; + STBSP__UNALIGNED(while (i >= 4) { + *(stbsp__uint32 volatile *)bf = *(stbsp__uint32 volatile *)s; + bf += 4; + s += 4; + i -= 4; + }) + while (i) { + *bf++ = *s++; + --i; + } + stbsp__chk_cb_buf(1); + } + + // copy trailing zeros + while (tz) { + stbsp__int32 i; + stbsp__cb_buf_clamp(i, tz); + tz -= i; + while (i) { + if ((((stbsp__uintptr)bf) & 3) == 0) + break; + *bf++ = '0'; + --i; + } + while (i >= 4) { + *(stbsp__uint32 *)bf = 0x30303030; + bf += 4; + i -= 4; + } + while (i) { + *bf++ = '0'; + --i; + } + stbsp__chk_cb_buf(1); + } + + // copy tail if there is one + sn = tail + 1; + while (tail[0]) { + stbsp__int32 i; + stbsp__cb_buf_clamp(i, tail[0]); + tail[0] -= (char)i; + while (i) { + *bf++ = *sn++; + --i; + } + stbsp__chk_cb_buf(1); + } + + // handle the left justify + if (fl & STBSP__LEFTJUST) + if (fw > 0) { + while (fw) { + stbsp__int32 i; + stbsp__cb_buf_clamp(i, fw); + fw -= i; + while (i) { + if ((((stbsp__uintptr)bf) & 3) == 0) + break; + *bf++ = ' '; + --i; + } + while (i >= 4) { + *(stbsp__uint32 *)bf = 0x20202020; + bf += 4; + i -= 4; + } + while (i--) + *bf++ = ' '; + stbsp__chk_cb_buf(1); + } + } + break; + + default: // unknown, just copy code + s = num + STBSP__NUMSZ - 1; + *s = f[0]; + l = 1; + fw = fl = 0; + lead[0] = 0; + tail[0] = 0; + pr = 0; + dp = 0; + cs = 0; + goto scopy; + } + ++f; + } +endfmt: + + if (!callback) + *bf = 0; + else + stbsp__flush_cb(); + +done: + return tlen + (int)(bf - buf); +} + +// cleanup +#undef STBSP__LEFTJUST +#undef STBSP__LEADINGPLUS +#undef STBSP__LEADINGSPACE +#undef STBSP__LEADING_0X +#undef STBSP__LEADINGZERO +#undef STBSP__INTMAX +#undef STBSP__TRIPLET_COMMA +#undef STBSP__NEGATIVE +#undef STBSP__METRIC_SUFFIX +#undef STBSP__NUMSZ +#undef stbsp__chk_cb_bufL +#undef stbsp__chk_cb_buf +#undef stbsp__flush_cb +#undef stbsp__cb_buf_clamp + +// ============================================================================ +// wrapper functions + +STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(sprintf)(char *buf, char const *fmt, + ...) { + int result; + va_list va; + va_start(va, fmt); + result = STB_SPRINTF_DECORATE(vsprintfcb)(0, 0, buf, fmt, va); + va_end(va); + return result; +} + +typedef struct stbsp__context { + char *buf; + int count; + int length; + char tmp[STB_SPRINTF_MIN]; +} stbsp__context; + +static char *stbsp__clamp_callback(const char *buf, void *user, int len) { + stbsp__context *c = (stbsp__context *)user; + c->length += len; + + if (len > c->count) + len = c->count; + + if (len) { + if (buf != c->buf) { + const char *s, *se; + char *d; + d = c->buf; + s = buf; + se = buf + len; + do { + *d++ = *s++; + } while (s < se); + } + c->buf += len; + c->count -= len; + } + + if (c->count <= 0) + return c->tmp; + return (c->count >= STB_SPRINTF_MIN) + ? c->buf + : c->tmp; // go direct into buffer if you can +} + +static char *stbsp__count_clamp_callback(const char *buf, void *user, int len) { + stbsp__context *c = (stbsp__context *)user; + (void)sizeof(buf); + + c->length += len; + return c->tmp; // go direct into buffer if you can +} + +STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsnprintf)(char *buf, int count, + char const *fmt, + va_list va) { + stbsp__context c; + + if ((count == 0) && !buf) { + c.length = 0; + + STB_SPRINTF_DECORATE(vsprintfcb) + (stbsp__count_clamp_callback, &c, c.tmp, fmt, va); + } else { + int l; + + c.buf = buf; + c.count = count; + c.length = 0; + + STB_SPRINTF_DECORATE(vsprintfcb) + (stbsp__clamp_callback, &c, stbsp__clamp_callback(0, &c, 0), fmt, va); + + // zero-terminate + l = (int)(c.buf - buf); + if (l >= count) // should never be greater, only equal (or less) than count + l = count - 1; + buf[l] = 0; + } + + return c.length; +} + +STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(snprintf)(char *buf, int count, + char const *fmt, ...) { + int result; + va_list va; + va_start(va, fmt); + + result = STB_SPRINTF_DECORATE(vsnprintf)(buf, count, fmt, va); + va_end(va); + + return result; +} + +STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsprintf)(char *buf, char const *fmt, + va_list va) { + return STB_SPRINTF_DECORATE(vsprintfcb)(0, 0, buf, fmt, va); +} + +// ======================================================================= +// low level float utility functions + +#ifndef STB_SPRINTF_NOFLOAT + +// copies d to bits w/ strict aliasing (this compiles to nothing on /Ox) +#define STBSP__COPYFP(dest, src) \ + { \ + int cn; \ + for (cn = 0; cn < 8; cn++) \ + ((char *)&dest)[cn] = ((char *)&src)[cn]; \ + } + +// get float info +static stbsp__int32 stbsp__real_to_parts(stbsp__int64 *bits, stbsp__int32 *expo, + double value) { + double d; + stbsp__int64 b = 0; + + // load value and round at the frac_digits + d = value; + + STBSP__COPYFP(b, d); + + *bits = b & ((((stbsp__uint64)1) << 52) - 1); + *expo = (stbsp__int32)(((b >> 52) & 2047) - 1023); + + return (stbsp__int32)((stbsp__uint64)b >> 63); +} + +static double const stbsp__bot[23] = { + 1e+000, 1e+001, 1e+002, 1e+003, 1e+004, 1e+005, 1e+006, 1e+007, + 1e+008, 1e+009, 1e+010, 1e+011, 1e+012, 1e+013, 1e+014, 1e+015, + 1e+016, 1e+017, 1e+018, 1e+019, 1e+020, 1e+021, 1e+022}; +static double const stbsp__negbot[22] = { + 1e-001, 1e-002, 1e-003, 1e-004, 1e-005, 1e-006, 1e-007, 1e-008, + 1e-009, 1e-010, 1e-011, 1e-012, 1e-013, 1e-014, 1e-015, 1e-016, + 1e-017, 1e-018, 1e-019, 1e-020, 1e-021, 1e-022}; +static double const stbsp__negboterr[22] = { + -5.551115123125783e-018, -2.0816681711721684e-019, + -2.0816681711721686e-020, -4.7921736023859299e-021, + -8.1803053914031305e-022, 4.5251888174113741e-023, + 4.5251888174113739e-024, -2.0922560830128471e-025, + -6.2281591457779853e-026, -3.6432197315497743e-027, + 6.0503030718060191e-028, 2.0113352370744385e-029, + -3.0373745563400371e-030, 1.1806906454401013e-032, + -7.7705399876661076e-032, 2.0902213275965398e-033, + -7.1542424054621921e-034, -7.1542424054621926e-035, + 2.4754073164739869e-036, 5.4846728545790429e-037, + 9.2462547772103625e-038, -4.8596774326570872e-039}; +static double const stbsp__top[13] = {1e+023, 1e+046, 1e+069, 1e+092, 1e+115, + 1e+138, 1e+161, 1e+184, 1e+207, 1e+230, + 1e+253, 1e+276, 1e+299}; +static double const stbsp__negtop[13] = {1e-023, 1e-046, 1e-069, 1e-092, 1e-115, + 1e-138, 1e-161, 1e-184, 1e-207, 1e-230, + 1e-253, 1e-276, 1e-299}; +static double const stbsp__toperr[13] = {8388608, + 6.8601809640529717e+028, + -7.253143638152921e+052, + -4.3377296974619174e+075, + -1.5559416129466825e+098, + -3.2841562489204913e+121, + -3.7745893248228135e+144, + -1.7356668416969134e+167, + -3.8893577551088374e+190, + -9.9566444326005119e+213, + 6.3641293062232429e+236, + -5.2069140800249813e+259, + -5.2504760255204387e+282}; +static double const stbsp__negtoperr[13] = { + 3.9565301985100693e-040, -2.299904345391321e-063, + 3.6506201437945798e-086, 1.1875228833981544e-109, + -5.0644902316928607e-132, -6.7156837247865426e-155, + -2.812077463003139e-178, -5.7778912386589953e-201, + 7.4997100559334532e-224, -4.6439668915134491e-247, + -6.3691100762962136e-270, -9.436808465446358e-293, + 8.0970921678014997e-317}; + +#if defined(_MSC_VER) && (_MSC_VER <= 1200) +static stbsp__uint64 const stbsp__powten[20] = {1, + 10, + 100, + 1000, + 10000, + 100000, + 1000000, + 10000000, + 100000000, + 1000000000, + 10000000000, + 100000000000, + 1000000000000, + 10000000000000, + 100000000000000, + 1000000000000000, + 10000000000000000, + 100000000000000000, + 1000000000000000000, + 10000000000000000000U}; +#define stbsp__tento19th ((stbsp__uint64)1000000000000000000) +#else +static stbsp__uint64 const stbsp__powten[20] = {1, + 10, + 100, + 1000, + 10000, + 100000, + 1000000, + 10000000, + 100000000, + 1000000000, + 10000000000ULL, + 100000000000ULL, + 1000000000000ULL, + 10000000000000ULL, + 100000000000000ULL, + 1000000000000000ULL, + 10000000000000000ULL, + 100000000000000000ULL, + 1000000000000000000ULL, + 10000000000000000000ULL}; +#define stbsp__tento19th (1000000000000000000ULL) +#endif + +#define stbsp__ddmulthi(oh, ol, xh, yh) \ + { \ + double ahi = 0, alo, bhi = 0, blo; \ + stbsp__int64 bt; \ + oh = xh * yh; \ + STBSP__COPYFP(bt, xh); \ + bt &= ((~(stbsp__uint64)0) << 27); \ + STBSP__COPYFP(ahi, bt); \ + alo = xh - ahi; \ + STBSP__COPYFP(bt, yh); \ + bt &= ((~(stbsp__uint64)0) << 27); \ + STBSP__COPYFP(bhi, bt); \ + blo = yh - bhi; \ + ol = ((ahi * bhi - oh) + ahi * blo + alo * bhi) + alo * blo; \ + } + +#define stbsp__ddtoS64(ob, xh, xl) \ + { \ + double ahi = 0, alo, vh, t; \ + ob = (stbsp__int64)xh; \ + vh = (double)ob; \ + ahi = (xh - vh); \ + t = (ahi - xh); \ + alo = (xh - (ahi - t)) - (vh + t); \ + ob += (stbsp__int64)(ahi + alo + xl); \ + } + +#define stbsp__ddrenorm(oh, ol) \ + { \ + double s; \ + s = oh + ol; \ + ol = ol - (s - oh); \ + oh = s; \ + } + +#define stbsp__ddmultlo(oh, ol, xh, xl, yh, yl) ol = ol + (xh * yl + xl * yh); + +#define stbsp__ddmultlos(oh, ol, xh, yl) ol = ol + (xh * yl); + +static void +stbsp__raise_to_power10(double *ohi, double *olo, double d, + stbsp__int32 power) // power can be -323 to +350 +{ + double ph, pl; + if ((power >= 0) && (power <= 22)) { + stbsp__ddmulthi(ph, pl, d, stbsp__bot[power]); + } else { + stbsp__int32 e, et, eb; + double p2h, p2l; + + e = power; + if (power < 0) + e = -e; + et = (e * 0x2c9) >> 14; /* %23 */ + if (et > 13) + et = 13; + eb = e - (et * 23); + + ph = d; + pl = 0.0; + if (power < 0) { + if (eb) { + --eb; + stbsp__ddmulthi(ph, pl, d, stbsp__negbot[eb]); + stbsp__ddmultlos(ph, pl, d, stbsp__negboterr[eb]); + } + if (et) { + stbsp__ddrenorm(ph, pl); + --et; + stbsp__ddmulthi(p2h, p2l, ph, stbsp__negtop[et]); + stbsp__ddmultlo(p2h, p2l, ph, pl, stbsp__negtop[et], + stbsp__negtoperr[et]); + ph = p2h; + pl = p2l; + } + } else { + if (eb) { + e = eb; + if (eb > 22) + eb = 22; + e -= eb; + stbsp__ddmulthi(ph, pl, d, stbsp__bot[eb]); + if (e) { + stbsp__ddrenorm(ph, pl); + stbsp__ddmulthi(p2h, p2l, ph, stbsp__bot[e]); + stbsp__ddmultlos(p2h, p2l, stbsp__bot[e], pl); + ph = p2h; + pl = p2l; + } + } + if (et) { + stbsp__ddrenorm(ph, pl); + --et; + stbsp__ddmulthi(p2h, p2l, ph, stbsp__top[et]); + stbsp__ddmultlo(p2h, p2l, ph, pl, stbsp__top[et], stbsp__toperr[et]); + ph = p2h; + pl = p2l; + } + } + } + stbsp__ddrenorm(ph, pl); + *ohi = ph; + *olo = pl; +} + +// given a float value, returns the significant bits in bits, and the position +// of the +// decimal point in decimal_pos. +/-INF and NAN are specified by special +// values returned in the decimal_pos parameter. +// frac_digits is absolute normally, but if you want from first significant +// digits (got %g and %e), or in 0x80000000 +static stbsp__int32 stbsp__real_to_str(char const **start, stbsp__uint32 *len, + char *out, stbsp__int32 *decimal_pos, + double value, + stbsp__uint32 frac_digits) { + double d; + stbsp__int64 bits = 0; + stbsp__int32 expo, e, ng, tens; + + d = value; + STBSP__COPYFP(bits, d); + expo = (stbsp__int32)((bits >> 52) & 2047); + ng = (stbsp__int32)((stbsp__uint64)bits >> 63); + if (ng) + d = -d; + + if (expo == 2047) // is nan or inf? + { + *start = (bits & ((((stbsp__uint64)1) << 52) - 1)) ? "NaN" : "Inf"; + *decimal_pos = STBSP__SPECIAL; + *len = 3; + return ng; + } + + if (expo == 0) // is zero or denormal + { + if (((stbsp__uint64)bits << 1) == 0) // do zero + { + *decimal_pos = 1; + *start = out; + out[0] = '0'; + *len = 1; + return ng; + } + // find the right expo for denormals + { + stbsp__int64 v = ((stbsp__uint64)1) << 51; + while ((bits & v) == 0) { + --expo; + v >>= 1; + } + } + } + + // find the decimal exponent as well as the decimal bits of the value + { + double ph, pl; + + // log10 estimate - very specifically tweaked to hit or undershoot by no + // more than 1 of log10 of all expos 1..2046 + tens = expo - 1023; + tens = (tens < 0) ? ((tens * 617) / 2048) : (((tens * 1233) / 4096) + 1); + + // move the significant bits into position and stick them into an int + stbsp__raise_to_power10(&ph, &pl, d, 18 - tens); + + // get full as much precision from double-double as possible + stbsp__ddtoS64(bits, ph, pl); + + // check if we undershot + if (((stbsp__uint64)bits) >= stbsp__tento19th) + ++tens; + } + + // now do the rounding in integer land + frac_digits = (frac_digits & 0x80000000) ? ((frac_digits & 0x7ffffff) + 1) + : (tens + frac_digits); + if ((frac_digits < 24)) { + stbsp__uint32 dg = 1; + if ((stbsp__uint64)bits >= stbsp__powten[9]) + dg = 10; + while ((stbsp__uint64)bits >= stbsp__powten[dg]) { + ++dg; + if (dg == 20) + goto noround; + } + if (frac_digits < dg) { + stbsp__uint64 r; + // add 0.5 at the right position and round + e = dg - frac_digits; + if ((stbsp__uint32)e >= 24) + goto noround; + r = stbsp__powten[e]; + bits = bits + (r / 2); + if ((stbsp__uint64)bits >= stbsp__powten[dg]) + ++tens; + bits /= r; + } + noround:; + } + + // kill long trailing runs of zeros + if (bits) { + stbsp__uint32 n; + for (;;) { + if (bits <= 0xffffffff) + break; + if (bits % 1000) + goto donez; + bits /= 1000; + } + n = (stbsp__uint32)bits; + while ((n % 1000) == 0) + n /= 1000; + bits = n; + donez:; + } + + // convert to string + out += 64; + e = 0; + for (;;) { + stbsp__uint32 n; + char *o = out - 8; + // do the conversion in chunks of U32s (avoid most 64-bit divides, worth it, + // constant denomiators be damned) + if (bits >= 100000000) { + n = (stbsp__uint32)(bits % 100000000); + bits /= 100000000; + } else { + n = (stbsp__uint32)bits; + bits = 0; + } + while (n) { + out -= 2; + *(stbsp__uint16 *)out = + *(stbsp__uint16 *)&stbsp__digitpair.pair[(n % 100) * 2]; + n /= 100; + e += 2; + } + if (bits == 0) { + if ((e) && (out[0] == '0')) { + ++out; + --e; + } + break; + } + while (out != o) { + *--out = '0'; + ++e; + } + } + + *decimal_pos = tens; + *start = out; + *len = e; + return ng; +} + +#undef stbsp__ddmulthi +#undef stbsp__ddrenorm +#undef stbsp__ddmultlo +#undef stbsp__ddmultlos +#undef STBSP__SPECIAL +#undef STBSP__COPYFP + +#endif // STB_SPRINTF_NOFLOAT + +// clean up +#undef stbsp__uint16 +#undef stbsp__uint32 +#undef stbsp__int32 +#undef stbsp__uint64 +#undef stbsp__int64 +#undef STBSP__UNALIGNED + +#endif // STB_SPRINTF_IMPLEMENTATION + +/* +------------------------------------------------------------------------------ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2017 Sean Barrett +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. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain (www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +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 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/src/core/string.c b/src/core/string.c new file mode 100644 index 0000000..53b1fb9 --- /dev/null +++ b/src/core/string.c @@ -0,0 +1,59 @@ +#define STR_FMT(buff, str) \ + va_list args1; \ + va_start(args1, str); \ + int len = stbsp_vsnprintf(buff, sizeof(buff), str, args1); \ + va_end(args1) + +int str_len(char *str) { + int i = 0; + while (str[i]) i += 1; + return i; +} + +b32 s8_equal(s8_t a, s8_t b); +s8_t s8_from_char(char *string); +b32 str_eq(char *a, char *b) { + return s8_equal(s8_from_char(a), s8_from_char(b)); +} + +char char_to_lower_case(char a) { + if (a >= 'A' && a <= 'Z') a += 32; + return a; +} + +char char_to_upper_case(char a) { + if (a >= 'a' && a <= 'z') a -= 32; + return a; +} + +b32 char_is_whitespace(char w) { + b32 result = w == '\n' || w == ' ' || w == '\t' || w == '\v' || w == '\r'; + return result; +} + +b32 char_is_alphabetic(char a) { + b32 result = (a >= 'a' && a <= 'z') || (a >= 'A' && a <= 'Z'); + return result; +} + +b32 char_is_ident(char a) { + b32 result = (a >= 'a' && a <= 'z') || (a >= 'A' && a <= 'Z') || a == '_'; + return result; +} + +b32 char_is_digit(char a) { + b32 result = a >= '0' && a <= '9'; + return result; +} + +b32 char_is_alphanumeric(char a) { + b32 result = char_is_digit(a) || char_is_alphabetic(a); + return result; +} + +i64 wstr_len(wchar_t *string) { + i64 len = 0; + while (*string++ != 0) + len++; + return len; +} \ No newline at end of file diff --git a/src/core/string8.c b/src/core/string8.c new file mode 100644 index 0000000..11ef337 --- /dev/null +++ b/src/core/string8.c @@ -0,0 +1,454 @@ +#define s8_vsnprintf stbsp_vsnprintf + +#define S8_FMT(ma, str, result) \ + va_list args1; \ + va_start(args1, str); \ + s8_t result = s8_vfmt(ma, str, args1); \ + va_end(args1) + + +s8_t s8_range(char *begin, char *end) { + assert(end >= begin); + intptr_t size = (intptr_t)end - (intptr_t)begin; + s8_t result = {begin, size}; + return result; +} + +b32 s8_equal_ex(s8_t a, s8_t b, b32 ignore_case) { + if (a.len != b.len) return false; + for (int64_t i = 0; i < a.len; i++) { + char A = a.str[i]; + char B = b.str[i]; + if (ignore_case) { + A = char_to_lower_case(A); + B = char_to_lower_case(B); + } + if (A != B) + return false; + } + return true; +} +b32 s8_equal(s8_t a, s8_t b) {return s8_equal_ex(a, b, false);} + +s8_t s8_get_postfix(s8_t string, int64_t len) { + len = CLAMP_TOP(len, string.len); + int64_t remain_len = string.len - len; + s8_t result = s8(string.str + remain_len, len); + return result; +} + +s8_t s8_get_prefix(s8_t string, int64_t len) { + len = CLAMP_TOP(len, string.len); + s8_t result = s8(string.str, len); + return result; +} + +s8_t s8_chop(s8_t string, int64_t len) { + len = CLAMP_TOP(len, string.len); + s8_t result = s8(string.str, string.len - len); + return result; +} + +s8_t s8_skip(s8_t string, int64_t len) { + len = CLAMP_TOP(len, string.len); + int64_t remain = string.len - len; + s8_t result = s8(string.str + len, remain); + return result; +} + +b32 s8_ends_with(s8_t a, s8_t end, b32 ignore_case) { + s8_t a_end = s8_get_postfix(a, end.len); + b32 result = s8_equal_ex(end, a_end, ignore_case); + return result; +} + +b32 s8_starts_with(s8_t a, s8_t start, b32 ignore_case) { + s8_t a_start = s8_get_prefix(a, start.len); + b32 result = s8_equal_ex(start, a_start, ignore_case); + return result; +} + +void s8_normalize_path_unsafe(s8_t s) { + for (int64_t i = 0; i < s.len; i++) { + if (s.str[i] == '\\') + s.str[i] = '/'; + } +} + +b32 s8_is_pointer_inside(s8_t string, char *p) { + uintptr_t pointer = (uintptr_t)p; + uintptr_t start = (uintptr_t)string.str; + uintptr_t stop = start + (uintptr_t)string.len; + b32 result = pointer >= start && pointer < stop; + return result; +} + +s8_t s8_skip_to_p(s8_t string, char *p) { + if (s8_is_pointer_inside(string, p)) { + s8_t result = s8(p, p - string.str); + return result; + } + return string; +} + +s8_t s8_skip_past(s8_t string, s8_t a) { + if (s8_is_pointer_inside(string, a.str)) { + s8_t on_p = s8(a.str, a.str - string.str); + s8_t result = s8_skip(on_p, a.len); + return result; + } + return string; +} + +s8_t s8_slice(s8_t string, int64_t first_index, int64_t one_past_last_index) { + if (one_past_last_index < 0) one_past_last_index = string.len + one_past_last_index + 1; + if (first_index < 0) first_index = string.len + first_index; + assert(first_index < one_past_last_index && "s8_slice, first_index is bigger then one_past_last_index"); + assert(string.len > 0 && "Slicing string of length 0! Might be an error!"); + s8_t result = string; + if (string.len > 0) { + if (one_past_last_index > first_index) { + first_index = CLAMP_TOP(first_index, string.len - 1); + one_past_last_index = CLAMP_TOP(one_past_last_index, string.len); + result.str += first_index; + result.len = one_past_last_index - first_index; + } + else { + result.len = 0; + } + } + return result; +} + +s8_t s8_trim(s8_t string) { + if (string.len == 0) + return string; + + int64_t whitespace_begin = 0; + for (; whitespace_begin < string.len; whitespace_begin++) { + if (!char_is_whitespace(string.str[whitespace_begin])) { + break; + } + } + + int64_t whitespace_end = string.len; + for (; whitespace_end != whitespace_begin; whitespace_end--) { + if (!char_is_whitespace(string.str[whitespace_end - 1])) { + break; + } + } + + if (whitespace_begin == whitespace_end) { + string.len = 0; + } + else { + string = s8_slice(string, whitespace_begin, whitespace_end); + } + + return string; +} + +s8_t s8_trim_end(s8_t string) { + int64_t whitespace_end = string.len; + for (; whitespace_end != 0; whitespace_end--) { + if (!char_is_whitespace(string.str[whitespace_end - 1])) { + break; + } + } + + s8_t result = s8_get_prefix(string, whitespace_end); + return result; +} + +typedef int s8_seek_t; +enum { + s8_seek_none = 0, + s8_seek_ignore_case = 1, + s8_seek_match_find_last = 2, +}; + +b32 s8_seek(s8_t string, s8_t find, s8_seek_t flags, int64_t *index_out) { + b32 ignore_case = flags & s8_seek_ignore_case ? true : false; + b32 result = false; + if (flags & s8_seek_match_find_last) { + for (int64_t i = string.len; i != 0; i--) { + int64_t index = i - 1; + s8_t substring = s8_slice(string, index, index + find.len); + if (s8_equal_ex(substring, find, ignore_case)) { + if (index_out) + *index_out = index; + result = true; + break; + } + } + } + else { + for (int64_t i = 0; i < string.len; i++) { + s8_t substring = s8_slice(string, i, i + find.len); + if (s8_equal_ex(substring, find, ignore_case)) { + if (index_out) + *index_out = i; + result = true; + break; + } + } + } + + return result; +} + +int64_t s8_find(s8_t string, s8_t find, s8_seek_t flag) { + int64_t result = -1; + s8_seek(string, find, flag, &result); + return result; +} + +s8_t s8_chop_last_slash(s8_t s) { + s8_t result = s; + s8_seek(s, s8_lit("/"), s8_seek_match_find_last, &result.len); + return result; +} + +s8_t s8_chop_last_period(s8_t s) { + s8_t result = s; + s8_seek(s, s8_lit("."), s8_seek_match_find_last, &result.len); + return result; +} + +s8_t s8_skip_to_last_slash(s8_t s) { + int64_t pos; + s8_t result = s; + if (s8_seek(s, s8_lit("/"), s8_seek_match_find_last, &pos)) { + result = s8_skip(result, pos + 1); + } + return result; +} + +s8_t s8_skip_to_last_period(s8_t s) { + int64_t pos; + s8_t result = s; + if (s8_seek(s, s8_lit("."), s8_seek_match_find_last, &pos)) { + result = s8_skip(result, pos + 1); + } + return result; +} + +s8_t s8_from_char(char *string) { + s8_t result; + result.str = (char *)string; + result.len = str_len(string); + return result; +} + +s8_t s8_get_name_no_ext(s8_t s) { + return s8_skip_to_last_slash(s8_chop_last_period(s)); +} + +s8_t s8_copy(ma_arena_t *ma, s8_t string) { + char *copy = (char *)ma_push_size(ma, sizeof(char) * (string.len + 1)); + if (copy) { + memory_copy(copy, string.str, string.len); + copy[string.len] = 0; + s8_t result = s8(copy, string.len); + return result; + } + return (s8_t){0}; +} + +s8_t s8_copy_char(ma_arena_t *ma, char *s) { + int64_t len = str_len(s); + char *copy = (char *)ma_push_size(ma, sizeof(char) * (len + 1)); + memory_copy(copy, s, len); + copy[len] = 0; + s8_t result = s8(copy, len); + return result; +} + +s8_t s8_normalize_path(ma_arena_t *ma, s8_t s) { + s8_t copy = s8_copy(ma, s); + for (int64_t i = 0; i < copy.len; i++) { + if (copy.str[i] == '\\') + copy.str[i] = '/'; + } + return copy; +} + +s8_t s8_to_lower_case(ma_arena_t *ma, s8_t s) { + s8_t copy = s8_copy(ma, s); + for (int64_t i = 0; i < copy.len; i++) { + copy.str[i] = char_to_lower_case(copy.str[i]); + } + return copy; +} + +s8_t s8_to_upper_case(ma_arena_t *ma, s8_t s) { + s8_t copy = s8_copy(ma, s); + for (int64_t i = 0; i < copy.len; i++) { + copy.str[i] = char_to_upper_case(copy.str[i]); + } + return copy; +} + +s8_t s8_vfmt(ma_arena_t *ma, const char *str, va_list args1) { + va_list args2; + va_copy(args2, args1); + int64_t len = s8_vsnprintf(0, 0, str, args2); + va_end(args2); + + char *result = (char *)ma_push_size(ma, sizeof(char) * (len + 1)); + s8_vsnprintf(result, (int)(len + 1), str, args1); + s8_t res = s8(result, len); + return res; +} + +s8_t s8_fmt(ma_arena_t *ma, const char *str, ...) { + S8_FMT(ma, str, result); + return result; +} + +// +// sb8_t +// +sb8_node_t *sb8_node(ma_arena_t *ma, s8_t str) { + sb8_node_t *node = ma_push_type(ma, sb8_node_t); + node->s = str; + return node; +} + +sb8_node_t *sb8_append(sb8_t *list, s8_t string) { + sb8_node_t *node = sb8_node(list->arena, string); + SLLQ_APPEND(list->first, list->last, node); + return node; +} + +s8_t sb8_printf(sb8_t *sb, const char *str, ...) { + S8_FMT(sb->arena, str, result); + sb8_append(sb, result); + return result; +} + +void sb8_indent(sb8_t *sb) { + sb8_printf(sb, "\n%.*s", sb->indent*4, " "); +} + +s8_t sb8_stmtf(sb8_t *sb, const char *str, ...) { + S8_FMT(sb->arena, str, result); + sb8_indent(sb); + sb8_append(sb, result); + return result; +} + +int64_t sb8_char_size(sb8_t *sb) { + int64_t result = 0; + for (sb8_node_t *it = sb->first; it; it = it->next) { + result += it->len; + } + return result; +} + +s8_t sb8_merge(sb8_t *sb) { + int64_t size = sb8_char_size(sb) + 1; + char *str = ma_push_size(sb->arena, size); + s8_t result = {str, 0}; + for (sb8_node_t *it = sb->first; it; it = it->next) { + memory_copy(result.str + result.len, it->str, it->len); + result.len += it->len; + } + result.str[result.len] = 0; + return result; +} + +typedef int s8_split_t; +enum { + s8_split_none = 0, + s8_split_ignore_case = 1, + s8_split_inclusive = 2, +}; + +sb8_t s8_split(ma_arena_t *ma, s8_t string, s8_t find, s8_split_t flags) { + sb8_t result = (sb8_t){ma}; + int64_t index = 0; + + s8_seek_t find_flag = flags & s8_split_ignore_case ? s8_seek_ignore_case : s8_seek_none; + while (s8_seek(string, find, find_flag, &index)) { + s8_t before_match = s8(string.str, index); + sb8_append(&result, before_match); + if (flags & s8_split_inclusive) { + s8_t match = s8(string.str + index, find.len); + sb8_append(&result, match); + } + string = s8_skip(string, index + find.len); + } + if (string.len) sb8_append(&result, string); + return result; +} + +s16_t s16_from_s8(ma_arena_t *ma, s8_t string) { + u16 *buffer = ma_push_array(ma, u16, string.len + 1); + i64 len = wstr_from_str(buffer, string.len + 1, string.str, string.len); + assert(len < string.len); + return (s16_t){buffer,len}; +} + +s8_t s8_from_s16(ma_arena_t *ma, s16_t string) { + i64 buffer_size = (string.len + 1) * 2; + char *buffer = ma_push_array(ma, char, buffer_size); + i64 len = str_from_wstr(buffer, buffer_size, string.str, string.len); + assert(len < buffer_size); + return (s8_t){buffer,len}; +} +#if 0 + +s8_t S8_MergeWithSeparator(ma_arena_t *ma, S8_List list, s8_t separator) { + if (list.node_count == 0) return s8_makeEmpty(); + if (list.char_count == 0) return s8_makeEmpty(); + + int64_t base_size = (list.char_count + 1); + int64_t sep_size = (list.node_count - 1) * separator.len; + int64_t size = base_size + sep_size; + char *buff = (char *)ma_push_size(ma, sizeof(char) * (size + 1)); + s8_t string = s8(buff, 0); + for (S8_Node *it = list.first; it; it = it->next) { + assert(string.len + it->string.len <= size); + memory_copy(string.str + string.len, it->string.str, it->string.len); + string.len += it->string.len; + if (it != list.last) { + memory_copy(string.str + string.len, separator.str, separator.len); + string.len += separator.len; + } + } + assert(string.len == size - 1); + string.str[size] = 0; + return string; +} + +s8_t S8_Merge(ma_arena_t *ma, S8_List list) { + return S8_MergeWithSeparator(ma, list, s8_lit("")); +} + +s8_t S8_ReplaceAll(ma_arena_t *ma, s8_t string, s8_t replace, s8_t with, b32 ignore_case) { + s8_split_flag split_flag = ignore_case ? S8_SplitFlag_IgnoreCase : S8_SplitFlag_None; + S8_List list = S8_Split(ma, string, replace, split_flag | S8_SplitFlag_SplitInclusive); + for (S8_Node *it = list.first; it; it = it->next) { + if (s8_equal_ex(it->string, replace, ignore_case)) { + S8_ReplaceNodeString(&list, it, with); + } + } + s8_t result = S8_Merge(ma, list); + return result; +} + +S8_List S8_FindAll(ma_arena_t *ma, s8_t string, s8_t find, b32 ignore_case) { // @untested + S8_List result = s8_makeEmptyList(); + int64_t index = 0; + + s8_seek find_flag = ignore_case ? s8_seek_ignore_case : 0; + while (s8_seek(string, find, find_flag, &index)) { + s8_t match = s8(string.str + index, find.len); + S8_AddNode(ma, &result, match); + string = s8_skip(string, index + find.len); + } + return result; +} + +#endif \ No newline at end of file diff --git a/src/core/type_info.c b/src/core/type_info.c new file mode 100644 index 0000000..7c4c56a --- /dev/null +++ b/src/core/type_info.c @@ -0,0 +1,403 @@ +s8_t ti_enum_value_to_name(type_t *type, i64 value) { + assert(type->kind == type_kind_enum); + for (i32 i = 0; i < type->count; i += 1) { + type_member_t *it = type->members + i; + if (value == it->value) { + return it->name; + } + } + return s8_lit("invalid"); +} + +i64 ti_enum_name_to_value(type_t *type, s8_t name) { + assert(type->kind == type_kind_enum); + for (i32 i = 0; i < type->count; i += 1) { + type_member_t *it = type->members + i; + if (s8_equal(it->name, name)) { + return it->value; + } + } + return -1; +} + +type_member_t *ti_get_member(type_t *type, s8_t name) { + for (i32 i = 0; i < type->count; i += 1) { + type_member_t *it = type->members + i; + if (s8_equal(it->name, name)) { + return it; + } + } + return NULL; +} + +void sb8_serial_data(sb8_t *sb, void *p, type_t *type) { + assert(type->kind != type_kind_invalid); + + switch(type->kind) { + case type_kind_i8: { + i8 n = *(i8 *)p; + sb8_printf(sb, "%d", n); + return; + } break; + case type_kind_i16: { + i16 n = *(i16 *)p; + sb8_printf(sb, "%d", n); + return; + } break; + case type_kind_i32: { + i32 n = *(i32 *)p; + sb8_printf(sb, "%d", n); + return; + } break; + case type_kind_i64: { + i64 n = *(i64 *)p; + sb8_printf(sb, "%lld", (long long)n); + return; + } break; + case type_kind_u8: { + u8 n = *(u8 *)p; + sb8_printf(sb, "%u", n); + return; + } break; + case type_kind_u16: { + u16 n = *(u16 *)p; + sb8_printf(sb, "%u", n); + return; + } break; + case type_kind_u32: { + u32 n = *(u32 *)p; + sb8_printf(sb, "%u", n); + return; + } break; + case type_kind_u64: { + u64 n = *(u64 *)p; + sb8_printf(sb, "%llu", (unsigned long long)n); + return; + } break; + case type_kind_b8: { + b8 n = *(b8 *)p; + sb8_printf(sb, "%d", n); + return; + } break; + case type_kind_b16: { + b16 n = *(b16 *)p; + sb8_printf(sb, "%d", n); + return; + } break; + case type_kind_b32: { + b32 n = *(b32 *)p; + sb8_printf(sb, "%d", n); + return; + } break; + case type_kind_b64: { + b64 n = *(b64 *)p; + sb8_printf(sb, "%lld", (long long)n); + return; + } break; + case type_kind_f32: { + f32 n = *(f32 *)p; + sb8_printf(sb, "%f", n); + return; + } break; + case type_kind_f64: { + f64 n = *(f64 *)p; + sb8_printf(sb, "%f", n); + return; + } break; + case type_kind_isize: { + isize n = *(isize *)p; + sb8_printf(sb, "%lld", (long long)n); + return; + } break; + case type_kind_usize: { + usize n = *(usize *)p; + sb8_printf(sb, "%llu", (unsigned long long)n); + return; + } break; + case type_kind_int: { + int n = *(int *)p; + sb8_printf(sb, "%d", n); + return; + } break; + case type_kind_char: { + char n = *(char *)p; + sb8_printf(sb, "%c", n); + return; + } break; + } + + if (type == &type__s8_t) { + s8_t n = *(s8_t *)p; + sb8_printf(sb, "\"%S\"", n); + return; + } + + + if (type->kind == type_kind_pointer) { + sb8_printf(sb, "0x%x", p); + return; + } + + if (type->kind == type_kind_enum) { + i64 value = 0; + if (type->size == 1) { + value = *(i8 *)p; + } else if (type->size == 2) { + value = *(i16 *)p; + } else if (type->size == 4) { + value = *(i32 *)p; + } else if (type->size == 8) { + value = *(i64 *)p; + } else { + panicf("invalid size of enum: %d", type->size); + } + s8_t s = ti_enum_value_to_name(type, value); + sb8_append(sb, s); + return; + } + + if (type->kind == type_kind_array) { + u8 *p8 = (u8 *)p; + sb8_printf(sb, "{"); + sb->indent += 1; + for (i32 i = 0; i < type->count; i += 1) { + type_t *base = type->base; + u8 *mem_p = p8 + base->size * i; + + sb8_indent(sb); + sb8_serial_data(sb, mem_p, base); + sb8_printf(sb, ","); + } + sb->indent -= 1; + sb8_stmtf(sb, "}"); + return; + } + + if (type->kind == type_kind_struct) { + u8 *p8 = (u8 *)p; + + sb8_printf(sb, "{"); + sb->indent += 1; + for (i32 i = 0; i < type->count; i += 1) { + type_member_t *mem = type->members + i; + u8 *mem_p = p8 + mem->offset; + sb8_indent(sb); + sb8_printf(sb, "%S: ", mem->name); + sb8_serial_data(sb, mem_p, mem->type); + sb8_printf(sb, ","); + } + sb->indent -= 1; + sb8_stmtf(sb, "}"); + return; + } + + panicf("can't serialize: unhandled type"); +} + +s8_t s8_serial_data(ma_arena_t *arena, void *p, type_t *type) { + sb8_t *sb = sb8_serial_begin(arena); + sb8_serial_data(sb, p, type); + s8_t string = sb8_serial_end(sb); + return string; +} + +i64 parser__match_i64(parser_t *par) { + i64 minus = 1; + if (parser_match(par, lex_kind_minus)) { + minus = -1; + } + lex_t *token = parser_expect(par, lex_kind_int); + i64 result = (i64)token->integer * minus; + return result; +} + +void s8_deserial_data_ex(ma_arena_t *arena, parser_t *par, void *p, type_t *type) { + switch(type->kind) { + case type_kind_i8: { + i8 *n = (i8 *)p; + n[0] = (i8)parser__match_i64(par); + return; + } break; + case type_kind_i16: { + i16 *n = (i16 *)p; + n[0] = (i16)parser__match_i64(par); + return; + } break; + case type_kind_i32: { + i32 *n = (i32 *)p; + n[0] = (i32)parser__match_i64(par); + return; + } break; + case type_kind_i64: { + i64 *n = (i64 *)p; + n[0] = (i64)parser__match_i64(par); + return; + } break; + case type_kind_b8: { + b8 *n = (b8 *)p; + n[0] = (b8)parser__match_i64(par); + return; + } break; + case type_kind_b16: { + b16 *n = (b16 *)p; + n[0] = (b16)parser__match_i64(par); + return; + } break; + case type_kind_b32: { + b32 *n = (b32 *)p; + n[0] = (b32)parser__match_i64(par); + return; + } break; + case type_kind_b64: { + b64 *n = (b64 *)p; + n[0] = (b64)parser__match_i64(par); + return; + } break; + case type_kind_u8: { + lex_t *token = parser_expect(par, lex_kind_int); + u8 *n = (u8 *)p; + n[0] = (u8)token->integer; + return; + } break; + case type_kind_u16: { + lex_t *token = parser_expect(par, lex_kind_int); + u16 *n = (u16 *)p; + n[0] = (u16)token->integer; + return; + } break; + case type_kind_u32: { + lex_t *token = parser_expect(par, lex_kind_int); + u32 *n = (u32 *)p; + n[0] = (u32)token->integer; + return; + } break; + case type_kind_u64: { + lex_t *token = parser_expect(par, lex_kind_int); + u64 *n = (u64 *)p; + n[0] = (u64)token->integer; + return; + } break; + case type_kind_f32: { + lex_t *token = parser_expect(par, lex_kind_real); + f32 *n = (f32 *)p; + n[0] = (f32)token->real; + return; + } break; + case type_kind_f64: { + lex_t *token = parser_expect(par, lex_kind_real); + f64 *n = (f64 *)p; + n[0] = token->real; + return; + } break; + case type_kind_isize: { + isize *n = (isize *)p; + n[0] = (isize)parser__match_i64(par); + return; + } break; + case type_kind_usize: { + lex_t *token = parser_expect(par, lex_kind_int); + usize *n = (usize *)p; + n[0] = (usize)token->integer; + return; + } break; + case type_kind_int: { + int *n = (int *)p; + n[0] = (int)parser__match_i64(par); + return; + } break; + case type_kind_char: { + char *n = (char *)p; + n[0] = (char)parser__match_i64(par); + return; + } break; + } + + if (type == &type__s8_t) { + lex_t *token = parser_expect(par, lex_kind_string); + s8_t *n = (s8_t *)p; + n[0] = s8_copy(arena, token->s8); + return; + } + + if (type->kind == type_kind_enum) { + lex_t *token = parser_expect(par, lex_kind_ident); + i64 value = ti_enum_name_to_value(type, token->s8); + if (value == -1) { + panicf("invalid enum value: %S", token->s8); + } + if (type->size == 1) { + *(i8 *)p = (i8)value; + } else if (type->size == 2) { + *(i16 *)p = (i16)value; + } else if (type->size == 4) { + *(i32 *)p = (i32)value; + } else if (type->size == 8) { + *(i64 *)p = (i64)value; + } else { + panicf("invalid size of enum: %d", type->size); + } + return; + } + + if (type->kind == type_kind_struct) { + u8 *p8 = (u8 *)p; + +#if 0 + parser_expect(par, lex_kind_open_brace); + while (!parser_match(par, lex_kind_close_brace)) { + lex_t *ident = parser_expect(par, lex_kind_ident); + parser_expect(par, lex_kind_colon); + + type_member_t *mem = ti_get_member(type, ident->s8); + if (mem) { + u8 *mem_p = p8 + mem->offset; + s8_deserial_data_ex(arena, par, mem_p, mem->type); + } else { + debugf("deserial - skipping field: %S", ident->s8); + parser_eat_until(par, lex_kind_comma); + } + + parser_expect(par, lex_kind_comma); + } + +#else // strict + parser_expect(par, lex_kind_open_brace); + for (i32 i = 0; i < type->count; i += 1) { + type_member_t *mem = type->members + i; + u8 *mem_p = p8 + mem->offset; + + lex_t *ident = parser_expect(par, lex_kind_ident); + parser_expect(par, lex_kind_colon); + assert(s8_equal(ident->s8, mem->name)); + + s8_deserial_data_ex(arena, par, mem_p, mem->type); + parser_expect(par, lex_kind_comma); + } + parser_expect(par, lex_kind_close_brace); +#endif + } + + if (type->kind == type_kind_array) { + u8 *p8 = (u8 *)p; + parser_expect(par, lex_kind_open_brace); + for (i32 i = 0; i < type->count; i += 1) { + type_t *base = type->base; + u8 *mem_p = p8 + base->size * i; + + s8_deserial_data_ex(arena, par, mem_p, base); + parser_expect(par, lex_kind_comma); + } + parser_expect(par, lex_kind_close_brace); + } +} + +#define s8_deserial_data(ARENA, DATA, TYPE) (TYPE *)s8__deserial_data(ARENA, DATA, &type__##TYPE) +void *s8__deserial_data(ma_arena_t *arena, s8_t data, type_t *type) { + void *p = ma_push_size(arena, type->size); + ma_temp_t scratch = ma_begin_scratch(); + lex_array_t tokens = lex_tokens(scratch.arena, "data serializing", data.str); + parser_t *par = parser_make(scratch.arena, tokens.data); + s8_deserial_data_ex(arena, par, p, type); + ma_end_scratch(scratch); + return p; +} \ No newline at end of file diff --git a/src/core/type_info.h b/src/core/type_info.h new file mode 100644 index 0000000..0ad68a1 --- /dev/null +++ b/src/core/type_info.h @@ -0,0 +1,314 @@ +typedef int type_kind_t; +enum { + type_kind_invalid, + + type_kind_i8, + type_kind_i16, + type_kind_i32, + type_kind_i64, + type_kind_b8, + type_kind_b16, + type_kind_b32, + type_kind_b64, + type_kind_u8, + type_kind_u16, + type_kind_u32, + type_kind_u64, + type_kind_f32, + type_kind_f64, + type_kind_isize, + type_kind_usize, + type_kind_int, + type_kind_char, + + type_kind_count, + + type_kind_pointer, + type_kind_array, + type_kind_struct, + type_kind_union, + type_kind_enum, +}; + +typedef struct type_member_t type_member_t; +typedef struct type_t type_t; +struct type_t { + type_kind_t kind; + s8_t name; + i32 size; + i32 count; + type_member_t *members; + type_t *base; +}; + +struct type_member_t { + s8_t name; + type_t *type; + i64 value; + u64 offset; +}; + +#define type(X) &type__##X +#define DEFINE_ENUM(x) type_t type__##x = {type_kind_enum, s8_const_lit(#x), sizeof(x), .members = members__##x, .count = lengthof(members__##x)} +#define DEFINE_STRUCT(x) type_t type__##x = {type_kind_struct, s8_const_lit(#x), sizeof(x), .members = members__##x, .count = lengthof(members__##x)} +#define POINTER(x) (type_t){type_kind_pointer, s8_const_lit(#x "*"), sizeof(void *), .base = &type__##x} + +s8_t ti_enum_value_to_name(type_t *type, i64 value); +i64 ti_enum_name_to_value(type_t *type, s8_t name); +type_member_t *ti_get_member(type_t *type, s8_t name); + +type_t type__i8 = {type_kind_i8, s8_const_lit("i8"), sizeof(i8)}; +type_t type__i16 = {type_kind_i16, s8_const_lit("i16"), sizeof(i16)}; +type_t type__i32 = {type_kind_i32, s8_const_lit("i32"), sizeof(i32)}; +type_t type__i64 = {type_kind_i64, s8_const_lit("i64"), sizeof(i64)}; +type_t type__u8 = {type_kind_u8, s8_const_lit("u8"), sizeof(u8)}; +type_t type__u16 = {type_kind_u16, s8_const_lit("u16"), sizeof(u16)}; +type_t type__u32 = {type_kind_u32, s8_const_lit("u32"), sizeof(u32)}; +type_t type__u64 = {type_kind_u64, s8_const_lit("u64"), sizeof(u64)}; +type_t type__b8 = {type_kind_b8, s8_const_lit("b8"), sizeof(b8)}; +type_t type__b16 = {type_kind_b16, s8_const_lit("b16"), sizeof(b16)}; +type_t type__b32 = {type_kind_b32, s8_const_lit("b32"), sizeof(b32)}; +type_t type__b64 = {type_kind_b64, s8_const_lit("b64"), sizeof(b64)}; +type_t type__f32 = {type_kind_f32, s8_const_lit("f32"), sizeof(f32)}; +type_t type__f64 = {type_kind_f64, s8_const_lit("f64"), sizeof(f64)}; +type_t type__isize = {type_kind_isize, s8_const_lit("isize"), sizeof(isize)}; +type_t type__usize = {type_kind_usize, s8_const_lit("usize"), sizeof(usize)}; +type_t type__int = {type_kind_int, s8_const_lit("int"), sizeof(int)}; +type_t type__char = {type_kind_char, s8_const_lit("char"), sizeof(char)}; + +type_t type__s8_t = { type_kind_struct, s8_const_lit("s8_t"), sizeof(s8_t), .count = 2, + .members = (type_member_t[]){ + {s8_const_lit("str"), &POINTER(char), .offset = offsetof(s8_t, str)}, + {s8_const_lit("len"), &type__i64, .offset = offsetof(s8_t, len)}, + } +}; + +type_t type__s16_t = { type_kind_struct, s8_const_lit("s16_t"), sizeof(s16_t), .count = 2, + .members = (type_member_t[]){ + {s8_const_lit("str"), &POINTER(u16), .offset = offsetof(s16_t, str)}, + {s8_const_lit("len"), &type__i64, .offset = offsetof(s16_t, len)}, + } +}; + +type_t type__s32_t = { type_kind_struct, s8_const_lit("s32_t"), sizeof(s32_t), .count = 2, + .members = (type_member_t[]){ + {s8_const_lit("str"), &POINTER(u32), .offset = offsetof(s32_t, str)}, + {s8_const_lit("len"), &type__i64, .offset = offsetof(s32_t, len)}, + } +}; + +type_t type__ma_arena_t = { type_kind_struct, s8_const_lit("ma_arena_t"), sizeof(ma_arena_t), .count = 6, + .members = (type_member_t[]){ + {s8_const_lit("data"), &POINTER(u8), .offset = offsetof(ma_arena_t, data)}, + {s8_const_lit("len"), &type__usize, .offset = offsetof(ma_arena_t, len)}, + {s8_const_lit("base_len"), &type__usize, .offset = offsetof(ma_arena_t, base_len)}, + {s8_const_lit("reserve"), &type__usize, .offset = offsetof(ma_arena_t, reserve)}, + {s8_const_lit("commit"), &type__usize, .offset = offsetof(ma_arena_t, commit)}, + {s8_const_lit("align"), &type__usize, .offset = offsetof(ma_arena_t, align)}, + } +}; + +type_t type__ma_temp_t = { type_kind_struct, s8_const_lit("ma_temp_t"), sizeof(ma_temp_t), .count = 2, + .members = (type_member_t[]){ + {s8_const_lit("arena"), &POINTER(ma_arena_t), .offset = offsetof(ma_temp_t, arena)}, + {s8_const_lit("len"), &type__usize, .offset = offsetof(ma_temp_t, len)}, + } +}; + + +type_t type__v2f32_t = { type_kind_struct, s8_const_lit("v2f32_t"), sizeof(v2f32_t), .count = 2, + .members = (type_member_t[]){ + {s8_const_lit("x"), &type__f32, .offset = offsetof(v2f32_t, x)}, + {s8_const_lit("y"), &type__f32, .offset = offsetof(v2f32_t, y)}, + } +}; + +type_t type__v3f32_t = { type_kind_struct, s8_const_lit("v3f32_t"), sizeof(v3f32_t), .count = 3, + .members = (type_member_t[]){ + {s8_const_lit("x"), &type__f32, .offset = offsetof(v3f32_t, x)}, + {s8_const_lit("y"), &type__f32, .offset = offsetof(v3f32_t, y)}, + {s8_const_lit("z"), &type__f32, .offset = offsetof(v3f32_t, z)}, + } +}; + +type_t type__v4f32_t = { type_kind_struct, s8_const_lit("v4f32_t"), sizeof(v4f32_t), .count = 4, + .members = (type_member_t[]){ + {s8_const_lit("x"), &type__f32, .offset = offsetof(v4f32_t, x)}, + {s8_const_lit("y"), &type__f32, .offset = offsetof(v4f32_t, y)}, + {s8_const_lit("z"), &type__f32, .offset = offsetof(v4f32_t, z)}, + {s8_const_lit("w"), &type__f32, .offset = offsetof(v4f32_t, w)}, + } +}; + +type_t type__r3f32_t = { type_kind_struct, s8_const_lit("r3f32_t"), sizeof(r3f32_t), .count = 6, + .members = (type_member_t[]){ + {s8_const_lit("x0"), &type__f32, .offset = offsetof(r3f32_t, x0)}, + {s8_const_lit("y0"), &type__f32, .offset = offsetof(r3f32_t, y0)}, + {s8_const_lit("x0"), &type__f32, .offset = offsetof(r3f32_t, z0)}, + {s8_const_lit("x1"), &type__f32, .offset = offsetof(r3f32_t, x1)}, + {s8_const_lit("y1"), &type__f32, .offset = offsetof(r3f32_t, y1)}, + {s8_const_lit("x1"), &type__f32, .offset = offsetof(r3f32_t, z1)}, + } +}; + +type_member_t members__r2f32_t[] = { + {s8_const_lit("x0"), &type__f32, .offset = offsetof(r2f32_t, x0)}, + {s8_const_lit("y0"), &type__f32, .offset = offsetof(r2f32_t, y0)}, + {s8_const_lit("x1"), &type__f32, .offset = offsetof(r2f32_t, x1)}, + {s8_const_lit("y1"), &type__f32, .offset = offsetof(r2f32_t, y1)}, +}; +DEFINE_STRUCT(r2f32_t); + +type_member_t members__r1f32_t[] = { + {s8_const_lit("x0"), &type__f32, .offset = offsetof(r1f32_t, x0)}, + {s8_const_lit("x1"), &type__f32, .offset = offsetof(r1f32_t, x1)}, +}; +DEFINE_STRUCT(r1f32_t); + +type_t type__v2f64_t = { type_kind_struct, s8_const_lit("v2f64_t"), sizeof(v2f64_t), .count = 2, + .members = (type_member_t[]){ + {s8_const_lit("x"), &type__f64, .offset = offsetof(v2f64_t, x)}, + {s8_const_lit("y"), &type__f64, .offset = offsetof(v2f64_t, y)}, + } +}; + +type_t type__v3f64_t = { type_kind_struct, s8_const_lit("v3f64_t"), sizeof(v3f64_t), .count = 3, + .members = (type_member_t[]){ + {s8_const_lit("x"), &type__f64, .offset = offsetof(v3f64_t, x)}, + {s8_const_lit("y"), &type__f64, .offset = offsetof(v3f64_t, y)}, + {s8_const_lit("z"), &type__f64, .offset = offsetof(v3f64_t, z)}, + } +}; + +type_t type__v4f64_t = { type_kind_struct, s8_const_lit("v4f64_t"), sizeof(v4f64_t), .count = 4, + .members = (type_member_t[]){ + {s8_const_lit("x"), &type__f64, .offset = offsetof(v4f64_t, x)}, + {s8_const_lit("y"), &type__f64, .offset = offsetof(v4f64_t, y)}, + {s8_const_lit("z"), &type__f64, .offset = offsetof(v4f64_t, z)}, + {s8_const_lit("w"), &type__f64, .offset = offsetof(v4f64_t, w)}, + } +}; + +type_t type__r3f64_t = { type_kind_struct, s8_const_lit("r3f64_t"), sizeof(r3f64_t), .count = 6, + .members = (type_member_t[]){ + {s8_const_lit("x0"), &type__f64, .offset = offsetof(r3f64_t, x0)}, + {s8_const_lit("y0"), &type__f64, .offset = offsetof(r3f64_t, y0)}, + {s8_const_lit("x0"), &type__f64, .offset = offsetof(r3f64_t, z0)}, + {s8_const_lit("x1"), &type__f64, .offset = offsetof(r3f64_t, x1)}, + {s8_const_lit("y1"), &type__f64, .offset = offsetof(r3f64_t, y1)}, + {s8_const_lit("x1"), &type__f64, .offset = offsetof(r3f64_t, z1)}, + } +}; + +type_member_t members__r2f64_t[] = { + {s8_const_lit("x0"), &type__f64, .offset = offsetof(r2f64_t, x0)}, + {s8_const_lit("y0"), &type__f64, .offset = offsetof(r2f64_t, y0)}, + {s8_const_lit("x1"), &type__f64, .offset = offsetof(r2f64_t, x1)}, + {s8_const_lit("y1"), &type__f64, .offset = offsetof(r2f64_t, y1)}, +}; +DEFINE_STRUCT(r2f64_t); + +type_member_t members__r1f64_t[] = { + {s8_const_lit("x0"), &type__f64, .offset = offsetof(r1f64_t, x0)}, + {s8_const_lit("x1"), &type__f64, .offset = offsetof(r1f64_t, x1)}, +}; +DEFINE_STRUCT(r1f64_t); + +type_t type__v2i32_t = { type_kind_struct, s8_const_lit("v2i32_t"), sizeof(v2i32_t), .count = 2, + .members = (type_member_t[]){ + {s8_const_lit("x"), &type__i32, .offset = offsetof(v2i32_t, x)}, + {s8_const_lit("y"), &type__i32, .offset = offsetof(v2i32_t, y)}, + } +}; + +type_t type__v3i32_t = { type_kind_struct, s8_const_lit("v3i32_t"), sizeof(v3i32_t), .count = 3, + .members = (type_member_t[]){ + {s8_const_lit("x"), &type__i32, .offset = offsetof(v3i32_t, x)}, + {s8_const_lit("y"), &type__i32, .offset = offsetof(v3i32_t, y)}, + {s8_const_lit("z"), &type__i32, .offset = offsetof(v3i32_t, z)}, + } +}; + +type_t type__v4i32_t = { type_kind_struct, s8_const_lit("v4i32_t"), sizeof(v4i32_t), .count = 4, + .members = (type_member_t[]){ + {s8_const_lit("x"), &type__i32, .offset = offsetof(v4i32_t, x)}, + {s8_const_lit("y"), &type__i32, .offset = offsetof(v4i32_t, y)}, + {s8_const_lit("z"), &type__i32, .offset = offsetof(v4i32_t, z)}, + {s8_const_lit("w"), &type__i32, .offset = offsetof(v4i32_t, w)}, + } +}; + +type_t type__r3i32_t = { type_kind_struct, s8_const_lit("r3i32_t"), sizeof(r3i32_t), .count = 6, + .members = (type_member_t[]){ + {s8_const_lit("x0"), &type__i32, .offset = offsetof(r3i32_t, x0)}, + {s8_const_lit("y0"), &type__i32, .offset = offsetof(r3i32_t, y0)}, + {s8_const_lit("x0"), &type__i32, .offset = offsetof(r3i32_t, z0)}, + {s8_const_lit("x1"), &type__i32, .offset = offsetof(r3i32_t, x1)}, + {s8_const_lit("y1"), &type__i32, .offset = offsetof(r3i32_t, y1)}, + {s8_const_lit("x1"), &type__i32, .offset = offsetof(r3i32_t, z1)}, + } +}; + +type_member_t members__r2i32_t[] = { + {s8_const_lit("x0"), &type__i32, .offset = offsetof(r2i32_t, x0)}, + {s8_const_lit("y0"), &type__i32, .offset = offsetof(r2i32_t, y0)}, + {s8_const_lit("x1"), &type__i32, .offset = offsetof(r2i32_t, x1)}, + {s8_const_lit("y1"), &type__i32, .offset = offsetof(r2i32_t, y1)}, +}; +DEFINE_STRUCT(r2i32_t); + +type_member_t members__r1i32_t[] = { + {s8_const_lit("x0"), &type__i32, .offset = offsetof(r1i32_t, x0)}, + {s8_const_lit("x1"), &type__i32, .offset = offsetof(r1i32_t, x1)}, +}; +DEFINE_STRUCT(r1i32_t); + + +type_t type__v2i64_t = { type_kind_struct, s8_const_lit("v2i64_t"), sizeof(v2i64_t), .count = 2, + .members = (type_member_t[]){ + {s8_const_lit("x"), &type__i64, .offset = offsetof(v2i64_t, x)}, + {s8_const_lit("y"), &type__i64, .offset = offsetof(v2i64_t, y)}, + } +}; + +type_t type__v3i64_t = { type_kind_struct, s8_const_lit("v3i64_t"), sizeof(v3i64_t), .count = 3, + .members = (type_member_t[]){ + {s8_const_lit("x"), &type__i64, .offset = offsetof(v3i64_t, x)}, + {s8_const_lit("y"), &type__i64, .offset = offsetof(v3i64_t, y)}, + {s8_const_lit("z"), &type__i64, .offset = offsetof(v3i64_t, z)}, + } +}; + +type_t type__v4i64_t = { type_kind_struct, s8_const_lit("v4i64_t"), sizeof(v4i64_t), .count = 4, + .members = (type_member_t[]){ + {s8_const_lit("x"), &type__i64, .offset = offsetof(v4i64_t, x)}, + {s8_const_lit("y"), &type__i64, .offset = offsetof(v4i64_t, y)}, + {s8_const_lit("z"), &type__i64, .offset = offsetof(v4i64_t, z)}, + {s8_const_lit("w"), &type__i64, .offset = offsetof(v4i64_t, w)}, + } +}; + +type_t type__r3i64_t = { type_kind_struct, s8_const_lit("r3i64_t"), sizeof(r3i64_t), .count = 6, + .members = (type_member_t[]){ + {s8_const_lit("x0"), &type__i64, .offset = offsetof(r3i64_t, x0)}, + {s8_const_lit("y0"), &type__i64, .offset = offsetof(r3i64_t, y0)}, + {s8_const_lit("x0"), &type__i64, .offset = offsetof(r3i64_t, z0)}, + {s8_const_lit("x1"), &type__i64, .offset = offsetof(r3i64_t, x1)}, + {s8_const_lit("y1"), &type__i64, .offset = offsetof(r3i64_t, y1)}, + {s8_const_lit("x1"), &type__i64, .offset = offsetof(r3i64_t, z1)}, + } +}; + +type_member_t members__r2i64_t[] = { + {s8_const_lit("x0"), &type__i64, .offset = offsetof(r2i64_t, x0)}, + {s8_const_lit("y0"), &type__i64, .offset = offsetof(r2i64_t, y0)}, + {s8_const_lit("x1"), &type__i64, .offset = offsetof(r2i64_t, x1)}, + {s8_const_lit("y1"), &type__i64, .offset = offsetof(r2i64_t, y1)}, +}; +DEFINE_STRUCT(r2i64_t); + +type_member_t members__r1i64_t[] = { + {s8_const_lit("x0"), &type__i64, .offset = offsetof(r1i64_t, x0)}, + {s8_const_lit("x1"), &type__i64, .offset = offsetof(r1i64_t, x1)}, +}; +DEFINE_STRUCT(r1i64_t); \ No newline at end of file diff --git a/src/core/types.h b/src/core/types.h new file mode 100644 index 0000000..28d7515 --- /dev/null +++ b/src/core/types.h @@ -0,0 +1,538 @@ +typedef uintptr_t usize; +typedef uint64_t u64; +typedef uint32_t u32; +typedef uint16_t u16; +typedef uint8_t u8; + +typedef intptr_t isize; +typedef int64_t i64; +typedef int32_t i32; +typedef int16_t i16; +typedef int8_t i8; + +typedef int64_t b64; +typedef int32_t b32; +typedef int16_t b16; +typedef int8_t b8; + +typedef float f32; +typedef double f64; + +#ifndef true +#define true 1 +#endif +#ifndef false +#define false 0 +#endif + +#define MIN(x,y) ((x) > (y) ? (y) : (x)) +#define MAX(x,y) ((x) > (y) ? (x) : (y)) + +#define CLAMP_TOP(A,X) MIN(A,X) +#define CLAMP_BOT(X,B) MAX(X,B) +#define CLAMP(x,a,b) (((x)<(a))?(a):((x)>(b))?(b):(x)) + +#define set_bit(x) (1ULL << (x)) +#define lengthof(x) (sizeof((x))/sizeof((x)[0])) +#ifndef offsetof +#define offsetof(st, m) ((usize)&(((st *)0)->m)) +#endif + +#define kib(x) (1024ULL * (x##ULL)) +#define mib(x) (1024ULL * kib(x)) +#define gib(x) (1024ULL * mib(x)) + +#define DEFER_LOOP(begin, end) for (int PASTE(_i_, __LINE__) = (begin, 0); !PASTE(_i_, __LINE__); PASTE(_i_, __LINE__) += (end, 1)) +#define STACK(type, size) struct { type data[size]; i32 len; } +#define STACK_PUSH(stack, ...) (assert((stack).len < lengthof((stack).data)), (stack).data[(stack).len++] = __VA_ARGS__) +#define STACK_POP(stack) (assert((stack).len > 0), (stack).data[--(stack).len]) +#define STACK_LAST(stack) (assert((stack).len > 0), (stack).data + ((stack).len-1)) + +#define STRINGIFY_(S) #S +#define STRINGIFY(S) STRINGIFY_(S) +#define PASTE_(a, b) a##b +#define PASTE(a, b) PASTE_(a, b) +#define SWAP(t, a, b) do { t PASTE(temp__, __LINE__) = a; a = b; b = PASTE(temp__, __LINE__); } while(0) +#define CODE(...) #__VA_ARGS__ +#define S8_CODE(...) s8_lit(#__VA_ARGS__) + +#if PLATFORM_CL +#define FILE_AND_LINE __FILE__"("STRINGIFY(__LINE__)")" +#else +#define FILE_AND_LINE __FILE__":"STRINGIFY(__LINE__) +#endif + +#if PLATFORM_CL +#define debug__break() __debugbreak() +#else +#define debug__break() __builtin_trap() +#endif +#define debug_break() (debug__break(), 0) + +#if PLATFORM_ASSERT +#define assert(x) (!(x) && debug_break()) + +#define assertf(x, ...) do {\ + debugf(__VA_ARGS__);\ + assert(x);\ +} while(0) + +#else +#define assert(x) ((void)(x)) +#define assertf(x,...) ((void)(x)) +#endif + +#if PLATFORM_WASM + #define THREAD_LOCAL +#elif PLATFORM_GCC | PLATFORM_CLANG + #define THREAD_LOCAL __thread +#elif PLATFORM_CL + #define THREAD_LOCAL __declspec(thread) +#else + #define THREAD_LOCAL _Thread_local +#endif + +// Single linked list Queue +#define SLLQ_APPEND_MOD(f, l, n, next) \ + do { \ + assert((n)->next == NULL); \ + if ((f) == 0) { \ + (f) = (l) = (n); \ + } else { \ + (l) = (l)->next = (n); \ + } \ + } while (0) +#define SLLQ_APPEND(f, l, n) SLLQ_APPEND_MOD(f, l, n, next) + +#define SLLQ_PREPEND_MOD(f, l, n, next) \ + do { \ + assert((n)->next == NULL); \ + if ((f) == 0) { \ + (f) = (l) = (n); \ + } else { \ + (n)->next = (f); \ + (f) = (n); \ + } \ + } while (0) +#define SLLQ_PREPEND(f, l, n) SLLQ_PREPEND_MOD(f, l, n, next) + +#define SLLQ_REMOVE_FIRST_MOD(f, l, next) \ + do { \ + if ((f) == (l)) { \ + (f) = (l) = 0; \ + } else { \ + (f) = (f)->next; \ + } \ + } while (0) +#define SLLQ_REMOVE_FIRST(f, l) SLLQ_REMOVE_FIRST_MOD(f, l, next) + +// Singly linked list stack +#define SLLS_PUSH_MOD(stack_base, new_stack_base, next) \ + do { \ + (new_stack_base)->next = (stack_base); \ + (stack_base) = (new_stack_base); \ + } while (0) +#define SLLS_PUSH(stack_base, new_stack_base) \ + SLLS_PUSH_MOD(stack_base, new_stack_base, next) + +#define SLLS_POP_AND_STORE(stack_base, out_node) \ + do { \ + if (stack_base) { \ + (out_node) = (stack_base); \ + (stack_base) = (stack_base)->next; \ + (out_node)->next = 0; \ + } \ + } while (0) + +// Doubly linked list Queue +#define DLLQ_APPEND_MOD(f, l, node, next, prev) \ + do { \ + assert((node)->next == NULL); \ + assert((node)->prev == NULL); \ + if ((f) == 0) { \ + (f) = (l) = (node); \ + } else { \ + (l)->next = (node); \ + (node)->prev = (l); \ + (l) = (node); \ + } \ + } while (0) +#define DLLQ_APPEND(f, l, node) DLLQ_APPEND_MOD(f, l, node, next, prev) + +#define DLLQ_PREPEND_MOD(f, l, node, next, prev) \ + do { \ + assert((node)->next == NULL); \ + assert((node)->prev == NULL); \ + if ((f) == 0) { \ + (f) = (l) = (node); \ + } else { \ + (node)->next = (f); \ + (f)->prev = (node); \ + (f) = (node); \ + } \ + } while (0) +#define DLLQ_PREPEND(f, l, node) DLLQ_PREPEND_MOD(f, l, node, next, prev) + +#define DLLQ_CONTAINS(f, l, n, next, prev) for ( + +#define DLLQ_REMOVE_MOD(first, last, node, next, prev) \ + do { \ + if ((first) == (last)) { \ + assert((node) == (first)); \ + (first) = (last) = 0; \ + } else if ((last) == (node)) { \ + (last) = (last)->prev; \ + (last)->next = 0; \ + } else if ((first) == (node)) { \ + (first) = (first)->next; \ + (first)->prev = 0; \ + } else { \ + (node)->prev->next = (node)->next; \ + (node)->next->prev = (node)->prev; \ + } \ + if (node) { \ + (node)->prev = 0; \ + (node)->next = 0; \ + } \ + } while (0) +#define DLLQ_REMOVE(first, last, node) DLLQ_REMOVE_MOD(first, last, node, next, prev) + +// Doubly linked list Stack +#define DLLS_ADD_MOD(first, node, next, prev) \ + do { \ + assert((node)->next == NULL); \ + assert((node)->prev == NULL); \ + (node)->next = (first); \ + if ((first)) \ + (first)->prev = (node); \ + (first) = (node); \ + } while (0) +#define DLLS_ADD(first, node) DLLS_ADD_MOD(first, node, next, prev) +#define DLLS_REMOVE_MOD(first, node, next, prev) \ + do { \ + if ((node) == (first)) { \ + (first) = (first)->next; \ + if ((first)) \ + (first)->prev = 0; \ + } else { \ + (node)->prev->next = (node)->next; \ + if ((node)->next) \ + (node)->next->prev = (node)->prev; \ + } \ + if (node) { \ + (node)->prev = 0; \ + (node)->next = 0; \ + } \ + } while (0) +#define DLLS_REMOVE(first, node) DLLS_REMOVE_MOD(first, node, next, prev) + +const usize ma_page_size = 4096; +const usize ma_default_alignment = sizeof(void *); +const usize ma_default_reserve_size = mib(256); + +typedef struct ma_arena_t ma_arena_t; +struct ma_arena_t { + u8 *data; + usize len; + usize base_len; // to prevent self deleting the arena + usize reserve; + usize commit; + usize align; +}; + +typedef struct ma_temp_t ma_temp_t; +struct ma_temp_t { + ma_arena_t *arena; + usize len; +}; + +void ma_init(ma_arena_t *arena, usize reserve); +ma_arena_t *ma_create(usize reserve); +void ma_destroy(ma_arena_t *arena); + +void *ma_push_size_ex(ma_arena_t *arena, usize size); +void *ma_push_size(ma_arena_t *arena, usize size); +ma_arena_t *ma_push_arena(ma_arena_t *allocator, usize size); + +#define ma_push_type(arena, Type) (Type *)ma_push_size((arena), sizeof(Type)) +#define ma_push_array(arena, Type, count) (Type *)ma_push_size((arena), sizeof(Type) * (count)) + +void ma_set_len(ma_arena_t *arena, usize pos); +void ma_pop(ma_arena_t *arena, usize size); +void ma_set0(ma_arena_t *arena); + +ma_temp_t ma_begin_temp(ma_arena_t *arena); +void ma_end_temp(ma_temp_t temp); + +typedef struct s8_t s8_t; +struct s8_t { + char *str; + int64_t len; +}; + +typedef struct s16_t s16_t; +struct s16_t { + u16 *str; + i64 len; +}; + +typedef struct s32_t s32_t; +struct s32_t { + u32 *str; + i64 len; +}; + +typedef struct sb8_node_t sb8_node_t; +struct sb8_node_t { + sb8_node_t *next; + union { + struct { char *str; int64_t len; }; + s8_t s; + }; +}; + +typedef struct sb8_t sb8_t; +struct sb8_t { + ma_arena_t *arena; + sb8_node_t *first; + sb8_node_t *last; + + int indent; +}; + +i64 wstr_len(wchar_t *string); +int str_len(char *str); + +char char_to_lower_case(char a); +char char_to_upper_case(char a); +b32 char_is_whitespace(char w); +b32 char_is_alphabetic(char a); +b32 char_is_ident(char a); +b32 char_is_digit(char a); +b32 char_is_alphanumeric(char a); + +b32 s8_equal(s8_t a, s8_t b); + +enum { s8_ignore_case = 1 }; + +#define s8(str,len) (s8_t){str, len} +#define s8_nil() (s8_t){0} +#define s8_lit(string) (s8_t){(char *)string, sizeof(string) - 1} +#define s8_const_lit(string) { string, sizeof(string) - 1 } +#define s8_fmtspec(string) (int)(string).len, (string).str + +#define sb8_serial_begin(arena) &(sb8_t){.arena = arena} +#define sb8_serial_end(sb) sb8_merge(sb) + +typedef struct core_desc_t core_desc_t; +struct core_desc_t { + void (*print)(char *string); + void (*panic)(void); + void (*on_exit)(void); + + ma_arena_t scratch[3]; + b32 break_on_panic; +}; +extern THREAD_LOCAL core_desc_t core_desc; +void debugf(const char *string, ...); +void panicf(const char *string, ...); + + +typedef union v2f32_t v2f32_t; +union v2f32_t { + struct { f32 x, y; }; + f32 e[2]; +}; + +typedef union v3f32_t v3f32_t; +union v3f32_t { + struct { f32 x, y, z; }; + struct { v2f32_t xy; }; + struct { f32 _x0; v2f32_t zw; }; + struct { f32 r, g, b; }; + struct { f32 h, s, l; }; + f32 e[3]; +}; + +typedef union v4f32_t v4f32_t; +union v4f32_t { + struct { f32 x, y, z, w; }; + struct { v2f32_t xy; v2f32_t zw; }; + struct { v3f32_t xyz; }; + struct { f32 _x0; f32 yzw; }; + struct { f32 r, g, b, a; }; + struct { f32 h, s, l, _a; }; + f32 e[4]; +}; + +typedef union r1f32_t r1f32_t; +union r1f32_t { + struct { f32 min, max; }; + struct { f32 x0, x1; }; + f32 e[2]; +}; + +typedef union r2f32_t r2f32_t; +union r2f32_t { + struct { v2f32_t min, max; }; + struct { f32 x0, y0, x1, y1; }; + v4f32_t e4; + f32 e[4]; +}; + +typedef union r3f32_t r3f32_t; +union r3f32_t { + struct { v3f32_t min, max; }; + struct { f32 x0, y0, z0, x1, y1, z1; }; + f32 e[6]; +}; + + +typedef union v2f64_t v2f64_t; +union v2f64_t { + struct { f64 x, y; }; + f64 e[2]; +}; + +typedef union v3f64_t v3f64_t; +union v3f64_t { + struct { f64 x, y, z; }; + struct { v2f64_t xy; }; + struct { f64 _x0; v2f64_t zw; }; + struct { f64 r, g, b; }; + struct { f64 h, s, l; }; + f64 e[3]; +}; + +typedef union v4f64_t v4f64_t; +union v4f64_t { + struct { f64 x, y, z, w; }; + struct { v2f64_t xy; v2f64_t zw; }; + struct { v3f64_t xyz; }; + struct { f64 _x0; f64 yzw; }; + struct { f64 r, g, b, a; }; + struct { f64 h, s, l, _a; }; + f64 e[4]; +}; + +typedef union r1f64_t r1f64_t; +union r1f64_t { + struct { f64 min, max; }; + struct { f64 x0, x1; }; + f64 e[2]; +}; + +typedef union r2f64_t r2f64_t; +union r2f64_t { + struct { v2f64_t min, max; }; + struct { f64 x0, y0, x1, y1; }; + v4f64_t e4; + f64 e[4]; +}; + +typedef union r3f64_t r3f64_t; +union r3f64_t { + struct { v3f64_t min, max; }; + struct { f64 x0, y0, z0, x1, y1, z1; }; + f64 e[6]; +}; + + +typedef union v2i32_t v2i32_t; +union v2i32_t { + struct { i32 x, y; }; + i32 e[2]; +}; + +typedef union v3i32_t v3i32_t; +union v3i32_t { + struct { i32 x, y, z; }; + struct { v2i32_t xy; }; + struct { i32 _x0; v2i32_t zw; }; + struct { i32 r, g, b; }; + struct { i32 h, s, l; }; + i32 e[3]; +}; + +typedef union v4i32_t v4i32_t; +union v4i32_t { + struct { i32 x, y, z, w; }; + struct { v2i32_t xy; v2i32_t zw; }; + struct { v3i32_t xyz; }; + struct { i32 _x0; i32 yzw; }; + struct { i32 r, g, b, a; }; + struct { i32 h, s, l, _a; }; + i32 e[4]; +}; + +typedef union r1i32_t r1i32_t; +union r1i32_t { + struct { i32 min, max; }; + struct { i32 x0, x1; }; + i32 e[2]; +}; + +typedef union r2i32_t r2i32_t; +union r2i32_t { + struct { v2i32_t min, max; }; + struct { i32 x0, y0, x1, y1; }; + v4i32_t e4; + i32 e[4]; +}; + +typedef union r3i32_t r3i32_t; +union r3i32_t { + struct { v3i32_t min, max; }; + struct { i32 x0, y0, z0, x1, y1, z1; }; + i32 e[6]; +}; + + +typedef union v2i64_t v2i64_t; +union v2i64_t { + struct { i64 x, y; }; + i64 e[2]; +}; + +typedef union v3i64_t v3i64_t; +union v3i64_t { + struct { i64 x, y, z; }; + struct { v2i64_t xy; }; + struct { i64 _x0; v2i64_t zw; }; + struct { i64 r, g, b; }; + struct { i64 h, s, l; }; + i64 e[3]; +}; + +typedef union v4i64_t v4i64_t; +union v4i64_t { + struct { i64 x, y, z, w; }; + struct { v2i64_t xy; v2i64_t zw; }; + struct { v3i64_t xyz; }; + struct { i64 _x0; i64 yzw; }; + struct { i64 r, g, b, a; }; + struct { i64 h, s, l, _a; }; + i64 e[4]; +}; + +typedef union r1i64_t r1i64_t; +union r1i64_t { + struct { i64 min, max; }; + struct { i64 x0, x1; }; + i64 e[2]; +}; + +typedef union r2i64_t r2i64_t; +union r2i64_t { + struct { v2i64_t min, max; }; + struct { i64 x0, y0, x1, y1; }; + v4i64_t e4; + i64 e[4]; +}; + +typedef union r3i64_t r3i64_t; +union r3i64_t { + struct { v3i64_t min, max; }; + struct { i64 x0, y0, z0, x1, y1, z1; }; + i64 e[6]; +}; diff --git a/src/core/unicode.c b/src/core/unicode.c new file mode 100644 index 0000000..367ddc6 --- /dev/null +++ b/src/core/unicode.c @@ -0,0 +1,226 @@ +typedef struct utf32_result_t utf32_result_t; +struct utf32_result_t { + uint32_t out_str; + int advance; + int error; +}; + +typedef struct utf16_result_t utf16_result_t; +struct utf16_result_t { + uint16_t out_str[2]; + int len; + int error; +}; + +typedef struct utf8_result_t utf8_result_t; +struct utf8_result_t { + uint8_t out_str[4]; + int len; + int error; +}; + +typedef struct utf8_iter_t utf8_iter_t; +struct utf8_iter_t { + char *str; + int len; + int utf8_codepoint_byte_size; + int i; + uint32_t item; +}; + +utf32_result_t utf16_to_utf32(uint16_t *c, int max_advance) { + utf32_result_t result = {0}; + if (max_advance >= 1) { + result.advance = 1; + result.out_str = c[0]; + if (c[0] >= 0xD800 && c[0] <= 0xDBFF && c[1] >= 0xDC00 && c[1] <= 0xDFFF) { + if (max_advance >= 2) { + result.out_str = 0x10000; + result.out_str += (uint32_t)(c[0] & 0x03FF) << 10u | (c[1] & 0x03FF); + result.advance = 2; + } + else + result.error = 2; + } + } + else { + result.error = 1; + } + return result; +} + +utf8_result_t utf32_to_utf8(uint32_t codepoint) { + utf8_result_t result = {0}; + + if (codepoint <= 0x7F) { + result.len = 1; + result.out_str[0] = (char)codepoint; + } + else if (codepoint <= 0x7FF) { + result.len = 2; + result.out_str[0] = 0xc0 | (0x1f & (codepoint >> 6)); + result.out_str[1] = 0x80 | (0x3f & codepoint); + } + else if (codepoint <= 0xFFFF) { // 16 bit word + result.len = 3; + result.out_str[0] = 0xe0 | (0xf & (codepoint >> 12)); // 4 bits + result.out_str[1] = 0x80 | (0x3f & (codepoint >> 6)); // 6 bits + result.out_str[2] = 0x80 | (0x3f & codepoint); // 6 bits + } + else if (codepoint <= 0x10FFFF) { // 21 bit word + result.len = 4; + result.out_str[0] = 0xf0 | (0x7 & (codepoint >> 18)); // 3 bits + result.out_str[1] = 0x80 | (0x3f & (codepoint >> 12)); // 6 bits + result.out_str[2] = 0x80 | (0x3f & (codepoint >> 6)); // 6 bits + result.out_str[3] = 0x80 | (0x3f & codepoint); // 6 bits + } + else { + result.error = 1; + } + + return result; +} + +utf32_result_t utf8_to_utf32(char *c, int max_advance) { + utf32_result_t result = {0}; + + if ((c[0] & 0x80) == 0) { // Check if leftmost zero of first byte is unset + if (max_advance >= 1) { + result.out_str = c[0]; + result.advance = 1; + } + else result.error = 1; + } + + else if ((c[0] & 0xe0) == 0xc0) { + if ((c[1] & 0xc0) == 0x80) { // Continuation byte required + if (max_advance >= 2) { + result.out_str = (uint32_t)(c[0] & 0x1f) << 6u | (c[1] & 0x3f); + result.advance = 2; + } + else result.error = 2; + } + else result.error = 2; + } + + else if ((c[0] & 0xf0) == 0xe0) { + if ((c[1] & 0xc0) == 0x80 && (c[2] & 0xc0) == 0x80) { // Two continuation bytes required + if (max_advance >= 3) { + result.out_str = (uint32_t)(c[0] & 0xf) << 12u | (uint32_t)(c[1] & 0x3f) << 6u | (c[2] & 0x3f); + result.advance = 3; + } + else result.error = 3; + } + else result.error = 3; + } + + else if ((c[0] & 0xf8) == 0xf0) { + if ((c[1] & 0xc0) == 0x80 && (c[2] & 0xc0) == 0x80 && (c[3] & 0xc0) == 0x80) { // Three continuation bytes required + if (max_advance >= 4) { + result.out_str = (uint32_t)(c[0] & 0xf) << 18u | (uint32_t)(c[1] & 0x3f) << 12u | (uint32_t)(c[2] & 0x3f) << 6u | (uint32_t)(c[3] & 0x3f); + result.advance = 4; + } + else result.error = 4; + } + else result.error = 4; + } + else result.error = 4; + + return result; +} + +utf16_result_t utf32_to_utf16(uint32_t codepoint) { + utf16_result_t result = {0}; + if (codepoint < 0x10000) { + result.out_str[0] = (uint16_t)codepoint; + result.out_str[1] = 0; + result.len = 1; + } + else if (codepoint <= 0x10FFFF) { + uint32_t code = (codepoint - 0x10000); + result.out_str[0] = (uint16_t)(0xD800 | (code >> 10)); + result.out_str[1] = (uint16_t)(0xDC00 | (code & 0x3FF)); + result.len = 2; + } + else { + result.error = 1; + } + + return result; +} + +#define UTF__HANDLE_DECODE_ERROR(question_mark) \ + { \ + if (outlen < buffer_size - 1) buffer[outlen++] = (question_mark); \ + break; \ + } + +int64_t str_from_wstr(char *buffer, int64_t buffer_size, uint16_t *in, int64_t inlen) { + int64_t outlen = 0; + for (int64_t i = 0; i < inlen && in[i];) { + utf32_result_t decode = utf16_to_utf32((uint16_t *)(in + i), (int)(inlen - i)); + if (!decode.error) { + i += decode.advance; + utf8_result_t encode = utf32_to_utf8(decode.out_str); + if (!encode.error) { + for (int64_t j = 0; j < encode.len; j++) { + if (outlen < buffer_size - 1) { + buffer[outlen++] = encode.out_str[j]; + } + } + } + else UTF__HANDLE_DECODE_ERROR('?'); + } + else UTF__HANDLE_DECODE_ERROR('?'); + } + + buffer[outlen] = 0; + return outlen; +} + +int64_t wstr_from_str(uint16_t *buffer, int64_t buffer_size, char *in, int64_t inlen) { + int64_t outlen = 0; + for (int64_t i = 0; i < inlen;) { + utf32_result_t decode = utf8_to_utf32(in + i, (int)(inlen - i)); + if (!decode.error) { + i += decode.advance; + utf16_result_t encode = utf32_to_utf16(decode.out_str); + if (!encode.error) { + for (int64_t j = 0; j < encode.len; j++) { + if (outlen < buffer_size - 1) { + buffer[outlen++] = encode.out_str[j]; + } + } + } + else UTF__HANDLE_DECODE_ERROR(0x003f); + } + else UTF__HANDLE_DECODE_ERROR(0x003f); + } + + buffer[outlen] = 0; + return outlen; +} + +void utf8_advance(utf8_iter_t *iter) { + iter->i += iter->utf8_codepoint_byte_size; + utf32_result_t r = utf8_to_utf32(iter->str + iter->i, iter->len - iter->i); + if (r.error) { + iter->item = 0; + return; + } + + iter->utf8_codepoint_byte_size = r.advance; + iter->item = r.out_str; +} + +utf8_iter_t utf8_iterate_ex(char *str, int len) { + utf8_iter_t result = {str, len}; + if (len) utf8_advance(&result); + return result; +} + +utf8_iter_t utf8_iterate(char *str) { + int length = 0; + while (str[length]) length += 1; + return utf8_iterate_ex(str, length); +} \ No newline at end of file diff --git a/src/core_test/core_test_entry.c b/src/core_test/core_test_entry.c new file mode 100644 index 0000000..799c6ed --- /dev/null +++ b/src/core_test/core_test_entry.c @@ -0,0 +1,110 @@ +#include "core/core.h" +#include "core/core.c" + +void test_s8(void) { + ma_arena_t *arena = ma_create(ma_default_reserve_size); + + { + ma_temp_t temp = ma_begin_temp(arena); + sb8_t *sb = &(sb8_t){arena}; + + s8_t memes = s8_lit("memes"); + sb8_printf(sb, "%S", memes); + assert(sb->first == sb->last); + assert(sb->first->len == 5); + assert(s8_equal(sb->first->s, memes)); + + sb8_printf(sb, "%S", s8_lit("things are going fine")); + s8_t string = sb8_merge(sb); + assert(s8_equal(string, s8_lit("memesthings are going fine"))); + + ma_end_temp(temp); + } + + { + s8_t str = s8_lit("thing|another|"); + sb8_t sb = s8_split(arena, str, s8_lit("|"), s8_split_none); + + assert(s8_equal(sb.first->s, s8_lit("thing"))); + assert(s8_equal(sb.first->next->s, s8_lit("another"))); + assert(sb.first->next->next == NULL); + } + + { + s8_t str = s8_lit("thing|another|"); + sb8_t sb = s8_split(arena, str, s8_lit("|"), s8_split_inclusive); + + assert(s8_equal(sb.first->s, s8_lit("thing"))); + assert(s8_equal(sb.first->next->s, s8_lit("|"))); + assert(s8_equal(sb.first->next->next->s, s8_lit("another"))); + assert(s8_equal(sb.first->next->next->next->s, s8_lit("|"))); + assert(sb.first->next->next->next->next == NULL); + } + + { + s8_t str = s8_lit("aabaaBaa"); + sb8_t sb = s8_split(arena, str, s8_lit("b"), s8_split_inclusive | s8_split_ignore_case); + + assert(s8_equal(sb.first->s, s8_lit("aa"))); + assert(s8_equal(sb.first->next->s, s8_lit("b"))); + assert(s8_equal(sb.first->next->next->s, s8_lit("aa"))); + assert(s8_equal(sb.first->next->next->next->s, s8_lit("B"))); + assert(s8_equal(sb.first->next->next->next->next->s, s8_lit("aa"))); + assert(sb.first->next->next->next->next->next == NULL); + } + + { + s8_t str = s8_lit("aabaaBaa"); + sb8_t sb = s8_split(arena, str, s8_lit("b"), s8_split_inclusive); + + assert(s8_equal(sb.first->s, s8_lit("aa"))); + assert(s8_equal(sb.first->next->s, s8_lit("b"))); + assert(s8_equal(sb.first->next->next->s, s8_lit("aaBaa"))); + } + + { + s8_t s = s8_lit("0123456789"); + assert(s8_equal(s8_slice(s, 0, 4), s8_lit("0123"))); + assert(s8_equal(s8_slice(s, -2, -1), s8_lit("89"))); + assert(s8_equal(s8_slice(s, -2, 10), s8_lit("89"))); + assert(s8_equal(s8_slice(s, 8, 10), s8_lit("89"))); + } + + { + s8_t s = s8_lit(" a \n"); + s = s8_trim(s); + assert(s8_equal(s, s8_lit("a"))); + } + + { + s8_t s = s8_lit("C:/memes/the_thing.c"); + s8_t ss = s8_get_name_no_ext(s); + assert(s8_equal(ss, s8_lit("the_thing"))); + } + + { + s8_t s = s8_fmt(arena, "%d%Sv%s", 32, s8_lit("|"), ">"); + assert(s8_equal(s, s8_lit("32|v>"))); + } + + ma_destroy(arena); +} + +#include + +int main(int argc, char **argv) { + printf("PLATFORM_WASM = %d\n", PLATFORM_WASM); + printf("PLATFORM_WINDOWS = %d\n", PLATFORM_WINDOWS); + printf("PLATFORM_LINUX = %d\n", PLATFORM_LINUX); + printf("PLATFORM_POSIX = %d\n", PLATFORM_POSIX); + printf("PLATFORM_MAC_OS = %d\n", PLATFORM_MAC_OS); + printf("PLATFORM_CLANG = %d\n", PLATFORM_CLANG); + printf("PLATFORM_GCC = %d\n", PLATFORM_GCC); + printf("PLATFORM_CL = %d\n", PLATFORM_CL); + printf("PLATFORM_TCC = %d\n", PLATFORM_TCC); + printf("PLATFORM_ASSERT = %d\n", PLATFORM_ASSERT); + + test_s8(); + + printf("all done!\n"); +} \ No newline at end of file diff --git a/src/meta/build_tool.c b/src/meta/build_tool.c new file mode 100644 index 0000000..1b42795 --- /dev/null +++ b/src/meta/build_tool.c @@ -0,0 +1,5187 @@ +/* +@echo off + +if not exist build\build_tool.exe ( + mkdir build + pushd build + cl ..\core\build_tool.c -Fe:build_tool.exe -Zi -FC -nologo + popd +) + +build\build_tool.exe + +///////////// +#define BUILD_TOOL_LIB +#include "core/build_tool.c" + +int main(int argc, char **argv) { + return 0; +} +*/ + +#ifndef _CRT_SECURE_NO_WARNINGS + #define _CRT_SECURE_NO_WARNINGS +#endif + +#ifndef FIRST_CORE_HEADER +#define FIRST_CORE_HEADER + + #if defined(__APPLE__) && defined(__MACH__) + #define OS_MAC 1 + #elif defined(_WIN32) + #define OS_WINDOWS 1 + #elif defined(__linux__) + #define OS_POSIX 1 + #define OS_LINUX 1 + #elif OS_WASM + #else + #error Unsupported platform + #endif + + #if defined(__clang__) + #define COMPILER_CLANG 1 + #elif defined(__GNUC__) || defined(__GNUG__) + #define COMPILER_GCC 1 + #elif defined(_MSC_VER) + #define COMPILER_MSVC 1 + #elif defined(__TINYC__) + #define COMPILER_TCC 1 + #else + #error Unsupported compiler + #endif + + #ifndef OS_MAC + #define OS_MAC 0 + #endif + + #ifndef OS_WINDOWS + #define OS_WINDOWS 0 + #endif + + #ifndef OS_LINUX + #define OS_LINUX 0 + #endif + + #ifndef OS_POSIX + #define OS_POSIX 0 + #endif + + #ifndef COMPILER_MSVC + #define COMPILER_MSVC 0 + #endif + + #ifndef COMPILER_CLANG + #define COMPILER_CLANG 0 + #endif + + #ifndef COMPILER_GCC + #define COMPILER_GCC 0 + #endif + + #ifndef COMPILER_TCC + #define COMPILER_TCC 0 + #endif + + #if COMPILER_MSVC + #define FORCE_INLINE __forceinline + #elif COMPILER_GCC || COMPILER_CLANG + #define FORCE_INLINE __attribute__((always_inline)) inline + #else + #define FORCE_INLINE inline + #endif + + #if OS_MAC + #define IF_MAC(x) x + #else + #define IF_MAC(x) + #endif + + #if OS_WINDOWS + #define IF_WINDOWS(x) x + #define IF_WINDOWS_ELSE(x, y) x + #else + #define IF_WINDOWS(x) + #define IF_WINDOWS_ELSE(x, y) y + #endif + + #if OS_LINUX + #define IF_LINUX(x) x + #define IF_LINUX_ELSE(x, y) x + #else + #define IF_LINUX(x) + #define IF_LINUX_ELSE(x, y) y + #endif + + #if OS_WINDOWS + #define OS_NAME "windows" + #elif OS_LINUX + #define OS_NAME "linux" + #elif OS_MAC + #define OS_NAME "mac_os" + #elif OS_WASM + #define OS_NAME "wasm" + #else + #error couldnt figure out OS + #endif + + #ifndef THREAD_LOCAL + #if defined(__cplusplus) && __cplusplus >= 201103L + #define THREAD_LOCAL thread_local + #elif defined(__GNUC__) + #define THREAD_LOCAL __thread + #elif defined(_MSC_VER) + #define THREAD_LOCAL __declspec(thread) + #elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L && !defined(__STDC_NO_THREADS__) + #define THREAD_LOCAL _Thread_local + #elif defined(__TINYC__) + #define THREAD_LOCAL _Thread_local + #else + #error Couldnt figure out thread local, needs to be provided manually + #endif + #endif +#endif + + +#ifndef FIRST_IO_HEADER + #define FIRST_IO_HEADER + #include + +typedef enum IO_ErrorResult { + IO_ErrorResult_Continue, + IO_ErrorResult_Break, + IO_ErrorResult_Exit, +} IO_ErrorResult; + + #if defined(_MSC_VER) + #define IO_DebugBreak() (__debugbreak(), 0) + #else + #define IO_DebugBreak() (__builtin_trap(), 0) + #endif + +typedef void IO_MessageHandler(int kind, const char *file, int line, char *str, int len); +extern THREAD_LOCAL void (*IO_User_OutputMessage)(int kind, const char *file, int line, char *str, int len); + + #define IO__STRINGIFY(x) #x + #define IO__TOSTRING(x) IO__STRINGIFY(x) + #define IO_LINE IO__TOSTRING(__LINE__) + + #define IO_Assert(x) !(x) && IO__FatalError((__FILE__ "(" IO_LINE "): " \ + "error: " #x "\n")) && \ + IO_DebugBreak() + #define IO_FatalErrorf(...) \ + do { \ + bool result = IO__FatalErrorf(__FILE__, __LINE__, __VA_ARGS__); \ + if (result) IO_DebugBreak(); \ + } while (0) + #define IO_FatalError(...) \ + do { \ + bool result = IO__FatalError(__FILE__ "(" IO_LINE "): error - " __VA_ARGS__); \ + if (result) IO_DebugBreak(); \ + } while (0) + #define IO_Assertf(x, ...) \ + do { \ + if (!(x)) { \ + bool result = IO__FatalErrorf(__FILE__, __LINE__, __VA_ARGS__); \ + if (result) IO_DebugBreak(); \ + } \ + } while (0) + + #define IO_InvalidElseIf(c) \ + else if (c) { \ + IO_InvalidCodepath(); \ + } + #define IO_InvalidElse() \ + else { \ + IO_InvalidCodepath(); \ + } + #define IO_InvalidCodepath() IO_FatalError("This codepath is invalid") + #define IO_InvalidDefaultCase() \ + default: { \ + IO_FatalError("Entered invalid switch statement case"); \ + } + #define IO_Todo() IO_FatalError("This codepath is not implemented yet") + +bool IO__FatalErrorf(const char *file, int line, const char *msg, ...); +void IO__Printf(int kind, const char *file, int line, const char *msg, ...); +bool IO__FatalError(const char *msg); +void IO_Print(int kind, const char *file, int line, char *msg, int len); +void IO_OutputMessage(char *str, int len); +IO_ErrorResult IO_OutputError(char *str, int len); +void IO_Exit(int error_code); +bool IO_IsDebuggerPresent(void); + +static const int IO_KindPrintf = 1; +static const int IO_KindWarningf = 2; + + #define IO_Printf(...) IO__Printf(IO_KindPrintf, __FILE__, __LINE__, __VA_ARGS__) + #define IO_Warningf(...) IO__Printf(IO_KindWarningf, __FILE__, __LINE__, __VA_ARGS__) +#endif + +#include + +#ifndef IO_SNPRINTF + #include + #define IO_SNPRINTF snprintf +#endif + +#ifndef IO_VSNPRINTF + #include + #define IO_VSNPRINTF vsnprintf +#endif + +#ifndef IO_ALLOCATE + #include + #define IO_ALLOCATE(x) malloc(x) + #define IO_FREE(x) free(x) +#endif + +static int IO_Strlen(char *string) { + int len = 0; + while (*string++ != 0) len++; + return len; +} + +THREAD_LOCAL void (*IO_User_OutputMessage)(int kind, const char *file, int line, char *str, int len); + +bool IO__FatalErrorf(const char *file, int line, const char *msg, ...) { + va_list args1; + va_list args2; + char buff[2048]; + + va_start(args1, msg); + va_copy(args2, args1); + int size = IO_VSNPRINTF(buff, sizeof(buff), msg, args2); + va_end(args2); + + char *new_buffer = 0; + char *user_message = buff; + if (size >= sizeof(buff)) { + size += 4; + new_buffer = (char *)IO_ALLOCATE(size); + IO_VSNPRINTF(new_buffer, size, msg, args1); + user_message = new_buffer; + } + va_end(args1); + + IO_ErrorResult ret = IO_ErrorResult_Continue; + { + char buff2[2048]; + char *result = buff2; + char *b = 0; + int size2 = IO_SNPRINTF(buff2, sizeof(buff2), "%s(%d): error: %s \n", file, line, user_message); + if (size2 >= sizeof(buff2)) { + size2 += 4; + b = (char *)IO_ALLOCATE(size2); + size2 = IO_SNPRINTF(b, size2, "%s(%d): error: %s \n", file, line, user_message); + result = b; + } + + ret = IO_OutputError(result, size2); + if (ret == IO_ErrorResult_Exit) { + IO_Exit(1); + } + + if (b) { + IO_FREE(b); + } + } + + if (new_buffer) { + IO_FREE(new_buffer); + } + + return ret == IO_ErrorResult_Break; +} + +void IO__Printf(int kind, const char *file, int line, const char *msg, ...) { + // First try to use a static buffer. That can fail because the message + // can be bigger then the buffer. Allocate enough memory to fit in that + // case. + va_list args1; + va_list args2; + char buff[2048]; + + va_start(args1, msg); + va_copy(args2, args1); + int size = IO_VSNPRINTF(buff, sizeof(buff), msg, args2); + va_end(args2); + + char *new_buffer = 0; + char *result = buff; + if (size >= sizeof(buff)) { + size += 4; + new_buffer = (char *)IO_ALLOCATE(size); + IO_VSNPRINTF(new_buffer, size, msg, args1); + result = new_buffer; + } + va_end(args1); + + if (IO_User_OutputMessage) { + IO_User_OutputMessage(kind, file, line, result, size); + } else { + IO_OutputMessage(result, size); + } + + if (new_buffer) { + IO_FREE(new_buffer); + } +} + +bool IO__FatalError(const char *msg) { + int len = IO_Strlen((char *)msg); + IO_ErrorResult result = IO_OutputError((char *)msg, len); + if (result == IO_ErrorResult_Exit) { + IO_Exit(1); + } + return result == IO_ErrorResult_Break; +} + +void IO_Print(int kind, const char *file, int line, char *msg, int len) { + if (IO_User_OutputMessage) { + IO_User_OutputMessage(kind, file, line, msg, len); + } else { + IO_OutputMessage(msg, len); + } +} +#ifdef _WIN32 + #ifndef NOMINMAX + #define NOMINMAX + #endif + #ifndef WIN32_LEAN_AND_MEAN + #define WIN32_LEAN_AND_MEAN + #endif + #include + + #pragma comment(lib, "user32") + + #include + +bool IO_IsDebuggerPresent(void) { + return IsDebuggerPresent(); +} + +void IO_OutputMessage(char *str, int len) { + if (IsDebuggerPresent()) { + OutputDebugStringA(str); + } + printf("%.*s", len, str); + fflush(stdout); +} + +IO_ErrorResult IO_OutputError(char *str, int len) { + IO_ErrorResult result = IO_ErrorResult_Continue; + IO_OutputMessage(str, len); + + char *msg = str; + if (str[len] != 0) { + msg = (char *)IO_ALLOCATE(len + 1); + for (int i = 0; i < len; i += 1) msg[i] = str[i]; + msg[len] = 0; + } + + OutputDebugStringA(msg); + if (!IsDebuggerPresent()) { + + // Limit size of error output message + char tmp = 0; + if (len > 4096) { + tmp = str[4096]; + str[4096] = 0; + } + + MessageBoxA(0, msg, "Error!", 0); + + if (tmp != 0) { + str[4096] = tmp; + } + + result = IO_ErrorResult_Exit; + } else { + result = IO_ErrorResult_Break; + } + + if (msg != str) { + IO_FREE(msg); + } + + return result; +} + +void IO_Exit(int error_code) { + ExitProcess(error_code); +} +#elif __linux__ || __unix__ || __APPLE__ + #include +IO_ErrorResult IO_OutputError(char *str, int len) { + fprintf(stderr, "%.*s", len, str); + return IO_ErrorResult_Exit; +} + +void IO_OutputMessage(char *str, int len) { + fprintf(stdout, "%.*s", len, str); +} + +void IO_Exit(int error_code) { + exit(error_code); +} + +bool IO_IsDebuggerPresent(void) { + return false; +} +#else +IO_ErrorResult IO_OutputError(char *str, int len) { + return IO_ErrorResult_Exit; +} + +void IO_OutputMessage(char *str, int len) { +} + +void IO_Exit(int error_code) { +} + +bool IO_IsDebuggerPresent(void) { + return false; +} + +#endif // LIBC + +#ifndef MA_HEADER + #define MA_HEADER + #include + #include + #include + + #define MA_KIB(x) ((x##ull) * 1024ull) + #define MA_MIB(x) (MA_KIB(x) * 1024ull) + #define MA_GIB(x) (MA_MIB(x) * 1024ull) + #define MA_TIB(x) (MA_GIB(x) * 1024ull) + +typedef struct MV_Memory MV_Memory; +typedef struct MA_Temp MA_Temp; +typedef struct MA_Arena MA_Arena; +typedef struct MA_SourceLoc MA_SourceLoc; + + #ifndef MA_DEFAULT_RESERVE_SIZE + #define MA_DEFAULT_RESERVE_SIZE MA_GIB(1) + #endif + + #ifndef MA_DEFAULT_ALIGNMENT + #define MA_DEFAULT_ALIGNMENT 8 + #endif + + #ifndef MA_COMMIT_ADD_SIZE + #define MA_COMMIT_ADD_SIZE MA_MIB(4) + #endif + + #ifndef MA_ZERO_IS_INITIALIZATION + #define MA_ZERO_IS_INITIALIZATION 1 + #endif + + #ifndef MA_MemoryZero + #include + #define MA_MemoryZero(p, size) memset(p, 0, size) + #endif + + #ifndef MA_MemoryCopy + #include + #define MA_MemoryCopy(dst, src, size) memcpy(dst, src, size); + #endif + +struct MV_Memory { + size_t commit; + size_t reserve; + uint8_t *data; +}; + +struct MA_Arena { + MV_Memory memory; + int alignment; + size_t len; + size_t base_len; // When popping to 0 this is the minimum "len" value + // It's so that Bootstrapped arena won't delete itself when Reseting. +}; + +struct MA_Temp { + MA_Arena *arena; + size_t pos; +}; + +struct MA_SourceLoc { + const char *file; + int line; +}; + +extern THREAD_LOCAL MA_SourceLoc MA_SavedSourceLoc; + #define MA_SaveSourceLoc() MA_SaveSourceLocEx(__FILE__, __LINE__) +void MA_SaveSourceLocEx(const char *file, int line); + + #define MA_PushSize(a, size) MA__PushSize(a, size) + #define MA_PushSizeNonZeroed(a, size) MA__PushSizeNonZeroed(a, size) + #define MA_PushCopy(a, p, size) MA__PushCopy(a, p, size) + #define MA_PushStringCopy(a, p, size) MA__PushStringCopy(a, p, size) + + #define MA_PushArrayNonZeroed(a, T, c) (T *)MA__PushSizeNonZeroed(a, sizeof(T) * (c)) + #define MA_PushStructNonZeroed(a, T) (T *)MA__PushSizeNonZeroed(a, sizeof(T)) + #define MA_PushStruct(a, T) (T *)MA__PushSize(a, sizeof(T)) + #define MA_PushArray(a, T, c) (T *)MA__PushSize(a, sizeof(T) * (c)) + #define MA_PushStructCopy(a, T, p) (T *)MA__PushCopy(a, (p), sizeof(T)) + +// clang-format off +void MA_InitEx(MA_Arena *a, size_t reserve); +void MA_Init(MA_Arena *a); +MA_Arena MA_Create(); +void MA_MakeSureInitialized(MA_Arena *a); +void MA_InitFromBuffer(MA_Arena *arena, void *buffer, size_t size); +MA_Arena MA_MakeFromBuffer(void *buffer, size_t size); +MA_Arena * MA_Bootstrap(void); +MA_Arena MA_PushArena(MA_Arena *arena, size_t size); +MA_Arena * MA_PushArenaP(MA_Arena *arena, size_t size); + +void * MA__PushSizeNonZeroed(MA_Arena *a, size_t size); +void * MA__PushSize(MA_Arena *arena, size_t size); +char * MA__PushStringCopy(MA_Arena *arena, char *p, size_t size); +void * MA__PushCopy(MA_Arena *arena, void *p, size_t size); +MA_Temp MA_BeginTemp(MA_Arena *arena); +void MA_EndTemp(MA_Temp checkpoint); + +void MA_PopToPos(MA_Arena *arena, size_t pos); +void MA_PopSize(MA_Arena *arena, size_t size); +void MA_DeallocateArena(MA_Arena *arena); +void MA_Reset(MA_Arena *arena); + +size_t MA_GetAlignOffset(size_t size, size_t align); +size_t MA_AlignUp(size_t size, size_t align); +size_t MA_AlignDown(size_t size, size_t align); + +bool MA_IsPointerInside(MA_Arena *arena, void *p); +void MA_SetAlignment(MA_Arena *arena, int alignment); +uint8_t * MA_GetTop(MA_Arena *a); + +MV_Memory MV_Reserve(size_t size); +bool MV_Commit(MV_Memory *m, size_t commit); +void MV_Deallocate(MV_Memory *m); +bool MV_DecommitPos(MV_Memory *m, size_t pos); +// clang-format on + +extern THREAD_LOCAL MA_Arena MA_ScratchArenaPool[4]; + #define MA_CheckpointScope(name, InArena) for (MA_Temp name = MA_BeginTemp(InArena); name.arena; (MA_EndTemp(name), name.arena = 0)) + #define MA_ScratchScope(x) for (MA_Temp x = MA_GetScratch(); x.arena; (MA_ReleaseScratch(x), x.arena = 0)) + #define MA_ReleaseScratch MA_EndTemp +MA_Temp MA_GetScratchEx(MA_Arena **conflicts, int conflict_count); +MA_Temp MA_GetScratch(void); +MA_Temp MA_GetScratch1(MA_Arena *conflict); + + #if defined(__cplusplus) +struct MA_Scratch { + MA_Temp checkpoint; + MA_Scratch() { this->checkpoint = MA_GetScratch(); } + MA_Scratch(MA_Temp conflict) { this->checkpoint = MA_GetScratch1(conflict.arena); } + MA_Scratch(MA_Temp c1, MA_Temp c2) { + MA_Arena *conflicts[] = {c1.arena, c2.arena}; + this->checkpoint = MA_GetScratchEx(conflicts, 2); + } + ~MA_Scratch() { MA_EndTemp(checkpoint); } + operator MA_Arena *() { return checkpoint.arena; } + + private: // @Note: Disable copy constructors, cause its error prone + MA_Scratch(MA_Scratch &arena); + MA_Scratch(MA_Scratch &arena, MA_Scratch &a2); +}; + #endif // __cplusplus + + #define MA_IS_POW2(x) (((x) & ((x)-1)) == 0) + #define MA_MIN(x, y) ((x) <= (y) ? (x) : (y)) + #define MA_MAX(x, y) ((x) >= (y) ? (x) : (y)) + #define MA_Lengthof(x) ((int64_t)((sizeof(x) / sizeof((x)[0])))) + + #define MA_CLAMP_TOP(x, max) ((x) >= (max) ? (max) : (x)) + #define MA_CLAMP_BOT(x, min) ((x) <= (min) ? (min) : (x)) + #define MA_CLAMP(x, min, max) ((x) >= (max) ? (max) : (x) <= (min) ? (min) \ + : (x)) + +#endif // MA_HEADER + +#define MA_Assertf(x, ...) IO_Assertf(x, __VA_ARGS__) + #ifndef MA_Assertf + #include + #define MA_Assertf(x, ...) assert(x) +#endif + +#ifndef MA_StaticFunc + #if defined(__GNUC__) || defined(__clang__) + #define MA_StaticFunc __attribute__((unused)) static + #else + #define MA_StaticFunc static + #endif +#endif + +#if defined(MA_USE_ADDRESS_SANITIZER) + #include +#endif + +#if !defined(ASAN_POISON_MEMORY_REGION) + #define MA_ASAN_POISON_MEMORY_REGION(addr, size) ((void)(addr), (void)(size)) + #define MA_ASAN_UNPOISON_MEMORY_REGION(addr, size) ((void)(addr), (void)(size)) +#else + #define MA_ASAN_POISON_MEMORY_REGION(addr, size) ASAN_POISON_MEMORY_REGION(addr, size) + #define MA_ASAN_UNPOISON_MEMORY_REGION(addr, size) ASAN_UNPOISON_MEMORY_REGION(addr, size) +#endif + +THREAD_LOCAL MA_SourceLoc MA_SavedSourceLoc; +void MA_SaveSourceLocEx(const char *file, int line) { + MA_SavedSourceLoc.file = file; + MA_SavedSourceLoc.line = line; +} + +size_t MA_GetAlignOffset(size_t size, size_t align) { + size_t mask = align - 1; + size_t val = size & mask; + if (val) { + val = align - val; + } + return val; +} + +size_t MA_AlignUp(size_t size, size_t align) { + size_t result = size + MA_GetAlignOffset(size, align); + return result; +} + +size_t MA_AlignDown(size_t size, size_t align) { + size += 1; // Make sure when align is 8 doesn't get rounded down to 0 + size_t result = size - (align - MA_GetAlignOffset(size, align)); + return result; +} + +MA_StaticFunc uint8_t *MV__AdvanceCommit(MV_Memory *m, size_t *commit_size, size_t page_size) { + size_t aligned_up_commit = MA_AlignUp(*commit_size, page_size); + size_t to_be_total_commited_size = aligned_up_commit + m->commit; + size_t to_be_total_commited_size_clamped_to_reserve = MA_CLAMP_TOP(to_be_total_commited_size, m->reserve); + size_t adjusted_to_boundary_commit = to_be_total_commited_size_clamped_to_reserve - m->commit; + MA_Assertf(adjusted_to_boundary_commit, "Reached the virtual memory reserved boundary"); + *commit_size = adjusted_to_boundary_commit; + + if (adjusted_to_boundary_commit == 0) { + return 0; + } + uint8_t *result = m->data + m->commit; + return result; +} + +void MA_PopToPos(MA_Arena *arena, size_t pos) { + MA_Assertf(arena->len >= arena->base_len, "Bug: arena->len shouldn't ever be smaller then arena->base_len"); + pos = MA_CLAMP(pos, arena->base_len, arena->len); + size_t size = arena->len - pos; + arena->len = pos; + MA_ASAN_POISON_MEMORY_REGION(arena->memory.data + arena->len, size); +} + +void MA_PopSize(MA_Arena *arena, size_t size) { + MA_PopToPos(arena, arena->len - size); +} + +void MA_DeallocateArena(MA_Arena *arena) { + MV_Deallocate(&arena->memory); +} + +void MA_Reset(MA_Arena *arena) { + MA_PopToPos(arena, 0); +} + +MA_StaticFunc size_t MA__AlignLen(MA_Arena *a) { + size_t align_offset = a->alignment ? MA_GetAlignOffset((uintptr_t)a->memory.data + (uintptr_t)a->len, a->alignment) : 0; + size_t aligned = a->len + align_offset; + return aligned; +} + +void MA_SetAlignment(MA_Arena *arena, int alignment) { + arena->alignment = alignment; +} + +uint8_t *MA_GetTop(MA_Arena *a) { + MA_Assertf(a->memory.data, "Arena needs to be inited, there is no top to get!"); + return a->memory.data + a->len; +} + +void *MA__PushSizeNonZeroed(MA_Arena *a, size_t size) { + size_t align_offset = a->alignment ? MA_GetAlignOffset((uintptr_t)a->memory.data + (uintptr_t)a->len, a->alignment) : 0; + size_t aligned_len = a->len + align_offset; + size_t size_with_alignment = size + align_offset; + + if (a->len + size_with_alignment > a->memory.commit) { + if (a->memory.reserve == 0) { +#if MA_ZERO_IS_INITIALIZATION + MA_Init(a); +#else + MA_Assertf(0, "Pushing on uninitialized arena with zero initialization turned off"); +#endif + } + bool result = MV_Commit(&a->memory, size_with_alignment + MA_COMMIT_ADD_SIZE); + MA_Assertf(result, "%s(%d): Failed to commit memory more memory! reserve: %zu commit: %zu len: %zu size_with_alignment: %zu", MA_SavedSourceLoc.file, MA_SavedSourceLoc.line, a->memory.reserve, a->memory.commit, a->len, size_with_alignment); + (void)result; + } + + uint8_t *result = a->memory.data + aligned_len; + a->len += size_with_alignment; + MA_Assertf(a->len <= a->memory.commit, "%s(%d): Reached commit boundary! reserve: %zu commit: %zu len: %zu base_len: %zu alignment: %d size_with_alignment: %zu", MA_SavedSourceLoc.file, MA_SavedSourceLoc.line, a->memory.reserve, a->memory.commit, a->len, a->base_len, a->alignment, size_with_alignment); + MA_ASAN_UNPOISON_MEMORY_REGION(result, size); + return (void *)result; +} + +void *MA__PushSize(MA_Arena *arena, size_t size) { + void *result = MA__PushSizeNonZeroed(arena, size); + MA_MemoryZero(result, size); + return result; +} + +char *MA__PushStringCopy(MA_Arena *arena, char *p, size_t size) { + char *copy_buffer = (char *)MA__PushSizeNonZeroed(arena, size + 1); + MA_MemoryCopy(copy_buffer, p, size); + copy_buffer[size] = 0; + return copy_buffer; +} + +void *MA__PushCopy(MA_Arena *arena, void *p, size_t size) { + void *copy_buffer = MA__PushSizeNonZeroed(arena, size); + MA_MemoryCopy(copy_buffer, p, size); + return copy_buffer; +} + +MA_Arena MA_PushArena(MA_Arena *arena, size_t size) { + MA_Arena result; + MA_MemoryZero(&result, sizeof(result)); + result.memory.data = MA_PushArrayNonZeroed(arena, uint8_t, size); + result.memory.commit = size; + result.memory.reserve = size; + result.alignment = arena->alignment; + return result; +} + +MA_Arena *MA_PushArenaP(MA_Arena *arena, size_t size) { + MA_Arena *result = MA_PushStruct(arena, MA_Arena); + *result = MA_PushArena(arena, size); + return result; +} + +void MA_InitEx(MA_Arena *a, size_t reserve) { + a->memory = MV_Reserve(reserve); + MA_ASAN_POISON_MEMORY_REGION(a->memory.data, a->memory.reserve); + a->alignment = MA_DEFAULT_ALIGNMENT; +} + +void MA_Init(MA_Arena *a) { + MA_InitEx(a, MA_DEFAULT_RESERVE_SIZE); +} + +void MA_MakeSureInitialized(MA_Arena *a) { + if (a->memory.data == 0) { + MA_Init(a); + } +} + +MA_Arena *MA_Bootstrap(void) { + MA_Arena bootstrap_arena = {0}; + MA_Arena *arena = MA_PushStruct(&bootstrap_arena, MA_Arena); + *arena = bootstrap_arena; + arena->base_len = arena->len; + return arena; +} + +void MA_InitFromBuffer(MA_Arena *arena, void *buffer, size_t size) { + arena->memory.data = (uint8_t *)buffer; + arena->memory.commit = size; + arena->memory.reserve = size; + arena->alignment = MA_DEFAULT_ALIGNMENT; + MA_ASAN_POISON_MEMORY_REGION(arena->memory.data, arena->memory.reserve); +} + +MA_Arena MA_MakeFromBuffer(void *buffer, size_t size) { + MA_Arena arena; + MA_MemoryZero(&arena, sizeof(arena)); + MA_InitFromBuffer(&arena, buffer, size); + return arena; +} + +MA_Arena MA_Create() { + MA_Arena arena = {0}; + MA_Init(&arena); + return arena; +} + +bool MA_IsPointerInside(MA_Arena *arena, void *p) { + uintptr_t pointer = (uintptr_t)p; + uintptr_t start = (uintptr_t)arena->memory.data; + uintptr_t stop = start + (uintptr_t)arena->len; + bool result = pointer >= start && pointer < stop; + return result; +} + +MA_Temp MA_BeginTemp(MA_Arena *arena) { + MA_Temp result; + result.pos = arena->len; + result.arena = arena; + return result; +} + +void MA_EndTemp(MA_Temp checkpoint) { + MA_PopToPos(checkpoint.arena, checkpoint.pos); +} + +THREAD_LOCAL MA_Arena MA_ScratchArenaPool[4]; + +MA_Temp MA_GetScratchEx(MA_Arena **conflicts, int conflict_count) { + MA_Arena *unoccupied = 0; + for (int i = 0; i < MA_Lengthof(MA_ScratchArenaPool); i += 1) { + MA_Arena *from_pool = MA_ScratchArenaPool + i; + unoccupied = from_pool; + for (int conflict_i = 0; conflict_i < conflict_count; conflict_i += 1) { + MA_Arena *from_conflict = conflicts[conflict_i]; + if (from_pool == from_conflict) { + unoccupied = 0; + break; + } + } + + if (unoccupied) { + break; + } + } + + MA_Assertf(unoccupied, "Failed to get free scratch memory, this is a fatal error, this shouldnt happen"); + MA_Temp result = MA_BeginTemp(unoccupied); + return result; +} + +MA_Temp MA_GetScratch(void) { + MA_Temp result = MA_BeginTemp(MA_ScratchArenaPool); + return result; +} + +MA_Temp MA_GetScratch1(MA_Arena *conflict) { + MA_Arena *conflicts[] = {conflict}; + return MA_GetScratchEx(conflicts, 1); +} + +#ifdef _WIN32 + #ifndef NOMINMAX + #define NOMINMAX + #endif + #ifndef WIN32_LEAN_AND_MEAN + #define WIN32_LEAN_AND_MEAN + #endif + #include + +const size_t MV__WIN32_PAGE_SIZE = 4096; + +MV_Memory MV_Reserve(size_t size) { + MV_Memory result; + MA_MemoryZero(&result, sizeof(result)); + size_t adjusted_size = MA_AlignUp(size, MV__WIN32_PAGE_SIZE); + result.data = (uint8_t *)VirtualAlloc(0, adjusted_size, MEM_RESERVE, PAGE_READWRITE); + MA_Assertf(result.data, "Failed to reserve virtual memory"); + result.reserve = adjusted_size; + return result; +} + +bool MV_Commit(MV_Memory *m, size_t commit) { + uint8_t *pointer = MV__AdvanceCommit(m, &commit, MV__WIN32_PAGE_SIZE); + if (pointer) { + void *result = VirtualAlloc(pointer, commit, MEM_COMMIT, PAGE_READWRITE); + MA_Assertf(result, "Failed to commit more memory"); + if (result) { + m->commit += commit; + return true; + } + } + return false; +} + +void MV_Deallocate(MV_Memory *m) { + BOOL result = VirtualFree(m->data, 0, MEM_RELEASE); + MA_Assertf(result != 0, "Failed to release MV_Memory"); +} + +bool MV_DecommitPos(MV_Memory *m, size_t pos) { + size_t aligned = MA_AlignDown(pos, MV__WIN32_PAGE_SIZE); + size_t adjusted_pos = MA_CLAMP_TOP(aligned, m->commit); + size_t size_to_decommit = m->commit - adjusted_pos; + if (size_to_decommit) { + uint8_t *base_address = m->data + adjusted_pos; + BOOL result = VirtualFree(base_address, size_to_decommit, MEM_DECOMMIT); + if (result) { + m->commit -= size_to_decommit; + return true; + } + } + return false; +} + +#elif __unix__ || __linux__ || __APPLE__ + #include + #define MV__UNIX_PAGE_SIZE 4096 +MV_Memory MV_Reserve(size_t size) { + MV_Memory result = {}; + size_t size_aligned = MA_AlignUp(size, MV__UNIX_PAGE_SIZE); + result.data = (uint8_t *)mmap(0, size_aligned, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + MA_Assertf(result.data, "Failed to reserve memory using mmap!!"); + if (result.data) { + result.reserve = size_aligned; + } + return result; +} + +bool MV_Commit(MV_Memory *m, size_t commit) { + uint8_t *pointer = MV__AdvanceCommit(m, &commit, MV__UNIX_PAGE_SIZE); + if (pointer) { + int mprotect_result = mprotect(pointer, commit, PROT_READ | PROT_WRITE); + MA_Assertf(mprotect_result == 0, "Failed to commit more memory using mmap"); + if (mprotect_result == 0) { + m->commit += commit; + return true; + } + } + return false; +} + +void MV_Deallocate(MV_Memory *m) { + int result = munmap(m->data, m->reserve); + MA_Assertf(result == 0, "Failed to release virtual memory using munmap"); +} +#else +MV_Memory MV_Reserve(size_t size) { + MV_Memory result = {0}; + return result; +} + +bool MV_Commit(MV_Memory *m, size_t commit) { + return false; +} + +void MV_Deallocate(MV_Memory *m) { +} + +#endif + +#ifndef FIRST_UTF_HEADER + #define FIRST_UTF_HEADER + #define UTF_HEADER + #include +typedef struct UTF32_Result UTF32_Result; +typedef struct UTF8_Result UTF8_Result; +typedef struct UTF16_Result UTF16_Result; +typedef struct UTF8_Iter UTF8_Iter; + + #ifndef UTF_API + #ifdef __cplusplus + #define UTF_API extern "C" + #else + #define UTF_API + #endif + #endif + +struct UTF32_Result { + uint32_t out_str; + int advance; + int error; +}; + +struct UTF8_Result { + uint8_t out_str[4]; + int len; + int error; +}; + +struct UTF16_Result { + uint16_t out_str[2]; + int len; + int error; +}; + +struct UTF8_Iter { + char *str; + int len; + int utf8_codepoint_byte_size; + int i; + uint32_t item; +}; + +UTF_API UTF32_Result UTF_ConvertUTF16ToUTF32(uint16_t *c, int max_advance); +UTF_API UTF8_Result UTF_ConvertUTF32ToUTF8(uint32_t codepoint); +UTF_API UTF32_Result UTF_ConvertUTF8ToUTF32(char *c, int max_advance); +UTF_API UTF16_Result UTF_ConvertUTF32ToUTF16(uint32_t codepoint); +UTF_API int64_t UTF_CreateCharFromWidechar(char *buffer, int64_t buffer_size, wchar_t *in, int64_t inlen); +UTF_API int64_t UTF_CreateWidecharFromChar(wchar_t *buffer, int64_t buffer_size, char *in, int64_t inlen); +UTF_API void UTF8_Advance(UTF8_Iter *iter); +UTF_API UTF8_Iter UTF8_IterateEx(char *str, int len); +UTF_API UTF8_Iter UTF8_Iterate(char *str); + + #define UTF8_For(name, str, len) for (UTF8_Iter name = UTF8_IterateEx(str, (int)len); name.item; UTF8_Advance(&name)) +#endif + + +#ifndef UTF__MemoryZero + #include + #define UTF__MemoryZero(p, size) memset(p, 0, size) +#endif + +UTF_API UTF32_Result UTF_ConvertUTF16ToUTF32(uint16_t *c, int max_advance) { + UTF32_Result result; + UTF__MemoryZero(&result, sizeof(result)); + if (max_advance >= 1) { + result.advance = 1; + result.out_str = c[0]; + if (c[0] >= 0xD800 && c[0] <= 0xDBFF && c[1] >= 0xDC00 && c[1] <= 0xDFFF) { + if (max_advance >= 2) { + result.out_str = 0x10000; + result.out_str += (uint32_t)(c[0] & 0x03FF) << 10u | (c[1] & 0x03FF); + result.advance = 2; + } + else + result.error = 2; + } + } + else { + result.error = 1; + } + return result; +} + +UTF_API UTF8_Result UTF_ConvertUTF32ToUTF8(uint32_t codepoint) { + UTF8_Result result; + UTF__MemoryZero(&result, sizeof(result)); + + if (codepoint <= 0x7F) { + result.len = 1; + result.out_str[0] = (char)codepoint; + } + else if (codepoint <= 0x7FF) { + result.len = 2; + result.out_str[0] = 0xc0 | (0x1f & (codepoint >> 6)); + result.out_str[1] = 0x80 | (0x3f & codepoint); + } + else if (codepoint <= 0xFFFF) { // 16 bit word + result.len = 3; + result.out_str[0] = 0xe0 | (0xf & (codepoint >> 12)); // 4 bits + result.out_str[1] = 0x80 | (0x3f & (codepoint >> 6)); // 6 bits + result.out_str[2] = 0x80 | (0x3f & codepoint); // 6 bits + } + else if (codepoint <= 0x10FFFF) { // 21 bit word + result.len = 4; + result.out_str[0] = 0xf0 | (0x7 & (codepoint >> 18)); // 3 bits + result.out_str[1] = 0x80 | (0x3f & (codepoint >> 12)); // 6 bits + result.out_str[2] = 0x80 | (0x3f & (codepoint >> 6)); // 6 bits + result.out_str[3] = 0x80 | (0x3f & codepoint); // 6 bits + } + else { + result.error = 1; + } + + return result; +} + +UTF_API UTF32_Result UTF_ConvertUTF8ToUTF32(char *c, int max_advance) { + UTF32_Result result; + UTF__MemoryZero(&result, sizeof(result)); + + if ((c[0] & 0x80) == 0) { // Check if leftmost zero of first byte is unset + if (max_advance >= 1) { + result.out_str = c[0]; + result.advance = 1; + } + else result.error = 1; + } + + else if ((c[0] & 0xe0) == 0xc0) { + if ((c[1] & 0xc0) == 0x80) { // Continuation byte required + if (max_advance >= 2) { + result.out_str = (uint32_t)(c[0] & 0x1f) << 6u | (c[1] & 0x3f); + result.advance = 2; + } + else result.error = 2; + } + else result.error = 2; + } + + else if ((c[0] & 0xf0) == 0xe0) { + if ((c[1] & 0xc0) == 0x80 && (c[2] & 0xc0) == 0x80) { // Two continuation bytes required + if (max_advance >= 3) { + result.out_str = (uint32_t)(c[0] & 0xf) << 12u | (uint32_t)(c[1] & 0x3f) << 6u | (c[2] & 0x3f); + result.advance = 3; + } + else result.error = 3; + } + else result.error = 3; + } + + else if ((c[0] & 0xf8) == 0xf0) { + if ((c[1] & 0xc0) == 0x80 && (c[2] & 0xc0) == 0x80 && (c[3] & 0xc0) == 0x80) { // Three continuation bytes required + if (max_advance >= 4) { + result.out_str = (uint32_t)(c[0] & 0xf) << 18u | (uint32_t)(c[1] & 0x3f) << 12u | (uint32_t)(c[2] & 0x3f) << 6u | (uint32_t)(c[3] & 0x3f); + result.advance = 4; + } + else result.error = 4; + } + else result.error = 4; + } + else result.error = 4; + + return result; +} + +UTF_API UTF16_Result UTF_ConvertUTF32ToUTF16(uint32_t codepoint) { + UTF16_Result result; + UTF__MemoryZero(&result, sizeof(result)); + if (codepoint < 0x10000) { + result.out_str[0] = (uint16_t)codepoint; + result.out_str[1] = 0; + result.len = 1; + } + else if (codepoint <= 0x10FFFF) { + uint32_t code = (codepoint - 0x10000); + result.out_str[0] = (uint16_t)(0xD800 | (code >> 10)); + result.out_str[1] = (uint16_t)(0xDC00 | (code & 0x3FF)); + result.len = 2; + } + else { + result.error = 1; + } + + return result; +} + +#define UTF__HANDLE_DECODE_ERROR(question_mark) \ + { \ + if (outlen < buffer_size - 1) buffer[outlen++] = (question_mark); \ + break; \ + } + +UTF_API int64_t UTF_CreateCharFromWidechar(char *buffer, int64_t buffer_size, wchar_t *in, int64_t inlen) { + int64_t outlen = 0; + for (int64_t i = 0; i < inlen && in[i];) { + UTF32_Result decode = UTF_ConvertUTF16ToUTF32((uint16_t *)(in + i), (int)(inlen - i)); + if (!decode.error) { + i += decode.advance; + UTF8_Result encode = UTF_ConvertUTF32ToUTF8(decode.out_str); + if (!encode.error) { + for (int64_t j = 0; j < encode.len; j++) { + if (outlen < buffer_size - 1) { + buffer[outlen++] = encode.out_str[j]; + } + } + } + else UTF__HANDLE_DECODE_ERROR('?'); + } + else UTF__HANDLE_DECODE_ERROR('?'); + } + + buffer[outlen] = 0; + return outlen; +} + +UTF_API int64_t UTF_CreateWidecharFromChar(wchar_t *buffer, int64_t buffer_size, char *in, int64_t inlen) { + int64_t outlen = 0; + for (int64_t i = 0; i < inlen;) { + UTF32_Result decode = UTF_ConvertUTF8ToUTF32(in + i, (int)(inlen - i)); + if (!decode.error) { + i += decode.advance; + UTF16_Result encode = UTF_ConvertUTF32ToUTF16(decode.out_str); + if (!encode.error) { + for (int64_t j = 0; j < encode.len; j++) { + if (outlen < buffer_size - 1) { + buffer[outlen++] = encode.out_str[j]; + } + } + } + else UTF__HANDLE_DECODE_ERROR(0x003f); + } + else UTF__HANDLE_DECODE_ERROR(0x003f); + } + + buffer[outlen] = 0; + return outlen; +} + +UTF_API void UTF8_Advance(UTF8_Iter *iter) { + iter->i += iter->utf8_codepoint_byte_size; + UTF32_Result r = UTF_ConvertUTF8ToUTF32(iter->str + iter->i, iter->len - iter->i); + if (r.error) { + iter->item = 0; + return; + } + + iter->utf8_codepoint_byte_size = r.advance; + iter->item = r.out_str; +} + +UTF_API UTF8_Iter UTF8_IterateEx(char *str, int len) { + UTF8_Iter result; + UTF__MemoryZero(&result, sizeof(result)); + result.str = str; + result.len = len; + if (len) UTF8_Advance(&result); + return result; +} + +UTF_API UTF8_Iter UTF8_Iterate(char *str) { + int length = 0; + while (str[length]) length += 1; + return UTF8_IterateEx(str, length); +} + + +#ifndef FIRST_S8_STRING + #define FIRST_S8_STRING + #include + #include + #include + + #ifndef S8_API + #define S8_API + #endif + + #ifdef __cplusplus + #define S8_IF_CPP(x) x + #else + #define S8_IF_CPP(x) + #endif + + #ifndef S8_Allocator +struct MA_Arena; + #define S8_Allocator MA_Arena * + #endif + +typedef struct S8_Node S8_Node; +typedef struct S8_List S8_List; +S8_API int64_t S8_Length(char *string); + +#ifndef S8_String +typedef struct S8_String S8_String; +struct S8_String { + char *str; + int64_t len; + #if defined(__cplusplus) + S8_String() = default; + S8_String(char *s) : str(s), len(S8_Length(s)) {} + S8_String(char *s, int64_t l) : str(s), len(l) {} + S8_String(const char *s) : str((char *)s), len(S8_Length((char *)s)) {} + S8_String(const char *s, int64_t l) : str((char *)s), len(l) {} + #if defined(FIRST_UTF_HEADER) + struct Iter { + UTF8_Iter i; + + Iter &operator++() { + UTF8_Advance(&i); + return *this; + } + + friend bool operator!=(const Iter &a, const Iter &b) { return a.i.item != b.i.item; } + UTF8_Iter &operator*() { return i; } + }; + + Iter begin() { return {UTF8_IterateEx(str, (int)len)}; } + Iter end() { return {}; } + #endif // FIRST_UTF_HEADER + #endif // __cplusplus +}; +#endif + +struct S8_Node { + S8_Node *next; + S8_String string; +}; + +struct S8_List { + int64_t node_count; + int64_t char_count; + S8_Node *first; + S8_Node *last; + + #if defined(__cplusplus) + struct Iter { + S8_Node *it; + + Iter &operator++() { + it = it->next; + return *this; + } + + friend bool operator!=(const Iter &a, const Iter &b) { return a.it != b.it; } + S8_String &operator*() { return it->string; } + }; + + Iter begin() { return {first}; } + Iter end() { return {0}; } + #endif +}; + +typedef struct S16_String { + wchar_t *str; + int64_t len; +} S16_String; + +typedef int S8_FindFlag; +enum { + S8_FindFlag_None = 0, + S8_FindFlag_IgnoreCase = 1, + S8_FindFlag_MatchFindLast = 2, +}; + +typedef int S8_SplitFlag; +enum { + S8_SplitFlag_None = 0, + S8_SplitFlag_IgnoreCase = 1, + S8_SplitFlag_SplitInclusive = 2, +}; + +static const bool S8_IgnoreCase = true; + + #if defined(__has_attribute) + #if __has_attribute(format) + #define S8__PrintfFormat(fmt, va) __attribute__((format(printf, fmt, va))) + #endif + #endif + + #ifndef S8__PrintfFormat + #define S8__PrintfFormat(fmt, va) + #endif + + #define S8_Lit(string) S8_Make((char *)string, sizeof(string) - 1) + #define S8_ConstLit(string) \ + { string, sizeof(string) - 1 } + #define S8_Expand(string) (int)(string).len, (string).str + + #define S8_FORMAT(allocator, str, result) \ + va_list args1; \ + va_start(args1, str); \ + S8_String result = S8_FormatV(allocator, str, args1); \ + va_end(args1) + + #define S8_For(it, x) for (S8_Node *it = (x).first; it; it = it->next) + +S8_API char CHAR_ToLowerCase(char a); +S8_API char CHAR_ToUpperCase(char a); +S8_API bool CHAR_IsWhitespace(char w); +S8_API bool CHAR_IsAlphabetic(char a); +S8_API bool CHAR_IsIdent(char a); +S8_API bool CHAR_IsDigit(char a); +S8_API bool CHAR_IsAlphanumeric(char a); +S8_API bool S8_AreEqual(S8_String a, S8_String b, unsigned ignore_case S8_IF_CPP(= false)); +S8_API bool S8_EndsWith(S8_String a, S8_String end, unsigned ignore_case S8_IF_CPP(= false)); +S8_API bool S8_StartsWith(S8_String a, S8_String start, unsigned ignore_case S8_IF_CPP(= false)); +S8_API S8_String S8_Make(char *str, int64_t len); +S8_API S8_String S8_Copy(S8_Allocator allocator, S8_String string); +S8_API S8_String S8_CopyChar(S8_Allocator allocator, char *s); +S8_API S8_String S8_NormalizePath(S8_Allocator allocator, S8_String s); +S8_API void S8_NormalizePathUnsafe(S8_String s); // make sure there is no way string is const etc. +S8_API S8_String S8_Chop(S8_String string, int64_t len); +S8_API S8_String S8_Skip(S8_String string, int64_t len); +S8_API S8_String S8_GetPostfix(S8_String string, int64_t len); +S8_API S8_String S8_GetPrefix(S8_String string, int64_t len); +S8_API S8_String S8_Slice(S8_String string, int64_t first_index, int64_t one_past_last_index); +S8_API S8_String S8_Trim(S8_String string); +S8_API S8_String S8_TrimEnd(S8_String string); +S8_API S8_String S8_ToLowerCase(S8_Allocator allocator, S8_String s); +S8_API S8_String S8_ToUpperCase(S8_Allocator allocator, S8_String s); +S8_API bool S8_Seek(S8_String string, S8_String find, S8_FindFlag flags S8_IF_CPP(= S8_FindFlag_None), int64_t *index_out S8_IF_CPP(= 0)); +S8_API int64_t S8_Find(S8_String string, S8_String find, S8_FindFlag flags S8_IF_CPP(= S8_FindFlag_None)); +S8_API S8_String S8_ChopLastSlash(S8_String s); +S8_API S8_String S8_ChopLastPeriod(S8_String s); +S8_API S8_String S8_SkipToLastSlash(S8_String s); +S8_API S8_String S8_SkipToLastPeriod(S8_String s); +S8_API S8_String S8_GetNameNoExt(S8_String s); +S8_API bool S8_IsPointerInside(S8_String string, char *p); +S8_API S8_String S8_SkipToP(S8_String string, char *p); +S8_API S8_String S8_SkipPast(S8_String string, S8_String a); +S8_API int64_t S8_WideLength(wchar_t *string); +S8_API S8_String S8_MakeFromChar(char *string); +S8_API S8_String S8_MakeEmpty(void); +S8_API S8_List S8_MakeEmptyList(void); +S8_API S8_String S8_FormatV(S8_Allocator allocator, const char *str, va_list args1); +S8_API S8_String S8_Format(S8_Allocator allocator, const char *str, ...) S8__PrintfFormat(2, 3); + +S8_API S8_List S8_Split(S8_Allocator allocator, S8_String string, S8_String find, S8_SplitFlag flags S8_IF_CPP(= S8_SplitFlag_None)); +S8_API S8_String S8_MergeWithSeparator(S8_Allocator allocator, S8_List list, S8_String separator S8_IF_CPP(= S8_Lit(" "))); +S8_API S8_String S8_Merge(S8_Allocator allocator, S8_List list); +S8_API S8_String S8_ReplaceAll(S8_Allocator allocator, S8_String string, S8_String replace, S8_String with, bool ignore_case S8_IF_CPP(= false)); +S8_API S8_List S8_FindAll(S8_Allocator allocator, S8_String string, S8_String find, bool ignore_case S8_IF_CPP(= false)); + +S8_API S8_Node *S8_CreateNode(S8_Allocator allocator, S8_String string); +S8_API void S8_ReplaceNodeString(S8_List *list, S8_Node *node, S8_String new_string); +S8_API void S8_AddExistingNode(S8_List *list, S8_Node *node); +S8_API void S8_AddArray(S8_Allocator allocator, S8_List *list, char **array, int count); +S8_API void S8_AddArrayWithPrefix(S8_Allocator allocator, S8_List *list, char *prefix, char **array, int count); +S8_API S8_List S8_MakeList(S8_Allocator allocator, S8_String a); +S8_API S8_List S8_CopyList(S8_Allocator allocator, S8_List a); +S8_API S8_List S8_ConcatLists(S8_Allocator allocator, S8_List a, S8_List b); +S8_API S8_Node *S8_AddNode(S8_Allocator allocator, S8_List *list, S8_String string); +S8_API S8_Node *S8_Add(S8_Allocator allocator, S8_List *list, S8_String string); +S8_API S8_String S8_AddF(S8_Allocator allocator, S8_List *list, const char *str, ...) S8__PrintfFormat(3, 4); + +S8_API S16_String S8_ToWidecharEx(S8_Allocator allocator, S8_String string); +S8_API wchar_t *S8_ToWidechar(S8_Allocator allocator, S8_String string); +S8_API S8_String S8_FromWidecharEx(S8_Allocator allocator, wchar_t *wstring, int64_t wsize); +S8_API S8_String S8_FromWidechar(S8_Allocator allocator, wchar_t *wstring); + + #if defined(__cplusplus) +inline S8_String operator""_s(const char *str, size_t size) { return {(char *)str, (int64_t)size}; } +inline bool operator==(S8_String a, S8_String b) { return S8_AreEqual(a, b, 0); } +inline bool operator!=(S8_String a, S8_String b) { return !S8_AreEqual(a, b, 0); } + #endif +#endif + +#define S8_ALLOCATE(allocator, size) MA_PushSize(allocator, size) +#define S8_ASSERT(x) IO_Assert(x) +#define S8_MemoryCopy MA_MemoryCopy +#include + +#ifndef S8_VSNPRINTF + #include + #define S8_VSNPRINTF vsnprintf +#endif + +#ifndef S8_ALLOCATE + #include + #define S8_ALLOCATE(allocator, size) malloc(size) +#endif + +#ifndef S8_ASSERT + #include + #define S8_ASSERT(x) assert(x) +#endif + +#ifndef S8_MemoryCopy + #include + #define S8_MemoryCopy(dst, src, s) memcpy(dst, src, s) +#endif + +#ifndef S8_StaticFunc + #if defined(__GNUC__) || defined(__clang__) + #define S8_StaticFunc __attribute__((unused)) static + #else + #define S8_StaticFunc static + #endif +#endif + +S8_StaticFunc int64_t S8__ClampTop(int64_t val, int64_t max) { + if (val > max) val = max; + return val; +} + +S8_API char CHAR_ToLowerCase(char a) { + if (a >= 'A' && a <= 'Z') a += 32; + return a; +} + +S8_API char CHAR_ToUpperCase(char a) { + if (a >= 'a' && a <= 'z') a -= 32; + return a; +} + +S8_API bool CHAR_IsWhitespace(char w) { + bool result = w == '\n' || w == ' ' || w == '\t' || w == '\v' || w == '\r'; + return result; +} + +S8_API bool CHAR_IsAlphabetic(char a) { + bool result = (a >= 'a' && a <= 'z') || (a >= 'A' && a <= 'Z'); + return result; +} + +S8_API bool CHAR_IsIdent(char a) { + bool result = (a >= 'a' && a <= 'z') || (a >= 'A' && a <= 'Z') || a == '_'; + return result; +} + +S8_API bool CHAR_IsDigit(char a) { + bool result = a >= '0' && a <= '9'; + return result; +} + +S8_API bool CHAR_IsAlphanumeric(char a) { + bool result = CHAR_IsDigit(a) || CHAR_IsAlphabetic(a); + return result; +} + +S8_API bool S8_AreEqual(S8_String a, S8_String b, unsigned ignore_case) { + if (a.len != b.len) return false; + for (int64_t i = 0; i < a.len; i++) { + char A = a.str[i]; + char B = b.str[i]; + if (ignore_case) { + A = CHAR_ToLowerCase(A); + B = CHAR_ToLowerCase(B); + } + if (A != B) + return false; + } + return true; +} + +S8_API bool S8_EndsWith(S8_String a, S8_String end, unsigned ignore_case) { + S8_String a_end = S8_GetPostfix(a, end.len); + bool result = S8_AreEqual(end, a_end, ignore_case); + return result; +} + +S8_API bool S8_StartsWith(S8_String a, S8_String start, unsigned ignore_case) { + S8_String a_start = S8_GetPrefix(a, start.len); + bool result = S8_AreEqual(start, a_start, ignore_case); + return result; +} + +S8_API S8_String S8_Make(char *str, int64_t len) { + S8_String result; + result.str = (char *)str; + result.len = len; + return result; +} + +S8_API S8_String S8_Copy(S8_Allocator allocator, S8_String string) { + char *copy = (char *)S8_ALLOCATE(allocator, sizeof(char) * (string.len + 1)); + S8_MemoryCopy(copy, string.str, string.len); + copy[string.len] = 0; + S8_String result = S8_Make(copy, string.len); + return result; +} + +S8_API S8_String S8_CopyChar(S8_Allocator allocator, char *s) { + int64_t len = S8_Length(s); + char *copy = (char *)S8_ALLOCATE(allocator, sizeof(char) * (len + 1)); + S8_MemoryCopy(copy, s, len); + copy[len] = 0; + S8_String result = S8_Make(copy, len); + return result; +} + +S8_API S8_String S8_NormalizePath(S8_Allocator allocator, S8_String s) { + S8_String copy = S8_Copy(allocator, s); + for (int64_t i = 0; i < copy.len; i++) { + if (copy.str[i] == '\\') + copy.str[i] = '/'; + } + return copy; +} + +S8_API void S8_NormalizePathUnsafe(S8_String s) { + for (int64_t i = 0; i < s.len; i++) { + if (s.str[i] == '\\') + s.str[i] = '/'; + } +} + +S8_API S8_String S8_Chop(S8_String string, int64_t len) { + len = S8__ClampTop(len, string.len); + S8_String result = S8_Make(string.str, string.len - len); + return result; +} + +S8_API S8_String S8_Skip(S8_String string, int64_t len) { + len = S8__ClampTop(len, string.len); + int64_t remain = string.len - len; + S8_String result = S8_Make(string.str + len, remain); + return result; +} + +S8_API bool S8_IsPointerInside(S8_String string, char *p) { + uintptr_t pointer = (uintptr_t)p; + uintptr_t start = (uintptr_t)string.str; + uintptr_t stop = start + (uintptr_t)string.len; + bool result = pointer >= start && pointer < stop; + return result; +} + +S8_API S8_String S8_SkipToP(S8_String string, char *p) { + if (S8_IsPointerInside(string, p)) { + S8_String result = S8_Make(p, p - string.str); + return result; + } + return string; +} + +S8_API S8_String S8_SkipPast(S8_String string, S8_String a) { + if (S8_IsPointerInside(string, a.str)) { + S8_String on_p = S8_Make(a.str, a.str - string.str); + S8_String result = S8_Skip(on_p, a.len); + return result; + } + return string; +} + +S8_API S8_String S8_GetPostfix(S8_String string, int64_t len) { + len = S8__ClampTop(len, string.len); + int64_t remain_len = string.len - len; + S8_String result = S8_Make(string.str + remain_len, len); + return result; +} + +S8_API S8_String S8_GetPrefix(S8_String string, int64_t len) { + len = S8__ClampTop(len, string.len); + S8_String result = S8_Make(string.str, len); + return result; +} + +S8_API S8_String S8_GetNameNoExt(S8_String s) { + return S8_SkipToLastSlash(S8_ChopLastPeriod(s)); +} + +S8_API S8_String S8_Slice(S8_String string, int64_t first_index, int64_t one_past_last_index) { + if (one_past_last_index < 0) one_past_last_index = string.len + one_past_last_index + 1; + if (first_index < 0) first_index = string.len + first_index; + S8_ASSERT(first_index < one_past_last_index && "S8_Slice, first_index is bigger then one_past_last_index"); + S8_ASSERT(string.len > 0 && "Slicing string of length 0! Might be an error!"); + S8_String result = string; + if (string.len > 0) { + if (one_past_last_index > first_index) { + first_index = S8__ClampTop(first_index, string.len - 1); + one_past_last_index = S8__ClampTop(one_past_last_index, string.len); + result.str += first_index; + result.len = one_past_last_index - first_index; + } + else { + result.len = 0; + } + } + return result; +} + +S8_API S8_String S8_Trim(S8_String string) { + if (string.len == 0) + return string; + + int64_t whitespace_begin = 0; + for (; whitespace_begin < string.len; whitespace_begin++) { + if (!CHAR_IsWhitespace(string.str[whitespace_begin])) { + break; + } + } + + int64_t whitespace_end = string.len; + for (; whitespace_end != whitespace_begin; whitespace_end--) { + if (!CHAR_IsWhitespace(string.str[whitespace_end - 1])) { + break; + } + } + + if (whitespace_begin == whitespace_end) { + string.len = 0; + } + else { + string = S8_Slice(string, whitespace_begin, whitespace_end); + } + + return string; +} + +S8_API S8_String S8_TrimEnd(S8_String string) { + int64_t whitespace_end = string.len; + for (; whitespace_end != 0; whitespace_end--) { + if (!CHAR_IsWhitespace(string.str[whitespace_end - 1])) { + break; + } + } + + S8_String result = S8_GetPrefix(string, whitespace_end); + return result; +} + +S8_API S8_String S8_ToLowerCase(S8_Allocator allocator, S8_String s) { + S8_String copy = S8_Copy(allocator, s); + for (int64_t i = 0; i < copy.len; i++) { + copy.str[i] = CHAR_ToLowerCase(copy.str[i]); + } + return copy; +} + +S8_API S8_String S8_ToUpperCase(S8_Allocator allocator, S8_String s) { + S8_String copy = S8_Copy(allocator, s); + for (int64_t i = 0; i < copy.len; i++) { + copy.str[i] = CHAR_ToUpperCase(copy.str[i]); + } + return copy; +} + +S8_API bool S8_Seek(S8_String string, S8_String find, S8_FindFlag flags, int64_t *index_out) { + bool ignore_case = flags & S8_FindFlag_IgnoreCase ? true : false; + bool result = false; + if (flags & S8_FindFlag_MatchFindLast) { + for (int64_t i = string.len; i != 0; i--) { + int64_t index = i - 1; + S8_String substring = S8_Slice(string, index, index + find.len); + if (S8_AreEqual(substring, find, ignore_case)) { + if (index_out) + *index_out = index; + result = true; + break; + } + } + } + else { + for (int64_t i = 0; i < string.len; i++) { + S8_String substring = S8_Slice(string, i, i + find.len); + if (S8_AreEqual(substring, find, ignore_case)) { + if (index_out) + *index_out = i; + result = true; + break; + } + } + } + + return result; +} + +S8_API int64_t S8_Find(S8_String string, S8_String find, S8_FindFlag flag) { + int64_t result = -1; + S8_Seek(string, find, flag, &result); + return result; +} + +S8_API S8_List S8_Split(S8_Allocator allocator, S8_String string, S8_String find, S8_SplitFlag flags) { + S8_List result = S8_MakeEmptyList(); + int64_t index = 0; + + S8_FindFlag find_flag = flags & S8_SplitFlag_IgnoreCase ? S8_FindFlag_IgnoreCase : S8_FindFlag_None; + while (S8_Seek(string, find, find_flag, &index)) { + S8_String before_match = S8_Make(string.str, index); + S8_AddNode(allocator, &result, before_match); + if (flags & S8_SplitFlag_SplitInclusive) { + S8_String match = S8_Make(string.str + index, find.len); + S8_AddNode(allocator, &result, match); + } + string = S8_Skip(string, index + find.len); + } + if (string.len) S8_AddNode(allocator, &result, string); + return result; +} + +S8_API S8_String S8_MergeWithSeparator(S8_Allocator allocator, S8_List list, S8_String separator) { + if (list.node_count == 0) return S8_MakeEmpty(); + if (list.char_count == 0) return S8_MakeEmpty(); + + int64_t base_size = (list.char_count + 1); + int64_t sep_size = (list.node_count - 1) * separator.len; + int64_t size = base_size + sep_size; + char *buff = (char *)S8_ALLOCATE(allocator, sizeof(char) * (size + 1)); + S8_String string = S8_Make(buff, 0); + for (S8_Node *it = list.first; it; it = it->next) { + S8_ASSERT(string.len + it->string.len <= size); + S8_MemoryCopy(string.str + string.len, it->string.str, it->string.len); + string.len += it->string.len; + if (it != list.last) { + S8_MemoryCopy(string.str + string.len, separator.str, separator.len); + string.len += separator.len; + } + } + S8_ASSERT(string.len == size - 1); + string.str[size] = 0; + return string; +} + +S8_API S8_String S8_Merge(S8_Allocator allocator, S8_List list) { + return S8_MergeWithSeparator(allocator, list, S8_Lit("")); +} + +S8_API S8_String S8_ReplaceAll(S8_Allocator allocator, S8_String string, S8_String replace, S8_String with, bool ignore_case) { + S8_SplitFlag split_flag = ignore_case ? S8_SplitFlag_IgnoreCase : S8_SplitFlag_None; + S8_List list = S8_Split(allocator, string, replace, split_flag | S8_SplitFlag_SplitInclusive); + for (S8_Node *it = list.first; it; it = it->next) { + if (S8_AreEqual(it->string, replace, ignore_case)) { + S8_ReplaceNodeString(&list, it, with); + } + } + S8_String result = S8_Merge(allocator, list); + return result; +} + +S8_API S8_List S8_FindAll(S8_Allocator allocator, S8_String string, S8_String find, bool ignore_case) { // @untested + S8_List result = S8_MakeEmptyList(); + int64_t index = 0; + + S8_FindFlag find_flag = ignore_case ? S8_FindFlag_IgnoreCase : 0; + while (S8_Seek(string, find, find_flag, &index)) { + S8_String match = S8_Make(string.str + index, find.len); + S8_AddNode(allocator, &result, match); + string = S8_Skip(string, index + find.len); + } + return result; +} + +S8_API S8_String S8_ChopLastSlash(S8_String s) { + S8_String result = s; + S8_Seek(s, S8_Lit("/"), S8_FindFlag_MatchFindLast, &result.len); + return result; +} + +S8_API S8_String S8_ChopLastPeriod(S8_String s) { + S8_String result = s; + S8_Seek(s, S8_Lit("."), S8_FindFlag_MatchFindLast, &result.len); + return result; +} + +S8_API S8_String S8_SkipToLastSlash(S8_String s) { + int64_t pos; + S8_String result = s; + if (S8_Seek(s, S8_Lit("/"), S8_FindFlag_MatchFindLast, &pos)) { + result = S8_Skip(result, pos + 1); + } + return result; +} + +S8_API S8_String S8_SkipToLastPeriod(S8_String s) { + int64_t pos; + S8_String result = s; + if (S8_Seek(s, S8_Lit("."), S8_FindFlag_MatchFindLast, &pos)) { + result = S8_Skip(result, pos + 1); + } + return result; +} + +S8_API int64_t S8_Length(char *string) { + int64_t len = 0; + while (*string++ != 0) + len++; + return len; +} + +S8_API int64_t S8_WideLength(wchar_t *string) { + int64_t len = 0; + while (*string++ != 0) + len++; + return len; +} + +S8_API S8_String S8_MakeFromChar(char *string) { + S8_String result; + result.str = (char *)string; + result.len = S8_Length(string); + return result; +} + +S8_API S8_String S8_MakeEmpty(void) { + return S8_Make(0, 0); +} + +S8_API bool strings_equal(char *a, char *b) { + S8_String as = S8_MakeFromChar(a); + S8_String bs = S8_MakeFromChar(b); + bool result = S8_AreEqual(as, bs, false); + return result; +} + +S8_API S8_List S8_MakeEmptyList(void) { + S8_List result; + result.first = 0; + result.last = 0; + result.char_count = 0; + result.node_count = 0; + return result; +} + +S8_API S8_String S8_FormatV(S8_Allocator allocator, const char *str, va_list args1) { + va_list args2; + va_copy(args2, args1); + int64_t len = S8_VSNPRINTF(0, 0, str, args2); + va_end(args2); + + char *result = (char *)S8_ALLOCATE(allocator, sizeof(char) * (len + 1)); + S8_VSNPRINTF(result, (int)(len + 1), str, args1); + S8_String res = S8_Make(result, len); + return res; +} + +S8_API S8_String S8_Format(S8_Allocator allocator, const char *str, ...) { + S8_FORMAT(allocator, str, result); + return result; +} + +S8_API S8_Node *S8_CreateNode(S8_Allocator allocator, S8_String string) { + S8_Node *result = (S8_Node *)S8_ALLOCATE(allocator, sizeof(S8_Node)); + result->string = string; + result->next = 0; + return result; +} + +S8_API void S8_ReplaceNodeString(S8_List *list, S8_Node *node, S8_String new_string) { + list->char_count -= node->string.len; + list->char_count += new_string.len; + node->string = new_string; +} + +S8_API void S8_AddExistingNode(S8_List *list, S8_Node *node) { + if (list->first) { + list->last->next = node; + list->last = list->last->next; + } + else { + list->first = list->last = node; + } + list->node_count += 1; + list->char_count += node->string.len; +} + +S8_API void S8_AddArray(S8_Allocator allocator, S8_List *list, char **array, int count) { + for (int i = 0; i < count; i += 1) { + S8_String s = S8_MakeFromChar(array[i]); + S8_AddNode(allocator, list, s); + } +} + +S8_API void S8_AddArrayWithPrefix(S8_Allocator allocator, S8_List *list, char *prefix, char **array, int count) { + for (int i = 0; i < count; i += 1) { + S8_AddF(allocator, list, "%s%s", prefix, array[i]); + } +} + +S8_API S8_List S8_MakeList(S8_Allocator allocator, S8_String a) { + S8_List result = S8_MakeEmptyList(); + S8_AddNode(allocator, &result, a); + return result; +} + +S8_API S8_List S8_CopyList(S8_Allocator allocator, S8_List a) { + S8_List result = S8_MakeEmptyList(); + for (S8_Node *it = a.first; it; it = it->next) S8_AddNode(allocator, &result, it->string); + return result; +} + +S8_API S8_List S8_ConcatLists(S8_Allocator allocator, S8_List a, S8_List b) { + S8_List result = S8_MakeEmptyList(); + for (S8_Node *it = a.first; it; it = it->next) S8_AddNode(allocator, &result, it->string); + for (S8_Node *it = b.first; it; it = it->next) S8_AddNode(allocator, &result, it->string); + return result; +} + +S8_API S8_Node *S8_AddNode(S8_Allocator allocator, S8_List *list, S8_String string) { + S8_Node *node = S8_CreateNode(allocator, string); + S8_AddExistingNode(list, node); + return node; +} + +S8_API S8_Node *S8_Add(S8_Allocator allocator, S8_List *list, S8_String string) { + S8_String copy = S8_Copy(allocator, string); + S8_Node *node = S8_CreateNode(allocator, copy); + S8_AddExistingNode(list, node); + return node; +} + +S8_API S8_String S8_AddF(S8_Allocator allocator, S8_List *list, const char *str, ...) { + S8_FORMAT(allocator, str, result); + S8_AddNode(allocator, list, result); + return result; +} + +#ifdef FIRST_UTF_HEADER + +S8_API S16_String S8_ToWidecharEx(S8_Allocator allocator, S8_String string) { + S8_ASSERT(sizeof(wchar_t) == 2); + wchar_t *buffer = (wchar_t *)S8_ALLOCATE(allocator, sizeof(wchar_t) * (string.len + 1)); + int64_t size = UTF_CreateWidecharFromChar(buffer, string.len + 1, string.str, string.len); + S16_String result = {buffer, size}; + return result; +} + +S8_API wchar_t *S8_ToWidechar(S8_Allocator allocator, S8_String string) { + S16_String result = S8_ToWidecharEx(allocator, string); + return result.str; +} + +S8_API S8_String S8_FromWidecharEx(S8_Allocator allocator, wchar_t *wstring, int64_t wsize) { + S8_ASSERT(sizeof(wchar_t) == 2); + + int64_t buffer_size = (wsize + 1) * 2; + char *buffer = (char *)S8_ALLOCATE(allocator, buffer_size); + int64_t size = UTF_CreateCharFromWidechar(buffer, buffer_size, wstring, wsize); + S8_String result = S8_Make(buffer, size); + + S8_ASSERT(size < buffer_size); + return result; +} + +S8_API S8_String S8_FromWidechar(S8_Allocator allocator, wchar_t *wstring) { + int64_t size = S8_WideLength(wstring); + S8_String result = S8_FromWidecharEx(allocator, wstring, size); + return result; +} + +#endif + +#ifndef FIRST_HASH_HEADER + #define FIRST_HASH_HEADER + #include + + #ifndef HASH_API_FUNCTION + #ifdef __cplusplus + #define HASH_API_FUNCTION extern "C" + #else + #define HASH_API_FUNCTION + #endif + #endif + +// FNV HASH (1a?) +HASH_API_FUNCTION uint64_t HashBytes(void *data, uint64_t size) { + uint8_t *data8 = (uint8_t *)data; + uint64_t hash = (uint64_t)14695981039346656037ULL; + for (uint64_t i = 0; i < size; i++) { + hash = hash ^ (uint64_t)(data8[i]); + hash = hash * (uint64_t)1099511628211ULL; + } + return hash; +} + +HASH_API_FUNCTION uint64_t HashMix(uint64_t x, uint64_t y) { + x ^= y; + x *= 0xff51afd7ed558ccd; + x ^= x >> 32; + return x; +} + #define WRAP_AROUND_POWER_OF_2(x, pow2) (((x) & ((pow2)-1llu))) +#endif + +#ifndef FIRST_LL_HEADER + #define FIRST_LL_HEADER + #define SLL_QUEUE_ADD_MOD(f, l, n, next) \ + do { \ + (n)->next = 0; \ + if ((f) == 0) { \ + (f) = (l) = (n); \ + } else { \ + (l) = (l)->next = (n); \ + } \ + } while (0) + #define SLL_QUEUE_ADD(f, l, n) SLL_QUEUE_ADD_MOD(f, l, n, next) + + #define SLL_QUEUE_POP_FIRST_MOD(f, l, next) \ + do { \ + if ((f) == (l)) { \ + (f) = (l) = 0; \ + } else { \ + (f) = (f)->next; \ + } \ + } while (0) + #define SLL_QUEUE_POP_FIRST(f, l) SLL_QUEUE_POP_FIRST_MOD(f, l, next) + + #define SLL_STACK_ADD_MOD(stack_base, new_stack_base, next) \ + do { \ + (new_stack_base)->next = (stack_base); \ + (stack_base) = (new_stack_base); \ + } while (0) + #define SLL_STACK_ADD(stack_base, new_stack_base) \ + SLL_STACK_ADD_MOD(stack_base, new_stack_base, next) + + #define SLL_STACK_POP_AND_STORE(stack_base, out_node) \ + do { \ + if (stack_base) { \ + (out_node) = (stack_base); \ + (stack_base) = (stack_base)->next; \ + (out_node)->next = 0; \ + } \ + } while (0) + + #define DLL_QUEUE_ADD_MOD(f, l, node, next, prev) \ + do { \ + if ((f) == 0) { \ + (f) = (l) = (node); \ + (node)->prev = 0; \ + (node)->next = 0; \ + } else { \ + (l)->next = (node); \ + (node)->prev = (l); \ + (node)->next = 0; \ + (l) = (node); \ + } \ + } while (0) + #define DLL_QUEUE_ADD(f, l, node) DLL_QUEUE_ADD_MOD(f, l, node, next, prev) + #define DLL_QUEUE_ADD_FRONT(f, l, node) DLL_QUEUE_ADD_MOD(l, f, node, prev, next) + #define DLL_QUEUE_REMOVE_MOD(first, last, node, next, prev) \ + do { \ + if ((first) == (last)) { \ + (first) = (last) = 0; \ + } else if ((last) == (node)) { \ + (last) = (last)->prev; \ + (last)->next = 0; \ + } else if ((first) == (node)) { \ + (first) = (first)->next; \ + (first)->prev = 0; \ + } else { \ + (node)->prev->next = (node)->next; \ + (node)->next->prev = (node)->prev; \ + } \ + if (node) { \ + (node)->prev = 0; \ + (node)->next = 0; \ + } \ + } while (0) + #define DLL_QUEUE_REMOVE(first, last, node) DLL_QUEUE_REMOVE_MOD(first, last, node, next, prev) + + #define DLL_STACK_ADD_MOD(first, node, next, prev) \ + do { \ + (node)->next = (first); \ + if ((first)) \ + (first)->prev = (node); \ + (first) = (node); \ + (node)->prev = 0; \ + } while (0) + #define DLL_STACK_ADD(first, node) DLL_STACK_ADD_MOD(first, node, next, prev) + #define DLL_STACK_REMOVE_MOD(first, node, next, prev) \ + do { \ + if ((node) == (first)) { \ + (first) = (first)->next; \ + if ((first)) \ + (first)->prev = 0; \ + } else { \ + (node)->prev->next = (node)->next; \ + if ((node)->next) \ + (node)->next->prev = (node)->prev; \ + } \ + if (node) { \ + (node)->prev = 0; \ + (node)->next = 0; \ + } \ + } while (0) + #define DLL_STACK_REMOVE(first, node) DLL_STACK_REMOVE_MOD(first, node, next, prev) + + #define DLL_INSERT_NEXT_MOD(base, new, next, prev) \ + do { \ + if ((base) == 0) { \ + (base) = (new); \ + (new)->next = 0; \ + (new)->prev = 0; \ + } else { \ + (new)->next = (base)->next; \ + (base)->next = (new); \ + (new)->prev = (base); \ + if ((new)->next) (new)->next->prev = (new); \ + } \ + } while (0) + #define DLL_INSERT_NEXT(base, new) DLL_INSERT_NEXT_MOD(base, new, next, prev) + #define DLL_INSERT_PREV(base, new) DLL_INSERT_NEXT_MOD(base, new, next, prev) +#endif + +// Quick and dirty filesystem operations + +#ifndef OS_API + #define OS_API +#endif + +typedef enum OS_Result { + OS_SUCCESS, + OS_ALREADY_EXISTS, + OS_PATH_NOT_FOUND, + OS_FAILURE, +} OS_Result; + +enum { + OS_NO_FLAGS = 0, + OS_RECURSIVE = 1, + OS_RELATIVE_PATHS = 2, +}; + +typedef struct OS_Date OS_Date; +struct OS_Date { + uint32_t year; + uint32_t month; + uint32_t day; + uint32_t hour; + uint32_t minute; + uint32_t second; +}; + +typedef struct OS_FileIter OS_FileIter; +struct OS_FileIter { + bool is_valid; + bool is_directory; + S8_String absolute_path; + S8_String relative_path; + S8_String filename; + + S8_String path; + MA_Arena *arena; + union { + struct OS_Win32_FileIter *w32; + void *dir; + }; +}; + +const bool os_copy_overwrite = true; +const bool os_copy_dont_overwrite = false; + +#ifdef _WIN32 + #ifndef NOMINMAX + #define NOMINMAX + #endif + #ifndef WIN32_LEAN_AND_MEAN + #define WIN32_LEAN_AND_MEAN + #endif + #include + #include + #include + +OS_API bool OS_EnableTerminalColors(void) { + // Enable color terminal output + { + // Set output mode to handle virtual terminal sequences + HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); + if (hOut != INVALID_HANDLE_VALUE) { + DWORD dwMode = 0; + if (GetConsoleMode(hOut, &dwMode)) { + dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; + if (SetConsoleMode(hOut, dwMode)) { + return true; + } else { + IO_Printf("Failed to enable colored terminal output C\n"); + } + } else { + IO_Printf("Failed to enable colored terminal output B\n"); + } + } else { + IO_Printf("Failed to enable colored terminal output A\n"); + } + } + return false; +} + +OS_API bool OS_IsAbsolute(S8_String path) { + bool result = path.len > 3 && CHAR_IsAlphabetic(path.str[0]) && path.str[1] == ':' && path.str[2] == '/'; + return result; +} + +OS_API S8_String OS_GetExePath(MA_Arena *arena) { + wchar_t wbuffer[1024]; + DWORD wsize = GetModuleFileNameW(0, wbuffer, MA_Lengthof(wbuffer)); + IO_Assert(wsize != 0); + + S8_String path = S8_FromWidecharEx(arena, wbuffer, wsize); + S8_NormalizePathUnsafe(path); + return path; +} + +OS_API S8_String OS_GetExeDir(MA_Arena *arena) { + MA_Temp scratch = MA_GetScratch(); + S8_String path = OS_GetExePath(scratch.arena); + path = S8_ChopLastSlash(path); + path = S8_Copy(arena, path); + MA_ReleaseScratch(scratch); + return path; +} + +OS_API S8_String os_cwd(MA_Arena *arena) { + wchar_t wbuffer[1024]; + DWORD wsize = GetCurrentDirectoryW(MA_Lengthof(wbuffer), wbuffer); + IO_Assert(wsize != 0); + IO_Assert(wsize < 1022); + wbuffer[wsize++] = '/'; + wbuffer[wsize] = 0; + + S8_String path = S8_FromWidecharEx(arena, wbuffer, wsize); + S8_NormalizePathUnsafe(path); + return path; +} + +OS_API void os_set_working_dir(char *path) { + IO_Printf("cd %s\n", path); + wchar_t wpath[1024]; + UTF_CreateWidecharFromChar(wpath, MA_Lengthof(wpath), path, S8_Length(path)); + SetCurrentDirectoryW(wpath); +} + +OS_API S8_String OS_GetAbsolutePath(MA_Arena *arena, S8_String relative) { + wchar_t wpath[1024]; + UTF_CreateWidecharFromChar(wpath, MA_Lengthof(wpath), relative.str, relative.len); + wchar_t wpath_abs[1024]; + DWORD written = GetFullPathNameW((wchar_t *)wpath, MA_Lengthof(wpath_abs), wpath_abs, 0); + if (written == 0) + return S8_MakeEmpty(); + S8_String path = S8_FromWidecharEx(arena, wpath_abs, written); + S8_NormalizePathUnsafe(path); + return path; +} + +OS_API bool OS_FileExists(S8_String path) { + wchar_t wbuff[1024]; + UTF_CreateWidecharFromChar(wbuff, MA_Lengthof(wbuff), path.str, path.len); + DWORD attribs = GetFileAttributesW(wbuff); + bool result = attribs == INVALID_FILE_ATTRIBUTES ? false : true; + return result; +} + +OS_API bool OS_IsDir(S8_String path) { + wchar_t wbuff[1024]; + UTF_CreateWidecharFromChar(wbuff, MA_Lengthof(wbuff), path.str, path.len); + DWORD dwAttrib = GetFileAttributesW(wbuff); + return dwAttrib != INVALID_FILE_ATTRIBUTES && (dwAttrib & FILE_ATTRIBUTE_DIRECTORY); +} + +OS_API bool OS_IsFile(S8_String path) { + wchar_t wbuff[1024]; + UTF_CreateWidecharFromChar(wbuff, MA_Lengthof(wbuff), path.str, path.len); + DWORD dwAttrib = GetFileAttributesW(wbuff); + bool is_file = (dwAttrib & FILE_ATTRIBUTE_DIRECTORY) == 0; + return dwAttrib != INVALID_FILE_ATTRIBUTES && is_file; +} + +OS_API double OS_GetTime(void) { + static int64_t counts_per_second; + if (counts_per_second == 0) { + LARGE_INTEGER freq; + QueryPerformanceFrequency(&freq); + counts_per_second = freq.QuadPart; + } + + LARGE_INTEGER time; + QueryPerformanceCounter(&time); + double result = (double)time.QuadPart / (double)counts_per_second; + return result; +} + +/* +User needs to copy particular filename to keep it. + +for (OS_FileIter it = OS_IterateFiles(it); OS_IsValid(iter); OS_Advance(it)) { +} + +*/ + +typedef struct OS_Win32_FileIter { + HANDLE handle; + WIN32_FIND_DATAW data; +} OS_Win32_FileIter; + +OS_API bool OS_IsValid(OS_FileIter it) { + return it.is_valid; +} + +OS_API void OS_Advance(OS_FileIter *it) { + while (FindNextFileW(it->w32->handle, &it->w32->data) != 0) { + WIN32_FIND_DATAW *data = &it->w32->data; + + // Skip '.' and '..' + if (data->cFileName[0] == '.' && data->cFileName[1] == '.' && data->cFileName[2] == 0) continue; + if (data->cFileName[0] == '.' && data->cFileName[1] == 0) continue; + + it->is_directory = data->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY; + it->filename = S8_FromWidecharEx(it->arena, data->cFileName, S8_WideLength(data->cFileName)); + const char *is_dir = it->is_directory ? "/" : ""; + const char *separator = it->path.str[it->path.len - 1] == '/' ? "" : "/"; + it->relative_path = S8_Format(it->arena, "%.*s%s%.*s%s", S8_Expand(it->path), separator, S8_Expand(it->filename), is_dir); + it->absolute_path = OS_GetAbsolutePath(it->arena, it->relative_path); + it->is_valid = true; + + if (it->is_directory) { + IO_Assert(it->relative_path.str[it->relative_path.len - 1] == '/'); + IO_Assert(it->absolute_path.str[it->absolute_path.len - 1] == '/'); + } + return; + } + + it->is_valid = false; + DWORD error = GetLastError(); + IO_Assert(error == ERROR_NO_MORE_FILES); + FindClose(it->w32->handle); +} + +OS_API OS_FileIter OS_IterateFiles(MA_Arena *scratch_arena, S8_String path) { + OS_FileIter it = {0}; + it.arena = scratch_arena; + it.path = path; + + S8_String modified_path = S8_Format(it.arena, "%.*s\\*", S8_Expand(path)); + wchar_t *wbuff = MA_PushArray(it.arena, wchar_t, modified_path.len + 1); + int64_t wsize = UTF_CreateWidecharFromChar(wbuff, modified_path.len + 1, modified_path.str, modified_path.len); + IO_Assert(wsize); + + it.w32 = MA_PushStruct(it.arena, OS_Win32_FileIter); + it.w32->handle = FindFirstFileW(wbuff, &it.w32->data); + if (it.w32->handle == INVALID_HANDLE_VALUE) { + it.is_valid = false; + return it; + } + + IO_Assert(it.w32->data.cFileName[0] == '.' && it.w32->data.cFileName[1] == 0); + OS_Advance(&it); + return it; +} + +OS_API OS_Result os_make_dir(char *path) { + IO_Printf("mkdir %s\n", path); + wchar_t wpath[1024]; + UTF_CreateWidecharFromChar(wpath, MA_Lengthof(wpath), path, S8_Length(path)); + BOOL success = CreateDirectoryW(wpath, NULL); + OS_Result result = OS_SUCCESS; + if (success == 0) { + DWORD error = GetLastError(); + if (error == ERROR_ALREADY_EXISTS) { + result = OS_ALREADY_EXISTS; + } else if (error == ERROR_PATH_NOT_FOUND) { + result = OS_PATH_NOT_FOUND; + } else { + IO_Assert(0); + } + } + return result; +} + +OS_API OS_Result os_copy(char *from, char *to, bool overwrite) { + const char *ow = overwrite ? "-n" : ""; + IO_Printf("cp %s %s %s\n", ow, from, to); + + wchar_t wfrom[1024]; + UTF_CreateWidecharFromChar(wfrom, MA_Lengthof(wfrom), from, S8_Length(from)); + + wchar_t wto[1024]; + UTF_CreateWidecharFromChar(wto, MA_Lengthof(wto), to, S8_Length(to)); + + BOOL fail_if_exists = !overwrite; + BOOL success = CopyFileW(wfrom, wto, fail_if_exists); + + OS_Result result = OS_SUCCESS; + if (success == FALSE) + result = OS_FAILURE; + return result; +} + +OS_API OS_Result os_delete_file(S8_String path) { + IO_Printf("rm %.*s\n", S8_Expand(path)); + wchar_t wpath[1024]; + UTF_CreateWidecharFromChar(wpath, MA_Lengthof(wpath), path.str, path.len); + BOOL success = DeleteFileW(wpath); + OS_Result result = OS_SUCCESS; + if (success == 0) + result = OS_PATH_NOT_FOUND; + return result; +} + +OS_API OS_Result OS_DeleteDir(S8_String path, unsigned flags) { + IO_Todo(); + return OS_FAILURE; + #if 0 + if (flags & OS_RECURSIVE) { + MA_Temp scratch = MA_GetScratch(); + S8_List list = OS_ListDir(scratch.arena, path, OS_RECURSIVE); + S8_Node *dirs_to_remove = 0; + for (S8_Node *it = list.first; it; it = it->next) { + if (!S8_EndsWith(it->string, S8_Lit("/"), S8_IgnoreCase)) { + os_delete_file(it->string); + } + else { + S8_Node *node = S8_CreateNode(scratch.arena, it->string); + SLL_STACK_ADD(dirs_to_remove, node); + } + } + for (S8_Node *it = dirs_to_remove; it; it = it->next) { + OS_DeleteDir(it->string, OS_NO_FLAGS); + } + OS_Result result = OS_DeleteDir(path, OS_NO_FLAGS); + MA_ReleaseScratch(scratch); + return result; + } + else { + wchar_t wpath[1024]; + UTF_CreateWidecharFromChar(wpath, MA_Lengthof(wpath), path.str, path.len); + BOOL success = RemoveDirectoryW(wpath); + OS_Result result = OS_SUCCESS; + if (success == 0) + result = OS_PATH_NOT_FOUND; + return result; + } + #endif +} + +static OS_Result OS__WriteFile(S8_String path, S8_String data, bool append) { + wchar_t wpath[1024]; + UTF_CreateWidecharFromChar(wpath, MA_Lengthof(wpath), path.str, path.len); + OS_Result result = OS_FAILURE; + + DWORD access = GENERIC_WRITE; + DWORD creation_disposition = CREATE_ALWAYS; + if (append) { + access = FILE_APPEND_DATA; + creation_disposition = OPEN_ALWAYS; + } + + HANDLE handle = CreateFileW(wpath, access, 0, NULL, creation_disposition, FILE_ATTRIBUTE_NORMAL, NULL); + if (handle != INVALID_HANDLE_VALUE) { + DWORD bytes_written = 0; + IO_Assert(data.len == (DWORD)data.len); // @Todo: can only read 32 byte size files? + BOOL error = WriteFile(handle, data.str, (DWORD)data.len, &bytes_written, NULL); + if (error == TRUE) { + if (bytes_written == data.len) { + result = OS_SUCCESS; + } + } + CloseHandle(handle); + } else result = OS_PATH_NOT_FOUND; + + return result; +} + +OS_API OS_Result OS_AppendFile(S8_String path, S8_String string) { + return OS__WriteFile(path, string, true); +} + +OS_API OS_Result os_write_file(S8_String path, S8_String string) { + return OS__WriteFile(path, string, false); +} + +OS_API S8_String OS_ReadFile(MA_Arena *arena, S8_String path) { + bool success = false; + S8_String result = S8_MakeEmpty(); + MA_Temp checkpoint = MA_BeginTemp(arena); + + wchar_t wpath[1024]; + UTF_CreateWidecharFromChar(wpath, MA_Lengthof(wpath), path.str, path.len); + HANDLE handle = CreateFileW(wpath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (handle != INVALID_HANDLE_VALUE) { + LARGE_INTEGER file_size; + if (GetFileSizeEx(handle, &file_size)) { + if (file_size.QuadPart != 0) { + result.len = (int64_t)file_size.QuadPart; + result.str = (char *)MA_PushSizeNonZeroed(arena, result.len + 1); + DWORD read; + if (ReadFile(handle, result.str, (DWORD)result.len, &read, NULL)) { // @todo: can only read 32 byte size files? + if (read == result.len) { + success = true; + result.str[result.len] = 0; + } + } + } + } + CloseHandle(handle); + } + + if (!success) { + result = S8_MakeEmpty(); + MA_EndTemp(checkpoint); + } + + return result; +} + +OS_API int64_t OS_GetFileModTime(S8_String file) { + FILETIME time = {0}; + WIN32_FIND_DATAW data; + + wchar_t wpath[1024]; + UTF_CreateWidecharFromChar(wpath, 1024, file.str, file.len); + HANDLE handle = FindFirstFileW(wpath, &data); + if (handle != INVALID_HANDLE_VALUE) { + FindClose(handle); + time = data.ftLastWriteTime; + } else { + return -1; + } + int64_t result = (int64_t)time.dwHighDateTime << 32 | time.dwLowDateTime; + return result; +} + +OS_API OS_Date OS_GetDate(void) { + SYSTEMTIME local; + GetLocalTime(&local); + + OS_Date result = {0}; + result.year = local.wYear; + result.month = local.wMonth; + result.day = local.wDay; + result.hour = local.wHour; + result.second = local.wSecond; + // result.milliseconds = local.wMilliseconds; + return result; +} + +#elif __linux__ || __APPLE__ || __unix__ + #include + #include + #include + #include + #include + + #if OS_MAC + #include + +OS_API S8_String OS_GetExePath(MA_Arena *arena) { + char buf[PATH_MAX]; + uint32_t bufsize = PATH_MAX; + if (_NSGetExecutablePath(buf, &bufsize)) { + return S8_MakeEmpty(); + } + + S8_String result = S8_Copy(arena, S8_MakeFromChar(buf)); + return result; +} + + #else + +OS_API S8_String OS_GetExePath(MA_Arena *arena) { + char buffer[PATH_MAX] = {}; + if (readlink("/proc/self/exe", buffer, PATH_MAX) == -1) { + return S8_MakeEmpty(); + } + S8_String result = S8_Copy(arena, S8_MakeFromChar(buffer)); + return result; +} + + #endif + +OS_API bool OS_EnableTerminalColors(void) { return true; } + +OS_API bool OS_IsAbsolute(S8_String path) { + bool result = path.len >= 1 && path.str[0] == '/'; + return result; +} + +OS_API S8_String OS_GetExeDir(MA_Arena *arena) { + MA_Temp scratch = MA_GetScratch(); + S8_String path = OS_GetExePath(scratch.arena); + S8_String dir = S8_ChopLastSlash(path); + S8_String copy = S8_Copy(arena, dir); + MA_ReleaseScratch(scratch); + return copy; +} + +OS_API S8_String os_cwd(MA_Arena *arena) { + char *buffer = (char *)MA_PushSizeNonZeroed(arena, PATH_MAX); + char *cwd = getcwd(buffer, PATH_MAX); + S8_String result = S8_MakeFromChar(cwd); + return result; +} + +OS_API void os_set_working_dir(char *path) { + IO_Printf("cd %s\n", path); + MA_Temp scratch = MA_GetScratch(); + S8_String copy = S8_Copy(scratch.arena, S8_MakeFromChar(path)); + chdir(copy.str); + MA_ReleaseScratch(scratch); +} + +OS_API S8_String OS_GetAbsolutePath(MA_Arena *arena, S8_String relative) { + MA_Temp scratch = MA_GetScratch1(arena); + S8_String copy = S8_Copy(scratch.arena, relative); + + char *buffer = (char *)MA_PushSizeNonZeroed(arena, PATH_MAX); + realpath((char *)copy.str, buffer); + S8_String result = S8_MakeFromChar(buffer); + + MA_ReleaseScratch(scratch); + return result; +} + +OS_API bool OS_FileExists(S8_String path) { + MA_Temp scratch = MA_GetScratch(); + S8_String copy = S8_Copy(scratch.arena, path); + + bool result = false; + if (access((char *)copy.str, F_OK) == 0) { + result = true; + } + + MA_ReleaseScratch(scratch); + return result; +} + +OS_API bool OS_IsDir(S8_String path) { + MA_Temp scratch = MA_GetScratch(); + S8_String copy = S8_Copy(scratch.arena, path); + + struct stat s; + if (stat(copy.str, &s) != 0) + return false; + + bool result = S_ISDIR(s.st_mode); + MA_ReleaseScratch(scratch); + return result; +} + +OS_API bool OS_IsFile(S8_String path) { + MA_Temp scratch = MA_GetScratch(); + S8_String copy = S8_Copy(scratch.arena, path); + + struct stat s; + if (stat(copy.str, &s) != 0) + return false; + bool result = S_ISREG(s.st_mode); + MA_ReleaseScratch(scratch); + return result; +} + +OS_API double OS_GetTime(void) { + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + uint64_t timeu64 = (((uint64_t)ts.tv_sec) * 1000000ull) + ((uint64_t)ts.tv_nsec) / 1000ull; + double timef = (double)timeu64; + double result = timef / 1000000.0; // Microseconds to seconds + return result; +} + +OS_API bool OS_IsValid(OS_FileIter it) { + return it.is_valid; +} + +OS_API void OS_Advance(OS_FileIter *it) { + struct dirent *file = 0; + while ((file = readdir((DIR *)it->dir)) != NULL) { + if (file->d_name[0] == '.' && file->d_name[1] == '.' && file->d_name[2] == 0) continue; + if (file->d_name[0] == '.' && file->d_name[1] == 0) continue; + + it->is_directory = file->d_type == DT_DIR; + it->filename = S8_CopyChar(it->arena, file->d_name); + + const char *dir_char_ending = it->is_directory ? "/" : ""; + const char *separator = it->path.str[it->path.len - 1] == '/' ? "" : "/"; + it->relative_path = S8_Format(it->arena, "%.*s%s%s%s", S8_Expand(it->path), separator, file->d_name, dir_char_ending); + it->absolute_path = OS_GetAbsolutePath(it->arena, it->relative_path); + if (it->is_directory) it->absolute_path = S8_Format(it->arena, "%.*s/", S8_Expand(it->absolute_path)); + it->is_valid = true; + return; + } + it->is_valid = false; + closedir((DIR *)it->dir); +} + +OS_API OS_FileIter OS_IterateFiles(MA_Arena *arena, S8_String path) { + OS_FileIter it = {0}; + it.arena = arena; + it.path = path = S8_Copy(arena, path); + it.dir = (void *)opendir((char *)path.str); + if (!it.dir) return it; + + OS_Advance(&it); + return it; +} + +OS_API OS_Result os_make_dir(char *path) { + IO_Printf("mkdir %s\n", path); + MA_Temp scratch = MA_GetScratch(); + path = S8_Copy(scratch.arena, S8_MakeFromChar(path)); + int error = mkdir(path.str, 0755); + MA_ReleaseScratch(scratch); + return error == 0 ? OS_SUCCESS : OS_FAILURE; +} + +OS_API OS_Result os_copy(char *from, char *to, bool overwrite) { + const char *ow = overwrite ? "-n" : ""; + int result = os_systemf("cp %s %s %s", ow, from, to); + return result == 0 ? OS_SUCCESS : OS_FAILURE; +} + +OS_API OS_Result os_delete_file(S8_String path) { + int result = os_systemf("rm %.*s", S8_Expand(path)); + return result == 0 ? OS_SUCCESS : OS_FAILURE; +} + +OS_API OS_Result OS_DeleteDir(S8_String path, unsigned flags) { + IO_Assert(flags & OS_RECURSIVE); + int result = os_systemf("rm -r %.*s", S8_Expand(path)); + return result == 0 ? OS_SUCCESS : OS_FAILURE; +} + +OS_API int64_t OS_GetFileModTime(S8_String file) { + MA_Temp scratch = MA_GetScratch(); + file = S8_Copy(scratch.arena, file); + + struct stat attrib = {}; + stat(file.str, &attrib); + struct timespec ts = attrib.IF_LINUX_ELSE(st_mtim, st_mtimespec); + int64_t result = (((int64_t)ts.tv_sec) * 1000000ll) + ((int64_t)ts.tv_nsec) / 1000ll; + + MA_ReleaseScratch(scratch); + return result; +} + +OS_API OS_Date OS_GetDate(void) { + time_t t = time(NULL); + struct tm date = *localtime(&t); + + OS_Date s = {0}; + s.second = date.tm_sec; + s.year = date.tm_year; + s.month = date.tm_mon; + s.day = date.tm_mday; + s.hour = date.tm_hour; + s.minute = date.tm_min; + + return s; +} + +OS_API OS_Result OS_AppendFile(S8_String path, S8_String string) { + MA_Temp scratch = MA_GetScratch(); + path = S8_Copy(scratch.arena, path); + + OS_Result result = OS_FAILURE; + FILE *f = fopen((const char *)path.str, "a"); + if (f) { + result = OS_SUCCESS; + + size_t written = fwrite(string.str, 1, string.len, f); + if (written < string.len) { + result = OS_FAILURE; + } + + int error = fclose(f); + if (error != 0) { + result = OS_FAILURE; + } + } + + MA_ReleaseScratch(scratch); + return result; +} + +OS_API S8_String OS_ReadFile(MA_Arena *arena, S8_String path) { + S8_String result = {}; + + // ftell returns insane size if file is + // a directory **on some machines** KEKW + if (OS_IsDir(path)) { + return result; + } + + MA_Temp scratch = MA_GetScratch1(arena); + path = S8_Copy(scratch.arena, path); + + FILE *f = fopen(path.str, "rb"); + if (f) { + fseek(f, 0, SEEK_END); + result.len = ftell(f); + fseek(f, 0, SEEK_SET); + + result.str = (char *)MA_PushSizeNonZeroed(arena, result.len + 1); + fread(result.str, result.len, 1, f); + result.str[result.len] = 0; + + fclose(f); + } + + MA_ReleaseScratch(scratch); + return result; +} + +OS_API OS_Result os_write_file(S8_String path, S8_String string) { + MA_Temp scratch = MA_GetScratch(); + path = S8_Copy(scratch.arena, path); + + OS_Result result = OS_FAILURE; + FILE *f = fopen((const char *)path.str, "w"); + if (f) { + result = OS_SUCCESS; + + size_t written = fwrite(string.str, 1, string.len, f); + if (written < string.len) { + result = OS_FAILURE; + } + + int error = fclose(f); + if (error != 0) { + result = OS_FAILURE; + } + } + + MA_ReleaseScratch(scratch); + return result; +} +#endif + +#if _WIN32 || __linux__ || __APPLE__ || __unix__ +OS_API int os_systemf(const char *string, ...) { + MA_Temp scratch = MA_GetScratch(); + S8_FORMAT(scratch.arena, string, result); + IO_Printf("%.*s\n", S8_Expand(result)); + int error_code = system(result.str); + MA_ReleaseScratch(scratch); + return error_code; +} + +OS_API bool OS_ExpandIncludesList(MA_Arena *arena, S8_List *out, S8_String filepath) { + S8_String c = OS_ReadFile(arena, filepath); + if (c.str == 0) return false; + S8_String path = S8_ChopLastSlash(filepath); + S8_String include = S8_Lit("#include \""); + for (;;) { + int64_t idx = -1; + if (S8_Seek(c, include, 0, &idx)) { + S8_String str_to_add = S8_GetPrefix(c, idx); + S8_AddNode(arena, out, str_to_add); + S8_String save = c; + c = S8_Skip(c, idx + include.len); + + S8_String filename = c; + filename.len = 0; + while (filename.str[filename.len] != '"' && filename.len < c.len) { + filename.len += 1; + } + + c = S8_Skip(c, filename.len + 1); + S8_String inc_path = S8_Format(arena, "%.*s/%.*s", S8_Expand(path), S8_Expand(filename)); + if (!OS_ExpandIncludesList(arena, out, inc_path)) { + S8_String s = S8_GetPrefix(save, save.len - c.len); + S8_AddNode(arena, out, s); + } + } else { + S8_AddNode(arena, out, c); + break; + } + } + return true; +} + +OS_API S8_String OS_ExpandIncludes(MA_Arena *arena, S8_String filepath) { + S8_List out = S8_MakeEmptyList(); + S8_String result = S8_MakeEmpty(); + MA_ScratchScope(s) { + OS_ExpandIncludesList(s.arena, &out, filepath); + result = S8_Merge(arena, out); + } + return result; +} +#endif + +typedef struct M_Allocator M_Allocator; + +typedef enum M_AllocatorOp { + M_AllocatorOp_Invalid, + M_AllocatorOp_Allocate, + M_AllocatorOp_Deallocate, + M_AllocatorOp_Reallocate, +} M_AllocatorOp; + +typedef void *M_AllocatorProc(void *allocator, M_AllocatorOp kind, void *p, size_t size, size_t old_size); +void *MA_AllocatorProc(void *allocator, M_AllocatorOp kind, void *p, size_t size, size_t old_size); +void *MA_ExclusiveAllocatorProc(void *allocator, M_AllocatorOp kind, void *p, size_t size, size_t old_size); + +struct M_Allocator { + // it's int for type safety because C++ somehow allows this: + // struct Array { M_Allocator allocator; } + // Array = {arena}; WTF???!??!? + // without actually passing M_Allocator but just a pointer + int *obj; + M_AllocatorProc *p; +}; + +#define M_AllocStruct(a, T) (T *)M_Alloc((a), sizeof(T)) +#define M_AllocArray(a, T, c) (T *)M_Alloc((a), sizeof(T) * (c)) +#define M_AllocStructNonZeroed(a, T) (T *)M_AllocNonZeroed((a), sizeof(T)) +#define M_AllocArrayNonZeroed(a, T, c) (T *)M_AllocNonZeroed((a), sizeof(T) * (c)) +#define M_AllocStructCopy(a, T, p) (T *)M_PushCopy(a, (p), sizeof(T)) + +#define M_Alloc(a, size) M__Alloc(a, size) +#define M_AllocNonZeroed(a, size) M__AllocNonZeroed(a, size) +#define M_AllocCopy(a, p, size) M__AllocCopy(a, p, size) +#define M_Realloc(a, p, size, old_size) M__Realloc(a, p, size, old_size) +#define M_Dealloc(a, p) M__Dealloc(a, p) + +void *M__AllocNonZeroed(M_Allocator allocator, size_t size); +void *M__Alloc(M_Allocator allocator, size_t size); +void *M__AllocCopy(M_Allocator allocator, void *p, size_t size); +void *M__Realloc(M_Allocator allocator, void *p, size_t size, size_t old_size); +void M__Dealloc(M_Allocator allocator, void *p); + +M_Allocator M_GetSystemAllocator(void); +M_Allocator MA_GetExclusiveAllocator(MA_Arena *arena); +M_Allocator MA_GetAllocator(MA_Arena *arena); +M_Allocator MA_BootstrapExclusive(void); + +#ifndef MA_CMalloc + #include + #define MA_CMalloc(x) malloc(x) + #define MA_CFree(x) free(x) + #define MA_CRealloc(p, size) realloc(p, size) +#endif + +M_Allocator MA_BootstrapExclusive(void) { + MA_Arena bootstrap_arena = {0}; + MA_Arena *arena = MA_PushStruct(&bootstrap_arena, MA_Arena); + *arena = bootstrap_arena; + arena->base_len = arena->len; + return MA_GetExclusiveAllocator(arena); +} + +void *M__AllocNonZeroed(M_Allocator allocator, size_t size) { + void *p = allocator.p(allocator.obj, M_AllocatorOp_Allocate, NULL, size, 0); + return p; +} + +void *M__Alloc(M_Allocator allocator, size_t size) { + void *p = allocator.p(allocator.obj, M_AllocatorOp_Allocate, NULL, size, 0); + MA_MemoryZero(p, size); + return p; +} + +void *M__AllocCopy(M_Allocator allocator, void *p, size_t size) { + void *copy_buffer = M__AllocNonZeroed(allocator, size); + MA_MemoryCopy(copy_buffer, p, size); + return copy_buffer; +} + +void M__Dealloc(M_Allocator allocator, void *p) { + allocator.p(allocator.obj, M_AllocatorOp_Deallocate, p, 0, 0); +} + +void *M__Realloc(M_Allocator allocator, void *p, size_t size, size_t old_size) { + void *result = allocator.p(allocator.obj, M_AllocatorOp_Reallocate, p, size, old_size); + // @todo: add old_size? because we can't zero + return result; +} + +MA_StaticFunc void *M_ClibAllocatorProc(void *allocator, M_AllocatorOp kind, void *p, size_t size, size_t old_size) { + if (kind == M_AllocatorOp_Allocate) { + return MA_CMalloc(size); + } + + if (kind == M_AllocatorOp_Deallocate) { + MA_CFree(p); + return NULL; + } + + if (kind == M_AllocatorOp_Reallocate) { + return MA_CRealloc(p, size); + } + + MA_Assertf(0, "MA_Arena invalid codepath"); + return NULL; +} + +void *MA_AllocatorProc(void *allocator, M_AllocatorOp kind, void *p, size_t size, size_t old_size) { + if (kind == M_AllocatorOp_Allocate) { + return MA__PushSizeNonZeroed((MA_Arena *)allocator, size); + } + + else if (kind == M_AllocatorOp_Reallocate) { + void *new_p = MA__PushSizeNonZeroed((MA_Arena *)allocator, size); + MA_MemoryCopy(new_p, p, old_size); + return new_p; + } + + else if (kind == M_AllocatorOp_Deallocate) { + return NULL; + } + + MA_Assertf(0, "MA_Arena invalid codepath"); + return NULL; +} + +void *MA_ExclusiveAllocatorProc(void *allocator, M_AllocatorOp kind, void *p, size_t size, size_t old_size) { + MA_Arena *arena = (MA_Arena *)allocator; + if (kind == M_AllocatorOp_Reallocate) { + if (size > old_size) { + size_t size_to_push = size - old_size; + void *result = MA__PushSizeNonZeroed(arena, size_to_push); + if (!p) p = result; + return p; + } + } + + if (kind == M_AllocatorOp_Deallocate) { + MA_DeallocateArena(arena); + return NULL; + } + + MA_Assertf(0, "MA_Arena invalid codepath"); + return NULL; +} + +M_Allocator MA_GetExclusiveAllocator(MA_Arena *arena) { + M_Allocator allocator = {(int *)arena, MA_ExclusiveAllocatorProc}; + return allocator; +} + +M_Allocator MA_GetAllocator(MA_Arena *arena) { + M_Allocator allocator = {(int *)arena, MA_AllocatorProc}; + return allocator; +} + +M_Allocator M_GetSystemAllocator(void) { + M_Allocator allocator; + allocator.obj = 0; + allocator.p = M_ClibAllocatorProc; + return allocator; +} + + +#define CL_Allocator MA_Arena * +#define CL_Allocate(a, s) MA_PushSizeNonZeroed(a, s) +#define CL_ASSERT IO_Assert +#define AND_CL_STRING_TERMINATE_ON_NEW_LINE + +#include +#include +#include + +#ifndef CL_API_FUNCTION + #ifdef __cplusplus + #define CL_API_FUNCTION extern "C" + #else + #define CL_API_FUNCTION + #endif +#endif + +#ifndef CL_INLINE + #ifndef _MSC_VER + #ifdef __cplusplus + #define CL_INLINE inline + #else + #define CL_INLINE + #endif + #else + #define CL_INLINE __forceinline + #endif +#endif + +#ifndef CL_Allocator +struct MA_Arena; + #define CL_Allocator MA_Arena * +#endif + +#ifndef AND_CL_STRING_TERMINATE_ON_NEW_LINE + #define AND_CL_STRING_TERMINATE_ON_NEW_LINE &&*T->stream != '\n' +#endif + +typedef enum CL_Kind { + CL_EOF, + CL_MUL, + CL_DIV, + CL_MOD, + CL_LEFTSHIFT, + CL_RIGHTSHIFT, + CL_ADD, + CL_SUB, + CL_EQUALS, + CL_LESSERTHEN, + CL_GREATERTHEN, + CL_LESSERTHEN_OR_EQUAL, + CL_GREATERTHEN_OR_EQUAL, + CL_NOTEQUALS, + CL_BITAND, + CL_BITOR, + CL_BITXOR, + CL_AND, + CL_OR, + CL_NEG, + CL_NOT, + CL_DECREMENT, + CL_INCREMENT, + CL_POSTDECREMENT, + CL_POSTINCREMENT, + CL_ASSIGN, + CL_DIVASSIGN, + CL_MULASSIGN, + CL_MODASSIGN, + CL_SUBASSIGN, + CL_ADDASSIGN, + CL_ANDASSIGN, + CL_ORASSIGN, + CL_XORASSIGN, + CL_LEFTSHIFTASSIGN, + CL_RIGHTSHIFTASSIGN, + CL_OPENPAREN, + CL_CLOSEPAREN, + CL_OPENBRACE, + CL_CLOSEBRACE, + CL_OPENBRACKET, + CL_CLOSEBRACKET, + CL_COMMA, + CL_MACRO_CONCAT, + CL_PREPROC_STRINGIFY, + CL_QUESTION, + CL_THREEDOTS, + CL_SEMICOLON, + CL_DOT, + CL_COLON, + CL_TAG, + CL_ARROW, + CL_EXPRSIZEOF, + CL_DOCCOMMENT, + CL_COMMENT, + CL_IDENTIFIER, + CL_STRINGLIT, + CL_CHARLIT, + CL_ERROR, + CL_FLOAT, + CL_INT, + CL_PREPROC_NULL, + CL_PREPROC_DEFINE, + CL_PREPROC_IFDEF, + CL_PREPROC_IFNDEF, + CL_PREPROC_INCLUDE, + CL_PREPROC_ENDIF, + CL_PREPROC_IF, + CL_PREPROC_PRAGMA, + CL_PREPROC_ERROR, + CL_PREPROC_ELSE, + CL_PREPROC_ELIF, + CL_PREPROC_UNDEF, + CL_KEYWORD_VOID, + CL_KEYWORD_INT, + CL_KEYWORD_CHAR, + CL_KEYWORD_UNSIGNED, + CL_KEYWORD_SIGNED, + CL_KEYWORD_LONG, + CL_KEYWORD_SHORT, + CL_KEYWORD_DOUBLE, + CL_KEYWORD_FLOAT, + CL_KEYWORD__BOOL, + CL_KEYWORD__COMPLEX, + CL_KEYWORD__IMAGINARY, + CL_KEYWORD_STATIC, + CL_KEYWORD_AUTO, + CL_KEYWORD_CONST, + CL_KEYWORD_EXTERN, + CL_KEYWORD_INLINE, + CL_KEYWORD_REGISTER, + CL_KEYWORD_RESTRICT, + CL_KEYWORD_VOLATILE, + CL_KEYWORD__THREAD_LOCAL, + CL_KEYWORD__ATOMIC, + CL_KEYWORD__NORETURN, + CL_KEYWORD_STRUCT, + CL_KEYWORD_UNION, + CL_KEYWORD_ENUM, + CL_KEYWORD_TYPEDEF, + CL_KEYWORD_DEFAULT, + CL_KEYWORD_BREAK, + CL_KEYWORD_RETURN, + CL_KEYWORD_SWITCH, + CL_KEYWORD_IF, + CL_KEYWORD_ELSE, + CL_KEYWORD_FOR, + CL_KEYWORD_WHILE, + CL_KEYWORD_CASE, + CL_KEYWORD_CONTINUE, + CL_KEYWORD_DO, + CL_KEYWORD_GOTO, + CL_KEYWORD_SIZEOF, + CL_KEYWORD__ALIGNAS, + CL_KEYWORD__ALIGNOF, + CL_KEYWORD__STATIC_ASSERT, + CL_KEYWORD__GENERIC, + CL_COUNT, +} CL_Kind; + +typedef enum CL_Fix { + CL_FIX_NONE, + CL_SUFFIX_U, + CL_SUFFIX_UL, + CL_SUFFIX_ULL, + CL_SUFFIX_L, + CL_SUFFIX_LL, + CL_SUFFIX_F, + CL_SUFFIX_FL, + CL_PREFIX_U8, + CL_PREFIX_U16, + CL_PREFIX_U32, + CL_PREFIX_L, +} CL_Fix; + +typedef struct CL_Token CL_Token; +struct CL_Token { + CL_Kind kind; + CL_Fix fix; + + bool is_hex : 1; + bool is_inside_macro : 1; + bool is_system_include : 1; + bool is_there_whitespace_before_token : 1; + + uint32_t id; + int len; + char *str; + + // Not storing line_begin like I would normally cause the user could + // override the line and file information using directives. + // On error need to do search if I want nice error context. + int line, column; + char *file; + + union { + double f64; + uint64_t u64; + char *intern; + char *string_literal; + struct CL_Message *error; + }; +}; + +typedef struct CL_Message CL_Message; +struct CL_Message { + CL_Message *next; + char *string; + CL_Token token; +}; + +typedef struct CL_Lexer CL_Lexer; +struct CL_Lexer { + CL_Message *first_message; + CL_Message *last_message; + int errors; + + char *stream; + char *stream_begin; + int line; + int column; + char *file; + bool inside_of_macro; + + // filters + bool skip_comments : 1; + bool skip_macros : 1; + bool select_includes : 1; + bool select_comments : 1; + bool select_macros : 1; + + CL_Allocator arena; +}; + +typedef struct CL_SearchPaths CL_SearchPaths; +struct CL_SearchPaths { + char **include_path; + int include_path_count; + + char **system_include_path; + int system_include_path_count; + + char *file_begin_to_ignore; +}; + +CL_API_FUNCTION CL_Token CL_Next(CL_Lexer *T); +CL_API_FUNCTION CL_Lexer CL_Begin(CL_Allocator arena, char *stream, char *filename); +CL_API_FUNCTION char *CL_ResolveFilepath(CL_Allocator arena, CL_SearchPaths *search_paths, char *filename, char *parent_file, bool is_system_include); +CL_API_FUNCTION void CL_StringifyMessage(char *buff, int buff_size, CL_Message *msg); +CL_API_FUNCTION void CL_Stringify(char *buff, int buff_size, CL_Token *token); + +extern const char *CL_FixString[]; +extern const char *CL_KindString[]; + +CL_INLINE int CL_StringLength(char *string) { + int len = 0; + while (*string++ != 0) len++; + return len; +} + +CL_INLINE bool CL_StringsAreEqual(char *a, int64_t alen, const char *b, int64_t blen) { + if (alen != blen) return false; + for (int i = 0; i < alen; i += 1) { + if (a[i] != b[i]) return false; + } + return true; +} + +CL_INLINE bool CL_IsIdentifier(CL_Token *token, char *str) { + int str_len = CL_StringLength(str); + bool result = token->kind == CL_IDENTIFIER && CL_StringsAreEqual(token->str, token->len, str, str_len); + return result; +} + +CL_INLINE bool CL_IsAssign(CL_Kind op) { + bool result = op >= CL_ASSIGN && op <= CL_RIGHTSHIFTASSIGN; + return result; +} + +CL_INLINE bool CL_IsKeywordType(CL_Kind op) { + bool result = op >= CL_KEYWORD_VOID && op <= CL_KEYWORD__IMAGINARY; + return result; +} + +CL_INLINE bool CL_IsKeywordTypeOrSpec(CL_Kind op) { + bool result = op >= CL_KEYWORD_VOID && op <= CL_KEYWORD_TYPEDEF; + return result; +} + +CL_INLINE bool CL_IsMacro(CL_Kind kind) { + bool result = kind >= CL_PREPROC_DEFINE && kind <= CL_PREPROC_UNDEF; + return result; +} + +CL_INLINE bool CL_IsKeyword(CL_Kind kind) { + bool result = kind >= CL_KEYWORD_VOID && kind <= CL_KEYWORD__GENERIC; + return result; +} + +CL_INLINE bool CL_IsKeywordOrIdent(CL_Kind kind) { + bool result = CL_IsKeyword(kind) || kind == CL_IDENTIFIER; + return result; +} + +/* +- I'm pretty sure I can remove allocations for most of the current functions. +- I also can fix ResolvePath stuff so that it uses string+len and doesn't need allocations +- Add lexing options like in stb_c_lexer.h + +Instead of AND_CL_STRING_TERMINATE_ON_NEW_LINE he is doing some weird cool stuff with redefining +https://github.com/nothings/stb/blob/master/stb_c_lexer.h + +CL_MULTILINE_SSTRINGS +CL_DOLLAR_IDENT + +- Add proper string parsing, as additional function, CL_ParseString() or something, this is the only one that would need allocations + +*/ + +#ifndef CL_PRIVATE_FUNCTION + #if defined(__GNUC__) || defined(__clang__) + #define CL_PRIVATE_FUNCTION __attribute__((unused)) static + #else + #define CL_PRIVATE_FUNCTION static + #endif +#endif + +#ifndef CL_Allocate + #include + #define CL_Allocate(allocator, size) malloc(size) +#endif + +#ifndef CL_STRING_TO_DOUBLE + #include + #define CL_STRING_TO_DOUBLE(str, len) strtod(str, 0) +#endif + +#ifndef CL_ASSERT + #include + #define CL_ASSERT(x) assert(x) +#endif + +#ifndef CL_VSNPRINTF + #include + #define CL_VSNPRINTF vsnprintf +#endif + +#ifndef CL_SNPRINTF + #include + #define CL_SNPRINTF snprintf +#endif + +#ifndef CL__MemoryCopy + #include + #define CL__MemoryCopy(dst, src, s) memcpy(dst, src, s) +#endif + +#ifndef CL_MemoryZero + #include + #define CL_MemoryZero(p, size) memset(p, 0, size) +#endif + +#ifndef CL_FileExists + #define CL_FileExists CL__FileExists + #include +CL_PRIVATE_FUNCTION bool CL_FileExists(char *name) { + bool result = false; + FILE *f = fopen(name, "rb"); + if (f) { + result = true; + fclose(f); + } + return result; +} +#endif + +CL_PRIVATE_FUNCTION void CL_ReportError(CL_Lexer *T, CL_Token *token, const char *string, ...); + +CL_PRIVATE_FUNCTION char *CL_PushStringCopy(CL_Allocator arena, char *p, int size) { + char *copy_buffer = (char *)CL_Allocate(arena, size + 1); + CL__MemoryCopy(copy_buffer, p, size); + copy_buffer[size] = 0; + return copy_buffer; +} + +CL_INLINE void CL_Advance(CL_Lexer *T) { + if (*T->stream == '\n') { + T->line += 1; + T->column = 0; + } + else if (*T->stream == ' ') { + T->column += 1; + } + else if (*T->stream == '\t') { + T->column += 1; + } + else if (*T->stream == 0) { + return; + } + T->stream += 1; +} + +CL_INLINE bool CL_IsAlphabetic(char c) { + bool result = (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); + return result; +} + +CL_INLINE bool CL_IsNumeric(char c) { + bool result = (c >= '0' && c <= '9'); + return result; +} + +CL_INLINE bool CL_IsHexNumeric(char c) { + bool result = (c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f'); + return result; +} + +CL_INLINE bool CL_IsWhitespace(char c) { + bool result = c == ' ' || c == '\n' || c == '\r' || c == '\t'; + return result; +} + +CL_INLINE bool CL_IsAlphanumeric(char c) { + bool result = CL_IsAlphabetic(c) || CL_IsNumeric(c); + return result; +} + +CL_API_FUNCTION void CL_SetTokenLength(CL_Lexer *T, CL_Token *token) { + intptr_t diff = T->stream - token->str; + CL_ASSERT(diff < 2147483647); + token->len = (int)diff; +} + +CL_PRIVATE_FUNCTION uint64_t CL_CharMapToNumber(char c) { + switch (c) { + case '0': return 0; break; + case '1': return 1; break; + case '2': return 2; break; + case '3': return 3; break; + case '4': return 4; break; + case '5': return 5; break; + case '6': return 6; break; + case '7': return 7; break; + case '8': return 8; break; + case '9': return 9; break; + case 'a': + case 'A': return 10; break; + case 'b': + case 'B': return 11; break; + case 'c': + case 'C': return 12; break; + case 'd': + case 'D': return 13; break; + case 'e': + case 'E': return 14; break; + case 'f': + case 'F': return 15; break; + default: return 255; + } +} + +CL_PRIVATE_FUNCTION uint64_t CL_ParseInteger(CL_Lexer *T, CL_Token *token, char *string, uint64_t len, uint64_t base) { + CL_ASSERT(base >= 2 && base <= 16); + uint64_t acc = 0; + for (uint64_t i = 0; i < len; i++) { + uint64_t num = CL_CharMapToNumber(string[i]); + if (num >= base) { + CL_ReportError(T, token, "Internal compiler error! Failed to parse a number"); + break; + } + acc *= base; + acc += num; + } + return acc; +} + +typedef struct CL_UTF32Result { + uint32_t out_str; + int advance; + int error; +} CL_UTF32Result; + +CL_PRIVATE_FUNCTION CL_UTF32Result CL_UTF8ToUTF32(char *c, int max_advance) { + CL_UTF32Result result = {0}; + + if ((c[0] & 0x80) == 0) { // Check if leftmost zero of first byte is unset + if (max_advance >= 1) { + result.out_str = c[0]; + result.advance = 1; + } + else result.error = 1; + } + + else if ((c[0] & 0xe0) == 0xc0) { + if ((c[1] & 0xc0) == 0x80) { // Continuation byte required + if (max_advance >= 2) { + result.out_str = (uint32_t)(c[0] & 0x1f) << 6u | (c[1] & 0x3f); + result.advance = 2; + } + else result.error = 2; + } + else result.error = 2; + } + + else if ((c[0] & 0xf0) == 0xe0) { + if ((c[1] & 0xc0) == 0x80 && (c[2] & 0xc0) == 0x80) { // Two continuation bytes required + if (max_advance >= 3) { + result.out_str = (uint32_t)(c[0] & 0xf) << 12u | (uint32_t)(c[1] & 0x3f) << 6u | (c[2] & 0x3f); + result.advance = 3; + } + else result.error = 3; + } + else result.error = 3; + } + + else if ((c[0] & 0xf8) == 0xf0) { + if ((c[1] & 0xc0) == 0x80 && (c[2] & 0xc0) == 0x80 && (c[3] & 0xc0) == 0x80) { // Three continuation bytes required + if (max_advance >= 4) { + result.out_str = (uint32_t)(c[0] & 0xf) << 18u | (uint32_t)(c[1] & 0x3f) << 12u | (uint32_t)(c[2] & 0x3f) << 6u | (uint32_t)(c[3] & 0x3f); + result.advance = 4; + } + else result.error = 4; + } + else result.error = 4; + } + else result.error = 4; + + return result; +} + +// @todo I think I should look at this again +CL_PRIVATE_FUNCTION void CL_ParseCharLiteral(CL_Lexer *T, CL_Token *token) { + token->kind = CL_CHARLIT; + token->str = T->stream; + while (*T->stream != '\'') { + if (*T->stream == '\\') { + CL_Advance(T); + } + if (*T->stream == 0) { + CL_ReportError(T, token, "Unclosed character literal!"); + return; + } + CL_Advance(T); + } + CL_SetTokenLength(T, token); + + if (token->str[0] == '\\') { + switch (token->str[1]) { + case '\\': token->u64 = '\\'; break; + case '\'': token->u64 = '\''; break; + case '"': token->u64 = '"'; break; + case 't': token->u64 = '\t'; break; + case 'v': token->u64 = '\v'; break; + case 'f': token->u64 = '\f'; break; + case 'n': token->u64 = '\n'; break; + case 'r': token->u64 = '\r'; break; + case 'a': token->u64 = '\a'; break; + case 'b': token->u64 = '\b'; break; + case '0': token->u64 = '\0'; break; + case 'x': + case 'X': CL_ASSERT(!"Not implemented"); break; // Hex constant + case 'u': CL_ASSERT(!"Not implemented"); break; // Unicode constant + default: { + CL_ReportError(T, token, "Unknown escape code"); + } + } + } + + else { + if (token->len > 4) { + CL_ReportError(T, token, "This character literal has invalid format, it's too big"); + goto skip_utf_encode; + } + + token->u64 = 0; + int i = 0; + + for (; i < token->len;) { + CL_UTF32Result result = CL_UTF8ToUTF32(token->str + i, (int)token->len); + i += result.advance; + token->u64 |= result.out_str << (8 * (token->len - i)); + if (result.error) { + CL_ReportError(T, token, "This character literal couldnt be parsed as utf8"); + break; + } + } + if (i != token->len) { + CL_ReportError(T, token, "Character literal decode error"); + } + } + +skip_utf_encode: + CL_Advance(T); +} + +// It combines strings, verifies the escape sequences but doesn't do any allocations +// so the final string actually needs additional transformation pass. A pass +// that will combine the string snippets, replace escape sequences with actual values etc. +// +// @warning: @not_sure: we are not setting token->string_literal +// +// "String 1" "String 2" - those strings snippets are combined +// @todo: look at this again +// @todo: make a manual correct version that user can execute if he needs to +CL_PRIVATE_FUNCTION void CL_CheckString(CL_Lexer *T, CL_Token *token) { + token->kind = CL_STRINGLIT; +combine_next_string_literal: + while (*T->stream != '"' && *T->stream != 0 AND_CL_STRING_TERMINATE_ON_NEW_LINE) { + if (*T->stream == '\\') { + CL_Advance(T); + switch (*T->stream) { + case 'a': + case 'b': + case 'e': + case 'f': + case 'n': + case 'r': + case 't': + case 'v': + case '\\': + case '\'': + case '?': + case '"': + case 'x': + case 'X': // Hex constant + case 'u': // Unicode constant + case 'U': + break; + case '0': // octal numbers or null + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + break; + default: { + CL_ReportError(T, token, "Invalid escape sequence"); + return; + } + } + } + CL_Advance(T); + } + CL_Advance(T); + + // Try to seek if there is a consecutive string. + // If there is such string we try to combine it. + { + char *seek_for_next_string = T->stream; + while (CL_IsWhitespace(*seek_for_next_string)) { + seek_for_next_string += 1; + } + + if (*seek_for_next_string == '"') { + seek_for_next_string += 1; + while (T->stream != seek_for_next_string) CL_Advance(T); + goto combine_next_string_literal; + } + } + CL_SetTokenLength(T, token); +} + +CL_PRIVATE_FUNCTION void CL_IsIdentifierKeyword(CL_Token *token) { + if (token->len == 1) return; + char *c = token->str; + switch (c[0]) { + case 'v': { + switch (c[1]) { + case 'o': { + if (CL_StringsAreEqual(token->str, token->len, "void", 4)) { + token->kind = CL_KEYWORD_VOID; + } + else if (CL_StringsAreEqual(token->str, token->len, "volatile", 8)) { + token->kind = CL_KEYWORD_VOLATILE; + } + } break; + } + } break; + case 'i': { + switch (c[1]) { + case 'n': { + if (CL_StringsAreEqual(token->str, token->len, "int", 3)) { + token->kind = CL_KEYWORD_INT; + } + else if (CL_StringsAreEqual(token->str, token->len, "inline", 6)) { + token->kind = CL_KEYWORD_INLINE; + } + } break; + case 'f': { + if (CL_StringsAreEqual(token->str, token->len, "if", 2)) { + token->kind = CL_KEYWORD_IF; + } + } break; + } + } break; + case 'c': { + switch (c[1]) { + case 'h': { + if (CL_StringsAreEqual(token->str, token->len, "char", 4)) { + token->kind = CL_KEYWORD_CHAR; + } + } break; + case 'o': { + if (CL_StringsAreEqual(token->str, token->len, "const", 5)) { + token->kind = CL_KEYWORD_CONST; + } + else if (CL_StringsAreEqual(token->str, token->len, "continue", 8)) { + token->kind = CL_KEYWORD_CONTINUE; + } + } break; + case 'a': { + if (CL_StringsAreEqual(token->str, token->len, "case", 4)) { + token->kind = CL_KEYWORD_CASE; + } + } break; + } + } break; + case 'u': { + switch (c[1]) { + case 'n': { + if (CL_StringsAreEqual(token->str, token->len, "unsigned", 8)) { + token->kind = CL_KEYWORD_UNSIGNED; + } + else if (CL_StringsAreEqual(token->str, token->len, "union", 5)) { + token->kind = CL_KEYWORD_UNION; + } + } break; + } + } break; + case 's': { + switch (c[1]) { + case 'i': { + if (CL_StringsAreEqual(token->str, token->len, "signed", 6)) { + token->kind = CL_KEYWORD_SIGNED; + } + else if (CL_StringsAreEqual(token->str, token->len, "sizeof", 6)) { + token->kind = CL_KEYWORD_SIZEOF; + } + } break; + case 'h': { + if (CL_StringsAreEqual(token->str, token->len, "short", 5)) { + token->kind = CL_KEYWORD_SHORT; + } + } break; + case 't': { + if (CL_StringsAreEqual(token->str, token->len, "static", 6)) { + token->kind = CL_KEYWORD_STATIC; + } + else if (CL_StringsAreEqual(token->str, token->len, "struct", 6)) { + token->kind = CL_KEYWORD_STRUCT; + } + } break; + case 'w': { + if (CL_StringsAreEqual(token->str, token->len, "switch", 6)) { + token->kind = CL_KEYWORD_SWITCH; + } + } break; + } + } break; + case 'l': { + switch (c[1]) { + case 'o': { + if (CL_StringsAreEqual(token->str, token->len, "long", 4)) { + token->kind = CL_KEYWORD_LONG; + } + } break; + } + } break; + case 'd': { + switch (c[1]) { + case 'o': { + if (CL_StringsAreEqual(token->str, token->len, "double", 6)) { + token->kind = CL_KEYWORD_DOUBLE; + } + else if (CL_StringsAreEqual(token->str, token->len, "do", 2)) { + token->kind = CL_KEYWORD_DO; + } + } break; + case 'e': { + if (CL_StringsAreEqual(token->str, token->len, "default", 7)) { + token->kind = CL_KEYWORD_DEFAULT; + } + } break; + } + } break; + case 'f': { + switch (c[1]) { + case 'l': { + if (CL_StringsAreEqual(token->str, token->len, "float", 5)) { + token->kind = CL_KEYWORD_FLOAT; + } + } break; + case 'o': { + if (CL_StringsAreEqual(token->str, token->len, "for", 3)) { + token->kind = CL_KEYWORD_FOR; + } + } break; + } + } break; + case '_': { + switch (c[1]) { + case 'B': { + if (CL_StringsAreEqual(token->str, token->len, "_Bool", 5)) { + token->kind = CL_KEYWORD__BOOL; + } + } break; + case 'C': { + if (CL_StringsAreEqual(token->str, token->len, "_Complex", 8)) { + token->kind = CL_KEYWORD__COMPLEX; + } + } break; + case 'I': { + if (CL_StringsAreEqual(token->str, token->len, "_Imaginary", 10)) { + token->kind = CL_KEYWORD__IMAGINARY; + } + } break; + case 'T': { + if (CL_StringsAreEqual(token->str, token->len, "_Thread_local", 13)) { + token->kind = CL_KEYWORD__THREAD_LOCAL; + } + } break; + case 'A': { + if (CL_StringsAreEqual(token->str, token->len, "_Atomic", 7)) { + token->kind = CL_KEYWORD__ATOMIC; + } + else if (CL_StringsAreEqual(token->str, token->len, "_Alignas", 8)) { + token->kind = CL_KEYWORD__ALIGNAS; + } + else if (CL_StringsAreEqual(token->str, token->len, "_Alignof", 8)) { + token->kind = CL_KEYWORD__ALIGNOF; + } + } break; + case 'N': { + if (CL_StringsAreEqual(token->str, token->len, "_Noreturn", 9)) { + token->kind = CL_KEYWORD__NORETURN; + } + } break; + case 'S': { + if (CL_StringsAreEqual(token->str, token->len, "_Static_assert", 14)) { + token->kind = CL_KEYWORD__STATIC_ASSERT; + } + } break; + case 'G': { + if (CL_StringsAreEqual(token->str, token->len, "_Generic", 8)) { + token->kind = CL_KEYWORD__GENERIC; + } + } break; + } + } break; + case 'a': { + switch (c[1]) { + case 'u': { + if (CL_StringsAreEqual(token->str, token->len, "auto", 4)) { + token->kind = CL_KEYWORD_AUTO; + } + } break; + } + } break; + case 'e': { + switch (c[1]) { + case 'x': { + if (CL_StringsAreEqual(token->str, token->len, "extern", 6)) { + token->kind = CL_KEYWORD_EXTERN; + } + } break; + case 'n': { + if (CL_StringsAreEqual(token->str, token->len, "enum", 4)) { + token->kind = CL_KEYWORD_ENUM; + } + } break; + case 'l': { + if (CL_StringsAreEqual(token->str, token->len, "else", 4)) { + token->kind = CL_KEYWORD_ELSE; + } + } break; + } + } break; + case 'r': { + switch (c[1]) { + case 'e': { + if (CL_StringsAreEqual(token->str, token->len, "register", 8)) { + token->kind = CL_KEYWORD_REGISTER; + } + else if (CL_StringsAreEqual(token->str, token->len, "restrict", 8)) { + token->kind = CL_KEYWORD_RESTRICT; + } + else if (CL_StringsAreEqual(token->str, token->len, "return", 6)) { + token->kind = CL_KEYWORD_RETURN; + } + } break; + } + } break; + case 't': { + switch (c[1]) { + case 'y': { + if (CL_StringsAreEqual(token->str, token->len, "typedef", 7)) { + token->kind = CL_KEYWORD_TYPEDEF; + } + } break; + } + } break; + case 'b': { + switch (c[1]) { + case 'r': { + if (CL_StringsAreEqual(token->str, token->len, "break", 5)) { + token->kind = CL_KEYWORD_BREAK; + } + } break; + } + } break; + case 'w': { + switch (c[1]) { + case 'h': { + if (CL_StringsAreEqual(token->str, token->len, "while", 5)) { + token->kind = CL_KEYWORD_WHILE; + } + } break; + } + } break; + case 'g': { + switch (c[1]) { + case 'o': { + if (CL_StringsAreEqual(token->str, token->len, "goto", 4)) { + token->kind = CL_KEYWORD_GOTO; + } + } break; + } + } break; + } +} + +CL_PRIVATE_FUNCTION void CL_EatMacroWhitespace(CL_Lexer *T) { + while (T->stream[0] == ' ' || T->stream[0] == '\t') CL_Advance(T); +} + +CL_PRIVATE_FUNCTION void CL_EatUntil(CL_Lexer *T, char c) { + while (T->stream[0] != c && T->stream[0] != 0) CL_Advance(T); +} + +CL_PRIVATE_FUNCTION void CL_LexMacroInclude(CL_Lexer *T, CL_Token *token) { + token->kind = CL_PREPROC_INCLUDE; + CL_EatMacroWhitespace(T); + char end = 0; + if (*T->stream == '"') { + end = '"'; + } + else if (*T->stream == '<') { + end = '>'; + token->is_system_include = true; + } + else { + CL_ReportError(T, token, "Invalid include directive, file not specified"); + return; + } + CL_Advance(T); + + token->str = T->stream; + while (*T->stream != end) { + if (*T->stream == 0) { + CL_ReportError(T, token, "Invalid include directive, reached end of file while reading filename"); + } + if (*T->stream == '\n') { + CL_ReportError(T, token, "Invalid include directive filename, got newline character while reading filename"); + } + CL_Advance(T); + } + CL_SetTokenLength(T, token); + CL_Advance(T); + + // @not_sure: this is because we want null terminated input into path resolution stuff + token->string_literal = CL_PushStringCopy(T->arena, token->str, token->len); +} + +CL_PRIVATE_FUNCTION bool CL_LexMacro(CL_Lexer *T, CL_Token *token) { + CL_EatMacroWhitespace(T); + token->str = T->stream; + while (CL_IsAlphabetic(*T->stream)) CL_Advance(T); + CL_SetTokenLength(T, token); + + switch (*token->str) { + case 'd': + if (CL_StringsAreEqual(token->str, token->len, "define", 6)) { + token->kind = CL_PREPROC_DEFINE; + } + break; + + case 'i': + if (CL_StringsAreEqual(token->str, token->len, "ifdef", 5)) { + token->kind = CL_PREPROC_IFDEF; + } + else if (CL_StringsAreEqual(token->str, token->len, "ifndef", 6)) { + token->kind = CL_PREPROC_IFNDEF; + } + else if (CL_StringsAreEqual(token->str, token->len, "include", 7)) { + token->kind = CL_PREPROC_INCLUDE; + CL_LexMacroInclude(T, token); + } + else if (CL_StringsAreEqual(token->str, token->len, "if", 2)) { + token->kind = CL_PREPROC_IF; + } + break; + + case 'e': + if (CL_StringsAreEqual(token->str, token->len, "endif", 5)) { + token->kind = CL_PREPROC_ENDIF; + } + else if (CL_StringsAreEqual(token->str, token->len, "error", 5)) { + token->kind = CL_PREPROC_ERROR; + CL_EatMacroWhitespace(T); + token->str = T->stream; + CL_EatUntil(T, '\n'); + CL_SetTokenLength(T, token); + } + else if (CL_StringsAreEqual(token->str, token->len, "else", 4)) { + token->kind = CL_PREPROC_ELSE; + } + else if (CL_StringsAreEqual(token->str, token->len, "elif", 4)) { + token->kind = CL_PREPROC_ELIF; + } + break; + + case 'p': + if (CL_StringsAreEqual(token->str, token->len, "pragma", 6)) { + token->kind = CL_PREPROC_PRAGMA; + } + break; + + case 'u': + if (CL_StringsAreEqual(token->str, token->len, "undef", 5)) { + token->kind = CL_PREPROC_UNDEF; + } + break; + default: return false; + } + return true; +} + +// Skipped space here is for case #define Memes (a), this is not a function like macro because of space +static uint32_t CL_TokenID; // @todo: make it read only +CL_PRIVATE_FUNCTION void CL_PrepareToken(CL_Lexer *T, CL_Token *token, bool skipped_space) { + CL_MemoryZero(token, sizeof(*token)); + token->str = T->stream; + token->line = T->line; + token->column = T->column; + token->file = T->file; + token->id = ++CL_TokenID; + if (skipped_space) token->is_there_whitespace_before_token = true; + CL_Advance(T); +} + +CL_PRIVATE_FUNCTION void CL_DefaultTokenize(CL_Lexer *T, CL_Token *token) { + char *c = token->str; + switch (*c) { + case 0: break; + case '(': token->kind = CL_OPENPAREN; break; + case ')': token->kind = CL_CLOSEPAREN; break; + case '{': token->kind = CL_OPENBRACE; break; + case '}': token->kind = CL_CLOSEBRACE; break; + case '[': token->kind = CL_OPENBRACKET; break; + case ']': token->kind = CL_CLOSEBRACKET; break; + case ',': token->kind = CL_COMMA; break; + case '~': token->kind = CL_NEG; break; + case '?': token->kind = CL_QUESTION; break; + case ';': token->kind = CL_SEMICOLON; break; + case ':': token->kind = CL_COLON; break; + case '.': { + token->kind = CL_DOT; + if (T->stream[0] == '.' && T->stream[1] == '.') { + CL_Advance(T); + CL_Advance(T); + token->kind = CL_THREEDOTS; + } + } break; + case '/': { + token->kind = CL_DIV; + if (*T->stream == '/') { + token->kind = CL_COMMENT; + CL_Advance(T); + CL_EatUntil(T, '\n'); + CL_SetTokenLength(T, token); + } + else if (*T->stream == '*') { + token->kind = CL_COMMENT; + CL_Advance(T); + for (;;) { + if (T->stream[0] == '*' && T->stream[1] == '/') { + break; + } + if (T->stream[0] == 0) { + CL_ReportError(T, token, "Unclosed block comment"); + goto error_end_path; + } + CL_Advance(T); + } + token->str += 2; + CL_SetTokenLength(T, token); + CL_Advance(T); + CL_Advance(T); + } + else if (*T->stream == '=') { + token->kind = CL_DIVASSIGN; + CL_Advance(T); + } + } break; + case '#': { + if (*T->stream == '#') { + token->kind = CL_MACRO_CONCAT; + CL_Advance(T); + } + else { + bool is_macro_directive = CL_LexMacro(T, token); + if (is_macro_directive) { + T->inside_of_macro = true; + } + else { + if (!T->inside_of_macro) { + CL_ReportError(T, token, "Invalid preprocessor directive"); + goto error_end_path; + } + + token->kind = CL_PREPROC_STRINGIFY; + token->str = T->stream; + while (*T->stream == '_' || CL_IsAlphanumeric(*T->stream)) + CL_Advance(T); + CL_SetTokenLength(T, token); + } + } + } break; + case '>': { + if (*T->stream == '=') { + token->kind = CL_GREATERTHEN_OR_EQUAL; + CL_Advance(T); + } + else if (*T->stream == '>') { + CL_Advance(T); + if (*T->stream == '=') { + CL_Advance(T); + token->kind = CL_RIGHTSHIFTASSIGN; + } + else { + token->kind = CL_RIGHTSHIFT; + } + } + else { + token->kind = CL_GREATERTHEN; + } + } break; + case '<': { + token->kind = CL_LESSERTHEN; + if (*T->stream == '=') { + token->kind = CL_LESSERTHEN_OR_EQUAL; + CL_Advance(T); + } + else if (*T->stream == '<') { + CL_Advance(T); + if (*T->stream == '=') { + CL_Advance(T); + token->kind = CL_LEFTSHIFTASSIGN; + } + else { + token->kind = CL_LEFTSHIFT; + } + } + } break; + case '&': { + if (*T->stream == '=') { + token->kind = CL_ANDASSIGN; + CL_Advance(T); + } + else if (*T->stream == '&') { + token->kind = CL_AND; + CL_Advance(T); + } + else { + token->kind = CL_BITAND; + } + } break; + case '-': { + if (*T->stream == '-') { + token->kind = CL_DECREMENT; + CL_Advance(T); + } + else if (*T->stream == '=') { + token->kind = CL_SUBASSIGN; + CL_Advance(T); + } + else { + token->kind = CL_SUB; + } + } break; + case '+': { + if (*T->stream == '+') { + token->kind = CL_INCREMENT; + CL_Advance(T); + } + else if (*T->stream == '=') { + token->kind = CL_ADDASSIGN; + CL_Advance(T); + } + else { + token->kind = CL_ADD; + } + } break; + case '|': { + if (*T->stream == '|') { + token->kind = CL_OR; + CL_Advance(T); + } + else if (*T->stream == '=') { + token->kind = CL_ORASSIGN; + CL_Advance(T); + } + else { + token->kind = CL_BITOR; + } + } break; + case '=': { + if (*T->stream != '=') { + token->kind = CL_ASSIGN; + } + else { + CL_Advance(T); + token->kind = CL_EQUALS; + } + } break; + case '!': { + if (*T->stream != '=') { + token->kind = CL_NOT; + } + else { + CL_Advance(T); + token->kind = CL_NOTEQUALS; + } + } break; + case '*': { + token->kind = CL_MUL; + if (*T->stream == '=') { + CL_Advance(T); + token->kind = CL_MULASSIGN; + } + } break; + case '%': { + token->kind = CL_MOD; + if (*T->stream == '=') { + token->kind = CL_MODASSIGN; + CL_Advance(T); + } + } break; + case '^': { + token->kind = CL_BITXOR; + if (*T->stream == '=') { + CL_Advance(T); + token->kind = CL_XORASSIGN; + } + } break; + case '"': { + CL_CheckString(T, token); + } break; + case '\'': { + CL_ParseCharLiteral(T, token); + } break; + case 'U': { // @todo Unicode32 + if (*T->stream == '"') { + token->fix = CL_PREFIX_U32; + CL_Advance(T); + CL_CheckString(T, token); + } + else if (*T->stream == '\'') { + token->fix = CL_PREFIX_U32; + CL_Advance(T); + CL_ParseCharLiteral(T, token); + } + else goto parse_regular_char; + } break; + case 'u': { // Unicode16 + if (*T->stream == '8') { // Unicode8 + if (T->stream[1] == '"') { // U8 STRING + token->fix = CL_PREFIX_U8; + CL_Advance(T); + CL_Advance(T); + CL_CheckString(T, token); + } + else if (T->stream[1] == '\'') { // U8 CHAR + token->fix = CL_PREFIX_U8; + CL_Advance(T); + CL_Advance(T); + CL_ParseCharLiteral(T, token); + } + else goto parse_regular_char; + } + else if (*T->stream == '"') { // U16 STRING + token->fix = CL_PREFIX_U16; + CL_Advance(T); + CL_CheckString(T, token); + } + else if (*T->stream == '\'') { // U16 CHAR + CL_Advance(T); + CL_ParseCharLiteral(T, token); + } + else goto parse_regular_char; + } + case 'L': { // Widechar + if (*T->stream == '"') { + token->fix = CL_PREFIX_L; + CL_Advance(T); + CL_CheckString(T, token); // @todo UTF16 + } + else if (*T->stream == '\'') { + token->fix = CL_PREFIX_L; + CL_Advance(T); + CL_ParseCharLiteral(T, token); + } + else goto parse_regular_char; + } break; + case 'A': + case 'a': + case 'B': + case 'b': + case 'C': + case 'c': + case 'D': + case 'd': + case 'E': + case 'e': + case 'F': + case 'f': + case 'G': + case 'g': + case 'H': + case 'h': + case 'I': + case 'i': + case 'J': + case 'j': + case 'K': + case 'k': + /*case 'L':*/ case 'l': + case 'M': + case 'm': + case 'N': + case 'n': + case 'O': + case 'o': + case 'P': + case 'p': + case 'Q': + case 'q': + case 'R': + case 'r': + case 'S': + case 's': + case 'T': + case 't': + // case 'U': case 'u': + case 'V': + case 'v': + case 'W': + case 'w': + case 'X': + case 'x': + case 'Y': + case 'y': + case 'Z': + case 'z': + case '_': + parse_regular_char : { + token->kind = CL_IDENTIFIER; + while (*T->stream == '_' || CL_IsAlphanumeric(*T->stream)) { + CL_Advance(T); + } + CL_SetTokenLength(T, token); + CL_IsIdentifierKeyword(token); + } break; + case '0': { + if (*T->stream == 'x' || *T->stream == 'X') { + token->kind = CL_INT; + token->is_hex = true; + CL_Advance(T); + while (CL_IsHexNumeric(*T->stream)) { + CL_Advance(T); + } + uint64_t len = T->stream - token->str; + CL_ASSERT(len > 2); + token->u64 = CL_ParseInteger(T, token, token->str + 2, len - 2, 16); + break; + } + } + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': { + token->kind = CL_INT; + for (;;) { + if (*T->stream == '.') { + if (token->kind == CL_FLOAT) { + CL_ReportError(T, token, "Failed to parse a floating point number, invalid format, found multiple '.'"); + } + + if (token->kind == CL_INT) { + token->kind = CL_FLOAT; + } + } + else if (CL_IsNumeric(*T->stream) == false) { + break; + } + CL_Advance(T); + } + + if (token->kind == CL_INT) { + uint64_t len = T->stream - token->str; + CL_ASSERT(len > 0); + token->u64 = CL_ParseInteger(T, token, token->str, len, 10); + } + + else if (token->kind == CL_FLOAT) { + token->f64 = CL_STRING_TO_DOUBLE(token->str, token->len); + } + + else { + CL_ASSERT(token->kind == CL_ERROR); + } + + if (*T->stream == 'f' || *T->stream == 'F') { + CL_Advance(T); + token->fix = CL_SUFFIX_F; + } + + else if (*T->stream == 'l' || *T->stream == 'L') { + CL_Advance(T); + token->fix = CL_SUFFIX_L; + if (*T->stream == 'l' || *T->stream == 'L') { + CL_Advance(T); + token->fix = CL_SUFFIX_LL; + if (*T->stream == 'u' || *T->stream == 'U') { + CL_Advance(T); + token->fix = CL_SUFFIX_ULL; + } + } + else if (*T->stream == 'u' || *T->stream == 'U') { + CL_Advance(T); + token->fix = CL_SUFFIX_UL; + } + } + + else if (*T->stream == 'u' || *T->stream == 'U') { + CL_Advance(T); + token->fix = CL_SUFFIX_U; + if (*T->stream == 'l' || *T->stream == 'L') { + CL_Advance(T); + token->fix = CL_SUFFIX_UL; + if (*T->stream == 'l' || *T->stream == 'L') { + CL_Advance(T); + token->fix = CL_SUFFIX_ULL; + } + } + } + + } break; + + default: { + CL_ReportError(T, token, "Unhandled character, skipping ..."); + } break; + } + +error_end_path:; +} + +CL_PRIVATE_FUNCTION bool CL_EatWhitespace(CL_Lexer *T) { + bool skipped = false; + for (;;) { + if (CL_IsWhitespace(*T->stream)) { + if (*T->stream == '\n') T->inside_of_macro = false; + CL_Advance(T); + skipped = true; + } + else if (T->stream[0] == '\\' && T->stream[1] == '\n') { + CL_Advance(T); + CL_Advance(T); + skipped = true; + } + else if (T->stream[0] == '\\' && T->stream[1] == '\r' && T->stream[2] == '\n') { + CL_Advance(T); + CL_Advance(T); + CL_Advance(T); + skipped = true; + } + else { + break; + } + } + return skipped; +} + +CL_PRIVATE_FUNCTION void CL_TryToFinalizeToken(CL_Lexer *T, CL_Token *token) { + if (!token->len) { + CL_SetTokenLength(T, token); + } + if (T->inside_of_macro) { + token->is_inside_macro = true; + } +} + +CL_PRIVATE_FUNCTION void CL_InitNextToken(CL_Lexer *T, CL_Token *token) { + // Skip comments, comments get allocated on perm and gathered on the Tokenizer. + // First non comment token gets those comments attached. + for (;;) { + bool skipped = CL_EatWhitespace(T); + CL_PrepareToken(T, token, skipped); + CL_DefaultTokenize(T, token); + + if (token->kind == CL_EOF) { + break; + } + + if (T->select_includes) { + if (token->kind != CL_PREPROC_INCLUDE) continue; + } + + if (T->select_macros) { + if (!token->is_inside_macro) continue; + } + + if (T->select_comments) { + if (token->kind != CL_COMMENT) continue; + } + + if (T->skip_comments) { + if (token->kind == CL_COMMENT) continue; + } + + if (T->skip_macros) { + if (token->is_inside_macro) continue; + } + + break; + } + CL_TryToFinalizeToken(T, token); +} + +CL_API_FUNCTION CL_Token CL_Next(CL_Lexer *T) { + CL_Token result; + CL_MemoryZero(&result, sizeof(CL_Token)); + CL_InitNextToken(T, &result); + return result; +} + +CL_API_FUNCTION CL_Lexer CL_Begin(CL_Allocator arena, char *stream, char *filename) { + CL_Lexer lexer = {0}; + lexer.stream = lexer.stream_begin = stream; + lexer.file = filename; + lexer.arena = arena; + lexer.skip_comments = true; + return lexer; +} + +// +// +// + +CL_PRIVATE_FUNCTION char *CL_ChopLastSlash(CL_Allocator arena, char *str) { + int i = 0; + int slash_pos = -1; + while (str[i]) { + if (str[i] == '/') { + slash_pos = i; + } + i += 1; + } + + char *result = str; + if (slash_pos != -1) { + result = CL_PushStringCopy(arena, str, slash_pos); + } + else { + result = (char *)"./"; + } + return result; +} + +CL_PRIVATE_FUNCTION char *CL_JoinPath(CL_Allocator arena, char *a, char *b) { + int alen = CL_StringLength(a); + int blen = CL_StringLength(b); + int additional_len = 0; + + if (alen && a[alen - 1] != '/') additional_len = 1; + char *result = (char *)CL_Allocate(arena, sizeof(char) * (alen + blen + 1 + additional_len)); + CL__MemoryCopy(result, a, alen); + if (additional_len) result[alen++] = '/'; + CL__MemoryCopy(result + alen, b, blen); + result[alen + blen] = 0; + return result; +} + +CL_PRIVATE_FUNCTION bool CL_IsAbsolutePath(char *path) { +#ifdef _WIN32 + bool result = CL_IsAlphabetic(path[0]) && path[1] == ':' && path[2] == '/'; +#else + bool result = path[0] == '/'; +#endif + return result; +} + +CL_PRIVATE_FUNCTION char *CL_SkipToLastSlash(char *p) { + int last_slash = 0; + for (int i = 0; p[i]; i += 1) { + if (p[i] == '/') last_slash = i; + } + return p + last_slash; +} + +CL_API_FUNCTION char *CL_ResolveFilepath(CL_Allocator arena, CL_SearchPaths *search_paths, char *filename, char *parent_file, bool is_system_include) { + CL_SearchPaths null_search_paths = {0}; + if (search_paths == 0) search_paths = &null_search_paths; + + if (search_paths->file_begin_to_ignore) { + char *name = CL_SkipToLastSlash(filename); + int namelen = CL_StringLength(name); + char *ignore = search_paths->file_begin_to_ignore; + int ignorelen = CL_StringLength(ignore); + if (namelen > ignorelen) { + namelen = ignorelen; + } + if (CL_StringsAreEqual(name, namelen, search_paths->file_begin_to_ignore, ignorelen)) { + return 0; + } + } + + if (CL_IsAbsolutePath(filename) && CL_FileExists(filename)) { + return filename; + } + + if (is_system_include) { + for (int path_i = 0; path_i < search_paths->system_include_path_count; path_i += 1) { + char *path_it = search_paths->system_include_path[path_i]; + char *file = CL_JoinPath(arena, path_it, filename); + if (CL_FileExists(file)) { + return file; + } + } + } + else { + if (parent_file) { + char *parent_dir = CL_ChopLastSlash(arena, parent_file); + char *file = CL_JoinPath(arena, parent_dir, filename); + if (CL_FileExists(file)) { + return file; + } + } + + for (int path_i = 0; path_i < search_paths->include_path_count; path_i += 1) { + char *path_it = search_paths->include_path[path_i]; + char *file = CL_JoinPath(arena, path_it, filename); + if (CL_FileExists(file)) { + return file; + } + } + } + return 0; +} + +// +// +// + +const char *CL_FixString[] = { + "SUFFIX_INVALID", + "SUFFIX_U", + "SUFFIX_UL", + "SUFFIX_ULL", + "SUFFIX_L", + "SUFFIX_LL", + "SUFFIX_F", + "SUFFIX_FL", + "PREFIX_U8", + "PREFIX_U16", + "PREFIX_U32", + "PREFIX_L", +}; + +const char *CL_KindString[] = { + "EOF", + "*", + "/", + "%", + "<<", + ">>", + "+", + "-", + "==", + "<", + ">", + "<=", + ">=", + "!=", + "&", + "|", + "^", + "&&", + "||", + "~", + "!", + "--", + "++", + "--", + "++", + "=", + "/=", + "*=", + "%=", + "-=", + "+=", + "&=", + "|=", + "^=", + "<<=", + ">>=", + "(", + ")", + "{", + "}", + "[", + "]", + ",", + "##", + "#Stringify", + "?", + "...", + ";", + ".", + ":", + "TAG", + "->", + "SIZEOF", + "DOCCOMMENT", + "COMMENT", + "IDENTIFIER", + "STRING_LITERAL", + "CHARACTER_LITERAL", + "ERROR TOKEN", + "FLOAT", + "INT", + "PREPROC_NULL", + "PREPROC_DEFINE", + "PREPROC_IFDEF", + "PREPROC_IFNDEF", + "PREPROC_INCLUDE", + "PREPROC_ENDIF", + "PREPROC_IF", + "PREPROC_PRAGMA", + "PREPROC_ERROR", + "PREPROC_ELSE", + "PREPROC_ELIF", + "PREPROC_UNDEF", + "KEYWORD_VOID", + "KEYWORD_INT", + "KEYWORD_CHAR", + "KEYWORD_UNSIGNED", + "KEYWORD_SIGNED", + "KEYWORD_LONG", + "KEYWORD_SHORT", + "KEYWORD_DOUBLE", + "KEYWORD_FLOAT", + "KEYWORD__BOOL", + "KEYWORD__COMPLEX", + "KEYWORD__IMAGINARY", + "KEYWORD_STATIC", + "KEYWORD_AUTO", + "KEYWORD_CONST", + "KEYWORD_EXTERN", + "KEYWORD_INLINE", + "KEYWORD_REGISTER", + "KEYWORD_RESTRICT", + "KEYWORD_VOLATILE", + "KEYWORD__THREAD_LOCAL", + "KEYWORD__ATOMIC", + "KEYWORD__NORETURN", + "KEYWORD_STRUCT", + "KEYWORD_UNION", + "KEYWORD_ENUM", + "KEYWORD_TYPEDEF", + "KEYWORD_DEFAULT", + "KEYWORD_BREAK", + "KEYWORD_RETURN", + "KEYWORD_SWITCH", + "KEYWORD_IF", + "KEYWORD_ELSE", + "KEYWORD_FOR", + "KEYWORD_WHILE", + "KEYWORD_CASE", + "KEYWORD_CONTINUE", + "KEYWORD_DO", + "KEYWORD_GOTO", + "KEYWORD_SIZEOF", + "KEYWORD__ALIGNAS", + "KEYWORD__ALIGNOF", + "KEYWORD__STATIC_ASSERT", + "KEYWORD__GENERIC", +}; + +CL_API_FUNCTION void CL_StringifyMessage(char *buff, int buff_size, CL_Message *msg) { + CL_SNPRINTF(buff, buff_size, "%s(%d,%d): %15s", msg->token.file, msg->token.line + 1, msg->token.column + 1, msg->string); +} + +CL_API_FUNCTION void CL_Stringify(char *buff, int buff_size, CL_Token *token) { + const char *token_kind = "UNKNOWN"; + if (token->kind < CL_COUNT) token_kind = CL_KindString[token->kind]; + CL_SNPRINTF(buff, buff_size, "%s(%d,%d): %15s %15.*s", token->file, token->line + 1, token->column + 1, token_kind, token->len, token->str); +} + +#define CL_SLL_QUEUE_ADD_MOD(f, l, n, next) \ + do { \ + (n)->next = 0; \ + if ((f) == 0) { \ + (f) = (l) = (n); \ + } \ + else { \ + (l) = (l)->next = (n); \ + } \ + } while (0) +#define CL_SLL_QUEUE_ADD(f, l, n) CL_SLL_QUEUE_ADD_MOD(f, l, n, next) + +#define CL__FORMAT(arena, string, result) \ + va_list args1, args2; \ + va_start(args1, string); \ + va_copy(args2, args1); \ + int len = CL_VSNPRINTF(0, 0, string, args2); \ + va_end(args2); \ + char *result = (char *)CL_Allocate((arena), len + 1); \ + CL_VSNPRINTF(result, len + 1, string, args1); \ + va_end(args1) + +CL_PRIVATE_FUNCTION void CL_ReportError(CL_Lexer *T, CL_Token *token, const char *string, ...) { + CL__FORMAT(T->arena, string, message_string); + CL_Message *result = (CL_Message *)CL_Allocate(T->arena, sizeof(CL_Message)); + CL_MemoryZero(result, sizeof(CL_Message)); + CL_SLL_QUEUE_ADD(T->first_message, T->last_message, result); + + result->string = (char *)string; + result->token = *token; + token->kind = CL_ERROR; + token->error = result; + T->errors += 1; +} + +THREAD_LOCAL MA_Arena Perm; +#define SRC_CACHE_ENTRY_COUNT 1024 + +typedef struct SRC_CacheEntry SRC_CacheEntry; +struct SRC_CacheEntry { + uint64_t filepath_hash; + uint64_t file_hash; + uint64_t includes_hash; + uint64_t total_hash; +}; + +typedef struct SRC_Cache SRC_Cache; +struct SRC_Cache { + int entry_cap; + int entry_len; + SRC_CacheEntry entries[SRC_CACHE_ENTRY_COUNT]; +}; + +double SRC_Time; +SRC_Cache *SRC_InMemoryCache; +SRC_Cache *SRC_FromFileCache; +S8_String SRC_CacheFilename; +CL_SearchPaths SRC_SearchPaths = {0}; // @todo; + +void SRC_InitCache(MA_Arena *arena, S8_String cachefilename) { + SRC_CacheFilename = cachefilename; + + SRC_InMemoryCache = MA_PushStruct(arena, SRC_Cache); + SRC_InMemoryCache->entry_cap = SRC_CACHE_ENTRY_COUNT; + + SRC_FromFileCache = MA_PushStruct(arena, SRC_Cache); + SRC_FromFileCache->entry_cap = SRC_CACHE_ENTRY_COUNT; + + S8_String cache = OS_ReadFile(arena, SRC_CacheFilename); + if (cache.len) MA_MemoryCopy(SRC_FromFileCache, cache.str, cache.len); +} + +void SRC_SaveCache() { + S8_String dump = S8_Make((char *)SRC_InMemoryCache, sizeof(SRC_Cache)); + os_write_file(SRC_CacheFilename, dump); +} + +SRC_CacheEntry *SRC_AddHash(uint64_t filepath, uint64_t file, uint64_t includes) { + IO_Assert(SRC_InMemoryCache->entry_len + 1 < SRC_InMemoryCache->entry_cap); + SRC_CacheEntry *result = SRC_InMemoryCache->entries + SRC_InMemoryCache->entry_len++; + result->filepath_hash = filepath; + result->file_hash = file; + result->includes_hash = includes; + result->total_hash = HashBytes(result, sizeof(uint64_t) * 3); + return result; +} + +SRC_CacheEntry *SRC_FindCache(SRC_Cache *cache, uint64_t filepath_hash) { + for (int cache_i = 0; cache_i < cache->entry_len; cache_i += 1) { + SRC_CacheEntry *it = cache->entries + cache_i; + if (it->filepath_hash == filepath_hash) { + return it; + } + } + return 0; +} + +SRC_CacheEntry *SRC_HashFile(S8_String file, char *parent_file) { + char *resolved_file = CL_ResolveFilepath(&Perm, &SRC_SearchPaths, file.str, parent_file, false); + if (!resolved_file) { + IO_Printf("Failed to resolve file: %.*s\n", S8_Expand(file)); + return 0; + } + + uint64_t filepath_hash = HashBytes(resolved_file, S8_Length(resolved_file)); + SRC_CacheEntry *entry = SRC_FindCache(SRC_InMemoryCache, filepath_hash); + if (entry) return entry; + + S8_String filecontent = OS_ReadFile(&Perm, S8_MakeFromChar(resolved_file)); + IO_Assert(filecontent.str); + + uint64_t file_hash = HashBytes(filecontent.str, filecontent.len); + uint64_t includes_hash = 13; + + CL_Lexer lexer = CL_Begin(&Perm, filecontent.str, resolved_file); + lexer.select_includes = true; + + for (CL_Token token = CL_Next(&lexer); token.kind != CL_EOF; token = CL_Next(&lexer)) { + if (token.is_system_include) continue; + + S8_String file_it = S8_MakeFromChar(token.string_literal); + SRC_CacheEntry *cache = SRC_HashFile(file_it, resolved_file); + if (!cache) { + // error was reported already IO_Printf("Missing cache for: %.*s\n", S8_Expand(file_it)); + continue; + } + + includes_hash = HashMix(includes_hash, cache->total_hash); + } + + SRC_CacheEntry *result = SRC_AddHash(filepath_hash, file_hash, includes_hash); + return result; +} + +bool SRC_WasModified(S8_String file, S8_String artifact_path) { + double time_start = OS_GetTime(); + + if (OS_FileExists(file) == false) { + IO_Printf("FAILED File doesnt exist: %.*s\n", S8_Expand(file)); + exit(0); + } + if (OS_IsAbsolute(file) == false) { + file = OS_GetAbsolutePath(&Perm, file); + } + + S8_String without_ext = S8_ChopLastPeriod(file); + S8_String name_only = S8_SkipToLastSlash(without_ext); + + if (artifact_path.len == 0) artifact_path = S8_Format(&Perm, "%.*s.%s", S8_Expand(name_only), IF_WINDOWS_ELSE("obj", "o")); + bool modified = OS_FileExists(artifact_path) == false; + + SRC_CacheEntry *in_memory = SRC_HashFile(file, 0); + IO_Assert(in_memory); + + if (modified == false) { + SRC_CacheEntry *from_file = SRC_FindCache(SRC_FromFileCache, in_memory->filepath_hash); + if (from_file == 0 || (in_memory->total_hash != from_file->total_hash)) { + modified = true; + } + } + + SRC_Time = SRC_Time + (OS_GetTime() - time_start); + + return modified; +} + +S8_String CL_Flags = S8_ConstLit("/MP /Zi /FC /WX /W3 /wd4200 /diagnostics:column /nologo -D_CRT_SECURE_NO_WARNINGS /GF /Gm- /Oi"); +S8_String CL_Link = S8_ConstLit("/link /incremental:no"); +S8_String CL_StdOff = S8_ConstLit("/GR- /EHa-"); +S8_String CL_StdOn = S8_ConstLit("/EHsc"); +S8_String CL_Debug = S8_ConstLit("-Od -D_DEBUG -fsanitize=address -RTC1"); +S8_String CL_Release = S8_ConstLit("-O2 -MT -DNDEBUG -GL"); +S8_String CL_ReleaseLink = S8_ConstLit("-opt:ref -opt:icf"); +/* +/FC = Print full paths in diagnostics +/Gm- = Old feature, 'minimal compilation', in case it's not off by default +/GF = Pools strings as read-only. If you try to modify strings under /GF, an application error occurs. +/Oi = Replaces some function calls with intrinsic +/MP = Multithreaded compilation +/GR- = Disable runtime type information +/EHa- = Disable exceptions +/EHsc = Enable exceptions +/MT = Link static libc. The 'd' means debug version +/MD = Link dynamic libc. The 'd' means debug version +/GL = Whole program optimization +/RTC1 = runtime error checks +/opt:ref = eliminates functions and data that are never referenced +/opt:icf = eliminates redundant 'COMDAT's +*/ + +S8_String Clang_Flags = S8_ConstLit("-fdiagnostics-absolute-paths -Wno-writable-strings"); +S8_String Clang_NoStd = S8_ConstLit("-fno-exceptions"); +S8_String Clang_Debug = S8_ConstLit("-fsanitize=address -g"); +/* +-std=c++11 + */ + +S8_String GCC_Flags = S8_ConstLit("-Wno-write-strings"); +S8_String GCC_NoStd = S8_ConstLit("-fno-exceptions"); +S8_String GCC_Debug = S8_ConstLit("-fsanitize=address -g"); + +#if !defined(BUILD_TOOL_LIB) +int main(int argument_count, char **arguments) { + os_make_dir("build"); + os_set_working_dir("build"); + S8_String working_dir = os_cwd(&Perm); + + S8_String cc = S8_Lit(IF_WINDOWS("cl") IF_MAC("clang") IF_LINUX("gcc")); + S8_List list = {0}; + for (int i = 1; i < argument_count; i += 1) { + S8_String it = S8_MakeFromChar(arguments[i]); + if (S8_StartsWith(it, S8_Lit("cc="), false)) cc = S8_Skip(it, 3); + S8_AddNode(&Perm, &list, it); + } + + // Search for build file in the project directory + S8_String build_file = {0}; + { + for (OS_FileIter it = OS_IterateFiles(&Perm, S8_Lit("..")); OS_IsValid(it); OS_Advance(&it)) { + if (S8_Seek(it.filename, S8_Lit("build_file.c"), S8_IgnoreCase, NULL)) { + build_file = it.absolute_path; + } + } + + if (build_file.str == 0) { + IO_Printf("ERROR: couldnt find build file in current dir: %.*s, exiting ... \n", S8_Expand(working_dir)); + IO_Printf("- proper build file contains 'build_file.c' in it's name\n"); + IO_Printf("- alternative compiler can be chosen like this: bld cc=clang\n"); + return 0; + } + } + + SRC_InitCache(&Perm, S8_Lit("build_tool.cache")); + S8_String name_no_ext = S8_GetNameNoExt(build_file); + S8_String exe_name = S8_Format(&Perm, "%.*s.exe", S8_Expand(name_no_ext)); + + // Compile the build file only if code changed + double time_build_file_compiled = 0; + if (SRC_WasModified(build_file, exe_name)) { + time_build_file_compiled = OS_GetTime(); + int result = 0; + if (S8_AreEqual(cc, S8_Lit("cl"), false)) { + result = os_systemf("cl \"%.*s\" -nologo -Zi -WX -W3 -wd4200 -diagnostics:column /Fe:%.*s /Fd:%.*s.pdb", S8_Expand(build_file), S8_Expand(exe_name), S8_Expand(name_no_ext)); + } else if (S8_AreEqual(cc, S8_Lit("clang"), false)) { + result = os_systemf("clang \"%.*s\" -o %.*s -g -fdiagnostics-absolute-paths -Wno-writable-strings %s", S8_Expand(build_file), S8_Expand(exe_name), IF_LINUX_ELSE("-lm", "")); + } else { + result = os_systemf("g \"%.*s\" -o %.*s -g -Wno-write-strings %s", S8_Expand(build_file), S8_Expand(exe_name), IF_LINUX_ELSE("-lm", "")); + } + + if (result != 0) { + IO_Printf("ERROR: failed to compile build file: %.*s, exitcode(%d), %f, deleting cache\n", S8_Expand(build_file), result, OS_GetTime() - time_build_file_compiled); + os_delete_file(S8_Lit("build_tool.cache")); + return 1; + } + time_build_file_compiled = OS_GetTime() - time_build_file_compiled; + } + + // Run the build file + S8_String cmd_args_merged = S8_MergeWithSeparator(&Perm, list, S8_Lit(" ")); + double time = OS_GetTime(); + if (build_file.str) { + exe_name = OS_GetAbsolutePath(&Perm, exe_name); + int result = os_systemf("%.*s %.*s", S8_Expand(exe_name), S8_Expand(cmd_args_merged)); + if (result != 0) { + IO_Printf("ERROR: failed to run build_file: %.*s, exitcode(%d), %f, deleting cache\n", S8_Expand(build_file), result, OS_GetTime() - time); + os_delete_file(S8_Lit("build_tool.cache")); + return 1; + } + } + + SRC_SaveCache(); + if (time_build_file_compiled) IO_Printf("build file compiled in: %f\n", time_build_file_compiled); + IO_Printf("executed in : %f\n", OS_GetTime() - time); +} +#endif diff --git a/src/meta/parser.c b/src/meta/parser.c new file mode 100644 index 0000000..8d51ee0 --- /dev/null +++ b/src/meta/parser.c @@ -0,0 +1,307 @@ +typedef enum { + +#define AST_FLAG_XLIST\ + X(null)\ + X(string)\ + X(integer)\ + X(real)\ + X(binary)\ + X(enum)\ + X(enum_member)\ + X(struct)\ + X(struct_member)\ + X(var)\ + X(type_name)\ + X(type_pointer)\ + X(type_array)\ + +#define X(NAME) ast_flag_##NAME, + AST_FLAG_XLIST +#undef X +} ast_flag_t; + +typedef struct ast_t ast_t; +struct ast_t { + ast_flag_t flags; + + lex_t *pos; + ast_t *next; + ast_t *first; + ast_t *last; + i32 len; + + s8_t string; + f64 real; + i64 integer; +}; + +s8_t s8_serial_ast_flag_t(ma_arena_t *arena, ast_flag_t flag) { + sb8_t *sb = sb8_serial_begin(arena); +#define X(NAME) if (flag & set_bit(ast_flag_##NAME)) sb8_printf(sb, #NAME); + AST_FLAG_XLIST +#undef X + s8_t result = sb8_serial_end(sb); + return result; +} + +ast_t *create_ast(parser_t *par, lex_t *pos, ast_flag_t flags) { + ast_t *result = ma_push_type(par->arena, ast_t); + memset(result, 0, sizeof(ast_t)); + result->flags = flags; + result->pos = pos; + return result; +} + +void ast_append(ast_t *parent, ast_t *node) { + SLLQ_APPEND(parent->first, parent->last, node); + parent->len += 1; +} + +ast_t *create_ast_binary(parser_t *par, lex_t *pos, ast_t *left, lex_kind_t op, ast_t *right) { + ast_t *result = create_ast(par, pos, set_bit(ast_flag_string) | set_bit(ast_flag_binary) | set_bit(ast_flag_integer)); + ast_append(result, left); + ast_append(result, right); + result->integer = op; + result->string = s8_serial_simple_lex_kind_t(op); + return result; +} + +ast_t *parse_expr(parser_t *par); +ast_t *parse_lit_expr(parser_t *par) { + lex_t *token = parser_next(par); + if (token->kind == lex_kind_int) { + ast_t *result = create_ast(par, token, set_bit(ast_flag_integer) | set_bit(ast_flag_string)); + result->integer = token->integer; + result->string = token->s8; + return result; + } else if (token->kind == lex_kind_real) { + ast_t *result = create_ast(par, token, set_bit(ast_flag_real) | set_bit(ast_flag_string)); + result->real = (double)token->real; + result->string = token->s8; + return result; + } else if (token->kind == lex_kind_open_paren) { + ast_t *result = parse_expr(par); + parser_expect(par, lex_kind_close_paren); + return result; + } else { + lex_panicf(token, "got invalid token of kind: %S while parsing expression", s8_serial_lex_kind_t(token->kind)); + return 0; + } +} + +ast_t *parse_mul_expr(parser_t *par) { + ast_t *left = parse_lit_expr(par); + while (par->at->kind == lex_kind_multiply || par->at->kind == lex_kind_divide || par->at->kind == lex_kind_modulo) { + lex_t *op = parser_next(par); + left = create_ast_binary(par, op, left, op->kind, parse_lit_expr(par)); + } + return left; +} + +ast_t *parse_add_expr(parser_t *par) { + ast_t *left = parse_mul_expr(par); + while (par->at->kind == lex_kind_plus || par->at->kind == lex_kind_minus) { + lex_t *op = parser_next(par); + left = create_ast_binary(par, op, left, op->kind, parse_lit_expr(par)); + } + return left; +} + +ast_t *parse_logical_and_expr(parser_t *par) { + ast_t *left = parse_add_expr(par); + while (par->at->kind == lex_kind_or) { + lex_t *op = parser_next(par); + left = create_ast_binary(par, op, left, op->kind, parse_lit_expr(par)); + } + return left; +} + +ast_t *parse_logical_or_expr(parser_t *par) { + ast_t *left = parse_logical_and_expr(par); + while (par->at->kind == lex_kind_or) { + lex_t *op = parser_next(par); + left = create_ast_binary(par, op, left, op->kind, parse_lit_expr(par)); + } + return left; +} + +ast_t *parse_expr(parser_t *par) { + ast_t *expr = parse_logical_or_expr(par); + return expr; +} + +ast_t *parse_expr_str(ma_arena_t *arena, char *file_name, char *stream) { + lex_array_t tokens = lex_tokens(arena, file_name, stream); + parser_t *par = parser_make(arena, tokens.data); + ast_t *result = parse_expr(par); + return result; +} + +i64 eval_const_expr(ast_t *expr) { + if (expr->flags & set_bit(ast_flag_integer)) { + return expr->integer; + } else if (expr->flags & set_bit(ast_flag_binary)) { + assert(expr->first != expr->last); + i64 left = eval_const_expr(expr->first); + i64 right = eval_const_expr(expr->last); + + switch(expr->integer) { + case lex_kind_plus: return left + right; + case lex_kind_minus: return left - right; + case lex_kind_multiply: return left * right; + case lex_kind_divide: return left / right; + case lex_kind_modulo: return left % right; + case lex_kind_and: return left && right; + case lex_kind_or: return left || right; + default: lex_panicf(expr->pos, "unhandled binary operator: %S", s8_serial_lex_kind_t(expr->integer)); + } + } else { + ma_temp_t scratch = ma_begin_scratch(); + lex_panicf(expr->pos, "unhandled ast in const expression evaluation: %S", s8_serial_ast_flag_t(scratch.arena, expr->flags)); + ma_end_scratch(scratch); + } + return 0; +} + +#define test_expr(x) do {\ + lex_array_t tokens = lex_tokens(scratch.arena, "parser_test", #x);\ + parser_t *par = parser_make(scratch.arena, tokens.data);\ + ast_t *expr = parse_expr(par);\ + assert(expr != NULL);\ + i64 value = eval_const_expr(expr);\ + assert(value == x);\ +} while (0) + +void run_parser_test() { + ma_temp_t scratch = ma_begin_scratch(); + test_expr(32 + 2 + 5 + 5); + test_expr(32 - 2 + 5 - 5); + test_expr(2 * 2 / 4 * 5 + 2 + 3); + test_expr(2 * 5 * 5 / 2 + 2 - 1 - 1); + test_expr(2 * (5 * 5) / 2 + (2 - 1 - 1)); + test_expr((2 * (5 * 5) / (2)) + (2 - 1 - 1)); + test_expr(10 % 3); + test_expr(10 % 3 + 4 || 2); + test_expr(10 % 3 + 4 || 2 && (4 && 2) || 3 && 1 || 0); + ma_end_scratch(scratch); +} + +ast_t *parse_struct_mem(parser_t *par, s8_t *name) { + lex_t *type_name = parser_expect(par, lex_kind_ident); + ast_t *type = create_ast(par, type_name, set_bit(ast_flag_type_name) | set_bit(ast_flag_string)); + type->string = type_name->s8; + + while (parser_match(par, lex_kind_multiply)) { + ast_t *pointer = create_ast(par, par->at, set_bit(ast_flag_type_pointer) | set_bit(ast_flag_string)); + ast_append(pointer, type); + pointer->string = s8_fmt(par->arena, "%S*", type->string); + type = pointer; + } + + *name = parser_expect(par, lex_kind_ident)->s8; + + while (parser_match(par, lex_kind_open_bracket)) { + ast_t *array = create_ast(par, par->at, set_bit(ast_flag_type_array) | set_bit(ast_flag_string)); + ast_append(array, type); + lex_t *num = parser_match(par, lex_kind_int); + if (num) { + array->flags |= set_bit(ast_flag_integer); + array->integer = (int)num->integer; + array->string = s8_fmt(par->arena, "%S[%d]", type->string, (int)array->integer); + } else { + array->string = s8_fmt(par->arena, "%S[]", type->string); + } + parser_expect(par, lex_kind_close_bracket); + type = array; + } + + return type; +} + +ast_t *parse_decls(ma_arena_t *arena, char *file, char *code) { + lex_array_t tokens = lex_tokens(arena, file, code); + parser_t *par = parser_make(arena, tokens.data); + ast_t *result = create_ast(par, par->at, set_bit(ast_flag_string)); + result->string = s8_copy_char(arena, file); + for (;par->at->kind != lex_kind_eof;) { + lex_t *pos = par->at; + if (parser_matchi(par, s8_lit("enum"))) { + ast_t *n = create_ast(par, pos, set_bit(ast_flag_string) | set_bit(ast_flag_enum)); + ast_append(result, n); + + parser_expect(par, lex_kind_open_brace); + while (par->at->kind == lex_kind_ident) { + lex_t *val = parser_expect(par, lex_kind_ident); + ast_t *mem = create_ast(par, val, set_bit(ast_flag_enum_member) | set_bit(ast_flag_string)); + mem->string = val->s8; + ast_append(n, mem); + + // if (parser_match(par, lex_kind_assign)) { + // parse_expr(); + // } + + if (!parser_match(par, lex_kind_comma)) break; + } + parser_expect(par, lex_kind_close_brace); + n->string = parser_expect(par, lex_kind_ident)->s8; + parser_expect(par, lex_kind_semicolon); + } else if (parser_matchi(par, s8_lit("struct"))) { + ast_t *n = create_ast(par, pos, set_bit(ast_flag_string) | set_bit(ast_flag_struct)); + ast_append(result, n); + n->string = parser_expect(par, lex_kind_ident)->s8; + + parser_expect(par, lex_kind_open_brace); + while (par->at->kind != lex_kind_close_brace) { + ast_t *mem = create_ast(par, par->at, set_bit(ast_flag_struct_member) | set_bit(ast_flag_var) | set_bit(ast_flag_string)); + ast_append(n, mem); + + ast_t *type = parse_struct_mem(par, &mem->string); + ast_append(mem, type); + + parser_expect(par, lex_kind_semicolon); + } + parser_expect(par, lex_kind_close_brace); + parser_expect(par, lex_kind_semicolon); + + } else { + parser_next(par); + } + } + + return result; +} + +ast_t *parse_table(ma_arena_t *arena, char *file, char *code) { + lex_array_t tokens = lex_tokens(arena, file, code); + parser_t *par = parser_make(arena, tokens.data); + ast_t *table = create_ast(par, par->at, 0); + while (par->at->kind != lex_kind_eof) { + ast_t *row = create_ast(par, par->at, 0); + ast_append(table, row); + while (par->at->kind != lex_kind_eof) { + parser_match(par, lex_kind_bit_or); + + lex_t *token = par->at; + if (parser_match(par, lex_kind_ident) || parser_match(par, lex_kind_string)) { + ast_t *col = create_ast(par, par->at, set_bit(ast_flag_string)); + ast_append(row, col); + col->string = token->s8; + } else if (parser_match(par, lex_kind_int)) { + ast_t *col = create_ast(par, par->at, set_bit(ast_flag_string) | set_bit(ast_flag_integer)); + ast_append(row, col); + col->string = token->s8; + col->integer = token->integer; + } else if (parser_match(par, lex_kind_real)) { + ast_t *col = create_ast(par, par->at, set_bit(ast_flag_string) | set_bit(ast_flag_real)); + ast_append(row, col); + col->string = token->s8; + col->real = token->real; + } else if (parser_match(par, lex_kind_bit_or) || parser_match(par, lex_kind_eof)) { + break; + } else { + lex_panicf(par->at, "invalid token: %S", s8_serial_lex_kind_t(par->at->kind)); + } + } + } + return table; +} \ No newline at end of file diff --git a/src/meta/serialize.c b/src/meta/serialize.c new file mode 100644 index 0000000..aa93461 --- /dev/null +++ b/src/meta/serialize.c @@ -0,0 +1,224 @@ +s8_t s8_ast_to_cvar(ma_arena_t *arena, ast_t *ast, s8_t *name) { + if (ast->flags & set_bit(ast_flag_type_name)) { + return ast->string; + } else if (ast->flags & set_bit(ast_flag_type_pointer)) { + s8_t base = s8_ast_to_cvar(arena, ast->first, name); + return s8_fmt(arena, "%S*", base); + } else if (ast->flags & set_bit(ast_flag_type_array)) { + if (ast->flags & set_bit(ast_flag_integer)) { + *name = s8_fmt(arena, "%S[%d]", *name, ast->integer); + } else { + *name = s8_fmt(arena, "%S[%d]", *name, ast->integer); + } + + s8_t base = s8_ast_to_cvar(arena, ast->first, name); + return base; + } else { + assert(!"invalid ast_str case"); + return (s8_t){0}; + } +} + +void sb8_serial_ast(sb8_t *sb, ast_t *n) { + if (n->flags & set_bit(ast_flag_string)) { + sb8_stmtf(sb, "%S", n->string); + } + + if (n->first) { + sb8_printf(sb, "{"); + sb->indent += 1; + for (ast_t *it = n->first; it; it = it->next) { + sb8_serial_ast(sb, it); + } + sb->indent -= 1; + sb8_stmtf(sb, "}"); + } +} + +s8_t s8_serial_ast(ma_arena_t *arena, ast_t *n) { + sb8_t *sb = sb8_serial_begin(arena); + sb8_serial_ast(sb, n); + return sb8_serial_end(sb); +} + +s8_t s8_serial_ast_to_code(ma_arena_t *arena, ast_t *n); +void sb8_serial_ast_to_code(sb8_t *sb, ast_t *n) { + if (n->flags & set_bit(ast_flag_enum)) { + sb8_printf(sb, "typedef enum {"); + sb->indent += 1; + for (ast_t *it = n->first; it; it = it->next) { + sb8_stmtf(sb, "%S,", it->string); + } + sb->indent -= 1; + sb8_stmtf(sb, "} %S;", n->string); + } else if (n->flags & set_bit(ast_flag_struct)) { + sb8_stmtf(sb, "typedef struct %S %S;", n->string, n->string); + sb8_stmtf(sb, "struct %S {", n->string); + sb->indent += 1; + for (ast_t *it = n->first; it; it = it->next) { + s8_t name = it->string; + s8_t type = s8_ast_to_cvar(sb->arena, it->first, &name); + sb8_stmtf(sb, "%S %S;", type, name); + } + sb->indent -= 1; + sb8_stmtf(sb, "};"); + + } else { + if (n->flags & set_bit(ast_flag_string)) { + sb8_printf(sb, "/*%S*/", n->string); + } else { + sb8_printf(sb, "/*null*/"); + } + + for (ast_t *it = n->first; it; it = it->next) { + sb8_indent(sb); + sb8_serial_ast_to_code(sb, it); + } + } +} + +s8_t s8_serial_ast_to_code(ma_arena_t *arena, ast_t *n) { + sb8_t *sb = sb8_serial_begin(arena); + sb8_serial_ast_to_code(sb, n); + s8_t result = sb8_serial_end(sb); + return result; +} + +s8_t s8_serial_ast_type_to_type_info(ma_arena_t *arena, ast_t *n) { + if (n->flags & set_bit(ast_flag_type_name)) { + return s8_fmt(arena, "type__%S", n->string); + } else if (n->flags & set_bit(ast_flag_type_pointer)) { + s8_t base = s8_serial_ast_type_to_type_info(arena, n->first); + return s8_fmt(arena, "(type_t){type_kind_pointer, s8_const_lit(\"%S\"), sizeof(void *), .base = &%S}", n->string, base); + } else if (n->flags & set_bit(ast_flag_type_array)) { + s8_t base = s8_serial_ast_type_to_type_info(arena, n->first); + return s8_fmt(arena, "(type_t){type_kind_array, s8_const_lit(\"%S\"), sizeof(%S), %d, .base = &%S}", n->string, n->string, (int)n->integer, base); + } else { + lex_panicf(n->pos, "expected type"); + } + return (s8_t){0}; +} + +void sb8_serial_ast_to_type_info(sb8_t *sb, ast_t *n) { + if (n->flags & set_bit(ast_flag_enum)) { + sb8_printf(sb, "type_t type__%S = { type_kind_enum, s8_const_lit(\"%S\"), sizeof(%S),", n->string, n->string, n->string); + sb->indent += 1; + + sb8_stmtf(sb, ".members = (type_member_t[]){"); + sb->indent += 1; + for (ast_t *it = n->first; it; it = it->next) { + sb8_stmtf(sb, "{.name = s8_const_lit(\"%S\"), .value = %S},", it->string, it->string); + } + sb->indent -= 1; + sb8_stmtf(sb, "},"); + + sb8_stmtf(sb, ".count = %d,", n->len); + sb->indent -= 1; + sb8_stmtf(sb, "};"); + } else if (n->flags & set_bit(ast_flag_struct)) { + sb8_printf(sb, "type_t type__%S = { type_kind_struct, s8_const_lit(\"%S\"), sizeof(%S),", n->string, n->string, n->string); + sb->indent += 1; + + sb8_stmtf(sb, ".members = (type_member_t[]){"); + sb->indent += 1; + for (ast_t *it = n->first; it; it = it->next) { + s8_t name = it->string; + s8_t type_info = s8_serial_ast_type_to_type_info(sb->arena, it->first); + sb8_stmtf(sb, "{.name = s8_const_lit(\"%S\"), .type = &%S, .offset = offsetof(%S, %S)},", name, type_info, n->string, name); + } + sb->indent -= 1; + sb8_stmtf(sb, "},"); + + sb8_stmtf(sb, ".count = %d,", n->len); + sb->indent -= 1; + sb8_stmtf(sb, "};"); + } else { + if (n->flags & set_bit(ast_flag_string)) { + sb8_printf(sb, "/*%S*/", n->string); + } else { + sb8_printf(sb, "/*null*/"); + } + + for (ast_t *it = n->first; it; it = it->next) { + sb8_indent(sb); + sb8_serial_ast_to_type_info(sb, it); + } + } +} + +s8_t s8_serial_ast_to_type_info(ma_arena_t *arena, ast_t *n) { + sb8_t *sb = sb8_serial_begin(arena); + sb8_serial_ast_to_type_info(sb, n); + s8_t result = sb8_serial_end(sb); + return result; +} + +// +// +// +int row_findi(ast_t *row, char *name) { + s8_t s = s8_from_char(name); + int i = 0; + for (ast_t *col = row->first; col; col = col->next) { + if (s8_equal(col->string, s)) { + return i; + } + i += 1; + } + return -1; +} + +ast_t *row_geti(ast_t *row, int idx) { + if (idx == -1) return NULL; + + int i = 0; + for (ast_t *col = row->first; col; col = col->next, i+=1) { + if (i == idx) return col; + } + return false; +} + +void sb8_serial_table_enum(sb8_t *c, sb8_t *h, ast_t *table, s8_t decl) { + int name_idx = row_findi(table->first, "name"); + int value_idx = row_findi(table->first, "value"); + + s8_t name_t = s8_fmt(c->arena, "%S_t", decl); + + sb8_printf(h, "typedef enum {\n"); + for (ast_t *row = table->first->next; row; row = row->next) { + s8_t name = row_geti(row, name_idx)->string; + ast_t *value = row_geti(row, value_idx); + sb8_printf(h, "%S_%S", decl, name); + if (value) sb8_printf(h, " = %S", value->string); + sb8_printf(h, ",\n"); + } + sb8_printf(h, "%S_count,\n", decl); + sb8_printf(h, "} %S;\n", name_t); + + sb8_stmtf(c, "type_t type__%S = { type_kind_enum, s8_const_lit(\"%S\"), sizeof(%S),", name_t, name_t, name_t); + c->indent += 1; + + sb8_stmtf(c, ".members = (type_member_t[]){"); + c->indent += 1; + int item_count = 0; + for (ast_t *row = table->first->next; row; row = row->next) { + s8_t name = row_geti(row, name_idx)->string; + ast_t *value = row_geti(row, value_idx); + sb8_stmtf(c, "{.name = s8_const_lit(\"%S_%S\"), .value = %S_%S},", decl, name, decl, name); + item_count += 1; + } + c->indent -= 1; + sb8_stmtf(c, "},"); + + sb8_stmtf(c, ".count = %d,", item_count); + c->indent -= 1; + sb8_stmtf(c, "};"); +} + +#define gen_c(arena) _gen_filename(arena, s8_lit(__FILE__), s8_lit("c")) +#define gen_h(arena) _gen_filename(arena, s8_lit(__FILE__), s8_lit("h")) +s8_t _gen_filename(ma_arena_t *arena, s8_t lit_file, s8_t ext) { + s8_t file_noext = s8_chop_last_period(s8_chop_last_period(lit_file)); + s8_t file = s8_fmt(arena, "%S.gen.%S", file_noext, ext); + return file; +} \ No newline at end of file diff --git a/src/wasm_app/debug.c b/src/wasm_app/debug.c new file mode 100644 index 0000000..f3a8a03 --- /dev/null +++ b/src/wasm_app/debug.c @@ -0,0 +1,3 @@ +void dr2f64(r2f64_t r) { + debugf("[%f %f %f %f]", r.x0, r.y0, r.x1, r.y1); +} \ No newline at end of file diff --git a/src/wasm_app/main.c b/src/wasm_app/main.c new file mode 100644 index 0000000..d6d9909 --- /dev/null +++ b/src/wasm_app/main.c @@ -0,0 +1,19 @@ +#include "core/core.h" +#include "core/core.c" +#include "app/app.h" +#include "app/app.c" +// #include "debug.c" +// #include "ui2.c" + +void on_update() { + // r2f64_t rect = {0, 0, 200, 200}; + // draw_rect(rect, (v4f32_t){0, 0, 0, 1}); + // draw_textf((v2f64_t){10 + i, 100}, "time = %f, dt = %f", time_g / 1000, dt_g); + + // f64_mod(4, 2); + + // i += 1; + // if (i > 200) i = 0; + + // ui_demo(); +} diff --git a/src/wasm_app/ui.c b/src/wasm_app/ui.c new file mode 100644 index 0000000..0ae7cad --- /dev/null +++ b/src/wasm_app/ui.c @@ -0,0 +1,280 @@ +#if 0 +typedef struct ui_id_t ui_id_t; +struct ui_id_t { + u64 value; +}; +ui_id_t ui_string_to_id(const char *string) { // FNV HASH (1a?) + u8 *data8 = (u8 *)string; + u64 hash = (u64)14695981039346656037ULL; + for (u64 i = 0; data8[i]; i++) { + hash = hash ^ (u64)(data8[i]); + hash = hash * (u64)1099511628211ULL; + } + return (ui_id_t){ .value = hash }; +} +#define ui_location_id() ui_string_to_id(FILE_AND_LINE) +ui_id_t ui_element_pressed; + +typedef enum { + cut_left, + cut_right, + cut_bottom, + cut_top, +} cut_t; + +typedef struct rcut_t rcut_t; +struct rcut_t { + r2f64_t *rect; + cut_t cut; +}; + +rcut_t rcut(r2f64_t *rect, cut_t cut) { + return (rcut_t){rect, cut}; +} + +r2f64_t ui_cut(r2f64_t *rect, f64 value, cut_t cut) { + if (cut == cut_left) return r2f64_cut_left(rect, value); + else if (cut == cut_right) return r2f64_cut_right(rect, value); + else if (cut == cut_bottom) return r2f64_cut_bottom(rect, value); + else if (cut == cut_top) return r2f64_cut_top(rect, value); + else assert(!"invalid codepath"); + return (r2f64_t){0}; +} + +r2f64_t ui_cut2(rcut_t rc, f64 x, f64 y) { + f64 cut_value = 0; + if (rc.cut == cut_left || rc.cut == cut_right) cut_value = x; + else cut_value = y; + r2f64_t result = ui_cut(rc.rect, cut_value, rc.cut); + return result; +} + +typedef struct ui_flags_t ui_flags_t; +struct ui_flags_t { + b32 draw_text : 1; + b32 text_centered : 1; + b32 text_y_centered; + + b32 draw_rect : 1; + + b32 checkable : 1; + b32 draw_checkbox : 1; + b32 draw_checked; + + b32 clickable : 1; + b32 slider : 1; +}; + +typedef struct ui_signal_t ui_signal_t; +struct ui_signal_t { + b32 pressed : 1; + b32 overlapping : 1; + b32 interacting : 1; +}; + +// TODO(Karol): Don't use inputs as flags! Use only flags. +typedef struct ui_input_t ui_input_t; +struct ui_input_t { + char *title; + b32 *value_b32; + f64 *value_f64; + ui_id_t id; +}; + +v2f64_t style_padding = {50, 10}; +ui_signal_t ui_widget(rcut_t rc, ui_input_t input, ui_flags_t flags) { + // Calculate rectangles + r2f64_t total_rect = {0}; + r2f64_t rect = {0}; + r2f64_t checkbox_rect = {0}; + { + v2f64_t size = style_padding; + f64 font_height = get_font_height(); + + size.y += font_height; + if (flags.draw_text) { + size.x += measure_text(input.title); + } + + total_rect = ui_cut2(rc, size.x, size.y); + rect = total_rect; + if (flags.draw_checkbox) { + checkbox_rect = r2f64_cut_left(&rect, size.y); + } + } + + // Solve interactions + ui_signal_t result = {0}; + { + if (r2f64_contains(total_rect, mouse_pos_g)) { + result.overlapping = true; + if (mouse_button_press_g[app_mouse_button_left]) { + ui_element_pressed = input.id; + result.pressed = true; + } + } + + if (ui_element_pressed.value == input.id.value && mouse_button_down_g[app_mouse_button_left]) { + result.interacting = true; + } + + if (flags.checkable && result.pressed) { + *input.value_b32 = !*input.value_b32; + } + + if (flags.slider && result.interacting) { + f64 mouse_pos = mouse_pos_g.x; + v2f64_t rect_size = r2f64_get_size(rect); + *input.value_f64 = (mouse_pos - rect.min.x) / rect_size.x; + *input.value_f64 = CLAMP(*input.value_f64, 0, 1.0); + } + } + + clip(total_rect); + + // Draw + if (flags.draw_rect) { + v4f32_t color = primary_color_global; + if (flags.clickable) { + if (result.overlapping) color = secondary_color_global; + if (flags.draw_checked && input.value_b32[0]) color = accent1_color_global; + } + draw_rect(rect, color); + } + + if (flags.slider) { + v2f64_t rect_size = r2f64_get_size(rect); + r2f64_t rect_split = r2f64_get_left(&rect, rect_size.x * input.value_f64[0]); + draw_rect(rect_split, accent1_color_global); + } + + if (flags.draw_text) { + v2f64_t text_pos = rect.min; + f64 text_width = measure_text(input.title); + f64 font_height = get_font_height(); + + v2f64_t rect_size = r2f64_get_size(rect); + v2f64_t offset_to_center = {(rect_size.x - text_width) / 2, (rect_size.y - font_height) / 2}; + if (flags.text_y_centered) { + text_pos.y += offset_to_center.y; + } + if (flags.text_centered) { + text_pos = v2f64_add(text_pos, offset_to_center); + } + draw_text(text_pos, input.title); + } + + if (flags.draw_checkbox) { + v4f32_t color = accent1_color_global; + if (flags.checkable) { + if (input.value_b32[0]) { + color = accent2_color_global; + } + } + if ((flags.checkable || flags.clickable) && result.overlapping) { + color = v4f32_lerp(color, v4f32(1,1,1,1), 0.25); + } + draw_rect(checkbox_rect, color); + } + + clip(r2f64(-1000, -1000, 1000000, 1000000)); + + return result; +} + +b32 ui_button(rcut_t rc, char *title) { + ui_signal_t sig = ui_widget(rc, (ui_input_t){.title = title}, (ui_flags_t){.draw_text = true, .text_centered = true, .draw_rect = true, .clickable = true}); + return sig.pressed; +} + +b32 ui_checked_button(b32 *value, rcut_t rc, char *title) { + ui_widget(rc, (ui_input_t){.title = title, .value_b32 = value}, (ui_flags_t){.draw_rect = true, .draw_text = true, .text_centered = true, .clickable = true, .checkable = true, .draw_checked = true}); + return *value; +} + +b32 ui_checkbox(rcut_t rc, b32 *value, char *title) { + ui_widget(rc, (ui_input_t){.value_b32 = value, .title = title}, (ui_flags_t){.draw_checkbox = true, .draw_text = true, .text_y_centered = true, .draw_rect = true, .checkable = true}); + return *value; +} + +void ui_label(rcut_t rc, char *title) { + ui_widget(rc, (ui_input_t){.title = title}, (ui_flags_t){.draw_text = true, .text_centered = true, .draw_rect = true}); +} + +void ui_slider(rcut_t rc, ui_id_t id, f64 *value, char *title) { + ui_widget(rc, (ui_input_t){.title = title, .value_f64 = value, .id = id}, (ui_flags_t){.draw_text = true, .text_centered = true, .slider = true, .draw_rect = true, .clickable = true}); +} + +void ui_begin_frame(void) { + if (mouse_button_release_g[app_mouse_button_left]) { + ui_element_pressed = (ui_id_t){0}; + } +} + +void ui_demo(void) { + ui_begin_frame(); + f64 font_height = get_font_height(); + r2f64_t window_rect = r2f64(0, 0, window_size_g.x, window_size_g.y); + { + draw_rect(window_rect, while_color_global); + } + + + static b32 panel_open = true; + static b32 cool_rect = false; + { + r2f64_t top_rect = r2f64_cut_top(&window_rect, font_height*2); + draw_rect(top_rect, primary_color_global); + + if (ui_checked_button(&cool_rect, rcut(&top_rect, cut_left), "file")) { + draw_rect(window_rect, v4f32(0.5, 0.1, 0.1, 1.0)); + } + if (ui_button(rcut(&top_rect, cut_left), "edit")) {} + if (ui_button(rcut(&top_rect, cut_left), "view")) {} + if (ui_checked_button(&panel_open, rcut(&top_rect, cut_left), "open panel")) { + + } + } + + if (panel_open) { + r2f64_t panel_rect = r2f64_cut_left(&window_rect, measure_text("1234567890")*2); + { + v4f32_t color = primary_color_global; color.a = 0.3f; + draw_rect(panel_rect, color); + } + + + + static f64 value = 0.2f; + ui_slider(rcut(&panel_rect, cut_top), ui_location_id(), &value, "value"); + + ui_widget(rcut(&panel_rect, cut_top), (ui_input_t){.title = "non centered label"}, (ui_flags_t){.draw_rect = true, .draw_text = true}); + + static b32 checkbox_memes; + ui_checkbox(rcut(&panel_rect, cut_top), &checkbox_memes, "checkbox"); + + + // TODO(Krzosa): Draw arrows / triangles + { + f64 font_height = get_font_height(); + f64 cut_size = font_height + style_padding.y; + r2f64_t rect = r2f64_cut_top(&panel_rect, cut_size); + + static int counter; + + if (ui_widget(rcut(&rect, cut_left), (ui_input_t){0}, (ui_flags_t){.draw_checkbox = true, .clickable = true}).pressed) { + counter -= 1; + } + char buff[256]; + stbsp_snprintf(buff, sizeof(buff), "counter: %d", counter); + ui_label(rcut(&rect, cut_left), buff); + if (ui_widget(rcut(&rect, cut_left), (ui_input_t){0}, (ui_flags_t){.draw_checkbox = true, .clickable = true}).pressed) { + counter += 1; + } + + } + + } + +} +#endif \ No newline at end of file diff --git a/src/wasm_app/ui2.c b/src/wasm_app/ui2.c new file mode 100644 index 0000000..3638b74 --- /dev/null +++ b/src/wasm_app/ui2.c @@ -0,0 +1,241 @@ +v2f64_t ui_calc_text_pos(r2f64_t rect, char *title) { + f64 font_height = get_font_height(); + f64 text_width = measure_text(title); + + v2f64_t text_pos = rect.min; + { + v2f64_t rect_size = r2f64_get_size(rect); + v2f64_t offset_to_center = {(rect_size.x - text_width) / 2, (rect_size.y - font_height) / 2}; + text_pos = v2f64_add(text_pos, offset_to_center); + } + + return text_pos; +} + +typedef struct ui_signal_t ui_signal_t; +struct ui_signal_t { + b8 pressed; + b8 overlapping; +}; + +ui_signal_t ui_interact(r2f64_t rect) { + ui_signal_t sig = {0}; + if (r2f64_contains(rect, mouse_pos_g)) { + sig.overlapping = true; + if (mouse_button_press_g[app_mouse_button_left]) { + sig.pressed = true; + } + } + return sig; +} + +b32 ui_button(r2f64_t rect, char *title) { + v2f64_t text_pos = ui_calc_text_pos(rect, title); + ui_signal_t sig = ui_interact(rect); + + v4f32_t rect_color = secondary_color_global; + v4f32_t text_color = black_color_global; + if (sig.overlapping) { + rect_color = primary_color_global; + } + + clip(rect); + draw_rect(rect, rect_color); + draw_text(text_pos, text_color, title, str_len(title)); + clip(r2f64(-1000, -1000, 1000000, 1000000)); + + return sig.pressed; +} + +b32 ui_checkbox(r2f64_t rect, b32 *value, char *title) { + v2f64_t text_pos = ui_calc_text_pos(rect, title); + ui_signal_t sig = ui_interact(rect); + if (sig.pressed) *value = !*value; + + v4f32_t rect_color = secondary_color_global; + v4f32_t text_color = black_color_global; + if (sig.overlapping) { + rect_color = primary_color_global; + } + if (*value) { + rect_color = v4f32_lerp(rect_color, accent2_color_global, 0.5); + } + + clip(rect); + draw_rect(rect, rect_color); + draw_text(text_pos, text_color, title, str_len(title)); + clip(r2f64(-1000, -1000, 1000000, 1000000)); + + return *value; +} + +typedef struct ui_id_t ui_id_t; +struct ui_id_t { u64 value; }; + +ui_id_t ui_string_to_id(const char *string, i32 len) { // FNV HASH (1a?) + u8 *data8 = (u8 *)string; + u64 hash = (u64)14695981039346656037ULL; + for (u64 i = 0; i < len; i++) { + hash = hash ^ (u64)(data8[i]); + hash = hash * (u64)1099511628211ULL; + } + return (ui_id_t){ .value = hash }; +} +#define ui_location_id() ui_string_to_id(FILE_AND_LINE, sizeof(FILE_AND_LINE) - 1) +ui_id_t ui_active_element = {0}; + +void ui_slider(r2f64_t rect, ui_id_t id, f64 *value, char *title) { + v2f64_t text_pos = ui_calc_text_pos(rect, title); + ui_signal_t sig = ui_interact(rect); + b32 interacting = false; + { + if (sig.pressed) { + ui_active_element = id; + } + if (id.value == ui_active_element.value && mouse_button_down_g[app_mouse_button_left]) { + interacting = true; + } + } + + if (interacting) { + f64 mouse_pos = mouse_pos_g.x; + v2f64_t rect_size = r2f64_get_size(rect); + *value = (mouse_pos - rect.min.x) / rect_size.x; + *value = CLAMP(*value, 0, 1.0); + } + + v2f64_t rect_size = r2f64_get_size(rect); + r2f64_t slider_rect = r2f64_get_left(&rect, rect_size.x * value[0]); + + v4f32_t rect_color = secondary_color_global; + if (sig.overlapping) rect_color = v4f32_lerp(rect_color, accent2_color_global, 0.2); + v4f32_t slider_color = accent2_color_global; + if (sig.overlapping) slider_color = v4f32_lerp(slider_color, accent2_color_global, 0.2); + v4f32_t text_color = black_color_global; + + clip(rect); + draw_rect(rect, rect_color); + draw_rect(slider_rect, slider_color); + draw_text(text_pos, text_color, title, str_len(title)); + clip(r2f64(-1000, -1000, 1000000, 1000000)); +} + +typedef struct ui_input_state ui_input_state; +struct ui_input_state { + char *buff; + int len; + int cap; + + int cursor; +}; + + +void ui_input_text(r2f64_t rect, ui_input_state *in) { + v2f64_t tp = ui_calc_text_pos(rect, "a"); + v2f64_t text_pos = {rect.min.x, tp.y}; + ui_signal_t sig = ui_interact(rect); + + v4f32_t rect_color = secondary_color_global; + if (r2f64_contains(rect, mouse_pos_g)) { + if (key_press_g == app_key_right) in->cursor += 1; + if (key_press_g == app_key_left) in->cursor -= 1; + if (key_press_g == app_key_backspace) { + + debugf("a"); + } + in->cursor = CLAMP(in->cursor, 0, in->len); + + + + + + if (sig.overlapping) rect_color = v4f32_lerp(rect_color, accent2_color_global, 0.2); + } + + + clip(rect); + draw_rect(rect, rect_color); + draw_text(text_pos, black_color_global, in->buff, in->len); + clip(r2f64(-1000, -1000, 1000000, 1000000)); + + f64 cx = measure_text_ex(in->buff, in->cursor); + v2f64_t cursor_pos = {text_pos.x + cx, text_pos.y}; + draw_rect(r2f64_center_halfdim(cursor_pos, v2f64(1, 6)), black_color_global); +} + +void ui_demo(void) { + if (mouse_button_release_g[app_mouse_button_left]) { + ui_active_element = (ui_id_t){0}; + } + + r2f64_t window_rect = (r2f64_t){(v2f64_t){0}, window_size_g}; + r2f64_t top_bar_rect = r2f64_cut_top(&window_rect, get_font_height() + 20); + draw_rect(window_rect, primary_color_global); + + f64 padding = 50; + draw_rect(top_bar_rect, secondary_color_global); + + static b32 open_file_panel; + f64 open_file_panel_xsize = 0; + { + // ▼▲▶◀ + char *title = "▶ file"; + if (open_file_panel) title = "▼ file"; + open_file_panel_xsize = measure_text(title) + padding*2; + r2f64_t rect = r2f64_cut_left(&top_bar_rect, open_file_panel_xsize); + if (ui_checkbox(rect, &open_file_panel, title)) { + } + r2f64_t gap_rect = r2f64_cut_left(&top_bar_rect, 1); + draw_rect(gap_rect, black_color_global); + } + + if (open_file_panel) { + f64 item_count = 20; + f64 item_size = get_font_height() + 20; + r2f64_t panel_rect = r2f64_get_top(&window_rect, item_count * item_size); + panel_rect = r2f64_get_left(&panel_rect, open_file_panel_xsize); + + { + static f64 slider_value; + char buff[64]; + stbsp_snprintf(buff, sizeof(buff), "%f", slider_value); + r2f64_t rect = r2f64_cut_top(&panel_rect, item_size); + ui_slider(rect, ui_location_id(), &slider_value, buff); + } + + { + static char buff[64]; + static ui_input_state input; + if (input.buff == NULL) { + input.buff = buff; + input.cap = sizeof(buff); + } + + r2f64_t rect = r2f64_cut_top(&panel_rect, item_size); + ui_input_text(rect, &input); + } + } + + + { + char *title = "edit"; + r2f64_t rect = r2f64_cut_left(&top_bar_rect, measure_text(title) + padding*2); + if (ui_button(rect, title)) { + } + r2f64_t gap_rect = r2f64_cut_left(&top_bar_rect, 1); + draw_rect(gap_rect, black_color_global); + } + + { + char *title = "view"; + r2f64_t rect = r2f64_cut_left(&top_bar_rect, measure_text(title) + padding*2); + if (ui_button(rect, title)) { + } + r2f64_t gap_rect = r2f64_cut_left(&top_bar_rect, 1); + draw_rect(gap_rect, black_color_global); + } + + if (input_text_len_g) { + debugf("%.*s", input_text_len_g, input_text_g); + } +} \ No newline at end of file diff --git a/todo.txt b/todo.txt new file mode 100644 index 0000000..d8ef0e3 --- /dev/null +++ b/todo.txt @@ -0,0 +1,23 @@ +platform + more then 2 platform abstraction + app platform game + in the future event based? + or maybe hybrid? + os functionality + gfx + w32 + canvas + dll hot reload + wasm: + input and key down (use KeyA KeyB etc.) + +type_info: + serialize + deserialize + + +app + loop styles: + infinite loop update -> gather all events -> loop event and update -> render (vsync wait) + block when no events + update (render) + other events (don't render) + block when no events + \ No newline at end of file