mirror of
https://github.com/Ed94/metadesk.git
synced 2026-07-29 10:50:02 +00:00
adjusting repo for removing extra features not part of the original library
This commit is contained in:
@@ -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.
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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; }
|
||||
@@ -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
|
||||
@@ -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" );
|
||||
}
|
||||
@@ -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, ... );
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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) \
|
||||
)
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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 );
|
||||
}
|
||||
@@ -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
|
||||
@@ -0,0 +1,5 @@
|
||||
#ifdef INTELLISENSE_DIRECTIVES
|
||||
# include "platform.h"
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
@@ -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
|
||||
@@ -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())
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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); }
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -0,0 +1,3 @@
|
||||
# Metagen
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
@@ -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>
|
||||
@@ -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
@@ -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);
|
||||
@@ -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
@@ -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; }
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user