#ifndef _pre_h_ #define _pre_h_ /* Prelude file, containing some useful macros and types. */ #include #include #include #include #include typedef int8_t i8; typedef uint8_t u8; typedef int16_t i16; typedef uint16_t u16; typedef int32_t i32; typedef uint32_t u32; typedef int64_t i64; typedef uint64_t u64; typedef float f32; typedef double f64; typedef uintptr_t uptr; typedef ptrdiff_t isize; typedef size_t usize; typedef _Bool bool; #define true ((bool)1) #define false ((bool)0) #define nil ((void *)0) #define U64_MAX ((u64)-1) #define Slice(T) \ struct { \ T *s; \ isize len; \ } typedef Slice(u8) Str; #define Optional(T) struct {T val; bool ok;} #define Some(T, v) (T){v, true} #define None(T) (T){.ok = false} /* Meant for use with stb_ds */ #define Vec(T) T * #define HashMap(K, V) struct { K key; V value; } #define HashMapStr(V) struct { char *key; V value; } /* Length of an array */ #define countof(arr) (isize)(sizeof(arr) / sizeof(*(arr))) /* Length of string literal */ #define lengthof(s) (countof(s) - 1) #define ViewMem(T, arr, lo, hi) ((Slice(T)){.s = arr+lo, .len = hi - lo}) #define View(sl, lo, hi) ((sl).s += lo, (sl).len = hi - lo, (sl)) #define foreach(val, arr) for(__typeof__(*(arr)) *__p = (arr), (val) = *__p; __p < (arr)+(arrlen((arr))); (val) = *(__p++)) #define foreach_getindex(val, arr) (&(val) - (arr)) /* Useful integer operations good to have. */ #define max(x, y) ((x) > (y) ? (x) : (y)) #define min(x, y) ((x) < (y) ? (x) : (y)) #define clamp(x, lo, hi) max(lo, min(x, hi)) #define BitPos(pos) (1 << (pos)) #if defined(__GNUC__) || defined(__clang__) # define debugtrap() __builtin_trap() # define unreachable() __builtin_unreachable() #else /* not optimal... */ # define debugtrap() abort() # define unreachable() abort() #endif #ifndef NDEBUG # if defined (__GNUC__) || defined(__clang__) # define Assert(pred) if (!(pred)) { __builtin_trap(); } # else # define Assert(pred) if (!(pred)) { *(volatile int *)0 = 0; } # endif # define trace(...) do { \ fprintf(stderr, "%s:%-5i", __FILE__, __LINE__); \ fprintf(stderr, __VA_ARGS__); \ } while (0) #else # define Assert(pred) # define trace(...) #endif /* Creates a `Str` from a string literal */ #define Sl(s) ((Str){ (u8 *)s, (isize)lengthof(s) }) /* Creates a `Str` from a buffer of size `len` */ #define Sb(s, len) ((Str){ (u8 *)s, (isize)len }) /* Creates a `Str` from a C string. */ #define Str_from_c(s) ((Str){ (u8 *)s, (isize)(s != nil ? strlen(s) : 0) }) #define Str_empty(s) ((s).len == 0) #define Str_default(s, sor) (!Str_empty(s) ? (s) : (sor)) int vsnprintf(char *, unsigned long, const char *, va_list); /* "Converts" a `Str` into a C string. Since `Str` are meant to be * null-terminated already, no conversion is made, but ensures that the * null terminator is present. */ static inline char * Str_to_c(Str s) { if (s.len == 0 || s.s == nil) return nil; Assert(s.s[s.len - 1] == '\0'); return (char *)s.s; } /* Returns `true` if both strings are equal. */ static inline bool Str_equal(Str s1, Str s2) { /* because passing nil to mem* is UB even if size == 0... */ return (s1.len == s2.len) && (s1.len == 0 || memcmp(s1.s, s2.s, s1.len) == 0); } /* Heaps allocates a new `Str` of size `len`, with contents from `data` if it is * not `nil`.*/ static inline Str Str_new(const u8 *data, isize len) { Assert(len >= 0); Str s; s.s = calloc(len + 1, sizeof(*s.s)); s.len = len; if (data != nil) { memcpy(s.s, data, len); s.s[len + 1] = '\0'; /* ensure */ } return s; } /* Returns a formatted string (heap allocated) of the exact required size. */ static inline Str Strafmt(const char *fmt, ...) { Str s = {0}; va_list args; va_start(args, fmt); /* Calculate buffer size required to hold the formatted string */ int reqs = vsnprintf(nil, 0, fmt, args); va_end(args); if (reqs < 0) return s; s = Str_new(nil, reqs); va_start(args, fmt); /* `vsnprintf` touched the arg list, reinitialize it */ /* the nil terminator is guaranteed by `Str_new` */ vsnprintf((char *)s.s, s.len + 1, fmt, args); va_end(args); return s; } #endif