Finish string module, add timing module

This commit is contained in:
Musa Mahmood 2025-11-28 05:28:25 -05:00
parent 6d8a580724
commit efdb057361
9 changed files with 261 additions and 58 deletions

View File

@ -3,8 +3,6 @@
// There should be some parameters regarding what the upper limit for idle
// committed pages should be and a heuristic for maximum number of arenas waiting
// API in Arena.h
global Arena_Table* arena_table;
// Only call once from main thread!
@ -65,15 +63,13 @@ void release_arena (Arena* arena, bool delete_extra_pages) {
array_add(arena_table->free_table[reserve_index], arena);
arena_table->in_flight_count[reserve_index] -= 1;
/*
// #TODO #garbage_collection
if (arena_free_table[reserve_index].count > 64) {
// s64 arenas_to_delete_count = arena_free_table[reserve_index].count - 64.
// while (arenas_to_delete_count > 0) {
// arena_delete(arena_free_table[arena_free_table.count-1]);
// array_unordered_remove_by_index(..);
// arenas_to_delete_count -= 1;
// }
}
*/
// if (arena_free_table[reserve_index].count > 64) {
// s64 arenas_to_delete_count = arena_free_table[reserve_index].count - 64;
// while (arenas_to_delete_count > 0) {
// arena_delete(arena_free_table[arena_free_table.count-1]);
// array_unordered_remove_by_index(..);
// arenas_to_delete_count -= 1;
// }
// }
}

View File

