This commit is contained in:
Kevin Trogant 2026-01-04 14:17:15 +01:00
parent 207fd90233
commit 8c226f5f31
2 changed files with 251 additions and 243 deletions

View File

@ -7,11 +7,11 @@ BinPackParameters: 'false'
BreakBeforeBraces: Allman BreakBeforeBraces: Allman
ColumnLimit: '120' ColumnLimit: '120'
IndentPPDirectives: BeforeHash IndentPPDirectives: BeforeHash
IndentWidth: '2' IndentWidth: '4'
Language: Cpp Language: Cpp
PointerAlignment: Right PointerAlignment: Right
SortIncludes: 'false' SortIncludes: 'false'
TabWidth: '2' TabWidth: '4'
UseTab: ForContinuationAndIndentation UseTab: AlignWithSpaces
... ...

470
rtcore.h
View File

@ -22,7 +22,7 @@
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE. * SOFTWARE.
*/ */
/* My base layer library. /* My base layer library.
* - fixed width types * - fixed width types
@ -42,11 +42,11 @@
#include <string.h> #include <string.h>
#ifndef RTC_API #ifndef RTC_API
#ifdef __cplusplus #ifdef __cplusplus
#define RTC_API extern "C" #define RTC_API extern "C"
#else #else
#define RTC_API extern #define RTC_API extern
#endif #endif
#endif #endif
/* Nowadays, immintrin.h includes everything I tend to care about (simd) /* Nowadays, immintrin.h includes everything I tend to care about (simd)
@ -86,16 +86,16 @@ typedef int32_t b32;
/* Utility macros */ /* Utility macros */
#if defined(__GNUC__) || defined(__clang__) #if defined(__GNUC__) || defined(__clang__)
#ifdef __has_builtin #ifdef __has_builtin
#if __has_builtin(__builtin_expect) #if __has_builtin(__builtin_expect)
#define likely(x) __builtin_expect(!!(x), 1) #define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0) #define unlikely(x) __builtin_expect(!!(x), 0)
#endif #endif
#endif #endif
#endif #endif
#ifndef likely #ifndef likely
#define likely(x) (x) #define likely(x) (x)
#define unlikely(x) (x) #define unlikely(x) (x)
#endif #endif
#define internal static #define internal static
@ -120,22 +120,22 @@ typedef int32_t b32;
/* number of characters in string constant s (exluding the terminating 0) */ /* number of characters in string constant s (exluding the terminating 0) */
#define lengthof(s) (countof(s) - 1) #define lengthof(s) (countof(s) - 1)
#if defined(__GNUC__) || defined(__clang__) #if defined(__GNUC__) || defined(__clang__)
#define assert(x) \ #define assert(x) \
while (!(x)) \ while (!(x)) \
{ \ { \
__builtin_trap(); \ __builtin_trap(); \
__builtin_unreachable(); \ __builtin_unreachable(); \
} }
#define force_inline inline __attribute__((always_inline)) #define force_inline inline __attribute__((always_inline))
#elif defined(_MSC_VER) #elif defined(_MSC_VER)
#define assert(x) \ #define assert(x) \
if (!(x)) \ if (!(x)) \
{ \ { \
__debugbreak(); \ __debugbreak(); \
} }
#define force_inline __forceinline #define force_inline __forceinline
#else #else
#define assert(x) #define assert(x)
#define force_inline inline #define force_inline inline
@ -144,31 +144,33 @@ typedef int32_t b32;
/* Arena allocator */ /* Arena allocator */
typedef struct arena typedef struct arena
{ {
byte *begin; byte *begin;
byte *end; byte *end;
} arena; } arena;
typedef struct arena_cp typedef struct arena_cp
{ {
byte *cp; byte *cp;
} arena_cp; } arena_cp;
static force_inline arena_cp SaveArena(arena a) static force_inline arena_cp
SaveArena(arena a)
{ {
return (arena_cp){a.begin}; return (arena_cp){a.begin};
} }
static force_inline void RestoreArena(arena *a, arena_cp cp) static force_inline void
RestoreArena(arena *a, arena_cp cp)
{ {
a->begin = cp.cp; a->begin = cp.cp;
} }
enum enum
{ {
/* Disables memory zeroing */ /* Disables memory zeroing */
ALLOC_NOZERO = 0x1, ALLOC_NOZERO = 0x1,
/* Allocation failure returns NULL instead of aborting */ /* Allocation failure returns NULL instead of aborting */
ALLOC_SOFTFAIL = 0x2, ALLOC_SOFTFAIL = 0x2,
}; };
/* allocate objects from an arena. /* allocate objects from an arena.
@ -179,9 +181,9 @@ enum
*/ */
#define alloc(...) allocx(__VA_ARGS__, alloc4, alloc3, alloc2)(__VA_ARGS__) #define alloc(...) allocx(__VA_ARGS__, alloc4, alloc3, alloc2)(__VA_ARGS__)
#define allocx(a, b, c, d, e, ...) e #define allocx(a, b, c, d, e, ...) e
#define alloc2(a, t) (t*)ArenaAlloc(a, isizeof(t), _Alignof(t), 1, 0) #define alloc2(a, t) (t *)ArenaAlloc(a, isizeof(t), _Alignof(t), 1, 0)
#define alloc3(a, t, n) (t*)ArenaAlloc(a, isizeof(t), _Alignof(t), n, 0) #define alloc3(a, t, n) (t *)ArenaAlloc(a, isizeof(t), _Alignof(t), n, 0)
#define alloc4(a, t, n, f) (t*)ArenaAlloc(a, isizeof(t), _Alignof(t), n, f) #define alloc4(a, t, n, f) (t *)ArenaAlloc(a, isizeof(t), _Alignof(t), n, f)
RTC_API void *ArenaAlloc(arena *a, isize size, isize align, isize n, int flags); RTC_API void *ArenaAlloc(arena *a, isize size, isize align, isize n, int flags);
@ -194,10 +196,14 @@ typedef struct s8
#define S8(_s) \ #define S8(_s) \
(s8) { .data = (u8 *)_s, .length = lengthof(_s), } (s8) { .data = (u8 *)_s, .length = lengthof(_s), }
typedef struct { s8 first; s8 second; } split_result; typedef struct
{
s8 first;
s8 second;
} split_result;
/* constructs a string containing the bytes between begin and end (exclusive) */ /* constructs a string containing the bytes between begin and end (exclusive) */
RTC_API s8 S8Span(u8* begin, u8* end); RTC_API s8 S8Span(u8 *begin, u8 *end);
/* check if two strings are equal */ /* check if two strings are equal */
RTC_API b32 S8Equals(s8 a, s8 b); RTC_API b32 S8Equals(s8 a, s8 b);
/* compare two strings. analogue to strcmp */ /* compare two strings. analogue to strcmp */
@ -230,8 +236,16 @@ RTC_API split_result S8Split2(s8 s, u8 c);
/* Creates a clone of string s on arena a */ /* Creates a clone of string s on arena a */
RTC_API s8 S8Clone(s8 s, arena *a); RTC_API s8 S8Clone(s8 s, arena *a);
typedef struct { i64 i; b32 ok; } s8_parse_i64_result; typedef struct
typedef struct { i32 i; b32 ok; } s8_parse_i32_result; {
i64 i;
b32 ok;
} s8_parse_i64_result;
typedef struct
{
i32 i;
b32 ok;
} s8_parse_i32_result;
/* Parses a integer from string s */ /* Parses a integer from string s */
RTC_API s8_parse_i64_result S8ParseI64(s8 s, int base); RTC_API s8_parse_i64_result S8ParseI64(s8 s, int base);
@ -273,49 +287,41 @@ RTC_API b32 WriteEntireFile(s8 path, byte *data, isize length);
#define AtomicLoad(_ptr) _InterlockedOr(_ptr, 0) #define AtomicLoad(_ptr) _InterlockedOr(_ptr, 0)
#define AtomicLoadAcquire(_ptr) _InterlockedOr_HLEAcquire(_ptr, 0) #define AtomicLoadAcquire(_ptr) _InterlockedOr_HLEAcquire(_ptr, 0)
#elif defined(__TINYC__) #elif defined(__TINYC__)
#define AtomicInc32(_addend) do { \ #define AtomicInc32(_addend) \
__asm__ volatile( \ do \
"lock incl %0" \ { \
: "+m" (*_addend) \ __asm__ volatile("lock incl %0" : "+m"(*_addend)); \
); \
} while (0) } while (0)
#define AtomicInc64(_addend) do { \ #define AtomicInc64(_addend) \
__asm__ volatile( \ do \
"lock incq %0" \ { \
: "+m" (*_addend) \ __asm__ volatile("lock incq %0" : "+m"(*_addend)); \
); \
} while (0) } while (0)
#define AtomicAdd32(_addend, _val) do { \ #define AtomicAdd32(_addend, _val) \
__asm__ volatile( \ do \
"lock addl %1, %0" \ { \
: "+m" (*_addend) \ __asm__ volatile("lock addl %1, %0" : "+m"(*_addend) : "r"(_val)); \
: "r" (_val) \
); \
} while (0) } while (0)
#define AtomicAdd64(_addend, _val) do { \ #define AtomicAdd64(_addend, _val) \
__asm__ volatile( \ do \
"lock addq %1, %0" \ { \
: "+m" (*_addend) \ __asm__ volatile("lock addq %1, %0" : "+m"(*_addend) : "r"(_val)); \
: "r" (_val) \
); \
} while (0) } while (0)
/* This uses mov followed by mfence to ensure that /* This uses mov followed by mfence to ensure that
* the store becomes globally visible to any subsequent load or store. */ * the store becomes globally visible to any subsequent load or store. */
#define AtomicStore(_ptr, _val) do { \ #define AtomicStore(_ptr, _val) \
__asm__ volatile( \ do \
"movl %1, %0;" \ { \
"mfence;" \ __asm__ volatile("movl %1, %0;" \
: "=m" (*_ptr) \ "mfence;" \
: "r" (_val) \ : "=m"(*_ptr) \
); \ : "r"(_val)); \
} while(0) } while (0)
#define AtomicStoreRelease(_ptr, _val) do { \ #define AtomicStoreRelease(_ptr, _val) \
__asm__ volatile( \ do \
"movl %1, %0" \ { \
: "=m" (*_ptr) \ __asm__ volatile("movl %1, %0" : "=m"(*_ptr) : "r"(_val)); \
: "r" (_val) \ } while (0)
); \
} while (0)
/* NOTE(Kevin): This should always compile to a mov, which is what we want. */ /* NOTE(Kevin): This should always compile to a mov, which is what we want. */
#define AtomicLoad(_ptr) (*(_ptr)) #define AtomicLoad(_ptr) (*(_ptr))
#define AtomicLoadAcquire(_ptr) (*(_ptr)) #define AtomicLoadAcquire(_ptr) (*(_ptr))
@ -334,30 +340,34 @@ RTC_API b32 WriteEntireFile(s8 path, byte *data, isize length);
#define PopCount32(_x) __builtin_popcount(_x) #define PopCount32(_x) __builtin_popcount(_x)
#define PopCount64(_x) __builtin_popcountl(_x) #define PopCount64(_x) __builtin_popcountl(_x)
#elif defined(_MSC_VER) #elif defined(_MSC_VER)
static force_inline unsigned int CTZ32(u32 x) static force_inline unsigned int
{ CTZ32(u32 x)
unsigned int index; {
_BitScanReverse(&index, x); unsigned int index;
return index; _BitScanReverse(&index, x);
} return index;
static force_inline unsigned int CTZ64(u64 x) }
{ static force_inline unsigned int
unsigned int index; CTZ64(u64 x)
_BitScanReverse64(&index, x); {
return index; unsigned int index;
} _BitScanReverse64(&index, x);
static force_inline unsigned int CLZ32(u32 x) return index;
{ }
unsigned int index; static force_inline unsigned int
_BitScanForward(&index, x); CLZ32(u32 x)
return index; {
} unsigned int index;
static force_inline unsigned int CLZ64(u64 x) _BitScanForward(&index, x);
{ return index;
unsigned int index; }
_BitScanForward64(&index, x); static force_inline unsigned int
return index; CLZ64(u64 x)
} {
unsigned int index;
_BitScanForward64(&index, x);
return index;
}
#define PopCount32(_x) __popcnt(_x) #define PopCount32(_x) __popcnt(_x)
#define PopCount64(_x) __popcnt64(_x) #define PopCount64(_x) __popcnt64(_x)
#else #else
@ -371,9 +381,9 @@ typedef struct thread thread;
/* Win32 uses DWORD as the return type, Linux uses void *. /* Win32 uses DWORD as the return type, Linux uses void *.
* I don't think that I've ever used a threads return value... */ * I don't think that I've ever used a threads return value... */
#ifdef _WIN32 #ifdef _WIN32
#define THREAD_RETURN_TYPE u32 #define THREAD_RETURN_TYPE u32
#elif defined(__linux__) #elif defined(__linux__)
#define THREAD_RETURN_TYPE void * #define THREAD_RETURN_TYPE void *
#endif #endif
/* Generates a thread entry point */ /* Generates a thread entry point */
@ -393,10 +403,10 @@ RTC_API THREAD_RETURN_TYPE JoinThread(thread *t);
#include <stdio.h> #include <stdio.h>
#ifdef __linux__ #ifdef __linux__
#include <pthread.h> #include <pthread.h>
#elif defined(_WIN32) #elif defined(_WIN32)
#define WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN
#include <windows.h> #include <windows.h>
#endif #endif
/* Alloc */ /* Alloc */
@ -418,104 +428,104 @@ ArenaAlloc(arena *a, isize size, isize align, isize n, int flags)
/* S8 funcs */ /* S8 funcs */
RTC_API s8 RTC_API s8
S8Span(u8* begin, u8* end) S8Span(u8 *begin, u8 *end)
{ {
s8 s = {0}; s8 s = {0};
s.data = begin; s.data = begin;
s.length = begin ? end - begin : 0; s.length = begin ? end - begin : 0;
return s; return s;
} }
RTC_API b32 RTC_API b32
S8Equals(s8 a, s8 b) S8Equals(s8 a, s8 b)
{ {
return a.length == b.length && (!a.length || (memcmp(a.data, b.data, a.length) == 0)); return a.length == b.length && (!a.length || (memcmp(a.data, b.data, a.length) == 0));
} }
RTC_API isize RTC_API isize
S8Compare(s8 a, s8 b) S8Compare(s8 a, s8 b)
{ {
isize n = min(a.length, b.length); isize n = min(a.length, b.length);
for (isize i = 0; i < n; ++i) for (isize i = 0; i < n; ++i)
{ {
if (a.data[i] != b.data[i]) if (a.data[i] != b.data[i])
return (isize)a.data[i] - (isize)b.data[i]; return (isize)a.data[i] - (isize)b.data[i];
} }
return a.length - b.length; return a.length - b.length;
} }
RTC_API u64 RTC_API u64
S8Hash(s8 s) S8Hash(s8 s)
{ {
u64 hash = 0xcbf29ce484222325; u64 hash = 0xcbf29ce484222325;
for (isize i = 0; i < s.length; ++i) for (isize i = 0; i < s.length; ++i)
hash = (hash ^ s.data[i]) * 0x00000100000001b3; hash = (hash ^ s.data[i]) * 0x00000100000001b3;
return hash; return hash;
} }
RTC_API s8 RTC_API s8
S8Trim(s8 s) S8Trim(s8 s)
{ {
for (; s.length && *s.data <= ' '; s.data++, s.length--) for (; s.length && *s.data <= ' '; s.data++, s.length--)
{ {
} }
for (; s.length && s.data[s.length - 1] <= ' '; s.length--) for (; s.length && s.data[s.length - 1] <= ' '; s.length--)
{ {
} }
return s; return s;
} }
RTC_API s8 RTC_API s8
S8TrimLeft(s8 s) S8TrimLeft(s8 s)
{ {
for (; s.length && *s.data <= ' '; s.data++, s.length--) for (; s.length && *s.data <= ' '; s.data++, s.length--)
{ {
} }
return s; return s;
} }
RTC_API s8 RTC_API s8
S8TrimRight(s8 s) S8TrimRight(s8 s)
{ {
for (; s.length && s.data[s.length - 1] <= ' '; s.length--) for (; s.length && s.data[s.length - 1] <= ' '; s.length--)
{ {
} }
return s; return s;
} }
RTC_API u8 * RTC_API u8 *
S8Chr(s8 s, u8 c) S8Chr(s8 s, u8 c)
{ {
for (isize i = 0; i < s.length; ++i) for (isize i = 0; i < s.length; ++i)
{ {
if (s.data[i] == c) if (s.data[i] == c)
return &s.data[i]; return &s.data[i];
} }
return NULL; return NULL;
} }
RTC_API u8 * RTC_API u8 *
S8End(s8 s) S8End(s8 s)
{ {
return &s.data[s.length - 1]; return &s.data[s.length - 1];
} }
RTC_API split_result RTC_API split_result
S8Split(s8 s, u8 c) S8Split(s8 s, u8 c)
{ {
u8 *chr = S8Chr(s, c); u8 *chr = S8Chr(s, c);
if (!chr) if (!chr)
return (split_result){s}; return (split_result){s};
return (split_result){ return (split_result){
S8Span(s.data, chr), S8Span(s.data, chr),
S8Span(chr + 1, S8End(s) + 1), S8Span(chr + 1, S8End(s) + 1),
}; };
} }
RTC_API split_result RTC_API split_result
S8Split2(s8 s, u8 c) S8Split2(s8 s, u8 c)
{ {
u8 *chr = S8Chr(s, c); u8 *chr = S8Chr(s, c);
if (!chr) if (!chr)
return (split_result){s}; return (split_result){s};
split_result r = { split_result r = {
.first = S8Span(s.data, chr), .first = S8Span(s.data, chr),
}; };
u8 *end = S8End(s) + 1; u8 *end = S8End(s) + 1;
while (chr != end && *chr == c) while (chr != end && *chr == c)
++chr; ++chr;
r.second = S8Span(chr, end); r.second = S8Span(chr, end);
return r; return r;
} }
RTC_API s8 RTC_API s8
S8Clone(s8 s, arena *a) S8Clone(s8 s, arena *a)
@ -529,38 +539,38 @@ S8Clone(s8 s, arena *a)
RTC_API s8_parse_i64_result RTC_API s8_parse_i64_result
S8ParseI64(s8 s, int base) S8ParseI64(s8 s, int base)
{ {
isize at = s.length - 1; isize at = s.length - 1;
i64 exp = 1; i64 exp = 1;
i64 val = 0; i64 val = 0;
while (at >= 0) while (at >= 0)
{ {
u8 c = s.data[at]; u8 c = s.data[at];
int digit = 0; int digit = 0;
if (c >= '0' && c <= '9') if (c >= '0' && c <= '9')
digit = c - '0'; digit = c - '0';
else if (c >= 'A' && c <= 'Z') else if (c >= 'A' && c <= 'Z')
digit = c - 'A'; digit = c - 'A';
else if (c >= 'a' && c <= 'z') else if (c >= 'a' && c <= 'z')
digit = c - 'a'; digit = c - 'a';
else if (c == '-') else if (c == '-')
{ {
val *= -1; val *= -1;
break; break;
} }
else if (c == '+') else if (c == '+')
{ {
break; break;
} }
else else
return (s8_parse_i64_result){0}; return (s8_parse_i64_result){0};
if (digit >= base) if (digit >= base)
return (s8_parse_i64_result){0}; return (s8_parse_i64_result){0};
val += digit * exp; val += digit * exp;
exp *= base; exp *= base;
--at; --at;
} }
return (s8_parse_i64_result){ return (s8_parse_i64_result){
.i = val, .i = val,
.ok = 1, .ok = 1,
}; };
@ -568,38 +578,38 @@ S8ParseI64(s8 s, int base)
RTC_API s8_parse_i32_result RTC_API s8_parse_i32_result
S8ParseI32(s8 s, int base) S8ParseI32(s8 s, int base)
{ {
isize at = s.length - 1; isize at = s.length - 1;
i32 exp = 1; i32 exp = 1;
i32 val = 0; i32 val = 0;
while (at >= 0) while (at >= 0)
{ {
u8 c = s.data[at]; u8 c = s.data[at];
int digit = 0; int digit = 0;
if (c >= '0' && c <= '9') if (c >= '0' && c <= '9')
digit = c - '0'; digit = c - '0';
else if (c >= 'A' && c <= 'Z') else if (c >= 'A' && c <= 'Z')
digit = c - 'A'; digit = c - 'A';
else if (c >= 'a' && c <= 'z') else if (c >= 'a' && c <= 'z')
digit = c - 'a'; digit = c - 'a';
else if (c == '-') else if (c == '-')
{ {
val *= -1; val *= -1;
break; break;
} }
else if (c == '+') else if (c == '+')
{ {
break; break;
} }
else else
return (s8_parse_i32_result){0}; return (s8_parse_i32_result){0};
if (digit >= base) if (digit >= base)
return (s8_parse_i32_result){0}; return (s8_parse_i32_result){0};
val += digit * exp; val += digit * exp;
exp *= base; exp *= base;
--at; --at;
} }
return (s8_parse_i32_result){ return (s8_parse_i32_result){
.i = val, .i = val,
.ok = 1, .ok = 1,
}; };
@ -679,13 +689,12 @@ JoinThread(thread *t)
RTC_API thread * RTC_API thread *
StartThread(thread_fn *fn, void *param) StartThread(thread_fn *fn, void *param)
{ {
HANDLE h = CreateThread( HANDLE h = CreateThread(NULL,
NULL, 0, /* Use default stack size */
0, /* Use default stack size */ (LPTHREAD_START_ROUTINE)fn,
(LPTHREAD_START_ROUTINE)fn, param,
param, 0,
0, NULL);
NULL);
return (thread *)h; return (thread *)h;
} }
RTC_API THREAD_RETURN_TYPE RTC_API THREAD_RETURN_TYPE
@ -705,4 +714,3 @@ JoinThread(thread *t)
#endif #endif
#endif #endif