//////////////////////////////// // 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 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); }