mr4th/src/base/base_big_functions.c

1404 lines
32 KiB
C

////////////////////////////////
// NOTE(allen): Static Configuration
#if !defined(MEM_DEFAULT_RESERVE_SIZE)
# define MEM_DEFAULT_RESERVE_SIZE GB(1)
#endif
#if !defined(MEM_COMMIT_BLOCK_SIZE)
# define MEM_COMMIT_BLOCK_SIZE MB(64)
#endif
#if !defined(MEM_MAX_ALIGN)
# define MEM_MAX_ALIGN 64
#endif
#if !defined(MEM_SCRATCH_POOL_COUNT)
# define MEM_SCRATCH_POOL_COUNT 2
#endif
////////////////////////////////
// NOTE(allen): Symbolic Constant Functions
link_function OperatingSystem
operating_system_from_context(void){
OperatingSystem result = OperatingSystem_Null;
#if OS_WINDOWS
result = OperatingSystem_Windows;
#elif OS_LINUX
result = OperatingSystem_Linux;
#elif OS_MAC
result = OperatingSystem_Mac;
#endif
return(result);
}
link_function Architecture
architecture_from_context(void){
Architecture result = Architecture_Null;
#if ARCH_X64
result = Architecture_X64;
#elif ARCH_X86
result = Architecture_X86;
#elif ARCH_ARM
result = Architecture_Arm;
#elif ARCH_ARM64
result = Architecture_Arm64;
#endif
return(result);
}
link_function char*
string_from_operating_system(OperatingSystem os){
char *result = "(null)";
switch (os){
case OperatingSystem_Windows:
{
result = "windows";
}break;
case OperatingSystem_Linux:
{
result = "linux";
}break;
case OperatingSystem_Mac:
{
result = "mac";
}break;
}
return(result);
}
link_function char*
string_from_architecture(Architecture arch){
char *result = "(null)";
switch (arch){
case Architecture_X64:
{
result = "x64";
}break;
case Architecture_X86:
{
result = "x86";
}break;
case Architecture_Arm:
{
result = "arm";
}break;
case Architecture_Arm64:
{
result = "armm64";
}break;
}
return(result);
}
link_function char*
string_from_month(Month month){
char *result = "(null)";
switch (month){
case Month_Jan:
{
result = "jan";
}break;
case Month_Feb:
{
result = "feb";
}break;
case Month_Mar:
{
result = "mar";
}break;
case Month_Apr:
{
result = "apr";
}break;
case Month_May:
{
result = "may";
}break;
case Month_Jun:
{
result = "jun";
}break;
case Month_Jul:
{
result = "jul";
}break;
case Month_Aug:
{
result = "aug";
}break;
case Month_Sep:
{
result = "sep";
}break;
case Month_Oct:
{
result = "oct";
}break;
case Month_Nov:
{
result = "nov";
}break;
case Month_Dec:
{
result = "dec";
}break;
}
return(result);
}
link_function char*
string_from_day_of_week(DayOfWeek day_of_week){
char *result = "(null)";
switch (day_of_week){
case DayOfWeek_Sunday:
{
result = "sunday";
}break;
case DayOfWeek_Monday:
{
result = "monday";
}break;
case DayOfWeek_Tuesday:
{
result = "tuesday";
}break;
case DayOfWeek_Wednesday:
{
result = "wednesday";
}break;
case DayOfWeek_Thursday:
{
result = "thursday";
}break;
case DayOfWeek_Friday:
{
result = "friday";
}break;
case DayOfWeek_Saturday:
{
result = "saturday";
}break;
}
return(result);
}
////////////////////////////////
// NOTE(allen): Time Functions
link_function DenseTime
dense_time_from_date_time(DateTime *in){
U32 year_encoded = (U32)((S32)in->year + 0x8000);
DenseTime result = 0;
result += year_encoded;
result *= 12;
result += (in->mon - 1);
result *= 31;
result += (in->day - 1);
result *= 24;
result += in->hour;
result *= 60;
result += in->min;
result *= 61;
result += in->sec;
result *= 1000;
result += in->msec;
return(result);
}
link_function DateTime
date_time_from_dense_time(DenseTime in){
DateTime result = {0};
result.msec = in%1000;
in /= 1000;
result.sec = in%61;
in /= 61;
result.min = in%60;
in /= 60;
result.hour = in%24;
in /= 24;
result.day = (in%31) + 1;
in /= 31;
result.mon = (in%12) + 1;
in /= 12;
S32 year_encoded = (S32)in;
result.year = (year_encoded - 0x8000);
return(result);
}
////////////////////////////////
// NOTE(allen): Memory Functions
#define MEM_INITIAL_COMMIT KB(4)
#define MEM_INTERNAL_MIN_SIZE AlignUpPow2(sizeof(Arena), MEM_MAX_ALIGN)
StaticAssert(sizeof(Arena) <= MEM_INITIAL_COMMIT,
mem_check_arena_size);
StaticAssert(IsPow2OrZero(MEM_COMMIT_BLOCK_SIZE) &&
MEM_COMMIT_BLOCK_SIZE != 0,
mem_check_commit_block_size);
StaticAssert(IsPow2OrZero(MEM_MAX_ALIGN) && MEM_MAX_ALIGN != 0,
mem_check_max_align);
link_function Arena*
arena_alloc_reserve(U64 reserve_size, B32 growing){
ProfBeginFunc();
Arena *result = 0;
if (reserve_size >= MEM_INITIAL_COMMIT){
void *memory = mem_reserve(reserve_size);
if (mem_commit(memory, MEM_INITIAL_COMMIT)){
AsanPoison(memory, reserve_size);
AsanUnpoison(memory, MEM_INTERNAL_MIN_SIZE);
result = (Arena*)memory;
result->current = result;
result->prev = 0;
result->alignment = sizeof(void*);
result->growing = growing;
result->base_pos = 0;
result->chunk_cap = reserve_size;
result->chunk_pos = MEM_INTERNAL_MIN_SIZE;
result->chunk_commit_pos = MEM_INITIAL_COMMIT;
}
}
Assert(result != 0);
ProfEndFunc();
return(result);
}
link_function Arena*
arena_alloc(void){
Arena *result = arena_alloc_reserve(MEM_DEFAULT_RESERVE_SIZE, 1);
return(result);
}
link_function void
arena_release(Arena *arena){
ProfBeginFunc();
Arena *ptr = arena->current;
for (;ptr != 0;){
Arena *prev = ptr->prev;
AsanPoison(ptr, ptr->chunk_cap);
mem_release(ptr, ptr->chunk_cap);
ptr = prev;
}
ProfEndFunc();
}
link_function void*
arena_push_no_zero(Arena *arena, U64 size){
ProfBeginFunc();
void *result = 0;
Arena *current = arena->current;
// allocate new chunk if necessary
if (arena->growing){
U64 next_chunk_pos = AlignUpPow2(current->chunk_pos, arena->alignment);
next_chunk_pos += size;
if (next_chunk_pos > current->chunk_cap){
U64 new_reserve_size = MEM_DEFAULT_RESERVE_SIZE;
U64 enough_to_fit = size + MEM_INTERNAL_MIN_SIZE;
if (new_reserve_size < enough_to_fit){
new_reserve_size = AlignUpPow2(enough_to_fit, KB(4));
}
void *memory = mem_reserve(new_reserve_size);
if (mem_commit(memory, MEM_INITIAL_COMMIT)){
AsanPoison(memory, new_reserve_size);
AsanUnpoison(memory, MEM_INTERNAL_MIN_SIZE);
Arena *new_chunk = (Arena*)memory;
new_chunk->prev = current;
new_chunk->base_pos = current->base_pos + current->chunk_cap;
new_chunk->chunk_cap = new_reserve_size;
new_chunk->chunk_pos = MEM_INTERNAL_MIN_SIZE;
new_chunk->chunk_commit_pos = MEM_INITIAL_COMMIT;
current = arena->current = new_chunk;
}
}
}
{
// if there is room in this chunk's reserve ...
U64 result_pos = AlignUpPow2(current->chunk_pos, arena->alignment);
U64 next_chunk_pos = result_pos + size;
if (next_chunk_pos <= current->chunk_cap){
// commit more memory if necessary
if (next_chunk_pos > current->chunk_commit_pos){
U64 next_commit_pos_aligned =
AlignUpPow2(next_chunk_pos, MEM_COMMIT_BLOCK_SIZE);
U64 next_commit_pos =
ClampTop(next_commit_pos_aligned, current->chunk_cap);
U64 commit_size = next_commit_pos - current->chunk_commit_pos;
if (mem_commit((U8*)current + current->chunk_commit_pos, commit_size)){
arena->chunk_commit_pos = next_commit_pos;
}
}
// if there is room in the commit range, return memory & advance pos
if (next_chunk_pos <= current->chunk_commit_pos){
AsanUnpoison((U8*)current + current->chunk_pos,
next_chunk_pos - current->chunk_pos);
result = (U8*)current + result_pos;
current->chunk_pos = next_chunk_pos;
}
}
}
ProfEndFunc();
return(result);
}
link_function void
arena_pop_to(Arena *arena, U64 pos){
ProfBeginFunc();
Arena *current = arena->current;
U64 total_pos = current->base_pos + current->chunk_pos;
if (pos < total_pos){
// release all chunks that begin after this pos
U64 clamped_total_pos = ClampBot(pos, MEM_INTERNAL_MIN_SIZE);
for (; clamped_total_pos < current->base_pos; ){
Arena *prev = current->prev;
AsanPoison(current, current->chunk_cap);
mem_release(current, current->chunk_cap);
current = prev;
}
// update arena's current
{
arena->current = current;
}
// update the chunk position of the chunk in
// the chain that contains this position.
{
U64 chunk_pos = clamped_total_pos - current->base_pos;
U64 clamped_chunk_pos = ClampBot(chunk_pos, MEM_INTERNAL_MIN_SIZE);
AsanPoison((U8*)current + clamped_chunk_pos,
current->chunk_pos - clamped_chunk_pos);
current->chunk_pos = clamped_chunk_pos;
}
}
ProfEndFunc();
}
link_function U64
arena_current_pos(Arena *arena){
Arena *current = arena->current;
U64 result = current->base_pos + current->chunk_pos;
return(result);
}
link_function void*
arena_push(Arena *arena, U64 size){
void *result = arena_push_no_zero(arena, size);
MemoryZero(result, size);
return(result);
}
link_function void
arena_align(Arena *arena, U64 pow2_align){
Assert(IsPow2OrZero(pow2_align) && pow2_align != 0 &&
pow2_align <= MEM_MAX_ALIGN);
Arena *current = arena->current;
U64 p = current->chunk_pos;
U64 p_aligned = AlignUpPow2(p, pow2_align);
U64 z = p_aligned - p;
if (z > 0){
arena_push(arena, z);
}
}
link_function void
arena_pop_amount(Arena *arena, U64 amount){
Arena *current = arena->current;
U64 total_pos = current->base_pos + current->chunk_pos;
if (amount <= total_pos){
U64 new_pos = total_pos - amount;
arena_pop_to(arena, new_pos);
}
}
////////////////////////////////
// NOTE(allen): Temp Helper Functions
link_function ArenaTemp
arena_begin_temp(Arena *arena){
U64 pos = arena_current_pos(arena);
ArenaTemp temp = {arena, pos};
return(temp);
}
link_function void
arena_end_temp(ArenaTemp *temp){
arena_pop_to(temp->arena, temp->pos);
}
////////////////////////////////
// NOTE(allen): Scratch
threadvar Arena *m__scratch_pool[MEM_SCRATCH_POOL_COUNT] = {0};
link_function ArenaTemp
arena_get_scratch(Arena **conflict_array, U32 count){
ProfBeginFunc();
// init on first time
if (m__scratch_pool[0] == 0){
Arena **scratch_slot = m__scratch_pool;
for (U64 i = 0;
i < MEM_SCRATCH_POOL_COUNT;
i += 1, scratch_slot += 1){
*scratch_slot = arena_alloc();
}
}
// get non-conflicting arena
ArenaTemp result = {0};
Arena **scratch_slot = m__scratch_pool;
for (U64 i = 0;
i < MEM_SCRATCH_POOL_COUNT;
i += 1, scratch_slot += 1){
B32 is_non_conflict = 1;
Arena **conflict_ptr = conflict_array;
for (U32 j = 0; j < count; j += 1, conflict_ptr += 1){
if (*scratch_slot == *conflict_ptr){
is_non_conflict = 0;
break;
}
}
if (is_non_conflict){
result = arena_begin_temp(*scratch_slot);
break;
}
}
ProfEndFunc();
return(result);
}
////////////////////////////////
// NOTE(allen): String Compound Constructor Functions
link_function void
str8_list_push_explicit(String8List *list, String8 string,
String8Node *node_memory){
node_memory->string = string;
SLLQueuePush(list->first, list->last, node_memory);
list->node_count += 1;
list->total_size += string.size;
}
link_function void
str8_list_push(Arena *arena, String8List *list, String8 string){
String8Node *node = push_array(arena, String8Node, 1);
str8_list_push_explicit(list, string, node);
}
link_function String8
str8_join(Arena *arena, String8List *list,
StringJoin *join_optional){
ProfBeginFunc();
// setup join parameters
local StringJoin dummy_join = {0};
StringJoin *join = join_optional;
if (join == 0){
join = &dummy_join;
}
// compute total size
U64 size = (join->pre.size +
join->post.size +
join->mid.size*(list->node_count - 1) +
list->total_size);
// begin string build
U8 *str = push_array(arena, U8, size + 1);
U8 *ptr = str;
// write pre
MemoryCopy(ptr, join->pre.str, join->pre.size);
ptr += join->pre.size;
B32 is_mid = 0;
for (String8Node *node = list->first;
node != 0;
node = node->next){
// write mid
if (is_mid){
MemoryCopy(ptr, join->mid.str, join->mid.size);
ptr += join->mid.size;
}
// write node string
MemoryCopy(ptr, node->string.str, node->string.size);
ptr += node->string.size;
is_mid = 1;
}
// write post
MemoryCopy(ptr, join->post.str, join->post.size);
ptr += join->post.size;
// write null
*ptr = 0;
String8 result = str8(str, size);
ProfEndFunc();
return(result);
}
link_function String8List
str8_split(Arena *arena, String8 string, U8 *splits, U32 count){
ProfBeginFunc();
String8List result = {0};
U8 *ptr = string.str;
U8 *word_first = ptr;
U8 *opl = string.str + string.size;
for (;ptr < opl; ptr += 1){
// is this a split
U8 byte = *ptr;
B32 is_split_byte = 0;
for (U32 i = 0; i < count; i += 1){
if (byte == splits[i]){
is_split_byte = 1;
break;
}
}
if (is_split_byte){
// try to emit word, advance word first pointer
if (word_first < ptr){
str8_list_push(arena, &result, str8_range(word_first, ptr));
}
word_first = ptr + 1;
}
}
// try to emit final word
if (word_first < ptr){
str8_list_push(arena, &result, str8_range(word_first, ptr));
}
ProfEndFunc();
return(result);
}
// TODO(allen): where do I want this to live??
#include <stdio.h>
link_function String8
str8_pushfv(Arena *arena, char *fmt, va_list args){
ProfBeginFunc();
// in case we need to try a second time
va_list args2;
va_copy(args2, args);
// try to build the string in 1024 bytes
U64 buffer_size = 1024;
U8 *buffer = push_array(arena, U8, buffer_size);
U64 actual_size = vsnprintf((char*)buffer, buffer_size, fmt, args);
String8 result = {0};
if (actual_size < buffer_size){
// if first try worked, put back what we didn't use and finish
arena_pop_amount(arena, buffer_size - actual_size - 1);
result = str8(buffer, actual_size);
}
else{
// if first try failed, reset and try again with correct size
arena_pop_amount(arena, buffer_size);
U8 *fixed_buffer = push_array(arena, U8, actual_size + 1);
U64 final_size = vsnprintf((char*)fixed_buffer, actual_size + 1, fmt, args2);
result = str8(fixed_buffer, final_size);
}
// end args2
va_end(args2);
ProfEndFunc();
return(result);
}
link_function String8
str8_pushf(Arena *arena, char *fmt, ...){
va_list args;
va_start(args, fmt);
String8 result = str8_pushfv(arena, fmt, args);
va_end(args);
return(result);
}
link_function void
str8_list_pushf(Arena *arena, String8List *list, char *fmt, ...){
va_list args;
va_start(args, fmt);
String8 string = str8_pushfv(arena, fmt, args);
va_end(args);
str8_list_push(arena, list, string);
}
link_function String8
str8_push_copy(Arena *arena, String8 string){
String8 result = {0};
result.str = push_array(arena, U8, string.size + 1);
result.size = string.size;
MemoryCopy(result.str, string.str, string.size);
result.str[result.size] = 0;
return(result);
}
////////////////////////////////
// NOTE(allen): String Comparison Functions
link_function B32
str8_match(String8 a, String8 b, StringMatchFlags flags){
ProfBeginFunc();
B32 result = 0;
if (a.size == b.size){
result = 1;
B32 no_case = ((flags & StringMatchFlag_NoCase) != 0);
for (U64 i = 0; i < a.size; i += 1){
U8 ac = a.str[i];
U8 bc = b.str[i];
if (no_case){
ac = str8_char_uppercase(ac);
bc = str8_char_uppercase(bc);
}
if (ac != bc){
result = 0;
break;
}
}
}
ProfEndFunc();
return(result);
}
////////////////////////////////
// NOTE(allen): Unicode Functions
link_function StringDecode
str_decode_utf8(U8 *str, U32 cap){
ProfBeginFunc();
local U8 length[] = {
1, 1, 1, 1, // 000xx
1, 1, 1, 1,
1, 1, 1, 1,
1, 1, 1, 1,
0, 0, 0, 0, // 100xx
0, 0, 0, 0,
2, 2, 2, 2, // 110xx
3, 3, // 1110x
4, // 11110
0, // 11111
};
local U8 first_byte_mask[] = { 0, 0x7F, 0x1F, 0x0F, 0x07 };
local U8 final_shift[] = { 0, 18, 12, 6, 0 };
StringDecode result = {0};
if (cap > 0){
result.codepoint = '#';
result.size = 1;
U8 byte = str[0];
U8 l = length[byte >> 3];
if (0 < l && l <= cap){
U32 cp = (byte & first_byte_mask[l]) << 18;
switch (l){
case 4: cp |= ((str[3] & 0x3F) << 0);
case 3: cp |= ((str[2] & 0x3F) << 6);
case 2: cp |= ((str[1] & 0x3F) << 12);
default: break;
}
cp >>= final_shift[l];
result.codepoint = cp;
result.size = l;
}
}
ProfEndFunc();
return(result);
}
link_function U32
str_encode_utf8(U8 *dst, U32 codepoint){
ProfBeginFunc();
U32 size = 0;
if (codepoint < (1 << 8)){
dst[0] = codepoint;
size = 1;
}
else if (codepoint < (1 << 11)){
dst[0] = 0xC0 | (codepoint >> 6);
dst[1] = 0x80 | (codepoint & 0x3F);
size = 2;
}
else if (codepoint < (1 << 16)){
dst[0] = 0xE0 | (codepoint >> 12);
dst[1] = 0x80 | ((codepoint >> 6) & 0x3F);
dst[2] = 0x80 | (codepoint & 0x3F);
size = 3;
}
else if (codepoint < (1 << 21)){
dst[0] = 0xF0 | (codepoint >> 18);
dst[1] = 0x80 | ((codepoint >> 12) & 0x3F);
dst[2] = 0x80 | ((codepoint >> 6) & 0x3F);
dst[3] = 0x80 | (codepoint & 0x3F);
size = 4;
}
else{
dst[0] = '#';
size = 1;
}
ProfEndFunc();
return(size);
}
link_function StringDecode
str_decode_utf16(U16 *str, U32 cap){
ProfBeginFunc();
StringDecode result = {'#', 1};
U16 x = str[0];
if (x < 0xD800 || 0xDFFF < x){
result.codepoint = x;
}
else if (cap >= 2){
U16 y = str[1];
if (0xD800 <= x && x < 0xDC00 &&
0xDC00 <= y && y < 0xE000){
U16 xj = x - 0xD800;
U16 yj = y - 0xDc00;
U32 xy = (xj << 10) | yj;
result.codepoint = xy + 0x10000;
result.size = 2;
}
}
ProfEndFunc();
return(result);
}
link_function U32
str_encode_utf16(U16 *dst, U32 codepoint){
ProfBeginFunc();
U32 size = 0;
if (codepoint < 0x10000){
dst[0] = codepoint;
size = 1;
}
else{
U32 cpj = codepoint - 0x10000;
dst[0] = (cpj >> 10) + 0xD800;
dst[1] = (cpj & 0x3FF) + 0xDC00;
size = 2;
}
ProfEndFunc();
return(size);
}
link_function String32
str32_from_str8(Arena *arena, String8 string){
ProfBeginFunc();
U32 *memory = push_array(arena, U32, string.size + 1);
U32 *dptr = memory;
U8 *ptr = string.str;
U8 *opl = string.str + string.size;
for (; ptr < opl;){
StringDecode decode = str_decode_utf8(ptr, (U64)(opl - ptr));
*dptr = decode.codepoint;
ptr += decode.size;
dptr += 1;
}
*dptr = 0;
U64 alloc_count = string.size + 1;
U64 string_count = (U64)(dptr - memory);
U64 unused_count = alloc_count - string_count - 1;
arena_pop_amount(arena, unused_count*sizeof(*memory));
String32 result = {memory, string_count};
ProfEndFunc();
return(result);
}
link_function String8
str8_from_str32(Arena *arena, String32 string){
ProfBeginFunc();
U8 *memory = push_array(arena, U8, string.size*4 + 1);
U8 *dptr = memory;
U32 *ptr = string.str;
U32 *opl = string.str + string.size;
for (; ptr < opl;){
U32 size = str_encode_utf8(dptr, *ptr);
ptr += 1;
dptr += size;
}
*dptr = 0;
U64 alloc_count = string.size*4 + 1;
U64 string_count = (U64)(dptr - memory);
U64 unused_count = alloc_count - string_count - 1;
arena_pop_amount(arena, unused_count*sizeof(*memory));
String8 result = {memory, string_count};
ProfEndFunc();
return(result);
}
link_function String16
str16_from_str8(Arena *arena, String8 string){
ProfBeginFunc();
U16 *memory = push_array(arena, U16, string.size*2 + 1);
U16 *dptr = memory;
U8 *ptr = string.str;
U8 *opl = string.str + string.size;
for (; ptr < opl;){
StringDecode decode = str_decode_utf8(ptr, (U64)(opl - ptr));
U32 enc_size = str_encode_utf16(dptr, decode.codepoint);
ptr += decode.size;
dptr += enc_size;
}
*dptr = 0;
U64 alloc_count = string.size*2 + 1;
U64 string_count = (U64)(dptr - memory);
U64 unused_count = alloc_count - string_count - 1;
arena_pop_amount(arena, unused_count*sizeof(*memory));
String16 result = {memory, string_count};
ProfEndFunc();
return(result);
}
link_function String8
str8_from_str16(Arena *arena, String16 string){
ProfBeginFunc();
U8 *memory = push_array(arena, U8, string.size*3 + 1);
U8 *dptr = memory;
U16 *ptr = string.str;
U16 *opl = string.str + string.size;
for (; ptr < opl;){
StringDecode decode = str_decode_utf16(ptr, (U64)(opl - ptr));
U16 enc_size = str_encode_utf8(dptr, decode.codepoint);
ptr += decode.size;
dptr += enc_size;
}
*dptr = 0;
U64 alloc_count = string.size*3 + 1;
U64 string_count = (U64)(dptr - memory);
U64 unused_count = alloc_count - string_count - 1;
arena_pop_amount(arena, unused_count*sizeof(*memory));
String8 result = {memory, string_count};
ProfEndFunc();
return(result);
}
////////////////////////////////
// NOTE(allen): Log
threadvar LOG_ThreadVars *log_vars = 0;
link_function void
log_accum_begin(void){
LOG_ThreadVars *vars = log_vars;
if (vars == 0){
Arena *arena = arena_alloc();
vars = log_vars = push_array(arena, LOG_ThreadVars, 1);
vars->arena = arena;
}
U64 pos = arena_current_pos(vars->arena);
LOG_Node *node = push_array(vars->arena, LOG_Node, 1);
if (node == 0){
vars->over_stack += 1;
}
else{
node->pos = pos;
SLLStackPush(vars->stack, node);
}
}
link_function void
log_emit(String8 message){
LOG_ThreadVars *vars = log_vars;
if (vars != 0 &&
vars->over_stack == 0){
LOG_Node *node = vars->stack;
if (node != 0){
String8 msg_copy = str8_push_copy(vars->arena, message);
str8_list_push(vars->arena, &node->log, msg_copy);
}
}
}
link_function void
log_emitf(char *fmt, ...){
LOG_ThreadVars *vars = log_vars;
if (vars != 0 &&
vars->over_stack == 0){
LOG_Node *node = vars->stack;
if (node != 0){
va_list args;
va_start(args, fmt);
String8 string = str8_pushfv(vars->arena, fmt, args);
va_end(args);
str8_list_push(vars->arena, &node->log, string);
}
}
}
link_function String8
log_accum_end(Arena *arena){
String8 result = {0};
LOG_ThreadVars *vars = log_vars;
if (vars != 0){
if (vars->over_stack == 0){
LOG_Node *node = vars->stack;
if (node != 0){
result = str8_join(arena, &node->log, 0);
SLLStackPop(vars->stack);
arena_pop_to(vars->arena, node->pos);
}
}
else{
vars->over_stack -= 1;
}
}
return(result);
}
////////////////////////////////
// NOTE(allen): Errors
// IMPORTANT: It is important that strings get pushed
// *on top* of the node that holds them within the arena.
// basically: Assert((U8*)node < (U8*)node->error.str);
// except that stops working if the arena is chained...
threadvar ER_ThreadVars *er_vars = 0;
link_function void
er_accum_begin(void){
ER_ThreadVars *vars = er_vars;
if (vars == 0){
Arena *arena = arena_alloc_reserve(KB(64), 0);
vars = er_vars = push_array(arena, ER_ThreadVars, 1);
vars->arena = arena;
}
U64 pos = arena_current_pos(vars->arena);
ER_Node *node = push_array(vars->arena, ER_Node, 1);
if (node == 0){
vars->over_stack += 1;
}
else{
node->pos = pos;
SLLStackPush(vars->stack, node);
}
}
link_function void
er_emit(String8 error){
ER_ThreadVars *vars = er_vars;
if (vars != 0 &&
vars->over_stack == 0){
ER_Node *node = vars->stack;
if (node != 0 && node->error.size == 0){
node->error = str8_push_copy(vars->arena, error);
}
}
}
link_function void
er_emitf(char *fmt, ...){
ER_ThreadVars *vars = er_vars;
if (vars != 0 &&
vars->over_stack == 0){
ER_Node *node = vars->stack;
if (node != 0 && node->error.size == 0){
va_list args;
va_start(args, fmt);
String8 string = str8_pushfv(vars->arena, fmt, args);
va_end(args);
node->error = string;
}
}
}
link_function String8
er_accum_end(Arena *arena){
String8 result = {0};
ER_ThreadVars *vars = er_vars;
if (vars != 0){
if (vars->over_stack == 0){
ER_Node *node = vars->stack;
if (node != 0){
result = str8_push_copy(arena, node->error);
SLLStackPop(vars->stack);
arena_pop_to(vars->arena, node->pos);
}
}
else{
vars->over_stack -= 1;
}
}
return(result);
}
////////////////////////////////
// NOTE(allen): Interleaving Functions
link_function String8
bop_interleave(Arena *arena, void **in,
U64 lane_count, U64 el_size, U64 el_count){
// TODO(allen): look at disassembly for real speed work
// setup buffer
String8 result = {0};
result.size = lane_count*el_size*el_count;
result.str = push_array(arena, U8, result.size);
// fill loop
U8 *out_ptr = result.str;
U64 in_off = 0;
for (U64 i = 0; i < el_count; i += 1, in_off += el_size){
U8 **in_base_ptr = (U8**)in;
for (U64 j = 0; j < lane_count; j += 1, in_base_ptr += 1){
MemoryCopy(out_ptr, *in_base_ptr + in_off, el_size);
out_ptr += el_size;
}
}
return(result);
}
link_function String8*
bop_uninterleave(Arena *arena, void *in,
U64 lane_count, U64 el_size, U64 el_count){
// TODO(allen): look at disassembly for real speed work
// compute sizes
U64 bytes_per_lane = el_size*el_count;
U64 total_size = lane_count*bytes_per_lane;
// allocate outs
String8 *result = push_array(arena, String8, lane_count);
for (U64 i = 0; i < lane_count; i += 1){
result[i].str = push_array(arena, U8, bytes_per_lane);
result[i].size = bytes_per_lane;
}
// fill loop
U8 *in_ptr = (U8*)in;
U64 out_off = 0;
for (U64 i = 0; i < el_count; i += 1, out_off += el_size){
String8 *out_buffer = result;
for (U64 j = 0; j < lane_count; j += 1, out_buffer += 1){
MemoryCopy(out_buffer->str + out_off, in_ptr, el_size);
in_ptr += el_size;
}
}
return(result);
}
////////////////////////////////
// NOTE(allen): Conversions
link_function String8
bop_f32_from_s24(Arena *arena, String8 in){
// TODO(allen): look at disassembly for real speed work
// round size
U64 el_count = in.size/3;
// allocate out
F32 *out = push_array(arena, F32, el_count);
// fill loop
U8 *in_ptr = in.str;
F32 *out_ptr = out;
F32 *opl_ptr = out_ptr + el_count;
for (; out_ptr < opl_ptr; out_ptr += 1){
// read s24
S32 x = (*in_ptr);
in_ptr += 1;
x |= (*in_ptr) << 8;
in_ptr += 1;
x |= (*in_ptr) << 16;
in_ptr += 1;
if ((x & (1 << 23)) != 0){
x |= (0xFF << 24);
}
// divide to float
F32 fx = 0.f;
if (x >= 0){
fx = ((F32)x)/(8388607.f);
}
else{
fx = ((F32)x)/(8388608.f);
}
// write f32
*out_ptr = fx;
}
// package output buffer
String8 result = {0};
result.str = (U8*)out;
result.size = el_count*sizeof(F32);
return(result);
}
link_function String8
bop_s24_from_f32(Arena *arena, String8 in){
// TODO(allen): look at disassembly for real speed work
// round size
U64 el_count = in.size/4;
// allocate out
U8 *out = push_array(arena, U8, el_count*3);
// fill loop
F32 *in_ptr = (F32*)in.str;
U8 *out_ptr = out;
U8 *opl_ptr = out_ptr + el_count*3;
for (; out_ptr < opl_ptr; out_ptr += 3, in_ptr += 1){
// read f32
F32 x = (*in_ptr);
F32 x_clamped = Clamp(-1.f, x, 1.f);
// multiply to s24 (inside a s32)
S32 fx = 0;
if (x >= 0){
fx = 0x7FFFFF*x_clamped;
}
else{
fx = 0x800000*x_clamped;
}
// write s24
out_ptr[0] = fx&0xFF;
out_ptr[1] = (fx >> 8)&0xFF;
out_ptr[2] = (fx >> 16)&0xFF;
}
// package output buffer
String8 result = {0};
result.str = (U8*)out;
result.size = el_count*3;
return(result);
}
link_function String8
bop_s16_from_f32(Arena *arena, String8 in){
// TODO(allen): look at disassembly for real speed work
// round size
U64 el_count = in.size/4;
// allocate out
S16 *out = push_array(arena, S16, el_count);
// fill loop
F32 *in_ptr = (F32*)in.str;
S16 *out_ptr = out;
S16 *opl_ptr = out_ptr + el_count;
for (; out_ptr < opl_ptr; out_ptr += 1, in_ptr += 1){
// read f32
F32 x = (*in_ptr);
F32 x_clamped = Clamp(-1.f, x, 1.f);
// multiply to s16 (inside a s32)
S32 fx = 0;
if (x >= 0){
fx = 0x7FFF*x_clamped;
}
else{
fx = 0x8000*x_clamped;
}
// write s16
*out_ptr = fx;
}
// package output buffer
String8 result = {0};
result.str = (U8*)out;
result.size = el_count*sizeof(S16);
return(result);
}
////////////////////////////////
// NOTE(allen): PRNG Functions
#include "pcg/pcg_basic.c"
link_function PRNG
prng_make_from_seed(PRNG_Seed *seed){
PRNG prng = {0};
U64 st0 = *(U64*)(seed->v + 0);
U64 seq0 = *(U64*)(seed->v + 8);
U64 st1 = *(U64*)(seed->v + 16);
U64 seq1 = *(U64*)(seed->v + 24);
if ((seq0|1) == (seq1|1)){
seq1 = ~seq1;
}
pcg32_srandom_r(&prng.gen[0], st0, seq0);
pcg32_srandom_r(&prng.gen[1], st1, seq1);
return(prng);
}
link_function U32
prng_next_u32(PRNG *prng){
U32 result = pcg32_random_r(&prng->gen[0]);
return(result);
}
link_function U64
prng_next_u64(PRNG *prng){
U32 hi = pcg32_random_r(&prng->gen[0]);
U32 lo = pcg32_random_r(&prng->gen[1]);
U32 result = (((U64)hi) << 32) | lo;
return(result);
}
link_function U32
prng_next_bounded(PRNG *prng, U32 bound){
U32 threshold = (-bound)%bound;
U32 r = pcg32_random_r(&prng->gen[0]);
for (;r < threshold;){
r = pcg32_random_r(&prng->gen[0]);
}
U32 result = r%bound;
return(result);
}
// [0,1]
link_function F32
prng_next_unital_f32(PRNG *prng){
U32 x = prng_next_u32(prng);
U32 x_masked = (x & 0x1FFFFF); // 21-bit mask
F32 result = (F32)x_masked/2097151.f;
return(result);
}
// [-1,1]
link_function F32
prng_next_biunital_f32(PRNG *prng){
U32 x = prng_next_u32(prng);
U32 x_masked = (x & 0x3FFFFF); // 22-bit mask
F32 result = ((F32)x_masked/2097151.f) - 1.f;
return(result);
}
link_function B32
prng_next_pcoin(PRNG *prng, F32 p){
F32 x = prng_next_unital_f32(prng);
B32 result = (x <= p);
return(result);
}
////////////////////////////////
// NOTE(allen): Math Functions
link_function F32
math_gaussian(F32 sigma, F32 x){
// 1/(sqrt(2*pi)*sigma) * e^(-x*x/(2*sigma*sigma))
F32 sqrt_2pi = sqrt_F32(tau_F32);
F32 inv_sqrt_2pi_sigma = 1.f/(sqrt_2pi*sigma);
F32 sigma_p2 = sigma*sigma;
F32 exponent = -x*x/(2*sigma_p2);
F32 power = pow_F32(e_F32, exponent);
F32 result = inv_sqrt_2pi_sigma*power;
return(result);
}
link_function Array_F32
math_gaussian_kernel(Arena *arena, F32 sigma, U32 extra_reach){
Assert(sigma > 0.f);
// allocate
U32 reach = (U32)ceil_F32(sigma) + extra_reach;
U64 count = reach*2 + 1;
F32 *vals = push_array(arena, F32, count);
// calculate guassian samples & sum
F32 sum = 0;
{
F32 sqrt_2pi = sqrt_F32(tau_F32);
F32 inv_sqrt_2pi_sigma = 1.f/(sqrt_2pi*sigma);
F32 sigma_p2 = sigma*sigma;
F32 mid = (F32)reach;
F32 *val_ptr = vals;
for (U64 i = 0; i < count; i += 1, val_ptr += 1){
F32 x = (F32)i - mid;
F32 exponent = -x*x/(2*sigma_p2);
F32 power = pow_F32(e_F32, exponent);
F32 g = inv_sqrt_2pi_sigma*power;
sum += g;
*val_ptr = g;
}
}
// normalise kernel
{
F32 *val_ptr = vals;
for (U64 i = 0; i < count; i += 1, val_ptr += 1){
*val_ptr = *val_ptr/sum;
}
}
// return result
Array_F32 result = {0};
result.vals = vals;
result.count = count;
return(result);
}