adjusting repo for removing extra features not part of the original library

This commit is contained in:
ed
2025-02-12 13:57:45 -05:00
parent ea67e889e0
commit d569b93bbd
59 changed files with 4 additions and 202 deletions
+3
View File
@@ -0,0 +1,3 @@
# Src
Currently updating this so that the library is kept minimally changed, feature additions will be handled in a separate repo.
+215
View File
@@ -0,0 +1,215 @@
#ifdef INTELLISENSE_DIRECTIVES
# pragma once
# include "arena.h"
#endif
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
////////////////////////////////
//~ rjf: Arena Functions
//- rjf: arena creation/destruction
Arena*
arena__alloc(ArenaParams* optional_params)
{
ArenaParams params = optional_params ? *optional_params : (ArenaParams){0};
SPTR const varena_header_size = align_pow2(size_of(VArena), MD_DEFAULT_MEMORY_ALIGNMENT);
SPTR const header_size = align_pow2(size_of(Arena), MD_DEFAULT_MEMORY_ALIGNMENT);
U64 const varena_reserve_size = VARENA_DEFAULT_RESERVE;
B32 is_virtual = allocator_type(params.backing) & AllocatorType_VArena;
params.flags |= ArenaFlag_Virtual * is_virtual;
if (params.backing.proc == nullptr) params.backing = default_allocator();
if (params.block_size == 0 ) params.block_size = ARENA_DEFAULT_BLOCK_SIZE;
SSIZE alloc_size = is_virtual ? header_size : params.block_size;
void* base = alloc(params.backing, alloc_size);
// rjf: extract arena header & fill
Arena* arena = (Arena*) base;
arena->prev = nullptr;
arena->current = arena;
arena->backing = params.backing;
arena->base_pos = 0;
arena->pos = header_size;
arena->block_size = params.block_size;
arena->flags = params.flags;
asan_unpoison_memory_region(base, sizeof(Arena));
return arena;
}
//- rjf: arena push/pop core functions
void*
arena_push(Arena* arena, SSIZE size, SSIZE align)
{
SPTR const header_size = align_pow2(size_of(Arena), MD_DEFAULT_MEMORY_ALIGNMENT);
Arena* current = arena->current;
SPTR curr_sptr = scast(SPTR, current);
SSIZE aligned_size = align_pow2(size, align);
SPTR pos_pre = current->pos;
SPTR pos_pst = pos_pre + aligned_size;
B32 is_virtual = arena->flags & ArenaFlag_Virtual;
// rjf: chain, if needed
if ( current->block_size < pos_pst && ! (arena->flags & ArenaFlag_NoChain) )
{
Arena* new_block = nullptr;
B32 vmem_chain = is_virtual && (arena->flags & ArenaFlag_NoChainVirtual);
if (vmem_chain) {
SPTR const varena_header_size = align_pow2(size_of(VArena), MD_DEFAULT_MEMORY_ALIGNMENT);
SPTR const arena_block_size = VARENA_DEFAULT_RESERVE - varena_header_size;
VArena* vcurrent = rcast(VArena*, arena->backing.data);
VArena* new_vm = varena_alloc(.reserve_size = vcurrent->reserve, .commit_size = vcurrent->commit_size);
new_block = arena_alloc(.backing = varena_allocator(new_vm), .block_size = arena_block_size);
}
else {
SPTR const arena_block_size = arena->block_size + header_size;
new_block = arena_alloc(.backing = arena->backing, .block_size = arena_block_size);
}
new_block->base_pos = current->base_pos + current->block_size;
sll_stack_push_n(arena->current, new_block, prev);
current = new_block;
pos_pre = current->pos;
pos_pst = pos_pre + aligned_size;
}
// rjf: push onto current block
void* result = 0;
{
result = scast(void*, curr_sptr + pos_pre);
current->pos = pos_pst;
asan_unpoison_memory_region(result, size);
}
if (is_virtual) {
// Sync virtual arena
void* vresult = alloc_align(arena->backing, size, align);
assert(vresult == result);
}
// rjf: panic on failure
#if OS_FEATURE_GRAPHICAL
if(unlikely(result == 0))
{
os_graphical_message(1, str8_lit("Fatal Allocation Failure"), str8_lit("Unexpected memory allocation failure."));
os_abort(1);
}
#endif
return result;
}
void
arena_pop_to(Arena *arena, SSIZE pos)
{
SPTR const header_size = align_pow2(size_of(Arena), MD_DEFAULT_MEMORY_ALIGNMENT);
Arena* current = arena->current;
AllocatorInfo backing = current->backing;
B32 is_virtual = allocator_type(backing) & AllocatorType_VArena;
SSIZE big_pos = clamp_bot(header_size, pos);
// If base position is larger than the position to pop to:
// We are in a previous arena and msut free the current
for(Arena* prev = 0; current->base_pos >= big_pos; current = prev)
{
prev = current->prev;
if (is_virtual) {
varena_release(rcast(VArena*, current->backing.data));
}
else if (allocator_query_support(backing) & AllocatorQuery_Free) {
alloc_free(current->backing, current);
}
}
arena->current = current;
SSIZE new_pos = big_pos - current->base_pos;
assert_always(new_pos <= current->pos);
asan_poison_memory_region((U8*)current + new_pos, (current->pos - new_pos));
current->pos = new_pos;
if (is_virtual) {
varena_rewind(rcast(VArena*, current->backing.data), current->pos);
}
}
void* arena_allocator_proc(void* allocator_data, AllocatorMode mode, SSIZE size, SSIZE alignment, void* old_memory, SSIZE old_size, U64 flags)
{
Arena* arena = rcast(Arena*, allocator_data);
void* allocated_ptr = nullptr;
switch (mode)
{
case AllocatorMode_Alloc:
{
allocated_ptr = arena_push(arena, size, alignment);
}
break;
case AllocatorMode_Free:
{
}
break;
case AllocatorMode_FreeAll:
{
arena_release(arena);
}
break;
case AllocatorMode_Resize:
{
assert(old_memory != nullptr);
assert(old_size > 0);
assert_msg(old_size == size, "Requested resize when none needed");
size = align_pow2(size, alignment);
old_size = align_pow2(size, alignment);
SPTR old_memory_offset = scast(SPTR, old_memory) + old_size;
SPTR current_offset = arena->pos;
assert_msg(old_memory_offset == current_offset, "Cannot resize existing allocation in VArena unless it was the last allocated");
B32 requested_shrink = size >= old_size;
if (requested_shrink) {
arena->pos -= size;
allocated_ptr = old_memory;
break;
}
allocated_ptr = old_memory;
arena->pos += size;
}
break;
case AllocatorMode_QueryType:
{
return (void*) AllocatorType_Arena;
}
break;
case AllocatorMode_QuerySupport:
{
return (void*) (AllocatorQuery_Alloc | AllocatorQuery_Resize | AllocatorQuery_FreeAll);
}
break;
}
return allocated_ptr;
}
+175
View File
@@ -0,0 +1,175 @@
#ifdef INTELLISENSE_DIRECTIVES
# pragma once
# include "macros.h"
# include "base_types.h"
# include "memory_substrate.h"
#endif
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
////////////////////////////////
//~ rjf: Types
typedef U32 ArenaFlags;
enum
{
// Don't chain this arena
ArenaFlag_NoChain = (1 << 0),
// Only relevant if backing is virtual memory, will prevent allocating a new backing VArena when the current block exhausts
// Will assume backing can chain multiple block_size arenas however. If there is an allocation failure it will assert.
ArenaFlag_NoChainVirtual = (1 << 1),
// Backing allocator identified as VArena during initialization
ArenaFlag_Virtual = (1 << 2),
};
typedef struct ArenaParams ArenaParams;
struct ArenaParams
{
AllocatorInfo backing;
ArenaFlags flags;
U64 block_size; // If chaining VArenas set this to the reserve size
};
#define ARENA_DEFAULT_BLOCK_SIZE VARENA_DEFAULT_RESERVE - align_pow2(size_of(VArena), MD_DEFAULT_MEMORY_ALIGNMENT)
/* NOTE(Ed): The original metadesk arena is a combination of several concepts into a single interface
* An OS virtual memory allocation scheme
* A arena 'block' of memory with segmented chaining of the blocks
* A push/pop stack allocation interface for the arena
The virtual memory has been abstracted into a backing allocator,
and chaining still supports reserving new virtual address regions .
(can be disabled with ArenaFlag_NoChainVirtual)
If large pages are desired, see VArena.
*/
typedef struct Arena Arena;
struct Arena
{
Arena* prev; // Previous arena in chain
Arena* current; // Current arena in chain
AllocatorInfo backing;
SSIZE base_pos; // Tracks how main arenas have been chained
SSIZE pos;
SSIZE block_size;
ArenaFlags flags;
};
// static_assert(size_of(Arena) <= ARENA_HEADER_SIZE, "sizeof(Arena) <= ARENA_HEADER_SIZE");
typedef struct TempArena TempArena;
struct TempArena
{
Arena* arena;
SSIZE pos;
};
////////////////////////////////
//~ rjf: Arena Functions
MD_API void* arena_allocator_proc(void* allocator_data, AllocatorMode mode, SSIZE size, SSIZE alignment, void* old_memory, SSIZE old_size, U64 flags);
force_inline AllocatorInfo arena_allocator(Arena* arena) { AllocatorInfo info = { arena_allocator_proc, arena}; return info; }
// Useful for providing an arena to scratch_begin_alloc
force_inline Arena*
extract_arena(AllocatorInfo ainfo) {
if (allocator_type(ainfo) == AllocatorType_Arena) {
return (Arena*) ainfo.data;
}
return nullptr;
}
//- rjf: arena creation/destruction
MD_API Arena* arena__alloc(ArenaParams* params);
#define arena_alloc(...) arena__alloc( &(ArenaParams){ __VA_ARGS__ } )
void arena_release(Arena *arena);
//- rjf: arena push/pop/pos core functions
MD_API void* arena_push (Arena* arena, SSIZE size, SSIZE align);
U64 arena_pos (Arena* arena);
MD_API void arena_pop_to(Arena* arena, SSIZE pos);
//- rjf: arena push/pop helpers
void arena_clear(Arena* arena);
void arena_pop (Arena* arena, SSIZE amt);
//- rjf: temporary arena scopes
TempArena temp_arena_begin(Arena* arena);
void temp_arena_end(TempArena temp);
//- rjf: push helper macros
#ifndef push_array
#define push_array_no_zero_aligned(a, T, c, align) (T *)arena_push((a), sizeof(T) * (c), (align))
#define push_array_aligned(a, T, c, align) (T *)memory_zero(push_array_no_zero_aligned(a, T, c, align), sizeof(T) * (c))
#define push_array_no_zero(a, T, c) push_array_no_zero_aligned(a, T, c, max(8, align_of(T)))
#define push_array(a, T, c) push_array_aligned (a, T, c, max(8, align_of(T)))
#endif
// Inlines
inline void
arena_release(Arena* arena) {
for (Arena* n = arena->current, *prev = 0; n != 0; n = prev) {
prev = n->prev;
alloc_free(arena->backing, n);
}
}
inline U64
arena_pos(Arena *arena) {
Arena* current = arena->current;
U64 pos = current->base_pos + current->pos;
return pos;
}
//- rjf: arena push/pop helpers
force_inline void arena_clear(Arena* arena) { arena_pop_to(arena, 0); }
inline void
arena_pop(Arena* arena, SSIZE amt) {
SSIZE pos_old = arena_pos(arena);
SSIZE pos_new = pos_old;
if (amt < pos_old)
{
pos_new = pos_old - amt;
}
arena_pop_to(arena, pos_new);
}
//- rjf: temporary arena scopes
inline TempArena
temp_begin(Arena* arena) {
U64 pos = arena_pos(arena);
TempArena temp = {arena, pos};
return temp;
}
force_inline void temp_end(TempArena temp) { arena_pop_to(temp.arena, temp.pos); }
// ======================================== DEFAULT_ALLOCATOR =====================================================
#ifndef MD_OVERRIDE_DEFAULT_ALLOCATOR
// The default allocator for this base module is the Arena allocator with a VArena backing
// NOTE(Ed): In order for this to work, either the os entry_point must have been utilized or os_init needs to be called.
inline AllocatorInfo
default_allocator()
{
local_persist thread_local Arena* arena = nullptr;
if (arena == nullptr) {
VArena* backing_vmem = varena_alloc(.flags = 0, .base_addr = 0x0, .reserve_size = VARENA_DEFAULT_RESERVE, .commit_size = VARENA_DEFAULT_COMMIT);
arena = arena_alloc(.backing = varena_allocator(backing_vmem), .block_size = VARENA_DEFAULT_RESERVE);
}
AllocatorInfo info = { arena_allocator_proc, arena };
return info;
}
#endif
+328
View File
@@ -0,0 +1,328 @@
#ifdef INTELLISENSE_DIRECTIVES
# pragma once
# include "context_cracking.h"
# include "linkage.h"
# include "macros.h"
# include "generic_macros.h"
# include "platform.h"
#endif
#if defined( COMPILER_MSVC )
# if _MSC_VER < 1300
typedef unsigned char, U8;
typedef signed char, S8;
typedef unsigned short, U16;
typedef signed short, S16;
typedef unsigned int, U32;
typedef signed int, S32;
# else
typedef unsigned __int8 U8;
typedef signed __int8 S8;
typedef unsigned __int16 U16;
typedef signed __int16 S16;
typedef unsigned __int32 U32;
typedef signed __int32 S32;
# endif
typedef unsigned __int64 U64;
typedef signed __int64 S64;
#else
# include <stdint.h>
typedef uint8_t, U8;
typedef int8_t, S8;
typedef uint16_t, U16;
typedef int16_t, S16;
typedef uint32_t, U32;
typedef int32_t, S32;
typedef uint64_t, U64;
typedef int64_t, S64;
#endif
typedef struct U128 U128;
struct U128 {
U64 data[2];
};
static_assert( sizeof( U8 ) == sizeof( S8 ), "sizeof(U8) != sizeof(S8)" );
static_assert( sizeof( U16 ) == sizeof( S16 ), "sizeof(U16) != sizeof(sS6)" );
static_assert( sizeof( U32 ) == sizeof( S32 ), "sizeof(U32) != sizeof(sS2)" );
static_assert( sizeof( U64 ) == sizeof( S64 ), "sizeof(U64) != sizeof(sS4)" );
static_assert( sizeof( U8 ) == 1, "sizeof(U8) != 1" );
static_assert( sizeof( U16 ) == 2, "sizeof(U16) != 2" );
static_assert( sizeof( U32 ) == 4, "sizeof(U32) != 4" );
static_assert( sizeof( U64 ) == 8, "sizeof(U64) != 8" );
typedef size_t USIZE;
typedef ptrdiff_t SSIZE;
static_assert( sizeof( USIZE ) == sizeof( SSIZE ), "sizeof(USIZE) != sizeof(SSIZE)" );
#ifndef size_of
#define size_of( x ) ( SSIZE )( sizeof( x ) )
#endif
// NOTE: (u)zpl_intptr is only here for semantic reasons really as this library will only support 32/64 bit OSes.
#if defined( _WIN64 )
typedef signed __int64 SPTR;
typedef unsigned __int64 UPTR;
#elif defined( _WIN32 )
// NOTE; To mark types changing their size, e.g. zpl_intptr
# ifndef _W64
# if ! defined( __midl ) && ( defined( _X86_ ) || defined( _M_IX86 ) ) && _MSC_VER >= 1300
# define _W64 __w64
# else
# define _W64
# endif
# endif
typedef _W64 signed int SPTR;
typedef _W64 unsigned int UPTR;
#else
typedef uintptr_t UPTR;
typedef intptr_t SPTR;
#endif
static_assert( sizeof( UPTR ) == sizeof( SPTR ), "sizeof(UPTR) != sizeof(SPTR)" );
typedef float F32;
typedef double F64;
static_assert( sizeof( F32 ) == 4, "sizeof(F32) != 4" );
static_assert( sizeof( F64 ) == 8, "sizeof(F64) != 8" );
typedef S8 B8;
typedef S16 B16;
typedef S32 B32;
typedef void VoidProc(void);
distinct_register_selector(VoidProc);
////////////////////////////////
//~ NOTE(allen): Constants
#define SIGN32 0x80000000
#define EXPONENT32 0x7F800000
#define MANTISSA32 0x007FFFFF
#define BIG_GOLDEN32 1.61803398875f
#define SMALL_GOLDEN32 0.61803398875f
#define PI32 3.1415926535897f
#define MACHINE_EPSILON64 4.94065645841247e-324
#define MIN_U8 0u
#define MAX_U8 0xffu
#define MIN_S8 ( -0x7f - 1 )
#define MAX_S8 0x7f
#define MIN_U16 0u
#define MAX_U16 0xffffu
#define MIN_S16 ( -0x7fff - 1 )
#define MAX_S16 0x7fff
#define MIN_U32 0u
#define MAX_U32 0xffffffffu
#define MIN_S32 ( -0x7fffffff - 1 )
#define MAX_S32 0x7fffffff
#define MIN_U64 0ull
#define MAX_U64 0xffffffffffffffffull
#define MIN_S64 ( -0x7fffffffffffffffll - 1 )
#define MAX_S64 0x7fffffffffffffffll
#if ARCH_32BIT
# define MIN_USIZE U32_MIN
# define MAX_USIZE U32_MAX
# define MIN_SSIZE S32_MIN
# define MAX_SSIZE S32_MAX
#elif ARCH_64BIT
# define MIN_USIZE U64_MIN
# define MAX_USIZE U64_MAX
# define MIN_SSIZE S64_MIN
# define MAX_SSIZE S64_MAX
#else
# error Unknown architecture size. This library only supports 32 bit and 64 bit architectures.
#endif
#define MIN_F32 1.17549435e-38f
#define MAX_F32 3.40282347e+38f
#define MIN_F64 2.2250738585072014e-308
#define MAX_F64 1.7976931348623157e+308
#define BITMASK1 0x00000001
#define BITMASK2 0x00000003
#define BITMASK3 0x00000007
#define BITMASK4 0x0000000f
#define BITMASK5 0x0000001f
#define BITMASK6 0x0000003f
#define BITMASK7 0x0000007f
#define BITMASK8 0x000000ff
#define BITMASK9 0x000001ff
#define BITMASK10 0x000003ff
#define BITMASK11 0x000007ff
#define BITMASK12 0x00000fff
#define BITMASK13 0x00001fff
#define BITMASK14 0x00003fff
#define BITMASK15 0x00007fff
#define BITMASK16 0x0000ffff
#define BITMASK17 0x0001ffff
#define BITMASK18 0x0003ffff
#define BITMASK19 0x0007ffff
#define BITMASK20 0x000fffff
#define BITMASK21 0x001fffff
#define BITMASK22 0x003fffff
#define BITMASK23 0x007fffff
#define BITMASK24 0x00ffffff
#define BITMASK25 0x01ffffff
#define BITMASK26 0x03ffffff
#define BITMASK27 0x07ffffff
#define BITMASK28 0x0fffffff
#define BITMASK29 0x1fffffff
#define BITMASK30 0x3fffffff
#define BITMASK31 0x7fffffff
#define BITMASK32 0xffffffff
#define BITMASK33 0x00000001ffffffffull
#define BITMASK34 0x00000003ffffffffull
#define BITMASK35 0x00000007ffffffffull
#define BITMASK36 0x0000000fffffffffull
#define BITMASK37 0x0000001fffffffffull
#define BITMASK38 0x0000003fffffffffull
#define BITMASK39 0x0000007fffffffffull
#define BITMASK40 0x000000ffffffffffull
#define BITMASK41 0x000001ffffffffffull
#define BITMASK42 0x000003ffffffffffull
#define BITMASK43 0x000007ffffffffffull
#define BITMASK44 0x00000fffffffffffull
#define BITMASK45 0x00001fffffffffffull
#define BITMASK46 0x00003fffffffffffull
#define BITMASK47 0x00007fffffffffffull
#define BITMASK48 0x0000ffffffffffffull
#define BITMASK49 0x0001ffffffffffffull
#define BITMASK50 0x0003ffffffffffffull
#define BITMASK51 0x0007ffffffffffffull
#define BITMASK52 0x000fffffffffffffull
#define BITMASK53 0x001fffffffffffffull
#define BITMASK54 0x003fffffffffffffull
#define BITMASK55 0x007fffffffffffffull
#define BITMASK56 0x00ffffffffffffffull
#define BITMASK57 0x01ffffffffffffffull
#define BITMASK58 0x03ffffffffffffffull
#define BITMASK59 0x07ffffffffffffffull
#define BITMASK60 0x0fffffffffffffffull
#define BITMASK61 0x1fffffffffffffffull
#define BITMASK62 0x3fffffffffffffffull
#define BITMASK63 0x7fffffffffffffffull
#define BITMASK64 0xffffffffffffffffull
#define BIT1 (1 << 0)
#define BIT2 (1 << 1)
#define BIT3 (1 << 2)
#define BIT4 (1 << 3)
#define BIT5 (1 << 4)
#define BIT6 (1 << 5)
#define BIT7 (1 << 6)
#define BIT8 (1 << 7)
#define BIT9 (1 << 8)
#define BIT10 (1 << 9)
#define BIT11 (1 << 10)
#define BIT12 (1 << 11)
#define BIT13 (1 << 12)
#define BIT14 (1 << 13)
#define BIT15 (1 << 14)
#define BIT16 (1 << 15)
#define BIT17 (1 << 16)
#define BIT18 (1 << 17)
#define BIT19 (1 << 18)
#define BIT20 (1 << 19)
#define BIT21 (1 << 20)
#define BIT22 (1 << 21)
#define BIT23 (1 << 22)
#define BIT24 (1 << 23)
#define BIT25 (1 << 24)
#define BIT26 (1 << 25)
#define BIT27 (1 << 26)
#define BIT28 (1 << 27)
#define BIT29 (1 << 28)
#define BIT30 (1 << 29)
#define BIT31 (1 << 30)
#define BIT32 (1 << 31)
#define BIT33 (1ull << 32)
#define BIT34 (1ull << 33)
#define BIT35 (1ull << 34)
#define BIT36 (1ull << 35)
#define BIT37 (1ull << 36)
#define BIT38 (1ull << 37)
#define BIT39 (1ull << 38)
#define BIT40 (1ull << 39)
#define BIT41 (1ull << 40)
#define BIT42 (1ull << 41)
#define BIT43 (1ull << 42)
#define BIT44 (1ull << 43)
#define BIT45 (1ull << 44)
#define BIT46 (1ull << 45)
#define BIT47 (1ull << 46)
#define BIT48 (1ull << 47)
#define BIT49 (1ull << 48)
#define BIT50 (1ull << 49)
#define BIT51 (1ull << 50)
#define BIT52 (1ull << 51)
#define BIT53 (1ull << 52)
#define BIT54 (1ull << 53)
#define BIT55 (1ull << 54)
#define BIT56 (1ull << 55)
#define BIT57 (1ull << 56)
#define BIT58 (1ull << 57)
#define BIT59 (1ull << 58)
#define BIT60 (1ull << 59)
#define BIT61 (1ull << 60)
#define BIT62 (1ull << 61)
#define BIT63 (1ull << 62)
#define BIT64 (1ull << 63)
////////////////////////////////
//~ Globally Unique Ids
typedef union Guid Guid;
union Guid
{
struct
{
U32 data1;
U16 data2;
U16 data3;
U8 data4[8];
};
U8 v[16];
};
static_assert(size_of(Guid) == 16, "Guid is not 16 bytes");
////////////////////////////////
//~ Arrays
typedef struct U16Array U16Array;
struct U16Array
{
U64 count;
U16* v;
};
typedef struct U32Array U32Array;
struct U32Array
{
U64 count;
U32* v;
};
typedef struct U64Array U64Array;
struct U64Array
{
U64 count;
U64* v;
};
typedef struct U128Array U128Array;
struct U128Array
{
U64 count;
U128* v;
};
+165
View File
@@ -0,0 +1,165 @@
#ifdef INTELLISENSE_DIRECTIVES
# include "command_line.h"
#endif
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
////////////////////////////////
//~ NOTE(rjf): Command Line Option Parsing
CmdLineOpt**
cmd_line_slot_from_string(CmdLine* cmd_line, String8 string) {
CmdLineOpt** slot = 0;
if (cmd_line->option_table_size != 0)
{
U64 hash = cmd_line_hash_from_string(string);
U64 bucket = hash % cmd_line->option_table_size;
slot = &cmd_line->option_table[bucket];
}
return slot;
}
CmdLineOpt*
cmd_line_opt_from_slot(CmdLineOpt** slot, String8 string) {
CmdLineOpt* result = 0;
for (CmdLineOpt* var = *slot; var; var = var->hash_next)
{
if (str8_match(string, var->string, 0)) {
result = var;
break;
}
}
return result;
}
CmdLineOpt*
cmd_line_insert_opt__ainfo(AllocatorInfo ainfo, CmdLine* cmd_line, String8 string, String8List values)
{
CmdLineOpt *var = 0;
CmdLineOpt **slot = cmd_line_slot_from_string(cmd_line, string);
CmdLineOpt *existing_var = cmd_line_opt_from_slot(slot, string);
if(existing_var != 0)
{
var = existing_var;
}
else
{
var = alloc_array(ainfo, CmdLineOpt, 1);
var->hash_next = *slot;
var->hash = cmd_line_hash_from_string(string);
var->string = str8_copy(ainfo, string);
var->value_strings = values;
StringJoin join = {0};
join.pre = str8_lit("");
join.sep = str8_lit(",");
join.post = str8_lit("");
var->value_string = str8_list_join(ainfo, &var->value_strings, &join);
*slot = var;
cmd_line_push_opt(&cmd_line->options, var);
}
return var;
}
CmdLine
cmd_line_from_string_list__ainfo(AllocatorInfo ainfo, String8List command_line)
{
CmdLine parsed = {0};
parsed.exe_name = command_line.first->string;
// NOTE(rjf): Set up config option table.
{
parsed.option_table_size = 4096;
parsed.option_table = alloc_array(ainfo, CmdLineOpt*, parsed.option_table_size);
}
// NOTE(rjf): Parse command line.
B32 after_passthrough_option = 0;
B32 first_passthrough = 1;
for (String8Node* node = command_line.first->next, *next = 0; node != 0; node = next)
{
next = node->next;
String8 option_name = node->string;
// NOTE(rjf): Look at -- or - at the start of an argument to determine if it's
// a flag option. All arguments after a single "--" (with no trailing string
// on the command line will be considered as input files.
B32 is_option = 1;
if(after_passthrough_option == 0)
{
if (str8_match(node->string, str8_lit("--"), 0)) {
after_passthrough_option = 1;
is_option = 0;
}
else if (str8_match(str8_prefix(node->string, 2), str8_lit("--"), 0)) {
option_name = str8_skip(option_name, 2);
}
else if (str8_match(str8_prefix(node->string, 1), str8_lit("-"), 0)) {
option_name = str8_skip(option_name, 1);
}
else {
is_option = 0;
}
}
else
{
is_option = 0;
}
// NOTE(rjf): This string is an option.
if(is_option)
{
B32 has_arguments = 0;
U64 arg_signifier_position1 = str8_find_needle(option_name, 0, str8_lit(":"), 0);
U64 arg_signifier_position2 = str8_find_needle(option_name, 0, str8_lit("="), 0);
U64 arg_signifier_position = min(arg_signifier_position1, arg_signifier_position2);
String8 arg_portion_this_string = str8_skip(option_name, arg_signifier_position+1);
if (arg_signifier_position < option_name.size) {
has_arguments = 1;
}
option_name = str8_prefix(option_name, arg_signifier_position);
String8List arguments = {0};
// NOTE(rjf): Parse arguments.
if (has_arguments)
{
for (String8Node* n = node; n; n = n->next)
{
next = n->next;
String8 string = n->string;
if (n == node) {
string = arg_portion_this_string;
}
U8 splits[] = { ',' };
String8List args_in_this_string = str8_split(ainfo, string, splits, array_count(splits), 0);
for (String8Node* sub_arg = args_in_this_string.first; sub_arg; sub_arg = sub_arg->next) {
str8_list_push(ainfo, &arguments, sub_arg->string);
}
if ( !str8_match(str8_postfix(n->string, 1), str8_lit(","), 0) && (n != node || arg_portion_this_string.size != 0)) {
break;
}
}
}
// NOTE(rjf): Register config variable.
cmd_line_insert_opt(ainfo, &parsed, option_name, arguments);
}
// NOTE(rjf): Default path, treat as a passthrough config option to be
// handled by tool-specific code.
else if ( !str8_match(node->string, str8_lit("--"), 0) || !first_passthrough) {
str8_list_push(ainfo, &parsed.inputs, node->string);
after_passthrough_option = 1;
first_passthrough = 0;
}
}
return parsed;
}
+106
View File
@@ -0,0 +1,106 @@
#ifdef INTELLISENSE_DIRECTIVES
# pragma once
# include "strings.h"
#endif
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
////////////////////////////////
//~ rjf: Parsed Command Line Types
typedef struct CmdLineOpt CmdLineOpt;
struct CmdLineOpt
{
CmdLineOpt* next;
CmdLineOpt* hash_next;
U64 hash;
String8 string;
String8List value_strings;
String8 value_string;
};
typedef struct CmdLineOptList CmdLineOptList;
struct CmdLineOptList
{
U64 count;
CmdLineOpt* first;
CmdLineOpt* last;
};
typedef struct CmdLine CmdLine;
struct CmdLine
{
String8 exe_name;
CmdLineOptList options;
String8List inputs;
U64 option_table_size;
CmdLineOpt** option_table;
};
////////////////////////////////
//~ NOTE(rjf): Command Line Option Parsing
U64 cmd_line_hash_from_string(String8 string);
MD_API CmdLineOpt** cmd_line_slot_from_string(CmdLine* cmd_line, String8 string);
MD_API CmdLineOpt* cmd_line_opt_from_slot (CmdLineOpt** slot, String8 string);
void cmd_line_push_opt (CmdLineOptList* list, CmdLineOpt* var);
CmdLineOpt* cmd_line_opt_from_string (CmdLine* cmd_line, String8 name);
String8List cmd_line_strings (CmdLine* cmd_line, String8 name);
String8 cmd_line_string (CmdLine* cmd_line, String8 name);
B32 cmd_line_has_flag (CmdLine* cmd_line, String8 name);
B32 cmd_line_has_argument (CmdLine* cmd_line, String8 name);
inline CmdLineOpt* cmd_line_insert_opt__arena (Arena* arena, CmdLine* cmd_line, String8 string, String8List values);
MD_API CmdLineOpt* cmd_line_insert_opt__ainfo (AllocatorInfo ainfo, CmdLine* cmd_line, String8 string, String8List values);
inline CmdLine cmd_line_from_string_list__arena(Arena* arena, String8List arguments);
MD_API CmdLine cmd_line_from_string_list__ainfo(AllocatorInfo ainfo, String8List arguments);
#define cmd_line_insert_opt(allocator, cmd_line, string, values) \
_Generic(allocator, \
Arena*: cmd_line_insert_opt__arena, \
AllocatorInfo: cmd_line_insert_opt__ainfo, \
default: assert_generic_sel_fail \
) generic_call(allocator, cmd_line, string, values)
#define cmd_line_from_string_list(allocator, arguments) \
_Generic(allocator, \
Arena*: cmd_line_from_string_list__arena, \
AllocatorInfo: cmd_line_from_string_list__ainfo, \
default: assert_generic_sel_fail \
) generic_call(allocator, arguments)
inline U64
cmd_line_hash_from_string(String8 string) {
U64 result = 5381;
for(U64 i = 0; i < string.size; i += 1) {
result = ((result << 5) + result) + string.str[i];
}
return result;
}
force_inline CmdLineOpt* cmd_line_insert_opt__arena (Arena* arena, CmdLine* cmd_line, String8 string, String8List values) { return cmd_line_insert_opt__ainfo (arena_allocator(arena), cmd_line, string, values); }
force_inline CmdLine cmd_line_from_string_list__arena(Arena* arena, String8List command_line) { return cmd_line_from_string_list__ainfo(arena_allocator(arena), command_line); }
inline CmdLineOpt* cmd_line_opt_from_string(CmdLine *cmd_line, String8 name) { return cmd_line_opt_from_slot(cmd_line_slot_from_string(cmd_line, name), name); }
inline B32 cmd_line_has_flag (CmdLine *cmd_line, String8 name) { CmdLineOpt *var = cmd_line_opt_from_string(cmd_line, name); return(var != 0); }
inline B32 cmd_line_has_argument (CmdLine *cmd_line, String8 name) { CmdLineOpt *var = cmd_line_opt_from_string(cmd_line, name); return(var != 0 && var->value_strings.node_count > 0); }
inline String8List
cmd_line_strings(CmdLine *cmd_line, String8 name) {
String8List result = {0};
CmdLineOpt* var = cmd_line_opt_from_string(cmd_line, name);
if (var != 0) { result = var->value_strings; }
return result;
}
inline String8
cmd_line_string(CmdLine *cmd_line, String8 name) {
String8 result = {0};
CmdLineOpt* var = cmd_line_opt_from_string(cmd_line, name);
if (var != 0) { result = var->value_string; }
return result;
}
inline void cmd_line_push_opt(CmdLineOptList* list, CmdLineOpt* var) { sll_queue_push(list->first, list->last, var); list->count += 1; }
+326
View File
@@ -0,0 +1,326 @@
#ifdef INTELLISENSE_DIRECTIVES
# pragma once
#endif
#pragma region Build Options
////////////////////////////////
//~ rjf: Build Options
#if ! defined(BUILD_DEBUG)
# define BUILD_DEBUG 1
#endif
#if ! defined(BUILD_STATIC)
# define BUILD_STATIC 0
#endif
#if ! defined(BUILD_DYANMIC)
# define BUILD_DYANMIC 0
#endif
#if ! defined(BUILD_API_EXPORT)
# define BUILD_API_EXPORT 0
#endif
#if !defined(BUILD_ENTRY_DEFINING_UNIT)
# define BUILD_ENTRY_DEFINING_UNIT 0
#endif
#if !defined(BUILD_CONSOLE_INTERFACE)
# define BUILD_CONSOLE_INTERFACE 0
#endif
#if !defined(BUILD_VERSION_MAJOR)
# define BUILD_VERSION_MAJOR 0
#endif
#if !defined(BUILD_VERSION_MINOR)
# define BUILD_VERSION_MINOR 1
#endif
#if !defined(BUILD_VERSION_PATCH)
# define BUILD_VERSION_PATCH 0
#endif
#define BUILD_VERSION_STRING_LITERAL stringify(BUILD_VERSION_MAJOR) "." stringify(BUILD_VERSION_MINOR) "." stringify(BUILD_VERSION_PATCH)
#if BUILD_DEBUG
# define BUILD_MODE_STRING_LITERAL_APPEND " [Debug]"
#else
# define BUILD_MODE_STRING_LITERAL_APPEND ""
#endif
#if defined(BUILD_GIT_HASH)
# define BUILD_GIT_HASH_STRING_LITERAL_APPEND " [" BUILD_GIT_HASH "]"
#else
# define BUILD_GIT_HASH_STRING_LITERAL_APPEND ""
#endif
#if !defined(BUILD_TITLE)
# define BUILD_TITLE "Untitled"
#endif
#if !defined(BUILD_RELEASE_PHASE_STRING_LITERAL)
# define BUILD_RELEASE_PHASE_STRING_LITERAL "ALPHA"
#endif
#if !defined(BUILD_ISSUES_LINK_STRING_LITERAL)
# define BUILD_ISSUES_LINK_STRING_LITERAL "ADD_BUILD_ISSUES_LINK"
#endif
#define BUILD_TITLE_STRING_LITERAL BUILD_TITLE " (" BUILD_VERSION_STRING_LITERAL " " BUILD_RELEASE_PHASE_STRING_LITERAL ") - " __DATE__ "" BUILD_GIT_HASH_STRING_LITERAL_APPEND BUILD_MODE_STRING_LITERAL_APPEND
#pragma endregion Build Options
#pragma region Vendor
#if defined( _MSC_VER )
# pragma message("Detected MSVC")
// # define COMPILER_CLANG 0
# define COMPILER_MSVC 1
// # define COMPILER_GCC 0
# if _MSC_VER >= 1920
# define COMPILER_MSVC_YEAR 2019
# elif _MSC_VER >= 1910
# define COMPILER_MSVC_YEAR 2017
# elif _MSC_VER >= 1900
# define COMPILER_MSVC_YEAR 2015
# elif _MSC_VER >= 1800
# define COMPILER_MSVC_YEAR 2013
# elif _MSC_VER >= 1700
# define COMPILER_MSVC_YEAR 2012
# elif _MSC_VER >= 1600
# define COMPILER_MSVC_YEAR 2010
# elif _MSC_VER >= 1500
# define M_DCOMPILER_MSVC_YEAR 2008
# elif _MSC_VER >= 1400
# define COMPILER_MSVC_YEAR 2005
# else
# define COMPILER_MSVC_YEAR 0
# endif
#elif defined( __GNUC__ )
# pragma message("Detected GCC")
// # define COMPILER_CLANG 0
// # define COMPILER_MSVC 0
# define COMPILER_GCC 1
#elif defined( __clang__ )
# pragma message("Detected CLANG")
# define COMPILER_CLANG 1
// # define COMPILER_MSVC 0
// # define COMPILER_GCC 0
#else
# error Unknown compiler
#endif
#if defined( __has_attribute )
# define HAS_ATTRIBUTE( attribute ) __has_attribute( attribute )
#else
# define HAS_ATTRIBUTE( attribute ) ( 0 )
#endif
#if defined(GCC_VERSION_CHECK)
# undef GCC_VERSION_CHECK
#endif
#if defined(GEN_GCC_VERSION)
# define GCC_VERSION_CHECK(major,minor,patch) (GEN_GCC_VERSION >= GEN_VERSION_ENCODE(major, minor, patch))
#else
# define GCC_VERSION_CHECK(major,minor,patch) (0)
#endif
#pragma endregion Vendor
#pragma region Language
#if ! defined(LANG_C)
# ifdef __cplusplus
# define LANG_C 0
# define LANG_CPP 1
# else
# if defined(__STDC__)
# define LANG_C 1
# define LANG_CPP 0
# else
// Fallback for very old C compilers
# define LANG_C 1
# define LANG_CPP 0
# endif
# endif
#endif
#if LANG_C
# pragma message("MD: Detected C")
#endif
#if LANG_CPP
# pragma message("MD: Detected CPP")
#endif
#pragma endregion Langauge
#pragma region Hardware Architecture
#if COMPILER_CLANG
# if defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64)
# define ARCH_X64 1
# elif defined(i386) || defined(__i386) || defined(__i386__)
# define ARCH_X86 1
# elif defined(__aarch64__)
# define ARCH_ARM64 1
# elif defined(__arm__)
# define ARCH_ARM32 1
# else
# error Architecture not supported.
# endif
#endif // COMPILER_CLANG
#if COMPILER_MSVC
# if defined(_M_AMD64)
# define ARCH_X64 1
# elif defined(_M_IX86)
# define ARCH_X86 1
# elif defined(_M_ARM64)
# define ARCH_ARM64 1
# elif defined(_M_ARM)
# define ARCH_ARM32 1
# else
# error Architecture not supported.
# endif
#endif // COMPILER_MSVC
#if COMPILER_GCC
# if defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64)
# define ARCH_X64 1
# elif defined(i386) || defined(__i386) || defined(__i386__)
# define ARCH_X86 1
# elif defined(__aarch64__)
# define ARCH_ARM64 1
# elif defined(__arm__)
# define ARCH_ARM32 1
# else
# error Architecture not supported.
# endif
#endif // COMPILER_GCC
#if defined(ARCH_X64)
# define ARCH_64BIT 1
#elif defined(ARCH_X86)
# define ARCH_32BIT 1
#endif
#if ARCH_ARM32 || ARCH_ARM64 || ARCH_X64 || ARCH_X86
# define ARCH_LITTLE_ENDIAN 1
#else
# error Endianness of this architecture not understood by context cracker.
#endif
#pragma endregion Hardware Architecture
#pragma region Operating System
#if defined( _WIN32 ) || defined( _WIN64 )
# ifndef OS_WINDOWS
# define OS_WINDOWS 1
# endif
#elif defined( __APPLE__ ) && defined( __MACH__ )
# ifndef OS_OSX
# define OS_OSX 1
# endif
# ifndef OS_MACOS
# define OS_MACOS 1
# endif
#elif defined( __unix__ )
# ifndef OS_UNIX
# define OS_UNIX 1
# endif
# if defined( ANDROID ) || defined( __ANDROID__ )
# ifndef OS_ANDROID
# define OS_ANDROID 1
# endif
# ifndef OS_LINUX
# define OS_LINUX 1
# endif
# elif defined( __linux__ )
# ifndef OS_LINUX
# define OS_LINUX 1
# endif
# elif defined( __FreeBSD__ ) || defined( __FreeBSD_kernel__ )
# ifndef OS_FREEBSD
# define OS_FREEBSD 1
# endif
# elif defined( __OpenBSD__ )
# ifndef OS_OPENBSD
# define OS_OPENBSD 1
# endif
# elif defined( __EMSCRIPTEN__ )
# ifndef OS_EMSCRIPTEN
# define OS_EMSCRIPTEN 1
# endif
# elif defined( __CYGWIN__ )
# ifndef OS_CYGWIN
# define OS_CYGWIN 1
# endif
# else
# error This UNIX operating system is not supported
# endif
#else
# error This operating system is not supported
#endif
#pragma endregion Operating System
////////////////////////////////
//~ rjf: Zero All Undefined Options
#if !defined(ARCH_32BIT)
# define ARCH_32BIT 0
#endif
#if !defined(ARCH_64BIT)
# define ARCH_64BIT 0
#endif
#if !defined(ARCH_X64)
# define ARCH_X64 0
#endif
#if !defined(ARCH_X86)
# define ARCH_X86 0
#endif
#if !defined(ARCH_ARM64)
# define ARCH_ARM64 0
#endif
#if !defined(ARCH_ARM32)
# define ARCH_ARM32 0
#endif
#if !defined(COMPILER_MSVC)
# define COMPILER_MSVC 0
#endif
#if !defined(COMPILER_GCC)
# define COMPILER_GCC 0
#endif
#if !defined(COMPILER_CLANG)
# define COMPILER_CLANG 0
#endif
#if !defined(OS_WINDOWS)
# define OS_WINDOWS 0
#endif
#if !defined(OS_LINUX)
# define OS_LINUX 0
#endif
#if !defined(OS_MAC)
# define OS_MAC 0
#endif
#if !defined(LANG_CPP)
# define LANG_CPP 0
#endif
#if !defined(LANG_C)
# define LANG_C 0
#endif
////////////////////////////////
//~ rjf: Unsupported Errors
#if ARCH_X86
# error You tried to build in x86 (32 bit) mode, but currently, only building in x64 (64 bit) mode is supported.
#endif
#if ! ARCH_X64
# error You tried to build with an unsupported architecture. Currently, only building in x64 mode is supported.
#endif
+26
View File
@@ -0,0 +1,26 @@
#ifdef INTELLISENSE_DIRECTIVES
# include "strings.h"
# include "debug.h"
# include "platform.c"
#endif
#define md__printf_err( fmt, ... ) fprintf( stderr, fmt, __VA_ARGS__ )
#define md__printf_err_va( fmt, va ) vfprintf( stderr, fmt, va )
void assert_handler( char const* condition, char const* file, char const* function, S32 line, char const* msg, ... )
{
md__printf_err( "%s - %s:(%d): Assert Failure: ", file, function, line );
if ( condition )
md__printf_err( "`%s` \n", condition );
if ( msg )
{
va_list va;
va_start( va, msg );
md__printf_err_va( msg, va );
va_end( va );
}
md__printf_err( "%s", "\n" );
}
+63
View File
@@ -0,0 +1,63 @@
#ifdef INTELLISENSE_DIRECTIVES
# pragma once
# include "context_cracking.h"
# include "linkage.h"
# include "macros.h"
# include "base_types.h"
#endif
// TODO(Ed): Review this
////////////////////////////////
//~ rjf: Asserts
#ifndef trap
# if COMPILER_MSVC
# if _MSC_VER < 1300
# define trap() __asm int 3 /* Trap to debugger! */
# else
# define trap() __debugbreak()
# endif
# elif COMPILER_CLANG || COMPILER_GCC
# define trap() __builtin_trap()
# else
# error Unknown trap intrinsic for this compiler.
# endif
#endif
#define assert_msg( cond, msg, ... ) \
do \
{ \
if ( ! ( cond ) ) \
{ \
assert_handler( #cond, __FILE__, __func__, scast( S64, __LINE__ ), msg, ##__VA_ARGS__ ); \
trap(); \
} \
} while ( 0 )
#ifndef assert_always
#define assert_always(x) do { if ( !(x) ) { trap(); } } while(0)
#endif
#ifndef assert
# if BUILD_DEBUG
# define assert(x) assert_always(x)
# else
# define assert(x) (void)(x)
# endif
#endif
#ifndef invalid_path
#define invalid_path assert( ! "Invalid Path!")
#endif
#ifndef not_implemented
#define not_implemented assert( ! "Not Implemented!")
#endif
#ifndef no_op
#define no_op ((void)0)
#endif
#ifndef md_static_assert
#define md_static_assert(C, ID) global U8 glue(ID, __LINE__)[ (C) ? 1 : -1 ]
#endif
MD_API void assert_handler( char const* condition, char const* file, char const* function, S32 line, char const* msg, ... );
+47
View File
@@ -0,0 +1,47 @@
#ifdef INTELLISENSE_DIRECTIVES
# include "entry_point.h"
# include "profiling.h"
# include "arena.h"
# include "thread_context.h"
# include "os/os.h"
#endif
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
void main_thread_base_entry_point(MainThread_EntryPointProc* entry_point, char** arguments, U64 arguments_count)
{
#if PROFILE_TELEMETRY
local_persist U8 tm_data[MB(64)];
tmLoadLibrary(TM_RELEASE);
tmSetMaxThreadCount(256);
tmInitialize(sizeof(tm_data), (char *)tm_data);
#endif
thread_namef("[main thread]");
TempArena scratch = scratch_begin(0, 0);
String8List command_line_argument_strings = os_string_list_from_argcv(scratch.arena, (int)arguments_count, arguments);
CmdLine cmdline = cmd_line_from_string_list(scratch.arena, command_line_argument_strings);
B32 capture = cmd_line_has_flag(&cmdline, str8_lit("capture"));
if (capture) {
prof_begin_capture(arguments[0]);
}
entry_point(&cmdline);
if (capture) {
prof_end_capture();
}
scratch_end(scratch);
}
void
supplement_thread_base_entry_point(SupplementThread_EntryPointProc* entry_point, void* params)
{
TCTX tctx;
tctx_init_and_equip(&tctx);
entry_point(params);
tctx_release();
}
+15
View File
@@ -0,0 +1,15 @@
#ifdef INTELLISENSE_DIRECTIVES
# pragma once
# include "context_cracking.h"
# include "linkage.h"
# include "command_line.h"
#endif
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
typedef void (MainThread_EntryPointProc) (CmdLine* cmd_line);
typedef void (SupplementThread_EntryPointProc)(void* params);
MD_API void main_thread_base_entry_point (MainThread_EntryPointProc* entry_point, char** arguments, U64 arguments_count);
MD_API void supplement_thread_base_entry_point(SupplementThread_EntryPointProc* entry_point, void* params);
+23
View File
@@ -0,0 +1,23 @@
#ifdef INTELLISENSE_DIRECTIVES
# pragma once
# include "base_types.h"
# include "time.h"
#endif
////////////////////////////////
//~ allen: Files
typedef U32 FilePropertyFlags;
enum
{
FilePropertyFlag_IsFolder = (1 << 0),
};
typedef struct FileProperties FileProperties;
struct FileProperties
{
U64 size;
DenseTime modified;
DenseTime created;
FilePropertyFlags flags;
};
+172
View File
@@ -0,0 +1,172 @@
#ifdef INTELLISENSE_DIRECTIVES
# pragma once
// NOTE(Ed): For C++ generation, this file will not be injected, any macros that are used will either be injected as transparent defines
// or have their usage removed during the library generation pass.
#endif
// ____ _ ______ _ _ ____ _ __ _
// / ___} (_) | ____} | | (_) / __ \ | | | |(_)
// | | ___ ___ _ __ ___ _ __ _ ___ | |__ _ _ _ __ ___| |_ _ ___ _ __ | | | |_ _____ _ __ | | ___ __ _ __| | _ _ __ __ _
// | |{__ |/ _ \ '_ \ / _ \ '__} |/ __| | __} | | | '_ \ / __} __} |/ _ \| '_ \ | | | \ \ / / _ \ '_ }| |/ _ \ / _` |/ _` || | '_ \ / _` |
// | |__j | __/ | | | __/ | | | (__ | | | |_| | | | | (__| l_| | (_) | | | | | l__| |\ V / __/ | | | (_) | (_| | (_| || | | | | (_| |
// \____/ \___}_l l_l\___}_l l_l\___| l_l \__,_l_l l_l\___}\__}_l\___/l_l l_l \____/ \_/ \___}_l l_l\___/ \__,_l\__,_l|_|_| |_|\__, |
// This implemnents macros for utilizing "The Naive Extendible _Generic Macro" explained in: __| |
// https://github.com/JacksonAllan/CC/blob/main/articles/Better_C_Generics_Part_1_The_Extendible_Generic.md {___/
// It was choosen over the more novel implementations to keep the macros as easy to understand and unobfuscated as possible.
// --------------------------------------------------------------------------------------------------------------------------------------------
// For explanation of intended usage with staged metaprogramming see: https://github.com/Ed94/gencpp/tree/main/gen_c_library#macro-usage
// Techncially this can be used for more than just implementing function overloading.
// Additional info: https://www.chiark.greenend.org.uk/~sgtatham/quasiblog/c11-generic/
// --------------------------------------------------------------------------------------------------------------------------------------------
#ifndef COMMA_OPERATOR
#define COMMA_OPERATOR , // The comma operator is used by preprocessor macros to delimit arguments, so we have to represent it via a macro to prevent parsing incorrectly.
#endif
// Helper macros for argument selection
#ifndef select_arg_1
#define select_arg_1( _1, ... ) _1 // <-- Of all th args passed pick _1.
#define select_arg_2( _1, _2, ... ) _2 // <-- Of all the args passed pick _2.
#define select_arg_3( _1, _2, _3, ... ) _3 // etc..
#define generic_sel_entry_type select_arg_1 // Use the arg expansion macro to select arg 1 which should have the type.
#define generic_sel_entry_function select_arg_2 // Use the arg expansion macro to select arg 2 which should have the function.
#define generic_sel_entry_comma_delimiter select_arg_3 // Use the arg expansion macro to select arg 3 which should have the comma delimiter ','.
#endif
#ifndef generic_call
#define generic_call // Just used to indicate where the call "occurs"
#endif
// ----------------------------------------------------------------------------------------------------------------------------------
#ifndef if_generic_selector_defined_include_slot
// if_generic_selector_defined_include_slot( macro ) includes a _Generic slot only if the specified macro is defined (as type, function_name).
// It takes advantage of the fact that if the macro is defined, then the expanded text will contain a comma.
// Expands to ',' if it can find (type): (function) <comma_operator: ',' >
// Where generic_sel_entry_comma_delimiter is specifically looking for that <comma> ,
#define if_generic_selector_defined_include_slot( slot_exp ) generic_sel_entry_comma_delimiter( slot_exp, generic_sel_entry_type( slot_exp, ): generic_sel_entry_function( slot_exp, ) COMMA_OPERATOR, , )
// ^ Selects the comma ^ is the type ^ is the function ^ Insert a comma
// The slot won't exist if that comma is not found.
#endif
// For the occastion where an expression didn't resolve to a selection option the "default: <value>" will be set to:
typedef struct UNRESOLVED_GENERIC_SELECTION UNRESOLVED_GENERIC_SELECTION;
struct UNRESOLVED_GENERIC_SELECTION {
void* _THE_VOID_SLOT_;
};
UNRESOLVED_GENERIC_SELECTION const assert_generic_sel_fail = {0};
// Which will provide the message: error: called object type 'struct NO_RESOLVED_GENERIC_SELECTION' is not a function or function pointer
// ----------------------------------------------------------------------------------------------------------------------------------
// Below are generated on demand for an overlaod depdendent on a type:
// ----------------------------------------------------------------------------------------------------------------------------------
#define function_generic_example( selector_arg ) _Generic( \
(selector_arg), /* Select Via Expression*/ \
/* Extendibility slots: */ \
if_generic_selector_defined_include_slot( GENERIC_SLOT_1__function_sig ) \
if_generic_selector_defined_include_slot( GENERIC_SLOT_2__function_sig ) \
default: assert_generic_sel_fail \
) generic_call( selector_arg )
// ----------------------------------------------------------------------------------------------------------------------------------
// Then each definiton of a function has an associated define:
// #define GENERIC_SLOT_<#>_<generic identifier> <typename>, <function_to_resolve>
// Then somehwere later on
// <etc> <return_type> <function_id> ( <arguments> ) { <implementation> }
// Concrete example:
// To add support for long:
#define GENERIC_SLOT_1__example_hash long, generic_example_hash__P_long
size_t generic_example_hash__P_long( long val ) { return val * 2654435761ull; }
// To add support for long long:
#define GENERIC_SLOT_2__example_hash long long, generic_example_hash__P_long_long
size_t generic_example_hash__P_long_long( long long val ) { return val * 2654435761ull; }
// If using an Editor with support for syntax hightlighting macros:
// GENERIC_SLOT_1__example_hash and GENERIC_SLOT_2__example_hash should show color highlighting indicating the slot is enabled,
// or, "defined" for usage during the compilation pass that handles the _Generic instrinsic.
#define generic_example_hash( function_arguments ) _Generic( \
(function_arguments), /* Select Via Expression*/ \
/* Extendibility slots: */ \
if_generic_selector_defined_include_slot( GENERIC_SLOT_1__example_hash ) \
if_generic_selector_defined_include_slot( GENERIC_SLOT_2__example_hash ) \
if_generic_selector_defined_include_slot( GENERIC_SLOT_3__example_hash ) \
if_generic_selector_defined_include_slot( GENERIC_SLOT_4__example_hash ) \
if_generic_selector_defined_include_slot( GENERIC_SLOT_5__example_hash ) \
if_generic_selector_defined_include_slot( GENERIC_SLOT_6__example_hash ) \
if_generic_selector_defined_include_slot( GENERIC_SLOT_7__example_hash ) \
if_generic_selector_defined_include_slot( GENERIC_SLOT_8__example_hash ) \
default: assert_generic_sel_fail \
) generic_call( function_arguments )
// Additional Variations:
// If the function takes more than one argument the following is used:
#define function_generic_example_varadic( selector_arg, ... ) _Generic( \
(selector_arg), \
if_generic_selector_defined_include_slot( GENERIC_SLOT_1__function_sig ) \
if_generic_selector_defined_include_slot( GENERIC_SLOT_2__function_sig ) \
/* ... */ \
if_generic_selector_defined_include_slot(GENERIC_SLOT_N__function_sig ) \
default: assert_generic_sel_fail \
) generic_call( selector_arg, __VA_ARG__ )
// If the function does not take the arugment as a parameter:
#define function_generic_example_direct_type( selector_arg ) _Generic( \
( type_to_expression(selector_arg) ), \
if_generic_selector_defined_include_slot( GENERIC_SLOT_1__function_sig ) \
if_generic_selector_defined_include_slot( GENERIC_SLOT_2__function_sig ) \
/* ... */ \
if_generic_selector_defined_include_slot(GENERIC_SLOT_N__function_sig ) \
default: assert_generic_sel_fail \
) generic_call(selector_arg)
#ifndef type_to_expression
// Used to keep the _Generic keyword happy as bare types are not considered "expressions"
#define type_to_expression(type) (* (type*)NULL)
// Instead of using this macro, it should be directly expanded by code generation.
#endif
// _Generic also suffers from type compability flatting distinctions between typedef by resolving the selection to the underling type and qualifier.
// To resolve this these distinctions must be enforce by keeping "compatible" types in separate _Generic expressions:
#define example_with__l2(expr) _Generic( \
(expr), \
S64 : example_with_s64, \
default: assert_generic_sel_fail \
)
#define example_with(expr) \
_Generic((expr), \
SSIZE : example_with_SSIZEZ \
default : example_with_not_SSIZE(allocator, in) \
) generic_call(allocator, in)
// This can be made more concise with he following "layer" indicating _Generic macros
#define _Generic_L2(expr, ...) default: _Generic(expr, __VA_ARGS__)
#define _Generic_L3(expr, ...) default: _Generic(expr, __VA_ARGS__)
#define example_with_generic_layers(expr) \
_Generic( (expr), \
S64 : example_with_s64, \
_Generic_L2((expr), \
SSIZE: example_with_SSIZZE \
default: assert_generic_sel_fail \
), \
) generic_call(expr)
// Removing example definitions
#undef example_with
#undef example_with__l2
#undef example_with_generic_layers
#undef function_generic_example
#undef GENERIC_SLOT_1__example_hash
#undef GENERIC_SLOT_2__example_hash
#undef generic_example_hash
#undef function_generic_example_varadic
#undef function_generic_example_direct_type
#undef generic_example_do_something_with
+49
View File
@@ -0,0 +1,49 @@
#ifdef INTELLISENSE_DIRECTIVES
# pragma once
# include "context_cracking.h"
#endif
#ifndef MD_API
#if COMPILER_MSVC
# if BUILD_DYANMIC
# if BUILD_API_EXPORT
# define MD_API __declspec(dllexport)
# else
# define MD_API __declspec(dllimport)
# endif
# else
# define MD_API // Empty for static builds
# endif
#else
# ifdef BUILD_DYANMIC
# define MD_API __attribute__((visibility("default")))
# else
# define MD_API // Empty for static builds
# endif
#endif
#endif // GEN_API
#ifndef MD_API_C_BEGIN
# if LANG_C
# define MD_API_C_BEGIN
# define MD_API_C_END
# define MD_API_C
# else
# define MD_API_C_BEGIN extern "C" {
# define MD_API_C_END }
# define MD_API_C_END extern "C"
# endif
#endif
#ifndef global // Global variables
# if BUILD_API_EXPORT || BUILD_STATIC
# define global
# else
# define global static
# endif
#endif
// Internal Linkage
#ifndef internal
#define internal static
#endif
+89
View File
@@ -0,0 +1,89 @@
#ifdef INTELLISENSE_DIRECTIVES
# include "logger.h"
# include "thread_context.h"
#endif
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
////////////////////////////////
//~ rjf: Globals/Thread-Locals
MD_API_C thread_static Log* log_active;
#if !BUILD_SUPPLEMENTARY_UNIT
MD_API_C thread_static Log* log_active = 0;
#endif
////////////////////////////////
//~ rjf: Log Creation/Selection
void
log_select(Log* log) {
log_active = log;
}
////////////////////////////////
//~ rjf: Log Building
void
log_msg(LogMsgKind kind, String8 string) {
if(log_active != 0 && log_active->top_scope != 0) {
String8 string_copy = str8_copy(log_active->arena, string);
str8_list_push(log_active->arena, &log_active->top_scope->strings[kind], string_copy);
}
}
void
log_msgf(LogMsgKind kind, char *fmt, ...) {
if(log_active != 0)
{
TempArena scratch = scratch_begin(0, 0);
va_list args;
va_start(args, fmt);
String8 string = str8fv(scratch.arena, fmt, args);
log_msg(kind, string);
va_end(args);
scratch_end(scratch);
}
}
////////////////////////////////
//~ rjf: Log Scopes
void
log_scope_begin(void) {
if (log_active != 0) {
U64 pos = arena_pos(log_active->arena);
LogScope* scope = push_array(log_active->arena, LogScope, 1);
scope->pos = pos;
sll_stack_push(log_active->top_scope, scope);
}
}
LogScopeResult
log_scope_end(Arena *arena)
{
LogScopeResult result = {0};
if(log_active != 0)
{
LogScope* scope = log_active->top_scope;
if(scope != 0)
{
sll_stack_pop(log_active->top_scope);
if(arena != 0)
{
for (each_enum_val(LogMsgKind, kind)) {
TempArena scratch = scratch_begin(&arena, 1);
String8
result_unindented = str8_list_join(scratch.arena, &scope->strings[kind], 0);
result.strings[kind] = indented_from_string(arena, result_unindented);
scratch_end(scratch);
}
}
arena_pop_to(log_active->arena, scope->pos);
}
}
return result;
}
+86
View File
@@ -0,0 +1,86 @@
#ifdef INTELLISENSE_DIRECTIVES
# pragma once
# include "base_types.h"
# include "strings.h"
# include "arena.h"
# include "thread_context.h"
#endif
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
////////////////////////////////
//~ rjf: Log Types
typedef enum LogMsgKind LogMsgKind;
enum LogMsgKind
{
LogMsgKind_Info,
LogMsgKind_UserError,
LogMsgKind_COUNT
};
typedef struct LogScope LogScope;
struct LogScope
{
LogScope* next;
U64 pos;
String8List strings[LogMsgKind_COUNT];
};
typedef struct LogScopeResult LogScopeResult;
struct LogScopeResult
{
String8 strings[LogMsgKind_COUNT];
};
typedef struct Log Log;
struct Log
{
Arena* arena;
LogScope* top_scope;
};
#ifndef LOG_DEFAULT_ARENA_BLOCK_SIZE
#define LOG_DEFAULT_ARENA_BLOCK_SIZE VARENA_DEFAULT_RESERVE
#endif
////////////////////////////////
//~ rjf: Log Creation/Selection
Log* log_alloc(AllocatorInfo ainfo, U64 arena_block_size);
void log_release(Log* log);
MD_API void log_select (Log* log);
inline Log*
log_alloc(AllocatorInfo ainfo, U64 arena_block_size) {
if (arena_block_size == 0) {
arena_block_size = LOG_DEFAULT_ARENA_BLOCK_SIZE;
}
Arena* arena = arena_alloc(.backing = ainfo, .block_size = arena_block_size);
Log* log = push_array(arena, Log, 1);
log->arena = arena;
return log;
}
inline void log_release(Log* log) { arena_release(log->arena); }
////////////////////////////////
//~ rjf: Log Building
MD_API void log_msg (LogMsgKind kind, String8 string);
MD_API void log_msgf(LogMsgKind kind, char* fmt, ...);
#define log_info(s) log_msg (LogMsgKind_Info, (s))
#define log_infof(fmt, ...) log_msgf(LogMsgKind_Info, (fmt), __VA_ARGS__)
#define log_user_error(s) log_msg (LogMsgKind_UserError, (s))
#define log_user_errorf(fmt, ...) log_msgf(LogMsgKind_UserError, (fmt), __VA_ARGS__)
#define log_info_named_block(s) defer_loop(log_infof("%S:\n{\n", (s)), log_infof("}\n"))
#define log_info_named_blockf(fmt, ...) defer_loop((log_infof(fmt, __VA_ARGS__), log_infof(":\n{\n")), log_infof("}\n"))
////////////////////////////////
//~ rjf: Log Scopes
void log_scope_begin(void);
MD_API LogScopeResult log_scope_end(Arena* arena);
+187
View File
@@ -0,0 +1,187 @@
#ifdef INTELLISENSE_DIRECTIVES
# pragma once
# include "context_cracking.h"
# include "linkage.h"
#endif
////////////////////////////////
//~ rjf: Branch Predictor Hints
#if COMPILER_CLANG
# define expect(expr, val) __builtin_expect((expr), (val))
#else
# define expect(expr, val) (expr)
#endif
#define likely(expr) expect(expr, 1)
#define unlikely(expr) expect(expr, 0)
////////////////////////////////
//~ erg: type casting
#if LANG_CPP
# ifndef ccast
# define ccast( type, value ) ( const_cast< type >( (value) ) )
# endif
# ifndef pcast
# define pcast( type, value ) ( * reinterpret_cast< type* >( & ( value ) ) )
# endif
# ifndef rcast
# define rcast( type, value ) reinterpret_cast< type >( value )
# endif
# ifndef scast
# define scast( type, value ) static_cast< type >( value )
# endif
#else
# ifndef ccast
# define ccast( type, value ) ( (type)(value) )
# endif
# ifndef pcast
# define pcast( type, value ) ( * (type*)(& value) )
# endif
# ifndef rcast
# define rcast( type, value ) ( (type)(value) )
# endif
# ifndef scast
# define scast( type, value ) ( (type)(value) )
# endif
#endif
#if ! defined(typeof) && ( ! LANG_C || __STDC_VERSION__ < 202311L)
# if ! LANG_C
# define typeof decltype
# elif defined(_MSC_VER)
# define typeof __typeof__
# elif defined(__GNUC__) || defined(__clang__)
# define typeof __typeof__
# else
# error "Compiler not supported"
# endif
#endif
#if LANG_C
# if __STDC_VERSION__ >= 202311L
# define enum_underlying(type) : type
# else
# define enum_underlying(type)
# endif
#else
# define enum_underlying(type) : type
#endif
#if LANG_C
# ifndef nullptr
# define nullptr NULL
# endif
# ifndef MD_REMOVE_PTR
# define MD_REMOVE_PTR(type) typeof(* ( (type) NULL) )
# endif
#endif
#if ! defined(PARAM_DEFAULT) && LANG_CPP
# define PARAM_DEFAULT = {}
#else
# define PARAM_DEFAULT
#endif
#if LANG_C
# ifndef static_assert
# undef static_assert
# if __STDC_VERSION__ >= 201112L
# define static_assert(condition, message) _Static_assert(condition, message)
# else
# define static_assert(condition, message) typedef char static_assertion_##__LINE__[(condition)?1:-1]
# endif
# endif
#endif
#ifndef force_inline
# if COMPILER_MSVC
# define force_inline __forceinline
# elif COMPILER_GCC
# define force_inline inline __attribute__((__always_inline__))
# elif COMPILER_CLANG
# if __has_attribute(__always_inline__)
# define force_inline inline __attribute__((__always_inline__))
# else
# define force_inline
# endif
# else
# define force_inline
# endif
#endif
#ifndef never_inline
# if COMPILER_MSVC
# define never_inline __declspec( noinline )
# elif COMPILER_GCC
# define never_inline __attribute__( ( __noinline__ ) )
# elif COMPILER_CLANG
# if __has_attribute(__always_inline__)
# define never_inline __attribute__( ( __noinline__ ) )
# else
# define never_inline
# endif
# else
# define never_inline
# endif
#endif
////////////////////////////////
//~ rjf: For-Loop Construct Macros
#ifndef defer_loop
#define defer_loop(begin, end) for (int _i_ = ((begin), 0); ! _i_; _i_ += 1, (end))
#endif
#ifndef defer_loop_checked
#define defer_loop_checked(begin, end) for (int _i_ = 2 * ! (begin); (_i_ == 2 ? ((end), 0) : !_i_); _i_ += 1, (end))
#endif
#ifndef each_enum_val
#define each_enum_val(type, it) type it = (type) 0; it < type ## _COUNT; it = (type)( it + 1 )
#endif
#ifndef each_non_zero_enum_val
#define each_non_zero_enum_val(type, it) type it = (type) 1; it < type ## _COUNT; it = (type)( it + 1 )
#endif
#ifndef stringify
#define stringify_(S) #S
#define stringify(S) stringify_(S)
#endif
#ifndef glue
#define glue_(A, B) A ## B
#define glue(A, B) glue_(A,B)
#endif
#define src_line_str stringify(__LINE__)
#ifndef do_once
#define do_once() \
local_persist int __do_once_counter_##src_line_str = 0; \
for(; __do_once_counter_##src_line_str != 1; __do_once_counter_##src_line_str = 1 ) \
#define do_once_defer( expression ) \
local_persist int __do_once_counter_##src_line_str = 0; \
for(;__do_once_counter_##src_line_str != 1; __do_once_counter_##src_line_str = 1, (expression)) \
#define do_once_start \
do \
{ \
local_persist \
bool done = false; \
if ( done ) \
break; \
done = true;
#define do_once_end \
} \
while(0);
#endif
#define ct_if(expr, then, else) _Generic( \
(&(char[1 + !!(EXPR)]){0}), \
char (*)[2]: (THEN), \
char (*)[1]: (ELSE) \
)
+10
View File
@@ -0,0 +1,10 @@
#ifdef INTELLISENSE_DIRECTIVES
# pragma once
# include "markup.h"
#endif
inline void
set_thread_name(String8 string) {
prof_thread_name("%.*s", str8_varg(string));
os_set_thread_name(string);
}
+28
View File
@@ -0,0 +1,28 @@
#ifdef INTELLISENSE_DIRECTIVES
# pragma once
# include "linkage.h"
# include "strings.h"
# include "thread_context.h"
#endif
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
MD_API void set_thread_name(String8 string);
inline void
set_thread_namef(char *fmt, ...)
{
TempArena scratch = scratch_begin(0, 0);
va_list args;
va_start(args, fmt);
String8 string = str8fv(scratch.arena, fmt, args);
set_thread_name(string);
va_end(args);
scratch_end(scratch);
}
#define thread_namef(...) (set_thread_namef(__VA_ARGS__))
#define thread_name(str) (set_thread_name(str))
+1001
View File
File diff suppressed because it is too large Load Diff
+993
View File
@@ -0,0 +1,993 @@
#ifdef INTELLISENSE_DIRECTIVES
# pragma once
# include "context_cracking.h"
# include "linkage.h"
# include "macros.h"
# include "platform.h"
# include "base_types.h"
#endif
////////////////////////////////
//~ rjf: Units
#ifndef KILOBTYES
#define KILOBYTES( x ) ( ( x ) * ( S64 )( 1024 ) )
#endif
#ifndef MEGABYTES
#define MEGABYTES( x ) ( MD_KILOBYTES( x ) * ( S64 )( 1024 ) )
#endif
#ifndef GIGABYTES
#define GIGABYTES( x ) ( MD_MEGABYTES( x ) * ( S64 )( 1024 ) )
#endif
#ifndef TERABYTES
#define TERABYTES( x ) ( MD_GIGABYTES( x ) * ( S64 )( 1024 ) )
#endif
#ifndef KB
#define KB(n) (((U64)(n)) << 10)
#endif
#ifndef MB
#define MB(n) (((U64)(n)) << 20)
#endif
#ifndef GB
#define GB(n) (((U64)(n)) << 30)
#endif
#ifndef TB
#define TB(n) (((U64)(n)) << 40)
#endif
#ifndef thosuand
#define thousand(n) ((n) * 1000)
#endif
#ifndef million
#define million(n) ((n) * 1000000)
#endif
#ifndef billion
#define billion(n) ((n) * 1000000000)
#endif
////////////////////////////////
//~ rjf: Type -> Alignment
#ifndef align_of
# if COMPILER_MSVC
# define align_of(T) __alignof(T)
# elif COMPILER_CLANG
# define align_of(T) __alignof(T)
# elif COMPILER_GCC
# define align_of(T) __alignof__(T)
# else
# error AlignOf not defined for this compiler.
# endif
#endif
////////////////////////////////
//~ rjf: Member Offsets
#ifndef member
#define member(T, m) ( ((T*) 0)->m )
#endif
#ifndef offset_of
#define offset_of(T, m) int_from_ptr(& member(T, m))
#endif
#ifndef member_from_offset
#define member_from_offset(T, ptr, off) (T) ((((U8 *) ptr) + (off)))
#endif
#ifndef cast_from_member
#define cast_from_member(T, m, ptr) (T*) (((U8*)ptr) - offset_of(T, m))
#endif
////////////////////////////////
//~ rjf: Memory Operation Macros
// TODO(Ed): Review usage of memmove here...(I guess wanting to avoid overlap faults..)
#ifndef memory_copy
# if OS_WINDOWS
void* memcpy_intrinsic(void* dest, const void* src, size_t count)
{
if (dest == NULL || src == NULL || count == 0) {
return NULL;
}
__movsb((unsigned char*)dest, (const unsigned char*)src, count);
return dest;
}
# define memory_copy(dst, src, size) memcpy_intrinsic((dst), (src), (size))
# else
# define memory_copy(dst, src, size) memmove((dst), (src), (size))
# endif
#endif
#ifndef memory_set
# if USE_VENDOR_MEMORY_OPS
# define memory_set(dst, byte, size) memset((dst), (byte), (size))
# else
# define memory_set(dst, byte, size) mem_set((dst), (byte), (size))
# endif
#endif
#ifndef memory_compare
#define memory_compare(a, b, size) memcmp((a), (b), (size))
#endif
#ifndef memory_str_len
#define memory_str_len(ptr) cstr_len(ptr)
#endif
#ifndef memory_copy_struct
#define memory_copy_struct(d, s) memory_copy((d), (s), sizeof( *(d)))
#endif
#ifndef memory_copy_array
#define memory_copy_array(d, s) memory_copy((d), (s), sizeof( d))
#endif
#ifndef memory_copy_type
#define memory_copy_type(d, s, c) memory_copy((d), (s), sizeof( *(d)) * (c))
#endif
#ifndef memory_zero
#define memory_zero(s,z) memory_set((s), 0, (z))
#endif
#ifndef memory_zero_struct
#define memory_zero_struct(s) memory_zero((s), sizeof( *(s)))
#endif
#ifndef memory_zero_array
#define memory_zero_array(a) memory_zero((a), sizeof(a))
#endif
#ifndef memory_zero_type
#define memory_zero_type(m, c) memory_zero((m), sizeof( *(m)) * (c))
#endif
#ifndef memory_match
#define memory_match(a, b, z) (memory_compare((a), (b), (z)) == 0)
#endif
#ifndef memory_match_struct
#define memory_match_struct(a, b) memory_match((a), (b), sizeof(*(a)))
#endif
#ifndef memory_match_array
#define memory_match_array(a, b) memory_match((a), (b), sizeof(a))
#endif
#ifndef memory_read
#define memory_read(T, p, e) ( ((p) + sizeof(T) <= (e)) ? ( *(T*)(p)) : (0) )
#endif
#ifndef memory_consume
#define memory_consume(T, p, e) ( ((p) + sizeof(T) <= (e)) ? ((p) += sizeof(T), *(T*)((p) - sizeof(T))) : ((p) = (e),0) )
#endif
////////////////////////////////
//~ rjf: Memory Functions
inline B32
memory_is_zero(void* ptr, U64 size)
{
B32 result = 1;
// break down size
U64 extra = (size & 0x7);
U64 count8 = (size >> 3);
// check with 8-byte stride
U64* p64 = (U64*)ptr;
if (result)
{
for (U64 i = 0; i < count8; i += 1, p64 += 1) {
if (*p64 != 0){
result = 0;
goto done;
}
}
}
// check extra
if (result)
{
U8* p8 = (U8*)p64;
for (U64 i = 0; i < extra; i += 1, p8 += 1) {
if (*p8 != 0) {
result = 0;
goto done;
}
}
}
done:;
return(result);
}
inline
void* mem_move( void* destination, void const* source, SSIZE byte_count )
{
if ( destination == NULL )
{
return NULL;
}
U8* dest_ptr = rcast( U8*, destination);
U8 const* src_ptr = rcast( U8 const*, source);
if ( dest_ptr == src_ptr )
return dest_ptr;
// NOTE: Non-overlapping
if ( src_ptr + byte_count <= dest_ptr || dest_ptr + byte_count <= src_ptr ) {
return memory_copy( dest_ptr, src_ptr, byte_count );
}
if ( dest_ptr < src_ptr )
{
if ( scast(UPTR, src_ptr) % size_of( SSIZE ) == scast(UPTR, dest_ptr) % size_of( SSIZE ) )
{
while ( pcast( UPTR, dest_ptr) % size_of( SSIZE ) )
{
if ( ! byte_count-- )
return destination;
*dest_ptr++ = *src_ptr++;
}
while ( byte_count >= size_of( SSIZE ) )
{
* rcast(SSIZE*, dest_ptr) = * rcast(SSIZE const*, src_ptr);
byte_count -= size_of( SSIZE );
dest_ptr += size_of( SSIZE );
src_ptr += size_of( SSIZE );
}
}
for ( ; byte_count; byte_count-- )
*dest_ptr++ = *src_ptr++;
}
else
{
if ( ( scast(UPTR, src_ptr) % size_of( SSIZE ) ) == ( scast(UPTR, dest_ptr) % size_of( SSIZE ) ) )
{
while ( scast(UPTR, dest_ptr + byte_count ) % size_of( SSIZE ) )
{
if ( ! byte_count-- )
return destination;
dest_ptr[ byte_count ] = src_ptr[ byte_count ];
}
while ( byte_count >= size_of( SSIZE ) )
{
byte_count -= size_of( SSIZE );
* rcast(SSIZE*, dest_ptr + byte_count ) = * rcast( SSIZE const*, src_ptr + byte_count );
}
}
while ( byte_count )
byte_count--, dest_ptr[ byte_count ] = src_ptr[ byte_count ];
}
return destination;
}
inline
void* mem_set( void* destination, U8 fill_byte, SSIZE byte_count )
{
if ( destination == NULL )
{
return NULL;
}
SSIZE align_offset;
U8* dest_ptr = rcast( U8*, destination);
U32 fill_word = ( ( U32 )-1 ) / 255 * fill_byte;
if ( byte_count == 0 )
return destination;
dest_ptr[ 0 ] = dest_ptr[ byte_count - 1 ] = fill_byte;
if ( byte_count < 3 )
return destination;
dest_ptr[ 1 ] = dest_ptr[ byte_count - 2 ] = fill_byte;
dest_ptr[ 2 ] = dest_ptr[ byte_count - 3 ] = fill_byte;
if ( byte_count < 7 )
return destination;
dest_ptr[ 3 ] = dest_ptr[ byte_count - 4 ] = fill_byte;
if ( byte_count < 9 )
return destination;
align_offset = -scast(SPTR, dest_ptr ) & 3;
dest_ptr += align_offset;
byte_count -= align_offset;
byte_count &= -4;
* rcast( U32*, ( dest_ptr + 0 ) ) = fill_word;
* rcast( U32*, ( dest_ptr + byte_count - 4 ) ) = fill_word;
if ( byte_count < 9 )
return destination;
* rcast( U32*, dest_ptr + 4 ) = fill_word;
* rcast( U32*, dest_ptr + 8 ) = fill_word;
* rcast( U32*, dest_ptr + byte_count - 12 ) = fill_word;
* rcast( U32*, dest_ptr + byte_count - 8 ) = fill_word;
if ( byte_count < 25 )
return destination;
* rcast( U32*, dest_ptr + 12 ) = fill_word;
* rcast( U32*, dest_ptr + 16 ) = fill_word;
* rcast( U32*, dest_ptr + 20 ) = fill_word;
* rcast( U32*, dest_ptr + 24 ) = fill_word;
* rcast( U32*, dest_ptr + byte_count - 28 ) = fill_word;
* rcast( U32*, dest_ptr + byte_count - 24 ) = fill_word;
* rcast( U32*, dest_ptr + byte_count - 20 ) = fill_word;
* rcast( U32*, dest_ptr + byte_count - 16 ) = fill_word;
align_offset = 24 + scast(UPTR, dest_ptr ) & 4;
dest_ptr += align_offset;
byte_count -= align_offset;
{
U64 fill_doubleword = ( scast( U64, fill_word) << 32 ) | fill_word;
while ( byte_count > 31 )
{
* rcast( U64*, dest_ptr + 0 ) = fill_doubleword;
* rcast( U64*, dest_ptr + 8 ) = fill_doubleword;
* rcast( U64*, dest_ptr + 16 ) = fill_doubleword;
* rcast( U64*, dest_ptr + 24 ) = fill_doubleword;
byte_count -= 32;
dest_ptr += 32;
}
}
return destination;
}
////////////////////////////////
//~ rjf: Atomic Operations
#ifndef ins_atomic_u64_eval
# if OS_WINDOWS
# if ARCH_X64
# define ins_atomic_u64_eval(x) InterlockedAdd64((volatile __int64 *)(x), 0)
# define ins_atomic_u64_inc_eval(x) InterlockedIncrement64((volatile __int64 *)(x))
# define ins_atomic_u64_dec_eval(x) InterlockedDecrement64((volatile __int64 *)(x))
# define ins_atomic_u64_eval_assign(x,c) InterlockedExchange64((volatile __int64 *)(x), (c))
# define ins_atomic_u64_add_eval(x,c) InterlockedAdd64((volatile __int64 *)(x), c)
# define ins_atomic_u64_eval_cond_assign(x,k,c) InterlockedCompareExchange64((volatile __int64 *)(x), (k), (c))
# define ins_atomic_u32_eval(x,c) InterlockedAdd((volatile LONG *)(x), 0)
# define ins_atomic_u32_eval_assign(x,c) InterlockedExchange((volatile LONG *)(x), (c))
# define ins_atomic_u32_eval_cond_assign(x,k,c) InterlockedCompareExchange((volatile LONG *)(x), (k), (c))
# define ins_atomic_ptr_eval_assign(x,c) (void*) ins_atomic_u64_eval_assign((volatile __int64 *)(x), (__int64)(c))
# else
# error Atomic intrinsics not defined for this operating system / architecture combination.
# endif
# elif OS_LINUX
# if ARCH_X64
# define ins_atomic_u64_inc_eval(x) __sync_fetch_and_add((volatile U64 *)(x), 1)
# else
# error Atomic intrinsics not defined for this operating system / architecture combination.
# endif
# else
# error Atomic intrinsics not defined for this operating system.
# endif
#endif
////////////////////////////////
//~ rjf: Linked List Building Macros
//- rjf: linked list macro helpers
#ifndef check_nil
#define check_nil(nil, p) ((p) == 0 || (p) == nil)
#endif
#ifndef set_nil
#define set_nil(nil, p) ((p) = nil)
#endif
//- rjf: doubly-linked-lists
#ifndef MD_LINKED_LIST_PURE_MACRO
#define MD_LINKED_LIST_PURE_MACRO 0
#endif
#ifndef dll_insert_npz
// TODO(Ed): Review...
inline void
dll__insert_npz(
void* nil,
void** f, void** f_prev,
void** l, void** l_next,
void* p, void** p_next, void** p_next_prev,
void* n, void** n_prev, void** n_next
)
{
if (check_nil(nil, *f)) {
*f = n;
*l = n;
*n_prev = nil;
*n_next = nil;
}
else
{
if (check_nil(nil, p)) {
*n_next = *f;
*f_prev = n;
*f = n;
*n_prev = nil;
}
else
{
if (p == *l) {
*l_next = n;
*n_prev = *l;
*l = n;
*n_next = nil;
}
else
{
if ( ! check_nil(nil, p) && check_nil(nil, *p_next)) {
*p_next_prev = n;
}
*n_next = *p_next;
*p_next = n;
*n_prev = p;
}
}
}
}
#if ! MD_LINKED_LIST_PURE_MACRO
// insert next-previous with nil
#define dll_insert_npz(nil, f, l, p, n, next, prev) dll__insert_npz(nil, &f, &f->prev, &l, &l->next, p, &p->next, &p->next->prev, n, &n->prev, &n->next)
#else
// insert next-previous with nil
#define dll_insert_npz(nil, f, l, p, n, next, prev) \
( \
check_nil(nil, f) ? ( \
(f) = (l) = (n), \
set_nil(nil, (n)->next), \
set_nil(nil, (n)->prev) \
) \
: ( \
check_nil(nil, p) ? ( \
(n)->next = (f), \
(f)->prev = (n), \
(f) = (n), \
set_nil(nil,(n)->prev) \
) \
: ((p) == (l)) ? ( \
(l)->next = (n), \
(n)->prev = (l), \
(l) = (n), \
set_nil(nil, (n)->next) \
) \
: ( \
( \
( ! check_nil(nil, p) && check_nil(nil, (p)->next) ) ? \
(0) \
: ( (p)->next->prev = (n) ) \
), \
((n)->next = (p)->next), \
((p)->next = (n)), \
((n)->prev = (p)) \
) \
) \
)
// ! MD_LINKED_LIST_PURE_MACRO
#endif
// dll_insert_npz
#endif
#ifndef dll_push_back_npz
// push-back next-previous with nil
#define dll_push_back_npz(nil, f, l, n, next, prev) dll_insert_npz(nil, f, l, l, n, next, prev)
#endif
#ifndef dll_push_front_npz
// push-fornt next-previous with nil
#define dll_push_front_npz(nil, f, l, n, next, prev) dll_insert_npz(nil, l, f, f, n, prev, next)
#endif
#ifndef dll_remove_npz
inline void
dll__remove_npz(
void* nil,
void** f,
void** l, void* l_prev,
void* n, void* n_next, void** n_next_prev,
void* n_prev, void** n_prev_next
)
{
if (n == *f) {
*f = n_next;
}
if (n == *l) {
*l = l_prev;
}
if (check_nil(nil, n_prev)) {
*n_prev_next = n_next;
}
if (! check_nil(nil, n_next)) {
*n_next_prev = n_prev;
}
}
#if ! MD_LINKED_LIST_PURE_MACRO
// remove next-previous with nil
#define dll_remove_npz(nil, f, l, n, next, prev) dll__remove_npz(nil, &f, &l, l->prev, n, n->next, &n->next->prev, n->prev, &n->prev->next)
#else
// remove next-previous with nil
#define dll_remove_npz(nil, f, l, n, next, prev) \
( \
( \
(n) == (f) ? \
(f) = (n)->next \
: (0) \
), \
( \
(n) == (l) ? \
(l) = (l)->prev \
: (0) \
), \
( \
check_nil(nil,(n)->prev) ? \
(0) \
: ((n)->prev->next = (n)->next) \
), \
( \
check_nil(nil,(n)->next) ? \
(0) \
: ((n)->next->prev = (n)->prev) \
) \
)
// ! MD_LINKED_LIST_PURE_MACRO
#endif
// dll_remove_npz
#endif
//- rjf: singly-linked, doubly-headed lists (queues)
#ifndef sll_queue_push_nz
inline void
sll__queue_push_nz(
void* nil,
void** f,
void** l, void** l_next,
void* n, void** n_next
)
{
if (check_nil(nil, *f)) {
*f = n;
*l = n;
*n_next = nil;
}
else {
*l_next = n;
*l = n;
*n_next = nil;
}
}
// queue-push next with nil
#if ! MD_LINKED_LIST_PURE_MACRO
#define sll_queue_push_nz(nil, f, l, n, next) sll__queue_push_nz(nil, &f, &l, &l->next, n, &n->next)
#else
#define sll_queue_push_nz(nil, f, l, n, next) \
( \
check_nil(nil, f) ? ( \
(f) = (l) = (n), \
set_nil(nil, (n)->next) \
) \
: ( \
(l)->next=(n), \
(l) = (n), \
set_nil(nil,(n)->next) \
) \
)
// ! MD_LINKED_LIST_PURE_MACRO
#endif
// sll_queue_push_nz
#endif
#ifndef sll_queue_push_front_nz
inline void
sll__queue_push_front_nz(void* nil, void** f, void** l, void* n, void** n_next) {
if (check_nil(nil, *f)) {
*f = n;
*l = n;
*n_next = nil;
}
else {
*n_next = f;
*f = n;
}
}
// queue-push-front next with nil
#if ! MD_LINKED_LIST_PURE_MACRO
#define sll_queue_push_front_nz(nil, f, l, n, next) sll__queue_push_front_nz(nil, &f, &l, n, &n->next)
#else
#define sll_queue_push_front_nz(nil, f, l, n, next) \
( \
check_nil(nil, f) ? ( \
(f) = (l) = (n), \
set_nil(nil,(n)->next) \
) \
: ( \
(n)->next = (f), \
(f) = (n) \
) \
)
// ! MD_LINKED_LIST_PURE_MACRO
#endif
#endif
#ifndef sll_queue_pop_nz
inline void
sll__queue_pop_nz(void* nil, void** f, void* f_next, void** l)
{
if (*f == *l) {
*f = nil;
*l = nil;
}
else {
*f = f_next;
}
}
// queue-pop next with nil
#if ! MD_LINKED_LIST_PURE_MACRO
#define sll_queue_pop_nz(nil, f, l, next) sll__queue_pop_nz(nil, &f, f->next, &l)
#else
#define sll_queue_pop_nz(nil, f, l, next) \
( \
(f) == (l) ? ( \
set_nil(nil,f), \
set_nil(nil,l) \
) \
: ( \
(f)=(f)->next \
) \
)
// ! MD_LINKED_LIST_PURE_MACRO
#endif
// sll_queue_pop_nz
#endif
//- rjf: singly-linked, singly-headed lists (stacks)
#ifndef sll_stack_push_n
#define sll_stack_push_n(f,n,next) ( (n)->next = (f), (f) = (n) )
#endif
#ifndef sll_stack_pop_n
#define sll_stack_pop_n(f,next) ( (f) = (f)->next )
#endif
//- rjf: doubly-linked-list helpers
#ifndef dll_insert_np
#define dll_insert_np(f, l, p, n, next, prev) dll_insert_npz (0, f, l, p, n, next, prev)
#endif
#ifndef dll_push_back_np
#define dll_push_back_np(f, l, n, next, prev) dll_push_back_npz (0, f, l, n, next, prev)
#endif
#ifndef dll_push_front_np
#define dll_push_front_np(f, l, n, next, prev) dll_push_front_npz(0, f, l, n, next, prev)
#endif
#ifndef dll_remove_np
#define dll_remove_np(f, l, n, next, prev) dll_remove_npz (0, f, l, n, next, prev)
#endif
#ifndef dll_insert
#define dll_insert(f, l, p, n) dll_insert_npz (0, f, l, p, n, next, prev)
#endif
#ifndef dll_push_back
#define dll_push_back(f, l, n) dll_push_back_npz (0, f, l, n, next, prev)
#endif
#ifndef dll_push_front
#define dll_push_front(f, l, n) dll_push_front_npz(0, f, l, n, next, prev)
#endif
#ifndef dll_remove
#define dll_remove(f, l, n) dll_remove_npz (0, f, l, n, next, prev)
#endif
//- rjf: singly-linked, doubly-headed list helpers
#ifndef sll_queue_push_n
#define sll_queue_push_n(f, l, n, next) sll_queue_push_nz (0, f, l, n, next)
#endif
#ifndef sll_queue_push_front_n
#define sll_queue_push_front_n(f, l, n, next) sll_queue_push_front_nz(0, f, l, n, next)
#endif
#ifndef sll_queue_pop_n
#define sll_queue_pop_n(f, l, next) sll_queue_pop_nzs (0, f, l, next)
#endif
#ifndef sll_queue_push
#define sll_queue_push(f, l, n) sll_queue_push_nz (0, f, l, n, next)
#endif
#ifndef sll_queue_push_front
#define sll_queue_push_front(f, l ,n) sll_queue_push_front_nz(0, f, l, n, next)
#endif
#ifndef sll_queue_pop
#define sll_queue_pop(f, l) sll_queue_pop_nz (0, f, l, next)
#endif
//- rjf: singly-linked, singly-headed list helpers
#ifndef sll_stack_push
#define sll_stack_push(f, n) sll_stack_push_n(f, n, next)
#endif
#ifndef sll_stack_pop
#define sll_stack_pop(f) sll_stack_pop_n (f, next)
#endif
////////////////////////////////
//~ rjf: Address Sanitizer Markup
#ifndef NO_ASAN
# if COMPILER_MSVC
# if defined(__SANITIZE_ADDRESS__)
# define ASAN_ENABLED 1
# define NO_ASAN __declspec(no_sanitize_address)
# else
# define NO_ASAN
# endif
# elif COMPILER_CLANG
# if defined(__has_feature)
# if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__)
# define ASAN_ENABLED 1
# endif
# endif
# define NO_ASAN __attribute__((no_sanitize("address")))
# else
# define NO_ASAN
# endif
#endif
#ifndef asan_poison_memory_region
# if MD_ASAN_ENABLED
# pragma comment(lib, "clang_rt.asan-x86_64.lib")
MD_C_API void __asan_poison_memory_region(void const volatile *addr, size_t size);
MD_C_API void __asan_unpoison_memory_region(void const volatile *addr, size_t size);
# define asan_poison_memory_region(addr, size) __asan_poison_memory_region((addr), (size))
# define asan_unpoison_memory_region(addr, size) __asan_unpoison_memory_region((addr), (size))
# else
# define asan_poison_memory_region(addr, size) ((void)(addr), (void)(size))
# define asan_unpoison_memory_region(addr, size) ((void)(addr), (void)(size))
# endif
#endif
////////////////////////////////
//~ rjf: Misc. Helper Macros
#ifndef array_count
#define array_count(a) (sizeof(a) / sizeof((a)[0]))
#endif
#ifndef ceil_integer_div
#define ceil_integer_div(a,b) (((a) + (b) - 1) / (b))
#endif
#ifndef swap
#define swap(T, a, b) do { T t__ = a; a = b; b = t__; } while(0)
#endif
#ifndef int_from_ptr
# if ARCH_64BIT
# define int_from_ptr(ptr) ((U64)(ptr))
# elif ARCH_32BIT
# define int_from_ptr(ptr) ((U32)(ptr))
# else
# error Missing pointer-to-integer cast for this architecture.
# endif
#endif
#ifndef ptr_from_int
#define ptr_from_int(i) (void*)((U8*)0 + (i))
#endif
#ifndef compose_64bit
#define compose_64bit(a,b) ((((U64)a) << 32) | ((U64)b));
#endif
#ifndef align_pow2
#define align_pow2(x,b) (((x) + (b) - 1) & ( ~((b) - 1)))
#endif
#ifndef align_down_pow2
#define align_down_pow2(x,b) ((x) & (~((b) - 1)))
#endif
#ifndef align_pad_pow2
#define align_pad_pow2(x,b) ((0-(x)) & ((b) - 1))
#endif
#ifndef is_pow2
#define is_pow2(x) ((x) != 0 && ((x ) & ((x) - 1)) == 0)
#endif
#ifndef is_pow2_or_zero
#define is_pow2_or_zero(x) ((((x) - 1) & (x)) == 0)
#endif
#ifndef extract_bit
#define extract_bit(word, idx) (((word) >> (idx)) & 1)
#endif
#ifndef zero_struct
# if LANG_CPP
# define zero_struct {}
# else
# define zero_struct {0}
# endif
#endif
#ifndef this_function_name
# if COMPILER_MSVC && COMPILER_MSVC_YEAR < 2015
# define this_function_name "unknown"
# else
# define this_function_name __func__
# endif
#endif
#ifndef read_only
# if COMPILER_MSVC || (COMPILER_CLANG && OS_WINDOWS)
# pragma section(".rdata$", read)
# define read_only __declspec(allocate(".rdata$"))
# elif (COMPILER_CLANG && OS_LINUX)
# define read_only __attribute__((section(".rodata")))
# else
// NOTE(rjf): I don't know of a useful way to do this in GCC land.
// __attribute__((section(".rodata"))) looked promising, but it introduces a
// strange warning about malformed section attributes, and it doesn't look
// like writing to that section reliably produces access violations, strangely
// enough. (It does on Clang)
# define read_only
# endif
#endif
#ifndef local_persist
#define local_persist static
#endif
#if COMPILER_MSVC
# define thread_static __declspec(thread)
#elif COMPILER_CLANG || COMPILER_GCC
# define thread_static __thread
#endif
#if COMPILER_CPP
// Already Defined
#elif COMPILER_C && __STDC_VERSION__ >= 201112L
# define thread_local _Thread_local
#elif COMPILER_MSVC
# define thread_local __declspec(thread)
#elif COMPILER_CLANG
# define thread_local __thread
#else
# error "No thread local support"
#endif
////////////////////////////////
//~ rjf: Safe Casts
inline U16
safe_cast_u16(U32 x) {
assert_always(x <= MAX_U16);
U16 result = (U16)x;
return result;
}
inline U32
safe_cast_u32(U64 x) {
assert_always(x <= MAX_U32);
U32 result = (U32)x;
return result;
}
inline S32
safe_cast_s32(S64 x) {
assert_always(x <= MAX_S32);
S32 result = (S32)x;
return result;
}
////////////////////////////////
//~ rjf: Large Base Type Functions
inline U128 u128_zero (void) { U128 v = {0}; return v; }
inline U128 u128_make (U64 v0, U64 v1) { U128 v = {v0, v1}; return v; }
inline B32 u128_match(U128 a, U128 b) { return memory_match_struct(&a, &b); }
////////////////////////////////
//~ rjf: Bit Patterns
inline U32 u32_from_u64_saturate(U64 x) { U32 x32 = (x > MAX_U32) ? MAX_U32 : (U32)x; return(x32); }
inline U64
u64_up_to_pow2(U64 x) {
if (x == 0) {
x = 1;
}
else {
x -= 1;
x |= (x >> 1);
x |= (x >> 2);
x |= (x >> 4);
x |= (x >> 8);
x |= (x >> 16);
x |= (x >> 32);
x += 1;
}
return(x);
}
inline S32
extend_sign32(U32 x, U32 size) {
U32 high_bit = size * 8;
U32 shift = 32 - high_bit;
S32 result = ((S32)x << shift) >> shift;
return result;
}
inline S64
extend_sign64(U64 x, U64 size) {
U64 high_bit = size * 8;
U64 shift = 64 - high_bit;
S64 result = ((S64)x << shift) >> shift;
return result;
}
inline F32 inf32 (void) { union { U32 u; F32 f; } x; x.u = EXPONENT32; return(x.f); }
inline F32 neg_inf32(void) { union { U32 u; F32 f; } x; x.u = SIGN32 | EXPONENT32; return(x.f); }
inline U16
bswap_u16(U16 x)
{
U16 result = (((x & 0xFF00) >> 8) |
((x & 0x00FF) << 8));
return result;
}
inline U32
bswap_u32(U32 x)
{
U32 result = (((x & 0xFF000000) >> 24) |
((x & 0x00FF0000) >> 8) |
((x & 0x0000FF00) << 8) |
((x & 0x000000FF) << 24));
return result;
}
inline U64
bswap_u64(U64 x)
{
// TODO(nick): naive bswap, replace with something that is faster like an intrinsic
U64 result = (((x & 0xFF00000000000000ULL) >> 56) |
((x & 0x00FF000000000000ULL) >> 40) |
((x & 0x0000FF0000000000ULL) >> 24) |
((x & 0x000000FF00000000ULL) >> 8) |
((x & 0x00000000FF000000ULL) << 8) |
((x & 0x0000000000FF0000ULL) << 24) |
((x & 0x000000000000FF00ULL) << 40) |
((x & 0x00000000000000FFULL) << 56));
return result;
}
#if ARCH_LITTLE_ENDIAN
# define from_be_u16(x) bswap_u16(x)
# define from_be_u32(x) bswap_u32(x)
# define from_be_u64(x) bswap_u64(x)
#else
# define from_be_u16(x) (x)
# define from_be_u32(x) (x)
# define from_be_u64(x) (x)
#endif
#if COMPILER_MSVC || (COMPILER_CLANG && OS_WINDOWS)
inline U64 count_bits_set16(U16 val) { return __popcnt16(val); }
inline U64 count_bits_set32(U32 val) { return __popcnt (val); }
inline U64 count_bits_set64(U64 val) { return __popcnt64(val); }
inline U64 ctz32(U32 mask) { unsigned long idx; _BitScanForward (&idx, mask); return idx; }
inline U64 ctz64(U64 mask) { unsigned long idx; _BitScanForward64(&idx, mask); return idx; }
inline U64 clz32(U32 mask) { unsigned long idx; _BitScanReverse (&idx, mask); return 31 - idx; }
inline U64 clz64(U64 mask) { unsigned long idx; _BitScanReverse64(&idx, mask); return 63 - idx; }
#elif COMPILER_CLANG || COMPILER_GCC
inline U64 count_bits_set16(U16 val) { NotImplemented; return 0; }
inline U64 count_bits_set32(U32 val) { NotImplemented; return 0; }
inline U64 count_bits_set64(U64 val) { NotImplemented; return 0; }
inline U64 ctz32(U32 val) { NotImplemented; return 0; }
inline U64 ctz64(U32 val) { NotImplemented; return 0; }
inline U64 clz32(U32 val) { NotImplemented; return 0; }
inline U64 clz64(U64 val) { NotImplemented; return 0; }
#else
# error "Bit intrinsic functions not defined for this compiler."
#endif
+486
View File
@@ -0,0 +1,486 @@
#ifdef INTELLISENSE_DIRECTIVES
# include "memory.h"
# include "memory_substrate.h"
# include "../os/os.h"
#endif
void*
default_resize_align( AllocatorInfo a, void* old_memory, SSIZE old_size, SSIZE new_size, SSIZE alignment )
{
if ( ! old_memory )
return alloc_align( a, new_size, alignment );
if ( new_size == 0 )
{
alloc_free( a, old_memory );
return nullptr;
}
if ( new_size < old_size )
new_size = old_size;
if ( old_size == new_size )
{
return old_memory;
}
else
{
void* new_memory = alloc_align( a, new_size, alignment );
if ( ! new_memory )
return nullptr;
mem_move( new_memory, old_memory, md_min( new_size, old_size ) );
alloc_free( a, old_memory );
return new_memory;
}
}
#ifdef MD_HEAP_ANALYSIS
#define GEN_HEAP_STATS_MAGIC 0xDEADC0DE
typedef struct _heap_stats _heap_stats;
struct _heap_stats
{
U32 magic;
SSIZE used_memory;
SSIZE alloc_count;
};
global _heap_stats _heap_stats_info;
void
heap_stats_init( void )
{
memory_zero_struct( &_heap_stats_info );
_heap_stats_info.magic = GEN_HEAP_STATS_MAGIC;
}
SSIZE
heap_stats_used_memory( void )
{
assert_msg( _heap_stats_info.magic == GEN_HEAP_STATS_MAGIC, "heap_stats is not initialised yet, call heap_stats_init first!" );
return _heap_stats_info.used_memory;
}
SSIZE
heap_stats_alloc_count( void )
{
assert_msg( _heap_stats_info.magic == GEN_HEAP_STATS_MAGIC, "heap_stats is not initialised yet, call heap_stats_init first!" );
return _heap_stats_info.alloc_count;
}
void
heap_stats_check( void )
{
assert_msg( _heap_stats_info.magic == GEN_HEAP_STATS_MAGIC, "heap_stats is not initialised yet, call heap_stats_init first!" );
assert( _heap_stats_info.used_memory == 0 );
assert( _heap_stats_info.alloc_count == 0 );
}
typedef struct _heap_alloc_info _heap_alloc_info;
struct _heap_alloc_info
{
SSIZE size;
void* physical_start;
};
#endif
void*
heap_allocator_proc( void* allocator_data, AllocatorMode mode, SSIZE size, SSIZE alignment, void* old_memory, SSIZE old_size, U64 flags )
{
void* ptr = nullptr;
// unused( allocator_data );
// unused( old_size );
if ( ! alignment )
alignment = MD_DEFAULT_MEMORY_ALIGNMENT;
#ifdef MD_HEAP_ANALYSIS
ssize alloc_info_size = size_of( _heap_alloc_info );
ssize alloc_info_remainder = ( alloc_info_size % alignment );
ssize track_size = max( alloc_info_size, alignment ) + alloc_info_remainder;
switch ( type )
{
case EAllocatorMode_FREE :
{
if ( ! old_memory )
break;
_heap_alloc_info* alloc_info = rcast( _heap_alloc_info*, old_memory) - 1;
_heap_stats_info.used_memory -= alloc_info->size;
_heap_stats_info.alloc_count--;
old_memory = alloc_info->physical_start;
}
break;
case EAllocatorMode_ALLOC :
{
size += track_size;
}
break;
default :
break;
}
#endif
switch ( mode )
{
#if defined( COMPILER_MSVC ) || ( defined( COMPILER_GCC ) && defined( OS_WINDOWS ) ) || ( defined( COMPILER_TINYC ) && defined( OS_WINDOWS ) )
case AllocatorMode_Alloc:
{
ptr = _aligned_malloc( size, alignment );
if ( flags & ALLOCATOR_FLAG_CLEAR_TO_ZERO )
memory_zero( ptr, size );
}
break;
case AllocatorMode_Free:
_aligned_free( old_memory );
break;
case AllocatorMode_Resize:
{
AllocatorInfo a = heap();
ptr = default_resize_align( a, old_memory, old_size, size, alignment );
}
break;
#elif defined( OS_LINUX ) && ! defined( CPU_ARM ) && ! defined( COMPILER_TINYC )
case EAllocation_ALLOC :
{
ptr = aligned_alloc( alignment, ( size + alignment - 1 ) & ~( alignment - 1 ) );
if ( flags & GEN_ALLOCATOR_FLAG_CLEAR_TO_ZERO )
{
zero_size( ptr, size );
}
}
break;
case EAllocation_Freee :
{
free( old_memory );
}
break;
case EAllocation_RESIZE :
{
AllocatorInfo a = heap();
ptr = default_resize_align( a, old_memory, old_size, size, alignment );
}
break;
#else
case EAllocType_ALLOC :
{
posix_memalign( &ptr, alignment, size );
if ( flags & ALLOCATOR_FLAG_CLEAR_TO_ZERO )
{
zero_size( ptr, size );
}
}
break;
case EAllocType_FREE :
{
free( old_memory );
}
break;
case EAllocType_RESIZE :
{
AllocatorInfo a = heap();
ptr = default_resize_align( a, old_memory, old_size, size, alignment );
}
break;
#endif
case AllocatorMode_FreeAll:
break;
case AllocatorMode_QueryType:
return (void*) AllocatorType_Heap;
case AllocatorMode_QuerySupport:
return (void*) (
AllocatorQuery_Alloc | AllocatorQuery_Free | AllocatorQuery_Resize | AllocatorQuery_ResizeGrow | AllocatorQuery_ResizeShrink
);
}
#ifdef GEN_HEAP_ANALYSIS
if ( type == AllocatorMode_Alloc )
{
_heap_alloc_info* alloc_info = rcast( _heap_alloc_info*, rcast( char*, ptr) + alloc_info_remainder );
zero_item( alloc_info );
alloc_info->size = size - track_size;
alloc_info->physical_start = ptr;
ptr = rcast( void*, alloc_info + 1 );
_heap_stats_info.used_memory += alloc_info->size;
_heap_stats_info.alloc_count++;
}
#endif
return ptr;
}
VArena*
varena__alloc(VArenaParams params)
{
if (params.reserve_size == 0) {
params.reserve_size = VARENA_DEFAULT_RESERVE;
}
if (params.commit_size == 0) {
params.commit_size = VARENA_DEFAULT_COMMIT;
}
// rjf: round up reserve/commit sizes
U64 reserve_size = params.reserve_size;
U64 commit_size = params.commit_size;
void* base = nullptr;
if (params.flags & VArenaFlag_LargePages)
{
reserve_size = align_pow2(reserve_size, os_get_system_info()->large_page_size);
commit_size = align_pow2(commit_size, os_get_system_info()->large_page_size);
base = os_reserve_large(reserve_size);
os_commit_large(base, commit_size);
asan_poison_memory_region(base, params.commit_size);
}
else
{
reserve_size = align_pow2(reserve_size, os_get_system_info()->page_size);
commit_size = align_pow2(commit_size, os_get_system_info()->page_size);
base = os_reserve(reserve_size);
B32 commit_result = os_commit(base, commit_size);
assert(commit_result == 1);
asan_poison_memory_region(base, params.commit_size);
}
// NOTE(Ed): Panic on varena creation failure
#if OS_FEATURE_GRAPHICAL
if(unlikely(base == 0))
{
os_graphical_message(1, str8_lit("Fatal Allocation Failure"), str8_lit("Unexpected memory allocation failure."));
os_abort(1);
}
#endif
SPTR header_size = align_pow2(size_of(VArena), MD_DEFAULT_MEMORY_ALIGNMENT);
asan_unpoison_memory_region(base, header_size);
VArena* vm = rcast(VArena*, base);
vm->reserve_start = rcast(SPTR, base) + header_size;
vm->reserve = reserve_size;
vm->commit_size = params.commit_size;
vm->committed = commit_size;
vm->commit_used = 0;
vm->flags = params.flags;
return vm;
}
void
varena_release(VArena* arena)
{
os_release(arena, arena->reserve);
arena = nullptr;
}
void*
varena_allocator_proc(void* allocator_data, AllocatorMode mode, SSIZE requested_size, SSIZE alignment, void* old_memory, SSIZE old_size, U64 flags)
{
OS_SystemInfo const* info = os_get_system_info();
VArena* vm = rcast(VArena*, allocator_data);
void* allocated_mem = nullptr;
switch (mode)
{
case AllocatorMode_Alloc:
{
assert_msg(requested_size != 0, "requested_size is 0");
requested_size = align_pow2(requested_size, alignment);
UPTR current_offset = vm->reserve_start + vm->commit_used;
UPTR size_to_allocate = requested_size;
UPTR to_be_used = vm->commit_used + size_to_allocate;
SPTR reserve_left = vm->reserve - vm->committed;
assert(to_be_used < reserve_left);
UPTR header_offset = vm->reserve_start - scast(UPTR, vm);
UPTR commit_left = vm->committed - vm->commit_used - header_offset;
B32 needs_more_commited = commit_left < size_to_allocate;
if (needs_more_commited)
{
UPTR next_commit_size;
if (vm->flags & VArenaFlag_LargePages) {
next_commit_size = reserve_left > 0 ? md_max(vm->commit_size, size_to_allocate) : scast(UPTR, align_pow2( abs(reserve_left), os_get_system_info()->large_page_size));
}
else {
next_commit_size = reserve_left > 0 ? md_max(vm->commit_size, size_to_allocate) : scast(UPTR, align_pow2(abs(reserve_left), os_get_system_info()->page_size));
}
if (next_commit_size) {
void* next_commit_start = rcast(void*, rcast(UPTR, vm) + vm->committed);
B32 commit_result = os_commit(next_commit_start, next_commit_size);
if (commit_result == false) {
break;
}
vm->committed += next_commit_size;
}
}
allocated_mem = rcast(void*, current_offset);
vm->commit_used += size_to_allocate;
}
break;
case AllocatorMode_Free:
{
}
break;
case AllocatorMode_FreeAll:
{
vm->commit_used = 0;
}
break;
case AllocatorMode_Resize:
{
assert(old_memory != nullptr);
assert(old_size > 0);
assert_msg(old_size == requested_size, "Requested resize when none needed");
requested_size = align_pow2(requested_size, alignment);
old_size = align_pow2(old_size, alignment);
UPTR old_memory_offset = scast(UPTR, old_memory) + scast(UPTR, old_size);
UPTR current_offset = scast(UPTR, vm->reserve_start) + scast(UPTR, vm->commit_used);
assert_msg(old_memory_offset == current_offset, "Cannot resize existing allocation in VArena unless it was the last allocated");
B32 requested_shrink = requested_size >= old_size;
if (requested_shrink) {
vm->commit_used -= rcast(UPTR, align_pow2(requested_size, alignment));
allocated_mem = old_memory;
break;
}
UPTR size_to_allocate = requested_size - old_size, alignment;
UPTR header_offset = vm->reserve_start - scast(UPTR, vm);
UPTR commit_left = vm->committed - vm->commit_used - header_offset;
B32 needs_more_commited = commit_left < size_to_allocate;
if (needs_more_commited)
{
SPTR reserve_left = vm->reserve - vm->committed;
UPTR next_commit_size;
if (vm->flags & VArenaFlag_LargePages) {
next_commit_size = reserve_left > 0 ? vm->commit_size : scast(UPTR, align_pow2( -reserve_left, os_get_system_info()->large_page_size));
}
else {
next_commit_size = reserve_left > 0 ? vm->commit_size : scast(UPTR, align_pow2(abs(reserve_left), os_get_system_info()->page_size));
}
if (next_commit_size) {
B32 commit_result = os_commit(vm, next_commit_size);
if (commit_result == false) {
break;
}
}
}
allocated_mem = old_memory;
vm->commit_used += size_to_allocate;
}
break;
case AllocatorMode_QueryType:
{
return (void*) AllocatorType_VArena;
}
break;
case AllocatorMode_QuerySupport:
{
return (void*) (
AllocatorQuery_Alloc | AllocatorQuery_FreeAll | AllocatorQuery_Resize | AllocatorQuery_ResizeGrow | AllocatorQuery_ResizeShrink
);
}
break;
}
return allocated_mem;
}
void*
farena_allocator_proc(void* allocator_data, AllocatorMode mode, SSIZE size, SSIZE alignment, void* old_memory, SSIZE old_size, U64 flags)
{
FArena* arena = rcast(FArena*, allocator_data);
void* allocated_mem = nullptr;
switch (mode)
{
case AllocatorMode_Alloc:
{
SPTR end = scast(SPTR, arena->slice.data) + arena->used;
SSIZE total_size = align_pow2(size, alignment);
if (arena->used + total_size > arena->slice.len ) {
// Out of memory
return allocated_mem;
}
allocated_mem = scast(void*, end);
arena->used += total_size;
}
break;
case AllocatorMode_Free:
{
}
break;
case AllocatorMode_FreeAll:
{
arena->used = 0;
}
break;
case AllocatorMode_Resize:
{
assert(old_memory != nullptr);
assert(old_size > 0);
assert_msg(old_size == size, "Requested resize when none needed");
size = align_pow2(size, alignment);
old_size = align_pow2(size, alignment);
SPTR old_memory_offset = scast(SPTR, old_memory) + old_size;
SPTR current_offset = scast(SPTR, arena->slice.data) + arena->used;
assert_msg(old_memory_offset == current_offset, "Cannot resize existing allocation in VArena unless it was the last allocated");
B32 requested_shrink = size >= old_size;
if (requested_shrink) {
arena->used -= size;
allocated_mem = old_memory;
break;
}
allocated_mem = old_memory;
arena->used += size;
}
break;
case AllocatorMode_QueryType:
return (void*) AllocatorType_FArena;
break;
case AllocatorMode_QuerySupport:
return (void*) (
AllocatorQuery_Alloc | AllocatorQuery_FreeAll | AllocatorQuery_Resize | AllocatorQuery_ResizeGrow | AllocatorQuery_ResizeShrink
);
break;
}
return allocated_mem;
}
+293
View File
@@ -0,0 +1,293 @@
#ifdef INTELLISENSE_DIRECTIVES
# pragma once
# include "context_cracking.h"
# include "linkage.h"
# include "macros.h"
# include "platform.h"
# include "base_types.h"
# include "memory.h"
#endif
// This provides an alterntive memory strategy to HMH/Casey Muratori/RJF styled arenas
// The library is derived from zpl-c which in-turn
// is related to the gb headers an thus the Odin-lang memory strategy
// Users can override the underlying memory allocator used, even for the HMH arena memory strategy.
#ifndef MD__ONES
#define MD__ONES ( scast( GEN_NS usize, - 1) / MD_U8_MAX )
#define MD__HIGHS ( MD__ONES * ( MD_U8_MAX / 2 + 1 ) )
#define MD__HAS_ZERO( x ) ( ( ( x ) - MD__ONES ) & ~( x ) & MD__HIGHS )
#endif
// Return value of allocator_type
typedef U64 AllocatorType;
enum
{
AllocatorType_Heap = 0, // Genreal heap allocator
AllocatorType_VArena = 1, // Arena allocator backed by virtual address space
AllocatorType_FArena = 2, // Fixed arena backed back by a fixed size block of memory (usually a byte slice)
AllocatorType_Arena = 3, // Composite arena used originally by RAD Debugger & Metadesk
};
typedef U32 AllocatorMode;
enum AllocatorMode
{
AllocatorMode_Alloc,
AllocatorMode_Free,
AllocatorMode_FreeAll,
AllocatorMode_Resize,
AllocatorMode_QueryType,
AllocatorMode_QuerySupport,
};
typedef U64 AllocatorQueryFlags;
enum
{
AllocatorQuery_Alloc = (1 << 0),
AllocatorQuery_Free = (1 << 1),
AllocatorQuery_FreeAll = (1 << 2),
AllocatorQuery_Resize = (1 << 3), // Supports both grow and shrink
AllocatorQuery_ResizeShrink = (1 << 4),
AllocatorQuery_ResizeGrow = (1 << 5),
};
typedef void*(AllocatorProc)( void* allocator_data, AllocatorMode type, SSIZE size, SSIZE alignment, void* old_memory, SSIZE old_size, U64 flags );
typedef struct AllocatorInfo AllocatorInfo;
struct AllocatorInfo
{
AllocatorProc* proc;
void* data;
};
// Overridable by the user by defining MD_OVERRIDE_DEFAULT_ALLOCATOR
AllocatorInfo default_allocator();
enum AllocFlag
{
ALLOCATOR_FLAG_CLEAR_TO_ZERO = (1 << 0),
};
#ifndef MD_DEFAULT_MEMORY_ALIGNMENT
# define MD_DEFAULT_MEMORY_ALIGNMENT ( 2 * size_of( void* ) )
#endif
#ifndef MD_DEFAULT_ALLOCATOR_FLAGS
# define MD_DEFAULT_ALLOCATOR_FLAGS ( ALLOCATOR_FLAG_CLEAR_TO_ZERO )
#endif
// Retrieve which type of allocator
AllocatorType allocator_type(AllocatorInfo a);
// Retreive which modes the allocator supports
AllocatorQueryFlags allocator_query_support(AllocatorInfo a);
// Allocate memory with default alignment.
void* alloc( AllocatorInfo a, SSIZE size );
// Allocate memory with specified alignment.
void* alloc_align( AllocatorInfo a, SSIZE size, SSIZE alignment );
// Free allocated memory.
void alloc_free( AllocatorInfo a, void* ptr );
// Free all memory allocated by an allocator.
void free_all( AllocatorInfo a );
// Resize an allocated memory.
void* resize( AllocatorInfo a, void* ptr, SSIZE old_size, SSIZE new_size );
// Resize an allocated memory with specified alignment.
void* resize_align( AllocatorInfo a, void* ptr, SSIZE old_size, SSIZE new_size, SSIZE alignment );
#ifndef alloc_item
// Allocate memory for an item.
#define alloc_item(allocator, Type) (Type*)memory_zero(alloc(allocator, size_of(Type)), size_of(Type))
// Allocate memory for an item.
#define alloc_item_no_zero( allocator, Type ) (Type*) alloc(allocator, size_of(Type))
#endif
#ifndef alloc_array
// Allocate memory for an array of items.
#define alloc_array( allocator_, Type, count ) (Type*)memory_zero(alloc( allocator_, size_of(Type) * (count) ), size_of(Type) * (count))
// Allocate memory for an array of items. (Don't zero initialize)
#define alloc_array_no_zero( allocator_, Type, count ) (Type*) alloc( allocator_, size_of(Type) * (count) )
#endif
// Allocate/Resize memory using default options.
// Use this if you don't need a "fancy" resize allocation
void* default_resize_align( AllocatorInfo a, void* ptr, SSIZE old_size, SSIZE new_size, SSIZE alignment );
#ifdef MD_HEAP_ANALYSIS
/* heap memory analysis tools */
/* define GEN_HEAP_ANALYSIS to enable this feature */
/* call zpl_heap_stats_init at the beginning of the entry point */
/* you can call zpl_heap_stats_check near the end of the execution to validate any possible leaks */
MD_API void heap_stats_init( void );
MD_API SSIZE heap_stats_used_memory( void );
MD_API SSIZE heap_stats_alloc_count( void );
MD_API void heap_stats_check( void );
#endif
MD_API void* heap_allocator_proc( void* allocator_data, AllocatorMode mode, SSIZE size, SSIZE alignment, void* old_memory, SSIZE old_size, U64 flags );
#ifndef heap
// The heap allocator backed by the platform vendor's malloc & free.
#define heap() (AllocatorInfo){ heap_allocator_proc, nullptr }
#endif
#ifndef md_malloc
// Helper to allocate memory using heap allocator.
#define md_malloc( sz ) alloc( heap(), sz )
#endif
#ifndef md_free
// Helper to free memory allocated by heap allocator.
#define md_free( ptr ) alloc_free( heap(), ptr )
#endif
/* Virtual Memory Arena
This is separate from the composite arena used by HMH/Casey Muratori/RJF
This arena stricly manages one reservation of the process's virtual address space.
Segregating this from composite Arena style causes more moremoy to be used for "allocator headers", however it allows
users of a library to have greater control over the allocation strategy used from their side instead of the library itself.
Like with the composite Arena, the VArena has its struct as the header of the reserve of memory.
*/
#ifndef VARENA_DEFUALT_RESERVE
#define VARENA_DEFAULT_RESERVE MB(64)
#endif
#ifndef VARENA_DEFUALT_COMMIT
#define VARENA_DEFAULT_COMMIT KB(64)
#endif
typedef U32 VArenaFlags;
enum
{
VArenaFlag_LargePages = (1 << 0),
};
typedef struct VArenaParams VArenaParams;
struct VArenaParams
{
U64 base_addr;
VArenaFlags flags;
U64 reserve_size;
U64 commit_size;
};
typedef struct VArena VArena;
struct VArena
{
SSIZE reserve_start;
SSIZE reserve;
SSIZE commit_size;
SSIZE committed;
SSIZE commit_used;
VArenaFlags flags;
};
MD_API VArena* varena__alloc(VArenaParams params PARAM_DEFAULT);
#define varena_alloc(...) varena__alloc( (VArenaParams){__VA_ARGS__} )
MD_API void varena_commit (VArena* vm, SSIZE commit_size);
MD_API void varena_release(VArena* vm);
force_inline void varena_rewind(VArena* vm, SSIZE pos) { vm->commit_used = pos; }
MD_API void* varena_allocator_proc(void* allocator_data, AllocatorMode mode, SSIZE size, SSIZE alignment, void* old_memory, SSIZE old_size, U64 flags);
#define varena_allocator(vm) (AllocatorInfo) { varena_allocator_proc, vm }
typedef struct ByteSlice ByteSlice;
struct ByteSlice
{
U8* data;
SSIZE len;
};
#define mem_to_byteslice(data, len) (ByteSlice){ (U8*)(data), (SSIZE)(len) }
// Fixed size arena
typedef struct FArena FArena;
struct FArena
{
ByteSlice slice;
SSIZE used;
};
#define farena_from_byteslice(slice) (FArena) { slice, 0 }
#define farena_from_memory(data, len) (FArena) { mem_to_byteslice(data, len), 0 }
MD_API void* farena_allocator_proc(void* allocator_data, AllocatorMode mode, SSIZE size, SSIZE alignment, void* old_memory, SSIZE old_size, U64 flags);
#define farena_allocator(arena) (AllocatorInfo){ farena_allocator_proc, & arena }
// Inlines
inline AllocatorType
allocator_type(AllocatorInfo a) {
if (a.proc == nullptr) {
a = default_allocator();
}
return (AllocatorType) a.proc(a.data, AllocatorMode_QueryType, 0, 0, nullptr, 0, MD_DEFAULT_ALLOCATOR_FLAGS);
}
inline AllocatorQueryFlags
allocator_query_support(AllocatorInfo a) {
if (a.proc == nullptr) {
a = default_allocator();
}
return (AllocatorType) a.proc(a.data, AllocatorMode_QuerySupport, 0, 0, nullptr, 0, MD_DEFAULT_ALLOCATOR_FLAGS);
}
inline void*
alloc_align( AllocatorInfo a, SSIZE size, SSIZE alignment ) {
if (a.proc == nullptr) {
a = default_allocator();
}
return a.proc( a.data, AllocatorMode_Alloc, size, alignment, nullptr, 0, MD_DEFAULT_ALLOCATOR_FLAGS );
}
inline void*
alloc( AllocatorInfo a, SSIZE size ) {
if (a.proc == nullptr) {
a = default_allocator();
}
return alloc_align( a, size, MD_DEFAULT_MEMORY_ALIGNMENT );
}
inline void
alloc_free( AllocatorInfo a, void* ptr ) {
if (a.proc == nullptr) {
a = default_allocator();
}
if ( ptr != nullptr ) {
a.proc( a.data, AllocatorMode_Free, 0, 0, ptr, 0, MD_DEFAULT_ALLOCATOR_FLAGS );
}
}
inline void
free_all( AllocatorInfo a ) {
if (a.proc == nullptr) {
a = default_allocator();
}
a.proc( a.data, AllocatorMode_FreeAll, 0, 0, nullptr, 0, MD_DEFAULT_ALLOCATOR_FLAGS );
}
inline void*
resize( AllocatorInfo a, void* ptr, SSIZE old_size, SSIZE new_size ) {
if (a.proc == nullptr) {
a = default_allocator();
}
return resize_align( a, ptr, old_size, new_size, MD_DEFAULT_ALLOCATOR_FLAGS );
}
inline void*
resize_align( AllocatorInfo a, void* ptr, SSIZE old_size, SSIZE new_size, SSIZE alignment ) {
if (a.proc == nullptr) {
a = default_allocator();
}
return a.proc( a.data, AllocatorMode_Resize, new_size, alignment, ptr, old_size, MD_DEFAULT_ALLOCATOR_FLAGS );
}
+24
View File
@@ -0,0 +1,24 @@
#ifdef INTELLISENSE_DIRECTIVES
# pragma once
# include "context_cracking.h"
#endif
// C++ namespace support
#if defined(MD_DONT_USE_NAMESPACE) || LANG_C
# if LANG_C
# define MD_NS
# define MD_NS_BEGIN
# define MD_NS_END
# else
# define MD_NS ::
# define MD_NS_BEGIN
# define MD_NS_END
# endif
#else
namespace MD {}
namespace md = MD;
# define MD_NS MD::
# define MD_NS_BEGIN namespace MD {
# define MD_NS_END }
#endif
+5
View File
@@ -0,0 +1,5 @@
#ifdef INTELLISENSE_DIRECTIVES
# include "platform.h"
#endif
#include <stdio.h>
+18
View File
@@ -0,0 +1,18 @@
#ifdef INTELLISENSE_DIRECTIVES
# pragma once
# include "context_cracking.h"
#endif
#include <stdarg.h>
#include <stddef.h>
#if defined( OS_WINDOWS )
# include <intrin.h>
# include <tmmintrin.h>
# include <wmmintrin.h>
#endif
#if LANG_C
# include <assert.h>
# include <stdbool.h>
#endif
+77
View File
@@ -0,0 +1,77 @@
#ifdef INTELLISENSE_DIRECTIVES
# pragma once
# include "macros.h"
#endif
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
////////////////////////////////
//~ rjf: Zero Settings
#if !defined(PROFILE_TELEMETRY)
# define PROFILE_TELEMETRY 0
#endif
#if !defined(MARKUP_LAYER_COLOR)
# define MARKUP_LAYER_COLOR 1.00f, 0.00f, 1.00f
#endif
////////////////////////////////
//~ rjf: Third Party Includes
#if PROFILE_TELEMETRY
# include "rad_tm.h"
# if OS_WINDOWS
# pragma comment(lib, "rad_tm_win64.lib")
# endif
#endif
////////////////////////////////
//~ rjf: Telemetry Profile Defines
#if PROFILE_TELEMETRY
# define prof_begin(...) tmEnter(0, 0, __VA_ARGS__)
# define prof_begin_dynamic(...) (TM_API_PTR ? TM_API_PTR->_tmEnterZoneV_Core(0, 0, __FILE__, &g_telemetry_filename_id, __LINE__, __VA_ARGS__) : (void)0)
# define prof_end(...) (TM_API_PTR ? TM_API_PTR->_tmLeaveZone(0) : (void)0)
# define prof_tick(...) tmTick(0)
# define prof_is_capturing(...) tmRunning()
# define prof_begin_capture(...) tmOpen(0, __VA_ARGS__, __DATE__, "localhost", TMCT_TCP, TELEMETRY_DEFAULT_PORT, TMOF_INIT_NETWORKING|TMOF_CAPTURE_CONTEXT_SWITCHES, 100)
# define prof_end_capture(...) tmClose(0)
# define prof_thread_name(...) (TM_API_PTR ? TM_API_PTR->_tmThreadName(0, 0, __VA_ARGS__) : (void)0)
# define prof_msg(...) (TM_API_PTR ? TM_API_PTR->_tmMessageV_Core(0, TMMF_ICON_NOTE, __FILE__, &g_telemetry_filename_id, __LINE__, __VA_ARGS__) : (void)0)
# define prof_begin_lock_wait(...) tmStartWaitForLock(0, 0, __VA_ARGS__)
# define prof_end_lock_wait(...) tmEndWaitForLock(0)
# define prof_lock_take(...) tmAcquiredLock(0, 0, __VA_ARGS__)
# define prof_lock_drop(...) tmReleasedLock(0, __VA_ARGS__)
# define prof_color(color) tmZoneColorSticky(color)
#endif
// TODO(Ed): Support spall?
// TODO(Ed): Support tracey?
////////////////////////////////
//~ rjf: Zeroify Undefined Defines
#if !defined(prof_begin)
# define prof_begin(...) (0)
# define prof_begin_dynamic(...) (0)
# define prof_end(...) (0)
# define prof_tick(...) (0)
# define prof_is_capturing(...) (0)
# define prof_begin_capture(...) (0)
# define prof_end_capture(...) (0)
# define prof_thread_name(...) (0)
# define prof_msg(...) (0)
# define prof_end_lock_wait(...) (0)
# define prof_end_lock_wait(...) (0)
# define prof_lock_take(...) (0)
# define prof_lock_drop(...) (0)
# define prof_color(...) (0)
#endif
////////////////////////////////
//~ rjf: Helper Wrappers
#define prof_begin_function(...) prof_begin(this_function_name)
#define prof_scope(...) defer_loop(prof_begin_dynamic(__VA_ARGS__), ProfEnd())
+46
View File
@@ -0,0 +1,46 @@
#ifdef INTELLISENSE_DIRECTIVES
# pragma once
# include "base_types.h"
#endif
////////////////////////////////
//~ rjf: Non-Fancy Ring Buffer Reads/Writes
U64 ring_write(U8* ring_base, U64 ring_size, U64 ring_pos, void* src_data, U64 src_data_size);
U64 ring_read (U8* ring_base, U64 ring_size, U64 ring_pos, void* dst_data, U64 read_size);
#define ring_write_struct(ring_base, ring_size, ring_pos, ptr) ring_write((ring_base), (ring_size), (ring_pos), (ptr), sizeof(*(ptr)))
#define ring_read_struct(ring_base, ring_size, ring_pos, ptr) ring_read ((ring_base), (ring_size), (ring_pos), (ptr), sizeof(*(ptr)))
////////////////////////////////
//~ rjf: Non-Fancy Ring Buffer Reads/Writes
inline U64
ring_write(U8* ring_base, U64 ring_size, U64 ring_pos, void* src_data, U64 src_data_size) {
assert(src_data_size <= ring_size);
{
U64 ring_off = ring_pos % ring_size;
U64 bytes_before_split = ring_size - ring_off;
U64 pre_split_bytes = min(bytes_before_split, src_data_size);
U64 pst_split_bytes = src_data_size - pre_split_bytes;
void* pre_split_data = src_data;
void* pst_split_data = ((U8*)src_data + pre_split_bytes);
memory_copy(ring_base + ring_off, pre_split_data, pre_split_bytes);
memory_copy(ring_base + 0, pst_split_data, pst_split_bytes);
}
return src_data_size;
}
inline U64
ring_read(U8* ring_base, U64 ring_size, U64 ring_pos, void* dst_data, U64 read_size) {
assert(read_size <= ring_size);
{
U64 ring_off = ring_pos % ring_size;
U64 bytes_before_split = ring_size-ring_off;
U64 pre_split_bytes = min(bytes_before_split, read_size);
U64 pst_split_bytes = read_size - pre_split_bytes;
memory_copy(dst_data, ring_base+ring_off, pre_split_bytes);
memory_copy((U8*)dst_data + pre_split_bytes, ring_base + 0, pst_split_bytes);
}
return read_size;
}
+10
View File
@@ -0,0 +1,10 @@
#ifdef INTELLISENSE_DIRECTIVES
# include "platform.h"
#endif
////////////////////////////////
//~ rjf: Sorts
#ifndef quick_sort
#define quick_sort(ptr, count, element_size, cmp_function) qsort((ptr), (count), (element_size), (int (*)(const void *, const void *))(cmp_function))
#endif
+67
View File
@@ -0,0 +1,67 @@
#ifdef INTELLISENSE_DIRECTIVES
# pragma once
# include "base_types.h"
#endif
////////////////////////////////
//~ rjf: Basic Types & Spaces
typedef enum Dimension Dimension;
enum Dimension
{
Dimension_X,
Dimension_Y,
Dimension_Z,
Dimension_W,
};
typedef enum Side Side;
enum Side
{
Side_Invalid = -1,
Side_Min,
Side_Max,
Side_COUNT,
};
#define side_flip(s) ((Side)(!(s)))
typedef enum Axis2 Axis2;
enum Axis2
{
Axis2_Invalid = -1,
Axis2_X,
Axis2_Y,
Axis2_COUNT,
};
#define axis2_flip(a) ((Axis2)(!(a)))
typedef enum Corner Corner;
enum Corner
{
Corner_Invalid = -1,
Corner_00,
Corner_01,
Corner_10,
Corner_11,
Corner_COUNT
};
typedef enum Dir2 Dir2;
enum Dir2
{
Dir2_Invalid = -1,
Dir2_Left,
Dir2_Up,
Dir2_Right,
Dir2_Down,
Dir2_COUNT
};
#define axis2_from_dir2(d) (((d) & 1) ? Axis2_Y : Axis2_X)
#define side_from_dir2(d) (((d) < Dir2_Right) ? Side_Min : Side_Max)
////////////////////////////////
//~ rjf: Enum -> Sign
inline S32 sign_from_side_S32(Side side) { return((side == Side_Min) ? -1 : 1 ); }
inline F32 sign_from_side_F32(Side side) { return((side == Side_Min) ? -1.f : 1.f); }
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+54
View File
@@ -0,0 +1,54 @@
#ifdef INTELLISENSE_DIRECTIVES
# include "text.h"
#endif
////////////////////////////////
//~ rjf: Text Path Helpers
String8TxtPtPair
str8_txt_pt_pair_from_string(String8 string)
{
String8TxtPtPair pair = {0};
{
String8 file_part = {0};
String8 line_part = {0};
String8 col_part = {0};
// rjf: grab file part
for(U64 idx = 0; idx <= string.size; idx += 1)
{
U8 byte = (idx < string.size) ? (string.str[idx ]) : 0;
U8 next_byte = ((idx + 1 < string.size) ? (string.str[idx + 1]) : 0);
if(byte == ':' && next_byte != '/' && next_byte != '\\') {
file_part = str8_prefix(string, idx);
line_part = str8_skip(string, idx+1);
break;
}
else if(byte == 0) {
file_part = string;
break;
}
}
// rjf: grab line/column
{
U64 colon_pos = str8_find_needle(line_part, 0, str8_lit(":"), 0);
if(colon_pos < line_part.size) {
col_part = str8_skip (line_part, colon_pos + 1);
line_part = str8_prefix(line_part, colon_pos);
}
}
// rjf: convert line/column strings to numerics
U64 line = 0;
U64 column = 0;
try_u64_from_str8_c_rules(line_part, &line);
try_u64_from_str8_c_rules(col_part, &column);
// rjf: fill
pair.string = file_part;
pair.pt = txt_pt((S64)line, (S64)column);
if(pair.pt.line == 0) { pair.pt.line = 1; }
if(pair.pt.column == 0) { pair.pt.column = 1; }
}
return pair;
}
+106
View File
@@ -0,0 +1,106 @@
#ifdef INTELLISENSE_DIRECTIVES
# pragma once
# include "strings.h"
#endif
////////////////////////////////
//~ rjf: Text 2D Coordinates & Ranges
typedef struct TxtPt TxtPt;
struct TxtPt
{
S64 line;
S64 column;
};
typedef struct TxtRng TxtRng;
struct TxtRng
{
TxtPt min;
TxtPt max;
};
////////////////////////////////
//~ rjf: String Pair Types
typedef struct String8TxtPtPair String8TxtPtPair;
struct String8TxtPtPair
{
String8 string;
TxtPt pt;
};
////////////////////////////////
//~ rjf: Text Path Helpers
MD_API String8TxtPtPair str8_txt_pt_pair_from_string(String8 string);
////////////////////////////////
//~ rjf: Text 2D Coordinate/Range Functions
inline TxtPt txt_pt(S64 line, S64 column) { TxtPt p = { line, column }; return p; }
inline B32 txt_pt_match(TxtPt a, TxtPt b) { return a.line == b.line && a.column == b.column; }
inline TxtPt txt_pt_min (TxtPt a, TxtPt b) { TxtPt result = b; if (txt_pt_less_than(a, b)) { result = a; } return result; }
inline TxtPt txt_pt_max (TxtPt a, TxtPt b) { TxtPt result = a; if (txt_pt_less_than(a, b)) { result = b; } return result; }
B32 txt_pt_less_than (TxtPt a, TxtPt b);
TxtRng txt_rng (TxtPt min, TxtPt max);
TxtRng txt_rng_intersect(TxtRng a, TxtRng b);
TxtRng txt_rng_union (TxtRng a, TxtRng b);
B32 txt_rng_contains (TxtRng r, TxtPt pt);
inline B32
txt_pt_less_than(TxtPt a, TxtPt b)
{
B32 result = 0;
if (a.line < b.line) {
result = 1;
}
else if (a.line == b.line) {
result = a.column < b.column;
}
return result;
}
inline TxtRng
txt_rng(TxtPt min, TxtPt max)
{
TxtRng range = {0};
if(txt_pt_less_than(min, max)) {
range.min = min;
range.max = max;
}
else {
range.min = max;
range.max = min;
}
return range;
}
inline TxtRng
txt_rng_intersect(TxtRng a, TxtRng b)
{
TxtRng result = {0};
result.min = txt_pt_max(a.min, b.min);
result.max = txt_pt_min(a.max, b.max);
if (txt_pt_less_than(result.max, result.min)) {
MemoryZeroStruct(&result);
}
return result;
}
inline TxtRng
txt_rng_union(TxtRng a, TxtRng b)
{
TxtRng result = {0};
result.min = txt_pt_min(a.min, b.min);
result.max = txt_pt_max(a.max, b.max);
return result;
}
inline B32
txt_rng_contains(TxtRng r, TxtPt pt) {
B32 result = ((txt_pt_less_than(r.min, pt) || txt_pt_match(r.min, pt)) && txt_pt_less_than(pt, r.max));
return result;
}
+87
View File
@@ -0,0 +1,87 @@
#ifdef INTELLISENSE_DIRECTIVES
# include "arena.h"
# include "thread_context.h"
#endif
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
////////////////////////////////
// NOTE(allen): Thread Context Functions
MD_API_C thread_static TCTX* tctx_thread_local;
#if ! MD_BUILD_SUPPLEMENTARY_UNIT
MD_API_C thread_static TCTX* tctx_thread_local = 0;
#endif
void
tctx_init_and_equip(TCTX* tctx)
{
// memory_zero_struct(tctx);
Arena** arena_ptr = tctx->arenas;
for (U64 i = 0; i < array_count(tctx->arenas); i += 1, arena_ptr += 1)
{
if (*arena_ptr == nullptr)
{
VArena* vm = varena_alloc(.reserve_size = VARENA_DEFAULT_RESERVE, .commit_size = VARENA_DEFAULT_COMMIT);
*arena_ptr = arena_alloc(.backing = varena_allocator(vm));
}
}
tctx_thread_local = tctx;
}
void
tctx_init_and_equip_alloc(TCTX* tctx, AllocatorInfo ainfo)
{
memory_zero_struct(tctx);
Arena** arena_ptr = tctx->arenas;
for (U64 i = 0; i < array_count(tctx->arenas); i += 1, arena_ptr += 1)
{
if (*arena_ptr == nullptr)
{
*arena_ptr = arena_alloc(.backing = ainfo);
}
}
tctx_thread_local = tctx;
}
void
tctx_release(void) {
for (U64 i = 0; i < array_count(tctx_thread_local->arenas); i += 1) {
arena_release(tctx_thread_local->arenas[i]);
}
}
TCTX*
tctx_get_equipped(void){
return(tctx_thread_local);
}
Arena*
tctx_get_scratch(Arena** conflicts, U64 count)
{
TCTX* tctx = tctx_get_equipped();
Arena* result = 0;
Arena** arena_ptr = tctx->arenas;
for (U64 i = 0; i < array_count(tctx->arenas); i += 1, arena_ptr += 1)
{
Arena** conflict_ptr = conflicts;
B32 has_conflict = 0;
for (U64 j = 0; j < count; j += 1, conflict_ptr += 1)
{
if (*arena_ptr == *conflict_ptr) {
has_conflict = 1;
break;
}
}
if ( ! has_conflict){
result = *arena_ptr;
break;
}
}
return(result);
}
+90
View File
@@ -0,0 +1,90 @@
#ifdef INTELLISENSE_DIRECTIVES
# pragma once
# include "arena.h"
# include "string.h"
#endif
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
////////////////////////////////
// NOTE(allen): Thread Context
typedef struct TCTX TCTX;
struct TCTX
{
Arena* arenas[2];
U8 thread_name[32];
U64 thread_name_size;
char* file_name;
U64 line_number;
};
////////////////////////////////
// NOTE(allen): Thread Context Functions
MD_API void tctx_init_and_equip (TCTX* tctx);
MD_API void tctx_init_and_equip_alloc(TCTX* tctx, AllocatorInfo ainfo);
MD_API void tctx_release (void);
MD_API TCTX* tctx_get_equipped (void);
MD_API Arena* tctx_get_scratch(Arena** conflicts, U64 count);
void tctx_set_thread_name(String8 name);
String8 tctx_get_thread_name(void);
void tctx_write_srcloc(char* file_name, U64 line_number);
void tctx_read_srcloc (char** file_name, U64* line_number);
#define tctx_write_this_srcloc() tctx_write_srcloc(__FILE__, __LINE__)
typedef struct { U64 count; } Opt_ScratchBegin;
inline TempArena
scratch_begin__ainfo(AllocatorInfo ainfo, Opt_ScratchBegin opt) {
Arena* arena = extract_arena(ainfo);
TempArena scratch = temp_begin(tctx_get_scratch(&arena, arena != nullptr));
return scratch;
}
force_inline TempArena scratch_begin__arena(Arena** arena, Opt_ScratchBegin opt) { TempArena scratch = temp_begin(tctx_get_scratch(arena, opt.count)); return scratch; }
#define scratch_begin(conflicts, ...) \
_Generic(conflicts, \
int : scratch_begin__arena, \
Arena** : scratch_begin__arena, \
AllocatorInfo: scratch_begin__ainfo, \
default : assert_generic_sel_fail \
) generic_call(conflicts, (Opt_ScratchBegin){__VA_ARGS__})
#define scratch_end(scratch) temp_end(scratch)
inline void
tctx_set_thread_name(String8 string){
TCTX* tctx = tctx_get_equipped();
U64 size = clamp_top(string.size, sizeof(tctx->thread_name));
memory_copy(tctx->thread_name, string.str, size);
tctx->thread_name_size = size;
}
inline String8
tctx_get_thread_name(void) {
TCTX* tctx = tctx_get_equipped();
String8 result = str8(tctx->thread_name, tctx->thread_name_size);
return(result);
}
inline void
tctx_write_srcloc(char* file_name, U64 line_number){
TCTX *tctx = tctx_get_equipped();
tctx->file_name = file_name;
tctx->line_number = line_number;
}
inline void
tctx_read_srcloc(char** file_name, U64* line_number){
TCTX* tctx = tctx_get_equipped();
*file_name = tctx->file_name;
*line_number = tctx->line_number;
}
+58
View File
@@ -0,0 +1,58 @@
#ifdef INTELLISENSE_DIRECTIVES
# include "debug.h"
# include "time.h"
#endif
DateTime
date_time_from_unix_time(U64 unix_time)
{
DateTime date = {0};
date.year = 1970;
date.day = 1 + (unix_time / 86400);
date.sec = (U32) unix_time % 60;
date.min = (U32)(unix_time / 60) % 60;
date.hour = (U32)(unix_time / 3600) % 24;
for(;;)
{
for(date.month = 0; date.month < 12; ++date.month)
{
U64 c = 0;
switch(date.month)
{
case Month_Jan: c = 31; break;
case Month_Feb:
{
if((date.year % 4 == 0) && ((date.year % 100) != 0 || (date.year % 400) == 0))
{
c = 29;
}
else
{
c = 28;
}
} break;
case Month_Mar: c = 31; break;
case Month_Apr: c = 30; break;
case Month_May: c = 31; break;
case Month_Jun: c = 30; break;
case Month_Jul: c = 31; break;
case Month_Aug: c = 31; break;
case Month_Sep: c = 30; break;
case Month_Oct: c = 31; break;
case Month_Nov: c = 30; break;
case Month_Dec: c = 31; break;
default: invalid_path;
}
if(date.day <= c)
{
goto exit;
}
date.day -= c;
}
++date.year;
}
exit:;
return date;
}
+115
View File
@@ -0,0 +1,115 @@
#ifdef INTELLISENSE_DIRECTIVES
# pragma once
# include "base_types.h"
#endif
////////////////////////////////
//~ allen: Time
typedef enum WeekDay WeekDay;
enum WeekDay
{
WeekDay_Sun,
WeekDay_Mon,
WeekDay_Tue,
WeekDay_Wed,
WeekDay_Thu,
WeekDay_Fri,
WeekDay_Sat,
WeekDay_COUNT,
};
typedef enum Month Month;
enum Month
{
Month_Jan,
Month_Feb,
Month_Mar,
Month_Apr,
Month_May,
Month_Jun,
Month_Jul,
Month_Aug,
Month_Sep,
Month_Oct,
Month_Nov,
Month_Dec,
Month_COUNT,
};
typedef struct DateTime DateTime;
struct DateTime
{
U16 micro_sec; // [0,999]
U16 msec; // [0,999]
U16 sec; // [0,60]
U16 min; // [0,59]
U16 hour; // [0,24]
U16 day; // [0,30]
union
{
WeekDay week_day;
U32 wday;
};
union
{
Month month;
U32 mon;
};
U32 year; // 1 = 1 CE, 0 = 1 BC
};
typedef U64 DenseTime;
////////////////////////////////
//~ rjf: Time Functions
DenseTime dense_time_from_date_time (DateTime date_time);
DateTime date_time_from_dense_time (DenseTime time);
DateTime date_time_from_micro_seconds(U64 time);
DateTime date_time_from_unix_time (U64 unix_time);
////////////////////////////////
//~ rjf: Time Functions
inline DenseTime
dense_time_from_date_time(DateTime date_time) {
DenseTime result = 0;
result += date_time.year; result *= 12;
result += date_time.mon; result *= 31;
result += date_time.day; result *= 24;
result += date_time.hour; result *= 60;
result += date_time.min; result *= 61;
result += date_time.sec; result *= 1000;
result += date_time.msec;
return(result);
}
inline DateTime
date_time_from_dense_time(DenseTime time) {
DateTime result = {0};
result.msec = time % 1000; time /= 1000;
result.sec = time % 61; time /= 61;
result.min = time % 60; time /= 60;
result.hour = time % 24; time /= 24;
result.day = time % 31; time /= 31;
result.mon = time % 12; time /= 12;
assert(time <= MAX_U32);
result.year = (U32)time;
return(result);
}
inline DateTime
date_time_from_micro_seconds(U64 time){
DateTime result = {0};
result.micro_sec = time % 1000; time /= 1000;
result.msec = time % 1000; time /= 1000;
result.sec = time % 60; time /= 60;
result.min = time % 60; time /= 60;
result.hour = time % 24; time /= 24;
result.day = time % 31; time /= 31;
result.mon = time % 12; time /= 12;
assert(time <= MAX_U32);
result.year = (U32)time;
return(result);
}
+115
View File
@@ -0,0 +1,115 @@
#ifdef INTELLISENSE_DIRECTIVES
# pragma once
# include "base_types.h"
#endif
////////////////////////////////
//~ rjf: Toolchain/Environment Enums
typedef enum OperatingSystem OperatingSystem;
enum OperatingSystem
{
OperatingSystem_Null,
OperatingSystem_Windows,
OperatingSystem_Linux,
OperatingSystem_Mac,
OperatingSystem_COUNT,
};
typedef enum ImageType ImageType;
enum ImageType
{
Image_Null,
Image_CoffPe,
Image_Elf32,
Image_Elf64,
Image_Macho
};
typedef enum Arch Arch;
enum Arch
{
Arch_Null,
Arch_x64,
Arch_x86,
Arch_arm64,
Arch_arm32,
Arch_COUNT,
};
typedef enum Compiler Compiler;
enum Compiler
{
Compiler_Null,
Compiler_msvc,
Compiler_gcc,
Compiler_clang,
Compiler_COUNT,
};
////////////////////////////////
//~ rjf: Toolchain/Environment Enum Functions
inline U64
bit_size_from_arch(Arch arch)
{
// TODO(rjf): metacode
U64 arch_bitsize = 0;
switch(arch)
{
case Arch_x64: arch_bitsize = 64; break;
case Arch_x86: arch_bitsize = 32; break;
case Arch_arm64: arch_bitsize = 64; break;
case Arch_arm32: arch_bitsize = 32; break;
default: break;
}
return arch_bitsize;
}
inline U64
max_instruction_size_from_arch(Arch arch)
{
// TODO(rjf): make this real
return 64;
}
inline OperatingSystem
operating_system_from_context(void) {
OperatingSystem os = OperatingSystem_Null;
#if OS_WINDOWS
os = OperatingSystem_Windows;
#elif OS_LINUX
os = OperatingSystem_Linux;
#elif OS_MAC
os = OperatingSystem_Mac;
#endif
return os;
}
inline Arch
arch_from_context(void) {
Arch arch = Arch_Null;
#if ARCH_X64
arch = Arch_x64;
#elif ARCH_X86
arch = Arch_x86;
#elif ARCH_ARM64
arch = Arch_arm64;
#elif ARCH_ARM32
arch = Arch_arm32;
#endif
return arch;
}
inline Compiler
compiler_from_context(void) {
Compiler compiler = Compiler_Null;
#if COMPILER_MSVC
compiler = Compiler_msvc;
#elif COMPILER_GCC
compiler = Compiler_gcc;
#elif COMPILER_CLANG
compiler = Compiler_clang;
#endif
return compiler;
}
+1047
View File
File diff suppressed because it is too large Load Diff
+608
View File
@@ -0,0 +1,608 @@
#ifdef INTELLISENSE_DIRECTIVES
# pragma once
# include "os/os.h"
#endif
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
////////////////////////////////
//~ rjf: Messages
typedef enum MsgKind
{
MsgKind_Null,
MsgKind_Note,
MsgKind_Warning,
MsgKind_Error,
MsgKind_FatalError,
}
MsgKind;
typedef struct Node Node;
typedef struct Msg Msg;
struct Msg
{
Msg* next;
Node* node;
MsgKind kind;
String8 string;
};
typedef struct MsgList MsgList;
struct MsgList
{
Msg* first;
Msg* last;
U64 count;
MsgKind worst_message_kind;
};
////////////////////////////////
//~ rjf: Token Types
typedef U32 TokenFlags;
enum
{
// TODO(Ed): Track type of comment, and opening/closing main delimiter.
// (The parser needs that info later and just forces str_matches that werent necessary)
// rjf: base kind info
TokenFlag_Identifier = (1 << 0),
TokenFlag_Numeric = (1 << 1),
TokenFlag_StringLiteral = (1 << 2),
TokenFlag_Symbol = (1 << 3),
TokenFlag_Reserved = (1 << 4),
TokenFlag_Comment = (1 << 5),
TokenFlag_Whitespace = (1 << 6),
TokenFlag_Newline = (1 << 7),
// rjf: decoration info
TokenFlag_StringSingleQuote = (1 << 8),
TokenFlag_StringDoubleQuote = (1 << 9),
TokenFlag_StringTick = (1 << 10),
TokenFlag_StringTriplet = (1 << 11),
// rjf: error info
TokenFlag_BrokenComment = (1 << 12),
TokenFlag_BrokenStringLiteral = (1 << 13),
TokenFlag_BadCharacter = (1 << 14),
};
typedef U32 TokenFlagGroups;
enum
{
TokenFlagGroup_Comment = TokenFlag_Comment,
TokenFlagGroup_Whitespace = (TokenFlag_Whitespace| TokenFlag_Newline),
TokenFlagGroup_Irregular = (TokenFlagGroup_Comment | TokenFlagGroup_Whitespace),
TokenFlagGroup_Regular = ~TokenFlagGroup_Irregular,
TokenFlagGroup_Label = (TokenFlag_Identifier | TokenFlag_Numeric | TokenFlag_StringLiteral | TokenFlag_Symbol),
TokenFlagGroup_Error = (TokenFlag_BrokenComment | TokenFlag_BrokenStringLiteral | TokenFlag_BadCharacter),
};
typedef struct Token Token;
struct Token
{
Rng1U64 range;
TokenFlags flags;
};
typedef struct TokenChunkNode TokenChunkNode;
struct TokenChunkNode
{
TokenChunkNode* next;
Token* v;
U64 count;
U64 cap;
};
typedef struct TokenChunkList TokenChunkList;
struct TokenChunkList
{
TokenChunkNode* first;
TokenChunkNode* last;
U64 chunk_count;
U64 total_token_count;
};
typedef struct TokenArray TokenArray;
struct TokenArray
{
Token* v;
U64 count;
};
////////////////////////////////
//~ rjf: Node Types
typedef enum NodeKind NodeKind;
enum NodeKind
{
NodeKind_Nil,
NodeKind_File,
NodeKind_ErrorMarker,
NodeKind_Main,
NodeKind_Tag,
NodeKind_List,
NodeKind_Reference,
NodeKind_COUNT
};
typedef U32 NodeFlags;
enum
{
NodeFlag_MaskSetDelimiters = (0x3F << 0),
NodeFlag_HasParenLeft = (1 << 0),
NodeFlag_HasParenRight = (1 << 1),
NodeFlag_HasBracketLeft = (1 << 2),
NodeFlag_HasBracketRight = (1 << 3),
NodeFlag_HasBraceLeft = (1 << 4),
NodeFlag_HasBraceRight = (1 << 5),
NodeFlag_MaskSeparators = (0xF << 6),
NodeFlag_IsBeforeSemicolon = (1 << 6),
NodeFlag_IsAfterSemicolon = (1 << 7),
NodeFlag_IsBeforeComma = (1 << 8),
NodeFlag_IsAfterComma = (1 << 9),
NodeFlag_MaskStringDelimiters = (0xF << 10),
NodeFlag_StringSingleQuote = (1 << 10),
NodeFlag_StringDoubleQuote = (1 << 11),
NodeFlag_StringTick = (1 << 12),
NodeFlag_StringTriplet = (1 << 13),
NodeFlag_MaskLabelKind = (0xF << 14),
NodeFlag_Numeric = (1 << 14),
NodeFlag_Identifier = (1 << 15),
NodeFlag_StringLiteral = (1 << 16),
NodeFlag_Symbol = (1 << 17),
};
#define NodeFlag_AfterFromBefore(f) ((f) << 1)
typedef struct Node Node;
struct Node
{
// rjf: tree links
Node* next;
Node* prev;
Node* parent;
Node* first;
Node* last;
// rjf: tag links
Node* first_tag;
Node* last_tag;
// rjf: node info
NodeKind kind;
NodeFlags flags;
String8 string;
String8 raw_string;
// rjf: source code info
U64 src_offset;
// rjf: user-controlled generation number
//
// (unused by mdesk layer, but can be used by usage code to use Node trees
// in a "retained mode" way, where stable generational handles can be formed
// to nodes)
U64 user_gen;
// rjf: extra padding to 128 bytes
U64 _unused_[2];
};
typedef struct NodeRec NodeRec;
struct NodeRec
{
Node* next;
S32 push_count;
S32 pop_count;
};
////////////////////////////////
//~ rjf: Text -> Tokens Types
typedef struct TokenizeResult TokenizeResult;
struct TokenizeResult
{
TokenArray tokens;
MsgList msgs;
};
////////////////////////////////
//~ rjf: Tokens -> Tree Types
typedef struct ParseResult ParseResult;
struct ParseResult
{
Node* root;
MsgList msgs;
};
////////////////////////////////
// Context
typedef struct Context Context;
struct Context
{
// Currently, this is only relevant if the user is utilizing this library via bindings
// or they are not utilizing metadesk's hosted `entry_point` runtime
// Its recommended that the user hooks up
// their own arena allocators to the thread context
// which will be utilized for thread local scratch arena
TCTX thread_ctx;
// Just as with TCTX its recommended the user setup the arena allocators
// for te os context
OS_Context os_ctx;
// This skips the os_init process but that means the user is expected to setup
// the thread context's arenas for the process
B32 dont_init_os;
};
////////////////////////////////
//~ rjf: Globals
inline Node*
nil_node()
{
read_only local_persist
Node nil =
{
&nil,
&nil,
&nil,
&nil,
&nil,
&nil,
&nil,
};
return &nil;
}
////////////////////////////////
// Context lifetime Functios
// NOTE(Ed): The are see comments in Context struct, this is only necessary when not utilizing
// the metadesk os runtime provided entry_point interface
MD_API void init(Context* ctx);
MD_API void deinit(Context* ctx); // Does nothing for now.
////////////////////////////////
//~ rjf: Message Type Functions
void msg_list_push__arena (Arena* arena, MsgList* msgs, Node* node, MsgKind kind, String8 string);
MD_API void msg_list_push__ainfo (AllocatorInfo ainfo, MsgList* msgs, Node* node, MsgKind kind, String8 string);
void msg_list_pushf__arena(Arena* arena, MsgList* msgs, Node* node, MsgKind kind, char *fmt, ...);
void msg_list_pushf__ainfo(AllocatorInfo ainfo, MsgList* msgs, Node* node, MsgKind kind, char *fmt, ...);
#define msg_list_push(allocator, msgs, node, kind, string) _Generic(allocator, Arena*: msg_list_push__arena, AllocatorInfo: msg_list_push__ainfo, default: assert_generic_sel_fail) generic_call(allocator, msgs, node, kind, string)
#define msg_list_pushf(allocator, msgs, node, kind, string, fmt, ...) _Generic(allocator, Arena*: msg_list_pushf__arena, AllocatorInfo: msg_list_pushf__ainfo, default: assert_generic_sel_fail) generic_call(allocator, msgs, node, kind, fmt, __VA_ARGS__)
MD_API void msg_list_concat_in_place(MsgList* dst, MsgList* to_push);
force_inline void msg_list_push__arena(Arena* arena, MsgList* msgs, Node* node, MsgKind kind, String8 string) { msg_list_push__ainfo(arena_allocator(arena), msgs, node, kind, string); }
inline void
msg_list_pushf__arena(Arena* arena, MsgList* msgs, Node* node, MsgKind kind, char* fmt, ...) {
va_list args;
va_start(args, fmt);
String8 string = str8fv(arena, fmt, args);
msg_list_push(arena, msgs, node, kind, string);
va_end(args);
}
inline void
msg_list_pushf__ainfo(AllocatorInfo ainfo, MsgList* msgs, Node* node, MsgKind kind, char* fmt, ...) {
va_list args;
va_start(args, fmt);
String8 string = str8fv(ainfo, fmt, args);
msg_list_push(ainfo, msgs, node, kind, string);
va_end(args);
}
////////////////////////////////
//~ rjf: Token Type Functions
Token token_make (Rng1U64 range, TokenFlags flags);
B32 token_match(Token a, Token b);
MD_API String8 content_string_from_token_flags_str8(TokenFlags flags, String8 string);
String8List string_list_from_token_flags__arena(Arena* arena, TokenFlags flags);
MD_API String8List string_list_from_token_flags__ainfo(AllocatorInfo ainfo, TokenFlags flags);
MD_API void token_chunk_list_push__arena (Arena* arena, TokenChunkList* list, U64 cap, Token token);
MD_API void token_chunk_list_push__ainfo (AllocatorInfo ainfo, TokenChunkList* list, U64 cap, Token token);
MD_API TokenArray token_array_from_chunk_list__arena (Arena* arena, TokenChunkList* chunks);
MD_API TokenArray token_array_from_chunk_list__ainfo (AllocatorInfo ainfo, TokenChunkList* chunks);
#define string_list_from_token_flags(allocator, flags) _Generic(allocator, Arena*: string_list_from_token_flags__arena, AllocatorInfo: string_list_from_token_flags__ainfo, default: assert_generic_sel_fail) generic_call(allocator, flags)
#define token_chunk_list_push(allocator, list, cap, token) _Generic(allocator, Arena*: token_chunk_list_push__arena, AllocatorInfo: token_chunk_list_push__ainfo, default: assert_generic_sel_fail) generic_call(allocator, list, cap, token)
#define token_array_from_chunk_list(allocator, chunks) _Generic(allocator, Arena*: token_array_from_chunk_list__arena, AllocatorInfo: token_array_from_chunk_list__ainfo, default: assert_generic_sel_fail) generic_call(allocator, chunks)
force_inline String8List string_list_from_token_flags__arena(Arena* arena, TokenFlags flags) { return string_list_from_token_flags__ainfo(arena_allocator(arena), flags); }
force_inline void token_chunk_list_push__arena (Arena* arena, TokenChunkList* list, U64 cap, Token token) { token_chunk_list_push__ainfo(arena_allocator(arena), list, cap, token); }
force_inline TokenArray token_array_from_chunk_list__arena (Arena* arena, TokenChunkList* chunks) { return token_array_from_chunk_list__ainfo(arena_allocator(arena), chunks); }
inline Token
token_make(Rng1U64 range, TokenFlags flags) {
Token token = { range, flags };
return token;
}
inline B32
token_match(Token a, Token b) {
return (a.range.min == b.range.min &&
a.range.max == b.range.max &&
a.flags == b.flags );
}
////////////////////////////////
//~ rjf: Node Type Functions
//- rjf: flag conversions
inline NodeFlags
node_flags_from_token_flags(TokenFlags flags)
{
NodeFlags result = 0;
result |= NodeFlag_Identifier *!! (flags & TokenFlag_Identifier );
result |= NodeFlag_Numeric *!! (flags & TokenFlag_Numeric );
result |= NodeFlag_StringLiteral *!! (flags & TokenFlag_StringLiteral );
result |= NodeFlag_Symbol *!! (flags & TokenFlag_Symbol );
result |= NodeFlag_StringSingleQuote *!! (flags & TokenFlag_StringSingleQuote);
result |= NodeFlag_StringDoubleQuote *!! (flags & TokenFlag_StringDoubleQuote);
result |= NodeFlag_StringTick *!! (flags & TokenFlag_StringTick );
result |= NodeFlag_StringTriplet *!! (flags & TokenFlag_StringTriplet );
return result;
}
//- rjf: nil
B32 node_is_nil(Node* node) { return (node == 0 || node == nil_node() || node->kind == NodeKind_Nil); }
//- rjf: iteration
#define each_node(it, first) (Node* it = first; !node_is_nil(it); it = it->next)
MD_API NodeRec node_rec_depth_first(Node* node, Node* subtree_root, U64 child_off, U64 sib_off);
#define node_rec_depth_first_pre(node, subtree_root) node_rec_depth_first((node), (subtree_root), offset_of(Node, first), offset_of(Node, next))
#define node_rec_depth_first_pre_rev(node, subtree_root) node_rec_depth_first((node), (subtree_root), offset_of(Node, last), offset_of(Node, prev))
//- rjf: tree building
Node* push_node__arena(Arena* arena, NodeKind kind, NodeFlags flags, String8 string, String8 raw_string, U64 src_offset);
Node* push_node__ainfo(AllocatorInfo ainfo, NodeKind kind, NodeFlags flags, String8 string, String8 raw_string, U64 src_offset);
#define push_node(allocator, kind, flags, string, raw_string, src_offset) _Generic(allocator, Arena*: push_node__arena, AllocatorInfo: push_node__ainfo, default: assert_generic_sel_fail) generic_call(allocator, kind, flags, string, raw_string, src_offset)
void node_insert_tag (Node* parent, Node* prev_child, Node* node);
void node_insert_child(Node* parent, Node* prev_child, Node* node);
void node_push_child (Node* parent, Node* node);
void node_push_tag (Node* parent, Node* node);
void unhook (Node* node);
inline Node* push_node__arena(Arena* arena, NodeKind kind, NodeFlags flags, String8 string, String8 raw_string, U64 src_offset) { push_node__ainfo(arena_allocator(arena), kind, flags, string, raw_string, src_offset); }
inline Node*
push_node__ainfo(AllocatorInfo ainfo, NodeKind kind, NodeFlags flags, String8 string, String8 raw_string, U64 src_offset) {
Node* node = alloc_array(ainfo, Node, 1);
node->first = node->last = node->parent = node->next = node->prev = node->first_tag = node->last_tag = nil_node();
node->kind = kind;
node->flags = flags;
node->string = string;
node->raw_string = raw_string;
node->src_offset = src_offset;
return node;
}
inline void
node_insert_child(Node* parent, Node* prev_child, Node* node) {
node->parent = parent;
dll_insert_npz(nil_node(), parent->first, parent->last, prev_child, node, next, prev);
}
inline void
node_insert_tag(Node* parent, Node* prev_child, Node* node) {
node->kind = NodeKind_Tag;
node->parent = parent;
dll_insert_npz(nil_node(), parent->first_tag, parent->last_tag, prev_child, node, next, prev);
}
inline void
node_push_child(Node* parent, Node* node) {
node->parent = parent;
dll_push_back_npz(nil_node(), parent->first, parent->last, node, next, prev);
}
inline void
node_push_tag(Node* parent, Node* node) {
node->kind = NodeKind_Tag;
node->parent = parent;
dll_push_back_npz(nil_node(), parent->first_tag, parent->last_tag, node, next, prev);
}
//- rjf: tree introspection
Node* node_from_chain_string(Node* first, Node* opl, String8 string, StringMatchFlags flags);
Node* node_from_chain_index (Node* first, Node* opl, U64 index);
Node* node_from_chain_flags (Node* first, Node* opl, NodeFlags flags);
U64 index_from_node (Node* node);
Node* root_from_node (Node* node);
Node* child_from_string (Node* node, String8 child_string, StringMatchFlags flags);
Node* tag_from_string (Node* node, String8 tag_string, StringMatchFlags flags);
Node* child_from_index (Node* node, U64 index);
Node* tag_from_index (Node* node, U64 index);
Node* tag_arg_from_index (Node* node, String8 tag_string, StringMatchFlags flags, U64 index);
Node* tag_arg_from_string (Node* node, String8 tag_string, StringMatchFlags tag_str_flags, String8 arg_string, StringMatchFlags arg_str_flags);
B32 node_has_child (Node* node, String8 string, StringMatchFlags flags);
B32 node_has_tag (Node* node, String8 string, StringMatchFlags flags);
U64 child_count_from_node (Node* node);
U64 tag_count_from_node (Node* node);
MD_API String8 string_from_children__arena(Arena* arena, Node* root);
String8 string_from_children__ainfo(AllocatorInfo ainfo, Node* root);
#define string_from_children(allocator, root) _Generic(allocator, Arena*: string_from_children__arena, AllocatorInfo: string_from_children__ainfo, default: assert_generic_sel_fail) generic_call(allocator, root)
force_inline String8 string_from_children__arena(Arena* arena, Node* root) { return string_from_children__ainfo(arena_allocator(arena), root); }
inline Node*
node_from_chain_string(Node* first, Node* opl, String8 string, StringMatchFlags flags)
{
Node* result = nil_node();
for (Node* n = first; !node_is_nil(n) && n != opl; n = n->next)
{
if (str8_match(n->string, string, flags)) {
result = n;
break;
}
}
return result;
}
inline Node*
node_from_chain_index(Node* first, Node* opl, U64 index) {
Node* result = nil_node();
S64 idx = 0;
for (Node* n = first; !node_is_nil(n) && n != opl; n = n->next, idx += 1)
{
if (index == idx) {
result = n;
break;
}
}
return result;
}
inline Node*
node_from_chain_flags(Node* first, Node* opl, NodeFlags flags) {
Node* result = nil_node();
for (Node* n = first; !node_is_nil(n) && n != opl; n = n->next)
{
if (n->flags & flags) {
result = n;
break;
}
}
return result;
}
inline U64
index_from_node(Node* node) {
U64 index = 0;
for (Node* n = node->prev; !node_is_nil(n); n = n->prev) {
index += 1;
}
return index;
}
inline Node*
root_from_node(Node* node) {
Node* result = node;
for (Node* p = node->parent; (p->kind == NodeKind_Main || p->kind == NodeKind_Tag) && !node_is_nil(p); p = p->parent) {
result = p;
}
return result;
}
inline Node* child_from_string(Node* node, String8 child_string, StringMatchFlags flags) { return node_from_chain_string(node->first, nil_node(), child_string, flags); }
inline Node* tag_from_string (Node* node, String8 tag_string, StringMatchFlags flags) { return node_from_chain_string(node->first_tag, nil_node(), tag_string, flags); }
inline Node* child_from_index (Node* node, U64 index) { return node_from_chain_index (node->first, nil_node(), index); }
inline Node* tag_from_index (Node* node, U64 index) { return node_from_chain_index (node->first_tag, nil_node(), index); }
inline Node*
tag_arg_from_index(Node* node, String8 tag_string, StringMatchFlags flags, U64 index) {
Node* tag = tag_from_string(node, tag_string, flags);
return child_from_index(tag, index);
}
inline Node*
tag_arg_from_string(Node* node, String8 tag_string, StringMatchFlags tag_str_flags, String8 arg_string, StringMatchFlags arg_str_flags) {
Node* tag = tag_from_string (node, tag_string, tag_str_flags);
Node* arg = child_from_string(tag, arg_string, arg_str_flags);
return arg;
}
inline B32 node_has_child(Node* node, String8 string, StringMatchFlags flags) { return !node_is_nil(child_from_string(node, string, flags)); }
inline B32 node_has_tag (Node* node, String8 string, StringMatchFlags flags) { return !node_is_nil(tag_from_string (node, string, flags)); }
inline U64
child_count_from_node(Node *node) {
U64 result = 0;
for (Node* child = node->first; !node_is_nil(child); child = child->next) {
result += 1;
}
return result;
}
inline U64
tag_count_from_node(Node* node) {
U64 result = 0;
for (Node* child = node->first_tag; !node_is_nil(child); child = child->next) {
result += 1;
}
return result;
}
//- rjf: tree comparison
MD_API B32 tree_match(Node* a, Node* b, StringMatchFlags flags);
MD_API B32 node_match(Node* a, Node* b, StringMatchFlags flags);
//- rjf: tree duplication
MD_API Node* tree_copy__arena(Arena* arena, Node* src_root);
MD_API Node* tree_copy__ainfo(AllocatorInfo ainfo, Node* src_root);
#define tree_copy(allocator, src_root) _Generic(allocator, Arena*: tree_copy__arena, AllocatorInfo: tree_copy__ainfo, default: assert_generic_sel_fail) generic_call(allocator, src_root)
force_inline Node* tree_copy__arena(Arena* arena, Node* src_root) { return tree_copy__ainfo(arena_allocator(arena), src_root); }
////////////////////////////////
//~ rjf: Text -> Tokens Functions
MD_API TokenizeResult tokenize_from_text__arena(Arena* arena, String8 text);
MD_API TokenizeResult tokenize_from_text__ainfo(AllocatorInfo ainfo, String8 text);
#define tokenize_from_text(allocator, text) _Generic(allocator, Arena*: tokenize_from_text__arena, AllocatorInfo: tokenize_from_text__ainfo, default: assert_generic_sel_fail) generic_call(allocator, text)
force_inline TokenizeResult tokenize_from_text__arena(Arena* arena, String8 text) { return tokenize_from_text__ainfo(arena_allocator(arena), text); }
////////////////////////////////
//~ rjf: Tokens -> Tree Functions
MD_API ParseResult parse_from_text_tokens__arena(Arena* arena, String8 filename, String8 text, TokenArray tokens);
MD_API ParseResult parse_from_text_tokens__ainfo(AllocatorInfo ainfo, String8 filename, String8 text, TokenArray tokens);
#define parse_from_text_tokens(allocator, filename, text, tokens) _Generic(allocator, Arena*: parse_from_text_tokens__arena, AllocatorInfo: parse_from_text_tokens__ainfo, default: assert_generic_sel_fail) generic_call(allocator, filename, text, tokens)
force_inline ParseResult parse_from_text_tokens__arena(Arena* arena, String8 filename, String8 text, TokenArray tokens) { return parse_from_text_tokens__ainfo(arena_allocator(arena), filename, text, tokens); }
////////////////////////////////
//~ rjf: Bundled Text -> Tree Functions
MD_API ParseResult parse_from_text__arena(Arena* arena, String8 filename, String8 text);
MD_API ParseResult parse_from_text__ainfo(AllocatorInfo ainfo, String8 filename, String8 text);
#define parse_from_text(allocator, filename, text) _Generic(allocator, Arena*: parse_from_text__arena, AllocatorInfo: parse_from_text__ainfo, default: assert_generic_sel_fail) generic_call(allocator, filename, text)
#define tree_from_string(allocator, string) (parse_from_text((allocator), str8_zero(), (string)).root)
force_inline ParseResult parse_from_text__arena(Arena* arena, String8 filename, String8 text) { return parse_from_text__ainfo(arena_allocator(arena), filename, text); }
////////////////////////////////
//~ rjf: Tree -> Text Functions
MD_API String8List debug_string_list_from_tree__arena(Arena* arena, Node* root);
MD_API String8List debug_string_list_from_tree__ainfo(AllocatorInfo ainfo, Node* root);
#define debug_string_list_from_tree(allocator, string) _Generic(allocator, Arena*: debug_string_list_from_tree__arena, AllocatorInfo: debug_string_list_from_tree__ainfo, default: assert_generic_sel_fail) generic_call(allocator, string)
force_inline String8List debug_string_list_from_tree__arena(Arena* arena, Node* root) { return debug_string_list_from_tree__ainfo(arena_allocator(arena), root); }
+38
View File
@@ -0,0 +1,38 @@
#include "metadesk.h"
#undef MARKUP_LAYER_COLOR
#define MARKUP_LAYER_COLOR 0.20f, 0.60f, 0.80f
#include "base/platform.c"
#define STB_SPRINTF_IMPLEMENTATION
#if BUILD_STATIC
# #define STB_SPRINTF_STATIC
#endif
#include "third_party/stb/stb_sprintf.h"
MD_NS_BEGIN
#include "base/debug.c"
#include "base/memory_substrate.c"
#include "base/arena.c"
#include "base/strings.c"
#include "base/text.c"
#include "base/thread_context.c"
#include "base/markup.c"
#include "base/command_line.c"
#include "base/logger.c"
#include "base/entry_point.c"
#include "base/time.c"
#if OS_WINDOWS
# include "os/win32/os_win32.c"
#elif OS_LINUX
# include "os/win32/os_win32.c"
#endif
#include "os/os.c"
#include "mdesk/mdesk.c"
MD_NS_END
+48
View File
@@ -0,0 +1,48 @@
#ifdef INTELLISENSE_DIRECTIVES
# pragma once
#endif
#include "base/context_cracking.h"
#include "base/platform.h"
#include "base/linkage.h"
#include "base/macros.h"
#include "base/generic_macros.h"
#include "base/profiling.h"
#include "base/namespace.h"
#define STB_SPRINTF_DECORATE(name) md_##name
#include "third_party/stb/stb_sprintf.h"
MD_NS_BEGIN
#include "base/base_types.h"
#include "base/ring.h"
#include "base/debug.h"
#include "base/memory.h"
#include "base/memory_substrate.h"
#include "base/arena.h"
#include "base/space.h"
#include "base/math.h"
#include "base/sort.h"
#include "base/toolchain.h"
#include "base/time.h"
#include "base/strings.h"
#include "base/text.h"
#include "base/thread_context.h"
#include "base/command_line.h"
#include "base/markup.h"
#include "base/logger.h"
#include "base/entry_point.h"
#include "base/file.h"
#include "os/os.h"
MD_NS_END
#include "os/os_resolve.h"
MD_NS_BEGIN
#include "mdesk/mdesk.h"
MD_NS_END
+3
View File
@@ -0,0 +1,3 @@
# Metagen
File diff suppressed because it is too large Load Diff
+329
View File
@@ -0,0 +1,329 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef METAGEN_H
#define METAGEN_H
////////////////////////////////
//~ rjf: Message Type
typedef struct MG_Msg MG_Msg;
struct MG_Msg
{
String8 location;
String8 kind;
String8 msg;
};
typedef struct MG_MsgNode MG_MsgNode;
struct MG_MsgNode
{
MG_MsgNode *next;
MG_Msg v;
};
typedef struct MG_MsgList MG_MsgList;
struct MG_MsgList
{
MG_MsgNode *first;
MG_MsgNode *last;
U64 count;
};
////////////////////////////////
//~ rjf: Parse Artifact Types
typedef struct MG_FileParse MG_FileParse;
struct MG_FileParse
{
Node *root;
};
typedef struct MG_FileParseNode MG_FileParseNode;
struct MG_FileParseNode
{
MG_FileParseNode *next;
MG_FileParse v;
};
typedef struct MG_FileParseList MG_FileParseList;
struct MG_FileParseList
{
MG_FileParseNode *first;
MG_FileParseNode *last;
U64 count;
};
////////////////////////////////
//~ rjf: Map Type
typedef struct MG_MapNode MG_MapNode;
struct MG_MapNode
{
MG_MapNode *next;
String8 key;
void *val;
};
typedef struct MG_MapSlot MG_MapSlot;
struct MG_MapSlot
{
MG_MapNode *first;
MG_MapNode *last;
};
typedef struct MG_Map MG_Map;
struct MG_Map
{
MG_MapSlot *slots;
U64 slots_count;
};
////////////////////////////////
//~ rjf: String Expression Types
typedef enum MG_StrExprOpKind
{
MG_StrExprOpKind_Null,
MG_StrExprOpKind_Prefix,
MG_StrExprOpKind_Postfix,
MG_StrExprOpKind_Binary,
MG_StrExprOpKind_COUNT
}
MG_StrExprOpKind;
typedef enum MG_StrExprOp
{
MG_StrExprOp_Null,
#define MG_StrExprOp_FirstString MG_StrExprOp_Dot
MG_StrExprOp_Dot,
MG_StrExprOp_ExpandIfTrue,
MG_StrExprOp_Concat,
MG_StrExprOp_BumpToColumn,
#define MG_StrExprOp_LastString MG_StrExprOp_BumpToColumn
#define MG_StrExprOp_FirstNumeric MG_StrExprOp_Add
MG_StrExprOp_Add,
MG_StrExprOp_Subtract,
MG_StrExprOp_Multiply,
MG_StrExprOp_Divide,
MG_StrExprOp_Modulo,
MG_StrExprOp_LeftShift,
MG_StrExprOp_RightShift,
MG_StrExprOp_BitwiseAnd,
MG_StrExprOp_BitwiseOr,
MG_StrExprOp_BitwiseXor,
MG_StrExprOp_BitwiseNegate,
MG_StrExprOp_BooleanAnd,
MG_StrExprOp_BooleanOr,
MG_StrExprOp_BooleanNot,
MG_StrExprOp_Equals,
MG_StrExprOp_DoesNotEqual,
#define MG_StrExprOp_LastNumeric MG_StrExprOp_DoesNotEqual
MG_StrExprOp_COUNT,
}
MG_StrExprOp;
typedef struct MG_StrExpr MG_StrExpr;
struct MG_StrExpr
{
MG_StrExpr *parent;
MG_StrExpr *left;
MG_StrExpr *right;
MG_StrExprOp op;
Node *node;
};
typedef struct MG_StrExprParseResult MG_StrExprParseResult;
struct MG_StrExprParseResult
{
MG_StrExpr *root;
MsgList msgs;
Node *next_node;
};
////////////////////////////////
//~ rjf: Table Generation Types
typedef struct MG_NodeArray MG_NodeArray;
struct MG_NodeArray
{
Node **v;
U64 count;
};
typedef struct MG_NodeGrid MG_NodeGrid;
struct MG_NodeGrid
{
U64 x_stride;
U64 y_stride;
MG_NodeArray cells;
MG_NodeArray row_parents;
};
typedef enum MG_ColumnKind
{
MG_ColumnKind_DirectCell,
MG_ColumnKind_CheckForTag,
MG_ColumnKind_TagChild,
MG_ColumnKind_COUNT
}
MG_ColumnKind;
typedef struct MG_ColumnDesc MG_ColumnDesc;
struct MG_ColumnDesc
{
String8 name;
MG_ColumnKind kind;
String8 tag_name;
};
typedef struct MG_ColumnDescArray MG_ColumnDescArray;
struct MG_ColumnDescArray
{
U64 count;
MG_ColumnDesc *v;
};
typedef struct MG_TableExpandTask MG_TableExpandTask;
struct MG_TableExpandTask
{
MG_TableExpandTask *next;
String8 expansion_label;
MG_NodeGrid *grid;
MG_ColumnDescArray column_descs;
U64 count;
U64 idx;
};
typedef struct MG_TableExpandInfo MG_TableExpandInfo;
struct MG_TableExpandInfo
{
MG_TableExpandTask *first_expand_task;
String8 missing_value_fallback;
};
////////////////////////////////
//~ rjf: Main Output Path Types
typedef struct MG_Layer MG_Layer;
struct MG_Layer
{
String8 key;
B32 is_library;
String8 gen_folder_name;
String8 h_name_override;
String8 c_name_override;
String8List enums;
String8List structs;
String8List h_functions;
String8List h_tables;
String8List h_catchall;
String8List h_header;
String8List h_footer;
String8List c_functions;
String8List c_tables;
String8List c_catchall;
String8List c_header;
String8List c_footer;
};
typedef struct MG_LayerNode MG_LayerNode;
struct MG_LayerNode
{
MG_LayerNode *next;
MG_Layer v;
};
typedef struct MG_LayerSlot MG_LayerSlot;
struct MG_LayerSlot
{
MG_LayerNode *first;
MG_LayerNode *last;
};
typedef struct MG_State MG_State;
struct MG_State
{
U64 slots_count;
MG_LayerSlot *slots;
};
////////////////////////////////
//~ rjf: Globals
global Arena *mg_arena = 0;
global MG_State *mg_state = 0;
read_only global MG_StrExpr mg_str_expr_nil = {&mg_str_expr_nil, &mg_str_expr_nil, &mg_str_expr_nil};
////////////////////////////////
//~ rjf: Basic Helpers
internal U64 mg_hash_from_string(String8 string);
internal TxtPt mg_txt_pt_from_string_off(String8 string, U64 off);
////////////////////////////////
//~ rjf: Message Lists
internal void mg_msg_list_push(Arena *arena, MG_MsgList *msgs, MG_Msg *msg);
////////////////////////////////
//~ rjf: String Escaping
internal String8 mg_escaped_from_str8(Arena *arena, String8 string);
////////////////////////////////
//~ rjf: String Wrapping
internal String8List mg_wrapped_lines_from_string(Arena *arena, String8 string, U64 first_line_max_width, U64 max_width, U64 wrap_indent);
////////////////////////////////
//~ rjf: C-String-Izing
internal String8 mg_c_string_literal_from_multiline_string(String8 string);
internal String8 mg_c_array_literal_contents_from_data(String8 data);
////////////////////////////////
//~ rjf: Map Functions
internal MG_Map mg_push_map(Arena *arena, U64 slot_count);
internal void *mg_map_ptr_from_string(MG_Map *map, String8 string);
internal void mg_map_insert_ptr(Arena *arena, MG_Map *map, String8 string, void *val);
////////////////////////////////
//~ rjf: String Expression Parsing
internal MG_StrExpr *mg_push_str_expr(Arena *arena, MG_StrExprOp op, Node *node);
internal MG_StrExprParseResult mg_str_expr_parse_from_first_opl__min_prec(Arena *arena, Node *first, Node *opl, S8 min_prec);
internal MG_StrExprParseResult mg_str_expr_parse_from_first_opl(Arena *arena, Node *first, Node *opl);
internal MG_StrExprParseResult mg_str_expr_parse_from_root(Arena *arena, Node *root);
////////////////////////////////
//~ rjf: Table Generation Functions
internal MG_NodeArray mg_node_array_make(Arena *arena, U64 count);
internal MG_NodeArray mg_child_array_from_node(Arena *arena, Node *node);
internal MG_NodeGrid mg_node_grid_make_from_node(Arena *arena, Node *root);
internal MG_NodeArray mg_row_from_index(MG_NodeGrid grid, U64 index);
internal MG_NodeArray mg_column_from_index(Arena *arena, MG_NodeGrid grid, U64 index);
internal Node *mg_node_from_grid_xy(MG_NodeGrid grid, U64 x, U64 y);
internal MG_ColumnDescArray mg_column_desc_array_make(Arena *arena, U64 count, MG_ColumnDesc *descs);
internal MG_ColumnDescArray mg_column_desc_array_from_tag(Arena *arena, Node *tag);
internal U64 mg_column_index_from_name(MG_ColumnDescArray descs, String8 name);
internal String8 mg_string_from_row_desc_idx(Node *row_parent, MG_ColumnDescArray descs, U64 idx);
internal S64 mg_eval_table_expand_expr__numeric(MG_StrExpr *expr, MG_TableExpandInfo *info);
internal void mg_eval_table_expand_expr__string(Arena *arena, MG_StrExpr *expr, MG_TableExpandInfo *info, String8List *out);
internal void mg_loop_table_column_expansion(Arena *arena, String8 strexpr, MG_TableExpandInfo *info, MG_TableExpandTask *task, String8List *out);
internal String8List mg_string_list_from_table_gen(Arena *arena, MG_Map grid_name_map, MG_Map grid_column_desc_map, String8 fallback, Node *gen);
////////////////////////////////
//~ rjf: Layer Lookup Functions
internal String8 mg_layer_key_from_path(String8 path);
internal MG_Layer *mg_layer_from_key(String8 key);
#endif //METAGEN_H
+656
View File
@@ -0,0 +1,656 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
////////////////////////////////
//~ rjf: Build Options
#define BUILD_CONSOLE_INTERFACE 1
////////////////////////////////
//~ rjf: Includes
//- rjf: headers
#include "metagen/metagen_base/metagen_base_inc.h"
#include "metagen/metagen_os/metagen_os_inc.h"
#include "mdesk/mdesk.h"
#include "metagen.h"
//- rjf: impls
#include "metagen/metagen_base/metagen_base_inc.c"
#include "metagen/metagen_os/metagen_os_inc.c"
#include "mdesk/mdesk.c"
#include "metagen.c"
////////////////////////////////
//~ rjf: Entry Point
internal void
entry_point(CmdLine *cmdline)
{
//////////////////////////////
//- rjf: set up state
//
MG_MsgList msgs = {0};
mg_arena = arena_alloc(.reserve_size = GB(64), .commit_size = MB(64));
mg_state = push_array(mg_arena, MG_State, 1);
mg_state->slots_count = 256;
mg_state->slots = push_array(mg_arena, MG_LayerSlot, mg_state->slots_count);
//////////////////////////////
//- rjf: extract paths
//
String8 build_dir_path = os_get_process_info()->binary_path;
String8 project_dir_path = str8_chop_last_slash(build_dir_path);
String8 code_dir_path = push_str8f(mg_arena, "%S/src", project_dir_path);
//////////////////////////////
//- rjf: search code directories for all files to consider
//
String8List file_paths = {0};
DeferLoop(printf("searching %.*s...", str8_varg(code_dir_path)), printf(" %i files found\n", (int)file_paths.node_count))
{
typedef struct Task Task;
struct Task
{
Task *next;
String8 path;
};
Task start_task = {0, code_dir_path};
Task* first_task = &start_task;
Task* last_task = &start_task;
for(Task *task = first_task; task != 0; task = task->next)
{
OS_FileIter* it = os_file_iter_begin(mg_arena, task->path, 0);
for(OS_FileInfo info = {0}; os_file_iter_next(mg_arena, it, &info);)
{
String8 file_path = push_str8f(mg_arena, "%S/%S", task->path, info.name);
if(info.props.flags & FilePropertyFlag_IsFolder)
{
Task *next_task = push_array(mg_arena, Task, 1);
SLLQueuePush(first_task, last_task, next_task);
next_task->path = file_path;
}
else
{
str8_list_push(mg_arena, &file_paths, file_path);
}
}
os_file_iter_end(it);
}
}
//////////////////////////////
//- rjf: parse all metadesk files
//
MG_FileParseList parses = {0};
DeferLoop(printf("parsing metadesk..."), printf(" %i metadesk files parsed\n", (int)parses.count))
{
for(String8Node *n = file_paths.first; n != 0; n = n->next)
{
String8 file_path = n->string;
String8 file_ext = str8_skip_last_dot(file_path);
if(str8_match(file_ext, str8_lit("mdesk"), 0))
{
String8 data = os_data_from_file_path(mg_arena, file_path);
TokenizeResult tokenize = tokenize_from_text(mg_arena, data);
ParseResult parse = parse_from_text_tokens(mg_arena, file_path, data, tokenize.tokens);
for(Msg *m = parse.msgs.first; m != 0; m = m->next)
{
TxtPt pt = mg_txt_pt_from_string_off(data, m->node->src_offset);
String8 msg_kind_string = {0};
switch(m->kind)
{
default:{}break;
case MD_MsgKind_Note: {msg_kind_string = str8_lit("note");}break;
case MsgKind_Warning: {msg_kind_string = str8_lit("warning");}break;
case MsgKind_Error: {msg_kind_string = str8_lit("error");}break;
case MD_MsgKind_FatalError: {msg_kind_string = str8_lit("fatal error");}break;
}
String8 location = push_str8f(mg_arena, "%S:%I64d:%I64d", file_path, pt.line, pt.column);
MG_Msg dst_m = {location, msg_kind_string, m->string};
mg_msg_list_push(mg_arena, &msgs, &dst_m);
}
MG_FileParseNode *parse_n = push_array(mg_arena, MG_FileParseNode, 1);
SLLQueuePush(parses.first, parses.last, parse_n);
parse_n->v.root = parse.root;
parses.count += 1;
}
}
}
//////////////////////////////
//- rjf: gather tables
//
MG_Map table_grid_map = mg_push_map(mg_arena, 1024);
MG_Map table_col_map = mg_push_map(mg_arena, 1024);
U64 table_count = 0;
DeferLoop(printf("gathering tables..."), printf(" %i tables found\n", (int)table_count))
{
for(MG_FileParseNode *n = parses.first; n != 0; n = n->next)
{
Node *file = n->v.root;
for each_node(node, file->first)
{
Node *table_tag = tag_from_string(node, str8_lit("table"), 0);
if(!node_is_nil(table_tag))
{
MG_NodeGrid *table = push_array(mg_arena, MG_NodeGrid, 1);
MG_ColumnDescArray *col_descs = push_array(mg_arena, MG_ColumnDescArray, 1);
*table = mg_node_grid_make_from_node(mg_arena, node);
*col_descs = mg_column_desc_array_from_tag(mg_arena, table_tag);
mg_map_insert_ptr(mg_arena, &table_grid_map, node->string, table);
mg_map_insert_ptr(mg_arena, &table_col_map, node->string, col_descs);
table_count += 1;
}
}
}
}
//////////////////////////////
//- rjf: gather layer options
//
for(MG_FileParseNode *n = parses.first; n != 0; n = n->next)
{
Node *file = n->v.root;
String8 layer_key = mg_layer_key_from_path(file->string);
MG_Layer *layer = mg_layer_from_key(layer_key);
for each_node(node, file->first)
{
if(node_has_tag(node, str8_lit("option"), 0))
{
if(str8_match(node->string, str8_lit("library"), 0))
{
layer->is_library = 1;
}
}
if(node_has_tag(node, str8_lit("gen_folder"), 0))
{
layer->gen_folder_name = node->string;
}
if(node_has_tag(node, str8_lit("h_name"), 0))
{
layer->h_name_override = node->string;
}
if(node_has_tag(node, str8_lit("c_name"), 0))
{
layer->c_name_override = node->string;
}
if(node_has_tag(node, str8_lit("h_header"), 0))
{
String8List gen_strings = mg_string_list_from_table_gen(mg_arena, table_grid_map, table_col_map, str8_lit(""), node);
for(String8Node *n = gen_strings.first; n != 0; n = n->next)
{
str8_list_push(mg_arena, &layer->h_header, n->string);
str8_list_push(mg_arena, &layer->h_header, str8_lit("\n"));
}
}
if(node_has_tag(node, str8_lit("h_footer"), 0))
{
String8List gen_strings = mg_string_list_from_table_gen(mg_arena, table_grid_map, table_col_map, str8_lit(""), node);
for(String8Node *n = gen_strings.first; n != 0; n = n->next)
{
str8_list_push(mg_arena, &layer->h_footer, n->string);
str8_list_push(mg_arena, &layer->h_footer, str8_lit("\n"));
}
}
if(node_has_tag(node, str8_lit("c_header"), 0))
{
String8List gen_strings = mg_string_list_from_table_gen(mg_arena, table_grid_map, table_col_map, str8_lit(""), node);
for(String8Node *n = gen_strings.first; n != 0; n = n->next)
{
str8_list_push(mg_arena, &layer->c_header, n->string);
str8_list_push(mg_arena, &layer->c_header, str8_lit("\n"));
}
}
if(node_has_tag(node, str8_lit("c_footer"), 0))
{
String8List gen_strings = mg_string_list_from_table_gen(mg_arena, table_grid_map, table_col_map, str8_lit(""), node);
for(String8Node *n = gen_strings.first; n != 0; n = n->next)
{
str8_list_push(mg_arena, &layer->c_footer, n->string);
str8_list_push(mg_arena, &layer->c_footer, str8_lit("\n"));
}
}
}
}
//////////////////////////////
//- rjf: generate enums
//
for(MG_FileParseNode *n = parses.first; n != 0; n = n->next)
{
Node *file = n->v.root;
for each_node(node, file->first)
{
Node *tag = tag_from_string(node, str8_lit("enum"), 0);
if(!node_is_nil(tag))
{
String8 enum_name = node->string;
String8 enum_member_prefix = enum_name;
if(str8_match(str8_postfix(enum_name, 5), str8_lit("Flags"), 0))
{
enum_member_prefix = str8_chop(enum_name, 1);
}
String8 enum_base_type_name = tag->first->string;
String8 layer_key = mg_layer_key_from_path(file->string);
MG_Layer *layer = mg_layer_from_key(layer_key);
String8List gen_strings = mg_string_list_from_table_gen(mg_arena, table_grid_map, table_col_map, str8_lit(""), node);
if(enum_base_type_name.size == 0)
{
str8_list_pushf(mg_arena, &layer->enums, "typedef enum %S\n{\n", enum_name);
}
else
{
str8_list_pushf(mg_arena, &layer->enums, "typedef %S %S;\n", enum_base_type_name, enum_name);
str8_list_pushf(mg_arena, &layer->enums, "typedef enum %SEnum\n{\n", enum_name);
}
for(String8Node *n = gen_strings.first; n != 0; n = n->next)
{
String8 escaped = mg_escaped_from_str8(mg_arena, n->string);
str8_list_pushf(mg_arena, &layer->enums, "%S_%S,\n", enum_member_prefix, escaped);
}
if(enum_base_type_name.size == 0)
{
str8_list_pushf(mg_arena, &layer->enums, "} %S;\n\n", enum_name);
}
else
{
str8_list_pushf(mg_arena, &layer->enums, "} %SEnum;\n\n", enum_name);
}
}
}
}
//////////////////////////////
//- rjf: generate xlists
//
for(MG_FileParseNode *n = parses.first; n != 0; n = n->next)
{
Node *file = n->v.root;
for each_node(node, file->first)
{
Node *tag = tag_from_string(node, str8_lit("xlist"), 0);
if(!node_is_nil(tag))
{
String8 layer_key = mg_layer_key_from_path(file->string);
MG_Layer *layer = mg_layer_from_key(layer_key);
String8List gen_strings = mg_string_list_from_table_gen(mg_arena, table_grid_map, table_col_map, str8_lit(""), node);
str8_list_pushf(mg_arena, &layer->enums, "#define %S \\\n", node->string);
for(String8Node *n = gen_strings.first; n != 0; n = n->next)
{
String8 escaped = mg_escaped_from_str8(mg_arena, n->string);
str8_list_pushf(mg_arena, &layer->enums, "X(%S)\\\n", escaped);
}
str8_list_push(mg_arena, &layer->enums, str8_lit("\n"));
}
}
}
//////////////////////////////
//- rjf: generate structs
//
for(MG_FileParseNode *n = parses.first; n != 0; n = n->next)
{
Node *file = n->v.root;
for each_node(node, file->first)
{
if(node_has_tag(node, str8_lit("struct"), 0))
{
String8 layer_key = mg_layer_key_from_path(file->string);
MG_Layer *layer = mg_layer_from_key(layer_key);
String8List gen_strings = mg_string_list_from_table_gen(mg_arena, table_grid_map, table_col_map, str8_lit(""), node);
str8_list_pushf(mg_arena, &layer->structs, "typedef struct %S %S;\n", node->string, node->string);
str8_list_pushf(mg_arena, &layer->structs, "struct %S\n{\n", node->string);
for(String8Node *n = gen_strings.first; n != 0; n = n->next)
{
String8 escaped = mg_escaped_from_str8(mg_arena, n->string);
str8_list_pushf(mg_arena, &layer->structs, "%S;\n", escaped);
}
str8_list_pushf(mg_arena, &layer->structs, "};\n\n");
}
}
}
//////////////////////////////
//- rjf: generate data tables
//
for(MG_FileParseNode *n = parses.first; n != 0; n = n->next)
{
Node *file = n->v.root;
for each_node(node, file->first)
{
Node *tag = tag_from_string(node, str8_lit("data"), 0);
if(!node_is_nil(tag))
{
String8 element_type = tag->first->string;
String8 layer_key = mg_layer_key_from_path(file->string);
MG_Layer *layer = mg_layer_from_key(layer_key);
String8List gen_strings = mg_string_list_from_table_gen(mg_arena, table_grid_map, table_col_map, str8_lit(""), node);
if(!node_has_tag(node, str8_lit("c_file"), 0))
{
str8_list_pushf(mg_arena, &layer->h_tables, "extern %S %S[%I64u];\n", element_type, node->string, gen_strings.node_count);
}
str8_list_pushf(mg_arena, &layer->c_tables, "%S %S[%I64u] =\n{\n", element_type, node->string, gen_strings.node_count);
for(String8Node *n = gen_strings.first; n != 0; n = n->next)
{
String8 escaped = mg_escaped_from_str8(mg_arena, n->string);
str8_list_pushf(mg_arena, &layer->c_tables, "%S,\n", escaped);
}
str8_list_push(mg_arena, &layer->c_tables, str8_lit("};\n\n"));
}
}
}
//////////////////////////////
//- rjf: generate enum -> string mapping functions
//
for(MG_FileParseNode *n = parses.first; n != 0; n = n->next)
{
Node *file = n->v.root;
for each_node(node, file->first)
{
Node *tag = tag_from_string(node, str8_lit("enum2string_switch"), 0);
if(!node_is_nil(tag))
{
String8 enum_type = tag->first->string;
String8 layer_key = mg_layer_key_from_path(file->string);
MG_Layer *layer = mg_layer_from_key(layer_key);
String8List gen_strings = mg_string_list_from_table_gen(mg_arena, table_grid_map, table_col_map, str8_lit(""), node);
str8_list_pushf(mg_arena, &layer->h_functions, "internal String8 %S(%S v);\n", node->string, enum_type);
str8_list_pushf(mg_arena, &layer->c_functions, "internal String8\n%S(%S v)\n{\n", node->string, enum_type);
str8_list_pushf(mg_arena, &layer->c_functions, "String8 result = str8_lit(\"<Unknown %S>\");\n", enum_type);
str8_list_pushf(mg_arena, &layer->c_functions, "switch(v)\n");
str8_list_pushf(mg_arena, &layer->c_functions, "{\n");
str8_list_pushf(mg_arena, &layer->c_functions, "default:{}break;\n");
for(String8Node *n = gen_strings.first; n != 0; n = n->next)
{
String8 escaped = mg_escaped_from_str8(mg_arena, n->string);
str8_list_pushf(mg_arena, &layer->c_functions, "%S;\n", escaped);
}
str8_list_pushf(mg_arena, &layer->c_functions, "}\n");
str8_list_pushf(mg_arena, &layer->c_functions, "return result;\n");
str8_list_pushf(mg_arena, &layer->c_functions, "}\n\n");
}
}
}
//////////////////////////////
//- rjf: generate catch-all generations
//
for(MG_FileParseNode *n = parses.first; n != 0; n = n->next)
{
Node *file = n->v.root;
for each_node(node, file->first)
{
Node *tag = tag_from_string(node, str8_lit("gen"), 0);
if(!node_is_nil(tag))
{
String8 layer_key = mg_layer_key_from_path(file->string);
MG_Layer *layer = mg_layer_from_key(layer_key);
B32 prefer_c_file = node_has_tag(node, str8_lit("c_file"), 0);
String8List *out = prefer_c_file ? &layer->c_catchall : &layer->h_catchall;
if(tag->first->string.size == 0){}
else if(str8_match(tag->first->string, str8_lit("enums"), 0)) { out = &layer->enums; }
else if(str8_match(tag->first->string, str8_lit("structs"), 0)) { out = &layer->structs; }
else if(str8_match(tag->first->string, str8_lit("functions"), 0)) { out = prefer_c_file ? &layer->c_functions : &layer->h_functions; }
else if(str8_match(tag->first->string, str8_lit("tables"), 0)) { out = prefer_c_file ? &layer->c_tables : &layer->h_tables; }
String8List gen_strings = mg_string_list_from_table_gen(mg_arena, table_grid_map, table_col_map, str8_lit(""), node);
for(String8Node *n = gen_strings.first; n != 0; n = n->next)
{
String8 trimmed = str8_skip_chop_whitespace(n->string);
String8 escaped = mg_escaped_from_str8(mg_arena, trimmed);
str8_list_push(mg_arena, out, escaped);
str8_list_push(mg_arena, out, str8_lit("\n"));
}
}
}
}
//////////////////////////////
//- rjf: gather & generate all embeds
//
for(MG_FileParseNode *n = parses.first; n != 0; n = n->next)
{
Node *file = n->v.root;
for each_node(node, file->first)
{
if(node_has_tag(node, str8_lit("embed_string"), 0))
{
String8 layer_key = mg_layer_key_from_path(file->string);
MG_Layer *layer = mg_layer_from_key(layer_key);
String8 embed_string = mg_c_string_literal_from_multiline_string(node->first->string);
str8_list_pushf(mg_arena, &layer->h_tables, "read_only global String8 %S =\nstr8_lit_comp(\n", node->string);
str8_list_push (mg_arena, &layer->h_tables, embed_string);
str8_list_pushf(mg_arena, &layer->h_tables, ");\n\n");
}
if(node_has_tag(node, str8_lit("embed_file"), 0))
{
String8 layer_key = mg_layer_key_from_path(file->string);
MG_Layer *layer = mg_layer_from_key(layer_key);
String8 data = os_data_from_file_path(mg_arena, node->first->string);
String8 embed_string = mg_c_array_literal_contents_from_data(data);
str8_list_pushf(mg_arena, &layer->h_tables, "read_only global U8 %S__data[] =\n{\n", node->string);
str8_list_push (mg_arena, &layer->h_tables, embed_string);
str8_list_pushf(mg_arena, &layer->h_tables, "};\n\n");
str8_list_pushf(mg_arena, &layer->h_tables, "read_only global String8 %S = {%S__data, sizeof(%S__data)};\n",
node->string,
node->string,
node->string);
}
}
}
//////////////////////////////
//- rjf: generate all markdown in build folder
//
for(MG_FileParseNode *n = parses.first; n != 0; n = n->next)
{
Node *file = n->v.root;
for each_node(node, file->first)
{
//- rjf: generate markdown page
if(node_has_tag(node, str8_lit("markdown"), 0))
{
String8List md_strs = {0};
for(Node *piece = node->first; !node_is_nil(piece); piece = piece->next)
{
if(node_has_tag(piece, str8_lit("title"), 0))
{
str8_list_pushf(mg_arena, &md_strs, "# %S\n\n", piece->string);
}
if(node_has_tag(piece, str8_lit("subtitle"), 0))
{
str8_list_pushf(mg_arena, &md_strs, "## %S\n\n", piece->string);
}
if(node_has_tag(piece, str8_lit("p"), 0))
{
String8 paragraph_text = piece->string;
String8List paragraph_lines = mg_wrapped_lines_from_string(mg_arena, paragraph_text, 80, 80, 0);
for(String8Node *n = paragraph_lines.first; n != 0; n = n->next)
{
str8_list_push(mg_arena, &md_strs, n->string);
str8_list_push(mg_arena, &md_strs, str8_lit("\n"));
}
str8_list_push(mg_arena, &md_strs, str8_lit("\n"));
}
if(node_has_tag(piece, str8_lit("unordered_list"), 0))
{
String8List gen_strings = mg_string_list_from_table_gen(mg_arena, table_grid_map, table_col_map, str8_lit(""), piece);
for(String8Node *n = gen_strings.first; n != 0; n = n->next)
{
str8_list_pushf(mg_arena, &md_strs, " - ");
String8 item_text = n->string;
String8List item_lines = mg_wrapped_lines_from_string(mg_arena, item_text, 80-3, 80, 3);
for(String8Node *line_n = item_lines.first; line_n != 0; line_n = line_n->next)
{
str8_list_push(mg_arena, &md_strs, line_n->string);
str8_list_pushf(mg_arena, &md_strs, "\n");
}
}
str8_list_pushf(mg_arena, &md_strs, "\n");
}
}
String8 output_path = push_str8f(mg_arena, "%S/%S.md", build_dir_path, node->string);
FILE *file = fopen((char *)output_path.str, "w");
for(String8Node *n = md_strs.first; n != 0; n = n->next)
{
fwrite(n->string.str, n->string.size, 1, file);
}
fclose(file);
}
}
}
//////////////////////////////
//- rjf: write all layer output files
//
DeferLoop(printf("generating layer code..."), printf("\n"))
{
for(U64 slot_idx = 0; slot_idx < mg_state->slots_count; slot_idx += 1)
{
MG_LayerSlot *slot = &mg_state->slots[slot_idx];
for(MG_LayerNode *n = slot->first; n != 0; n = n->next)
{
MG_Layer *layer = &n->v;
String8 layer_generated_folder = {0};
if(layer->gen_folder_name.size != 0)
{
String8 gen_folder = layer->gen_folder_name;
layer_generated_folder = push_str8f(mg_arena, "%S/%S", code_dir_path, gen_folder);
}
else
{
String8 gen_folder = str8_lit("generated");
layer_generated_folder = push_str8f(mg_arena, "%S/%S/%S", code_dir_path, layer->key, gen_folder);
}
if(os_make_directory(layer_generated_folder))
{
String8List layer_key_parts = str8_split_path(mg_arena, layer->key);
StringJoin join = {0};
join.sep = str8_lit("_");
String8 layer_key_filename = str8_list_join(mg_arena, &layer_key_parts, &join);
String8 layer_key_filename_upper = upper_from_str8(mg_arena, layer_key_filename);
String8 h_path = push_str8f(mg_arena, "%S/%S.meta.h", layer_generated_folder, layer_key_filename);
String8 c_path = push_str8f(mg_arena, "%S/%S.meta.c", layer_generated_folder, layer_key_filename);
if(layer->h_name_override.size != 0)
{
h_path = push_str8f(mg_arena, "%S/%S", layer_generated_folder, str8_skip_last_slash(layer->h_name_override));
}
if(layer->c_name_override.size != 0)
{
c_path = push_str8f(mg_arena, "%S/%S", layer_generated_folder, str8_skip_last_slash(layer->c_name_override));
}
{
FILE *h = fopen((char *)h_path.str, "w");
fprintf(h, "// Copyright (c) 2024 Epic Games Tools\n");
fprintf(h, "// Licensed under the MIT license (https://opensource.org/license/mit/)\n\n");
if(layer->h_header.first == 0)
{
fprintf(h, "//- GENERATED CODE\n\n");
fprintf(h, "#ifndef %.*s_META_H\n", str8_varg(layer_key_filename_upper));
fprintf(h, "#define %.*s_META_H\n\n", str8_varg(layer_key_filename_upper));
}
else for(String8Node *n = layer->h_header.first; n != 0; n = n->next)
{
fwrite(n->string.str, n->string.size, 1, h);
}
for(String8Node *n = layer->enums.first; n != 0; n = n->next)
{
fwrite(n->string.str, n->string.size, 1, h);
}
for(String8Node *n = layer->structs.first; n != 0; n = n->next)
{
fwrite(n->string.str, n->string.size, 1, h);
}
for(String8Node *n = layer->h_catchall.first; n != 0; n = n->next)
{
fwrite(n->string.str, n->string.size, 1, h);
}
for(String8Node *n = layer->h_functions.first; n != 0; n = n->next)
{
fwrite(n->string.str, n->string.size, 1, h);
}
if(layer->h_tables.first != 0)
{
if(!layer->is_library)
{
fprintf(h, "C_LINKAGE_BEGIN\n");
}
for(String8Node *n = layer->h_tables.first; n != 0; n = n->next)
{
fwrite(n->string.str, n->string.size, 1, h);
}
fprintf(h, "\n");
if(!layer->is_library)
{
fprintf(h, "C_LINKAGE_END\n\n");
}
}
if(layer->h_footer.first == 0)
{
fprintf(h, "#endif // %.*s_META_H\n", str8_varg(layer_key_filename_upper));
}
else for(String8Node *n = layer->h_footer.first; n != 0; n = n->next)
{
fwrite(n->string.str, n->string.size, 1, h);
}
fclose(h);
}
{
FILE *c = fopen((char *)c_path.str, "w");
fprintf(c, "// Copyright (c) 2024 Epic Games Tools\n");
fprintf(c, "// Licensed under the MIT license (https://opensource.org/license/mit/)\n\n");
if(layer->c_header.first == 0)
{
fprintf(c, "//- GENERATED CODE\n\n");
}
else for(String8Node *n = layer->c_header.first; n != 0; n = n->next)
{
fwrite(n->string.str, n->string.size, 1, c);
}
for(String8Node *n = layer->c_catchall.first; n != 0; n = n->next)
{
fwrite(n->string.str, n->string.size, 1, c);
}
if(layer->c_tables.first != 0)
{
if(!layer->is_library)
{
fprintf(c, "C_LINKAGE_BEGIN\n");
}
for(String8Node *n = layer->c_tables.first; n != 0; n = n->next)
{
fwrite(n->string.str, n->string.size, 1, c);
}
if(!layer->is_library)
{
fprintf(c, "C_LINKAGE_END\n\n");
}
}
for(String8Node *n = layer->c_functions.first; n != 0; n = n->next)
{
fwrite(n->string.str, n->string.size, 1, c);
}
if(layer->c_footer.first != 0)
{
for(String8Node *n = layer->c_footer.first; n != 0; n = n->next)
{
fwrite(n->string.str, n->string.size, 1, c);
}
}
fclose(c);
}
}
}
}
}
//////////////////////////////
//- rjf: write out all messages to stderr
//
for(MG_MsgNode *n = msgs.first; n != 0; n = n->next)
{
MG_Msg *msg = &n->v;
fprintf(stderr, "%.*s: %.*s: %.*s\n", str8_varg(msg->location), str8_varg(msg->kind), str8_varg(msg->msg));
}
}
File diff suppressed because it is too large Load Diff
+220
View File
@@ -0,0 +1,220 @@
#ifdef INTELLISENSE_DIRECTIVES
# pragma once
# include "base/debug.h"
# include "base/strings.h"
# include "base/thread_context.h"
# include "os/os.h"
# include "os/linux/os_linux_includes.h"
#endif
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
int pthread_setname_np(pthread_t thread, const char* name);
int pthread_getname_np(pthread_t thread, char* name, size_t size);
typedef struct tm tm;
typedef struct timespec timespec;
////////////////////////////////
//~ rjf: File Iterator
typedef struct OS_LNX_FileIter OS_LNX_FileIter;
struct OS_LNX_FileIter
{
DIR* dir;
struct dirent* dp;
String8 path;
};
md_assert(sizeof(Member(OS_FileIter, memory)) >= sizeof(OS_LNX_FileIter), os_lnx_file_iter_size_check);
////////////////////////////////
//~ rjf: Safe Call Handler Chain
typedef struct OS_LNX_SafeCallChain OS_LNX_SafeCallChain;
struct OS_LNX_SafeCallChain
{
OS_LNX_SafeCallChain* next;
OS_ThreadFunctionType* fail_handler;
void *ptr;
};
////////////////////////////////
//~ rjf: Entities
typedef enum OS_LNX_EntityKind OS_LNX_EntityKind
enum OS_LNX_EntityKind
{
OS_LNX_EntityKind_Thread,
OS_LNX_EntityKind_Mutex,
OS_LNX_EntityKind_RWMutex,
OS_LNX_EntityKind_ConditionVariable,
};
typedef struct OS_LNX_EntityThread OS_LNX_EntityThread;
struct OS_LNX_EntityThread
{
pthread_t handle;
OS_ThreadFunctionType* func;
void* ptr;
};
typedef struct OS_LNX_Entity OS_LNX_Entity;
struct OS_LNX_Entity
{
OS_LNX_Entity* next;
OS_LNX_EntityKind kind;
union
{
OS_LNX_EntityThread thread;
pthread_mutex_t mutex_handle;
pthread_rwlock_t rwmutex_handle;
struct {
pthread_cond_t cond_handle;
pthread_mutex_t rwlock_mutex_handle;
} cv;
};
};
////////////////////////////////
//~ rjf: State
typedef struct OS_LNX_State OS_LNX_State;
struct OS_LNX_State
{
Arena* arena;
OS_SystemInfo system_info;
OS_ProcessInfo process_info;
pthread_mutex_t entity_mutex;
Arena* entity_arena;
OS_LNX_Entity* entity_free;
};
////////////////////////////////
//~ rjf: Helpers
DateTime os_lnx_date_time_from_tm (tm in, U32 msec);
tm os_lnx_tm_from_date_time (DateTime dt);
timespec os_lnx_timespec_from_date_time (DateTime dt);
DenseTime os_lnx_dense_time_from_timespec (timespec in);
FileProperties os_lnx_file_properties_from_stat(struct stat* s);
void os_lnx_safe_call_sig_handler (int x);
inline DateTime
os_lnx_date_time_from_tm(tm in, U32 msec) {
DateTime dt = {0};
dt.sec = in.tm_sec;
dt.min = in.tm_min;
dt.hour = in.tm_hour;
dt.day = in.tm_mday-1;
dt.mon = in.tm_mon;
dt.year = in.tm_year+1900;
dt.msec = msec;
return dt;
}
inline tm
os_lnx_tm_from_date_time(DateTime dt) {
tm result = {0};
result.tm_sec = dt.sec;
result.tm_min = dt.min;
result.tm_hour= dt.hour;
result.tm_mday= dt.day+1;
result.tm_mon = dt.mon;
result.tm_year= dt.year-1900;
return result;
}
inline timespec
os_lnx_timespec_from_date_time(DateTime dt) {
tm tm_val = os_lnx_tm_from_date_time(dt);
time_t seconds = timegm(&tm_val);
timespec result = {0};
result.tv_sec = seconds;
return result;
}
inline DenseTime
os_lnx_dense_time_from_timespec(timespec in) {
DenseTime result = 0; {
struct tm tm_time = {0};
gmtime_r(&in.tv_sec, &tm_time);
DateTime date_time = os_lnx_date_time_from_tm(tm_time, in.tv_nsec/Million(1));
result = dense_time_from_date_time(date_time);
}
return result;
}
inline FileProperties
os_lnx_file_properties_from_stat(struct stat* s) {
FileProperties props = {0};
props.size = s->st_size;
props.created = os_lnx_dense_time_from_timespec(s->st_ctim);
props.modified = os_lnx_dense_time_from_timespec(s->st_mtim);
if (s->st_mode & S_IFDIR) {
props.flags |= FilePropertyFlag_IsFolder;
}
return props;
}
inline void
os_lnx_safe_call_sig_handler(int x) {
OS_LNX_SafeCallChain* chain = os_lnx_safe_call_chain;
if (chain != 0 && chain->fail_handler != 0) {
chain->fail_handler(chain->ptr);
}
abort();
}
////////////////////////////////
//~ rjf: Entities
MD_API OS_LNX_Entity* os_lnx_entity_alloc (OS_LNX_EntityKind kind);
MD_API void os_lnx_entity_release(OS_LNX_Entity* entity);
////////////////////////////////
//~ rjf: Thread Entry Point
MD_API void* os_lnx_thread_entry_point(void* ptr);
////////////////////////////////
//~ rjf: @os_hooks System/Process Info (Implemented Per-OS)
inline String8
os_get_current_path__ainfo(AllocatorInfo ainfo) {
char* cwdir = getcwd(0, 0);
String8 string = str8_copy(ainfo, str8_cstring(cwdir));
return string;
}
////////////////////////////////
//~ rjf: @os_hooks Memory Allocation (Implemented Per-OS)
//- rjf: basic
inline void* os_reserve ( U64 size) { void* result = mmap(0, size, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); return result; }
inline B32 os_commit (void *ptr, U64 size) { mprotect(ptr, size, PROT_READ | PROT_WRITE); return 1; }
inline void os_decommit(void *ptr, U64 size) { madvise(ptr, size, MADV_DONTNEED); mprotect(ptr, size, PROT_NONE); }
inline void os_release (void *ptr, U64 size) { munmap(ptr, size); }
//- rjf: large pages
inline void* os_reserve_large( U64 size) { void* result = mmap(0, size, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB, -1, 0); return result; }
inline B32 os_commit_large (void *ptr, U64 size) { mprotect(ptr, size, PROT_READ | PROT_WRITE); return 1; }
////////////////////////////////
//~ rjf: @os_hooks Thread Info (Implemented Per-OS)
inline U32
os_tid(void) {
U32 result = 0;
#if defined(SYS_gettid)
result = syscall(SYS_gettid);
#else
result = gettid();
#endif
return result;
}
+25
View File
@@ -0,0 +1,25 @@
#ifdef INTELLISENSE_DIRECTIVES
# pragma once
#endif
////////////////////////////////
//~ rjf: Includes
#define _GNU_SOURCE
#include <features.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <linux/limits.h>
#include <time.h>
#include <dirent.h>
#include <pthread.h>
#include <sys/syscall.h>
#include <signal.h>
#include <errno.h>
#include <dlfcn.h>
#include <sys/sysinfo.h>
#include <sys/random.h>
+88
View File
@@ -0,0 +1,88 @@
#ifdef INTELLISENSE_DIRECTIVES
# include "os.h"
#endif
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
////////////////////////////////
//~ rjf: Filesystem Helpers (Helpers, Implemented Once)
B32
os_write_data_to_file_path(String8 path, String8 data)
{
B32 good = 0;
OS_Handle file = os_file_open(OS_AccessFlag_Write, path);
if(! os_handle_match(file, os_handle_zero()))
{
good = 1;
os_file_write(file, r1u64(0, data.size), data.str);
os_file_close(file);
}
return good;
}
B32
os_write_data_list_to_file_path(String8 path, String8List list)
{
B32 good = 0;
OS_Handle file = os_file_open(OS_AccessFlag_Write, path);
if( ! os_handle_match(file, os_handle_zero()))
{
good = 1;
U64 off = 0;
for(String8Node* n = list.first; n != 0; n = n->next)
{
os_file_write(file, r1u64(off, off+n->string.size), n->string.str);
off += n->string.size;
}
os_file_close(file);
}
return good;
}
B32
os_append_data_to_file_path(String8 path, String8 data)
{
B32 good = 0;
if(data.size != 0)
{
OS_Handle file = os_file_open(OS_AccessFlag_Write|OS_AccessFlag_Append, path);
if( ! os_handle_match(file, os_handle_zero()))
{
good = 1;
U64 pos = os_properties_from_file(file).size;
os_file_write(file, r1u64(pos, pos+data.size), data.str);
os_file_close(file);
}
}
return good;
}
String8
os_string_from_file_range__arena(Arena* arena, OS_Handle file, Rng1U64 range)
{
U64 pre_pos = arena_pos(arena);
String8 result;
result.size = dim_1u64(range);
result.str = push_array_no_zero(arena, U8, result.size);
U64 actual_read_size = os_file_read(file, range, result.str);
if(actual_read_size < result.size) {
arena_pop_to(arena, pre_pos + actual_read_size);
result.size = actual_read_size;
}
return result;
}
String8
os_string_from_file_range__ainfo(AllocatorInfo ainfo, OS_Handle file, Rng1U64 range) {
String8 result;
result.size = dim_1u64(range);
result.str = alloc_array_no_zero(ainfo, U8, result.size);
U64 actual_read_size = os_file_read(file, range, result.str);
if ((allocator_query_support(ainfo) & AllocatorQuery_ResizeShrink) && actual_read_size < result.size) {
resize(ainfo, result.str, result.size, actual_read_size);
result.size = actual_read_size;
}
return result;
}
+471
View File
@@ -0,0 +1,471 @@
#ifdef INTELLISENSE_DIRECTIVES
# pragma once
# include "base/debug.h"
# include "base/strings.h"
# include "base/thread_context.h"
# include "base/file.h"
#endif
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
////////////////////////////////
//~ rjf: System Info
typedef struct OS_SystemInfo OS_SystemInfo;
struct OS_SystemInfo
{
U32 logical_processor_count;
U64 page_size;
U64 large_page_size;
U64 allocation_granularity;
String8 machine_name;
};
////////////////////////////////
//~ rjf: Process Info
typedef struct OS_ProcessInfo OS_ProcessInfo;
struct OS_ProcessInfo
{
U32 pid;
String8 binary_path;
String8 initial_path;
String8 user_program_data_path;
String8List module_load_paths;
String8List environment;
};
////////////////////////////////
//~ rjf: Access Flags
typedef U32 OS_AccessFlags;
enum
{
OS_AccessFlag_Read = (1 << 0),
OS_AccessFlag_Write = (1 << 1),
OS_AccessFlag_Execute = (1 << 2),
OS_AccessFlag_Append = (1 << 3),
OS_AccessFlag_ShareRead = (1 << 4),
OS_AccessFlag_ShareWrite = (1 << 5),
};
////////////////////////////////
//~ rjf: Files
typedef U32 OS_FileIterFlags;
enum
{
OS_FileIterFlag_SkipFolders = (1 << 0),
OS_FileIterFlag_SkipFiles = (1 << 1),
OS_FileIterFlag_SkipHiddenFiles = (1 << 2),
OS_FileIterFlag_Done = (1 << 31),
};
typedef struct OS_FileIter OS_FileIter;
struct OS_FileIter
{
OS_FileIterFlags flags;
U8 memory[800];
};
typedef struct OS_FileInfo OS_FileInfo;
struct OS_FileInfo
{
String8 name;
FileProperties props;
};
// nick: on-disk file identifier
typedef struct OS_FileID OS_FileID;
struct OS_FileID
{
U64 v[3];
};
////////////////////////////////
//~ rjf: Process Launch Parameters
typedef struct OS_ProcessLaunchParams OS_ProcessLaunchParams;
struct OS_ProcessLaunchParams
{
String8List cmd_line;
String8 path;
String8List env;
B32 inherit_env;
B32 consoleless;
};
////////////////////////////////
//~ rjf: Handle Type
typedef struct OS_Handle OS_Handle;
struct OS_Handle
{
U64 u64[1];
};
typedef struct OS_HandleNode OS_HandleNode;
struct OS_HandleNode
{
OS_HandleNode* next;
OS_Handle v;
};
typedef struct OS_HandleList OS_HandleList;
struct OS_HandleList
{
OS_HandleNode* first;
OS_HandleNode* last;
U64 count;
};
typedef struct OS_HandleArray OS_HandleArray;
struct OS_HandleArray
{
OS_Handle* v;
U64 count;
};
////////////////////////////////
//~ rjf: Globally Unique IDs
typedef struct OS_Guid OS_Guid;
struct OS_Guid
{
U32 data1;
U16 data2;
U16 data3;
U8 data4[8];
};
md_static_assert(size_of(OS_Guid) == 16, os_guid_check);
////////////////////////////////
//~ rjf: Thread Types
typedef void OS_ThreadFunctionType(void *ptr);
////////////////////////////////
//~ rjf: Handle Type Functions (Helpers, Implemented Once)
force_inline OS_Handle os_handle_zero (void) { OS_Handle handle = {0}; return handle; }
force_inline B32 os_handle_match(OS_Handle a, OS_Handle b) { return a.u64[0] == b.u64[0]; }
void os_handle_list_push__arena (Arena* arena, OS_HandleList* handles, OS_Handle handle);
void os_handle_list_push__ainfo (AllocatorInfo ainfo, OS_HandleList* handles, OS_Handle handle);
OS_HandleArray os_handle_array_from_list__arena(Arena* arena, OS_HandleList* list);
OS_HandleArray os_handle_array_from_list__ainfo(AllocatorInfo ainfo, OS_HandleList* list);
#define os_handle_ist_push(arena, handles, handle) _Generic(allocator, Arena*: os_handle_list_push__arena, AllocatorInfo: os_handle_list_push__ainfo, default: assert_generic_sel_fail) generic_call(allocator, handles, handle)
#define os_handle_array_from_list(allocator, list) _Generic(allocator, Arena*: os_handle_array_from_list__arena, AllocatorInfo: os_handle_list_push__ainfo, default: assert_generic_sel_fail) generic_call(allocator, list)
force_inline void os_handle_list_push__arena (Arena* arena, OS_HandleList* handles, OS_Handle handle) { os_handle_list__push_ainfo (arena_allocator(arena), handles, handle); }
force_inline OS_HandleArray os_handle_array_from_list__arena(Arena* arena, OS_HandleList* list) { return os_handle_array_from_list__ainfo(arena_allocator(arena), list); }
inline void
os_handle_list_alloc(AllocatorInfo ainfo, OS_HandleList* handles, OS_Handle handle) {
OS_HandleNode* n = alloc_array(ainfo, OS_HandleNode, 1);
n->v = handle;
sll_queue_push(handles->first, handles->last, n);
handles->count += 1;
}
inline OS_HandleArray
os_handle_array_from_list_alloc(AllocatorInfo ainfo, OS_HandleList* list) {
OS_HandleArray result = {0};
result.count = list->count;
result.v = alloc_array_no_zero(ainfo, OS_Handle, result.count);
U64 idx = 0;
for(OS_HandleNode* n = list->first; n != 0; n = n->next, idx += 1) {
result.v[idx] = n->v;
}
return result;
}
////////////////////////////////
//~ rjf: Command Line Argc/Argv Helper (Helper, Implemented Once)
String8List os_string_list_from_argcv__arena(Arena* arena, int argc, char** argv);
String8List os_string_list_from_argcv__ainfo(AllocatorInfo ainfo, int argc, char** argv);
inline String8List
os_string_list_from_argcv__ainfo(AllocatorInfo ainfo, int argc, char** argv) {
String8List result = {0};
for(int i = 0; i < argc; i += 1)
{
String8 str = str8_cstring(argv[i]);
str8_list_push(ainfo, &result, str);
}
return result;
}
#define os_string_list_from_argcv(allocator, argc, argv) _Generic(allocator, Arena*: os_string_list_from_argcv__arena, AllocatorInfo: os_string_list_from_argcv__ainfo, default: assert_generic_sel_fail) generic_call(allocator, argc, argv)
force_inline String8List os_string_list_from_argcv__arena(Arena* arena, int argc, char** argv) { return os_string_list_from_argcv__ainfo(arena_allocator(arena), argc, argv); }
////////////////////////////////
//~ rjf: @os_hooks File System (Implemented Per-OS)
//- rjf: files
MD_API OS_Handle os_file_open (OS_AccessFlags flags, String8 path);
MD_API void os_file_close (OS_Handle file);
MD_API U64 os_file_read (OS_Handle file, Rng1U64 rng, void *out_data);
MD_API U64 os_file_write (OS_Handle file, Rng1U64 rng, void *data);
MD_API B32 os_file_set_times (OS_Handle file, DateTime time);
MD_API FileProperties os_properties_from_file (OS_Handle file);
MD_API OS_FileID os_id_from_file (OS_Handle file);
MD_API B32 os_delete_file_at_path (String8 path);
MD_API B32 os_copy_file_path (String8 dst, String8 src);
MD_API B32 os_file_path_exists (String8 path);
MD_API FileProperties os_properties_from_file_path (String8 path);
MD_API String8 os_full_path_from_path__arena(Arena* arena, String8 path);
MD_API String8 os_full_path_from_path__ainfo(AllocatorInfo arena, String8 path);
#define os_full_path_from_path(allocator, path) _Generic(allocator, Arena*: os_full_path_from_path__arena, AllocatorInfo: os_full_path_from_path__ainfo, default: assert_generic_sel_fail) generic_call(allocator, path)
force_inline String8 os_full_path_from_path__arena(Arena* arena, String8 path) { return os_full_path_from_path__ainfo(arena_allocator(arena), path); }
//- rjf: file maps
MD_API OS_Handle os_file_map_open (OS_AccessFlags flags, OS_Handle file);
MD_API void os_file_map_close (OS_Handle map);
MD_API void* os_file_map_view_open (OS_Handle map, OS_AccessFlags flags, Rng1U64 range);
MD_API void os_file_map_view_close(OS_Handle map, void* ptr, Rng1U64 range);
//- rjf: directory iteration
OS_FileIter* os_file_iter_begin__arena(Arena* arena, String8 path, OS_FileIterFlags flags);
MD_API OS_FileIter* os_file_iter_begin__ainfo(AllocatorInfo ainfo, String8 path, OS_FileIterFlags flags);
B32 os_file_iter_next__arena (Arena* arena, OS_FileIter* iter, OS_FileInfo* info_out);
MD_API B32 os_file_iter_next__ainfo (AllocatorInfo arena, OS_FileIter* iter, OS_FileInfo* info_out);
MD_API void os_file_iter_end ( OS_FileIter* iter);
#define os_file_iter_begin(allocator, path, flags) _Generic(allocator, Arena*: os_file_iter_begin__arena, AllocatorInfo: os_file_iter_begin__ainfo, default: assert_generic_sel_fail) generic_call(allocator, path)
#define os_file_iter_next(allocator, iter, info_out) _Generic(allocator, Arena*: os_file_iter_next__arena, AllocatorInfo: os_file_iter_next__ainfo, default: assert_generic_sel_fail) generic_call(allocator, path)
force_inline OS_FileIter* os_file_iter_begin__arena(Arena* arena, String8 path, OS_FileIterFlags flags) { reutrn (); }
force_inline B32 os_file_iter_next__arena (Arena* arena, OS_FileIter* iter, OS_FileInfo* info_out) { reutrn (); }
//- rjf: directory creation
MD_API B32 os_make_directory(String8 path);
////////////////////////////////
//~ rjf: Filesystem Helpers (Helpers, Implemented Once)
MD_API B32 os_write_data_to_file_path (String8 path, String8 data);
MD_API B32 os_write_data_list_to_file_path(String8 path, String8List list);
MD_API B32 os_append_data_to_file_path (String8 path, String8 data);
OS_FileID os_id_from_file_path (String8 path);
S64 os_file_id_compare (OS_FileID a, OS_FileID b);
String8 os_data_from_file_path__arena (Arena* arena, String8 path);
String8 os_data_from_file_path__ainfo (AllocatorInfo ainfo, String8 path);
MD_API String8 os_string_from_file_range__arena(Arena* arena, OS_Handle file, Rng1U64 range);
MD_API String8 os_string_from_file_range__ainfo(AllocatorInfo ainfo, OS_Handle file, Rng1U64 range);
#define os_data_from_file_path(allocator, path) _Generic(allocator, Arena*: os_data_from_file_path__arena, AllocatorInfo: os_data_from_file_path__ainfo, default: assert_generic_sel_fail) generic_call(allocator, path)
#define os_string_from_file_range(allocator, file, range) _Generic(allocator, Arena*: os_string_from_file_range__arena, AllocatorInfo: os_string_from_file_range__ainfo, default: assert_generic_sel_fail) generic_call(allocator, file, range)
force_inline String8 os_data_from_file_path__arena(Arena* arena, String8 path) { return os_data_from_file_path__ainfo(arena_allocator(arena), path); }
inline String8
os_data_from_file_path__ainfo(AllocatorInfo ainfo, String8 path)
{
OS_Handle file = os_file_open(OS_AccessFlag_Read | OS_AccessFlag_ShareRead, path);
FileProperties props = os_properties_from_file(file);
String8 data = os_string_from_file_range(ainfo, file, r1u64(0, props.size));
os_file_close(file);
return data;
}
inline OS_FileID
os_id_from_file_path(String8 path) {
OS_Handle file = os_file_open(OS_AccessFlag_Read | OS_AccessFlag_ShareRead, path);
OS_FileID id = os_id_from_file(file);
os_file_close(file);
return id;
}
inline S64 os_file_id_compare(OS_FileID a, OS_FileID b) { S64 cmp = memory_compare((void*)&a.v[0], (void*)&b.v[0], sizeof(a.v)); return cmp; }
////////////////////////////////
//~ rjf: GUID Helpers (Helpers, Implemented Once)
String8 os_string_from_guid__arena(Arena* arena, OS_Guid guid);
String8 os_string_from_guid__ainfo(AllocatorInfo ainfo, OS_Guid guid);
#define os_string_from_guid(allocator, guid) _Generic(allocator, Arena*: os_string_from_guid__arena, AllocatorInfo: os_string_from_guid__ainfo, default: assert_generic_sel_fail) generic_call(allocator, guid)
force_inline String8 os_string_from_guid__arena(Arena* arena, OS_Guid guid) { os_string_from_guid__ainfo(arena_allocator(arena), guid); }
inline String8
os_string_from_guid_alloc(AllocatorInfo ainfo, OS_Guid guid) {
String8 result = str8f(ainfo,
"%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X",
guid.data1,
guid.data2,
guid.data3,
guid.data4[0],
guid.data4[1],
guid.data4[2],
guid.data4[3],
guid.data4[4],
guid.data4[5],
guid.data4[6],
guid.data4[7]
);
return result;
}
////////////////////////////////
//~ rjf: @os_hooks System/Process Info (Implemented Per-OS)
MD_API OS_SystemInfo* os_get_system_info (void);
MD_API OS_ProcessInfo* os_get_process_info(void);
String8 os_get_current_path__arena(Arena* arena);
String8 os_get_current_path__ainfo(AllocatorInfo arena);
#define os_get_current_path(allocator) _Generic(allocator, Arena*: os_get_current_path__arena, AllocatorInfo: os_get_current_path__ainfo) generic_call(allocator)
force_inline String8 os_get_current_path__arena(Arena* arena) { return os_get_current_path__ainfo(arena_allocator(arena)); }
////////////////////////////////
//~ rjf: @os_hooks Memory Allocation (Implemented Per-OS)
//- rjf: basic
void* os_reserve ( U64 size);
B32 os_commit (void* ptr, U64 size);
void os_decommit(void* ptr, U64 size);
void os_release (void* ptr, U64 size);
//- rjf: large pages
void* os_reserve_large( U64 size);
B32 os_commit_large (void* ptr, U64 size);
////////////////////////////////
//~ rjf: @os_hooks Thread Info (Implemented Per-OS)
U32 os_tid(void);
MD_API void os_set_thread_name(String8 string);
////////////////////////////////
//~ rjf: @os_hooks Aborting (Implemented Per-OS)
MD_API void os_abort(S32 exit_code);
////////////////////////////////
//~ rjf: @os_hooks Shared Memory (Implemented Per-OS)
MD_API OS_Handle os_shared_memory_alloc (U64 size, String8 name);
MD_API OS_Handle os_shared_memory_open (String8 name);
MD_API void os_shared_memory_close (OS_Handle handle);
MD_API void* os_shared_memory_view_open (OS_Handle handle, Rng1U64 range);
MD_API void os_shared_memory_view_close(OS_Handle handle, void* ptr, Rng1U64 range);
////////////////////////////////
//~ rjf: @os_hooks Time (Implemented Per-OS)
MD_API U64 os_now_microseconds (void);
MD_API U32 os_now_unix (void);
MD_API DateTime os_now_universal_time (void);
MD_API DateTime os_universal_time_from_local(DateTime* local_time);
MD_API DateTime os_local_time_from_universal(DateTime* universal_time);
MD_API void os_sleep_milliseconds (U32 msec);
////////////////////////////////
//~ rjf: @os_hooks Child Processes (Implemented Per-OS)
MD_API OS_Handle os_process_launch(OS_ProcessLaunchParams* params);
MD_API B32 os_process_join (OS_Handle handle, U64 endt_us);
MD_API void os_process_detach(OS_Handle handle);
////////////////////////////////
//~ rjf: @os_hooks Threads (Implemented Per-OS)
MD_API OS_Handle os_thread_launch(OS_ThreadFunctionType* func, void* ptr, void* params);
MD_API B32 os_thread_join (OS_Handle handle, U64 endt_us);
MD_API void os_thread_detach(OS_Handle handle);
////////////////////////////////
//~ rjf: @os_hooks Synchronization Primitives (Implemented Per-OS)
//- rjf: recursive mutexes
MD_API OS_Handle os_mutex_alloc (void);
MD_API void os_mutex_release(OS_Handle mutex);
MD_API void os_mutex_take (OS_Handle mutex);
MD_API void os_mutex_drop (OS_Handle mutex);
//- rjf: reader/writer mutexes
MD_API OS_Handle os_rw_mutex_alloc (void);
MD_API void os_rw_mutex_release(OS_Handle rw_mutex);
MD_API void os_rw_mutex_take_r (OS_Handle mutex);
MD_API void os_rw_mutex_drop_r (OS_Handle mutex);
MD_API void os_rw_mutex_take_w (OS_Handle mutex);
MD_API void os_rw_mutex_drop_w (OS_Handle mutex);
//- rjf: condition variables
MD_API OS_Handle os_condition_variable_alloc (void);
MD_API void os_condition_variable_release(OS_Handle cv);
// returns false on timeout, true on signal, (max_wait_ms = max_U64) -> no timeout
MD_API B32 os_condition_variable_wait (OS_Handle cv, OS_Handle mutex, U64 endt_us);
MD_API B32 os_condition_variable_wait_rw_r(OS_Handle cv, OS_Handle mutex_rw, U64 endt_us);
MD_API B32 os_condition_variable_wait_rw_w(OS_Handle cv, OS_Handle mutex_rw, U64 endt_us);
MD_API void os_condition_variable_signal (OS_Handle cv);
MD_API void os_condition_variable_broadcast(OS_Handle cv);
//- rjf: cross-process semaphores
MD_API OS_Handle os_semaphore_alloc (U32 initial_count, U32 max_count, String8 name);
MD_API void os_semaphore_release(OS_Handle semaphore);
MD_API OS_Handle os_semaphore_open (String8 name);
MD_API void os_semaphore_close (OS_Handle semaphore);
MD_API B32 os_semaphore_take (OS_Handle semaphore, U64 endt_us);
MD_API void os_semaphore_drop (OS_Handle semaphore);
//- rjf: scope macros
#define os_mutex_scope(mutex) defer_loop( os_mutex_take (mutex), os_mutex_drop (mutex))
#define os_mutex_scope_r(mutex) defer_loop( os_rw_mutex_take_r(mutex), os_rw_mutex_drop_r(mutex))
#define os_mutex_scope_W(mutex) defer_loop( os_rw_mutex_take_w(mutex), os_rw_mutex_drop_w(mutex))
#define os_mutex_scope_rw_promote(mutex) defer_loop((os_rw_mutex_drop_r(mutex), os_rw_mutex_take_w(mutex)), (os_rw_mutex_drop_w(mutex), os_rw_mutex_take_r(mutex)))
////////////////////////////////
//~ rjf: @os_hooks Dynamically-Loaded Libraries (Implemented Per-OS)
MD_API OS_Handle os_library_open (String8 path);
MD_API void os_library_close (OS_Handle lib);
MD_API VoidProc* os_library_load_proc(OS_Handle lib, String8 name);
////////////////////////////////
//~ rjf: @os_hooks Safe Calls (Implemented Per-OS)
MD_API void os_safe_call(OS_ThreadFunctionType* func, OS_ThreadFunctionType* fail_handler, void* ptr);
////////////////////////////////
//~ rjf: @os_hooks GUIDs (Implemented Per-OS)
MD_API OS_Guid os_make_guid(void);
////////////////////////////////
//~ rjf: @os_hooks Entry Points (Implemented Per-OS)
// NOTE(rjf): The implementation of `os_core` will define low-level entry
// points if BUILD_ENTRY_DEFINING_UNIT is defined to 1. These will call
// into the standard codebase program entry points, named "entry_point".
#if BUILD_ENTRY_DEFINING_UNIT
void entry_point(CmdLine* cmdline);
#endif
////////////////////////////////
//~ Ed: Manual OS Bootstrap (Implemented Per-OS)
typedef struct OS_Context OS_Context;
struct OS_Context
{
Arena* state_arena;
Arena* entity_arena;
B32 enable_large_pages;
};
// OS layer initialization
MD_API void os_init(OS_Context* ctx, TCTX* thread_ctx);
+28
View File
@@ -0,0 +1,28 @@
#ifdef INTELLISENSE_DIRECTIVES
# include "base/context_cracking.h"
# include "base/namespace.h"
#endif
#if !defined(OS_FEATURE_GRAPHICAL)
# define OS_FEATURE_GRAPHICAL 0
#endif
#if !defined(OS_GFX_STUB)
# define OS_GFX_STUB 0
#endif
#if OS_WINDOWS
# include "os/win32/os_win32_includes.h"
MD_NS_BEGIN
# include "os/win32/os_win32.h"
MD_NS_END
#elif OS_LINUX
# include "os/linux/os_linux_includes.h"
MD_NS_BEGIN
# include "os/linux/os_linux.h"
MD_NS_END
# error OS core layer not implemented for this operating system.
#endif
File diff suppressed because it is too large Load Diff
+209
View File
@@ -0,0 +1,209 @@
#ifdef INTELLISENSE_DIRECTIVES
# pragma once
# include "base/strings.h"
# include "base/thread_context.h"
# include "os.h"
# include "os_win32_includes.h"
#endif
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
////////////////////////////////
//~ rjf: File Iterator Types
typedef struct OS_W32_FileIter OS_W32_FileIter;
struct OS_W32_FileIter
{
HANDLE handle;
WIN32_FIND_DATAW find_data;
B32 is_volume_iter;
String8Array drive_strings;
U64 drive_strings_iter_idx;
};
md_static_assert(sizeof(member(OS_FileIter, memory)) >= sizeof(OS_W32_FileIter), file_iter_memory_size);
////////////////////////////////
//~ rjf: Entity Types
typedef enum OS_W32_EntityKind OS_W32_EntityKind;
enum OS_W32_EntityKind
{
OS_W32_EntityKind_Null,
OS_W32_EntityKind_Thread,
OS_W32_EntityKind_Mutex,
OS_W32_EntityKind_RWMutex,
OS_W32_EntityKind_ConditionVariable,
};
typedef struct OS_W32_EntityThread OS_W32_EntityThread;
struct OS_W32_EntityThread
{
OS_ThreadFunctionType* func;
void* ptr;
HANDLE handle;
DWORD tid;
};
typedef struct OS_W32_Entity OS_W32_Entity;
struct OS_W32_Entity
{
OS_W32_Entity* next;
OS_W32_EntityKind kind;
union
{
OS_W32_EntityThread thread;
CRITICAL_SECTION mutex;
SRWLOCK rw_mutex;
CONDITION_VARIABLE cv;
};
};
////////////////////////////////
//~ rjf: State
typedef struct OS_W32_State OS_W32_State;
struct OS_W32_State
{
Arena* arena;
// rjf: info
OS_SystemInfo system_info;
OS_ProcessInfo process_info;
U64 microsecond_resolution;
// rjf: entity storage
CRITICAL_SECTION entity_mutex;
Arena* entity_arena;
OS_W32_Entity* entity_free;
};
////////////////////////////////
//~ rjf: Globals
MD_API extern OS_W32_State os_w32_state;
////////////////////////////////
//~ rjf: Time Conversion Helpers
void os_w32_date_time_from_system_time(DateTime* out, SYSTEMTIME* in);
void os_w32_system_time_from_date_time(SYSTEMTIME* out, DateTime* in);
void os_w32_dense_time_from_file_time (DenseTime* out, FILETIME* in);
MD_API U32 os_w32_sleep_ms_from_endt_us(U64 endt_us);
inline void
os_w32_date_time_from_system_time(DateTime* out, SYSTEMTIME* in)
{
out->year = in->wYear;
out->mon = in->wMonth - 1;
out->wday = in->wDayOfWeek;
out->day = in->wDay;
out->hour = in->wHour;
out->min = in->wMinute;
out->sec = in->wSecond;
out->msec = in->wMilliseconds;
}
inline void
os_w32_system_time_from_date_time(SYSTEMTIME* out, DateTime* in)
{
out->wYear = (WORD)(in->year);
out->wMonth = in->mon + 1;
out->wDay = in->day;
out->wHour = in->hour;
out->wMinute = in->min;
out->wSecond = in->sec;
out->wMilliseconds = in->msec;
}
inline void
os_w32_dense_time_from_file_time(DenseTime* out, FILETIME* in) {
SYSTEMTIME systime = {0};
DateTime date_time = {0};
FileTimeToSystemTime(in, &systime);
os_w32_date_time_from_system_time(&date_time, &systime);
*out = dense_time_from_date_time(date_time);
}
////////////////////////////////
//~ rjf: File Info Conversion Helpers
inline FilePropertyFlags
os_w32_file_property_flags_from_dwFileAttributes(DWORD dwFileAttributes)
{
FilePropertyFlags flags = 0;
if (dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
flags |= FilePropertyFlag_IsFolder;
}
return flags;
}
inline void
os_w32_file_properties_from_attribute_data(FileProperties* properties, WIN32_FILE_ATTRIBUTE_DATA* attributes) {
properties->size = compose_64bit(attributes->nFileSizeHigh, attributes->nFileSizeLow);
os_w32_dense_time_from_file_time(&properties->created, &attributes->ftCreationTime);
os_w32_dense_time_from_file_time(&properties->modified, &attributes->ftLastWriteTime);
properties->flags = os_w32_file_property_flags_from_dwFileAttributes(attributes->dwFileAttributes);
}
////////////////////////////////
//~ rjf: Entity Functions
MD_API OS_W32_Entity* os_w32_entity_alloc (OS_W32_EntityKind kind);
MD_API void os_w32_entity_release(OS_W32_Entity* entity);
////////////////////////////////
//~ rjf: Thread Entry Point
MD_API DWORD os_w32_thread_entry_point(void* ptr);
////////////////////////////////
//~ rjf: @os_hooks System/Process Info (Implemented Per-OS)
inline String8
os_get_current_path__ainfo(AllocatorInfo ainfo) {
String8 name;
TempArena scratch = scratch_begin(ainfo);
{
DWORD length = GetCurrentDirectoryW(0, 0);
U16* memory = push_array_no_zero(scratch.arena, U16, length + 1);
length = GetCurrentDirectoryW(length + 1, (WCHAR*)memory);
name = str8_from(ainfo, str16(memory, length));
}
scratch_end(scratch);
return name;
}
////////////////////////////////
//~ rjf: @os_hooks Memory Allocation (Implemented Per-OS)
//- rjf: basic
inline void* os_reserve ( U64 size) { void* result = VirtualAlloc( 0, size, MEM_RESERVE, PAGE_READWRITE); return result; }
inline B32 os_commit (void* ptr, U64 size) { B32 result = (VirtualAlloc(ptr, size, MEM_COMMIT, PAGE_READWRITE) != 0); return result; }
inline void os_decommit(void* ptr, U64 size) { VirtualFree (ptr, size, MEM_DECOMMIT); }
inline void
os_release(void* ptr, U64 size) {
// NOTE(rjf): size not used - not necessary on Windows, but necessary for other OSes.
VirtualFree(ptr, 0, MEM_RELEASE);
}
//- rjf: large pages
inline void*
os_reserve_large(U64 size) {
// we commit on reserve because windows
void* result = VirtualAlloc(0, size, MEM_RESERVE|MEM_COMMIT|MEM_LARGE_PAGES, PAGE_READWRITE);
return result;
}
inline B32 os_commit_large(void* ptr, U64 size) { return 1; }
////////////////////////////////
//~ rjf: @os_hooks Thread Info (Implemented Per-OS)
inline U32 os_tid(void) { DWORD id = GetCurrentThreadId(); return (U32)id; }
+31
View File
@@ -0,0 +1,31 @@
#ifdef INTELLISENSE_DIRECTIVES
# pragma once
#endif
////////////////////////////////
//~ rjf: Includes / Libraries
#ifndef NOMINMAX
#define NOMINMAX
#endif
#define WIN32_LEAN_AND_MEAN
#define WIN32_MEAN_AND_LEAN
#define VC_EXTRALEAN
#include <windows.h>
#include <windowsx.h>
#include <timeapi.h>
#include <tlhelp32.h>
#include <Shlobj.h>
#include <processthreadsapi.h>
#pragma comment(lib, "user32")
#pragma comment(lib, "winmm")
#pragma comment(lib, "shell32")
#pragma comment(lib, "advapi32")
#pragma comment(lib, "rpcrt4")
#pragma comment(lib, "shlwapi")
#pragma comment(lib, "comctl32")
#pragma comment(linker,"\"/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") // this is required for loading correct comctl32 dll file
#undef NOMINMAX
#undef WIN32_LEAN_AND_MEAN
#undef WIN32_MEAN_AND_LEAN
#undef VC_EXTRALEAN