From 6879130bb081fbc43cac78121960234c7a8becfc Mon Sep 17 00:00:00 2001 From: Charlie <66182434+asbott@users.noreply.github.com> Date: Sat, 29 Jun 2024 20:55:43 +0200 Subject: [PATCH] - File IO - os_file_open(string path, Os_Io_Open_Flags flags) - os_file_close(File f) - os_file_delete(string path) - os_file_write_string(File f, string s) - os_file_write_bytes(File f, void *buffer, u64 size_in_bytes) - os_file_read(File f, void* buffer, u64 bytes_to_read, u64 *actual_read_bytes) - os_write_entire_file_handle(File f, string data) - os_write_entire_file(string path, string data) - os_read_entire_file_handle(File f, string *result) - os_read_entire_file(string path, string *result) - fprint(File, string/char*, ...) - Buncha tests - os_high_precision_sleep - talloc_string - Program memory is touched on VirtualAlloc so it actually allocates physical memory (and we can tell when an address is untouched) --- build.c | 2 +- build_clang.bat | 2 +- build_release_clang.bat | 2 +- entry.c | 9 + oogabooga/base.c | 10 +- oogabooga/memory.c | 4 +- oogabooga/oogabooga.c | 56 +++--- oogabooga/os_impl_windows.c | 328 ++++++++++++++++++++++++++++-------- oogabooga/os_interface.c | 67 +++++++- oogabooga/string.c | 9 +- oogabooga/string_format.c | 1 + oogabooga/tests.c | 139 +++++++++++---- 12 files changed, 486 insertions(+), 143 deletions(-) diff --git a/build.c b/build.c index 7a37ad7..01dd40c 100644 --- a/build.c +++ b/build.c @@ -2,7 +2,7 @@ /// // Build config stuff -#define RUN_TESTS 0 +#define RUN_TESTS 1 // When we need very debug // #define CONFIGURATION VERY_DEBUG diff --git a/build_clang.bat b/build_clang.bat index 8a5df6d..f6f9774 100644 --- a/build_clang.bat +++ b/build_clang.bat @@ -4,6 +4,6 @@ mkdir build pushd build -clang -g -o cgame.exe ../build.c -O0 -std=c11 -Wextra -Wno-incompatible-library-redeclaration -Wno-sign-compare -Wno-unused-parameter -lgdi32 -luser32 -lopengl32 +clang -g -o cgame.exe ../build.c -O0 -std=c11 -Wextra -Wno-incompatible-library-redeclaration -Wno-sign-compare -Wno-unused-parameter -Wno-builtin-requires-header -lgdi32 -luser32 -lopengl32 -lwinmm popd \ No newline at end of file diff --git a/build_release_clang.bat b/build_release_clang.bat index 3caa7dc..c457534 100644 --- a/build_release_clang.bat +++ b/build_release_clang.bat @@ -7,7 +7,7 @@ pushd build mkdir release pushd release -clang -o cgame.exe ../../build.c -Ofast -std=c11 -Wextra -Wno-incompatible-library-redeclaration -Wno-sign-compare -Wno-unused-parameter -lgdi32 -luser32 -lopengl32 +clang -o cgame.exe ../../build.c -Ofast -std=c11 -Wextra -Wno-incompatible-library-redeclaration -Wno-sign-compare -Wno-unused-parameter -Wno-builtin-requires-header -lgdi32 -luser32 -lopengl32 -lwinmm popd popd \ No newline at end of file diff --git a/entry.c b/entry.c index ea354af..59e96a8 100644 --- a/entry.c +++ b/entry.c @@ -10,6 +10,9 @@ int start(int argc, char **argv) { seed_for_random = os_get_current_cycle_count(); + const float64 fps_limit = 420; + const float64 min_frametime = 1.0 / fps_limit; + float64 last_time = os_get_current_time_in_seconds(); while (!window.should_close) { reset_temporary_storage(); @@ -19,6 +22,11 @@ int start(int argc, char **argv) { float64 now = os_get_current_time_in_seconds(); float64 delta = now - last_time; + if (delta < min_frametime) { + os_high_precision_sleep((min_frametime-delta)*1000.0); + now = os_get_current_time_in_seconds(); + delta = now - last_time; + } last_time = now; if (is_key_just_released(KEY_ESCAPE)) { @@ -27,6 +35,7 @@ int start(int argc, char **argv) { if (is_key_just_released('E')) { print("Mouse pos: %f, %f\n", input_frame.mouse_x, input_frame.mouse_y); + print("FPS: %.2f\n", 1.0 / delta); } for (u64 i = 0; i < input_frame.number_of_events; i++) { diff --git a/oogabooga/base.c b/oogabooga/base.c index e75e29a..1249e62 100644 --- a/oogabooga/base.c +++ b/oogabooga/base.c @@ -23,6 +23,8 @@ typedef u8 bool; #define local_persist static +#define forward_global extern + #define ifnt(x) if (!(x)) #ifdef _MSC_VER @@ -42,7 +44,7 @@ void printf(const char* fmt, ...); #define assert_line(line, cond, ...) if (!(cond)) { printf("Assertion failed in file " __FILE__ " on line " STR(line) "\nFailed Condition: " #cond ". Message: " __VA_ARGS__); os_break(); } #define assert(cond, ...) assert_line(__LINE__, cond, __VA_ARGS__); -#if CONFIGRATION == RELEASE +#if CONFIGURATION == RELEASE #undef assert #define assert(...) #endif @@ -97,15 +99,15 @@ void printf(const char* fmt, ...); #define FIRST_ARG(arg1, ...) arg1 #define print(...) _Generic((FIRST_ARG(__VA_ARGS__)), \ - string: prints, \ + string: prints, \ default: printf \ )(__VA_ARGS__) #define sprint(...) _Generic((FIRST_ARG(__VA_ARGS__)), \ - string: sprints, \ + string: sprints, \ default: sprintf \ )(__VA_ARGS__) #define tprint(...) _Generic((FIRST_ARG(__VA_ARGS__)), \ - string: tprints, \ + string: tprints, \ default: tprintf \ )(__VA_ARGS__) diff --git a/oogabooga/memory.c b/oogabooga/memory.c index 1984c15..a99bfb1 100644 --- a/oogabooga/memory.c +++ b/oogabooga/memory.c @@ -298,7 +298,7 @@ void *heap_alloc(u64 size) { meta->size = size; meta->block = best_fit_block; -#if VERY_DEBUG +#if CONFIGURATION == VERY_DEBUG santiy_check_free_node_tree(meta->block); #endif @@ -368,7 +368,7 @@ void heap_dealloc(void *p) { } } -#if VERY_DEBUG +#if CONFIGURATION == VERY_DEBUG santiy_check_free_node_tree(block); #endif diff --git a/oogabooga/oogabooga.c b/oogabooga/oogabooga.c index 4559f6e..7ed3c0b 100644 --- a/oogabooga/oogabooga.c +++ b/oogabooga/oogabooga.c @@ -1,4 +1,26 @@ +#define DEBUG 0 +#define VERY_DEBUG 1 +#define RELEASE 2 + +#if !defined(CONFIGURATION) + + #if defined(NDEBUG) + #define CONFIGURATION RELEASE + #else + #define CONFIGURATION DEBUG + #endif + +#endif + +#ifndef ENTRY_PROC + #define ENTRY_PROC entry +#endif + +#define WINDOWS 0 +#define LINUX 1 +#define MACOS 2 + // This needs to be included before dependencies #include "base.c" @@ -25,34 +47,18 @@ void lodepng_free(void* ptr) { ///// -#define DEBUG 0 -#define VERY_DEBUG 1 -#define RELEASE 2 -#if !defined(CONFIGURATION) - - #ifdef _DEBUG - #define CONFIGURATION DEBUG - #elif defined(NDEBUG) - #define CONFIGURATION RELEASE - #endif - -#endif - -#ifndef ENTRY_PROC - #define ENTRY_PROC entry -#endif #ifdef _WIN32 #include - #define OS_WINDOWS + #define TARGET_OS WINDOWS #elif defined(__linux__) // Include whatever #Incomplete #Portability - #define OS_LINUX + #define TARGET_OS LINUX #error "Linux is not supported yet"; #elif defined(__APPLE__) && defined(__MACH__) // Include whatever #Incomplete #Portability - #define OS_MAC + #define TARGET_OS MACOS #error "Mac is not supported yet"; #else #error "Current OS not supported!"; @@ -69,11 +75,11 @@ void lodepng_free(void* ptr) { #ifndef GFX_RENDERER // #Portability - #ifdef OS_WINDOWS + #if TARGET_OS == WINDOWS #define GFX_RENDERER GFX_RENDERER_D3D11 - #elif defined (OS_LINUX) + #elif TARGET_OS == LINUX #define GFX_RENDERER GFX_RENDERER_VULKAN - #elif defined (OS_MAC) + #elif TARGET_OS == MACOS #define GFX_RENDERER GFX_RENDERER_METAL #endif #endif @@ -107,11 +113,11 @@ void lodepng_free(void* ptr) { #error "Unknown renderer defined in GFX_RENDERER" #endif -#ifdef OS_WINDOWS +#if TARGET_OS == WINDOWS #include "os_impl_windows.c" -#elif defined (OS_LINUX) +#elif TARGET_OS == LINUX -#elif defined (OS_MAC) +#elif TARGET_OS == MACOS #endif diff --git a/oogabooga/os_impl_windows.c b/oogabooga/os_impl_windows.c index c2b9ea9..05720b1 100644 --- a/oogabooga/os_impl_windows.c +++ b/oogabooga/os_impl_windows.c @@ -264,6 +264,8 @@ bool os_grow_program_memory(u64 new_size) { return false; } program_memory_size = aligned_size; + + memset(program_memory, 0xBA, program_memory_size); } else { void* tail = (u8*)program_memory + program_memory_size; u64 m = ((u64)program_memory_size % os.granularity); @@ -276,6 +278,7 @@ bool os_grow_program_memory(u64 new_size) { assert(m == 0, "amount_to_allocate is not aligned to granularity!"); // Just keep allocating at the tail of the current chunk void* result = VirtualAlloc(tail, amount_to_allocate, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); + memset(result, 0xBA, amount_to_allocate); if (result == 0) { os_unlock_mutex(program_memory_mutex); // #Sync return false; @@ -294,31 +297,15 @@ bool os_grow_program_memory(u64 new_size) { return true; } -Mutex_Handle os_make_mutex() { - return CreateMutex(0, FALSE, 0); -} -void os_destroy_mutex(Mutex_Handle m) { - CloseHandle(m); -} -void os_lock_mutex(Mutex_Handle m) { - DWORD wait_result = WaitForSingleObject(m, INFINITE); - - switch (wait_result) { - case WAIT_OBJECT_0: - break; - case WAIT_ABANDONED: - break; +/// +/// +// Threading +/// - default: - assert(false, "Unexpected mutex lock result"); - break; - } -} -void os_unlock_mutex(Mutex_Handle m) { - BOOL result = ReleaseMutex(m); - assert(result, "Unlock mutex failed"); -} + +/// +// Thread primitive DWORD WINAPI win32_thread_invoker(LPVOID param) { Thread *t = (Thread*)param; @@ -353,56 +340,37 @@ void os_join_thread(Thread *t) { CloseHandle(t->os_handle); } -void os_sleep(u32 ms) { - Sleep(ms); -} +/// +// Mutex primitive -void os_yield_thread() { - SwitchToThread(); +Mutex_Handle os_make_mutex() { + return CreateMutex(0, FALSE, 0); } - -#include -u64 os_get_current_cycle_count() { - return __rdtsc(); +void os_destroy_mutex(Mutex_Handle m) { + CloseHandle(m); } - -float64 os_get_current_time_in_seconds() { - LARGE_INTEGER frequency, counter; - if (!QueryPerformanceFrequency(&frequency) || !QueryPerformanceCounter(&counter)) { - return -1.0; - } - return (double)counter.QuadPart / (double)frequency.QuadPart; -} - - -Dynamic_Library_Handle os_load_dynamic_library(string path) { - return LoadLibraryA(temp_convert_to_null_terminated_string(path)); -} -void *os_dynamic_library_load_symbol(Dynamic_Library_Handle l, string identifier) { - return GetProcAddress(l, temp_convert_to_null_terminated_string(identifier)); -} -void os_unload_dynamic_library(Dynamic_Library_Handle l) { - FreeLibrary(l); -} - - -void os_write_string_to_stdout(string s) { - HANDLE win32_stdout = GetStdHandle(STD_OUTPUT_HANDLE); - if (win32_stdout == INVALID_HANDLE_VALUE) return; +void os_lock_mutex(Mutex_Handle m) { + DWORD wait_result = WaitForSingleObject(m, INFINITE); - WriteFile(win32_stdout, s.data, s.count, 0, NULL); + switch (wait_result) { + case WAIT_OBJECT_0: + break; + + case WAIT_ABANDONED: + break; + + default: + assert(false, "Unexpected mutex lock result"); + break; + } +} +void os_unlock_mutex(Mutex_Handle m) { + BOOL result = ReleaseMutex(m); + assert(result, "Unlock mutex failed"); } -void* os_get_stack_base() { - NT_TIB* tib = (NT_TIB*)NtCurrentTeb(); - return tib->StackBase; -} -void* os_get_stack_limit() { - NT_TIB* tib = (NT_TIB*)NtCurrentTeb(); - return tib->StackLimit; -} - - +/// +// Spinlock "primitive" Spinlock *os_make_spinlock() { Spinlock *l = cast(Spinlock*)alloc(sizeof(Spinlock)); @@ -427,6 +395,10 @@ void os_spinlock_unlock(Spinlock *l) { assert(success, "This thread should have acquired the spinlock but compare_and_swap failed"); } + +/// +// Concurrency utilities + bool os_compare_and_swap_8(u8 *a, u8 b, u8 old) { // #Portability not sure how portable this is. return _InterlockedCompareExchange8((volatile CHAR*)a, (CHAR)b, (CHAR)old) == (CHAR)old; @@ -449,6 +421,226 @@ bool os_compare_and_swap_bool(bool *a, bool b, bool old) { } + +void os_sleep(u32 ms) { + Sleep(ms); +} + +void os_yield_thread() { + SwitchToThread(); +} + +void os_high_precision_sleep(f64 ms) { + + const f64 s = ms/1000.0; + + f64 start = os_get_current_time_in_seconds(); + f64 end = start + (f64)s; + u32 sleep_time = (u32)((end-start)-1.0); + bool do_sleep = sleep_time >= 1; + + timeBeginPeriod(1); // I don't see a reason to reset this + + if (do_sleep) os_sleep(sleep_time); + + while (os_get_current_time_in_seconds() < end) { + os_yield_thread(); + } +} + + +/// +/// +// Time +/// + +#include // #Cdep +u64 os_get_current_cycle_count() { + return __rdtsc(); +} + +float64 os_get_current_time_in_seconds() { + LARGE_INTEGER frequency, counter; + if (!QueryPerformanceFrequency(&frequency) || !QueryPerformanceCounter(&counter)) { + return -1.0; + } + return (double)counter.QuadPart / (double)frequency.QuadPart; +} + + +/// +/// +// Dynamic Libraries +/// + +Dynamic_Library_Handle os_load_dynamic_library(string path) { + return LoadLibraryA(temp_convert_to_null_terminated_string(path)); +} +void *os_dynamic_library_load_symbol(Dynamic_Library_Handle l, string identifier) { + return GetProcAddress(l, temp_convert_to_null_terminated_string(identifier)); +} +void os_unload_dynamic_library(Dynamic_Library_Handle l) { + FreeLibrary(l); +} + + +/// +/// +// IO +/// + +const File OS_INVALID_FILE = INVALID_HANDLE_VALUE; +void os_write_string_to_stdout(string s) { + HANDLE win32_stdout = GetStdHandle(STD_OUTPUT_HANDLE); + if (win32_stdout == INVALID_HANDLE_VALUE) return; + + WriteFile(win32_stdout, s.data, s.count, 0, NULL); +} + +// context.allocator +u16 *win32_fixed_utf8_to_null_terminated_wide(string utf8) { + u64 utf16_length = MultiByteToWideChar(CP_UTF8, 0, (LPCCH)utf8.data, (int)utf8.count, 0, 0); + + u16 *utf16_str = (u16 *)alloc((utf16_length + 1) * sizeof(u16)); + + int result = MultiByteToWideChar(CP_UTF8, 0, (LPCCH)utf8.data, (int)utf8.count, utf16_str, utf16_length); + if (result == 0) { + dealloc(utf16_str); + return NULL; + } + + utf16_str[utf16_length] = 0; + + return utf16_str; +} +u16 *temp_win32_fixed_utf8_to_null_terminated_wide(string utf8) { + push_temp_allocator(); + u16 *result = win32_fixed_utf8_to_null_terminated_wide(utf8); + pop_allocator(); + return result; +} + +File os_file_open(string path, Os_Io_Open_Flags flags) { + DWORD access = GENERIC_READ; + DWORD creation = 0; + + if (flags & O_WRITE) { + access |= GENERIC_WRITE; + } + if (flags & O_CREATE) { + creation = CREATE_ALWAYS; + } else { + creation = OPEN_EXISTING; + } + + u16 *wide = temp_win32_fixed_utf8_to_null_terminated_wide(path); + + return CreateFileW(wide, access, 0, NULL, creation, FILE_ATTRIBUTE_NORMAL, NULL); +} + +void os_file_close(File f) { + CloseHandle(f); +} + +bool os_file_delete(string path) { + u16 *path_wide = temp_win32_fixed_utf8_to_null_terminated_wide(path); + return (bool)DeleteFileW(path_wide); +} + +bool os_file_write_string(File f, string s) { + DWORD written; + BOOL result = WriteFile(f, s.data, s.count, &written, NULL); + return result && (written == s.count); +} + +bool os_file_write_bytes(File f, void *buffer, u64 size_in_bytes) { + DWORD written; + BOOL result = WriteFile(f, buffer, (DWORD)size_in_bytes, &written, NULL); + return result && (written == size_in_bytes); +} + +bool os_file_read(File f, void* buffer, u64 bytes_to_read, u64 *actual_read_bytes) { + DWORD read; + BOOL result = ReadFile(f, buffer, (DWORD)bytes_to_read, &read, NULL); + if (actual_read_bytes) { + *actual_read_bytes = read; + } + return result; +} + +bool os_write_entire_file_handle(File f, string data) { + return os_file_write_string(f, data); +} + +bool os_write_entire_file(string path, string data) { + File file = os_file_open(path, O_WRITE | O_CREATE); + if (file == OS_INVALID_FILE) { + return false; + } + bool result = os_file_write_string(file, data); + os_file_close(file); + return result; +} + +bool os_read_entire_file_handle(File f, string *result) { + LARGE_INTEGER file_size; + if (!GetFileSizeEx(f, &file_size)) { + return false; + } + + result->data = (u8*)alloc(file_size.QuadPart); + if (!result->data) { + return false; + } + + result->count = file_size.QuadPart; + return os_file_read(f, result->data, file_size.QuadPart, NULL); +} + +bool os_read_entire_file(string path, string *result) { + File file = os_file_open(path, O_READ); + if (file == OS_INVALID_FILE) { + return false; + } + bool res = os_read_entire_file_handle(file, result); + os_file_close(file); + return res; +} + +void fprints(File f, string fmt, ...) { + va_list args; + va_start(args, fmt); + fprint_va_list_buffered(f, fmt, args); + va_end(args); +} +void fprintf(File f, const char* fmt, ...) { + va_list args; + va_start(args, fmt); + string s; + s.data = cast(u8*)fmt; + s.count = strlen(fmt); + fprint_va_list_buffered(f, s, args); + va_end(args); +} + +/// +/// +// Memory +/// + +void* os_get_stack_base() { + NT_TIB* tib = (NT_TIB*)NtCurrentTeb(); + return tib->StackBase; +} +void* os_get_stack_limit() { + NT_TIB* tib = (NT_TIB*)NtCurrentTeb(); + return tib->StackLimit; +} + + + + + void os_update() { local_persist Os_Window last_window; @@ -509,7 +701,7 @@ void os_update() { memcpy(win32_key_states, input_frame.key_states, sizeof(input_frame.key_states)); input_frame.number_of_events = 0; - + // #Simd ? for (u64 i = 0; i < INPUT_KEY_CODE_COUNT; i++) { win32_key_states[i] &= ~(INPUT_STATE_REPEAT); win32_key_states[i] &= ~(INPUT_STATE_JUST_PRESSED); diff --git a/oogabooga/os_interface.c b/oogabooga/os_interface.c index 54346c5..d932a32 100644 --- a/oogabooga/os_interface.c +++ b/oogabooga/os_interface.c @@ -5,18 +5,21 @@ typedef HANDLE Thread_Handle; typedef HMODULE Dynamic_Library_Handle; typedef HWND Window_Handle; + typedef HANDLE File; #elif defined(__linux__) typedef SOMETHING Mutex_Handle; typedef SOMETHING Thread_Handle; typedef SOMETHING Dynamic_Library_Handle; typedef SOMETHING Window_Handle; + typedef SOMETHING File; #error "Linux is not supported yet"; #elif defined(__APPLE__) && defined(__MACH__) typedef SOMETHING Mutex_Handle; typedef SOMETHING Thread_Handle; typedef SOMETHING Dynamic_Library_Handle; typedef SOMETHING Window_Handle; + typedef SOMETHING File; #error "Mac is not supported yet"; #else #error "Current OS not supported!"; @@ -128,8 +131,7 @@ Thread* os_make_thread(Thread_Proc proc); void os_start_thread(Thread* t); void os_join_thread(Thread* t); -void os_sleep(u32 ms); -void os_yield_thread(); + /// // Mutex primitive @@ -148,7 +150,7 @@ void os_spinlock_lock(Spinlock* l); void os_spinlock_unlock(Spinlock* l); /// -// Sync utilities +// Concurrency utilities bool os_compare_and_swap_8 (u8 *a, u8 b, u8 old); bool os_compare_and_swap_16 (u16 *a, u16 b, u16 old); @@ -156,6 +158,9 @@ bool os_compare_and_swap_32 (u32 *a, u32 b, u32 old); bool os_compare_and_swap_64 (u64 *a, u64 b, u64 old); bool os_compare_and_swap_bool(bool *a, bool b, bool old); +void os_sleep(u32 ms); +void os_yield_thread(); +void os_high_precision_sleep(f64 ms); /// /// @@ -182,8 +187,64 @@ void os_unload_dynamic_library(Dynamic_Library_Handle l); // IO /// +forward_global const File OS_INVALID_FILE; + void os_write_string_to_stdout(string s); +typedef enum Os_Io_Open_Flags { + O_READ = 0, + O_CREATE = 1<<0, // Will replace existing file and start writing from 0 (if writing) + O_WRITE = 1<<1, + + // To append, pass WRITE flag without CREATE flag +} Os_Io_Open_Flags; + +File os_file_open(string path, Os_Io_Open_Flags flags); +void os_file_close(File f); +bool os_file_delete(string path); + +bool os_file_write_string(File f, string s); +bool os_file_write_bytes(File f, void *buffer, u64 size_in_bytes); + +bool os_file_read(File f, void* buffer, u64 bytes_to_read, u64 *actual_read_bytes); + +bool os_write_entire_file_handle(File f, string data); +bool os_write_entire_file(string path, string data); +bool os_read_entire_file_handle(File f, string *result); +bool os_read_entire_file(string path, string *result); + + +void fprints(File f, string fmt, ...); +void fprintf(File f, const char* fmt, ...); +#define fprint(...) _Generic((FIRST_ARG(__VA_ARGS__)), \ + string: fprints, \ + default: fprintf \ + )(__VA_ARGS__) + +void fprint_va_list_buffered(File f, const string fmt, va_list args) { + + string current = fmt; + + char buffer[PRINT_BUFFER_SIZE]; + + while (true) { + u64 size = min(current.count, PRINT_BUFFER_SIZE-1); + if (current.count <= 0) break; + + memcpy(buffer, current.data, size); + + char fmt_cstring[PRINT_BUFFER_SIZE+1]; + memcpy(fmt_cstring, current.data, size); + fmt_cstring[size] = 0; + + string s = sprint_null_terminated_string_va_list_to_buffer(fmt_cstring, args, buffer, PRINT_BUFFER_SIZE); + os_file_write_string(f, s); + + current.count -= size; + current.data += size; + } +} + /// /// diff --git a/oogabooga/string.c b/oogabooga/string.c index 140575a..bcb19a8 100644 --- a/oogabooga/string.c +++ b/oogabooga/string.c @@ -73,6 +73,12 @@ string alloc_string(u64 count) { void dealloc_string(string s) { dealloc(s.data); } +string talloc_string(u64 count) { + push_temp_allocator(); + string s = alloc_string(count); + pop_allocator(); + return s; +} // context.allocator ! string string_concat(const string left, const string right) { @@ -97,13 +103,12 @@ char *temp_convert_to_null_terminated_string(const string s) { pop_allocator(); return c; } - bool strings_match(string a, string b) { if (a.count != b.count) return false; // Count match, pointer match: they are the same if (a.data == b.data) return true; - + return memcmp(a.data, b.data, a.count) == 0; } diff --git a/oogabooga/string_format.c b/oogabooga/string_format.c index d492fdd..a8c4410 100644 --- a/oogabooga/string_format.c +++ b/oogabooga/string_format.c @@ -203,6 +203,7 @@ void print_va_list_buffered(const string fmt, va_list args) { } } + // context.allocator (alloc & dealloc) void prints(const string fmt, ...) { va_list args; diff --git a/oogabooga/tests.c b/oogabooga/tests.c index 8e32249..06bd98c 100644 --- a/oogabooga/tests.c +++ b/oogabooga/tests.c @@ -190,20 +190,11 @@ void test_thread_proc1(Thread* t) { os_sleep(5); printf("Hello from thread %llu\n", t->id); } -Mutex_Handle test_mutex; -void test_thread_proc2(Thread* t) { - os_lock_mutex(test_mutex); - printf("Thread %llu part 1\n", t->id); - os_sleep(1); - printf("Thread %llu part 2\n", t->id); - os_sleep(1); - printf("Thread %llu part 3\n", t->id); - os_unlock_mutex(test_mutex); -} + void test_threads() { Thread* t = os_make_thread(test_thread_proc1); os_start_thread(t); - os_sleep(10); + os_sleep(20); printf("This should be printed in middle of thread execution\n"); os_join_thread(t); printf("Thread is joined\n"); @@ -211,17 +202,6 @@ void test_threads() { Mutex_Handle m = os_make_mutex(); os_lock_mutex(m); os_unlock_mutex(m); - - - test_mutex = os_make_mutex(); - Thread *threads[100]; - for (int i = 0; i < 100; i++) { - threads[i] = os_make_thread(test_thread_proc2); - os_start_thread(threads[i]); - } - for (int i = 0; i < 100; i++) { - os_join_thread(threads[i]); - } } void test_allocator_threaded(Thread *t) { @@ -266,8 +246,8 @@ void test_strings() { dealloc_string(alloc_str); // Test string_concat - string str1 = const_string("Hello, "); - string str2 = const_string("World!"); + string str1 = fixed_string("Hello, "); + string str2 = fixed_string("World!"); string concat_str = string_concat(str1, str2); assert(concat_str.count == str1.count + str2.count, "Failed: string_concat"); assert(memcmp(concat_str.data, "Hello, World!", concat_str.count) == 0, "Failed: string_concat"); @@ -284,7 +264,7 @@ void test_strings() { // No need to dealloc, it's temporary storage // Test sprint - string format_str = const_string("Number: %d"); + string format_str = fixed_string("Number: %d"); string formatted_str = sprint(format_str, 42); char* formatted_cstr = convert_to_null_terminated_string(formatted_str); assert(strcmp(formatted_cstr, "Number: 42") == 0, "Failed: sprint"); @@ -299,16 +279,16 @@ void test_strings() { // Test print and printf (visual inspection) printf("Expected output: Hello, World!\n"); - print("Hello, %s!\n", const_string("World")); + print("Hello, %s!\n", fixed_string("World")); printf("Expected output: Number: 1234\n"); - print(const_string("Number: %d\n"), 1234); + print(fixed_string("Number: %d\n"), 1234); printf("Expected output: Number: 1234\n"); - print(const_string("Number: %d\n"), 1234); + print(fixed_string("Number: %d\n"), 1234); printf("Expected output: Mixed values: 42 and 3.14\n"); - print(const_string("Mixed values: %d and %.2f\n"), 42, 3.14); + print(fixed_string("Mixed values: %d and %.2f\n"), 42, 3.14); // This should fail assert and print descriptive error //printf("Expected output (printf): Hello, World!\n"); @@ -327,7 +307,7 @@ void test_strings() { printf("Mixed values: %d and %.2f\n", 99, 2.71); // Test handling of empty strings - string empty_str = const_string(""); + string empty_str = fixed_string(""); string concat_empty_str = string_concat(empty_str, empty_str); assert(concat_empty_str.count == 0, "Failed: string_concat with empty strings"); dealloc_string(concat_empty_str); @@ -342,35 +322,118 @@ void test_strings() { dealloc_string(large_concat_str); // Test string with special characters - string special_char_str = const_string("Special chars: \n\t\r"); + string special_char_str = fixed_string("Special chars: \n\t\r"); cstr = convert_to_null_terminated_string(special_char_str); assert(strcmp(cstr, "Special chars: \n\t\r") == 0, "Failed: special character string"); dealloc(cstr); string a = tprintf("Hello, %cs!\n", "balls"); string balls1 = string_view(a, 7, 5); - string balls2 = const_string("balls"); + string balls2 = fixed_string("balls"); assert(strings_match(balls1, balls2), "String match failed"); assert(!strings_match(balls1, a), "String match failed"); } +void test_file_io() { +#if TARGET_OS == WINDOWS + // Test win32_fixed_utf8_to_null_terminated_wide + string utf8_str = fixed_string("Test"); + u16 *wide_str = win32_fixed_utf8_to_null_terminated_wide(utf8_str); + assert(wide_str != NULL, "Failed: win32_fixed_utf8_to_null_terminated_wide"); + assert(wide_str[4] == 0, "Failed: win32_fixed_utf8_to_null_terminated_wide"); + dealloc(wide_str); + + // Test temp_win32_fixed_utf8_to_null_terminated_wide + push_temp_allocator(); + wide_str = temp_win32_fixed_utf8_to_null_terminated_wide(utf8_str); + assert(wide_str != NULL, "Failed: temp_win32_fixed_utf8_to_null_terminated_wide"); + assert(wide_str[4] == 0, "Failed: temp_win32_fixed_utf8_to_null_terminated_wide"); + pop_allocator(); +#endif + + File file = OS_INVALID_FILE; + + os_file_close(file); + // Test os_file_open and os_file_close + file = os_file_open(fixed_string("test.txt"), O_WRITE | O_CREATE); + assert(file != OS_INVALID_FILE, "Failed: os_file_open (write/create)"); + os_file_close(file); + + + // Test os_file_write_string and os_file_read + string hello_world_write = fixed_string("Hello, World!"); + file = os_file_open(fixed_string("test.txt"), O_WRITE | O_CREATE); + assert(file != OS_INVALID_FILE, "Failed: os_file_open (write/create)"); + bool write_result = os_file_write_string(file, hello_world_write); + assert(write_result, "Failed: os_file_write_string"); + os_file_close(file); + + file = os_file_open(fixed_string("test.txt"), O_READ); + assert(file != OS_INVALID_FILE, "Failed: os_file_open (read)"); + string hello_world_read = talloc_string(hello_world_write.count); + bool read_result = os_file_read(file, hello_world_read.data, hello_world_read.count, &hello_world_read.count); + assert(read_result, "Failed: os_file_read %d", GetLastError()); + assert(strings_match(hello_world_read, hello_world_write), "Failed: os_file_read write/read mismatch"); + os_file_close(file); + + // Test os_file_write_bytes + file = os_file_open(fixed_string("test_bytes.txt"), O_WRITE | O_CREATE); + assert(file != OS_INVALID_FILE, "Failed: os_file_open (write/create)"); + int int_data = 42; + write_result = os_file_write_bytes(file, &int_data, sizeof(int)); + assert(write_result, "Failed: os_file_write_bytes"); + os_file_close(file); + + // Test os_read_entire_file and os_write_entire_file + string write_data = fixed_string("Entire file test"); + bool write_entire_result = os_write_entire_file(fixed_string("entire_test.txt"), write_data); + assert(write_entire_result, "Failed: os_write_entire_file"); + + string read_data; + bool read_entire_result = os_read_entire_file(fixed_string("entire_test.txt"), &read_data); + assert(read_entire_result, "Failed: os_read_entire_file"); + assert(strings_match(read_data, write_data), "Failed: os_read_entire_file write/read mismatch"); + assert(memcmp(read_data.data, write_data.data, write_data.count) == 0, "Failed: os_read_entire_file (content mismatch)"); + dealloc(read_data.data); + + // Test fprint + File balls = os_file_open(fixed_string("balls.txt"), O_WRITE | O_CREATE); + assert(balls != OS_INVALID_FILE, "Failed: Could not create balls.txt"); + fprint(balls, "Hello, %cs!", "Balls"); + os_file_close(balls); + string hello_balls; + read_entire_result = os_read_entire_file(fixed_string("balls.txt"), &hello_balls); + assert(read_entire_result, "Failed: could not read balls.txt"); + assert(strings_match(hello_balls, fixed_string("Hello, Balls!")), "Failed: balls read/write mismatch. Expected 'Hello, Balls!', got '%s'", hello_balls); + + // Clean up test files + bool delete_ok = false; + delete_ok = os_file_delete(fixed_string("test.txt")); + assert(delete_ok, "Failed: could not delete test.txt"); + delete_ok = os_file_delete(fixed_string("test_bytes.txt")); + assert(delete_ok, "Failed: could not delete test_bytes.txt"); + delete_ok = os_file_delete(fixed_string("entire_test.txt")); + assert(delete_ok, "Failed: could not delete entire_test.txt"); + delete_ok = os_file_delete(fixed_string("balls.txt")); + assert(delete_ok, "Failed: could not delete balls.txt"); +} void oogabooga_run_tests() { - printf("Testing allocator...\n"); + printf("Testing allocator... "); test_allocator(true); printf("OK!\n"); - printf("Testing threads...\n"); + printf("Testing threads... "); test_threads(); printf("OK!\n"); - printf("Testing strings...\n"); + printf("Testing strings... "); test_strings(); printf("OK!\n"); - printf("Thread bombing allocator...\n"); + printf("Thread bombing allocator... "); Thread* threads[100]; for (int i = 0; i < 100; i++) { threads[i] = os_make_thread(test_allocator_threaded); @@ -380,4 +443,8 @@ void oogabooga_run_tests() { os_join_thread(threads[i]); } printf("OK!\n"); + + printf("Testing file IO... "); + test_file_io(); + printf("OK!\n"); } \ No newline at end of file