--- /dev/null
+#include "cbuild.h"
+
+#if OS_WINDOWS
+# include "src_build/cbuild_win32.c"
+#else
+# include "src_build/cbuild_unix.c"
+#endif
+
+int32_t main(int32_t argc, char **argv) {
+ cb_rebuild_self_with(argc, argv, "src_build/cbuild_unix.c",
+ "src_build/cbuild_win32.c");
+
+ CB_Cmd cmd = {};
+ build_setup(&cmd);
+ CB_Process proc = cb_cmd_run(&cmd);
+ if (proc.status_code) { return proc.status_code; }
+}
--- /dev/null
+#ifndef CBUILD_H
+#define CBUILD_H
+
+#if defined(__GNUC__) && !defined(__llvm__) && !defined(__INTEL_COMPILER)
+# define COMPILER_GCC 1
+#elif defined(__clang__) && !defined(_MSC_VER)
+# define COMPILER_CLANG 1
+#elif defined(_MSC_VER)
+# define COMPILER_CL 1
+#else
+# error Unsupported compiler
+#endif
+
+#if defined(__gnu_linux__)
+# define OS_LINUX 1
+#elif defined(__unix__)
+# define OS_BSD 1
+#elif defined(_WIN32)
+# define OS_WINDOWS 1
+#elif defined(__APPLE__)
+# define OS_MAC 1
+#else
+# error Unknown operating system
+#endif
+
+#if defined(__cplusplus)
+# define CPP 1
+#else
+# define CPP 0
+#endif
+
+#if !defined(COMPILER_GCC)
+# define COMPILER_GCC 0
+#endif
+#if !defined(COMPILER_CLANG)
+# define COMPILER_CLANG 0
+#endif
+#if !defined(COMPILER_CL)
+# define COMPILER_CL 0
+#endif
+
+#if !defined(OS_LINUX)
+# define OS_LINUX 0
+#endif
+#if !defined(OS_BSD)
+# define OS_BSD 0
+#endif
+#if !defined(OS_MAC)
+# define OS_MAC 0
+#endif
+#if !defined(OS_WINDOWS)
+# define OS_WINDOWS 0
+#endif
+#if !defined(OS_NONE)
+# define OS_NONE 0
+#endif
+
+#if !CPP
+# if __STDC_VERSION__ >= 199901L
+# include <stdbool.h>
+# else
+typedef enum {false, true} bool;
+# endif
+#endif
+
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#if OS_WINDOWS
+# include <windows.h>
+# include <direct.h>
+# define win32_stdin stdin
+# undef stdin
+# define win32_stdout stdout
+# undef stdout
+# define win32_stderr stderr
+# undef stderr
+# define _cb_platform_mkdir(Path) _mkdir((Path));
+# define MAX_ENVVAR 32767
+# define CB_PROC_INVALID INVALID_HANDLE_VALUE
+# define CB_HANDLE_INVALID INVALID_HANDLE_VALUE
+
+ typedef HANDLE CB_Handle;
+ typedef HANDLE CB_ProcHandle;
+#else
+# include <unistd.h>
+# include <fcntl.h>
+# include <sys/stat.h>
+# include <sys/wait.h>
+# include <sys/stat.h>
+# define _cb_platform_mkdir(Path) mkdir((Path), S_IRWXU | (S_IRGRP | S_IXGRP) | (S_IROTH | S_IXOTH));
+# define CB_PROC_INVALID -1
+# define CB_HANDLE_INVALID -1
+ typedef int32_t CB_Handle;
+ typedef pid_t CB_ProcHandle;
+#endif
+
+#define internal static
+#ifndef _assert_break
+# if OS_WINDOWS
+# define _assert_break() __debugbreak()
+# else
+# define _assert_break() __builtin_trap()
+# endif
+#endif
+#define Assert(COND) do { if (!(COND)) { _assert_break(); } } while (0)
+#define StackPush(Head, Nodeptr) \
+ LLPushFrontCustom((Head), (Head), (Nodeptr), next)
+#define StackPop(Head) (Head ? (Head = Head->next) : 0)
+#define LLPushFrontCustom(Head, Last, Nodeptr, Next) \
+ (!(Head) ? (Head) = (Nodeptr), (Last) = (Nodeptr) \
+ : ((Nodeptr)->Next = (Head), (Head) = (Nodeptr)))
+#define QueuePush(Head, Last, Nodeptr) \
+ LLPushBackCustom((Head), (Last), (Nodeptr), next)
+#define QueuePop(Head) (Head ? (Head = Head->next) : 0)
+#define LLPushBackCustom(Head, Last, Nodeptr, Next) \
+ (!(Head) ? (Head) = (Last) = (Nodeptr) \
+ : ((Last) ? ((Last)->Next = (Nodeptr), (Last) = (Nodeptr)) \
+ : ((Head)->Next = (Last) = (Nodeptr))))
+
+struct CB_PathList {
+ char **values;
+ size_t count;
+ size_t capacity;
+};
+typedef struct CB_PathList CB_Cmd;
+
+struct Cb_Cmd_RunArgs {
+ bool async;
+ bool reset;
+
+ CB_Handle stdin;
+ CB_Handle stdout;
+ CB_Handle stderr;
+};
+
+typedef struct {
+ int32_t status_code;
+ CB_ProcHandle handle;
+} CB_Process;
+
+typedef struct {
+ CB_Process *values;
+ size_t count;
+ size_t capacity;
+} CB_ProcessList;
+
+typedef uint8_t CB_AccessFlag;
+enum {
+ CB_AccessFlag_Read = 1 << 0,
+ CB_AccessFlag_Write = 1 << 1,
+ CB_AccessFlag_Append = 1 << 2,
+};
+
+typedef uint8_t CB_LogLevel;
+enum {
+ CB_LogLevel_Info,
+ CB_LogLevel_Warn,
+ CB_LogLevel_Error,
+};
+
+#ifndef CB_DYN_DEFAULT_CAPACITY
+# define CB_DYN_DEFAULT_CAPACITY 8
+#endif
+
+#ifndef CB_RECOMPILE_OPTIONS
+# define CB_RECOMPILE_OPTIONS
+#endif
+
+#if OS_WINDOWS
+# define CB_CMD_REBUILD_SELF(Exe_name, Builder_src) "cl.exe", "/Fe:", (Exe_name), \
+ (Builder_src), CB_RECOMPILE_OPTIONS
+#else
+# define CB_CMD_REBUILD_SELF(Exe_name, Builder_src) "cc", "-o", (Exe_name), (Builder_src), \
+ CB_RECOMPILE_OPTIONS
+#endif
+
+#define cb_dyn_reserve(Dynarr, HowMany) cb_dyn_reserve_custom((Dynarr), (HowMany), values, count, capacity)
+#define cb_dyn_free(Dynarr) cb_dyn_free_custom((Dynarr), values, count)
+#define cb_dyn_push(Dynarr, Node) cb_dyn_push_custom((Dynarr), (Node), values, count, capacity)
+#define cb_dyn_append(Dynarr, Array, Size) cb_dyn_append_custom((Dynarr), (Array), (Size), values, count, capacity)
+#define cb_cmd_push(Dynarr, Value) cb_dyn_push(Dynarr, Value)
+#define cb_cmd_append_dyn(Dynarr, Values, Count) cb_dyn_append((Dynarr), (Values), (Count));
+#define cb_cmd_append(Dynarr, ...) cb_dyn_append((Dynarr), \
+ ((char*[]){__VA_ARGS__}), \
+ (sizeof((char*[]){__VA_ARGS__}) / sizeof(char*)))
+#define cb_println(Level, Fmt, ...) cb_print((Level), Fmt "\n", ##__VA_ARGS__)
+#define cb_rebuild_self(argc, argv) _cb_rebuild(argc, argv, __FILE__, 0)
+#define cb_rebuild_self_with(argc, argv, ...) _cb_rebuild(argc, argv, __FILE__, __VA_ARGS__, 0)
+#define cb_is_outdated(OutputFile, ...) _cb_is_outdated((OutputFile), __VA_ARGS__, 0)
+#define cb_cmd_run(Cmd, ...) _cb_cmd_run((Cmd), (struct Cb_Cmd_RunArgs) { \
+ .async = false, \
+ .reset = true, \
+ __VA_ARGS__ \
+ })
+#define cb_proclist_push(Dynarr, Value) cb_dyn_push(Dynarr, Value)
+
+#define cb_dyn_free_custom(Dynarr, Values, Count) \
+ do { \
+ free((Dynarr)->Values); \
+ (Dynarr)->Count = 0; \
+ } while (0)
+#define cb_dyn_reserve_custom(Dynarr, HowMany, Values, Count, Capacity) \
+ do { \
+ if (!(Dynarr)->Capacity) { \
+ (Dynarr)->Capacity = CB_DYN_DEFAULT_CAPACITY; \
+ } \
+ while ((HowMany) > (Dynarr)->Capacity) { \
+ (Dynarr)->Capacity *= 2; \
+ } \
+ (Dynarr)->Values = realloc((Dynarr)->Values, (Dynarr)->Capacity * \
+ sizeof((Dynarr)->Values[0])); \
+ Assert((Dynarr)->Values); \
+ } while(0)
+#define cb_dyn_push_custom(Dynarr, Node, Values, Count, Capacity) \
+ do { \
+ cb_dyn_reserve_custom((Dynarr), (Dynarr)->Count + 1, \
+ Values, Count, Capacity); \
+ (Dynarr)->Values[(Dynarr)->Count++] = (Node); \
+ } while(0)
+#define cb_dyn_append_custom(Dynarr, Array, Size, Values, Count, Capacity) \
+ do { \
+ cb_dyn_reserve((Dynarr), (Dynarr)->Count + (Size)); \
+ memcpy((Dynarr)->Values + (Dynarr)->Count, (Array), \
+ (Size) * sizeof((Dynarr)->Values[0])); \
+ (Dynarr)->Count += (Size); \
+ } while(0)
+
+#define cb_handle_write(Handle, Content) _is_literal(Content) ? _cb_handle_write_lit(Handle, Content) : _cb_handle_write_dyn(Handle, Content)
+#define _cb_handle_write_lit(Handle, Content) \
+ _cb_handle_write((Handle), (Content), (sizeof((Content)) / sizeof(*(Content))))
+#define _cb_handle_write_dyn(Handle, Content) \
+ _cb_handle_write((Handle), (Content), strlen((Content)))
+#define _is_literal(x) _is_literal_(x)
+#define _is_literal_(x) _is_literal_f(#x, sizeof(#x) - 1)
+
+static char* cb_format(const char *format, ...);
+static void cb_print(CB_LogLevel level, const char *fmt, ...);
+static char* cb_getenv(char *varname);
+static bool cb_setenv(char *varname, char *value);
+static void cb_process_wait(CB_Process *handle);
+static void cb_proclist_wait(CB_ProcessList *procs);
+static CB_Handle cb_handle_open(char *path, CB_AccessFlag permission);
+static void cb_handle_close(CB_Handle fd);
+static char* cb_handle_read(CB_Handle fd);
+static bool cb_dir_create(char *path);
+static void cb_dir_delete(char *path);
+static void cb_file_delete(char *path);
+static bool cb_file_rename(char *path, char *to);
+static bool cb_file_exists(char *path);
+
+internal void _cb_handle_write(CB_Handle fd, char *buffer, size_t buffsize);
+internal char* _cb_format(const char *format, va_list args);
+internal bool _cb_need_rebuild(char *output_path, struct CB_PathList sources);
+internal void _cb_rebuild(int argc, char **argv, char *cb_src, ...);
+internal bool _cb_is_outdated(char *output, ...);
+internal CB_Process _cb_cmd_run(CB_Cmd *cmd, struct Cb_Cmd_RunArgs args);
+internal size_t _last_occurance_of(char *string, char ch);
+internal bool _is_literal_f(char *str, size_t l);
+
+
+// ==============================================================================
+// Implementation
+static char* cb_format(const char *format, ...) {
+ va_list args;
+ va_start(args, format);
+ char *res = _cb_format(format, args);
+ va_end(args);
+ return res;
+}
+
+static void cb_print(CB_LogLevel level, const char *fmt, ...) {
+#define ANSI_COLOR_RED "\x1b[31m"
+#define ANSI_COLOR_GREEN "\x1b[32m"
+#define ANSI_COLOR_YELLOW "\x1b[33m"
+#define ANSI_COLOR_BLUE "\x1b[34m"
+#define ANSI_COLOR_MAGENTA "\x1b[35m"
+#define ANSI_COLOR_CYAN "\x1b[36m"
+#define ANSI_COLOR_RESET "\x1b[0m"
+ va_list args;
+ switch (level) {
+ case CB_LogLevel_Info: {
+ printf(ANSI_COLOR_CYAN "[INFO] ");
+ } break;
+ case CB_LogLevel_Warn: {
+ printf(ANSI_COLOR_YELLOW "[WARNING] ");
+ } break;
+ case CB_LogLevel_Error: {
+ printf(ANSI_COLOR_RED "[ERROR] ");
+ } break;
+ default: printf(ANSI_COLOR_RESET); goto print_str;
+ }
+ printf(ANSI_COLOR_RESET);
+
+ print_str: ;
+ va_start(args, fmt);
+ printf("%s", _cb_format(fmt, args));
+ va_end(args);
+#undef ANSI_COLOR_RED
+#undef ANSI_COLOR_GREEN
+#undef ANSI_COLOR_YELLOW
+#undef ANSI_COLOR_BLUE
+#undef ANSI_COLOR_MAGENTA
+#undef ANSI_COLOR_CYAN
+#undef ANSI_COLOR_RESET
+}
+
+static char* cb_getenv(char *varname) {
+#if OS_WINDOWS
+ char *res = malloc(MAX_ENVVAR);
+ if (!GetEnvironmentVariableA(varname, res, MAX_ENVVAR)) {
+ free(res);
+ res = 0;
+ }
+ return res;
+#else
+ return getenv(varname);
+#endif
+}
+
+static bool cb_setenv(char *varname, char *value) {
+#if OS_WINDOWS
+ return SetEnvironmentVariableA(varname, value);
+#else
+ return !setenv(varname, value, true);
+#endif
+}
+
+static void cb_process_wait(CB_Process *proc) {
+ if (proc->handle == CB_PROC_INVALID) {
+ cb_println(CB_LogLevel_Warn, "Waiting on invalid process handle");
+ return;
+ }
+
+#if OS_WINDOWS
+ WaitForSingleObject(proc->handle, INFINITE);
+ GetExitCodeProcess(proc->handle, &proc->status_code);
+ CloseHandle(proc->handle);
+#else
+ int32_t status = 0;
+ Assert(waitpid(proc->handle, &status, 0) == proc->handle);
+ if (WIFEXITED(status)) {
+ proc->status_code = WEXITSTATUS(status);
+ } else if (WIFSIGNALED(status)) {
+ proc->status_code = WTERMSIG(status);
+ } else {
+ proc->status_code = 0;
+ }
+#endif
+}
+
+static void cb_proclist_wait(CB_ProcessList *procs) {
+ for (size_t i = 0; i < procs->count; ++i) {
+ cb_process_wait(&procs->values[i]);
+ }
+ cb_dyn_free(procs);
+}
+
+static CB_Handle cb_handle_open(char *path, CB_AccessFlag permission) {
+#if OS_WINDOWS
+ SECURITY_ATTRIBUTES security_attributes = {sizeof(SECURITY_ATTRIBUTES), 0, 0};
+ DWORD access_flags = 0;
+ DWORD share_mode = 0;
+ DWORD creation_disposition = OPEN_EXISTING;
+
+ if(permission & CB_AccessFlag_Read) { access_flags |= GENERIC_READ; }
+ if(permission & CB_AccessFlag_Write) {
+ access_flags |= GENERIC_WRITE;
+ creation_disposition = CREATE_ALWAYS;
+ }
+ if(permission & CB_AccessFlag_Append) {
+ access_flags |= FILE_APPEND_DATA;
+ creation_disposition = OPEN_ALWAYS;
+ }
+ return CreateFileA(path, access_flags,
+ share_mode, &security_attributes,
+ creation_disposition,
+ FILE_ATTRIBUTE_NORMAL, 0);
+#else
+ int32_t flags = O_CREAT;
+ if ((permission & CB_AccessFlag_Read) &&
+ ((permission & CB_AccessFlag_Write) || (permission & CB_AccessFlag_Append))) {
+ flags |= O_RDWR;
+ } else if (permission & CB_AccessFlag_Read) {
+ flags |= O_RDONLY;
+ } else if (permission & CB_AccessFlag_Write) {
+ flags |= O_WRONLY | O_TRUNC;
+ }
+ if (permission & CB_AccessFlag_Append) { flags |= O_APPEND; }
+ return open(path, flags, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+#endif
+}
+
+static void cb_handle_close(CB_Handle fd) {
+ if (fd == CB_HANDLE_INVALID) {
+ cb_println(CB_LogLevel_Warn, "Closing invalid handle");
+ return;
+ }
+
+#if OS_WINDOWS
+ FlushFileBuffers(fd);
+ CloseHandle(fd);
+#else
+ fsync(fd);
+ close(fd);
+#endif
+}
+
+static char* cb_handle_read(CB_Handle fd) {
+ if (fd == CB_HANDLE_INVALID) {
+ cb_println(CB_LogLevel_Warn, "Reading from invalid handle");
+ return 0;
+ }
+
+#if OS_WINDOWS
+ LARGE_INTEGER file_size = {0};
+ GetFileSizeEx(fd, &file_size);
+
+ uint64_t to_read = file_size.QuadPart;
+ uint64_t total_read = 0;
+ uint8_t *ptr = malloc(to_read + 1);
+
+ for(;total_read < to_read;) {
+ uint64_t amount64 = to_read - total_read;
+ uint32_t amount32 = amount64 > 0xFFFFFFFF ? 0xFFFFFFFF : (uint32_t)amount64;
+ OVERLAPPED overlapped = {0};
+ DWORD bytes_read = 0;
+ (void)ReadFile(fd, ptr + total_read, amount32, &bytes_read, &overlapped);
+ total_read += bytes_read;
+ if(bytes_read != amount32) { break; }
+ }
+
+ ptr[to_read] = 0;
+ if(total_read != to_read) {
+ free(ptr);
+ ptr = 0;
+ }
+ return ptr;
+#else
+ struct stat file_stat;
+ if (!fstat(fd, &file_stat)) {
+ char *res = malloc(file_stat.st_size);
+ if(pread(fd, res, file_stat.st_size, 0) >= 0) {
+ return res;
+ }
+ }
+ return 0;
+#endif
+}
+
+static bool cb_dir_create(char *path) {
+ int32_t mkdir_res = _cb_platform_mkdir(path);
+ if (mkdir_res < 0 && errno == ENOENT) {
+ size_t parent_end = _last_occurance_of(path, '/');
+ if (!parent_end) { return false; }
+ char *parent = malloc(parent_end + 1);
+ memcpy(parent, path, parent_end);
+ parent[parent_end] = 0;
+ cb_dir_create(parent);
+ free(parent);
+ _cb_platform_mkdir(path);
+ }
+
+ return !mkdir_res;
+}
+
+static void cb_dir_delete(char *path) {
+#if OS_WINDOWS
+ _rmdir(path);
+#else
+ rmdir(path);
+#endif
+}
+
+static void cb_file_delete(char *path) {
+#if OS_WINDOWS
+ _unlink(path);
+#else
+ unlink(path);
+#endif
+}
+
+static bool cb_file_rename(char *path, char *to) {
+#if OS_WINDOWS
+ return MoveFileEx(path, to,
+ MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH);
+#else
+ return !rename(path, to);
+#endif
+}
+
+static bool cb_file_exists(char *path) {
+ CB_Handle file = cb_handle_open(path, CB_AccessFlag_Read);
+ if (file == CB_HANDLE_INVALID) {
+ return false;
+ }
+ cb_handle_close(file);
+ return true;
+}
+
+
+internal bool _cb_is_outdated(char *output, ...) {
+ struct CB_PathList sources = {};
+ va_list args;
+ va_start(args, output);
+ for (;;) {
+ char *path = va_arg(args, char*);
+ if (!path) { break; }
+ cb_dyn_push(&sources, path);
+ }
+ va_end(args);
+ return _cb_need_rebuild(output, sources);
+}
+
+internal void _cb_handle_write(CB_Handle fd, char *buffer, size_t buffsize) {
+ if (fd == CB_HANDLE_INVALID) {
+ cb_println(CB_LogLevel_Warn, "Writing to invalid handle");
+ return;
+ }
+
+ while (!buffer[buffsize - 1]) { buffsize -= 1; }
+#if OS_WINDOWS
+ uint64_t to_write = buffsize;
+ uint64_t total_write = 0;
+ char *ptr = buffer;
+ for(;total_write < to_write;) {
+ uint64_t amount64 = to_write - total_write;;
+ uint32_t amount32 = amount64 > 0xFFFFFFFF ? 0xFFFFFFFF : (uint32_t)amount64;
+ DWORD bytes_written = 0;
+ WriteFile(fd, ptr + total_write, amount32, &bytes_written, 0);
+ total_write += bytes_written;
+ if(bytes_written != amount32) { break; }
+ }
+#else
+ write(fd, buffer, buffsize);
+#endif
+}
+
+internal char* _cb_format(const char *format, va_list args) {
+ va_list args2;
+ va_copy(args2, args);
+ uint32_t needed_bytes = vsnprintf(0, 0, format, args2) + 1;
+ va_end(args2);
+
+ char *res = malloc(needed_bytes);
+ (void)vsnprintf(res, needed_bytes, format, args);
+ return res;
+}
+
+internal CB_Process _cb_cmd_run(CB_Cmd *cmd, struct Cb_Cmd_RunArgs args) {
+ CB_Process res = {};
+
+#if OS_WINDOWS
+ char cmdline[32767] = {};
+ size_t offset = 0;
+ for (size_t i = 0; i < cmd->count; ++i) {
+ strcat(cmdline, strstr(cmd->values[i], " ") || strstr(cmd->values[i], "\\")
+ ? cb_format("\"%s\"", cmd->values[i])
+ : cmd->values[i]);
+ if (i != cmd->count - 1) { strcat(cmdline, " "); }
+ }
+
+ STARTUPINFO si = {0};
+ si.cb = sizeof(si);
+ si.dwFlags = STARTF_USESTDHANDLES;
+ si.hStdInput = args.stdin ? args.stdin : GetStdHandle(STD_INPUT_HANDLE);
+ si.hStdOutput = args.stdout ? args.stdout : GetStdHandle(STD_OUTPUT_HANDLE);
+ si.hStdError = args.stderr ? args.stderr : GetStdHandle(STD_ERROR_HANDLE);
+
+ PROCESS_INFORMATION pi = {};
+ if (!CreateProcessA(0, cmdline, 0, 0, TRUE, 0, 0, 0, &si, &pi)) {
+ LPVOID lpMsgBuf;
+ DWORD error = GetLastError();
+ FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
+ FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS,
+ 0, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (LPTSTR)&lpMsgBuf, 0, 0);
+ cb_println(CB_LogLevel_Error, "Child process `%s` creation failed with error %u: %s",
+ cmd->values[0], error, lpMsgBuf);
+ exit(-1);
+ }
+
+ CloseHandle(pi.hThread);
+ res.handle = pi.hProcess;
+#else
+ res.handle = fork();
+ if (res.handle < 0) {
+ cb_println(CB_LogLevel_Error, "Child process `%s` creation failed with error %d: %s\n",
+ cmd->values[0], errno, strerror(errno));
+ exit(-1);
+ } else if (!res.handle) {
+ if (args.stdout) { dup2(args.stdout, STDOUT_FILENO); }
+ if (args.stderr) { dup2(args.stderr, STDERR_FILENO); }
+ if (args.stdin) {
+ lseek(args.stdin, 0, SEEK_SET);
+ dup2(args.stdin, STDIN_FILENO);
+ }
+
+ CB_Cmd _cmd = {};
+ cb_cmd_append_dyn(&_cmd, cmd->values, cmd->count);
+ cb_cmd_push(&_cmd, 0);
+ if (execvp(_cmd.values[0], _cmd.values) < 0) {
+ cb_println(CB_LogLevel_Error, "Child process `%s` creation failed with error %d: %s\n",
+ cmd->values[0], errno, strerror(errno));
+ exit(-1);
+ }
+ // NOTE(lb): unreachable, execvp only returns on error.
+ }
+#endif
+
+ if (args.reset) {
+ cmd->count = 0;
+ }
+ if (!args.async) {
+ cb_process_wait(&res);
+ res.handle = CB_PROC_INVALID;
+ }
+ return res;
+}
+
+internal void _cb_rebuild(int argc, char **argv, char *builder_src, ...) {
+ Assert(argc >= 1);
+ char *exe_name = argv[0];
+
+ struct CB_PathList sources = {};
+ cb_dyn_push(&sources, builder_src);
+ va_list args;
+ va_start(args, builder_src);
+ for (;;) {
+ char *path = va_arg(args, char*);
+ if (!path) { break; }
+ cb_dyn_push(&sources, path);
+ }
+ va_end(args);
+
+ if (!_cb_need_rebuild(exe_name, sources)) {
+ cb_dyn_free(&sources);
+ return;
+ }
+ cb_println(CB_LogLevel_Info, "rebuilding %s", exe_name);
+
+#if OS_WINDOWS
+ char *exe_name_old = cb_format("%s.old", exe_name);
+ if (!cb_file_rename(exe_name, exe_name_old)) {
+ cb_println(CB_LogLevel_Info, "File rename failed: %s -> %s",
+ exe_name, exe_name_old);
+ exit(-1);
+ }
+#endif
+
+ CB_Cmd cmd = {};
+ cb_cmd_append(&cmd, CB_CMD_REBUILD_SELF(exe_name, builder_src));
+ cb_print(CB_LogLevel_Info, "running: `");
+ for (int32_t i = 0; i < cmd.count; ++i) {
+ printf("%s ", cmd.values[i]);
+ }
+ printf("\b`\n");
+ CB_Process recompiler = cb_cmd_run(&cmd);
+ if (recompiler.status_code) {
+#if OS_WINDOWS
+ cb_file_rename(exe_name_old, exe_name);
+#endif
+ exit(-1);
+ }
+
+ cb_cmd_push(&cmd, exe_name);
+ cb_cmd_append_dyn(&cmd, argv, argc);
+ (void)cb_cmd_run(&cmd);
+ exit(0);
+}
+
+internal bool _cb_need_rebuild(char *output_path, struct CB_PathList sources) {
+#if OS_WINDOWS
+ FILETIME output_mtime_large = {};
+ HANDLE output_handle = CreateFileA(output_path, GENERIC_READ,
+ FILE_SHARE_READ, 0, OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL, 0);
+ if (output_handle == INVALID_HANDLE_VALUE ||
+ !GetFileTime(output_handle, 0, 0, &output_mtime_large)) {
+ CloseHandle(output_handle);
+ return true;
+ }
+ CloseHandle(output_handle);
+
+ ULARGE_INTEGER output_mtime = {};
+ output_mtime.LowPart = output_mtime_large.dwLowDateTime;
+ output_mtime.HighPart = output_mtime_large.dwHighDateTime;
+
+ for (size_t i = 0; i < sources.count; ++i) {
+ FILETIME source_mtime_large = {};
+ HANDLE source_handle = CreateFileA(sources.values[i], GENERIC_READ,
+ FILE_SHARE_READ, 0, OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL, 0);
+ if (source_handle == INVALID_HANDLE_VALUE) { return true; }
+ if (!GetFileTime(source_handle, 0, 0, &source_mtime_large)) {
+ CloseHandle(output_handle);
+ return true;
+ }
+ CloseHandle(output_handle);
+
+ ULARGE_INTEGER source_mtime = {};
+ source_mtime.LowPart = source_mtime_large.dwLowDateTime;
+ source_mtime.HighPart = source_mtime_large.dwHighDateTime;
+ if (output_mtime.QuadPart < source_mtime.QuadPart) {
+ return true;
+ }
+ }
+ return false;
+#else
+ struct stat output_stat = {};
+ if (stat(output_path, &output_stat) < 0) { return true; }
+ for (size_t i = 0; i < sources.count; ++i) {
+ struct stat source_stat = {};
+ if (stat(sources.values[i], &source_stat) < 0) {
+ rebuild_failure:
+ cb_println(CB_LogLevel_Error,
+ "`%s` modification time unreadable: %s",
+ sources.values[i], strerror(errno));
+ exit(-1);
+ }
+ if (output_stat.st_mtime < source_stat.st_mtime) { return true; }
+ }
+ return false;
+#endif
+}
+
+internal size_t _last_occurance_of(char *string, char ch) {
+ char *res = string;
+ for (char *curr = string; curr && *curr; ++curr) {
+ if (*curr == ch) { res = curr; }
+ }
+ return res - string;
+}
+
+internal bool _is_literal_f(char *str, size_t l) {
+ const char *e = str + l;
+ if (str[0] == 'L') str++;
+ if (str[0] != '"') return false;
+ for (; str != e; str = strchr(str + 1, '"')) {
+ if (!str) { return false; }
+ for (str++;
+ *str == '\f' || *str == '\n' || *str == '\r' ||
+ *str == '\t' || *str == '\v';
+ ++str);
+ if (*str != '"') { return false; }
+ }
+ return true;
+}
+
+#endif
--- /dev/null
+global threadvar TlsContext tls_ctx = {0};
+
+fn bool is_power_of_two(usize value) {
+ return !(value & (value - 1));
+}
+
+fn usize align_forward(usize ptr, usize align) {
+ Assert(is_power_of_two(align));
+ usize mod = ptr & (align - 1);
+ return (mod ? ptr = ptr + align - mod : ptr);
+}
+
+fn usize os_page_size(void) {
+ local usize res = 0;
+ if (!res) {
+#if OS_WINDOWS
+ SYSTEM_INFO sys_info = {0};
+ GetSystemInfo(&sys_info);
+ res = sys_info.dwPageSize;
+#elif OS_LINUX
+ res = (usize)getpagesize();
+#elif OS_BSD
+ usize page_sizes[3] = {0};
+ getpagesizes(page_sizes, 3);
+ res = page_sizes[0];
+#endif
+ }
+ return res;
+}
+
+fn void* os_reserve(usize size) {
+#if OS_WINDOWS
+ return VirtualAlloc(0, size, MEM_RESERVE, PAGE_READWRITE);
+#else
+ void *res = mmap(0, size, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ if (res == MAP_FAILED) {res = 0;}
+ return res;
+#endif
+}
+
+fn void os_release(void *base, usize size) {
+#if OS_WINDOWS
+ (void)VirtualFree(base, size, MEM_RELEASE);
+#else
+ (void)munmap(base, size);
+#endif
+}
+
+fn void os_commit(void *base, usize size) {
+#if OS_WINDOWS
+ (void)VirtualAlloc(base, size, MEM_COMMIT, PAGE_READWRITE);
+#else
+ Assert(mprotect(base, size, PROT_READ | PROT_WRITE) == 0);
+#endif
+}
+
+fn void os_decommit(void *base, usize size) {
+#if OS_WINDOWS
+ (void)VirtualFree(base, size, MEM_DECOMMIT);
+#else
+ (void)mprotect(base, size, PROT_NONE);
+ (void)madvise(base, size, MADV_DONTNEED);
+#endif
+}
+
+internal Arena *_arena_build(ArenaArgs args) {
+ usize reserve = align_forward(args.reserve_size, os_page_size());
+ usize commit = align_forward(args.commit_size, os_page_size());
+ void *mem = os_reserve(reserve);
+ if (!mem) { return 0; }
+ os_commit(mem, commit);
+
+ Arena *arena = (Arena *)mem;
+ arena->base = (void *)((usize)mem + sizeof(Arena));
+ arena->head = 0;
+ arena->flags = args.flags;
+ arena->commit_size = commit;
+ arena->reserve_size = reserve;
+ arena->commits = 1;
+ return arena;
+}
+
+fn void arena_pop(Arena *arena, usize bytes) {
+ arena->head = (usize)ClampBot((ssize)arena->head - (ssize)bytes, 0);
+ usize pages2decommit = (usize)ceil((f64)bytes / (f64)arena->commit_size);
+ if (pages2decommit) {
+ arena->commits = (usize)ClampBot((ssize)arena->commits - (ssize)pages2decommit, 0);
+ os_decommit((void *)align_forward((usize)arena->base + arena->head,
+ arena->commit_size),
+ pages2decommit * arena->commit_size);
+ }
+}
+
+fn void arena_free(Arena *arena) {
+ os_release((void *)((usize)arena->base - sizeof(Arena)), arena->reserve_size);
+}
+
+fn void *arena_push(Arena *arena, usize size, usize align) {
+ if (!align) { align = DefaultAlignment; }
+ usize res = align_forward((usize)arena->base + arena->head, align);
+ usize offset = res - ((usize)arena->base + arena->head);
+ usize new_head = arena->head + size + offset + sizeof(Arena);
+
+ Assert(new_head < arena->reserve_size);
+ if (new_head > arena->commits * arena->commit_size) {
+ usize need2commit_bytes = align_forward(new_head, arena->commit_size);
+ arena->commits = need2commit_bytes/arena->commit_size;
+ os_commit((void *)((usize)arena->base - sizeof(Arena)), need2commit_bytes);
+ }
+
+ arena->head = new_head - sizeof(Arena);
+ memzero((void*)res, size);
+ return (void*)res;
+}
+
+fn Scratch tmp_begin(Arena *arena) {
+ Scratch scratch = { arena, arena->head };
+ return scratch;
+}
+
+fn void tmp_end(Scratch tmp) {
+ arena_pop(tmp.arena, tmp.arena->head - tmp.pos);
+ tmp.arena->head = tmp.pos;
+}
+
+fn Arena *tls_get_scratch(Arena **conflicts, usize count) {
+ if(!tls_ctx.arenas[0]) {
+ for(usize i = 0; i < Arrsize(tls_ctx.arenas); ++i) {
+ tls_ctx.arenas[i] = ArenaBuild(.reserve_size = TLS_CTX_SIZE);
+ }
+ }
+ for(usize i = 0; i < Arrsize(tls_ctx.arenas); ++i) {
+ bool has_conflict = false;
+ for(usize j = 0; j < count; ++j) {
+ if(tls_ctx.arenas[i] == conflicts[j]) {
+ has_conflict = true;
+ break;
+ }
+ }
+ if(!has_conflict) {
+ return tls_ctx.arenas[i];
+ }
+ }
+ return 0;
+}
--- /dev/null
+#ifndef ARENA_H
+#define ARENA_H
+
+#include <math.h>
+#if OS_WINDOWS
+# include <windows.h>
+#else
+# include <unistd.h>
+# include <sys/mman.h>
+#endif
+
+#define New(...) Newx(__VA_ARGS__,New3,New2)(__VA_ARGS__)
+#define Newx(a,b,c,d,...) d
+#define New2(arenaptr, type) \
+ (type*)arena_push(arenaptr, sizeof(type), AlignOf(type))
+#define New3(arenaptr, type, count) \
+ (type*)arena_push(arenaptr, (count) * sizeof(type), AlignOf(type))
+
+#define DefaultAlignment (2 * sizeof(void*))
+#define ArenaDefaultReserveSize MB(4)
+#define ArenaDefaultCommitSize KiB(4)
+
+typedef u64 ArenaFlags;
+enum {
+ Arena_Growable = 1 << 0,
+ Arena_UseHugePage = 1 << 1,
+};
+
+typedef struct {
+ usize commit_size;
+ usize reserve_size;
+ ArenaFlags flags;
+} ArenaArgs;
+
+typedef struct Arena {
+ void *base;
+ usize head;
+
+ u64 flags;
+ usize commits;
+ usize commit_size;
+ usize reserve_size;
+
+ struct Arena *next;
+ struct Arena *prev;
+} Arena;
+
+typedef struct {
+ Arena *arena;
+ usize pos;
+} Scratch;
+
+fn bool is_power_of_two(usize value);
+fn usize align_forward(usize ptr, usize align);
+
+fn void arena_pop(Arena *arena, usize bytes);
+fn void arena_free(Arena *arena);
+fn void *arena_push(Arena *arena, usize size, usize align);
+
+internal Arena *_arena_build(ArenaArgs args);
+#define ArenaBuild(...) _arena_build((ArenaArgs) { \
+ .commit_size = ArenaDefaultCommitSize, \
+ .reserve_size = ArenaDefaultReserveSize, \
+ __VA_ARGS__ \
+})
+
+fn Scratch tmp_begin(Arena *arena);
+fn void tmp_end(Scratch tmp);
+
+
+typedef struct {
+ Arena *arenas[2];
+} TlsContext;
+
+fn Arena *tls_get_scratch(Arena **conflicts, usize count);
+
+#define ScratchBegin(conflicts, count) tmp_begin(tls_get_scratch((conflicts), \
+ (count)))
+#define ScratchEnd(scratch) tmp_end((scratch))
+
+#endif
--- /dev/null
+#ifndef BASE_H
+#define BASE_H
+
+#include <string.h>
+
+#if defined(__GNUC__) && !defined(__llvm__) && !defined(__INTEL_COMPILER)
+# define COMPILER_GCC 1
+#elif defined(__clang__) && !defined(_MSC_VER)
+# define COMPILER_CLANG 1
+#elif defined(_MSC_VER)
+# define COMPILER_CL 1
+# if defined(_M_IX86)
+# define ARCH_X86 1
+# elif defined(_M_AMD64)
+# define ARCH_X64 1
+# elif defined(_M_ARM)
+# define ARCH_ARM32 1
+# elif defined(_M_ARM64)
+# define ARCH_ARM64 1
+# else
+# error "Unsupported platform"
+# endif
+#else
+# error "Unsupported compiler"
+#endif
+
+#if defined(__gnu_linux__)
+# define OS_LINUX 1
+#elif defined(__unix__)
+# define OS_BSD 1
+#elif defined(_WIN32)
+# define OS_WINDOWS 1
+#elif defined(__APPLE__)
+# define OS_MAC 1
+#else
+# define OS_NONE 1
+#endif
+
+#if !defined(COMPILER_GCC)
+# define COMPILER_GCC 0
+#endif
+#if !defined(COMPILER_CLANG)
+# define COMPILER_CLANG 0
+#endif
+#if !defined(COMPILER_CL)
+# define COMPILER_CL 0
+#endif
+
+#if !defined(OS_LINUX)
+# define OS_LINUX 0
+#endif
+#if !defined(OS_BSD)
+# define OS_BSD 0
+#endif
+#if !defined(OS_MAC)
+# define OS_MAC 0
+#endif
+#if !defined(OS_WINDOWS)
+# define OS_WINDOWS 0
+#endif
+#if !defined(OS_NONE)
+# define OS_NONE 0
+#endif
+
+#if defined(DEBUG)
+# undef DEBUG
+# define DEBUG 1
+# define NDEBUG 0
+#else
+# define DEBUG 0
+# define NDEBUG 1
+#endif
+
+#define TLS_CTX_SIZE MB(64)
+
+#if COMPILER_GCC
+# define AlignOf(TYPE) __alignof__(TYPE)
+#elif COMPILER_CLANG
+# define AlignOf(TYPE) __alignof(TYPE)
+#elif COMPILER_CL
+# define AlignOf(TYPE) __alignof(TYPE)
+#else
+# define AlignOf(TYPE) 1
+#endif
+
+#if COMPILER_CL
+# define threadvar __declspec(thread)
+#elif COMPILER_CLANG || COMPILER_GCC
+# define threadvar __thread
+#endif
+
+#define _stmt(S) do { S } while (0)
+
+#ifndef _assert_break
+# if OS_WINDOWS
+# define _assert_break() __debugbreak()
+# else
+# define _assert_break() __builtin_trap()
+# endif
+#endif
+
+#define StaticAssert(C, ID) static u8 Glue(ID, __LINE__)[(C) ? 1 : -1]
+#define Assert(COND) _stmt(if (!(COND)) { _assert_break(); })
+
+#define Stringify_(S) (#S)
+#define Stringify(S) Stringify_(S)
+
+#define Glue_(A, B) (A##B)
+#define Glue(A, B) Glue_(A,B)
+
+#define Arrsize(ARR) (sizeof((ARR)) / sizeof(*(ARR)))
+#define Max(a, b) ((a) >= (b) ? (a) : (b))
+#define Min(a, b) ((a) <= (b) ? (a) : (b))
+#define ClampTop(a, b) Min((a), (b))
+#define ClampBot(a, b) Max((a), (b))
+#define Clamp(v, min, max) ClampBot(ClampTop(v, max), min)
+#define Swap(a, b, _tmp) ((_tmp) = (a), (a) = (b), (b) = (_tmp))
+#define Abs(a) ((a) >= 0 ? (a) : (-(a)))
+#define Random(low, high) ((rand() % (high + 1 - low)) + low)
+
+#define unused(x) (void)(x)
+#define unreachable() Assert(false)
+#define memcopy(Dest, Src, Size) memcpy((Dest), (Src), (Size))
+#define memzero(Dest, Size) memset((Dest), 0, (Size))
+
+#define KiB(BYTES) ((BYTES)*1024)
+#define MB(BYTES) (KiB((BYTES)) * 1024)
+#define GB(BYTES) (MB((BYTES)) * 1024ULL)
+#define TB(BYTES) (GB((BYTES)) * 1024ULL)
+
+#define Thousand(x) ((x)*1000)
+#define Million(x) ((x)*1000000ULL)
+
+#define fallthrough
+#define internal static
+#define global static
+#define local static
+#define fn static
+
+// =============================================================================
+// Singly Linked List
+#define StackPush(Head, Nodeptr) \
+ LLPushFrontCustom((Head), (Head), (Nodeptr), next)
+
+#define StackPop(Head) (Head ? (Head = Head->next) : 0)
+
+#define LLPushFrontCustom(Head, Last, Nodeptr, Next) \
+ (!(Head) ? (Head) = (Nodeptr), (Last) = (Nodeptr) \
+ : ((Nodeptr)->Next = (Head), (Head) = (Nodeptr)))
+
+#define QueuePush(Head, Last, Nodeptr) \
+ LLPushBackCustom((Head), (Last), (Nodeptr), next)
+
+#define QueuePop(Head) (Head ? (Head = Head->next) : 0)
+
+#define LLPushBackCustom(Head, Last, Nodeptr, Next) \
+ (!(Head) ? (Head) = (Last) = (Nodeptr) \
+ : ((Last) ? ((Last)->Next = (Nodeptr), (Last) = (Nodeptr)) \
+ : ((Head)->Next = (Last) = (Nodeptr))))
+
+// =============================================================================
+// Doubly Linked List
+#define DLLPushFront(Head, Last, Nodeptr) \
+ DLLPushFrontCustom(Head, Last, Nodeptr, next, prev)
+
+#define DLLPushBack(Head, Last, Nodeptr) \
+ DLLPushBackCustom(Head, Last, Nodeptr, next, prev)
+
+#define DLLPushFrontCustom(Head, Last, Nodeptr, Next, Prev) \
+ (!(Head) ? (Head) = (Last) = (Nodeptr) \
+ : ((Nodeptr)->Next = (Head), (Head)->Prev = (Nodeptr), \
+ (Head) = (Nodeptr)))
+
+#define DLLPushBackCustom(DLLNodeHead, DLLNodeLast, NodeToInsertptr, Next, \
+ Prev) \
+ (!(DLLNodeHead) \
+ ? (DLLNodeHead) = (DLLNodeLast) = (NodeToInsertptr) \
+ : ((DLLNodeLast)->Next = (NodeToInsertptr), \
+ (NodeToInsertptr)->Prev = (DLLNodeLast), \
+ (DLLNodeLast) = (NodeToInsertptr)))
+
+#define DLLDelete(Head, Last, Nodeptr) \
+ ((Head) == (Last) && (Head) == (Nodeptr) \
+ ? (Head) = (Last) = 0 \
+ : ((Last) == (Nodeptr) \
+ ? ((Last) = (Last)->prev, (Last)->next = 0) \
+ : ((Head) == (Nodeptr) \
+ ? ((Head) = (Head)->next, (Head)->prev = 0) \
+ : ((Nodeptr)->prev->next = (Nodeptr)->next, \
+ (Nodeptr)->next->prev = (Nodeptr)->prev))))
+
+#define DLLPop(Head, Last) DLLPopBack(Head, Last)
+#define DLLPopBack(Head, Last) \
+ (!(Last) \
+ ? 0 \
+ : (!(Last)->prev ? (Head) = (Last) = 0 \
+ : ((Last)->prev->next = 0, (Last) = (Last)->prev)))
+
+#define DLLPopFront(Head, Last) \
+ (!(Head) \
+ ? 0 \
+ : (!(Head)->next ? (Head) = (Last) = 0 \
+ : ((Head)->next->prev = 0, (Head) = (Head)->next)))
+// =============================================================================
+
+#include <stdint.h>
+#include <stddef.h>
+typedef float f32;
+typedef double f64;
+typedef long double f128;
+
+typedef int8_t s8;
+typedef uint8_t u8;
+typedef int16_t s16;
+typedef uint16_t u16;
+typedef int32_t s32;
+typedef uint32_t u32;
+typedef int64_t s64;
+typedef uint64_t u64;
+typedef ptrdiff_t ssize;
+typedef size_t usize;
+
+#if __STDC_VERSION__ >= 199901L
+# include <stdbool.h>
+#else
+ typedef enum {false, true} bool;
+#endif
+
+#define U8_MAX 0xFF
+#define U8_MIN 0
+#define U16_MAX 0xFFFF
+#define U16_MIN 0
+#define U32_MAX 0xFFFFFFFF
+#define U32_MIN 0
+#define U64_MAX 0xFFFFFFFFFFFFFFFF
+#define U64_MIN 0
+#define S8_MAX 0x7F
+#define S8_MIN (-0x80)
+#define S16_MAX 0x7FFF
+#define S16_MIN (-0x8000)
+#define S32_MAX 0x7FFFFFFF
+#define S32_MIN (-0x80000000)
+#define S64_MAX 0x7FFFFFFFFFFFFFFF
+#define S64_MIN (-0x8000000000000000)
+
+#define F32_MAX 3.40282347E+38
+#define F32_MIN -F32_MAX
+#define F64_MAX 1.7976931348623157E+308
+#define F64_MIN -F64_MAX
+
+global const u32 bitmask1 = 0x00000001;
+global const u32 bitmask2 = 0x00000003;
+global const u32 bitmask3 = 0x00000007;
+global const u32 bitmask4 = 0x0000000f;
+global const u32 bitmask5 = 0x0000001f;
+global const u32 bitmask6 = 0x0000003f;
+global const u32 bitmask7 = 0x0000007f;
+global const u32 bitmask8 = 0x000000ff;
+global const u32 bitmask9 = 0x000001ff;
+global const u32 bitmask10 = 0x000003ff;
+global const u32 bitmask11 = 0x000007ff;
+global const u32 bitmask12 = 0x00000fff;
+global const u32 bitmask13 = 0x00001fff;
+global const u32 bitmask14 = 0x00003fff;
+global const u32 bitmask15 = 0x00007fff;
+global const u32 bitmask16 = 0x0000ffff;
+global const u32 bitmask17 = 0x0001ffff;
+global const u32 bitmask18 = 0x0003ffff;
+global const u32 bitmask19 = 0x0007ffff;
+global const u32 bitmask20 = 0x000fffff;
+global const u32 bitmask21 = 0x001fffff;
+global const u32 bitmask22 = 0x003fffff;
+global const u32 bitmask23 = 0x007fffff;
+global const u32 bitmask24 = 0x00ffffff;
+global const u32 bitmask25 = 0x01ffffff;
+global const u32 bitmask26 = 0x03ffffff;
+global const u32 bitmask27 = 0x07ffffff;
+global const u32 bitmask28 = 0x0fffffff;
+global const u32 bitmask29 = 0x1fffffff;
+global const u32 bitmask30 = 0x3fffffff;
+global const u32 bitmask31 = 0x7fffffff;
+global const u32 bitmask32 = 0xffffffff;
+
+global const u64 bitmask33 = 0x00000001ffffffffull;
+global const u64 bitmask34 = 0x00000003ffffffffull;
+global const u64 bitmask35 = 0x00000007ffffffffull;
+global const u64 bitmask36 = 0x0000000fffffffffull;
+global const u64 bitmask37 = 0x0000001fffffffffull;
+global const u64 bitmask38 = 0x0000003fffffffffull;
+global const u64 bitmask39 = 0x0000007fffffffffull;
+global const u64 bitmask40 = 0x000000ffffffffffull;
+global const u64 bitmask41 = 0x000001ffffffffffull;
+global const u64 bitmask42 = 0x000003ffffffffffull;
+global const u64 bitmask43 = 0x000007ffffffffffull;
+global const u64 bitmask44 = 0x00000fffffffffffull;
+global const u64 bitmask45 = 0x00001fffffffffffull;
+global const u64 bitmask46 = 0x00003fffffffffffull;
+global const u64 bitmask47 = 0x00007fffffffffffull;
+global const u64 bitmask48 = 0x0000ffffffffffffull;
+global const u64 bitmask49 = 0x0001ffffffffffffull;
+global const u64 bitmask50 = 0x0003ffffffffffffull;
+global const u64 bitmask51 = 0x0007ffffffffffffull;
+global const u64 bitmask52 = 0x000fffffffffffffull;
+global const u64 bitmask53 = 0x001fffffffffffffull;
+global const u64 bitmask54 = 0x003fffffffffffffull;
+global const u64 bitmask55 = 0x007fffffffffffffull;
+global const u64 bitmask56 = 0x00ffffffffffffffull;
+global const u64 bitmask57 = 0x01ffffffffffffffull;
+global const u64 bitmask58 = 0x03ffffffffffffffull;
+global const u64 bitmask59 = 0x07ffffffffffffffull;
+global const u64 bitmask60 = 0x0fffffffffffffffull;
+global const u64 bitmask61 = 0x1fffffffffffffffull;
+global const u64 bitmask62 = 0x3fffffffffffffffull;
+global const u64 bitmask63 = 0x7fffffffffffffffull;
+global const u64 bitmask64 = 0xffffffffffffffffull;
+
+global const u32 bit1 = (1ul << 0);
+global const u32 bit2 = (1ul << 1);
+global const u32 bit3 = (1ul << 2);
+global const u32 bit4 = (1ul << 3);
+global const u32 bit5 = (1ul << 4);
+global const u32 bit6 = (1ul << 5);
+global const u32 bit7 = (1ul << 6);
+global const u32 bit8 = (1ul << 7);
+global const u32 bit9 = (1ul << 8);
+global const u32 bit10 = (1ul << 9);
+global const u32 bit11 = (1ul << 10);
+global const u32 bit12 = (1ul << 11);
+global const u32 bit13 = (1ul << 12);
+global const u32 bit14 = (1ul << 13);
+global const u32 bit15 = (1ul << 14);
+global const u32 bit16 = (1ul << 15);
+global const u32 bit17 = (1ul << 16);
+global const u32 bit18 = (1ul << 17);
+global const u32 bit19 = (1ul << 18);
+global const u32 bit20 = (1ul << 19);
+global const u32 bit21 = (1ul << 20);
+global const u32 bit22 = (1ul << 21);
+global const u32 bit23 = (1ul << 22);
+global const u32 bit24 = (1ul << 23);
+global const u32 bit25 = (1ul << 24);
+global const u32 bit26 = (1ul << 25);
+global const u32 bit27 = (1ul << 26);
+global const u32 bit28 = (1ul << 27);
+global const u32 bit29 = (1ul << 28);
+global const u32 bit30 = (1ul << 29);
+global const u32 bit31 = (1ul << 30);
+global const u32 bit32 = (1ul << 31);
+
+global const u64 bit33 = (1ull << 32);
+global const u64 bit34 = (1ull << 33);
+global const u64 bit35 = (1ull << 34);
+global const u64 bit36 = (1ull << 35);
+global const u64 bit37 = (1ull << 36);
+global const u64 bit38 = (1ull << 37);
+global const u64 bit39 = (1ull << 38);
+global const u64 bit40 = (1ull << 39);
+global const u64 bit41 = (1ull << 40);
+global const u64 bit42 = (1ull << 41);
+global const u64 bit43 = (1ull << 42);
+global const u64 bit44 = (1ull << 43);
+global const u64 bit45 = (1ull << 44);
+global const u64 bit46 = (1ull << 45);
+global const u64 bit47 = (1ull << 46);
+global const u64 bit48 = (1ull << 47);
+global const u64 bit49 = (1ull << 48);
+global const u64 bit50 = (1ull << 49);
+global const u64 bit51 = (1ull << 50);
+global const u64 bit52 = (1ull << 51);
+global const u64 bit53 = (1ull << 52);
+global const u64 bit54 = (1ull << 53);
+global const u64 bit55 = (1ull << 54);
+global const u64 bit56 = (1ull << 55);
+global const u64 bit57 = (1ull << 56);
+global const u64 bit58 = (1ull << 57);
+global const u64 bit59 = (1ull << 58);
+global const u64 bit60 = (1ull << 59);
+global const u64 bit61 = (1ull << 60);
+global const u64 bit62 = (1ull << 61);
+global const u64 bit63 = (1ull << 62);
+global const u64 bit64 = (1ull << 63);
+
+#endif
--- /dev/null
+#include <stdint.h>
+#include <stdio.h>
+
+#include "base.h"
+#include "arena.h"
+#include "arena.c"
+
+fn u8 utf8_encode(u8 *res, u32 num) {
+ if (num <= 0x7F) {
+ res[0] = (u8)num;
+ return 1;
+ } else if (num <= 0x7FF) {
+ res[0] = (u8)(0xC0 | (num >> 6));
+ res[1] = 0x80 | (num & 0x3F);
+ return 2;
+ } else if (num <= 0xFFFF) {
+ res[0] = (u8)(0xE0 | (num >> 12));
+ res[1] = 0x80 | ((num >> 6) & 0x3F);
+ res[2] = 0x80 | (num & 0x3F);
+ return 3;
+ } else if (num <= 0x10FFFF) {
+ res[0] = (u8)(0xF0 | (num >> 18));
+ res[1] = 0x80 | ((num >> 12) & 0x3F);
+ res[2] = 0x80 | ((num >> 6) & 0x3F);
+ res[3] = 0x80 | (num & 0x3F);
+ return 4;
+ } else {
+ unreachable();
+ return U8_MAX;
+ }
+}
+
+fn u32 utf8_decode(u8 *glyph_start, u8 *byte_len) {
+ u32 res = 0;
+ if ((*glyph_start & 0x80) == 0) {
+ res = *glyph_start;
+ *byte_len = 1;
+ } else if ((*glyph_start & 0xE0) == 0xC0) {
+ res = glyph_start[1] & 0x3F;
+ res |= (u32)((glyph_start[0] & 0x1F) << 6);
+ *byte_len = 2;
+ } else if ((*glyph_start & 0xF0) == 0xE0) {
+ res = glyph_start[2] & 0x3F;
+ res |= (u32)((glyph_start[1] & 0x3F) << 6);
+ res |= (u32)((glyph_start[0] & 0xf) << 12);
+ *byte_len = 3;
+ } else if ((*glyph_start & 0xF8) == 0xF0) {
+ res = glyph_start[3] & 0x3F;
+ res |= (u32)((glyph_start[2] & 0x3F) << 6);
+ res |= (u32)((glyph_start[1] & 0x3F) << 12);
+ res |= (u32)((glyph_start[0] & 0x7) << 18);
+ *byte_len = 4;
+ } else {
+ unreachable();
+ }
+ return res;
+}
+
+
+typedef struct {
+ u8 *bytes;
+ ssize size;
+} EditSequence, DNASequence;
+
+typedef u8 EditSequence_Operation;
+enum {
+ EditSequence_Operation_Swap,
+ EditSequence_Operation_Insert,
+ EditSequence_Operation_Delete,
+};
+
+typedef struct EditHistoryTree {
+ EditSequence value;
+
+ struct EditHistoryTree *parent;
+ // Siblings
+ struct EditHistoryTree *prev;
+ struct EditHistoryTree *next;
+ // Childrens
+ struct EditHistoryTree *first;
+ struct EditHistoryTree *last;
+} EditHistoryTree;
+
+#define dnasequence_from_str(sequence) \
+ (DNASequence) {(u8*)(sequence), sizeof(sequence) - 1}
+
+#define editsequence_from_bytes(sequence) \
+ (EditSequence) {(sequence), sizeof(sequence) - 1}
+
+fn void editsequence_log(EditSequence sequence) {
+ for (ssize i = 0; i < sequence.size;) {
+ EditSequence_Operation op = sequence.bytes[i++];
+ switch (op) {
+ case EditSequence_Operation_Swap: {
+ u8 inc = 0;
+ u32 pos = utf8_decode(&sequence.bytes[i], &inc);
+ i += inc;
+ char new_nucleotide = (char)sequence.bytes[i++];
+ printf("SWAP (%d,->%c)\n", pos, new_nucleotide);
+ } break;
+ case EditSequence_Operation_Insert: {
+ u8 inc = 0;
+ u32 pos = utf8_decode(&sequence.bytes[i], &inc);
+ i += inc;
+ u32 insert_size = utf8_decode(&sequence.bytes[i], &inc);
+ i += inc;
+ printf("INSERT (%d,%d,`%.*s`)\n", pos, insert_size,
+ insert_size, &sequence.bytes[i]);
+ i += insert_size;
+ } break;
+ case EditSequence_Operation_Delete: {
+ u8 inc = 0;
+ u32 pos = utf8_decode(&sequence.bytes[i], &inc);
+ i += inc;
+ u32 delete_size = utf8_decode(&sequence.bytes[i], &inc);
+ i += inc;
+ printf("DELETE (%d,%d)\n", pos, delete_size);
+ i += delete_size;
+ } break;
+ }
+ }
+ printf("\n");
+}
+
+fn DNASequence dnasequence_edit_apply(Arena *arena, DNASequence original,
+ EditHistoryTree *edit) {
+ Assert(edit);
+ DNASequence input = original;
+ if (edit->parent) {
+ input = dnasequence_edit_apply(arena, original, edit->parent);
+ }
+
+ struct DNASequence_Builder_Node {
+ DNASequence value;
+ struct DNASequence_Builder_Node *next;
+ };
+ struct {
+ ssize total_size;
+ struct DNASequence_Builder_Node *first;
+ struct DNASequence_Builder_Node *last;
+ } builder = {0};
+
+ Scratch scratch = ScratchBegin(&arena, 1);
+ u32 input_pos = 0;
+ for (u32 i = 0; i < edit->value.size;) {
+ EditSequence_Operation op = edit->value.bytes[i++];
+ switch (op) {
+ case EditSequence_Operation_Swap: {
+ u8 inc = 0;
+ u32 pos = utf8_decode(&edit->value.bytes[i], &inc);
+ i += inc;
+ char new_nucleotide = (char)edit->value.bytes[i++];
+ Assert(input.size > pos);
+
+ u32 relative_pos = pos - input_pos + 1;
+ struct DNASequence_Builder_Node *node = New(scratch.arena,
+ struct DNASequence_Builder_Node);
+ node->value.size = relative_pos;
+ node->value.bytes = New(scratch.arena, u8, relative_pos);
+ memcopy(node->value.bytes, &input.bytes[input_pos], relative_pos);
+ node->value.bytes[relative_pos - 1] = (u8)new_nucleotide;
+ QueuePush(builder.first, builder.last, node);
+ builder.total_size += node->value.size;
+ input_pos += relative_pos;
+ } break;
+ case EditSequence_Operation_Insert: {
+ u8 inc = 0;
+ u32 pos = utf8_decode(&edit->value.bytes[i], &inc);
+ i += inc;
+ u32 insert_size = utf8_decode(&edit->value.bytes[i], &inc);
+ i += inc;
+
+ u32 relative_pos = pos - input_pos;
+ struct DNASequence_Builder_Node *node = New(scratch.arena,
+ struct DNASequence_Builder_Node);
+ node->value.size = relative_pos + insert_size;
+ node->value.bytes = New(scratch.arena, u8, relative_pos + insert_size);
+ memcopy(node->value.bytes, &input.bytes[input_pos], relative_pos);
+ memcopy(node->value.bytes + relative_pos, &edit->value.bytes[i],
+ insert_size);
+ QueuePush(builder.first, builder.last, node);
+ builder.total_size += node->value.size;
+ input_pos += relative_pos;
+
+ i += insert_size;
+ } break;
+ case EditSequence_Operation_Delete: {
+ u8 inc = 0;
+ u32 pos = utf8_decode(&edit->value.bytes[i], &inc);
+ i += inc;
+ u32 delete_size = utf8_decode(&edit->value.bytes[i], &inc);
+ i += inc;
+
+ u32 relative_pos = pos - input_pos;
+ struct DNASequence_Builder_Node *node = New(scratch.arena,
+ struct DNASequence_Builder_Node);
+ node->value.size = relative_pos;
+ node->value.bytes = New(scratch.arena, u8, relative_pos);
+ memcopy(node->value.bytes, &input.bytes[input_pos], relative_pos);
+ QueuePush(builder.first, builder.last, node);
+ builder.total_size += node->value.size;
+
+ input_pos += relative_pos + delete_size;
+ } break;
+ }
+ }
+
+ // pushes the rest of the unchanged dna sequence
+ struct DNASequence_Builder_Node *node = New(scratch.arena,
+ struct DNASequence_Builder_Node);
+ node->value.size = input.size - input_pos;
+ node->value.bytes = New(scratch.arena, u8, (usize)node->value.size);
+ memcopy(node->value.bytes, &input.bytes[input_pos], (usize)node->value.size);
+ QueuePush(builder.first, builder.last, node);
+ builder.total_size += node->value.size;
+
+ // build the final output dna sequence
+ DNASequence res = {0};
+ res.size = builder.total_size;
+ res.bytes = New(arena, u8, (usize)builder.total_size);
+ u32 output_pos = 0;
+ for (struct DNASequence_Builder_Node *curr = builder.first;
+ curr;
+ curr = curr->next) {
+ memcopy(res.bytes + output_pos, curr->value.bytes,
+ (usize)curr->value.size);
+ output_pos += curr->value.size;
+ }
+ ScratchEnd(scratch);
+ return res;
+}
+
+fn EditHistoryTree* edit_history_tree_append(Arena *arena,
+ EditHistoryTree *root,
+ EditSequence seq) {
+ EditHistoryTree *node = New(arena, EditHistoryTree);
+ node->parent = root;
+ node->value = seq;
+ if (root) { DLLPushBack(root->first, root->last, node); }
+ return node;
+}
+
+s32 main(void) {
+ Arena *arena = ArenaBuild();
+ DNASequence input = dnasequence_from_str("AACGACTAGTAATTTGA");
+
+ EditHistoryTree *history_root =
+ edit_history_tree_append(arena, 0,
+ editsequence_from_bytes(((u8[]) {
+ EditSequence_Operation_Swap, 0,'G',
+ EditSequence_Operation_Swap, 1,'G',
+ EditSequence_Operation_Swap, 2,'G',
+ EditSequence_Operation_Insert, 3,4,'A','T','A','T',
+ EditSequence_Operation_Swap, 4,'T',
+ EditSequence_Operation_Swap, 5,'G',
+ EditSequence_Operation_Insert, 6,4,'A','T','A','T',
+ EditSequence_Operation_Insert, 12,2,'A','T',
+ EditSequence_Operation_Delete, 12,3,
+ EditSequence_Operation_Swap, 16,'G',
+ })));
+
+ EditHistoryTree *inner =
+ edit_history_tree_append(arena, history_root,
+ editsequence_from_bytes(((u8[]) {
+ EditSequence_Operation_Insert, 3,4,'_','_','_','_',
+ EditSequence_Operation_Delete, 3,4,
+ EditSequence_Operation_Delete, 10,4,
+ EditSequence_Operation_Delete, 20,2,
+ })));
+ EditHistoryTree *leaf1 =
+ edit_history_tree_append(arena, inner,
+ editsequence_from_bytes(((u8[]) {
+ EditSequence_Operation_Delete, 3,4,
+ })));
+ EditHistoryTree *leaf2 =
+ edit_history_tree_append(arena, history_root,
+ editsequence_from_bytes(((u8[]) {
+ EditSequence_Operation_Swap, 0,'A',
+ EditSequence_Operation_Delete, 3,4,
+ EditSequence_Operation_Delete, 10,4,
+ EditSequence_Operation_Delete, 20,2,
+ })));
+
+ DNASequence output1 = dnasequence_edit_apply(arena, input, inner);
+ DNASequence output2 = dnasequence_edit_apply(arena, input, leaf1);
+ DNASequence output3 = dnasequence_edit_apply(arena, input, leaf2);
+ printf("Sequence input: %.*s\n", (s32)input.size, input.bytes);
+ printf("Sequence output(inner): %.*s\n", (s32)output1.size, output1.bytes);
+ printf("Sequence output(1): %.*s\n", (s32)output2.size, output2.bytes);
+ printf("Sequence output(2): %.*s\n", (s32)output3.size, output3.bytes);
+}
--- /dev/null
+void build_setup(CB_Cmd *cmd) {
+ cb_cmd_append(cmd, "clang", "src/main.c", "-o", "main");
+ cb_cmd_append(cmd,
+ "-pedantic", "-Wall", "-Werror", "-Wextra",
+ "-Wconversion", "-Wdouble-promotion",
+ "-Wundef", "-Wcast-qual", "-Wmissing-declarations",
+ "-Wredundant-decls", "-Wno-initializer-overrides");
+ cb_cmd_append(cmd,
+ "-O0", "-g3", "-ggdb",
+ "-fsanitize=address,undefined,leak",
+ "-fsanitize-trap", "-fstack-protector-strong");
+ cb_cmd_append(cmd, "-Wno-unused-function");
+
+#if OS_LINUX
+ cb_cmd_append(cmd, "-Wno-variadic-macro-arguments-omitted");
+#else
+ cb_cmd_append(cmd, "-Wno-gnu-zero-variadic-macro-arguments");
+#endif
+}
--- /dev/null
+void build_setup(CB_Cmd *cmd) {
+ cb_cmd_append(cmd, "cl.exe", "src/main.c", "/Fe:main");
+ cb_cmd_append(cmd, "/Zc:preprocessor", "/GA", "/Gw",
+ "/permissive", "/fastfail", "/sdl",
+ "/std:c11");
+ cb_cmd_append(cmd, "/Od", "/Zi", "/fsanitize=address", "/RTC1");
+}