Refactoring, add basic file module (Win32)

This commit is contained in:
Musa Mahmood 2025-11-29 10:13:09 -05:00
parent 747f7d92b9
commit 916777c2b5
20 changed files with 354 additions and 72 deletions

View File

@ -1,18 +1,18 @@
void* internal_alloc (s64 size) {
Allocator allocator = get_context_allocator();
Allocator allocator = context_allocator();
void* result = allocator.proc(Allocator_Mode::ALLOCATE, size, 0, nullptr, allocator.data);
return result;
}
// #NOTE: internal_realloc does NOT copy anything! It just hands you new memory to work with!
void* internal_realloc (void* memory, s64 size, s64 old_size) {
Allocator allocator = get_context_allocator();
Allocator allocator = context_allocator();
void* result = allocator.proc(Allocator_Mode::RESIZE, size, old_size, memory, allocator.data);
return result;
}
void internal_free (void* memory) {
Allocator allocator = get_context_allocator();
Allocator allocator = context_allocator();
allocator.proc(Allocator_Mode::DEALLOCATE, 0, 0, memory, allocator.data);
}

View File

@ -204,6 +204,6 @@ s64 arena_usage_bytes (Arena* arena) { return (s64)(arena->current_point - arena
s64 arena_usage_committed_bytes (Arena* arena) { return (s64)(arena->first_uncommitted_page - arena->memory_base); }
// for arena details, I need to setup my string builder first.
Allocator get_allocator (Arena* arena) {
Allocator allocator (Arena* arena) {
return { arena_allocator_proc, arena };
}

View File

@ -105,7 +105,7 @@ s64 arena_usage_committed_bytes (Arena* arena);
s64 reserve_size (Arena* arena);
s64 reserve_size (Arena_Reserve ar);
bool is_valid (Arena* arena);
Allocator get_allocator (Arena* arena);
Allocator allocator (Arena* arena);
// Platform-Specific Implementations (forward-declared)
void platform_init (Arena* arena, s64 new_reserve);

View File

@ -19,7 +19,7 @@ struct Array { // downcasts to an ArrayView.
Array(s64 new_count, bool initialize=false) { // old: NewArray ::, array_new :
count = new_count;
allocator = get_context_allocator();
allocator = context_allocator();
data = NewArray<T>(new_count, initialize);
allocated = new_count;
}
@ -29,7 +29,7 @@ struct Array { // downcasts to an ArrayView.
count = new_count;
data = (T*)new_data;
allocated = _allocated;
allocator = get_context_allocator();
allocator = context_allocator();
}
Array(s64 new_count, void* new_data, s64 _allocated, Allocator _allocator) {
@ -124,7 +124,7 @@ void array_reserve (Array<T>& src, s64 desired_items) {
src.data = nullptr;
if (src.allocator.proc == nullptr) {
src.allocator = get_context_allocator();
src.allocator = context_allocator();
}
Assert(src.allocator.proc != nullptr);
@ -249,7 +249,8 @@ struct ArrayView {
s64 count;
T* data;
ArrayView(Array<T> array) {
// #TODO: Add initializers ArrayView<u8> from string, ArrayView<T> from ArenaArray<T>
ArrayView(Array<T> array) { // auto-downcast from Array<T>
count = array.count;
data = array.data;
}
@ -266,7 +267,6 @@ struct ArrayView {
count = _count;
data = _data;
}
T& operator[](s64 index) {
#if ARRAY_ENABLE_BOUNDS_CHECKING
if (index < 0 || index >= count) { debug_break(); } // index out of bounds
@ -275,6 +275,11 @@ struct ArrayView {
}
};
template <typename T> ArrayView<u8> to_byte_view (ArrayView<T> src) {
ArrayView<u8> byte_view = { src.count * sizeof(T), src.data };
return byte_view;
}
template <typename T>
bool is_empty (ArrayView<T> src) {
if (src.count == 0) return true;

View File

@ -188,7 +188,46 @@ force_inline s64 Next_Power_Of_Two(s64 v) {
#define push_expandable_arena(x) \
Push_Expandable_Arena Concat(_push_ex_arena_guard_, __LINE__)(x)
#define auto_release_temp() \
auto_release(get_temp_allocator());
auto_release(temp());
#define auto_release(x) \
Auto_Release Concat(_auto_release_guard_, __LINE__)(x)
#define thread_context() thread_local_context
#define temp() allocator(thread_context()->temp)
#define context_allocator() thread_context()->allocator
#define context_logger() &thread_context()->logger
// #TODO #constexpr #MATH - make these constexpr
/*
TAU :: cast(float32) 6.283185;
TAU64 :: 6.28318530717958648;
PI :: cast(float32) 3.1415927;
PI64 :: 3.141592653589793;
FLOAT16_MAX : float : 65504.0;
FLOAT32_MIN :: 0h00800000;
FLOAT32_MAX :: 0h7F7FFFFF;
FLOAT32_INFINITY :: 0h7F800000;
FLOAT32_NAN :: 0h7FBFFFFF;
FLOAT64_MIN :: 0h00100000_00000000;
FLOAT64_MAX :: 0h7FEFFFFF_FFFFFFFF;
FLOAT64_INFINITY :: 0h7FF00000_00000000;
FLOAT64_NAN :: 0h7FF7FFFF_FFFFFFFF;
S8_MIN :s8: -128;
S8_MAX :s8: 127;
U8_MAX :u8: 255;
S16_MIN :s16: -32768;
S16_MAX :s16: 32767;
U16_MAX :u16: 0xffff;
S32_MIN :s32: 0x8000_0000;
S32_MAX :s32: 0x7fff_ffff;
U32_MAX :u32: 0xffff_ffff;
S64_MIN :s64: 0x8000_0000_0000_0000;
S64_MAX :s64: 0x7fff_ffff_ffff_ffff;
U64_MAX :u64: 0xffff_ffff_ffff_ffff;
*/

View File

@ -14,10 +14,10 @@ internal void Bootstrap_Main_Thread_Context () {
// #NewContext
ExpandableArena* arena_ex = expandable_arena_new(Arena_Reserve::Size_64M, 16);
thread_local_context = New<Thread_Context>(get_allocator(arena_ex));
thread_local_context = New<Thread_Context>(allocator(arena_ex));
thread_local_context->temp = expandable_arena_new(Arena_Reserve::Size_2M, 16);
thread_local_context->arena = arena_ex;
thread_local_context->allocator = get_allocator(arena_ex);
thread_local_context->allocator = allocator(arena_ex);
thread_local_context->thread_idx = 0;
thread_local_context->thread_name = "Main Thread";
thread_local_context->log_builder = new_string_builder(Arena_Reserve::Size_64M);
@ -32,18 +32,18 @@ struct Push_Arena {
Push_Arena(ExpandableArena* arena_ex) {
Assert(is_valid(arena_ex));
context = get_thread_context();
context = thread_context();
Assert(context != nullptr);
original_allocator = context->allocator;
context->allocator = get_allocator(arena_ex);
context->allocator = allocator(arena_ex);
}
Push_Arena(Arena* arena) {
Assert(is_valid(arena));
context = get_thread_context();
context = thread_context();
Assert(context != nullptr);
original_allocator = context->allocator;
context->allocator = get_allocator(arena);
context->allocator = allocator(arena);
}
~Push_Arena() {
@ -55,29 +55,12 @@ force_inline void set_thread_context (Thread_Context* new_context) {
thread_local_context = new_context;
}
Thread_Context* get_thread_context () {
return (Thread_Context*)thread_local_context;
}
Logger* get_context_logger () {
return &get_thread_context()->logger;
}
force_inline Allocator get_temp_allocator () {
return get_allocator(get_thread_context()->temp);
}
force_inline Allocator get_context_allocator() {
Thread_Context* context = get_thread_context();
return context->allocator;
}
void temp_reset_keeping_memory() {
Thread_Context* context = get_thread_context();
Thread_Context* context = thread_context();
arena_reset(context->temp, false);
}
void temp_reset() { // alias: reset_temporary_storage.
Thread_Context* context = get_thread_context();
Thread_Context* context = thread_context();
arena_reset(context->temp, true);
}

View File

@ -25,8 +25,6 @@ struct Thread_Context {
// C_LINKAGE thread_static TCTX* tctx_thread_local;
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 ();
@ -36,7 +34,7 @@ struct Push_Allocator {
Allocator old_allocator;
Push_Allocator (Allocator new_allocator) {
context = get_thread_context();
context = thread_context();
old_allocator = context->allocator;
context->allocator = new_allocator;
}

View File

@ -89,7 +89,7 @@ u8* expandable_arena_start (ExpandableArena* arena_ex) {
return Align(arena_ex->memory_base + sizeof(ExpandableArena), ARENA_DEFAULT_ALIGNMENT);
}
Allocator get_allocator (ExpandableArena* arena_ex) {
Allocator allocator (ExpandableArena* arena_ex) {
return { expandable_arena_allocator_proc, arena_ex };
}

View File

@ -23,6 +23,6 @@ void* expandable_arena_allocator_proc (Allocator_Mode mode, s64 requested_size,
bool is_valid (ExpandableArena* arena);
void* expandable_arena_alloc (ExpandableArena* arena_ex, s64 byte_count);
u8* expandable_arena_start (ExpandableArena* arena_ex);
Allocator get_allocator (ExpandableArena* arena_ex);
Allocator allocator (ExpandableArena* arena_ex);
void arena_reset (ExpandableArena* arena_ex, bool free_extra_pages=true);
force_inline void arena_delete (ExpandableArena* arena_ex);

View File

@ -145,7 +145,7 @@ Allocator GPAllocator () {
void* GPAllocator_Proc (Allocator_Mode mode, s64 requested_size, s64 old_size, void* old_memory, void* allocator_data) {
u16 alignment = 16; // default alignment
Thread_Context* tctx = get_thread_context();
Thread_Context* tctx = thread_context();
if (tctx) alignment = tctx->GPAllocator_alignment;
switch (mode) {

View File

@ -1,5 +1,5 @@
void log (string fmt, ...) {
String_Builder* sb = get_thread_context()->log_builder;
void log_error (string fmt, ...) {
String_Builder* sb = thread_context()->log_builder;
va_list args;
va_start(args, fmt);
@ -15,13 +15,36 @@ void log (string fmt, ...) {
string message = string_view(sb);
Logger* logger = get_context_logger();
Logger* logger = context_logger();
logger->proc(message, Log_Level::Error, logger->data);
reset_string_builder(sb);
}
void log (string fmt, ...) {
String_Builder* sb = thread_context()->log_builder;
va_list args;
va_start(args, fmt);
print_to_builder(sb, fmt, args);
va_end(args);
// Append newline if needed
string result = string_view(sb);
bool ends_with_newline = (result.data[result.count-1] == '\n');
if (!ends_with_newline) {
append(sb, "\n");
}
string message = string_view(sb);
Logger* logger = context_logger();
logger->proc(message, Log_Level::None, logger->data);
reset_string_builder(sb);
}
void print (string message) {
Logger* logger = get_context_logger();
Logger* logger = context_logger();
logger->proc(message, Log_Level::None, logger->data);
}

View File

@ -65,11 +65,7 @@ void default_logger_initialize() {
#endif
}
// more hacky forward declares
Logger* get_context_logger ();
Allocator get_temp_allocator ();
Allocator get_context_allocator ();
void log_error (string fmt, ...);
void log (string fmt, ...);
void print (string message);

View File

@ -90,7 +90,9 @@ string wide_to_utf8 (u16* source, s32 length) {
if (query_result <= 0) return { };
// Make room for a null terminator:
query_result += 1;
if (length != -1) {
query_result += 1;
}
u8* memory = NewArray<u8>(query_result);

View File

@ -84,7 +84,7 @@ 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);
string wide_to_utf8 (u16* source, s32 length=-1);
wstring utf8_to_wide (string source);
string format_string (char* format, ...);

View File

@ -121,7 +121,7 @@ void init (Thread_Group* group, s32 group_thread_count, Thread_Group_Proc group_
bool enable_work_stealing = false) {
// Set allocator if not already set:
if (!group->allocator.proc) {
group->allocator = get_context_allocator();
group->allocator = context_allocator();
}
push_allocator(group->allocator);
@ -253,7 +253,7 @@ void add_work (Thread_Group* group, void* work) {
ArrayView<void*> get_completed_work (Thread_Group* group) {
Array<void*> results = Array<void*>();
results.allocator = get_temp_allocator();
results.allocator = temp();
push_allocator(group->allocator);

View File

@ -85,7 +85,7 @@ struct timed_block_print {
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());
push_allocator(temp());
log("[Timed_Block %s]: %s (%s)", block_name.data, format_time_seconds(elapsed_time_seconds).data, format_cycles(tick_difference).data);
}
};
@ -124,7 +124,7 @@ struct system_timed_block_print {
~system_timed_block_print() {
f64 end_time = GetUnixTimestamp();
f64 elapsed_time_seconds = end_time - start_time;
push_allocator(get_temp_allocator());
push_allocator(temp());
log("[Timed_Block %s]: %s", block_name.data, format_time_seconds(elapsed_time_seconds).data);
}
};

View File

@ -100,7 +100,7 @@ internal void Win32_Entry_Point (int argc, WCHAR **argv) {
// }
// }
push_arena(get_thread_context()->arena);
push_arena(thread_context()->arena);
{ OS_System_Info* info = &os_state_w32.system_info;
info->logical_processor_count = (s32)sysinfo.dwNumberOfProcessors;
@ -166,7 +166,7 @@ internal void Win32_Entry_Point (int argc, WCHAR **argv) {
{ OS_Process_Info* info = &os_state_w32.process_info;
DWORD length = GetCurrentDirectoryW(0, 0);
// This can be freed later when we call temp_reset();
u16* memory = NewArray<u16>(get_temp_allocator(), length + 1);
u16* memory = NewArray<u16>(temp(), length + 1);
length = GetCurrentDirectoryW(length + 1, (WCHAR*)memory);
info->working_path = wide_to_utf8(memory, length);
Assert(is_valid(info->working_path));
@ -205,10 +205,10 @@ internal bool thread_init (Thread* thread, Thread_Proc proc, string thread_name=
// #NewContext
ExpandableArena* arena_ex = expandable_arena_new(Arena_Reserve::Size_64M, 16);
thread->context = New<Thread_Context>(get_allocator(arena_ex));
thread->context = New<Thread_Context>(allocator(arena_ex));
thread->context->temp = expandable_arena_new(Arena_Reserve::Size_2M, 16);
thread->context->arena = arena_ex;
thread->context->allocator = get_allocator(arena_ex);
thread->context->allocator = allocator(arena_ex);
thread->context->thread_idx = (s32)this_thread_index;
thread->context->thread_name = copy_string(thread_name);
thread->context->log_builder = new_string_builder(Arena_Reserve::Size_64M);
@ -302,6 +302,208 @@ internal void wake_all (Condition_Variable* cv) {
WakeAllConditionVariable(&cv->condition_variable);
}
internal string get_error_string (OS_Error_Code error_code) {
u16* lpMsgBuf;
bool success = (bool)FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
nullptr, error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
(LPWSTR)&lpMsgBuf, 0, nullptr);
if (!success) { return ""; }
push_allocator(temp());
string result = wide_to_utf8(lpMsgBuf);
LocalFree(lpMsgBuf);
return result; // trim_right(result, "\r\n");
}
internal bool file_is_valid (File file) {
if (file.handle == INVALID_HANDLE_VALUE) return false;
if (file.handle == 0) return false;
return true;
}
internal File file_open (string file_path, bool for_writing, bool keep_existing_content, bool log_errors) {
HANDLE handle;
push_allocator(temp()); // for utf8 -> wide conversions:
if (for_writing) {
u32 creation = (keep_existing_content) ? OPEN_ALWAYS : CREATE_ALWAYS;
handle = CreateFileW(
(LPCWSTR)utf8_to_wide(file_path).data,
FILE_GENERIC_READ | FILE_GENERIC_WRITE,
FILE_SHARE_READ,
nullptr, creation, 0, nullptr);
} else {
u32 creation = OPEN_EXISTING;
handle = CreateFileW(
(LPCWSTR)utf8_to_wide(file_path).data,
FILE_GENERIC_READ,
FILE_SHARE_READ,
nullptr, creation, 0, nullptr);
}
if (handle == INVALID_HANDLE_VALUE && log_errors) {
OS_Error_Code error_code = GetLastError();
log_error("Could not open file `%s`, code: %d, %s", file_path, error_code, get_error_string(error_code).data);
}
File file;
file.handle = handle;
return file;
}
internal void file_close (File* file) {
CloseHandle(file->handle);
}
internal bool file_read (File file, u8* data, s64 bytes_to_read_count, s64* bytes_read_count) {
// ignore bytes_read_count if null.
if (data == nullptr) {
log_error("file_read called with null destination pointer.\n");
if (bytes_read_count) (*bytes_read_count) = 0;
return false;
}
if (bytes_to_read_count <= 0) {
if (bytes_read_count) (*bytes_read_count) = 0;
return false;
}
bool read_success = false;
s64 total_read = 0;
// loop to read more data than can be specified by the DWORD param ReadFile takes:
while (total_read < bytes_to_read_count) {
s64 remaining = bytes_to_read_count - total_read;
DWORD to_read;
if (remaining <= 0x7FFFFFFF) {
to_read = (DWORD)remaining;
} else {
to_read = 0x7FFFFFFF; // 2147483647 bytes ~2GB
}
DWORD single_read_length = 0;
read_success = (bool)ReadFile(file.handle, data + total_read, to_read, &single_read_length, nullptr);
total_read += single_read_length;
if (!read_success || single_read_length == 0) {
break;
}
}
if (bytes_read_count) (*bytes_read_count) = total_read;
return read_success;
}
internal bool file_length (File file, s64* length) {
if (length == nullptr) {
log_error("Calling file_length with null `length` param!");
return false;
}
if (!file_is_valid(file)) { return false; }
s64 size;
bool success = (bool)GetFileSizeEx(file.handle, (PLARGE_INTEGER)&size);
(*length) = size;
return true;
}
internal bool file_length (string file_path, s64* length) {
if (length == nullptr) {
log_error("Calling file_length with null `length` param!");
return false;
}
File f = file_open(file_path);
if (!file_is_valid(f)) { return false; }
bool success = file_length(f, length);
return success;
}
internal s64 file_current_position (File file) {
constexpr s64 invalid_file_position = -1;
if (!file_is_valid(file)) { return invalid_file_position; }
s64 offset = 0;
LARGE_INTEGER liDistanceToMove;
bool result = (bool)SetFilePointerEx(file.handle, liDistanceToMove, (PLARGE_INTEGER)&offset, FILE_CURRENT);
if (!result) { return invalid_file_position; }
return (s64)offset;
}
internal bool file_set_position (File file, s64 position) {
if (!file_is_valid(file)) { return false; }
if (position < 0) { Assert(false); return false; }
LARGE_INTEGER position_li; position_li.QuadPart = position;
return (bool)SetFilePointerEx(file.handle, position_li, nullptr, FILE_BEGIN);
}
internal ArrayView<u8> read_entire_file (File file) {
ArrayView<u8> file_data;
bool result = file_length(file, &file_data.count);
if (!result) return {};
result = file_set_position(file, 0);
if (!result) return {};
file_data.data = NewArray<u8>(file_data.count, false);
if (file_data.data == nullptr) return {};
s64 bytes_read = 0;
result = file_read(file, file_data.data, file_data.count, &bytes_read);
if (!result) {
array_free(file_data);
return {};
}
Assert(bytes_read == file_data.count);
file_data.count = bytes_read;
return file_data;
}
internal ArrayView<u8> read_entire_file (string file_path, bool log_errors) {
File f = file_open(file_path, log_errors=log_errors);
if (!file_is_valid(f)) return {};
ArrayView<u8> file_data = read_entire_file(f);
file_close(&f);
return file_data;
}
internal bool file_write (File* file, void* data, s64 length) {
// @incomplete - deal with inputs > 32 bits (>2GB)
u32 length_u32 = (u32)length;
Assert(length == length_u32);
u32 bytes_written;
bool result = (bool)WriteFile(file->handle, data, length_u32, (LPDWORD)&bytes_written, nullptr);
return result;
}
internal bool write_entire_file (string file_path, void* file_data, s64 count) {
File f = file_open(file_path, true, false);
if (!file_is_valid(f)) return false;
bool result = file_write(&f, file_data, count);
file_close(&f);
return result;
}
internal bool write_entire_file (string file_path, ArrayView<u8> file_data) {
return write_entire_file(file_path, file_data.data, file_data.count);
}
// #window_creation
Window_Type create_window (string new_window_name) {
return 0;

View File

@ -5,6 +5,15 @@ struct Condition_Variable;
struct Semaphore;
struct Mutex;
struct OS_Thread;
struct File {
HANDLE handle;
};
// struct File_Contents {
// File file = {};
// ArrayView<u8> file_data = {};
// bool read_success = false;
// };
enum class Wait_For_Result : s32 {
SUCCESS = 0,
@ -28,5 +37,30 @@ internal void wait (Condition_Variable* cv, Mutex* mutex, s32 wait_time_ms = -1)
internal void wake (Condition_Variable* cv);
internal void wake_all (Condition_Variable* cv);
typedef u32 OS_Error_Code;
internal string get_error_string (OS_Error_Code error_code);
internal bool file_is_valid (File file);
internal File file_open (string file_path, bool for_writing=false, bool keep_existing_content=false, bool log_errors=false);
internal void file_close (File* file);
internal bool file_read (File file, void* data, s64 bytes_to_read_count, s64* bytes_read_count=nullptr);
internal bool file_length (File file, s64* length);
internal bool file_length (string file_path, s64* length);
internal s64 file_current_position (File file);
internal bool file_set_position (File file, s64 position);
internal ArrayView<u8> read_entire_file (File file);
internal ArrayView<u8> read_entire_file (string file_path, bool log_errors=false);
// use to_byte_view to convert ArrayView<non-u8> to ArrayView<u8>
internal bool file_write (File* file, void* data, s64 length);
internal bool write_entire_file (string file_path, void* file_data, s64 count);
internal bool write_entire_file (string file_path, ArrayView<u8> file_data);
// file_write
// write_entire_file...
// #TODO #fs File System Operations
// file_move, file_delete
// #window_creation
typedef HWND Window_Type;

View File

@ -12,13 +12,13 @@
#include "lib/Base/Array.h"
#include "lib/Base/General_Purpose_Allocator.h"
#if OS_WINDOWS
# include "lib/OS/OS_Win32.h"
#endif
#include "lib/Base/Arena.h"
#include "lib/Base/Arena_Array.h"
#include "lib/Base/String.h"
#if OS_WINDOWS
# include "lib/OS/OS_Win32.h"
#endif
#include "lib/Base/Logger.h"
#include "lib/Base/String.cpp"
@ -27,9 +27,9 @@
#include "lib/Base/ErrorType.cpp"
#include "lib/Base/Base_Thread_Context.h"
#include "lib/Base/Expandable_Arena.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

@ -197,8 +197,8 @@ internal void Main_Entry_Point (int argc, WCHAR **argv) {
// See: main_thread_base_entry_point
{ 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`
push_allocator(temp());
// tip: use auto_reset or auto_release with `thread_context()->arena`
// String builder example:
// OK. I can work with this.
@ -223,11 +223,11 @@ internal void Main_Entry_Point (int argc, WCHAR **argv) {
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);
}
// { 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", (wchar_t*)sw.data);
// }
// debug_break();
// ImGui_Application();