begin meta layer

This commit is contained in:
jacob 2025-08-11 22:44:18 -05:00
parent 280f92e096
commit cf8a4820da
29 changed files with 4677 additions and 1149 deletions

57
build.bat Normal file
View File

@ -0,0 +1,57 @@
@echo off
setlocal enabledelayedexpansion
cd /D "%~dp0"
if not exist build mkdir build
::::::::::::::::::::::::::::::::
::~ Unpack args
for %%a in (%*) do set "%%a=1"
if "%~1"=="" (
echo [No arguments provided, assuming default `pp` build]
set meta=1
set pp=1
)
if "%~1"=="release" if "%~2"=="" (
echo [No arguments provided, assuming default 'pp' build]
set meta=1
set pp=1
)
if not "%release%"=="1" set debug=1
if "%release%"=="1" echo [Release]
if "%debug%"=="1" echo [Debug]
::::::::::::::::::::::::::::::::
::~ Build meta program
if "%meta%"=="1" (
echo [Building meta]
:: cl.exe /Fe"build/" /Fo"build/" /c src\\meta\\meta.c /link build/meta.obj
"cl" /Zi /DEBUG src/meta/meta.c /Fo:build/meta.obj /Fe:build/meta.exe /nologo
if not "!errorlevel!" == "0" (
echo .
echo Failed to build meta program
exit /b 1
)
)
::::::::::::::::::::::::::::::::
::~ Build power play
if "%pp%"=="1" (
echo [Building power play]
"build/meta.exe" pp.lay
if not "!errorlevel!" == "0" (
echo.
echo Failed to run meta program
exit /b 1
)
)

1124
build.c

File diff suppressed because it is too large Load Diff

View File

@ -251,7 +251,7 @@ void Startup(void)
P_LogInfoF("Settings file not found, loading default");
window_settings = GetDefaultAppWindowSettings(window);
}
PushStringToBuffer(StringFromArray(window_settings.title), Lit(WINDOW_TITLE));
PushStringToBuff(StringFromArray(window_settings.title), Lit(WINDOW_TITLE));
P_UpdateWindowSettings(window, &window_settings);
EndTempArena(temp);

View File

@ -1,5 +1,10 @@
@Layer base
////////////////////////////////
//~ Deps
@Dep prof
////////////////////////////////
//~ Api

View File

