]> git.leonardobizzoni.com Git - dna-sequence-editing/commitdiff
Final toy version master
authorLeonardoBizzoni <leo2002714@gmail.com>
Tue, 20 Jan 2026 20:51:38 +0000 (21:51 +0100)
committerLeonardoBizzoni <leo2002714@gmail.com>
Tue, 20 Jan 2026 20:51:38 +0000 (21:51 +0100)
cbuild.c [new file with mode: 0644]
cbuild.h [new file with mode: 0644]
src/arena.c [new file with mode: 0755]
src/arena.h [new file with mode: 0644]
src/base.h [new file with mode: 0644]
src/main.c [new file with mode: 0644]
src_build/cbuild_unix.c [new file with mode: 0644]
src_build/cbuild_win32.c [new file with mode: 0644]

diff --git a/cbuild.c b/cbuild.c
new file mode 100644 (file)
index 0000000..38785ec
--- /dev/null
+++ b/cbuild.c
@@ -0,0 +1,17 @@
+#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; }
+}
diff --git a/cbuild.h b/cbuild.h
new file mode 100644 (file)
index 0000000..2fbce3a
--- /dev/null
+++ b/cbuild.h
@@ -0,0 +1,755 @@
+#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
diff --git a/src/arena.c b/src/arena.c
new file mode 100755 (executable)
index 0000000..f2b19d0
--- /dev/null
@@ -0,0 +1,145 @@
+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;
+}
diff --git a/src/arena.h b/src/arena.h
new file mode 100644 (file)
index 0000000..6370bd1
--- /dev/null
@@ -0,0 +1,81 @@
+#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
diff --git a/src/base.h b/src/base.h
new file mode 100644 (file)
index 0000000..4a8b1f8
--- /dev/null
@@ -0,0 +1,383 @@
+#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
diff --git a/src/main.c b/src/main.c
new file mode 100644 (file)
index 0000000..46c92d7
--- /dev/null
@@ -0,0 +1,291 @@
+#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);
+}
diff --git a/src_build/cbuild_unix.c b/src_build/cbuild_unix.c
new file mode 100644 (file)
index 0000000..09c6c7d
--- /dev/null
@@ -0,0 +1,19 @@
+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
+}
diff --git a/src_build/cbuild_win32.c b/src_build/cbuild_win32.c
new file mode 100644 (file)
index 0000000..c1edaf4
--- /dev/null
@@ -0,0 +1,7 @@
+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");
+}