First version
This commit is contained in:
commit
f63877562d
17
.clang-format
Normal file
17
.clang-format
Normal file
@ -0,0 +1,17 @@
|
||||
---
|
||||
AllowAllArgumentsOnNextLine: 'false'
|
||||
AllowAllParametersOfDeclarationOnNextLine: 'false'
|
||||
AlwaysBreakAfterReturnType: AllDefinitions
|
||||
BinPackArguments: 'false'
|
||||
BinPackParameters: 'false'
|
||||
BreakBeforeBraces: Allman
|
||||
ColumnLimit: '120'
|
||||
IndentPPDirectives: BeforeHash
|
||||
IndentWidth: '2'
|
||||
Language: Cpp
|
||||
PointerAlignment: Right
|
||||
SortIncludes: 'false'
|
||||
TabWidth: '2'
|
||||
UseTab: ForContinuationAndIndentation
|
||||
|
||||
...
|
||||
473
rtcore.h
Normal file
473
rtcore.h
Normal file
@ -0,0 +1,473 @@
|
||||
#ifndef RT_CORE_H
|
||||
#define RT_CORE_H
|
||||
|
||||
/* My base layer library.
|
||||
* - fixed width types
|
||||
* - arena allocator
|
||||
* - utility macros
|
||||
* - string type
|
||||
* - simd header inclusion
|
||||
* - threading wrapper
|
||||
* - atomics
|
||||
* - simple file io
|
||||
*/
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <uchar.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifndef RTC_API
|
||||
#ifdef __cplusplus
|
||||
#define RTC_API extern "C"
|
||||
#else
|
||||
#define RTC_API extern
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Nowadays, immintrin.h includes everything I tend to care about (simd)
|
||||
* This assume x64, which is what I'm always using.
|
||||
* You still need to build with the appropriate compilation
|
||||
* flags (-march=avx2 etc. for gcc, /arch:AVX2 etc. for msvc) */
|
||||
#include <immintrin.h>
|
||||
/* MSVC additionally has intrin.h for microsoft specific intrinsics */
|
||||
#ifdef _MSC_VER
|
||||
#include <intrin.h>
|
||||
#endif
|
||||
|
||||
/* Types */
|
||||
typedef uint8_t u8;
|
||||
typedef uint16_t u16;
|
||||
typedef uint32_t u32;
|
||||
typedef uint64_t u64;
|
||||
typedef int8_t i8;
|
||||
typedef int16_t i16;
|
||||
typedef int32_t i32;
|
||||
typedef int64_t i64;
|
||||
typedef float f32;
|
||||
typedef double f64;
|
||||
typedef ptrdiff_t isize;
|
||||
typedef size_t usize;
|
||||
typedef char byte;
|
||||
typedef char16_t c16;
|
||||
typedef int32_t b32;
|
||||
|
||||
/* Utility macros */
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
#ifdef __has_builtin
|
||||
#if __has_builtin(__builtin_expect)
|
||||
#define likely(x) __builtin_expect(!!(x), 1)
|
||||
#define unlikely(x) __builtin_expect(!!(x), 0)
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
#ifndef likely
|
||||
#define likely(x) (x)
|
||||
#define unlikely(x) (x)
|
||||
#endif
|
||||
|
||||
#define internal static
|
||||
#define global_variable static
|
||||
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
#define exported __attribute__((visibility("default")))
|
||||
#elif defined(_MSC_VER)
|
||||
#define exported __dllexport
|
||||
#endif
|
||||
|
||||
#define min(a, b) ((a) < (b) ? (a) : (b))
|
||||
#define max(a, b) ((a) > (b) ? (a) : (b))
|
||||
|
||||
#define kilobytes(n) ((n) * 1024)
|
||||
#define megabytes(n) ((n) * 1024 * 1024)
|
||||
#define gigabytes(n) ((n) * 1024 * 1024 * 1024)
|
||||
|
||||
#define isizeof(x) (isize)sizeof(x)
|
||||
/* number of elements in array a */
|
||||
#define countof(a) (isize)(sizeof(a) / sizeof(*(a)))
|
||||
/* number of characters in string constant s (exluding the terminating 0) */
|
||||
#define lengthof(s) (countof(s) - 1)
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
#define assert(x) \
|
||||
while (!(x)) \
|
||||
{ \
|
||||
__builtin_trap(); \
|
||||
__builtin_unreachable(); \
|
||||
}
|
||||
|
||||
#define force_inline inline __attribute__((always_inline))
|
||||
#elif defined(_MSC_VER)
|
||||
#define assert(x) \
|
||||
if (!(x)) \
|
||||
{ \
|
||||
__debugbreak(); \
|
||||
}
|
||||
|
||||
#define force_inline __forceinline
|
||||
#endif
|
||||
|
||||
/* Arena allocator */
|
||||
typedef struct arena
|
||||
{
|
||||
byte *begin;
|
||||
byte *end;
|
||||
} arena;
|
||||
|
||||
typedef struct arena_cp
|
||||
{
|
||||
byte *cp;
|
||||
} arena_cp;
|
||||
|
||||
static force_inline arena_cp SaveArena(arena a)
|
||||
{
|
||||
return (arena_cp){a.begin};
|
||||
}
|
||||
|
||||
static force_inline void RestoreArena(arena *a, arena_cp cp)
|
||||
{
|
||||
a->begin = cp.cp;
|
||||
}
|
||||
|
||||
enum
|
||||
{
|
||||
/* Disables memory zeroing */
|
||||
ALLOC_NOZERO = 0x1,
|
||||
/* Allocation failure returns NULL instead of aborting */
|
||||
ALLOC_SOFTFAIL = 0x2,
|
||||
};
|
||||
|
||||
/* allocate objects from an arena.
|
||||
* usage is one of:
|
||||
* - a(a, t) - allocate one object of type t from arena a
|
||||
* - alloc(a, t, n) - allocate n objects of type t from arena a
|
||||
* - (a, t, n, f) - allocate n objects of type t from arena a using flags f
|
||||
*/
|
||||
#define alloc(...) allocx(__VA_ARGS__, alloc4, alloc3, alloc2)(__VA_ARGS__)
|
||||
#define allocx(a, b, c, d, e, ...) e
|
||||
#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 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);
|
||||
|
||||
/* String type */
|
||||
typedef struct s8
|
||||
{
|
||||
u8 *data;
|
||||
isize length;
|
||||
} s8;
|
||||
#define S8(_s) \
|
||||
(s8) { .data = (u8 *)_s, .length = lengthof(_s), }
|
||||
|
||||
typedef struct { s8 first; s8 second; } split_result;
|
||||
|
||||
/* constructs a string containing the bytes between begin and end (exclusive) */
|
||||
RTC_API s8 S8Span(u8* begin, u8* end);
|
||||
/* check if two strings are equal */
|
||||
RTC_API b32 S8Equals(s8 a, s8 b);
|
||||
/* compare two strings. analogue to strcmp */
|
||||
RTC_API isize S8Compare(s8 a, s8 b);
|
||||
/* compute a simple hash value for a string. */
|
||||
RTC_API u64 S8Hash(s8 s);
|
||||
/* returns s with leading and trailing whitespace removed.
|
||||
* Treats all characters with a numerical value smaller and equal to 0x20 (space) as whitespace
|
||||
* The new string shares memory with s */
|
||||
RTC_API s8 S8Trim(s8 s);
|
||||
/* returns s with leading whitespace removed.
|
||||
* Treats all characters with a numerical value smaller and equal to 0x20 (space) as whitespace.
|
||||
* The new string shares memory with s */
|
||||
RTC_API s8 S8TrimLeft(s8 s);
|
||||
/* returns s with trailing whitespace removed.
|
||||
* Treats all characters with a numerical value smaller and equal to 0x20 (space) as whitespace.
|
||||
* The new string shares memory with s */
|
||||
RTC_API s8 S8TrimRight(s8 s);
|
||||
/* Returns a pointer to the first occurence of character c (or NULL) */
|
||||
RTC_API u8 *S8Chr(s8 s, u8 c);
|
||||
/* Returns a pointer to the last valid character of s */
|
||||
RTC_API u8 *S8End(s8 s);
|
||||
/* Splits the given string s at the first occurence of c.
|
||||
* If c does not occur, split_result.first contains the whole string */
|
||||
RTC_API split_result S8Split(s8 s, u8 c);
|
||||
/* Creates a clone of string s on arena a */
|
||||
RTC_API s8 S8Clone(s8 s, arena *a);
|
||||
|
||||
/* Basic file io */
|
||||
typedef struct file_buffer
|
||||
{
|
||||
byte *data;
|
||||
isize length;
|
||||
} file_buffer;
|
||||
/* Loads the content of a file. */
|
||||
RTC_API file_buffer ReadEntireFile(s8 path, arena *a);
|
||||
/* Loads the content of a file into a s8 */
|
||||
RTC_API s8 ReadEntireFileS8(s8 path, arena *a);
|
||||
/* Write the given buffer to a file */
|
||||
RTC_API b32 WriteEntireFile(s8 path, byte *data, isize length);
|
||||
|
||||
/* Atomics */
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
/* Atomic add */
|
||||
#define AtomicAdd32(_addend, _val) __atomic_add_fetch((i32 *)_addend, _val, __ATOMIC_SEQ_CST)
|
||||
#define AtomicAdd64(_addend, _val) __atomic_add_fetch((i64 *)_addend, _val, __ATOMIC_SEQ_CST)
|
||||
#define AtomicStore(_ptr, _val) __atomic_store_n(_ptr, _val, __ATOMIC_SEQ_CST)
|
||||
#define AtomicStoreRelease(_ptr, _val) __atomic_store_n(_ptr, _val, __ATOMIC_RELEASE)
|
||||
#define AtomicLoad(_ptr) __atomic_load_n(_ptr, __ATOMIC_SEQ_CST)
|
||||
#define AtomicLoadAcquire(_ptr) __atomic_load_n(_ptr, __ATOMIC_ACQUIRE)
|
||||
#elif defined(_MSC_VER)
|
||||
#define AtomicAdd32(_addend, _val) _InterlockedExchangeAdd((volatile long *)_addend, _val)
|
||||
#define AtomicAdd64(_addend, _val) _InterlockedExchangeAdd64((volatile __int64 *)_addend, _val)
|
||||
#define AtomicStore(_ptr, _val) _InterlockedExchange((volatile long *)_ptr, _val)
|
||||
#define AtomicStoreRelease(_ptr, _val) _InterlockedExchange_HLERelease(_ptr, _val)
|
||||
#define AtomicLoad(_ptr) _InterlockedOr(_ptr, 0)
|
||||
#define AtomicLoadAcquire(_ptr) _InterlockedOr_HLEAcquire(_ptr, 0)
|
||||
#endif
|
||||
|
||||
/* Threading wrapper */
|
||||
typedef struct thread thread;
|
||||
|
||||
/* Win32 uses DWORD as the return type, Linux uses void *.
|
||||
* I don't think that I've ever used a threads return value... */
|
||||
#ifdef _WIN32
|
||||
#define THREAD_RETURN_TYPE u32
|
||||
#elif defined(__linux__)
|
||||
#define THREAD_RETURN_TYPE void *
|
||||
#endif
|
||||
|
||||
/* Generates a thread entry point */
|
||||
#define THREAD_FN(_name) THREAD_RETURN_TYPE _name(void *param)
|
||||
typedef THREAD_FN(thread_fn);
|
||||
/* Starts a new thread */
|
||||
RTC_API thread *StartThread(thread_fn *fn, void *param);
|
||||
/* Waits until a thread terminates, returns its return value. */
|
||||
RTC_API THREAD_RETURN_TYPE JoinThread(thread *t);
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef RT_CORE_IMPLEMENTATION
|
||||
#undef RT_CORE_IMPLEMENTATION
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#ifdef __linux__
|
||||
#include <pthread.h>
|
||||
#elif defined(_WIN32)
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
/* Alloc */
|
||||
RTC_API void *
|
||||
ArenaAlloc(arena *a, isize size, isize align, isize n, int flags)
|
||||
{
|
||||
isize padding = -(usize)a->begin & (align - 1);
|
||||
isize available = a->end - a->begin - padding;
|
||||
if (available < 0 || n > available / size)
|
||||
{
|
||||
if (!(flags & ALLOC_SOFTFAIL))
|
||||
abort();
|
||||
return NULL;
|
||||
}
|
||||
void *p = a->begin + padding;
|
||||
a->begin += padding - n * size;
|
||||
return (flags & ALLOC_NOZERO) ? p : memset(p, 0, n * size);
|
||||
}
|
||||
|
||||
/* S8 funcs */
|
||||
RTC_API s8
|
||||
S8Span(u8* begin, u8* end)
|
||||
{
|
||||
s8 s = {0};
|
||||
s.data = begin;
|
||||
s.length = begin ? end - begin : 0;
|
||||
return s;
|
||||
}
|
||||
RTC_API b32
|
||||
S8Equals(s8 a, s8 b)
|
||||
{
|
||||
return a.length == b.length && (!a.length || (memcmp(a.data, b.data, a.length) == 0));
|
||||
}
|
||||
RTC_API isize
|
||||
S8Compare(s8 a, s8 b)
|
||||
{
|
||||
isize n = min(a.length, b.length);
|
||||
for (isize i = 0; i < n; ++i)
|
||||
{
|
||||
if (a.data[i] != b.data[i])
|
||||
return (isize)a.data[i] - (isize)b.data[i];
|
||||
}
|
||||
return a.length - b.length;
|
||||
}
|
||||
RTC_API u64
|
||||
S8Hash(s8 s)
|
||||
{
|
||||
u64 hash = 0xcbf29ce484222325;
|
||||
for (isize i = 0; i < s.length; ++i)
|
||||
hash = (hash ^ s.data[i]) * 0x00000100000001b3;
|
||||
return hash;
|
||||
}
|
||||
RTC_API s8
|
||||
S8Trim(s8 s)
|
||||
{
|
||||
for (; s.length && *s.data <= ' '; s.data++, s.length--)
|
||||
{
|
||||
}
|
||||
for (; s.length && s.data[s.length - 1] <= ' '; s.length--)
|
||||
{
|
||||
}
|
||||
return s;
|
||||
}
|
||||
RTC_API s8
|
||||
S8TrimLeft(s8 s)
|
||||
{
|
||||
for (; s.length && *s.data <= ' '; s.data++, s.length--)
|
||||
{
|
||||
}
|
||||
return s;
|
||||
}
|
||||
RTC_API s8
|
||||
S8TrimRight(s8 s)
|
||||
{
|
||||
for (; s.length && s.data[s.length - 1] <= ' '; s.length--)
|
||||
{
|
||||
}
|
||||
return s;
|
||||
}
|
||||
RTC_API u8 *
|
||||
S8Chr(s8 s, u8 c)
|
||||
{
|
||||
for (isize i = 0; i < s.length; ++i)
|
||||
{
|
||||
if (s.data[i] == c)
|
||||
return &s.data[i];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
RTC_API u8 *
|
||||
S8End(s8 s)
|
||||
{
|
||||
return &s.data[s.length - 1];
|
||||
}
|
||||
RTC_API split_result
|
||||
S8Split(s8 s, u8 c)
|
||||
{
|
||||
u8 *chr = S8Chr(s, c);
|
||||
if (!chr)
|
||||
return (split_result){s};
|
||||
return (split_result){
|
||||
S8Span(s.data, chr),
|
||||
S8Span(chr, S8End(s)),
|
||||
};
|
||||
}
|
||||
RTC_API s8
|
||||
S8Clone(s8 s, arena *a)
|
||||
{
|
||||
s8 c = {0};
|
||||
c.data = alloc(a, u8, s.length);
|
||||
c.length = s.length;
|
||||
memcpy(c.data, s.data, s.length);
|
||||
return s;
|
||||
}
|
||||
|
||||
/* Basic file io */
|
||||
RTC_API file_buffer
|
||||
ReadEntireFile(s8 path, arena *a)
|
||||
{
|
||||
char _p[260];
|
||||
if (path.length >= countof(_p))
|
||||
return (file_buffer){0};
|
||||
memcpy(_p, path.data, path.length);
|
||||
_p[path.length] = '\0';
|
||||
FILE *f = fopen(_p, "rb");
|
||||
if (!f)
|
||||
return (file_buffer){0};
|
||||
size_t max = (size_t)(a->end - a->begin);
|
||||
size_t num = fread(a->begin, 1, max, f);
|
||||
if (ferror(f))
|
||||
{
|
||||
fclose(f);
|
||||
return (file_buffer){0};
|
||||
}
|
||||
fclose(f);
|
||||
file_buffer fbuf = {
|
||||
.data = (byte *)a->begin,
|
||||
.length = (isize)num,
|
||||
};
|
||||
a->begin += num;
|
||||
return fbuf;
|
||||
}
|
||||
RTC_API s8
|
||||
ReadEntireFileS8(s8 path, arena *a)
|
||||
{
|
||||
file_buffer fb = ReadEntireFile(path, a);
|
||||
return (s8){
|
||||
.data = (u8 *)fb.data,
|
||||
.length = fb.length,
|
||||
};
|
||||
}
|
||||
RTC_API b32
|
||||
WriteEntireFile(s8 path, byte *data, isize length)
|
||||
{
|
||||
char _p[260];
|
||||
if (path.length >= countof(_p))
|
||||
return 0;
|
||||
memcpy(_p, path.data, path.length);
|
||||
_p[path.length] = '\0';
|
||||
FILE *f = fopen(_p, "wb");
|
||||
if (!f)
|
||||
return 0;
|
||||
size_t num = fwrite(data, (size_t)length, 1, f);
|
||||
fclose(f);
|
||||
return num == 1;
|
||||
}
|
||||
|
||||
/* Threading */
|
||||
#ifdef __linux__
|
||||
RTC_API thread *
|
||||
StartThread(thread_fn *fn, void *param)
|
||||
{
|
||||
pthread_t t;
|
||||
if (pthread_create(&t, NULL, fn, param) != 0)
|
||||
return NULL;
|
||||
return (thread *)(usize)t;
|
||||
}
|
||||
RTC_API THREAD_RETURN_TYPE
|
||||
JoinThread(thread *t)
|
||||
{
|
||||
void *ret = NULL;
|
||||
pthread_join((pthread_t)(usize)t, &ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#elif defined(_WIN32)
|
||||
RTC_API thread *
|
||||
StartThread(thread_fn *fn, void *param)
|
||||
{
|
||||
HANDLE h = CreateThread(
|
||||
NULL,
|
||||
0, /* Use default stack size */
|
||||
(LPTHREAD_START_ROUTINE)fn,
|
||||
param,
|
||||
0,
|
||||
NULL);
|
||||
return (thread *)h;
|
||||
}
|
||||
RTC_API THREAD_RETURN_TYPE
|
||||
JoinThread(thread *t)
|
||||
{
|
||||
HANDLE h = (HANDLE)t;
|
||||
if (WaitForSingleObject(h, INFINITE) == WAIT_OBJECT_0)
|
||||
{
|
||||
DWORD exit;
|
||||
GetExitCodeThread(h, &exit);
|
||||
CloseHandle(h);
|
||||
return exit;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
1
somedata.txt
Normal file
1
somedata.txt
Normal file
@ -0,0 +1 @@
|
||||
1234567890
|
||||
60
test.c
Normal file
60
test.c
Normal file
@ -0,0 +1,60 @@
|
||||
#define RT_CORE_IMPLEMENTATION
|
||||
#include "rtcore.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
THREAD_FN(TestThread)
|
||||
{
|
||||
int *p = param;
|
||||
printf("Thread got param %d\n", AtomicLoad(p));
|
||||
AtomicStore(p, 42);
|
||||
return 0;
|
||||
}
|
||||
|
||||
internal int
|
||||
AllocTest(void)
|
||||
{
|
||||
char space[260];
|
||||
arena a = {.begin = space, .end = space + countof(space) };
|
||||
|
||||
int *t = alloc(&a, int);
|
||||
if (!t)
|
||||
return 0;
|
||||
*t = 42;
|
||||
int *t2 = alloc(&a, int);
|
||||
if (t == t2)
|
||||
return 0;
|
||||
*t2 = *t;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
internal int
|
||||
ReadFileTest(void)
|
||||
{
|
||||
char space[260];
|
||||
arena a = {.begin = space, .end = space + countof(space) };
|
||||
s8 data = ReadEntireFileS8(S8("somedata.txt"), &a);
|
||||
return S8Equals(data, S8("1234567890\n"));
|
||||
}
|
||||
|
||||
internal int
|
||||
ThreadTest(void)
|
||||
{
|
||||
int p = 32;
|
||||
thread *t = StartThread(TestThread, &p);
|
||||
JoinThread(t);
|
||||
return AtomicLoad(&p) == 42;
|
||||
}
|
||||
|
||||
int
|
||||
main()
|
||||
{
|
||||
if (!AllocTest())
|
||||
return 1;
|
||||
if (!ReadFileTest())
|
||||
return 2;
|
||||
if (!ThreadTest())
|
||||
return 3;
|
||||
return 0;
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user