@ -157,7 +157,7 @@ Inline void EndTempArena(TempArena temp)
}
////////////////////////////////
//~ Arena ctx from operations
//~ Arena ctx operations
Inline ArenaCtx *ArenaCtxFromFiberId(i16 fiber_id)
{

View File

@ -1,15 +1,13 @@
////////////////////////////////
//~ Win32 memory allocation
#if PlatformIsWindows
////////////////////////////////
//~ Windows headers
//- Windows headers
#define WIN32_LEAN_AND_MEAN
#define UNICODE
#include <Windows.h>
////////////////////////////////
//~ Memory allocation
//- Reserve
void *ReserveMemory(u64 size)
{

View File

@ -208,7 +208,7 @@ String PushString(Arena *arena, String src)
return str;
}
String PushStringToBuffer(String dst, String src)
String PushStringToBuff(String dst, String src)
{
String result = ZI;
result.len = MinU64(dst.len, src.len);
@ -477,6 +477,7 @@ b32 StringEndsWith(String str, String substring)
* FmtHex: Format a u64 in hexadecimal notation
* FmtPtr: Format a pointer in hexadecimal notation prefixed by "0x"
* FmtHandle: Format a 128 bit handle
* FmtUid: Format a 128 bit uid
*
* FmtEnd (internal): Denote the end of the va_list
*
@ -798,7 +799,7 @@ char *CstrFromString(Arena *arena, String src)
return (char *)text;
}
char *CstrBuffFromStringToBuff(String dst_buff, String src)
char *CstrFromStringToBuff(String dst_buff, String src)
{
if (dst_buff.len > 0)
{

View File

@ -85,9 +85,6 @@ Struct(CodepointIter)
////////////////////////////////
//~ String utils
#define STRING(size, data) (CppCompatInitListType(String) { (size), (data) })
#define Lit(cstr_lit) CppCompatInitListType(String) { (sizeof((cstr_lit)) - 1), (u8 *)(cstr_lit) }
#define LitNoCast(cstr_lit) { .len = (sizeof((cstr_lit)) - 1), .text = (u8 *)(cstr_lit) }
#define StringFromPointers(p0, p1) (CppCompatInitListType(String) { (u8 *)(p1) - (u8 *)(p0), (u8 *)p0 })
#define StringFromStruct(ptr) (CppCompatInitListType(String) { sizeof(*(ptr)), (u8 *)(ptr) })
#define StringFromArena(arena) (STRING((arena)->pos, ArenaBase(arena)))
@ -100,9 +97,8 @@ Struct(CodepointIter)
)
////////////////////////////////
//~ String operartions
//~ Conversion operations
//- Conversion
String StringFromChar(Arena *arena, char c);
String StringFromU64(Arena *arena, u64 n, u64 base, u64 zfill);
String StringFromI64(Arena *arena, i64 n, u64 base, u64 zfill);
@ -111,9 +107,11 @@ String StringFromF64(Arena *arena, f64 f, u32 precision);
String StringFromhandle(Arena *arena, u64 v0, u64 v1);
String StringFromUid(Arena *arena, Uid uid);
//- Modification
////////////////////////////////
//~ String operations
String PushString(Arena *arena, String src);
String PushStringToBuffer(String dst, String src);
String PushStringToBuff(String dst, String src);
String RepeatString(Arena *arena, String src, u64 count);
String CatString(Arena *arena, String str1, String str2);
StringArray SplitString(Arena *arena, String str, String delim);
@ -125,7 +123,10 @@ b32 StringContains(String str, String substring);
b32 StringStartsWith(String str, String substring);
b32 StringEndsWith(String str, String substring);
//- Formatting helpers
////////////////////////////////
//~ Formatting
//- Format arg helpers
#define FmtChar(v) (FmtArg) {.kind = FmtKind_Char, .value.c = (v)}
#define FmtString(v) (FmtArg) {.kind = FmtKind_String, .value.string = (v)}
#define FmtUint(v) (FmtArg) {.kind = FmtKind_Uint, .value.uint = (v)}
@ -139,7 +140,7 @@ b32 StringEndsWith(String str, String substring);
#define FmtUid(v) (FmtArg) {.kind = FmtKind_Uid, .value.uid = (v) }
#define FmtEnd (FmtArg) {.kind = FmtKind_End}
//- Formatting
//- Format functions
#define StringFormat(arena, fmt, ...) _StringFormat((arena), (fmt), __VA_ARGS__, FmtEnd)
String _StringFormat(Arena *arena, String fmt, ...);
String StringFormatV(Arena *arena, String fmt, va_list args);
@ -167,7 +168,7 @@ String32 String32FromString(Arena *arena, String str8);
u64 CstrLenNoLimit(char *cstr);
u64 CstrLen(char *cstr, u64 limit);
char *CstrFromString(Arena *arena, String src);
char *CstrBuffFromStringToBuff(String dest_buff, String src);
char *CstrFromStringToBuff(String dest_buff, String src);
String StringFromCstrNoLimit(char *cstr);
String StringFromCstr(char *cstr, u64 limit);

View File

@ -1,5 +1,3 @@
#include "kernel.h"
ConstantBuffer<K_BlitSig> sig : register(b0);
/* ========================== *

View File

@ -1,5 +1,3 @@
#include "kernel.h"
ConstantBuffer<K_FloodSig> sig : register(b0);
/* ========================== *

View File

@ -1,5 +1,3 @@
#include "kernel.h"
ConstantBuffer<K_MaterialSig> sig : register(b0);
/* ========================== *

68
src/meta/meta.c Normal file
View File

@ -0,0 +1,68 @@
////////////////////////////////
//~ Includes
//- Header files
#include "meta_core.h"
#include "meta.h"
#include "meta_intrinsics.h"
#include "meta_memory.h"
#include "meta_math.h"
#include "meta_arena.h"
#include "meta_string.h"
#include "meta_uni.h"
#include "meta_os.h"
#if PlatformIsWindows
# include "win32/meta_win32.h"
#endif
//- Source files
#include "meta_core.c"
#include "meta_memory.c"
#include "meta_math.c"
#include "meta_arena.c"
#include "meta_string.c"
#include "meta_uni.c"
#if PlatformIsWindows
# include "win32/meta_win32.c"
#endif
////////////////////////////////
//~ Globals
static Arena *perm = 0;
static StringList args = ZI;
////////////////////////////////
//~ Util
/* TODO: Remove printf */
#include <stdio.h>
void Echo(String msg)
{
TempArena scratch = BeginScratchNoConflict();
char *msg_cstr = CstrFromString(scratch.arena, msg);
printf(msg_cstr);
printf("\n");
fflush(stdout);
EndScratch(scratch);
}
////////////////////////////////
//~ Entry point
i32 main(i32 argc, u8 **argv)
{
//- Startup
StartupOs();
perm = AcquireArena(Gibi(64));
//- Unpack args
for (i32 i = 1; i < argc; ++i)
{
String arg = StringFromCstrNoLimit(argv[i]);
PushStringNode(perm, &args, arg);
}
return 0;
}

1
src/meta/meta.h Normal file
View File

@ -0,0 +1 @@
i32 main(i32 argc, u8 **argv);

139
src/meta/meta_arena.c Normal file
View File

@ -0,0 +1,139 @@
SharedArenaCtx shared_arena_ctx = ZI;
/* NOTE: Application will exit if arena fails to reserve or commit initial memory. */
Arena *AcquireArena(u64 reserve)
{
__prof;
reserve += ArenaHeaderSize;
/* Round up to nearest block size */
u64 block_remainder = reserve % ArenaBlockSize;
if (block_remainder > 0)
{
reserve += ArenaBlockSize - block_remainder;
}
u8 *base = ReserveMemory(reserve);
if (!base)
{
/* Hard fail on memory reserve failure for now */
Panic(Lit("Failed to reserve memory"));
}
u64 reserved = reserve;
/* Commit initial block */
base = CommitMemory(base, ArenaBlockSize);
if (!base)
{
/* Hard fail on commit failure */
Panic(Lit("Failed to commit initial memory block: System may be out of memory"));
}
Assert(((u64)base & 0xFFF) == 0); /* Base should be 4k aligned */
StaticAssert(ArenaHeaderSize <= ArenaBlockSize); /* Header must fit in first block */
StaticAssert(sizeof(Arena) <= ArenaHeaderSize); /* Arena struct must fit in header */
__profalloc(base, ArenaBlockSize);
AsanPoison(base + sizeof(Arena), ArenaBlockSize - sizeof(Arena));
/* Create & return arena header at beginning of block */
Arena *arena = (Arena *)base;
ZeroStruct(arena);
arena->committed = ArenaBlockSize - ArenaHeaderSize;
arena->reserved = reserved;
return arena;
}
void ReleaseArena(Arena *arena)
{
AsanUnpoison(arena, arena->committed + ArenaHeaderSize);
__prof;
__proffree(arena);
ReleaseMemory(arena);
}
/* NOTE: Application will exit if arena fails to commit memory */
void *PushBytesNoZero(Arena *arena, u64 size, u64 align)
{
Assert(align > 0);
Assert(!arena->readonly);
void *ptr = 0;
u8 *base = ArenaBase(arena);
/* Check to avoid aligning when size = 0 */
if (size > 0)
{
u64 aligned_start_pos = (arena->pos + (align - 1));
aligned_start_pos -= aligned_start_pos % align;
u64 new_pos = aligned_start_pos + size;
if (new_pos > arena->committed)
{
__profn("Arena commit");
/* Commit new block(s) */
u64 blocks_needed = (new_pos - arena->committed + ArenaBlockSize - 1) / ArenaBlockSize;
u64 commit_bytes = blocks_needed * ArenaBlockSize;
u64 new_capacity = arena->committed + commit_bytes;
if (new_capacity > arena->reserved)
{
/* Hard fail if we overflow reserved memory for now */
Panic(Lit("Failed to commit new memory block: Overflow of reserved memory"));
}
void *commit_address = base + arena->committed;
if (!CommitMemory(commit_address, commit_bytes))
{
/* Hard fail on memory allocation failure for now */
Panic(Lit("Failed to commit new memory block: System may be out of memory"));
}
arena->committed += commit_bytes;
__proffree(arena);
__profalloc(arena, arena->committed + ArenaHeaderSize);
AsanPoison(commit_address, commit_bytes);
}
ptr = base + aligned_start_pos;
AsanUnpoison(ptr, new_pos - aligned_start_pos);
arena->pos = new_pos;
}
else
{
ptr = base + arena->pos;
}
return ptr;
}
/* Copies the memory from the source arena into the destination arena,
* replacing old contents. Destination arena will be expanded if necessary. */
void CopyArena(Arena *dst, Arena *src)
{
ResetArena(dst);
u64 data_size = src->pos;
u8 *data_src = ArenaBase(src);
u8 *data_dst = PushBytesNoZero(dst, data_size, 1);
CopyBytes(data_dst, data_src, data_size);
}
void ShrinkArena(Arena *arena)
{
/* Not implemented */
Assert(0);
LAX arena;
}
void SetArenaReadonly(Arena *arena)
{
#if RtcIsEnabled
arena->readonly = 1;
#endif
SetMemoryReadonly(arena, arena->committed + ArenaHeaderSize);
}
void SetArenaReadWrite(Arena *arena)
{
SetMemoryReadWrite(arena, arena->committed + ArenaHeaderSize);
#if RtcIsEnabled
arena->readonly = 0;
#endif
}

238
src/meta/meta_arena.h Normal file
View File

@ -0,0 +1,238 @@
////////////////////////////////
//~ Arena types
#define ArenaHeaderSize 64
#define ArenaBlockSize 16384
Struct(Arena)
{
u64 pos;
u64 committed;
u64 reserved;
#if RtcIsEnabled
b32 readonly;
#endif
};
Struct(TempArena) {
Arena *arena;
u64 start_pos;
#if RtcIsEnabled
u64 scratch_id;
#endif
};
////////////////////////////////
//~ Per-thread arena ctx types
#define ScratchArenasPerCtx 2
Struct(ArenaCtx)
{
Arena *scratch_arenas[ScratchArenasPerCtx];
Arena *perm_arena;
};
////////////////////////////////
//~ Shared state
Struct(SharedArenaCtx)
{
ArenaCtx arena_contexts[MaxThreads];
};
extern SharedArenaCtx shared_arena_ctx;
////////////////////////////////
//~ Arena push/pop
#define PushStruct(a, type) ((type *)PushBytes((a), sizeof(type), alignof(type)))
#define PushStructNoZero(a, type) ((type *)PushBytesNoZero((a), sizeof(type), alignof(type)))
#define PushStructs(a, type, n) ((type *)PushBytes((a), (sizeof(type) * (n)), alignof(type)))
#define PushStructsNoZero(a, type, n) ((type *)PushBytesNoZero((a), (sizeof(type) * (n)), alignof(type)))
#define PopStruct(a, type, dst) PopBytes((a), sizeof(type), dst)
#define PopStructs(a, type, n, dst) PopBytes((a), sizeof(type) * (n), dst)
/* Returns a pointer to where the next push would be (at alignment of type).
* Equivalent to PushStruct but without actually allocating anything or modifying the arena. */
#define PushDry(a, type) (type *)(_PushDry((a), alignof(type)))
void *PushBytesNoZero(Arena *arena, u64 size, u64 align);
Inline void *PushBytes(Arena *arena, u64 size, u64 align)
{
void *p = PushBytesNoZero(arena, size, align);
ZeroBytes(p, size);
return p;
}
Inline u8 *ArenaBase(Arena *arena)
{
return (u8 *)arena + ArenaHeaderSize;
}
Inline void PopTo(Arena *arena, u64 pos)
{
Assert(arena->pos >= pos);
Assert(!arena->readonly);
AsanPoison(ArenaBase(arena) + pos, arena->pos - pos);
arena->pos = pos;
}
Inline void PopBytes(Arena *arena, u64 size, void *copy_dst)
{
Assert(arena->pos >= size);
Assert(!arena->readonly);
u64 new_pos = arena->pos - size;
void *src = (void *)(ArenaBase(arena) + new_pos);
CopyBytes(copy_dst, src, size);
AsanPoison(ArenaBase(arena) + new_pos, arena->pos - new_pos);
arena->pos = new_pos;
}
Inline void *_PushDry(Arena *arena, u64 align)
{
u64 aligned_start_pos = (arena->pos + (align - 1));
aligned_start_pos -= aligned_start_pos % align;
void *ptr = ArenaBase(arena) + aligned_start_pos;
return ptr;
}
////////////////////////////////
//~ Arena management
Arena *AcquireArena(u64 reserve);
void ReleaseArena(Arena *arena);
void CopyArena(Arena *dst, Arena *src);
void ShrinkArena(Arena *arena);
void SetArenaReadonly(Arena *arena);
void SetArenaReadWrite(Arena *arena);
Inline void *AlignArena(Arena *arena, u64 align)
{
Assert(!arena->readonly);
if (align > 0) {
u64 aligned_start_pos = (arena->pos + (align - 1));
aligned_start_pos -= aligned_start_pos % align;
u64 align_bytes = aligned_start_pos - (u64)arena->pos;
if (align_bytes > 0) {
return (void *)PushStructsNoZero(arena, u8, align_bytes);
} else {
return (void *)(ArenaBase(arena) + arena->pos);
}
} else {
/* 0 alignment */
Assert(0);
return (void *)(ArenaBase(arena) + arena->pos);
}
}
Inline void *ResetArena(Arena *arena)
{
PopTo(arena, 0);
return (void *)ArenaBase(arena);
}
////////////////////////////////
//~ Temp arena operations
Inline TempArena BeginTempArena(Arena *arena)
{
TempArena t = ZI;
t.arena = arena;
t.start_pos = arena->pos;
return t;
}
Inline void EndTempArena(TempArena temp)
{
PopTo(temp.arena, temp.start_pos);
}
////////////////////////////////
//~ Arena ctx operations
Inline ArenaCtx *ArenaCtxFromThreadId(i16 thread_id)
{
SharedArenaCtx *shared = &shared_arena_ctx;
ArenaCtx *ctx = &shared->arena_contexts[thread_id];
if (!ctx->scratch_arenas[0]) {
__profn("Initialize thread arena ctx");
for (i32 i = 0; i < (i32)countof(ctx->scratch_arenas); ++i) {
ctx->scratch_arenas[i] = AcquireArena(Gibi(64));
}
ctx->perm_arena = AcquireArena(Gibi(64));
}
return ctx;
}
////////////////////////////////
//~ Scratch helpers
/* Any parameterized arenas in the caller's scope should be passed into this
* function as a potential "conflict". This is to prevent friction in case the
* passed arena is itself a scratch arena from another scope (since
* parameterized arenas are often used to allocate persistent results for the
* caller).
*
* Use `BeginScratchNoConflict` instead if there is no arena in the current
* scope that could potentially be a scratch arena from another scope. */
#define BeginScratch(potential_conflict) _BeginScratch(potential_conflict)
Inline TempArena _BeginScratch(Arena *potential_conflict)
{
/* This function is currently hard-coded to support 2 scratch arenas */
StaticAssert(ScratchArenasPerCtx == 2);
/* Use `BeginScratchNoConflict` if no conflicts are present */
Assert(potential_conflict != 0);
ArenaCtx *ctx = ArenaCtxFromThreadId(ThreadId());
Arena *scratch_arena = ctx->scratch_arenas[0];
if (potential_conflict && scratch_arena == potential_conflict) {
scratch_arena = ctx->scratch_arenas[1];
}
TempArena temp = BeginTempArena(scratch_arena);
return temp;
}
/* This macro declares an unused "arena" variable that will error if an existing "arena"
* variable is present (due to shadowing). This is for catching obvious cases of
* `BeginScratchNoConflict` getting called when an `arena` variable already
* exists in the caller's scope (`BeginScratch(arena)` should be called
* instead). */
#define BeginScratchNoConflict() \
BeginScratchNoConflict_(); \
do { \
u8 arena = 0; \
LAX arena; \
} while (0)
Inline TempArena BeginScratchNoConflict_(void)
{
ArenaCtx *ctx = ArenaCtxFromThreadId(ThreadId());
Arena *scratch_arena = ctx->scratch_arenas[0];
TempArena temp = BeginTempArena(scratch_arena);
return temp;
}
Inline void EndScratch(TempArena scratch_temp)
{
EndTempArena(scratch_temp);
}
////////////////////////////////
//~ Perm arena helpers
Inline Arena *GetPermArena(void)
{
ArenaCtx *ctx = ArenaCtxFromThreadId(ThreadId());
return ctx->perm_arena;
}

0
src/meta/meta_core.c Normal file
View File

588
src/meta/meta_core.h Normal file
View File

@ -0,0 +1,588 @@
/* TODO: Remove this */
#define RtcIsEnabled 1
#define UnoptimizedIsEnabled 1
////////////////////////////////
//~ Compiler feature flags
/* Compile definition defaults */
#ifndef RtcIsEnabled
# define RtcIsEnabled 0
#endif
#ifndef AsanIsEnabled
# define AsanIsEnabled 0
#endif
#ifndef CrtlibIsEnabled
# define CrtlibIsEnabled 0
#endif
#ifndef DebinfoEnabled
# define DebinfoEnabled 0
#endif
#ifndef DeveloperIsEnabled
# define DeveloperIsEnabled 0
#endif
#ifndef ProfilingIsEnabled
# define ProfilingIsEnabled 0
#endif
#ifndef UnoptimizedIsEnabled
# define UnoptimizedIsEnabled 0
#endif
#ifndef TestsAreEnabled
# define TestsAreEnabled 0
#endif
#ifndef IncbinRawDir
# define IncbinDir ""
#else
# define IncbinDir Stringize(IncbinRawDir)
#endif
////////////////////////////////
//~ Machine context
//- Compiler
#if defined(__clang__)
# define CompilerIsClang 1
# define CompilerIsMsvc 0
#elif defined(_MSC_VER)
# define CompilerIsClang 0
# define CompilerIsMsvc 1
#else
# error Unknown compiler
#endif
//- Language
#if defined(__cplusplus)
# define LanguageIsCpp 1
# define LanguageIsC 0
# define LanguageIsGpu 0
#elif defined(__HLSL_VERSION)
# define LanguageIsCpp 0
# define LanguageIsC 0
# define LanguageIsGpu 1
#else
# define LanguageIsCpp 0
# define LanguageIsC 1
# define LanguageIsGpu 0
#endif
//- Operating system
#if defined(_WIN32)
# define PlatformIsWindows 1
# define PlatformIsMac 0
# define PlatformIsLinux 0
#elif defined(__APPLE__) && defined(__MACH__)
# define PlatformIsWindows 0
# define PlatformIsMac 1
# define PlatformIsLinux 0
#elif defined(__gnu_linux__)
# define PlatformIsWindows 0
# define PlatformIsMac 0
# define PlatformIsLinux 1
#elif LanguageIsGpu
# define PlatformIsWindows 0
# define PlatformIsMac 0
# define PlatformIsLinux 0
#else
# error Unknown platform
#endif
//- Windows NTDDI version
/* FIXME: Remove this */
#if 0
#if CompilerIsMsvc
# define NTDDI_WIN11_DT 0x0C0A0000
# define NTDDI_VERSION 0x0A000000
# if RtcIsEnabled
# define _ALLOW_RTCc_IN_STL 1
# endif
#endif
#endif
////////////////////////////////
//~ Debug
//- Static assert
#if CompilerIsMsvc || (LanguageIsC && __STDC_VERSION__ < 202311L) || LanguageIsGpu
# if CompilerIsMsvc
# define StaticAssert2(cond, line) struct STATIC_ASSERT_____##line {int foo[(cond) ? 1 : -1];}
# define StaticAssert1(cond, line) StaticAssert2(cond, line)
# define StaticAssert(cond) StaticAssert1(cond, __LINE__)
# else
# define StaticAssert(cond) _Static_assert(cond, "")
# endif
#else
# define StaticAssert(c) static_assert(c, "")
#endif
//- Debug assert
#if RtcIsEnabled
# if CompilerIsMsvc
// # define Assert(cond) ((cond) ? 1 : (IsRunningInDebugger() ? (*(volatile i32 *)0 = 0) : Panic(Lit("Assert(" #cond ") failed at " __FILE__ ":" Stringize(__LINE__)))))
# define Assert(cond) ((cond) ? 1 : (IsRunningInDebugger() ? (*(volatile i32 *)0 = 0) : Panic(Lit(__FILE__ "(" Stringize(__LINE__) "): error Assert("#cond")"))))
# define DEBUGBREAK __debugbreak
# else
# define Assert(cond) ((cond) ? 1 : (__builtin_trap(), 0))
# define DEBUGBREAK __builtin_debugtrap()
# endif
# define DEBUGBREAKABLE { volatile i32 __DEBUGBREAKABLE_VAR = 0; LAX __DEBUGBREAKABLE_VAR; } (void)0
#else
# define Assert(cond) (void)(0)
#endif
//- Root constant assert
#define AssertRootConst(s, n) StaticAssert((sizeof(s) % 16 == 0) && /* Root constant struct should pad to 16 byte alignment */ \
((sizeof(s) / 4) == n) && /* Root constant struct size should match the specified 32-bit-constant count */ \
(sizeof(s) <= 256)) /* Root constant struct can only fit 64 DWORDS */
//- Debug alias
/* TODO: Remove this */
#if CompilerIsMsvc
# if DebinfoEnabled
# define DebugAlias(var, alias) *(alias) = &(var)
# else
# define DebugAlias(var, alias) *(alias) = &(var)
# endif
#else
# if DebinfoEnabled
# define DebugAlias(var, alias) __attribute((used)) *(alias) = &(var)
# else
# define DebugAlias(var, alias) __attribute((unused)) *(alias) = &(var)
# endif
#endif
//- Address sanitization
#if AsanIsEnabled
void __asan_poison_memory_region(void const volatile *, size_t);
void __asan_unpoison_memory_region(void const volatile *add, size_t);
# define AsanPoison(addr, size) __asan_poison_memory_region((addr), (size))
# define AsanUnpoison(addr, size) __asan_unpoison_memory_region((addr), (size))
#else
# define AsanPoison(addr, size)
# define AsanUnpoison(addr, size)
#endif
////////////////////////////////
//~ Common utility macros
//- Initlist compatibility
#if CompilerIsMsvc && LanguageIsCpp
# define CppCompatInitListType(type)
#else
# define CppCompatInitListType(type) (type)
#endif
//- ZeroStruct initialization macro
#if LanguageIsC
# define ZI { 0 }
#else
# define ZI { }
#endif
//- Inline
#define Inline static inline
#if CompilerIsMsvc
# define ForceInline Inline __forceinline
#else
# define ForceInline Inline __attribute((always_inline))
#endif
#if CompilerIsMsvc
# define ForceNoInline __declspec(noinline)
#else
# define ForceNoInline __attribute__((noinline))
#endif
//- Static
#define LocalPersist static
#define Global static
/* TODO: Remove this */
#define internal static
//- Read-only
#if PlatformIsWindows
# if CompilerIsMsvc
# pragma section(".rdata$", read)
# define Readonly __declspec(allocate(".rdata$"))
# else
# define Readonly __declspec(allocate(".rdata"))
# endif
#elif PlatformIsMac
# define Readonly __attribute((section("__TEXT,__const")))
#else
# define Readonly __attribute((section(".rodata")))
#endif
//- Barriers
#if CompilerIsMsvc
# define WriteBarrier() _WriteBarrier()
# define ReadBarrier() _ReadBarrier()
#elif defined(__x86_64) || defined(__i386__)
# define WriteBarrier() __asm__ volatile("" ::: "memory")
# define ReadBarrier() __asm__ volatile("" ::: "memory")
#elif LanguageIsGpu
# define WriteBarrier()
# define ReadBarrier()
#else
# error Memory barriers not implemented
#endif
//- Unused markup
/* Strict unused markup */
#if CompilerIsClang
# define UNUSED __attribute((unused))
#else
# define UNUSED
#endif
/* Relaxed unused markup */
#define LAX (void)
//- Fallthrough
#if CompilerIsMsvc
# if LanguageIsCpp
# define FALLTHROUGH [[fallthrough]]
# else
# define FALLTHROUGH
# endif
#elif CompilerIsClang
# define FALLTHROUGH __attribute((fallthrough))
#else
# define FALLTHROUGH
#endif
//- Preprocessor concatenation
#define Cat1(a, b) a ## b
#define Cat(a, b) Cat1(a, b)
//- Preprocessor stringization
#define Stringize1(x) #x
#define Stringize(x) Stringize1(x)
//- Sizes
#define Kibi(n) (n*1024ULL)
#define Mebi(n) (n*Kibi(1024ULL))
#define Gibi(n) (n*Mebi(1024ULL))
#define Tebi(n) (n*Gibi(1024ULL))
//- Time
#define NsFromSeconds(s) ((i64)((s) * 1000000000.0))
#define SecondsFromNs(ns) ((f64)(ns) / 1000000000.0)
////////////////////////////////
//~ Type helper macros
//- typeof
#if CompilerIsMsvc
/* Typeof not supported in MSVC */
# define TypeofIsDefined 0
# define typeof(type) Assert(0)
#else
# define TypeofIsDefined 1
# if LanguageIsCpp || (__STDC_VERSION__ < 202311L)
# define typeof(type) __typeof__(type)
# endif
#endif
//- alignof
#if (CompilerIsMsvc && LanguageIsC) || (LanguageIsC && (__STDC_VERSION__ < 202311L))
# define alignof(type) __alignof(type)
#endif
//- sizeof_field
#define sizeof_field(type, field) sizeof(((type *)0)->field)
//- offsetof
#if 0
#if !CompilerIsMsvc
# if !defined _CRT_USE_BUILTIN_OFFSETOF
# define offsetof(type, field) ((u64)&(((type *)0)->field))
# else
# define offsetof(type, field) __builtin_offsetof(type, field)
# endif
#endif
#endif
////////////////////////////////
//~ Array helper macros
//- countof
#define countof(a) (sizeof(a) / sizeof((a)[0]))
//- IsArray
#define IsIndexable(a) (sizeof(a[0]) != 0)
#define IsArray(a) (IsIndexable(a) && (((void *)&a) == ((void *)a)))
////////////////////////////////
//~ Struct alignment / padding macros
//- Pack
#if CompilerIsMsvc
# define Packed(s) __pragma(pack(push, 1)) s __pragma(pack(pop))
#elif CompilerIsClang
# define Packed(s) s __attribute((__packed__))
#elif LanguageIsGpu
# define Packed(s) s
#endif
//- alignas
#if (CompilerIsMsvc && LanguageIsC) || (LanguageIsC && __STDC_VERSION__ < 202311L)
# if CompilerIsMsvc
# define alignas(n) __declspec(align(n))
# else
# define alignas(n) __attribute__((aligned(n)))
# endif
#endif
////////////////////////////////
//~ Color helper macros
//- Rgba 32 bit helpers
#define Rgb32(r, g, b) Rgba32((r), (g), (b), 0xFF)
#define Rgba32(r, g, b, a) (u32)((u32)(r) | ((u32)(g) << 8) | ((u32)(b) << 16) | ((u32)(a) << 24))
#define Bgr32(rgb) ((((rgb >> 0) & 0xFF) << 16) | (((rgb >> 8) & 0xFF) << 8) | (((rgb >> 16) & 0xFF) << 0))
//- Rgba 32 bit float float helpers
#define _Rgb32U8FromF(fl) ((u8)((fl * 255.0) + 0.5))
#define Rgba32F(r, g, b, a) Rgba32(_Rgb32U8FromF((r)), _Rgb32U8FromF((g)), _Rgb32U8FromF((b)), _Rgb32U8FromF((a)))
#define Rgb32F(r, g, b) Rgba32F((r), (g), (b), 1.f)
#define Alpha32F(color, a) ((color) & 0x00FFFFFF) | (_Rgb32U8FromF((a)) << 24)
//- Pre-defined colors
#define ColorWhite Rgb32(0xFF, 0xFF, 0xFF)
#define ColorBlack Rgb32(0x00, 0x00, 0x00)
#define ColorRed Rgb32(0xFF, 0x00, 0x00)
#define ColorGreen Rgb32(0x00, 0xFF, 0x00)
#define ColorBlue Rgb32(0x00, 0x00, 0xFF)
#define ColorYellow Rgb32(0xFF, 0xFF, 0x00)
#define ColorOrange Rgb32(0xFF, 0xA5, 0x00)
#define ColorPurple Rgb32(0xFF, 0x00, 0XFF)
////////////////////////////////
//~ Gpu helpers
#if LanguageIsGpu
//- Resource heap index
# define GpuResourceFromUrid(urid) ResourceDescriptorHeap[urid]
# define GpuResourceFromNurid(nurid) ResourceDescriptorHeap[NonUniformResourceIndex(nurid)]
//- Semantic declaration
# define Semantic(t, n) t n : n
#endif
////////////////////////////////
//~ Intrinsic headers
#if !LanguageIsGpu
/* Intrinsic header info:
* <mmintrin.h" MMX
* <xmmintrin.h" SSE
* <emmintrin.h" SSE2
* <pmmintrin.h" SSE3
* <tmmintrin.h" SSSE3
* <smmintrin.h" SSE4.1
* <nmmintrin.h" SSE4.2
* <ammintrin.h" SSE4A
* <wmmintrin.h" AES
* <immintrin.h" AVX, AVX2, FMA
*/
#include "intrin.h"
#include "nmmintrin.h" /* SSE4.2 */
#endif
////////////////////////////////
//~ Struct helper macros
#define Struct(name) typedef struct name name; struct name
#define AlignedStruct(name, n) typedef struct name name; struct alignas(n) name
////////////////////////////////
//~ Scalar types
#if !LanguageIsGpu
//- Cpu scalar types
#include "stdint.h"
typedef int8_t i8;
typedef int16_t i16;
typedef int32_t i32;
typedef int64_t i64;
typedef uint8_t u8;
typedef uint16_t u16;
typedef uint32_t u32;
typedef uint64_t u64;
typedef float f32;
typedef double f64;
typedef i8 b8;
typedef u32 b32;
#else
//- Gpu scalar types
typedef int i32;
typedef uint u32;
typedef float f32;
typedef uint b32;
#endif
//- Min / max constants
#define U8Max (0xFF)
#define U16Max (0xFFFF)
#define U32Max (0xFFFFFFFF)
#define U64Max (0xFFFFFFFFFFFFFFFFULL)
#define I8Max (0x7F)
#define I16Max (0x7FFF)
#define I32Max (0x7FFFFFFF)
#define I64Max (0x7FFFFFFFFFFFFFFFLL)
#define I8Min ((i8)-0x80)
#define I16Min ((i16)0x8000)
#define I32Min ((i32)0x80000000)
#define I64Min ((i64)0x8000000000000000LL)
//- Float infinity / nan constants
#if !LanguageIsGpu
Global const u32 _f32_infinity_u32 = 0x7f800000;
Global const f32 *_f32_infinity = (f32 *)&_f32_infinity_u32;
#define F32Infinity (*_f32_infinity)
Global const u64 _f64_infinity_u64 = 0x7ff0000000000000ULL;
Global const f64 *_f64_infinity = (f64 *)&_f64_infinity_u64;
#define F64Infinity (*_f64_infinity)
Global const u32 _f32_nan_u32 = 0x7f800001;
Global const f32 *_f32_nan = (f32 *)&_f32_nan_u32;
#define F32Nan (*_f32_nan)
Global const u64 _f64_nan_u64 = 0x7ff8000000000001;
Global const f64 *_f64_nan = (f64 *)&_f64_nan_u64;
#define F64Nan (*_f64_nan)
#define IsF32Nan(x) (x != x)
#define IsF64Nan(x) (x != x)
#endif
////////////////////////////////
//~ Atomics
#if !LanguageIsGpu
//- Atomic types
Struct(Atomic8) { volatile i8 _v; };
Struct(Atomic16) { volatile i16 _v; };
Struct(Atomic32) { volatile i32 _v; };
Struct(Atomic64) { volatile i64 _v; };
//- Cache-line isolated aligned atomic types
AlignedStruct(Atomic8Padded, 64) { Atomic8 v; u8 _pad[60]; };
AlignedStruct(Atomic16Padded, 64) { Atomic16 v; u8 _pad[60]; };
AlignedStruct(Atomic32Padded, 64) { Atomic32 v; u8 _pad[60]; };
AlignedStruct(Atomic64Padded, 64) { Atomic64 v; u8 _pad[56]; };
StaticAssert(sizeof(Atomic8Padded) == 64 && alignof(Atomic8Padded) == 64);
StaticAssert(sizeof(Atomic16Padded) == 64 && alignof(Atomic16Padded) == 64);
StaticAssert(sizeof(Atomic32Padded) == 64 && alignof(Atomic32Padded) == 64);
StaticAssert(sizeof(Atomic64Padded) == 64 && alignof(Atomic64Padded) == 64);
#if PlatformIsWindows
//- 8 bit atomics operations
ForceInline i8 Atomic8Fetch(Atomic8 *x) { return (i8)_InterlockedCompareExchange8((char *)&x->_v, 0, 0); }
ForceInline i8 Atomic8FetchSet(Atomic8 *x, i8 e) { return (i8)_InterlockedExchange8((char *)&x->_v, e); }
ForceInline i8 Atomic8FetchTestSet(Atomic8 *x, i8 c, i8 e) { return (i8)_InterlockedCompareExchange8((char *)&x->_v, e, c); }
ForceInline i8 Atomic8FetchXor(Atomic8 *x, i8 c) { return (i8)_InterlockedXor8((char *)&x->_v, c); }
ForceInline i8 Atomic8FetchAdd(Atomic8 *x, i8 a) { return (i8)_InterlockedExchangeAdd8((char *)&x->_v, a); }
//- 16 bit atomic operations
ForceInline i16 Atomic16Fetch(Atomic16 *x) { return (i16)_InterlockedCompareExchange16(&x->_v, 0, 0); }
ForceInline i16 Atomic16FetchSet(Atomic16 *x, i16 e) { return (i16)_InterlockedExchange16(&x->_v, e); }
ForceInline i16 Atomic16FetchTestSet(Atomic16 *x, i16 c, i16 e) { return (i16)_InterlockedCompareExchange16(&x->_v, e, c); }
ForceInline i16 Atomic16FetchTestXor(Atomic16 *x, i16 c) { return (i16)_InterlockedXor16(&x->_v, c); }
ForceInline i16 Atomic16FetchTestAdd(Atomic16 *x, i16 a) { return (i16)_InterlockedExchangeAdd16(&x->_v, a); }
//- 32 bit atomic operations
ForceInline i32 Atomic32Fetch(Atomic32 *x) { return (i32)_InterlockedCompareExchange((volatile long *)&x->_v, 0, 0); }
ForceInline i32 Atomic32FetchSet(Atomic32 *x, i32 e) { return (i32)_InterlockedExchange((volatile long *)&x->_v, e); }
ForceInline i32 Atomic32FetchTestSet(Atomic32 *x, i32 c, i32 e) { return (i32)_InterlockedCompareExchange((volatile long *)&x->_v, e, c); }
ForceInline i32 Atomic32FetchXor(Atomic32 *x, i32 c) { return (i32)_InterlockedXor((volatile long *)&x->_v, c); }
ForceInline i32 Atomic32FetchAdd(Atomic32 *x, i32 a) { return (i32)_InterlockedExchangeAdd((volatile long *)&x->_v, a); }
//- 64 bit atomic operations
ForceInline i64 Atomic64Fetch(Atomic64 *x) { return (i64)_InterlockedCompareExchange64(&x->_v, 0, 0); }
ForceInline i64 Atomic64FetchSet(Atomic64 *x, i64 e) { return (i64)_InterlockedExchange64(&x->_v, e); }
ForceInline i64 Atomic64FetchTestSet(Atomic64 *x, i64 c, i64 e) { return (i64)_InterlockedCompareExchange64(&x->_v, e, c); }
ForceInline i64 Atomic64FetchXor(Atomic64 *x, i64 c) { return (i64)_InterlockedXor64(&x->_v, c); }
ForceInline i64 Atomic64FetchAdd(Atomic64 *x, i64 a) { return (i64)_InterlockedExchangeAdd64(&x->_v, a); }
#else
# error Atomics not implemented
#endif
#endif
////////////////////////////////
//~ Ticket mutex
#if !LanguageIsGpu
Struct(TicketMutex)
{
Atomic64Padded ticket;
Atomic64Padded serving;
};
ForceInline void LockTicketMutex(TicketMutex *tm)
{
i64 ticket = Atomic64FetchAdd(&tm->ticket.v, 1);
while (Atomic64Fetch(&tm->serving.v) != ticket)
{
_mm_pause();
}
}
ForceInline void UnlockTicketMutex(TicketMutex *tm)
{
Atomic64FetchAdd(&tm->serving.v, 1);
}
#endif
////////////////////////////////
//~ String types
#define STRING(size, data) (CppCompatInitListType(String) { (size), (data) })
#define Lit(cstr_lit) CppCompatInitListType(String) { (sizeof((cstr_lit)) - 1), (u8 *)(cstr_lit) }
#define LitNoCast(cstr_lit) { .len = (sizeof((cstr_lit)) - 1), .text = (u8 *)(cstr_lit) }
Struct(String)
{
u64 len;
u8 *text;
};
Struct(String16)
{
u64 len;
u16 *text;
};
Struct(String32)
{
u64 len;
u32 *text;
};
////////////////////////////////
//~ @hookdecl Core hooks
void Panic(String msg);
b32 IsRunningInDebugger(void);
i16 ThreadId(void);
#define MaxThreads 1024
StaticAssert(MaxThreads < I16Max); /* Thread id type should fit max threads */
////////////////////////////////
//~ Prof
#include "../prof/prof_tracy.h"

105
src/meta/meta_intrinsics.h Normal file
View File

@ -0,0 +1,105 @@
////////////////////////////////
//~ Sqrt intrinsics
Inline f32 IxSqrtF32(f32 f)
{
__m128 n = _mm_set_ss(f);
n = _mm_sqrt_ss(n);
return _mm_cvtss_f32(n);
}
Inline f64 IxSqrtF64(f64 f)
{
__m128d n = _mm_set_sd(f);
n = _mm_sqrt_sd(_mm_setzero_pd(), n);
return _mm_cvtsd_f64(n);
}
Inline f32 IxRsqrtF32(f32 f)
{
__m128 n = _mm_set_ss(f);
n = _mm_rsqrt_ss(n);
return _mm_cvtss_f32(n);
}
////////////////////////////////
//~ Round intrinsics
Inline i32 IxRoundF32ToI32(f32 f)
{
return _mm_cvtss_si32(_mm_round_ss(_mm_setzero_ps(), _mm_set_ss(f), _MM_FROUND_TO_NEAREST_INT | _MM_FROUND_NO_EXC));
}
Inline f32 IxRoundF32ToF32(f32 f)
{
return _mm_cvtss_f32(_mm_round_ss(_mm_setzero_ps(), _mm_set_ss(f), _MM_FROUND_TO_NEAREST_INT | _MM_FROUND_NO_EXC));
}
Inline i64 IxRoundF64ToI64(f64 f)
{
return _mm_cvtsd_si64(_mm_round_sd(_mm_setzero_pd(), _mm_set_sd(f), _MM_FROUND_TO_NEAREST_INT | _MM_FROUND_NO_EXC));
}
Inline f64 IxRoundF64ToF64(f64 f)
{
return _mm_cvtsd_f64(_mm_round_sd(_mm_setzero_pd(), _mm_set_sd(f), _MM_FROUND_TO_NEAREST_INT | _MM_FROUND_NO_EXC));
}
////////////////////////////////
//~ Floor intrinsics
Inline i32 IxFloorF32ToI32(f32 f)
{
return _mm_cvtss_si32(_mm_floor_ss(_mm_setzero_ps(), _mm_set_ss(f)));
}
Inline f32 IxFloorF32ToF32(f32 f)
{
return _mm_cvtss_f32(_mm_floor_ss(_mm_setzero_ps(), _mm_set_ss(f)));
}
Inline i64 IxFloorF64ToI64(f64 f)
{
return _mm_cvtsd_si64(_mm_floor_sd(_mm_setzero_pd(), _mm_set_sd(f)));
}
Inline f64 IxFloorF64ToF64(f64 f)
{
return _mm_cvtsd_f64(_mm_floor_sd(_mm_setzero_pd(), _mm_set_sd(f)));
}
////////////////////////////////
//~ Ceil intrinsics
Inline i32 IxCeilF32ToI32(f32 f)
{
return _mm_cvtss_si32(_mm_ceil_ss(_mm_setzero_ps(), _mm_set_ss(f)));
}
Inline f32 IxCeilF32ToF32(f32 f)
{
return _mm_cvtss_f32(_mm_ceil_ss(_mm_setzero_ps(), _mm_set_ss(f)));
}
Inline i64 IxCeilF64ToI64(f64 f)
{
return _mm_cvtsd_si64(_mm_ceil_sd(_mm_setzero_pd(), _mm_set_sd(f)));
}
Inline f64 IxCeilF64ToF64(f64 f)
{
return _mm_cvtsd_f64(_mm_ceil_sd(_mm_setzero_pd(), _mm_set_sd(f)));
}
////////////////////////////////
//~ Truncate intrinsics
Inline f32 IxTruncF32ToF32(f32 f)
{
return _mm_cvtss_f32(_mm_round_ss(_mm_setzero_ps(), _mm_set_ss(f), _MM_FROUND_TO_ZERO | _MM_FROUND_NO_EXC));
}
Inline f64 IxTruncF64ToF64(f64 f)
{
return _mm_cvtsd_f64(_mm_round_sd(_mm_setzero_pd(), _mm_set_sd(f), _MM_FROUND_TO_ZERO | _MM_FROUND_NO_EXC));
}

1447
src/meta/meta_math.c Normal file

File diff suppressed because it is too large Load Diff

413
src/meta/meta_math.h Normal file
View File

@ -0,0 +1,413 @@
/* Math functions are default 32 bit (f32, i32, etc) unless specified */
#define Pi ((f32)3.14159265358979323846)
#define Tau ((f32)6.28318530717958647693)
#define GoldenRatio ((f32)1.61803398874989484820)
////////////////////////////////
//~ Floating point vector2 types
Struct(Vec2) {
f32 x, y;
};
#define VEC2(x, y) CppCompatInitListType(Vec2) { (x), (y) }
Struct(Vec2Array) {
Vec2 *points;
u64 count;
};
////////////////////////////////
//~ Floating point vector3 types
Struct(Vec3) {
f32 x, y, z;
};
#define VEC3(x, y, z) CppCompatInitListType(Vec3) { (x), (y), (z) }
Struct(Vec3Array) {
Vec3 *points;
u64 count;
};
////////////////////////////////
//~ Floating point vector4 types
Struct(Vec4) {
f32 x, y, z, w;
};
#define VEC4(x, y, z, w) ((Vec4) { (x), (y), (z), (w) })
Struct(Vec4Array) {
Vec4 *points;
u64 count;
};
////////////////////////////////
//~ Integer vec2 types
Struct(Vec2I32) {
i32 x, y;
};
#define VEC2I32(x, y) CppCompatInitListType(Vec2I32) { (x), (y) }
////////////////////////////////
//~ Integer vector3 types
Struct(Vec3I32) {
i32 x, y, z;
};
#define VEC3I32(x, y, z) CppCompatInitListType(Vec3I32) { (x), (y), (z) }
////////////////////////////////
//~ Integer vector4 types
Struct(Vec4I32)
{
i32 x, y, z, w;
};
#define VEC4I32(x, y, z, w) CppCompatInitListType(Vec4I32) { (x), (y), (z), (w) }
////////////////////////////////
//~ Xform types
Struct(Xform)
{
Vec2 bx; /* X basis vector (x axis) */
Vec2 by; /* Y basis vector (y axis)*/
Vec2 og; /* Translation vector (origin) */
};
/* (T)ranslation, (R)otation, (S)cale */
Struct(Trs)
{
Vec2 t;
Vec2 s;
f32 r;
};
////////////////////////////////
//~ Rect types
Struct(Rect) {
union {
struct { f32 x, y, width, height; };
struct { Vec2 pos, size; };
};
};
/* Values expected to be normalized 0.0 -> 1.0 */
Struct(ClipRect)
{
Vec2 p0, p1;
};
////////////////////////////////
//~ Axis aligned bounding box types
Struct(Aabb) {
Vec2 p0, p1;
};
////////////////////////////////
//~ Quad types
Struct(Quad) {
union {
struct { Vec2 p0, p1, p2, p3; };
struct { Vec2 e[4]; };
};
};
////////////////////////////////
//~ Spring types
Struct(SoftSpring)
{
f32 bias_rate;
f32 mass_scale;
f32 impulse_scale;
};
////////////////////////////////
//~ Mat4x4 types
Struct(Mat4x4)
{
union
{
struct { Vec4 bx, by, bz, bw; };
f32 e[4][4];
};
};
////////////////////////////////
//~ Min / max
//- Min
u8 MinU8(u8 a, u8 b);
i8 MinI8(i8 a, i8 b);
u32 MinU32(u32 a, u32 b);
i32 MinI32(i32 a, i32 b);
f32 MinF32(f32 a, f32 b);
u64 MinU64(u64 a, u64 b);
i64 MinI64(i64 a, i64 b);
f64 MinF64(f64 a, f64 b);
//- Max
u8 MaxU8(u8 a, u8 b);
i8 MaxI8(i8 a, i8 b);
u32 MaxU32(u32 a, u32 b);
i32 MaxI32(i32 a, i32 b);
f32 MaxF32(f32 a, f32 b);
u64 MaxU64(u64 a, u64 b);
i64 MaxI64(i64 a, i64 b);
f64 MaxF64(f64 a, f64 b);
//- Clamp
u32 ClampU32(u32 v, u32 min, u32 max);
i32 ClampI32(i32 v, i32 min, i32 max);
f32 ClampF32(f32 v, f32 min, f32 max);
u64 ClampU64(u64 v, u64 min, u64 max);
i64 ClampI64(i64 v, i64 min, i64 max);
f64 ClampF64(f64 v, f64 min, f64 max);
////////////////////////////////
//~ Rounding ops
//- Round
f32 RoundF32(f32 f);
f64 RoundF64(f64 f);
i32 RoundF32ToI32(f32 f);
i64 RoundF64ToI64(f64 f);
//- Floor
f32 FloorF32(f32 f);
f64 FloorF64(f64 f);
i32 FloorF32ToI32(f32 f);
i64 FloorF64ToI64(f64 f);
//- Ceil
f32 CeilF32(f32 f);
f64 CeilF64(f64 f);
i32 CeilF32ToI32(f32 f);
i64 CeilF64ToI64(f64 f);
//- Trunc
f32 TruncF32(f32 f);
f64 TruncF64(f64 f);
////////////////////////////////
//~ Fmod
f32 ModF32(f32 x, f32 m);
f64 ModF64(f64 x, f64 m);
////////////////////////////////
//~ Abs
f32 AbsF32(f32 f);
f64 AbsF64(f64 f);
u32 AbsI32(i32 v);
u64 AbsI64(i64 v);
i32 SignF32(f32 f);
i64 SignF64(f64 f);
////////////////////////////////
//~ Exponential ops
u64 PowU64(u64 base, u8 exp);
u64 AlignU64Pow2(u64 x);
f32 LnF32(f32 x);
f32 ExpF32(f32 x);
f32 PowF32(f32 a, f32 b);
f32 SqrtF32(f32 x);
f64 SqrtF64(f64 x);
f32 RSqrtF32(f32 x);
////////////////////////////////
//~ Trig
f32 ReduceToPio4(f32 x, i32 *octant_out);
f32 SinApproxF32(f32 x);
f32 CosApproxF32(f32 x);
f32 SinF32(f32 x);
f32 CosF32(f32 x);
f32 ArcTanF32(f32 x);
f32 ArcTan2F32(f32 y, f32 x);
f32 ArcSinF32(f32 x);
f32 ArcCosF32(f32 x);
////////////////////////////////
//~ Angle unwind
/* Returns angle in range [-Pi, Pi] */
f32 UnwindAngleF32(f32 a);
////////////////////////////////
//~ Float lerp
f32 LerpF32(f32 val0, f32 val1, f32 t);
f64 LerpF64(f64 val0, f64 val1, f64 t);
f32 LerpAngleF32(f32 a, f32 b, f32 t);
////////////////////////////////
//~ Int lerp
i32 LerpI32(i32 val0, i32 val1, f32 t);
i64 LerpI64(i64 val0, i64 val1, f64 t);
////////////////////////////////
//~ Vec2 operations
#define Vec2FromVec2I32(v) VEC2((v).x, (v).y)
b32 IsVec2Zero(Vec2 a);
b32 EqVec2(Vec2 a, Vec2 b);
//- Mul
Vec2 MulVec2(Vec2 a, f32 s);
Vec2 MulVec2Vec2(Vec2 a, Vec2 b);
Vec2 NegVec2(Vec2 a);
//- Div
Vec2 DivVec2(Vec2 a, f32 s);
Vec2 DivVec2Vec2(Vec2 a, Vec2 b);
//- Add
Vec2 AddVec2(Vec2 a, Vec2 b);
Vec2 SubVec2(Vec2 a, Vec2 b);
//- Len
f32 Vec2Len(Vec2 a);
f32 Vec2LenSq(Vec2 a);
Vec2 Vec2WithLen(Vec2 a, f32 len);
Vec2 ClampVec2Len(Vec2 a, f32 max);
f32 Vec2Distance(Vec2 a, Vec2 b);
Vec2 NormVec2(Vec2 a);
//- Dot
f32 DotVec2(Vec2 a, Vec2 b);
f32 WedgeVec2(Vec2 a, Vec2 b);
Vec2 PerpVec2(Vec2 a);
Vec2 MulPerpVec2(Vec2 a, f32 s);
Vec2 PerpVec2TowardsDir(Vec2 v, Vec2 dir);
//- Round / floor / ceil
Vec2 RoundVec2(Vec2 a);
Vec2I32 RoundVec2ToVec2I32(Vec2 a);
Vec2 FloorVec2(Vec2 a);
Vec2 CeilVec2(Vec2 a);
//- Angle
i32 WindingFromVec2(Vec2 a, Vec2 b);
Vec2 RotateVec2(Vec2 v, f32 a);
Vec2 Vec2FromAngle(f32 a);
f32 AngleFromVec2(Vec2 v);
f32 AngleFromVec2Dirs(Vec2 dir1, Vec2 dir2);
f32 AngleFromVec2Points(Vec2 pt1, Vec2 pt2);
//- Closest point
Vec2 ClosestPointFromRay(Vec2 ray_pos, Vec2 ray_dir_norm, Vec2 p);
//- Lerp
Vec2 LerpVec2(Vec2 val0, Vec2 val1, f32 t);
Vec2 SlerpVec2(Vec2 val0, Vec2 val1, f32 t);
////////////////////////////////
//~ Vec2I32 Operations
b32 EqVec2I32(Vec2I32 a, Vec2I32 b);
Vec2I32 NegVec2I32(Vec2I32 a);
Vec2I32 AddVec2I32(Vec2I32 a, Vec2I32 b);
Vec2I32 SubVec2I32(Vec2I32 a, Vec2I32 b);
////////////////////////////////
//~ Xform operations
b32 EqXform(Xform xf1, Xform xf2);
//- Initialization
#define XformIdentity CppCompatInitListType(Xform) { .bx = VEC2(1, 0), .by = VEC2(0, 1) }
#define XformIdentityNoCast { .bx = VEC2(1, 0), .by = VEC2(0, 1) }
Xform XformFromPos(Vec2 v);
Xform XformFromRot(f32 r);
Xform XformFromScale(Vec2 scale);
Xform XformFromTrs(Trs trs);
Xform XformFromRect(Rect rect);
//- Translation
Xform TranslateXform(Xform xf, Vec2 v);
Xform WorldTranslateXform(Xform xf, Vec2 v);
//- Rotation
Xform RotateXform(Xform xf, f32 r);
Xform WorldRotateXform(Xform xf, f32 r);
Xform WorldRotateXformBasis(Xform xf, f32 r);
Xform XformWIthWorldRotation(Xform xf, f32 r);
//- Scale
Xform ScaleXform(Xform xf, Vec2 scale);
Xform WorldScaleXform(Xform xf, Vec2 scale);
//- Lerp
Xform LerpXform(Xform a, Xform b, f32 t);
//- Invert
Xform InvertXform(Xform xf);
//- Mul
Vec2 MulXformV2(Xform xf, Vec2 v);
Xform MulXform(Xform a, Xform b);
Quad MulXformQuad(Xform xf, Quad quad);
Vec2 MulXformBasisV2(Xform xf, Vec2 v);
Vec2 InvertXformMulV2(Xform xf, Vec2 v);
Vec2 InvertXformBasisMulV2(Xform xf, Vec2 v);
//- Helpers
Xform BasisFromXform(Xform xf);
f32 DeterminantFromXform(Xform xf);
Vec2 RightFromXform(Xform xf);
Vec2 LeftFromXform(Xform xf);
Vec2 UpFromXform(Xform xf);
Vec2 DownFromXform(Xform xf);
f32 RotationFromXform(Xform xf);
Vec2 ScaleFromXform(Xform xf);
//- Trs
#define TRS(...) ((Trs) { .t = VEC2(0,0), .s = VEC2(1, 1), .r = 0, __VA_ARGS__ })
////////////////////////////////
//~ Rect operations
#define RectFromScalar(_x, _y, _width, _height) (Rect) { .x = (_x), .y = (_y), .width = (_width), .height = (_height) }
#define RectFromVec2(_pos, _size) (Rect) { .pos = (_pos), .size = (_size) }
#define AllClipped ((ClipRect) { { 0.0f, 0.0f }, { 1.0f, 1.0f } })
////////////////////////////////
//~ Quad operations
#define UnitSquareQuad (Quad) { .p0 = VEC2(0, 0), .p1 = VEC2(0, 1), .p2 = VEC2(1, 1), .p3 = VEC2(1, 0) }
#define CenteredUnitSquareQuad (Quad) { .p0 = VEC2(-0.5f, -0.5f), .p1 = VEC2(0.5f, -0.5f), .p2 = VEC2(0.5f, 0.5f), .p3 = VEC2(-0.5f, 0.5f) }
Quad QuadFromRect(Rect rect);
Quad QuadFromAabb(Aabb aabb);
Quad QuadFromLine(Vec2 start, Vec2 end, f32 thickness);
Quad QuadFromRay(Vec2 pos, Vec2 rel, f32 thickness);
Quad ScaleQuad(Quad q, f32 s);
Quad RoundQuad(Quad quad);
Quad FloorQuad(Quad quad);
////////////////////////////////
//~ Spring operations
SoftSpring MakeSpring(f32 hertz, f32 damping_ratio, f32 dt);
////////////////////////////////
//~ Mat4x4 operations
Mat4x4 Mat4x4FromXform(Xform xf);
Mat4x4 Mat4x4FromOrtho(f32 left, f32 right, f32 bottom, f32 top, f32 near_z, f32 far_z);
Mat4x4 MulMat4x4(Mat4x4 m1, Mat4x4 m2);
Mat4x4 ProjectMat4x4View(Xform view, f32 viewport_width, f32 viewport_height);

92
src/meta/meta_memory.c Normal file
View File

@ -0,0 +1,92 @@
#if PlatformIsWindows
////////////////////////////////
//~ Windows headers
#define WIN32_LEAN_AND_MEAN
#define UNICODE
#include <Windows.h>
////////////////////////////////
//~ Memory allocation
//- Reserve
void *ReserveMemory(u64 size)
{
void *ptr = VirtualAlloc(0, size, MEM_RESERVE, PAGE_NOACCESS);
return ptr;
}
void ReleaseMemory(void *address)
{
VirtualFree(address, 0, MEM_RELEASE);
}
//- Commit
void *CommitMemory(void *address, u64 size)
{
void *ptr = VirtualAlloc(address, size, MEM_COMMIT, PAGE_READWRITE);
return ptr;
}
void DecommitMemory(void *address, u64 size)
{
VirtualFree(address, size, MEM_DECOMMIT);
}
//- Protect
void SetMemoryReadonly(void *address, u64 size)
{
DWORD old;
VirtualProtect(address, size, PAGE_READONLY, &old);
}
void SetMemoryReadWrite(void *address, u64 size)
{
DWORD old;
VirtualProtect(address, size, PAGE_READWRITE, &old);
}
#else
# error Memory allocation not implemented for this platform
#endif
////////////////////////////////
//~ Memory operations
void *CopyBytes(void *dst, void *src, u64 count)
{
char *dst_pchar = dst;
char *src_pchar = src;
for (u64 i = 0; i < count; ++i)
{
dst_pchar[i] = src_pchar[i];
}
return dst;
}
void *SetBytes(void *dst, u8 c, u64 count)
{
char *dst_pchar = dst;
for (u64 i = 0; i < count; ++i)
{
dst_pchar[i] = (char)c;
}
return dst;
}
i32 CmpBytes(void *p1, void *p2, u64 count)
{
i32 result = 0;
char *p1_pchar = p1;
char *p2_pchar = p2;
for (u64 i = 0; i < count; ++i)
{
result = p1_pchar[i] - p2_pchar[i];
if (result != 0)
{
break;
}
}
return result;
}

29
src/meta/meta_memory.h Normal file
View File

@ -0,0 +1,29 @@
////////////////////////////////
//~ Memory allocation
//- Reserve
void *ReserveMemory(u64 size);
void ReleaseMemory(void *address);
//- Commit
void *CommitMemory(void *address, u64 size);
void DecommitMemory(void *address, u64 size);
//- Protect
void SetMemoryReadonly(void *address, u64 size);
void SetMemoryReadWrite(void *address, u64 size);
////////////////////////////////
//~ Memory operations
//- Wrappers
#define ZeroStruct(ptr) ZeroBytes((ptr), sizeof(*(ptr)))
#define ZeroArray(a) Assert(IsArray(a)); ZeroBytes((a), sizeof((a)))
#define CopyStruct(ptr_dst, ptr_src) CopyBytes((ptr_dst), (ptr_src), sizeof(*(ptr_dst)))
#define EqStruct(p1, p2) EqBytes((p1), (p2), sizeof(*p1))
#define ZeroBytes(ptr, count) SetBytes((ptr), 0, (count))
#define EqBytes(p1, p2, n) (CmpBytes((p1), (p2), (n)) == 0)
void *CopyBytes(void *dst, void *src, u64 count);
void *SetBytes(void *dst, u8 c, u64 count);
i32 CmpBytes(void *p1, void *p2, u64 count);

4
src/meta/meta_os.h Normal file
View File

@ -0,0 +1,4 @@
////////////////////////////////
//~ @hookdeclStartup hooks
void StartupOs(void);

897
src/meta/meta_string.c Normal file
View File

@ -0,0 +1,897 @@
////////////////////////////////
//~ Conversion
//- Char conversion
String StringFromChar(Arena *arena, char c)
{
u8 *dst = PushStructNoZero(arena, u8);
*dst = c;
return (String)
{
.len = 1,
.text = dst
};
}
//- Unsigned int conversion
String StringFromU64(Arena *arena, u64 n, u64 base, u64 zfill)
{
/* Base too large */
Assert(base <= (countof(IntChars) - 1));
TempArena scratch = BeginScratch(arena);
/* Build backwards text starting from least significant digit */
u64 len = 0;
u8 *backwards_text = PushDry(scratch.arena, u8);
do
{
StringFromChar(scratch.arena, IntChars[n % base]);
++len;
n /= base;
} while (n > 0);
while (len < zfill)
{
StringFromChar(scratch.arena, '0');
++len;
}
/* Reverse text into final string */
u8 *final_text = PushStructsNoZero(arena, u8, len);
for (u64 i = 0; i < len; ++i)
{
final_text[i] = backwards_text[len - i - 1];
}
EndScratch(scratch);
return (String)
{
.len = len,
.text = final_text
};
}
//- Signed int conversion
String StringFromI64(Arena *arena, i64 n, u64 base, u64 zfill)
{
u8 *final_text = PushDry(arena, u8);
u8 len = 0;
if (n < 0)
{
/* Push sign */
StringFromChar(arena, '-');
len = 1;
n = -n;
}
/* Push unsigned number */
String uint_str = StringFromU64(arena, n, base, zfill);
return (String)
{
.len = len + uint_str.len,
.text = final_text
};
}
//- Pointer conversion
String StringFromPtr(Arena *arena, void *ptr)
{
String prepend = PushString(arena, Lit("0x"));
String uint_str = StringFromU64(arena, (u64)ptr, 16, sizeof(ptr));
return (String)
{
.len = prepend.len + uint_str.len,
.text = prepend.text
};
}
//- Floating point conversion
String StringFromF64(Arena *arena, f64 f, u32 precision)
{
TempArena scratch = BeginScratch(arena);
u8 *final_text = PushDry(arena, u8);
u64 final_len = 0;
if (IsF32Nan(f))
{
final_len += PushString(arena, Lit("NaN")).len;
}
else if (f == F64Infinity)
{
final_len += PushString(arena, Lit("inf")).len;
}
else if (f == -F64Infinity)
{
final_len += PushString(arena, Lit("-inf")).len;
}
else
{
if (f < 0)
{
StringFromChar(arena, '-');
f = -f;
++final_len;
}
/* Add one half of next precision level to round up */
f += 0.5 / (f64)PowU64(10, (u8)precision);
f64 part_whole = TruncF64(f);
f64 part_decimal = f - part_whole;
/* Print whole part */
{
/* Build backwards text starting from least significant digit */
u8 *backwards_text = PushDry(scratch.arena, u8);
u64 backwards_text_len = 0;
do
{
u64 digit = (u64)RoundF64ToI64(ModF64(part_whole, 10.0));
StringFromChar(scratch.arena, IntChars[digit % 10]);
++backwards_text_len;
part_whole = TruncF64(part_whole / 10.0);
} while (part_whole > 0);
/* Reverse text into final string */
PushStructsNoZero(arena, u8, backwards_text_len);
for (u64 i = backwards_text_len; i-- > 0;)
{
final_text[final_len++] = backwards_text[i];
}
}
/* Print decimal part */
if (precision > 0)
{
StringFromChar(arena, '.');
for (u64 i = 0; i < precision; ++i)
{
part_decimal *= 10.0;
u64 digit = (u64)part_decimal;
part_decimal -= digit;
StringFromChar(arena, IntChars[digit % 10]);
}
final_len += (u64)precision + 1;
}
}
EndScratch(scratch);
return (String)
{
.len = final_len,
.text = final_text
};
}
////////////////////////////////
//~ String operations
//- Copy
String PushString(Arena *arena, String src)
{
String str = {
.len = src.len,
.text = PushStructsNoZero(arena, u8, src.len)
};
CopyBytes(str.text, src.text, src.len);
return str;
}
String PushStringToBuff(String dst, String src)
{
String result = ZI;
result.len = MinU64(dst.len, src.len);
result.text = dst.text;
CopyBytes(result.text, src.text, result.len);
return result;
}
//- Repeat
String RepeatString(Arena *arena, String src, u64 count)
{
u64 final_len = src.len * count;
u8 *final_text = PushStructsNoZero(arena, u8, final_len);
for (u64 i = 0; i < count; ++i)
{
CopyBytes(final_text + (src.len * i), src.text, src.len);
}
return (String)
{
.text = final_text,
.len = final_len
};
}
//- Concatenate
String CatString(Arena *arena, String str1, String str2)
{
String new_str = ZI;
new_str.len = str1.len + str2.len;
new_str.text = PushStructsNoZero(arena, u8, new_str.len);
CopyBytes(new_str.text, str1.text, str1.len);
CopyBytes(new_str.text + str1.len, str2.text, str2.len);
return new_str;
}
//- Split
/* `arena` is where pieces will be allocated. These strings point
* into the existing string and do not allocate any new text. */
StringArray SplitString(Arena *arena, String str, String delim)
{
StringArray pieces = ZI;
pieces.strings = PushDry(arena, String);
i64 piece_start = 0;
for (i64 i = 0; i < (i64)str.len - (i64)delim.len; ++i)
{
String cmp = ZI;
cmp.text = &str.text[i];
cmp.len = MinI64(str.len - i, delim.len);
b32 is_delimiter = EqString(cmp, delim);
if (is_delimiter)
{
String piece = ZI;
piece.text = &str.text[piece_start];
piece.len = i - piece_start;
i += delim.len;
piece_start = i;
if (piece.len > 0)
{
*PushStructNoZero(arena, String) = piece;
++pieces.count;
}
}
}
if (piece_start < (i64)str.len)
{
String piece = ZI;
piece.text = &str.text[piece_start];
piece.len = str.len - piece_start;
*PushStructNoZero(arena, String) = piece;
++pieces.count;
}
return pieces;
}
//- Indent
/* NOTE: Really slow */
String IndentString(Arena *arena, String str, u32 indent)
{
TempArena scratch = BeginScratch(arena);
u64 final_len = 0;
u8 *final_text = PushDry(arena, u8);
StringArray split = SplitString(scratch.arena, str, Lit("\n"));
for (u64 i = 0; i < split.count; ++i)
{
String piece = split.strings[i];
for (u32 j = 0; j < indent; ++j)
{
StringFromChar(arena, ' ');
++final_len;
}
PushString(arena, piece);
final_len += piece.len;
if (i < split.count - 1)
{
StringFromChar(arena, '\n');
++final_len;
}
}
EndScratch(scratch);
return (String)
{
.len = final_len,
.text = final_text
};
}
//- Lower
String LowerString(Arena *arena, String str)
{
String result = ZI;
result.text = PushStructsNoZero(arena, u8, str.len);
result.len = str.len;
for (u64 i = 0; i < str.len; ++i)
{
u8 c = str.text[i];
if (65 <= c && c <= 90)
{
c += 32;
}
result.text[i] = c;
}
return result;
}
//- Compare
b32 EqString(String str1, String str2)
{
b32 eq = 1;
if (str1.len == str2.len)
{
for (u64 i = 0; i < str1.len; ++i)
{
if (str1.text[i] != str2.text[i])
{
eq = 0;
break;
}
}
}
else
{
eq = 0;
}
return eq;
}
i32 CmpString(String str1, String str2)
{
i32 result = 0;
for (u64 i = 0; i < MinU64(str1.len, str2.len); ++i)
{
result = str1.text[i] - str2.text[i];
if (result != 0)
{
break;
}
}
if (result == 0)
{
if (str1.len > str2.len)
{
result = str1.text[str2.len];
}
else if (str2.len > str1.len)
{
result = str2.text[str1.len];
}
}
return result;
}
//- Match
b32 StringContains(String str, String substring)
{
if (substring.len > str.len)
{
return 0;
}
for (u64 i = 0; i <= str.len - substring.len; ++i)
{
b32 match = 1;
for (u64 j = 0; j < substring.len; ++j)
{
if (str.text[i + j] != substring.text[j])
{
match = 0;
break;
}
}
if (match)
{
return 1;
}
}
return 0;
}
b32 StringStartsWith(String str, String substring)
{
if (str.len >= substring.len)
{
for (u64 i = 0; i < substring.len; ++i)
{
if (str.text[i] != substring.text[i])
{
return 0;
}
}
return 1;
}
return 0;
}
b32 StringEndsWith(String str, String substring)
{
if (str.len >= substring.len)
{
u64 start = str.len - substring.len;
for (u64 i = 0; i < substring.len; ++i)
{
if (str.text[start + i] != substring.text[i])
{
return 0;
}
}
return 1;
}
return 0;
}
////////////////////////////////
//~ String list operations
StringNode *PushStringNode(Arena *arena, StringList *l, String s)
{
StringNode *n = PushStruct(arena, StringNode);
n->s = s;
n->prev = l->last;
if (l->last)
{
l->last->next = n;
}
else
{
l->first = n;
}
l->last = n;
return n;
}
////////////////////////////////
//~ Formatting
/* String formatting only has one format specifier: "%F". All specifier info is
* included in the arguments (instead of w/ the specifier like in printf).
*
* Example:
* StringFormat(arena, Lit("Hello there %F"), FmtString(Lit("George")))
*
* NOTE: FmtEnd must be passed as the last arg in the va_list (This is
* done automatically by the `StringFormat` macro).
*
* Format arguments:
* FmtChar: Format a single u8 character
* FmtString: Format a `string` struct
* FmtUint: Format a u64
* FmtSint: Format an i64
* FmtFloat: Format an f64 with DefaultFmtPrecision
* FmtFloatP: Format an f64 with specified precision
* FmtHex: Format a u64 in hexadecimal notation
* FmtPtr: Format a pointer in hexadecimal notation prefixed by "0x"
*
* FmtEnd (internal): Denote the end of the va_list
*
* TODO:
* %n equivalent? (nothing)
* %e/%E equivalent? (scientific notation of floats)
* %o equivalent? (octal representation)
*/
String StringFormatV(Arena *arena, String fmt, va_list args)
{
__prof;
u64 final_len = 0;
u8 *final_text = PushDry(arena, u8);
u8 *end = fmt.text + fmt.len;
b32 no_more_args = 0;
for (u8 *c = fmt.text; c < end; ++c)
{
u8 *next = ((c + 1) < end) ? (c + 1) : (u8 *)"\0";
/* Escape '%%' */
b32 escape = !no_more_args && *c == '%' && *next == '%';
if (escape)
{
/* Skip the escape '%' char from parsing */
++c;
}
if (!no_more_args && !escape && *c == '%' && *next == 'F')
{
String parsed_str = ZI;
/* Detect arg type and parse to string */
FmtArg arg = va_arg(args, FmtArg);
switch (arg.kind)
{
case FmtKind_Char:
{
parsed_str = StringFromChar(arena, arg.value.c);
} break;
case FmtKind_String:
{
parsed_str = PushString(arena, arg.value.string);
} break;
case FmtKind_Uint:
{
parsed_str = StringFromU64(arena, arg.value.uint, 10, arg.zfill);
} break;
case FmtKind_Sint:
{
parsed_str = StringFromI64(arena, arg.value.sint, 10, arg.zfill);
} break;
case FmtKind_Hex:
{
parsed_str = StringFromU64(arena, arg.value.sint, 16, arg.zfill);
} break;
case FmtKind_Ptr:
{
parsed_str = StringFromPtr(arena, arg.value.ptr);
} break;
case FmtKind_Float:
{
parsed_str = StringFromF64(arena, arg.value.f, arg.precision);
} break;
case FmtKind_End:
{
/* Unexpected end. Not enough FMT args passed to function. */
Assert(0);
parsed_str = PushString(arena, Lit("<?>"));
no_more_args = 1;
} break;
default:
{
/* Unknown format type */
Assert(0);
parsed_str = PushString(arena, Lit("<?>"));
no_more_args = 1;
} break;
}
/* Update final string len / start */
final_len += parsed_str.len;
/* Skip 'F' from parsing */
++c;
}
else
{
/* Parse character normally */
StringFromChar(arena, *c);
++final_len;
}
}
#if RtcIsEnabled
if (!no_more_args)
{
FmtArg last_arg = va_arg(args, FmtArg);
/* End arg not reached. Too many FMT values passed to function. */
Assert(last_arg.kind == FmtKind_End);
}
#endif
return (String)
{
.len = final_len,
.text = final_text
};
}
String _StringFormat(Arena *arena, String fmt, ...)
{
va_list args;
va_start(args, fmt);
String new_str = StringFormatV(arena, fmt, args);
va_end(args);
return new_str;
}
////////////////////////////////
//~ Unicode
//- Codepoint iteration
CodepointIter BeginCodepointIter(String str)
{
return (CodepointIter)
{
.src = str
};
}
/* Returns 0 if done iterating */
b32 AdvanceCodepointIter(CodepointIter *iter)
{
if (iter->pos < iter->src.len)
{
String str_remaining = { .len = (iter->src.len - iter->pos), .text = iter->src.text + iter->pos };
Utf8DecodeResult decoded = DecodeUtf8(str_remaining);
iter->pos += decoded.advance8;
iter->codepoint = decoded.codepoint;
return 1;
}
else
{
return 0;
}
}
void EndCodepointIter(CodepointIter *iter)
{
/* Do nothing */
LAX iter;
}
//- String decode
/* utf8 <- utf16 */
String StringFromString16(Arena *arena, String16 str16)
{
String result = {
.len = 0,
.text = PushDry(arena, u8)
};
u64 pos16 = 0;
while (pos16 < str16.len)
{
String16 str16_remaining = { .len = (str16.len - pos16), .text = str16.text + pos16 };
Utf16DecodeResult decoded = DecodeUtf16(str16_remaining);
Utf8EncodeResult encoded = EncodeUtf8(decoded.codepoint);
u8 *dst = PushStructsNoZero(arena, u8, encoded.count8);
CopyBytes(dst, encoded.chars8, encoded.count8);
pos16 += decoded.advance16;
result.len += encoded.count8;
}
return result;
}
/* utf8 <- utf32 */
String StringFromString32(Arena *arena, String32 str32)
{
String result = {
.len = 0,
.text = PushDry(arena, u8)
};
u64 pos32 = 0;
while (pos32 < str32.len)
{
String32 str32_remaining = { .len = (str32.len - pos32), .text = str32.text + pos32 };
Utf32DecodeResult decoded = DecodeUtf32(str32_remaining);
Utf8EncodeResult encoded = EncodeUtf8(decoded.codepoint);
u8 *dst = PushStructsNoZero(arena, u8, encoded.count8);
CopyBytes(dst, &encoded.chars8, encoded.count8);
pos32 += 1;
result.len += encoded.count8;
}
return result;
}
//- String encode
/* utf16 <- utf8 */
String16 String16FromString(Arena *arena, String str8)
{
String16 result = {
.len = 0,
.text = PushDry(arena, u16)
};
u64 pos8 = 0;
while (pos8 < str8.len)
{
String str8_remaining = { .len = (str8.len - pos8), .text = str8.text + pos8 };
Utf8DecodeResult decoded = DecodeUtf8(str8_remaining);
Utf16EncodeResult encoded = EncodeUtf16(decoded.codepoint);
u16 *dst = PushStructsNoZero(arena, u16, encoded.count16);
CopyBytes(dst, encoded.chars16, (encoded.count16 << 1));
pos8 += decoded.advance8;
result.len += encoded.count16;
}
return result;
}
/* utf32 <- utf8 */
String32 String32FromString(Arena *arena, String str8)
{
String32 result = {
.len = 0,
.text = PushDry(arena, u32)
};
u64 pos8 = 0;
while (pos8 < str8.len)
{
String str8_remaining = { .len = (str8.len - pos8), .text = str8.text + pos8 };
Utf8DecodeResult decoded = DecodeUtf8(str8_remaining);
Utf32EncodeResult encoded = EncodeUtf32(decoded.codepoint);
u32 *dst = PushStructNoZero(arena, u32);
*dst = encoded.chars32;
pos8 += decoded.advance8;
result.len += 1;
}
return result;
}
////////////////////////////////
//~ Legacy null-terminated C string operations
//- Narrow C strings
u64 CstrLenNoLimit(char *cstr)
{
char *end = cstr;
if (cstr)
{
while (*end)
{
++end;
}
}
return end - cstr;
}
u64 CstrLen(char *cstr, u64 limit)
{
char *end = cstr;
if (cstr)
{
for (u64 i = 0; i < limit; ++i)
{
if (*end)
{
++end;
}
else
{
break;
}
}
}
return end - cstr;
}
char *CstrFromString(Arena *arena, String src)
{
u8 *text = PushStructsNoZero(arena, u8, src.len + 1);
CopyBytes(text, src.text, src.len);
text[src.len] = 0;
return (char *)text;
}
char *CstrFromStringToBuff(String dst_buff, String src)
{
if (dst_buff.len > 0)
{
u64 len = MinU64(src.len, dst_buff.len - 1);
CopyBytes(dst_buff.text, src.text, len);
dst_buff.text[len] = 0;
}
return (char *)dst_buff.text;
}
String StringFromCstrNoLimit(char *cstr)
{
u64 len = CstrLenNoLimit(cstr);
return (String)
{
.len = len,
.text = (u8 *)cstr
};
}
String StringFromCstr(char *cstr, u64 limit)
{
u64 len = CstrLen(cstr, limit);
return (String)
{
.text = (u8 *)cstr,
.len = len
};
}
//- Wide C strings
u64 WstrLenNoLimit(wchar_t *wstr)
{
wchar_t *end = wstr;
if (end)
{
while (*end)
{
++end;
}
}
return end - wstr;
}
u64 WstrLen(wchar_t *wstr, u64 limit)
{
wchar_t *end = wstr;
if (wstr)
{
for (u64 i = 0; i < limit; ++i)
{
if (*end)
{
++end;
}
else
{
break;
}
}
}
return end - wstr;
}
wchar_t *WstrFromString(Arena *arena, String src)
{
String16 str16 = String16FromString(arena, src);
*PushStructNoZero(arena, u16) = 0;
return (wchar_t *)str16.text;
}
wchar_t *WstrFromString16(Arena *arena, String16 src)
{
u16 *text = PushStructsNoZero(arena, u16, src.len + 1);
text[src.len] = 0;
return (wchar_t *)text;
}
String StringFromWstrNoLimit(Arena *arena, wchar_t *wstr)
{
String16 str16 = String16FromWstrNoLimit(wstr);
return StringFromString16(arena, str16);
}
String StringFromWstr(Arena *arena, wchar_t *wstr, u64 limit)
{
String16 str16 = String16FromWstr(wstr, limit);
return StringFromString16(arena, str16);
}
String16 String16FromWstrNoLimit(wchar_t *wstr)
{
u64 len = WstrLenNoLimit(wstr);
return (String16)
{
.len = len,
.text = (u16 *)wstr
};
}
String16 String16FromWstr(wchar_t *wstr, u64 limit)
{
u64 len = WstrLen(wstr, limit);
return (String16)
{
.len = len,
.text = (u16 *)wstr
};
}

172
src/meta/meta_string.h Normal file
View File

@ -0,0 +1,172 @@
////////////////////////////////
//~ String types
Struct(StringArray)
{
u64 count;
String *strings;
};
Struct(StringNode)
{
String s;
StringNode *next;
StringNode *prev;
};
Struct(StringList)
{
StringNode *first;
StringNode *last;
};
////////////////////////////////
//~ Formatting types
#define DefaultFmtPrecision 3
#define IntChars ("0123456789abcdef")
typedef i32 FmtKind; enum
{
FmtKind_None,
/* Arbitrary magic numbers for argument validation */
FmtKind_Char = 0x0f5281df,
FmtKind_String = 0x0a5ffa9a,
FmtKind_Uint = 0x0746f19b,
FmtKind_Sint = 0x08603694,
FmtKind_Hex = 0x0a3d0792,
FmtKind_Ptr = 0x0c4519e4,
FmtKind_Float = 0x04814143,
FmtKind_End = 0x0ecbc5ae
};
Struct(FmtArg)
{
FmtKind kind;
u32 precision;
u32 zfill;
union
{
u8 c;
String string;
u64 uint;
i64 sint;
void *ptr;
f64 f;
} value;
};
////////////////////////////////
//~ Unicode types
Struct(CodepointIter)
{
u32 codepoint;
/* Internal */
String src;
u64 pos;
};
////////////////////////////////
//~ String utils
#define STRING(size, data) (CppCompatInitListType(String) { (size), (data) })
#define Lit(cstr_lit) CppCompatInitListType(String) { (sizeof((cstr_lit)) - 1), (u8 *)(cstr_lit) }
#define LitNoCast(cstr_lit) { .len = (sizeof((cstr_lit)) - 1), .text = (u8 *)(cstr_lit) }
#define StringFromPointers(p0, p1) (CppCompatInitListType(String) { (u8 *)(p1) - (u8 *)(p0), (u8 *)p0 })
#define StringFromStruct(ptr) (CppCompatInitListType(String) { sizeof(*(ptr)), (u8 *)(ptr) })
#define StringFromArena(arena) (STRING((arena)->pos, ArenaBase(arena)))
/* String from static array */
#define StringFromArray(a) \
( \
Assert(IsArray(a)), \
((String) { .len = sizeof(a), .text = (u8 *)(a) }) \
)
////////////////////////////////
//~ Conversion operations
String StringFromChar(Arena *arena, char c);
String StringFromU64(Arena *arena, u64 n, u64 base, u64 zfill);
String StringFromI64(Arena *arena, i64 n, u64 base, u64 zfill);
String StringFromPtr(Arena *arena, void *ptr);
String StringFromF64(Arena *arena, f64 f, u32 precision);
////////////////////////////////
//~ String operation
String PushString(Arena *arena, String src);
String PushStringToBuff(String dst, String src);
String RepeatString(Arena *arena, String src, u64 count);
String CatString(Arena *arena, String str1, String str2);
StringArray SplitString(Arena *arena, String str, String delim);
String IndentString(Arena *arena, String str, u32 indent);
String LowerString(Arena *arena, String str);
b32 EqString(String str1, String str2);
i32 CmpString(String str1, String str2);
b32 StringContains(String str, String substring);
b32 StringStartsWith(String str, String substring);
b32 StringEndsWith(String str, String substring);
////////////////////////////////
//~ Formatting
//- Format arg helpers
#define FmtChar(v) (FmtArg) {.kind = FmtKind_Char, .value.c = (v)}
#define FmtString(v) (FmtArg) {.kind = FmtKind_String, .value.string = (v)}
#define FmtUint(v) (FmtArg) {.kind = FmtKind_Uint, .value.uint = (v)}
#define FmtUintZ(v, z) (FmtArg) {.kind = FmtKind_Uint, .value.uint = (v), .zfill = (z)}
#define FmtSint(v) (FmtArg) {.kind = FmtKind_Sint, .value.sint = (v)}
#define FmtHex(v) (FmtArg) {.kind = FmtKind_Hex, .value.uint = (v)}
#define FmtPtr(v) (FmtArg) {.kind = FmtKind_Ptr, .value.ptr = (v)}
#define FmtFloat(v) FmtFloatP(v, DefaultFmtPrecision)
#define FmtFloatP(v, p) (FmtArg) {.kind = FmtKind_Float, .value.f = (v), .precision = (p)}
#define FmtHandle(v) (FmtArg) {.kind = FmtKind_Handle, .value.handle.h64[0] = (v).idx, .value.handle.h64[1] = (v).gen}
#define FmtUid(v) (FmtArg) {.kind = FmtKind_Uid, .value.uid = (v) }
#define FmtEnd (FmtArg) {.kind = FmtKind_End}
//- Format functions
#define StringFormat(arena, fmt, ...) _StringFormat((arena), (fmt), __VA_ARGS__, FmtEnd)
String _StringFormat(Arena *arena, String fmt, ...);
String StringFormatV(Arena *arena, String fmt, va_list args);
////////////////////////////////
//~ Unicode operations
//- Iter
CodepointIter BeginCodepointIter(String str);
b32 AdvanceCodepointIter(CodepointIter *iter);
void EndCodepointIter(CodepointIter *iter);
//- Decode string
String StringFromString16(Arena *arena, String16 str16);
String StringFromString32(Arena *arena, String32 str32);
//- Encode string
String16 String16FromString(Arena *arena, String str8);
String32 String32FromString(Arena *arena, String str8);
////////////////////////////////
//~ Legacy null-terminated C string operations
//- Narrow strings
u64 CstrLenNoLimit(char *cstr);
u64 CstrLen(char *cstr, u64 limit);
char *CstrFromString(Arena *arena, String src);
char *CstrFromStringToBuff(String dest_buff, String src);
String StringFromCstrNoLimit(char *cstr);
String StringFromCstr(char *cstr, u64 limit);
//- Wide strings
u64 WstrLenNoLimit(wchar_t *wstr);
u64 WstrLen(wchar_t *wstr, u64 limit);
wchar_t *WstrFromString(Arena *arena, String src);
wchar_t *WstrFromString16(Arena *arena, String16 src);
String StringFromWstrNoLimit(Arena *arena, wchar_t *wstr);
String StringFromWstr(Arena *arena, wchar_t *wstr, u64 limit);
String16 String16FromWstrNoLimit(wchar_t *wstr);
String16 String16FromWstr(wchar_t *wstr, u64 limit);

245
src/meta/meta_uni.c Normal file
View File

@ -0,0 +1,245 @@
////////////////////////////////
//~ Utf8
//- Decode
Utf8DecodeResult DecodeUtf8(String str)
{
LocalPersist const u8 lengths[32] = {
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,2,2,2,2,3,3,4,5
};
Utf8DecodeResult result = ZI;
u32 codepoint = U32Max;
u32 advance = 0;
if (str.len > 0)
{
u8 c0 = str.text[0];
u8 utf8_len = lengths[c0 >> 3];
advance = 1;
switch (utf8_len)
{
case 1:
{
codepoint = c0;
} break;
case 2:
{
if (str.len >= 2)
{
u8 c1 = str.text[1];
if (lengths[c1 >> 3] == 0)
{
codepoint = (c1 & 0x3F) << 0;
codepoint |= (c0 & 0x1F) << 6;
advance = 2;
}
}
} break;
case 3:
{
if (str.len >= 3)
{
u8 c1 = str.text[1];
u8 c2 = str.text[2];
if (lengths[c1 >> 3] == 0 &&
lengths[c2 >> 3] == 0)
{
codepoint = (c2 & 0x3F) << 0;
codepoint |= (c1 & 0x3F) << 6;
codepoint |= (c0 & 0x0F) << 12;
advance = 3;
}
}
} break;
case 4:
{
if (str.len >= 4)
{
u8 c1 = str.text[1];
u8 c2 = str.text[2];
u8 c3 = str.text[3];
if (lengths[c1 >> 3] == 0 &&
lengths[c2 >> 3] == 0 &&
lengths[c3 >> 3] == 0)
{
codepoint = (c3 & 0x3F) << 0;
codepoint |= (c2 & 0x3F) << 6;
codepoint |= (c1 & 0x3F) << 12;
codepoint |= (c0 & 0x07) << 16;
advance = 3;
}
}
} break;
default: break;
}
}
result.advance8 = advance;
result.codepoint = codepoint;
return result;
}
//- Encode
Utf8EncodeResult EncodeUtf8(u32 codepoint)
{
Utf8EncodeResult result = ZI;
if (codepoint <= 0x7F)
{
result.count8 = 1;
result.chars8[0] = codepoint;
}
else if (codepoint <= 0x7FF)
{
result.count8 = 2;
result.chars8[1] = 0x80 | ((codepoint >> 0) & 0x3F);
result.chars8[0] = 0xC0 | ((codepoint >> 6) & 0x1F);
}
else if (codepoint <= 0xFFFF)
{
result.count8 = 3;
result.chars8[2] = 0x80 | ((codepoint >> 0) & 0x3F);
result.chars8[1] = 0x80 | ((codepoint >> 6) & 0x3F);
result.chars8[0] = 0xE0 | ((codepoint >> 12) & 0x0F);
}
else if (codepoint <= 0x10FFFF)
{
result.count8 = 4;
result.chars8[3] = 0x80 | ((codepoint >> 0) & 0x3F);
result.chars8[2] = 0x80 | ((codepoint >> 6) & 0x3F);
result.chars8[1] = 0x80 | ((codepoint >> 12) & 0x3F);
result.chars8[0] = 0xF0 | ((codepoint >> 18) & 0x07);
}
else
{
/* Invalid codepoint */
result.count8 = 1;
result.chars8[0] = '?';
}
return result;
}
////////////////////////////////
//~ Utf16
//- Decode
Utf16DecodeResult DecodeUtf16(String16 str)
{
Utf16DecodeResult result = ZI;
u32 codepoint = U32Max;
u32 advance = 0;
if (str.len >= 1)
{
u16 c0 = str.text[0];
codepoint = c0;
advance = 1;
if (str.len >= 2)
{
u16 c1 = str.text[1];
if ((0xD800 <= c0 && c0 < 0xDC00) && (0xDC00 <= c1 && c1 < 0xE000))
{
codepoint = (c1 & 0x3FF) << 0;
codepoint |= (c0 & 0x3FF) << 10;
advance = 2;
}
}
}
result.advance16 = advance;
result.codepoint = codepoint;
return result;
}
//- Encode
Utf16EncodeResult EncodeUtf16(u32 codepoint)
{
Utf16EncodeResult result = ZI;
if (codepoint <= 0xFFFF)
{
result.count16 = 1;
result.chars16[0] = codepoint;
}
else if (codepoint <= 0x10FFFF)
{
result.count16 = 2;
result.chars16[1] = 0xDC00 | ((codepoint >> 0) & 0x3FF);
result.chars16[0] = 0xD800 | ((codepoint >> 10) & 0x3FF);
}
else
{
/* Invalid codepoint */
result.count16 = 1;
result.chars16[0] = '?';
}
return result;
}
//- Surrogate check
b32 IsUtf16HighSurrogate(u16 c)
{
return 0xD800 <= c && c < 0xDC00;
}
b32 IsUtf16LowSurrogate(u16 c)
{
return 0xDC00 <= c && c < 0xE000;
}
////////////////////////////////
//~ Utf32
//- Decode
Utf32DecodeResult DecodeUtf32(String32 str)
{
Utf32DecodeResult result = ZI;
u32 codepoint = U32Max;
u32 advance = 0;
if (str.len >= 1)
{
u32 c = str.text[0];
advance = 1;
if (c <= 0x10FFFF)
{
codepoint = c;
}
}
result.advance32 = advance;
result.codepoint = codepoint;
return result;
}
//- Encode
Utf32EncodeResult EncodeUtf32(u32 codepoint)
{
Utf32EncodeResult result = ZI;
if (codepoint <= 0x10FFFF)
{
result.chars32 = codepoint;
}
else
{
/* Invalid codepoint */
result.chars32 = '?';
}
return result;
}

64
src/meta/meta_uni.h Normal file
View File

@ -0,0 +1,64 @@
////////////////////////////////
//~ Utf8 types
Struct(Utf8DecodeResult)
{
u32 advance8;
u32 codepoint;
};
Struct(Utf8EncodeResult)
{
u32 count8;
u8 chars8[4];
};
////////////////////////////////
//~ Utf16 types
Struct(Utf16DecodeResult)
{
u32 advance16;
u32 codepoint;
};
Struct(Utf16EncodeResult)
{
u32 count16;
u16 chars16[2];
};
////////////////////////////////
//~ Utf32 types
Struct(Utf32DecodeResult)
{
u32 advance32;
u32 codepoint;
};
Struct(Utf32EncodeResult)
{
u32 chars32;
};
////////////////////////////////
//~ Utf8 operations
Utf8DecodeResult DecodeUtf8(String str);
Utf8EncodeResult EncodeUtf8(u32 codepoint);
////////////////////////////////
//~ Utf16 operations
Utf16DecodeResult DecodeUtf16(String16 str);
Utf16EncodeResult EncodeUtf16(u32 codepoint);
b32 IsUtf16HighSurrogate(u16 c);
b32 IsUtf16LowSurrogate(u16 c);
////////////////////////////////
//~ Utf32 operations
Utf32DecodeResult DecodeUtf32(String32 str);
Utf32EncodeResult EncodeUtf32(u32 codepoint);

View File

@ -0,0 +1,68 @@
W32_SharedState W32_shared_state = ZI;
////////////////////////////////
//~ Windows libs
#pragma comment(lib, "kernel32")
#pragma comment(lib, "user32")
////////////////////////////////
//~ @hookdef OS startup hook
void StartupOs(void)
{
W32_SharedState *g = &W32_shared_state;
g->tls_index = TlsAlloc();
TlsSetValue(g->tls_index, 0);
}
////////////////////////////////
//~ @hookdef Core Panic hooks
/* TODO: Remove stdio & printf */
#include <stdio.h>
void Panic(String msg)
{
char msg_cstr[4096];
CstrFromStringToBuff(StringFromArray(msg_cstr), msg);
ShowMessageBoxCstr("Fatal error", msg_cstr, 1);
printf(msg_cstr);
fflush(stdout);
ExitProcess(1);
}
////////////////////////////////
//~ @hookdef Core Debugger hooks
b32 IsRunningInDebugger(void)
{
return IsDebuggerPresent();
}
////////////////////////////////
//~ @hookdef Core Thread hooks
i16 ThreadId(void)
{
W32_SharedState *g = &W32_shared_state;
return (i16)(i64)TlsGetValue(g->tls_index);
}
////////////////////////////////
//~ @hookdef OS Message box hooks
void ShowMessageBox(String title, String msg, b32 error)
{
char title_cstr[256];
char msg_cstr[4096];
CstrFromStringToBuff(StringFromArray(title_cstr), title);
CstrFromStringToBuff(StringFromArray(msg_cstr), msg);
ShowMessageBoxCstr(title_cstr, msg_cstr, error);
}
void ShowMessageBoxCstr(char *title_cstr, char *msg_cstr, b32 error)
{
u32 mb_flags = MB_SETFOREGROUND | (!!error * MB_ICONERROR) | (!error * MB_ICONINFORMATION);
MessageBoxExA(0, msg_cstr, title_cstr, mb_flags, 0);
}

View File

@ -0,0 +1,24 @@
////////////////////////////////
//~ Windows headers
#pragma warning(push, 0)
# define UNICODE
# define WIN32_LEAN_AND_MEAN
# include <Windows.h>
#pragma warning(pop)
////////////////////////////////
//~ Shared state
Struct(W32_SharedState)
{
DWORD tls_index;
};
extern W32_SharedState W32_shared_state;
////////////////////////////////
//~ @hookdecl Message box
void ShowMessageBox(String title, String msg, b32 error);
void ShowMessageBoxCstr(char *title_cstr, char *msg_cstr, b32 error);