@ -1,6 +1,7 @@
// See Context_Base in jai, and TCTX in raddebugger:
internal void Bootstrap_Main_Thread_Context () {
// Timed_Block_Print_No_Context("Bootstrap_Main_Thread_Context");
// 0. Setup general allocator
GPAllocator_Initialize_Allocation_Tracker();
@ -10,6 +11,7 @@ internal void Bootstrap_Main_Thread_Context () {
initialize_arena_table(GPAllocator());
// 2. Setup thread local context
// #NewContext
ExpandableArena* arena_ex = expandable_arena_new(Arena_Reserve::Size_64M, 16);
thread_local_context = New<Thread_Context>(get_allocator(arena_ex));
@ -75,7 +77,7 @@ void temp_reset_keeping_memory() {
arena_reset(context->temp, false);
}
void temp_reset() {
void temp_reset() { // alias: reset_temporary_storage.
Thread_Context* context = get_thread_context();
arena_reset(context->temp, true);
}

View File

@ -23,6 +23,8 @@ thread_static Thread_Context* thread_local_context;
Thread_Context* get_thread_context ();
// #TODO #NewContext void create_thread_context (Thread_Context** context, string thread_name, bool is_main_thread);
internal void Bootstrap_Main_Thread_Context ();
struct Push_Allocator {

View File

@ -35,7 +35,7 @@ string copy_string (string s) {
}
string copy_string (char* c_string) {
string str = {0};
string str = {};
s64 string_length = strlen(c_string);
if (string_length == 0)
return "";
@ -72,11 +72,6 @@ force_inline string string_view (string s, s64 start_index, s64 view_count) {
return { new_count, s.data + start_index };
}
string copy_string_view (string s, s64 start_index, s64 view_count) {
// maybe redundant...
return copy_string(string_view(s, start_index, view_count));
}
bool strings_match (string first_string, string second_string) {
return (first_string == second_string);
}
@ -109,10 +104,33 @@ string wide_to_utf8 (u16* source, s32 length) {
return utf8_string;
}
wstring utf8_to_wide (string source) {
if (!source) return {};
s32 query_num_chars = MultiByteToWideChar(CP_UTF8, 0,
(LPCCH)source.data, (s32)source.count, // @Robustness: Silent failure if too long. @Cleanup.
nullptr, 0);
if (query_num_chars <= 0) return {};
wstring name_u16s = wstring(query_num_chars);
s32 result_num_chars = MultiByteToWideChar(CP_UTF8, 0,
(LPCCH)source.data, (s32)source.count, // @Robustness: Silent failure if too long. @Cleanup.
(LPWSTR)name_u16s.data, query_num_chars);
if (!result_num_chars) {
internal_free(name_u16s.data);
return {};
}
Assert(result_num_chars <= query_num_chars);
name_u16s.data[result_num_chars] = 0; // null terminate
return name_u16s;
}
string format_string (char* format, ...) {
constexpr s64 BUFFER_SIZE = 4096;
string str = {0};
string str = {};
str.data = NewArray<u8>(BUFFER_SIZE);
@ -125,6 +143,23 @@ string format_string (char* format, ...) {
return str;
}
// #MemoryLeak #NoContext - memory will leak from this operation.
string format_string_no_context (char* format, ...) {
constexpr s64 BUFFER_SIZE = 4096;
string str = {};
str.data = (u8*)GPAllocator_New(BUFFER_SIZE);
va_list args;
va_start(args, format);
// Note that this *is* null-terminated for compatibility.
str.count = (s64)vsnprintf((char*)str.data, (size_t)BUFFER_SIZE, format, args);
va_end(args);
return str;
}
force_inline String_Builder* new_string_builder (Arena_Reserve new_reserve) {
return arena_array_new<u8>(1, new_reserve);
}

View File

@ -40,6 +40,11 @@ struct string {
return true;
}
bool operator ! () {
Assert(count >= 0);
return (data == nullptr || count == 0);
}
};
struct wstring {
@ -50,6 +55,17 @@ struct wstring {
count = 0;
data = nullptr;
}
wstring (s32 length) {
data = NewArray<u16>(length + 1);
s32 length_bytes = (length + 1) * sizeof(u16);
count = length_bytes;
}
bool operator ! () {
Assert(count >= 0);
return (data == nullptr || count == 0);
}
};
// ~Keep these API
@ -63,23 +79,28 @@ void free(string& s);
// String manipulation & comparison
force_inline string string_view (string s, s64 start_index, s64 view_count);
string copy_string_view (string s, s64 start_index, s64 view_count);
bool strings_match (string first_string, string second_string);
// #Unicode
// #TODO: Make a raw version that returns the raw pointer?
string wide_to_utf8 (u16* source, s32 length);
// wstring utf8_to_wide (string source); TODO.
wstring utf8_to_wide (string source);
string format_string (char* format, ...);
string format_string_no_context (char* format, ...);
// Parsing stuff:
// #TODO #Parsing stuff:
// is_white_space(char: u8)
// advance
// eat_spaces
// Print stuff
// string to type or type to string conversions
// s64 string_to_int (string v, s32 base = 10, s64* remainder=nullptr);
//
// f64 string_to_f64
// f32 string_to_f32
// Need an API for inserting various types (ints, floats, etc.) into a String_Builder, and advancing
// the count.
// #string_builder
// #limitations This won't be as fast as Jon's String_Builder in jai because we're backing it with an

130
lib/Base/Timing.h Normal file
View File

@ -0,0 +1,130 @@
// #Timing API:
#define System_Timed_Block_Print(name) \
system_timed_block_print Concat(_sys_timed_block_print_guard, __LINE__)(name)
#define Timed_Block_Print(name) \
timed_block_print Concat(_timed_block_print_guard, __LINE__)(name)
// This is for timing stuff that happens where context is not available.
// memory will leak from this operation. #MemoryLeak #NoContext
#define Timed_Block_Print_No_Context(name) \
timed_block_print_no_context Concat(_timed_block_print_guard, __LINE__)(name)
force_inline u64 rdtsc();
#if OS_WINDOWS
#include <intrin.h>
#pragma intrinsic(__rdtsc)
force_inline u64 rdtsc() {
return __rdtsc();
}
#endif
global u32 g_cpu_base_frequency_megahertz;
void set_cpu_base_frequency (u32 frequency_megahertz) {
g_cpu_base_frequency_megahertz = frequency_megahertz;
}
string format_time_seconds_no_context (f64 time) { // #MemoryLeak #NoContext
if (time < 1.0e-6) { return format_string_no_context("%1.2f ns", time * 1.0e9); }
if (time < 1.0e-3) { return format_string_no_context("%1.3f us", time * 1.0e6); }
if (time < 1) { return format_string_no_context("%1.3f ms", time * 1.0e3); }
return format_string_no_context("%1.3f s", time);
}
string format_cycles_no_context (u64 ticks) { // #MemoryLeak #NoContext
string units[5] = { "cycles", "K cycles", "M cycles", "B cycles", "T cycles" };
f64 count_f64 = (f64)ticks;
s64 unit_index = 0;
while (count_f64 >= 1000 && unit_index < 4) { // 4 is from (units.count-1)
count_f64 /= 1000.0;
unit_index += 1;
}
return format_string_no_context("%1.2f %s", count_f64, units[unit_index].data);
}
string format_time_seconds (f64 time) {
if (time < 1.0e-6) { return format_string("%1.2f ns", time * 1.0e9); }
if (time < 1.0e-3) { return format_string("%1.3f us", time * 1.0e6); }
if (time < 1) { return format_string("%1.3f ms", time * 1.0e3); }
return format_string("%1.3f s", time);
}
string format_cycles (u64 ticks) {
string units[5] = { "cycles", "K cycles", "M cycles", "B cycles", "T cycles" };
f64 count_f64 = (f64)ticks;
s64 unit_index = 0;
while (count_f64 >= 1000 && unit_index < 4) { // 4 is from (units.count-1)
count_f64 /= 1000.0;
unit_index += 1;
}
return format_string("%1.2f %s", count_f64, units[unit_index].data);
}
struct timed_block_print {
string block_name;
u64 start_tick;
timed_block_print(string _block_name) {
Assert(g_cpu_base_frequency_megahertz != 0);
Assert(thread_local_context != nullptr); // we need temp allocator initialized!
block_name = _block_name;
start_tick = rdtsc();
}
~timed_block_print() {
u64 end_tick = rdtsc();
u64 tick_difference = end_tick - start_tick;
f64 ticks_f64 = (f64)tick_difference;
f64 elapsed_time_seconds = ticks_f64 / (f64)((s64)g_cpu_base_frequency_megahertz * 1000000);
push_allocator(get_temp_allocator());
log("[Timed_Block %s]: %s (%s)", block_name.data, format_time_seconds(elapsed_time_seconds).data, format_cycles(tick_difference).data);
}
};
struct timed_block_print_no_context {
string block_name;
u64 start_tick;
timed_block_print_no_context(string _block_name) {
Assert(g_cpu_base_frequency_megahertz != 0);
block_name = _block_name;
start_tick = rdtsc();
}
~timed_block_print_no_context() {
u64 end_tick = rdtsc();
u64 tick_difference = end_tick - start_tick;
f64 ticks_f64 = (f64)tick_difference;
f64 elapsed_time_seconds = ticks_f64 / (f64)((s64)g_cpu_base_frequency_megahertz * 1000000);
printf("[Timed_Block(No Context) %s]: %s (%s)\n",
block_name.data,
format_time_seconds_no_context(elapsed_time_seconds).data,
format_cycles_no_context(tick_difference).data);
}
};
struct system_timed_block_print {
string block_name;
f64 start_time;
system_timed_block_print(string _block_name) {
block_name = _block_name;
start_time = GetUnixTimestamp();
}
~system_timed_block_print() {
f64 end_time = GetUnixTimestamp();
f64 elapsed_time_seconds = end_time - start_time;
push_allocator(get_temp_allocator());
log("[Timed_Block %s]: %s", block_name.data, format_time_seconds(elapsed_time_seconds).data);
}
};

View File

@ -74,6 +74,7 @@ internal LONG WINAPI Win32_Exception_Filter (EXCEPTION_POINTERS* exception_ptrs)
// internal void Main_Entry_Point (int argc, WCHAR **argv);
internal void Win32_Entry_Point (int argc, WCHAR **argv) {
// Timed_Block_Print("Win32_Entry_Point");
// See: w32_entry_point_caller(); (raddebugger)
SetUnhandledExceptionFilter(&Win32_Exception_Filter);
@ -173,7 +174,7 @@ internal void Win32_Entry_Point (int argc, WCHAR **argv) {
// [ ] Get Working directory (info->working_path)
// [ ] GetEnvironmentStringsW
// temp_reset();
temp_reset();
printf("Hello there!\n\n");
}
@ -201,7 +202,7 @@ internal bool thread_init (Thread* thread, Thread_Proc proc, string thread_name=
}
s64 this_thread_index = InterlockedIncrement(&next_thread_index);
// #NewContext
ExpandableArena* arena_ex = expandable_arena_new(Arena_Reserve::Size_64M, 16);
thread->context = New<Thread_Context>(get_allocator(arena_ex));
@ -209,8 +210,8 @@ internal bool thread_init (Thread* thread, Thread_Proc proc, string thread_name=
thread->context->arena = arena_ex;
thread->context->allocator = get_allocator(arena_ex);
thread->context->thread_idx = (s32)this_thread_index;
push_arena(arena_ex);
thread->context->thread_name = copy_string(thread_name);
thread->context->log_builder = new_string_builder(Arena_Reserve::Size_64M);
thread->os_thread.windows_thread = windows_thread;
thread->os_thread.windows_thread_id = windows_thread_id;
@ -229,6 +230,9 @@ internal void thread_deinit (Thread* thread) {
arena_delete(thread->context->temp);
arena_delete(thread->context->arena);
free_string_builder(thread->context->log_builder);
// memset(thread, 0xCD, sizeof(Thread);
}
internal void thread_start (Thread* thread) {

View File

@ -23,8 +23,9 @@
#include "lib/Base/String.cpp"
#include "lib/Base/ErrorType.cpp"
#include "lib/Base/Arena_Table.cpp"
#include "lib/Base/Base_Thread_Context.h"
#include "lib/Base/Timing.h"
#include "lib/Base/Arena_Table.cpp"
#include "lib/Base/Expandable_Arena.h"
#include "lib/Base/Arena.cpp"

View File

@ -184,40 +184,52 @@ void ImGui_Application () {
string string_literal_example = "Hello, I am a string literal.";
internal void Main_Entry_Point (int argc, WCHAR **argv) {
set_cpu_base_frequency(3200); // REQUIRED FOR TIMING MODULE!
// #NOTE: Be careful using a timing or auto-release macros
// before setting up the thread context!
Bootstrap_Main_Thread_Context();
#if OS_WINDOWS
Win32_Entry_Point(argc, argv); // This might be the problem.
#endif
#if OS_LINUX
// Linux_Entry_Point(argc, argv);
#endif
// See: main_thread_base_entry_point
temp_reset();
push_allocator(get_temp_allocator());
// tip: use auto_reset or auto_release with `get_thread_context()->arena`
{ Timed_Block_Print("string_builder_testing");
temp_reset();
push_allocator(get_temp_allocator());
// tip: use auto_reset or auto_release with `get_thread_context()->arena`
// String builder example:
// OK. I can work with this.
auto sb = new_string_builder(Arena_Reserve::Size_64K);
append(sb, "string_literal_example");
append(sb, " ");
print_to_builder(sb, "There are %d cats in the %s", 64, "house.\n");
append(sb, " > ");
print_to_builder(sb, "some size_t: %u", (u64)3982739867);
append(sb, "\n");
print_to_builder(sb, "the literal: %s", string_literal_example.data);
// string result = string_view(sb);
string result = builder_to_string(sb); // also frees
// print((char*)result.data);
log("Hello.");
log("log() should automatically append newlines to these prints.");
log("If it doesn't, I will be very upset.");
log(result);
log("Hello. There are %s things here\n", string_literal_example.data);
print("Hello, I am just a printed message to stdout\n\n");
}
{ string s = "hello I am cool";
wstring sw = utf8_to_wide(s);
printf("testing operator overload: %d\n", !s);
printf("testing utf8_to_wide: %ls\n", sw.data);
}
// debug_break();
// String builder example:
// OK. I can work with this.
auto sb = new_string_builder(Arena_Reserve::Size_64K);
append(sb, "string_literal_example");
append(sb, " ");
print_to_builder(sb, "There are %d cats in the %s", 64, "house.\n");
append(sb, " > ");
print_to_builder(sb, "some size_t: %u", (u64)3982739867);
append(sb, "\n");
print_to_builder(sb, "the literal: %s", string_literal_example.data);
// string result = string_view(sb);
string result = builder_to_string(sb); // also frees
// print((char*)result.data);
log("Hello.");
log("log() should automatically append newlines to these prints.");
log("If it doesn't, I will be very upset.");
log(result);
log("Hello. There are %s things here\n", string_literal_example.data);
print("Hello, I am just a printed message to stdout\n\n");
// free_string_builder(sb);
// ImGui_Application();
// #TODO: #Main - `Main_Entry_Point`