From: LeonardoBizzoni Date: Tue, 20 Jan 2026 20:51:38 +0000 (+0100) Subject: Final toy version X-Git-Url: http://git.leonardobizzoni.com/?a=commitdiff_plain;ds=inline;p=dna-sequence-editing Final toy version --- 4f88eb2501e5d3aeb2d73f1a8421a6542e574b47 diff --git a/cbuild.c b/cbuild.c new file mode 100644 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 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 +# else +typedef enum {false, true} bool; +# endif +#endif + +#include +#include +#include +#include +#include +#include + +#if OS_WINDOWS +# include +# include +# 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 +# include +# include +# include +# include +# 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 index 0000000..f2b19d0 --- /dev/null +++ b/src/arena.c @@ -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 index 0000000..6370bd1 --- /dev/null +++ b/src/arena.h @@ -0,0 +1,81 @@ +#ifndef ARENA_H +#define ARENA_H + +#include +#if OS_WINDOWS +# include +#else +# include +# include +#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 index 0000000..4a8b1f8 --- /dev/null +++ b/src/base.h @@ -0,0 +1,383 @@ +#ifndef BASE_H +#define BASE_H + +#include + +#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 +#include +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 +#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 index 0000000..46c92d7 --- /dev/null +++ b/src/main.c @@ -0,0 +1,291 @@ +#include +#include + +#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 index 0000000..09c6c7d --- /dev/null +++ b/src_build/cbuild_unix.c @@ -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 index 0000000..c1edaf4 --- /dev/null +++ b/src_build/cbuild_win32.c @@ -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"); +}