diff --git a/src/metagen/metagen_base/metagen_base_arena.c b/src/metagen/metagen_base/metagen_base_arena.c index d7297f71..d4dd1b69 100644 --- a/src/metagen/metagen_base/metagen_base_arena.c +++ b/src/metagen/metagen_base/metagen_base_arena.c @@ -1,316 +1,195 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -//////////////////////////////// -// Implementation - -internal Arena * -arena_alloc__sized(U64 init_res, U64 init_cmt) -{ - ProfBeginFunction(); - Assert(ARENA_HEADER_SIZE < init_cmt && init_cmt <= init_res); - - void *memory; - U64 res; - U64 cmt; - - B32 large_pages = os_large_pages_enabled(); - if (large_pages) { - U64 page_size = os_large_page_size(); - res = AlignPow2(init_res, page_size); - -#if OS_WINDOWS - cmt = res; -#else - cmt AlignPow2(init_cmt, page_size); -#endif - - memory = os_reserve_large(res); - if (!os_commit_large(memory, cmt)) { - memory = 0; - os_release(memory, res); - } - } else { - U64 page_size = os_page_size(); - res = AlignPow2(init_res, page_size); - cmt = AlignPow2(init_cmt, page_size); - - memory = os_reserve(res); - if (!os_commit(memory, cmt)) { - memory = 0; - os_release(memory, res); - } - } - Assert(memory); - - AsanPoisonMemoryRegion(memory, cmt); - AsanUnpoisonMemoryRegion(memory, ARENA_HEADER_SIZE); - - Arena *arena = (Arena*)memory; - if (arena) { - arena->prev = 0; - arena->current = arena; - arena->base_pos = 0; - arena->pos = ARENA_HEADER_SIZE; - arena->cmt = cmt; - arena->res = res; - arena->align = 8; -#if ENABLE_DEV - arena->dev = 0; -#endif - arena->grow = 1; - arena->large_pages = large_pages; - } - - ProfEnd(); - return arena; -} - -internal Arena * -arena_alloc(void) -{ - ProfBeginFunction(); - - U64 init_res, init_cmt; - if (os_large_pages_enabled()) { - init_res = ARENA_RESERVE_SIZE_LARGE_PAGES; - init_cmt = ARENA_COMMIT_SIZE_LARGE_PAGES; - } else { - init_res = ARENA_RESERVE_SIZE; - init_cmt = ARENA_COMMIT_SIZE; - } - - Arena *arena = arena_alloc__sized(init_res, init_cmt); - - ProfEnd(); - return arena; -} - -internal void -arena_release(Arena *arena) -{ - for (Arena *node = arena->current, *prev = 0; node != 0; node = prev) { - prev = node->prev; - os_release(node, node->res); - } -} - -internal U64 -arena_huge_push_threshold(void) -{ - U64 reserve_size = os_large_pages_enabled() ? ARENA_RESERVE_SIZE_LARGE_PAGES : ARENA_RESERVE_SIZE; - U64 threshold = (reserve_size - ARENA_HEADER_SIZE) / 2 + 1; - return threshold; -} - -internal void * -arena_push__impl(Arena *arena, U64 size) -{ - Arena *current = arena->current; - U64 pos_mem = AlignPow2(current->pos, arena->align); - U64 pos_new = pos_mem + size; - - if (current->res < pos_new && arena->grow) { - Arena *new_block; - - // normal growth path - if (size < arena_huge_push_threshold()) { - new_block = arena_alloc(); - } - // huge growth path - else { - U64 new_block_size = size + ARENA_HEADER_SIZE; - new_block = arena_alloc__sized(new_block_size, new_block_size); - } - - if (new_block) { - new_block->base_pos = current->base_pos + current->res; - SLLStackPush_N(arena->current, new_block, prev); - - current = new_block; - pos_mem = AlignPow2(current->pos, current->align); - pos_new = pos_mem + size; - } - } - - if (current->cmt < pos_new) { - U64 cmt_new_aligned, cmt_new_clamped, cmt_new_size; - B32 is_cmt_ok; - - if (current->large_pages) { - cmt_new_aligned = AlignPow2(pos_new, ARENA_COMMIT_SIZE_LARGE_PAGES); - cmt_new_clamped = ClampTop(cmt_new_aligned, current->res); - cmt_new_size = cmt_new_clamped - current->cmt; - is_cmt_ok = os_commit_large((U8*)current + current->cmt, cmt_new_size); - } else { - cmt_new_aligned = AlignPow2(pos_new, ARENA_COMMIT_SIZE); - cmt_new_clamped = ClampTop(cmt_new_aligned, current->res); - cmt_new_size = cmt_new_clamped - current->cmt; - is_cmt_ok = os_commit((U8*)current + current->cmt, cmt_new_size); - } - Assert(is_cmt_ok); - - if (is_cmt_ok) { - current->cmt = cmt_new_clamped; - } - } - - void *memory = 0; - - if (current->cmt >= pos_new) { - memory = (U8*)current + pos_mem; - current->pos = pos_new; - AsanUnpoisonMemoryRegion(memory, size); - } - -#if OS_FEATURE_GRAPHICAL - if(Unlikely(memory == 0)) - { - os_graphical_message(1, str8_lit("Fatal Allocation Failure"), str8_lit("Unexpected memory allocation failure.")); - os_exit_process(1); - } -#endif - - return memory; -} - -internal U64 -arena_pos(Arena *arena) -{ - Arena *current = arena->current; - U64 pos = current->base_pos + current->pos; - return pos; -} - -internal void -arena_pop_to(Arena *arena, U64 big_pos_unclamped) -{ - U64 big_pos = ClampBot(ARENA_HEADER_SIZE, big_pos_unclamped); - - // unroll the chain - Arena *current = arena->current; - for (Arena *prev = 0; current->base_pos >= big_pos; current = prev) { - prev = current->prev; - os_release(current, current->res); - } - AssertAlways(current); - arena->current = current; - - // compute arena-relative position - U64 new_pos = big_pos - current->base_pos; - AssertAlways(new_pos <= current->pos); - - // poison popped memory block - AsanPoisonMemoryRegion((U8*)current + new_pos, (current->pos - new_pos)); - - // update position - current->pos = new_pos; -} - -internal void -arena_absorb(Arena *arena, Arena *sub) -{ -#if ENABLE_DEV - arena_annotate_absorb__dev(arena, sub); -#endif - - // base adjustment - Arena *current = arena->current; - U64 base_adjust = current->base_pos + current->res; - for (Arena *node = sub->current; node != 0; node = node->prev) { - node->base_pos += base_adjust; - } - - // attach sub to arena - sub->prev = arena->current; - arena->current = sub->current; - sub->current = sub; -} - -//////////////////////////////// -// Wrappers - -internal void * -arena_push(Arena *arena, U64 size) -{ - void *memory = arena_push__impl(arena, size); -#if ENABLE_DEV - arena_annotate_push__dev(arena, size, memory); -#endif - return memory; -} - -internal void * -arena_push_contiguous(Arena *arena, U64 size) -{ - B32 restore = arena->grow; - arena->grow = 0; - void *memory = arena_push(arena, size); - arena->grow = restore; -#if ENABLE_DEV - arena_annotate_push__dev(arena, size, memory); -#endif - return memory; -} - -internal void -arena_push_align(Arena *arena, U64 align) -{ - Assert(IsPow2(align)); - U64 amt = AlignPadPow2(arena->pos, align); - void *ptr = arena_push(arena, amt); - MemoryZero(ptr, amt); -} - -internal void -arena_put_back(Arena *arena, U64 amt) -{ - U64 pos_old = arena_pos(arena); - U64 pos_new = pos_old; - if (amt < pos_old) { - pos_new = pos_old - amt; - } - arena_pop_to(arena, pos_new); -} - -internal void -arena_clear(Arena *arena) -{ - arena_pop_to(arena, 0); -} - -internal Temp -temp_begin(Arena *arena) -{ - U64 pos = arena_pos(arena); - Temp temp = {arena, pos}; - return temp; -} - -internal void -temp_end(Temp temp) -{ - arena_pop_to(temp.arena, temp.pos); -} - -//////////////////////////////// -//~ NOTE(allen): "Mini-Arena" Helper - -internal B32 -ensure_commit(void **cmtptr, void *pos, U64 cmt_block_size){ - B32 result = 0; - U8 *cmt = (U8*)*cmtptr; - if (cmt < (U8*)pos){ - U64 cmt_size_raw = (U8*)pos - cmt; - U64 cmt_size = AlignPow2(cmt_size_raw, cmt_block_size); - if (os_commit(cmt, cmt_size)){ - *cmtptr = cmt + cmt_size; - result = 1; - } - } - else{ - result = 1; - } - return(result); -} - +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +//////////////////////////////// +//~ rjf: Arena Functions + +//- rjf: arena creation/destruction + +internal Arena * +arena_alloc_(ArenaParams *params) +{ + // rjf: round up reserve/commit sizes + U64 reserve_size = params->reserve_size; + U64 commit_size = params->commit_size; + if(params->flags & ArenaFlag_LargePages) + { + reserve_size = AlignPow2(reserve_size, os_get_system_info()->large_page_size); + commit_size = AlignPow2(commit_size, os_get_system_info()->large_page_size); + } + else + { + reserve_size = AlignPow2(reserve_size, os_get_system_info()->page_size); + commit_size = AlignPow2(commit_size, os_get_system_info()->page_size); + } + + // rjf: reserve/commit initial block + void *base = params->optional_backing_buffer; + if(base == 0) + { + if(params->flags & ArenaFlag_LargePages) + { + base = os_reserve_large(reserve_size); + os_commit_large(base, commit_size); + } + else + { + base = os_reserve(reserve_size); + os_commit(base, commit_size); + } + } + + // rjf: panic on arena 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 + + // rjf: extract arena header & fill + Arena *arena = (Arena *)base; + arena->current = arena; + arena->flags = params->flags; + arena->cmt_size = (U32)params->commit_size; + arena->res_size = params->reserve_size; + arena->base_pos = 0; + arena->pos = ARENA_HEADER_SIZE; + arena->cmt = commit_size; + arena->res = reserve_size; + AsanPoisonMemoryRegion(base, commit_size); + AsanUnpoisonMemoryRegion(base, ARENA_HEADER_SIZE); + return arena; +} + +internal void +arena_release(Arena *arena) +{ + for(Arena *n = arena->current, *prev = 0; n != 0; n = prev) + { + prev = n->prev; + os_release(n, n->res); + } +} + +//- rjf: arena push/pop core functions + +internal void * +arena_push(Arena *arena, U64 size, U64 align) +{ + Arena *current = arena->current; + U64 pos_pre = AlignPow2(current->pos, align); + U64 pos_pst = pos_pre + size; + + // rjf: chain, if needed + if(current->res < pos_pst && !(arena->flags & ArenaFlag_NoChain)) + { + U64 res_size = current->res_size; + U64 cmt_size = current->cmt_size; + if(size > cmt_size) + { + res_size = size + ARENA_HEADER_SIZE; + cmt_size = size + ARENA_HEADER_SIZE; + } + Arena *new_block = arena_alloc(.reserve_size = res_size, + .commit_size = cmt_size, + .flags = current->flags); + new_block->base_pos = current->base_pos + current->res; + SLLStackPush_N(arena->current, new_block, prev); + current = new_block; + pos_pre = AlignPow2(current->pos, align); + pos_pst = pos_pst + size; + } + + // rjf: commit new pages, if needed + if(current->cmt < pos_pst && !(current->flags & ArenaFlag_LargePages)) + { + U64 cmt_pst_aligned = AlignPow2(pos_pst, current->cmt_size); + U64 cmt_pst_clamped = ClampTop(cmt_pst_aligned, current->res); + U64 cmt_size = cmt_pst_clamped - current->cmt; + os_commit((U8 *)current + current->cmt, cmt_size); + current->cmt = cmt_pst_clamped; + } + + // rjf: push onto current block + void *result = 0; + if(current->cmt >= pos_pst) + { + result = (U8 *)current+pos_pre; + current->pos = pos_pst; + AsanUnpoisonMemoryRegion(result, size); + } + + // 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; +} + +internal U64 +arena_pos(Arena *arena) +{ + Arena *current = arena->current; + U64 pos = current->base_pos + current->pos; + return pos; +} + +internal void +arena_pop_to(Arena *arena, U64 pos) +{ + U64 big_pos = ClampBot(ARENA_HEADER_SIZE, pos); + Arena *current = arena->current; + for(Arena *prev = 0; current->base_pos >= big_pos; current = prev) + { + prev = current->prev; + os_release(current, current->res); + } + arena->current = current; + U64 new_pos = big_pos - current->base_pos; + AssertAlways(new_pos <= current->pos); + AsanPoisonMemoryRegion((U8*)current + new_pos, (current->pos - new_pos)); + current->pos = new_pos; +} + +//- rjf: arena push/pop helpers + +internal void +arena_clear(Arena *arena) +{ + arena_pop_to(arena, 0); +} + +internal void +arena_pop(Arena *arena, U64 amt) +{ + U64 pos_old = arena_pos(arena); + U64 pos_new = pos_old; + if(amt < pos_old) + { + pos_new = pos_old - amt; + } + arena_pop_to(arena, pos_new); +} + +//- rjf: temporary arena scopes + +internal Temp +temp_begin(Arena *arena) +{ + U64 pos = arena_pos(arena); + Temp temp = {arena, pos}; + return temp; +} + +internal void +temp_end(Temp temp) +{ + arena_pop_to(temp.arena, temp.pos); +} diff --git a/src/metagen/metagen_base/metagen_base_arena.h b/src/metagen/metagen_base/metagen_base_arena.h index 0a9ca92f..f941bcd7 100644 --- a/src/metagen/metagen_base/metagen_base_arena.h +++ b/src/metagen/metagen_base/metagen_base_arena.h @@ -1,94 +1,80 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#ifndef BASE_ARENA_H -#define BASE_ARENA_H - -//////////////////////////////// -//~ rjf: Constants - -#define ARENA_HEADER_SIZE 128 - -#ifndef ARENA_RESERVE_SIZE -# define ARENA_RESERVE_SIZE MB(64) -#endif -#ifndef ARENA_COMMIT_SIZE -# define ARENA_COMMIT_SIZE KB(64) -#endif - -#ifndef ARENA_RESERVE_SIZE_LARGE_PAGES -# define ARENA_RESERVE_SIZE_LARGE_PAGES MB(8) -#endif -#ifndef ARENA_COMMIT_SIZE_LARGE_PAGES -# define ARENA_COMMIT_SIZE_LARGE_PAGES MB(2) -#endif - -//////////////////////////////// -//~ rjf: Arena Types - -typedef struct Arena Arena; -struct Arena -{ - struct Arena *prev; - struct Arena *current; - U64 base_pos; - U64 pos; - U64 cmt; - U64 res; - U64 align; - struct ArenaDev *dev; - B8 grow; - B8 large_pages; -}; - -typedef struct Temp Temp; -struct Temp -{ - Arena *arena; - U64 pos; -}; - -//////////////////////////////// -// Implementation - -internal Arena* arena_alloc__sized(U64 init_res, U64 init_cmt); - -internal Arena* arena_alloc(void); -internal void arena_release(Arena *arena); - -internal void* arena_push__impl(Arena *arena, U64 size); -internal U64 arena_pos(Arena *arena); -internal void arena_pop_to(Arena *arena, U64 pos); - -internal void arena_absorb(Arena *arena, Arena *sub); - -//////////////////////////////// -// Wrappers - -internal void* arena_push(Arena *arena, U64 size); -internal void* arena_push_contiguous(Arena *arena, U64 size); -internal void arena_clear(Arena *arena); -internal void arena_push_align(Arena *arena, U64 align); -internal void arena_put_back(Arena *arena, U64 amt); - -internal Temp temp_begin(Arena *arena); -internal void temp_end(Temp temp); - -//////////////////////////////// -//~ NOTE(allen): "Mini-Arena" Helper - -internal B32 ensure_commit(void **cmt, void *pos, U64 cmt_block_size); - -//////////////////////////////// -//~ NOTE(allen): Main API Macros - -#if !ENABLE_DEV -# define push_array_no_zero(a,T,c) (T*)arena_push((a), sizeof(T)*(c)) -#else -# define push_array_no_zero(a,T,c) (tctx_write_this_srcloc(), (T*)arena_push((a), sizeof(T)*(c))) -#endif -#define push_array_no_zero__no_annotation(a,T,c) (T*)arena_push__impl((a), sizeof(T)*(c)) - -#define push_array(a,T,c) (T*)MemoryZero(push_array_no_zero(a,T,c), sizeof(T)*(c)) - -#endif // BASE_ARENA_H +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef BASE_ARENA_H +#define BASE_ARENA_H + +//////////////////////////////// +//~ rjf: Constants + +#define ARENA_HEADER_SIZE 64 + +//////////////////////////////// +//~ rjf: Types + +typedef U32 ArenaFlags; +enum +{ + ArenaFlag_NoChain = (1<<0), + ArenaFlag_LargePages = (1<<1), +}; + +typedef struct ArenaParams ArenaParams; +struct ArenaParams +{ + ArenaFlags flags; + U64 reserve_size; + U64 commit_size; + void *optional_backing_buffer; +}; + +typedef struct Arena Arena; +struct Arena +{ + Arena *prev; // previous arena in chain + Arena *current; // current arena in chain + ArenaFlags flags; + U32 cmt_size; + U64 res_size; + U64 base_pos; + U64 pos; + U64 cmt; + U64 res; +}; +StaticAssert(sizeof(Arena) <= ARENA_HEADER_SIZE, arena_header_size_check); + +typedef struct Temp Temp; +struct Temp +{ + Arena *arena; + U64 pos; +}; + +//////////////////////////////// +//~ rjf: Arena Functions + +//- rjf: arena creation/destruction +internal Arena *arena_alloc_(ArenaParams *params); +#define arena_alloc(...) arena_alloc_(&(ArenaParams){.reserve_size = MB(64), .commit_size = KB(64), __VA_ARGS__}) +internal void arena_release(Arena *arena); + +//- rjf: arena push/pop/pos core functions +internal void *arena_push(Arena *arena, U64 size, U64 align); +internal U64 arena_pos(Arena *arena); +internal void arena_pop_to(Arena *arena, U64 pos); + +//- rjf: arena push/pop helpers +internal void arena_clear(Arena *arena); +internal void arena_pop(Arena *arena, U64 amt); + +//- rjf: temporary arena scopes +internal Temp temp_begin(Arena *arena); +internal void temp_end(Temp temp); + +//- rjf: push helper macros +#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 *)MemoryZero(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, AlignOf(T))) +#define push_array(a, T, c) push_array_aligned(a, T, c, Max(8, AlignOf(T))) + +#endif // BASE_ARENA_H diff --git a/src/metagen/metagen_base/metagen_base_arena_dev.c b/src/metagen/metagen_base/metagen_base_arena_dev.c deleted file mode 100644 index 94c334bd..00000000 --- a/src/metagen/metagen_base/metagen_base_arena_dev.c +++ /dev/null @@ -1,197 +0,0 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -//////////////////////////////// -// NOTE(allen): Dev Arena - -#if ENABLE_DEV - -internal void -arena_annotate_push__dev(Arena *arena, U64 size, void *ptr){ - ArenaDev *dev = arena->dev; - if (dev != 0 && ptr != 0){ - //- read location - char *file_name = 0; - U64 line_number = 0; - tctx_read_srcloc(&file_name, &line_number); - tctx_write_srcloc(0, 0); - - //- profile - ArenaProf *prof = dev->prof; - if (prof != 0){ - // c string -> string - String8 file_name_str = str8_lit("(null)"); - if (file_name != 0){ - file_name_str = str8_cstring(file_name); - } - // record - arena_prof_inc_counters__dev(dev->arena, prof, file_name_str, line_number, size, 1); - } - } -} - -internal void -arena_annotate_absorb__dev(Arena *arena, Arena *sub){ - ArenaDev *dev = arena->dev; - ArenaDev *sub_dev = sub->dev; - if (dev != 0 && sub_dev != 0){ - //- merge profiles - ArenaProf *prof = dev->prof; - ArenaProf *sub_prof = sub_dev->prof; - if (prof != 0 && sub_prof != 0){ - for (ArenaProfNode *sub_node = sub_prof->first; - sub_node != 0; - sub_node = sub_node->next){ - arena_prof_inc_counters__dev(dev->arena, prof, sub_node->file_name, sub_node->line, - sub_node->size, sub_node->count); - } - } - } - //- release the sub dev memory - if (sub_dev != 0){ - arena_release(sub_dev->arena); - } -} - -internal ArenaDev* -arena_equip__dev(Arena *arena){ - ArenaDev *result = arena->dev; - if (result == 0){ - Arena *dev_arena = arena_alloc(); - ArenaDev *dev = (ArenaDev*)arena_push__impl(dev_arena, sizeof(ArenaDev)); - MemoryZeroStruct(dev); - dev->arena = dev_arena; - arena->dev = dev; - result = dev; - } - return(result); -} - -internal void -arena_equip_profile__dev(Arena *arena){ - ArenaDev *dev = arena_equip__dev(arena); - if (dev->prof == 0){ - dev->prof = (ArenaProf*)arena_push__impl(dev->arena, sizeof(ArenaProf)); - MemoryZeroStruct(dev->prof); - } -} - -internal void -arena_print_profile__dev(Arena *arena, Arena *out_arena, String8List *out){ - Assert(arena != out_arena); - - //- get dev & disable - ArenaDev *dev = arena->dev; - arena->dev = 0; - - //- get prof - ArenaProf *prof = (dev != 0)?dev->prof:0; - - //- not equipped with prof - if (prof == 0){ - str8_list_push(out_arena, out, str8_lit("not equipped with a memory profile\n")); - } - - //- print prof - if (prof != 0){ - Temp scratch = temp_begin(dev->arena); - - //- make flat array - U64 note_count = prof->count; - ArenaProfNode **notes = push_array_no_zero__no_annotation(scratch.arena, ArenaProfNode*, note_count); - { - ArenaProfNode **note_ptr = notes; - for (ArenaProfNode *node = prof->first; - node != 0; - node = node->next, note_ptr += 1){ - *note_ptr = node; - } - } - - //- file name size - U64 max_file_name_size = 0; - { - ArenaProfNode **note_ptr = notes; - for (U64 i = 0; i < note_count; i += 1, note_ptr += 1){ - max_file_name_size = Max(max_file_name_size, (**note_ptr).file_name.size); - } - } - - //- sort (> size, < [address]) - for (U64 i = 0; i < note_count; i += 1){ - ArenaProfNode **i_note = notes + i; - ArenaProfNode **min_note = i_note; - for (U64 j = i + 1; j < note_count; j += 1){ - ArenaProfNode **j_note = notes + j; - if ((**j_note).size > (**min_note).size || - ((**j_note).size == (**min_note).size && *j_note < *min_note)){ - min_note = j_note; - } - } - if (min_note != i_note){ - ArenaProfNode *t = *i_note; - *i_note = *min_note; - *min_note = t; - } - } - - //- total size - U64 total_size = 0; - { - ArenaProfNode **note_ptr = notes; - for (U64 i = 0; i < note_count; i += 1, note_ptr += 1){ - ArenaProfNode *note = *note_ptr; - total_size += note->size; - } - } - - //- print - { - str8_list_pushf(out_arena, out, "memory total: %llu\n", total_size); - - ArenaProfNode **note_ptr = notes; - for (U64 i = 0; i < note_count; i += 1, note_ptr += 1){ - ArenaProfNode *note = *note_ptr; - String8 location = push_str8f(scratch.arena, "%S:%5llu:", - note->file_name, note->line); - F32 percent = 100.f*((F32)note->size)/total_size; - str8_list_pushf(out_arena, out, "%*.*s %12llu %5.2f%% [%5llu]\n", - max_file_name_size + 7, str8_varg(location), - note->size, percent, note->count); - } - } - - temp_end(scratch); - } - - //- restore dev - arena->dev = dev; -} - -internal void -arena_prof_inc_counters__dev(Arena *dev_arena, ArenaProf *prof, String8 file_name, U64 line, - U64 size, U64 count){ - // find existing profile node - ArenaProfNode *prof_node = 0; - for (ArenaProfNode *node = prof->first; - node != 0; - node = node->next){ - if (node->line == line && str8_match(file_name, node->file_name, 0)){ - prof_node = node; - break; - } - } - // make new histogram node if necessary - if (prof_node == 0){ - prof_node = (ArenaProfNode*)arena_push(dev_arena, sizeof(*prof_node)); - SLLQueuePush(prof->first, prof->last, prof_node); - prof->count += 1; - prof_node->file_name = file_name; - prof_node->line = line; - } - // record this allocation - prof_node->size += size; - prof_node->count += count; -} - -#endif diff --git a/src/metagen/metagen_base/metagen_base_arena_dev.h b/src/metagen/metagen_base/metagen_base_arena_dev.h deleted file mode 100644 index 14d16b1d..00000000 --- a/src/metagen/metagen_base/metagen_base_arena_dev.h +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#ifndef BASE_ARENA_DEV_H -#define BASE_ARENA_DEV_H - -//////////////////////////////// -//~ NOTE(allen): Dev Arena Types - -typedef struct ArenaDev ArenaDev; -struct ArenaDev -{ - Arena *arena; - struct ArenaProf *prof; -}; - -typedef struct ArenaProf ArenaProf; -struct ArenaProf -{ - struct ArenaProfNode *first; - struct ArenaProfNode *last; - U64 count; -}; - -typedef struct ArenaProfNode ArenaProfNode; -struct ArenaProfNode -{ - ArenaProfNode *next; - String8 file_name; - U64 line; - U64 size; - U64 count; -}; - -//////////////////////////////// -//~ NOTE(allen): Dev Arena Functions - -#if ENABLE_DEV -internal void arena_annotate_push__dev(Arena *arena, U64 size, void *ptr); -internal void arena_annotate_absorb__dev(Arena *arena, Arena *sub); -internal ArenaDev* arena_equip__dev(Arena *arena); -internal void arena_equip_profile__dev(Arena *arena); -internal void arena_print_profile__dev(Arena *arena, Arena *out_arena, String8List *out); -internal void arena_prof_inc_counters__dev(Arena *dev_arena, ArenaProf *prof, String8 file_name, U64 line, U64 size, U64 count); -#endif - -#endif // BASE_ARENA_DEV_H diff --git a/src/metagen/metagen_base/metagen_base_bits.c b/src/metagen/metagen_base/metagen_base_bits.c deleted file mode 100644 index 98ae781f..00000000 --- a/src/metagen/metagen_base/metagen_base_bits.c +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#if COMPILER_CL || (COMPILER_CLANG && OS_WINDOWS) - -internal U64 -count_bits_set16(U16 val) -{ - return __popcnt16(val); -} - -internal U64 -count_bits_set32(U32 val) -{ - return __popcnt(val); -} - -internal U64 -count_bits_set64(U64 val) -{ - return __popcnt64(val); -} - -internal U64 -ctz32(U32 mask) -{ - unsigned long idx; - _BitScanForward(&idx, mask); - return idx; -} - -internal U64 -ctz64(U64 mask) -{ - unsigned long idx; - _BitScanForward64(&idx, mask); - return idx; -} - -internal U64 -clz32(U32 mask) -{ - unsigned long idx; - _BitScanReverse(&idx, mask); - return 31 - idx; -} - -internal U64 -clz64(U64 mask) -{ - unsigned long idx; - _BitScanReverse64(&idx, mask); - return 63 - idx; -} - -#elif COMPILER_CLANG || COMPILER_GCC - -internal U64 -count_bits_set16(U16 val) -{ - NotImplemented; - return 0; -} - -internal U64 -count_bits_set32(U32 val) -{ - NotImplemented; - return 0; -} - -internal U64 -count_bits_set64(U64 val) -{ - NotImplemented; - return 0; -} - -internal U64 -ctz32(U32 val) -{ - NotImplemented; - return 0; -} - -internal U64 -clz32(U32 val) -{ - NotImplemented; - return 0; -} - -internal U64 -clz64(U64 val) -{ - NotImplemented; - return 0; -} - -#else -# error "bits not defined for this target" -#endif - diff --git a/src/metagen/metagen_base/metagen_base_bits.h b/src/metagen/metagen_base/metagen_base_bits.h deleted file mode 100644 index ab9482d3..00000000 --- a/src/metagen/metagen_base/metagen_base_bits.h +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#ifndef BASE_BITS_H -#define BASE_BITS_H - -#define ExtractBit(word, idx) (((word) >> (idx)) & 1) - -internal U64 count_bits_set16(U16 val); -internal U64 count_bits_set32(U32 val); -internal U64 count_bits_set64(U64 val); - -internal U64 ctz32(U32 val); -internal U64 ctz64(U64 val); -internal U64 clz32(U32 val); -internal U64 clz64(U64 val); - -#endif // BASE_BITS_H diff --git a/src/metagen/metagen_base/metagen_base_command_line.c b/src/metagen/metagen_base/metagen_base_command_line.c index 2a40b5c8..ecf23745 100644 --- a/src/metagen/metagen_base/metagen_base_command_line.c +++ b/src/metagen/metagen_base/metagen_base_command_line.c @@ -1,229 +1,232 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -//////////////////////////////// -//~ NOTE(rjf): Command Line Option Parsing - -internal 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; -} - -internal 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; -} - -internal 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; -} - -internal void -cmd_line_push_opt(CmdLineOptList *list, CmdLineOpt *var) -{ - SLLQueuePush(list->first, list->last, var); - list->count += 1; -} - -internal CmdLineOpt * -cmd_line_insert_opt(Arena *arena, 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 = push_array(arena, CmdLineOpt, 1); - var->hash_next = *slot; - var->hash = cmd_line_hash_from_string(string); - var->string = push_str8_copy(arena, 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(arena, &var->value_strings, &join); - *slot = var; - cmd_line_push_opt(&cmd_line->options, var); - } - return var; -} - -internal CmdLine -cmd_line_from_string_list(Arena *arena, String8List command_line) -{ - CmdLine parsed = {0}; - - // NOTE(rjf): Set up config option table. - { - parsed.option_table_size = 4096; - parsed.option_table = push_array(arena, CmdLineOpt *, parsed.option_table_size); - } - - // NOTE(rjf): Parse command line. - B32 after_passthrough_option = 0; - 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(arena, string, splits, ArrayCount(splits), 0); - for(String8Node *sub_arg = args_in_this_string.first; sub_arg; sub_arg = sub_arg->next) - { - str8_list_push(arena, &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(arena, &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)) - { - str8_list_push(arena, &parsed.inputs, node->string); - after_passthrough_option = 1; - } - } - - return parsed; -} - -internal 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); -} - -internal 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; -} - -internal 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; -} - -internal B32 -cmd_line_has_flag(CmdLine *cmd_line, String8 name) -{ - CmdLineOpt *var = cmd_line_opt_from_string(cmd_line, name); - return(var != 0); -} - -internal 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); -} +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +//////////////////////////////// +//~ NOTE(rjf): Command Line Option Parsing + +internal 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; +} + +internal 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; +} + +internal 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; +} + +internal void +cmd_line_push_opt(CmdLineOptList *list, CmdLineOpt *var) +{ + SLLQueuePush(list->first, list->last, var); + list->count += 1; +} + +internal CmdLineOpt * +cmd_line_insert_opt(Arena *arena, 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 = push_array(arena, CmdLineOpt, 1); + var->hash_next = *slot; + var->hash = cmd_line_hash_from_string(string); + var->string = push_str8_copy(arena, 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(arena, &var->value_strings, &join); + *slot = var; + cmd_line_push_opt(&cmd_line->options, var); + } + return var; +} + +internal CmdLine +cmd_line_from_string_list(Arena *arena, 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 = push_array(arena, 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(arena, string, splits, ArrayCount(splits), 0); + for(String8Node *sub_arg = args_in_this_string.first; sub_arg; sub_arg = sub_arg->next) + { + str8_list_push(arena, &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(arena, &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(arena, &parsed.inputs, node->string); + after_passthrough_option = 1; + first_passthrough = 0; + } + } + + return parsed; +} + +internal 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); +} + +internal 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; +} + +internal 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; +} + +internal B32 +cmd_line_has_flag(CmdLine *cmd_line, String8 name) +{ + CmdLineOpt *var = cmd_line_opt_from_string(cmd_line, name); + return(var != 0); +} + +internal 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); +} diff --git a/src/metagen/metagen_base/metagen_base_command_line.h b/src/metagen/metagen_base/metagen_base_command_line.h index bfbc35ef..c37f91dc 100644 --- a/src/metagen/metagen_base/metagen_base_command_line.h +++ b/src/metagen/metagen_base/metagen_base_command_line.h @@ -1,53 +1,54 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#ifndef BASE_COMMAND_LINE_H -#define BASE_COMMAND_LINE_H - -//////////////////////////////// -//~ 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 -{ - CmdLineOptList options; - String8List inputs; - U64 option_table_size; - CmdLineOpt **option_table; -}; - -//////////////////////////////// -//~ NOTE(rjf): Command Line Option Parsing - -internal U64 cmd_line_hash_from_string(String8 string); -internal CmdLineOpt** cmd_line_slot_from_string(CmdLine *cmd_line, String8 string); -internal CmdLineOpt* cmd_line_opt_from_slot(CmdLineOpt **slot, String8 string); -internal void cmd_line_push_opt(CmdLineOptList *list, CmdLineOpt *var); -internal CmdLineOpt* cmd_line_insert_opt(Arena *arena, CmdLine *cmd_line, String8 string, String8List values); -internal CmdLine cmd_line_from_string_list(Arena *arena, String8List arguments); -internal CmdLineOpt* cmd_line_opt_from_string(CmdLine *cmd_line, String8 name); -internal String8List cmd_line_strings(CmdLine *cmd_line, String8 name); -internal String8 cmd_line_string(CmdLine *cmd_line, String8 name); -internal B32 cmd_line_has_flag(CmdLine *cmd_line, String8 name); -internal B32 cmd_line_has_argument(CmdLine *cmd_line, String8 name); - -#endif // BASE_COMMAND_LINE_H +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef BASE_COMMAND_LINE_H +#define BASE_COMMAND_LINE_H + +//////////////////////////////// +//~ 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 + +internal U64 cmd_line_hash_from_string(String8 string); +internal CmdLineOpt** cmd_line_slot_from_string(CmdLine *cmd_line, String8 string); +internal CmdLineOpt* cmd_line_opt_from_slot(CmdLineOpt **slot, String8 string); +internal void cmd_line_push_opt(CmdLineOptList *list, CmdLineOpt *var); +internal CmdLineOpt* cmd_line_insert_opt(Arena *arena, CmdLine *cmd_line, String8 string, String8List values); +internal CmdLine cmd_line_from_string_list(Arena *arena, String8List arguments); +internal CmdLineOpt* cmd_line_opt_from_string(CmdLine *cmd_line, String8 name); +internal String8List cmd_line_strings(CmdLine *cmd_line, String8 name); +internal String8 cmd_line_string(CmdLine *cmd_line, String8 name); +internal B32 cmd_line_has_flag(CmdLine *cmd_line, String8 name); +internal B32 cmd_line_has_argument(CmdLine *cmd_line, String8 name); + +#endif // BASE_COMMAND_LINE_H diff --git a/src/metagen/metagen_base/metagen_base_context_cracking.h b/src/metagen/metagen_base/metagen_base_context_cracking.h index e35b10f4..040c0004 100644 --- a/src/metagen/metagen_base/metagen_base_context_cracking.h +++ b/src/metagen/metagen_base/metagen_base_context_cracking.h @@ -1,154 +1,247 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#ifndef BASE_CONTEXT_CRACKING_H -#define BASE_CONTEXT_CRACKING_H - -#if defined(__clang__) - -# define COMPILER_CLANG 1 - -# if defined(_WIN32) -# define OS_WINDOWS 1 -# elif defined(__gnu_linux__) || defined(__linux__) -# define OS_LINUX 1 -# elif defined(__APPLE__) && defined(__MACH__) -# define OS_MAC 1 -# else -# error This compiler/platform combo is not supported yet -# endif - -# 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 yet -# endif - -#elif defined(_MSC_VER) - -# define COMPILER_CL 1 - -# if defined(_WIN32) -# define OS_WINDOWS 1 -# else -# error This compiler/platform combo is not supported yet -# endif - -# 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 yet -# endif - -#elif defined(__GNUC__) || defined(__GNUG__) - -# define COMPILER_GCC 1 - -# if defined(__gnu_linux__) || defined(__linux__) -# define OS_LINUX 1 -# else -# error This compiler/platform combo is not supported yet -# endif - -# 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 yet -# endif - -#else -# error This compiler is not supported yet -#endif - -#if defined(ARCH_X64) -# define ARCH_64BIT 1 -#elif defined(ARCH_X86) -# define ARCH_32BIT 1 -#endif - -#if defined(__cplusplus) -# define LANG_CPP 1 -#else -# define LANG_C 1 -#endif - -// zeroify - -#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_CL) -# define COMPILER_CL 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 - -#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 - -//////////////////////////////// -//~ 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 - -#endif // BASE_CONTEXT_CRACKING_H +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef BASE_CONTEXT_CRACKING_H +#define BASE_CONTEXT_CRACKING_H + +//////////////////////////////// +//~ rjf: Clang OS/Arch Cracking + +#if defined(__clang__) + +# define COMPILER_CLANG 1 + +# if defined(_WIN32) +# define OS_WINDOWS 1 +# elif defined(__gnu_linux__) || defined(__linux__) +# define OS_LINUX 1 +# elif defined(__APPLE__) && defined(__MACH__) +# define OS_MAC 1 +# else +# error This compiler/OS combo is not supported. +# endif + +# 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 + +//////////////////////////////// +//~ rjf: MSVC OS/Arch Cracking + +#elif defined(_MSC_VER) + +# define COMPILER_MSVC 1 + +# 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 COMPILER_MSVC_YEAR 2008 +# elif _MSC_VER >= 1400 +# define COMPILER_MSVC_YEAR 2005 +# else +# define COMPILER_MSVC_YEAR 0 +# endif + +# if defined(_WIN32) +# define OS_WINDOWS 1 +# else +# error This compiler/OS combo is not supported. +# endif + +# 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 + +//////////////////////////////// +//~ rjf: GCC OS/Arch Cracking + +#elif defined(__GNUC__) || defined(__GNUG__) + +# define COMPILER_GCC 1 + +# if defined(__gnu_linux__) || defined(__linux__) +# define OS_LINUX 1 +# else +# error This compiler/OS combo is not supported. +# endif + +# 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 + +#else +# error Compiler not supported. +#endif + +//////////////////////////////// +//~ rjf: Arch Cracking + +#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 + +//////////////////////////////// +//~ rjf: Language Cracking + +#if defined(__cplusplus) +# define LANG_CPP 1 +#else +# define LANG_C 1 +#endif + +//////////////////////////////// +//~ rjf: Build Option Cracking + +#if !defined(BUILD_DEBUG) +# define BUILD_DEBUG 1 +#endif + +#if !defined(BUILD_SUPPLEMENTARY_UNIT) +# define BUILD_SUPPLEMENTARY_UNIT 0 +#endif + +#if !defined(BUILD_ENTRY_DEFINING_UNIT) +# define BUILD_ENTRY_DEFINING_UNIT 1 +#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 0 +#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 "https://github.com/EpicGames/raddebugger/issues" +#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 + +//////////////////////////////// +//~ 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 + +#endif // BASE_CONTEXT_CRACKING_H diff --git a/src/metagen/metagen_base/metagen_base_types.c b/src/metagen/metagen_base/metagen_base_core.c similarity index 81% rename from src/metagen/metagen_base/metagen_base_types.c rename to src/metagen/metagen_base/metagen_base_core.c index 9627f478..877dcb4e 100644 --- a/src/metagen/metagen_base/metagen_base_types.c +++ b/src/metagen/metagen_base/metagen_base_core.c @@ -1,454 +1,562 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -//////////////////////////////// -//~ Safe Casts - -internal U16 -safe_cast_u16(U32 x) -{ - AssertAlways(x <= max_U16); - U16 result = (U16)x; - return result; -} - -internal U32 -safe_cast_u32(U64 x) -{ - AssertAlways(x <= max_U32); - U32 result = (U32)x; - return result; -} - -internal S32 -safe_cast_s32(S64 x) -{ - AssertAlways(x <= max_S32); - S32 result = (S32)x; - return result; -} - -//////////////////////////////// -//~ rjf: Large Base Type Functions - -internal U128 -u128_zero(void) -{ - U128 v = {0}; - return v; -} - -internal U128 -u128_make(U64 v0, U64 v1) -{ - U128 v = {v0, v1}; - return v; -} - -internal B32 -u128_match(U128 a, U128 b) -{ - return MemoryMatchStruct(&a, &b); -} - -//////////////////////////////// -//~ rjf: Bit Patterns - -internal U32 -u32_from_u64_saturate(U64 x){ - U32 x32 = (x > max_U32)?max_U32:(U32)x; - return(x32); -} - -internal 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); -} - -internal 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; -} - -internal 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; -} - -internal F32 -inf32(void){ - union { U32 u; F32 f; } x; - x.u = exponent32; - return(x.f); -} - -internal F32 -neg_inf32(void){ - union { U32 u; F32 f; } x; - x.u = sign32 | exponent32; - return(x.f); -} - -internal U16 -bswap_u16(U16 x) -{ - U16 result = (((x & 0xFF00) >> 8) | - ((x & 0x00FF) << 8)); - return result; -} - -internal U32 -bswap_u32(U32 x) -{ - U32 result = (((x & 0xFF000000) >> 24) | - ((x & 0x00FF0000) >> 8) | - ((x & 0x0000FF00) << 8) | - ((x & 0x000000FF) << 24)); - return result; -} - -internal 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; -} - -//////////////////////////////// -//~ rjf: Enum -> Sign - -internal S32 -sign_from_side_S32(Side side){ - return((side == Side_Min)?-1:1); -} - -internal F32 -sign_from_side_F32(Side side){ - return((side == Side_Min)?-1.f:1.f); -} - -//////////////////////////////// -//~ rjf: Memory Functions - -internal 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); -} - -//////////////////////////////// -//~ rjf: Text 2D Coordinate/Range Functions - -internal TxtPt -txt_pt(S64 line, S64 column) -{ - TxtPt p = {0}; - p.line = line; - p.column = column; - return p; -} - -internal B32 -txt_pt_match(TxtPt a, TxtPt b) -{ - return a.line == b.line && a.column == b.column; -} - -internal 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; -} - -internal TxtPt -txt_pt_min(TxtPt a, TxtPt b) -{ - TxtPt result = b; - if(txt_pt_less_than(a, b)) - { - result = a; - } - return result; -} - -internal TxtPt -txt_pt_max(TxtPt a, TxtPt b) -{ - TxtPt result = a; - if(txt_pt_less_than(a, b)) - { - result = b; - } - return result; -} - -internal 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; -} - -internal 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; -} - -internal 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; -} - -//////////////////////////////// -//~ rjf: Toolchain/Environment Enum Functions - -internal U64 -bit_size_from_arch(Architecture arch) -{ - // TODO(rjf): metacode - U64 arch_bitsize = 0; - switch(arch) - { - case Architecture_x64: arch_bitsize = 64; break; - case Architecture_x86: arch_bitsize = 32; break; - case Architecture_arm64: arch_bitsize = 64; break; - case Architecture_arm32: arch_bitsize = 32; break; - default: break; - } - return arch_bitsize; -} - -internal U64 -max_instruction_size_from_arch(Architecture arch) -{ - // TODO(rjf): make this real - return 64; -} - -internal 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; -} - -internal Architecture -architecture_from_context(void){ - Architecture arch = Architecture_Null; -#if ARCH_X64 - arch = Architecture_x64; -#elif ARCH_X86 - arch = Architecture_x86; -#elif ARCH_ARM64 - arch = Architecture_arm64; -#elif ARCH_ARM32 - arch = Architecture_arm32; -#endif - return arch; -} - -internal Compiler -compiler_from_context(void){ - Compiler compiler = Compiler_Null; -#if COMPILER_CL - compiler = Compiler_cl; -#elif COMPILER_GCC - compiler = Compiler_gcc; -#elif COMPILER_CLANG - compiler = Compiler_clang; -#endif - return compiler; -} - -//////////////////////////////// -//~ rjf: Time Functions - -internal 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); -} - -internal 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); -} - -internal 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); -} - -//////////////////////////////// -//~ rjf: Non-Fancy Ring Buffer Reads/Writes - -internal 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); - MemoryCopy(ring_base+ring_off, pre_split_data, pre_split_bytes); - MemoryCopy(ring_base+0, pst_split_data, pst_split_bytes); - } - return src_data_size; -} - -internal 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; - MemoryCopy(dst_data, ring_base+ring_off, pre_split_bytes); - MemoryCopy((U8 *)dst_data + pre_split_bytes, ring_base+0, pst_split_bytes); - } - return read_size; -} +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +//////////////////////////////// +//~ rjf: Safe Casts + +internal U16 +safe_cast_u16(U32 x) +{ + AssertAlways(x <= max_U16); + U16 result = (U16)x; + return result; +} + +internal U32 +safe_cast_u32(U64 x) +{ + AssertAlways(x <= max_U32); + U32 result = (U32)x; + return result; +} + +internal S32 +safe_cast_s32(S64 x) +{ + AssertAlways(x <= max_S32); + S32 result = (S32)x; + return result; +} + +//////////////////////////////// +//~ rjf: Large Base Type Functions + +internal U128 +u128_zero(void) +{ + U128 v = {0}; + return v; +} + +internal U128 +u128_make(U64 v0, U64 v1) +{ + U128 v = {v0, v1}; + return v; +} + +internal B32 +u128_match(U128 a, U128 b) +{ + return MemoryMatchStruct(&a, &b); +} + +//////////////////////////////// +//~ rjf: Bit Patterns + +internal U32 +u32_from_u64_saturate(U64 x){ + U32 x32 = (x > max_U32)?max_U32:(U32)x; + return(x32); +} + +internal 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); +} + +internal 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; +} + +internal 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; +} + +internal F32 +inf32(void){ + union { U32 u; F32 f; } x; + x.u = exponent32; + return(x.f); +} + +internal F32 +neg_inf32(void){ + union { U32 u; F32 f; } x; + x.u = sign32 | exponent32; + return(x.f); +} + +internal U16 +bswap_u16(U16 x) +{ + U16 result = (((x & 0xFF00) >> 8) | + ((x & 0x00FF) << 8)); + return result; +} + +internal U32 +bswap_u32(U32 x) +{ + U32 result = (((x & 0xFF000000) >> 24) | + ((x & 0x00FF0000) >> 8) | + ((x & 0x0000FF00) << 8) | + ((x & 0x000000FF) << 24)); + return result; +} + +internal 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 COMPILER_MSVC || (COMPILER_CLANG && OS_WINDOWS) + +internal U64 +count_bits_set16(U16 val) +{ + return __popcnt16(val); +} + +internal U64 +count_bits_set32(U32 val) +{ + return __popcnt(val); +} + +internal U64 +count_bits_set64(U64 val) +{ + return __popcnt64(val); +} + +internal U64 +ctz32(U32 mask) +{ + unsigned long idx; + _BitScanForward(&idx, mask); + return idx; +} + +internal U64 +ctz64(U64 mask) +{ + unsigned long idx; + _BitScanForward64(&idx, mask); + return idx; +} + +internal U64 +clz32(U32 mask) +{ + unsigned long idx; + _BitScanReverse(&idx, mask); + return 31 - idx; +} + +internal U64 +clz64(U64 mask) +{ + unsigned long idx; + _BitScanReverse64(&idx, mask); + return 63 - idx; +} + +#elif COMPILER_CLANG || COMPILER_GCC + +internal U64 +count_bits_set16(U16 val) +{ + NotImplemented; + return 0; +} + +internal U64 +count_bits_set32(U32 val) +{ + NotImplemented; + return 0; +} + +internal U64 +count_bits_set64(U64 val) +{ + NotImplemented; + return 0; +} + +internal U64 +ctz32(U32 val) +{ + NotImplemented; + return 0; +} + +internal U64 +clz32(U32 val) +{ + NotImplemented; + return 0; +} + +internal U64 +clz64(U64 val) +{ + NotImplemented; + return 0; +} + +#else +# error "Bit intrinsic functions not defined for this compiler." +#endif + +//////////////////////////////// +//~ rjf: Enum -> Sign + +internal S32 +sign_from_side_S32(Side side){ + return((side == Side_Min)?-1:1); +} + +internal F32 +sign_from_side_F32(Side side){ + return((side == Side_Min)?-1.f:1.f); +} + +//////////////////////////////// +//~ rjf: Memory Functions + +internal 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); +} + +//////////////////////////////// +//~ rjf: Text 2D Coordinate/Range Functions + +internal TxtPt +txt_pt(S64 line, S64 column) +{ + TxtPt p = {0}; + p.line = line; + p.column = column; + return p; +} + +internal B32 +txt_pt_match(TxtPt a, TxtPt b) +{ + return a.line == b.line && a.column == b.column; +} + +internal 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; +} + +internal TxtPt +txt_pt_min(TxtPt a, TxtPt b) +{ + TxtPt result = b; + if(txt_pt_less_than(a, b)) + { + result = a; + } + return result; +} + +internal TxtPt +txt_pt_max(TxtPt a, TxtPt b) +{ + TxtPt result = a; + if(txt_pt_less_than(a, b)) + { + result = b; + } + return result; +} + +internal 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; +} + +internal 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; +} + +internal 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; +} + +internal 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; +} + +//////////////////////////////// +//~ rjf: Toolchain/Environment Enum Functions + +internal U64 +bit_size_from_arch(Architecture arch) +{ + // TODO(rjf): metacode + U64 arch_bitsize = 0; + switch(arch) + { + case Architecture_x64: arch_bitsize = 64; break; + case Architecture_x86: arch_bitsize = 32; break; + case Architecture_arm64: arch_bitsize = 64; break; + case Architecture_arm32: arch_bitsize = 32; break; + default: break; + } + return arch_bitsize; +} + +internal U64 +max_instruction_size_from_arch(Architecture arch) +{ + // TODO(rjf): make this real + return 64; +} + +internal 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; +} + +internal Architecture +architecture_from_context(void){ + Architecture arch = Architecture_Null; +#if ARCH_X64 + arch = Architecture_x64; +#elif ARCH_X86 + arch = Architecture_x86; +#elif ARCH_ARM64 + arch = Architecture_arm64; +#elif ARCH_ARM32 + arch = Architecture_arm32; +#endif + return arch; +} + +internal 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; +} + +//////////////////////////////// +//~ rjf: Time Functions + +internal 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); +} + +internal 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); +} + +internal 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); +} + +//////////////////////////////// +//~ rjf: Non-Fancy Ring Buffer Reads/Writes + +internal 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); + MemoryCopy(ring_base+ring_off, pre_split_data, pre_split_bytes); + MemoryCopy(ring_base+0, pst_split_data, pst_split_bytes); + } + return src_data_size; +} + +internal 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; + MemoryCopy(dst_data, ring_base+ring_off, pre_split_bytes); + MemoryCopy((U8 *)dst_data + pre_split_bytes, ring_base+0, pst_split_bytes); + } + return read_size; +} diff --git a/src/metagen/metagen_base/metagen_base_types.h b/src/metagen/metagen_base/metagen_base_core.h similarity index 68% rename from src/metagen/metagen_base/metagen_base_types.h rename to src/metagen/metagen_base/metagen_base_core.h index d006a541..565b05bd 100644 --- a/src/metagen/metagen_base/metagen_base_types.h +++ b/src/metagen/metagen_base/metagen_base_core.h @@ -1,681 +1,800 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#ifndef BASE_TYPES_H -#define BASE_TYPES_H - -//////////////////////////////// -//~ rjf: Foreign Includes - -#include -#include -#include -#include -#include - -//////////////////////////////// -//~ rjf: Build Configuration - -#if !defined(ENABLE_DEV) -# define ENABLE_DEV 0 -#endif - -#if !defined(SUPPLEMENT_UNIT) -# define SUPPLEMENT_UNIT 0 -#endif - -//////////////////////////////// -//~ rjf: Codebase Keywords - -#define internal static -#define global static -#define local_persist static - -#if COMPILER_CL || (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 - -//////////////////////////////// -//~ rjf: Memory Operation Macros - -#define MemoryCopy(dst, src, size) memmove((dst), (src), (size)) -#define MemorySet(dst, byte, size) memset((dst), (byte), (size)) -#define MemoryCompare(a, b, size) memcmp((a), (b), (size)) -#define MemoryStrlen(ptr) strlen(ptr) - -#define MemoryCopyStruct(d,s) MemoryCopy((d),(s),sizeof(*(d))) -#define MemoryCopyArray(d,s) MemoryCopy((d),(s),sizeof(d)) -#define MemoryCopyTyped(d,s,c) MemoryCopy((d),(s),sizeof(*(d))*(c)) - -#define MemoryZero(s,z) memset((s),0,(z)) -#define MemoryZeroStruct(s) MemoryZero((s),sizeof(*(s))) -#define MemoryZeroArray(a) MemoryZero((a),sizeof(a)) -#define MemoryZeroTyped(m,c) MemoryZero((m),sizeof(*(m))*(c)) - -#define MemoryMatch(a,b,z) (MemoryCompare((a),(b),(z)) == 0) -#define MemoryMatchStruct(a,b) MemoryMatch((a),(b),sizeof(*(a))) -#define MemoryMatchArray(a,b) MemoryMatch((a),(b),sizeof(a)) - -#define MemoryRead(T,p,e) ( ((p)+sizeof(T)<=(e))?(*(T*)(p)):(0) ) -#define MemoryConsume(T,p,e) \ -( ((p)+sizeof(T)<=(e))?((p)+=sizeof(T),*(T*)((p)-sizeof(T))):((p)=(e),0) ) - -//////////////////////////////// -//~ rjf: Units - -#define KB(n) (((U64)(n)) << 10) -#define MB(n) (((U64)(n)) << 20) -#define GB(n) (((U64)(n)) << 30) -#define TB(n) (((U64)(n)) << 40) -#define Thousand(n) ((n)*1000) -#define Million(n) ((n)*1000000) -#define Billion(n) ((n)*1000000000) - -//////////////////////////////// -//~ rjf: Asserts - -#if COMPILER_CL -# define Trap() __debugbreak() -#elif COMPILER_CLANG || COMPILER_GCC -# define Trap() __builtin_trap() -# else -# error "undefined trap" -#endif - -#define AssertAlways(x) do{if(!(x)) {Trap();}}while(0) -#if !defined(NDEBUG) -# define Assert(x) AssertAlways(x) -#else -# define Assert(x) (void)(x) -#endif -#define AssertImplies(a,b) Assert(!(a) || b) -#define AssertIff(a,b) Assert(!!(a) == !!(b)) -#define InvalidPath Assert(!"Invalid Path!") -#define NotImplemented Assert(!"Not Implemented!") - -#define StaticAssert(C,ID) global U8 Glue(ID,__LINE__)[(C)?1:-1] - -//////////////////////////////// -//~ rjf: Branch Predictor Hints - -#if defined(__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) - -//////////////////////////////// -//~ rjf: Misc. Helper Macros - -#define ArrayCount(a) (sizeof(a) / sizeof((a)[0])) - -#define Stmnt(S) do{ S }while(0) - -#define Stringify_(S) #S -#define Stringify(S) Stringify_(S) - -#define Glue_(A,B) A##B -#define Glue(A,B) Glue_(A,B) - -#define Min(A,B) ( ((A)<(B))?(A):(B) ) -#define Max(A,B) ( ((A)>(B))?(A):(B) ) - -#define ClampTop(A,X) Min(A,X) -#define ClampBot(X,B) Max(X,B) -#define Clamp(A,X,B) ( ((X)<(A))?(A):((X)>(B))?(B):(X) ) - -#define PtrClampTop(A,X) ClampTop(A,X) -#define PtrClampBot(X,B) ClampBot(X,B) -#define PtrClamp(A,X,B) Clamp(A,X,B) - -#define CeilIntegerDiv(a,b) (((a) + (b) - 1)/(b)) - -#define Swap(T,a,b) Stmnt( T t__ = a; a = b; b = t__; ) - -#if ARCH_64BIT -# define IntFromPtr(ptr) ((U64)(ptr)) -#elif ARCH_32BIT -# define IntFromPtr(ptr) ((U32)(ptr)) -#else -# error missing ptr cast for this architecture -#endif - -#define PtrFromInt(i) (void*)((U8*)0 + (i)) - -#define Member(T,m) (((T*)0)->m) -#define OffsetOf(T,m) IntFromPtr(&Member(T,m)) -#define MemberFromOffset(T,ptr,off) (T)((((U8 *)ptr)+(off))) -#define CastFromMember(T,m,ptr) (T*)(((U8*)ptr) - OffsetOf(T,m)) - -#define Compose64Bit(a,b) ((((U64)a) << 32) | ((U64)b)); -#define AlignPow2(x,b) (((x) + (b) - 1)&(~((b) - 1))) -#define AlignDownPow2(x,b) ((x)&(~((b) - 1))) -#define AlignPadPow2(x,b) ((0-(x)) & ((b) - 1)) -#define IsPow2(x) ((x)!=0 && ((x)&((x)-1))==0) -#define IsPow2OrZero(x) ((((x) - 1)&(x)) == 0) - -#define DeferLoop(begin, end) for(int _i_ = ((begin), 0); !_i_; _i_ += 1, (end)) -#define DeferLoopChecked(begin, end) for(int _i_ = 2 * !(begin); (_i_ == 2 ? ((end), 0) : !_i_); _i_ += 1, (end)) - -#define B8 S8 -#define B32 rrbool - -#if LANG_CPP -# define zero_struct {} -#else -# define zero_struct {0} -#endif - -#if COMPILER_MSVC && COMPILER_MSVC_YEAR < 2015 -# define this_function_name "unknown" -#else -# define this_function_name __func__ -#endif - -#if LANG_CPP -# define C_LINKAGE_BEGIN extern "C"{ -# define C_LINKAGE_END } -# define C_LINKAGE extern "C" -#else -# define C_LINKAGE_BEGIN -# define C_LINKAGE_END -# define C_LINKAGE -#endif - -#if COMPILER_CL -# define thread_static __declspec(thread) -#elif COMPILER_CLANG || COMPILER_GCC -# define thread_static __thread -#endif - -#if OS_WINDOWS -# define shared_function C_LINKAGE __declspec(dllexport) -#else -# define shared_function C_LINKAGE -#endif - -//////////////////////////////// -//~ ASAN - -#if COMPILER_CL -# if defined(__SANITIZE_ADDRESS__) -# define ASAN_ENABLED 1 -# endif -# define NO_ASAN __declspec(no_sanitize_address) -#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 -# error "NO_ASAN is not defined" -#endif - -#if ASAN_ENABLED - -#pragma comment(lib, "clang_rt.asan-x86_64.lib") - -C_LINKAGE_BEGIN -void __asan_poison_memory_region(void const volatile *addr, size_t size); -void __asan_unpoison_memory_region(void const volatile *addr, size_t size); -C_LINKAGE_END - -# define AsanPoisonMemoryRegion(addr, size) __asan_poison_memory_region((addr), (size)) -# define AsanUnpoisonMemoryRegion(addr, size) __asan_unpoison_memory_region((addr), (size)) -#else -# define AsanPoisonMemoryRegion(addr, size) ((void)(addr), (void)(size)) -# define AsanUnpoisonMemoryRegion(addr, size) ((void)(addr), (void)(size)) - -#endif - -//////////////////////////////// -//~ rjf: Base Types - -typedef uint8_t U8; -typedef uint16_t U16; -typedef uint32_t U32; -typedef uint64_t U64; -typedef int8_t S8; -typedef int16_t S16; -typedef int32_t S32; -typedef int64_t S64; -typedef S8 B8; -typedef S16 B16; -typedef S32 B32; -typedef S64 B64; -typedef float F32; -typedef double F64; - -//////////////////////////////// -//~ rjf: Large Base Types - -typedef struct U128 U128; -struct U128 -{ - U64 u64[2]; -}; - -//////////////////////////////// -//~ rjf: Basic Types & Spaces - -typedef void VoidProc(void); - -typedef enum Dimension -{ - Dimension_X, - Dimension_Y, - Dimension_Z, - Dimension_W, -} -Dimension; - -typedef enum Side -{ - Side_Invalid = -1, - Side_Min, - Side_Max, - Side_COUNT, -} -Side; -#define side_flip(s) ((Side)(!(s))) - -typedef enum Axis2 -{ - Axis2_Invalid = -1, - Axis2_X, - Axis2_Y, - Axis2_COUNT, -} -Axis2; -#define axis2_flip(a) ((Axis2)(!(a))) - -typedef enum Corner -{ - Corner_Invalid = -1, - Corner_00, - Corner_01, - Corner_10, - Corner_11, - Corner_COUNT -} -Corner; - -//////////////////////////////// -//~ rjf: Toolchain/Environment Enums - -typedef enum OperatingSystem -{ - OperatingSystem_Null, - OperatingSystem_Windows, - OperatingSystem_Linux, - OperatingSystem_Mac, - OperatingSystem_COUNT, -} -OperatingSystem; - -typedef enum Architecture -{ - Architecture_Null, - Architecture_x64, - Architecture_x86, - Architecture_arm64, - Architecture_arm32, - Architecture_COUNT, -} -Architecture; - -typedef enum Compiler -{ - Compiler_Null, - Compiler_cl, - Compiler_gcc, - Compiler_clang, - Compiler_COUNT, -} -Compiler; - -//////////////////////////////// -//~ 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; -}; - -//////////////////////////////// -//~ NOTE(allen): Constants - -global U32 sign32 = 0x80000000; -global U32 exponent32 = 0x7F800000; -global U32 mantissa32 = 0x007FFFFF; - -global F32 big_golden32 = 1.61803398875f; -global F32 small_golden32 = 0.61803398875f; - -global F32 pi32 = 3.1415926535897f; - -global F64 machine_epsilon64 = 4.94065645841247e-324; - -global U64 max_U64 = 0xffffffffffffffffull; -global U32 max_U32 = 0xffffffff; -global U16 max_U16 = 0xffff; -global U8 max_U8 = 0xff; - -global S64 max_S64 = (S64)0x7fffffffffffffffull; -global S32 max_S32 = (S32)0x7fffffff; -global S16 max_S16 = (S16)0x7fff; -global S8 max_S8 = (S8)0x7f; - -global S64 min_S64 = (S64)0xffffffffffffffffull; -global S32 min_S32 = (S32)0xffffffff; -global S16 min_S16 = (S16)0xffff; -global S8 min_S8 = (S8)0xff; - -global const U32 bitmask1 = 0x00000001; -global const U32 bitmask2 = 0x00000003; -global const U32 bitmask3 = 0x00000007; -global const U32 bitmask4 = 0x0000000f; -global const U32 bitmask5 = 0x0000001f; -global const U32 bitmask6 = 0x0000003f; -global const U32 bitmask7 = 0x0000007f; -global const U32 bitmask8 = 0x000000ff; -global const U32 bitmask9 = 0x000001ff; -global const U32 bitmask10 = 0x000003ff; -global const U32 bitmask11 = 0x000007ff; -global const U32 bitmask12 = 0x00000fff; -global const U32 bitmask13 = 0x00001fff; -global const U32 bitmask14 = 0x00003fff; -global const U32 bitmask15 = 0x00007fff; -global const U32 bitmask16 = 0x0000ffff; -global const U32 bitmask17 = 0x0001ffff; -global const U32 bitmask18 = 0x0003ffff; -global const U32 bitmask19 = 0x0007ffff; -global const U32 bitmask20 = 0x000fffff; -global const U32 bitmask21 = 0x001fffff; -global const U32 bitmask22 = 0x003fffff; -global const U32 bitmask23 = 0x007fffff; -global const U32 bitmask24 = 0x00ffffff; -global const U32 bitmask25 = 0x01ffffff; -global const U32 bitmask26 = 0x03ffffff; -global const U32 bitmask27 = 0x07ffffff; -global const U32 bitmask28 = 0x0fffffff; -global const U32 bitmask29 = 0x1fffffff; -global const U32 bitmask30 = 0x3fffffff; -global const U32 bitmask31 = 0x7fffffff; -global const U32 bitmask32 = 0xffffffff; - -global const U64 bitmask33 = 0x00000001ffffffffull; -global const U64 bitmask34 = 0x00000003ffffffffull; -global const U64 bitmask35 = 0x00000007ffffffffull; -global const U64 bitmask36 = 0x0000000fffffffffull; -global const U64 bitmask37 = 0x0000001fffffffffull; -global const U64 bitmask38 = 0x0000003fffffffffull; -global const U64 bitmask39 = 0x0000007fffffffffull; -global const U64 bitmask40 = 0x000000ffffffffffull; -global const U64 bitmask41 = 0x000001ffffffffffull; -global const U64 bitmask42 = 0x000003ffffffffffull; -global const U64 bitmask43 = 0x000007ffffffffffull; -global const U64 bitmask44 = 0x00000fffffffffffull; -global const U64 bitmask45 = 0x00001fffffffffffull; -global const U64 bitmask46 = 0x00003fffffffffffull; -global const U64 bitmask47 = 0x00007fffffffffffull; -global const U64 bitmask48 = 0x0000ffffffffffffull; -global const U64 bitmask49 = 0x0001ffffffffffffull; -global const U64 bitmask50 = 0x0003ffffffffffffull; -global const U64 bitmask51 = 0x0007ffffffffffffull; -global const U64 bitmask52 = 0x000fffffffffffffull; -global const U64 bitmask53 = 0x001fffffffffffffull; -global const U64 bitmask54 = 0x003fffffffffffffull; -global const U64 bitmask55 = 0x007fffffffffffffull; -global const U64 bitmask56 = 0x00ffffffffffffffull; -global const U64 bitmask57 = 0x01ffffffffffffffull; -global const U64 bitmask58 = 0x03ffffffffffffffull; -global const U64 bitmask59 = 0x07ffffffffffffffull; -global const U64 bitmask60 = 0x0fffffffffffffffull; -global const U64 bitmask61 = 0x1fffffffffffffffull; -global const U64 bitmask62 = 0x3fffffffffffffffull; -global const U64 bitmask63 = 0x7fffffffffffffffull; -global const U64 bitmask64 = 0xffffffffffffffffull; - -global const U32 bit1 = (1<<0); -global const U32 bit2 = (1<<1); -global const U32 bit3 = (1<<2); -global const U32 bit4 = (1<<3); -global const U32 bit5 = (1<<4); -global const U32 bit6 = (1<<5); -global const U32 bit7 = (1<<6); -global const U32 bit8 = (1<<7); -global const U32 bit9 = (1<<8); -global const U32 bit10 = (1<<9); -global const U32 bit11 = (1<<10); -global const U32 bit12 = (1<<11); -global const U32 bit13 = (1<<12); -global const U32 bit14 = (1<<13); -global const U32 bit15 = (1<<14); -global const U32 bit16 = (1<<15); -global const U32 bit17 = (1<<16); -global const U32 bit18 = (1<<17); -global const U32 bit19 = (1<<18); -global const U32 bit20 = (1<<19); -global const U32 bit21 = (1<<20); -global const U32 bit22 = (1<<21); -global const U32 bit23 = (1<<22); -global const U32 bit24 = (1<<23); -global const U32 bit25 = (1<<24); -global const U32 bit26 = (1<<25); -global const U32 bit27 = (1<<26); -global const U32 bit28 = (1<<27); -global const U32 bit29 = (1<<28); -global const U32 bit30 = (1<<29); -global const U32 bit31 = (1<<30); -global const U32 bit32 = (1<<31); - -global const U64 bit33 = (1ull<<32); -global const U64 bit34 = (1ull<<33); -global const U64 bit35 = (1ull<<34); -global const U64 bit36 = (1ull<<35); -global const U64 bit37 = (1ull<<36); -global const U64 bit38 = (1ull<<37); -global const U64 bit39 = (1ull<<38); -global const U64 bit40 = (1ull<<39); -global const U64 bit41 = (1ull<<40); -global const U64 bit42 = (1ull<<41); -global const U64 bit43 = (1ull<<42); -global const U64 bit44 = (1ull<<43); -global const U64 bit45 = (1ull<<44); -global const U64 bit46 = (1ull<<45); -global const U64 bit47 = (1ull<<46); -global const U64 bit48 = (1ull<<47); -global const U64 bit49 = (1ull<<48); -global const U64 bit50 = (1ull<<49); -global const U64 bit51 = (1ull<<50); -global const U64 bit52 = (1ull<<51); -global const U64 bit53 = (1ull<<52); -global const U64 bit54 = (1ull<<53); -global const U64 bit55 = (1ull<<54); -global const U64 bit56 = (1ull<<55); -global const U64 bit57 = (1ull<<56); -global const U64 bit58 = (1ull<<57); -global const U64 bit59 = (1ull<<58); -global const U64 bit60 = (1ull<<59); -global const U64 bit61 = (1ull<<60); -global const U64 bit62 = (1ull<<61); -global const U64 bit63 = (1ull<<62); -global const U64 bit64 = (1ull<<63); - -//////////////////////////////// -//~ allen: Time - -typedef enum WeekDay -{ - WeekDay_Sun, - WeekDay_Mon, - WeekDay_Tue, - WeekDay_Wed, - WeekDay_Thu, - WeekDay_Fri, - WeekDay_Sat, - WeekDay_COUNT, -} -WeekDay; - -typedef 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, -} -Month; - -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; - -//////////////////////////////// -//~ allen: Files - -typedef U32 FilePropertyFlags; -enum -{ - FilePropertyFlag_IsFolder = (1 << 0), -}; - -typedef struct FileProperties FileProperties; -struct FileProperties -{ - U64 size; - DenseTime modified; - DenseTime created; - FilePropertyFlags flags; -}; - -//////////////////////////////// -//~ Safe Casts - -internal U16 safe_cast_u16(U32 x); -internal U32 safe_cast_u32(U64 x); -internal S32 safe_cast_s32(S64 x); - -//////////////////////////////// -//~ rjf: Large Base Type Functions - -internal U128 u128_zero(void); -internal U128 u128_make(U64 v0, U64 v1); -internal B32 u128_match(U128 a, U128 b); - -//////////////////////////////// -//~ rjf: Bit Patterns - -internal U32 u32_from_u64_saturate(U64 x); -internal U64 u64_up_to_pow2(U64 x); -internal S32 extend_sign32(U32 x, U32 size); -internal S64 extend_sign64(U64 x, U64 size); - -internal F32 inf32(void); -internal F32 neg_inf32(void); - -internal U16 bswap_u16(U16 x); -internal U32 bswap_u32(U32 x); -internal U64 bswap_u64(U64 x); - -//////////////////////////////// -//~ rjf: Enum -> Sign - -internal S32 sign_from_side_S32(Side side); -internal F32 sign_from_side_F32(Side side); - -//////////////////////////////// -//~ rjf: Memory Functions - -internal B32 memory_is_zero(void *ptr, U64 size); - -//////////////////////////////// -//~ rjf: Text 2D Coordinate/Range Functions - -internal TxtPt txt_pt(S64 line, S64 column); -internal B32 txt_pt_match(TxtPt a, TxtPt b); -internal B32 txt_pt_less_than(TxtPt a, TxtPt b); -internal TxtPt txt_pt_min(TxtPt a, TxtPt b); -internal TxtPt txt_pt_max(TxtPt a, TxtPt b); -internal TxtRng txt_rng(TxtPt min, TxtPt max); -internal TxtRng txt_rng_intersect(TxtRng a, TxtRng b); -internal TxtRng txt_rng_union(TxtRng a, TxtRng b); - -//////////////////////////////// -//~ rjf: Toolchain/Environment Enum Functions - -internal U64 bit_size_from_arch(Architecture arch); -internal U64 max_instruction_size_from_arch(Architecture arch); - -internal OperatingSystem operating_system_from_context(void); -internal Architecture architecture_from_context(void); -internal Compiler compiler_from_context(void); - -//////////////////////////////// -//~ rjf: Time Functions - -internal DenseTime dense_time_from_date_time(DateTime date_time); -internal DateTime date_time_from_dense_time(DenseTime time); -internal DateTime date_time_from_micro_seconds(U64 time); - -//////////////////////////////// -//~ rjf: Non-Fancy Ring Buffer Reads/Writes - -internal U64 ring_write(U8 *ring_base, U64 ring_size, U64 ring_pos, void *src_data, U64 src_data_size); -internal 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))) - -#endif // BASE_TYPES_H +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef BASE_CORE_H +#define BASE_CORE_H + +//////////////////////////////// +//~ rjf: Foreign Includes + +#include +#include +#include +#include +#include + +//////////////////////////////// +//~ rjf: Codebase Keywords + +#define internal static +#define global static +#define local_persist static + +#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 + +#if COMPILER_MSVC +# define thread_static __declspec(thread) +#elif COMPILER_CLANG || COMPILER_GCC +# define thread_static __thread +#endif + +//////////////////////////////// +//~ rjf: Linkage Keyword Macros + +#if OS_WINDOWS +# define shared_function C_LINKAGE __declspec(dllexport) +#else +# define shared_function C_LINKAGE +#endif + +#if LANG_CPP +# define C_LINKAGE_BEGIN extern "C"{ +# define C_LINKAGE_END } +# define C_LINKAGE extern "C" +#else +# define C_LINKAGE_BEGIN +# define C_LINKAGE_END +# define C_LINKAGE +#endif + +//////////////////////////////// +//~ rjf: Units + +#define KB(n) (((U64)(n)) << 10) +#define MB(n) (((U64)(n)) << 20) +#define GB(n) (((U64)(n)) << 30) +#define TB(n) (((U64)(n)) << 40) +#define Thousand(n) ((n)*1000) +#define Million(n) ((n)*1000000) +#define Billion(n) ((n)*1000000000) + +//////////////////////////////// +//~ rjf: Branch Predictor Hints + +#if defined(__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) + +//////////////////////////////// +//~ rjf: Clamps, Mins, Maxes + +#define Min(A,B) (((A)<(B))?(A):(B)) +#define Max(A,B) (((A)>(B))?(A):(B)) +#define ClampTop(A,X) Min(A,X) +#define ClampBot(X,B) Max(X,B) +#define Clamp(A,X,B) (((X)<(A))?(A):((X)>(B))?(B):(X)) + +//////////////////////////////// +//~ rjf: Type -> Alignment + +#if COMPILER_MSVC +# define AlignOf(T) __alignof(T) +#elif COMPILER_CLANG +# define AlignOf(T) __alignof(T) +#elif COMPILER_GCC +# define AlignOf(T) __alignof__(T) +#else +# error AlignOf not defined for this compiler. +#endif + +//////////////////////////////// +//~ rjf: Member Offsets + +#define Member(T,m) (((T*)0)->m) +#define OffsetOf(T,m) IntFromPtr(&Member(T,m)) +#define MemberFromOffset(T,ptr,off) (T)((((U8 *)ptr)+(off))) +#define CastFromMember(T,m,ptr) (T*)(((U8*)ptr) - OffsetOf(T,m)) + +//////////////////////////////// +//~ rjf: For-Loop Construct Macros + +#define DeferLoop(begin, end) for(int _i_ = ((begin), 0); !_i_; _i_ += 1, (end)) +#define DeferLoopChecked(begin, end) for(int _i_ = 2 * !(begin); (_i_ == 2 ? ((end), 0) : !_i_); _i_ += 1, (end)) + +#define EachEnumVal(type, it) type it = (type)0; it < type##_COUNT; it = (type)(it+1) +#define EachNonZeroEnumVal(type, it) type it = (type)1; it < type##_COUNT; it = (type)(it+1) + +//////////////////////////////// +//~ rjf: Memory Operation Macros + +#define MemoryCopy(dst, src, size) memmove((dst), (src), (size)) +#define MemorySet(dst, byte, size) memset((dst), (byte), (size)) +#define MemoryCompare(a, b, size) memcmp((a), (b), (size)) +#define MemoryStrlen(ptr) strlen(ptr) + +#define MemoryCopyStruct(d,s) MemoryCopy((d),(s),sizeof(*(d))) +#define MemoryCopyArray(d,s) MemoryCopy((d),(s),sizeof(d)) +#define MemoryCopyTyped(d,s,c) MemoryCopy((d),(s),sizeof(*(d))*(c)) + +#define MemoryZero(s,z) memset((s),0,(z)) +#define MemoryZeroStruct(s) MemoryZero((s),sizeof(*(s))) +#define MemoryZeroArray(a) MemoryZero((a),sizeof(a)) +#define MemoryZeroTyped(m,c) MemoryZero((m),sizeof(*(m))*(c)) + +#define MemoryMatch(a,b,z) (MemoryCompare((a),(b),(z)) == 0) +#define MemoryMatchStruct(a,b) MemoryMatch((a),(b),sizeof(*(a))) +#define MemoryMatchArray(a,b) MemoryMatch((a),(b),sizeof(a)) + +#define MemoryRead(T,p,e) ( ((p)+sizeof(T)<=(e))?(*(T*)(p)):(0) ) +#define MemoryConsume(T,p,e) ( ((p)+sizeof(T)<=(e))?((p)+=sizeof(T),*(T*)((p)-sizeof(T))):((p)=(e),0) ) + +//////////////////////////////// +//~ rjf: Asserts + +#if COMPILER_MSVC +# define Trap() __debugbreak() +#elif COMPILER_CLANG || COMPILER_GCC +# define Trap() __builtin_trap() +#else +# error Unknown trap intrinsic for this compiler. +#endif + +#define AssertAlways(x) do{if(!(x)) {Trap();}}while(0) +#if BUILD_DEBUG +# define Assert(x) AssertAlways(x) +#else +# define Assert(x) (void)(x) +#endif +#define InvalidPath Assert(!"Invalid Path!") +#define NotImplemented Assert(!"Not Implemented!") +#define NoOp ((void)0) +#define StaticAssert(C, ID) global U8 Glue(ID, __LINE__)[(C)?1:-1] + +//////////////////////////////// +//~ rjf: Atomic Operations + +#if OS_WINDOWS +# include +# include +# include +# include +# 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 + +//////////////////////////////// +//~ rjf: Linked List Building Macros + +//- rjf: linked list macro helpers +#define CheckNil(nil,p) ((p) == 0 || (p) == nil) +#define SetNil(nil,p) ((p) = nil) + +//- rjf: doubly-linked-lists +#define DLLInsert_NPZ(nil,f,l,p,n,next,prev) (CheckNil(nil,f) ? \ +((f) = (l) = (n), SetNil(nil,(n)->next), SetNil(nil,(n)->prev)) :\ +CheckNil(nil,p) ? \ +((n)->next = (f), (f)->prev = (n), (f) = (n), SetNil(nil,(n)->prev)) :\ +((p)==(l)) ? \ +((l)->next = (n), (n)->prev = (l), (l) = (n), SetNil(nil, (n)->next)) :\ +(((!CheckNil(nil,p) && CheckNil(nil,(p)->next)) ? (0) : ((p)->next->prev = (n))), ((n)->next = (p)->next), ((p)->next = (n)), ((n)->prev = (p)))) +#define DLLPushBack_NPZ(nil,f,l,n,next,prev) DLLInsert_NPZ(nil,f,l,l,n,next,prev) +#define DLLPushFront_NPZ(nil,f,l,n,next,prev) DLLInsert_NPZ(nil,l,f,f,n,prev,next) +#define DLLRemove_NPZ(nil,f,l,n,next,prev) (((n) == (f) ? (f) = (n)->next : (0)),\ +((n) == (l) ? (l) = (l)->prev : (0)),\ +(CheckNil(nil,(n)->prev) ? (0) :\ +((n)->prev->next = (n)->next)),\ +(CheckNil(nil,(n)->next) ? (0) :\ +((n)->next->prev = (n)->prev))) + +//- rjf: singly-linked, doubly-headed lists (queues) +#define SLLQueuePush_NZ(nil,f,l,n,next) (CheckNil(nil,f)?\ +((f)=(l)=(n),SetNil(nil,(n)->next)):\ +((l)->next=(n),(l)=(n),SetNil(nil,(n)->next))) +#define SLLQueuePushFront_NZ(nil,f,l,n,next) (CheckNil(nil,f)?\ +((f)=(l)=(n),SetNil(nil,(n)->next)):\ +((n)->next=(f),(f)=(n))) +#define SLLQueuePop_NZ(nil,f,l,next) ((f)==(l)?\ +(SetNil(nil,f),SetNil(nil,l)):\ +((f)=(f)->next)) + +//- rjf: singly-linked, singly-headed lists (stacks) +#define SLLStackPush_N(f,n,next) ((n)->next=(f), (f)=(n)) +#define SLLStackPop_N(f,next) ((f)=(f)->next) + +//- rjf: doubly-linked-list helpers +#define DLLInsert_NP(f,l,p,n,next,prev) DLLInsert_NPZ(0,f,l,p,n,next,prev) +#define DLLPushBack_NP(f,l,n,next,prev) DLLPushBack_NPZ(0,f,l,n,next,prev) +#define DLLPushFront_NP(f,l,n,next,prev) DLLPushFront_NPZ(0,f,l,n,next,prev) +#define DLLRemove_NP(f,l,n,next,prev) DLLRemove_NPZ(0,f,l,n,next,prev) +#define DLLInsert(f,l,p,n) DLLInsert_NPZ(0,f,l,p,n,next,prev) +#define DLLPushBack(f,l,n) DLLPushBack_NPZ(0,f,l,n,next,prev) +#define DLLPushFront(f,l,n) DLLPushFront_NPZ(0,f,l,n,next,prev) +#define DLLRemove(f,l,n) DLLRemove_NPZ(0,f,l,n,next,prev) + +//- rjf: singly-linked, doubly-headed list helpers +#define SLLQueuePush_N(f,l,n,next) SLLQueuePush_NZ(0,f,l,n,next) +#define SLLQueuePushFront_N(f,l,n,next) SLLQueuePushFront_NZ(0,f,l,n,next) +#define SLLQueuePop_N(f,l,next) SLLQueuePop_NZ(0,f,l,next) +#define SLLQueuePush(f,l,n) SLLQueuePush_NZ(0,f,l,n,next) +#define SLLQueuePushFront(f,l,n) SLLQueuePushFront_NZ(0,f,l,n,next) +#define SLLQueuePop(f,l) SLLQueuePop_NZ(0,f,l,next) + +//- rjf: singly-linked, singly-headed list helpers +#define SLLStackPush(f,n) SLLStackPush_N(f,n,next) +#define SLLStackPop(f) SLLStackPop_N(f,next) + +//////////////////////////////// +//~ rjf: Address Sanitizer Markup + +#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 +# error "NO_ASAN is not defined for this compiler." +#endif + +#if ASAN_ENABLED +#pragma comment(lib, "clang_rt.asan-x86_64.lib") +C_LINKAGE void __asan_poison_memory_region(void const volatile *addr, size_t size); +C_LINKAGE void __asan_unpoison_memory_region(void const volatile *addr, size_t size); +# define AsanPoisonMemoryRegion(addr, size) __asan_poison_memory_region((addr), (size)) +# define AsanUnpoisonMemoryRegion(addr, size) __asan_unpoison_memory_region((addr), (size)) +#else +# define AsanPoisonMemoryRegion(addr, size) ((void)(addr), (void)(size)) +# define AsanUnpoisonMemoryRegion(addr, size) ((void)(addr), (void)(size)) +#endif + +//////////////////////////////// +//~ rjf: Misc. Helper Macros + +#define Stringify_(S) #S +#define Stringify(S) Stringify_(S) + +#define Glue_(A,B) A##B +#define Glue(A,B) Glue_(A,B) + +#define ArrayCount(a) (sizeof(a) / sizeof((a)[0])) + +#define CeilIntegerDiv(a,b) (((a) + (b) - 1)/(b)) + +#define Swap(T,a,b) do{T t__ = a; a = b; b = t__;}while(0) + +#if ARCH_64BIT +# define IntFromPtr(ptr) ((U64)(ptr)) +#elif ARCH_32BIT +# define IntFromPtr(ptr) ((U32)(ptr)) +#else +# error Missing pointer-to-integer cast for this architecture. +#endif +#define PtrFromInt(i) (void*)((U8*)0 + (i)) + +#define Compose64Bit(a,b) ((((U64)a) << 32) | ((U64)b)); +#define AlignPow2(x,b) (((x) + (b) - 1)&(~((b) - 1))) +#define AlignDownPow2(x,b) ((x)&(~((b) - 1))) +#define AlignPadPow2(x,b) ((0-(x)) & ((b) - 1)) +#define IsPow2(x) ((x)!=0 && ((x)&((x)-1))==0) +#define IsPow2OrZero(x) ((((x) - 1)&(x)) == 0) + +#define ExtractBit(word, idx) (((word) >> (idx)) & 1) + +#if LANG_CPP +# define zero_struct {} +#else +# define zero_struct {0} +#endif + +#if COMPILER_MSVC && COMPILER_MSVC_YEAR < 2015 +# define this_function_name "unknown" +#else +# define this_function_name __func__ +#endif + +//////////////////////////////// +//~ rjf: Base Types + +typedef uint8_t U8; +typedef uint16_t U16; +typedef uint32_t U32; +typedef uint64_t U64; +typedef int8_t S8; +typedef int16_t S16; +typedef int32_t S32; +typedef int64_t S64; +typedef S8 B8; +typedef S16 B16; +typedef S32 B32; +typedef S64 B64; +typedef float F32; +typedef double F64; +typedef void VoidProc(void); +typedef struct U128 U128; +struct U128 +{ + U64 u64[2]; +}; + +//////////////////////////////// +//~ rjf: Basic Types & Spaces + +typedef enum Dimension +{ + Dimension_X, + Dimension_Y, + Dimension_Z, + Dimension_W, +} +Dimension; + +typedef enum Side +{ + Side_Invalid = -1, + Side_Min, + Side_Max, + Side_COUNT, +} +Side; +#define side_flip(s) ((Side)(!(s))) + +typedef enum Axis2 +{ + Axis2_Invalid = -1, + Axis2_X, + Axis2_Y, + Axis2_COUNT, +} +Axis2; +#define axis2_flip(a) ((Axis2)(!(a))) + +typedef enum Corner +{ + Corner_Invalid = -1, + Corner_00, + Corner_01, + Corner_10, + Corner_11, + Corner_COUNT +} +Corner; + +typedef enum Dir2 +{ + Dir2_Invalid = -1, + Dir2_Left, + Dir2_Up, + Dir2_Right, + Dir2_Down, + Dir2_COUNT +} +Dir2; +#define axis2_from_dir2(d) (((d) & 1) ? Axis2_Y : Axis2_X) +#define side_from_dir2(d) (((d) < Dir2_Right) ? Side_Min : Side_Max) + +//////////////////////////////// +//~ rjf: Toolchain/Environment Enums + +typedef enum OperatingSystem +{ + OperatingSystem_Null, + OperatingSystem_Windows, + OperatingSystem_Linux, + OperatingSystem_Mac, + OperatingSystem_COUNT, +} +OperatingSystem; + +typedef enum Architecture +{ + Architecture_Null, + Architecture_x64, + Architecture_x86, + Architecture_arm64, + Architecture_arm32, + Architecture_COUNT, +} +Architecture; + +typedef enum Compiler +{ + Compiler_Null, + Compiler_msvc, + Compiler_gcc, + Compiler_clang, + Compiler_COUNT, +} +Compiler; + +//////////////////////////////// +//~ 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; +}; + +//////////////////////////////// +//~ NOTE(allen): Constants + +global U32 sign32 = 0x80000000; +global U32 exponent32 = 0x7F800000; +global U32 mantissa32 = 0x007FFFFF; + +global F32 big_golden32 = 1.61803398875f; +global F32 small_golden32 = 0.61803398875f; + +global F32 pi32 = 3.1415926535897f; + +global F64 machine_epsilon64 = 4.94065645841247e-324; + +global U64 max_U64 = 0xffffffffffffffffull; +global U32 max_U32 = 0xffffffff; +global U16 max_U16 = 0xffff; +global U8 max_U8 = 0xff; + +global S64 max_S64 = (S64)0x7fffffffffffffffull; +global S32 max_S32 = (S32)0x7fffffff; +global S16 max_S16 = (S16)0x7fff; +global S8 max_S8 = (S8)0x7f; + +global S64 min_S64 = (S64)0xffffffffffffffffull; +global S32 min_S32 = (S32)0xffffffff; +global S16 min_S16 = (S16)0xffff; +global S8 min_S8 = (S8)0xff; + +global const U32 bitmask1 = 0x00000001; +global const U32 bitmask2 = 0x00000003; +global const U32 bitmask3 = 0x00000007; +global const U32 bitmask4 = 0x0000000f; +global const U32 bitmask5 = 0x0000001f; +global const U32 bitmask6 = 0x0000003f; +global const U32 bitmask7 = 0x0000007f; +global const U32 bitmask8 = 0x000000ff; +global const U32 bitmask9 = 0x000001ff; +global const U32 bitmask10 = 0x000003ff; +global const U32 bitmask11 = 0x000007ff; +global const U32 bitmask12 = 0x00000fff; +global const U32 bitmask13 = 0x00001fff; +global const U32 bitmask14 = 0x00003fff; +global const U32 bitmask15 = 0x00007fff; +global const U32 bitmask16 = 0x0000ffff; +global const U32 bitmask17 = 0x0001ffff; +global const U32 bitmask18 = 0x0003ffff; +global const U32 bitmask19 = 0x0007ffff; +global const U32 bitmask20 = 0x000fffff; +global const U32 bitmask21 = 0x001fffff; +global const U32 bitmask22 = 0x003fffff; +global const U32 bitmask23 = 0x007fffff; +global const U32 bitmask24 = 0x00ffffff; +global const U32 bitmask25 = 0x01ffffff; +global const U32 bitmask26 = 0x03ffffff; +global const U32 bitmask27 = 0x07ffffff; +global const U32 bitmask28 = 0x0fffffff; +global const U32 bitmask29 = 0x1fffffff; +global const U32 bitmask30 = 0x3fffffff; +global const U32 bitmask31 = 0x7fffffff; +global const U32 bitmask32 = 0xffffffff; + +global const U64 bitmask33 = 0x00000001ffffffffull; +global const U64 bitmask34 = 0x00000003ffffffffull; +global const U64 bitmask35 = 0x00000007ffffffffull; +global const U64 bitmask36 = 0x0000000fffffffffull; +global const U64 bitmask37 = 0x0000001fffffffffull; +global const U64 bitmask38 = 0x0000003fffffffffull; +global const U64 bitmask39 = 0x0000007fffffffffull; +global const U64 bitmask40 = 0x000000ffffffffffull; +global const U64 bitmask41 = 0x000001ffffffffffull; +global const U64 bitmask42 = 0x000003ffffffffffull; +global const U64 bitmask43 = 0x000007ffffffffffull; +global const U64 bitmask44 = 0x00000fffffffffffull; +global const U64 bitmask45 = 0x00001fffffffffffull; +global const U64 bitmask46 = 0x00003fffffffffffull; +global const U64 bitmask47 = 0x00007fffffffffffull; +global const U64 bitmask48 = 0x0000ffffffffffffull; +global const U64 bitmask49 = 0x0001ffffffffffffull; +global const U64 bitmask50 = 0x0003ffffffffffffull; +global const U64 bitmask51 = 0x0007ffffffffffffull; +global const U64 bitmask52 = 0x000fffffffffffffull; +global const U64 bitmask53 = 0x001fffffffffffffull; +global const U64 bitmask54 = 0x003fffffffffffffull; +global const U64 bitmask55 = 0x007fffffffffffffull; +global const U64 bitmask56 = 0x00ffffffffffffffull; +global const U64 bitmask57 = 0x01ffffffffffffffull; +global const U64 bitmask58 = 0x03ffffffffffffffull; +global const U64 bitmask59 = 0x07ffffffffffffffull; +global const U64 bitmask60 = 0x0fffffffffffffffull; +global const U64 bitmask61 = 0x1fffffffffffffffull; +global const U64 bitmask62 = 0x3fffffffffffffffull; +global const U64 bitmask63 = 0x7fffffffffffffffull; +global const U64 bitmask64 = 0xffffffffffffffffull; + +global const U32 bit1 = (1<<0); +global const U32 bit2 = (1<<1); +global const U32 bit3 = (1<<2); +global const U32 bit4 = (1<<3); +global const U32 bit5 = (1<<4); +global const U32 bit6 = (1<<5); +global const U32 bit7 = (1<<6); +global const U32 bit8 = (1<<7); +global const U32 bit9 = (1<<8); +global const U32 bit10 = (1<<9); +global const U32 bit11 = (1<<10); +global const U32 bit12 = (1<<11); +global const U32 bit13 = (1<<12); +global const U32 bit14 = (1<<13); +global const U32 bit15 = (1<<14); +global const U32 bit16 = (1<<15); +global const U32 bit17 = (1<<16); +global const U32 bit18 = (1<<17); +global const U32 bit19 = (1<<18); +global const U32 bit20 = (1<<19); +global const U32 bit21 = (1<<20); +global const U32 bit22 = (1<<21); +global const U32 bit23 = (1<<22); +global const U32 bit24 = (1<<23); +global const U32 bit25 = (1<<24); +global const U32 bit26 = (1<<25); +global const U32 bit27 = (1<<26); +global const U32 bit28 = (1<<27); +global const U32 bit29 = (1<<28); +global const U32 bit30 = (1<<29); +global const U32 bit31 = (1<<30); +global const U32 bit32 = (1<<31); + +global const U64 bit33 = (1ull<<32); +global const U64 bit34 = (1ull<<33); +global const U64 bit35 = (1ull<<34); +global const U64 bit36 = (1ull<<35); +global const U64 bit37 = (1ull<<36); +global const U64 bit38 = (1ull<<37); +global const U64 bit39 = (1ull<<38); +global const U64 bit40 = (1ull<<39); +global const U64 bit41 = (1ull<<40); +global const U64 bit42 = (1ull<<41); +global const U64 bit43 = (1ull<<42); +global const U64 bit44 = (1ull<<43); +global const U64 bit45 = (1ull<<44); +global const U64 bit46 = (1ull<<45); +global const U64 bit47 = (1ull<<46); +global const U64 bit48 = (1ull<<47); +global const U64 bit49 = (1ull<<48); +global const U64 bit50 = (1ull<<49); +global const U64 bit51 = (1ull<<50); +global const U64 bit52 = (1ull<<51); +global const U64 bit53 = (1ull<<52); +global const U64 bit54 = (1ull<<53); +global const U64 bit55 = (1ull<<54); +global const U64 bit56 = (1ull<<55); +global const U64 bit57 = (1ull<<56); +global const U64 bit58 = (1ull<<57); +global const U64 bit59 = (1ull<<58); +global const U64 bit60 = (1ull<<59); +global const U64 bit61 = (1ull<<60); +global const U64 bit62 = (1ull<<61); +global const U64 bit63 = (1ull<<62); +global const U64 bit64 = (1ull<<63); + +//////////////////////////////// +//~ allen: Time + +typedef enum WeekDay +{ + WeekDay_Sun, + WeekDay_Mon, + WeekDay_Tue, + WeekDay_Wed, + WeekDay_Thu, + WeekDay_Fri, + WeekDay_Sat, + WeekDay_COUNT, +} +WeekDay; + +typedef 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, +} +Month; + +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; + +//////////////////////////////// +//~ allen: Files + +typedef U32 FilePropertyFlags; +enum +{ + FilePropertyFlag_IsFolder = (1 << 0), +}; + +typedef struct FileProperties FileProperties; +struct FileProperties +{ + U64 size; + DenseTime modified; + DenseTime created; + FilePropertyFlags flags; +}; + +//////////////////////////////// +//~ rjf: Safe Casts + +internal U16 safe_cast_u16(U32 x); +internal U32 safe_cast_u32(U64 x); +internal S32 safe_cast_s32(S64 x); + +//////////////////////////////// +//~ rjf: Large Base Type Functions + +internal U128 u128_zero(void); +internal U128 u128_make(U64 v0, U64 v1); +internal B32 u128_match(U128 a, U128 b); + +//////////////////////////////// +//~ rjf: Bit Patterns + +internal U32 u32_from_u64_saturate(U64 x); +internal U64 u64_up_to_pow2(U64 x); +internal S32 extend_sign32(U32 x, U32 size); +internal S64 extend_sign64(U64 x, U64 size); + +internal F32 inf32(void); +internal F32 neg_inf32(void); + +internal U16 bswap_u16(U16 x); +internal U32 bswap_u32(U32 x); +internal U64 bswap_u64(U64 x); + +internal U64 count_bits_set16(U16 val); +internal U64 count_bits_set32(U32 val); +internal U64 count_bits_set64(U64 val); + +internal U64 ctz32(U32 val); +internal U64 ctz64(U64 val); +internal U64 clz32(U32 val); +internal U64 clz64(U64 val); + +//////////////////////////////// +//~ rjf: Enum -> Sign + +internal S32 sign_from_side_S32(Side side); +internal F32 sign_from_side_F32(Side side); + +//////////////////////////////// +//~ rjf: Memory Functions + +internal B32 memory_is_zero(void *ptr, U64 size); + +//////////////////////////////// +//~ rjf: Text 2D Coordinate/Range Functions + +internal TxtPt txt_pt(S64 line, S64 column); +internal B32 txt_pt_match(TxtPt a, TxtPt b); +internal B32 txt_pt_less_than(TxtPt a, TxtPt b); +internal TxtPt txt_pt_min(TxtPt a, TxtPt b); +internal TxtPt txt_pt_max(TxtPt a, TxtPt b); +internal TxtRng txt_rng(TxtPt min, TxtPt max); +internal TxtRng txt_rng_intersect(TxtRng a, TxtRng b); +internal TxtRng txt_rng_union(TxtRng a, TxtRng b); +internal B32 txt_rng_contains(TxtRng r, TxtPt pt); + +//////////////////////////////// +//~ rjf: Toolchain/Environment Enum Functions + +internal U64 bit_size_from_arch(Architecture arch); +internal U64 max_instruction_size_from_arch(Architecture arch); + +internal OperatingSystem operating_system_from_context(void); +internal Architecture architecture_from_context(void); +internal Compiler compiler_from_context(void); + +//////////////////////////////// +//~ rjf: Time Functions + +internal DenseTime dense_time_from_date_time(DateTime date_time); +internal DateTime date_time_from_dense_time(DenseTime time); +internal DateTime date_time_from_micro_seconds(U64 time); + +//////////////////////////////// +//~ rjf: Non-Fancy Ring Buffer Reads/Writes + +internal U64 ring_write(U8 *ring_base, U64 ring_size, U64 ring_pos, void *src_data, U64 src_data_size); +internal 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: Sorts + +#define quick_sort(ptr, count, element_size, cmp_function) qsort((ptr), (count), (element_size), (int (*)(const void *, const void *))(cmp_function)) + +#endif // BASE_CORE_H diff --git a/src/metagen/metagen_base/metagen_base_entry_point.c b/src/metagen/metagen_base/metagen_base_entry_point.c new file mode 100644 index 00000000..498ec0b5 --- /dev/null +++ b/src/metagen/metagen_base/metagen_base_entry_point.c @@ -0,0 +1,92 @@ +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +internal void +main_thread_base_entry_point(void (*entry_point)(CmdLine *cmdline), 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 + ThreadNameF("[main thread]"); + Temp 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) + { + ProfBeginCapture(arguments[0]); + } +#if defined(TASK_SYSTEM_H) && !defined(TS_INIT_MANUAL) + ts_init(); +#endif +#if defined(HASH_STORE_H) && !defined(HS_INIT_MANUAL) + hs_init(); +#endif +#if defined(FILE_STREAM_H) && !defined(FS_INIT_MANUAL) + fs_init(); +#endif +#if defined(TEXT_CACHE_H) && !defined(TXT_INIT_MANUAL) + txt_init(); +#endif +#if defined(MUTABLE_TEXT_H) && !defined(MTX_INIT_MANUAL) + mtx_init(); +#endif +#if defined(DASM_CACHE_H) && !defined(DASM_INIT_MANUAL) + dasm_init(); +#endif +#if defined(DI_H) && !defined(DI_INIT_MANUAL) + di_init(); +#endif +#if defined(FUZZY_SEARCH_H) && !defined(FZY_INIT_MANUAL) + fzy_init(); +#endif +#if defined(DEMON_CORE_H) && !defined(DMN_INIT_MANUAL) + dmn_init(); +#endif +#if defined(CTRL_CORE_H) && !defined(CTRL_INIT_MANUAL) + ctrl_init(); +#endif +#if defined(OS_GRAPHICAL_H) && !defined(OS_GFX_INIT_MANUAL) + os_gfx_init(); +#endif +#if defined(FONT_PROVIDER_H) && !defined(FP_INIT_MANUAL) + fp_init(); +#endif +#if defined(RENDER_CORE_H) && !defined(R_INIT_MANUAL) + r_init(&cmdline); +#endif +#if defined(TEXTURE_CACHE_H) && !defined(TEX_INIT_MANUAL) + tex_init(); +#endif +#if defined(GEO_CACHE_H) && !defined(GEO_INIT_MANUAL) + geo_init(); +#endif +#if defined(FONT_CACHE_H) && !defined(F_INIT_MANUAL) + f_init(); +#endif +#if defined(DF_CORE_H) && !defined(DF_INIT_MANUAL) + DF_StateDeltaHistory *hist = df_state_delta_history_alloc(); + df_core_init(&cmdline, hist); +#endif +#if defined(DF_GFX_H) && !defined(DF_GFX_INIT_MANUAL) + df_gfx_init(update_and_render, df_state_delta_history()); +#endif + entry_point(&cmdline); + if(capture) + { + ProfEndCapture(); + } + scratch_end(scratch); +} + +internal void +supplement_thread_base_entry_point(void (*entry_point)(void *params), void *params) +{ + TCTX tctx; + tctx_init_and_equip(&tctx); + entry_point(params); + tctx_release(); +} diff --git a/src/metagen/metagen_base/metagen_base_entry_point.h b/src/metagen/metagen_base/metagen_base_entry_point.h new file mode 100644 index 00000000..560bdcc7 --- /dev/null +++ b/src/metagen/metagen_base/metagen_base_entry_point.h @@ -0,0 +1,10 @@ +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef BASE_ENTRY_POINT_H +#define BASE_ENTRY_POINT_H + +internal void main_thread_base_entry_point(void (*entry_point)(CmdLine *cmdline), char **arguments, U64 arguments_count); +internal void supplement_thread_base_entry_point(void (*entry_point)(void *params), void *params); + +#endif // BASE_ENTRY_POINT_H diff --git a/src/metagen/metagen_base/metagen_base_inc.c b/src/metagen/metagen_base/metagen_base_inc.c index ba36ca9d..74dcfe02 100644 --- a/src/metagen/metagen_base/metagen_base_inc.c +++ b/src/metagen/metagen_base/metagen_base_inc.c @@ -1,18 +1,19 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -//////////////////////////////// -//~ rjf: Base Includes - -#undef RADDBG_LAYER_COLOR -#define RADDBG_LAYER_COLOR 0.20f, 0.60f, 0.80f - -#include "metagen_base_types.c" -#include "metagen_base_markup.c" -#include "metagen_base_arena.c" -#include "metagen_base_math.c" -#include "metagen_base_string.c" -#include "metagen_base_thread_context.c" -#include "metagen_base_command_line.c" -#include "metagen_base_arena_dev.c" -#include "metagen_base_bits.c" +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +//////////////////////////////// +//~ rjf: Base Includes + +#undef RADDBG_LAYER_COLOR +#define RADDBG_LAYER_COLOR 0.20f, 0.60f, 0.80f + +#include "metagen_base_core.c" +#include "metagen_base_profile.c" +#include "metagen_base_arena.c" +#include "metagen_base_math.c" +#include "metagen_base_strings.c" +#include "metagen_base_thread_context.c" +#include "metagen_base_command_line.c" +#include "metagen_base_markup.c" +#include "metagen_base_log.c" +#include "metagen_base_entry_point.c" diff --git a/src/metagen/metagen_base/metagen_base_inc.h b/src/metagen/metagen_base/metagen_base_inc.h index 27a51e5c..88aa65f7 100644 --- a/src/metagen/metagen_base/metagen_base_inc.h +++ b/src/metagen/metagen_base/metagen_base_inc.h @@ -1,23 +1,23 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#ifndef BASE_INC_H -#define BASE_INC_H - -//////////////////////////////// -//~ rjf: Base Includes - -#include "metagen_base_context_cracking.h" -#include "metagen_base_types.h" -#include "metagen_base_markup.h" -#include "metagen_base_ins.h" -#include "metagen_base_linked_lists.h" -#include "metagen_base_arena.h" -#include "metagen_base_math.h" -#include "metagen_base_string.h" -#include "metagen_base_thread_context.h" -#include "metagen_base_command_line.h" -#include "metagen_base_arena_dev.h" -#include "metagen_base_bits.h" - -#endif // BASE_INC_H +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef BASE_INC_H +#define BASE_INC_H + +//////////////////////////////// +//~ rjf: Base Includes + +#include "metagen_base_context_cracking.h" + +#include "metagen_base_core.h" +#include "metagen_base_profile.h" +#include "metagen_base_arena.h" +#include "metagen_base_math.h" +#include "metagen_base_strings.h" +#include "metagen_base_thread_context.h" +#include "metagen_base_command_line.h" +#include "metagen_base_markup.h" +#include "metagen_base_log.h" +#include "metagen_base_entry_point.h" + +#endif // BASE_INC_H diff --git a/src/metagen/metagen_base/metagen_base_ins.h b/src/metagen/metagen_base/metagen_base_ins.h deleted file mode 100644 index 9f8d3202..00000000 --- a/src/metagen/metagen_base/metagen_base_ins.h +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#ifndef BASE_INS_H -#define BASE_INS_H - -//////////////////////////////// -// NOTE(allen): Implementations of Intrinsics - -#if OS_WINDOWS - -# include -# include -# include -# include - -# if ARCH_X64 -# define ins_atomic_u64_eval(x) InterlockedAdd((volatile LONG *)(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) InterlockedAdd((volatile LONG *)(x), c) -# 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)) -# endif - -#elif OS_LINUX - -# if ARCH_X64 -# define ins_atomic_u64_inc_eval(x) __sync_fetch_and_add((volatile U64 *)(x), 1) -# endif - -#else -// TODO(allen): -#endif - -//////////////////////////////// -// NOTE(allen): Intrinsic Checks - -#if ARCH_X64 - -# if !defined(ins_atomic_u64_inc_eval) -# error missing: ins_atomic_u64_inc_eval -# endif - -#else -# error the intrinsic set for this arch is not developed -#endif - - -#endif //BASE_INS_H diff --git a/src/metagen/metagen_base/metagen_base_linked_lists.h b/src/metagen/metagen_base/metagen_base_linked_lists.h deleted file mode 100644 index 436cedc5..00000000 --- a/src/metagen/metagen_base/metagen_base_linked_lists.h +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#ifndef BASE_LINKED_LIST_H -#define BASE_LINKED_LIST_H - -//////////////////////////////// -//~ rjf: Helpers - -#define CheckNil(nil,p) ((p) == 0 || (p) == nil) -#define SetNil(nil,p) ((p) = nil) - -//////////////////////////////// -//~ rjf: Base Macros - -//- rjf: Base Doubly-Linked-List Macros -#define DLLInsert_NPZ(nil,f,l,p,n,next,prev) (CheckNil(nil,f) ? \ -((f) = (l) = (n), SetNil(nil,(n)->next), SetNil(nil,(n)->prev)) :\ -CheckNil(nil,p) ? \ -((n)->next = (f), (f)->prev = (n), (f) = (n), SetNil(nil,(n)->prev)) :\ -((p)==(l)) ? \ -((l)->next = (n), (n)->prev = (l), (l) = (n), SetNil(nil, (n)->next)) :\ -(((!CheckNil(nil,p) && CheckNil(nil,(p)->next)) ? (0) : ((p)->next->prev = (n))), ((n)->next = (p)->next), ((p)->next = (n)), ((n)->prev = (p)))) -#define DLLPushBack_NPZ(nil,f,l,n,next,prev) DLLInsert_NPZ(nil,f,l,l,n,next,prev) -#define DLLPushFront_NPZ(nil,f,l,n,next,prev) DLLInsert_NPZ(nil,l,f,f,n,prev,next) -#define DLLRemove_NPZ(nil,f,l,n,next,prev) (((n) == (f) ? (f) = (n)->next : (0)),\ -((n) == (l) ? (l) = (l)->prev : (0)),\ -(CheckNil(nil,(n)->prev) ? (0) :\ -((n)->prev->next = (n)->next)),\ -(CheckNil(nil,(n)->next) ? (0) :\ -((n)->next->prev = (n)->prev))) - -//- rjf: Base Singly-Linked-List Queue Macros -#define SLLQueuePush_NZ(nil,f,l,n,next) (CheckNil(nil,f)?\ -((f)=(l)=(n),SetNil(nil,(n)->next)):\ -((l)->next=(n),(l)=(n),SetNil(nil,(n)->next))) -#define SLLQueuePushFront_NZ(nil,f,l,n,next) (CheckNil(nil,f)?\ -((f)=(l)=(n),SetNil(nil,(n)->next)):\ -((n)->next=(f),(f)=(n))) -#define SLLQueuePop_NZ(nil,f,l,next) ((f)==(l)?\ -(SetNil(nil,f),SetNil(nil,l)):\ -((f)=(f)->next)) - -//- rjf: Base Singly-Linked-List Stack Macros -#define SLLStackPush_N(f,n,next) ((n)->next=(f), (f)=(n)) -#define SLLStackPop_N(f,next) ((f)=(f)->next) - -//////////////////////////////// -//~ rjf: Convenience Wrappers - -//- rjf: Doubly-Linked-List Wrappers -#define DLLInsert_NP(f,l,p,n,next,prev) DLLInsert_NPZ(0,f,l,p,n,next,prev) -#define DLLPushBack_NP(f,l,n,next,prev) DLLPushBack_NPZ(0,f,l,n,next,prev) -#define DLLPushFront_NP(f,l,n,next,prev) DLLPushFront_NPZ(0,f,l,n,next,prev) -#define DLLRemove_NP(f,l,n,next,prev) DLLRemove_NPZ(0,f,l,n,next,prev) -#define DLLInsert(f,l,p,n) DLLInsert_NPZ(0,f,l,p,n,next,prev) -#define DLLPushBack(f,l,n) DLLPushBack_NPZ(0,f,l,n,next,prev) -#define DLLPushFront(f,l,n) DLLPushFront_NPZ(0,f,l,n,next,prev) -#define DLLRemove(f,l,n) DLLRemove_NPZ(0,f,l,n,next,prev) - -//- rjf: Singly-Linked-List Queue Wrappers -#define SLLQueuePush_N(f,l,n,next) SLLQueuePush_NZ(0,f,l,n,next) -#define SLLQueuePushFront_N(f,l,n,next) SLLQueuePushFront_NZ(0,f,l,n,next) -#define SLLQueuePop_N(f,l,next) SLLQueuePop_NZ(0,f,l,next) -#define SLLQueuePush(f,l,n) SLLQueuePush_NZ(0,f,l,n,next) -#define SLLQueuePushFront(f,l,n) SLLQueuePushFront_NZ(0,f,l,n,next) -#define SLLQueuePop(f,l) SLLQueuePop_NZ(0,f,l,next) - -//- rjf: Singly-Linked-List Stack Wrappers -#define SLLStackPush(f,n) SLLStackPush_N(f,n,next) -#define SLLStackPop(f) SLLStackPop_N(f,next) - -#endif //BASE_LINKED_LIST_H diff --git a/src/metagen/metagen_base/metagen_base_log.c b/src/metagen/metagen_base/metagen_base_log.c new file mode 100644 index 00000000..418b29ff --- /dev/null +++ b/src/metagen/metagen_base/metagen_base_log.c @@ -0,0 +1,103 @@ +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +//////////////////////////////// +//~ rjf: Globals/Thread-Locals + +C_LINKAGE thread_static Log *log_active; +#if !BUILD_SUPPLEMENTARY_UNIT +C_LINKAGE thread_static Log *log_active = 0; +#endif + +//////////////////////////////// +//~ rjf: Log Creation/Selection + +internal Log * +log_alloc(void) +{ + Arena *arena = arena_alloc(); + Log *log = push_array(arena, Log, 1); + log->arena = arena; + return log; +} + +internal void +log_release(Log *log) +{ + arena_release(log->arena); +} + +internal void +log_select(Log *log) +{ + log_active = log; +} + +//////////////////////////////// +//~ rjf: Log Building/Clearing + +internal void +log_msg(LogMsgKind kind, String8 string) +{ + if(log_active != 0 && log_active->top_scope != 0) + { + String8 string_copy = push_str8_copy(log_active->arena, string); + str8_list_push(log_active->arena, &log_active->top_scope->strings[kind], string_copy); + } +} + +internal void +log_msgf(LogMsgKind kind, char *fmt, ...) +{ + if(log_active != 0) + { + Temp scratch = scratch_begin(0, 0); + va_list args; + va_start(args, fmt); + String8 string = push_str8fv(scratch.arena, fmt, args); + log_msg(kind, string); + va_end(args); + scratch_end(scratch); + } +} + +//////////////////////////////// +//~ rjf: Log Scopes + +internal 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; + SLLStackPush(log_active->top_scope, scope); + } +} + +internal LogScopeResult +log_scope_end(Arena *arena) +{ + LogScopeResult result = {0}; + if(log_active != 0) + { + LogScope *scope = log_active->top_scope; + if(scope != 0) + { + SLLStackPop(log_active->top_scope); + if(arena != 0) + { + for(EachEnumVal(LogMsgKind, kind)) + { + Temp 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; +} diff --git a/src/metagen/metagen_base/metagen_base_log.h b/src/metagen/metagen_base/metagen_base_log.h new file mode 100644 index 00000000..3687390e --- /dev/null +++ b/src/metagen/metagen_base/metagen_base_log.h @@ -0,0 +1,65 @@ +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef BASE_LOG_H +#define BASE_LOG_H + +//////////////////////////////// +//~ rjf: Log Types + +typedef enum LogMsgKind +{ + LogMsgKind_Info, + LogMsgKind_UserError, + LogMsgKind_COUNT +} +LogMsgKind; + +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; +}; + +//////////////////////////////// +//~ rjf: Log Creation/Selection + +internal Log *log_alloc(void); +internal void log_release(Log *log); +internal void log_select(Log *log); + +//////////////////////////////// +//~ rjf: Log Building + +internal void log_msg(LogMsgKind kind, String8 string); +internal 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 LogInfoNamedBlock(s) DeferLoop(log_infof("%S:\n{\n", (s)), log_infof("}\n")) +#define LogInfoNamedBlockF(fmt, ...) DeferLoop((log_infof(fmt, __VA_ARGS__), log_infof(":\n{\n")), log_infof("}\n")) + +//////////////////////////////// +//~ rjf: Log Scopes + +internal void log_scope_begin(void); +internal LogScopeResult log_scope_end(Arena *arena); + +#endif // BASE_LOG_H diff --git a/src/metagen/metagen_base/metagen_base_markup.c b/src/metagen/metagen_base/metagen_base_markup.c index 7ea8904c..5ad47ef5 100644 --- a/src/metagen/metagen_base/metagen_base_markup.c +++ b/src/metagen/metagen_base/metagen_base_markup.c @@ -1,2 +1,21 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +internal void +set_thread_name(String8 string) +{ + ProfThreadName("%.*s", str8_varg(string)); + os_set_thread_name(string); +} + +internal void +set_thread_namef(char *fmt, ...) +{ + Temp scratch = scratch_begin(0, 0); + va_list args; + va_start(args, fmt); + String8 string = push_str8fv(scratch.arena, fmt, args); + set_thread_name(string); + va_end(args); + scratch_end(scratch); +} diff --git a/src/metagen/metagen_base/metagen_base_markup.h b/src/metagen/metagen_base/metagen_base_markup.h index 03c1adab..fd291375 100644 --- a/src/metagen/metagen_base/metagen_base_markup.h +++ b/src/metagen/metagen_base/metagen_base_markup.h @@ -1,79 +1,12 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#ifndef BASE_MARKUP_H -#define BASE_MARKUP_H - -//////////////////////////////// -//~ 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 ProfBegin(...) tmEnter(0, 0, __VA_ARGS__) -# define ProfBeginDynamic(...) (TM_API_PTR ? TM_API_PTR->_tmEnterZoneV_Core(0, 0, __FILE__, &g_telemetry_filename_id, __LINE__, __VA_ARGS__) : (void)0) -# define ProfEnd(...) (TM_API_PTR ? TM_API_PTR->_tmLeaveZone(0) : (void)0) -# define ProfTick(...) tmTick(0) -# define ProfIsCapturing(...) tmRunning() -# define ProfBeginCapture(...) tmOpen(0, __VA_ARGS__, __DATE__, "localhost", TMCT_TCP, TELEMETRY_DEFAULT_PORT, TMOF_INIT_NETWORKING|TMOF_CAPTURE_CONTEXT_SWITCHES, 100) -# define ProfEndCapture(...) tmClose(0) -# define ProfThreadName(...) (TM_API_PTR ? TM_API_PTR->_tmThreadName(0, 0, __VA_ARGS__) : (void)0) -# define ProfMsg(...) (TM_API_PTR ? TM_API_PTR->_tmMessageV_Core(0, TMMF_ICON_NOTE, __FILE__, &g_telemetry_filename_id, __LINE__, __VA_ARGS__) : (void)0) -# define ProfBeginLockWait(...) tmStartWaitForLock(0, 0, __VA_ARGS__) -# define ProfEndLockWait(...) tmEndWaitForLock(0) -# define ProfLockTake(...) tmAcquiredLock(0, 0, __VA_ARGS__) -# define ProfLockDrop(...) tmReleasedLock(0, __VA_ARGS__) -# define ProfColor(color) tmZoneColorSticky(color) -#endif - -//////////////////////////////// -//~ rjf: Zeroify Undefined Defines - -#if !defined(ProfBegin) -# define ProfBegin(...) (0) -# define ProfBeginDynamic(...) (0) -# define ProfEnd(...) (0) -# define ProfTick(...) (0) -# define ProfIsCapturing(...) (0) -# define ProfBeginCapture(...) (0) -# define ProfEndCapture(...) (0) -# define ProfThreadName(...) (0) -# define ProfMsg(...) (0) -# define ProfBeginLockWait(...) (0) -# define ProfEndLockWait(...) (0) -# define ProfLockTake(...) (0) -# define ProfLockDrop(...) (0) -# define ProfColor(...) (0) -#endif - -//////////////////////////////// -//~ rjf: Helper Wrappers - -#define ProfBeginFunction(...) ProfBegin(this_function_name) -#define ProfScope(...) DeferLoop(ProfBeginDynamic(__VA_ARGS__), ProfEnd()) - -//////////////////////////////// -//~ rjf: General Markup - -#define ThreadName(...) (ProfThreadName(__VA_ARGS__)) - -#endif // BASE_MARKUP_H +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef BASE_MARKUP_H +#define BASE_MARKUP_H + +internal void set_thread_name(String8 string); +internal void set_thread_namef(char *fmt, ...); +#define ThreadNameF(...) (set_thread_namef(__VA_ARGS__)) +#define ThreadName(str) (set_thread_name(str)) + +#endif // BASE_MARKUP_H diff --git a/src/metagen/metagen_base/metagen_base_math.c b/src/metagen/metagen_base/metagen_base_math.c index 0ddf662f..465d342d 100644 --- a/src/metagen/metagen_base/metagen_base_math.c +++ b/src/metagen/metagen_base/metagen_base_math.c @@ -1,607 +1,616 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -//////////////////////////////// -//~ rjf: Scalar Ops - -internal F32 -mix_1f32(F32 a, F32 b, F32 t) -{ - F32 c = (a + (b-a) * Clamp(0.f, t, 1.f)); - return c; -} - -internal F64 -mix_1f64(F64 a, F64 b, F64 t) -{ - F64 c = (a + (b-a) * Clamp(0.0, t, 1.0)); - return c; -} - -//////////////////////////////// -//~ rjf: Vector Ops - -internal Vec2F32 vec_2f32(F32 x, F32 y) {Vec2F32 v = {x, y}; return v;} -internal Vec2F32 add_2f32(Vec2F32 a, Vec2F32 b) {Vec2F32 c = {a.x+b.x, a.y+b.y}; return c;} -internal Vec2F32 sub_2f32(Vec2F32 a, Vec2F32 b) {Vec2F32 c = {a.x-b.x, a.y-b.y}; return c;} -internal Vec2F32 mul_2f32(Vec2F32 a, Vec2F32 b) {Vec2F32 c = {a.x*b.x, a.y*b.y}; return c;} -internal Vec2F32 div_2f32(Vec2F32 a, Vec2F32 b) {Vec2F32 c = {a.x/b.x, a.y/b.y}; return c;} -internal Vec2F32 scale_2f32(Vec2F32 v, F32 s) {Vec2F32 c = {v.x*s, v.y*s}; return c;} -internal F32 dot_2f32(Vec2F32 a, Vec2F32 b) {F32 c = a.x*b.x + a.y*b.y; return c;} -internal F32 length_squared_2f32(Vec2F32 v) {F32 c = v.x*v.x + v.y*v.y; return c;} -internal F32 length_2f32(Vec2F32 v) {F32 c = sqrt_f32(v.x*v.x + v.y*v.y); return c;} -internal Vec2F32 normalize_2f32(Vec2F32 v) {v = scale_2f32(v, 1.f/length_2f32(v)); return v;} -internal Vec2F32 mix_2f32(Vec2F32 a, Vec2F32 b, F32 t) {Vec2F32 c = {mix_1f32(a.x, b.x, t), mix_1f32(a.y, b.y, t)}; return c;} - -internal Vec2S64 vec_2s64(S64 x, S64 y) {Vec2S64 v = {x, y}; return v;} -internal Vec2S64 add_2s64(Vec2S64 a, Vec2S64 b) {Vec2S64 c = {a.x+b.x, a.y+b.y}; return c;} -internal Vec2S64 sub_2s64(Vec2S64 a, Vec2S64 b) {Vec2S64 c = {a.x-b.x, a.y-b.y}; return c;} -internal Vec2S64 mul_2s64(Vec2S64 a, Vec2S64 b) {Vec2S64 c = {a.x*b.x, a.y*b.y}; return c;} -internal Vec2S64 div_2s64(Vec2S64 a, Vec2S64 b) {Vec2S64 c = {a.x/b.x, a.y/b.y}; return c;} -internal Vec2S64 scale_2s64(Vec2S64 v, S64 s) {Vec2S64 c = {v.x*s, v.y*s}; return c;} -internal S64 dot_2s64(Vec2S64 a, Vec2S64 b) {S64 c = a.x*b.x + a.y*b.y; return c;} -internal S64 length_squared_2s64(Vec2S64 v) {S64 c = v.x*v.x + v.y*v.y; return c;} -internal S64 length_2s64(Vec2S64 v) {S64 c = (S64)sqrt_f64((F64)(v.x*v.x + v.y*v.y)); return c;} -internal Vec2S64 normalize_2s64(Vec2S64 v) {v = scale_2s64(v, (S64)(1.f/length_2s64(v))); return v;} -internal Vec2S64 mix_2s64(Vec2S64 a, Vec2S64 b, F32 t) {Vec2S64 c = {(S64)mix_1f32((F32)a.x, (F32)b.x, t), (S64)mix_1f32((F32)a.y, (F32)b.y, t)}; return c;} - -internal Vec2S32 vec_2s32(S32 x, S32 y) {Vec2S32 v = {x, y}; return v;} -internal Vec2S32 add_2s32(Vec2S32 a, Vec2S32 b) {Vec2S32 c = {a.x+b.x, a.y+b.y}; return c;} -internal Vec2S32 sub_2s32(Vec2S32 a, Vec2S32 b) {Vec2S32 c = {a.x-b.x, a.y-b.y}; return c;} -internal Vec2S32 mul_2s32(Vec2S32 a, Vec2S32 b) {Vec2S32 c = {a.x*b.x, a.y*b.y}; return c;} -internal Vec2S32 div_2s32(Vec2S32 a, Vec2S32 b) {Vec2S32 c = {a.x/b.x, a.y/b.y}; return c;} -internal Vec2S32 scale_2s32(Vec2S32 v, S32 s) {Vec2S32 c = {v.x*s, v.y*s}; return c;} -internal S32 dot_2s32(Vec2S32 a, Vec2S32 b) {S32 c = a.x*b.x + a.y*b.y; return c;} -internal S32 length_squared_2s32(Vec2S32 v) {S32 c = v.x*v.x + v.y*v.y; return c;} -internal S32 length_2s32(Vec2S32 v) {S32 c = (S32)sqrt_f32((F32)v.x*(F32)v.x + (F32)v.y*(F32)v.y); return c;} -internal Vec2S32 normalize_2s32(Vec2S32 v) {v = scale_2s32(v, (S32)(1.f/length_2s32(v))); return v;} -internal Vec2S32 mix_2s32(Vec2S32 a, Vec2S32 b, F32 t) {Vec2S32 c = {(S32)mix_1f32((F32)a.x, (F32)b.x, t), (S32)mix_1f32((F32)a.y, (F32)b.y, t)}; return c;} - -internal Vec2S16 vec_2s16(S16 x, S16 y) {Vec2S16 v = {x, y}; return v;} -internal Vec2S16 add_2s16(Vec2S16 a, Vec2S16 b) {Vec2S16 c = {(S16)(a.x+b.x), (S16)(a.y+b.y)}; return c;} -internal Vec2S16 sub_2s16(Vec2S16 a, Vec2S16 b) {Vec2S16 c = {(S16)(a.x-b.x), (S16)(a.y-b.y)}; return c;} -internal Vec2S16 mul_2s16(Vec2S16 a, Vec2S16 b) {Vec2S16 c = {(S16)(a.x*b.x), (S16)(a.y*b.y)}; return c;} -internal Vec2S16 div_2s16(Vec2S16 a, Vec2S16 b) {Vec2S16 c = {(S16)(a.x/b.x), (S16)(a.y/b.y)}; return c;} -internal Vec2S16 scale_2s16(Vec2S16 v, S16 s) {Vec2S16 c = {(S16)(v.x*s), (S16)(v.y*s)}; return c;} -internal S16 dot_2s16(Vec2S16 a, Vec2S16 b) {S16 c = a.x*b.x + a.y*b.y; return c;} -internal S16 length_squared_2s16(Vec2S16 v) {S16 c = v.x*v.x + v.y*v.y; return c;} -internal S16 length_2s16(Vec2S16 v) {S16 c = (S16)sqrt_f32((F32)(v.x*v.x + v.y*v.y)); return c;} -internal Vec2S16 normalize_2s16(Vec2S16 v) {v = scale_2s16(v, (S16)(1.f/length_2s16(v))); return v;} -internal Vec2S16 mix_2s16(Vec2S16 a, Vec2S16 b, F32 t) {Vec2S16 c = {(S16)mix_1f32((F32)a.x, (F32)b.x, t), (S16)mix_1f32((F32)a.y, (F32)b.y, t)}; return c;} - -internal Vec3F32 vec_3f32(F32 x, F32 y, F32 z) {Vec3F32 v = {x, y, z}; return v;} -internal Vec3F32 add_3f32(Vec3F32 a, Vec3F32 b) {Vec3F32 c = {a.x+b.x, a.y+b.y, a.z+b.z}; return c;} -internal Vec3F32 sub_3f32(Vec3F32 a, Vec3F32 b) {Vec3F32 c = {a.x-b.x, a.y-b.y, a.z-b.z}; return c;} -internal Vec3F32 mul_3f32(Vec3F32 a, Vec3F32 b) {Vec3F32 c = {a.x*b.x, a.y*b.y, a.z*b.z}; return c;} -internal Vec3F32 div_3f32(Vec3F32 a, Vec3F32 b) {Vec3F32 c = {a.x/b.x, a.y/b.y, a.z/b.z}; return c;} -internal Vec3F32 scale_3f32(Vec3F32 v, F32 s) {Vec3F32 c = {v.x*s, v.y*s, v.z*s}; return c;} -internal F32 dot_3f32(Vec3F32 a, Vec3F32 b) {F32 c = a.x*b.x + a.y*b.y + a.z*b.z; return c;} -internal F32 length_squared_3f32(Vec3F32 v) {F32 c = v.x*v.x + v.y*v.y + v.z*v.z; return c;} -internal F32 length_3f32(Vec3F32 v) {F32 c = sqrt_f32(v.x*v.x + v.y*v.y + v.z*v.z); return c;} -internal Vec3F32 normalize_3f32(Vec3F32 v) {v = scale_3f32(v, 1.f/length_3f32(v)); return v;} -internal Vec3F32 mix_3f32(Vec3F32 a, Vec3F32 b, F32 t) {Vec3F32 c = {mix_1f32(a.x, b.x, t), mix_1f32(a.y, b.y, t), mix_1f32(a.z, b.z, t)}; return c;} -internal Vec3F32 cross_3f32(Vec3F32 a, Vec3F32 b) {Vec3F32 c = {a.y*b.z - a.z*b.y, a.z*b.x - a.x*b.z, a.x*b.y - a.y*b.x}; return c;} - -internal Vec3S32 vec_3s32(S32 x, S32 y, S32 z) {Vec3S32 v = {x, y, z}; return v;} -internal Vec3S32 add_3s32(Vec3S32 a, Vec3S32 b) {Vec3S32 c = {a.x+b.x, a.y+b.y, a.z+b.z}; return c;} -internal Vec3S32 sub_3s32(Vec3S32 a, Vec3S32 b) {Vec3S32 c = {a.x-b.x, a.y-b.y, a.z-b.z}; return c;} -internal Vec3S32 mul_3s32(Vec3S32 a, Vec3S32 b) {Vec3S32 c = {a.x*b.x, a.y*b.y, a.z*b.z}; return c;} -internal Vec3S32 div_3s32(Vec3S32 a, Vec3S32 b) {Vec3S32 c = {a.x/b.x, a.y/b.y, a.z/b.z}; return c;} -internal Vec3S32 scale_3s32(Vec3S32 v, S32 s) {Vec3S32 c = {v.x*s, v.y*s, v.z*s}; return c;} -internal S32 dot_3s32(Vec3S32 a, Vec3S32 b) {S32 c = a.x*b.x + a.y*b.y + a.z*b.z; return c;} -internal S32 length_squared_3s32(Vec3S32 v) {S32 c = v.x*v.x + v.y*v.y + v.z*v.z; return c;} -internal S32 length_3s32(Vec3S32 v) {S32 c = (S32)sqrt_f32((F32)(v.x*v.x + v.y*v.y + v.z*v.z)); return c;} -internal Vec3S32 normalize_3s32(Vec3S32 v) {v = scale_3s32(v, (S32)(1.f/length_3s32(v))); return v;} -internal Vec3S32 mix_3s32(Vec3S32 a, Vec3S32 b, F32 t) {Vec3S32 c = {(S32)mix_1f32((F32)a.x, (F32)b.x, t), (S32)mix_1f32((F32)a.y, (F32)b.y, t), (S32)mix_1f32((F32)a.z, (F32)b.z, t)}; return c;} -internal Vec3S32 cross_3s32(Vec3S32 a, Vec3S32 b) {Vec3S32 c = {a.y*b.z - a.z*b.y, a.z*b.x - a.x*b.z, a.x*b.y - a.y*b.x}; return c;} - -internal Vec4F32 vec_4f32(F32 x, F32 y, F32 z, F32 w) {Vec4F32 v = {x, y, z, w}; return v;} -internal Vec4F32 add_4f32(Vec4F32 a, Vec4F32 b) {Vec4F32 c = {a.x+b.x, a.y+b.y, a.z+b.z, a.w+b.w}; return c;} -internal Vec4F32 sub_4f32(Vec4F32 a, Vec4F32 b) {Vec4F32 c = {a.x-b.x, a.y-b.y, a.z-b.z, a.w-b.w}; return c;} -internal Vec4F32 mul_4f32(Vec4F32 a, Vec4F32 b) {Vec4F32 c = {a.x*b.x, a.y*b.y, a.z*b.z, a.w*b.w}; return c;} -internal Vec4F32 div_4f32(Vec4F32 a, Vec4F32 b) {Vec4F32 c = {a.x/b.x, a.y/b.y, a.z/b.z, a.w/b.w}; return c;} -internal Vec4F32 scale_4f32(Vec4F32 v, F32 s) {Vec4F32 c = {v.x*s, v.y*s, v.z*s, v.w*s}; return c;} -internal F32 dot_4f32(Vec4F32 a, Vec4F32 b) {F32 c = a.x*b.x + a.y*b.y + a.z*b.z + a.w*b.w; return c;} -internal F32 length_squared_4f32(Vec4F32 v) {F32 c = v.x*v.x + v.y*v.y + v.z*v.z + v.w*v.w; return c;} -internal F32 length_4f32(Vec4F32 v) {F32 c = sqrt_f32(v.x*v.x + v.y*v.y + v.z*v.z + v.w*v.w); return c;} -internal Vec4F32 normalize_4f32(Vec4F32 v) {v = scale_4f32(v, 1.f/length_4f32(v)); return v;} -internal Vec4F32 mix_4f32(Vec4F32 a, Vec4F32 b, F32 t) {Vec4F32 c = {mix_1f32(a.x, b.x, t), mix_1f32(a.y, b.y, t), mix_1f32(a.z, b.z, t), mix_1f32(a.w, b.w, t)}; return c;} - -internal Vec4S32 vec_4s32(S32 x, S32 y, S32 z, S32 w) {Vec4S32 v = {x, y, z, w}; return v;} -internal Vec4S32 add_4s32(Vec4S32 a, Vec4S32 b) {Vec4S32 c = {a.x+b.x, a.y+b.y, a.z+b.z, a.w+b.w}; return c;} -internal Vec4S32 sub_4s32(Vec4S32 a, Vec4S32 b) {Vec4S32 c = {a.x-b.x, a.y-b.y, a.z-b.z, a.w-b.w}; return c;} -internal Vec4S32 mul_4s32(Vec4S32 a, Vec4S32 b) {Vec4S32 c = {a.x*b.x, a.y*b.y, a.z*b.z, a.w*b.w}; return c;} -internal Vec4S32 div_4s32(Vec4S32 a, Vec4S32 b) {Vec4S32 c = {a.x/b.x, a.y/b.y, a.z/b.z, a.w/b.w}; return c;} -internal Vec4S32 scale_4s32(Vec4S32 v, S32 s) {Vec4S32 c = {v.x*s, v.y*s, v.z*s, v.w*s}; return c;} -internal S32 dot_4s32(Vec4S32 a, Vec4S32 b) {S32 c = a.x*b.x + a.y*b.y + a.z*b.z + a.w*b.w; return c;} -internal S32 length_squared_4s32(Vec4S32 v) {S32 c = v.x*v.x + v.y*v.y + v.z*v.z + v.w*v.w; return c;} -internal S32 length_4s32(Vec4S32 v) {S32 c = (S32)sqrt_f32((F32)(v.x*v.x + v.y*v.y + v.z*v.z + v.w*v.w)); return c;} -internal Vec4S32 normalize_4s32(Vec4S32 v) {v = scale_4s32(v, (S32)(1.f/length_4s32(v))); return v;} -internal Vec4S32 mix_4s32(Vec4S32 a, Vec4S32 b, F32 t) {Vec4S32 c = {(S32)mix_1f32((F32)a.x, (F32)b.x, t), (S32)mix_1f32((F32)a.y, (F32)b.y, t), (S32)mix_1f32((F32)a.z, (F32)b.z, t), (S32)mix_1f32((F32)a.w, (F32)b.w, t)}; return c;} - -//////////////////////////////// -//~ rjf: Matrix Ops - -internal Mat3x3F32 -mat_3x3f32(F32 diagonal) -{ - Mat3x3F32 result = {0}; - result.v[0][0] = diagonal; - result.v[1][1] = diagonal; - result.v[2][2] = diagonal; - return result; -} - -internal Mat3x3F32 -make_translate_3x3f32(Vec2F32 delta) -{ - Mat3x3F32 mat = mat_3x3f32(1.f); - mat.v[2][0] = delta.x; - mat.v[2][1] = delta.y; - return mat; -} - -internal Mat3x3F32 -mul_3x3f32(Mat3x3F32 a, Mat3x3F32 b) -{ - Mat3x3F32 c = {0}; - for(int j = 0; j < 3; j += 1) - { - for(int i = 0; i < 3; i += 1) - { - c.v[i][j] = (a.v[0][j]*b.v[i][0] + - a.v[1][j]*b.v[i][1] + - a.v[2][j]*b.v[i][2]); - } - } - return c; -} - -internal Mat4x4F32 -mat_4x4f32(F32 diagonal) -{ - Mat4x4F32 result = {0}; - result.v[0][0] = diagonal; - result.v[1][1] = diagonal; - result.v[2][2] = diagonal; - result.v[3][3] = diagonal; - return result; -} - -internal Mat4x4F32 -make_translate_4x4f32(Vec3F32 delta) -{ - Mat4x4F32 result = mat_4x4f32(1.f); - result.v[3][0] = delta.x; - result.v[3][1] = delta.y; - result.v[3][2] = delta.z; - return result; -} - -internal Mat4x4F32 -make_scale_4x4f32(Vec3F32 scale) -{ - Mat4x4F32 result = mat_4x4f32(1.f); - result.v[0][0] = scale.x; - result.v[1][1] = scale.y; - result.v[2][2] = scale.z; - return result; -} - -internal Mat4x4F32 -make_perspective_4x4f32(F32 fov, F32 aspect_ratio, F32 near_z, F32 far_z) -{ - Mat4x4F32 result = mat_4x4f32(1.f); - F32 tan_theta_over_2 = tan_f32(fov / 2); - result.v[0][0] = 1.f / tan_theta_over_2; - result.v[1][1] = aspect_ratio / tan_theta_over_2; - result.v[2][3] = 1.f; - result.v[2][2] = -(near_z + far_z) / (near_z - far_z); - result.v[3][2] = (2.f * near_z * far_z) / (near_z - far_z); - result.v[3][3] = 0.f; - return result; -} - -internal Mat4x4F32 -make_orthographic_4x4f32(F32 left, F32 right, F32 bottom, F32 top, F32 near_z, F32 far_z) -{ - Mat4x4F32 result = mat_4x4f32(1.f); - - result.v[0][0] = 2.f / (right - left); - result.v[1][1] = 2.f / (top - bottom); - result.v[2][2] = 2.f / (far_z - near_z); - result.v[3][3] = 1.f; - - result.v[3][0] = (left + right) / (left - right); - result.v[3][1] = (bottom + top) / (bottom - top); - result.v[3][2] = (near_z + far_z) / (near_z - far_z); - - return result; -} - -internal Mat4x4F32 -make_look_at_4x4f32(Vec3F32 eye, Vec3F32 center, Vec3F32 up) -{ - Mat4x4F32 result; - Vec3F32 f = normalize_3f32(sub_3f32(eye, center)); - Vec3F32 s = normalize_3f32(cross_3f32(f, up)); - Vec3F32 u = cross_3f32(s, f); - result.v[0][0] = s.x; - result.v[0][1] = u.x; - result.v[0][2] = -f.x; - result.v[0][3] = 0.0f; - result.v[1][0] = s.y; - result.v[1][1] = u.y; - result.v[1][2] = -f.y; - result.v[1][3] = 0.0f; - result.v[2][0] = s.z; - result.v[2][1] = u.z; - result.v[2][2] = -f.z; - result.v[2][3] = 0.0f; - result.v[3][0] = -dot_3f32(s, eye); - result.v[3][1] = -dot_3f32(u, eye); - result.v[3][2] = dot_3f32(f, eye); - result.v[3][3] = 1.0f; - return result; -} - -internal Mat4x4F32 -make_rotate_4x4f32(Vec3F32 axis, F32 turns) -{ - Mat4x4F32 result = mat_4x4f32(1.f); - axis = normalize_3f32(axis); - F32 sin_theta = sin_f32(turns); - F32 cos_theta = cos_f32(turns); - F32 cos_value = 1.f - cos_theta; - result.v[0][0] = (axis.x * axis.x * cos_value) + cos_theta; - result.v[0][1] = (axis.x * axis.y * cos_value) + (axis.z * sin_theta); - result.v[0][2] = (axis.x * axis.z * cos_value) - (axis.y * sin_theta); - result.v[1][0] = (axis.y * axis.x * cos_value) - (axis.z * sin_theta); - result.v[1][1] = (axis.y * axis.y * cos_value) + cos_theta; - result.v[1][2] = (axis.y * axis.z * cos_value) + (axis.x * sin_theta); - result.v[2][0] = (axis.z * axis.x * cos_value) + (axis.y * sin_theta); - result.v[2][1] = (axis.z * axis.y * cos_value) - (axis.x * sin_theta); - result.v[2][2] = (axis.z * axis.z * cos_value) + cos_theta; - return result; -} - -internal Mat4x4F32 -mul_4x4f32(Mat4x4F32 a, Mat4x4F32 b) -{ - Mat4x4F32 c = {0}; - for(int j = 0; j < 4; j += 1) - { - for(int i = 0; i < 4; i += 1) - { - c.v[i][j] = (a.v[0][j]*b.v[i][0] + - a.v[1][j]*b.v[i][1] + - a.v[2][j]*b.v[i][2] + - a.v[3][j]*b.v[i][3]); - } - } - return c; -} - -internal Mat4x4F32 -scale_4x4f32(Mat4x4F32 m, F32 scale) -{ - for(int j = 0; j < 4; j += 1) - { - for(int i = 0; i < 4; i += 1) - { - m.v[i][j] *= scale; - } - } - return m; -} - -internal Mat4x4F32 -inverse_4x4f32(Mat4x4F32 m) -{ - F32 coef00 = m.v[2][2] * m.v[3][3] - m.v[3][2] * m.v[2][3]; - F32 coef02 = m.v[1][2] * m.v[3][3] - m.v[3][2] * m.v[1][3]; - F32 coef03 = m.v[1][2] * m.v[2][3] - m.v[2][2] * m.v[1][3]; - F32 coef04 = m.v[2][1] * m.v[3][3] - m.v[3][1] * m.v[2][3]; - F32 coef06 = m.v[1][1] * m.v[3][3] - m.v[3][1] * m.v[1][3]; - F32 coef07 = m.v[1][1] * m.v[2][3] - m.v[2][1] * m.v[1][3]; - F32 coef08 = m.v[2][1] * m.v[3][2] - m.v[3][1] * m.v[2][2]; - F32 coef10 = m.v[1][1] * m.v[3][2] - m.v[3][1] * m.v[1][2]; - F32 coef11 = m.v[1][1] * m.v[2][2] - m.v[2][1] * m.v[1][2]; - F32 coef12 = m.v[2][0] * m.v[3][3] - m.v[3][0] * m.v[2][3]; - F32 coef14 = m.v[1][0] * m.v[3][3] - m.v[3][0] * m.v[1][3]; - F32 coef15 = m.v[1][0] * m.v[2][3] - m.v[2][0] * m.v[1][3]; - F32 coef16 = m.v[2][0] * m.v[3][2] - m.v[3][0] * m.v[2][2]; - F32 coef18 = m.v[1][0] * m.v[3][2] - m.v[3][0] * m.v[1][2]; - F32 coef19 = m.v[1][0] * m.v[2][2] - m.v[2][0] * m.v[1][2]; - F32 coef20 = m.v[2][0] * m.v[3][1] - m.v[3][0] * m.v[2][1]; - F32 coef22 = m.v[1][0] * m.v[3][1] - m.v[3][0] * m.v[1][1]; - F32 coef23 = m.v[1][0] * m.v[2][1] - m.v[2][0] * m.v[1][1]; - - Vec4F32 fac0 = { coef00, coef00, coef02, coef03 }; - Vec4F32 fac1 = { coef04, coef04, coef06, coef07 }; - Vec4F32 fac2 = { coef08, coef08, coef10, coef11 }; - Vec4F32 fac3 = { coef12, coef12, coef14, coef15 }; - Vec4F32 fac4 = { coef16, coef16, coef18, coef19 }; - Vec4F32 fac5 = { coef20, coef20, coef22, coef23 }; - - Vec4F32 vec0 = { m.v[1][0], m.v[0][0], m.v[0][0], m.v[0][0] }; - Vec4F32 vec1 = { m.v[1][1], m.v[0][1], m.v[0][1], m.v[0][1] }; - Vec4F32 vec2 = { m.v[1][2], m.v[0][2], m.v[0][2], m.v[0][2] }; - Vec4F32 vec3 = { m.v[1][3], m.v[0][3], m.v[0][3], m.v[0][3] }; - - Vec4F32 inv0 = add_4f32(sub_4f32(mul_4f32(vec1, fac0), mul_4f32(vec2, fac1)), mul_4f32(vec3, fac2)); - Vec4F32 inv1 = add_4f32(sub_4f32(mul_4f32(vec0, fac0), mul_4f32(vec2, fac3)), mul_4f32(vec3, fac4)); - Vec4F32 inv2 = add_4f32(sub_4f32(mul_4f32(vec0, fac1), mul_4f32(vec1, fac3)), mul_4f32(vec3, fac5)); - Vec4F32 inv3 = add_4f32(sub_4f32(mul_4f32(vec0, fac2), mul_4f32(vec1, fac4)), mul_4f32(vec2, fac5)); - - Vec4F32 sign_a = { +1, -1, +1, -1 }; - Vec4F32 sign_b = { -1, +1, -1, +1 }; - - Mat4x4F32 inverse; - for(U32 i = 0; i < 4; i += 1) - { - inverse.v[0][i] = inv0.v[i] * sign_a.v[i]; - inverse.v[1][i] = inv1.v[i] * sign_b.v[i]; - inverse.v[2][i] = inv2.v[i] * sign_a.v[i]; - inverse.v[3][i] = inv3.v[i] * sign_b.v[i]; - } - - Vec4F32 row0 = { inverse.v[0][0], inverse.v[1][0], inverse.v[2][0], inverse.v[3][0] }; - Vec4F32 m0 = { m.v[0][0], m.v[0][1], m.v[0][2], m.v[0][3] }; - Vec4F32 dot0 = mul_4f32(m0, row0); - F32 dot1 = (dot0.x + dot0.y) + (dot0.z + dot0.w); - - F32 one_over_det = 1 / dot1; - - return scale_4x4f32(inverse, one_over_det); -} - -internal Mat4x4F32 -derotate_4x4f32(Mat4x4F32 mat) -{ - Vec3F32 scale = - { - length_3f32(v3f32(mat.v[0][0], mat.v[0][1], mat.v[0][2])), - length_3f32(v3f32(mat.v[1][0], mat.v[1][1], mat.v[1][2])), - length_3f32(v3f32(mat.v[2][0], mat.v[2][1], mat.v[2][2])), - }; - mat.v[0][0] = scale.x; - mat.v[1][0] = 0.f; - mat.v[2][0] = 0.f; - mat.v[0][1] = 0.f; - mat.v[1][1] = scale.y; - mat.v[2][1] = 0.f; - mat.v[0][2] = 0.f; - mat.v[1][2] = 0.f; - mat.v[2][2] = scale.z; - return mat; -} - -//////////////////////////////// -//~ rjf: Range Ops - -internal Rng1U32 rng_1u32(U32 min, U32 max) {Rng1U32 r = {min, max}; if(r.min > r.max) { Swap(U32, r.min, r.max); } return r;} -internal Rng1U32 shift_1u32(Rng1U32 r, U32 x) {r.min += x; r.max += x; return r;} -internal Rng1U32 pad_1u32(Rng1U32 r, U32 x) {r.min -= x; r.max += x; return r;} -internal U32 center_1u32(Rng1U32 r) {U32 c = (r.min+r.max)/2; return c;} -internal B32 contains_1u32(Rng1U32 r, U32 x) {B32 c = (r.min <= x && x < r.max); return c;} -internal U32 dim_1u32(Rng1U32 r) {U32 c = r.max-r.min; return c;} -internal Rng1U32 union_1u32(Rng1U32 a, Rng1U32 b) {Rng1U32 c = {Min(a.min, b.min), Max(a.max, b.max)}; return c;} -internal Rng1U32 intersect_1u32(Rng1U32 a, Rng1U32 b) {Rng1U32 c = {Max(a.min, b.min), Min(a.max, b.max)}; return c;} -internal U32 clamp_1u32(Rng1U32 r, U32 v) {v = Clamp(r.min, v, r.max); return v;} - -internal Rng1S32 rng_1s32(S32 min, S32 max) {Rng1S32 r = {min, max}; if(r.min > r.max) { Swap(S32, r.min, r.max); } return r;} -internal Rng1S32 shift_1s32(Rng1S32 r, S32 x) {r.min += x; r.max += x; return r;} -internal Rng1S32 pad_1s32(Rng1S32 r, S32 x) {r.min -= x; r.max += x; return r;} -internal S32 center_1s32(Rng1S32 r) {S32 c = (r.min+r.max)/2; return c;} -internal B32 contains_1s32(Rng1S32 r, S32 x) {B32 c = (r.min <= x && x < r.max); return c;} -internal S32 dim_1s32(Rng1S32 r) {S32 c = r.max-r.min; return c;} -internal Rng1S32 union_1s32(Rng1S32 a, Rng1S32 b) {Rng1S32 c = {Min(a.min, b.min), Max(a.max, b.max)}; return c;} -internal Rng1S32 intersect_1s32(Rng1S32 a, Rng1S32 b) {Rng1S32 c = {Max(a.min, b.min), Min(a.max, b.max)}; return c;} -internal S32 clamp_1s32(Rng1S32 r, S32 v) {v = Clamp(r.min, v, r.max); return v;} - -internal Rng1U64 rng_1u64(U64 min, U64 max) {Rng1U64 r = {min, max}; if(r.min > r.max) { Swap(U64, r.min, r.max); } return r;} -internal Rng1U64 shift_1u64(Rng1U64 r, U64 x) {r.min += x; r.max += x; return r;} -internal Rng1U64 pad_1u64(Rng1U64 r, U64 x) {r.min -= x; r.max += x; return r;} -internal U64 center_1u64(Rng1U64 r) {U64 c = (r.min+r.max)/2; return c;} -internal B32 contains_1u64(Rng1U64 r, U64 x) {B32 c = (r.min <= x && x < r.max); return c;} -internal U64 dim_1u64(Rng1U64 r) {U64 c = r.max-r.min; return c;} -internal Rng1U64 union_1u64(Rng1U64 a, Rng1U64 b) {Rng1U64 c = {Min(a.min, b.min), Max(a.max, b.max)}; return c;} -internal Rng1U64 intersect_1u64(Rng1U64 a, Rng1U64 b) {Rng1U64 c = {Max(a.min, b.min), Min(a.max, b.max)}; return c;} -internal U64 clamp_1u64(Rng1U64 r, U64 v) {v = Clamp(r.min, v, r.max); return v;} - -internal Rng1S64 rng_1s64(S64 min, S64 max) {Rng1S64 r = {min, max}; if(r.min > r.max) { Swap(S64, r.min, r.max); } return r;} -internal Rng1S64 shift_1s64(Rng1S64 r, S64 x) {r.min += x; r.max += x; return r;} -internal Rng1S64 pad_1s64(Rng1S64 r, S64 x) {r.min -= x; r.max += x; return r;} -internal S64 center_1s64(Rng1S64 r) {S64 c = (r.min+r.max)/2; return c;} -internal B32 contains_1s64(Rng1S64 r, S64 x) {B32 c = (r.min <= x && x < r.max); return c;} -internal S64 dim_1s64(Rng1S64 r) {S64 c = r.max-r.min; return c;} -internal Rng1S64 union_1s64(Rng1S64 a, Rng1S64 b) {Rng1S64 c = {Min(a.min, b.min), Max(a.max, b.max)}; return c;} -internal Rng1S64 intersect_1s64(Rng1S64 a, Rng1S64 b) {Rng1S64 c = {Max(a.min, b.min), Min(a.max, b.max)}; return c;} -internal S64 clamp_1s64(Rng1S64 r, S64 v) {v = Clamp(r.min, v, r.max); return v;} - -internal Rng1F32 rng_1f32(F32 min, F32 max) {Rng1F32 r = {min, max}; if(r.min > r.max) { Swap(F32, r.min, r.max); } return r;} -internal Rng1F32 shift_1f32(Rng1F32 r, F32 x) {r.min += x; r.max += x; return r;} -internal Rng1F32 pad_1f32(Rng1F32 r, F32 x) {r.min -= x; r.max += x; return r;} -internal F32 center_1f32(Rng1F32 r) {F32 c = (r.min+r.max)/2; return c;} -internal B32 contains_1f32(Rng1F32 r, F32 x) {B32 c = (r.min <= x && x < r.max); return c;} -internal F32 dim_1f32(Rng1F32 r) {F32 c = r.max-r.min; return c;} -internal Rng1F32 union_1f32(Rng1F32 a, Rng1F32 b) {Rng1F32 c = {Min(a.min, b.min), Max(a.max, b.max)}; return c;} -internal Rng1F32 intersect_1f32(Rng1F32 a, Rng1F32 b) {Rng1F32 c = {Max(a.min, b.min), Min(a.max, b.max)}; return c;} -internal F32 clamp_1f32(Rng1F32 r, F32 v) {v = Clamp(r.min, v, r.max); return v;} - -internal Rng2S16 rng_2s16(Vec2S16 min, Vec2S16 max) {Rng2S16 r = {min, max}; return r;} -internal Rng2S16 shift_2s16(Rng2S16 r, Vec2S16 x) {r.min = add_2s16(r.min, x); r.max = add_2s16(r.max, x); return r;} -internal Rng2S16 pad_2s16(Rng2S16 r, S16 x) {Vec2S16 xv = {x, x}; r.min = sub_2s16(r.min, xv); r.max = add_2s16(r.max, xv); return r;} -internal Vec2S16 center_2s16(Rng2S16 r) {Vec2S16 c = {(S16)((r.min.x+r.max.x)/2), (S16)((r.min.y+r.max.y)/2)}; return c;} -internal B32 contains_2s16(Rng2S16 r, Vec2S16 x) {B32 c = (r.min.x <= x.x && x.x < r.max.x && r.min.y <= x.y && x.y < r.max.y); return c;} -internal Vec2S16 dim_2s16(Rng2S16 r) {Vec2S16 dim = {(S16)(r.max.x-r.min.x), (S16)(r.max.y-r.min.y)}; return dim;} -internal Rng2S16 union_2s16(Rng2S16 a, Rng2S16 b) {Rng2S16 c; c.p0.x = Min(a.min.x, b.min.x); c.p0.y = Min(a.min.y, b.min.y); c.p1.x = Max(a.max.x, b.max.x); c.p1.y = Max(a.max.y, b.max.y); return c;} -internal Rng2S16 intersect_2s16(Rng2S16 a, Rng2S16 b) {Rng2S16 c; c.p0.x = Max(a.min.x, b.min.x); c.p0.y = Max(a.min.y, b.min.y); c.p1.x = Min(a.max.x, b.max.x); c.p1.y = Min(a.max.y, b.max.y); return c;} -internal Vec2S16 clamp_2s16(Rng2S16 r, Vec2S16 v) {v.x = Clamp(r.min.x, v.x, r.max.x); v.y = Clamp(r.min.y, v.y, r.max.y); return v;} - -internal Rng2S32 rng_2s32(Vec2S32 min, Vec2S32 max) {Rng2S32 r = {min, max}; return r;} -internal Rng2S32 shift_2s32(Rng2S32 r, Vec2S32 x) {r.min = add_2s32(r.min, x); r.max = add_2s32(r.max, x); return r;} -internal Rng2S32 pad_2s32(Rng2S32 r, S32 x) {Vec2S32 xv = {x, x}; r.min = sub_2s32(r.min, xv); r.max = add_2s32(r.max, xv); return r;} -internal Vec2S32 center_2s32(Rng2S32 r) {Vec2S32 c = {(r.min.x+r.max.x)/2, (r.min.y+r.max.y)/2}; return c;} -internal B32 contains_2s32(Rng2S32 r, Vec2S32 x) {B32 c = (r.min.x <= x.x && x.x < r.max.x && r.min.y <= x.y && x.y < r.max.y); return c;} -internal Vec2S32 dim_2s32(Rng2S32 r) {Vec2S32 dim = {r.max.x-r.min.x, r.max.y-r.min.y}; return dim;} -internal Rng2S32 union_2s32(Rng2S32 a, Rng2S32 b) {Rng2S32 c; c.p0.x = Min(a.min.x, b.min.x); c.p0.y = Min(a.min.y, b.min.y); c.p1.x = Max(a.max.x, b.max.x); c.p1.y = Max(a.max.y, b.max.y); return c;} -internal Rng2S32 intersect_2s32(Rng2S32 a, Rng2S32 b) {Rng2S32 c; c.p0.x = Max(a.min.x, b.min.x); c.p0.y = Max(a.min.y, b.min.y); c.p1.x = Min(a.max.x, b.max.x); c.p1.y = Min(a.max.y, b.max.y); return c;} -internal Vec2S32 clamp_2s32(Rng2S32 r, Vec2S32 v) {v.x = Clamp(r.min.x, v.x, r.max.x); v.y = Clamp(r.min.y, v.y, r.max.y); return v;} - -internal Rng2S64 rng_2s64(Vec2S64 min, Vec2S64 max) {Rng2S64 r = {min, max}; return r;} -internal Rng2S64 shift_2s64(Rng2S64 r, Vec2S64 x) {r.min = add_2s64(r.min, x); r.max = add_2s64(r.max, x); return r;} -internal Rng2S64 pad_2s64(Rng2S64 r, S64 x) {Vec2S64 xv = {x, x}; r.min = sub_2s64(r.min, xv); r.max = add_2s64(r.max, xv); return r;} -internal Vec2S64 center_2s64(Rng2S64 r) {Vec2S64 c = {(r.min.x+r.max.x)/2, (r.min.y+r.max.y)/2}; return c;} -internal B32 contains_2s64(Rng2S64 r, Vec2S64 x) {B32 c = (r.min.x <= x.x && x.x < r.max.x && r.min.y <= x.y && x.y < r.max.y); return c;} -internal Vec2S64 dim_2s64(Rng2S64 r) {Vec2S64 dim = {r.max.x-r.min.x, r.max.y-r.min.y}; return dim;} -internal Rng2S64 union_2s64(Rng2S64 a, Rng2S64 b) {Rng2S64 c; c.p0.x = Min(a.min.x, b.min.x); c.p0.y = Min(a.min.y, b.min.y); c.p1.x = Max(a.max.x, b.max.x); c.p1.y = Max(a.max.y, b.max.y); return c;} -internal Rng2S64 intersect_2s64(Rng2S64 a, Rng2S64 b) {Rng2S64 c; c.p0.x = Max(a.min.x, b.min.x); c.p0.y = Max(a.min.y, b.min.y); c.p1.x = Min(a.max.x, b.max.x); c.p1.y = Min(a.max.y, b.max.y); return c;} -internal Vec2S64 clamp_2s64(Rng2S64 r, Vec2S64 v) {v.x = Clamp(r.min.x, v.x, r.max.x); v.y = Clamp(r.min.y, v.y, r.max.y); return v;} - -internal Rng2F32 rng_2f32(Vec2F32 min, Vec2F32 max) {Rng2F32 r = {min, max}; return r;} -internal Rng2F32 shift_2f32(Rng2F32 r, Vec2F32 x) {r.min = add_2f32(r.min, x); r.max = add_2f32(r.max, x); return r;} -internal Rng2F32 pad_2f32(Rng2F32 r, F32 x) {Vec2F32 xv = {x, x}; r.min = sub_2f32(r.min, xv); r.max = add_2f32(r.max, xv); return r;} -internal Vec2F32 center_2f32(Rng2F32 r) {Vec2F32 c = {(r.min.x+r.max.x)/2, (r.min.y+r.max.y)/2}; return c;} -internal B32 contains_2f32(Rng2F32 r, Vec2F32 x) {B32 c = (r.min.x <= x.x && x.x < r.max.x && r.min.y <= x.y && x.y < r.max.y); return c;} -internal Vec2F32 dim_2f32(Rng2F32 r) {Vec2F32 dim = {r.max.x-r.min.x, r.max.y-r.min.y}; return dim;} -internal Rng2F32 union_2f32(Rng2F32 a, Rng2F32 b) {Rng2F32 c; c.p0.x = Min(a.min.x, b.min.x); c.p0.y = Min(a.min.y, b.min.y); c.p1.x = Max(a.max.x, b.max.x); c.p1.y = Max(a.max.y, b.max.y); return c;} -internal Rng2F32 intersect_2f32(Rng2F32 a, Rng2F32 b) {Rng2F32 c; c.p0.x = Max(a.min.x, b.min.x); c.p0.y = Max(a.min.y, b.min.y); c.p1.x = Min(a.max.x, b.max.x); c.p1.y = Min(a.max.y, b.max.y); return c;} -internal Vec2F32 clamp_2f32(Rng2F32 r, Vec2F32 v) {v.x = Clamp(r.min.x, v.x, r.max.x); v.y = Clamp(r.min.y, v.y, r.max.y); return v;} - -//////////////////////////////// -//~ rjf: Miscellaneous Ops - -internal Vec3F32 -hsv_from_rgb(Vec3F32 rgb) -{ - F32 c_max = Max(rgb.x, Max(rgb.y, rgb.z)); - F32 c_min = Min(rgb.x, Min(rgb.y, rgb.z)); - F32 delta = c_max - c_min; - F32 h = ((delta == 0.f) ? 0.f : - (c_max == rgb.x) ? mod_f32((rgb.y - rgb.z)/delta + 6.f, 6.f) : - (c_max == rgb.y) ? (rgb.z - rgb.x)/delta + 2.f : - (c_max == rgb.z) ? (rgb.x - rgb.y)/delta + 4.f : - 0.f); - F32 s = (c_max == 0.f) ? 0.f : (delta/c_max); - F32 v = c_max; - Vec3F32 hsv = {h/6.f, s, v}; - return hsv; -} - -internal Vec3F32 -rgb_from_hsv(Vec3F32 hsv) -{ - F32 h = mod_f32(hsv.x * 360.f, 360.f); - F32 s = hsv.y; - F32 v = hsv.z; - - F32 c = v*s; - F32 x = c*(1.f - abs_f32(mod_f32(h/60.f, 2.f) - 1.f)); - F32 m = v - c; - - F32 r = 0; - F32 g = 0; - F32 b = 0; - - if ((h >= 0.f && h < 60.f) || (h >= 360.f && h < 420.f)){ - r = c; - g = x; - b = 0; - } - else if (h >= 60.f && h < 120.f){ - r = x; - g = c; - b = 0; - } - else if (h >= 120.f && h < 180.f){ - r = 0; - g = c; - b = x; - } - else if (h >= 180.f && h < 240.f){ - r = 0; - g = x; - b = c; - } - else if (h >= 240.f && h < 300.f){ - r = x; - g = 0; - b = c; - } - else if ((h >= 300.f && h <= 360.f) || (h >= -60.f && h <= 0.f)){ - r = c; - g = 0; - b = x; - } - - Vec3F32 rgb = {r + m, g + m, b + m}; - return(rgb); -} - -internal Vec4F32 -hsva_from_rgba(Vec4F32 rgba) -{ - Vec3F32 rgb = v3f32(rgba.x, rgba.y, rgba.z); - Vec3F32 hsv = hsv_from_rgb(rgb); - Vec4F32 hsva = v4f32(hsv.x, hsv.y, hsv.z, rgba.w); - return hsva; -} - -internal Vec4F32 -rgba_from_hsva(Vec4F32 hsva) -{ - Vec3F32 hsv = v3f32(hsva.x, hsva.y, hsva.z); - Vec3F32 rgb = rgb_from_hsv(hsv); - Vec4F32 rgba = v4f32(rgb.x, rgb.y, rgb.z, hsva.w); - return rgba; -} - -internal Vec4F32 -rgba_from_u32(U32 hex) -{ - Vec4F32 result = v4f32(((hex&0xff000000)>>24)/255.f, - ((hex&0x00ff0000)>>16)/255.f, - ((hex&0x0000ff00)>> 8)/255.f, - ((hex&0x000000ff)>> 0)/255.f); - return result; -} - -internal U32 -u32_from_rgba(Vec4F32 rgba) -{ - U32 result = 0; - result |= ((U32)((U8)(rgba.x*255.f))) << 24; - result |= ((U32)((U8)(rgba.y*255.f))) << 16; - result |= ((U32)((U8)(rgba.z*255.f))) << 8; - result |= ((U32)((U8)(rgba.w*255.f))) << 0; - return result; -} - -//////////////////////////////// -//~ rjf: List Type Functions - -internal void -rng1s64_list_push(Arena *arena, Rng1S64List *list, Rng1S64 rng) -{ - Rng1S64Node *n = push_array(arena, Rng1S64Node, 1); - MemoryCopyStruct(&n->v, &rng); - SLLQueuePush(list->first, list->last, n); - list->count += 1; -} - -internal Rng1S64Array -rng1s64_array_from_list(Arena *arena, Rng1S64List *list) -{ - Rng1S64Array arr = {0}; - arr.count = list->count; - arr.v = push_array_no_zero(arena, Rng1S64, arr.count); - U64 idx = 0; - for(Rng1S64Node *n = list->first; n != 0; n = n->next) - { - arr.v[idx] = n->v; - idx += 1; - } - return arr; -} +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +//////////////////////////////// +//~ rjf: Scalar Ops + +internal F32 +mix_1f32(F32 a, F32 b, F32 t) +{ + F32 c = (a + (b-a) * Clamp(0.f, t, 1.f)); + return c; +} + +internal F64 +mix_1f64(F64 a, F64 b, F64 t) +{ + F64 c = (a + (b-a) * Clamp(0.0, t, 1.0)); + return c; +} + +//////////////////////////////// +//~ rjf: Vector Ops + +internal Vec2F32 vec_2f32(F32 x, F32 y) {Vec2F32 v = {x, y}; return v;} +internal Vec2F32 add_2f32(Vec2F32 a, Vec2F32 b) {Vec2F32 c = {a.x+b.x, a.y+b.y}; return c;} +internal Vec2F32 sub_2f32(Vec2F32 a, Vec2F32 b) {Vec2F32 c = {a.x-b.x, a.y-b.y}; return c;} +internal Vec2F32 mul_2f32(Vec2F32 a, Vec2F32 b) {Vec2F32 c = {a.x*b.x, a.y*b.y}; return c;} +internal Vec2F32 div_2f32(Vec2F32 a, Vec2F32 b) {Vec2F32 c = {a.x/b.x, a.y/b.y}; return c;} +internal Vec2F32 scale_2f32(Vec2F32 v, F32 s) {Vec2F32 c = {v.x*s, v.y*s}; return c;} +internal F32 dot_2f32(Vec2F32 a, Vec2F32 b) {F32 c = a.x*b.x + a.y*b.y; return c;} +internal F32 length_squared_2f32(Vec2F32 v) {F32 c = v.x*v.x + v.y*v.y; return c;} +internal F32 length_2f32(Vec2F32 v) {F32 c = sqrt_f32(v.x*v.x + v.y*v.y); return c;} +internal Vec2F32 normalize_2f32(Vec2F32 v) {v = scale_2f32(v, 1.f/length_2f32(v)); return v;} +internal Vec2F32 mix_2f32(Vec2F32 a, Vec2F32 b, F32 t) {Vec2F32 c = {mix_1f32(a.x, b.x, t), mix_1f32(a.y, b.y, t)}; return c;} + +internal Vec2S64 vec_2s64(S64 x, S64 y) {Vec2S64 v = {x, y}; return v;} +internal Vec2S64 add_2s64(Vec2S64 a, Vec2S64 b) {Vec2S64 c = {a.x+b.x, a.y+b.y}; return c;} +internal Vec2S64 sub_2s64(Vec2S64 a, Vec2S64 b) {Vec2S64 c = {a.x-b.x, a.y-b.y}; return c;} +internal Vec2S64 mul_2s64(Vec2S64 a, Vec2S64 b) {Vec2S64 c = {a.x*b.x, a.y*b.y}; return c;} +internal Vec2S64 div_2s64(Vec2S64 a, Vec2S64 b) {Vec2S64 c = {a.x/b.x, a.y/b.y}; return c;} +internal Vec2S64 scale_2s64(Vec2S64 v, S64 s) {Vec2S64 c = {v.x*s, v.y*s}; return c;} +internal S64 dot_2s64(Vec2S64 a, Vec2S64 b) {S64 c = a.x*b.x + a.y*b.y; return c;} +internal S64 length_squared_2s64(Vec2S64 v) {S64 c = v.x*v.x + v.y*v.y; return c;} +internal S64 length_2s64(Vec2S64 v) {S64 c = (S64)sqrt_f64((F64)(v.x*v.x + v.y*v.y)); return c;} +internal Vec2S64 normalize_2s64(Vec2S64 v) {v = scale_2s64(v, (S64)(1.f/length_2s64(v))); return v;} +internal Vec2S64 mix_2s64(Vec2S64 a, Vec2S64 b, F32 t) {Vec2S64 c = {(S64)mix_1f32((F32)a.x, (F32)b.x, t), (S64)mix_1f32((F32)a.y, (F32)b.y, t)}; return c;} + +internal Vec2S32 vec_2s32(S32 x, S32 y) {Vec2S32 v = {x, y}; return v;} +internal Vec2S32 add_2s32(Vec2S32 a, Vec2S32 b) {Vec2S32 c = {a.x+b.x, a.y+b.y}; return c;} +internal Vec2S32 sub_2s32(Vec2S32 a, Vec2S32 b) {Vec2S32 c = {a.x-b.x, a.y-b.y}; return c;} +internal Vec2S32 mul_2s32(Vec2S32 a, Vec2S32 b) {Vec2S32 c = {a.x*b.x, a.y*b.y}; return c;} +internal Vec2S32 div_2s32(Vec2S32 a, Vec2S32 b) {Vec2S32 c = {a.x/b.x, a.y/b.y}; return c;} +internal Vec2S32 scale_2s32(Vec2S32 v, S32 s) {Vec2S32 c = {v.x*s, v.y*s}; return c;} +internal S32 dot_2s32(Vec2S32 a, Vec2S32 b) {S32 c = a.x*b.x + a.y*b.y; return c;} +internal S32 length_squared_2s32(Vec2S32 v) {S32 c = v.x*v.x + v.y*v.y; return c;} +internal S32 length_2s32(Vec2S32 v) {S32 c = (S32)sqrt_f32((F32)v.x*(F32)v.x + (F32)v.y*(F32)v.y); return c;} +internal Vec2S32 normalize_2s32(Vec2S32 v) {v = scale_2s32(v, (S32)(1.f/length_2s32(v))); return v;} +internal Vec2S32 mix_2s32(Vec2S32 a, Vec2S32 b, F32 t) {Vec2S32 c = {(S32)mix_1f32((F32)a.x, (F32)b.x, t), (S32)mix_1f32((F32)a.y, (F32)b.y, t)}; return c;} + +internal Vec2S16 vec_2s16(S16 x, S16 y) {Vec2S16 v = {x, y}; return v;} +internal Vec2S16 add_2s16(Vec2S16 a, Vec2S16 b) {Vec2S16 c = {(S16)(a.x+b.x), (S16)(a.y+b.y)}; return c;} +internal Vec2S16 sub_2s16(Vec2S16 a, Vec2S16 b) {Vec2S16 c = {(S16)(a.x-b.x), (S16)(a.y-b.y)}; return c;} +internal Vec2S16 mul_2s16(Vec2S16 a, Vec2S16 b) {Vec2S16 c = {(S16)(a.x*b.x), (S16)(a.y*b.y)}; return c;} +internal Vec2S16 div_2s16(Vec2S16 a, Vec2S16 b) {Vec2S16 c = {(S16)(a.x/b.x), (S16)(a.y/b.y)}; return c;} +internal Vec2S16 scale_2s16(Vec2S16 v, S16 s) {Vec2S16 c = {(S16)(v.x*s), (S16)(v.y*s)}; return c;} +internal S16 dot_2s16(Vec2S16 a, Vec2S16 b) {S16 c = a.x*b.x + a.y*b.y; return c;} +internal S16 length_squared_2s16(Vec2S16 v) {S16 c = v.x*v.x + v.y*v.y; return c;} +internal S16 length_2s16(Vec2S16 v) {S16 c = (S16)sqrt_f32((F32)(v.x*v.x + v.y*v.y)); return c;} +internal Vec2S16 normalize_2s16(Vec2S16 v) {v = scale_2s16(v, (S16)(1.f/length_2s16(v))); return v;} +internal Vec2S16 mix_2s16(Vec2S16 a, Vec2S16 b, F32 t) {Vec2S16 c = {(S16)mix_1f32((F32)a.x, (F32)b.x, t), (S16)mix_1f32((F32)a.y, (F32)b.y, t)}; return c;} + +internal Vec3F32 vec_3f32(F32 x, F32 y, F32 z) {Vec3F32 v = {x, y, z}; return v;} +internal Vec3F32 add_3f32(Vec3F32 a, Vec3F32 b) {Vec3F32 c = {a.x+b.x, a.y+b.y, a.z+b.z}; return c;} +internal Vec3F32 sub_3f32(Vec3F32 a, Vec3F32 b) {Vec3F32 c = {a.x-b.x, a.y-b.y, a.z-b.z}; return c;} +internal Vec3F32 mul_3f32(Vec3F32 a, Vec3F32 b) {Vec3F32 c = {a.x*b.x, a.y*b.y, a.z*b.z}; return c;} +internal Vec3F32 div_3f32(Vec3F32 a, Vec3F32 b) {Vec3F32 c = {a.x/b.x, a.y/b.y, a.z/b.z}; return c;} +internal Vec3F32 scale_3f32(Vec3F32 v, F32 s) {Vec3F32 c = {v.x*s, v.y*s, v.z*s}; return c;} +internal F32 dot_3f32(Vec3F32 a, Vec3F32 b) {F32 c = a.x*b.x + a.y*b.y + a.z*b.z; return c;} +internal F32 length_squared_3f32(Vec3F32 v) {F32 c = v.x*v.x + v.y*v.y + v.z*v.z; return c;} +internal F32 length_3f32(Vec3F32 v) {F32 c = sqrt_f32(v.x*v.x + v.y*v.y + v.z*v.z); return c;} +internal Vec3F32 normalize_3f32(Vec3F32 v) {v = scale_3f32(v, 1.f/length_3f32(v)); return v;} +internal Vec3F32 mix_3f32(Vec3F32 a, Vec3F32 b, F32 t) {Vec3F32 c = {mix_1f32(a.x, b.x, t), mix_1f32(a.y, b.y, t), mix_1f32(a.z, b.z, t)}; return c;} +internal Vec3F32 cross_3f32(Vec3F32 a, Vec3F32 b) {Vec3F32 c = {a.y*b.z - a.z*b.y, a.z*b.x - a.x*b.z, a.x*b.y - a.y*b.x}; return c;} + +internal Vec3S32 vec_3s32(S32 x, S32 y, S32 z) {Vec3S32 v = {x, y, z}; return v;} +internal Vec3S32 add_3s32(Vec3S32 a, Vec3S32 b) {Vec3S32 c = {a.x+b.x, a.y+b.y, a.z+b.z}; return c;} +internal Vec3S32 sub_3s32(Vec3S32 a, Vec3S32 b) {Vec3S32 c = {a.x-b.x, a.y-b.y, a.z-b.z}; return c;} +internal Vec3S32 mul_3s32(Vec3S32 a, Vec3S32 b) {Vec3S32 c = {a.x*b.x, a.y*b.y, a.z*b.z}; return c;} +internal Vec3S32 div_3s32(Vec3S32 a, Vec3S32 b) {Vec3S32 c = {a.x/b.x, a.y/b.y, a.z/b.z}; return c;} +internal Vec3S32 scale_3s32(Vec3S32 v, S32 s) {Vec3S32 c = {v.x*s, v.y*s, v.z*s}; return c;} +internal S32 dot_3s32(Vec3S32 a, Vec3S32 b) {S32 c = a.x*b.x + a.y*b.y + a.z*b.z; return c;} +internal S32 length_squared_3s32(Vec3S32 v) {S32 c = v.x*v.x + v.y*v.y + v.z*v.z; return c;} +internal S32 length_3s32(Vec3S32 v) {S32 c = (S32)sqrt_f32((F32)(v.x*v.x + v.y*v.y + v.z*v.z)); return c;} +internal Vec3S32 normalize_3s32(Vec3S32 v) {v = scale_3s32(v, (S32)(1.f/length_3s32(v))); return v;} +internal Vec3S32 mix_3s32(Vec3S32 a, Vec3S32 b, F32 t) {Vec3S32 c = {(S32)mix_1f32((F32)a.x, (F32)b.x, t), (S32)mix_1f32((F32)a.y, (F32)b.y, t), (S32)mix_1f32((F32)a.z, (F32)b.z, t)}; return c;} +internal Vec3S32 cross_3s32(Vec3S32 a, Vec3S32 b) {Vec3S32 c = {a.y*b.z - a.z*b.y, a.z*b.x - a.x*b.z, a.x*b.y - a.y*b.x}; return c;} + +internal Vec4F32 vec_4f32(F32 x, F32 y, F32 z, F32 w) {Vec4F32 v = {x, y, z, w}; return v;} +internal Vec4F32 add_4f32(Vec4F32 a, Vec4F32 b) {Vec4F32 c = {a.x+b.x, a.y+b.y, a.z+b.z, a.w+b.w}; return c;} +internal Vec4F32 sub_4f32(Vec4F32 a, Vec4F32 b) {Vec4F32 c = {a.x-b.x, a.y-b.y, a.z-b.z, a.w-b.w}; return c;} +internal Vec4F32 mul_4f32(Vec4F32 a, Vec4F32 b) {Vec4F32 c = {a.x*b.x, a.y*b.y, a.z*b.z, a.w*b.w}; return c;} +internal Vec4F32 div_4f32(Vec4F32 a, Vec4F32 b) {Vec4F32 c = {a.x/b.x, a.y/b.y, a.z/b.z, a.w/b.w}; return c;} +internal Vec4F32 scale_4f32(Vec4F32 v, F32 s) {Vec4F32 c = {v.x*s, v.y*s, v.z*s, v.w*s}; return c;} +internal F32 dot_4f32(Vec4F32 a, Vec4F32 b) {F32 c = a.x*b.x + a.y*b.y + a.z*b.z + a.w*b.w; return c;} +internal F32 length_squared_4f32(Vec4F32 v) {F32 c = v.x*v.x + v.y*v.y + v.z*v.z + v.w*v.w; return c;} +internal F32 length_4f32(Vec4F32 v) {F32 c = sqrt_f32(v.x*v.x + v.y*v.y + v.z*v.z + v.w*v.w); return c;} +internal Vec4F32 normalize_4f32(Vec4F32 v) {v = scale_4f32(v, 1.f/length_4f32(v)); return v;} +internal Vec4F32 mix_4f32(Vec4F32 a, Vec4F32 b, F32 t) {Vec4F32 c = {mix_1f32(a.x, b.x, t), mix_1f32(a.y, b.y, t), mix_1f32(a.z, b.z, t), mix_1f32(a.w, b.w, t)}; return c;} + +internal Vec4S32 vec_4s32(S32 x, S32 y, S32 z, S32 w) {Vec4S32 v = {x, y, z, w}; return v;} +internal Vec4S32 add_4s32(Vec4S32 a, Vec4S32 b) {Vec4S32 c = {a.x+b.x, a.y+b.y, a.z+b.z, a.w+b.w}; return c;} +internal Vec4S32 sub_4s32(Vec4S32 a, Vec4S32 b) {Vec4S32 c = {a.x-b.x, a.y-b.y, a.z-b.z, a.w-b.w}; return c;} +internal Vec4S32 mul_4s32(Vec4S32 a, Vec4S32 b) {Vec4S32 c = {a.x*b.x, a.y*b.y, a.z*b.z, a.w*b.w}; return c;} +internal Vec4S32 div_4s32(Vec4S32 a, Vec4S32 b) {Vec4S32 c = {a.x/b.x, a.y/b.y, a.z/b.z, a.w/b.w}; return c;} +internal Vec4S32 scale_4s32(Vec4S32 v, S32 s) {Vec4S32 c = {v.x*s, v.y*s, v.z*s, v.w*s}; return c;} +internal S32 dot_4s32(Vec4S32 a, Vec4S32 b) {S32 c = a.x*b.x + a.y*b.y + a.z*b.z + a.w*b.w; return c;} +internal S32 length_squared_4s32(Vec4S32 v) {S32 c = v.x*v.x + v.y*v.y + v.z*v.z + v.w*v.w; return c;} +internal S32 length_4s32(Vec4S32 v) {S32 c = (S32)sqrt_f32((F32)(v.x*v.x + v.y*v.y + v.z*v.z + v.w*v.w)); return c;} +internal Vec4S32 normalize_4s32(Vec4S32 v) {v = scale_4s32(v, (S32)(1.f/length_4s32(v))); return v;} +internal Vec4S32 mix_4s32(Vec4S32 a, Vec4S32 b, F32 t) {Vec4S32 c = {(S32)mix_1f32((F32)a.x, (F32)b.x, t), (S32)mix_1f32((F32)a.y, (F32)b.y, t), (S32)mix_1f32((F32)a.z, (F32)b.z, t), (S32)mix_1f32((F32)a.w, (F32)b.w, t)}; return c;} + +//////////////////////////////// +//~ rjf: Matrix Ops + +internal Mat3x3F32 +mat_3x3f32(F32 diagonal) +{ + Mat3x3F32 result = {0}; + result.v[0][0] = diagonal; + result.v[1][1] = diagonal; + result.v[2][2] = diagonal; + return result; +} + +internal Mat3x3F32 +make_translate_3x3f32(Vec2F32 delta) +{ + Mat3x3F32 mat = mat_3x3f32(1.f); + mat.v[2][0] = delta.x; + mat.v[2][1] = delta.y; + return mat; +} + +internal Mat3x3F32 +make_scale_3x3f32(Vec2F32 scale) +{ + Mat3x3F32 mat = mat_3x3f32(1.f); + mat.v[0][0] = scale.x; + mat.v[1][1] = scale.y; + return mat; +} + +internal Mat3x3F32 +mul_3x3f32(Mat3x3F32 a, Mat3x3F32 b) +{ + Mat3x3F32 c = {0}; + for(int j = 0; j < 3; j += 1) + { + for(int i = 0; i < 3; i += 1) + { + c.v[i][j] = (a.v[0][j]*b.v[i][0] + + a.v[1][j]*b.v[i][1] + + a.v[2][j]*b.v[i][2]); + } + } + return c; +} + +internal Mat4x4F32 +mat_4x4f32(F32 diagonal) +{ + Mat4x4F32 result = {0}; + result.v[0][0] = diagonal; + result.v[1][1] = diagonal; + result.v[2][2] = diagonal; + result.v[3][3] = diagonal; + return result; +} + +internal Mat4x4F32 +make_translate_4x4f32(Vec3F32 delta) +{ + Mat4x4F32 result = mat_4x4f32(1.f); + result.v[3][0] = delta.x; + result.v[3][1] = delta.y; + result.v[3][2] = delta.z; + return result; +} + +internal Mat4x4F32 +make_scale_4x4f32(Vec3F32 scale) +{ + Mat4x4F32 result = mat_4x4f32(1.f); + result.v[0][0] = scale.x; + result.v[1][1] = scale.y; + result.v[2][2] = scale.z; + return result; +} + +internal Mat4x4F32 +make_perspective_4x4f32(F32 fov, F32 aspect_ratio, F32 near_z, F32 far_z) +{ + Mat4x4F32 result = mat_4x4f32(1.f); + F32 tan_theta_over_2 = tan_f32(fov / 2); + result.v[0][0] = 1.f / tan_theta_over_2; + result.v[1][1] = aspect_ratio / tan_theta_over_2; + result.v[2][3] = 1.f; + result.v[2][2] = -(near_z + far_z) / (near_z - far_z); + result.v[3][2] = (2.f * near_z * far_z) / (near_z - far_z); + result.v[3][3] = 0.f; + return result; +} + +internal Mat4x4F32 +make_orthographic_4x4f32(F32 left, F32 right, F32 bottom, F32 top, F32 near_z, F32 far_z) +{ + Mat4x4F32 result = mat_4x4f32(1.f); + + result.v[0][0] = 2.f / (right - left); + result.v[1][1] = 2.f / (top - bottom); + result.v[2][2] = 2.f / (far_z - near_z); + result.v[3][3] = 1.f; + + result.v[3][0] = (left + right) / (left - right); + result.v[3][1] = (bottom + top) / (bottom - top); + result.v[3][2] = (near_z + far_z) / (near_z - far_z); + + return result; +} + +internal Mat4x4F32 +make_look_at_4x4f32(Vec3F32 eye, Vec3F32 center, Vec3F32 up) +{ + Mat4x4F32 result; + Vec3F32 f = normalize_3f32(sub_3f32(eye, center)); + Vec3F32 s = normalize_3f32(cross_3f32(f, up)); + Vec3F32 u = cross_3f32(s, f); + result.v[0][0] = s.x; + result.v[0][1] = u.x; + result.v[0][2] = -f.x; + result.v[0][3] = 0.0f; + result.v[1][0] = s.y; + result.v[1][1] = u.y; + result.v[1][2] = -f.y; + result.v[1][3] = 0.0f; + result.v[2][0] = s.z; + result.v[2][1] = u.z; + result.v[2][2] = -f.z; + result.v[2][3] = 0.0f; + result.v[3][0] = -dot_3f32(s, eye); + result.v[3][1] = -dot_3f32(u, eye); + result.v[3][2] = dot_3f32(f, eye); + result.v[3][3] = 1.0f; + return result; +} + +internal Mat4x4F32 +make_rotate_4x4f32(Vec3F32 axis, F32 turns) +{ + Mat4x4F32 result = mat_4x4f32(1.f); + axis = normalize_3f32(axis); + F32 sin_theta = sin_f32(turns); + F32 cos_theta = cos_f32(turns); + F32 cos_value = 1.f - cos_theta; + result.v[0][0] = (axis.x * axis.x * cos_value) + cos_theta; + result.v[0][1] = (axis.x * axis.y * cos_value) + (axis.z * sin_theta); + result.v[0][2] = (axis.x * axis.z * cos_value) - (axis.y * sin_theta); + result.v[1][0] = (axis.y * axis.x * cos_value) - (axis.z * sin_theta); + result.v[1][1] = (axis.y * axis.y * cos_value) + cos_theta; + result.v[1][2] = (axis.y * axis.z * cos_value) + (axis.x * sin_theta); + result.v[2][0] = (axis.z * axis.x * cos_value) + (axis.y * sin_theta); + result.v[2][1] = (axis.z * axis.y * cos_value) - (axis.x * sin_theta); + result.v[2][2] = (axis.z * axis.z * cos_value) + cos_theta; + return result; +} + +internal Mat4x4F32 +mul_4x4f32(Mat4x4F32 a, Mat4x4F32 b) +{ + Mat4x4F32 c = {0}; + for(int j = 0; j < 4; j += 1) + { + for(int i = 0; i < 4; i += 1) + { + c.v[i][j] = (a.v[0][j]*b.v[i][0] + + a.v[1][j]*b.v[i][1] + + a.v[2][j]*b.v[i][2] + + a.v[3][j]*b.v[i][3]); + } + } + return c; +} + +internal Mat4x4F32 +scale_4x4f32(Mat4x4F32 m, F32 scale) +{ + for(int j = 0; j < 4; j += 1) + { + for(int i = 0; i < 4; i += 1) + { + m.v[i][j] *= scale; + } + } + return m; +} + +internal Mat4x4F32 +inverse_4x4f32(Mat4x4F32 m) +{ + F32 coef00 = m.v[2][2] * m.v[3][3] - m.v[3][2] * m.v[2][3]; + F32 coef02 = m.v[1][2] * m.v[3][3] - m.v[3][2] * m.v[1][3]; + F32 coef03 = m.v[1][2] * m.v[2][3] - m.v[2][2] * m.v[1][3]; + F32 coef04 = m.v[2][1] * m.v[3][3] - m.v[3][1] * m.v[2][3]; + F32 coef06 = m.v[1][1] * m.v[3][3] - m.v[3][1] * m.v[1][3]; + F32 coef07 = m.v[1][1] * m.v[2][3] - m.v[2][1] * m.v[1][3]; + F32 coef08 = m.v[2][1] * m.v[3][2] - m.v[3][1] * m.v[2][2]; + F32 coef10 = m.v[1][1] * m.v[3][2] - m.v[3][1] * m.v[1][2]; + F32 coef11 = m.v[1][1] * m.v[2][2] - m.v[2][1] * m.v[1][2]; + F32 coef12 = m.v[2][0] * m.v[3][3] - m.v[3][0] * m.v[2][3]; + F32 coef14 = m.v[1][0] * m.v[3][3] - m.v[3][0] * m.v[1][3]; + F32 coef15 = m.v[1][0] * m.v[2][3] - m.v[2][0] * m.v[1][3]; + F32 coef16 = m.v[2][0] * m.v[3][2] - m.v[3][0] * m.v[2][2]; + F32 coef18 = m.v[1][0] * m.v[3][2] - m.v[3][0] * m.v[1][2]; + F32 coef19 = m.v[1][0] * m.v[2][2] - m.v[2][0] * m.v[1][2]; + F32 coef20 = m.v[2][0] * m.v[3][1] - m.v[3][0] * m.v[2][1]; + F32 coef22 = m.v[1][0] * m.v[3][1] - m.v[3][0] * m.v[1][1]; + F32 coef23 = m.v[1][0] * m.v[2][1] - m.v[2][0] * m.v[1][1]; + + Vec4F32 fac0 = { coef00, coef00, coef02, coef03 }; + Vec4F32 fac1 = { coef04, coef04, coef06, coef07 }; + Vec4F32 fac2 = { coef08, coef08, coef10, coef11 }; + Vec4F32 fac3 = { coef12, coef12, coef14, coef15 }; + Vec4F32 fac4 = { coef16, coef16, coef18, coef19 }; + Vec4F32 fac5 = { coef20, coef20, coef22, coef23 }; + + Vec4F32 vec0 = { m.v[1][0], m.v[0][0], m.v[0][0], m.v[0][0] }; + Vec4F32 vec1 = { m.v[1][1], m.v[0][1], m.v[0][1], m.v[0][1] }; + Vec4F32 vec2 = { m.v[1][2], m.v[0][2], m.v[0][2], m.v[0][2] }; + Vec4F32 vec3 = { m.v[1][3], m.v[0][3], m.v[0][3], m.v[0][3] }; + + Vec4F32 inv0 = add_4f32(sub_4f32(mul_4f32(vec1, fac0), mul_4f32(vec2, fac1)), mul_4f32(vec3, fac2)); + Vec4F32 inv1 = add_4f32(sub_4f32(mul_4f32(vec0, fac0), mul_4f32(vec2, fac3)), mul_4f32(vec3, fac4)); + Vec4F32 inv2 = add_4f32(sub_4f32(mul_4f32(vec0, fac1), mul_4f32(vec1, fac3)), mul_4f32(vec3, fac5)); + Vec4F32 inv3 = add_4f32(sub_4f32(mul_4f32(vec0, fac2), mul_4f32(vec1, fac4)), mul_4f32(vec2, fac5)); + + Vec4F32 sign_a = { +1, -1, +1, -1 }; + Vec4F32 sign_b = { -1, +1, -1, +1 }; + + Mat4x4F32 inverse; + for(U32 i = 0; i < 4; i += 1) + { + inverse.v[0][i] = inv0.v[i] * sign_a.v[i]; + inverse.v[1][i] = inv1.v[i] * sign_b.v[i]; + inverse.v[2][i] = inv2.v[i] * sign_a.v[i]; + inverse.v[3][i] = inv3.v[i] * sign_b.v[i]; + } + + Vec4F32 row0 = { inverse.v[0][0], inverse.v[1][0], inverse.v[2][0], inverse.v[3][0] }; + Vec4F32 m0 = { m.v[0][0], m.v[0][1], m.v[0][2], m.v[0][3] }; + Vec4F32 dot0 = mul_4f32(m0, row0); + F32 dot1 = (dot0.x + dot0.y) + (dot0.z + dot0.w); + + F32 one_over_det = 1 / dot1; + + return scale_4x4f32(inverse, one_over_det); +} + +internal Mat4x4F32 +derotate_4x4f32(Mat4x4F32 mat) +{ + Vec3F32 scale = + { + length_3f32(v3f32(mat.v[0][0], mat.v[0][1], mat.v[0][2])), + length_3f32(v3f32(mat.v[1][0], mat.v[1][1], mat.v[1][2])), + length_3f32(v3f32(mat.v[2][0], mat.v[2][1], mat.v[2][2])), + }; + mat.v[0][0] = scale.x; + mat.v[1][0] = 0.f; + mat.v[2][0] = 0.f; + mat.v[0][1] = 0.f; + mat.v[1][1] = scale.y; + mat.v[2][1] = 0.f; + mat.v[0][2] = 0.f; + mat.v[1][2] = 0.f; + mat.v[2][2] = scale.z; + return mat; +} + +//////////////////////////////// +//~ rjf: Range Ops + +internal Rng1U32 rng_1u32(U32 min, U32 max) {Rng1U32 r = {min, max}; if(r.min > r.max) { Swap(U32, r.min, r.max); } return r;} +internal Rng1U32 shift_1u32(Rng1U32 r, U32 x) {r.min += x; r.max += x; return r;} +internal Rng1U32 pad_1u32(Rng1U32 r, U32 x) {r.min -= x; r.max += x; return r;} +internal U32 center_1u32(Rng1U32 r) {U32 c = (r.min+r.max)/2; return c;} +internal B32 contains_1u32(Rng1U32 r, U32 x) {B32 c = (r.min <= x && x < r.max); return c;} +internal U32 dim_1u32(Rng1U32 r) {U32 c = r.max-r.min; return c;} +internal Rng1U32 union_1u32(Rng1U32 a, Rng1U32 b) {Rng1U32 c = {Min(a.min, b.min), Max(a.max, b.max)}; return c;} +internal Rng1U32 intersect_1u32(Rng1U32 a, Rng1U32 b) {Rng1U32 c = {Max(a.min, b.min), Min(a.max, b.max)}; return c;} +internal U32 clamp_1u32(Rng1U32 r, U32 v) {v = Clamp(r.min, v, r.max); return v;} + +internal Rng1S32 rng_1s32(S32 min, S32 max) {Rng1S32 r = {min, max}; if(r.min > r.max) { Swap(S32, r.min, r.max); } return r;} +internal Rng1S32 shift_1s32(Rng1S32 r, S32 x) {r.min += x; r.max += x; return r;} +internal Rng1S32 pad_1s32(Rng1S32 r, S32 x) {r.min -= x; r.max += x; return r;} +internal S32 center_1s32(Rng1S32 r) {S32 c = (r.min+r.max)/2; return c;} +internal B32 contains_1s32(Rng1S32 r, S32 x) {B32 c = (r.min <= x && x < r.max); return c;} +internal S32 dim_1s32(Rng1S32 r) {S32 c = r.max-r.min; return c;} +internal Rng1S32 union_1s32(Rng1S32 a, Rng1S32 b) {Rng1S32 c = {Min(a.min, b.min), Max(a.max, b.max)}; return c;} +internal Rng1S32 intersect_1s32(Rng1S32 a, Rng1S32 b) {Rng1S32 c = {Max(a.min, b.min), Min(a.max, b.max)}; return c;} +internal S32 clamp_1s32(Rng1S32 r, S32 v) {v = Clamp(r.min, v, r.max); return v;} + +internal Rng1U64 rng_1u64(U64 min, U64 max) {Rng1U64 r = {min, max}; if(r.min > r.max) { Swap(U64, r.min, r.max); } return r;} +internal Rng1U64 shift_1u64(Rng1U64 r, U64 x) {r.min += x; r.max += x; return r;} +internal Rng1U64 pad_1u64(Rng1U64 r, U64 x) {r.min -= x; r.max += x; return r;} +internal U64 center_1u64(Rng1U64 r) {U64 c = (r.min+r.max)/2; return c;} +internal B32 contains_1u64(Rng1U64 r, U64 x) {B32 c = (r.min <= x && x < r.max); return c;} +internal U64 dim_1u64(Rng1U64 r) {U64 c = r.max-r.min; return c;} +internal Rng1U64 union_1u64(Rng1U64 a, Rng1U64 b) {Rng1U64 c = {Min(a.min, b.min), Max(a.max, b.max)}; return c;} +internal Rng1U64 intersect_1u64(Rng1U64 a, Rng1U64 b) {Rng1U64 c = {Max(a.min, b.min), Min(a.max, b.max)}; return c;} +internal U64 clamp_1u64(Rng1U64 r, U64 v) {v = Clamp(r.min, v, r.max); return v;} + +internal Rng1S64 rng_1s64(S64 min, S64 max) {Rng1S64 r = {min, max}; if(r.min > r.max) { Swap(S64, r.min, r.max); } return r;} +internal Rng1S64 shift_1s64(Rng1S64 r, S64 x) {r.min += x; r.max += x; return r;} +internal Rng1S64 pad_1s64(Rng1S64 r, S64 x) {r.min -= x; r.max += x; return r;} +internal S64 center_1s64(Rng1S64 r) {S64 c = (r.min+r.max)/2; return c;} +internal B32 contains_1s64(Rng1S64 r, S64 x) {B32 c = (r.min <= x && x < r.max); return c;} +internal S64 dim_1s64(Rng1S64 r) {S64 c = r.max-r.min; return c;} +internal Rng1S64 union_1s64(Rng1S64 a, Rng1S64 b) {Rng1S64 c = {Min(a.min, b.min), Max(a.max, b.max)}; return c;} +internal Rng1S64 intersect_1s64(Rng1S64 a, Rng1S64 b) {Rng1S64 c = {Max(a.min, b.min), Min(a.max, b.max)}; return c;} +internal S64 clamp_1s64(Rng1S64 r, S64 v) {v = Clamp(r.min, v, r.max); return v;} + +internal Rng1F32 rng_1f32(F32 min, F32 max) {Rng1F32 r = {min, max}; if(r.min > r.max) { Swap(F32, r.min, r.max); } return r;} +internal Rng1F32 shift_1f32(Rng1F32 r, F32 x) {r.min += x; r.max += x; return r;} +internal Rng1F32 pad_1f32(Rng1F32 r, F32 x) {r.min -= x; r.max += x; return r;} +internal F32 center_1f32(Rng1F32 r) {F32 c = (r.min+r.max)/2; return c;} +internal B32 contains_1f32(Rng1F32 r, F32 x) {B32 c = (r.min <= x && x < r.max); return c;} +internal F32 dim_1f32(Rng1F32 r) {F32 c = r.max-r.min; return c;} +internal Rng1F32 union_1f32(Rng1F32 a, Rng1F32 b) {Rng1F32 c = {Min(a.min, b.min), Max(a.max, b.max)}; return c;} +internal Rng1F32 intersect_1f32(Rng1F32 a, Rng1F32 b) {Rng1F32 c = {Max(a.min, b.min), Min(a.max, b.max)}; return c;} +internal F32 clamp_1f32(Rng1F32 r, F32 v) {v = Clamp(r.min, v, r.max); return v;} + +internal Rng2S16 rng_2s16(Vec2S16 min, Vec2S16 max) {Rng2S16 r = {min, max}; return r;} +internal Rng2S16 shift_2s16(Rng2S16 r, Vec2S16 x) {r.min = add_2s16(r.min, x); r.max = add_2s16(r.max, x); return r;} +internal Rng2S16 pad_2s16(Rng2S16 r, S16 x) {Vec2S16 xv = {x, x}; r.min = sub_2s16(r.min, xv); r.max = add_2s16(r.max, xv); return r;} +internal Vec2S16 center_2s16(Rng2S16 r) {Vec2S16 c = {(S16)((r.min.x+r.max.x)/2), (S16)((r.min.y+r.max.y)/2)}; return c;} +internal B32 contains_2s16(Rng2S16 r, Vec2S16 x) {B32 c = (r.min.x <= x.x && x.x < r.max.x && r.min.y <= x.y && x.y < r.max.y); return c;} +internal Vec2S16 dim_2s16(Rng2S16 r) {Vec2S16 dim = {(S16)(r.max.x-r.min.x), (S16)(r.max.y-r.min.y)}; return dim;} +internal Rng2S16 union_2s16(Rng2S16 a, Rng2S16 b) {Rng2S16 c; c.p0.x = Min(a.min.x, b.min.x); c.p0.y = Min(a.min.y, b.min.y); c.p1.x = Max(a.max.x, b.max.x); c.p1.y = Max(a.max.y, b.max.y); return c;} +internal Rng2S16 intersect_2s16(Rng2S16 a, Rng2S16 b) {Rng2S16 c; c.p0.x = Max(a.min.x, b.min.x); c.p0.y = Max(a.min.y, b.min.y); c.p1.x = Min(a.max.x, b.max.x); c.p1.y = Min(a.max.y, b.max.y); return c;} +internal Vec2S16 clamp_2s16(Rng2S16 r, Vec2S16 v) {v.x = Clamp(r.min.x, v.x, r.max.x); v.y = Clamp(r.min.y, v.y, r.max.y); return v;} + +internal Rng2S32 rng_2s32(Vec2S32 min, Vec2S32 max) {Rng2S32 r = {min, max}; return r;} +internal Rng2S32 shift_2s32(Rng2S32 r, Vec2S32 x) {r.min = add_2s32(r.min, x); r.max = add_2s32(r.max, x); return r;} +internal Rng2S32 pad_2s32(Rng2S32 r, S32 x) {Vec2S32 xv = {x, x}; r.min = sub_2s32(r.min, xv); r.max = add_2s32(r.max, xv); return r;} +internal Vec2S32 center_2s32(Rng2S32 r) {Vec2S32 c = {(r.min.x+r.max.x)/2, (r.min.y+r.max.y)/2}; return c;} +internal B32 contains_2s32(Rng2S32 r, Vec2S32 x) {B32 c = (r.min.x <= x.x && x.x < r.max.x && r.min.y <= x.y && x.y < r.max.y); return c;} +internal Vec2S32 dim_2s32(Rng2S32 r) {Vec2S32 dim = {r.max.x-r.min.x, r.max.y-r.min.y}; return dim;} +internal Rng2S32 union_2s32(Rng2S32 a, Rng2S32 b) {Rng2S32 c; c.p0.x = Min(a.min.x, b.min.x); c.p0.y = Min(a.min.y, b.min.y); c.p1.x = Max(a.max.x, b.max.x); c.p1.y = Max(a.max.y, b.max.y); return c;} +internal Rng2S32 intersect_2s32(Rng2S32 a, Rng2S32 b) {Rng2S32 c; c.p0.x = Max(a.min.x, b.min.x); c.p0.y = Max(a.min.y, b.min.y); c.p1.x = Min(a.max.x, b.max.x); c.p1.y = Min(a.max.y, b.max.y); return c;} +internal Vec2S32 clamp_2s32(Rng2S32 r, Vec2S32 v) {v.x = Clamp(r.min.x, v.x, r.max.x); v.y = Clamp(r.min.y, v.y, r.max.y); return v;} + +internal Rng2S64 rng_2s64(Vec2S64 min, Vec2S64 max) {Rng2S64 r = {min, max}; return r;} +internal Rng2S64 shift_2s64(Rng2S64 r, Vec2S64 x) {r.min = add_2s64(r.min, x); r.max = add_2s64(r.max, x); return r;} +internal Rng2S64 pad_2s64(Rng2S64 r, S64 x) {Vec2S64 xv = {x, x}; r.min = sub_2s64(r.min, xv); r.max = add_2s64(r.max, xv); return r;} +internal Vec2S64 center_2s64(Rng2S64 r) {Vec2S64 c = {(r.min.x+r.max.x)/2, (r.min.y+r.max.y)/2}; return c;} +internal B32 contains_2s64(Rng2S64 r, Vec2S64 x) {B32 c = (r.min.x <= x.x && x.x < r.max.x && r.min.y <= x.y && x.y < r.max.y); return c;} +internal Vec2S64 dim_2s64(Rng2S64 r) {Vec2S64 dim = {r.max.x-r.min.x, r.max.y-r.min.y}; return dim;} +internal Rng2S64 union_2s64(Rng2S64 a, Rng2S64 b) {Rng2S64 c; c.p0.x = Min(a.min.x, b.min.x); c.p0.y = Min(a.min.y, b.min.y); c.p1.x = Max(a.max.x, b.max.x); c.p1.y = Max(a.max.y, b.max.y); return c;} +internal Rng2S64 intersect_2s64(Rng2S64 a, Rng2S64 b) {Rng2S64 c; c.p0.x = Max(a.min.x, b.min.x); c.p0.y = Max(a.min.y, b.min.y); c.p1.x = Min(a.max.x, b.max.x); c.p1.y = Min(a.max.y, b.max.y); return c;} +internal Vec2S64 clamp_2s64(Rng2S64 r, Vec2S64 v) {v.x = Clamp(r.min.x, v.x, r.max.x); v.y = Clamp(r.min.y, v.y, r.max.y); return v;} + +internal Rng2F32 rng_2f32(Vec2F32 min, Vec2F32 max) {Rng2F32 r = {min, max}; return r;} +internal Rng2F32 shift_2f32(Rng2F32 r, Vec2F32 x) {r.min = add_2f32(r.min, x); r.max = add_2f32(r.max, x); return r;} +internal Rng2F32 pad_2f32(Rng2F32 r, F32 x) {Vec2F32 xv = {x, x}; r.min = sub_2f32(r.min, xv); r.max = add_2f32(r.max, xv); return r;} +internal Vec2F32 center_2f32(Rng2F32 r) {Vec2F32 c = {(r.min.x+r.max.x)/2, (r.min.y+r.max.y)/2}; return c;} +internal B32 contains_2f32(Rng2F32 r, Vec2F32 x) {B32 c = (r.min.x <= x.x && x.x < r.max.x && r.min.y <= x.y && x.y < r.max.y); return c;} +internal Vec2F32 dim_2f32(Rng2F32 r) {Vec2F32 dim = {r.max.x-r.min.x, r.max.y-r.min.y}; return dim;} +internal Rng2F32 union_2f32(Rng2F32 a, Rng2F32 b) {Rng2F32 c; c.p0.x = Min(a.min.x, b.min.x); c.p0.y = Min(a.min.y, b.min.y); c.p1.x = Max(a.max.x, b.max.x); c.p1.y = Max(a.max.y, b.max.y); return c;} +internal Rng2F32 intersect_2f32(Rng2F32 a, Rng2F32 b) {Rng2F32 c; c.p0.x = Max(a.min.x, b.min.x); c.p0.y = Max(a.min.y, b.min.y); c.p1.x = Min(a.max.x, b.max.x); c.p1.y = Min(a.max.y, b.max.y); return c;} +internal Vec2F32 clamp_2f32(Rng2F32 r, Vec2F32 v) {v.x = Clamp(r.min.x, v.x, r.max.x); v.y = Clamp(r.min.y, v.y, r.max.y); return v;} + +//////////////////////////////// +//~ rjf: Miscellaneous Ops + +internal Vec3F32 +hsv_from_rgb(Vec3F32 rgb) +{ + F32 c_max = Max(rgb.x, Max(rgb.y, rgb.z)); + F32 c_min = Min(rgb.x, Min(rgb.y, rgb.z)); + F32 delta = c_max - c_min; + F32 h = ((delta == 0.f) ? 0.f : + (c_max == rgb.x) ? mod_f32((rgb.y - rgb.z)/delta + 6.f, 6.f) : + (c_max == rgb.y) ? (rgb.z - rgb.x)/delta + 2.f : + (c_max == rgb.z) ? (rgb.x - rgb.y)/delta + 4.f : + 0.f); + F32 s = (c_max == 0.f) ? 0.f : (delta/c_max); + F32 v = c_max; + Vec3F32 hsv = {h/6.f, s, v}; + return hsv; +} + +internal Vec3F32 +rgb_from_hsv(Vec3F32 hsv) +{ + F32 h = mod_f32(hsv.x * 360.f, 360.f); + F32 s = hsv.y; + F32 v = hsv.z; + + F32 c = v*s; + F32 x = c*(1.f - abs_f32(mod_f32(h/60.f, 2.f) - 1.f)); + F32 m = v - c; + + F32 r = 0; + F32 g = 0; + F32 b = 0; + + if ((h >= 0.f && h < 60.f) || (h >= 360.f && h < 420.f)){ + r = c; + g = x; + b = 0; + } + else if (h >= 60.f && h < 120.f){ + r = x; + g = c; + b = 0; + } + else if (h >= 120.f && h < 180.f){ + r = 0; + g = c; + b = x; + } + else if (h >= 180.f && h < 240.f){ + r = 0; + g = x; + b = c; + } + else if (h >= 240.f && h < 300.f){ + r = x; + g = 0; + b = c; + } + else if ((h >= 300.f && h <= 360.f) || (h >= -60.f && h <= 0.f)){ + r = c; + g = 0; + b = x; + } + + Vec3F32 rgb = {r + m, g + m, b + m}; + return(rgb); +} + +internal Vec4F32 +hsva_from_rgba(Vec4F32 rgba) +{ + Vec3F32 rgb = v3f32(rgba.x, rgba.y, rgba.z); + Vec3F32 hsv = hsv_from_rgb(rgb); + Vec4F32 hsva = v4f32(hsv.x, hsv.y, hsv.z, rgba.w); + return hsva; +} + +internal Vec4F32 +rgba_from_hsva(Vec4F32 hsva) +{ + Vec3F32 hsv = v3f32(hsva.x, hsva.y, hsva.z); + Vec3F32 rgb = rgb_from_hsv(hsv); + Vec4F32 rgba = v4f32(rgb.x, rgb.y, rgb.z, hsva.w); + return rgba; +} + +internal Vec4F32 +rgba_from_u32(U32 hex) +{ + Vec4F32 result = v4f32(((hex&0xff000000)>>24)/255.f, + ((hex&0x00ff0000)>>16)/255.f, + ((hex&0x0000ff00)>> 8)/255.f, + ((hex&0x000000ff)>> 0)/255.f); + return result; +} + +internal U32 +u32_from_rgba(Vec4F32 rgba) +{ + U32 result = 0; + result |= ((U32)((U8)(rgba.x*255.f))) << 24; + result |= ((U32)((U8)(rgba.y*255.f))) << 16; + result |= ((U32)((U8)(rgba.z*255.f))) << 8; + result |= ((U32)((U8)(rgba.w*255.f))) << 0; + return result; +} + +//////////////////////////////// +//~ rjf: List Type Functions + +internal void +rng1s64_list_push(Arena *arena, Rng1S64List *list, Rng1S64 rng) +{ + Rng1S64Node *n = push_array(arena, Rng1S64Node, 1); + MemoryCopyStruct(&n->v, &rng); + SLLQueuePush(list->first, list->last, n); + list->count += 1; +} + +internal Rng1S64Array +rng1s64_array_from_list(Arena *arena, Rng1S64List *list) +{ + Rng1S64Array arr = {0}; + arr.count = list->count; + arr.v = push_array_no_zero(arena, Rng1S64, arr.count); + U64 idx = 0; + for(Rng1S64Node *n = list->first; n != 0; n = n->next) + { + arr.v[idx] = n->v; + idx += 1; + } + return arr; +} diff --git a/src/metagen/metagen_base/metagen_base_math.h b/src/metagen/metagen_base/metagen_base_math.h index b0c9d15d..645a6d31 100644 --- a/src/metagen/metagen_base/metagen_base_math.h +++ b/src/metagen/metagen_base/metagen_base_math.h @@ -1,648 +1,649 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#ifndef BASE_MATH_H -#define BASE_MATH_H - -//////////////////////////////// -//~ rjf: Vector Types - -//- rjf: 2-vectors - -typedef union Vec2F32 Vec2F32; -union Vec2F32 -{ - struct - { - F32 x; - F32 y; - }; - F32 v[2]; -}; - -typedef union Vec2S64 Vec2S64; -union Vec2S64 -{ - struct - { - S64 x; - S64 y; - }; - S64 v[2]; -}; - -typedef union Vec2S32 Vec2S32; -union Vec2S32 -{ - struct - { - S32 x; - S32 y; - }; - S32 v[2]; -}; - -typedef union Vec2S16 Vec2S16; -union Vec2S16 -{ - struct - { - S16 x; - S16 y; - }; - S16 v[2]; -}; - -//- rjf: 3-vectors - -typedef union Vec3F32 Vec3F32; -union Vec3F32 -{ - struct - { - F32 x; - F32 y; - F32 z; - }; - struct - { - Vec2F32 xy; - F32 _z0; - }; - struct - { - F32 _x0; - Vec2F32 yz; - }; - F32 v[3]; -}; - -typedef union Vec3S32 Vec3S32; -union Vec3S32 -{ - struct - { - S32 x; - S32 y; - S32 z; - }; - struct - { - Vec2S32 xy; - S32 _z0; - }; - struct - { - S32 _x0; - Vec2S32 yz; - }; - S32 v[3]; -}; - -//- rjf: 4-vectors - -typedef union Vec4F32 Vec4F32; -union Vec4F32 -{ - struct - { - F32 x; - F32 y; - F32 z; - F32 w; - }; - struct - { - Vec2F32 xy; - Vec2F32 zw; - }; - struct - { - Vec3F32 xyz; - F32 _z0; - }; - struct - { - F32 _x0; - Vec3F32 yzw; - }; - F32 v[4]; -}; - -typedef union Vec4S32 Vec4S32; -union Vec4S32 -{ - struct - { - S32 x; - S32 y; - S32 z; - S32 w; - }; - struct - { - Vec2S32 xy; - Vec2S32 zw; - }; - struct - { - Vec3S32 xyz; - S32 _z0; - }; - struct - { - S32 _x0; - Vec3S32 yzw; - }; - S32 v[4]; -}; - -//////////////////////////////// -//~ rjf: Matrix Types - -typedef struct Mat3x3F32 Mat3x3F32; -struct Mat3x3F32 -{ - F32 v[3][3]; -}; - -typedef struct Mat4x4F32 Mat4x4F32; -struct Mat4x4F32 -{ - F32 v[4][4]; -}; - -//////////////////////////////// -//~ rjf: Range Types - -//- rjf: 1-range - -typedef union Rng1U32 Rng1U32; -union Rng1U32 -{ - struct - { - U32 min; - U32 max; - }; - U32 v[2]; -}; - -typedef union Rng1S32 Rng1S32; -union Rng1S32 -{ - struct - { - S32 min; - S32 max; - }; - S32 v[2]; -}; - -typedef union Rng1U64 Rng1U64; -union Rng1U64 -{ - struct - { - U64 min; - U64 max; - }; - U64 v[2]; -}; - -typedef union Rng1S64 Rng1S64; -union Rng1S64 -{ - struct - { - S64 min; - S64 max; - }; - S64 v[2]; -}; - -typedef union Rng1F32 Rng1F32; -union Rng1F32 -{ - struct - { - F32 min; - F32 max; - }; - F32 v[2]; -}; - -//- rjf: 2-range (rectangles) - -typedef union Rng2S16 Rng2S16; -union Rng2S16 -{ - struct - { - Vec2S16 min; - Vec2S16 max; - }; - struct - { - Vec2S16 p0; - Vec2S16 p1; - }; - struct - { - S16 x0; - S16 y0; - S16 x1; - S16 y1; - }; - Vec2S16 v[2]; -}; - -typedef union Rng2S32 Rng2S32; -union Rng2S32 -{ - struct - { - Vec2S32 min; - Vec2S32 max; - }; - struct - { - Vec2S32 p0; - Vec2S32 p1; - }; - struct - { - S32 x0; - S32 y0; - S32 x1; - S32 y1; - }; - Vec2S32 v[2]; -}; - -typedef union Rng2F32 Rng2F32; -union Rng2F32 -{ - struct - { - Vec2F32 min; - Vec2F32 max; - }; - struct - { - Vec2F32 p0; - Vec2F32 p1; - }; - struct - { - F32 x0; - F32 y0; - F32 x1; - F32 y1; - }; - Vec2F32 v[2]; -}; - -typedef union Rng2S64 Rng2S64; -union Rng2S64 -{ - struct - { - Vec2S64 min; - Vec2S64 max; - }; - struct - { - Vec2S64 p0; - Vec2S64 p1; - }; - struct - { - S64 x0; - S64 y0; - S64 x1; - S64 y1; - }; - Vec2S64 v[2]; -}; - -//////////////////////////////// -//~ rjf: List Types - -typedef struct Rng1S64Node Rng1S64Node; -struct Rng1S64Node -{ - Rng1S64Node *next; - Rng1S64 v; -}; - -typedef struct Rng1S64List Rng1S64List; -struct Rng1S64List -{ - Rng1S64Node *first; - Rng1S64Node *last; - U64 count; -}; - -typedef struct Rng1S64Array Rng1S64Array; -struct Rng1S64Array -{ - Rng1S64 *v; - U64 count; -}; - -//////////////////////////////// -//~ rjf: Scalar Ops - -#define abs_s64(v) (S64)llabs(v) - -#define sqrt_f32(v) sqrtf(v) -#define mod_f32(a, b) fmodf((a), (b)) -#define pow_f32(b, e) powf((b), (e)) -#define ceil_f32(v) ceilf(v) -#define floor_f32(v) floorf(v) -#define round_f32(v) roundf(v) -#define abs_f32(v) fabsf(v) -#define radians_from_turns_f32(v) ((v)*2*3.1415926535897f) -#define turns_from_radians_f32(v) ((v)/2*3.1415926535897f) -#define degrees_from_turns_f32(v) ((v)*360.f) -#define turns_from_degrees_f32(v) ((v)/360.f) -#define degrees_from_radians_f32(v) (degrees_from_turns_f32(turns_from_radians_f32(v))) -#define radians_from_degrees_f32(v) (radians_from_turns_f32(turns_from_degrees_f32(v))) -#define sin_f32(v) sinf(radians_from_turns_f32(v)) -#define cos_f32(v) cosf(radians_from_turns_f32(v)) -#define tan_f32(v) tanf(radians_from_turns_f32(v)) - -#define sqrt_f64(v) sqrt(v) -#define mod_f64(a, b) fmod((a), (b)) -#define pow_f64(b, e) pow((b), (e)) -#define ceil_f64(v) ceil(v) -#define floor_f64(v) floor(v) -#define round_f64(v) round(v) -#define abs_f64(v) fabs(v) -#define radians_from_turns_f64(v) ((v)*2*3.1415926535897) -#define turns_from_radians_f64(v) ((v)/2*3.1415926535897) -#define degrees_from_turns_f64(v) ((v)*360.0) -#define turns_from_degrees_f64(v) ((v)/360.0) -#define degrees_from_radians_f64(v) (degrees_from_turns_f64(turns_from_radians_f64(v))) -#define radians_from_degrees_f64(v) (radians_from_turns_f64(turns_from_degrees_f64(v))) -#define sin_f64(v) sin(radians_from_turns_f64(v)) -#define cos_f64(v) cos(radians_from_turns_f64(v)) -#define tan_f64(v) tan(radians_from_turns_f64(v)) - -internal F32 mix_1f32(F32 a, F32 b, F32 t); -internal F64 mix_1f64(F64 a, F64 b, F64 t); - -//////////////////////////////// -//~ rjf: Vector Ops - -#define v2f32(x, y) vec_2f32((x), (y)) -internal Vec2F32 vec_2f32(F32 x, F32 y); -internal Vec2F32 add_2f32(Vec2F32 a, Vec2F32 b); -internal Vec2F32 sub_2f32(Vec2F32 a, Vec2F32 b); -internal Vec2F32 mul_2f32(Vec2F32 a, Vec2F32 b); -internal Vec2F32 div_2f32(Vec2F32 a, Vec2F32 b); -internal Vec2F32 scale_2f32(Vec2F32 v, F32 s); -internal F32 dot_2f32(Vec2F32 a, Vec2F32 b); -internal F32 length_squared_2f32(Vec2F32 v); -internal F32 length_2f32(Vec2F32 v); -internal Vec2F32 normalize_2f32(Vec2F32 v); -internal Vec2F32 mix_2f32(Vec2F32 a, Vec2F32 b, F32 t); - -#define v2s64(x, y) vec_2s64((x), (y)) -internal Vec2S64 vec_2s64(S64 x, S64 y); -internal Vec2S64 add_2s64(Vec2S64 a, Vec2S64 b); -internal Vec2S64 sub_2s64(Vec2S64 a, Vec2S64 b); -internal Vec2S64 mul_2s64(Vec2S64 a, Vec2S64 b); -internal Vec2S64 div_2s64(Vec2S64 a, Vec2S64 b); -internal Vec2S64 scale_2s64(Vec2S64 v, S64 s); -internal S64 dot_2s64(Vec2S64 a, Vec2S64 b); -internal S64 length_squared_2s64(Vec2S64 v); -internal S64 length_2s64(Vec2S64 v); -internal Vec2S64 normalize_2s64(Vec2S64 v); -internal Vec2S64 mix_2s64(Vec2S64 a, Vec2S64 b, F32 t); - -#define v2s32(x, y) vec_2s32((x), (y)) -internal Vec2S32 vec_2s32(S32 x, S32 y); -internal Vec2S32 add_2s32(Vec2S32 a, Vec2S32 b); -internal Vec2S32 sub_2s32(Vec2S32 a, Vec2S32 b); -internal Vec2S32 mul_2s32(Vec2S32 a, Vec2S32 b); -internal Vec2S32 div_2s32(Vec2S32 a, Vec2S32 b); -internal Vec2S32 scale_2s32(Vec2S32 v, S32 s); -internal S32 dot_2s32(Vec2S32 a, Vec2S32 b); -internal S32 length_squared_2s32(Vec2S32 v); -internal S32 length_2s32(Vec2S32 v); -internal Vec2S32 normalize_2s32(Vec2S32 v); -internal Vec2S32 mix_2s32(Vec2S32 a, Vec2S32 b, F32 t); - -#define v2s16(x, y) vec_2s16((x), (y)) -internal Vec2S16 vec_2s16(S16 x, S16 y); -internal Vec2S16 add_2s16(Vec2S16 a, Vec2S16 b); -internal Vec2S16 sub_2s16(Vec2S16 a, Vec2S16 b); -internal Vec2S16 mul_2s16(Vec2S16 a, Vec2S16 b); -internal Vec2S16 div_2s16(Vec2S16 a, Vec2S16 b); -internal Vec2S16 scale_2s16(Vec2S16 v, S16 s); -internal S16 dot_2s16(Vec2S16 a, Vec2S16 b); -internal S16 length_squared_2s16(Vec2S16 v); -internal S16 length_2s16(Vec2S16 v); -internal Vec2S16 normalize_2s16(Vec2S16 v); -internal Vec2S16 mix_2s16(Vec2S16 a, Vec2S16 b, F32 t); - -#define v3f32(x, y, z) vec_3f32((x), (y), (z)) -internal Vec3F32 vec_3f32(F32 x, F32 y, F32 z); -internal Vec3F32 add_3f32(Vec3F32 a, Vec3F32 b); -internal Vec3F32 sub_3f32(Vec3F32 a, Vec3F32 b); -internal Vec3F32 mul_3f32(Vec3F32 a, Vec3F32 b); -internal Vec3F32 div_3f32(Vec3F32 a, Vec3F32 b); -internal Vec3F32 scale_3f32(Vec3F32 v, F32 s); -internal F32 dot_3f32(Vec3F32 a, Vec3F32 b); -internal F32 length_squared_3f32(Vec3F32 v); -internal F32 length_3f32(Vec3F32 v); -internal Vec3F32 normalize_3f32(Vec3F32 v); -internal Vec3F32 mix_3f32(Vec3F32 a, Vec3F32 b, F32 t); -internal Vec3F32 cross_3f32(Vec3F32 a, Vec3F32 b); - -#define v3s32(x, y, z) vec_3s32((x), (y), (z)) -internal Vec3S32 vec_3s32(S32 x, S32 y, S32 z); -internal Vec3S32 add_3s32(Vec3S32 a, Vec3S32 b); -internal Vec3S32 sub_3s32(Vec3S32 a, Vec3S32 b); -internal Vec3S32 mul_3s32(Vec3S32 a, Vec3S32 b); -internal Vec3S32 div_3s32(Vec3S32 a, Vec3S32 b); -internal Vec3S32 scale_3s32(Vec3S32 v, S32 s); -internal S32 dot_3s32(Vec3S32 a, Vec3S32 b); -internal S32 length_squared_3s32(Vec3S32 v); -internal S32 length_3s32(Vec3S32 v); -internal Vec3S32 normalize_3s32(Vec3S32 v); -internal Vec3S32 mix_3s32(Vec3S32 a, Vec3S32 b, F32 t); -internal Vec3S32 cross_3s32(Vec3S32 a, Vec3S32 b); - -#define v4f32(x, y, z, w) vec_4f32((x), (y), (z), (w)) -internal Vec4F32 vec_4f32(F32 x, F32 y, F32 z, F32 w); -internal Vec4F32 add_4f32(Vec4F32 a, Vec4F32 b); -internal Vec4F32 sub_4f32(Vec4F32 a, Vec4F32 b); -internal Vec4F32 mul_4f32(Vec4F32 a, Vec4F32 b); -internal Vec4F32 div_4f32(Vec4F32 a, Vec4F32 b); -internal Vec4F32 scale_4f32(Vec4F32 v, F32 s); -internal F32 dot_4f32(Vec4F32 a, Vec4F32 b); -internal F32 length_squared_4f32(Vec4F32 v); -internal F32 length_4f32(Vec4F32 v); -internal Vec4F32 normalize_4f32(Vec4F32 v); -internal Vec4F32 mix_4f32(Vec4F32 a, Vec4F32 b, F32 t); - -#define v4s32(x, y, z, w) vec_4s32((x), (y), (z), (w)) -internal Vec4S32 vec_4s32(S32 x, S32 y, S32 z, S32 w); -internal Vec4S32 add_4s32(Vec4S32 a, Vec4S32 b); -internal Vec4S32 sub_4s32(Vec4S32 a, Vec4S32 b); -internal Vec4S32 mul_4s32(Vec4S32 a, Vec4S32 b); -internal Vec4S32 div_4s32(Vec4S32 a, Vec4S32 b); -internal Vec4S32 scale_4s32(Vec4S32 v, S32 s); -internal S32 dot_4s32(Vec4S32 a, Vec4S32 b); -internal S32 length_squared_4s32(Vec4S32 v); -internal S32 length_4s32(Vec4S32 v); -internal Vec4S32 normalize_4s32(Vec4S32 v); -internal Vec4S32 mix_4s32(Vec4S32 a, Vec4S32 b, F32 t); - -//////////////////////////////// -//~ rjf: Matrix Ops - -internal Mat3x3F32 mat_3x3f32(F32 diagonal); -internal Mat3x3F32 make_translate_3x3f32(Vec2F32 delta); -internal Mat3x3F32 mul_3x3f32(Mat3x3F32 a, Mat3x3F32 b); - -internal Mat4x4F32 mat_4x4f32(F32 diagonal); -internal Mat4x4F32 make_translate_4x4f32(Vec3F32 delta); -internal Mat4x4F32 make_scale_4x4f32(Vec3F32 scale); -internal Mat4x4F32 make_perspective_4x4f32(F32 fov, F32 aspect_ratio, F32 near_z, F32 far_z); -internal Mat4x4F32 make_orthographic_4x4f32(F32 left, F32 right, F32 bottom, F32 top, F32 near_z, F32 far_z); -internal Mat4x4F32 make_look_at_4x4f32(Vec3F32 eye, Vec3F32 center, Vec3F32 up); -internal Mat4x4F32 make_rotate_4x4f32(Vec3F32 axis, F32 turns); -internal Mat4x4F32 mul_4x4f32(Mat4x4F32 a, Mat4x4F32 b); -internal Mat4x4F32 scale_4x4f32(Mat4x4F32 m, F32 scale); -internal Mat4x4F32 inverse_4x4f32(Mat4x4F32 m); -internal Mat4x4F32 derotate_4x4f32(Mat4x4F32 mat); - -//////////////////////////////// -//~ rjf: Range Ops - -#define r1u32(min, max) rng_1u32((min), (max)) -internal Rng1U32 rng_1u32(U32 min, U32 max); -internal Rng1U32 shift_1u32(Rng1U32 r, U32 x); -internal Rng1U32 pad_1u32(Rng1U32 r, U32 x); -internal U32 center_1u32(Rng1U32 r); -internal B32 contains_1u32(Rng1U32 r, U32 x); -internal U32 dim_1u32(Rng1U32 r); -internal Rng1U32 union_1u32(Rng1U32 a, Rng1U32 b); -internal Rng1U32 intersect_1u32(Rng1U32 a, Rng1U32 b); -internal U32 clamp_1u32(Rng1U32 r, U32 v); - -#define r1s32(min, max) rng_1s32((min), (max)) -internal Rng1S32 rng_1s32(S32 min, S32 max); -internal Rng1S32 shift_1s32(Rng1S32 r, S32 x); -internal Rng1S32 pad_1s32(Rng1S32 r, S32 x); -internal S32 center_1s32(Rng1S32 r); -internal B32 contains_1s32(Rng1S32 r, S32 x); -internal S32 dim_1s32(Rng1S32 r); -internal Rng1S32 union_1s32(Rng1S32 a, Rng1S32 b); -internal Rng1S32 intersect_1s32(Rng1S32 a, Rng1S32 b); -internal S32 clamp_1s32(Rng1S32 r, S32 v); - -#define r1u64(min, max) rng_1u64((min), (max)) -internal Rng1U64 rng_1u64(U64 min, U64 max); -internal Rng1U64 shift_1u64(Rng1U64 r, U64 x); -internal Rng1U64 pad_1u64(Rng1U64 r, U64 x); -internal U64 center_1u64(Rng1U64 r); -internal B32 contains_1u64(Rng1U64 r, U64 x); -internal U64 dim_1u64(Rng1U64 r); -internal Rng1U64 union_1u64(Rng1U64 a, Rng1U64 b); -internal Rng1U64 intersect_1u64(Rng1U64 a, Rng1U64 b); -internal U64 clamp_1u64(Rng1U64 r, U64 v); - -#define r1s64(min, max) rng_1s64((min), (max)) -internal Rng1S64 rng_1s64(S64 min, S64 max); -internal Rng1S64 shift_1s64(Rng1S64 r, S64 x); -internal Rng1S64 pad_1s64(Rng1S64 r, S64 x); -internal S64 center_1s64(Rng1S64 r); -internal B32 contains_1s64(Rng1S64 r, S64 x); -internal S64 dim_1s64(Rng1S64 r); -internal Rng1S64 union_1s64(Rng1S64 a, Rng1S64 b); -internal Rng1S64 intersect_1s64(Rng1S64 a, Rng1S64 b); -internal S64 clamp_1s64(Rng1S64 r, S64 v); - -#define r1f32(min, max) rng_1f32((min), (max)) -internal Rng1F32 rng_1f32(F32 min, F32 max); -internal Rng1F32 shift_1f32(Rng1F32 r, F32 x); -internal Rng1F32 pad_1f32(Rng1F32 r, F32 x); -internal F32 center_1f32(Rng1F32 r); -internal B32 contains_1f32(Rng1F32 r, F32 x); -internal F32 dim_1f32(Rng1F32 r); -internal Rng1F32 union_1f32(Rng1F32 a, Rng1F32 b); -internal Rng1F32 intersect_1f32(Rng1F32 a, Rng1F32 b); -internal F32 clamp_1f32(Rng1F32 r, F32 v); - -#define r2s16(min, max) rng_2s16((min), (max)) -#define r2s16p(x, y, z, w) r2s16(v2s16((x), (y)), v2s16((z), (w))) -internal Rng2S16 rng_2s16(Vec2S16 min, Vec2S16 max); -internal Rng2S16 shift_2s16(Rng2S16 r, Vec2S16 x); -internal Rng2S16 pad_2s16(Rng2S16 r, S16 x); -internal Vec2S16 center_2s16(Rng2S16 r); -internal B32 contains_2s16(Rng2S16 r, Vec2S16 x); -internal Vec2S16 dim_2s16(Rng2S16 r); -internal Rng2S16 union_2s16(Rng2S16 a, Rng2S16 b); -internal Rng2S16 intersect_2s16(Rng2S16 a, Rng2S16 b); -internal Vec2S16 clamp_2s16(Rng2S16 r, Vec2S16 v); - -#define r2s32(min, max) rng_2s32((min), (max)) -#define r2s32p(x, y, z, w) r2s32(v2s32((x), (y)), v2s32((z), (w))) -internal Rng2S32 rng_2s32(Vec2S32 min, Vec2S32 max); -internal Rng2S32 shift_2s32(Rng2S32 r, Vec2S32 x); -internal Rng2S32 pad_2s32(Rng2S32 r, S32 x); -internal Vec2S32 center_2s32(Rng2S32 r); -internal B32 contains_2s32(Rng2S32 r, Vec2S32 x); -internal Vec2S32 dim_2s32(Rng2S32 r); -internal Rng2S32 union_2s32(Rng2S32 a, Rng2S32 b); -internal Rng2S32 intersect_2s32(Rng2S32 a, Rng2S32 b); -internal Vec2S32 clamp_2s32(Rng2S32 r, Vec2S32 v); - -#define r2s64(min, max) rng_2s64((min), (max)) -#define r2s64p(x, y, z, w) r2s64(v2s64((x), (y)), v2s64((z), (w))) -internal Rng2S64 rng_2s64(Vec2S64 min, Vec2S64 max); -internal Rng2S64 shift_2s64(Rng2S64 r, Vec2S64 x); -internal Rng2S64 pad_2s64(Rng2S64 r, S64 x); -internal Vec2S64 center_2s64(Rng2S64 r); -internal B32 contains_2s64(Rng2S64 r, Vec2S64 x); -internal Vec2S64 dim_2s64(Rng2S64 r); -internal Rng2S64 union_2s64(Rng2S64 a, Rng2S64 b); -internal Rng2S64 intersect_2s64(Rng2S64 a, Rng2S64 b); -internal Vec2S64 clamp_2s64(Rng2S64 r, Vec2S64 v); - -#define r2f32(min, max) rng_2f32((min), (max)) -#define r2f32p(x, y, z, w) r2f32(v2f32((x), (y)), v2f32((z), (w))) -internal Rng2F32 rng_2f32(Vec2F32 min, Vec2F32 max); -internal Rng2F32 shift_2f32(Rng2F32 r, Vec2F32 x); -internal Rng2F32 pad_2f32(Rng2F32 r, F32 x); -internal Vec2F32 center_2f32(Rng2F32 r); -internal B32 contains_2f32(Rng2F32 r, Vec2F32 x); -internal Vec2F32 dim_2f32(Rng2F32 r); -internal Rng2F32 union_2f32(Rng2F32 a, Rng2F32 b); -internal Rng2F32 intersect_2f32(Rng2F32 a, Rng2F32 b); -internal Vec2F32 clamp_2f32(Rng2F32 r, Vec2F32 v); - -//////////////////////////////// -//~ rjf: Miscellaneous Ops - -internal Vec3F32 hsv_from_rgb(Vec3F32 rgb); -internal Vec3F32 rgb_from_hsv(Vec3F32 hsv); -internal Vec4F32 hsva_from_rgba(Vec4F32 rgba); -internal Vec4F32 rgba_from_hsva(Vec4F32 hsva); -internal Vec4F32 rgba_from_u32(U32 hex); -internal U32 u32_from_rgba(Vec4F32 rgba); - -#define rgba_from_u32_lit_comp(h) { (((h)&0xff000000)>>24)/255.f, (((h)&0x00ff0000)>>16)/255.f, (((h)&0x0000ff00)>> 8)/255.f, (((h)&0x000000ff)>> 0)/255.f } - -//////////////////////////////// -//~ rjf: List Type Functions - -internal void rng1s64_list_push(Arena *arena, Rng1S64List *list, Rng1S64 rng); -internal Rng1S64Array rng1s64_array_from_list(Arena *arena, Rng1S64List *list); - -#endif // BASE_MATH_H +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef BASE_MATH_H +#define BASE_MATH_H + +//////////////////////////////// +//~ rjf: Vector Types + +//- rjf: 2-vectors + +typedef union Vec2F32 Vec2F32; +union Vec2F32 +{ + struct + { + F32 x; + F32 y; + }; + F32 v[2]; +}; + +typedef union Vec2S64 Vec2S64; +union Vec2S64 +{ + struct + { + S64 x; + S64 y; + }; + S64 v[2]; +}; + +typedef union Vec2S32 Vec2S32; +union Vec2S32 +{ + struct + { + S32 x; + S32 y; + }; + S32 v[2]; +}; + +typedef union Vec2S16 Vec2S16; +union Vec2S16 +{ + struct + { + S16 x; + S16 y; + }; + S16 v[2]; +}; + +//- rjf: 3-vectors + +typedef union Vec3F32 Vec3F32; +union Vec3F32 +{ + struct + { + F32 x; + F32 y; + F32 z; + }; + struct + { + Vec2F32 xy; + F32 _z0; + }; + struct + { + F32 _x0; + Vec2F32 yz; + }; + F32 v[3]; +}; + +typedef union Vec3S32 Vec3S32; +union Vec3S32 +{ + struct + { + S32 x; + S32 y; + S32 z; + }; + struct + { + Vec2S32 xy; + S32 _z0; + }; + struct + { + S32 _x0; + Vec2S32 yz; + }; + S32 v[3]; +}; + +//- rjf: 4-vectors + +typedef union Vec4F32 Vec4F32; +union Vec4F32 +{ + struct + { + F32 x; + F32 y; + F32 z; + F32 w; + }; + struct + { + Vec2F32 xy; + Vec2F32 zw; + }; + struct + { + Vec3F32 xyz; + F32 _z0; + }; + struct + { + F32 _x0; + Vec3F32 yzw; + }; + F32 v[4]; +}; + +typedef union Vec4S32 Vec4S32; +union Vec4S32 +{ + struct + { + S32 x; + S32 y; + S32 z; + S32 w; + }; + struct + { + Vec2S32 xy; + Vec2S32 zw; + }; + struct + { + Vec3S32 xyz; + S32 _z0; + }; + struct + { + S32 _x0; + Vec3S32 yzw; + }; + S32 v[4]; +}; + +//////////////////////////////// +//~ rjf: Matrix Types + +typedef struct Mat3x3F32 Mat3x3F32; +struct Mat3x3F32 +{ + F32 v[3][3]; +}; + +typedef struct Mat4x4F32 Mat4x4F32; +struct Mat4x4F32 +{ + F32 v[4][4]; +}; + +//////////////////////////////// +//~ rjf: Range Types + +//- rjf: 1-range + +typedef union Rng1U32 Rng1U32; +union Rng1U32 +{ + struct + { + U32 min; + U32 max; + }; + U32 v[2]; +}; + +typedef union Rng1S32 Rng1S32; +union Rng1S32 +{ + struct + { + S32 min; + S32 max; + }; + S32 v[2]; +}; + +typedef union Rng1U64 Rng1U64; +union Rng1U64 +{ + struct + { + U64 min; + U64 max; + }; + U64 v[2]; +}; + +typedef union Rng1S64 Rng1S64; +union Rng1S64 +{ + struct + { + S64 min; + S64 max; + }; + S64 v[2]; +}; + +typedef union Rng1F32 Rng1F32; +union Rng1F32 +{ + struct + { + F32 min; + F32 max; + }; + F32 v[2]; +}; + +//- rjf: 2-range (rectangles) + +typedef union Rng2S16 Rng2S16; +union Rng2S16 +{ + struct + { + Vec2S16 min; + Vec2S16 max; + }; + struct + { + Vec2S16 p0; + Vec2S16 p1; + }; + struct + { + S16 x0; + S16 y0; + S16 x1; + S16 y1; + }; + Vec2S16 v[2]; +}; + +typedef union Rng2S32 Rng2S32; +union Rng2S32 +{ + struct + { + Vec2S32 min; + Vec2S32 max; + }; + struct + { + Vec2S32 p0; + Vec2S32 p1; + }; + struct + { + S32 x0; + S32 y0; + S32 x1; + S32 y1; + }; + Vec2S32 v[2]; +}; + +typedef union Rng2F32 Rng2F32; +union Rng2F32 +{ + struct + { + Vec2F32 min; + Vec2F32 max; + }; + struct + { + Vec2F32 p0; + Vec2F32 p1; + }; + struct + { + F32 x0; + F32 y0; + F32 x1; + F32 y1; + }; + Vec2F32 v[2]; +}; + +typedef union Rng2S64 Rng2S64; +union Rng2S64 +{ + struct + { + Vec2S64 min; + Vec2S64 max; + }; + struct + { + Vec2S64 p0; + Vec2S64 p1; + }; + struct + { + S64 x0; + S64 y0; + S64 x1; + S64 y1; + }; + Vec2S64 v[2]; +}; + +//////////////////////////////// +//~ rjf: List Types + +typedef struct Rng1S64Node Rng1S64Node; +struct Rng1S64Node +{ + Rng1S64Node *next; + Rng1S64 v; +}; + +typedef struct Rng1S64List Rng1S64List; +struct Rng1S64List +{ + Rng1S64Node *first; + Rng1S64Node *last; + U64 count; +}; + +typedef struct Rng1S64Array Rng1S64Array; +struct Rng1S64Array +{ + Rng1S64 *v; + U64 count; +}; + +//////////////////////////////// +//~ rjf: Scalar Ops + +#define abs_s64(v) (S64)llabs(v) + +#define sqrt_f32(v) sqrtf(v) +#define mod_f32(a, b) fmodf((a), (b)) +#define pow_f32(b, e) powf((b), (e)) +#define ceil_f32(v) ceilf(v) +#define floor_f32(v) floorf(v) +#define round_f32(v) roundf(v) +#define abs_f32(v) fabsf(v) +#define radians_from_turns_f32(v) ((v)*2*3.1415926535897f) +#define turns_from_radians_f32(v) ((v)/2*3.1415926535897f) +#define degrees_from_turns_f32(v) ((v)*360.f) +#define turns_from_degrees_f32(v) ((v)/360.f) +#define degrees_from_radians_f32(v) (degrees_from_turns_f32(turns_from_radians_f32(v))) +#define radians_from_degrees_f32(v) (radians_from_turns_f32(turns_from_degrees_f32(v))) +#define sin_f32(v) sinf(radians_from_turns_f32(v)) +#define cos_f32(v) cosf(radians_from_turns_f32(v)) +#define tan_f32(v) tanf(radians_from_turns_f32(v)) + +#define sqrt_f64(v) sqrt(v) +#define mod_f64(a, b) fmod((a), (b)) +#define pow_f64(b, e) pow((b), (e)) +#define ceil_f64(v) ceil(v) +#define floor_f64(v) floor(v) +#define round_f64(v) round(v) +#define abs_f64(v) fabs(v) +#define radians_from_turns_f64(v) ((v)*2*3.1415926535897) +#define turns_from_radians_f64(v) ((v)/2*3.1415926535897) +#define degrees_from_turns_f64(v) ((v)*360.0) +#define turns_from_degrees_f64(v) ((v)/360.0) +#define degrees_from_radians_f64(v) (degrees_from_turns_f64(turns_from_radians_f64(v))) +#define radians_from_degrees_f64(v) (radians_from_turns_f64(turns_from_degrees_f64(v))) +#define sin_f64(v) sin(radians_from_turns_f64(v)) +#define cos_f64(v) cos(radians_from_turns_f64(v)) +#define tan_f64(v) tan(radians_from_turns_f64(v)) + +internal F32 mix_1f32(F32 a, F32 b, F32 t); +internal F64 mix_1f64(F64 a, F64 b, F64 t); + +//////////////////////////////// +//~ rjf: Vector Ops + +#define v2f32(x, y) vec_2f32((x), (y)) +internal Vec2F32 vec_2f32(F32 x, F32 y); +internal Vec2F32 add_2f32(Vec2F32 a, Vec2F32 b); +internal Vec2F32 sub_2f32(Vec2F32 a, Vec2F32 b); +internal Vec2F32 mul_2f32(Vec2F32 a, Vec2F32 b); +internal Vec2F32 div_2f32(Vec2F32 a, Vec2F32 b); +internal Vec2F32 scale_2f32(Vec2F32 v, F32 s); +internal F32 dot_2f32(Vec2F32 a, Vec2F32 b); +internal F32 length_squared_2f32(Vec2F32 v); +internal F32 length_2f32(Vec2F32 v); +internal Vec2F32 normalize_2f32(Vec2F32 v); +internal Vec2F32 mix_2f32(Vec2F32 a, Vec2F32 b, F32 t); + +#define v2s64(x, y) vec_2s64((x), (y)) +internal Vec2S64 vec_2s64(S64 x, S64 y); +internal Vec2S64 add_2s64(Vec2S64 a, Vec2S64 b); +internal Vec2S64 sub_2s64(Vec2S64 a, Vec2S64 b); +internal Vec2S64 mul_2s64(Vec2S64 a, Vec2S64 b); +internal Vec2S64 div_2s64(Vec2S64 a, Vec2S64 b); +internal Vec2S64 scale_2s64(Vec2S64 v, S64 s); +internal S64 dot_2s64(Vec2S64 a, Vec2S64 b); +internal S64 length_squared_2s64(Vec2S64 v); +internal S64 length_2s64(Vec2S64 v); +internal Vec2S64 normalize_2s64(Vec2S64 v); +internal Vec2S64 mix_2s64(Vec2S64 a, Vec2S64 b, F32 t); + +#define v2s32(x, y) vec_2s32((x), (y)) +internal Vec2S32 vec_2s32(S32 x, S32 y); +internal Vec2S32 add_2s32(Vec2S32 a, Vec2S32 b); +internal Vec2S32 sub_2s32(Vec2S32 a, Vec2S32 b); +internal Vec2S32 mul_2s32(Vec2S32 a, Vec2S32 b); +internal Vec2S32 div_2s32(Vec2S32 a, Vec2S32 b); +internal Vec2S32 scale_2s32(Vec2S32 v, S32 s); +internal S32 dot_2s32(Vec2S32 a, Vec2S32 b); +internal S32 length_squared_2s32(Vec2S32 v); +internal S32 length_2s32(Vec2S32 v); +internal Vec2S32 normalize_2s32(Vec2S32 v); +internal Vec2S32 mix_2s32(Vec2S32 a, Vec2S32 b, F32 t); + +#define v2s16(x, y) vec_2s16((x), (y)) +internal Vec2S16 vec_2s16(S16 x, S16 y); +internal Vec2S16 add_2s16(Vec2S16 a, Vec2S16 b); +internal Vec2S16 sub_2s16(Vec2S16 a, Vec2S16 b); +internal Vec2S16 mul_2s16(Vec2S16 a, Vec2S16 b); +internal Vec2S16 div_2s16(Vec2S16 a, Vec2S16 b); +internal Vec2S16 scale_2s16(Vec2S16 v, S16 s); +internal S16 dot_2s16(Vec2S16 a, Vec2S16 b); +internal S16 length_squared_2s16(Vec2S16 v); +internal S16 length_2s16(Vec2S16 v); +internal Vec2S16 normalize_2s16(Vec2S16 v); +internal Vec2S16 mix_2s16(Vec2S16 a, Vec2S16 b, F32 t); + +#define v3f32(x, y, z) vec_3f32((x), (y), (z)) +internal Vec3F32 vec_3f32(F32 x, F32 y, F32 z); +internal Vec3F32 add_3f32(Vec3F32 a, Vec3F32 b); +internal Vec3F32 sub_3f32(Vec3F32 a, Vec3F32 b); +internal Vec3F32 mul_3f32(Vec3F32 a, Vec3F32 b); +internal Vec3F32 div_3f32(Vec3F32 a, Vec3F32 b); +internal Vec3F32 scale_3f32(Vec3F32 v, F32 s); +internal F32 dot_3f32(Vec3F32 a, Vec3F32 b); +internal F32 length_squared_3f32(Vec3F32 v); +internal F32 length_3f32(Vec3F32 v); +internal Vec3F32 normalize_3f32(Vec3F32 v); +internal Vec3F32 mix_3f32(Vec3F32 a, Vec3F32 b, F32 t); +internal Vec3F32 cross_3f32(Vec3F32 a, Vec3F32 b); + +#define v3s32(x, y, z) vec_3s32((x), (y), (z)) +internal Vec3S32 vec_3s32(S32 x, S32 y, S32 z); +internal Vec3S32 add_3s32(Vec3S32 a, Vec3S32 b); +internal Vec3S32 sub_3s32(Vec3S32 a, Vec3S32 b); +internal Vec3S32 mul_3s32(Vec3S32 a, Vec3S32 b); +internal Vec3S32 div_3s32(Vec3S32 a, Vec3S32 b); +internal Vec3S32 scale_3s32(Vec3S32 v, S32 s); +internal S32 dot_3s32(Vec3S32 a, Vec3S32 b); +internal S32 length_squared_3s32(Vec3S32 v); +internal S32 length_3s32(Vec3S32 v); +internal Vec3S32 normalize_3s32(Vec3S32 v); +internal Vec3S32 mix_3s32(Vec3S32 a, Vec3S32 b, F32 t); +internal Vec3S32 cross_3s32(Vec3S32 a, Vec3S32 b); + +#define v4f32(x, y, z, w) vec_4f32((x), (y), (z), (w)) +internal Vec4F32 vec_4f32(F32 x, F32 y, F32 z, F32 w); +internal Vec4F32 add_4f32(Vec4F32 a, Vec4F32 b); +internal Vec4F32 sub_4f32(Vec4F32 a, Vec4F32 b); +internal Vec4F32 mul_4f32(Vec4F32 a, Vec4F32 b); +internal Vec4F32 div_4f32(Vec4F32 a, Vec4F32 b); +internal Vec4F32 scale_4f32(Vec4F32 v, F32 s); +internal F32 dot_4f32(Vec4F32 a, Vec4F32 b); +internal F32 length_squared_4f32(Vec4F32 v); +internal F32 length_4f32(Vec4F32 v); +internal Vec4F32 normalize_4f32(Vec4F32 v); +internal Vec4F32 mix_4f32(Vec4F32 a, Vec4F32 b, F32 t); + +#define v4s32(x, y, z, w) vec_4s32((x), (y), (z), (w)) +internal Vec4S32 vec_4s32(S32 x, S32 y, S32 z, S32 w); +internal Vec4S32 add_4s32(Vec4S32 a, Vec4S32 b); +internal Vec4S32 sub_4s32(Vec4S32 a, Vec4S32 b); +internal Vec4S32 mul_4s32(Vec4S32 a, Vec4S32 b); +internal Vec4S32 div_4s32(Vec4S32 a, Vec4S32 b); +internal Vec4S32 scale_4s32(Vec4S32 v, S32 s); +internal S32 dot_4s32(Vec4S32 a, Vec4S32 b); +internal S32 length_squared_4s32(Vec4S32 v); +internal S32 length_4s32(Vec4S32 v); +internal Vec4S32 normalize_4s32(Vec4S32 v); +internal Vec4S32 mix_4s32(Vec4S32 a, Vec4S32 b, F32 t); + +//////////////////////////////// +//~ rjf: Matrix Ops + +internal Mat3x3F32 mat_3x3f32(F32 diagonal); +internal Mat3x3F32 make_translate_3x3f32(Vec2F32 delta); +internal Mat3x3F32 make_scale_3x3f32(Vec2F32 scale); +internal Mat3x3F32 mul_3x3f32(Mat3x3F32 a, Mat3x3F32 b); + +internal Mat4x4F32 mat_4x4f32(F32 diagonal); +internal Mat4x4F32 make_translate_4x4f32(Vec3F32 delta); +internal Mat4x4F32 make_scale_4x4f32(Vec3F32 scale); +internal Mat4x4F32 make_perspective_4x4f32(F32 fov, F32 aspect_ratio, F32 near_z, F32 far_z); +internal Mat4x4F32 make_orthographic_4x4f32(F32 left, F32 right, F32 bottom, F32 top, F32 near_z, F32 far_z); +internal Mat4x4F32 make_look_at_4x4f32(Vec3F32 eye, Vec3F32 center, Vec3F32 up); +internal Mat4x4F32 make_rotate_4x4f32(Vec3F32 axis, F32 turns); +internal Mat4x4F32 mul_4x4f32(Mat4x4F32 a, Mat4x4F32 b); +internal Mat4x4F32 scale_4x4f32(Mat4x4F32 m, F32 scale); +internal Mat4x4F32 inverse_4x4f32(Mat4x4F32 m); +internal Mat4x4F32 derotate_4x4f32(Mat4x4F32 mat); + +//////////////////////////////// +//~ rjf: Range Ops + +#define r1u32(min, max) rng_1u32((min), (max)) +internal Rng1U32 rng_1u32(U32 min, U32 max); +internal Rng1U32 shift_1u32(Rng1U32 r, U32 x); +internal Rng1U32 pad_1u32(Rng1U32 r, U32 x); +internal U32 center_1u32(Rng1U32 r); +internal B32 contains_1u32(Rng1U32 r, U32 x); +internal U32 dim_1u32(Rng1U32 r); +internal Rng1U32 union_1u32(Rng1U32 a, Rng1U32 b); +internal Rng1U32 intersect_1u32(Rng1U32 a, Rng1U32 b); +internal U32 clamp_1u32(Rng1U32 r, U32 v); + +#define r1s32(min, max) rng_1s32((min), (max)) +internal Rng1S32 rng_1s32(S32 min, S32 max); +internal Rng1S32 shift_1s32(Rng1S32 r, S32 x); +internal Rng1S32 pad_1s32(Rng1S32 r, S32 x); +internal S32 center_1s32(Rng1S32 r); +internal B32 contains_1s32(Rng1S32 r, S32 x); +internal S32 dim_1s32(Rng1S32 r); +internal Rng1S32 union_1s32(Rng1S32 a, Rng1S32 b); +internal Rng1S32 intersect_1s32(Rng1S32 a, Rng1S32 b); +internal S32 clamp_1s32(Rng1S32 r, S32 v); + +#define r1u64(min, max) rng_1u64((min), (max)) +internal Rng1U64 rng_1u64(U64 min, U64 max); +internal Rng1U64 shift_1u64(Rng1U64 r, U64 x); +internal Rng1U64 pad_1u64(Rng1U64 r, U64 x); +internal U64 center_1u64(Rng1U64 r); +internal B32 contains_1u64(Rng1U64 r, U64 x); +internal U64 dim_1u64(Rng1U64 r); +internal Rng1U64 union_1u64(Rng1U64 a, Rng1U64 b); +internal Rng1U64 intersect_1u64(Rng1U64 a, Rng1U64 b); +internal U64 clamp_1u64(Rng1U64 r, U64 v); + +#define r1s64(min, max) rng_1s64((min), (max)) +internal Rng1S64 rng_1s64(S64 min, S64 max); +internal Rng1S64 shift_1s64(Rng1S64 r, S64 x); +internal Rng1S64 pad_1s64(Rng1S64 r, S64 x); +internal S64 center_1s64(Rng1S64 r); +internal B32 contains_1s64(Rng1S64 r, S64 x); +internal S64 dim_1s64(Rng1S64 r); +internal Rng1S64 union_1s64(Rng1S64 a, Rng1S64 b); +internal Rng1S64 intersect_1s64(Rng1S64 a, Rng1S64 b); +internal S64 clamp_1s64(Rng1S64 r, S64 v); + +#define r1f32(min, max) rng_1f32((min), (max)) +internal Rng1F32 rng_1f32(F32 min, F32 max); +internal Rng1F32 shift_1f32(Rng1F32 r, F32 x); +internal Rng1F32 pad_1f32(Rng1F32 r, F32 x); +internal F32 center_1f32(Rng1F32 r); +internal B32 contains_1f32(Rng1F32 r, F32 x); +internal F32 dim_1f32(Rng1F32 r); +internal Rng1F32 union_1f32(Rng1F32 a, Rng1F32 b); +internal Rng1F32 intersect_1f32(Rng1F32 a, Rng1F32 b); +internal F32 clamp_1f32(Rng1F32 r, F32 v); + +#define r2s16(min, max) rng_2s16((min), (max)) +#define r2s16p(x, y, z, w) r2s16(v2s16((x), (y)), v2s16((z), (w))) +internal Rng2S16 rng_2s16(Vec2S16 min, Vec2S16 max); +internal Rng2S16 shift_2s16(Rng2S16 r, Vec2S16 x); +internal Rng2S16 pad_2s16(Rng2S16 r, S16 x); +internal Vec2S16 center_2s16(Rng2S16 r); +internal B32 contains_2s16(Rng2S16 r, Vec2S16 x); +internal Vec2S16 dim_2s16(Rng2S16 r); +internal Rng2S16 union_2s16(Rng2S16 a, Rng2S16 b); +internal Rng2S16 intersect_2s16(Rng2S16 a, Rng2S16 b); +internal Vec2S16 clamp_2s16(Rng2S16 r, Vec2S16 v); + +#define r2s32(min, max) rng_2s32((min), (max)) +#define r2s32p(x, y, z, w) r2s32(v2s32((x), (y)), v2s32((z), (w))) +internal Rng2S32 rng_2s32(Vec2S32 min, Vec2S32 max); +internal Rng2S32 shift_2s32(Rng2S32 r, Vec2S32 x); +internal Rng2S32 pad_2s32(Rng2S32 r, S32 x); +internal Vec2S32 center_2s32(Rng2S32 r); +internal B32 contains_2s32(Rng2S32 r, Vec2S32 x); +internal Vec2S32 dim_2s32(Rng2S32 r); +internal Rng2S32 union_2s32(Rng2S32 a, Rng2S32 b); +internal Rng2S32 intersect_2s32(Rng2S32 a, Rng2S32 b); +internal Vec2S32 clamp_2s32(Rng2S32 r, Vec2S32 v); + +#define r2s64(min, max) rng_2s64((min), (max)) +#define r2s64p(x, y, z, w) r2s64(v2s64((x), (y)), v2s64((z), (w))) +internal Rng2S64 rng_2s64(Vec2S64 min, Vec2S64 max); +internal Rng2S64 shift_2s64(Rng2S64 r, Vec2S64 x); +internal Rng2S64 pad_2s64(Rng2S64 r, S64 x); +internal Vec2S64 center_2s64(Rng2S64 r); +internal B32 contains_2s64(Rng2S64 r, Vec2S64 x); +internal Vec2S64 dim_2s64(Rng2S64 r); +internal Rng2S64 union_2s64(Rng2S64 a, Rng2S64 b); +internal Rng2S64 intersect_2s64(Rng2S64 a, Rng2S64 b); +internal Vec2S64 clamp_2s64(Rng2S64 r, Vec2S64 v); + +#define r2f32(min, max) rng_2f32((min), (max)) +#define r2f32p(x, y, z, w) r2f32(v2f32((x), (y)), v2f32((z), (w))) +internal Rng2F32 rng_2f32(Vec2F32 min, Vec2F32 max); +internal Rng2F32 shift_2f32(Rng2F32 r, Vec2F32 x); +internal Rng2F32 pad_2f32(Rng2F32 r, F32 x); +internal Vec2F32 center_2f32(Rng2F32 r); +internal B32 contains_2f32(Rng2F32 r, Vec2F32 x); +internal Vec2F32 dim_2f32(Rng2F32 r); +internal Rng2F32 union_2f32(Rng2F32 a, Rng2F32 b); +internal Rng2F32 intersect_2f32(Rng2F32 a, Rng2F32 b); +internal Vec2F32 clamp_2f32(Rng2F32 r, Vec2F32 v); + +//////////////////////////////// +//~ rjf: Miscellaneous Ops + +internal Vec3F32 hsv_from_rgb(Vec3F32 rgb); +internal Vec3F32 rgb_from_hsv(Vec3F32 hsv); +internal Vec4F32 hsva_from_rgba(Vec4F32 rgba); +internal Vec4F32 rgba_from_hsva(Vec4F32 hsva); +internal Vec4F32 rgba_from_u32(U32 hex); +internal U32 u32_from_rgba(Vec4F32 rgba); + +#define rgba_from_u32_lit_comp(h) { (((h)&0xff000000)>>24)/255.f, (((h)&0x00ff0000)>>16)/255.f, (((h)&0x0000ff00)>> 8)/255.f, (((h)&0x000000ff)>> 0)/255.f } + +//////////////////////////////// +//~ rjf: List Type Functions + +internal void rng1s64_list_push(Arena *arena, Rng1S64List *list, Rng1S64 rng); +internal Rng1S64Array rng1s64_array_from_list(Arena *arena, Rng1S64List *list); + +#endif // BASE_MATH_H diff --git a/src/metagen/metagen_base/metagen_base_profile.c b/src/metagen/metagen_base/metagen_base_profile.c new file mode 100644 index 00000000..8ac6bce5 --- /dev/null +++ b/src/metagen/metagen_base/metagen_base_profile.c @@ -0,0 +1,2 @@ +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) diff --git a/src/metagen/metagen_base/metagen_base_profile.h b/src/metagen/metagen_base/metagen_base_profile.h new file mode 100644 index 00000000..fa67b823 --- /dev/null +++ b/src/metagen/metagen_base/metagen_base_profile.h @@ -0,0 +1,74 @@ +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef BASE_PROFILE_H +#define BASE_PROFILE_H + +//////////////////////////////// +//~ 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 ProfBegin(...) tmEnter(0, 0, __VA_ARGS__) +# define ProfBeginDynamic(...) (TM_API_PTR ? TM_API_PTR->_tmEnterZoneV_Core(0, 0, __FILE__, &g_telemetry_filename_id, __LINE__, __VA_ARGS__) : (void)0) +# define ProfEnd(...) (TM_API_PTR ? TM_API_PTR->_tmLeaveZone(0) : (void)0) +# define ProfTick(...) tmTick(0) +# define ProfIsCapturing(...) tmRunning() +# define ProfBeginCapture(...) tmOpen(0, __VA_ARGS__, __DATE__, "localhost", TMCT_TCP, TELEMETRY_DEFAULT_PORT, TMOF_INIT_NETWORKING|TMOF_CAPTURE_CONTEXT_SWITCHES, 100) +# define ProfEndCapture(...) tmClose(0) +# define ProfThreadName(...) (TM_API_PTR ? TM_API_PTR->_tmThreadName(0, 0, __VA_ARGS__) : (void)0) +# define ProfMsg(...) (TM_API_PTR ? TM_API_PTR->_tmMessageV_Core(0, TMMF_ICON_NOTE, __FILE__, &g_telemetry_filename_id, __LINE__, __VA_ARGS__) : (void)0) +# define ProfBeginLockWait(...) tmStartWaitForLock(0, 0, __VA_ARGS__) +# define ProfEndLockWait(...) tmEndWaitForLock(0) +# define ProfLockTake(...) tmAcquiredLock(0, 0, __VA_ARGS__) +# define ProfLockDrop(...) tmReleasedLock(0, __VA_ARGS__) +# define ProfColor(color) tmZoneColorSticky(color) +#endif + +//////////////////////////////// +//~ rjf: Zeroify Undefined Defines + +#if !defined(ProfBegin) +# define ProfBegin(...) (0) +# define ProfBeginDynamic(...) (0) +# define ProfEnd(...) (0) +# define ProfTick(...) (0) +# define ProfIsCapturing(...) (0) +# define ProfBeginCapture(...) (0) +# define ProfEndCapture(...) (0) +# define ProfThreadName(...) (0) +# define ProfMsg(...) (0) +# define ProfBeginLockWait(...) (0) +# define ProfEndLockWait(...) (0) +# define ProfLockTake(...) (0) +# define ProfLockDrop(...) (0) +# define ProfColor(...) (0) +#endif + +//////////////////////////////// +//~ rjf: Helper Wrappers + +#define ProfBeginFunction(...) ProfBegin(this_function_name) +#define ProfScope(...) DeferLoop(ProfBeginDynamic(__VA_ARGS__), ProfEnd()) + +#endif // BASE_PROFILE_H diff --git a/src/metagen/metagen_base/metagen_base_string.c b/src/metagen/metagen_base/metagen_base_strings.c similarity index 85% rename from src/metagen/metagen_base/metagen_base_string.c rename to src/metagen/metagen_base/metagen_base_strings.c index ac974599..56f2c1b5 100644 --- a/src/metagen/metagen_base/metagen_base_string.c +++ b/src/metagen/metagen_base/metagen_base_strings.c @@ -1,1878 +1,1975 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -//////////////////////////////// -//~ rjf: Third Party Includes - -#if !SUPPLEMENT_UNIT -# define STB_SPRINTF_IMPLEMENTATION -# define STB_SPRINTF_STATIC -# include "third_party/stb/stb_sprintf.h" -#endif - -//////////////////////////////// -//~ NOTE(allen): String <-> Integer Tables - -read_only global U8 integer_symbols[16] = { - '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F', -}; - -// NOTE(allen): Includes reverses for uppercase and lowercase hex. -read_only global U8 integer_symbol_reverse[128] = { - 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, - 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, - 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, - 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, - 0xFF,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, - 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, - 0xFF,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, - 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, -}; - -read_only global U8 base64[64] = { - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', - 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', - 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', - 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', - '_', '$', -}; - -read_only global U8 base64_reverse[128] = { - 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, - 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, - 0xFF,0xFF,0xFF,0xFF,0x3F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, - 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0xFF,0xFF,0xFF,0xFF,0xFF,0x00, - 0xFF,0x24,0x25,0x26,0x27,0x28,0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F,0x30,0x31,0x32, - 0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3A,0x3B,0x3C,0x3D,0xFF,0xFF,0xFF,0xFF,0x3E, - 0xFF,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18, - 0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,0x20,0x21,0x22,0x23,0xFF,0xFF,0xFF,0xFF,0xFF, -}; - -//////////////////////////////// -//~ rjf: Character Classification & Conversion Functions - -internal B32 -char_is_space(U8 c){ - return(c == ' ' || c == '\n' || c == '\t' || c == '\r' || c == '\f' || c == '\v'); -} - -internal B32 -char_is_upper(U8 c){ - return('A' <= c && c <= 'Z'); -} - -internal B32 -char_is_lower(U8 c){ - return('a' <= c && c <= 'z'); -} - -internal B32 -char_is_alpha(U8 c){ - return(char_is_upper(c) || char_is_lower(c)); -} - -internal B32 -char_is_slash(U8 c){ - return(c == '/' || c == '\\'); -} - -internal B32 -char_is_digit(U8 c, U32 base){ - B32 result = 0; - if (0 < base && base <= 16){ - U8 val = integer_symbol_reverse[c]; - if (val < base){ - result = 1; - } - } - return(result); -} - -internal U8 -char_to_lower(U8 c){ - if (char_is_upper(c)){ - c += ('a' - 'A'); - } - return(c); -} - -internal U8 -char_to_upper(U8 c){ - if (char_is_lower(c)){ - c += ('A' - 'a'); - } - return(c); -} - -internal U8 -char_to_correct_slash(U8 c){ - if(char_is_slash(c)){ - c = '/'; - } - return(c); -} - -//////////////////////////////// -//~ rjf: C-String Measurement - -internal U64 -cstring8_length(U8 *c){ - U8 *p = c; - for (;*p != 0; p += 1); - return(p - c); -} - -internal U64 -cstring16_length(U16 *c){ - U16 *p = c; - for (;*p != 0; p += 1); - return(p - c); -} - -internal U64 -cstring32_length(U32 *c){ - U32 *p = c; - for (;*p != 0; p += 1); - return(p - c); -} - -//////////////////////////////// -//~ rjf: String Constructors - -internal String8 -str8(U8 *str, U64 size){ - String8 result = {str, size}; - return(result); -} - -internal String8 -str8_range(U8 *first, U8 *one_past_last){ - String8 result = {first, (U64)(one_past_last - first)}; - return(result); -} - -internal String8 -str8_zero(void){ - String8 result = {0}; - return(result); -} - -internal String16 -str16(U16 *str, U64 size){ - String16 result = {str, size}; - return(result); -} - -internal String16 -str16_range(U16 *first, U16 *one_past_last){ - String16 result = {first, (U64)(one_past_last - first)}; - return(result); -} - -internal String16 -str16_zero(void){ - String16 result = {0}; - return(result); -} - -internal String32 -str32(U32 *str, U64 size){ - String32 result = {str, size}; - return(result); -} - -internal String32 -str32_range(U32 *first, U32 *one_past_last){ - String32 result = {first, (U64)(one_past_last - first)}; - return(result); -} - -internal String32 -str32_zero(void){ - String32 result = {0}; - return(result); -} - -internal String8 -str8_cstring(char *c){ - String8 result = {(U8*)c, cstring8_length((U8*)c)}; - return(result); -} - -internal String16 -str16_cstring(U16 *c){ - String16 result = {(U16*)c, cstring16_length((U16*)c)}; - return(result); -} - -internal String32 -str32_cstring(U32 *c){ - String32 result = {(U32*)c, cstring32_length((U32*)c)}; - return(result); -} - -internal String8 -str8_cstring_capped(void *cstr, void *cap) -{ - char *ptr = (char*)cstr; - char *opl = (char*)cap; - for (;ptr < opl && *ptr != 0; ptr += 1); - U64 size = (U64)(ptr - (char *)cstr); - String8 result = {(U8*)cstr, size}; - return(result); -} - -//////////////////////////////// -//~ rjf: String Stylization - -internal String8 -upper_from_str8(Arena *arena, String8 string) -{ - string = push_str8_copy(arena, string); - for(U64 idx = 0; idx < string.size; idx += 1) - { - string.str[idx] = char_to_upper(string.str[idx]); - } - return string; -} - -internal String8 -lower_from_str8(Arena *arena, String8 string) -{ - string = push_str8_copy(arena, string); - for(U64 idx = 0; idx < string.size; idx += 1) - { - string.str[idx] = char_to_lower(string.str[idx]); - } - return string; -} - -internal String8 -backslashed_from_str8(Arena *arena, String8 string) -{ - string = push_str8_copy(arena, string); - for(U64 idx = 0; idx < string.size; idx += 1) - { - string.str[idx] = char_is_slash(string.str[idx]) ? '\\' : string.str[idx]; - } - return string; -} - -//////////////////////////////// -//~ rjf: String Matching - -internal B32 -str8_match(String8 a, String8 b, StringMatchFlags flags){ - B32 result = 0; - if (a.size == b.size || (flags & StringMatchFlag_RightSideSloppy)){ - B32 case_insensitive = (flags & StringMatchFlag_CaseInsensitive); - B32 slash_insensitive = (flags & StringMatchFlag_SlashInsensitive); - U64 size = Min(a.size, b.size); - result = 1; - for (U64 i = 0; i < size; i += 1){ - U8 at = a.str[i]; - U8 bt = b.str[i]; - if (case_insensitive){ - at = char_to_upper(at); - bt = char_to_upper(bt); - } - if (slash_insensitive){ - at = char_to_correct_slash(at); - bt = char_to_correct_slash(bt); - } - if (at != bt){ - result = 0; - break; - } - } - } - return(result); -} - -internal U64 -str8_find_needle(String8 string, U64 start_pos, String8 needle, StringMatchFlags flags){ - U8 *p = string.str + start_pos; - U64 stop_offset = Max(string.size + 1, needle.size) - needle.size; - U8 *stop_p = string.str + stop_offset; - if (needle.size > 0){ - U8 *string_opl = string.str + string.size; - String8 needle_tail = str8_skip(needle, 1); - StringMatchFlags adjusted_flags = flags | StringMatchFlag_RightSideSloppy; - U8 needle_first_char_adjusted = needle.str[0]; - if(adjusted_flags & StringMatchFlag_CaseInsensitive){ - needle_first_char_adjusted = char_to_upper(needle_first_char_adjusted); - } - for (;p < stop_p; p += 1){ - U8 haystack_char_adjusted = *p; - if(adjusted_flags & StringMatchFlag_CaseInsensitive){ - haystack_char_adjusted = char_to_upper(haystack_char_adjusted); - } - if (haystack_char_adjusted == needle_first_char_adjusted){ - if (str8_match(str8_range(p + 1, string_opl), needle_tail, adjusted_flags)){ - break; - } - } - } - } - U64 result = string.size; - if (p < stop_p){ - result = (U64)(p - string.str); - } - return(result); -} - -internal B32 -str8_ends_with(String8 string, String8 end, StringMatchFlags flags){ - String8 postfix = str8_postfix(string, end.size); - B32 is_match = str8_match(end, postfix, flags); - return is_match; -} - -//////////////////////////////// -//~ rjf: String Slicing - -internal String8 -str8_substr(String8 str, Rng1U64 range){ - range.min = ClampTop(range.min, str.size); - range.max = ClampTop(range.max, str.size); - str.str += range.min; - str.size = dim_1u64(range); - return(str); -} - -internal String8 -str8_prefix(String8 str, U64 size){ - str.size = ClampTop(size, str.size); - return(str); -} - -internal String8 -str8_skip(String8 str, U64 amt){ - amt = ClampTop(amt, str.size); - str.str += amt; - str.size -= amt; - return(str); -} - -internal String8 -str8_postfix(String8 str, U64 size){ - size = ClampTop(size, str.size); - str.str = (str.str + str.size) - size; - str.size = size; - return(str); -} - -internal String8 -str8_chop(String8 str, U64 amt){ - amt = ClampTop(amt, str.size); - str.size -= amt; - return(str); -} - -internal String8 -str8_skip_chop_whitespace(String8 string){ - U8 *first = string.str; - U8 *opl = first + string.size; - for (;first < opl; first += 1){ - if (!char_is_space(*first)){ - break; - } - } - for (;opl > first;){ - opl -= 1; - if (!char_is_space(*opl)){ - opl += 1; - break; - } - } - String8 result = str8_range(first, opl); - return(result); -} - -//////////////////////////////// -//~ rjf: String Formatting & Copying - -internal String8 -push_str8_cat(Arena *arena, String8 s1, String8 s2){ - String8 str; - str.size = s1.size + s2.size; - str.str = push_array_no_zero(arena, U8, str.size + 1); - MemoryCopy(str.str, s1.str, s1.size); - MemoryCopy(str.str + s1.size, s2.str, s2.size); - str.str[str.size] = 0; - return(str); -} - -internal String8 -push_str8_copy(Arena *arena, String8 s){ - //ProfBeginFunction(); - String8 str; - str.size = s.size; - str.str = push_array_no_zero(arena, U8, str.size + 1); - MemoryCopy(str.str, s.str, s.size); - str.str[str.size] = 0; - //ProfEnd(); - return(str); -} - -internal String8 -push_str8fv(Arena *arena, char *fmt, va_list args){ - va_list args2; - va_copy(args2, args); - U32 needed_bytes = raddbg_vsnprintf(0, 0, fmt, args) + 1; - String8 result = {0}; - result.str = push_array_no_zero(arena, U8, needed_bytes); - result.size = raddbg_vsnprintf((char*)result.str, needed_bytes, fmt, args2); - result.str[result.size] = 0; - va_end(args2); - return(result); -} - -internal String8 -push_str8f(Arena *arena, char *fmt, ...){ - va_list args; - va_start(args, fmt); - String8 result = push_str8fv(arena, fmt, args); - va_end(args); - return(result); -} - -//////////////////////////////// -//~ rjf: String <=> Integer Conversions - -//- rjf: string -> integer - -internal S64 -sign_from_str8(String8 string, String8 *string_tail){ - // count negative signs - U64 neg_count = 0; - U64 i = 0; - for (; i < string.size; i += 1){ - if (string.str[i] == '-'){ - neg_count += 1; - } - else if (string.str[i] != '+'){ - break; - } - } - - // output part of string after signs - *string_tail = str8_skip(string, i); - - // output integer sign - S64 sign = (neg_count & 1)?-1:+1; - return(sign); -} - -internal B32 -str8_is_integer(String8 string, U32 radix){ - B32 result = 0; - if (string.size > 0){ - if (1 < radix && radix <= 16){ - result = 1; - for (U64 i = 0; i < string.size; i += 1){ - U8 c = string.str[i]; - if (!(c < 0x80) || integer_symbol_reverse[c] >= radix){ - result = 0; - break; - } - } - } - } - return(result); -} - -internal U64 -u64_from_str8(String8 string, U32 radix){ - U64 x = 0; - if (1 < radix && radix <= 16){ - for (U64 i = 0; i < string.size; i += 1){ - x *= radix; - x += integer_symbol_reverse[string.str[i]&0x7F]; - } - } - return(x); -} - -internal S64 -s64_from_str8(String8 string, U32 radix){ - S64 sign = sign_from_str8(string, &string); - S64 x = (S64)u64_from_str8(string, radix) * sign; - return(x); -} - -internal B32 -try_u64_from_str8_c_rules(String8 string, U64 *x){ - B32 is_integer = 0; - if (str8_is_integer(string, 10)){ - is_integer = 1; - *x = u64_from_str8(string, 10); - } - else{ - String8 hex_string = str8_skip(string, 2); - if (str8_match(str8_prefix(string, 2), str8_lit("0x"), 0) && - str8_is_integer(hex_string, 0x10)){ - is_integer = 1; - *x = u64_from_str8(hex_string, 0x10); - } - else if (str8_match(str8_prefix(string, 2), str8_lit("0b"), 0) && - str8_is_integer(hex_string, 2)){ - is_integer = 1; - *x = u64_from_str8(hex_string, 2); - } - else{ - String8 oct_string = str8_skip(string, 1); - if (str8_match(str8_prefix(string, 1), str8_lit("0"), 0) && - str8_is_integer(hex_string, 010)){ - is_integer = 1; - *x = u64_from_str8(oct_string, 010); - } - } - } - return(is_integer); -} - -internal B32 -try_s64_from_str8_c_rules(String8 string, S64 *x){ - String8 string_tail = {0}; - S64 sign = sign_from_str8(string, &string_tail); - U64 x_u64 = 0; - B32 is_integer = try_u64_from_str8_c_rules(string_tail, &x_u64); - *x = x_u64*sign; - return(is_integer); -} - -//- rjf: string -> integer (base64 & base16) - -internal U64 -base64_size_from_data_size(U64 size_in_bytes){ - U64 bits = size_in_bytes*8; - U64 base64_size = (bits + 5)/6; - return(base64_size); -} - -internal U64 -base64_from_data(U8 *dst, U8 *src, U64 src_size){ - U8 *dst_base = dst; - U8 *opl = src + src_size; - U32 bit_num = 0; - if (src < opl){ - U8 byte = *src; - for (;;){ - U32 x = 0; - for (U32 i = 0; i < 6; i += 1){ - x |= ((byte >> bit_num) & 1) << i; - bit_num += 1; - if (bit_num == 8){ - bit_num = 0; - src += 1; - byte = (src < opl)?(*src):0; - } - } - *dst = base64[x]; - dst += 1; - if (src >= opl){ - break; - } - } - } - return(dst - dst_base); -} - -internal U64 -base16_size_from_data_size(U64 size_in_bytes){ - U64 base16_size = size_in_bytes*2; - return(base16_size); -} - -internal U64 -base16_from_data(U8 *dst, U8 *src, U64 src_size){ - U8 *dst_base = dst; - U8 *opl = src + src_size; - for (;src < opl;){ - U8 byte = *src; - *dst = integer_symbols[byte & 0xF]; - dst += 1; - *dst = integer_symbols[byte >> 4]; - dst += 1; - src += 1; - } - return(dst - dst_base); -} - -//- rjf: integer -> string - -internal String8 -str8_from_memory_size(Arena *arena, U64 z){ - String8 result = {0}; - if (z < KB(1)){ - result = push_str8f(arena, "%llu b", z); - } - else if (z < MB(1)){ - result = push_str8f(arena, "%llu.%02llu Kb", z/KB(1), ((100*z)/KB(1))%100); - } - else if (z < GB(1)){ - result = push_str8f(arena, "%llu.%02llu Mb", z/MB(1), ((100*z)/MB(1))%100); - } - else{ - result = push_str8f(arena, "%llu.%02llu Gb", z/GB(1), ((100*z)/GB(1))%100); - } - return(result); -} - -internal String8 -str8_from_u64(Arena *arena, U64 u64, U32 radix, U8 min_digits, U8 digit_group_separator) -{ - String8 result = {0}; - { - // rjf: prefix - String8 prefix = {0}; - switch(radix) - { - case 16:{prefix = str8_lit("0x");}break; - case 8: {prefix = str8_lit("0o");}break; - case 2: {prefix = str8_lit("0b");}break; - } - - // rjf: determine # of chars between separators - U8 digit_group_size = 3; - switch(radix) - { - default:break; - case 2: - case 8: - case 16: - {digit_group_size = 4;}break; - } - - // rjf: prep - U64 needed_leading_0s = 0; - { - U64 needed_digits = 1; - { - U64 u64_reduce = u64; - for(;;) - { - u64_reduce /= radix; - if(u64_reduce == 0) - { - break; - } - needed_digits += 1; - } - } - needed_leading_0s = (min_digits > needed_digits) ? min_digits - needed_digits : 0; - U64 needed_separators = 0; - if(digit_group_separator != 0) - { - needed_separators = (needed_digits+needed_leading_0s)/digit_group_size; - if(needed_separators > 0 && (needed_digits+needed_leading_0s)%digit_group_size == 0) - { - needed_separators -= 1; - } - } - result.size = prefix.size + needed_leading_0s + needed_separators + needed_digits; - result.str = push_array_no_zero(arena, U8, result.size + 1); - result.str[result.size] = 0; - } - - // rjf: fill contents - { - U64 u64_reduce = u64; - U64 digits_until_separator = digit_group_size; - for(U64 idx = 0; idx < result.size; idx += 1) - { - if(digits_until_separator == 0 && digit_group_separator != 0) - { - result.str[result.size - idx - 1] = digit_group_separator; - digits_until_separator = digit_group_size+1; - } - else - { - result.str[result.size - idx - 1] = char_to_lower(integer_symbols[u64_reduce%radix]); - u64_reduce /= radix; - } - digits_until_separator -= 1; - if(u64_reduce == 0) - { - break; - } - } - for(U64 leading_0_idx = 0; leading_0_idx < needed_leading_0s; leading_0_idx += 1) - { - result.str[prefix.size + leading_0_idx] = '0'; - } - } - - // rjf: fill prefix - if(prefix.size != 0) - { - MemoryCopy(result.str, prefix.str, prefix.size); - } - } - return result; -} - -internal String8 -str8_from_s64(Arena *arena, S64 s64, U32 radix, U8 min_digits, U8 digit_group_separator) -{ - String8 result = {0}; - // TODO(rjf): preeeeetty sloppy... - if(s64 < 0) - { - Temp scratch = scratch_begin(&arena, 1); - String8 numeric_part = str8_from_u64(scratch.arena, (U64)(-s64), radix, min_digits, digit_group_separator); - result = push_str8f(arena, "-%S", numeric_part); - scratch_end(scratch); - } - else - { - result = str8_from_u64(arena, (U64)s64, radix, min_digits, digit_group_separator); - } - return result; -} - -//////////////////////////////// -//~ rjf: String <=> Float Conversions - -internal F64 -f64_from_str8(String8 string) -{ - // TODO(rjf): crappy implementation for now that just uses atof. - F64 result = 0; - if(string.size > 0) - { - // rjf: find starting pos of numeric string, as well as sign - F64 sign = +1.0; - //U64 first_numeric = 0; - if(string.str[0] == '-') - { - //first_numeric = 1; - sign = -1.0; - } - else if(string.str[0] == '+') - { - //first_numeric = 1; - sign = 1.0; - } - - // rjf: gather numerics - U64 num_valid_chars = 0; - char buffer[64]; - for(U64 idx = 0; idx < string.size && num_valid_chars < sizeof(buffer)-1; idx += 1) - { - if(char_is_digit(string.str[idx], 10) || string.str[idx] == '.') - { - buffer[num_valid_chars] = string.str[idx]; - num_valid_chars += 1; - } - } - - // rjf: null-terminate (the reason for all of this!!!!!!) - buffer[num_valid_chars] = 0; - - // rjf: do final conversion - result = sign * atof(buffer); - } - return result; -} - -//////////////////////////////// -//~ rjf: String List Construction Functions - -internal String8Node* -str8_list_push_node(String8List *list, String8Node *node){ - SLLQueuePush(list->first, list->last, node); - list->node_count += 1; - list->total_size += node->string.size; - return(node); -} - -internal String8Node* -str8_list_push_node_set_string(String8List *list, String8Node *node, String8 string){ - SLLQueuePush(list->first, list->last, node); - list->node_count += 1; - list->total_size += string.size; - node->string = string; - return(node); -} - -internal String8Node* -str8_list_push_node_front(String8List *list, String8Node *node){ - SLLQueuePushFront(list->first, list->last, node); - list->node_count += 1; - list->total_size += node->string.size; - return(node); -} - -internal String8Node* -str8_list_push_node_front_set_string(String8List *list, String8Node *node, String8 string){ - SLLQueuePushFront(list->first, list->last, node); - list->node_count += 1; - list->total_size += string.size; - node->string = string; - return(node); -} - -internal String8Node* -str8_list_push(Arena *arena, String8List *list, String8 string){ - String8Node *node = push_array_no_zero(arena, String8Node, 1); - str8_list_push_node_set_string(list, node, string); - return(node); -} - -internal String8Node* -str8_list_push_front(Arena *arena, String8List *list, String8 string){ - String8Node *node = push_array_no_zero(arena, String8Node, 1); - str8_list_push_node_front_set_string(list, node, string); - return(node); -} - -internal void -str8_list_concat_in_place(String8List *list, String8List *to_push){ - if(to_push->node_count != 0){ - if (list->last){ - list->node_count += to_push->node_count; - list->total_size += to_push->total_size; - list->last->next = to_push->first; - list->last = to_push->last; - } - else{ - *list = *to_push; - } - MemoryZeroStruct(to_push); - } -} - -internal String8Node* -str8_list_push_aligner(Arena *arena, String8List *list, U64 min, U64 align){ - String8Node *node = push_array_no_zero(arena, String8Node, 1); - U64 new_size = list->total_size + min; - U64 increase_size = 0; - if (align > 1){ - // NOTE(allen): assert is power of 2 - Assert(((align - 1) & align) == 0); - U64 mask = align - 1; - new_size += mask; - new_size &= (~mask); - increase_size = new_size - list->total_size; - } - local_persist const U8 zeroes_buffer[64] = {0}; - Assert(increase_size <= ArrayCount(zeroes_buffer)); - SLLQueuePush(list->first, list->last, node); - list->node_count += 1; - list->total_size = new_size; - node->string.str = (U8*)zeroes_buffer; - node->string.size = increase_size; - return(node); -} - -internal String8Node* -str8_list_pushf(Arena *arena, String8List *list, char *fmt, ...){ - va_list args; - va_start(args, fmt); - String8 string = push_str8fv(arena, fmt, args); - String8Node *result = str8_list_push(arena, list, string); - va_end(args); - return(result); -} - -internal String8Node* -str8_list_push_frontf(Arena *arena, String8List *list, char *fmt, ...){ - va_list args; - va_start(args, fmt); - String8 string = push_str8fv(arena, fmt, args); - String8Node *result = str8_list_push_front(arena, list, string); - va_end(args); - return(result); -} - -internal String8List -str8_list_copy(Arena *arena, String8List *list){ - String8List result = {0}; - for (String8Node *node = list->first; - node != 0; - node = node->next){ - String8Node *new_node = push_array_no_zero(arena, String8Node, 1); - String8 new_string = push_str8_copy(arena, node->string); - str8_list_push_node_set_string(&result, new_node, new_string); - } - return(result); -} - -internal String8List -str8_split(Arena *arena, String8 string, U8 *split_chars, U64 split_char_count, StringSplitFlags flags){ - String8List list = {0}; - - B32 keep_empties = (flags & StringSplitFlag_KeepEmpties); - - U8 *ptr = string.str; - U8 *opl = string.str + string.size; - for (;ptr < opl;){ - U8 *first = ptr; - for (;ptr < opl; ptr += 1){ - U8 c = *ptr; - B32 is_split = 0; - for (U64 i = 0; i < split_char_count; i += 1){ - if (split_chars[i] == c){ - is_split = 1; - break; - } - } - if (is_split){ - break; - } - } - - String8 string = str8_range(first, ptr); - if (keep_empties || string.size > 0){ - str8_list_push(arena, &list, string); - } - ptr += 1; - } - - return(list); -} - -internal String8List -str8_split_by_string_chars(Arena *arena, String8 string, String8 split_chars, StringSplitFlags flags){ - String8List list = str8_split(arena, string, split_chars.str, split_chars.size, flags); - return list; -} - -internal String8List -str8_list_split_by_string_chars(Arena *arena, String8List list, String8 split_chars, StringSplitFlags flags){ - String8List result = {0}; - for (String8Node *node = list.first; node != 0; node = node->next){ - String8List split = str8_split_by_string_chars(arena, node->string, split_chars, flags); - str8_list_concat_in_place(&result, &split); - } - return result; -} - -internal String8 -str8_list_join(Arena *arena, String8List *list, StringJoin *optional_params){ - StringJoin join = {0}; - if (optional_params != 0){ - MemoryCopyStruct(&join, optional_params); - } - - U64 sep_count = 0; - if (list->node_count > 0){ - sep_count = list->node_count - 1; - } - - String8 result; - result.size = join.pre.size + join.post.size + sep_count*join.sep.size + list->total_size; - U8 *ptr = result.str = push_array_no_zero(arena, U8, result.size + 1); - - MemoryCopy(ptr, join.pre.str, join.pre.size); - ptr += join.pre.size; - for (String8Node *node = list->first; - node != 0; - node = node->next){ - MemoryCopy(ptr, node->string.str, node->string.size); - ptr += node->string.size; - if (node->next != 0){ - MemoryCopy(ptr, join.sep.str, join.sep.size); - ptr += join.sep.size; - } - } - MemoryCopy(ptr, join.post.str, join.post.size); - ptr += join.post.size; - - *ptr = 0; - - return(result); -} - -internal void -str8_list_from_flags(Arena *arena, String8List *list, - U32 flags, String8 *flag_string_table, U32 flag_string_count){ - for (U32 i = 0; i < flag_string_count; i += 1){ - U32 flag = (1 << i); - if (flags & flag){ - str8_list_push(arena, list, flag_string_table[i]); - } - } -} - -//////////////////////////////// -//~ rjf; String Arrays - -internal String8Array -str8_array_from_list(Arena *arena, String8List *list) -{ - String8Array array; - array.count = list->node_count; - array.strings = push_array_no_zero(arena, String8, array.count); - U64 idx = 0; - for(String8Node *n = list->first; n != 0; n = n->next, idx += 1) - { - array.strings[idx] = n->string; - } - return array; -} - -internal String8Array -str8_array_reserve(Arena *arena, U64 count) -{ - String8Array arr; - arr.count = 0; - arr.strings = push_array(arena, String8, count); - return arr; -} - -//////////////////////////////// -//~ rjf: String Path Helpers - -internal String8 -str8_chop_last_slash(String8 string){ - if (string.size > 0){ - U8 *ptr = string.str + string.size - 1; - for (;ptr >= string.str; ptr -= 1){ - if (*ptr == '/' || *ptr == '\\'){ - break; - } - } - if (ptr >= string.str){ - string.size = (U64)(ptr - string.str); - } - else{ - string.size = 0; - } - } - return(string); -} - -internal String8 -str8_skip_last_slash(String8 string){ - if (string.size > 0){ - U8 *ptr = string.str + string.size - 1; - for (;ptr >= string.str; ptr -= 1){ - if (*ptr == '/' || *ptr == '\\'){ - break; - } - } - if (ptr >= string.str){ - ptr += 1; - string.size = (U64)(string.str + string.size - ptr); - string.str = ptr; - } - } - return(string); -} - -internal String8 -str8_chop_last_dot(String8 string) -{ - String8 result = string; - U64 p = string.size; - for (;p > 0;){ - p -= 1; - if (string.str[p] == '.'){ - result = str8_prefix(string, p); - break; - } - } - return(result); -} - -internal String8 -str8_skip_last_dot(String8 string){ - String8 result = string; - U64 p = string.size; - for (;p > 0;){ - p -= 1; - if (string.str[p] == '.'){ - result = str8_skip(string, p + 1); - break; - } - } - return(result); -} - -internal PathStyle -path_style_from_str8(String8 string){ - PathStyle result = PathStyle_Relative; - if (string.size >= 1 && string.str[0] == '/'){ - result = PathStyle_UnixAbsolute; - } - else if (string.size >= 2 && - char_is_alpha(string.str[0]) && - string.str[1] == ':'){ - if (string.size == 2 || - char_is_slash(string.str[2])){ - result = PathStyle_WindowsAbsolute; - } - } - return(result); -} - -internal String8List -str8_split_path(Arena *arena, String8 string){ - String8List result = str8_split(arena, string, (U8*)"/\\", 2, 0); - return(result); -} - -internal void -str8_path_list_resolve_dots_in_place(String8List *path, PathStyle style){ - Temp scratch = scratch_begin(0, 0); - - String8MetaNode *stack = 0; - String8MetaNode *free_meta_node = 0; - String8Node *first = path->first; - - MemoryZeroStruct(path); - for (String8Node *node = first, *next = 0; - node != 0; - node = next){ - // save next now - next = node->next; - - // cases: - if (node == first && style == PathStyle_WindowsAbsolute){ - goto save_without_stack; - } - if (node->string.size == 1 && node->string.str[0] == '.'){ - goto do_nothing; - } - if (node->string.size == 2 && node->string.str[0] == '.' && node->string.str[1] == '.'){ - if (stack != 0){ - goto eliminate_stack_top; - } - else{ - goto save_without_stack; - } - } - goto save_with_stack; - - - // handlers: - save_with_stack: - { - str8_list_push_node(path, node); - - String8MetaNode *stack_node = free_meta_node; - if (stack_node != 0){ - SLLStackPop(free_meta_node); - } - else{ - stack_node = push_array_no_zero(scratch.arena, String8MetaNode, 1); - } - SLLStackPush(stack, stack_node); - stack_node->node = node; - - continue; - } - - save_without_stack: - { - str8_list_push_node(path, node); - - continue; - } - - eliminate_stack_top: - { - path->node_count -= 1; - path->total_size -= stack->node->string.size; - - SLLStackPop(stack); - - if (stack == 0){ - path->last = path->first; - } - else{ - path->last = stack->node; - } - continue; - } - - do_nothing: continue; - } - scratch_end(scratch); -} - -internal String8 -str8_path_list_join_by_style(Arena *arena, String8List *path, PathStyle style){ - StringJoin params = {0}; - switch (style){ - case PathStyle_Relative: - case PathStyle_WindowsAbsolute: - { - params.sep = str8_lit("/"); - }break; - - case PathStyle_UnixAbsolute: - { - params.pre = str8_lit("/"); - params.sep = str8_lit("/"); - }break; - } - - String8 result = str8_list_join(arena, path, ¶ms); - return(result); -} - -internal 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; -} - -//////////////////////////////// -//~ rjf: UTF-8 & UTF-16 Decoding/Encoding - -read_only global U8 utf8_class[32] = { - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,2,2,2,2,3,3,4,5, -}; - -internal UnicodeDecode -utf8_decode(U8 *str, U64 max){ - UnicodeDecode result = {1, max_U32}; - U8 byte = str[0]; - U8 byte_class = utf8_class[byte >> 3]; - switch (byte_class) - { - case 1: - { - result.codepoint = byte; - }break; - case 2: - { - if (2 < max) - { - U8 cont_byte = str[1]; - if (utf8_class[cont_byte >> 3] == 0) - { - result.codepoint = (byte & bitmask5) << 6; - result.codepoint |= (cont_byte & bitmask6); - result.inc = 2; - } - } - }break; - case 3: - { - if (2 < max) - { - U8 cont_byte[2] = {str[1], str[2]}; - if (utf8_class[cont_byte[0] >> 3] == 0 && - utf8_class[cont_byte[1] >> 3] == 0) - { - result.codepoint = (byte & bitmask4) << 12; - result.codepoint |= ((cont_byte[0] & bitmask6) << 6); - result.codepoint |= (cont_byte[1] & bitmask6); - result.inc = 3; - } - } - }break; - case 4: - { - if (3 < max) - { - U8 cont_byte[3] = {str[1], str[2], str[3]}; - if (utf8_class[cont_byte[0] >> 3] == 0 && - utf8_class[cont_byte[1] >> 3] == 0 && - utf8_class[cont_byte[2] >> 3] == 0) - { - result.codepoint = (byte & bitmask3) << 18; - result.codepoint |= ((cont_byte[0] & bitmask6) << 12); - result.codepoint |= ((cont_byte[1] & bitmask6) << 6); - result.codepoint |= (cont_byte[2] & bitmask6); - result.inc = 4; - } - } - } - } - return(result); -} - -internal UnicodeDecode -utf16_decode(U16 *str, U64 max){ - UnicodeDecode result = {1, max_U32}; - result.codepoint = str[0]; - result.inc = 1; - if (max > 1 && 0xD800 <= str[0] && str[0] < 0xDC00 && 0xDC00 <= str[1] && str[1] < 0xE000){ - result.codepoint = ((str[0] - 0xD800) << 10) | (str[1] - 0xDC00); - result.inc = 2; - } - return(result); -} - -internal U32 -utf8_encode(U8 *str, U32 codepoint){ - U32 inc = 0; - if (codepoint <= 0x7F){ - str[0] = (U8)codepoint; - inc = 1; - } - else if (codepoint <= 0x7FF){ - str[0] = (bitmask2 << 6) | ((codepoint >> 6) & bitmask5); - str[1] = bit8 | (codepoint & bitmask6); - inc = 2; - } - else if (codepoint <= 0xFFFF){ - str[0] = (bitmask3 << 5) | ((codepoint >> 12) & bitmask4); - str[1] = bit8 | ((codepoint >> 6) & bitmask6); - str[2] = bit8 | ( codepoint & bitmask6); - inc = 3; - } - else if (codepoint <= 0x10FFFF){ - str[0] = (bitmask4 << 3) | ((codepoint >> 18) & bitmask3); - str[1] = bit8 | ((codepoint >> 12) & bitmask6); - str[2] = bit8 | ((codepoint >> 6) & bitmask6); - str[3] = bit8 | ( codepoint & bitmask6); - inc = 4; - } - else{ - str[0] = '?'; - inc = 1; - } - return(inc); -} - -internal U32 -utf16_encode(U16 *str, U32 codepoint){ - U32 inc = 1; - if (codepoint == max_U32){ - str[0] = (U16)'?'; - } - else if (codepoint < 0x10000){ - str[0] = (U16)codepoint; - } - else{ - U32 v = codepoint - 0x10000; - str[0] = safe_cast_u16(0xD800 + (v >> 10)); - str[1] = safe_cast_u16(0xDC00 + (v & bitmask10)); - inc = 2; - } - return(inc); -} - -internal U32 -utf8_from_utf32_single(U8 *buffer, U32 character){ - return(utf8_encode(buffer, character)); -} - -//////////////////////////////// -//~ rjf: Unicode String Conversions - -internal String8 -str8_from_16(Arena *arena, String16 in){ - U64 cap = in.size*3; - U8 *str = push_array_no_zero(arena, U8, cap + 1); - U16 *ptr = in.str; - U16 *opl = ptr + in.size; - U64 size = 0; - UnicodeDecode consume; - for (;ptr < opl; ptr += consume.inc){ - consume = utf16_decode(ptr, opl - ptr); - size += utf8_encode(str + size, consume.codepoint); - } - str[size] = 0; - arena_put_back(arena, (cap - size)); - return(str8(str, size)); -} - -internal String16 -str16_from_8(Arena *arena, String8 in){ - U64 cap = in.size*2; - U16 *str = push_array_no_zero(arena, U16, cap + 1); - U8 *ptr = in.str; - U8 *opl = ptr + in.size; - U64 size = 0; - UnicodeDecode consume; - for (;ptr < opl; ptr += consume.inc){ - consume = utf8_decode(ptr, opl - ptr); - size += utf16_encode(str + size, consume.codepoint); - } - str[size] = 0; - arena_put_back(arena, (cap - size)*2); - return(str16(str, size)); -} - -internal String8 -str8_from_32(Arena *arena, String32 in){ - U64 cap = in.size*4; - U8 *str = push_array_no_zero(arena, U8, cap + 1); - U32 *ptr = in.str; - U32 *opl = ptr + in.size; - U64 size = 0; - for (;ptr < opl; ptr += 1){ - size += utf8_encode(str + size, *ptr); - } - str[size] = 0; - arena_put_back(arena, (cap - size)); - return(str8(str, size)); -} - -internal String32 -str32_from_8(Arena *arena, String8 in){ - U64 cap = in.size; - U32 *str = push_array_no_zero(arena, U32, cap + 1); - U8 *ptr = in.str; - U8 *opl = ptr + in.size; - U64 size = 0; - UnicodeDecode consume; - for (;ptr < opl; ptr += consume.inc){ - consume = utf8_decode(ptr, opl - ptr); - str[size] = consume.codepoint; - size += 1; - } - str[size] = 0; - arena_put_back(arena, (cap - size)*4); - return(str32(str, size)); -} - -//////////////////////////////// -//~ rjf: Basic Types & Space Enum -> String Conversions - -internal String8 -string_from_dimension(Dimension dimension){ - local_persist String8 strings[] = { - str8_lit_comp("X"), - str8_lit_comp("Y"), - str8_lit_comp("Z"), - str8_lit_comp("W"), - }; - String8 result = str8_lit("error"); - if ((U32)dimension < 4){ - result = strings[dimension]; - } - return(result); -} - -internal String8 -string_from_side(Side side){ - local_persist String8 strings[] = { - str8_lit_comp("Min"), - str8_lit_comp("Max"), - }; - String8 result = str8_lit("error"); - if ((U32)side < 2){ - result = strings[side]; - } - return(result); -} - -internal String8 -string_from_operating_system(OperatingSystem os){ - local_persist String8 strings[] = { - str8_lit_comp("Null"), - str8_lit_comp("Windows"), - str8_lit_comp("Linux"), - str8_lit_comp("Mac"), - }; - String8 result = str8_lit("error"); - if (os < OperatingSystem_COUNT){ - result = strings[os]; - } - return(result); -} - -internal String8 -string_from_architecture(Architecture arch){ - local_persist String8 strings[] = { - str8_lit_comp("Null"), - str8_lit_comp("x64"), - str8_lit_comp("x86"), - str8_lit_comp("arm64"), - str8_lit_comp("arm32"), - }; - String8 result = str8_lit("error"); - if (arch < Architecture_COUNT){ - result = strings[arch]; - } - return(result); -} - -//////////////////////////////// -//~ rjf: Time Types -> String - -internal String8 -string_from_week_day(WeekDay week_day){ - local_persist String8 strings[] = { - str8_lit_comp("Sun"), - str8_lit_comp("Mon"), - str8_lit_comp("Tue"), - str8_lit_comp("Wed"), - str8_lit_comp("Thu"), - str8_lit_comp("Fri"), - str8_lit_comp("Sat"), - }; - String8 result = str8_lit("Err"); - if ((U32)week_day < WeekDay_COUNT){ - result = strings[week_day]; - } - return(result); -} - -internal String8 -string_from_month(Month month){ - local_persist String8 strings[] = { - str8_lit_comp("Jan"), - str8_lit_comp("Feb"), - str8_lit_comp("Mar"), - str8_lit_comp("Apr"), - str8_lit_comp("May"), - str8_lit_comp("Jun"), - str8_lit_comp("Jul"), - str8_lit_comp("Aug"), - str8_lit_comp("Sep"), - str8_lit_comp("Oct"), - str8_lit_comp("Nov"), - str8_lit_comp("Dec"), - }; - String8 result = str8_lit("Err"); - if ((U32)month < Month_COUNT){ - result = strings[month]; - } - return(result); -} - -internal String8 -push_date_time_string(Arena *arena, DateTime *date_time){ - char *mon_str = (char*)string_from_month(date_time->month).str; - U32 adjusted_hour = date_time->hour%12; - if (adjusted_hour == 0){ - adjusted_hour = 12; - } - char *ampm = "am"; - if (date_time->hour >= 12){ - ampm = "pm"; - } - String8 result = push_str8f(arena, "%d %s %d, %02d:%02d:%02d %s", - date_time->day, mon_str, date_time->year, - adjusted_hour, date_time->min, date_time->sec, ampm); - return(result); -} - -internal String8 -push_file_name_date_time_string(Arena *arena, DateTime *date_time){ - char *mon_str = (char*)string_from_month(date_time->month).str; - String8 result = push_str8f(arena, "%d-%s-%0d--%02d-%02d-%02d", - date_time->year, mon_str, date_time->day, - date_time->hour, date_time->min, date_time->sec); - return(result); -} - -internal String8 -string_from_elapsed_time(Arena *arena, DateTime dt){ - Temp scratch = scratch_begin(&arena, 1); - String8List list = {0}; - if (dt.year){ - str8_list_pushf(scratch.arena, &list, "%dy", dt.year); - str8_list_pushf(scratch.arena, &list, "%um", dt.mon); - str8_list_pushf(scratch.arena, &list, "%ud", dt.day); - } else if (dt.mon){ - str8_list_pushf(scratch.arena, &list, "%um", dt.mon); - str8_list_pushf(scratch.arena, &list, "%ud", dt.day); - } else if (dt.day){ - str8_list_pushf(scratch.arena, &list, "%ud", dt.day); - } - str8_list_pushf(scratch.arena, &list, "%u:%u:%u:%u ms", dt.hour, dt.min, dt.sec, dt.msec); - StringJoin join = { str8_lit_comp(""), str8_lit_comp(" "), str8_lit_comp("") }; - String8 result = str8_list_join(arena, &list, &join); - scratch_end(scratch); - return(result); -} - -//////////////////////////////// -//~ rjf: String <-> Color - -internal String8 -hex_string_from_rgba_4f32(Arena *arena, Vec4F32 rgba) -{ - String8 hex_string = push_str8f(arena, "%02x%02x%02x%02x", (U8)(rgba.x*255.f), (U8)(rgba.y*255.f), (U8)(rgba.z*255.f), (U8)(rgba.w*255.f)); - return hex_string; -} - -internal Vec4F32 -rgba_from_hex_string_4f32(String8 hex_string) -{ - U8 byte_text[8] = {0}; - U64 byte_text_idx = 0; - for(U64 idx = 0; idx < hex_string.size && byte_text_idx < ArrayCount(byte_text); idx += 1) - { - if(char_is_digit(hex_string.str[idx], 16)) - { - byte_text[byte_text_idx] = char_to_lower(hex_string.str[idx]); - byte_text_idx += 1; - } - } - U8 byte_vals[4] = {0}; - for(U64 idx = 0; idx < 4; idx += 1) - { - byte_vals[idx] = (U8)u64_from_str8(str8(&byte_text[idx*2], 2), 16); - } - Vec4F32 rgba = v4f32(byte_vals[0]/255.f, byte_vals[1]/255.f, byte_vals[2]/255.f, byte_vals[3]/255.f); - return rgba; -} - -//////////////////////////////// -//~ NOTE(allen): Serialization Helpers - -internal void -str8_serial_begin(Arena *arena, String8List *srl){ - String8Node *node = push_array(arena, String8Node, 1); - node->string.str = push_array_no_zero(arena, U8, 0); - srl->first = srl->last = node; - srl->node_count = 1; - srl->total_size = 0; -} - -internal String8 -str8_serial_end(Arena *arena, String8List *srl){ - U64 size = srl->total_size; - U8 *out = push_array_no_zero(arena, U8, size); - str8_serial_write_to_dst(srl, out); - String8 result = str8(out, size); - return result; -} - -internal void -str8_serial_write_to_dst(String8List *srl, void *out){ - U8 *ptr = (U8*)out; - for (String8Node *node = srl->first; - node != 0; - node = node->next){ - U64 size = node->string.size; - MemoryCopy(ptr, node->string.str, size); - ptr += size; - } -} - -internal U64 -str8_serial_push_align(Arena *arena, String8List *srl, U64 align){ - Assert(IsPow2(align)); - - U64 pos = srl->total_size; - U64 new_pos = AlignPow2(pos, align); - U64 size = (new_pos - pos); - - if(size != 0) - { - U8 *buf = push_array(arena, U8, size); - - String8 *str = &srl->last->string; - if (str->str + str->size == buf){ - srl->last->string.size += size; - srl->total_size += size; - } - else{ - str8_list_push(arena, srl, str8(buf, size)); - } - } - return size; -} - -internal void * -str8_serial_push_size(Arena *arena, String8List *srl, U64 size) -{ - void *result = 0; - if(size != 0) - { - U8 *buf = push_array_no_zero(arena, U8, size); - String8 *str = &srl->last->string; - if (str->str + str->size == buf){ - srl->last->string.size += size; - srl->total_size += size; - } - else{ - str8_list_push(arena, srl, str8(buf, size)); - } - result = buf; - } - return result; -} - -internal void * -str8_serial_push_data(Arena *arena, String8List *srl, void *data, U64 size){ - void *result = str8_serial_push_size(arena, srl, size); - if(result != 0) - { - MemoryCopy(result, data, size); - } - return result; -} - -internal void -str8_serial_push_data_list(Arena *arena, String8List *srl, String8Node *first){ - for (String8Node *node = first; - node != 0; - node = node->next){ - str8_serial_push_data(arena, srl, node->string.str, node->string.size); - } -} - -internal void -str8_serial_push_u64(Arena *arena, String8List *srl, U64 x){ - U8 *buf = push_array_no_zero(arena, U8, 8); - MemoryCopy(buf, &x, 8); - String8 *str = &srl->last->string; - if (str->str + str->size == buf){ - srl->last->string.size += 8; - srl->total_size += 8; - } - else{ - str8_list_push(arena, srl, str8(buf, 8)); - } -} - -internal void -str8_serial_push_u32(Arena *arena, String8List *srl, U32 x){ - U8 *buf = push_array_no_zero(arena, U8, 4); - MemoryCopy(buf, &x, 4); - String8 *str = &srl->last->string; - if (str->str + str->size == buf){ - srl->last->string.size += 4; - srl->total_size += 4; - } - else{ - str8_list_push(arena, srl, str8(buf, 4)); - } -} - -internal void -str8_serial_push_u16(Arena *arena, String8List *srl, U16 x){ - str8_serial_push_data(arena, srl, &x, sizeof(x)); -} - -internal void -str8_serial_push_u8(Arena *arena, String8List *srl, U8 x){ - str8_serial_push_data(arena, srl, &x, sizeof(x)); -} - -internal void -str8_serial_push_cstr(Arena *arena, String8List *srl, String8 str){ - str8_serial_push_data(arena, srl, str.str, str.size); - str8_serial_push_u8(arena, srl, 0); -} - -internal void -str8_serial_push_string(Arena *arena, String8List *srl, String8 str){ - str8_serial_push_data(arena, srl, str.str, str.size); -} - -//////////////////////////////// -//~ rjf: Deserialization Helpers - -internal U64 -str8_deserial_read(String8 string, U64 off, void *read_dst, U64 read_size, U64 granularity) -{ - U64 bytes_left = string.size-Min(off, string.size); - U64 actually_readable_size = Min(bytes_left, read_size); - U64 legally_readable_size = actually_readable_size - actually_readable_size%granularity; - if(legally_readable_size > 0) - { - MemoryCopy(read_dst, string.str+off, legally_readable_size); - } - return legally_readable_size; -} - -internal U64 -str8_deserial_find_first_match(String8 string, U64 off, U16 scan_val) -{ - U64 cursor = off; - for (;;) { - U16 val = 0; - str8_deserial_read_struct(string, cursor, &val); - if (val == scan_val) { - break; - } - cursor += sizeof(val); - } - return cursor; -} - -internal void * -str8_deserial_get_raw_ptr(String8 string, U64 off, U64 size) -{ - void *raw_ptr = 0; - if (off + size <= string.size) { - raw_ptr = string.str + off; - } - return raw_ptr; -} - -internal U64 -str8_deserial_read_cstr(String8 string, U64 off, String8 *cstr_out) -{ - U64 cstr_size = 0; - if (off < string.size) { - U8 *ptr = string.str + off; - U8 *cap = string.str + string.size; - *cstr_out = str8_cstring_capped(ptr, cap); - cstr_size = (cstr_out->size + 1); - } - return cstr_size; -} - -internal U64 -str8_deserial_read_windows_utf16_string16(String8 string, U64 off, String16 *str_out) -{ - U64 null_off = str8_deserial_find_first_match(string, off, 0); - U64 size = null_off - off; - U16 *str = (U16 *)str8_deserial_get_raw_ptr(string, off, size); - U64 count = size / sizeof(*str); - *str_out = str16(str, count); - - U64 read_size_with_null = size + sizeof(*str); - return read_size_with_null; -} - -internal U64 -str8_deserial_read_block(String8 string, U64 off, U64 size, String8 *block_out) -{ - Rng1U64 range = rng_1u64(off, off + size); - *block_out = str8_substr(string, range); - return block_out->size; -} +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +//////////////////////////////// +//~ rjf: Third Party Includes + +#if !BUILD_SUPPLEMENTARY_UNIT +# define STB_SPRINTF_IMPLEMENTATION +# define STB_SPRINTF_STATIC +# include "third_party/stb/stb_sprintf.h" +#endif + +//////////////////////////////// +//~ NOTE(allen): String <-> Integer Tables + +read_only global U8 integer_symbols[16] = { + '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F', +}; + +// NOTE(allen): Includes reverses for uppercase and lowercase hex. +read_only global U8 integer_symbol_reverse[128] = { + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +}; + +read_only global U8 base64[64] = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', + 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', + 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + '_', '$', +}; + +read_only global U8 base64_reverse[128] = { + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFF,0x3F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0xFF,0xFF,0xFF,0xFF,0xFF,0x00, + 0xFF,0x24,0x25,0x26,0x27,0x28,0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F,0x30,0x31,0x32, + 0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3A,0x3B,0x3C,0x3D,0xFF,0xFF,0xFF,0xFF,0x3E, + 0xFF,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18, + 0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,0x20,0x21,0x22,0x23,0xFF,0xFF,0xFF,0xFF,0xFF, +}; + +//////////////////////////////// +//~ rjf: Character Classification & Conversion Functions + +internal B32 +char_is_space(U8 c){ + return(c == ' ' || c == '\n' || c == '\t' || c == '\r' || c == '\f' || c == '\v'); +} + +internal B32 +char_is_upper(U8 c){ + return('A' <= c && c <= 'Z'); +} + +internal B32 +char_is_lower(U8 c){ + return('a' <= c && c <= 'z'); +} + +internal B32 +char_is_alpha(U8 c){ + return(char_is_upper(c) || char_is_lower(c)); +} + +internal B32 +char_is_slash(U8 c){ + return(c == '/' || c == '\\'); +} + +internal B32 +char_is_digit(U8 c, U32 base){ + B32 result = 0; + if (0 < base && base <= 16){ + U8 val = integer_symbol_reverse[c]; + if (val < base){ + result = 1; + } + } + return(result); +} + +internal U8 +char_to_lower(U8 c){ + if (char_is_upper(c)){ + c += ('a' - 'A'); + } + return(c); +} + +internal U8 +char_to_upper(U8 c){ + if (char_is_lower(c)){ + c += ('A' - 'a'); + } + return(c); +} + +internal U8 +char_to_correct_slash(U8 c){ + if(char_is_slash(c)){ + c = '/'; + } + return(c); +} + +//////////////////////////////// +//~ rjf: C-String Measurement + +internal U64 +cstring8_length(U8 *c){ + U8 *p = c; + for (;*p != 0; p += 1); + return(p - c); +} + +internal U64 +cstring16_length(U16 *c){ + U16 *p = c; + for (;*p != 0; p += 1); + return(p - c); +} + +internal U64 +cstring32_length(U32 *c){ + U32 *p = c; + for (;*p != 0; p += 1); + return(p - c); +} + +//////////////////////////////// +//~ rjf: String Constructors + +internal String8 +str8(U8 *str, U64 size){ + String8 result = {str, size}; + return(result); +} + +internal String8 +str8_range(U8 *first, U8 *one_past_last){ + String8 result = {first, (U64)(one_past_last - first)}; + return(result); +} + +internal String8 +str8_zero(void){ + String8 result = {0}; + return(result); +} + +internal String16 +str16(U16 *str, U64 size){ + String16 result = {str, size}; + return(result); +} + +internal String16 +str16_range(U16 *first, U16 *one_past_last){ + String16 result = {first, (U64)(one_past_last - first)}; + return(result); +} + +internal String16 +str16_zero(void){ + String16 result = {0}; + return(result); +} + +internal String32 +str32(U32 *str, U64 size){ + String32 result = {str, size}; + return(result); +} + +internal String32 +str32_range(U32 *first, U32 *one_past_last){ + String32 result = {first, (U64)(one_past_last - first)}; + return(result); +} + +internal String32 +str32_zero(void){ + String32 result = {0}; + return(result); +} + +internal String8 +str8_cstring(char *c){ + String8 result = {(U8*)c, cstring8_length((U8*)c)}; + return(result); +} + +internal String16 +str16_cstring(U16 *c){ + String16 result = {(U16*)c, cstring16_length((U16*)c)}; + return(result); +} + +internal String32 +str32_cstring(U32 *c){ + String32 result = {(U32*)c, cstring32_length((U32*)c)}; + return(result); +} + +internal String8 +str8_cstring_capped(void *cstr, void *cap) +{ + char *ptr = (char*)cstr; + char *opl = (char*)cap; + for (;ptr < opl && *ptr != 0; ptr += 1); + U64 size = (U64)(ptr - (char *)cstr); + String8 result = {(U8*)cstr, size}; + return(result); +} + +//////////////////////////////// +//~ rjf: String Stylization + +internal String8 +upper_from_str8(Arena *arena, String8 string) +{ + string = push_str8_copy(arena, string); + for(U64 idx = 0; idx < string.size; idx += 1) + { + string.str[idx] = char_to_upper(string.str[idx]); + } + return string; +} + +internal String8 +lower_from_str8(Arena *arena, String8 string) +{ + string = push_str8_copy(arena, string); + for(U64 idx = 0; idx < string.size; idx += 1) + { + string.str[idx] = char_to_lower(string.str[idx]); + } + return string; +} + +internal String8 +backslashed_from_str8(Arena *arena, String8 string) +{ + string = push_str8_copy(arena, string); + for(U64 idx = 0; idx < string.size; idx += 1) + { + string.str[idx] = char_is_slash(string.str[idx]) ? '\\' : string.str[idx]; + } + return string; +} + +//////////////////////////////// +//~ rjf: String Matching + +internal B32 +str8_match(String8 a, String8 b, StringMatchFlags flags){ + B32 result = 0; + if (a.size == b.size || (flags & StringMatchFlag_RightSideSloppy)){ + B32 case_insensitive = (flags & StringMatchFlag_CaseInsensitive); + B32 slash_insensitive = (flags & StringMatchFlag_SlashInsensitive); + U64 size = Min(a.size, b.size); + result = 1; + for (U64 i = 0; i < size; i += 1){ + U8 at = a.str[i]; + U8 bt = b.str[i]; + if (case_insensitive){ + at = char_to_upper(at); + bt = char_to_upper(bt); + } + if (slash_insensitive){ + at = char_to_correct_slash(at); + bt = char_to_correct_slash(bt); + } + if (at != bt){ + result = 0; + break; + } + } + } + return(result); +} + +internal U64 +str8_find_needle(String8 string, U64 start_pos, String8 needle, StringMatchFlags flags){ + U8 *p = string.str + start_pos; + U64 stop_offset = Max(string.size + 1, needle.size) - needle.size; + U8 *stop_p = string.str + stop_offset; + if (needle.size > 0){ + U8 *string_opl = string.str + string.size; + String8 needle_tail = str8_skip(needle, 1); + StringMatchFlags adjusted_flags = flags | StringMatchFlag_RightSideSloppy; + U8 needle_first_char_adjusted = needle.str[0]; + if(adjusted_flags & StringMatchFlag_CaseInsensitive){ + needle_first_char_adjusted = char_to_upper(needle_first_char_adjusted); + } + for (;p < stop_p; p += 1){ + U8 haystack_char_adjusted = *p; + if(adjusted_flags & StringMatchFlag_CaseInsensitive){ + haystack_char_adjusted = char_to_upper(haystack_char_adjusted); + } + if (haystack_char_adjusted == needle_first_char_adjusted){ + if (str8_match(str8_range(p + 1, string_opl), needle_tail, adjusted_flags)){ + break; + } + } + } + } + U64 result = string.size; + if (p < stop_p){ + result = (U64)(p - string.str); + } + return(result); +} + +internal B32 +str8_ends_with(String8 string, String8 end, StringMatchFlags flags){ + String8 postfix = str8_postfix(string, end.size); + B32 is_match = str8_match(end, postfix, flags); + return is_match; +} + +//////////////////////////////// +//~ rjf: String Slicing + +internal String8 +str8_substr(String8 str, Rng1U64 range){ + range.min = ClampTop(range.min, str.size); + range.max = ClampTop(range.max, str.size); + str.str += range.min; + str.size = dim_1u64(range); + return(str); +} + +internal String8 +str8_prefix(String8 str, U64 size){ + str.size = ClampTop(size, str.size); + return(str); +} + +internal String8 +str8_skip(String8 str, U64 amt){ + amt = ClampTop(amt, str.size); + str.str += amt; + str.size -= amt; + return(str); +} + +internal String8 +str8_postfix(String8 str, U64 size){ + size = ClampTop(size, str.size); + str.str = (str.str + str.size) - size; + str.size = size; + return(str); +} + +internal String8 +str8_chop(String8 str, U64 amt){ + amt = ClampTop(amt, str.size); + str.size -= amt; + return(str); +} + +internal String8 +str8_skip_chop_whitespace(String8 string){ + U8 *first = string.str; + U8 *opl = first + string.size; + for (;first < opl; first += 1){ + if (!char_is_space(*first)){ + break; + } + } + for (;opl > first;){ + opl -= 1; + if (!char_is_space(*opl)){ + opl += 1; + break; + } + } + String8 result = str8_range(first, opl); + return(result); +} + +//////////////////////////////// +//~ rjf: String Formatting & Copying + +internal String8 +push_str8_cat(Arena *arena, String8 s1, String8 s2){ + String8 str; + str.size = s1.size + s2.size; + str.str = push_array_no_zero(arena, U8, str.size + 1); + MemoryCopy(str.str, s1.str, s1.size); + MemoryCopy(str.str + s1.size, s2.str, s2.size); + str.str[str.size] = 0; + return(str); +} + +internal String8 +push_str8_copy(Arena *arena, String8 s){ + String8 str; + str.size = s.size; + str.str = push_array_no_zero(arena, U8, str.size + 1); + MemoryCopy(str.str, s.str, s.size); + str.str[str.size] = 0; + return(str); +} + +internal String8 +push_str8fv(Arena *arena, char *fmt, va_list args){ + va_list args2; + va_copy(args2, args); + U32 needed_bytes = raddbg_vsnprintf(0, 0, fmt, args) + 1; + String8 result = {0}; + result.str = push_array_no_zero(arena, U8, needed_bytes); + result.size = raddbg_vsnprintf((char*)result.str, needed_bytes, fmt, args2); + result.str[result.size] = 0; + va_end(args2); + return(result); +} + +internal String8 +push_str8f(Arena *arena, char *fmt, ...){ + va_list args; + va_start(args, fmt); + String8 result = push_str8fv(arena, fmt, args); + va_end(args); + return(result); +} + +//////////////////////////////// +//~ rjf: String <=> Integer Conversions + +//- rjf: string -> integer + +internal S64 +sign_from_str8(String8 string, String8 *string_tail){ + // count negative signs + U64 neg_count = 0; + U64 i = 0; + for (; i < string.size; i += 1){ + if (string.str[i] == '-'){ + neg_count += 1; + } + else if (string.str[i] != '+'){ + break; + } + } + + // output part of string after signs + *string_tail = str8_skip(string, i); + + // output integer sign + S64 sign = (neg_count & 1)?-1:+1; + return(sign); +} + +internal B32 +str8_is_integer(String8 string, U32 radix){ + B32 result = 0; + if (string.size > 0){ + if (1 < radix && radix <= 16){ + result = 1; + for (U64 i = 0; i < string.size; i += 1){ + U8 c = string.str[i]; + if (!(c < 0x80) || integer_symbol_reverse[c] >= radix){ + result = 0; + break; + } + } + } + } + return(result); +} + +internal U64 +u64_from_str8(String8 string, U32 radix){ + U64 x = 0; + if (1 < radix && radix <= 16){ + for (U64 i = 0; i < string.size; i += 1){ + x *= radix; + x += integer_symbol_reverse[string.str[i]&0x7F]; + } + } + return(x); +} + +internal S64 +s64_from_str8(String8 string, U32 radix){ + S64 sign = sign_from_str8(string, &string); + S64 x = (S64)u64_from_str8(string, radix) * sign; + return(x); +} + +internal B32 +try_u64_from_str8_c_rules(String8 string, U64 *x){ + B32 is_integer = 0; + if (str8_is_integer(string, 10)){ + is_integer = 1; + *x = u64_from_str8(string, 10); + } + else{ + String8 hex_string = str8_skip(string, 2); + if (str8_match(str8_prefix(string, 2), str8_lit("0x"), 0) && + str8_is_integer(hex_string, 0x10)){ + is_integer = 1; + *x = u64_from_str8(hex_string, 0x10); + } + else if (str8_match(str8_prefix(string, 2), str8_lit("0b"), 0) && + str8_is_integer(hex_string, 2)){ + is_integer = 1; + *x = u64_from_str8(hex_string, 2); + } + else{ + String8 oct_string = str8_skip(string, 1); + if (str8_match(str8_prefix(string, 1), str8_lit("0"), 0) && + str8_is_integer(hex_string, 010)){ + is_integer = 1; + *x = u64_from_str8(oct_string, 010); + } + } + } + return(is_integer); +} + +internal B32 +try_s64_from_str8_c_rules(String8 string, S64 *x){ + String8 string_tail = {0}; + S64 sign = sign_from_str8(string, &string_tail); + U64 x_u64 = 0; + B32 is_integer = try_u64_from_str8_c_rules(string_tail, &x_u64); + *x = x_u64*sign; + return(is_integer); +} + +//- rjf: integer -> string + +internal String8 +str8_from_memory_size(Arena *arena, U64 z){ + String8 result = {0}; + if (z < KB(1)){ + result = push_str8f(arena, "%llu b", z); + } + else if (z < MB(1)){ + result = push_str8f(arena, "%llu.%02llu Kb", z/KB(1), ((100*z)/KB(1))%100); + } + else if (z < GB(1)){ + result = push_str8f(arena, "%llu.%02llu Mb", z/MB(1), ((100*z)/MB(1))%100); + } + else{ + result = push_str8f(arena, "%llu.%02llu Gb", z/GB(1), ((100*z)/GB(1))%100); + } + return(result); +} + +internal String8 +str8_from_u64(Arena *arena, U64 u64, U32 radix, U8 min_digits, U8 digit_group_separator) +{ + String8 result = {0}; + { + // rjf: prefix + String8 prefix = {0}; + switch(radix) + { + case 16:{prefix = str8_lit("0x");}break; + case 8: {prefix = str8_lit("0o");}break; + case 2: {prefix = str8_lit("0b");}break; + } + + // rjf: determine # of chars between separators + U8 digit_group_size = 3; + switch(radix) + { + default:break; + case 2: + case 8: + case 16: + {digit_group_size = 4;}break; + } + + // rjf: prep + U64 needed_leading_0s = 0; + { + U64 needed_digits = 1; + { + U64 u64_reduce = u64; + for(;;) + { + u64_reduce /= radix; + if(u64_reduce == 0) + { + break; + } + needed_digits += 1; + } + } + needed_leading_0s = (min_digits > needed_digits) ? min_digits - needed_digits : 0; + U64 needed_separators = 0; + if(digit_group_separator != 0) + { + needed_separators = (needed_digits+needed_leading_0s)/digit_group_size; + if(needed_separators > 0 && (needed_digits+needed_leading_0s)%digit_group_size == 0) + { + needed_separators -= 1; + } + } + result.size = prefix.size + needed_leading_0s + needed_separators + needed_digits; + result.str = push_array_no_zero(arena, U8, result.size + 1); + result.str[result.size] = 0; + } + + // rjf: fill contents + { + U64 u64_reduce = u64; + U64 digits_until_separator = digit_group_size; + for(U64 idx = 0; idx < result.size; idx += 1) + { + if(digits_until_separator == 0 && digit_group_separator != 0) + { + result.str[result.size - idx - 1] = digit_group_separator; + digits_until_separator = digit_group_size+1; + } + else + { + result.str[result.size - idx - 1] = char_to_lower(integer_symbols[u64_reduce%radix]); + u64_reduce /= radix; + } + digits_until_separator -= 1; + if(u64_reduce == 0) + { + break; + } + } + for(U64 leading_0_idx = 0; leading_0_idx < needed_leading_0s; leading_0_idx += 1) + { + result.str[prefix.size + leading_0_idx] = '0'; + } + } + + // rjf: fill prefix + if(prefix.size != 0) + { + MemoryCopy(result.str, prefix.str, prefix.size); + } + } + return result; +} + +internal String8 +str8_from_s64(Arena *arena, S64 s64, U32 radix, U8 min_digits, U8 digit_group_separator) +{ + String8 result = {0}; + // TODO(rjf): preeeeetty sloppy... + if(s64 < 0) + { + Temp scratch = scratch_begin(&arena, 1); + String8 numeric_part = str8_from_u64(scratch.arena, (U64)(-s64), radix, min_digits, digit_group_separator); + result = push_str8f(arena, "-%S", numeric_part); + scratch_end(scratch); + } + else + { + result = str8_from_u64(arena, (U64)s64, radix, min_digits, digit_group_separator); + } + return result; +} + +//////////////////////////////// +//~ rjf: String <=> Float Conversions + +internal F64 +f64_from_str8(String8 string) +{ + // TODO(rjf): crappy implementation for now that just uses atof. + F64 result = 0; + if(string.size > 0) + { + // rjf: find starting pos of numeric string, as well as sign + F64 sign = +1.0; + //U64 first_numeric = 0; + if(string.str[0] == '-') + { + //first_numeric = 1; + sign = -1.0; + } + else if(string.str[0] == '+') + { + //first_numeric = 1; + sign = 1.0; + } + + // rjf: gather numerics + U64 num_valid_chars = 0; + char buffer[64]; + for(U64 idx = 0; idx < string.size && num_valid_chars < sizeof(buffer)-1; idx += 1) + { + if(char_is_digit(string.str[idx], 10) || string.str[idx] == '.') + { + buffer[num_valid_chars] = string.str[idx]; + num_valid_chars += 1; + } + } + + // rjf: null-terminate (the reason for all of this!!!!!!) + buffer[num_valid_chars] = 0; + + // rjf: do final conversion + result = sign * atof(buffer); + } + return result; +} + +//////////////////////////////// +//~ rjf: String List Construction Functions + +internal String8Node* +str8_list_push_node(String8List *list, String8Node *node){ + SLLQueuePush(list->first, list->last, node); + list->node_count += 1; + list->total_size += node->string.size; + return(node); +} + +internal String8Node* +str8_list_push_node_set_string(String8List *list, String8Node *node, String8 string){ + SLLQueuePush(list->first, list->last, node); + list->node_count += 1; + list->total_size += string.size; + node->string = string; + return(node); +} + +internal String8Node* +str8_list_push_node_front(String8List *list, String8Node *node){ + SLLQueuePushFront(list->first, list->last, node); + list->node_count += 1; + list->total_size += node->string.size; + return(node); +} + +internal String8Node* +str8_list_push_node_front_set_string(String8List *list, String8Node *node, String8 string){ + SLLQueuePushFront(list->first, list->last, node); + list->node_count += 1; + list->total_size += string.size; + node->string = string; + return(node); +} + +internal String8Node* +str8_list_push(Arena *arena, String8List *list, String8 string){ + String8Node *node = push_array_no_zero(arena, String8Node, 1); + str8_list_push_node_set_string(list, node, string); + return(node); +} + +internal String8Node* +str8_list_push_front(Arena *arena, String8List *list, String8 string){ + String8Node *node = push_array_no_zero(arena, String8Node, 1); + str8_list_push_node_front_set_string(list, node, string); + return(node); +} + +internal void +str8_list_concat_in_place(String8List *list, String8List *to_push){ + if(to_push->node_count != 0){ + if (list->last){ + list->node_count += to_push->node_count; + list->total_size += to_push->total_size; + list->last->next = to_push->first; + list->last = to_push->last; + } + else{ + *list = *to_push; + } + MemoryZeroStruct(to_push); + } +} + +internal String8Node* +str8_list_push_aligner(Arena *arena, String8List *list, U64 min, U64 align){ + String8Node *node = push_array_no_zero(arena, String8Node, 1); + U64 new_size = list->total_size + min; + U64 increase_size = 0; + if (align > 1){ + // NOTE(allen): assert is power of 2 + Assert(((align - 1) & align) == 0); + U64 mask = align - 1; + new_size += mask; + new_size &= (~mask); + increase_size = new_size - list->total_size; + } + local_persist const U8 zeroes_buffer[64] = {0}; + Assert(increase_size <= ArrayCount(zeroes_buffer)); + SLLQueuePush(list->first, list->last, node); + list->node_count += 1; + list->total_size = new_size; + node->string.str = (U8*)zeroes_buffer; + node->string.size = increase_size; + return(node); +} + +internal String8Node* +str8_list_pushf(Arena *arena, String8List *list, char *fmt, ...){ + va_list args; + va_start(args, fmt); + String8 string = push_str8fv(arena, fmt, args); + String8Node *result = str8_list_push(arena, list, string); + va_end(args); + return(result); +} + +internal String8Node* +str8_list_push_frontf(Arena *arena, String8List *list, char *fmt, ...){ + va_list args; + va_start(args, fmt); + String8 string = push_str8fv(arena, fmt, args); + String8Node *result = str8_list_push_front(arena, list, string); + va_end(args); + return(result); +} + +internal String8List +str8_list_copy(Arena *arena, String8List *list){ + String8List result = {0}; + for (String8Node *node = list->first; + node != 0; + node = node->next){ + String8Node *new_node = push_array_no_zero(arena, String8Node, 1); + String8 new_string = push_str8_copy(arena, node->string); + str8_list_push_node_set_string(&result, new_node, new_string); + } + return(result); +} + +internal String8List +str8_split(Arena *arena, String8 string, U8 *split_chars, U64 split_char_count, StringSplitFlags flags){ + String8List list = {0}; + + B32 keep_empties = (flags & StringSplitFlag_KeepEmpties); + + U8 *ptr = string.str; + U8 *opl = string.str + string.size; + for (;ptr < opl;){ + U8 *first = ptr; + for (;ptr < opl; ptr += 1){ + U8 c = *ptr; + B32 is_split = 0; + for (U64 i = 0; i < split_char_count; i += 1){ + if (split_chars[i] == c){ + is_split = 1; + break; + } + } + if (is_split){ + break; + } + } + + String8 string = str8_range(first, ptr); + if (keep_empties || string.size > 0){ + str8_list_push(arena, &list, string); + } + ptr += 1; + } + + return(list); +} + +internal String8List +str8_split_by_string_chars(Arena *arena, String8 string, String8 split_chars, StringSplitFlags flags){ + String8List list = str8_split(arena, string, split_chars.str, split_chars.size, flags); + return list; +} + +internal String8List +str8_list_split_by_string_chars(Arena *arena, String8List list, String8 split_chars, StringSplitFlags flags){ + String8List result = {0}; + for (String8Node *node = list.first; node != 0; node = node->next){ + String8List split = str8_split_by_string_chars(arena, node->string, split_chars, flags); + str8_list_concat_in_place(&result, &split); + } + return result; +} + +internal String8 +str8_list_join(Arena *arena, String8List *list, StringJoin *optional_params){ + StringJoin join = {0}; + if (optional_params != 0){ + MemoryCopyStruct(&join, optional_params); + } + + U64 sep_count = 0; + if (list->node_count > 0){ + sep_count = list->node_count - 1; + } + + String8 result; + result.size = join.pre.size + join.post.size + sep_count*join.sep.size + list->total_size; + U8 *ptr = result.str = push_array_no_zero(arena, U8, result.size + 1); + + MemoryCopy(ptr, join.pre.str, join.pre.size); + ptr += join.pre.size; + for (String8Node *node = list->first; + node != 0; + node = node->next){ + MemoryCopy(ptr, node->string.str, node->string.size); + ptr += node->string.size; + if (node->next != 0){ + MemoryCopy(ptr, join.sep.str, join.sep.size); + ptr += join.sep.size; + } + } + MemoryCopy(ptr, join.post.str, join.post.size); + ptr += join.post.size; + + *ptr = 0; + + return(result); +} + +internal void +str8_list_from_flags(Arena *arena, String8List *list, + U32 flags, String8 *flag_string_table, U32 flag_string_count){ + for (U32 i = 0; i < flag_string_count; i += 1){ + U32 flag = (1 << i); + if (flags & flag){ + str8_list_push(arena, list, flag_string_table[i]); + } + } +} + +//////////////////////////////// +//~ rjf; String Arrays + +internal String8Array +str8_array_from_list(Arena *arena, String8List *list) +{ + String8Array array; + array.count = list->node_count; + array.v = push_array_no_zero(arena, String8, array.count); + U64 idx = 0; + for(String8Node *n = list->first; n != 0; n = n->next, idx += 1) + { + array.v[idx] = n->string; + } + return array; +} + +internal String8Array +str8_array_reserve(Arena *arena, U64 count) +{ + String8Array arr; + arr.count = 0; + arr.v = push_array(arena, String8, count); + return arr; +} + +//////////////////////////////// +//~ rjf: String Path Helpers + +internal String8 +str8_chop_last_slash(String8 string){ + if (string.size > 0){ + U8 *ptr = string.str + string.size - 1; + for (;ptr >= string.str; ptr -= 1){ + if (*ptr == '/' || *ptr == '\\'){ + break; + } + } + if (ptr >= string.str){ + string.size = (U64)(ptr - string.str); + } + else{ + string.size = 0; + } + } + return(string); +} + +internal String8 +str8_skip_last_slash(String8 string){ + if (string.size > 0){ + U8 *ptr = string.str + string.size - 1; + for (;ptr >= string.str; ptr -= 1){ + if (*ptr == '/' || *ptr == '\\'){ + break; + } + } + if (ptr >= string.str){ + ptr += 1; + string.size = (U64)(string.str + string.size - ptr); + string.str = ptr; + } + } + return(string); +} + +internal String8 +str8_chop_last_dot(String8 string) +{ + String8 result = string; + U64 p = string.size; + for (;p > 0;){ + p -= 1; + if (string.str[p] == '.'){ + result = str8_prefix(string, p); + break; + } + } + return(result); +} + +internal String8 +str8_skip_last_dot(String8 string){ + String8 result = string; + U64 p = string.size; + for (;p > 0;){ + p -= 1; + if (string.str[p] == '.'){ + result = str8_skip(string, p + 1); + break; + } + } + return(result); +} + +internal PathStyle +path_style_from_str8(String8 string){ + PathStyle result = PathStyle_Relative; + if (string.size >= 1 && string.str[0] == '/'){ + result = PathStyle_UnixAbsolute; + } + else if (string.size >= 2 && + char_is_alpha(string.str[0]) && + string.str[1] == ':'){ + if (string.size == 2 || + char_is_slash(string.str[2])){ + result = PathStyle_WindowsAbsolute; + } + } + return(result); +} + +internal String8List +str8_split_path(Arena *arena, String8 string){ + String8List result = str8_split(arena, string, (U8*)"/\\", 2, 0); + return(result); +} + +internal void +str8_path_list_resolve_dots_in_place(String8List *path, PathStyle style){ + Temp scratch = scratch_begin(0, 0); + + String8MetaNode *stack = 0; + String8MetaNode *free_meta_node = 0; + String8Node *first = path->first; + + MemoryZeroStruct(path); + for (String8Node *node = first, *next = 0; + node != 0; + node = next){ + // save next now + next = node->next; + + // cases: + if (node == first && style == PathStyle_WindowsAbsolute){ + goto save_without_stack; + } + if (node->string.size == 1 && node->string.str[0] == '.'){ + goto do_nothing; + } + if (node->string.size == 2 && node->string.str[0] == '.' && node->string.str[1] == '.'){ + if (stack != 0){ + goto eliminate_stack_top; + } + else{ + goto save_without_stack; + } + } + goto save_with_stack; + + + // handlers: + save_with_stack: + { + str8_list_push_node(path, node); + + String8MetaNode *stack_node = free_meta_node; + if (stack_node != 0){ + SLLStackPop(free_meta_node); + } + else{ + stack_node = push_array_no_zero(scratch.arena, String8MetaNode, 1); + } + SLLStackPush(stack, stack_node); + stack_node->node = node; + + continue; + } + + save_without_stack: + { + str8_list_push_node(path, node); + + continue; + } + + eliminate_stack_top: + { + path->node_count -= 1; + path->total_size -= stack->node->string.size; + + SLLStackPop(stack); + + if (stack == 0){ + path->last = path->first; + } + else{ + path->last = stack->node; + } + continue; + } + + do_nothing: continue; + } + scratch_end(scratch); +} + +internal String8 +str8_path_list_join_by_style(Arena *arena, String8List *path, PathStyle style){ + StringJoin params = {0}; + switch (style){ + case PathStyle_Relative: + case PathStyle_WindowsAbsolute: + { + params.sep = str8_lit("/"); + }break; + + case PathStyle_UnixAbsolute: + { + params.pre = str8_lit("/"); + params.sep = str8_lit("/"); + }break; + } + + String8 result = str8_list_join(arena, path, ¶ms); + return(result); +} + +internal 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; +} + +//////////////////////////////// +//~ rjf: UTF-8 & UTF-16 Decoding/Encoding + +read_only global U8 utf8_class[32] = { + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,2,2,2,2,3,3,4,5, +}; + +internal UnicodeDecode +utf8_decode(U8 *str, U64 max){ + UnicodeDecode result = {1, max_U32}; + U8 byte = str[0]; + U8 byte_class = utf8_class[byte >> 3]; + switch (byte_class) + { + case 1: + { + result.codepoint = byte; + }break; + case 2: + { + if (2 < max) + { + U8 cont_byte = str[1]; + if (utf8_class[cont_byte >> 3] == 0) + { + result.codepoint = (byte & bitmask5) << 6; + result.codepoint |= (cont_byte & bitmask6); + result.inc = 2; + } + } + }break; + case 3: + { + if (2 < max) + { + U8 cont_byte[2] = {str[1], str[2]}; + if (utf8_class[cont_byte[0] >> 3] == 0 && + utf8_class[cont_byte[1] >> 3] == 0) + { + result.codepoint = (byte & bitmask4) << 12; + result.codepoint |= ((cont_byte[0] & bitmask6) << 6); + result.codepoint |= (cont_byte[1] & bitmask6); + result.inc = 3; + } + } + }break; + case 4: + { + if (3 < max) + { + U8 cont_byte[3] = {str[1], str[2], str[3]}; + if (utf8_class[cont_byte[0] >> 3] == 0 && + utf8_class[cont_byte[1] >> 3] == 0 && + utf8_class[cont_byte[2] >> 3] == 0) + { + result.codepoint = (byte & bitmask3) << 18; + result.codepoint |= ((cont_byte[0] & bitmask6) << 12); + result.codepoint |= ((cont_byte[1] & bitmask6) << 6); + result.codepoint |= (cont_byte[2] & bitmask6); + result.inc = 4; + } + } + } + } + return(result); +} + +internal UnicodeDecode +utf16_decode(U16 *str, U64 max){ + UnicodeDecode result = {1, max_U32}; + result.codepoint = str[0]; + result.inc = 1; + if (max > 1 && 0xD800 <= str[0] && str[0] < 0xDC00 && 0xDC00 <= str[1] && str[1] < 0xE000){ + result.codepoint = ((str[0] - 0xD800) << 10) | (str[1] - 0xDC00) + 0x10000; + result.inc = 2; + } + return(result); +} + +internal U32 +utf8_encode(U8 *str, U32 codepoint){ + U32 inc = 0; + if (codepoint <= 0x7F){ + str[0] = (U8)codepoint; + inc = 1; + } + else if (codepoint <= 0x7FF){ + str[0] = (bitmask2 << 6) | ((codepoint >> 6) & bitmask5); + str[1] = bit8 | (codepoint & bitmask6); + inc = 2; + } + else if (codepoint <= 0xFFFF){ + str[0] = (bitmask3 << 5) | ((codepoint >> 12) & bitmask4); + str[1] = bit8 | ((codepoint >> 6) & bitmask6); + str[2] = bit8 | ( codepoint & bitmask6); + inc = 3; + } + else if (codepoint <= 0x10FFFF){ + str[0] = (bitmask4 << 4) | ((codepoint >> 18) & bitmask3); + str[1] = bit8 | ((codepoint >> 12) & bitmask6); + str[2] = bit8 | ((codepoint >> 6) & bitmask6); + str[3] = bit8 | ( codepoint & bitmask6); + inc = 4; + } + else{ + str[0] = '?'; + inc = 1; + } + return(inc); +} + +internal U32 +utf16_encode(U16 *str, U32 codepoint){ + U32 inc = 1; + if (codepoint == max_U32){ + str[0] = (U16)'?'; + } + else if (codepoint < 0x10000){ + str[0] = (U16)codepoint; + } + else{ + U32 v = codepoint - 0x10000; + str[0] = safe_cast_u16(0xD800 + (v >> 10)); + str[1] = safe_cast_u16(0xDC00 + (v & bitmask10)); + inc = 2; + } + return(inc); +} + +internal U32 +utf8_from_utf32_single(U8 *buffer, U32 character){ + return(utf8_encode(buffer, character)); +} + +//////////////////////////////// +//~ rjf: Unicode String Conversions + +internal String8 +str8_from_16(Arena *arena, String16 in){ + U64 cap = in.size*3; + U8 *str = push_array_no_zero(arena, U8, cap + 1); + U16 *ptr = in.str; + U16 *opl = ptr + in.size; + U64 size = 0; + UnicodeDecode consume; + for (;ptr < opl; ptr += consume.inc){ + consume = utf16_decode(ptr, opl - ptr); + size += utf8_encode(str + size, consume.codepoint); + } + str[size] = 0; + arena_pop(arena, (cap - size)); + return(str8(str, size)); +} + +internal String16 +str16_from_8(Arena *arena, String8 in){ + U64 cap = in.size*2; + U16 *str = push_array_no_zero(arena, U16, cap + 1); + U8 *ptr = in.str; + U8 *opl = ptr + in.size; + U64 size = 0; + UnicodeDecode consume; + for (;ptr < opl; ptr += consume.inc){ + consume = utf8_decode(ptr, opl - ptr); + size += utf16_encode(str + size, consume.codepoint); + } + str[size] = 0; + arena_pop(arena, (cap - size)*2); + return(str16(str, size)); +} + +internal String8 +str8_from_32(Arena *arena, String32 in){ + U64 cap = in.size*4; + U8 *str = push_array_no_zero(arena, U8, cap + 1); + U32 *ptr = in.str; + U32 *opl = ptr + in.size; + U64 size = 0; + for (;ptr < opl; ptr += 1){ + size += utf8_encode(str + size, *ptr); + } + str[size] = 0; + arena_pop(arena, (cap - size)); + return(str8(str, size)); +} + +internal String32 +str32_from_8(Arena *arena, String8 in){ + U64 cap = in.size; + U32 *str = push_array_no_zero(arena, U32, cap + 1); + U8 *ptr = in.str; + U8 *opl = ptr + in.size; + U64 size = 0; + UnicodeDecode consume; + for (;ptr < opl; ptr += consume.inc){ + consume = utf8_decode(ptr, opl - ptr); + str[size] = consume.codepoint; + size += 1; + } + str[size] = 0; + arena_pop(arena, (cap - size)*4); + return(str32(str, size)); +} + +//////////////////////////////// +//~ rjf: Basic Types & Space Enum -> String Conversions + +internal String8 +string_from_dimension(Dimension dimension){ + local_persist String8 strings[] = { + str8_lit_comp("X"), + str8_lit_comp("Y"), + str8_lit_comp("Z"), + str8_lit_comp("W"), + }; + String8 result = str8_lit("error"); + if ((U32)dimension < 4){ + result = strings[dimension]; + } + return(result); +} + +internal String8 +string_from_side(Side side){ + local_persist String8 strings[] = { + str8_lit_comp("Min"), + str8_lit_comp("Max"), + }; + String8 result = str8_lit("error"); + if ((U32)side < 2){ + result = strings[side]; + } + return(result); +} + +internal String8 +string_from_operating_system(OperatingSystem os){ + local_persist String8 strings[] = { + str8_lit_comp("Null"), + str8_lit_comp("Windows"), + str8_lit_comp("Linux"), + str8_lit_comp("Mac"), + }; + String8 result = str8_lit("error"); + if (os < OperatingSystem_COUNT){ + result = strings[os]; + } + return(result); +} + +internal String8 +string_from_architecture(Architecture arch){ + local_persist String8 strings[] = { + str8_lit_comp("Null"), + str8_lit_comp("x64"), + str8_lit_comp("x86"), + str8_lit_comp("arm64"), + str8_lit_comp("arm32"), + }; + String8 result = str8_lit("error"); + if (arch < Architecture_COUNT){ + result = strings[arch]; + } + return(result); +} + +//////////////////////////////// +//~ rjf: Time Types -> String + +internal String8 +string_from_week_day(WeekDay week_day){ + local_persist String8 strings[] = { + str8_lit_comp("Sun"), + str8_lit_comp("Mon"), + str8_lit_comp("Tue"), + str8_lit_comp("Wed"), + str8_lit_comp("Thu"), + str8_lit_comp("Fri"), + str8_lit_comp("Sat"), + }; + String8 result = str8_lit("Err"); + if ((U32)week_day < WeekDay_COUNT){ + result = strings[week_day]; + } + return(result); +} + +internal String8 +string_from_month(Month month){ + local_persist String8 strings[] = { + str8_lit_comp("Jan"), + str8_lit_comp("Feb"), + str8_lit_comp("Mar"), + str8_lit_comp("Apr"), + str8_lit_comp("May"), + str8_lit_comp("Jun"), + str8_lit_comp("Jul"), + str8_lit_comp("Aug"), + str8_lit_comp("Sep"), + str8_lit_comp("Oct"), + str8_lit_comp("Nov"), + str8_lit_comp("Dec"), + }; + String8 result = str8_lit("Err"); + if ((U32)month < Month_COUNT){ + result = strings[month]; + } + return(result); +} + +internal String8 +push_date_time_string(Arena *arena, DateTime *date_time){ + char *mon_str = (char*)string_from_month(date_time->month).str; + U32 adjusted_hour = date_time->hour%12; + if (adjusted_hour == 0){ + adjusted_hour = 12; + } + char *ampm = "am"; + if (date_time->hour >= 12){ + ampm = "pm"; + } + String8 result = push_str8f(arena, "%d %s %d, %02d:%02d:%02d %s", + date_time->day, mon_str, date_time->year, + adjusted_hour, date_time->min, date_time->sec, ampm); + return(result); +} + +internal String8 +push_file_name_date_time_string(Arena *arena, DateTime *date_time){ + char *mon_str = (char*)string_from_month(date_time->month).str; + String8 result = push_str8f(arena, "%d-%s-%0d--%02d-%02d-%02d", + date_time->year, mon_str, date_time->day, + date_time->hour, date_time->min, date_time->sec); + return(result); +} + +internal String8 +string_from_elapsed_time(Arena *arena, DateTime dt){ + Temp scratch = scratch_begin(&arena, 1); + String8List list = {0}; + if (dt.year){ + str8_list_pushf(scratch.arena, &list, "%dy", dt.year); + str8_list_pushf(scratch.arena, &list, "%um", dt.mon); + str8_list_pushf(scratch.arena, &list, "%ud", dt.day); + } else if (dt.mon){ + str8_list_pushf(scratch.arena, &list, "%um", dt.mon); + str8_list_pushf(scratch.arena, &list, "%ud", dt.day); + } else if (dt.day){ + str8_list_pushf(scratch.arena, &list, "%ud", dt.day); + } + str8_list_pushf(scratch.arena, &list, "%u:%u:%u:%u ms", dt.hour, dt.min, dt.sec, dt.msec); + StringJoin join = { str8_lit_comp(""), str8_lit_comp(" "), str8_lit_comp("") }; + String8 result = str8_list_join(arena, &list, &join); + scratch_end(scratch); + return(result); +} + +//////////////////////////////// +//~ rjf: Basic Text Indentation + +internal String8 +indented_from_string(Arena *arena, String8 string) +{ + Temp scratch = scratch_begin(&arena, 1); + read_only local_persist U8 indentation_bytes[] = " "; + String8List indented_strings = {0}; + S64 depth = 0; + S64 next_depth = 0; + U64 line_begin_off = 0; + for(U64 off = 0; off <= string.size; off += 1) + { + U8 byte = off width_this_line){ + String8 line = str8_substr(string, line_range); + if (wrapped_indent_level > 0){ + line = push_str8f(arena, "%.*s%S", wrapped_indent_level, spaces, line); + } + str8_list_push(arena, &list, line); + line_range = r1u64(line_range.max+1, candidate_line_range.max); + wrapped_indent_level = ClampTop(64, wrap_indent); + } + else{ + line_range = candidate_line_range; + } + } + } + if (line_range.min < string.size && line_range.max > line_range.min){ + String8 line = str8_substr(string, line_range); + if (wrapped_indent_level > 0){ + line = push_str8f(arena, "%.*s%S", wrapped_indent_level, spaces, line); + } + str8_list_push(arena, &list, line); + } + return list; +} + +//////////////////////////////// +//~ rjf: String <-> Color + +internal String8 +hex_string_from_rgba_4f32(Arena *arena, Vec4F32 rgba) +{ + String8 hex_string = push_str8f(arena, "%02x%02x%02x%02x", (U8)(rgba.x*255.f), (U8)(rgba.y*255.f), (U8)(rgba.z*255.f), (U8)(rgba.w*255.f)); + return hex_string; +} + +internal Vec4F32 +rgba_from_hex_string_4f32(String8 hex_string) +{ + U8 byte_text[8] = {0}; + U64 byte_text_idx = 0; + for(U64 idx = 0; idx < hex_string.size && byte_text_idx < ArrayCount(byte_text); idx += 1) + { + if(char_is_digit(hex_string.str[idx], 16)) + { + byte_text[byte_text_idx] = char_to_lower(hex_string.str[idx]); + byte_text_idx += 1; + } + } + U8 byte_vals[4] = {0}; + for(U64 idx = 0; idx < 4; idx += 1) + { + byte_vals[idx] = (U8)u64_from_str8(str8(&byte_text[idx*2], 2), 16); + } + Vec4F32 rgba = v4f32(byte_vals[0]/255.f, byte_vals[1]/255.f, byte_vals[2]/255.f, byte_vals[3]/255.f); + return rgba; +} + +//////////////////////////////// +//~ rjf: String Fuzzy Matching + +internal FuzzyMatchRangeList +fuzzy_match_find(Arena *arena, String8 needle, String8 haystack) +{ + FuzzyMatchRangeList result = {0}; + Temp scratch = scratch_begin(&arena, 1); + String8List needles = str8_split(scratch.arena, needle, (U8*)" ", 1, 0); + result.needle_part_count = needles.node_count; + for(String8Node *needle_n = needles.first; needle_n != 0; needle_n = needle_n->next) + { + U64 find_pos = 0; + for(;find_pos < haystack.size;) + { + find_pos = str8_find_needle(haystack, find_pos, needle_n->string, StringMatchFlag_CaseInsensitive); + B32 is_in_gathered_ranges = 0; + for(FuzzyMatchRangeNode *n = result.first; n != 0; n = n->next) + { + if(n->range.min <= find_pos && find_pos < n->range.max) + { + is_in_gathered_ranges = 1; + find_pos = n->range.max; + break; + } + } + if(!is_in_gathered_ranges) + { + break; + } + } + if(find_pos < haystack.size) + { + Rng1U64 range = r1u64(find_pos, find_pos+needle_n->string.size); + FuzzyMatchRangeNode *n = push_array(arena, FuzzyMatchRangeNode, 1); + n->range = range; + SLLQueuePush(result.first, result.last, n); + result.count += 1; + result.total_dim += dim_1u64(range); + } + } + scratch_end(scratch); + return result; +} + +internal FuzzyMatchRangeList +fuzzy_match_range_list_copy(Arena *arena, FuzzyMatchRangeList *src) +{ + FuzzyMatchRangeList dst = {0}; + for(FuzzyMatchRangeNode *src_n = src->first; src_n != 0; src_n = src_n->next) + { + FuzzyMatchRangeNode *dst_n = push_array(arena, FuzzyMatchRangeNode, 1); + SLLQueuePush(dst.first, dst.last, dst_n); + dst_n->range = src_n->range; + } + dst.count = src->count; + dst.needle_part_count = src->needle_part_count; + dst.total_dim = src->total_dim; + return dst; +} + +//////////////////////////////// +//~ NOTE(allen): Serialization Helpers + +internal void +str8_serial_begin(Arena *arena, String8List *srl){ + String8Node *node = push_array(arena, String8Node, 1); + node->string.str = push_array_no_zero(arena, U8, 0); + srl->first = srl->last = node; + srl->node_count = 1; + srl->total_size = 0; +} + +internal String8 +str8_serial_end(Arena *arena, String8List *srl){ + U64 size = srl->total_size; + U8 *out = push_array_no_zero(arena, U8, size); + str8_serial_write_to_dst(srl, out); + String8 result = str8(out, size); + return result; +} + +internal void +str8_serial_write_to_dst(String8List *srl, void *out){ + U8 *ptr = (U8*)out; + for (String8Node *node = srl->first; + node != 0; + node = node->next){ + U64 size = node->string.size; + MemoryCopy(ptr, node->string.str, size); + ptr += size; + } +} + +internal U64 +str8_serial_push_align(Arena *arena, String8List *srl, U64 align){ + Assert(IsPow2(align)); + + U64 pos = srl->total_size; + U64 new_pos = AlignPow2(pos, align); + U64 size = (new_pos - pos); + + if(size != 0) + { + U8 *buf = push_array(arena, U8, size); + + String8 *str = &srl->last->string; + if (str->str + str->size == buf){ + srl->last->string.size += size; + srl->total_size += size; + } + else{ + str8_list_push(arena, srl, str8(buf, size)); + } + } + return size; +} + +internal void * +str8_serial_push_size(Arena *arena, String8List *srl, U64 size) +{ + void *result = 0; + if(size != 0) + { + U8 *buf = push_array_no_zero(arena, U8, size); + String8 *str = &srl->last->string; + if (str->str + str->size == buf){ + srl->last->string.size += size; + srl->total_size += size; + } + else{ + str8_list_push(arena, srl, str8(buf, size)); + } + result = buf; + } + return result; +} + +internal void * +str8_serial_push_data(Arena *arena, String8List *srl, void *data, U64 size){ + void *result = str8_serial_push_size(arena, srl, size); + if(result != 0) + { + MemoryCopy(result, data, size); + } + return result; +} + +internal void +str8_serial_push_data_list(Arena *arena, String8List *srl, String8Node *first){ + for (String8Node *node = first; + node != 0; + node = node->next){ + str8_serial_push_data(arena, srl, node->string.str, node->string.size); + } +} + +internal void +str8_serial_push_u64(Arena *arena, String8List *srl, U64 x){ + U8 *buf = push_array_no_zero(arena, U8, 8); + MemoryCopy(buf, &x, 8); + String8 *str = &srl->last->string; + if (str->str + str->size == buf){ + srl->last->string.size += 8; + srl->total_size += 8; + } + else{ + str8_list_push(arena, srl, str8(buf, 8)); + } +} + +internal void +str8_serial_push_u32(Arena *arena, String8List *srl, U32 x){ + U8 *buf = push_array_no_zero(arena, U8, 4); + MemoryCopy(buf, &x, 4); + String8 *str = &srl->last->string; + if (str->str + str->size == buf){ + srl->last->string.size += 4; + srl->total_size += 4; + } + else{ + str8_list_push(arena, srl, str8(buf, 4)); + } +} + +internal void +str8_serial_push_u16(Arena *arena, String8List *srl, U16 x){ + str8_serial_push_data(arena, srl, &x, sizeof(x)); +} + +internal void +str8_serial_push_u8(Arena *arena, String8List *srl, U8 x){ + str8_serial_push_data(arena, srl, &x, sizeof(x)); +} + +internal void +str8_serial_push_cstr(Arena *arena, String8List *srl, String8 str){ + str8_serial_push_data(arena, srl, str.str, str.size); + str8_serial_push_u8(arena, srl, 0); +} + +internal void +str8_serial_push_string(Arena *arena, String8List *srl, String8 str){ + str8_serial_push_data(arena, srl, str.str, str.size); +} + +//////////////////////////////// +//~ rjf: Deserialization Helpers + +internal U64 +str8_deserial_read(String8 string, U64 off, void *read_dst, U64 read_size, U64 granularity) +{ + U64 bytes_left = string.size-Min(off, string.size); + U64 actually_readable_size = Min(bytes_left, read_size); + U64 legally_readable_size = actually_readable_size - actually_readable_size%granularity; + if(legally_readable_size > 0) + { + MemoryCopy(read_dst, string.str+off, legally_readable_size); + } + return legally_readable_size; +} + +internal U64 +str8_deserial_find_first_match(String8 string, U64 off, U16 scan_val) +{ + U64 cursor = off; + for (;;) { + U16 val = 0; + str8_deserial_read_struct(string, cursor, &val); + if (val == scan_val) { + break; + } + cursor += sizeof(val); + } + return cursor; +} + +internal void * +str8_deserial_get_raw_ptr(String8 string, U64 off, U64 size) +{ + void *raw_ptr = 0; + if (off + size <= string.size) { + raw_ptr = string.str + off; + } + return raw_ptr; +} + +internal U64 +str8_deserial_read_cstr(String8 string, U64 off, String8 *cstr_out) +{ + U64 cstr_size = 0; + if (off < string.size) { + U8 *ptr = string.str + off; + U8 *cap = string.str + string.size; + *cstr_out = str8_cstring_capped(ptr, cap); + cstr_size = (cstr_out->size + 1); + } + return cstr_size; +} + +internal U64 +str8_deserial_read_windows_utf16_string16(String8 string, U64 off, String16 *str_out) +{ + U64 null_off = str8_deserial_find_first_match(string, off, 0); + U64 size = null_off - off; + U16 *str = (U16 *)str8_deserial_get_raw_ptr(string, off, size); + U64 count = size / sizeof(*str); + *str_out = str16(str, count); + + U64 read_size_with_null = size + sizeof(*str); + return read_size_with_null; +} + +internal U64 +str8_deserial_read_block(String8 string, U64 off, U64 size, String8 *block_out) +{ + Rng1U64 range = rng_1u64(off, off + size); + *block_out = str8_substr(string, range); + return block_out->size; +} diff --git a/src/metagen/metagen_base/metagen_base_string.h b/src/metagen/metagen_base/metagen_base_strings.h similarity index 89% rename from src/metagen/metagen_base/metagen_base_string.h rename to src/metagen/metagen_base/metagen_base_strings.h index cb443c18..c68bdff6 100644 --- a/src/metagen/metagen_base/metagen_base_string.h +++ b/src/metagen/metagen_base/metagen_base_strings.h @@ -1,351 +1,381 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#ifndef BASE_STRING_H -#define BASE_STRING_H - -//////////////////////////////// -//~ rjf: Third Party Includes - -#define STB_SPRINTF_DECORATE(name) raddbg_##name -#include "third_party/stb/stb_sprintf.h" - -//////////////////////////////// -//~ rjf: String Types - -typedef struct String8 String8; -struct String8 -{ - U8 *str; - U64 size; -}; - -typedef struct String16 String16; -struct String16 -{ - U16 *str; - U64 size; -}; - -typedef struct String32 String32; -struct String32 -{ - U32 *str; - U64 size; -}; - -//////////////////////////////// -//~ rjf: String List & Array Types - -typedef struct String8Node String8Node; -struct String8Node -{ - String8Node *next; - String8 string; -}; - -typedef struct String8MetaNode String8MetaNode; -struct String8MetaNode -{ - String8MetaNode *next; - String8Node *node; -}; - -typedef struct String8List String8List; -struct String8List -{ - String8Node *first; - String8Node *last; - U64 node_count; - U64 total_size; -}; - -typedef struct String8Array String8Array; -struct String8Array -{ - String8 *strings; - U64 count; -}; - -//////////////////////////////// -//~ rjf: String Matching, Splitting, & Joining Types - -typedef U32 StringMatchFlags; -enum -{ - StringMatchFlag_CaseInsensitive = (1 << 0), - StringMatchFlag_RightSideSloppy = (1 << 1), - StringMatchFlag_SlashInsensitive = (1 << 2), -}; - -typedef U32 StringSplitFlags; -enum -{ - StringSplitFlag_KeepEmpties = (1 << 0), -}; - -typedef enum PathStyle -{ - PathStyle_Relative, - PathStyle_WindowsAbsolute, - PathStyle_UnixAbsolute, - -#if OS_WINDOWS - PathStyle_SystemAbsolute = PathStyle_WindowsAbsolute -#elif OS_LINUX - PathStyle_SystemAbsolute = PathStyle_UnixAbsolute -#else -# error "absolute path style is undefined for this OS" -#endif -} -PathStyle; - -typedef struct StringJoin StringJoin; -struct StringJoin -{ - String8 pre; - String8 sep; - String8 post; -}; - -//////////////////////////////// -//~ rjf: String Pair Types - -typedef struct String8TxtPtPair String8TxtPtPair; -struct String8TxtPtPair -{ - String8 string; - TxtPt pt; -}; - -//////////////////////////////// -//~ rjf: UTF Decoding Types - -typedef struct UnicodeDecode UnicodeDecode; -struct UnicodeDecode -{ - U32 inc; - U32 codepoint; -}; - -//////////////////////////////// -//~ rjf: Character Classification & Conversion Functions - -internal B32 char_is_space(U8 c); -internal B32 char_is_upper(U8 c); -internal B32 char_is_lower(U8 c); -internal B32 char_is_alpha(U8 c); -internal B32 char_is_slash(U8 c); -internal B32 char_is_digit(U8 c, U32 base); -internal U8 char_to_lower(U8 c); -internal U8 char_to_upper(U8 c); -internal U8 char_to_correct_slash(U8 c); - -//////////////////////////////// -//~ rjf: C-String Measurement - -internal U64 cstring8_length(U8 *c); -internal U64 cstring16_length(U16 *c); -internal U64 cstring32_length(U32 *c); - -//////////////////////////////// -//~ rjf: String Constructors - -#define str8_lit(S) str8((U8*)(S), sizeof(S) - 1) -#define str8_lit_comp(S) {(U8*)(S), sizeof(S) - 1,} -#define str8_varg(S) (int)((S).size), ((S).str) - -#define str8_array(S,C) str8((U8*)(S), sizeof(*(S))*(C)) -#define str8_array_fixed(S) str8((U8*)(S), sizeof(S)) -#define str8_struct(S) str8((U8*)(S), sizeof(*(S))) - -internal String8 str8(U8 *str, U64 size); -internal String8 str8_range(U8 *first, U8 *one_past_last); -internal String8 str8_zero(void); -internal String16 str16(U16 *str, U64 size); -internal String16 str16_range(U16 *first, U16 *one_past_last); -internal String16 str16_zero(void); -internal String32 str32(U32 *str, U64 size); -internal String32 str32_range(U32 *first, U32 *one_past_last); -internal String32 str32_zero(void); -internal String8 str8_cstring(char *c); -internal String16 str16_cstring(U16 *c); -internal String32 str32_cstring(U32 *c); -internal String8 str8_cstring_capped(void *cstr, void *cap); - -//////////////////////////////// -//~ rjf: String Stylization - -internal String8 upper_from_str8(Arena *arena, String8 string); -internal String8 lower_from_str8(Arena *arena, String8 string); -internal String8 backslashed_from_str8(Arena *arena, String8 string); - -//////////////////////////////// -//~ rjf: String Matching - -internal B32 str8_match(String8 a, String8 b, StringMatchFlags flags); -internal U64 str8_find_needle(String8 string, U64 start_pos, String8 needle, StringMatchFlags flags); -internal B32 str8_ends_with(String8 string, String8 end, StringMatchFlags flags); - -//////////////////////////////// -//~ rjf: String Slicing - -internal String8 str8_substr(String8 str, Rng1U64 range); -internal String8 str8_prefix(String8 str, U64 size); -internal String8 str8_skip(String8 str, U64 amt); -internal String8 str8_postfix(String8 str, U64 size); -internal String8 str8_chop(String8 str, U64 amt); -internal String8 str8_skip_chop_whitespace(String8 string); - -//////////////////////////////// -//~ rjf: String Formatting & Copying - -internal String8 push_str8_cat(Arena *arena, String8 s1, String8 s2); -internal String8 push_str8_copy(Arena *arena, String8 s); -internal String8 push_str8fv(Arena *arena, char *fmt, va_list args); -internal String8 push_str8f(Arena *arena, char *fmt, ...); - -//////////////////////////////// -//~ rjf: String <=> Integer Conversions - -//- rjf: string -> integer -internal S64 sign_from_str8(String8 string, String8 *string_tail); -internal B32 str8_is_integer(String8 string, U32 radix); -internal U64 u64_from_str8(String8 string, U32 radix); -internal S64 s64_from_str8(String8 string, U32 radix); -internal B32 try_u64_from_str8_c_rules(String8 string, U64 *x); -internal B32 try_s64_from_str8_c_rules(String8 string, S64 *x); - -//- rjf: string -> integer (base64 & base16) -internal U64 base64_size_from_data_size(U64 size_in_bytes); -internal U64 base64_from_data(U8 *dst, U8 *src, U64 src_size); -internal U64 base16_size_from_data_size(U64 size_in_bytes); -internal U64 base16_from_data(U8 *dst, U8 *src, U64 src_size); - -//- rjf: integer -> string -internal String8 str8_from_memory_size(Arena *arena, U64 z); -internal String8 str8_from_u64(Arena *arena, U64 u64, U32 radix, U8 min_digits, U8 digit_group_separator); -internal String8 str8_from_s64(Arena *arena, S64 s64, U32 radix, U8 min_digits, U8 digit_group_separator); - -//////////////////////////////// -//~ rjf: String <=> Float Conversions - -internal F64 f64_from_str8(String8 string); - -//////////////////////////////// -//~ rjf: String List Construction Functions - -internal String8Node* str8_list_push_node(String8List *list, String8Node *node); -internal String8Node* str8_list_push_node_set_string(String8List *list, String8Node *node, String8 string); -internal String8Node* str8_list_push_node_front(String8List *list, String8Node *node); -internal String8Node* str8_list_push_node_front_set_string(String8List *list, String8Node *node, String8 string); -internal String8Node* str8_list_push(Arena *arena, String8List *list, String8 string); -internal String8Node* str8_list_push_front(Arena *arena, String8List *list, String8 string); -internal void str8_list_concat_in_place(String8List *list, String8List *to_push); -internal String8Node* str8_list_push_aligner(Arena *arena, String8List *list, U64 min, U64 align); -internal String8Node* str8_list_pushf(Arena *arena, String8List *list, char *fmt, ...); -internal String8Node* str8_list_push_frontf(Arena *arena, String8List *list, char *fmt, ...); -internal String8List str8_list_copy(Arena *arena, String8List *list); -#define str8_list_first(list) ((list)->first ? (list)->first->string : str8_zero()) - -//////////////////////////////// -//~ rjf: String Splitting & Joining - -internal String8List str8_split(Arena *arena, String8 string, U8 *split_chars, U64 split_char_count, StringSplitFlags flags); -internal String8List str8_split_by_string_chars(Arena *arena, String8 string, String8 split_chars, StringSplitFlags flags); -internal String8List str8_list_split_by_string_chars(Arena *arena, String8List list, String8 split_chars, StringSplitFlags flags); -internal String8 str8_list_join(Arena *arena, String8List *list, StringJoin *optional_params); -internal void str8_list_from_flags(Arena *arena, String8List *list, U32 flags, String8 *flag_string_table, U32 flag_string_count); - -//////////////////////////////// -//~ rjf; String Arrays - -internal String8Array str8_array_from_list(Arena *arena, String8List *list); -internal String8Array str8_array_reserve(Arena *arena, U64 count); - -//////////////////////////////// -//~ rjf: String Path Helpers - -internal String8 str8_chop_last_slash(String8 string); -internal String8 str8_skip_last_slash(String8 string); -internal String8 str8_chop_last_dot(String8 string); -internal String8 str8_skip_last_dot(String8 string); - -internal PathStyle path_style_from_str8(String8 string); -internal String8List str8_split_path(Arena *arena, String8 string); -internal void str8_path_list_resolve_dots_in_place(String8List *path, PathStyle style); -internal String8 str8_path_list_join_by_style(Arena *arena, String8List *path, PathStyle style); - -internal String8TxtPtPair str8_txt_pt_pair_from_string(String8 string); - -//////////////////////////////// -//~ rjf: UTF-8 & UTF-16 Decoding/Encoding - -internal UnicodeDecode utf8_decode(U8 *str, U64 max); -internal UnicodeDecode utf16_decode(U16 *str, U64 max); -internal U32 utf8_encode(U8 *str, U32 codepoint); -internal U32 utf16_encode(U16 *str, U32 codepoint); -internal U32 utf8_from_utf32_single(U8 *buffer, U32 character); - -//////////////////////////////// -//~ rjf: Unicode String Conversions - -internal String8 str8_from_16(Arena *arena, String16 in); -internal String16 str16_from_8(Arena *arena, String8 in); -internal String8 str8_from_32(Arena *arena, String32 in); -internal String32 str32_from_8(Arena *arena, String8 in); - -//////////////////////////////// -//~ rjf: Basic Types & Space Enum -> String Conversions - -internal String8 string_from_dimension(Dimension dimension); -internal String8 string_from_side(Side side); -internal String8 string_from_operating_system(OperatingSystem os); -internal String8 string_from_architecture(Architecture arch); - -//////////////////////////////// -//~ rjf: Time Types -> String - -internal String8 string_from_week_day(WeekDay week_day); -internal String8 string_from_month(Month month); -internal String8 push_date_time_string(Arena *arena, DateTime *date_time); -internal String8 push_file_name_date_time_string(Arena *arena, DateTime *date_time); -internal String8 string_from_elapsed_time(Arena *arena, DateTime dt); - -//////////////////////////////// -//~ rjf: String <-> Color - -internal String8 hex_string_from_rgba_4f32(Arena *arena, Vec4F32 rgba); -internal Vec4F32 rgba_from_hex_string_4f32(String8 hex_string); - -//////////////////////////////// -//~ NOTE(allen): Serialization Helpers - -internal void str8_serial_begin(Arena *arena, String8List *srl); -internal String8 str8_serial_end(Arena *arena, String8List *srl); -internal void str8_serial_write_to_dst(String8List *srl, void *out); -internal U64 str8_serial_push_align(Arena *arena, String8List *srl, U64 align); -internal void * str8_serial_push_size(Arena *arena, String8List *srl, U64 size); -internal void * str8_serial_push_data(Arena *arena, String8List *srl, void *data, U64 size); -internal void str8_serial_push_data_list(Arena *arena, String8List *srl, String8Node *first); -internal void str8_serial_push_u64(Arena *arena, String8List *srl, U64 x); -internal void str8_serial_push_u32(Arena *arena, String8List *srl, U32 x); -internal void str8_serial_push_u16(Arena *arena, String8List *srl, U16 x); -internal void str8_serial_push_u8(Arena *arena, String8List *srl, U8 x); -internal void str8_serial_push_cstr(Arena *arena, String8List *srl, String8 str); -internal void str8_serial_push_string(Arena *arena, String8List *srl, String8 str); -#define str8_serial_push_array(arena, srl, ptr, count) str8_serial_push_data(arena, srl, ptr, sizeof(*(ptr)) * (count)) -#define str8_serial_push_struct(arena, srl, ptr) str8_serial_push_array(arena, srl, ptr, 1) - -//////////////////////////////// -//~ rjf: Deserialization Helpers - -internal U64 str8_deserial_read(String8 string, U64 off, void *read_dst, U64 read_size, U64 granularity); -internal U64 str8_deserial_find_first_match(String8 string, U64 off, U16 scan_val); -internal void * str8_deserial_get_raw_ptr(String8 string, U64 off, U64 size);internal U64 str8_deserial_read_cstr(String8 string, U64 off, String8 *cstr_out); -internal U64 str8_deserial_read_windows_utf16_string16(String8 string, U64 off, String16 *str_out); -internal U64 str8_deserial_read_block(String8 string, U64 off, U64 size, String8 *block_out); -#define str8_deserial_read_array(string, off, ptr, count) str8_deserial_read((string), (off), (ptr), sizeof(*(ptr))*(count), sizeof(*(ptr))) -#define str8_deserial_read_struct(string, off, ptr) str8_deserial_read((string), (off), (ptr), sizeof(*(ptr)), sizeof(*(ptr))) - -#endif // BASE_STRING_H +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef BASE_STRINGS_H +#define BASE_STRINGS_H + +//////////////////////////////// +//~ rjf: Third Party Includes + +#define STB_SPRINTF_DECORATE(name) raddbg_##name +#include "third_party/stb/stb_sprintf.h" + +//////////////////////////////// +//~ rjf: String Types + +typedef struct String8 String8; +struct String8 +{ + U8 *str; + U64 size; +}; + +typedef struct String16 String16; +struct String16 +{ + U16 *str; + U64 size; +}; + +typedef struct String32 String32; +struct String32 +{ + U32 *str; + U64 size; +}; + +//////////////////////////////// +//~ rjf: String List & Array Types + +typedef struct String8Node String8Node; +struct String8Node +{ + String8Node *next; + String8 string; +}; + +typedef struct String8MetaNode String8MetaNode; +struct String8MetaNode +{ + String8MetaNode *next; + String8Node *node; +}; + +typedef struct String8List String8List; +struct String8List +{ + String8Node *first; + String8Node *last; + U64 node_count; + U64 total_size; +}; + +typedef struct String8Array String8Array; +struct String8Array +{ + String8 *v; + U64 count; +}; + +//////////////////////////////// +//~ rjf: String Matching, Splitting, & Joining Types + +typedef U32 StringMatchFlags; +enum +{ + StringMatchFlag_CaseInsensitive = (1 << 0), + StringMatchFlag_RightSideSloppy = (1 << 1), + StringMatchFlag_SlashInsensitive = (1 << 2), +}; + +typedef U32 StringSplitFlags; +enum +{ + StringSplitFlag_KeepEmpties = (1 << 0), +}; + +typedef enum PathStyle +{ + PathStyle_Relative, + PathStyle_WindowsAbsolute, + PathStyle_UnixAbsolute, + +#if OS_WINDOWS + PathStyle_SystemAbsolute = PathStyle_WindowsAbsolute +#elif OS_LINUX + PathStyle_SystemAbsolute = PathStyle_UnixAbsolute +#else +# error "absolute path style is undefined for this OS" +#endif +} +PathStyle; + +typedef struct StringJoin StringJoin; +struct StringJoin +{ + String8 pre; + String8 sep; + String8 post; +}; + +//////////////////////////////// +//~ rjf: String Pair Types + +typedef struct String8TxtPtPair String8TxtPtPair; +struct String8TxtPtPair +{ + String8 string; + TxtPt pt; +}; + +//////////////////////////////// +//~ rjf: UTF Decoding Types + +typedef struct UnicodeDecode UnicodeDecode; +struct UnicodeDecode +{ + U32 inc; + U32 codepoint; +}; + +//////////////////////////////// +//~ rjf: String Fuzzy Matching Types + +typedef struct FuzzyMatchRangeNode FuzzyMatchRangeNode; +struct FuzzyMatchRangeNode +{ + FuzzyMatchRangeNode *next; + Rng1U64 range; +}; + +typedef struct FuzzyMatchRangeList FuzzyMatchRangeList; +struct FuzzyMatchRangeList +{ + FuzzyMatchRangeNode *first; + FuzzyMatchRangeNode *last; + U64 count; + U64 needle_part_count; + U64 total_dim; +}; + +//////////////////////////////// +//~ rjf: Character Classification & Conversion Functions + +internal B32 char_is_space(U8 c); +internal B32 char_is_upper(U8 c); +internal B32 char_is_lower(U8 c); +internal B32 char_is_alpha(U8 c); +internal B32 char_is_slash(U8 c); +internal B32 char_is_digit(U8 c, U32 base); +internal U8 char_to_lower(U8 c); +internal U8 char_to_upper(U8 c); +internal U8 char_to_correct_slash(U8 c); + +//////////////////////////////// +//~ rjf: C-String Measurement + +internal U64 cstring8_length(U8 *c); +internal U64 cstring16_length(U16 *c); +internal U64 cstring32_length(U32 *c); + +//////////////////////////////// +//~ rjf: String Constructors + +#define str8_lit(S) str8((U8*)(S), sizeof(S) - 1) +#define str8_lit_comp(S) {(U8*)(S), sizeof(S) - 1,} +#define str8_varg(S) (int)((S).size), ((S).str) + +#define str8_array(S,C) str8((U8*)(S), sizeof(*(S))*(C)) +#define str8_array_fixed(S) str8((U8*)(S), sizeof(S)) +#define str8_struct(S) str8((U8*)(S), sizeof(*(S))) + +internal String8 str8(U8 *str, U64 size); +internal String8 str8_range(U8 *first, U8 *one_past_last); +internal String8 str8_zero(void); +internal String16 str16(U16 *str, U64 size); +internal String16 str16_range(U16 *first, U16 *one_past_last); +internal String16 str16_zero(void); +internal String32 str32(U32 *str, U64 size); +internal String32 str32_range(U32 *first, U32 *one_past_last); +internal String32 str32_zero(void); +internal String8 str8_cstring(char *c); +internal String16 str16_cstring(U16 *c); +internal String32 str32_cstring(U32 *c); +internal String8 str8_cstring_capped(void *cstr, void *cap); + +//////////////////////////////// +//~ rjf: String Stylization + +internal String8 upper_from_str8(Arena *arena, String8 string); +internal String8 lower_from_str8(Arena *arena, String8 string); +internal String8 backslashed_from_str8(Arena *arena, String8 string); + +//////////////////////////////// +//~ rjf: String Matching + +internal B32 str8_match(String8 a, String8 b, StringMatchFlags flags); +internal U64 str8_find_needle(String8 string, U64 start_pos, String8 needle, StringMatchFlags flags); +internal B32 str8_ends_with(String8 string, String8 end, StringMatchFlags flags); + +//////////////////////////////// +//~ rjf: String Slicing + +internal String8 str8_substr(String8 str, Rng1U64 range); +internal String8 str8_prefix(String8 str, U64 size); +internal String8 str8_skip(String8 str, U64 amt); +internal String8 str8_postfix(String8 str, U64 size); +internal String8 str8_chop(String8 str, U64 amt); +internal String8 str8_skip_chop_whitespace(String8 string); + +//////////////////////////////// +//~ rjf: String Formatting & Copying + +internal String8 push_str8_cat(Arena *arena, String8 s1, String8 s2); +internal String8 push_str8_copy(Arena *arena, String8 s); +internal String8 push_str8fv(Arena *arena, char *fmt, va_list args); +internal String8 push_str8f(Arena *arena, char *fmt, ...); + +//////////////////////////////// +//~ rjf: String <=> Integer Conversions + +//- rjf: string -> integer +internal S64 sign_from_str8(String8 string, String8 *string_tail); +internal B32 str8_is_integer(String8 string, U32 radix); +internal U64 u64_from_str8(String8 string, U32 radix); +internal S64 s64_from_str8(String8 string, U32 radix); +internal B32 try_u64_from_str8_c_rules(String8 string, U64 *x); +internal B32 try_s64_from_str8_c_rules(String8 string, S64 *x); + +//- rjf: integer -> string +internal String8 str8_from_memory_size(Arena *arena, U64 z); +internal String8 str8_from_u64(Arena *arena, U64 u64, U32 radix, U8 min_digits, U8 digit_group_separator); +internal String8 str8_from_s64(Arena *arena, S64 s64, U32 radix, U8 min_digits, U8 digit_group_separator); + +//////////////////////////////// +//~ rjf: String <=> Float Conversions + +internal F64 f64_from_str8(String8 string); + +//////////////////////////////// +//~ rjf: String List Construction Functions + +internal String8Node* str8_list_push_node(String8List *list, String8Node *node); +internal String8Node* str8_list_push_node_set_string(String8List *list, String8Node *node, String8 string); +internal String8Node* str8_list_push_node_front(String8List *list, String8Node *node); +internal String8Node* str8_list_push_node_front_set_string(String8List *list, String8Node *node, String8 string); +internal String8Node* str8_list_push(Arena *arena, String8List *list, String8 string); +internal String8Node* str8_list_push_front(Arena *arena, String8List *list, String8 string); +internal void str8_list_concat_in_place(String8List *list, String8List *to_push); +internal String8Node* str8_list_push_aligner(Arena *arena, String8List *list, U64 min, U64 align); +internal String8Node* str8_list_pushf(Arena *arena, String8List *list, char *fmt, ...); +internal String8Node* str8_list_push_frontf(Arena *arena, String8List *list, char *fmt, ...); +internal String8List str8_list_copy(Arena *arena, String8List *list); +#define str8_list_first(list) ((list)->first ? (list)->first->string : str8_zero()) + +//////////////////////////////// +//~ rjf: String Splitting & Joining + +internal String8List str8_split(Arena *arena, String8 string, U8 *split_chars, U64 split_char_count, StringSplitFlags flags); +internal String8List str8_split_by_string_chars(Arena *arena, String8 string, String8 split_chars, StringSplitFlags flags); +internal String8List str8_list_split_by_string_chars(Arena *arena, String8List list, String8 split_chars, StringSplitFlags flags); +internal String8 str8_list_join(Arena *arena, String8List *list, StringJoin *optional_params); +internal void str8_list_from_flags(Arena *arena, String8List *list, U32 flags, String8 *flag_string_table, U32 flag_string_count); + +//////////////////////////////// +//~ rjf; String Arrays + +internal String8Array str8_array_from_list(Arena *arena, String8List *list); +internal String8Array str8_array_reserve(Arena *arena, U64 count); + +//////////////////////////////// +//~ rjf: String Path Helpers + +internal String8 str8_chop_last_slash(String8 string); +internal String8 str8_skip_last_slash(String8 string); +internal String8 str8_chop_last_dot(String8 string); +internal String8 str8_skip_last_dot(String8 string); + +internal PathStyle path_style_from_str8(String8 string); +internal String8List str8_split_path(Arena *arena, String8 string); +internal void str8_path_list_resolve_dots_in_place(String8List *path, PathStyle style); +internal String8 str8_path_list_join_by_style(Arena *arena, String8List *path, PathStyle style); + +internal String8TxtPtPair str8_txt_pt_pair_from_string(String8 string); + +//////////////////////////////// +//~ rjf: UTF-8 & UTF-16 Decoding/Encoding + +internal UnicodeDecode utf8_decode(U8 *str, U64 max); +internal UnicodeDecode utf16_decode(U16 *str, U64 max); +internal U32 utf8_encode(U8 *str, U32 codepoint); +internal U32 utf16_encode(U16 *str, U32 codepoint); +internal U32 utf8_from_utf32_single(U8 *buffer, U32 character); + +//////////////////////////////// +//~ rjf: Unicode String Conversions + +internal String8 str8_from_16(Arena *arena, String16 in); +internal String16 str16_from_8(Arena *arena, String8 in); +internal String8 str8_from_32(Arena *arena, String32 in); +internal String32 str32_from_8(Arena *arena, String8 in); + +//////////////////////////////// +//~ rjf: Basic Types & Space Enum -> String Conversions + +internal String8 string_from_dimension(Dimension dimension); +internal String8 string_from_side(Side side); +internal String8 string_from_operating_system(OperatingSystem os); +internal String8 string_from_architecture(Architecture arch); + +//////////////////////////////// +//~ rjf: Time Types -> String + +internal String8 string_from_week_day(WeekDay week_day); +internal String8 string_from_month(Month month); +internal String8 push_date_time_string(Arena *arena, DateTime *date_time); +internal String8 push_file_name_date_time_string(Arena *arena, DateTime *date_time); +internal String8 string_from_elapsed_time(Arena *arena, DateTime dt); + +//////////////////////////////// +//~ rjf: Basic Text Indentation + +internal String8 indented_from_string(Arena *arena, String8 string); + +//////////////////////////////// +//~ rjf: Text Wrapping + +internal String8List wrapped_lines_from_string(Arena *arena, String8 string, U64 first_line_max_width, U64 max_width, U64 wrap_indent); + +//////////////////////////////// +//~ rjf: String <-> Color + +internal String8 hex_string_from_rgba_4f32(Arena *arena, Vec4F32 rgba); +internal Vec4F32 rgba_from_hex_string_4f32(String8 hex_string); + +//////////////////////////////// +//~ rjf: String Fuzzy Matching + +internal FuzzyMatchRangeList fuzzy_match_find(Arena *arena, String8 needle, String8 haystack); +internal FuzzyMatchRangeList fuzzy_match_range_list_copy(Arena *arena, FuzzyMatchRangeList *src); + +//////////////////////////////// +//~ NOTE(allen): Serialization Helpers + +internal void str8_serial_begin(Arena *arena, String8List *srl); +internal String8 str8_serial_end(Arena *arena, String8List *srl); +internal void str8_serial_write_to_dst(String8List *srl, void *out); +internal U64 str8_serial_push_align(Arena *arena, String8List *srl, U64 align); +internal void * str8_serial_push_size(Arena *arena, String8List *srl, U64 size); +internal void * str8_serial_push_data(Arena *arena, String8List *srl, void *data, U64 size); +internal void str8_serial_push_data_list(Arena *arena, String8List *srl, String8Node *first); +internal void str8_serial_push_u64(Arena *arena, String8List *srl, U64 x); +internal void str8_serial_push_u32(Arena *arena, String8List *srl, U32 x); +internal void str8_serial_push_u16(Arena *arena, String8List *srl, U16 x); +internal void str8_serial_push_u8(Arena *arena, String8List *srl, U8 x); +internal void str8_serial_push_cstr(Arena *arena, String8List *srl, String8 str); +internal void str8_serial_push_string(Arena *arena, String8List *srl, String8 str); +#define str8_serial_push_array(arena, srl, ptr, count) str8_serial_push_data(arena, srl, ptr, sizeof(*(ptr)) * (count)) +#define str8_serial_push_struct(arena, srl, ptr) str8_serial_push_array(arena, srl, ptr, 1) + +//////////////////////////////// +//~ rjf: Deserialization Helpers + +internal U64 str8_deserial_read(String8 string, U64 off, void *read_dst, U64 read_size, U64 granularity); +internal U64 str8_deserial_find_first_match(String8 string, U64 off, U16 scan_val); +internal void * str8_deserial_get_raw_ptr(String8 string, U64 off, U64 size);internal U64 str8_deserial_read_cstr(String8 string, U64 off, String8 *cstr_out); +internal U64 str8_deserial_read_windows_utf16_string16(String8 string, U64 off, String16 *str_out); +internal U64 str8_deserial_read_block(String8 string, U64 off, U64 size, String8 *block_out); +#define str8_deserial_read_array(string, off, ptr, count) str8_deserial_read((string), (off), (ptr), sizeof(*(ptr))*(count), sizeof(*(ptr))) +#define str8_deserial_read_struct(string, off, ptr) str8_deserial_read((string), (off), (ptr), sizeof(*(ptr)), sizeof(*(ptr))) + +#endif // BASE_STRINGS_H diff --git a/src/metagen/metagen_base/metagen_base_thread_context.c b/src/metagen/metagen_base/metagen_base_thread_context.c index 1efd6e60..1a3f84ed 100644 --- a/src/metagen/metagen_base/metagen_base_thread_context.c +++ b/src/metagen/metagen_base/metagen_base_thread_context.c @@ -1,78 +1,87 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -//////////////////////////////// -// NOTE(allen): Thread Context Functions - -C_LINKAGE thread_static TCTX* tctx_thread_local; -#if !SUPPLEMENT_UNIT -C_LINKAGE thread_static TCTX* tctx_thread_local = 0; -#endif - -internal void -tctx_init_and_equip(TCTX *tctx){ - MemoryZeroStruct(tctx); - Arena **arena_ptr = tctx->arenas; - for (U64 i = 0; i < ArrayCount(tctx->arenas); i += 1, arena_ptr += 1){ - *arena_ptr = arena_alloc(); - } - tctx_thread_local = tctx; -} - -internal TCTX* -tctx_get_equipped(void){ - return(tctx_thread_local); -} - -internal 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 < ArrayCount(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); -} - -internal void -tctx_set_thread_name(String8 string){ - TCTX *tctx = tctx_get_equipped(); - U64 size = ClampTop(string.size, sizeof(tctx->thread_name)); - MemoryCopy(tctx->thread_name, string.str, size); - tctx->thread_name_size = size; -} - -internal String8 -tctx_get_thread_name(void){ - TCTX *tctx = tctx_get_equipped(); - String8 result = str8(tctx->thread_name, tctx->thread_name_size); - return(result); -} - -internal 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; -} - -internal 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; -} +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +//////////////////////////////// +// NOTE(allen): Thread Context Functions + +C_LINKAGE thread_static TCTX* tctx_thread_local; +#if !BUILD_SUPPLEMENTARY_UNIT +C_LINKAGE thread_static TCTX* tctx_thread_local = 0; +#endif + +internal void +tctx_init_and_equip(TCTX *tctx){ + MemoryZeroStruct(tctx); + Arena **arena_ptr = tctx->arenas; + for (U64 i = 0; i < ArrayCount(tctx->arenas); i += 1, arena_ptr += 1){ + *arena_ptr = arena_alloc(); + } + tctx_thread_local = tctx; +} + +internal void +tctx_release(void) +{ + for(U64 i = 0; i < ArrayCount(tctx_thread_local->arenas); i += 1) + { + arena_release(tctx_thread_local->arenas[i]); + } +} + +internal TCTX* +tctx_get_equipped(void){ + return(tctx_thread_local); +} + +internal 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 < ArrayCount(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); +} + +internal void +tctx_set_thread_name(String8 string){ + TCTX *tctx = tctx_get_equipped(); + U64 size = ClampTop(string.size, sizeof(tctx->thread_name)); + MemoryCopy(tctx->thread_name, string.str, size); + tctx->thread_name_size = size; +} + +internal String8 +tctx_get_thread_name(void){ + TCTX *tctx = tctx_get_equipped(); + String8 result = str8(tctx->thread_name, tctx->thread_name_size); + return(result); +} + +internal 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; +} + +internal 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; +} diff --git a/src/metagen/metagen_base/metagen_base_thread_context.h b/src/metagen/metagen_base/metagen_base_thread_context.h index ce05d47f..90a396fe 100644 --- a/src/metagen/metagen_base/metagen_base_thread_context.h +++ b/src/metagen/metagen_base/metagen_base_thread_context.h @@ -1,40 +1,41 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#ifndef BASE_THREAD_CONTEXT_H -#define BASE_THREAD_CONTEXT_H - -//////////////////////////////// -// 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 - -internal void tctx_init_and_equip(TCTX *tctx); -internal TCTX* tctx_get_equipped(void); - -internal Arena* tctx_get_scratch(Arena **conflicts, U64 count); - -internal void tctx_set_thread_name(String8 name); -internal String8 tctx_get_thread_name(void); - -internal void tctx_write_srcloc(char *file_name, U64 line_number); -internal void tctx_read_srcloc(char **file_name, U64 *line_number); -#define tctx_write_this_srcloc() tctx_write_srcloc(__FILE__, __LINE__) - -#define scratch_begin(conflicts, count) temp_begin(tctx_get_scratch((conflicts), (count))) -#define scratch_end(scratch) temp_end(scratch) - -#endif //BASE_THREAD_CONTEXT_H +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef BASE_THREAD_CONTEXT_H +#define BASE_THREAD_CONTEXT_H + +//////////////////////////////// +// 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 + +internal void tctx_init_and_equip(TCTX *tctx); +internal void tctx_release(void); +internal TCTX* tctx_get_equipped(void); + +internal Arena* tctx_get_scratch(Arena **conflicts, U64 count); + +internal void tctx_set_thread_name(String8 name); +internal String8 tctx_get_thread_name(void); + +internal void tctx_write_srcloc(char *file_name, U64 line_number); +internal void tctx_read_srcloc(char **file_name, U64 *line_number); +#define tctx_write_this_srcloc() tctx_write_srcloc(__FILE__, __LINE__) + +#define scratch_begin(conflicts, count) temp_begin(tctx_get_scratch((conflicts), (count))) +#define scratch_end(scratch) temp_end(scratch) + +#endif // BASE_THREAD_CONTEXT_H diff --git a/src/metagen/metagen_main.c b/src/metagen/metagen_main.c index 1ff486e8..d8acda1f 100644 --- a/src/metagen/metagen_main.c +++ b/src/metagen/metagen_main.c @@ -1,661 +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 - -int main(int argument_count, char **arguments) -{ - local_persist TCTX main_tctx = {0}; - tctx_init_and_equip(&main_tctx); - os_init(argument_count, arguments); - - ////////////////////////////// - //- rjf: set up state - // - MG_MsgList msgs = {0}; - mg_arena = arena_alloc__sized(GB(64), 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_string_from_system_path(mg_arena, OS_SystemPath_Binary); - 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); - MD_TokenizeResult tokenize = md_tokenize_from_text(mg_arena, data); - MD_ParseResult parse = md_parse_from_text_tokens(mg_arena, file_path, data, tokenize.tokens); - for(MD_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 MD_MsgKind_Warning: {msg_kind_string = str8_lit("warning");}break; - case MD_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) - { - MD_Node *file = n->v.root; - for(MD_EachNode(node, file->first)) - { - MD_Node *table_tag = md_tag_from_string(node, str8_lit("table"), 0); - if(!md_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) - { - MD_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(MD_EachNode(node, file->first)) - { - if(md_node_has_tag(node, str8_lit("option"), 0)) - { - if(str8_match(node->string, str8_lit("library"), 0)) - { - layer->is_library = 1; - } - } - if(md_node_has_tag(node, str8_lit("gen_folder"), 0)) - { - layer->gen_folder_name = node->string; - } - if(md_node_has_tag(node, str8_lit("h_name"), 0)) - { - layer->h_name_override = node->string; - } - if(md_node_has_tag(node, str8_lit("c_name"), 0)) - { - layer->c_name_override = node->string; - } - if(md_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(md_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(md_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(md_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) - { - MD_Node *file = n->v.root; - for(MD_EachNode(node, file->first)) - { - MD_Node *tag = md_tag_from_string(node, str8_lit("enum"), 0); - if(!md_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) - { - MD_Node *file = n->v.root; - for(MD_EachNode(node, file->first)) - { - MD_Node *tag = md_tag_from_string(node, str8_lit("xlist"), 0); - if(!md_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) - { - MD_Node *file = n->v.root; - for(MD_EachNode(node, file->first)) - { - if(md_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) - { - MD_Node *file = n->v.root; - for(MD_EachNode(node, file->first)) - { - MD_Node *tag = md_tag_from_string(node, str8_lit("data"), 0); - if(!md_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(!md_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) - { - MD_Node *file = n->v.root; - for(MD_EachNode(node, file->first)) - { - MD_Node *tag = md_tag_from_string(node, str8_lit("enum2string_switch"), 0); - if(!md_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(\"\");\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) - { - MD_Node *file = n->v.root; - for(MD_EachNode(node, file->first)) - { - MD_Node *tag = md_tag_from_string(node, str8_lit("gen"), 0); - if(!md_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 = md_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) - { - MD_Node *file = n->v.root; - for(MD_EachNode(node, file->first)) - { - if(md_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(md_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) - { - MD_Node *file = n->v.root; - for(MD_EachNode(node, file->first)) - { - //- rjf: generate markdown page - if(md_node_has_tag(node, str8_lit("markdown"), 0)) - { - String8List md_strs = {0}; - for(MD_Node *piece = node->first; !md_node_is_nil(piece); piece = piece->next) - { - if(md_node_has_tag(piece, str8_lit("title"), 0)) - { - str8_list_pushf(mg_arena, &md_strs, "# %S\n\n", piece->string); - } - if(md_node_has_tag(piece, str8_lit("subtitle"), 0)) - { - str8_list_pushf(mg_arena, &md_strs, "## %S\n\n", piece->string); - } - if(md_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(md_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)); - } - - return 0; -} +// 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); + MD_TokenizeResult tokenize = md_tokenize_from_text(mg_arena, data); + MD_ParseResult parse = md_parse_from_text_tokens(mg_arena, file_path, data, tokenize.tokens); + for(MD_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 MD_MsgKind_Warning: {msg_kind_string = str8_lit("warning");}break; + case MD_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) + { + MD_Node *file = n->v.root; + for(MD_EachNode(node, file->first)) + { + MD_Node *table_tag = md_tag_from_string(node, str8_lit("table"), 0); + if(!md_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) + { + MD_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(MD_EachNode(node, file->first)) + { + if(md_node_has_tag(node, str8_lit("option"), 0)) + { + if(str8_match(node->string, str8_lit("library"), 0)) + { + layer->is_library = 1; + } + } + if(md_node_has_tag(node, str8_lit("gen_folder"), 0)) + { + layer->gen_folder_name = node->string; + } + if(md_node_has_tag(node, str8_lit("h_name"), 0)) + { + layer->h_name_override = node->string; + } + if(md_node_has_tag(node, str8_lit("c_name"), 0)) + { + layer->c_name_override = node->string; + } + if(md_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(md_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(md_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(md_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) + { + MD_Node *file = n->v.root; + for(MD_EachNode(node, file->first)) + { + MD_Node *tag = md_tag_from_string(node, str8_lit("enum"), 0); + if(!md_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) + { + MD_Node *file = n->v.root; + for(MD_EachNode(node, file->first)) + { + MD_Node *tag = md_tag_from_string(node, str8_lit("xlist"), 0); + if(!md_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) + { + MD_Node *file = n->v.root; + for(MD_EachNode(node, file->first)) + { + if(md_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) + { + MD_Node *file = n->v.root; + for(MD_EachNode(node, file->first)) + { + MD_Node *tag = md_tag_from_string(node, str8_lit("data"), 0); + if(!md_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(!md_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) + { + MD_Node *file = n->v.root; + for(MD_EachNode(node, file->first)) + { + MD_Node *tag = md_tag_from_string(node, str8_lit("enum2string_switch"), 0); + if(!md_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(\"\");\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) + { + MD_Node *file = n->v.root; + for(MD_EachNode(node, file->first)) + { + MD_Node *tag = md_tag_from_string(node, str8_lit("gen"), 0); + if(!md_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 = md_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) + { + MD_Node *file = n->v.root; + for(MD_EachNode(node, file->first)) + { + if(md_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(md_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) + { + MD_Node *file = n->v.root; + for(MD_EachNode(node, file->first)) + { + //- rjf: generate markdown page + if(md_node_has_tag(node, str8_lit("markdown"), 0)) + { + String8List md_strs = {0}; + for(MD_Node *piece = node->first; !md_node_is_nil(piece); piece = piece->next) + { + if(md_node_has_tag(piece, str8_lit("title"), 0)) + { + str8_list_pushf(mg_arena, &md_strs, "# %S\n\n", piece->string); + } + if(md_node_has_tag(piece, str8_lit("subtitle"), 0)) + { + str8_list_pushf(mg_arena, &md_strs, "## %S\n\n", piece->string); + } + if(md_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(md_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)); + } +} diff --git a/src/metagen/metagen_os/core/linux/metagen_os_core_linux.c b/src/metagen/metagen_os/core/linux/metagen_os_core_linux.c index f9c2af02..5169ba0f 100644 --- a/src/metagen/metagen_os/core/linux/metagen_os_core_linux.c +++ b/src/metagen/metagen_os/core/linux/metagen_os_core_linux.c @@ -1,1682 +1,1258 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#include - -//////////////////////////////// -//~ rjf: Globals - -global pthread_mutex_t lnx_mutex = {0}; -global Arena *lnx_perm_arena = 0; -global String8List lnx_cmd_line_args = {0}; -global LNX_Entity lnx_entity_buffer[1024]; -global LNX_Entity *lnx_entity_free = 0; -global String8 lnx_initial_path = {0}; -thread_static LNX_SafeCallChain *lnx_safe_call_chain = 0; - -//////////////////////////////// -//~ rjf: Helpers - -internal B32 -lnx_write_list_to_file_descriptor(int fd, String8List list){ - B32 success = true; - - String8Node *node = list.first; - if (node != 0){ - U8 *ptr = node->string.str;; - U8 *opl = ptr + node->string.size; - - U64 p = 0; - for (;p < list.total_size;){ - U64 amt64 = (U64)(opl - ptr); - U32 amt = u32_from_u64_saturate(amt64); - S64 written_amt = write(fd, ptr, amt); - if (written_amt < 0){ - break; - } - p += written_amt; - ptr += written_amt; - - Assert(ptr <= opl); - if (ptr == opl){ - node = node->next; - if (node == 0){ - if (p < list.total_size){ - success = false; - } - break; - } - ptr = node->string.str; - opl = ptr + node->string.size; - } - } - } - - return(success); -} - -internal void -lnx_date_time_from_tm(DateTime *out, struct tm *in, U32 msec){ - out->msec = msec; - out->sec = in->tm_sec; - out->min = in->tm_min; - out->hour = in->tm_hour; - out->day = in->tm_mday - 1; - out->wday = in->tm_wday; - out->mon = in->tm_mon; - out->year = in->tm_year + 1900; -} - -internal void -lnx_tm_from_date_time(struct tm *out, DateTime *in){ - out->tm_sec = in->sec; - out->tm_min = in->min; - out->tm_hour = in->hour; - out->tm_mday = in->day + 1; - out->tm_mon = in->mon; - out->tm_year = in->year - 1900; -} - -internal void -lnx_dense_time_from_timespec(DenseTime *out, struct timespec *in){ - struct tm tm_time = {0}; - gmtime_r(&in->tv_sec, &tm_time); - DateTime date_time = {0}; - lnx_date_time_from_tm(&date_time, &tm_time, in->tv_nsec/Million(1)); - *out = dense_time_from_date_time(date_time); -} - -internal void -lnx_file_properties_from_stat(FileProperties *out, struct stat *in){ - MemoryZeroStruct(out); - out->size = in->st_size; - lnx_dense_time_from_timespec(&out->created, &in->st_ctim); - lnx_dense_time_from_timespec(&out->modified, &in->st_mtim); - if ((in->st_mode & S_IFDIR) != 0){ - out->flags |= FilePropertyFlag_IsFolder; - } -} - -internal String8 -lnx_string_from_signal(int signum){ - String8 result = str8_lit(""); - switch (signum){ - case SIGABRT: - { - result = str8_lit("SIGABRT"); - }break; - case SIGALRM: - { - result = str8_lit("SIGALRM"); - }break; - case SIGBUS: - { - result = str8_lit("SIGBUS"); - }break; - case SIGCHLD: - { - result = str8_lit("SIGCHLD"); - }break; - case SIGCONT: - { - result = str8_lit("SIGCONT"); - }break; - case SIGFPE: - { - result = str8_lit("SIGFPE"); - }break; - case SIGHUP: - { - result = str8_lit("SIGHUP"); - }break; - case SIGILL: - { - result = str8_lit("SIGILL"); - }break; - case SIGINT: - { - result = str8_lit("SIGINT"); - }break; - case SIGIO: - { - result = str8_lit("SIGIO"); - } - case SIGKILL: - { - result = str8_lit("SIGKILL"); - }break; - case SIGPIPE: - { - result = str8_lit("SIGPIPE"); - }break; - case SIGPROF: - { - result = str8_lit("SIGPROF"); - }break; - case SIGPWR: - { - result = str8_lit("SIGPWR"); - }break; - case SIGQUIT: - { - result = str8_lit("SIGQUIT"); - }break; - case SIGSEGV: - { - result = str8_lit("SIGSEGV"); - }break; - case SIGSTKFLT: - { - result = str8_lit("SIGSTKFLT"); - }break; - case SIGSTOP: - { - result = str8_lit("SIGSTOP"); - }break; - case SIGTSTP: - { - result = str8_lit("SIGTSTP"); - }break; - case SIGSYS: - { - result = str8_lit("SIGSYS"); - }break; - case SIGTERM: - { - result = str8_lit("SIGTERM"); - }break; - case SIGTRAP: - { - result = str8_lit("SIGTRAP"); - }break; - case SIGTTIN: - { - result = str8_lit("SIGTTIN"); - }break; - case SIGTTOU: - { - result = str8_lit("SIGTTOU"); - }break; - case SIGURG: - { - result = str8_lit("SIGURG"); - }break; - case SIGUSR1: - { - result = str8_lit("SIGUSR1"); - }break; - case SIGUSR2: - { - result = str8_lit("SIGUSR2"); - }break; - case SIGVTALRM: - { - result = str8_lit("SIGVTALRM"); - }break; - case SIGXCPU: - { - result = str8_lit("SIGXCPU"); - }break; - case SIGXFSZ: - { - result = str8_lit("SIGXFSZ"); - }break; - case SIGWINCH: - { - result = str8_lit("SIGWINCH"); - }break; - } - return(result); -} - -internal String8 -lnx_string_from_errno(int error_number){ - String8 result = str8_lit(""); - switch (error_number){ - case EPERM: - { - result = str8_lit("EPERM"); - }break; - case ENOENT: - { - result = str8_lit("ENOENT"); - }break; - case ESRCH: - { - result = str8_lit("ESRCH"); - }break; - case EINTR: - { - result = str8_lit("EINTR"); - }break; - case EIO: - { - result = str8_lit("EIO"); - }break; - case ENXIO: - { - result = str8_lit("ENXIO"); - }break; - case E2BIG: - { - result = str8_lit("E2BIG"); - }break; - case ENOEXEC: - { - result = str8_lit("ENOEXEC"); - }break; - case EBADF: - { - result = str8_lit("EBADF"); - }break; - case ECHILD: - { - result = str8_lit("ECHILD"); - }break; - case EAGAIN: - { - result = str8_lit("EAGAIN"); - }break; - case ENOMEM: - { - result = str8_lit("ENOMEM"); - }break; - case EACCES: - { - result = str8_lit("EACCES"); - }break; - case EFAULT: - { - result = str8_lit("EFAULT"); - }break; - case ENOTBLK: - { - result = str8_lit("ENOTBLK"); - }break; - case EBUSY: - { - result = str8_lit("EBUSY"); - }break; - case EEXIST: - { - result = str8_lit("EEXIST"); - }break; - case EXDEV: - { - result = str8_lit("EXDEV"); - }break; - case ENODEV: - { - result = str8_lit("ENODEV"); - }break; - case ENOTDIR: - { - result = str8_lit("ENOTDIR"); - }break; - case EISDIR: - { - result = str8_lit("EISDIR"); - }break; - case EINVAL: - { - result = str8_lit("EINVAL"); - }break; - case ENFILE: - { - result = str8_lit("ENFILE"); - }break; - case EMFILE: - { - result = str8_lit("EMFILE"); - }break; - case ENOTTY: - { - result = str8_lit("ENOTTY"); - }break; - case ETXTBSY: - { - result = str8_lit("ETXTBSY"); - }break; - case EFBIG: - { - result = str8_lit("EFBIG"); - }break; - case ENOSPC: - { - result = str8_lit("ENOSPC"); - }break; - case ESPIPE: - { - result = str8_lit("ESPIPE"); - }break; - case EROFS: - { - result = str8_lit("EROFS"); - }break; - case EMLINK: - { - result = str8_lit("EMLINK"); - }break; - case EPIPE: - { - result = str8_lit("EPIPE"); - }break; - case EDOM: - { - result = str8_lit("EDOM"); - }break; - case ERANGE: - { - result = str8_lit("ERANGE"); - }break; - case EDEADLK: - { - result = str8_lit("EDEADLK"); - }break; - case ENAMETOOLONG: - { - result = str8_lit("ENAMETOOLONG"); - }break; - case ENOLCK: - { - result = str8_lit("ENOLCK"); - }break; - case ENOSYS: - { - result = str8_lit("ENOSYS"); - }break; - case ENOTEMPTY: - { - result = str8_lit("ENOTEMPTY"); - }break; - case ELOOP: - { - result = str8_lit("ELOOP"); - }break; - case ENOMSG: - { - result = str8_lit("ENOMSG"); - }break; - case EIDRM: - { - result = str8_lit("EIDRM"); - }break; - case ECHRNG: - { - result = str8_lit("ECHRNG"); - }break; - case EL2NSYNC: - { - result = str8_lit("EL2NSYNC"); - }break; - case EL3HLT: - { - result = str8_lit("EL3HLT"); - }break; - case EL3RST: - { - result = str8_lit("EL3RST"); - }break; - case ELNRNG: - { - result = str8_lit("ELNRNG"); - }break; - case EUNATCH: - { - result = str8_lit("EUNATCH"); - }break; - case ENOCSI: - { - result = str8_lit("ENOCSI"); - }break; - case EL2HLT: - { - result = str8_lit("EL2HLT"); - }break; - case EBADE: - { - result = str8_lit("EBADE"); - }break; - case EBADR: - { - result = str8_lit("EBADR"); - }break; - case EXFULL: - { - result = str8_lit("EXFULL"); - }break; - case ENOANO: - { - result = str8_lit("ENOANO"); - }break; - case EBADRQC: - { - result = str8_lit("EBADRQC"); - }break; - case EBADSLT: - { - result = str8_lit("EBADSLT"); - }break; - case EBFONT: - { - result = str8_lit("EBFONT"); - }break; - case ENOSTR: - { - result = str8_lit("ENOSTR"); - }break; - case ENODATA: - { - result = str8_lit("ENODATA"); - }break; - case ETIME: - { - result = str8_lit("ETIME"); - }break; - case ENOSR: - { - result = str8_lit("ENOSR"); - }break; - case ENONET: - { - result = str8_lit("ENONET"); - }break; - case ENOPKG: - { - result = str8_lit("ENOPKG"); - }break; - case EREMOTE: - { - result = str8_lit("EREMOTE"); - }break; - case ENOLINK: - { - result = str8_lit("ENOLINK"); - }break; - case EADV: - { - result = str8_lit("EADV"); - }break; - case ESRMNT: - { - result = str8_lit("ESRMNT"); - }break; - case ECOMM: - { - result = str8_lit("ECOMM"); - }break; - case EPROTO: - { - result = str8_lit("EPROTO"); - }break; - case EMULTIHOP: - { - result = str8_lit("EMULTIHOP"); - }break; - case EDOTDOT: - { - result = str8_lit("EDOTDOT"); - }break; - case EBADMSG: - { - result = str8_lit("EBADMSG"); - }break; - case EOVERFLOW: - { - result = str8_lit("EOVERFLOW"); - }break; - case ENOTUNIQ: - { - result = str8_lit("ENOTUNIQ"); - }break; - case EBADFD: - { - result = str8_lit("EBADFD"); - }break; - case EREMCHG: - { - result = str8_lit("EREMCHG"); - }break; - case ELIBACC: - { - result = str8_lit("ELIBACC"); - }break; - case ELIBBAD: - { - result = str8_lit("ELIBBAD"); - }break; - case ELIBSCN: - { - result = str8_lit("ELIBSCN"); - }break; - case ELIBMAX: - { - result = str8_lit("ELIBMAX"); - }break; - case ELIBEXEC: - { - result = str8_lit("ELIBEXEC"); - }break; - case EILSEQ: - { - result = str8_lit("EILSEQ"); - }break; - case ERESTART: - { - result = str8_lit("ERESTART"); - }break; - case ESTRPIPE: - { - result = str8_lit("ESTRPIPE"); - }break; - case EUSERS: - { - result = str8_lit("EUSERS"); - }break; - case ENOTSOCK: - { - result = str8_lit("ENOTSOCK"); - }break; - case EDESTADDRREQ: - { - result = str8_lit("EDESTADDRREQ"); - }break; - case EMSGSIZE: - { - result = str8_lit("EMSGSIZE"); - }break; - case EPROTOTYPE: - { - result = str8_lit("EPROTOTYPE"); - }break; - case ENOPROTOOPT: - { - result = str8_lit("ENOPROTOOPT"); - }break; - case EPROTONOSUPPORT: - { - result = str8_lit("EPROTONOSUPPORT"); - }break; - case ESOCKTNOSUPPORT: - { - result = str8_lit("ESOCKTNOSUPPORT"); - }break; - case EOPNOTSUPP: - { - result = str8_lit("EOPNOTSUPP"); - }break; - case EPFNOSUPPORT: - { - result = str8_lit("EPFNOSUPPORT"); - }break; - case EAFNOSUPPORT: - { - result = str8_lit("EAFNOSUPPORT"); - }break; - case EADDRINUSE: - { - result = str8_lit("EADDRINUSE"); - }break; - case EADDRNOTAVAIL: - { - result = str8_lit("EADDRNOTAVAIL"); - }break; - case ENETDOWN: - { - result = str8_lit("ENETDOWN"); - }break; - case ENETUNREACH: - { - result = str8_lit("ENETUNREACH"); - }break; - case ENETRESET: - { - result = str8_lit("ENETRESET"); - }break; - case ECONNABORTED: - { - result = str8_lit("ECONNABORTED"); - }break; - case ECONNRESET: - { - result = str8_lit("ECONNRESET"); - }break; - case ENOBUFS: - { - result = str8_lit("ENOBUFS"); - }break; - case EISCONN: - { - result = str8_lit("EISCONN"); - }break; - case ENOTCONN: - { - result = str8_lit("ENOTCONN"); - }break; - case ESHUTDOWN: - { - result = str8_lit("ESHUTDOWN"); - }break; - case ETOOMANYREFS: - { - result = str8_lit("ETOOMANYREFS"); - }break; - case ETIMEDOUT: - { - result = str8_lit("ETIMEDOUT"); - }break; - case ECONNREFUSED: - { - result = str8_lit("ECONNREFUSED"); - }break; - case EHOSTDOWN: - { - result = str8_lit("EHOSTDOWN"); - }break; - case EHOSTUNREACH: - { - result = str8_lit("EHOSTUNREACH"); - }break; - case EALREADY: - { - result = str8_lit("EALREADY"); - }break; - case EINPROGRESS: - { - result = str8_lit("EINPROGRESS"); - }break; - case ESTALE: - { - result = str8_lit("ESTALE"); - }break; - case EUCLEAN: - { - result = str8_lit("EUCLEAN"); - }break; - case ENOTNAM: - { - result = str8_lit("ENOTNAM"); - }break; - case ENAVAIL: - { - result = str8_lit("ENAVAIL"); - }break; - case EISNAM: - { - result = str8_lit("EISNAM"); - }break; - case EREMOTEIO: - { - result = str8_lit("EREMOTEIO"); - }break; - case EDQUOT: - { - result = str8_lit("EDQUOT"); - }break; - case ENOMEDIUM: - { - result = str8_lit("ENOMEDIUM"); - }break; - case EMEDIUMTYPE: - { - result = str8_lit("EMEDIUMTYPE"); - }break; - case ECANCELED: - { - result = str8_lit("ECANCELED"); - }break; - case ENOKEY: - { - result = str8_lit("ENOKEY"); - }break; - case EKEYEXPIRED: - { - result = str8_lit("EKEYEXPIRED"); - }break; - case EKEYREVOKED: - { - result = str8_lit("EKEYREVOKED"); - }break; - case EKEYREJECTED: - { - result = str8_lit("EKEYREJECTED"); - }break; - case EOWNERDEAD: - { - result = str8_lit("EOWNERDEAD"); - }break; - case ENOTRECOVERABLE: - { - result = str8_lit("ENOTRECOVERABLE"); - }break; - case ERFKILL: - { - result = str8_lit("ERFKILL"); - }break; - case EHWPOISON: - { - result = str8_lit("EHWPOISON"); - }break; - } - return(result); -} - -internal LNX_Entity* -lnx_alloc_entity(LNX_EntityKind kind){ - pthread_mutex_lock(&lnx_mutex); - LNX_Entity *result = lnx_entity_free; - Assert(result != 0); - SLLStackPop(lnx_entity_free); - pthread_mutex_unlock(&lnx_mutex); - result->kind = kind; - return(result); -} - -internal void -lnx_free_entity(LNX_Entity *entity){ - entity->kind = LNX_EntityKind_Null; - pthread_mutex_lock(&lnx_mutex); - SLLStackPush(lnx_entity_free, entity); - pthread_mutex_unlock(&lnx_mutex); -} - -internal void* -lnx_thread_base(void *ptr){ - LNX_Entity *entity = (LNX_Entity*)ptr; - OS_ThreadFunctionType *func = entity->thread.func; - void *thread_ptr = entity->thread.ptr; - - TCTX tctx_; - tctx_init_and_equip(&tctx_); - - func(thread_ptr); - - // remove my bit - U32 result = __sync_fetch_and_and(&entity->reference_mask, ~0x2); - // if the other bit is also gone, free entity - if ((result & 0x1) == 0){ - lnx_free_entity(entity); - } - return(0); -} - -internal void -lnx_safe_call_sig_handler(int){ - LNX_SafeCallChain *chain = lnx_safe_call_chain; - if (chain != 0 && chain->fail_handler != 0){ - chain->fail_handler(chain->ptr); - } - abort(); -} - -//////////////////////////////// -//~ rjf: @os_hooks Main Initialization API (Implemented Per-OS) - -internal void -os_init(int argc, char **argv) -{ - // NOTE(allen): Initialize linux layer mutex - { - pthread_mutexattr_t attr; - pthread_mutexattr_init(&attr); - pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); - int pthread_result = pthread_mutex_init(&lnx_mutex, &attr); - pthread_mutexattr_destroy(&attr); - if (pthread_result == -1){ - abort(); - } - } - MemoryZeroArray(lnx_entity_buffer); - { - LNX_Entity *ptr = lnx_entity_free = lnx_entity_buffer; - for (U64 i = 1; i < ArrayCount(lnx_entity_buffer); i += 1, ptr += 1){ - ptr->next = ptr + 1; - } - ptr->next = 0; - } - - // NOTE(allen): Permanent memory allocator for this layer - Arena *perm_arena = arena_alloc(); - lnx_perm_arena = perm_arena; - - // NOTE(allen): Initialize Paths - lnx_initial_path = os_get_path(lnx_perm_arena, OS_SystemPath_Current); - - // NOTE(rjf): Setup command line args - lnx_cmd_line_args = os_string_list_from_argcv(lnx_perm_arena, argc, argv); -} - -//////////////////////////////// -//~ rjf: @os_hooks Memory Allocation (Implemented Per-OS) - -internal void* -os_reserve(U64 size){ - void *result = mmap(0, size, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); - return(result); -} - -internal B32 -os_commit(void *ptr, U64 size){ - mprotect(ptr, size, PROT_READ|PROT_WRITE); - // TODO(allen): can we test this? - return(true); -} - -internal void* -os_reserve_large(U64 size){ - NotImplemented; - return 0; -} - -internal B32 -os_commit_large(void *ptr, U64 size){ - NotImplemented; - return 0; -} - -internal void -os_decommit(void *ptr, U64 size){ - madvise(ptr, size, MADV_DONTNEED); - mprotect(ptr, size, PROT_NONE); -} - -internal void -os_release(void *ptr, U64 size){ - munmap(ptr, size); -} - -internal void -os_set_large_pages(B32 flag) -{ - NotImplemented; -} - -internal B32 -os_large_pages_enabled(void) -{ - NotImplemented; - return 0; -} - -internal U64 -os_large_page_size(void) -{ - NotImplemented; - return 0; -} - -internal void* -os_alloc_ring_buffer(U64 size, U64 *actual_size_out) -{ - NotImplemented; - return 0; -} - -internal void -os_free_ring_buffer(void *ring_buffer, U64 actual_size) -{ - NotImplemented; -} - -//////////////////////////////// -//~ rjf: @os_hooks System Info (Implemented Per-OS) - -internal String8 -os_machine_name(void){ - local_persist B32 first = true; - local_persist String8 name = {0}; - - // TODO(allen): let's just pre-compute this at init and skip the complexity - pthread_mutex_lock(&lnx_mutex); - if (first){ - Temp scratch = scratch_begin(0, 0); - first = false; - - // get name - B32 got_final_result = false; - U8 *buffer = 0; - int size = 0; - for (S64 cap = 4096, r = 0; - r < 4; - cap *= 2, r += 1){ - scratch.restore(); - buffer = push_array_no_zero(scratch.arena, U8, cap); - size = gethostname((char*)buffer, cap); - if (size < cap){ - got_final_result = true; - break; - } - } - - // save string - if (got_final_result && size > 0){ - name.size = size; - name.str = push_array_no_zero(lnx_perm_arena, U8, name.size + 1); - MemoryCopy(name.str, buffer, name.size); - name.str[name.size] = 0; - } - - scratch_end(scratch); - } - pthread_mutex_unlock(&lnx_mutex); - - return(name); -} - -internal U64 -os_page_size(void){ - int size = getpagesize(); - return((U64)size); -} - -internal U64 -os_allocation_granularity(void) -{ - // On linux there is no equivalent of "dwAllocationGranularity" - os_page_size(); -} - -internal U64 -os_logical_core_count(void) -{ - // TODO(rjf): check this - return get_nprocs(); -} - -//////////////////////////////// -//~ rjf: @os_hooks Process Info (Implemented Per-OS) - -internal String8List -os_get_command_line_arguments(void) -{ - return lnx_cmd_line_args; -} - -internal S32 -os_get_pid(void){ - S32 result = getpid(); - return(result); -} - -internal S32 -os_get_tid(void){ - S32 result = 0; -#ifdef SYS_gettid - result = syscall(SYS_gettid); -#else - result = gettid(); -#endif - return(result); -} - -internal String8List -os_get_environment(void) -{ - NotImplemented; - String8List result = {0}; - return result; -} - -internal U64 -os_string_list_from_system_path(Arena *arena, OS_SystemPath path, String8List *out){ - U64 result = 0; - - switch (path){ - case OS_SystemPath_Binary: - { - local_persist B32 first = true; - local_persist String8 name = {0}; - - // TODO(allen): let's just pre-compute this at init and skip the complexity - pthread_mutex_lock(&lnx_mutex); - if (first){ - Temp scratch = scratch_begin(&arena, 1); - first = false; - - // get self string - B32 got_final_result = false; - U8 *buffer = 0; - int size = 0; - for (S64 cap = PATH_MAX, r = 0; - r < 4; - cap *= 2, r += 1){ - scratch.restore(); - buffer = push_array_no_zero(scratch.arena, U8, cap); - size = readlink("/proc/self/exe", (char*)buffer, cap); - if (size < cap){ - got_final_result = true; - break; - } - } - - // save string - if (got_final_result && size > 0){ - String8 full_name = str8(buffer, size); - String8 name_chopped = string_path_chop_last_slash(full_name); - name = push_str8_copy(lnx_perm_arena, name_chopped); - } - - scratch_end(scratch); - } - pthread_mutex_unlock(&lnx_mutex); - - result = 1; - str8_list_push(arena, out, name); - }break; - - case OS_SystemPath_Initial: - { - Assert(lnx_initial_path.str != 0); - result = 1; - str8_list_push(arena, out, lnx_initial_path); - }break; - - case OS_SystemPath_Current: - { - char *cwdir = getcwd(0, 0); - String8 string = push_str8_copy(arena, str8_cstring(cwdir)); - free(cwdir); - result = 1; - str8_list_push(arena, out, string); - }break; - - case OS_SystemPath_UserProgramData: - { - char *home = getenv("HOME"); - String8 string = str8_cstring(home); - result = 1; - str8_list_push(arena, out, string); - }break; - - case OS_SystemPath_ModuleLoad: - { - // TODO(allen): this one is big and complicated and only needed for making - // a debugger, skipping for now. - NotImplemented; - }break; - } - - return(result); -} - -//////////////////////////////// -//~ rjf: @os_hooks Process Control (Implemented Per-OS) - -internal void -os_exit_process(S32 exit_code){ - exit(exit_code); -} - -//////////////////////////////// -//~ rjf: @os_hooks File System (Implemented Per-OS) - -//- rjf: files - -internal OS_Handle -os_file_open(OS_AccessFlags flags, String8 path) -{ - OS_Handle file = {0}; - NotImplemented; - return file; -} - -internal void -os_file_close(OS_Handle file) -{ - NotImplemented; -} - -internal U64 -os_file_read(OS_Handle file, Rng1U64 rng, void *out_data) -{ - NotImplemented; - return 0; -} - -internal void -os_file_write(OS_Handle file, Rng1U64 rng, void *data) -{ - NotImplemented; -} - -internal B32 -os_file_set_times(OS_Handle file, DateTime time) -{ - NotImplemented; -} - -internal FileProperties -os_properties_from_file(OS_Handle file) -{ - FileProperties props = {0}; - NotImplemented; - return props; -} - -internal OS_FileID -os_id_from_file(OS_Handle file) -{ - // TODO(nick): querry struct stat with fstat(2) and use st_dev and st_ino as ids - OS_FileID id = {0}; - NotImplemented; - return id; -} - -internal B32 -os_delete_file_at_path(String8 path) -{ - Temp scratch = scratch_begin(0, 0); - B32 result = false; - String8 name_copy = push_str8_copy(scratch.arena, name); - if (remove((char*)name_copy.str) != -1){ - result = true; - } - scratch_end(scratch); - return(result); -} - -internal B32 -os_copy_file_path(String8 dst, String8 src) -{ - NotImplemented; - return 0; -} - -internal String8 -os_full_path_from_path(Arena *arena, String8 path) -{ - // TODO: realpath can be used to resolve full path - String8 result = {0}; - NotImplemented; - return result; -} - -internal B32 -os_file_path_exists(String8 path) -{ - NotImplemented; - return 0; -} - -//- rjf: file maps - -internal OS_Handle -os_file_map_open(OS_AccessFlags flags, OS_Handle file) -{ - NotImplemented; - OS_Handle handle = {0}; - return handle; -} - -internal void -os_file_map_close(OS_Handle map) -{ - NotImplemented; -} - -internal void * -os_file_map_view_open(OS_Handle map, OS_AccessFlags flags, Rng1U64 range) -{ - NotImplemented; - return 0; -} - -internal void -os_file_map_view_close(OS_Handle map, void *ptr) -{ - NotImplemented; -} - -//- rjf: directory iteration - -internal OS_FileIter * -os_file_iter_begin(Arena *arena, String8 path, OS_FileIterFlags flags) -{ - NotImplemented; - return 0; -} - -internal B32 -os_file_iter_next(Arena *arena, OS_FileIter *iter, OS_FileInfo *info_out) -{ - NotImplemented; - return 0; -} - -internal void -os_file_iter_end(OS_FileIter *iter) -{ - NotImplemented; -} - -//- rjf: directory creation - -internal B32 -os_make_directory(String8 path) -{ - Temp scratch = scratch_begin(0, 0); - B32 result = false; - String8 name_copy = push_str8_copy(scratch.arena, name); - if (mkdir((char*)name_copy.str, 0777) != -1){ - result = true; - } - scratch_end(scratch); - return(result); -} - -//////////////////////////////// -//~ rjf: @os_hooks Shared Memory (Implemented Per-OS) - -internal OS_Handle -os_shared_memory_alloc(U64 size, String8 name) -{ - OS_Handle result = {0}; - NotImplemented; - return result; -} - -internal OS_Handle -os_shared_memory_open(String8 name) -{ - OS_Handle result = {0}; - NotImplemented; - return result; -} - -internal void -os_shared_memory_close(OS_Handle handle) -{ - NotImplemented; -} - -internal void * -os_shared_memory_view_open(OS_Handle handle, Rng1U64 range) -{ - NotImplemented; - return 0; -} - -internal void -os_shared_memory_view_close(OS_Handle handle, void *ptr) -{ - NotImplemented; -} - -//////////////////////////////// -//~ rjf: @os_hooks Time (Implemented Per-OS) - -internal OS_UnixTime -os_now_unix(void) -{ - time_t t = time(0); - return (OS_UnixTime)t; -} - -internal DateTime -os_now_universal_time(void){ - time_t t = 0; - time(&t); - struct tm universal_tm = {0}; - gmtime_r(&t, &universal_tm); - DateTime result = {0}; - lnx_date_time_from_tm(&result, &universal_tm, 0); - return(result); -} - -internal DateTime -os_universal_time_from_local_time(DateTime *local_time){ - // local time -> universal time (using whatever types it takes) - struct tm local_tm = {0}; - lnx_tm_from_date_time(&local_tm, local_time); - local_tm.tm_isdst = -1; - time_t universal_t = mktime(&local_tm); - - // whatever type we ended up with -> DateTime (don't alter the space along the way) - struct tm universal_tm = {0}; - gmtime_r(&universal_t, &universal_tm); - DateTime result = {0}; - lnx_date_time_from_tm(&result, &universal_tm, 0); - return(result); -} - -internal DateTime -os_local_time_from_universal_time(DateTime *universal_time){ - // universal time -> local time (using whatever types it takes) - struct tm universal_tm = {0}; - lnx_tm_from_date_time(&universal_tm, universal_time); - universal_tm.tm_isdst = -1; - time_t universal_t = timegm(&universal_tm); - struct tm local_tm = {0}; - localtime_r(&universal_t, &local_tm); - - // whatever type we ended up with -> DateTime (don't alter the space along the way) - DateTime result = {0}; - lnx_date_time_from_tm(&result, &local_tm, 0); - return(result); -} - -internal U64 -os_now_microseconds(void){ - struct timespec t; - clock_gettime(CLOCK_MONOTONIC, &t); - U64 result = t.tv_sec*Million(1) + (t.tv_nsec/Thousand(1)); - return(result); -} - -internal void -os_sleep_milliseconds(U32 msec){ - usleep(msec*Thousand(1)); -} - -//////////////////////////////// -//~ rjf: @os_hooks Child Processes (Implemented Per-OS) - -internal B32 -os_launch_process(OS_LaunchOptions *options){ - // TODO(allen): I want to redo this API before I bother implementing it here - NotImplemented; - return(false); -} - -//////////////////////////////// -//~ rjf: @os_hooks Threads (Implemented Per-OS) - -internal OS_Handle -os_launch_thread(OS_ThreadFunctionType *func, void *ptr, void *params){ - // entity - LNX_Entity *entity = lnx_alloc_entity(LNX_EntityKind_Thread); - entity->reference_mask = 0x3; - entity->thread.func = func; - entity->thread.ptr = ptr; - - // pthread - pthread_attr_t attr; - pthread_attr_init(&attr); - int pthread_result = pthread_create(&entity->thread.handle, &attr, lnx_thread_base, entity); - pthread_attr_destroy(&attr); - if (pthread_result == -1){ - lnx_free_entity(entity); - entity = 0; - } - - // cast to opaque handle - OS_Handle result = {IntFromPtr(entity)}; - return(result); -} - -internal void -os_release_thread_handle(OS_Handle thread){ - LNX_Entity *entity = (LNX_Entity*)PtrFromInt(thread.id); - // remove my bit - U32 result = __sync_fetch_and_and(&entity->reference_mask, ~0x1); - // if the other bit is also gone, free entity - if ((result & 0x2) == 0){ - lnx_free_entity(entity); - } -} - -//////////////////////////////// -//~ rjf: @os_hooks Synchronization Primitives (Implemented Per-OS) - -// NOTE(allen): Mutexes are recursive - support counted acquire/release nesting -// on a single thread - -//- rjf: recursive mutexes - -internal OS_Handle -os_mutex_alloc(void){ - // entity - LNX_Entity *entity = lnx_alloc_entity(LNX_EntityKind_Mutex); - - // pthread - pthread_mutexattr_t attr; - pthread_mutexattr_init(&attr); - pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); - int pthread_result = pthread_mutex_init(&entity->mutex, &attr); - pthread_mutexattr_destroy(&attr); - if (pthread_result == -1){ - lnx_free_entity(entity); - entity = 0; - } - - // cast to opaque handle - OS_Handle result = {IntFromPtr(entity)}; - return(result); -} - -internal void -os_mutex_release(OS_Handle mutex){ - LNX_Entity *entity = (LNX_Entity*)PtrFromInt(mutex.id); - pthread_mutex_destroy(&entity->mutex); - lnx_free_entity(entity); -} - -internal void -os_mutex_take_(OS_Handle mutex){ - LNX_Entity *entity = (LNX_Entity*)PtrFromInt(mutex.id); - pthread_mutex_lock(&entity->mutex); -} - -internal void -os_mutex_drop_(OS_Handle mutex){ - LNX_Entity *entity = (LNX_Entity*)PtrFromInt(mutex.id); - pthread_mutex_unlock(&entity->mutex); -} - -//- rjf: reader/writer mutexes - -internal OS_Handle -os_rw_mutex_alloc(void) -{ - OS_Handle result = {0}; - NotImplemented; - return result; -} - -internal void -os_rw_mutex_release(OS_Handle rw_mutex) -{ - NotImplemented; -} - -internal void -os_rw_mutex_take_r_(OS_Handle mutex) -{ - NotImplemented; -} - -internal void -os_rw_mutex_drop_r_(OS_Handle mutex) -{ - NotImplemented; -} - -internal void -os_rw_mutex_take_w_(OS_Handle mutex) -{ - NotImplemented; -} - -internal void -os_rw_mutex_drop_w_(OS_Handle mutex) -{ - NotImplemented; -} - -//- rjf: condition variables - -internal OS_Handle -os_condition_variable_alloc(void){ - // entity - LNX_Entity *entity = lnx_alloc_entity(LNX_EntityKind_ConditionVariable); - - // pthread - pthread_condattr_t attr; - pthread_condattr_init(&attr); - int pthread_result = pthread_cond_init(&entity->cond, &attr); - pthread_condattr_destroy(&attr); - if (pthread_result == -1){ - lnx_free_entity(entity); - entity = 0; - } - - // cast to opaque handle - OS_Handle result = {IntFromPtr(entity)}; - return(result); -} - -internal void -os_condition_variable_release(OS_Handle cv){ - LNX_Entity *entity = (LNX_Entity*)PtrFromInt(cv.id); - pthread_cond_destroy(&entity->cond); - lnx_free_entity(entity); -} - -internal B32 -os_condition_variable_wait_(OS_Handle cv, OS_Handle mutex, U64 endt_us){ - B32 result = false; - LNX_Entity *entity_cond = (LNX_Entity*)PtrFromInt(cv.id); - LNX_Entity *entity_mutex = (LNX_Entity*)PtrFromInt(mutex.id); - // TODO(allen): implement the time control - pthread_cond_timedwait(&entity_cond->cond, &entity_mutex->mutex); - return(result); -} - -internal B32 -os_condition_variable_wait_rw_r_(OS_Handle cv, OS_Handle mutex_rw, U64 endt_us) -{ - NotImplemented; - return 0; -} - -internal B32 -os_condition_variable_wait_rw_w_(OS_Handle cv, OS_Handle mutex_rw, U64 endt_us) -{ - NotImplemented; - return 0; -} - -internal void -os_condition_variable_signal_(OS_Handle cv){ - LNX_Entity *entity = (LNX_Entity*)PtrFromInt(cv.id); - pthread_cond_signal(&entity->cond); -} - -internal void -os_condition_variable_broadcast_(OS_Handle cv){ - LNX_Entity *entity = (LNX_Entity*)PtrFromInt(cv.id); - DontCompile; -} - -//- rjf: cross-process semaphores - -internal OS_Handle -os_semaphore_alloc(U32 initial_count, U32 max_count, String8 name) -{ - OS_Handle result = {0}; - NotImplemented; - return result; -} - -internal void -os_semaphore_release(OS_Handle semaphore) -{ - NotImplemented; -} - -internal OS_Handle -os_semaphore_open(String8 name) -{ - OS_Handle result = {0}; - NotImplemented; - return result; -} - -internal void -os_semaphore_close(OS_Handle semaphore) -{ - NotImplemented; -} - -internal B32 -os_semaphore_take(OS_Handle semaphore) -{ - NotImplemented; - return 0; -} - -internal void -os_semaphore_drop(OS_Handle semaphore) -{ - NotImplemented; -} - -//////////////////////////////// -//~ rjf: @os_hooks Dynamically-Loaded Libraries (Implemented Per-OS) - -internal OS_Handle -os_library_open(String8 path) -{ - Temp scratch = scratch_begin(0, 0); - char *path_cstr = (char *)push_str8_copy(scratch.arena, path).str; - void *so = dlopen(path_cstr, RTLD_LAZY); - OS_Handle lib = { (U64)so }; - scratch_end(scratch); - return lib; -} - -internal VoidProc * -os_library_load_proc(OS_Handle lib, String8 name) -{ - Temp scratch = scratch_begin(0, 0); - void *so = (void *)lib.id; - char *name_cstr = (char *)push_str8_copy(scratch.arena, name).str; - VoidProc *proc = (VoidProc *)dlsym(so, name_cstr); - scratch_end(scratch); - return proc; -} - -internal void -os_library_close(OS_Handle lib) -{ - void *so = (void *)lib.id; - dlclose(so); -} - -//////////////////////////////// -//~ rjf: @os_hooks Dynamically-Loaded Libraries (Implemented Per-OS) - -internal void -os_safe_call(OS_ThreadFunctionType *func, OS_ThreadFunctionType *fail_handler, void *ptr){ - LNX_SafeCallChain chain = {0}; - SLLStackPush(lnx_safe_call_chain, &chain); - chain.fail_handler = fail_handler; - chain.ptr = ptr; - - struct sigaction new_act = {0}; - new_act.sa_handler = lnx_safe_call_sig_handler; - - int signals_to_handle[] = { - SIGILL, SIGFPE, SIGSEGV, SIGBUS, SIGTRAP, - }; - struct sigaction og_act[ArrayCount(signals_to_handle)] = {0}; - - for (U32 i = 0; i < ArrayCount(signals_to_handle); i += 1){ - sigaction(signals_to_handle[i], &new_act, &og_act[i]); - } - - func(ptr); - - for (U32 i = 0; i < ArrayCount(signals_to_handle); i += 1){ - sigaction(signals_to_handle[i], &og_act[i], 0); - } -} - -//////////////////////////////// - -internal OS_Guid -os_make_guid(void) -{ - NotImplemented; -} - +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +//////////////////////////////// +//~ rjf: Helpers + +internal 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; +} + +internal 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; +} + +internal 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; +} + +internal 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; +} + +internal 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; +} + +internal 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 + +internal OS_LNX_Entity * +os_lnx_entity_alloc(OS_LNX_EntityKind kind) +{ + OS_LNX_Entity *entity = 0; + DeferLoop(pthread_mutex_lock(&os_lnx_state.entity_mutex), + pthread_mutex_unlock(&os_lnx_state.entity_mutex)) + { + entity = os_lnx_state.entity_free; + if(entity) + { + SLLStackPop(os_lnx_state.entity_free); + } + else + { + entity = push_array_no_zero(os_lnx_state.entity_arena, OS_LNX_Entity, 1); + } + } + MemoryZeroStruct(entity); + entity->kind = kind; + return entity; +} + +internal void +os_lnx_entity_release(OS_LNX_Entity *entity) +{ + DeferLoop(pthread_mutex_lock(&os_lnx_state.entity_mutex), + pthread_mutex_unlock(&os_lnx_state.entity_mutex)) + { + SLLStackPush(os_lnx_state.entity_free, entity); + } +} + +//////////////////////////////// +//~ rjf: Thread Entry Point + +internal void * +os_lnx_thread_entry_point(void *ptr) +{ + OS_LNX_Entity *entity = (OS_LNX_Entity *)ptr; + OS_ThreadFunctionType *func = entity->thread.func; + void *thread_ptr = entity->thread.ptr; + TCTX tctx_; + tctx_init_and_equip(&tctx_); + func(thread_ptr); + tctx_release(); + return 0; +} + +//////////////////////////////// +//~ rjf: @os_hooks System/Process Info (Implemented Per-OS) + +internal OS_SystemInfo * +os_get_system_info(void) +{ + return &os_lnx_state.system_info; +} + +internal OS_ProcessInfo * +os_get_process_info(void) +{ + return &os_lnx_state.process_info; +} + +internal String8 +os_get_current_path(Arena *arena) +{ + char *cwdir = getcwd(0, 0); + String8 string = push_str8_copy(arena, str8_cstring(cwdir)); + return string; +} + +//////////////////////////////// +//~ rjf: @os_hooks Memory Allocation (Implemented Per-OS) + +//- rjf: basic + +internal void * +os_reserve(U64 size) +{ + void *result = mmap(0, size, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); + return result; +} + +internal B32 +os_commit(void *ptr, U64 size) +{ + mprotect(ptr, size, PROT_READ|PROT_WRITE); + return 1; +} + +internal void +os_decommit(void *ptr, U64 size) +{ + madvise(ptr, size, MADV_DONTNEED); + mprotect(ptr, size, PROT_NONE); +} + +internal void +os_release(void *ptr, U64 size) +{ + munmap(ptr, size); +} + +//- rjf: large pages + +internal void * +os_reserve_large(U64 size) +{ + void *result = mmap(0, size, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_HUGETLB, -1, 0); + return result; +} + +internal 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) + +internal U32 +os_tid(void) +{ + U32 result = 0; +#if defined(SYS_gettid) + result = syscall(SYS_gettid); +#else + result = gettid(); +#endif + return result; +} + +internal void +os_set_thread_name(String8 name) +{ + Temp scratch = scratch_begin(0, 0); + String8 name_copy = push_str8_copy(scratch.arena, name); + pthread_t current_thread = pthread_self(); + pthread_setname_np(current_thread, (char *)name_copy.str); + scratch_end(scratch); +} + +//////////////////////////////// +//~ rjf: @os_hooks Aborting (Implemented Per-OS) + +internal void +os_abort(S32 exit_code) +{ + exit(exit_code); +} + +//////////////////////////////// +//~ rjf: @os_hooks File System (Implemented Per-OS) + +//- rjf: files + +internal OS_Handle +os_file_open(OS_AccessFlags flags, String8 path) +{ + Temp scratch = scratch_begin(0, 0); + String8 path_copy = push_str8_copy(scratch.arena, path); + int lnx_flags = 0; + if(flags & (OS_AccessFlag_Read|OS_AccessFlag_Write)) + { + lnx_flags = O_RDWR; + } + else if(flags & OS_AccessFlag_Write) + { + lnx_flags = O_WRONLY; + } + else if(flags & OS_AccessFlag_Read) + { + lnx_flags = O_RDONLY; + } + if(flags & OS_AccessFlag_Append) + { + lnx_flags |= O_APPEND; + } + int fd = open((char *)path_copy.str, lnx_flags); + OS_Handle handle = {0}; + if(fd != -1) + { + handle.u64[0] = fd; + } + scratch_end(scratch); + return handle; +} + +internal void +os_file_close(OS_Handle file) +{ + if(os_handle_match(file, os_handle_zero())) { return; } + int fd = (int)file.u64[0]; + close(fd); +} + +internal U64 +os_file_read(OS_Handle file, Rng1U64 rng, void *out_data) +{ + if(os_handle_match(file, os_handle_zero())) { return 0; } + int fd = (int)file.u64[0]; + if(rng.min != 0) + { + lseek(fd, rng.min, SEEK_SET); + } + U64 total_num_bytes_to_read = dim_1u64(rng); + U64 total_num_bytes_read = 0; + U64 total_num_bytes_left_to_read = total_num_bytes_to_read; + for(;total_num_bytes_left_to_read > 0;) + { + int read_result = read(fd, (U8 *)out_data + total_num_bytes_read, total_num_bytes_left_to_read); + if(read_result >= 0) + { + total_num_bytes_read += read_result; + total_num_bytes_left_to_read -= read_result; + } + else if(errno != EINTR) + { + break; + } + } + return total_num_bytes_read; +} + +internal U64 +os_file_write(OS_Handle file, Rng1U64 rng, void *data) +{ + if(os_handle_match(file, os_handle_zero())) { return 0; } + int fd = (int)file.u64[0]; + if(rng.min != 0) + { + lseek(fd, rng.min, SEEK_SET); + } + U64 total_num_bytes_to_write = dim_1u64(rng); + U64 total_num_bytes_written = 0; + U64 total_num_bytes_left_to_write = total_num_bytes_to_write; + for(;total_num_bytes_left_to_write > 0;) + { + int write_result = write(fd, (U8 *)data + total_num_bytes_written, total_num_bytes_left_to_write); + if(write_result >= 0) + { + total_num_bytes_written += write_result; + total_num_bytes_left_to_write -= write_result; + } + else if(errno != EINTR) + { + break; + } + } + return total_num_bytes_written; +} + +internal B32 +os_file_set_times(OS_Handle file, DateTime date_time) +{ + if(os_handle_match(file, os_handle_zero())) { return 0; } + int fd = (int)file.u64[0]; + timespec time = os_lnx_timespec_from_date_time(date_time); + timespec times[2] = {time, time}; + int futimens_result = futimens(fd, times); + B32 good = (futimens_result != -1); + return good; +} + +internal FileProperties +os_properties_from_file(OS_Handle file) +{ + if(os_handle_match(file, os_handle_zero())) { return (FileProperties){0}; } + int fd = (int)file.u64[0]; + struct stat fd_stat = {0}; + int fstat_result = fstat(fd, &fd_stat); + FileProperties props = {0}; + if(fstat_result != -1) + { + props = os_lnx_file_properties_from_stat(&fd_stat); + } + return props; +} + +internal OS_FileID +os_id_from_file(OS_Handle file) +{ + if(os_handle_match(file, os_handle_zero())) { return (OS_FileID){0}; } + int fd = (int)file.u64[0]; + struct stat fd_stat = {0}; + int fstat_result = fstat(fd, &fd_stat); + OS_FileID id = {0}; + if(fstat_result != -1) + { + id.v[0] = fd_stat.st_dev; + id.v[1] = fd_stat.st_ino; + } + return id; +} + +internal B32 +os_delete_file_at_path(String8 path) +{ + Temp scratch = scratch_begin(0, 0); + B32 result = 0; + String8 path_copy = push_str8_copy(scratch.arena, path); + if(remove((char*)path_copy.str) != -1) + { + result = 1; + } + scratch_end(scratch); + return result; +} + +internal B32 +os_copy_file_path(String8 dst, String8 src) +{ + B32 result = 0; + OS_Handle src_h = os_file_open(OS_AccessFlag_Read, src); + OS_Handle dst_h = os_file_open(OS_AccessFlag_Write, dst); + if(!os_handle_match(src_h, os_handle_zero()) && + !os_handle_match(dst_h, os_handle_zero())) + { + FileProperties src_props = os_properties_from_file(src_h); + U64 size = src_props.size; + U64 total_bytes_copied = 0; + U64 bytes_left_to_copy = size; + for(;bytes_left_to_copy > 0;) + { + Temp scratch = scratch_begin(0, 0); + U64 buffer_size = Min(bytes_left_to_copy, MB(8)); + U8 *buffer = push_array_no_zero(scratch.arena, U8, buffer_size); + U64 bytes_read = os_file_read(src_h, r1u64(total_bytes_copied, total_bytes_copied+buffer_size), buffer); + U64 bytes_written = os_file_write(dst_h, r1u64(total_bytes_copied, total_bytes_copied+bytes_read), buffer); + U64 bytes_copied = Min(bytes_read, bytes_written); + bytes_left_to_copy -= bytes_copied; + total_bytes_copied += bytes_copied; + scratch_end(scratch); + if(bytes_copied == 0) + { + break; + } + } + } + os_file_close(src_h); + os_file_close(dst_h); + return result; +} + +internal String8 +os_full_path_from_path(Arena *arena, String8 path) +{ + Temp scratch = scratch_begin(&arena, 1); + String8 path_copy = push_str8_copy(scratch.arena, path); + char buffer[PATH_MAX] = {0}; + realpath((char *)path_copy.str, buffer); + String8 result = push_str8_copy(arena, str8_cstring(buffer)); + scratch_end(scratch); + return result; +} + +internal B32 +os_file_path_exists(String8 path) +{ + Temp scratch = scratch_begin(0, 0); + String8 path_copy = push_str8_copy(scratch.arena, path); + int access_result = access((char *)path_copy.str, F_OK); + B32 result = 0; + if(access_result == 0) + { + result = 1; + } + scratch_end(scratch); + return result; +} + +internal FileProperties +os_properties_from_file_path(String8 path) +{ + Temp scratch = scratch_begin(0, 0); + String8 path_copy = push_str8_copy(scratch.arena, path); + struct stat f_stat = {0}; + int stat_result = stat((char *)path_copy.str, &f_stat); + FileProperties props = {0}; + if(stat_result != -1) + { + props = os_lnx_file_properties_from_stat(&f_stat); + } + scratch_end(scratch); + return props; +} + +//- rjf: file maps + +internal OS_Handle +os_file_map_open(OS_AccessFlags flags, OS_Handle file) +{ + OS_Handle map = file; + return map; +} + +internal void +os_file_map_close(OS_Handle map) +{ + // NOTE(rjf): nothing to do; `map` handles are the same as `file` handles in + // the linux implementation (on Windows they require separate handles) +} + +internal void * +os_file_map_view_open(OS_Handle map, OS_AccessFlags flags, Rng1U64 range) +{ + if(os_handle_match(map, os_handle_zero())) { return 0; } + int fd = (int)map.u64[0]; + int prot_flags = 0; + if(flags & OS_AccessFlag_Write) { prot_flags |= PROT_WRITE; } + if(flags & OS_AccessFlag_Read) { prot_flags |= PROT_READ; } + int map_flags = MAP_PRIVATE; + void *base = mmap(0, dim_1u64(range), prot_flags, map_flags, fd, range.min); + return base; +} + +internal void +os_file_map_view_close(OS_Handle map, void *ptr, Rng1U64 range) +{ + munmap(ptr, dim_1u64(range)); +} + +//- rjf: directory iteration + +internal OS_FileIter * +os_file_iter_begin(Arena *arena, String8 path, OS_FileIterFlags flags) +{ + OS_FileIter *base_iter = push_array(arena, OS_FileIter, 1); + base_iter->flags = flags; + OS_LNX_FileIter *iter = (OS_LNX_FileIter *)base_iter->memory; + { + String8 path_copy = push_str8_copy(arena, path); + iter->dir = opendir((char *)path_copy.str); + iter->path = path_copy; + } + return base_iter; +} + +internal B32 +os_file_iter_next(Arena *arena, OS_FileIter *iter, OS_FileInfo *info_out) +{ + B32 good = 0; + OS_LNX_FileIter *lnx_iter = (OS_LNX_FileIter *)iter->memory; + for(;;) + { + // rjf: get next entry + lnx_iter->dp = readdir(lnx_iter->dir); + good = (lnx_iter->dp != 0); + + // rjf: unpack entry info + struct stat st = {0}; + int stat_result = 0; + if(good) + { + Temp scratch = scratch_begin(&arena, 1); + String8 full_path = push_str8f(scratch.arena, "%S/%s", lnx_iter->path, lnx_iter->dp->d_name); + stat_result = stat((char *)full_path.str, &st); + scratch_end(scratch); + } + + // rjf: determine if filtered + B32 filtered = 0; + if(good) + { + filtered = ((st.st_mode == S_IFDIR && iter->flags & OS_FileIterFlag_SkipFolders) || + (st.st_mode == S_IFREG && iter->flags & OS_FileIterFlag_SkipFiles)); + } + + // rjf: output & exit, if good & unfiltered + if(good && !filtered) + { + info_out->name = push_str8_copy(arena, str8_cstring(lnx_iter->dp->d_name)); + if(stat_result != -1) + { + info_out->props = os_lnx_file_properties_from_stat(&st); + } + break; + } + + // rjf: exit if not good + if(!good) + { + break; + } + } + return good; +} + +internal void +os_file_iter_end(OS_FileIter *iter) +{ + OS_LNX_FileIter *lnx_iter = (OS_LNX_FileIter *)iter->memory; + closedir(lnx_iter->dir); +} + +//- rjf: directory creation + +internal B32 +os_make_directory(String8 path) +{ + Temp scratch = scratch_begin(0, 0); + B32 result = 0; + String8 path_copy = push_str8_copy(scratch.arena, path); + if(mkdir((char*)path_copy.str, 0777) != -1) + { + result = 1; + } + scratch_end(scratch); + return result; +} + +//////////////////////////////// +//~ rjf: @os_hooks Shared Memory (Implemented Per-OS) + +internal OS_Handle +os_shared_memory_alloc(U64 size, String8 name) +{ + Temp scratch = scratch_begin(0, 0); + String8 name_copy = push_str8_copy(scratch.arena, name); + int id = shm_open((char *)name_copy.str, O_RDWR, 0); + ftruncate(id, size); + OS_Handle result = {(U64)id}; + scratch_end(scratch); + return result; +} + +internal OS_Handle +os_shared_memory_open(String8 name) +{ + Temp scratch = scratch_begin(0, 0); + String8 name_copy = push_str8_copy(scratch.arena, name); + int id = shm_open((char *)name_copy.str, O_RDWR, 0); + OS_Handle result = {(U64)id}; + scratch_end(scratch); + return result; +} + +internal void +os_shared_memory_close(OS_Handle handle) +{ + if(os_handle_match(handle, os_handle_zero())){return;} + int id = (int)handle.u64[0]; + close(id); +} + +internal void * +os_shared_memory_view_open(OS_Handle handle, Rng1U64 range) +{ + if(os_handle_match(handle, os_handle_zero())){return 0;} + int id = (int)handle.u64[0]; + void *base = mmap(0, dim_1u64(range), PROT_READ|PROT_WRITE, MAP_SHARED, id, range.min); + return base; +} + +internal void +os_shared_memory_view_close(OS_Handle handle, void *ptr, Rng1U64 range) +{ + if(os_handle_match(handle, os_handle_zero())){return;} + munmap(ptr, dim_1u64(range)); +} + +//////////////////////////////// +//~ rjf: @os_hooks Time (Implemented Per-OS) + +internal U64 +os_now_microseconds(void) +{ + struct timespec t; + clock_gettime(CLOCK_MONOTONIC, &t); + U64 result = t.tv_sec*Million(1) + (t.tv_nsec/Thousand(1)); + return result; +} + +internal U32 +os_now_unix(void) +{ + time_t t = time(0); + return (U32)t; +} + +internal DateTime +os_now_universal_time(void) +{ + time_t t = 0; + time(&t); + struct tm universal_tm = {0}; + gmtime_r(&t, &universal_tm); + DateTime result = os_lnx_date_time_from_tm(universal_tm, 0); + return result; +} + +internal DateTime +os_universal_time_from_local(DateTime *date_time) +{ + // rjf: local DateTime -> universal time_t + tm local_tm = os_lnx_tm_from_date_time(*date_time); + local_tm.tm_isdst = -1; + time_t universal_t = mktime(&local_tm); + + // rjf: universal time_t -> DateTime + tm universal_tm = {0}; + gmtime_r(&universal_t, &universal_tm); + DateTime result = os_lnx_date_time_from_tm(universal_tm, 0); + return result; +} + +internal DateTime +os_local_time_from_universal(DateTime *date_time) +{ + // rjf: universal DateTime -> local time_t + tm universal_tm = os_lnx_tm_from_date_time(*date_time); + universal_tm.tm_isdst = -1; + time_t universal_t = timegm(&universal_tm); + tm local_tm = {0}; + localtime_r(&universal_t, &local_tm); + + // rjf: local tm -> DateTime + DateTime result = os_lnx_date_time_from_tm(local_tm, 0); + return result; +} + +internal void +os_sleep_milliseconds(U32 msec) +{ + usleep(msec*Thousand(1)); +} + +//////////////////////////////// +//~ rjf: @os_hooks Child Processes (Implemented Per-OS) + +internal OS_Handle +os_process_launch(OS_ProcessLaunchParams *params) +{ + NotImplemented; +} + +internal B32 +os_process_join(OS_Handle handle, U64 endt_us) +{ + NotImplemented; +} + +internal void +os_process_detach(OS_Handle handle) +{ + NotImplemented; +} + +//////////////////////////////// +//~ rjf: @os_hooks Threads (Implemented Per-OS) + +internal OS_Handle +os_thread_launch(OS_ThreadFunctionType *func, void *ptr, void *params) +{ + OS_LNX_Entity *entity = os_lnx_entity_alloc(OS_LNX_EntityKind_Thread); + entity->thread.func = func; + entity->thread.ptr = ptr; + { + pthread_attr_t attr; + pthread_attr_init(&attr); + int pthread_result = pthread_create(&entity->thread.handle, &attr, os_lnx_thread_entry_point, entity); + pthread_attr_destroy(&attr); + if(pthread_result == -1) + { + os_lnx_entity_release(entity); + entity = 0; + } + } + OS_Handle handle = {(U64)entity}; + return handle; +} + +internal B32 +os_thread_join(OS_Handle handle, U64 endt_us) +{ + if(os_handle_match(handle, os_handle_zero())) { return 0; } + OS_LNX_Entity *entity = (OS_LNX_Entity *)handle.u64[0]; + int join_result = pthread_join(entity->thread.handle, 0); + B32 result = (join_result == 0); + os_lnx_entity_release(entity); + return result; +} + +internal void +os_thread_detach(OS_Handle handle) +{ + if(os_handle_match(handle, os_handle_zero())) { return; } + OS_LNX_Entity *entity = (OS_LNX_Entity *)handle.u64[0]; + os_lnx_entity_release(entity); +} + +//////////////////////////////// +//~ rjf: @os_hooks Synchronization Primitives (Implemented Per-OS) + +//- rjf: mutexes + +internal OS_Handle +os_mutex_alloc(void) +{ + OS_LNX_Entity *entity = os_lnx_entity_alloc(OS_LNX_EntityKind_Mutex); + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); + int init_result = pthread_mutex_init(&entity->mutex_handle, &attr); + pthread_mutexattr_destroy(&attr); + if(init_result == -1) + { + os_lnx_entity_release(entity); + entity = 0; + } + OS_Handle handle = {(U64)entity}; + return handle; +} + +internal void +os_mutex_release(OS_Handle mutex) +{ + if(os_handle_match(mutex, os_handle_zero())) { return; } + OS_LNX_Entity *entity = (OS_LNX_Entity *)mutex.u64[0]; + pthread_mutex_destroy(&entity->mutex_handle); + os_lnx_entity_release(entity); +} + +internal void +os_mutex_take(OS_Handle mutex) +{ + if(os_handle_match(mutex, os_handle_zero())) { return; } + OS_LNX_Entity *entity = (OS_LNX_Entity *)mutex.u64[0]; + pthread_mutex_lock(&entity->mutex_handle); +} + +internal void +os_mutex_drop(OS_Handle mutex) +{ + if(os_handle_match(mutex, os_handle_zero())) { return; } + OS_LNX_Entity *entity = (OS_LNX_Entity *)mutex.u64[0]; + pthread_mutex_unlock(&entity->mutex_handle); +} + +//- rjf: reader/writer mutexes + +internal OS_Handle +os_rw_mutex_alloc(void) +{ + OS_LNX_Entity *entity = os_lnx_entity_alloc(OS_LNX_EntityKind_RWMutex); + int init_result = pthread_rwlock_init(&entity->rwmutex_handle, 0); + if(init_result == -1) + { + os_lnx_entity_release(entity); + entity = 0; + } + OS_Handle handle = {(U64)entity}; + return handle; +} + +internal void +os_rw_mutex_release(OS_Handle rw_mutex) +{ + if(os_handle_match(rw_mutex, os_handle_zero())) { return; } + OS_LNX_Entity *entity = (OS_LNX_Entity *)rw_mutex.u64[0]; + pthread_rwlock_destroy(&entity->rwmutex_handle); + os_lnx_entity_release(entity); +} + +internal void +os_rw_mutex_take_r(OS_Handle rw_mutex) +{ + if(os_handle_match(rw_mutex, os_handle_zero())) { return; } + OS_LNX_Entity *entity = (OS_LNX_Entity *)rw_mutex.u64[0]; + pthread_rwlock_rdlock(&entity->rwmutex_handle); +} + +internal void +os_rw_mutex_drop_r(OS_Handle rw_mutex) +{ + if(os_handle_match(rw_mutex, os_handle_zero())) { return; } + OS_LNX_Entity *entity = (OS_LNX_Entity *)rw_mutex.u64[0]; + pthread_rwlock_unlock(&entity->rwmutex_handle); +} + +internal void +os_rw_mutex_take_w(OS_Handle rw_mutex) +{ + if(os_handle_match(rw_mutex, os_handle_zero())) { return; } + OS_LNX_Entity *entity = (OS_LNX_Entity *)rw_mutex.u64[0]; + pthread_rwlock_wrlock(&entity->rwmutex_handle); +} + +internal void +os_rw_mutex_drop_w(OS_Handle rw_mutex) +{ + if(os_handle_match(rw_mutex, os_handle_zero())) { return; } + OS_LNX_Entity *entity = (OS_LNX_Entity *)rw_mutex.u64[0]; + pthread_rwlock_unlock(&entity->rwmutex_handle); +} + +//- rjf: condition variables + +internal OS_Handle +os_condition_variable_alloc(void) +{ + OS_LNX_Entity *entity = os_lnx_entity_alloc(OS_LNX_EntityKind_ConditionVariable); + int init_result = pthread_cond_init(&entity->cv.cond_handle, 0); + if(init_result == -1) + { + os_lnx_entity_release(entity); + entity = 0; + } + int init2_result = 0; + if(entity) + { + init2_result = pthread_mutex_init(&entity->cv.rwlock_mutex_handle, 0); + } + if(init2_result == -1) + { + pthread_cond_destroy(&entity->cv.cond_handle); + os_lnx_entity_release(entity); + entity = 0; + } + OS_Handle handle = {(U64)entity}; + return handle; +} + +internal void +os_condition_variable_release(OS_Handle cv) +{ + if(os_handle_match(cv, os_handle_zero())) { return; } + OS_LNX_Entity *entity = (OS_LNX_Entity *)cv.u64[0]; + pthread_cond_destroy(&entity->cv.cond_handle); + pthread_mutex_destroy(&entity->cv.rwlock_mutex_handle); + os_lnx_entity_release(entity); +} + +internal B32 +os_condition_variable_wait(OS_Handle cv, OS_Handle mutex, U64 endt_us) +{ + if(os_handle_match(cv, os_handle_zero())) { return 0; } + if(os_handle_match(mutex, os_handle_zero())) { return 0; } + OS_LNX_Entity *cv_entity = (OS_LNX_Entity *)cv.u64[0]; + OS_LNX_Entity *mutex_entity = (OS_LNX_Entity *)mutex.u64[0]; + struct timespec endt_timespec; + endt_timespec.tv_sec = endt_us/Million(1); + endt_timespec.tv_nsec = Thousand(1) * (endt_us - (endt_us/Million(1))*Million(1)); + int wait_result = pthread_cond_timedwait(&cv_entity->cv.cond_handle, &mutex_entity->mutex_handle, &endt_timespec); + B32 result = (wait_result != ETIMEDOUT); + return result; +} + +internal B32 +os_condition_variable_wait_rw_r(OS_Handle cv, OS_Handle mutex_rw, U64 endt_us) +{ + // TODO(rjf): because pthread does not supply cv/rw natively, I had to hack + // this together, but this would probably just be a lot better if we just + // implemented the primitives ourselves with e.g. futexes + // + if(os_handle_match(cv, os_handle_zero())) { return 0; } + if(os_handle_match(mutex_rw, os_handle_zero())) { return 0; } + OS_LNX_Entity *cv_entity = (OS_LNX_Entity *)cv.u64[0]; + OS_LNX_Entity *rw_mutex_entity = (OS_LNX_Entity *)mutex_rw.u64[0]; + struct timespec endt_timespec; + endt_timespec.tv_sec = endt_us/Million(1); + endt_timespec.tv_nsec = Thousand(1) * (endt_us - (endt_us/Million(1))*Million(1)); + B32 result = 0; + for(;;) + { + pthread_mutex_lock(&cv_entity->cv.rwlock_mutex_handle); + int wait_result = pthread_cond_timedwait(&cv_entity->cv.cond_handle, &cv_entity->cv.rwlock_mutex_handle, &endt_timespec); + if(wait_result != ETIMEDOUT) + { + pthread_rwlock_rdlock(&rw_mutex_entity->rwmutex_handle); + pthread_mutex_unlock(&cv_entity->cv.rwlock_mutex_handle); + result = 1; + break; + } + pthread_mutex_unlock(&cv_entity->cv.rwlock_mutex_handle); + if(wait_result == ETIMEDOUT) + { + break; + } + } + return result; +} + +internal B32 +os_condition_variable_wait_rw_w(OS_Handle cv, OS_Handle mutex_rw, U64 endt_us) +{ + // TODO(rjf): because pthread does not supply cv/rw natively, I had to hack + // this together, but this would probably just be a lot better if we just + // implemented the primitives ourselves with e.g. futexes + // + if(os_handle_match(cv, os_handle_zero())) { return 0; } + if(os_handle_match(mutex_rw, os_handle_zero())) { return 0; } + OS_LNX_Entity *cv_entity = (OS_LNX_Entity *)cv.u64[0]; + OS_LNX_Entity *rw_mutex_entity = (OS_LNX_Entity *)mutex_rw.u64[0]; + struct timespec endt_timespec; + endt_timespec.tv_sec = endt_us/Million(1); + endt_timespec.tv_nsec = Thousand(1) * (endt_us - (endt_us/Million(1))*Million(1)); + B32 result = 0; + for(;;) + { + pthread_mutex_lock(&cv_entity->cv.rwlock_mutex_handle); + int wait_result = pthread_cond_timedwait(&cv_entity->cv.cond_handle, &cv_entity->cv.rwlock_mutex_handle, &endt_timespec); + if(wait_result != ETIMEDOUT) + { + pthread_rwlock_wrlock(&rw_mutex_entity->rwmutex_handle); + pthread_mutex_unlock(&cv_entity->cv.rwlock_mutex_handle); + result = 1; + break; + } + pthread_mutex_unlock(&cv_entity->cv.rwlock_mutex_handle); + if(wait_result == ETIMEDOUT) + { + break; + } + } + return result; +} + +internal void +os_condition_variable_signal(OS_Handle cv) +{ + if(os_handle_match(cv, os_handle_zero())) { return; } + OS_LNX_Entity *cv_entity = (OS_LNX_Entity *)cv.u64[0]; + pthread_cond_signal(&cv_entity->cv.cond_handle); +} + +internal void +os_condition_variable_broadcast(OS_Handle cv) +{ + if(os_handle_match(cv, os_handle_zero())) { return; } + OS_LNX_Entity *cv_entity = (OS_LNX_Entity *)cv.u64[0]; + pthread_cond_broadcast(&cv_entity->cv.cond_handle); +} + +//- rjf: cross-process semaphores + +internal OS_Handle +os_semaphore_alloc(U32 initial_count, U32 max_count, String8 name) +{ + NotImplemented; +} + +internal void +os_semaphore_release(OS_Handle semaphore) +{ + NotImplemented; +} + +internal OS_Handle +os_semaphore_open(String8 name) +{ + NotImplemented; +} + +internal void +os_semaphore_close(OS_Handle semaphore) +{ + NotImplemented; +} + +internal B32 +os_semaphore_take(OS_Handle semaphore, U64 endt_us) +{ + NotImplemented; +} + +internal void +os_semaphore_drop(OS_Handle semaphore) +{ + NotImplemented; +} + +//////////////////////////////// +//~ rjf: @os_hooks Dynamically-Loaded Libraries (Implemented Per-OS) + +internal OS_Handle +os_library_open(String8 path) +{ + Temp scratch = scratch_begin(0, 0); + char *path_cstr = (char *)push_str8_copy(scratch.arena, path).str; + void *so = dlopen(path_cstr, RTLD_LAZY); + OS_Handle lib = { (U64)so }; + scratch_end(scratch); + return lib; +} + +internal VoidProc* +os_library_load_proc(OS_Handle lib, String8 name) +{ + Temp scratch = scratch_begin(0, 0); + void *so = (void *)lib.u64; + char *name_cstr = (char *)push_str8_copy(scratch.arena, name).str; + VoidProc *proc = (VoidProc *)dlsym(so, name_cstr); + scratch_end(scratch); + return proc; +} + +internal void +os_library_close(OS_Handle lib) +{ + void *so = (void *)lib.u64; + dlclose(so); +} + +//////////////////////////////// +//~ rjf: @os_hooks Safe Calls (Implemented Per-OS) + +internal void +os_safe_call(OS_ThreadFunctionType *func, OS_ThreadFunctionType *fail_handler, void *ptr) +{ + // rjf: push handler to chain + OS_LNX_SafeCallChain chain = {0}; + SLLStackPush(os_lnx_safe_call_chain, &chain); + chain.fail_handler = fail_handler; + chain.ptr = ptr; + + // rjf: set up sig handler info + struct sigaction new_act = {0}; + new_act.sa_handler = os_lnx_safe_call_sig_handler; + int signals_to_handle[] = + { + SIGILL, SIGFPE, SIGSEGV, SIGBUS, SIGTRAP, + }; + struct sigaction og_act[ArrayCount(signals_to_handle)] = {0}; + + // rjf: attach handler info for all signals + for(U32 i = 0; i < ArrayCount(signals_to_handle); i += 1) + { + sigaction(signals_to_handle[i], &new_act, &og_act[i]); + } + + // rjf: call function + func(ptr); + + // rjf: reset handler info for all signals + for(U32 i = 0; i < ArrayCount(signals_to_handle); i += 1) + { + sigaction(signals_to_handle[i], &og_act[i], 0); + } +} + +//////////////////////////////// +//~ rjf: @os_hooks GUIDs (Implemented Per-OS) + +internal OS_Guid +os_make_guid(void) +{ + U8 random_bytes[16] = {0}; + StaticAssert(sizeof(random_bytes) == sizeof(OS_Guid), os_lnx_guid_size_check); + getrandom(random_bytes, sizeof(random_bytes), 0); + OS_Guid guid = {0}; + MemoryCopy(&guid, random_bytes, sizeof(random_bytes)); + guid.data3 &= 0x0fff; + guid.data3 |= (4 << 12); + guid.data4[0] &= 0x3f; + guid.data4[0] |= 0x80; + return guid; +} + +//////////////////////////////// +//~ rjf: @os_hooks Entry Points (Implemented Per-OS) + +int +main(int argc, char **argv) +{ + //- rjf: set up OS layer + { + //- rjf: get statically-allocated system/process info + { + OS_SystemInfo *info = &os_lnx_state.system_info; + info->logical_processor_count = (U32)get_nprocs(); + info->page_size = (U64)getpagesize(); + info->large_page_size = MB(2); + info->allocation_granularity = info->page_size; + } + { + OS_ProcessInfo *info = &os_lnx_state.process_info; + info->pid = (U32)getpid(); + } + + //- rjf: set up thread context + local_persist TCTX tctx; + tctx_init_and_equip(&tctx); + + //- rjf: set up dynamically allocated state + os_lnx_state.arena = arena_alloc(); + os_lnx_state.entity_arena = arena_alloc(); + pthread_mutex_init(&os_lnx_state.entity_mutex, 0); + + //- rjf: grab dynamically allocated system info + { + Temp scratch = scratch_begin(0, 0); + OS_SystemInfo *info = &os_lnx_state.system_info; + + // rjf: get machine name + B32 got_final_result = 0; + U8 *buffer = 0; + int size = 0; + for(S64 cap = 4096, r = 0; r < 4; cap *= 2, r += 1) + { + scratch_end(scratch); + buffer = push_array_no_zero(scratch.arena, U8, cap); + size = gethostname((char*)buffer, cap); + if(size < cap) + { + got_final_result = 1; + break; + } + } + + // rjf: save name to info + if(got_final_result && size > 0) + { + info->machine_name.size = size; + info->machine_name.str = push_array_no_zero(os_lnx_state.arena, U8, info->machine_name.size + 1); + MemoryCopy(info->machine_name.str, buffer, info->machine_name.size); + info->machine_name.str[info->machine_name.size] = 0; + } + + scratch_end(scratch); + } + + //- rjf: grab dynamically allocated process info + { + Temp scratch = scratch_begin(0, 0); + OS_ProcessInfo *info = &os_lnx_state.process_info; + + // rjf: grab binary path + { + // rjf: get self string + B32 got_final_result = 0; + U8 *buffer = 0; + int size = 0; + for(S64 cap = PATH_MAX, r = 0; r < 4; cap *= 2, r += 1) + { + scratch_end(scratch); + buffer = push_array_no_zero(scratch.arena, U8, cap); + size = readlink("/proc/self/exe", (char*)buffer, cap); + if(size < cap) + { + got_final_result = 1; + break; + } + } + + // rjf: save + if(got_final_result && size > 0) + { + String8 full_name = str8(buffer, size); + String8 name_chopped = str8_chop_last_slash(full_name); + info->binary_path = push_str8_copy(os_lnx_state.arena, name_chopped); + } + } + + // rjf: grab initial directory + { + info->initial_path = os_get_current_path(os_lnx_state.arena); + } + + // rjf: grab home directory + { + char *home = getenv("HOME"); + info->user_program_data_path = str8_cstring(home); + } + + scratch_end(scratch); + } + } + + //- rjf: call into "real" entry point + main_thread_base_entry_point(entry_point, argv, (U64)argc); +} diff --git a/src/metagen/metagen_os/core/linux/metagen_os_core_linux.h b/src/metagen/metagen_os/core/linux/metagen_os_core_linux.h index 9899f94a..531c6af3 100644 --- a/src/metagen/metagen_os/core/linux/metagen_os_core_linux.h +++ b/src/metagen/metagen_os/core/linux/metagen_os_core_linux.h @@ -1,88 +1,134 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#ifndef LINUX_H -#define LINUX_H - -//////////////////////////////// -//~ NOTE(allen): Get all these linux includes - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -//////////////////////////////// -//~ NOTE(allen): File Iterator - -struct LNX_FileIter{ - int fd; - DIR *dir; -}; -StaticAssert(sizeof(Member(OS_FileIter, memory)) >= sizeof(LNX_FileIter), file_iter_memory_size); - -//////////////////////////////// -//~ NOTE(allen): Threading Entities - -enum LNX_EntityKind{ - LNX_EntityKind_Null, - LNX_EntityKind_Thread, - LNX_EntityKind_Mutex, - LNX_EntityKind_ConditionVariable, -}; - -struct LNX_Entity{ - LNX_Entity *next; - LNX_EntityKind kind; - volatile U32 reference_mask; - union{ - struct{ - OS_ThreadFunctionType *func; - void *ptr; - pthread_t handle; - } thread; - pthread_mutex_t mutex; - pthread_cond_t cond; - }; -}; - -//////////////////////////////// -//~ NOTE(allen): Safe Call Chain - -struct LNX_SafeCallChain{ - LNX_SafeCallChain *next; - OS_ThreadFunctionType *fail_handler; - void *ptr; -}; - -//////////////////////////////// -//~ NOTE(allen): Helpers - -internal B32 lnx_write_list_to_file_descriptor(int fd, String8List list); - -internal void lnx_date_time_from_tm(DateTime *out, struct tm *in, U32 msec); -internal void lnx_tm_from_date_time(struct tm *out, DateTime *in); -internal void lnx_dense_time_from_timespec(DenseTime *out, struct timespec *in); -internal void lnx_file_properties_from_stat(FileProperties *out, struct stat *in); - -internal String8 lnx_string_from_signal(int signum); -internal String8 lnx_string_from_errno(int error_number); - -internal LNX_Entity* lnx_alloc_entity(LNX_EntityKind kind); -internal void lnx_free_entity(LNX_Entity *entity); -internal void* lnx_thread_base(void *ptr); - -internal void lnx_safe_call_sig_handler(int); - -#endif //LINUX_H +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef OS_CORE_LINUX_H +#define OS_CORE_LINUX_H + +//////////////////////////////// +//~ rjf: Includes + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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; +}; +StaticAssert(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_Thread, + OS_LNX_EntityKind_Mutex, + OS_LNX_EntityKind_RWMutex, + OS_LNX_EntityKind_ConditionVariable, +} +OS_LNX_EntityKind; + +typedef struct OS_LNX_Entity OS_LNX_Entity; +struct OS_LNX_Entity +{ + OS_LNX_Entity *next; + OS_LNX_EntityKind kind; + union + { + struct + { + pthread_t handle; + OS_ThreadFunctionType *func; + void *ptr; + } 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: Globals + +global OS_LNX_State os_lnx_state = {0}; +thread_static OS_LNX_SafeCallChain *os_lnx_safe_call_chain = 0; + +//////////////////////////////// +//~ rjf: Helpers + +internal DateTime os_lnx_date_time_from_tm(tm in, U32 msec); +internal tm os_lnx_tm_from_date_time(DateTime dt); +internal timespec os_lnx_timespec_from_date_time(DateTime dt); +internal DenseTime os_lnx_dense_time_from_timespec(timespec in); +internal FileProperties os_lnx_file_properties_from_stat(struct stat *s); +internal void os_lnx_safe_call_sig_handler(int x); + +//////////////////////////////// +//~ rjf: Entities + +internal OS_LNX_Entity *os_lnx_entity_alloc(OS_LNX_EntityKind kind); +internal void os_lnx_entity_release(OS_LNX_Entity *entity); + +//////////////////////////////// +//~ rjf: Thread Entry Point + +internal void *os_lnx_thread_entry_point(void *ptr); + +#endif // OS_CORE_LINUX_H diff --git a/src/metagen/metagen_os/core/metagen_os_core.c b/src/metagen/metagen_os/core/metagen_os_core.c index 0323091d..a6718e32 100644 --- a/src/metagen/metagen_os/core/metagen_os_core.c +++ b/src/metagen/metagen_os/core/metagen_os_core.c @@ -1,265 +1,173 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -//////////////////////////////// -//~ rjf: Handle Type Functions (Helpers, Implemented Once) - -internal OS_Handle -os_handle_zero(void) -{ - OS_Handle handle = {0}; - return handle; -} - -internal B32 -os_handle_match(OS_Handle a, OS_Handle b) -{ - return a.u64[0] == b.u64[0]; -} - -internal void -os_handle_list_push(Arena *arena, OS_HandleList *handles, OS_Handle handle) -{ - OS_HandleNode *n = push_array(arena, OS_HandleNode, 1); - n->v = handle; - SLLQueuePush(handles->first, handles->last, n); - handles->count += 1; -} - -internal OS_HandleArray -os_handle_array_from_list(Arena *arena, OS_HandleList *list) -{ - OS_HandleArray result = {0}; - result.count = list->count; - result.v = push_array_no_zero(arena, 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: System Path Helper (Helper, Implemented Once) - -internal String8 -os_string_from_system_path(Arena *arena, OS_SystemPath path) -{ - String8List strs = {0}; - os_string_list_from_system_path(arena, path, &strs); - String8 result = str8_list_first(&strs); - return result; -} - -//////////////////////////////// -//~ rjf: Command Line Argc/Argv Helper (Helper, Implemented Once) - -internal String8List -os_string_list_from_argcv(Arena *arena, int argc, char **argv) -{ - String8List result = {0}; - for(int i = 0; i < argc; i += 1) - { - String8 str = str8_cstring(argv[i]); - str8_list_push(arena, &result, str); - } - return result; -} - -//////////////////////////////// -//~ rjf: Process Helpers (Helper, Implemented Once) - -internal void -os_relaunch_self(void){ - Temp scratch = scratch_begin(0, 0); - OS_LaunchOptions opts = {0}; - opts.cmd_line = os_get_command_line_arguments(); - opts.path = os_string_from_system_path(scratch.arena, OS_SystemPath_Initial); - os_launch_process(&opts, 0); - scratch_end(scratch); -} - -//////////////////////////////// -//~ rjf: Filesystem Helpers (Helpers, Implemented Once) - -internal String8 -os_data_from_file_path(Arena *arena, String8 path) -{ - OS_Handle file = os_file_open(OS_AccessFlag_Read|OS_AccessFlag_Shared, path); - FileProperties props = os_properties_from_file(file); - String8 data = os_string_from_file_range(arena, file, r1u64(0, props.size)); - os_file_close(file); - return data; -} - -internal 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; -} - -internal 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; -} - -internal FileProperties -os_properties_from_file_path(String8 path) -{ - OS_Handle file = os_file_open(OS_AccessFlag_Read|OS_AccessFlag_Shared, path); - FileProperties props = os_properties_from_file(file); - os_file_close(file); - return props; -} - -internal OS_FileID -os_id_from_file_path(String8 path) -{ - OS_Handle file = os_file_open(OS_AccessFlag_Read|OS_AccessFlag_Shared, path); - OS_FileID id = os_id_from_file(file); - os_file_close(file); - return id; -} - -internal S64 -os_file_id_compare(OS_FileID a, OS_FileID b) -{ - S64 cmp = MemoryCompare((void*)&a.v[0], (void*)&b.v[0], sizeof(a.v)); - return cmp; -} - -internal String8 -os_string_from_file_range(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; -} - -//////////////////////////////// -//~ rjf: Synchronization Primitive Helpers (Helpers, Implemented Once) - -internal void -os_mutex_take(OS_Handle mutex){ - ProfBeginLockWait((void *)(mutex.u64[0]), "take mutex"); - os_mutex_take_(mutex); - ProfEndLockWait(); - ProfLockTake((void *)(mutex.u64[0]), "take mutex"); -} - -internal void -os_mutex_drop(OS_Handle mutex){ - os_mutex_drop_(mutex); - ProfLockDrop((void *)(mutex.u64[0])); -} - -internal void -os_rw_mutex_take_r(OS_Handle rw_mutex){ - ProfBeginLockWait((void *)(rw_mutex.u64[0]), "rw mutex take r"); - os_rw_mutex_take_r_(rw_mutex); - ProfEndLockWait(); - ProfLockTake((void *)(rw_mutex.u64[0]), "rw mutex take r"); -} - -internal void -os_rw_mutex_drop_r(OS_Handle rw_mutex){ - os_rw_mutex_drop_r_(rw_mutex); - ProfLockDrop((void *)(rw_mutex.u64[0])); -} - -internal void -os_rw_mutex_take_w(OS_Handle rw_mutex){ - ProfBeginLockWait((void *)(rw_mutex.u64[0]), "rw mutex take rw"); - os_rw_mutex_take_w_(rw_mutex); - ProfEndLockWait(); - ProfLockTake((void *)(rw_mutex.u64[0]), "rw mutex take rw"); -} - -internal void -os_rw_mutex_drop_w(OS_Handle rw_mutex){ - os_rw_mutex_drop_w_(rw_mutex); - ProfLockDrop((void *)(rw_mutex.u64[0])); -} - -internal B32 -os_condition_variable_wait(OS_Handle cv, OS_Handle mutex, U64 endt_us){ - ProfLockDrop((void *)(mutex.u64[0])); - B32 result = os_condition_variable_wait_(cv, mutex, endt_us); - ProfLockTake((void *)(mutex.u64[0]), "wait cv"); - return(result); -} - -internal B32 -os_condition_variable_wait_rw_r(OS_Handle cv, OS_Handle mutex_rw, U64 endt_us){ - ProfLockDrop((void *)(mutex_rw.u64[0])); - B32 result = os_condition_variable_wait_rw_r_(cv, mutex_rw, endt_us); - ProfLockTake((void *)(mutex_rw.u64[0]), "wait cv rw r"); - return(result); -} - -internal B32 -os_condition_variable_wait_rw_w(OS_Handle cv, OS_Handle mutex_rw, U64 endt_us){ - ProfLockDrop((void *)(mutex_rw.u64[0])); - B32 result = os_condition_variable_wait_rw_w_(cv, mutex_rw, endt_us); - ProfLockTake((void *)(mutex_rw.u64[0]), "wait cv rw w"); - return(result); -} - -internal void -os_condition_variable_signal(OS_Handle cv){ - os_condition_variable_signal_(cv); -} - -internal void -os_condition_variable_broadcast(OS_Handle cv){ - os_condition_variable_broadcast_(cv); -} - -internal String8 -os_string_from_guid(Arena *arena, OS_Guid guid) -{ - String8 result = push_str8f(arena, "%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; -} +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +//////////////////////////////// +//~ rjf: Handle Type Functions (Helpers, Implemented Once) + +internal OS_Handle +os_handle_zero(void) +{ + OS_Handle handle = {0}; + return handle; +} + +internal B32 +os_handle_match(OS_Handle a, OS_Handle b) +{ + return a.u64[0] == b.u64[0]; +} + +internal void +os_handle_list_push(Arena *arena, OS_HandleList *handles, OS_Handle handle) +{ + OS_HandleNode *n = push_array(arena, OS_HandleNode, 1); + n->v = handle; + SLLQueuePush(handles->first, handles->last, n); + handles->count += 1; +} + +internal OS_HandleArray +os_handle_array_from_list(Arena *arena, OS_HandleList *list) +{ + OS_HandleArray result = {0}; + result.count = list->count; + result.v = push_array_no_zero(arena, 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) + +internal String8List +os_string_list_from_argcv(Arena *arena, int argc, char **argv) +{ + String8List result = {0}; + for(int i = 0; i < argc; i += 1) + { + String8 str = str8_cstring(argv[i]); + str8_list_push(arena, &result, str); + } + return result; +} + +//////////////////////////////// +//~ rjf: Filesystem Helpers (Helpers, Implemented Once) + +internal String8 +os_data_from_file_path(Arena *arena, 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(arena, file, r1u64(0, props.size)); + os_file_close(file); + return data; +} + +internal 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; +} + +internal 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; +} + +internal 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; +} + +internal 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; +} + +internal S64 +os_file_id_compare(OS_FileID a, OS_FileID b) +{ + S64 cmp = MemoryCompare((void*)&a.v[0], (void*)&b.v[0], sizeof(a.v)); + return cmp; +} + +internal String8 +os_string_from_file_range(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; +} + +//////////////////////////////// +//~ rjf: GUID Helpers (Helpers, Implemented Once) + +internal String8 +os_string_from_guid(Arena *arena, OS_Guid guid) +{ + String8 result = push_str8f(arena, "%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; +} diff --git a/src/metagen/metagen_os/core/metagen_os_core.h b/src/metagen/metagen_os/core/metagen_os_core.h index 34d33b99..dcfb900d 100644 --- a/src/metagen/metagen_os/core/metagen_os_core.h +++ b/src/metagen/metagen_os/core/metagen_os_core.h @@ -1,365 +1,336 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#ifndef OS_CORE_H -#define OS_CORE_H - -//////////////////////////////// -//~ 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_Shared = (1<<3), -}; - -//////////////////////////////// -//~ allen: 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[600]; -}; - -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: System Paths - -typedef enum OS_SystemPath -{ - OS_SystemPath_Binary, - OS_SystemPath_Initial, - OS_SystemPath_Current, - OS_SystemPath_UserProgramData, - OS_SystemPath_ModuleLoad, -} -OS_SystemPath; - -typedef enum OS_PathFromUserKind -{ - OS_PathFromUserKind_Save, - OS_PathFromUserKind_Load, -} -OS_PathFromUserKind; - -typedef struct OS_PathFromUser OS_PathFromUser; -struct OS_PathFromUser -{ - OS_PathFromUserKind kind; - String8 path; - U64 filter_count; - String8 *filter_extensions; - String8 *filter_names; -}; - -//////////////////////////////// -//~ allen: Launch Input - -typedef struct OS_LaunchOptions OS_LaunchOptions; -struct OS_LaunchOptions -{ - 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; -}; - -//////////////////////////////// -// Time - -#define OS_UNIX_TIME_MAX max_U32 -typedef U32 OS_UnixTime; - -//////////////////////////////// -// Global Unique ID - -typedef struct OS_Guid -{ - U32 data1; - U16 data2; - U16 data3; - U8 data4[8]; -} OS_Guid; -StaticAssert(sizeof(OS_Guid) == 16, os_guid_check); - -//////////////////////////////// -//~ rjf: Thread Types - -typedef void OS_ThreadFunctionType(void *ptr); - -//////////////////////////////// -//~ rjf: Handle Type Functions (Helpers, Implemented Once) - -internal OS_Handle os_handle_zero(void); -internal B32 os_handle_match(OS_Handle a, OS_Handle b); -internal void os_handle_list_push(Arena *arena, OS_HandleList *handles, OS_Handle handle); -internal OS_HandleArray os_handle_array_from_list(Arena *arena, OS_HandleList *list); - -//////////////////////////////// -//~ rjf: System Path Helper (Helper, Implemented Once) - -internal String8 os_string_from_system_path(Arena *arena, OS_SystemPath path); - -//////////////////////////////// -//~ rjf: Command Line Argc/Argv Helper (Helper, Implemented Once) - -internal String8List os_string_list_from_argcv(Arena *arena, int argc, char **argv); - -//////////////////////////////// -//~ rjf: Process Helpers (Helper, Implemented Once) - -internal void os_relaunch_self(void); - -//////////////////////////////// -//~ rjf: Filesystem Helpers (Helpers, Implemented Once) - -internal String8 os_data_from_file_path(Arena *arena, String8 path); -internal B32 os_write_data_to_file_path(String8 path, String8 data); -internal B32 os_write_data_list_to_file_path(String8 path, String8List list); -internal FileProperties os_properties_from_file_path(String8 path); -internal OS_FileID os_id_from_file_path(String8 path); -internal S64 os_file_id_compare(OS_FileID a, OS_FileID b); -internal String8 os_string_from_file_range(Arena *arena, OS_Handle file, Rng1U64 range); - -//////////////////////////////// -//~ rjf: Synchronization Primitive Helpers (Helpers, Implemented Once) - -internal void os_mutex_take(OS_Handle mutex); -internal void os_mutex_drop(OS_Handle mutex); -internal void os_rw_mutex_take_r(OS_Handle rw_mutex); -internal void os_rw_mutex_drop_r(OS_Handle rw_mutex); -internal void os_rw_mutex_take_w(OS_Handle rw_mutex); -internal void os_rw_mutex_drop_w(OS_Handle rw_mutex); -// returns false on timeout, true on signal, (max_wait_ms = max_U64) -> no timeout -internal B32 os_condition_variable_wait(OS_Handle cv, OS_Handle mutex, U64 endt_us); -internal B32 os_condition_variable_wait_rw_r(OS_Handle cv, OS_Handle rw_mutex, U64 endt_us); -internal B32 os_condition_variable_wait_rw_w(OS_Handle cv, OS_Handle rw_mutex, U64 endt_us); -internal void os_condition_variable_signal(OS_Handle cv); -internal void os_condition_variable_broadcast(OS_Handle cv); - -#define OS_MutexScope(mutex) DeferLoop(os_mutex_take(mutex), os_mutex_drop(mutex)) -#define OS_MutexScopeR(mutex) DeferLoop(os_rw_mutex_take_r(mutex), os_rw_mutex_drop_r(mutex)) -#define OS_MutexScopeW(mutex) DeferLoop(os_rw_mutex_take_w(mutex), os_rw_mutex_drop_w(mutex)) - -//////////////////////////////// -//~ rjf: @os_hooks Main Initialization API (Implemented Per-OS) - -internal void os_init(int argc, char **argv); - -//////////////////////////////// -//~ rjf: @os_hooks Memory Allocation (Implemented Per-OS) - -internal void* os_reserve(U64 size); -internal B32 os_commit(void *ptr, U64 size); -internal void* os_reserve_large(U64 size); -internal B32 os_commit_large(void *ptr, U64 size); -internal void os_decommit(void *ptr, U64 size); -internal void os_release(void *ptr, U64 size); - -internal B32 os_set_large_pages(B32 flag); -internal B32 os_large_pages_enabled(void); -internal U64 os_large_page_size(void); - -internal void* os_alloc_ring_buffer(U64 size, U64 *actual_size_out); -internal void os_free_ring_buffer(void *ring_buffer, U64 actual_size); - -//////////////////////////////// -//~ rjf: @os_hooks System Info (Implemented Per-OS) - -internal String8 os_machine_name(void); -internal U64 os_page_size(void); -internal U64 os_allocation_granularity(void); -internal U64 os_logical_core_count(void); - -//////////////////////////////// -//~ rjf: @os_hooks Process Info (Implemented Per-OS) - -internal String8List os_get_command_line_arguments(void); -internal S32 os_get_pid(void); -internal S32 os_get_tid(void); -internal String8List os_get_environment(void); -internal U64 os_string_list_from_system_path(Arena *arena, OS_SystemPath path, String8List *out); - -//////////////////////////////// -//~ rjf: @os_hooks Process Control (Implemented Per-OS) - -internal void os_exit_process(S32 exit_code); - -//////////////////////////////// -//~ rjf: @os_hooks File System (Implemented Per-OS) - -//- rjf: files -internal OS_Handle os_file_open(OS_AccessFlags flags, String8 path); -internal void os_file_close(OS_Handle file); -internal U64 os_file_read(OS_Handle file, Rng1U64 rng, void *out_data); -internal void os_file_write(OS_Handle file, Rng1U64 rng, void *data); -internal B32 os_file_set_times(OS_Handle file, DateTime time); -internal FileProperties os_properties_from_file(OS_Handle file); -internal OS_FileID os_id_from_file(OS_Handle file); -internal B32 os_delete_file_at_path(String8 path); -internal B32 os_copy_file_path(String8 dst, String8 src); -internal String8 os_full_path_from_path(Arena *arena, String8 path); -internal B32 os_file_path_exists(String8 path); - -//- rjf: file maps -internal OS_Handle os_file_map_open(OS_AccessFlags flags, OS_Handle file); -internal void os_file_map_close(OS_Handle map); -internal void * os_file_map_view_open(OS_Handle map, OS_AccessFlags flags, Rng1U64 range); -internal void os_file_map_view_close(OS_Handle map, void *ptr); - -//- rjf: directory iteration -internal OS_FileIter *os_file_iter_begin(Arena *arena, String8 path, OS_FileIterFlags flags); -internal B32 os_file_iter_next(Arena *arena, OS_FileIter *iter, OS_FileInfo *info_out); -internal void os_file_iter_end(OS_FileIter *iter); - -//- rjf: directory creation -internal B32 os_make_directory(String8 path); - -//////////////////////////////// -//~ rjf: @os_hooks Shared Memory (Implemented Per-OS) - -internal OS_Handle os_shared_memory_alloc(U64 size, String8 name); -internal OS_Handle os_shared_memory_open(String8 name); -internal void os_shared_memory_close(OS_Handle handle); -internal void * os_shared_memory_view_open(OS_Handle handle, Rng1U64 range); -internal void os_shared_memory_view_close(OS_Handle handle, void *ptr); - -//////////////////////////////// -//~ rjf: @os_hooks Time (Implemented Per-OS) - -internal OS_UnixTime os_now_unix(void); -internal DateTime os_now_universal_time(void); -internal DateTime os_universal_time_from_local_time(DateTime *local_time); -internal DateTime os_local_time_from_universal_time(DateTime *universal_time); -internal U64 os_now_microseconds(void); -internal void os_sleep_milliseconds(U32 msec); - -//////////////////////////////// -//~ rjf: @os_hooks Child Processes (Implemented Per-OS) - -internal B32 os_launch_process(OS_LaunchOptions *options, OS_Handle *handle_out); -internal B32 os_process_wait(OS_Handle handle, U64 endt_us); -internal void os_process_release_handle(OS_Handle handle); - -//////////////////////////////// -//~ rjf: @os_hooks Threads (Implemented Per-OS) - -internal OS_Handle os_launch_thread(OS_ThreadFunctionType *func, void *ptr, void *params); -internal void os_release_thread_handle(OS_Handle thread); - -//////////////////////////////// -//~ rjf: @os_hooks Synchronization Primitives (Implemented Per-OS) - -// NOTE(allen): Mutexes are recursive - support counted acquire/release nesting -// on a single thread - -//- rjf: recursive mutexes -internal OS_Handle os_mutex_alloc(void); -internal void os_mutex_release(OS_Handle mutex); -internal void os_mutex_take_(OS_Handle mutex); -internal void os_mutex_drop_(OS_Handle mutex); - -//- rjf: reader/writer mutexes -internal OS_Handle os_rw_mutex_alloc(void); -internal void os_rw_mutex_release(OS_Handle rw_mutex); -internal void os_rw_mutex_take_r_(OS_Handle mutex); -internal void os_rw_mutex_drop_r_(OS_Handle mutex); -internal void os_rw_mutex_take_w_(OS_Handle mutex); -internal void os_rw_mutex_drop_w_(OS_Handle mutex); - -//- rjf: condition variables -internal OS_Handle os_condition_variable_alloc(void); -internal void os_condition_variable_release(OS_Handle cv); -// returns false on timeout, true on signal, (max_wait_ms = max_U64) -> no timeout -internal B32 os_condition_variable_wait_(OS_Handle cv, OS_Handle mutex, U64 endt_us); -internal B32 os_condition_variable_wait_rw_r_(OS_Handle cv, OS_Handle mutex_rw, U64 endt_us); -internal B32 os_condition_variable_wait_rw_w_(OS_Handle cv, OS_Handle mutex_rw, U64 endt_us); -internal void os_condition_variable_signal_(OS_Handle cv); -internal void os_condition_variable_broadcast_(OS_Handle cv); - -//- rjf: cross-process semaphores -internal OS_Handle os_semaphore_alloc(U32 initial_count, U32 max_count, String8 name); -internal void os_semaphore_release(OS_Handle semaphore); -internal OS_Handle os_semaphore_open(String8 name); -internal void os_semaphore_close(OS_Handle semaphore); -internal B32 os_semaphore_take(OS_Handle semaphore, U64 endt_us); -internal void os_semaphore_drop(OS_Handle semaphore); - -//////////////////////////////// -//~ rjf: @os_hooks Dynamically-Loaded Libraries (Implemented Per-OS) - -internal OS_Handle os_library_open(String8 path); -internal VoidProc *os_library_load_proc(OS_Handle lib, String8 name); -internal void os_library_close(OS_Handle lib); - -//////////////////////////////// -//~ rjf: @os_hooks Safe Calls (Implemented Per-OS) - -internal void os_safe_call(OS_ThreadFunctionType *func, OS_ThreadFunctionType *fail_handler, void *ptr); - -//////////////////////////////// -//~ rjf: @os_hooks GUIDs (Implemented Per-OS) - -internal OS_Guid os_make_guid(void); -internal String8 os_string_from_guid(Arena *arena, OS_Guid guid); - -#endif // OS_CORE_H +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef OS_CORE_H +#define OS_CORE_H + +//////////////////////////////// +//~ 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]; +}; +StaticAssert(sizeof(OS_Guid) == 16, os_guid_check); + +//////////////////////////////// +//~ rjf: Thread Types + +typedef void OS_ThreadFunctionType(void *ptr); + +//////////////////////////////// +//~ rjf: Handle Type Functions (Helpers, Implemented Once) + +internal OS_Handle os_handle_zero(void); +internal B32 os_handle_match(OS_Handle a, OS_Handle b); +internal void os_handle_list_push(Arena *arena, OS_HandleList *handles, OS_Handle handle); +internal OS_HandleArray os_handle_array_from_list(Arena *arena, OS_HandleList *list); + +//////////////////////////////// +//~ rjf: Command Line Argc/Argv Helper (Helper, Implemented Once) + +internal String8List os_string_list_from_argcv(Arena *arena, int argc, char **argv); + +//////////////////////////////// +//~ rjf: Filesystem Helpers (Helpers, Implemented Once) + +internal String8 os_data_from_file_path(Arena *arena, String8 path); +internal B32 os_write_data_to_file_path(String8 path, String8 data); +internal B32 os_write_data_list_to_file_path(String8 path, String8List list); +internal B32 os_append_data_to_file_path(String8 path, String8 data); +internal OS_FileID os_id_from_file_path(String8 path); +internal S64 os_file_id_compare(OS_FileID a, OS_FileID b); +internal String8 os_string_from_file_range(Arena *arena, OS_Handle file, Rng1U64 range); + +//////////////////////////////// +//~ rjf: GUID Helpers (Helpers, Implemented Once) + +internal String8 os_string_from_guid(Arena *arena, OS_Guid guid); + +//////////////////////////////// +//~ rjf: @os_hooks System/Process Info (Implemented Per-OS) + +internal OS_SystemInfo *os_get_system_info(void); +internal OS_ProcessInfo *os_get_process_info(void); +internal String8 os_get_current_path(Arena *arena); + +//////////////////////////////// +//~ rjf: @os_hooks Memory Allocation (Implemented Per-OS) + +//- rjf: basic +internal void *os_reserve(U64 size); +internal B32 os_commit(void *ptr, U64 size); +internal void os_decommit(void *ptr, U64 size); +internal void os_release(void *ptr, U64 size); + +//- rjf: large pages +internal void *os_reserve_large(U64 size); +internal B32 os_commit_large(void *ptr, U64 size); + +//////////////////////////////// +//~ rjf: @os_hooks Thread Info (Implemented Per-OS) + +internal U32 os_tid(void); +internal void os_set_thread_name(String8 string); + +//////////////////////////////// +//~ rjf: @os_hooks Aborting (Implemented Per-OS) + +internal void os_abort(S32 exit_code); + +//////////////////////////////// +//~ rjf: @os_hooks File System (Implemented Per-OS) + +//- rjf: files +internal OS_Handle os_file_open(OS_AccessFlags flags, String8 path); +internal void os_file_close(OS_Handle file); +internal U64 os_file_read(OS_Handle file, Rng1U64 rng, void *out_data); +internal U64 os_file_write(OS_Handle file, Rng1U64 rng, void *data); +internal B32 os_file_set_times(OS_Handle file, DateTime time); +internal FileProperties os_properties_from_file(OS_Handle file); +internal OS_FileID os_id_from_file(OS_Handle file); +internal B32 os_delete_file_at_path(String8 path); +internal B32 os_copy_file_path(String8 dst, String8 src); +internal String8 os_full_path_from_path(Arena *arena, String8 path); +internal B32 os_file_path_exists(String8 path); +internal FileProperties os_properties_from_file_path(String8 path); + +//- rjf: file maps +internal OS_Handle os_file_map_open(OS_AccessFlags flags, OS_Handle file); +internal void os_file_map_close(OS_Handle map); +internal void * os_file_map_view_open(OS_Handle map, OS_AccessFlags flags, Rng1U64 range); +internal void os_file_map_view_close(OS_Handle map, void *ptr, Rng1U64 range); + +//- rjf: directory iteration +internal OS_FileIter *os_file_iter_begin(Arena *arena, String8 path, OS_FileIterFlags flags); +internal B32 os_file_iter_next(Arena *arena, OS_FileIter *iter, OS_FileInfo *info_out); +internal void os_file_iter_end(OS_FileIter *iter); + +//- rjf: directory creation +internal B32 os_make_directory(String8 path); + +//////////////////////////////// +//~ rjf: @os_hooks Shared Memory (Implemented Per-OS) + +internal OS_Handle os_shared_memory_alloc(U64 size, String8 name); +internal OS_Handle os_shared_memory_open(String8 name); +internal void os_shared_memory_close(OS_Handle handle); +internal void * os_shared_memory_view_open(OS_Handle handle, Rng1U64 range); +internal void os_shared_memory_view_close(OS_Handle handle, void *ptr, Rng1U64 range); + +//////////////////////////////// +//~ rjf: @os_hooks Time (Implemented Per-OS) + +internal U64 os_now_microseconds(void); +internal U32 os_now_unix(void); +internal DateTime os_now_universal_time(void); +internal DateTime os_universal_time_from_local(DateTime *local_time); +internal DateTime os_local_time_from_universal(DateTime *universal_time); +internal void os_sleep_milliseconds(U32 msec); + +//////////////////////////////// +//~ rjf: @os_hooks Child Processes (Implemented Per-OS) + +internal OS_Handle os_process_launch(OS_ProcessLaunchParams *params); +internal B32 os_process_join(OS_Handle handle, U64 endt_us); +internal void os_process_detach(OS_Handle handle); + +//////////////////////////////// +//~ rjf: @os_hooks Threads (Implemented Per-OS) + +internal OS_Handle os_thread_launch(OS_ThreadFunctionType *func, void *ptr, void *params); +internal B32 os_thread_join(OS_Handle handle, U64 endt_us); +internal void os_thread_detach(OS_Handle handle); + +//////////////////////////////// +//~ rjf: @os_hooks Synchronization Primitives (Implemented Per-OS) + +//- rjf: recursive mutexes +internal OS_Handle os_mutex_alloc(void); +internal void os_mutex_release(OS_Handle mutex); +internal void os_mutex_take(OS_Handle mutex); +internal void os_mutex_drop(OS_Handle mutex); + +//- rjf: reader/writer mutexes +internal OS_Handle os_rw_mutex_alloc(void); +internal void os_rw_mutex_release(OS_Handle rw_mutex); +internal void os_rw_mutex_take_r(OS_Handle mutex); +internal void os_rw_mutex_drop_r(OS_Handle mutex); +internal void os_rw_mutex_take_w(OS_Handle mutex); +internal void os_rw_mutex_drop_w(OS_Handle mutex); + +//- rjf: condition variables +internal OS_Handle os_condition_variable_alloc(void); +internal void os_condition_variable_release(OS_Handle cv); +// returns false on timeout, true on signal, (max_wait_ms = max_U64) -> no timeout +internal B32 os_condition_variable_wait(OS_Handle cv, OS_Handle mutex, U64 endt_us); +internal B32 os_condition_variable_wait_rw_r(OS_Handle cv, OS_Handle mutex_rw, U64 endt_us); +internal B32 os_condition_variable_wait_rw_w(OS_Handle cv, OS_Handle mutex_rw, U64 endt_us); +internal void os_condition_variable_signal(OS_Handle cv); +internal void os_condition_variable_broadcast(OS_Handle cv); + +//- rjf: cross-process semaphores +internal OS_Handle os_semaphore_alloc(U32 initial_count, U32 max_count, String8 name); +internal void os_semaphore_release(OS_Handle semaphore); +internal OS_Handle os_semaphore_open(String8 name); +internal void os_semaphore_close(OS_Handle semaphore); +internal B32 os_semaphore_take(OS_Handle semaphore, U64 endt_us); +internal void os_semaphore_drop(OS_Handle semaphore); + +//- rjf: scope macros +#define OS_MutexScope(mutex) DeferLoop(os_mutex_take(mutex), os_mutex_drop(mutex)) +#define OS_MutexScopeR(mutex) DeferLoop(os_rw_mutex_take_r(mutex), os_rw_mutex_drop_r(mutex)) +#define OS_MutexScopeW(mutex) DeferLoop(os_rw_mutex_take_w(mutex), os_rw_mutex_drop_w(mutex)) +#define OS_MutexScopeRWPromote(mutex) DeferLoop((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) + +internal OS_Handle os_library_open(String8 path); +internal void os_library_close(OS_Handle lib); +internal VoidProc *os_library_load_proc(OS_Handle lib, String8 name); + +//////////////////////////////// +//~ rjf: @os_hooks Safe Calls (Implemented Per-OS) + +internal void os_safe_call(OS_ThreadFunctionType *func, OS_ThreadFunctionType *fail_handler, void *ptr); + +//////////////////////////////// +//~ rjf: @os_hooks GUIDs (Implemented Per-OS) + +internal 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 +internal void entry_point(CmdLine *cmdline); +#endif + +#endif // OS_CORE_H diff --git a/src/metagen/metagen_os/core/win32/metagen_os_core_win32.c b/src/metagen/metagen_os/core/win32/metagen_os_core_win32.c index 19f5260b..37e02710 100644 --- a/src/metagen/metagen_os/core/win32/metagen_os_core_win32.c +++ b/src/metagen/metagen_os/core/win32/metagen_os_core_win32.c @@ -1,1474 +1,1646 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#pragma comment(lib, "user32") -#pragma comment(lib, "winmm") -#pragma comment(lib, "shell32") -#pragma comment(lib, "advapi32") - -//////////////////////////////// -//~ allen: Definitions For Symbols That Are Sometimes Missing in Older Windows SDKs - -#if !defined(MEM_PRESERVE_PLACEHOLDER) -#define MEM_PRESERVE_PLACEHOLDER 0x2 -#endif -#if !defined(MEM_RESERVE_PLACEHOLDER) -# define MEM_REPLACE_PLACEHOLDER 0x00004000 -#endif -#if !defined(MEM_RESERVE_PLACEHOLDER) -# define MEM_RESERVE_PLACEHOLDER 0x00040000 -#endif - -typedef PVOID W32_VirtualAlloc2_Type(HANDLE Process, - PVOID BaseAddress, - SIZE_T Size, - ULONG AllocationType, - ULONG PageProtection, - void* ExtendedParameters, - ULONG ParameterCount); -typedef PVOID W32_MapViewOfFile3_Type(HANDLE FileMapping, - HANDLE Process, - PVOID BaseAddress, - ULONG64 Offset, - SIZE_T ViewSize, - ULONG AllocationType, - ULONG PageProtection, - void* ExtendedParameters, - ULONG ParameterCount); - -global W32_VirtualAlloc2_Type *w32_VirtualAlloc2_func = 0; -global W32_MapViewOfFile3_Type *w32_MapViewOfFile3_func = 0; - -//////////////////////////////// -//~ rjf: Globals - -global Arena *w32_perm_arena = 0; -global String8List w32_cmd_line_args = {0}; -global String8List w32_environment = {0}; -global CRITICAL_SECTION w32_mutex = {0}; -global String8 w32_initial_path = {0}; -global U64 w32_microsecond_resolution = 0; -global W32_Entity *w32_entity_free = 0; -global B32 w32_large_pages_enabled = 0; - -//////////////////////////////// -//~ rjf: Helpers - -//- rjf: files - -internal FilePropertyFlags -w32_file_property_flags_from_dwFileAttributes(DWORD dwFileAttributes){ - FilePropertyFlags flags = 0; - if (dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY){ - flags |= FilePropertyFlag_IsFolder; - } - return(flags); -} - -internal void -w32_file_properties_from_attributes(FileProperties *properties, WIN32_FILE_ATTRIBUTE_DATA *attributes){ - properties->size = Compose64Bit(attributes->nFileSizeHigh, attributes->nFileSizeLow); - w32_dense_time_from_file_time(&properties->created, &attributes->ftCreationTime); - w32_dense_time_from_file_time(&properties->modified, &attributes->ftLastWriteTime); - properties->flags = w32_file_property_flags_from_dwFileAttributes(attributes->dwFileAttributes); -} - -//- rjf: time - -internal void -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; -} - -internal void -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; -} - -internal void -w32_dense_time_from_file_time(DenseTime *out, FILETIME *in){ - SYSTEMTIME systime = {0}; - FileTimeToSystemTime(in, &systime); - DateTime date_time = {0}; - w32_date_time_from_system_time(&date_time, &systime); - *out = dense_time_from_date_time(date_time); -} - -internal U32 -w32_sleep_ms_from_endt_us(U64 endt_us){ - U32 sleep_ms = 0; - if (endt_us == max_U64){ - sleep_ms = INFINITE; - } - else{ - U64 begint = os_now_microseconds(); - if (begint < endt_us){ - U64 sleep_us = endt_us - begint; - sleep_ms = (U32)((sleep_us + 999)/1000); - } - } - return(sleep_ms); -} - -//- rjf: entities - -internal W32_Entity* -w32_alloc_entity(W32_EntityKind kind){ - EnterCriticalSection(&w32_mutex); - W32_Entity *result = w32_entity_free; - if(result != 0) - { - SLLStackPop(w32_entity_free); - } - else - { - result = push_array_no_zero(w32_perm_arena, W32_Entity, 1); - } - MemoryZeroStruct(result); - Assert(result != 0); - LeaveCriticalSection(&w32_mutex); - MemoryZeroStruct(result); - result->kind = kind; - return(result); -} - -internal void -w32_free_entity(W32_Entity *entity){ - entity->kind = W32_EntityKind_Null; - EnterCriticalSection(&w32_mutex); - SLLStackPush(w32_entity_free, entity); - LeaveCriticalSection(&w32_mutex); -} - -//- rjf: threads - -internal DWORD -w32_thread_base(void *ptr){ - W32_Entity *entity = (W32_Entity*)ptr; - OS_ThreadFunctionType *func = entity->thread.func; - void *thread_ptr = entity->thread.ptr; - - func(thread_ptr); - - // remove my bit - LONG result = InterlockedAnd((LONG*)&entity->reference_mask, ~0x2); - // if the other bit is also gone, free entity - if ((result & 0x1) == 0){ - w32_free_entity(entity); - } - return(0); -} - -//////////////////////////////// -//~ rjf: @os_hooks Main Initialization API (Implemented Per-OS) - -internal void -os_init(int argc, char **argv){ - // Load Fancy Memory Functions - { - HMODULE module = LoadLibraryA("kernel32.dll"); - if (module != 0){ - w32_VirtualAlloc2_func = (W32_VirtualAlloc2_Type*)GetProcAddress(module, "VirtualAlloc2"); - w32_MapViewOfFile3_func = (W32_MapViewOfFile3_Type*)GetProcAddress(module, "MapViewOfFile3"); - FreeLibrary(module); - } - } - - // Thread handshake - InitializeCriticalSection(&w32_mutex); - - // Permanent memory allocator for this layer - w32_perm_arena = arena_alloc(); - - // Init microsecond counter resolution - LARGE_INTEGER large_int_resolution; - if (QueryPerformanceFrequency(&large_int_resolution)){ - w32_microsecond_resolution = large_int_resolution.QuadPart; - } - else{ - w32_microsecond_resolution = 1; - } - - // Setup initial path - w32_initial_path = os_string_from_system_path(w32_perm_arena, OS_SystemPath_Current); - - // Setup command line arguments - w32_cmd_line_args = os_string_list_from_argcv(w32_perm_arena, argc, argv); - - // rjf: setup environment variables - { - CHAR *this_proc_env = GetEnvironmentStrings(); - U64 start_idx = 0; - for(U64 idx = 0;; idx += 1) - { - if(this_proc_env[idx] == 0) - { - if(start_idx == idx) - { - break; - } - else - { - String8 string = str8((U8 *)this_proc_env + start_idx, idx - start_idx); - str8_list_push(w32_perm_arena, &w32_environment, string); - start_idx = idx+1; - } - } - } - } -} - -//////////////////////////////// -//~ rjf: @os_hooks Memory Allocation (Implemented Per-OS) - -internal void* -os_reserve(U64 size){ - void *result = VirtualAlloc(0, size, MEM_RESERVE, PAGE_READWRITE); - return(result); -} - -internal B32 -os_commit(void *ptr, U64 size){ - B32 result = (VirtualAlloc(ptr, size, MEM_COMMIT, PAGE_READWRITE) != 0); - return(result); -} - -internal 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); -} - -internal B32 -os_commit_large(void *ptr, U64 size){ - return 1; -} - -internal void -os_decommit(void *ptr, U64 size){ - VirtualFree(ptr, size, MEM_DECOMMIT); -} - -internal 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); -} - -internal B32 -os_set_large_pages(B32 flag) -{ - B32 is_ok = 0; - HANDLE token; - if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token)) { - LUID luid; - if (LookupPrivilegeValue(0, SE_LOCK_MEMORY_NAME, &luid)) { - TOKEN_PRIVILEGES priv; - priv.PrivilegeCount = 1; - priv.Privileges[0].Luid = luid; - priv.Privileges[0].Attributes = flag ? SE_PRIVILEGE_ENABLED: 0; - if (AdjustTokenPrivileges(token, 0, &priv, sizeof(priv), 0, 0)) { - w32_large_pages_enabled = flag; - is_ok = 1; - } - } - CloseHandle(token); - } - return is_ok; -} - -internal B32 -os_large_pages_enabled(void) -{ - return w32_large_pages_enabled; -} - -internal U64 -os_large_page_size(void) -{ - U64 page_size = GetLargePageMinimum(); - return page_size; -} - -internal void* -os_alloc_ring_buffer(U64 size, U64 *actual_size_out){ - void *result = 0; - -#define W32_MAX_RING_SIZE GB(1) - - Assert(IsPow2(size)); - Assert(size <= W32_MAX_RING_SIZE); - - // get allocation granularity - SYSTEM_INFO info = {0}; - GetSystemInfo(&info); - Assert(IsPow2(info.dwAllocationGranularity)); - - // align size - U64 aligned_size = AlignPow2(size, (U64)(info.dwAllocationGranularity)); - - // split size - U32 lo_size = (U32)(aligned_size & 0xFFFFFFFF); - U32 hi_size = (U32)(aligned_size >> 32); - - // create pagefile-backed section - HANDLE section = CreateFileMappingA(INVALID_HANDLE_VALUE, 0, PAGE_READWRITE, hi_size, lo_size, 0); - - if (section != 0){ - if (w32_VirtualAlloc2_func != 0 && w32_MapViewOfFile3_func != 0){ - void *ptr1 = 0; - void *ptr2 = 0; - void *view1 = 0; - void *view2 = 0; - - // reserve virtual space placeholder - ptr1 = w32_VirtualAlloc2_func(0, 0, aligned_size*2, - MEM_RESERVE|MEM_RESERVE_PLACEHOLDER, PAGE_NOACCESS, 0, 0); - if (ptr1 != 0){ - - // split off the first part of placeholder - VirtualFree(ptr1, aligned_size, MEM_RELEASE|MEM_PRESERVE_PLACEHOLDER); - ptr2 = ((U8*)ptr1 + aligned_size); - - // create views - view1 = w32_MapViewOfFile3_func(section, 0, ptr1, 0, aligned_size, - MEM_REPLACE_PLACEHOLDER, PAGE_READWRITE, 0, 0); - view2 = w32_MapViewOfFile3_func(section, 0, ptr2, 0, aligned_size, - MEM_REPLACE_PLACEHOLDER, PAGE_READWRITE, 0, 0); - if (view1 != 0 && view2 != 0){ - result = ptr1; - *actual_size_out = aligned_size; - } - } - - // cleanup - if (result == 0){ - if (ptr1 != 0){ - VirtualFree(ptr1, 0, MEM_RELEASE); - } - if (ptr2 != 0){ - VirtualFree(ptr2, 0, MEM_RELEASE); - } - if (view1 != 0){ - UnmapViewOfFileEx(view1, 0); - } - if (view2 != 0){ - UnmapViewOfFileEx(view2, 0); - } - } - } - - // no fancy memory functions available - else{ - for (U64 addr = GB(16); - addr < GB(272); - addr += W32_MAX_RING_SIZE){ - - // create the first view - void *view1 = MapViewOfFileEx(section, FILE_MAP_ALL_ACCESS, 0, 0, aligned_size, (void*)addr); - if (view1 != 0){ - - // create the second view - void *view2 = MapViewOfFileEx(section, FILE_MAP_ALL_ACCESS, 0, 0, aligned_size, (U8*)view1 + aligned_size); - - // on success make this the result - if (view2 != 0){ - result = view1; - *actual_size_out = aligned_size; - break; - } - - // cleanup view1 on failure - UnmapViewOfFile(view1); - } - } - } - } - - // cleanup - if (section != 0){ - CloseHandle(section); - } - - return(result); -} - -internal void -os_free_ring_buffer(void *ring_buffer, U64 actual_size){ - void *ptr1 = ring_buffer; - void *ptr2 = ((U8*)ptr1 + actual_size); - VirtualFree(ptr1, 0, MEM_RELEASE); - VirtualFree(ptr2, 0, MEM_RELEASE); - UnmapViewOfFileEx(ptr1, 0); - UnmapViewOfFileEx(ptr2, 0); -} - -//////////////////////////////// -//~ rjf: @os_hooks System Info (Implemented Per-OS) - -internal String8 -os_machine_name(void){ - local_persist U8 buffer[MAX_COMPUTERNAME_LENGTH + 1]; - local_persist String8 string = {0}; - local_persist B32 first = 1; - if (first){ - first = 0; - DWORD size = MAX_COMPUTERNAME_LENGTH + 1; - if (GetComputerNameA((char*)buffer, &size)){ - string = str8(buffer, size); - } - } - return(string); -} - -internal U64 -os_page_size(void){ - SYSTEM_INFO sysinfo = {0}; - GetSystemInfo(&sysinfo); - return(sysinfo.dwPageSize); -} - -internal U64 -os_allocation_granularity(void) -{ - SYSTEM_INFO sysinfo = {0}; - GetSystemInfo(&sysinfo); - return sysinfo.dwAllocationGranularity; -} - -internal U64 -os_logical_core_count(void) -{ - SYSTEM_INFO sysinfo = {0}; - GetSystemInfo(&sysinfo); - return sysinfo.dwNumberOfProcessors; -} - -//////////////////////////////// -//~ rjf: @os_hooks Process Info (Implemented Per-OS) - -internal String8List -os_get_command_line_arguments(void) -{ - return w32_cmd_line_args; -} - -internal S32 -os_get_pid(void){ - DWORD id = GetCurrentProcessId(); - return((S32)id); -} - -internal S32 -os_get_tid(void){ - DWORD id = GetCurrentThreadId(); - return((S32)id); -} - -internal String8List -os_get_environment(void) -{ - return w32_environment; -} - -internal U64 -os_string_list_from_system_path(Arena *arena, OS_SystemPath path, String8List *out){ - Temp scratch = scratch_begin(&arena, 1); - - U64 result = 0; - - switch (path){ - case OS_SystemPath_Binary: - { - local_persist B32 first = 1; - local_persist String8 name = {0}; - - // TODO(allen): let's just pre-compute this at init and skip the complexity - EnterCriticalSection(&w32_mutex); - if (first){ - first = 0; - DWORD size = KB(32); - U16 *buffer = push_array_no_zero(scratch.arena, U16, size); - DWORD length = GetModuleFileNameW(0, (WCHAR*)buffer, size); - String8 name8 = str8_from_16(scratch.arena, str16(buffer, length)); - String8 name_chopped = str8_chop_last_slash(name8); - name = push_str8_copy(w32_perm_arena, name_chopped); - } - LeaveCriticalSection(&w32_mutex); - - result = 1; - str8_list_push(arena, out, name); - }break; - - case OS_SystemPath_Initial: - { - Assert(w32_initial_path.str != 0); - result = 1; - str8_list_push(arena, out, w32_initial_path); - }break; - - case OS_SystemPath_Current: - { - DWORD length = GetCurrentDirectoryW(0, 0); - U16 *memory = push_array_no_zero(scratch.arena, U16, length + 1); - length = GetCurrentDirectoryW(length + 1, (WCHAR*)memory); - String8 name = str8_from_16(arena, str16(memory, length)); - result = 1; - str8_list_push(arena, out, name); - }break; - - case OS_SystemPath_UserProgramData: - { - local_persist B32 first = 1; - local_persist String8 name = {0}; - if (first){ - first = 0; - U64 size = KB(32); - U16 *buffer = push_array_no_zero(scratch.arena, U16, size); - if (SUCCEEDED(SHGetFolderPathW(0, CSIDL_APPDATA, 0, 0, (WCHAR*)buffer))){ - name = str8_from_16(scratch.arena, str16_cstring(buffer)); - EnterCriticalSection(&w32_mutex); - U8 *buffer8 = push_array_no_zero(w32_perm_arena, U8, name.size); - LeaveCriticalSection(&w32_mutex); - MemoryCopy(buffer8, name.str, name.size); - name.str = buffer8; - } - } - result = 1; - str8_list_push(arena, out, name); - }break; - - case OS_SystemPath_ModuleLoad: - { - U64 og_count = out->node_count; - - { - UINT cap = GetSystemDirectoryW(0, 0); - if (cap > 0){ - U16 *buffer = push_array_no_zero(scratch.arena, U16, cap); - UINT size = GetSystemDirectoryW((WCHAR*)buffer, cap); - if (size > 0){ - str8_list_push(arena, out, str8_from_16(arena, str16(buffer, size))); - } - } - } - - { - UINT cap = GetWindowsDirectoryW(0, 0); - if (cap > 0){ - U16 *buffer = push_array_no_zero(scratch.arena, U16, cap); - UINT size = GetWindowsDirectoryW((WCHAR*)buffer, cap); - if (size > 0){ - str8_list_push(arena, out, str8_from_16(arena, str16(buffer, size))); - } - } - } - - result = out->node_count - og_count; - }break; - } - - scratch_end(scratch); - return(result); -} - -//////////////////////////////// -//~ rjf: @os_hooks Process Control (Implemented Per-OS) - -internal void -os_exit_process(S32 exit_code){ - ExitProcess(exit_code); -} - -//////////////////////////////// -//~ rjf: @os_hooks File System (Implemented Per-OS) - -//- rjf: files - -internal OS_Handle -os_file_open(OS_AccessFlags flags, String8 path) -{ - OS_Handle result = {0}; - Temp scratch = scratch_begin(0, 0); - String16 path16 = str16_from_8(scratch.arena, path); - DWORD access_flags = 0; - DWORD share_mode = 0; - DWORD creation_disposition = OPEN_EXISTING; - if(flags & OS_AccessFlag_Read) {access_flags |= GENERIC_READ;} - if(flags & OS_AccessFlag_Write) {access_flags |= GENERIC_WRITE;} - if(flags & OS_AccessFlag_Execute) {access_flags |= GENERIC_EXECUTE;} - if(flags & OS_AccessFlag_Shared) {share_mode = (!!(flags & OS_AccessFlag_Write)*FILE_SHARE_WRITE)|FILE_SHARE_READ;} - if(flags & OS_AccessFlag_Write) {creation_disposition = CREATE_ALWAYS;} - HANDLE file = CreateFileW((WCHAR *)path16.str, access_flags, share_mode, 0, creation_disposition, FILE_ATTRIBUTE_NORMAL, 0); - if(file != INVALID_HANDLE_VALUE) - { - result.u64[0] = (U64)file; - } - scratch_end(scratch); - return result; -} - -internal void -os_file_close(OS_Handle file) -{ - if(os_handle_match(file, os_handle_zero())) { return; } - HANDLE handle = (HANDLE)file.u64[0]; - CloseHandle(handle); -} - -internal U64 -os_file_read(OS_Handle file, Rng1U64 rng, void *out_data) -{ - if(os_handle_match(file, os_handle_zero())) { return 0; } - HANDLE handle = (HANDLE)file.u64[0]; - - // rjf: clamp range by file size - U64 size = 0; - GetFileSizeEx(handle, (LARGE_INTEGER *)&size); - Rng1U64 rng_clamped = r1u64(ClampTop(rng.min, size), ClampTop(rng.max, size)); - U64 total_read_size = 0; - - // rjf: read loop - { - U64 to_read = dim_1u64(rng_clamped); - for(U64 off = rng.min; total_read_size < to_read;) - { - U64 amt64 = to_read - total_read_size; - U32 amt32 = u32_from_u64_saturate(amt64); - DWORD read_size = 0; - OVERLAPPED overlapped = {0}; - overlapped.Offset = (off&0x00000000ffffffffull); - overlapped.OffsetHigh = (off&0xffffffff00000000ull) >> 32; - ReadFile(handle, (U8 *)out_data + total_read_size, amt32, &read_size, &overlapped); - off += read_size; - total_read_size += read_size; - if(read_size != amt32) - { - break; - } - } - } - - return total_read_size; -} - -internal void -os_file_write(OS_Handle file, Rng1U64 rng, void *data) -{ - if(os_handle_match(file, os_handle_zero())) { return; } - HANDLE win_handle = (HANDLE)file.u64[0]; - U64 src_off = 0; - U64 dst_off = rng.min; - U64 bytes_to_write_total = rng.max-rng.min; - for(;src_off < bytes_to_write_total;) - { - void *bytes_src = (void *)((U8 *)data + src_off); - U64 bytes_to_write_64 = (bytes_to_write_total-src_off); - U32 bytes_to_write_32 = u32_from_u64_saturate(bytes_to_write_64); - U32 bytes_written = 0; - OVERLAPPED overlapped = {0}; - overlapped.Offset = (dst_off&0x00000000ffffffffull); - overlapped.OffsetHigh = (dst_off&0xffffffff00000000ull) >> 32; - BOOL success = WriteFile(win_handle, bytes_src, bytes_to_write_32, (DWORD *)&bytes_written, &overlapped); - if(success == 0) - { - break; - } - src_off += bytes_written; - dst_off += bytes_written; - } -} - -internal B32 -os_file_set_times(OS_Handle file, DateTime time) -{ - if(os_handle_match(file, os_handle_zero())) { return 0; } - B32 result = 0; - HANDLE handle = (HANDLE)file.u64[0]; - SYSTEMTIME system_time = {0}; - w32_system_time_from_date_time(&system_time, &time); - FILETIME file_time = {0}; - result = (SystemTimeToFileTime(&system_time, &file_time) && - SetFileTime(handle, &file_time, &file_time, &file_time)); - return result; -} - -internal FileProperties -os_properties_from_file(OS_Handle file) -{ - if(os_handle_match(file, os_handle_zero())) { FileProperties r = {0}; return r; } - FileProperties props = {0}; - HANDLE handle = (HANDLE)file.u64[0]; - BY_HANDLE_FILE_INFORMATION info; - BOOL info_good = GetFileInformationByHandle(handle, &info); - if(info_good) - { - U32 size_lo = info.nFileSizeLow; - U32 size_hi = info.nFileSizeHigh; - props.size = (U64)size_lo | (((U64)size_hi)<<32); - w32_dense_time_from_file_time(&props.modified, &info.ftLastWriteTime); - w32_dense_time_from_file_time(&props.created, &info.ftCreationTime); - props.flags = w32_file_property_flags_from_dwFileAttributes(info.dwFileAttributes); - } - return props; -} - -internal OS_FileID -os_id_from_file(OS_Handle file) -{ - if(os_handle_match(file, os_handle_zero())) { OS_FileID r = {0}; return r; } - OS_FileID result = {0}; - HANDLE handle = (HANDLE)file.u64[0]; - BY_HANDLE_FILE_INFORMATION info; - BOOL is_ok = GetFileInformationByHandle(handle, &info); - if(is_ok) - { - result.v[0] = info.dwVolumeSerialNumber; - result.v[1] = info.nFileIndexLow; - result.v[2] = info.nFileIndexHigh; - } - return result; -} - -internal B32 -os_delete_file_at_path(String8 path) -{ - Temp scratch = scratch_begin(0, 0); - String16 path16 = str16_from_8(scratch.arena, path); - B32 result = DeleteFileW((WCHAR*)path16.str); - scratch_end(scratch); - return result; -} - -internal B32 -os_copy_file_path(String8 dst, String8 src) -{ - Temp scratch = scratch_begin(0, 0); - String16 dst16 = str16_from_8(scratch.arena, dst); - String16 src16 = str16_from_8(scratch.arena, src); - B32 result = CopyFileW((WCHAR*)src16.str, (WCHAR*)dst16.str, 0); - scratch_end(scratch); - return result; -} - -internal String8 -os_full_path_from_path(Arena *arena, String8 path) -{ - Temp scratch = scratch_begin(&arena, 1); - DWORD buffer_size = MAX_PATH + 1; - U16 *buffer = push_array_no_zero(scratch.arena, U16, buffer_size); - String16 path16 = str16_from_8(scratch.arena, path); - DWORD path16_size = GetFullPathNameW((WCHAR*)path16.str, buffer_size, (WCHAR*)buffer, NULL); - String8 full_path = str8_from_16(arena, str16(buffer, path16_size)); - scratch_end(scratch); - return full_path; -} - -internal B32 -os_file_path_exists(String8 path) -{ - Temp scratch = scratch_begin(0,0); - String16 path16 = str16_from_8(scratch.arena, path); - DWORD attributes = GetFileAttributesW((WCHAR *)path16.str); - B32 exists = (attributes != INVALID_FILE_ATTRIBUTES) && !!(~attributes & FILE_ATTRIBUTE_DIRECTORY); - scratch_end(scratch); - return exists; -} - -//- rjf: file maps - -internal OS_Handle -os_file_map_open(OS_AccessFlags flags, OS_Handle file) -{ - OS_Handle map = {0}; - { - HANDLE file_handle = (HANDLE)file.u64[0]; - DWORD protect_flags = 0; - { - switch(flags) - { - default:{}break; - case OS_AccessFlag_Read: - {protect_flags = PAGE_READONLY;}break; - case OS_AccessFlag_Write: - case OS_AccessFlag_Read|OS_AccessFlag_Write: - {protect_flags = PAGE_READWRITE;}break; - case OS_AccessFlag_Execute: - case OS_AccessFlag_Read|OS_AccessFlag_Execute: - {protect_flags = PAGE_EXECUTE_READ;}break; - case OS_AccessFlag_Execute|OS_AccessFlag_Write|OS_AccessFlag_Read: - case OS_AccessFlag_Execute|OS_AccessFlag_Write: - {protect_flags = PAGE_EXECUTE_READWRITE;}break; - } - } - HANDLE map_handle = CreateFileMappingA(file_handle, 0, protect_flags, 0, 0, 0); - map.u64[0] = (U64)map_handle; - } - return map; -} - -internal void -os_file_map_close(OS_Handle map) -{ - HANDLE handle = (HANDLE)map.u64[0]; - CloseHandle(handle); -} - -internal void * -os_file_map_view_open(OS_Handle map, OS_AccessFlags flags, Rng1U64 range) -{ - HANDLE handle = (HANDLE)map.u64[0]; - U32 off_lo = (U32)((range.min&0x00000000ffffffffull)>>0); - U32 off_hi = (U32)((range.min&0xffffffff00000000ull)>>32); - U64 size = dim_1u64(range); - DWORD access_flags = 0; - { - switch(flags) - { - default:{}break; - case OS_AccessFlag_Read: - { - access_flags = FILE_MAP_READ; - }break; - case OS_AccessFlag_Write: - { - access_flags = FILE_MAP_WRITE; - }break; - case OS_AccessFlag_Read|OS_AccessFlag_Write: - { - access_flags = FILE_MAP_ALL_ACCESS; - }break; - case OS_AccessFlag_Execute: - case OS_AccessFlag_Read|OS_AccessFlag_Execute: - case OS_AccessFlag_Write|OS_AccessFlag_Execute: - case OS_AccessFlag_Read|OS_AccessFlag_Write|OS_AccessFlag_Execute: - { - access_flags = FILE_MAP_ALL_ACCESS|FILE_MAP_EXECUTE; - }break; - } - } - void *result = MapViewOfFile(handle, access_flags, off_hi, off_lo, size); - return result; -} - -internal void -os_file_map_view_close(OS_Handle map, void *ptr) -{ - UnmapViewOfFile(ptr); -} - -//- rjf: directory iteration - -internal OS_FileIter * -os_file_iter_begin(Arena *arena, String8 path, OS_FileIterFlags flags) -{ - Temp scratch = scratch_begin(&arena, 1); - String8 path_with_wildcard = push_str8_cat(scratch.arena, path, str8_lit("\\*")); - String16 path16 = str16_from_8(scratch.arena, path_with_wildcard); - OS_FileIter *iter = push_array(arena, OS_FileIter, 1); - iter->flags = flags; - W32_FileIter *w32_iter = (W32_FileIter*)iter->memory; - w32_iter->handle = FindFirstFileW((WCHAR*)path16.str, &w32_iter->find_data); - scratch_end(scratch); - return iter; -} - -internal B32 -os_file_iter_next(Arena *arena, OS_FileIter *iter, OS_FileInfo *info_out) -{ - B32 result = 0; - OS_FileIterFlags flags = iter->flags; - W32_FileIter *w32_iter = (W32_FileIter*)iter->memory; - if (!(flags & OS_FileIterFlag_Done) && w32_iter->handle != INVALID_HANDLE_VALUE) - { - do - { - // check is usable - B32 usable_file = 1; - - WCHAR *file_name = w32_iter->find_data.cFileName; - DWORD attributes = w32_iter->find_data.dwFileAttributes; - if (file_name[0] == '.'){ - if (flags & OS_FileIterFlag_SkipHiddenFiles){ - usable_file = 0; - } - else if (file_name[1] == 0){ - usable_file = 0; - } - else if (file_name[1] == '.' && file_name[2] == 0){ - usable_file = 0; - } - } - if (attributes & FILE_ATTRIBUTE_DIRECTORY){ - if (flags & OS_FileIterFlag_SkipFolders){ - usable_file = 0; - } - } - else{ - if (flags & OS_FileIterFlag_SkipFiles){ - usable_file = 0; - } - } - - // emit if usable - if (usable_file){ - info_out->name = str8_from_16(arena, str16_cstring((U16*)file_name)); - info_out->props.size = (U64)w32_iter->find_data.nFileSizeLow | (((U64)w32_iter->find_data.nFileSizeHigh)<<32); - w32_dense_time_from_file_time(&info_out->props.created, &w32_iter->find_data.ftCreationTime); - w32_dense_time_from_file_time(&info_out->props.modified, &w32_iter->find_data.ftLastWriteTime); - info_out->props.flags = w32_file_property_flags_from_dwFileAttributes(attributes); - result = 1; - if (!FindNextFileW(w32_iter->handle, &w32_iter->find_data)){ - iter->flags |= OS_FileIterFlag_Done; - } - break; - } - }while(FindNextFileW(w32_iter->handle, &w32_iter->find_data)); - - if (!result){ - iter->flags |= OS_FileIterFlag_Done; - } - } - return result; -} - -internal void -os_file_iter_end(OS_FileIter *iter) -{ - W32_FileIter *w32_iter = (W32_FileIter*)iter->memory; - FindClose(w32_iter->handle); -} - -//- rjf: directory creation - -internal B32 -os_make_directory(String8 path) -{ - B32 result = 0; - Temp scratch = scratch_begin(0, 0); - String16 name16 = str16_from_8(scratch.arena, path); - WIN32_FILE_ATTRIBUTE_DATA attributes = {0}; - GetFileAttributesExW((WCHAR*)name16.str, GetFileExInfoStandard, &attributes); - if(attributes.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) - { - result = 1; - } - else if(CreateDirectoryW((WCHAR*)name16.str, 0)) - { - result = 1; - } - scratch_end(scratch); - return(result); -} - -//////////////////////////////// -//~ rjf: @os_hooks Shared Memory (Implemented Per-OS) - -internal OS_Handle -os_shared_memory_alloc(U64 size, String8 name) -{ - Temp scratch = scratch_begin(0, 0); - String16 name16 = str16_from_8(scratch.arena, name); - HANDLE file = CreateFileMappingW(INVALID_HANDLE_VALUE, - 0, - PAGE_READWRITE, - (U32)((size & 0xffffffff00000000) >> 32), - (U32)((size & 0x00000000ffffffff)), - (WCHAR *)name16.str); - OS_Handle result = {(U64)file}; - scratch_end(scratch); - return result; -} - -internal OS_Handle -os_shared_memory_open(String8 name) -{ - Temp scratch = scratch_begin(0, 0); - String16 name16 = str16_from_8(scratch.arena, name); - HANDLE file = OpenFileMappingW(FILE_MAP_ALL_ACCESS, 0, (WCHAR *)name16.str); - OS_Handle result = {(U64)file}; - scratch_end(scratch); - return result; -} - -internal void -os_shared_memory_close(OS_Handle handle) -{ - HANDLE file = (HANDLE)(handle.u64[0]); - CloseHandle(file); -} - -internal void * -os_shared_memory_view_open(OS_Handle handle, Rng1U64 range) -{ - HANDLE file = (HANDLE)(handle.u64[0]); - U64 offset = range.min; - U64 size = range.max-range.min; - void *ptr = MapViewOfFile(file, FILE_MAP_ALL_ACCESS, - (U32)((offset & 0xffffffff00000000) >> 32), - (U32)((offset & 0x00000000ffffffff)), - size); - return ptr; -} - -internal void -os_shared_memory_view_close(OS_Handle handle, void *ptr) -{ - UnmapViewOfFile(ptr); -} - -//////////////////////////////// -//~ rjf: @os_hooks Time (Implemented Per-OS) - -internal OS_UnixTime -os_now_unix(void) -{ - FILETIME file_time; - GetSystemTimeAsFileTime(&file_time); - - U64 win32_time = ((U64)file_time.dwHighDateTime << 32) | file_time.dwLowDateTime; - U64 unix_time64 = ((win32_time - 0x19DB1DED53E8000ULL) / 10000000); - - Assert(unix_time64 <= OS_UNIX_TIME_MAX); - OS_UnixTime unix_time32 = (OS_UnixTime)unix_time64; - - return unix_time32; -} - -internal DateTime -os_now_universal_time(void){ - SYSTEMTIME systime = {0}; - GetSystemTime(&systime); - DateTime result = {0}; - w32_date_time_from_system_time(&result, &systime); - return(result); -} - -internal DateTime -os_universal_time_from_local_time(DateTime *date_time){ - SYSTEMTIME systime = {0}; - w32_system_time_from_date_time(&systime, date_time); - FILETIME ftime = {0}; - SystemTimeToFileTime(&systime, &ftime); - FILETIME ftime_local = {0}; - LocalFileTimeToFileTime(&ftime, &ftime_local); - FileTimeToSystemTime(&ftime_local, &systime); - DateTime result = {0}; - w32_date_time_from_system_time(&result, &systime); - return(result); -} - -internal DateTime -os_local_time_from_universal_time(DateTime *date_time){ - SYSTEMTIME systime = {0}; - w32_system_time_from_date_time(&systime, date_time); - FILETIME ftime = {0}; - SystemTimeToFileTime(&systime, &ftime); - FILETIME ftime_local = {0}; - FileTimeToLocalFileTime(&ftime, &ftime_local); - FileTimeToSystemTime(&ftime_local, &systime); - DateTime result = {0}; - w32_date_time_from_system_time(&result, &systime); - return(result); -} - -internal U64 -os_now_microseconds(void){ - U64 result = 0; - LARGE_INTEGER large_int_counter; - if (QueryPerformanceCounter(&large_int_counter)){ - result = (large_int_counter.QuadPart*Million(1))/w32_microsecond_resolution; - } - return(result); -} - -internal void -os_sleep_milliseconds(U32 msec){ - Sleep(msec); -} - -//////////////////////////////// -//~ rjf: @os_hooks Child Processes (Implemented Per-OS) - -internal B32 -os_launch_process(OS_LaunchOptions *options, OS_Handle *handle_out){ - B32 result = 0; - Temp scratch = scratch_begin(0, 0); - - StringJoin join_params = {0}; - join_params.pre = str8_lit("\""); - join_params.sep = str8_lit("\" \""); - join_params.post = str8_lit("\""); - String8 cmd = str8_list_join(scratch.arena, &options->cmd_line, &join_params); - - StringJoin join_params2 = {0}; - join_params2.sep = str8_lit("\0"); - join_params2.post = str8_lit("\0"); - B32 use_null_env_arg = 0; - String8List all_opts = options->env; - if(options->inherit_env != 0) - { - if(all_opts.node_count != 0) - { - MemoryZeroStruct(&all_opts); - for(String8Node *n = options->env.first; n != 0; n = n->next) - { - str8_list_push(scratch.arena, &all_opts, n->string); - } - for(String8Node *n = w32_environment.first; n != 0; n = n->next) - { - str8_list_push(scratch.arena, &all_opts, n->string); - } - } - else - { - use_null_env_arg = 1; - } - } - String8 env = {0}; - if(use_null_env_arg == 0) - { - env = str8_list_join(scratch.arena, &all_opts, &join_params2); - } - - String16 cmd16 = str16_from_8(scratch.arena, cmd); - String16 dir16 = str16_from_8(scratch.arena, options->path); - String16 env16 = {0}; - if(use_null_env_arg == 0) - { - env16 = str16_from_8(scratch.arena, env); - } - - DWORD creation_flags = 0; - if(options->consoleless) - { - creation_flags |= CREATE_NO_WINDOW; - } - STARTUPINFOW startup_info = {sizeof(startup_info)}; - PROCESS_INFORMATION process_info = {0}; - if (CreateProcessW(0, (WCHAR*)cmd16.str, 0, 0, 0, creation_flags, use_null_env_arg ? 0 : (WCHAR*)env16.str, (WCHAR*)dir16.str, - &startup_info, &process_info)){ - if (handle_out == 0){ - CloseHandle(process_info.hProcess); - } - CloseHandle(process_info.hThread); - - if (handle_out != 0){ - OS_Handle handle_result = {(U64)process_info.hProcess}; - *handle_out = handle_result; - } - result = 1; - } - - scratch_end(scratch); - return(result); -} - -internal B32 -os_process_wait(OS_Handle handle, U64 endt_us){ - HANDLE process = (HANDLE)(handle.u64[0]); - DWORD sleep_ms = w32_sleep_ms_from_endt_us(endt_us); - DWORD result = WaitForSingleObject(process, sleep_ms); - return (result == WAIT_OBJECT_0); -} - -internal void -os_process_release_handle(OS_Handle handle){ - HANDLE process = (HANDLE)(handle.u64[0]); - CloseHandle(process); -} - -//////////////////////////////// -//~ rjf: @os_hooks Threads (Implemented Per-OS) - -internal OS_Handle -os_launch_thread(OS_ThreadFunctionType *func, void *ptr, void *params){ - W32_Entity *entity = w32_alloc_entity(W32_EntityKind_Thread); - entity->reference_mask = 0x3; - entity->thread.func = func; - entity->thread.ptr = ptr; - entity->thread.handle = CreateThread(0, 0, w32_thread_base, entity, 0, &entity->thread.tid); - OS_Handle result = {IntFromPtr(entity)}; - return(result); -} - -internal void -os_release_thread_handle(OS_Handle thread){ - W32_Entity *entity = (W32_Entity*)PtrFromInt(thread.u64[0]); - // remove my bit - LONG result = InterlockedAnd((LONG*)&entity->reference_mask, ~0x1); - // if the other bit is also gone, free entity - if ((result & 0x2) == 0){ - w32_free_entity(entity); - } -} - -//////////////////////////////// -//~ rjf: @os_hooks Synchronization Primitives (Implemented Per-OS) - -//- rjf: mutexes - -internal OS_Handle -os_mutex_alloc(void){ - W32_Entity *entity = w32_alloc_entity(W32_EntityKind_Mutex); - InitializeCriticalSection(&entity->mutex); - - OS_Handle result = {IntFromPtr(entity)}; - return(result); -} - -internal void -os_mutex_release(OS_Handle mutex){ - W32_Entity *entity = (W32_Entity*)PtrFromInt(mutex.u64[0]); - w32_free_entity(entity); -} - -internal void -os_mutex_take_(OS_Handle mutex){ - W32_Entity *entity = (W32_Entity*)PtrFromInt(mutex.u64[0]); - EnterCriticalSection(&entity->mutex); -} - -internal void -os_mutex_drop_(OS_Handle mutex){ - W32_Entity *entity = (W32_Entity*)PtrFromInt(mutex.u64[0]); - LeaveCriticalSection(&entity->mutex); -} - -//- rjf: reader/writer mutexes - -internal OS_Handle -os_rw_mutex_alloc(void){ - W32_Entity *entity = w32_alloc_entity(W32_EntityKind_RWMutex); - InitializeSRWLock(&entity->rw_mutex); - - OS_Handle result = {IntFromPtr(entity)}; - return(result); -} - -internal void -os_rw_mutex_release(OS_Handle rw_mutex){ - W32_Entity *entity = (W32_Entity*)PtrFromInt(rw_mutex.u64[0]); - w32_free_entity(entity); -} - -internal void -os_rw_mutex_take_r_(OS_Handle rw_mutex){ - W32_Entity *entity = (W32_Entity*)PtrFromInt(rw_mutex.u64[0]); - AcquireSRWLockShared(&entity->rw_mutex); -} - -internal void -os_rw_mutex_drop_r_(OS_Handle rw_mutex){ - W32_Entity *entity = (W32_Entity*)PtrFromInt(rw_mutex.u64[0]); - ReleaseSRWLockShared(&entity->rw_mutex); -} - -internal void -os_rw_mutex_take_w_(OS_Handle rw_mutex){ - W32_Entity *entity = (W32_Entity*)PtrFromInt(rw_mutex.u64[0]); - AcquireSRWLockExclusive(&entity->rw_mutex); -} - -internal void -os_rw_mutex_drop_w_(OS_Handle rw_mutex){ - W32_Entity *entity = (W32_Entity*)PtrFromInt(rw_mutex.u64[0]); - ReleaseSRWLockExclusive(&entity->rw_mutex); -} - -//- rjf: condition variables - -internal OS_Handle -os_condition_variable_alloc(void){ - W32_Entity *entity = w32_alloc_entity(W32_EntityKind_ConditionVariable); - InitializeConditionVariable(&entity->cv); - OS_Handle result = {IntFromPtr(entity)}; - return(result); -} - -internal void -os_condition_variable_release(OS_Handle cv){ - W32_Entity *entity = (W32_Entity*)PtrFromInt(cv.u64[0]); - w32_free_entity(entity); -} - -internal B32 -os_condition_variable_wait_(OS_Handle cv, OS_Handle mutex, U64 endt_us){ - U32 sleep_ms = w32_sleep_ms_from_endt_us(endt_us); - BOOL result = 0; - if (sleep_ms > 0){ - W32_Entity *entity = (W32_Entity*)PtrFromInt(cv.u64[0]); - W32_Entity *mutex_entity = (W32_Entity*)PtrFromInt(mutex.u64[0]); - result = SleepConditionVariableCS(&entity->cv, &mutex_entity->mutex, sleep_ms); - } - return(result); -} - -internal B32 -os_condition_variable_wait_rw_r_(OS_Handle cv, OS_Handle mutex_rw, U64 endt_us){ - U32 sleep_ms = w32_sleep_ms_from_endt_us(endt_us); - BOOL result = 0; - if (sleep_ms > 0){ - W32_Entity *entity = (W32_Entity*)PtrFromInt(cv.u64[0]); - W32_Entity *mutex_entity = (W32_Entity*)PtrFromInt(mutex_rw.u64[0]); - result = SleepConditionVariableSRW(&entity->cv, &mutex_entity->rw_mutex, sleep_ms, - CONDITION_VARIABLE_LOCKMODE_SHARED); - } - return(result); -} - -internal B32 -os_condition_variable_wait_rw_w_(OS_Handle cv, OS_Handle mutex_rw, U64 endt_us){ - U32 sleep_ms = w32_sleep_ms_from_endt_us(endt_us); - BOOL result = 0; - if (sleep_ms > 0){ - W32_Entity *entity = (W32_Entity*)PtrFromInt(cv.u64[0]); - W32_Entity *mutex_entity = (W32_Entity*)PtrFromInt(mutex_rw.u64[0]); - result = SleepConditionVariableSRW(&entity->cv, &mutex_entity->rw_mutex, sleep_ms, 0); - } - return(result); -} - -internal void -os_condition_variable_signal_(OS_Handle cv){ - W32_Entity *entity = (W32_Entity*)PtrFromInt(cv.u64[0]); - WakeConditionVariable(&entity->cv); -} - -internal void -os_condition_variable_broadcast_(OS_Handle cv){ - W32_Entity *entity = (W32_Entity*)PtrFromInt(cv.u64[0]); - WakeAllConditionVariable(&entity->cv); -} - -//- rjf: cross-process semaphores - -internal OS_Handle -os_semaphore_alloc(U32 initial_count, U32 max_count, String8 name) -{ - Temp scratch = scratch_begin(0, 0); - String16 name16 = str16_from_8(scratch.arena, name); - HANDLE handle = CreateSemaphoreW(0, initial_count, max_count, (WCHAR *)name16.str); - OS_Handle result = {(U64)handle}; - scratch_end(scratch); - return result; -} - -internal void -os_semaphore_release(OS_Handle semaphore) -{ - HANDLE handle = (HANDLE)semaphore.u64[0]; - CloseHandle(handle); -} - -internal OS_Handle -os_semaphore_open(String8 name) -{ - Temp scratch = scratch_begin(0, 0); - String16 name16 = str16_from_8(scratch.arena, name); - HANDLE handle = OpenSemaphoreW(SEMAPHORE_ALL_ACCESS , 0, (WCHAR *)name16.str); - OS_Handle result = {(U64)handle}; - scratch_end(scratch); - return result; -} - -internal void -os_semaphore_close(OS_Handle semaphore) -{ - HANDLE handle = (HANDLE)semaphore.u64[0]; - CloseHandle(handle); -} - -internal B32 -os_semaphore_take(OS_Handle semaphore, U64 endt_us) -{ - U32 sleep_ms = w32_sleep_ms_from_endt_us(endt_us); - HANDLE handle = (HANDLE)semaphore.u64[0]; - DWORD wait_result = WaitForSingleObject(handle, sleep_ms); - B32 result = (wait_result == WAIT_OBJECT_0); - return result; -} - -internal void -os_semaphore_drop(OS_Handle semaphore) -{ - HANDLE handle = (HANDLE)semaphore.u64[0]; - ReleaseSemaphore(handle, 1, 0); -} - -//////////////////////////////// -//~ rjf: @os_hooks Dynamically-Loaded Libraries (Implemented Per-OS) - -internal OS_Handle -os_library_open(String8 path){ - Temp scratch = scratch_begin(0, 0); - String16 path16 = str16_from_8(scratch.arena, path); - HMODULE mod = LoadLibraryW((LPCWSTR)path16.str); - OS_Handle result = { (U64)mod }; - scratch_end(scratch); - return(result); -} - -internal VoidProc* -os_library_load_proc(OS_Handle lib, String8 name){ - Temp scratch = scratch_begin(0, 0); - HMODULE mod = (HMODULE)lib.u64[0]; - name = push_str8_copy(scratch.arena, name); - VoidProc *result = (VoidProc*)GetProcAddress(mod, (LPCSTR)name.str); - scratch_end(scratch); - return(result); -} - -internal void -os_library_close(OS_Handle lib){ - HMODULE mod = (HMODULE)lib.u64[0]; - FreeLibrary(mod); -} - -//////////////////////////////// -//~ rjf: @os_hooks Safe Calls (Implemented Per-OS) - -internal void -os_safe_call(OS_ThreadFunctionType *func, OS_ThreadFunctionType *fail_handler, void *ptr){ - __try{ - func(ptr); - } - __except (EXCEPTION_EXECUTE_HANDLER){ - if (fail_handler != 0){ - fail_handler(ptr); - } - ExitProcess(1); - } -} - -//////////////////////////////// - -internal OS_Guid -os_make_guid(void) -{ - OS_Guid result; MemoryZeroStruct(&result); - UUID uuid; - RPC_STATUS rpc_status = UuidCreate(&uuid); - if (rpc_status == RPC_S_OK) { - result.data1 = uuid.Data1; - result.data2 = uuid.Data2; - result.data3 = uuid.Data3; - MemoryCopyArray(result.data4, uuid.Data4); - } - return result; -} - +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +//////////////////////////////// +//~ rjf: Modern Windows SDK Functions +// +// (We must dynamically link to them, since they can be missing in older SDKs) + +typedef HRESULT W32_SetThreadDescription_Type(HANDLE hThread, PCWSTR lpThreadDescription); +global W32_SetThreadDescription_Type *w32_SetThreadDescription_func = 0; + +//////////////////////////////// +//~ rjf: File Info Conversion Helpers + +internal FilePropertyFlags +os_w32_file_property_flags_from_dwFileAttributes(DWORD dwFileAttributes) +{ + FilePropertyFlags flags = 0; + if(dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + flags |= FilePropertyFlag_IsFolder; + } + return flags; +} + +internal void +os_w32_file_properties_from_attribute_data(FileProperties *properties, WIN32_FILE_ATTRIBUTE_DATA *attributes) +{ + properties->size = Compose64Bit(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: Time Conversion Helpers + +internal 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; +} + +internal 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; +} + +internal void +os_w32_dense_time_from_file_time(DenseTime *out, FILETIME *in) +{ + SYSTEMTIME systime = {0}; + FileTimeToSystemTime(in, &systime); + DateTime date_time = {0}; + os_w32_date_time_from_system_time(&date_time, &systime); + *out = dense_time_from_date_time(date_time); +} + +internal U32 +os_w32_sleep_ms_from_endt_us(U64 endt_us) +{ + U32 sleep_ms = 0; + if(endt_us == max_U64) + { + sleep_ms = INFINITE; + } + else + { + U64 begint = os_now_microseconds(); + if(begint < endt_us) + { + U64 sleep_us = endt_us - begint; + sleep_ms = (U32)((sleep_us + 999)/1000); + } + } + return sleep_ms; +} + +//////////////////////////////// +//~ rjf: Entity Functions + +internal OS_W32_Entity * +os_w32_entity_alloc(OS_W32_EntityKind kind) +{ + OS_W32_Entity *result = 0; + EnterCriticalSection(&os_w32_state.entity_mutex); + { + result = os_w32_state.entity_free; + if(result) + { + SLLStackPop(os_w32_state.entity_free); + } + else + { + result = push_array_no_zero(os_w32_state.entity_arena, OS_W32_Entity, 1); + } + MemoryZeroStruct(result); + } + LeaveCriticalSection(&os_w32_state.entity_mutex); + result->kind = kind; + return result; +} + +internal void +os_w32_entity_release(OS_W32_Entity *entity) +{ + entity->kind = OS_W32_EntityKind_Null; + EnterCriticalSection(&os_w32_state.entity_mutex); + SLLStackPush(os_w32_state.entity_free, entity); + LeaveCriticalSection(&os_w32_state.entity_mutex); +} + +//////////////////////////////// +//~ rjf: Thread Entry Point + +internal DWORD +os_w32_thread_entry_point(void *ptr) +{ + OS_W32_Entity *entity = (OS_W32_Entity *)ptr; + OS_ThreadFunctionType *func = entity->thread.func; + void *thread_ptr = entity->thread.ptr; + TCTX tctx_; + tctx_init_and_equip(&tctx_); + func(thread_ptr); + tctx_release(); + return 0; +} + +//////////////////////////////// +//~ rjf: @os_hooks System/Process Info (Implemented Per-OS) + +internal OS_SystemInfo * +os_get_system_info(void) +{ + return &os_w32_state.system_info; +} + +internal OS_ProcessInfo * +os_get_process_info(void) +{ + return &os_w32_state.process_info; +} + +internal String8 +os_get_current_path(Arena *arena) +{ + Temp scratch = scratch_begin(&arena, 1); + DWORD length = GetCurrentDirectoryW(0, 0); + U16 *memory = push_array_no_zero(scratch.arena, U16, length + 1); + length = GetCurrentDirectoryW(length + 1, (WCHAR*)memory); + String8 name = str8_from_16(arena, str16(memory, length)); + scratch_end(scratch); + return name; +} + +//////////////////////////////// +//~ rjf: @os_hooks Memory Allocation (Implemented Per-OS) + +//- rjf: basic + +internal void * +os_reserve(U64 size) +{ + void *result = VirtualAlloc(0, size, MEM_RESERVE, PAGE_READWRITE); + return result; +} + +internal B32 +os_commit(void *ptr, U64 size) +{ + B32 result = (VirtualAlloc(ptr, size, MEM_COMMIT, PAGE_READWRITE) != 0); + return result; +} + +internal void +os_decommit(void *ptr, U64 size) +{ + VirtualFree(ptr, size, MEM_DECOMMIT); +} + +internal 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 + +internal 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; +} + +internal B32 +os_commit_large(void *ptr, U64 size) +{ + return 1; +} + +//////////////////////////////// +//~ rjf: @os_hooks Thread Info (Implemented Per-OS) + +internal U32 +os_tid(void) +{ + DWORD id = GetCurrentThreadId(); + return (U32)id; +} + +internal void +os_set_thread_name(String8 name) +{ + Temp scratch = scratch_begin(0, 0); + + // rjf: windows 10 style + if(w32_SetThreadDescription_func) + { + String16 name16 = str16_from_8(scratch.arena, name); + HRESULT hr = w32_SetThreadDescription_func(GetCurrentThread(), (WCHAR*)name16.str); + } + + // rjf: raise-exception style + { + String8 name_copy = push_str8_copy(scratch.arena, name); +#pragma pack(push,8) + typedef struct THREADNAME_INFO THREADNAME_INFO; + struct THREADNAME_INFO + { + U32 dwType; // Must be 0x1000. + char *szName; // Pointer to name (in user addr space). + U32 dwThreadID; // Thread ID (-1=caller thread). + U32 dwFlags; // Reserved for future use, must be zero. + }; +#pragma pack(pop) + THREADNAME_INFO info; + info.dwType = 0x1000; + info.szName = (char *)name_copy.str; + info.dwThreadID = os_tid(); + info.dwFlags = 0; +#pragma warning(push) +#pragma warning(disable: 6320 6322) + __try + { + RaiseException(0x406D1388, 0, sizeof(info) / sizeof(void *), (const ULONG_PTR *)&info); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + } +#pragma warning(pop) + } + + scratch_end(scratch); +} + +//////////////////////////////// +//~ rjf: @os_hooks Aborting (Implemented Per-OS) + +internal void +os_abort(S32 exit_code) +{ + ExitProcess(exit_code); +} + +//////////////////////////////// +//~ rjf: @os_hooks File System (Implemented Per-OS) + +//- rjf: files + +internal OS_Handle +os_file_open(OS_AccessFlags flags, String8 path) +{ + OS_Handle result = {0}; + Temp scratch = scratch_begin(0, 0); + String16 path16 = str16_from_8(scratch.arena, path); + DWORD access_flags = 0; + DWORD share_mode = 0; + DWORD creation_disposition = OPEN_EXISTING; + if(flags & OS_AccessFlag_Read) {access_flags |= GENERIC_READ;} + if(flags & OS_AccessFlag_Write) {access_flags |= GENERIC_WRITE;} + if(flags & OS_AccessFlag_Execute) {access_flags |= GENERIC_EXECUTE;} + if(flags & OS_AccessFlag_ShareRead) {share_mode |= FILE_SHARE_READ;} + if(flags & OS_AccessFlag_ShareWrite) {share_mode |= FILE_SHARE_WRITE|FILE_SHARE_DELETE;} + if(flags & OS_AccessFlag_Write) {creation_disposition = CREATE_ALWAYS;} + if(flags & OS_AccessFlag_Append) {creation_disposition = OPEN_ALWAYS;} + HANDLE file = CreateFileW((WCHAR *)path16.str, access_flags, share_mode, 0, creation_disposition, FILE_ATTRIBUTE_NORMAL, 0); + if(file != INVALID_HANDLE_VALUE) + { + result.u64[0] = (U64)file; + } + scratch_end(scratch); + return result; +} + +internal void +os_file_close(OS_Handle file) +{ + if(os_handle_match(file, os_handle_zero())) { return; } + HANDLE handle = (HANDLE)file.u64[0]; + BOOL result = CloseHandle(handle); + (void)result; +} + +internal U64 +os_file_read(OS_Handle file, Rng1U64 rng, void *out_data) +{ + if(os_handle_match(file, os_handle_zero())) { return 0; } + HANDLE handle = (HANDLE)file.u64[0]; + + // rjf: clamp range by file size + U64 size = 0; + GetFileSizeEx(handle, (LARGE_INTEGER *)&size); + Rng1U64 rng_clamped = r1u64(ClampTop(rng.min, size), ClampTop(rng.max, size)); + U64 total_read_size = 0; + + // rjf: read loop + { + U64 to_read = dim_1u64(rng_clamped); + for(U64 off = rng.min; total_read_size < to_read;) + { + U64 amt64 = to_read - total_read_size; + U32 amt32 = u32_from_u64_saturate(amt64); + DWORD read_size = 0; + OVERLAPPED overlapped = {0}; + overlapped.Offset = (off&0x00000000ffffffffull); + overlapped.OffsetHigh = (off&0xffffffff00000000ull) >> 32; + ReadFile(handle, (U8 *)out_data + total_read_size, amt32, &read_size, &overlapped); + off += read_size; + total_read_size += read_size; + if(read_size != amt32) + { + break; + } + } + } + + return total_read_size; +} + +internal U64 +os_file_write(OS_Handle file, Rng1U64 rng, void *data) +{ + if(os_handle_match(file, os_handle_zero())) { return 0; } + HANDLE win_handle = (HANDLE)file.u64[0]; + U64 src_off = 0; + U64 dst_off = rng.min; + U64 bytes_to_write_total = rng.max-rng.min; + U64 total_bytes_written = 0; + for(;src_off < bytes_to_write_total;) + { + void *bytes_src = (void *)((U8 *)data + src_off); + U64 bytes_to_write_64 = (bytes_to_write_total-src_off); + U32 bytes_to_write_32 = u32_from_u64_saturate(bytes_to_write_64); + U32 bytes_written = 0; + OVERLAPPED overlapped = {0}; + overlapped.Offset = (dst_off&0x00000000ffffffffull); + overlapped.OffsetHigh = (dst_off&0xffffffff00000000ull) >> 32; + BOOL success = WriteFile(win_handle, bytes_src, bytes_to_write_32, (DWORD *)&bytes_written, &overlapped); + if(success == 0) + { + break; + } + src_off += bytes_written; + dst_off += bytes_written; + total_bytes_written += bytes_written; + } + return total_bytes_written; +} + +internal B32 +os_file_set_time(OS_Handle file, DateTime time) +{ + if(os_handle_match(file, os_handle_zero())) { return 0; } + B32 result = 0; + HANDLE handle = (HANDLE)file.u64[0]; + SYSTEMTIME system_time = {0}; + os_w32_system_time_from_date_time(&system_time, &time); + FILETIME file_time = {0}; + result = (SystemTimeToFileTime(&system_time, &file_time) && + SetFileTime(handle, &file_time, &file_time, &file_time)); + return result; +} + +internal FileProperties +os_properties_from_file(OS_Handle file) +{ + if(os_handle_match(file, os_handle_zero())) { FileProperties r = {0}; return r; } + FileProperties props = {0}; + HANDLE handle = (HANDLE)file.u64[0]; + BY_HANDLE_FILE_INFORMATION info; + BOOL info_good = GetFileInformationByHandle(handle, &info); + if(info_good) + { + U32 size_lo = info.nFileSizeLow; + U32 size_hi = info.nFileSizeHigh; + props.size = (U64)size_lo | (((U64)size_hi)<<32); + os_w32_dense_time_from_file_time(&props.modified, &info.ftLastWriteTime); + os_w32_dense_time_from_file_time(&props.created, &info.ftCreationTime); + props.flags = os_w32_file_property_flags_from_dwFileAttributes(info.dwFileAttributes); + } + return props; +} + +internal OS_FileID +os_id_from_file(OS_Handle file) +{ + if(os_handle_match(file, os_handle_zero())) { OS_FileID r = {0}; return r; } + OS_FileID result = {0}; + HANDLE handle = (HANDLE)file.u64[0]; + BY_HANDLE_FILE_INFORMATION info; + BOOL is_ok = GetFileInformationByHandle(handle, &info); + if(is_ok) + { + result.v[0] = info.dwVolumeSerialNumber; + result.v[1] = info.nFileIndexLow; + result.v[2] = info.nFileIndexHigh; + } + return result; +} + +internal B32 +os_delete_file_at_path(String8 path) +{ + Temp scratch = scratch_begin(0, 0); + String16 path16 = str16_from_8(scratch.arena, path); + B32 result = DeleteFileW((WCHAR*)path16.str); + scratch_end(scratch); + return result; +} + +internal B32 +os_copy_file_path(String8 dst, String8 src) +{ + Temp scratch = scratch_begin(0, 0); + String16 dst16 = str16_from_8(scratch.arena, dst); + String16 src16 = str16_from_8(scratch.arena, src); + B32 result = CopyFileW((WCHAR*)src16.str, (WCHAR*)dst16.str, 0); + scratch_end(scratch); + return result; +} + +internal String8 +os_full_path_from_path(Arena *arena, String8 path) +{ + Temp scratch = scratch_begin(&arena, 1); + DWORD buffer_size = MAX_PATH + 1; + U16 *buffer = push_array_no_zero(scratch.arena, U16, buffer_size); + String16 path16 = str16_from_8(scratch.arena, path); + DWORD path16_size = GetFullPathNameW((WCHAR*)path16.str, buffer_size, (WCHAR*)buffer, NULL); + String8 full_path = str8_from_16(arena, str16(buffer, path16_size)); + scratch_end(scratch); + return full_path; +} + +internal B32 +os_file_path_exists(String8 path) +{ + Temp scratch = scratch_begin(0,0); + String16 path16 = str16_from_8(scratch.arena, path); + DWORD attributes = GetFileAttributesW((WCHAR *)path16.str); + B32 exists = (attributes != INVALID_FILE_ATTRIBUTES) && !!(~attributes & FILE_ATTRIBUTE_DIRECTORY); + scratch_end(scratch); + return exists; +} + +internal FileProperties +os_properties_from_file_path(String8 path) +{ + WIN32_FIND_DATAW find_data = {0}; + Temp scratch = scratch_begin(0, 0); + String16 path16 = str16_from_8(scratch.arena, path); + HANDLE handle = FindFirstFileW((WCHAR *)path16.str, &find_data); + FileProperties props = {0}; + if(handle != INVALID_HANDLE_VALUE) + { + props.size = Compose64Bit(find_data.nFileSizeHigh, find_data.nFileSizeLow); + os_w32_dense_time_from_file_time(&props.created, &find_data.ftCreationTime); + os_w32_dense_time_from_file_time(&props.modified, &find_data.ftLastWriteTime); + props.flags = os_w32_file_property_flags_from_dwFileAttributes(find_data.dwFileAttributes); + } + FindClose(handle); + scratch_end(scratch); + return props; +} + +//- rjf: file maps + +internal OS_Handle +os_file_map_open(OS_AccessFlags flags, OS_Handle file) +{ + OS_Handle map = {0}; + { + HANDLE file_handle = (HANDLE)file.u64[0]; + DWORD protect_flags = 0; + { + switch(flags) + { + default:{}break; + case OS_AccessFlag_Read: + {protect_flags = PAGE_READONLY;}break; + case OS_AccessFlag_Write: + case OS_AccessFlag_Read|OS_AccessFlag_Write: + {protect_flags = PAGE_READWRITE;}break; + case OS_AccessFlag_Execute: + case OS_AccessFlag_Read|OS_AccessFlag_Execute: + {protect_flags = PAGE_EXECUTE_READ;}break; + case OS_AccessFlag_Execute|OS_AccessFlag_Write|OS_AccessFlag_Read: + case OS_AccessFlag_Execute|OS_AccessFlag_Write: + {protect_flags = PAGE_EXECUTE_READWRITE;}break; + } + } + HANDLE map_handle = CreateFileMappingA(file_handle, 0, protect_flags, 0, 0, 0); + map.u64[0] = (U64)map_handle; + } + return map; +} + +internal void +os_file_map_close(OS_Handle map) +{ + HANDLE handle = (HANDLE)map.u64[0]; + BOOL result = CloseHandle(handle); + (void)result; +} + +internal void * +os_file_map_view_open(OS_Handle map, OS_AccessFlags flags, Rng1U64 range) +{ + HANDLE handle = (HANDLE)map.u64[0]; + U32 off_lo = (U32)((range.min&0x00000000ffffffffull)>>0); + U32 off_hi = (U32)((range.min&0xffffffff00000000ull)>>32); + U64 size = dim_1u64(range); + DWORD access_flags = 0; + { + switch(flags) + { + default:{}break; + case OS_AccessFlag_Read: + { + access_flags = FILE_MAP_READ; + }break; + case OS_AccessFlag_Write: + { + access_flags = FILE_MAP_WRITE; + }break; + case OS_AccessFlag_Read|OS_AccessFlag_Write: + { + access_flags = FILE_MAP_ALL_ACCESS; + }break; + case OS_AccessFlag_Execute: + case OS_AccessFlag_Read|OS_AccessFlag_Execute: + case OS_AccessFlag_Write|OS_AccessFlag_Execute: + case OS_AccessFlag_Read|OS_AccessFlag_Write|OS_AccessFlag_Execute: + { + access_flags = FILE_MAP_ALL_ACCESS|FILE_MAP_EXECUTE; + }break; + } + } + void *result = MapViewOfFile(handle, access_flags, off_hi, off_lo, size); + return result; +} + +internal void +os_file_map_view_close(OS_Handle map, void *ptr, Rng1U64 range) +{ + BOOL result = UnmapViewOfFile(ptr); + (void)result; +} + +//- rjf: directory iteration + +internal OS_FileIter * +os_file_iter_begin(Arena *arena, String8 path, OS_FileIterFlags flags) +{ + Temp scratch = scratch_begin(&arena, 1); + String8 path_with_wildcard = push_str8_cat(scratch.arena, path, str8_lit("\\*")); + String16 path16 = str16_from_8(scratch.arena, path_with_wildcard); + OS_FileIter *iter = push_array(arena, OS_FileIter, 1); + iter->flags = flags; + OS_W32_FileIter *w32_iter = (OS_W32_FileIter*)iter->memory; + if(path.size == 0) + { + w32_iter->is_volume_iter = 1; + WCHAR buffer[512] = {0}; + DWORD length = GetLogicalDriveStringsW(sizeof(buffer), buffer); + String8List drive_strings = {0}; + for(U64 off = 0; off < (U64)length;) + { + String16 next_drive_string_16 = str16_cstring((U16 *)buffer+off); + off += next_drive_string_16.size+1; + String8 next_drive_string = str8_from_16(arena, next_drive_string_16); + next_drive_string = str8_chop_last_slash(next_drive_string); + str8_list_push(scratch.arena, &drive_strings, next_drive_string); + } + w32_iter->drive_strings = str8_array_from_list(arena, &drive_strings); + w32_iter->drive_strings_iter_idx = 0; + } + else + { + w32_iter->handle = FindFirstFileW((WCHAR*)path16.str, &w32_iter->find_data); + } + scratch_end(scratch); + return iter; +} + +internal B32 +os_file_iter_next(Arena *arena, OS_FileIter *iter, OS_FileInfo *info_out) +{ + B32 result = 0; + OS_FileIterFlags flags = iter->flags; + OS_W32_FileIter *w32_iter = (OS_W32_FileIter*)iter->memory; + switch(w32_iter->is_volume_iter) + { + //- rjf: file iteration + default: + case 0: + { + if (!(flags & OS_FileIterFlag_Done) && w32_iter->handle != INVALID_HANDLE_VALUE) + { + do + { + // check is usable + B32 usable_file = 1; + + WCHAR *file_name = w32_iter->find_data.cFileName; + DWORD attributes = w32_iter->find_data.dwFileAttributes; + if (file_name[0] == '.'){ + if (flags & OS_FileIterFlag_SkipHiddenFiles){ + usable_file = 0; + } + else if (file_name[1] == 0){ + usable_file = 0; + } + else if (file_name[1] == '.' && file_name[2] == 0){ + usable_file = 0; + } + } + if (attributes & FILE_ATTRIBUTE_DIRECTORY){ + if (flags & OS_FileIterFlag_SkipFolders){ + usable_file = 0; + } + } + else{ + if (flags & OS_FileIterFlag_SkipFiles){ + usable_file = 0; + } + } + + // emit if usable + if (usable_file){ + info_out->name = str8_from_16(arena, str16_cstring((U16*)file_name)); + info_out->props.size = (U64)w32_iter->find_data.nFileSizeLow | (((U64)w32_iter->find_data.nFileSizeHigh)<<32); + os_w32_dense_time_from_file_time(&info_out->props.created, &w32_iter->find_data.ftCreationTime); + os_w32_dense_time_from_file_time(&info_out->props.modified, &w32_iter->find_data.ftLastWriteTime); + info_out->props.flags = os_w32_file_property_flags_from_dwFileAttributes(attributes); + result = 1; + if (!FindNextFileW(w32_iter->handle, &w32_iter->find_data)){ + iter->flags |= OS_FileIterFlag_Done; + } + break; + } + }while(FindNextFileW(w32_iter->handle, &w32_iter->find_data)); + } + }break; + + //- rjf: volume iteration + case 1: + { + result = w32_iter->drive_strings_iter_idx < w32_iter->drive_strings.count; + if(result != 0) + { + MemoryZeroStruct(info_out); + info_out->name = w32_iter->drive_strings.v[w32_iter->drive_strings_iter_idx]; + info_out->props.flags |= FilePropertyFlag_IsFolder; + w32_iter->drive_strings_iter_idx += 1; + } + }break; + } + if(!result) + { + iter->flags |= OS_FileIterFlag_Done; + } + return result; +} + +internal void +os_file_iter_end(OS_FileIter *iter) +{ + OS_W32_FileIter *w32_iter = (OS_W32_FileIter*)iter->memory; + FindClose(w32_iter->handle); +} + +//- rjf: directory creation + +internal B32 +os_make_directory(String8 path) +{ + B32 result = 0; + Temp scratch = scratch_begin(0, 0); + String16 name16 = str16_from_8(scratch.arena, path); + WIN32_FILE_ATTRIBUTE_DATA attributes = {0}; + GetFileAttributesExW((WCHAR*)name16.str, GetFileExInfoStandard, &attributes); + if(attributes.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + result = 1; + } + else if(CreateDirectoryW((WCHAR*)name16.str, 0)) + { + result = 1; + } + scratch_end(scratch); + return(result); +} + +//////////////////////////////// +//~ rjf: @os_hooks Shared Memory (Implemented Per-OS) + +internal OS_Handle +os_shared_memory_alloc(U64 size, String8 name) +{ + Temp scratch = scratch_begin(0, 0); + String16 name16 = str16_from_8(scratch.arena, name); + HANDLE file = CreateFileMappingW(INVALID_HANDLE_VALUE, + 0, + PAGE_READWRITE, + (U32)((size & 0xffffffff00000000) >> 32), + (U32)((size & 0x00000000ffffffff)), + (WCHAR *)name16.str); + OS_Handle result = {(U64)file}; + scratch_end(scratch); + return result; +} + +internal OS_Handle +os_shared_memory_open(String8 name) +{ + Temp scratch = scratch_begin(0, 0); + String16 name16 = str16_from_8(scratch.arena, name); + HANDLE file = OpenFileMappingW(FILE_MAP_ALL_ACCESS, 0, (WCHAR *)name16.str); + OS_Handle result = {(U64)file}; + scratch_end(scratch); + return result; +} + +internal void +os_shared_memory_close(OS_Handle handle) +{ + HANDLE file = (HANDLE)(handle.u64[0]); + CloseHandle(file); +} + +internal void * +os_shared_memory_view_open(OS_Handle handle, Rng1U64 range) +{ + HANDLE file = (HANDLE)(handle.u64[0]); + U64 offset = range.min; + U64 size = range.max-range.min; + void *ptr = MapViewOfFile(file, FILE_MAP_ALL_ACCESS, + (U32)((offset & 0xffffffff00000000) >> 32), + (U32)((offset & 0x00000000ffffffff)), + size); + return ptr; +} + +internal void +os_shared_memory_view_close(OS_Handle handle, void *ptr, Rng1U64 range) +{ + UnmapViewOfFile(ptr); +} + +//////////////////////////////// +//~ rjf: @os_hooks Time (Implemented Per-OS) + +internal U64 +os_now_microseconds(void) +{ + U64 result = 0; + LARGE_INTEGER large_int_counter; + if(QueryPerformanceCounter(&large_int_counter)) + { + result = (large_int_counter.QuadPart*Million(1))/os_w32_state.microsecond_resolution; + } + return result; +} + +internal U32 +os_now_unix(void) +{ + FILETIME file_time; + GetSystemTimeAsFileTime(&file_time); + U64 win32_time = ((U64)file_time.dwHighDateTime << 32) | file_time.dwLowDateTime; + U64 unix_time64 = ((win32_time - 0x19DB1DED53E8000ULL) / 10000000); + U32 unix_time32 = (U32)unix_time64; + return unix_time32; +} + +internal DateTime +os_now_universal_time(void) +{ + SYSTEMTIME systime = {0}; + GetSystemTime(&systime); + DateTime result = {0}; + os_w32_date_time_from_system_time(&result, &systime); + return result; +} + +internal DateTime +os_universal_time_from_local(DateTime *date_time) +{ + SYSTEMTIME systime = {0}; + os_w32_system_time_from_date_time(&systime, date_time); + FILETIME ftime = {0}; + SystemTimeToFileTime(&systime, &ftime); + FILETIME ftime_local = {0}; + LocalFileTimeToFileTime(&ftime, &ftime_local); + FileTimeToSystemTime(&ftime_local, &systime); + DateTime result = {0}; + os_w32_date_time_from_system_time(&result, &systime); + return result; +} + +internal DateTime +os_local_time_from_universal(DateTime *date_time) +{ + SYSTEMTIME systime = {0}; + os_w32_system_time_from_date_time(&systime, date_time); + FILETIME ftime = {0}; + SystemTimeToFileTime(&systime, &ftime); + FILETIME ftime_local = {0}; + FileTimeToLocalFileTime(&ftime, &ftime_local); + FileTimeToSystemTime(&ftime_local, &systime); + DateTime result = {0}; + os_w32_date_time_from_system_time(&result, &systime); + return result; +} + +internal void +os_sleep_milliseconds(U32 msec) +{ + Sleep(msec); +} + +//////////////////////////////// +//~ rjf: @os_hooks Child Processes (Implemented Per-OS) + +internal OS_Handle +os_process_launch(OS_ProcessLaunchParams *params) +{ + OS_Handle result = {0}; + Temp scratch = scratch_begin(0, 0); + + //- rjf: form full command string + String8 cmd = {0}; + { + StringJoin join_params = {0}; + join_params.pre = str8_lit("\""); + join_params.sep = str8_lit("\" \""); + join_params.post = str8_lit("\""); + cmd = str8_list_join(scratch.arena, ¶ms->cmd_line, &join_params); + } + + //- rjf: form environment + B32 use_null_env_arg = 0; + String8 env = {0}; + { + StringJoin join_params2 = {0}; + join_params2.sep = str8_lit("\0"); + join_params2.post = str8_lit("\0"); + String8List all_opts = params->env; + if(params->inherit_env != 0) + { + if(all_opts.node_count != 0) + { + MemoryZeroStruct(&all_opts); + for(String8Node *n = params->env.first; n != 0; n = n->next) + { + str8_list_push(scratch.arena, &all_opts, n->string); + } + for(String8Node *n = os_w32_state.process_info.environment.first; n != 0; n = n->next) + { + str8_list_push(scratch.arena, &all_opts, n->string); + } + } + else + { + use_null_env_arg = 1; + } + } + if(use_null_env_arg == 0) + { + env = str8_list_join(scratch.arena, &all_opts, &join_params2); + } + } + + //- rjf: utf-8 -> utf-16 + String16 cmd16 = str16_from_8(scratch.arena, cmd); + String16 dir16 = str16_from_8(scratch.arena, params->path); + String16 env16 = {0}; + if(use_null_env_arg == 0) + { + env16 = str16_from_8(scratch.arena, env); + } + + //- rjf: determine creation flags + DWORD creation_flags = CREATE_UNICODE_ENVIRONMENT; + if(params->consoleless) + { + creation_flags |= CREATE_NO_WINDOW; + } + + //- rjf: launch + STARTUPINFOW startup_info = {sizeof(startup_info)}; + PROCESS_INFORMATION process_info = {0}; + if(CreateProcessW(0, (WCHAR*)cmd16.str, 0, 0, 0, creation_flags, use_null_env_arg ? 0 : (WCHAR*)env16.str, (WCHAR*)dir16.str, &startup_info, &process_info)) + { + result.u64[0] = (U64)process_info.hProcess; + CloseHandle(process_info.hThread); + } + + scratch_end(scratch); + return result; +} + +internal B32 +os_process_join(OS_Handle handle, U64 endt_us) +{ + HANDLE process = (HANDLE)(handle.u64[0]); + DWORD sleep_ms = os_w32_sleep_ms_from_endt_us(endt_us); + DWORD result = WaitForSingleObject(process, sleep_ms); + return (result == WAIT_OBJECT_0); +} + +internal void +os_process_detach(OS_Handle handle) +{ + HANDLE process = (HANDLE)(handle.u64[0]); + CloseHandle(process); +} + +//////////////////////////////// +//~ rjf: @os_hooks Threads (Implemented Per-OS) + +internal OS_Handle +os_thread_launch(OS_ThreadFunctionType *func, void *ptr, void *params) +{ + OS_W32_Entity *entity = os_w32_entity_alloc(OS_W32_EntityKind_Thread); + entity->thread.func = func; + entity->thread.ptr = ptr; + entity->thread.handle = CreateThread(0, 0, os_w32_thread_entry_point, entity, 0, &entity->thread.tid); + OS_Handle result = {IntFromPtr(entity)}; + return result; +} + +internal B32 +os_thread_join(OS_Handle handle, U64 endt_us) +{ + DWORD sleep_ms = os_w32_sleep_ms_from_endt_us(endt_us); + OS_W32_Entity *entity = (OS_W32_Entity *)PtrFromInt(handle.u64[0]); + DWORD wait_result = WAIT_OBJECT_0; + if(entity != 0) + { + wait_result = WaitForSingleObject(entity->thread.handle, sleep_ms); + } + os_w32_entity_release(entity); + return (wait_result == WAIT_OBJECT_0); +} + +internal void +os_thread_detach(OS_Handle thread) +{ + OS_W32_Entity *entity = (OS_W32_Entity*)PtrFromInt(thread.u64[0]); + os_w32_entity_release(entity); +} + +//////////////////////////////// +//~ rjf: @os_hooks Synchronization Primitives (Implemented Per-OS) + +//- rjf: mutexes + +internal OS_Handle +os_mutex_alloc(void) +{ + OS_W32_Entity *entity = os_w32_entity_alloc(OS_W32_EntityKind_Mutex); + InitializeCriticalSection(&entity->mutex); + OS_Handle result = {IntFromPtr(entity)}; + return result; +} + +internal void +os_mutex_release(OS_Handle mutex) +{ + OS_W32_Entity *entity = (OS_W32_Entity*)PtrFromInt(mutex.u64[0]); + os_w32_entity_release(entity); +} + +internal void +os_mutex_take(OS_Handle mutex) +{ + OS_W32_Entity *entity = (OS_W32_Entity*)PtrFromInt(mutex.u64[0]); + EnterCriticalSection(&entity->mutex); +} + +internal void +os_mutex_drop(OS_Handle mutex) +{ + OS_W32_Entity *entity = (OS_W32_Entity*)PtrFromInt(mutex.u64[0]); + LeaveCriticalSection(&entity->mutex); +} + +//- rjf: reader/writer mutexes + +internal OS_Handle +os_rw_mutex_alloc(void) +{ + OS_W32_Entity *entity = os_w32_entity_alloc(OS_W32_EntityKind_RWMutex); + InitializeSRWLock(&entity->rw_mutex); + OS_Handle result = {IntFromPtr(entity)}; + return result; +} + +internal void +os_rw_mutex_release(OS_Handle rw_mutex) +{ + OS_W32_Entity *entity = (OS_W32_Entity*)PtrFromInt(rw_mutex.u64[0]); + os_w32_entity_release(entity); +} + +internal void +os_rw_mutex_take_r(OS_Handle rw_mutex) +{ + OS_W32_Entity *entity = (OS_W32_Entity*)PtrFromInt(rw_mutex.u64[0]); + AcquireSRWLockShared(&entity->rw_mutex); +} + +internal void +os_rw_mutex_drop_r(OS_Handle rw_mutex) +{ + OS_W32_Entity *entity = (OS_W32_Entity*)PtrFromInt(rw_mutex.u64[0]); + ReleaseSRWLockShared(&entity->rw_mutex); +} + +internal void +os_rw_mutex_take_w(OS_Handle rw_mutex) +{ + OS_W32_Entity *entity = (OS_W32_Entity*)PtrFromInt(rw_mutex.u64[0]); + AcquireSRWLockExclusive(&entity->rw_mutex); +} + +internal void +os_rw_mutex_drop_w(OS_Handle rw_mutex) +{ + OS_W32_Entity *entity = (OS_W32_Entity*)PtrFromInt(rw_mutex.u64[0]); + ReleaseSRWLockExclusive(&entity->rw_mutex); +} + +//- rjf: condition variables + +internal OS_Handle +os_condition_variable_alloc(void) +{ + OS_W32_Entity *entity = os_w32_entity_alloc(OS_W32_EntityKind_ConditionVariable); + InitializeConditionVariable(&entity->cv); + OS_Handle result = {IntFromPtr(entity)}; + return result; +} + +internal void +os_condition_variable_release(OS_Handle cv) +{ + OS_W32_Entity *entity = (OS_W32_Entity*)PtrFromInt(cv.u64[0]); + os_w32_entity_release(entity); +} + +internal B32 +os_condition_variable_wait(OS_Handle cv, OS_Handle mutex, U64 endt_us) +{ + U32 sleep_ms = os_w32_sleep_ms_from_endt_us(endt_us); + BOOL result = 0; + if(sleep_ms > 0) + { + OS_W32_Entity *entity = (OS_W32_Entity*)PtrFromInt(cv.u64[0]); + OS_W32_Entity *mutex_entity = (OS_W32_Entity*)PtrFromInt(mutex.u64[0]); + result = SleepConditionVariableCS(&entity->cv, &mutex_entity->mutex, sleep_ms); + } + return result; +} + +internal B32 +os_condition_variable_wait_rw_r(OS_Handle cv, OS_Handle mutex_rw, U64 endt_us) +{ + U32 sleep_ms = os_w32_sleep_ms_from_endt_us(endt_us); + BOOL result = 0; + if(sleep_ms > 0) + { + OS_W32_Entity *entity = (OS_W32_Entity*)PtrFromInt(cv.u64[0]); + OS_W32_Entity *mutex_entity = (OS_W32_Entity*)PtrFromInt(mutex_rw.u64[0]); + result = SleepConditionVariableSRW(&entity->cv, &mutex_entity->rw_mutex, sleep_ms, + CONDITION_VARIABLE_LOCKMODE_SHARED); + } + return result; +} + +internal B32 +os_condition_variable_wait_rw_w(OS_Handle cv, OS_Handle mutex_rw, U64 endt_us) +{ + U32 sleep_ms = os_w32_sleep_ms_from_endt_us(endt_us); + BOOL result = 0; + if(sleep_ms > 0) + { + OS_W32_Entity *entity = (OS_W32_Entity*)PtrFromInt(cv.u64[0]); + OS_W32_Entity *mutex_entity = (OS_W32_Entity*)PtrFromInt(mutex_rw.u64[0]); + result = SleepConditionVariableSRW(&entity->cv, &mutex_entity->rw_mutex, sleep_ms, 0); + } + return result; +} + +internal void +os_condition_variable_signal(OS_Handle cv) +{ + OS_W32_Entity *entity = (OS_W32_Entity*)PtrFromInt(cv.u64[0]); + WakeConditionVariable(&entity->cv); +} + +internal void +os_condition_variable_broadcast(OS_Handle cv) +{ + OS_W32_Entity *entity = (OS_W32_Entity*)PtrFromInt(cv.u64[0]); + WakeAllConditionVariable(&entity->cv); +} + +//- rjf: cross-process semaphores + +internal OS_Handle +os_semaphore_alloc(U32 initial_count, U32 max_count, String8 name) +{ + Temp scratch = scratch_begin(0, 0); + String16 name16 = str16_from_8(scratch.arena, name); + HANDLE handle = CreateSemaphoreW(0, initial_count, max_count, (WCHAR *)name16.str); + OS_Handle result = {(U64)handle}; + scratch_end(scratch); + return result; +} + +internal void +os_semaphore_release(OS_Handle semaphore) +{ + HANDLE handle = (HANDLE)semaphore.u64[0]; + CloseHandle(handle); +} + +internal OS_Handle +os_semaphore_open(String8 name) +{ + Temp scratch = scratch_begin(0, 0); + String16 name16 = str16_from_8(scratch.arena, name); + HANDLE handle = OpenSemaphoreW(SEMAPHORE_ALL_ACCESS , 0, (WCHAR *)name16.str); + OS_Handle result = {(U64)handle}; + scratch_end(scratch); + return result; +} + +internal void +os_semaphore_close(OS_Handle semaphore) +{ + HANDLE handle = (HANDLE)semaphore.u64[0]; + CloseHandle(handle); +} + +internal B32 +os_semaphore_take(OS_Handle semaphore, U64 endt_us) +{ + U32 sleep_ms = os_w32_sleep_ms_from_endt_us(endt_us); + HANDLE handle = (HANDLE)semaphore.u64[0]; + DWORD wait_result = WaitForSingleObject(handle, sleep_ms); + B32 result = (wait_result == WAIT_OBJECT_0); + return result; +} + +internal void +os_semaphore_drop(OS_Handle semaphore) +{ + HANDLE handle = (HANDLE)semaphore.u64[0]; + ReleaseSemaphore(handle, 1, 0); +} + +//////////////////////////////// +//~ rjf: @os_hooks Dynamically-Loaded Libraries (Implemented Per-OS) + +internal OS_Handle +os_library_open(String8 path) +{ + Temp scratch = scratch_begin(0, 0); + String16 path16 = str16_from_8(scratch.arena, path); + HMODULE mod = LoadLibraryW((LPCWSTR)path16.str); + OS_Handle result = { (U64)mod }; + scratch_end(scratch); + return result; +} + +internal VoidProc* +os_library_load_proc(OS_Handle lib, String8 name) +{ + Temp scratch = scratch_begin(0, 0); + HMODULE mod = (HMODULE)lib.u64[0]; + name = push_str8_copy(scratch.arena, name); + VoidProc *result = (VoidProc*)GetProcAddress(mod, (LPCSTR)name.str); + scratch_end(scratch); + return result; +} + +internal void +os_library_close(OS_Handle lib) +{ + HMODULE mod = (HMODULE)lib.u64[0]; + FreeLibrary(mod); +} + +//////////////////////////////// +//~ rjf: @os_hooks Safe Calls (Implemented Per-OS) + +internal void +os_safe_call(OS_ThreadFunctionType *func, OS_ThreadFunctionType *fail_handler, void *ptr) +{ + __try + { + func(ptr); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + if(fail_handler != 0) + { + fail_handler(ptr); + } + ExitProcess(1); + } +} + +//////////////////////////////// +//~ rjf: @os_hooks GUIDs (Implemented Per-OS) + +internal OS_Guid +os_make_guid(void) +{ + OS_Guid result; MemoryZeroStruct(&result); + UUID uuid; + RPC_STATUS rpc_status = UuidCreate(&uuid); + if(rpc_status == RPC_S_OK) + { + result.data1 = uuid.Data1; + result.data2 = uuid.Data2; + result.data3 = uuid.Data3; + MemoryCopyArray(result.data4, uuid.Data4); + } + return result; +} + +//////////////////////////////// +//~ rjf: @os_hooks Entry Points (Implemented Per-OS) + +#include +#undef OS_WINDOWS // shlwapi uses its own OS_WINDOWS include inside +#include + +internal B32 win32_g_is_quiet = 0; + +internal HRESULT WINAPI +win32_dialog_callback(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam, LONG_PTR data) +{ + if(msg == TDN_HYPERLINK_CLICKED) + { + ShellExecuteW(NULL, L"open", (LPWSTR)lparam, NULL, NULL, SW_SHOWNORMAL); + } + return S_OK; +} + +internal LONG WINAPI +win32_exception_filter(EXCEPTION_POINTERS* exception_ptrs) +{ + if(win32_g_is_quiet) + { + ExitProcess(1); + } + + static volatile LONG first = 0; + if(InterlockedCompareExchange(&first, 1, 0) != 0) + { + // prevent failures in other threads to popup same message box + // this handler just shows first thread that crashes + // we are terminating afterwards anyway + for (;;) Sleep(1000); + } + + WCHAR buffer[4096] = {0}; + int buflen = 0; + + DWORD exception_code = exception_ptrs->ExceptionRecord->ExceptionCode; + buflen += wnsprintfW(buffer + buflen, ArrayCount(buffer) - buflen, L"A fatal exception (code 0x%x) occurred. The process is terminating.\n", exception_code); + + // load dbghelp dynamically just in case if it is missing + HMODULE dbghelp = LoadLibraryA("dbghelp.dll"); + if(dbghelp) + { + DWORD (WINAPI *dbg_SymSetOptions)(DWORD SymOptions); + BOOL (WINAPI *dbg_SymInitializeW)(HANDLE hProcess, PCWSTR UserSearchPath, BOOL fInvadeProcess); + BOOL (WINAPI *dbg_StackWalk64)(DWORD MachineType, HANDLE hProcess, HANDLE hThread, + LPSTACKFRAME64 StackFrame, PVOID ContextRecord, PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryRoutine, + PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine, PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine, + PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress); + PVOID (WINAPI *dbg_SymFunctionTableAccess64)(HANDLE hProcess, DWORD64 AddrBase); + DWORD64 (WINAPI *dbg_SymGetModuleBase64)(HANDLE hProcess, DWORD64 qwAddr); + BOOL (WINAPI *dbg_SymFromAddrW)(HANDLE hProcess, DWORD64 Address, PDWORD64 Displacement, PSYMBOL_INFOW Symbol); + BOOL (WINAPI *dbg_SymGetLineFromAddrW64)(HANDLE hProcess, DWORD64 dwAddr, PDWORD pdwDisplacement, PIMAGEHLP_LINEW64 Line); + BOOL (WINAPI *dbg_SymGetModuleInfoW64)(HANDLE hProcess, DWORD64 qwAddr, PIMAGEHLP_MODULEW64 ModuleInfo); + + *(FARPROC*)&dbg_SymSetOptions = GetProcAddress(dbghelp, "SymSetOptions"); + *(FARPROC*)&dbg_SymInitializeW = GetProcAddress(dbghelp, "SymInitializeW"); + *(FARPROC*)&dbg_StackWalk64 = GetProcAddress(dbghelp, "StackWalk64"); + *(FARPROC*)&dbg_SymFunctionTableAccess64 = GetProcAddress(dbghelp, "SymFunctionTableAccess64"); + *(FARPROC*)&dbg_SymGetModuleBase64 = GetProcAddress(dbghelp, "SymGetModuleBase64"); + *(FARPROC*)&dbg_SymFromAddrW = GetProcAddress(dbghelp, "SymFromAddrW"); + *(FARPROC*)&dbg_SymGetLineFromAddrW64 = GetProcAddress(dbghelp, "SymGetLineFromAddrW64"); + *(FARPROC*)&dbg_SymGetModuleInfoW64 = GetProcAddress(dbghelp, "SymGetModuleInfoW64"); + + if(dbg_SymSetOptions && dbg_SymInitializeW && dbg_StackWalk64 && dbg_SymFunctionTableAccess64 && dbg_SymGetModuleBase64 && dbg_SymFromAddrW && dbg_SymGetLineFromAddrW64 && dbg_SymGetModuleInfoW64) + { + HANDLE process = GetCurrentProcess(); + HANDLE thread = GetCurrentThread(); + CONTEXT* context = exception_ptrs->ContextRecord; + + dbg_SymSetOptions(SYMOPT_EXACT_SYMBOLS | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_LOAD_LINES | SYMOPT_UNDNAME); + if(dbg_SymInitializeW(process, L"", TRUE)) + { + // check that raddbg.pdb file is good + B32 raddbg_pdb_valid = 0; + { + IMAGEHLP_MODULEW64 module = {0}; + module.SizeOfStruct = sizeof(module); + if(dbg_SymGetModuleInfoW64(process, (DWORD64)&win32_exception_filter, &module)) + { + raddbg_pdb_valid = (module.SymType == SymPdb); + } + } + + if(!raddbg_pdb_valid) + { + buflen += wnsprintfW(buffer + buflen, sizeof(buffer) - buflen, + L"\nThe PDB debug information file for this executable is not valid or was not found. Please rebuild binary to get the call stack.\n"); + } + else + { + STACKFRAME64 frame = {0}; + DWORD image_type; +#if defined(_M_AMD64) + image_type = IMAGE_FILE_MACHINE_AMD64; + frame.AddrPC.Offset = context->Rip; + frame.AddrPC.Mode = AddrModeFlat; + frame.AddrFrame.Offset = context->Rbp; + frame.AddrFrame.Mode = AddrModeFlat; + frame.AddrStack.Offset = context->Rsp; + frame.AddrStack.Mode = AddrModeFlat; +#elif defined(_M_ARM64) + image_type = IMAGE_FILE_MACHINE_ARM64; + frame.AddrPC.Offset = context->Pc; + frame.AddrPC.Mode = AddrModeFlat; + frame.AddrFrame.Offset = context->Fp; + frame.AddrFrame.Mode = AddrModeFlat; + frame.AddrStack.Offset = context->Sp; + frame.AddrStack.Mode = AddrModeFlat; +#else +# error Architecture not supported! +#endif + + for(U32 idx=0; ;idx++) + { + const U32 max_frames = 32; + if(idx == max_frames) + { + buflen += wnsprintfW(buffer + buflen, ArrayCount(buffer) - buflen, L"..."); + break; + } + + if(!dbg_StackWalk64(image_type, process, thread, &frame, context, 0, dbg_SymFunctionTableAccess64, dbg_SymGetModuleBase64, 0)) + { + break; + } + + U64 address = frame.AddrPC.Offset; + if(address == 0) + { + break; + } + + if(idx==0) + { +#if BUILD_CONSOLE_INTERFACE + buflen += wnsprintfW(buffer + buflen, ArrayCount(buffer) - buflen, L"\nCreate a new issue with this report at %S.\n\n", BUILD_ISSUES_LINK_STRING_LITERAL); +#else + buflen += wnsprintfW(buffer + buflen, ArrayCount(buffer) - buflen, + L"\nPress Ctrl+C to copy this text to clipboard, then create a new issue at\n" + L"%S\n\n", BUILD_ISSUES_LINK_STRING_LITERAL, BUILD_ISSUES_LINK_STRING_LITERAL); +#endif + buflen += wnsprintfW(buffer + buflen, ArrayCount(buffer) - buflen, L"Call stack:\n"); + } + + buflen += wnsprintfW(buffer + buflen, ArrayCount(buffer) - buflen, L"%u. [0x%I64x]", idx + 1, address); + + struct { + SYMBOL_INFOW info; + WCHAR name[MAX_SYM_NAME]; + } symbol = {0}; + + symbol.info.SizeOfStruct = sizeof(symbol.info); + symbol.info.MaxNameLen = MAX_SYM_NAME; + + DWORD64 displacement = 0; + if(dbg_SymFromAddrW(process, address, &displacement, &symbol.info)) + { + buflen += wnsprintfW(buffer + buflen, ArrayCount(buffer) - buflen, L" %s +%u", symbol.info.Name, (DWORD)displacement); + + IMAGEHLP_LINEW64 line = {0}; + line.SizeOfStruct = sizeof(line); + + DWORD line_displacement = 0; + if(dbg_SymGetLineFromAddrW64(process, address, &line_displacement, &line)) + { + buflen += wnsprintfW(buffer + buflen, ArrayCount(buffer) - buflen, L", %s line %u", PathFindFileNameW(line.FileName), line.LineNumber); + } + } + else + { + IMAGEHLP_MODULEW64 module = {0}; + module.SizeOfStruct = sizeof(module); + if(dbg_SymGetModuleInfoW64(process, address, &module)) + { + buflen += wnsprintfW(buffer + buflen, ArrayCount(buffer) - buflen, L" %s", module.ModuleName); + } + } + + buflen += wnsprintfW(buffer + buflen, ArrayCount(buffer) - buflen, L"\n"); + } + } + } + } + } + + buflen += wnsprintfW(buffer + buflen, ArrayCount(buffer) - buflen, L"\nVersion: %S%S", BUILD_VERSION_STRING_LITERAL, BUILD_GIT_HASH_STRING_LITERAL_APPEND); + +#if BUILD_CONSOLE_INTERFACE + fwprintf(stderr, L"\n--- Fatal Exception ---\n"); + fwprintf(stderr, L"%s\n\n", buffer); +#else + TASKDIALOGCONFIG dialog = {0}; + dialog.cbSize = sizeof(dialog); + dialog.dwFlags = TDF_SIZE_TO_CONTENT | TDF_ENABLE_HYPERLINKS | TDF_ALLOW_DIALOG_CANCELLATION; + dialog.pszMainIcon = TD_ERROR_ICON; + dialog.dwCommonButtons = TDCBF_CLOSE_BUTTON; + dialog.pszWindowTitle = L"Fatal Exception"; + dialog.pszContent = buffer; + dialog.pfCallback = &win32_dialog_callback; + TaskDialogIndirect(&dialog, 0, 0, 0); +#endif + + ExitProcess(1); +} + +#undef OS_WINDOWS // shlwapi uses its own OS_WINDOWS include inside +#define OS_WINDOWS 1 + +internal void +w32_entry_point_caller(int argc, WCHAR **wargv) +{ + SetUnhandledExceptionFilter(&win32_exception_filter); + + //- rjf: do OS layer initialization + { + // rjf: dynamically load windows functions which are not guaranteed + // in all SDKs + { + HMODULE module = LoadLibraryA("kernel32.dll"); + w32_SetThreadDescription_func = (W32_SetThreadDescription_Type *)GetProcAddress(module, "SetThreadDescription"); + FreeLibrary(module); + } + + // rjf: try to enable large pages if we can + { + HANDLE token; + if(OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token)) + { + LUID luid; + if(LookupPrivilegeValue(0, SE_LOCK_MEMORY_NAME, &luid)) + { + TOKEN_PRIVILEGES priv; + priv.PrivilegeCount = 1; + priv.Privileges[0].Luid = luid; + priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; + AdjustTokenPrivileges(token, 0, &priv, sizeof(priv), 0, 0); + } + CloseHandle(token); + } + } + + // rjf: get system info + SYSTEM_INFO sysinfo = {0}; + GetSystemInfo(&sysinfo); + + // rjf: set up non-dynamically-alloc'd state + // + // (we need to set up some basics before this layer can supply + // memory allocation primitives) + { + os_w32_state.microsecond_resolution = 1; + LARGE_INTEGER large_int_resolution; + if(QueryPerformanceFrequency(&large_int_resolution)) + { + os_w32_state.microsecond_resolution = large_int_resolution.QuadPart; + } + } + { + OS_SystemInfo *info = &os_w32_state.system_info; + info->logical_processor_count = (U64)sysinfo.dwNumberOfProcessors; + info->page_size = sysinfo.dwPageSize; + info->large_page_size = GetLargePageMinimum(); + info->allocation_granularity = sysinfo.dwAllocationGranularity; + } + { + OS_ProcessInfo *info = &os_w32_state.process_info; + info->pid = GetCurrentProcessId(); + } + + // rjf: set up thread context + local_persist TCTX tctx; + tctx_init_and_equip(&tctx); + + // rjf: set up dynamically-alloc'd state + Arena *arena = arena_alloc(); + { + os_w32_state.arena = arena; + { + OS_SystemInfo *info = &os_w32_state.system_info; + U8 buffer[MAX_COMPUTERNAME_LENGTH + 1] = {0}; + DWORD size = MAX_COMPUTERNAME_LENGTH + 1; + if(GetComputerNameA((char*)buffer, &size)) + { + info->machine_name = push_str8_copy(arena, str8(buffer, size)); + } + } + } + { + OS_ProcessInfo *info = &os_w32_state.process_info; + { + Temp scratch = scratch_begin(0, 0); + DWORD size = KB(32); + U16 *buffer = push_array_no_zero(scratch.arena, U16, size); + DWORD length = GetModuleFileNameW(0, (WCHAR*)buffer, size); + String8 name8 = str8_from_16(scratch.arena, str16(buffer, length)); + String8 name_chopped = str8_chop_last_slash(name8); + info->binary_path = push_str8_copy(arena, name_chopped); + scratch_end(scratch); + } + info->initial_path = os_get_current_path(arena); + { + Temp scratch = scratch_begin(0, 0); + U64 size = KB(32); + U16 *buffer = push_array_no_zero(scratch.arena, U16, size); + if(SUCCEEDED(SHGetFolderPathW(0, CSIDL_APPDATA, 0, 0, (WCHAR*)buffer))) + { + info->user_program_data_path = str8_from_16(arena, str16_cstring(buffer)); + } + scratch_end(scratch); + } + { + WCHAR *this_proc_env = GetEnvironmentStringsW(); + U64 start_idx = 0; + for(U64 idx = 0;; idx += 1) + { + if(this_proc_env[idx] == 0) + { + if(start_idx == idx) + { + break; + } + else + { + String16 string16 = str16((U16 *)this_proc_env + start_idx, idx - start_idx); + String8 string = str8_from_16(arena, string16); + str8_list_push(arena, &info->environment, string); + start_idx = idx+1; + } + } + } + } + } + + // rjf: set up entity storage + InitializeCriticalSection(&os_w32_state.entity_mutex); + os_w32_state.entity_arena = arena_alloc(); + } + + //- rjf: extract arguments + Arena *args_arena = arena_alloc(.reserve_size = MB(1), .commit_size = KB(32)); + char **argv = push_array(args_arena, char *, argc); + for(int i = 0; i < argc; i += 1) + { + String16 arg16 = str16_cstring((U16 *)wargv[i]); + String8 arg8 = str8_from_16(args_arena, arg16); + if(str8_match(arg8, str8_lit("--quiet"), StringMatchFlag_CaseInsensitive)) + { + win32_g_is_quiet = 1; + } + argv[i] = (char *)arg8.str; + } + + //- rjf: call into "real" entry point + main_thread_base_entry_point(entry_point, argv, (U64)argc); +} + +#if BUILD_CONSOLE_INTERFACE +int wmain(int argc, WCHAR **argv) +{ + w32_entry_point_caller(argc, argv); + return 0; +} +#else +int wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nShowCmd) +{ + w32_entry_point_caller(__argc, __wargv); + return 0; +} +#endif diff --git a/src/metagen/metagen_os/core/win32/metagen_os_core_win32.h b/src/metagen/metagen_os/core/win32/metagen_os_core_win32.h index 3ce6aac4..a8c031fd 100644 --- a/src/metagen/metagen_os/core/win32/metagen_os_core_win32.h +++ b/src/metagen/metagen_os/core/win32/metagen_os_core_win32.h @@ -1,91 +1,122 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#ifndef WIN32_H -#define WIN32_H - -//////////////////////////////// -//~ NOTE(allen): Negotiate the windows header include order - -#if OS_FEATURE_SOCKET -#include -#endif - -#include -#include - -#if OS_FEATURE_GRAPHICAL -#include -#endif - -#if OS_FEATURE_SOCKET -#include -#include -#endif - -#include - -//////////////////////////////// -//~ NOTE(allen): File Iterator - -typedef struct W32_FileIter W32_FileIter; -struct W32_FileIter -{ - HANDLE handle; - WIN32_FIND_DATAW find_data; -}; -StaticAssert(sizeof(Member(OS_FileIter, memory)) >= sizeof(W32_FileIter), file_iter_memory_size); - -//////////////////////////////// -//~ NOTE(allen): Threading Entities - -typedef enum W32_EntityKind -{ - W32_EntityKind_Null, - W32_EntityKind_Thread, - W32_EntityKind_Mutex, - W32_EntityKind_RWMutex, - W32_EntityKind_ConditionVariable, -} -W32_EntityKind; - -typedef struct W32_Entity W32_Entity; -struct W32_Entity -{ - W32_Entity *next; - W32_EntityKind kind; - volatile U32 reference_mask; - union{ - struct{ - OS_ThreadFunctionType *func; - void *ptr; - HANDLE handle; - DWORD tid; - } thread; - CRITICAL_SECTION mutex; - SRWLOCK rw_mutex; - CONDITION_VARIABLE cv; - }; -}; - -//////////////////////////////// -//~ rjf: Helpers - -//- rjf: files -internal FilePropertyFlags w32_file_property_flags_from_dwFileAttributes(DWORD dwFileAttributes); -internal void w32_file_properties_from_attributes(FileProperties *properties, WIN32_FILE_ATTRIBUTE_DATA *attributes); - -//- rjf: time -internal void w32_date_time_from_system_time(DateTime *out, SYSTEMTIME *in); -internal void w32_system_time_from_date_time(SYSTEMTIME *out, DateTime *in); -internal void w32_dense_time_from_file_time(DenseTime *out, FILETIME *in); -internal U32 w32_sleep_ms_from_endt_us(U64 endt_us); - -//- rjf: entities -internal W32_Entity* w32_alloc_entity(W32_EntityKind kind); -internal void w32_free_entity(W32_Entity *entity); - -//- rjf: threads -internal DWORD w32_thread_base(void *ptr); - -#endif //WIN32_H +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef OS_CORE_WIN32_H +#define OS_CORE_WIN32_H + +//////////////////////////////// +//~ rjf: Includes / Libraries + +#define WIN32_LEAN_AND_MEAN +#include +#include +#include +#include +#include +#include +#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 + +//////////////////////////////// +//~ 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; +}; +StaticAssert(sizeof(Member(OS_FileIter, memory)) >= sizeof(OS_W32_FileIter), file_iter_memory_size); + +//////////////////////////////// +//~ rjf: Entity Types + +typedef enum OS_W32_EntityKind +{ + OS_W32_EntityKind_Null, + OS_W32_EntityKind_Thread, + OS_W32_EntityKind_Mutex, + OS_W32_EntityKind_RWMutex, + OS_W32_EntityKind_ConditionVariable, +} +OS_W32_EntityKind; + +typedef struct OS_W32_Entity OS_W32_Entity; +struct OS_W32_Entity +{ + OS_W32_Entity *next; + OS_W32_EntityKind kind; + union + { + struct + { + OS_ThreadFunctionType *func; + void *ptr; + HANDLE handle; + DWORD tid; + } 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 + +global OS_W32_State os_w32_state = {0}; + +//////////////////////////////// +//~ rjf: File Info Conversion Helpers + +internal FilePropertyFlags os_w32_file_property_flags_from_dwFileAttributes(DWORD dwFileAttributes); +internal void os_w32_file_properties_from_attribute_data(FileProperties *properties, WIN32_FILE_ATTRIBUTE_DATA *attributes); + +//////////////////////////////// +//~ rjf: Time Conversion Helpers + +internal void os_w32_date_time_from_system_time(DateTime *out, SYSTEMTIME *in); +internal void os_w32_system_time_from_date_time(SYSTEMTIME *out, DateTime *in); +internal void os_w32_dense_time_from_file_time(DenseTime *out, FILETIME *in); +internal U32 os_w32_sleep_ms_from_endt_us(U64 endt_us); + +//////////////////////////////// +//~ rjf: Entity Functions + +internal OS_W32_Entity *os_w32_entity_alloc(OS_W32_EntityKind kind); +internal void os_w32_entity_release(OS_W32_Entity *entity); + +//////////////////////////////// +//~ rjf: Thread Entry Point + +internal DWORD os_w32_thread_entry_point(void *ptr); + +#endif // OS_CORE_WIN32_H diff --git a/src/metagen/metagen_os/metagen_os_inc.c b/src/metagen/metagen_os/metagen_os_inc.c index ffa847ef..70bb1565 100644 --- a/src/metagen/metagen_os/metagen_os_inc.c +++ b/src/metagen/metagen_os/metagen_os_inc.c @@ -1,29 +1,12 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -//////////////////////////////// -// NOTE(allen): Include OS features for extra features and target OS - -#include "core/metagen_os_core.c" - -#if OS_FEATURE_SOCKET -#include "socket/metagen_os_socket.c" -#endif - -#if OS_FEATURE_GRAPHICAL -#include "gfx/metagen_os_gfx.c" -#endif - -#if OS_WINDOWS -# include "core/win32/metagen_os_core_win32.c" -# if OS_FEATURE_SOCKET -# include "socket/win32/metagen_os_socket_win32.c" -# endif -# if OS_FEATURE_GRAPHICAL -# include "gfx/win32/metagen_os_gfx_win32.c" -# endif -#elif OS_LINUX -# include "core/linux/metagen_os_core_linux.c" -#else -# error no OS layer setup -#endif +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#include "metagen/metagen_os/core/metagen_os_core.c" + +#if OS_WINDOWS +# include "metagen/metagen_os/core/win32/metagen_os_core_win32.c" +#elif OS_LINUX +# include "metagen/metagen_os/core/linux/metagen_os_core_linux.c" +#else +# error OS core layer not implemented for this operating system. +#endif diff --git a/src/metagen/metagen_os/metagen_os_inc.h b/src/metagen/metagen_os/metagen_os_inc.h index e53f926c..f8c8e06c 100644 --- a/src/metagen/metagen_os/metagen_os_inc.h +++ b/src/metagen/metagen_os/metagen_os_inc.h @@ -4,36 +4,22 @@ #ifndef OS_INC_H #define OS_INC_H -#if !defined(OS_FEATURE_SOCKET) -# define OS_FEATURE_SOCKET 0 -#endif - #if !defined(OS_FEATURE_GRAPHICAL) # define OS_FEATURE_GRAPHICAL 0 #endif -#include "core/metagen_os_core.h" - -#if OS_FEATURE_SOCKET -#include "socket/metagen_os_socket.h" +#if !defined(OS_GFX_STUB) +# define OS_GFX_STUB 0 #endif -#if OS_FEATURE_GRAPHICAL -#include "gfx/metagen_os_gfx.h" -#endif +#include "metagen/metagen_os/core/metagen_os_core.h" #if OS_WINDOWS -# include "core/win32/metagen_os_core_win32.h" -# if OS_FEATURE_SOCKET -# include "socket/win32/metagen_os_socket_win32.h" -# endif -# if OS_FEATURE_GRAPHICAL -# include "gfx/win32/metagen_os_gfx_win32.h" -# endif +# include "metagen/metagen_os/core/win32/metagen_os_core_win32.h" #elif OS_LINUX -# include "core/linux/metagen_os_core_linux.h" +# include "metagen/metagen_os/core/linux/metagen_os_core_linux.h" #else -# error no OS layer setup +# error OS core layer not implemented for this operating system. #endif #endif // OS_INC_H