#include #include #include #include #include #include #include #include #ifdef NDEBUG #define _DEBUG 0 #else #define _DEBUG 1 #endif #ifdef STATIC #define LINK_LIBC 0 #else #define LINK_LIBC 1 #endif #if LINK_LIBC #include #include #include #endif typedef uint8_t u8; typedef uint16_t u16; typedef uint32_t u32; typedef uint64_t u64; typedef int8_t s8; typedef int16_t s16; typedef int32_t s32; typedef int64_t s64; typedef float f32; typedef double f64; typedef u64 Hash; #define MIN(a, b) (((a) < (b)) ? (a) : (b)) #define MAX(a, b) (((a) > (b)) ? (a) : (b)) #define fn static #define method __attribute__((visibility("internal"))) #define global static #define forceinline __attribute__((always_inline)) #define expect(x, b) __builtin_expect(x, b) #define breakpoint() __builtin_debugtrap() #define fail() trap() #define trap() bad_exit("Trap reached", __FILE__, __LINE__) #define array_length(arr) sizeof(arr) / sizeof((arr)[0]) #define KB(n) ((n) * 1024) #define MB(n) ((n) * 1024 * 1024) #define GB(n) ((u64)(n) * 1024 * 1024 * 1024) #define TB(n) ((u64)(n) * 1024 * 1024 * 1024 * 1024) #define unused(x) (void)(x) #define may_be_unused __attribute__((unused)) #if _DEBUG #define assert(x) if (__builtin_expect(!(x), 0)) { bad_exit("Assert failed", __FILE__, __LINE__); } #else #define assert(x) __builtin_expect(!(x), 0) #endif #ifdef unreachable #undef unreachable #endif #if _DEBUG #define unreachable() bad_exit("Unreachable triggered", __FILE__, __LINE__) #else #define unreachable() __builtin_unreachable() #endif #define static_assert(x) _Static_assert((x), "Static assert failed!") #define alignof(x) _Alignof(x) #define auto __auto_type #define bad_exit(message, file, line) do { print(message " at {cstr}:{u32}\n", file, line); __builtin_trap(); } while(0) #define todo() do { print("TODO at {cstr}:{u32}\n", __FILE__, __LINE__); __builtin_trap(); } while(0) may_be_unused fn void print(const char* format, ...); #if __APPLE__ const global u64 page_size = KB(16); #else const global u64 page_size = KB(4); #endif const may_be_unused global u8 brace_open = '{'; const may_be_unused global u8 brace_close = '}'; const may_be_unused global u8 parenthesis_open = '('; const may_be_unused global u8 parenthesis_close = ')'; const may_be_unused global u8 bracket_open = '['; const may_be_unused global u8 bracket_close = ']'; may_be_unused fn u8 log2_alignment(u64 alignment) { assert(alignment != 0); assert((alignment & (alignment - 1)) == 0); u64 left = (sizeof(alignment) * 8) - 1; u64 right = __builtin_clzl(alignment); u8 result = left - right; return result; } // Lehmer's generator // https://lemire.me/blog/2019/03/19/the-fastest-conventional-random-number-generator-that-can-pass-big-crush/ may_be_unused global __uint128_t rn_state; may_be_unused fn u64 generate_random_number() { rn_state *= 0xda942042e4dd58b5; return rn_state >> 64; } may_be_unused fn u64 round_up_to_next_power_of_2(u64 n) { n -= 1; n |= n >> 1; n |= n >> 2; n |= n >> 4; n |= n >> 8; n |= n >> 16; n |= n >> 32; n += 1; return n; } #if LINK_LIBC == 0 void* memcpy(void* __restrict dst, void* __restrict src, u64 size) { auto* destination = (u8*)dst; auto* source = (u8*)src; for (u64 i = 0; i < size; i += 1) { destination[i] = source[i]; } return dst; } void* memset(void* dst, u8 n, u64 size) { auto* destination = (u8*)dst; for (u64 i = 0; i < size; i += 1) { destination[i] = n; } return dst; } fn int memcmp(const void* left, const void* right, u64 n) { const u8 *l=(const u8*)left, *r=(const u8*)right; for (; n && *l == *r; n--, l++, r++); return n ? *l - *r : 0; } fn u64 strlen (const char* c_string) { auto* it = c_string; while (*it) { it += 1; } return it - c_string; } #endif #define slice_from_pointer_range(T, start, end) (Slice(T)) { .pointer = start, .length = (u64)(end - start), } #define strlit_len(s) (sizeof(s) - 1) #define strlit(s) (String){ .pointer = (u8*)(s), .length = strlit_len(s), } #define ch_to_str(ch) (String){ .pointer = &ch, .length = 1 } #define array_to_slice(arr) { .pointer = (arr), .length = array_length(arr) } #define pointer_to_bytes(p) (String) { .pointer = (u8*)(p), .length = sizeof(*p) } #define struct_to_bytes(s) pointer_to_bytes(&(s)) #define string_to_c(s) ((char*)((s).pointer)) #define cstr(s) ((String) { .pointer = (u8*)(s), .length = strlen(s), } ) #define case_to_name(prefix, e) case prefix ## e: return strlit(#e) #define Slice(T) Slice_ ## T #define SliceP(T) SliceP_ ## T #define declare_slice_ex(T, StructName) struct StructName \ {\ T* pointer;\ u64 length;\ };\ typedef struct StructName StructName #define declare_slice(T) declare_slice_ex(T, Slice(T)) #define declare_slice_p(T) declare_slice_ex(T*, SliceP(T)) #define s_get(s, i) (s).pointer[i] #define s_get_pointer(s, i) &((s).pointer[i]) #define s_get_slice(T, s, start, end) (Slice(T)){ .pointer = ((s).pointer) + (start), .length = (end) - (start) } #define s_equal(a, b) ((a).length == (b).length && memcmp((a).pointer, (b).pointer, sizeof(*((a).pointer))) == 0) declare_slice(u8); typedef Slice(u8) String; // Array of strings declare_slice(String); declare_slice_p(char); typedef SliceP_char CStringSlice; // fn s32 string_first_ch(String string, u8 ch) // { // s32 result = -1; // for (u64 i = 0; i < string.length; i += 1) // { // if (string.pointer[i] == ch) // { // result = i; // break; // } // } // // return result; // } fn s32 string_last_ch(String string, u8 ch) { s32 result = -1; u64 i = string.length; while (i > 0) { i -= 1; if (string.pointer[i] == ch) { result = i; break; } } return result; } // fn String string_dir(String string) // { // String result = {}; // auto index = string_last_ch(string, '/'); // if (index != -1) // { // result = s_get_slice(u8, string, 0, index); // } // // return result; // } may_be_unused fn String string_base(String string) { String result = {}; auto index = string_last_ch(string, '/'); if (index != -1) { result = s_get_slice(u8, string, index + 1, string.length); } return result; } may_be_unused fn String string_no_extension(String string) { String result = {}; auto index = string_last_ch(string, '.'); if (index != -1) { result = s_get_slice(u8, string, 0, index); } return result; } fn u64 parse_decimal(String string) { u64 value = 0; for (u64 i = 0; i < string.length; i += 1) { u8 ch = s_get(string, i); assert(((ch >= '0') & (ch <= '9'))); value = (value * 10) + (ch - '0'); } return value; } fn u64 safe_flag(u64 value, u64 flag) { u64 result = value & ((u64)0 - flag); return result; } may_be_unused fn u8 get_next_ch_safe(String string, u64 index) { u64 next_index = index + 1; u64 is_in_range = next_index < string.length; u64 safe_index = safe_flag(next_index, is_in_range); u8 unsafe_result = string.pointer[safe_index]; u64 safe_result = safe_flag(unsafe_result, is_in_range); assert(safe_result < 256); return (u8)safe_result; } may_be_unused fn u32 is_space(u8 ch, u8 next_ch) { u32 is_comment = (ch == '/') & (next_ch == '/'); u32 is_whitespace = ch == ' '; u32 is_vertical_tab = ch == 0x0b; u32 is_horizontal_tab = ch == '\t'; u32 is_line_feed = ch == '\n'; u32 is_carry_return = ch == '\r'; u32 result = (((is_vertical_tab | is_horizontal_tab) | (is_line_feed | is_carry_return)) | (is_comment | is_whitespace)); return result; } fn u64 is_lower(u8 ch) { return (ch >= 'a') & (ch <= 'z'); } fn u64 is_upper(u8 ch) { return (ch >= 'A') & (ch <= 'Z'); } fn u64 is_alphabetic(u8 ch) { return is_lower(ch) | is_upper(ch); } fn u64 is_decimal_digit(u8 ch) { return (ch >= '0') & (ch <= '9'); } // fn u64 is_hex_digit(u8 ch) // { // return (is_decimal_digit(ch) | ((ch == 'a' | ch == 'A') | (ch == 'b' | ch == 'B'))) | (((ch == 'c' | ch == 'C') | (ch == 'd' | ch == 'D')) | ((ch == 'e' | ch == 'E') | (ch == 'f' | ch == 'F'))); // } fn u64 is_identifier_start(u8 ch) { u64 alphabetic = is_alphabetic(ch); u64 is_underscore = ch == '_'; return alphabetic | is_underscore; } may_be_unused fn u64 is_identifier_ch(u8 ch) { u64 identifier_start = is_identifier_start(ch); u64 decimal = is_decimal_digit(ch); return identifier_start | decimal; } global const Hash fnv_offset = 14695981039346656037ull; global const u64 fnv_prime = 1099511628211ull; fn Hash hash_byte(Hash source, u8 ch) { source ^= ch; source *= fnv_prime; return source; } may_be_unused fn Hash hash_bytes(String bytes) { u64 result = fnv_offset; for (u64 i = 0; i < bytes.length; i += 1) { result = hash_byte(result, bytes.pointer[i]); } return result; } #if LINK_LIBC == 0 #ifdef __linux__ may_be_unused fn forceinline long syscall0(long n) { unsigned long ret; __asm__ __volatile__ ("syscall" : "=a"(ret) : "a"(n) : "rcx", "r11", "memory"); return ret; } may_be_unused fn forceinline long syscall1(long n, long a1) { unsigned long ret; __asm__ __volatile__ ("syscall" : "=a"(ret) : "a"(n), "D"(a1) : "rcx", "r11", "memory"); return ret; } may_be_unused fn forceinline long syscall2(long n, long a1, long a2) { unsigned long ret; __asm__ __volatile__ ("syscall" : "=a"(ret) : "a"(n), "D"(a1), "S"(a2) : "rcx", "r11", "memory"); return ret; } may_be_unused fn forceinline long syscall3(long n, long a1, long a2, long a3) { unsigned long ret; __asm__ __volatile__ ("syscall" : "=a"(ret) : "a"(n), "D"(a1), "S"(a2), "d"(a3) : "rcx", "r11", "memory"); return ret; } may_be_unused fn forceinline long syscall4(long n, long a1, long a2, long a3, long a4) { unsigned long ret; register long r10 __asm__("r10") = a4; __asm__ __volatile__ ("syscall" : "=a"(ret) : "a"(n), "D"(a1), "S"(a2), "d"(a3), "r"(r10): "rcx", "r11", "memory"); return ret; } may_be_unused fn forceinline long syscall5(long n, long a1, long a2, long a3, long a4, long a5) { unsigned long ret; register long r10 __asm__("r10") = a4; register long r8 __asm__("r8") = a5; __asm__ __volatile__ ("syscall" : "=a"(ret) : "a"(n), "D"(a1), "S"(a2), "d"(a3), "r"(r10), "r"(r8) : "rcx", "r11", "memory"); return ret; } may_be_unused fn forceinline long syscall6(long n, long a1, long a2, long a3, long a4, long a5, long a6) { unsigned long ret; register long r10 __asm__("r10") = a4; register long r8 __asm__("r8") = a5; register long r9 __asm__("r9") = a6; __asm__ __volatile__ ("syscall" : "=a"(ret) : "a"(n), "D"(a1), "S"(a2), "d"(a3), "r"(r10), "r"(r8), "r"(r9) : "rcx", "r11", "memory"); return ret; } enum SyscallX86_64 : u64 { syscall_x86_64_read = 0, syscall_x86_64_write = 1, syscall_x86_64_open = 2, syscall_x86_64_close = 3, syscall_x86_64_stat = 4, syscall_x86_64_fstat = 5, syscall_x86_64_lstat = 6, syscall_x86_64_poll = 7, syscall_x86_64_lseek = 8, syscall_x86_64_mmap = 9, syscall_x86_64_mprotect = 10, syscall_x86_64_munmap = 11, syscall_x86_64_brk = 12, syscall_x86_64_rt_sigaction = 13, syscall_x86_64_rt_sigprocmask = 14, syscall_x86_64_rt_sigreturn = 15, syscall_x86_64_ioctl = 16, syscall_x86_64_pread64 = 17, syscall_x86_64_pwrite64 = 18, syscall_x86_64_readv = 19, syscall_x86_64_writev = 20, syscall_x86_64_access = 21, syscall_x86_64_pipe = 22, syscall_x86_64_select = 23, syscall_x86_64_sched_yield = 24, syscall_x86_64_mremap = 25, syscall_x86_64_msync = 26, syscall_x86_64_mincore = 27, syscall_x86_64_madvise = 28, syscall_x86_64_shmget = 29, syscall_x86_64_shmat = 30, syscall_x86_64_shmctl = 31, syscall_x86_64_dup = 32, syscall_x86_64_dup2 = 33, syscall_x86_64_pause = 34, syscall_x86_64_nanosleep = 35, syscall_x86_64_getitimer = 36, syscall_x86_64_alarm = 37, syscall_x86_64_setitimer = 38, syscall_x86_64_getpid = 39, syscall_x86_64_sendfile = 40, syscall_x86_64_socket = 41, syscall_x86_64_connect = 42, syscall_x86_64_accept = 43, syscall_x86_64_sendto = 44, syscall_x86_64_recvfrom = 45, syscall_x86_64_sendmsg = 46, syscall_x86_64_recvmsg = 47, syscall_x86_64_shutdown = 48, syscall_x86_64_bind = 49, syscall_x86_64_listen = 50, syscall_x86_64_getsockname = 51, syscall_x86_64_getpeername = 52, syscall_x86_64_socketpair = 53, syscall_x86_64_setsockopt = 54, syscall_x86_64_getsockopt = 55, syscall_x86_64_clone = 56, syscall_x86_64_fork = 57, syscall_x86_64_vfork = 58, syscall_x86_64_execve = 59, syscall_x86_64_exit = 60, syscall_x86_64_wait4 = 61, syscall_x86_64_kill = 62, syscall_x86_64_uname = 63, syscall_x86_64_semget = 64, syscall_x86_64_semop = 65, syscall_x86_64_semctl = 66, syscall_x86_64_shmdt = 67, syscall_x86_64_msgget = 68, syscall_x86_64_msgsnd = 69, syscall_x86_64_msgrcv = 70, syscall_x86_64_msgctl = 71, syscall_x86_64_fcntl = 72, syscall_x86_64_flock = 73, syscall_x86_64_fsync = 74, syscall_x86_64_fdatasync = 75, syscall_x86_64_truncate = 76, syscall_x86_64_ftruncate = 77, syscall_x86_64_getdents = 78, syscall_x86_64_getcwd = 79, syscall_x86_64_chdir = 80, syscall_x86_64_fchdir = 81, syscall_x86_64_rename = 82, syscall_x86_64_mkdir = 83, syscall_x86_64_rmdir = 84, syscall_x86_64_creat = 85, syscall_x86_64_link = 86, syscall_x86_64_unlink = 87, syscall_x86_64_symlink = 88, syscall_x86_64_readlink = 89, syscall_x86_64_chmod = 90, syscall_x86_64_fchmod = 91, syscall_x86_64_chown = 92, syscall_x86_64_fchown = 93, syscall_x86_64_lchown = 94, syscall_x86_64_umask = 95, syscall_x86_64_gettimeofday = 96, syscall_x86_64_getrlimit = 97, syscall_x86_64_getrusage = 98, syscall_x86_64_sysinfo = 99, syscall_x86_64_times = 100, syscall_x86_64_ptrace = 101, syscall_x86_64_getuid = 102, syscall_x86_64_syslog = 103, syscall_x86_64_getgid = 104, syscall_x86_64_setuid = 105, syscall_x86_64_setgid = 106, syscall_x86_64_geteuid = 107, syscall_x86_64_getegid = 108, syscall_x86_64_setpgid = 109, syscall_x86_64_getppid = 110, syscall_x86_64_getpgrp = 111, syscall_x86_64_setsid = 112, syscall_x86_64_setreuid = 113, syscall_x86_64_setregid = 114, syscall_x86_64_getgroups = 115, syscall_x86_64_setgroups = 116, syscall_x86_64_setresuid = 117, syscall_x86_64_getresuid = 118, syscall_x86_64_setresgid = 119, syscall_x86_64_getresgid = 120, syscall_x86_64_getpgid = 121, syscall_x86_64_setfsuid = 122, syscall_x86_64_setfsgid = 123, syscall_x86_64_getsid = 124, syscall_x86_64_capget = 125, syscall_x86_64_capset = 126, syscall_x86_64_rt_sigpending = 127, syscall_x86_64_rt_sigtimedwait = 128, syscall_x86_64_rt_sigqueueinfo = 129, syscall_x86_64_rt_sigsuspend = 130, syscall_x86_64_sigaltstack = 131, syscall_x86_64_utime = 132, syscall_x86_64_mknod = 133, syscall_x86_64_uselib = 134, syscall_x86_64_personality = 135, syscall_x86_64_ustat = 136, syscall_x86_64_statfs = 137, syscall_x86_64_fstatfs = 138, syscall_x86_64_sysfs = 139, syscall_x86_64_getpriority = 140, syscall_x86_64_setpriority = 141, syscall_x86_64_sched_setparam = 142, syscall_x86_64_sched_getparam = 143, syscall_x86_64_sched_setscheduler = 144, syscall_x86_64_sched_getscheduler = 145, syscall_x86_64_sched_get_priority_max = 146, syscall_x86_64_sched_get_priority_min = 147, syscall_x86_64_sched_rr_get_interval = 148, syscall_x86_64_mlock = 149, syscall_x86_64_munlock = 150, syscall_x86_64_mlockall = 151, syscall_x86_64_munlockall = 152, syscall_x86_64_vhangup = 153, syscall_x86_64_modify_ldt = 154, syscall_x86_64_pivot_root = 155, syscall_x86_64__sysctl = 156, syscall_x86_64_prctl = 157, syscall_x86_64_arch_prctl = 158, syscall_x86_64_adjtimex = 159, syscall_x86_64_setrlimit = 160, syscall_x86_64_chroot = 161, syscall_x86_64_sync = 162, syscall_x86_64_acct = 163, syscall_x86_64_settimeofday = 164, syscall_x86_64_mount = 165, syscall_x86_64_umount2 = 166, syscall_x86_64_swapon = 167, syscall_x86_64_swapoff = 168, syscall_x86_64_reboot = 169, syscall_x86_64_sethostname = 170, syscall_x86_64_setdomainname = 171, syscall_x86_64_iopl = 172, syscall_x86_64_ioperm = 173, syscall_x86_64_create_module = 174, syscall_x86_64_init_module = 175, syscall_x86_64_delete_module = 176, syscall_x86_64_get_kernel_syms = 177, syscall_x86_64_query_module = 178, syscall_x86_64_quotactl = 179, syscall_x86_64_nfsservctl = 180, syscall_x86_64_getpmsg = 181, syscall_x86_64_putpmsg = 182, syscall_x86_64_afs_syscall = 183, syscall_x86_64_tuxcall = 184, syscall_x86_64_security = 185, syscall_x86_64_gettid = 186, syscall_x86_64_readahead = 187, syscall_x86_64_setxattr = 188, syscall_x86_64_lsetxattr = 189, syscall_x86_64_fsetxattr = 190, syscall_x86_64_getxattr = 191, syscall_x86_64_lgetxattr = 192, syscall_x86_64_fgetxattr = 193, syscall_x86_64_listxattr = 194, syscall_x86_64_llistxattr = 195, syscall_x86_64_flistxattr = 196, syscall_x86_64_removexattr = 197, syscall_x86_64_lremovexattr = 198, syscall_x86_64_fremovexattr = 199, syscall_x86_64_tkill = 200, syscall_x86_64_time = 201, syscall_x86_64_futex = 202, syscall_x86_64_sched_setaffinity = 203, syscall_x86_64_sched_getaffinity = 204, syscall_x86_64_set_thread_area = 205, syscall_x86_64_io_setup = 206, syscall_x86_64_io_destroy = 207, syscall_x86_64_io_getevents = 208, syscall_x86_64_io_submit = 209, syscall_x86_64_io_cancel = 210, syscall_x86_64_get_thread_area = 211, syscall_x86_64_lookup_dcookie = 212, syscall_x86_64_epoll_create = 213, syscall_x86_64_epoll_ctl_old = 214, syscall_x86_64_epoll_wait_old = 215, syscall_x86_64_remap_file_pages = 216, syscall_x86_64_getdents64 = 217, syscall_x86_64_set_tid_address = 218, syscall_x86_64_restart_syscall = 219, syscall_x86_64_semtimedop = 220, syscall_x86_64_fadvise64 = 221, syscall_x86_64_timer_create = 222, syscall_x86_64_timer_settime = 223, syscall_x86_64_timer_gettime = 224, syscall_x86_64_timer_getoverrun = 225, syscall_x86_64_timer_delete = 226, syscall_x86_64_clock_settime = 227, syscall_x86_64_clock_gettime = 228, syscall_x86_64_clock_getres = 229, syscall_x86_64_clock_nanosleep = 230, syscall_x86_64_exit_group = 231, syscall_x86_64_epoll_wait = 232, syscall_x86_64_epoll_ctl = 233, syscall_x86_64_tgkill = 234, syscall_x86_64_utimes = 235, syscall_x86_64_vserver = 236, syscall_x86_64_mbind = 237, syscall_x86_64_set_mempolicy = 238, syscall_x86_64_get_mempolicy = 239, syscall_x86_64_mq_open = 240, syscall_x86_64_mq_unlink = 241, syscall_x86_64_mq_timedsend = 242, syscall_x86_64_mq_timedreceive = 243, syscall_x86_64_mq_notify = 244, syscall_x86_64_mq_getsetattr = 245, syscall_x86_64_kexec_load = 246, syscall_x86_64_waitid = 247, syscall_x86_64_add_key = 248, syscall_x86_64_request_key = 249, syscall_x86_64_keyctl = 250, syscall_x86_64_ioprio_set = 251, syscall_x86_64_ioprio_get = 252, syscall_x86_64_inotify_init = 253, syscall_x86_64_inotify_add_watch = 254, syscall_x86_64_inotify_rm_watch = 255, syscall_x86_64_migrate_pages = 256, syscall_x86_64_openat = 257, syscall_x86_64_mkdirat = 258, syscall_x86_64_mknodat = 259, syscall_x86_64_fchownat = 260, syscall_x86_64_futimesat = 261, syscall_x86_64_fstatat64 = 262, syscall_x86_64_unlinkat = 263, syscall_x86_64_renameat = 264, syscall_x86_64_linkat = 265, syscall_x86_64_symlinkat = 266, syscall_x86_64_readlinkat = 267, syscall_x86_64_fchmodat = 268, syscall_x86_64_faccessat = 269, syscall_x86_64_pselect6 = 270, syscall_x86_64_ppoll = 271, syscall_x86_64_unshare = 272, syscall_x86_64_set_robust_list = 273, syscall_x86_64_get_robust_list = 274, syscall_x86_64_splice = 275, syscall_x86_64_tee = 276, syscall_x86_64_sync_file_range = 277, syscall_x86_64_vmsplice = 278, syscall_x86_64_move_pages = 279, syscall_x86_64_utimensat = 280, syscall_x86_64_epoll_pwait = 281, syscall_x86_64_signalfd = 282, syscall_x86_64_timerfd_create = 283, syscall_x86_64_eventfd = 284, syscall_x86_64_fallocate = 285, syscall_x86_64_timerfd_settime = 286, syscall_x86_64_timerfd_gettime = 287, syscall_x86_64_accept4 = 288, syscall_x86_64_signalfd4 = 289, syscall_x86_64_eventfd2 = 290, syscall_x86_64_epoll_create1 = 291, syscall_x86_64_dup3 = 292, syscall_x86_64_pipe2 = 293, syscall_x86_64_inotify_init1 = 294, syscall_x86_64_preadv = 295, syscall_x86_64_pwritev = 296, syscall_x86_64_rt_tgsigqueueinfo = 297, syscall_x86_64_perf_event_open = 298, syscall_x86_64_recvmmsg = 299, syscall_x86_64_fanotify_init = 300, syscall_x86_64_fanotify_mark = 301, syscall_x86_64_prlimit64 = 302, syscall_x86_64_name_to_handle_at = 303, syscall_x86_64_open_by_handle_at = 304, syscall_x86_64_clock_adjtime = 305, syscall_x86_64_syncfs = 306, syscall_x86_64_sendmmsg = 307, syscall_x86_64_setns = 308, syscall_x86_64_getcpu = 309, syscall_x86_64_process_vm_readv = 310, syscall_x86_64_process_vm_writev = 311, syscall_x86_64_kcmp = 312, syscall_x86_64_finit_module = 313, syscall_x86_64_sched_setattr = 314, syscall_x86_64_sched_getattr = 315, syscall_x86_64_renameat2 = 316, syscall_x86_64_seccomp = 317, syscall_x86_64_getrandom = 318, syscall_x86_64_memfd_create = 319, syscall_x86_64_kexec_file_load = 320, syscall_x86_64_bpf = 321, syscall_x86_64_execveat = 322, syscall_x86_64_userfaultfd = 323, syscall_x86_64_membarrier = 324, syscall_x86_64_mlock2 = 325, syscall_x86_64_copy_file_range = 326, syscall_x86_64_preadv2 = 327, syscall_x86_64_pwritev2 = 328, syscall_x86_64_pkey_mprotect = 329, syscall_x86_64_pkey_alloc = 330, syscall_x86_64_pkey_free = 331, syscall_x86_64_statx = 332, syscall_x86_64_io_pgetevents = 333, syscall_x86_64_rseq = 334, syscall_x86_64_pidfd_send_signal = 424, syscall_x86_64_io_uring_setup = 425, syscall_x86_64_io_uring_enter = 426, syscall_x86_64_io_uring_register = 427, syscall_x86_64_open_tree = 428, syscall_x86_64_move_mount = 429, syscall_x86_64_fsopen = 430, syscall_x86_64_fsconfig = 431, syscall_x86_64_fsmount = 432, syscall_x86_64_fspick = 433, syscall_x86_64_pidfd_open = 434, syscall_x86_64_clone3 = 435, syscall_x86_64_close_range = 436, syscall_x86_64_openat2 = 437, syscall_x86_64_pidfd_getfd = 438, syscall_x86_64_faccessat2 = 439, syscall_x86_64_process_madvise = 440, syscall_x86_64_epoll_pwait2 = 441, syscall_x86_64_mount_setattr = 442, syscall_x86_64_quotactl_fd = 443, syscall_x86_64_landlock_create_ruleset = 444, syscall_x86_64_landlock_add_rule = 445, syscall_x86_64_landlock_restrict_self = 446, syscall_x86_64_memfd_secret = 447, syscall_x86_64_process_mrelease = 448, syscall_x86_64_futex_waitv = 449, syscall_x86_64_set_mempolicy_home_node = 450, syscall_x86_64_cachestat = 451, syscall_x86_64_fchmodat2 = 452, syscall_x86_64_map_shadow_stack = 453, syscall_x86_64_futex_wake = 454, syscall_x86_64_futex_wait = 455, syscall_x86_64_futex_requeue = 456, }; #endif #endif may_be_unused fn void* syscall_mmap(void* address, size_t length, int protection_flags, int map_flags, int fd, signed long offset) { #if LINK_LIBC return mmap(address, length, protection_flags, map_flags, fd, offset); #else #ifdef __linux__ return (void*) syscall6(syscall_x86_64_mmap, (unsigned long)address, length, protection_flags, map_flags, fd, offset); #else #error "Unsupported operating system for static linking" #endif #endif } may_be_unused fn int syscall_mprotect(void *address, size_t length, int protection_flags) { #if LINK_LIBC return mprotect(address, length, protection_flags); #else #ifdef __linux__ return syscall3(syscall_x86_64_mprotect, (unsigned long)address, length, protection_flags); #else return mprotect(address, length, protection_flags); #endif #endif } may_be_unused fn int syscall_open(const char *file_path, int flags, int mode) { #if LINK_LIBC return open(file_path, flags, mode); #else #ifdef __linux__ return syscall3(syscall_x86_64_open, (unsigned long)file_path, flags, mode); #else return open(file_path, flags, mode); #endif #endif } may_be_unused fn int syscall_close(int fd) { #if LINK_LIBC return close(fd); #else #ifdef __linux__ return syscall1(syscall_x86_64_close, fd); #else return close(fd); #endif #endif } fn int syscall_fstat(int fd, struct stat *buffer) { #if LINK_LIBC return fstat(fd, buffer); #else #ifdef __linux__ return syscall2(syscall_x86_64_fstat, fd, (unsigned long)buffer); #else return fstat(fd, buffer); #endif #endif } may_be_unused fn u64 file_get_size(int fd) { struct stat stat_buffer; int stat_result = syscall_fstat(fd, &stat_buffer); assert(stat_result == 0); u64 size = stat_buffer.st_size; return size; } may_be_unused fn ssize_t syscall_read(int fd, void* buffer, size_t bytes) { #if LINK_LIBC return read(fd, buffer, bytes); #else #ifdef __linux__ return syscall3(syscall_x86_64_read, fd, (unsigned long)buffer, bytes); #else return read(fd, buffer, bytes); #endif #endif } may_be_unused fn ssize_t syscall_write(int fd, const void *buffer, size_t bytes) { #if LINK_LIBC return write(fd, buffer, bytes); #else #ifdef __linux__ return syscall3(syscall_x86_64_write, fd, (unsigned long)buffer, bytes); #else return write(fd, buffer, bytes); #endif #endif } may_be_unused fn int syscall_mkdir(const char* path, u32 mode) { #if LINK_LIBC return mkdir(path, mode); #else return syscall2(syscall_x86_64_mkdir, (unsigned long)path, mode); #endif } may_be_unused fn int syscall_rmdir(const char* path) { #if LINK_LIBC return rmdir(path); #else return syscall1(syscall_x86_64_rmdir, (unsigned long)path); #endif } may_be_unused fn int syscall_unlink(const char* path) { #if LINK_LIBC return unlink(path); #else return syscall1(syscall_x86_64_unlink, (unsigned long)path); #endif } may_be_unused fn pid_t syscall_fork() { #if LINK_LIBC return fork(); #else return syscall0(syscall_x86_64_fork); #endif } may_be_unused fn signed long syscall_execve(const char* path, char *const argv[], char *const envp[]) { #if LINK_LIBC return execve(path, argv, envp); #else return syscall3(syscall_x86_64_execve, (unsigned long)path, (unsigned long)argv, (unsigned long)envp); #endif } may_be_unused fn pid_t syscall_waitpid(pid_t pid, int* status, int options) { #if LINK_LIBC return waitpid(pid, status, options); #else return syscall4(syscall_x86_64_wait4, pid, (unsigned long)status, options, 0); #endif } may_be_unused [[noreturn]] [[gnu::cold]] fn void syscall_exit(int status) { #if LINK_LIBC _exit(status); #else #ifdef __linux__ (void)syscall1(231, status); trap(); #else _exit(status); #endif #endif } fn u8* reserve(u64 size) { int protection_flags = PROT_NONE; int map_flags = MAP_ANONYMOUS | MAP_PRIVATE | MAP_NORESERVE; u8* result = (u8*)syscall_mmap(0, size, protection_flags, map_flags, -1, 0); assert(result != MAP_FAILED); return result; } fn void commit(void* address, u64 size) { int result = syscall_mprotect(address, size, PROT_READ | PROT_WRITE); assert(result == 0); } fn u64 align_forward(u64 value, u64 alignment) { u64 mask = alignment - 1; u64 result = (value + mask) & ~mask; return result; } fn u32 format_hexadecimal(String buffer, u64 hexadecimal) { u64 value = hexadecimal; if (value) { u8 reverse_buffer[16]; u8 reverse_index = 0; while (value) { u8 digit_value = value % 16; u8 ascii_ch = digit_value >= 10 ? (digit_value + 'a' - 10) : (digit_value + '0'); value /= 16; reverse_buffer[reverse_index] = ascii_ch; reverse_index += 1; } u32 index = 0; while (reverse_index > 0) { reverse_index -= 1; buffer.pointer[index] = reverse_buffer[reverse_index]; index += 1; } return index; } else { buffer.pointer[0] = '0'; return 1; } } fn u32 format_decimal(String buffer, u64 decimal) { u64 value = decimal; if (value) { u8 reverse_buffer[64]; u8 reverse_index = 0; while (value) { u8 digit_value = (value % 10); u8 ascii_ch = digit_value + '0'; value /= 10; reverse_buffer[reverse_index] = ascii_ch; reverse_index += 1; } u32 index = 0; while (reverse_index > 0) { reverse_index -= 1; buffer.pointer[index] = reverse_buffer[reverse_index]; index += 1; } return index; } else { buffer.pointer[0] = '0'; return 1; } } #define SILENT (0) may_be_unused fn void print(const char* format, ...) { #if SILENT == 0 u8 stack_buffer[4096]; va_list args; va_start(args, format); String buffer = { .pointer = stack_buffer, .length = array_length(stack_buffer) }; u8* it = (u8*)format; u64 buffer_i = 0; while (*it) { while (*it && *it != brace_open) { s_get(buffer, buffer_i) = *it; buffer_i += 1; it += 1; } if (*it == brace_open) { it += 1; char next_ch = *it; if (next_ch == brace_open) { trap(); } else { switch (next_ch) { case 'c': { int done = 0; it += 1; if (*it == 's') { it += 1; if (*it == 't') { it += 1; if (*it == 'r') { it += 1; done = 1; auto* cstring = va_arg(args, const char*); while (*cstring) { buffer.pointer[buffer_i] = *cstring; buffer_i += 1; cstring += 1; } } } } assert(done); } break; case 's': { it += 1; if (is_decimal_digit(*it)) { trap(); } else { String string = va_arg(args, String); memcpy(buffer.pointer + buffer_i, string.pointer, string.length); buffer_i += string.length; } } break; case 'u': { it += 1; u8* bit_count_start = it; while (is_decimal_digit(*it)) { it += 1; } u8* bit_count_end = it; u64 bit_count = parse_decimal(slice_from_pointer_range(u8, (u8*)bit_count_start, (u8*)bit_count_end)); typedef enum IntegerFormat : u8 { INTEGER_FORMAT_HEXADECIMAL, INTEGER_FORMAT_DECIMAL, INTEGER_FORMAT_OCTAL, INTEGER_FORMAT_BINARY, } IntegerFormat; IntegerFormat format = INTEGER_FORMAT_DECIMAL; if (*it == ':') { it += 1; switch (*it) { case 'x': format = INTEGER_FORMAT_HEXADECIMAL; break; case 'd': format = INTEGER_FORMAT_DECIMAL; break; case 'o': format = INTEGER_FORMAT_OCTAL; break; case 'b': format = INTEGER_FORMAT_BINARY; break; default: trap(); } it += 1; } u64 original_value; switch (bit_count) { case 8: case 16: case 32: original_value = va_arg(args, u32); break; case 64: original_value = va_arg(args, u64); break; default: trap(); } auto buffer_slice = s_get_slice(u8, buffer, buffer_i, buffer.length); switch (format) { case INTEGER_FORMAT_HEXADECIMAL: { auto written_characters = format_hexadecimal(buffer_slice, original_value); buffer_i += written_characters; } break; case INTEGER_FORMAT_DECIMAL: { auto written_characters = format_decimal(buffer_slice, original_value); buffer_i += written_characters; } break; case INTEGER_FORMAT_OCTAL: case INTEGER_FORMAT_BINARY: trap(); } } break; default: buffer.pointer[buffer_i] = '{'; buffer_i += 1; continue; } if (*it != brace_close) { fail(); } it += 1; } } } String final_string = s_get_slice(u8, buffer, 0, buffer_i); syscall_write(1, final_string.pointer, final_string.length); #endif } global u64 minimum_granularity = page_size; // global u64 middle_granularity = MB(2); global u64 default_size = GB(4); struct Arena { u64 reserved_size; u64 committed; u64 commit_position; u64 granularity; u8 reserved[4 * 8]; }; typedef struct Arena Arena; static_assert(sizeof(Arena) == 64); fn Arena* arena_init(u64 reserved_size, u64 granularity, u64 initial_size) { Arena* arena = (Arena*)reserve(reserved_size); commit(arena, initial_size); *arena = (Arena){ .reserved_size = reserved_size, .committed = initial_size, .commit_position = sizeof(Arena), .granularity = granularity, }; return arena; } fn Arena* arena_init_default(u64 initial_size) { return arena_init(default_size, minimum_granularity, initial_size); } fn u8* arena_allocate_bytes(Arena* arena, u64 size, u64 alignment) { u64 aligned_offset = align_forward(arena->commit_position, alignment); u64 aligned_size_after = aligned_offset + size; if (aligned_size_after > arena->committed) { u64 committed_size = align_forward(aligned_size_after, arena->granularity); u64 size_to_commit = committed_size - arena->committed; void* commit_pointer = (u8*)arena + arena->committed; commit(commit_pointer, size_to_commit); arena->committed = committed_size; } auto* result = (u8*)arena + aligned_offset; arena->commit_position = aligned_size_after; assert(arena->commit_position <= arena->committed); return result; } fn String arena_join_string(Arena* arena, Slice(String) pieces) { u64 size = 0; for (u64 i = 0; i < pieces.length; i += 1) { String piece = pieces.pointer[i]; size += piece.length; } u8* pointer = arena_allocate_bytes(arena, size + 1, 1); auto* it = pointer; for (u64 i = 0; i < pieces.length; i += 1) { String piece = pieces.pointer[i]; memcpy(it, piece.pointer, piece.length); it += piece.length; } assert((u64)(it - pointer) == size); *it = 0; return (String) { .pointer = pointer, .length = size }; } #define arena_allocate(arena, T, count) (T*)(arena_allocate_bytes(arena, sizeof(T) * count, alignof(T))) #define arena_allocate_slice(arena, T, count) (Slice(T)){ .pointer = arena_allocate(arena, T, count), .length = count } may_be_unused fn void arena_reset(Arena* arena) { arena->commit_position = sizeof(Arena); memset(arena + 1, 0, arena->committed - sizeof(Arena)); } #define transmute(D, source) *(D*)&source fn void run_command(CStringSlice arguments, char* envp[]) { print("Running command:\n"); assert(arguments.pointer[arguments.length - 1] == 0); for (u32 i = 0; i < arguments.length - 1; i += 1) { char* argument = arguments.pointer[i]; print("{cstr} ", argument); } print("\n"); pid_t pid = syscall_fork(); if (pid == -1) { trap(); } if (pid == 0) { // close(pipes[0]); // fcntl(pipes[1], F_SETFD, FD_CLOEXEC); auto result = syscall_execve(arguments.pointer[0], arguments.pointer, envp); #if LINK_LIBC print("Execve failed! Error: {cstr}\n", strerror(errno)); #else trap(); #endif unused(result); trap(); } else { int status = 0; int options = 0; pid_t result = syscall_waitpid(pid, &status, options); int success = 0; if (result == pid) { if (WIFEXITED(status)) { auto exit_code = WEXITSTATUS(status); if (exit_code == 0) { success = 1; } } } else { trap(); } if (!success) { print("Program failed to run!\n"); fail(); } } } #define VirtualBuffer(T) VirtualBuffer_ ## T #define VirtualBufferP(T) VirtualBufferPointerTo_ ## T #define decl_vb_ex(T, StructName) \ struct StructName \ {\ T* pointer;\ u32 length;\ u32 capacity;\ };\ typedef struct StructName StructName #define decl_vb(T) decl_vb_ex(T, VirtualBuffer(T)) #define decl_vbp(T) decl_vb_ex(T*, VirtualBufferP(T)) decl_vb(u8); decl_vbp(u8); fn void vb_generic_ensure_capacity(VirtualBuffer(u8)* vb, u32 item_size, u32 item_count) { u32 old_capacity = vb->capacity; u32 wanted_capacity = vb->length + item_count; if (old_capacity < wanted_capacity) { if (old_capacity == 0) { vb->pointer = reserve(item_size * UINT32_MAX); } u32 old_page_capacity = align_forward(old_capacity * item_size, minimum_granularity); u32 new_page_capacity = align_forward(wanted_capacity * item_size, minimum_granularity); u32 commit_size = new_page_capacity - old_page_capacity; void* commit_pointer = vb->pointer + old_page_capacity; commit(commit_pointer, commit_size); u32 new_capacity = new_page_capacity / item_size; vb->capacity = new_capacity; } } fn u8* vb_generic_add_assume_capacity(VirtualBuffer(u8)* vb, u32 item_size, u32 item_count) { u32 index = vb->length; assert(vb->capacity >= index + item_count); vb->length = index + item_count; return vb->pointer + (index * item_size); } // fn u8* vb_generic_append_assume_capacity(VirtualBuffer(u8)* vb, void* item_pointer, u32 item_size, u32 item_count) // { // u8* new_memory = vb_generic_add_assume_capacity(vb, item_size, item_count); // memcpy(new_memory, item_pointer, item_size * item_count); // return new_memory; // } fn u8* vb_generic_add(VirtualBuffer(u8)* vb, u32 item_size, u32 item_count) { vb_generic_ensure_capacity(vb, item_size, item_count); return vb_generic_add_assume_capacity(vb, item_size, item_count); } may_be_unused fn u8* vb_append_bytes(VirtualBuffer(u8*) vb, Slice(u8) bytes) { vb_generic_ensure_capacity(vb, sizeof(u8), bytes.length); auto* pointer = vb_generic_add_assume_capacity(vb, sizeof(u8), bytes.length); memcpy(pointer, bytes.pointer, bytes.length); return pointer; } // fn u8* vb_generic_append(VirtualBuffer(u8)* vb, void* item_pointer, u32 item_size, u32 item_count) // { // vb_generic_ensure_capacity(vb, item_size, item_count); // return vb_generic_append_assume_capacity(vb, item_pointer, item_size, item_count); // } #define vb_add(a, count) (typeof((a)->pointer)) vb_generic_add((VirtualBuffer(u8)*)(a), sizeof(*((a)->pointer)), count) #define vb_append_one(a, item) (typeof((a)->pointer)) vb_generic_append((VirtualBuffer(u8)*)(a), &(item), sizeof(*((a)->pointer)), 1) #define vb_to_bytes(vb) (Slice(u8)) { .pointer = (u8*)((vb).pointer), .length = sizeof(*((vb).pointer)) * (vb).length, }