- 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)
This commit is contained in:
parent
8a0fc81576
commit
6879130bb0
12 changed files with 486 additions and 143 deletions
2
build.c
2
build.c
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
///
|
///
|
||||||
// Build config stuff
|
// Build config stuff
|
||||||
#define RUN_TESTS 0
|
#define RUN_TESTS 1
|
||||||
|
|
||||||
// When we need very debug
|
// When we need very debug
|
||||||
// #define CONFIGURATION VERY_DEBUG
|
// #define CONFIGURATION VERY_DEBUG
|
||||||
|
|
|
@ -4,6 +4,6 @@ mkdir build
|
||||||
|
|
||||||
pushd 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
|
popd
|
|
@ -7,7 +7,7 @@ pushd build
|
||||||
mkdir release
|
mkdir release
|
||||||
pushd 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
|
||||||
popd
|
popd
|
9
entry.c
9
entry.c
|
@ -10,6 +10,9 @@ int start(int argc, char **argv) {
|
||||||
|
|
||||||
seed_for_random = os_get_current_cycle_count();
|
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();
|
float64 last_time = os_get_current_time_in_seconds();
|
||||||
while (!window.should_close) {
|
while (!window.should_close) {
|
||||||
reset_temporary_storage();
|
reset_temporary_storage();
|
||||||
|
@ -19,6 +22,11 @@ int start(int argc, char **argv) {
|
||||||
|
|
||||||
float64 now = os_get_current_time_in_seconds();
|
float64 now = os_get_current_time_in_seconds();
|
||||||
float64 delta = now - last_time;
|
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;
|
last_time = now;
|
||||||
|
|
||||||
if (is_key_just_released(KEY_ESCAPE)) {
|
if (is_key_just_released(KEY_ESCAPE)) {
|
||||||
|
@ -27,6 +35,7 @@ int start(int argc, char **argv) {
|
||||||
|
|
||||||
if (is_key_just_released('E')) {
|
if (is_key_just_released('E')) {
|
||||||
print("Mouse pos: %f, %f\n", input_frame.mouse_x, input_frame.mouse_y);
|
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++) {
|
for (u64 i = 0; i < input_frame.number_of_events; i++) {
|
||||||
|
|
|
@ -23,6 +23,8 @@ typedef u8 bool;
|
||||||
|
|
||||||
#define local_persist static
|
#define local_persist static
|
||||||
|
|
||||||
|
#define forward_global extern
|
||||||
|
|
||||||
#define ifnt(x) if (!(x))
|
#define ifnt(x) if (!(x))
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
#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_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__);
|
#define assert(cond, ...) assert_line(__LINE__, cond, __VA_ARGS__);
|
||||||
|
|
||||||
#if CONFIGRATION == RELEASE
|
#if CONFIGURATION == RELEASE
|
||||||
#undef assert
|
#undef assert
|
||||||
#define assert(...)
|
#define assert(...)
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -298,7 +298,7 @@ void *heap_alloc(u64 size) {
|
||||||
meta->size = size;
|
meta->size = size;
|
||||||
meta->block = best_fit_block;
|
meta->block = best_fit_block;
|
||||||
|
|
||||||
#if VERY_DEBUG
|
#if CONFIGURATION == VERY_DEBUG
|
||||||
santiy_check_free_node_tree(meta->block);
|
santiy_check_free_node_tree(meta->block);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -368,7 +368,7 @@ void heap_dealloc(void *p) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#if VERY_DEBUG
|
#if CONFIGURATION == VERY_DEBUG
|
||||||
santiy_check_free_node_tree(block);
|
santiy_check_free_node_tree(block);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -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
|
// This needs to be included before dependencies
|
||||||
#include "base.c"
|
#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
|
#ifdef _WIN32
|
||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
#define OS_WINDOWS
|
#define TARGET_OS WINDOWS
|
||||||
#elif defined(__linux__)
|
#elif defined(__linux__)
|
||||||
// Include whatever #Incomplete #Portability
|
// Include whatever #Incomplete #Portability
|
||||||
#define OS_LINUX
|
#define TARGET_OS LINUX
|
||||||
#error "Linux is not supported yet";
|
#error "Linux is not supported yet";
|
||||||
#elif defined(__APPLE__) && defined(__MACH__)
|
#elif defined(__APPLE__) && defined(__MACH__)
|
||||||
// Include whatever #Incomplete #Portability
|
// Include whatever #Incomplete #Portability
|
||||||
#define OS_MAC
|
#define TARGET_OS MACOS
|
||||||
#error "Mac is not supported yet";
|
#error "Mac is not supported yet";
|
||||||
#else
|
#else
|
||||||
#error "Current OS not supported!";
|
#error "Current OS not supported!";
|
||||||
|
@ -69,11 +75,11 @@ void lodepng_free(void* ptr) {
|
||||||
|
|
||||||
#ifndef GFX_RENDERER
|
#ifndef GFX_RENDERER
|
||||||
// #Portability
|
// #Portability
|
||||||
#ifdef OS_WINDOWS
|
#if TARGET_OS == WINDOWS
|
||||||
#define GFX_RENDERER GFX_RENDERER_D3D11
|
#define GFX_RENDERER GFX_RENDERER_D3D11
|
||||||
#elif defined (OS_LINUX)
|
#elif TARGET_OS == LINUX
|
||||||
#define GFX_RENDERER GFX_RENDERER_VULKAN
|
#define GFX_RENDERER GFX_RENDERER_VULKAN
|
||||||
#elif defined (OS_MAC)
|
#elif TARGET_OS == MACOS
|
||||||
#define GFX_RENDERER GFX_RENDERER_METAL
|
#define GFX_RENDERER GFX_RENDERER_METAL
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
@ -107,11 +113,11 @@ void lodepng_free(void* ptr) {
|
||||||
#error "Unknown renderer defined in GFX_RENDERER"
|
#error "Unknown renderer defined in GFX_RENDERER"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef OS_WINDOWS
|
#if TARGET_OS == WINDOWS
|
||||||
#include "os_impl_windows.c"
|
#include "os_impl_windows.c"
|
||||||
#elif defined (OS_LINUX)
|
#elif TARGET_OS == LINUX
|
||||||
|
|
||||||
#elif defined (OS_MAC)
|
#elif TARGET_OS == MACOS
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -264,6 +264,8 @@ bool os_grow_program_memory(u64 new_size) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
program_memory_size = aligned_size;
|
program_memory_size = aligned_size;
|
||||||
|
|
||||||
|
memset(program_memory, 0xBA, program_memory_size);
|
||||||
} else {
|
} else {
|
||||||
void* tail = (u8*)program_memory + program_memory_size;
|
void* tail = (u8*)program_memory + program_memory_size;
|
||||||
u64 m = ((u64)program_memory_size % os.granularity);
|
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!");
|
assert(m == 0, "amount_to_allocate is not aligned to granularity!");
|
||||||
// Just keep allocating at the tail of the current chunk
|
// Just keep allocating at the tail of the current chunk
|
||||||
void* result = VirtualAlloc(tail, amount_to_allocate, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
|
void* result = VirtualAlloc(tail, amount_to_allocate, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
|
||||||
|
memset(result, 0xBA, amount_to_allocate);
|
||||||
if (result == 0) {
|
if (result == 0) {
|
||||||
os_unlock_mutex(program_memory_mutex); // #Sync
|
os_unlock_mutex(program_memory_mutex); // #Sync
|
||||||
return false;
|
return false;
|
||||||
|
@ -294,31 +297,15 @@ bool os_grow_program_memory(u64 new_size) {
|
||||||
return true;
|
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;
|
// Threading
|
||||||
|
///
|
||||||
|
|
||||||
case WAIT_ABANDONED:
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
///
|
||||||
assert(false, "Unexpected mutex lock result");
|
// Thread primitive
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void os_unlock_mutex(Mutex_Handle m) {
|
|
||||||
BOOL result = ReleaseMutex(m);
|
|
||||||
assert(result, "Unlock mutex failed");
|
|
||||||
}
|
|
||||||
|
|
||||||
DWORD WINAPI win32_thread_invoker(LPVOID param) {
|
DWORD WINAPI win32_thread_invoker(LPVOID param) {
|
||||||
Thread *t = (Thread*)param;
|
Thread *t = (Thread*)param;
|
||||||
|
@ -353,56 +340,37 @@ void os_join_thread(Thread *t) {
|
||||||
CloseHandle(t->os_handle);
|
CloseHandle(t->os_handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
void os_sleep(u32 ms) {
|
///
|
||||||
Sleep(ms);
|
// Mutex primitive
|
||||||
}
|
|
||||||
|
|
||||||
void os_yield_thread() {
|
Mutex_Handle os_make_mutex() {
|
||||||
SwitchToThread();
|
return CreateMutex(0, FALSE, 0);
|
||||||
}
|
}
|
||||||
|
void os_destroy_mutex(Mutex_Handle m) {
|
||||||
#include <intrin.h>
|
CloseHandle(m);
|
||||||
u64 os_get_current_cycle_count() {
|
|
||||||
return __rdtsc();
|
|
||||||
}
|
}
|
||||||
|
void os_lock_mutex(Mutex_Handle m) {
|
||||||
|
DWORD wait_result = WaitForSingleObject(m, INFINITE);
|
||||||
|
|
||||||
float64 os_get_current_time_in_seconds() {
|
switch (wait_result) {
|
||||||
LARGE_INTEGER frequency, counter;
|
case WAIT_OBJECT_0:
|
||||||
if (!QueryPerformanceFrequency(&frequency) || !QueryPerformanceCounter(&counter)) {
|
break;
|
||||||
return -1.0;
|
|
||||||
|
case WAIT_ABANDONED:
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
assert(false, "Unexpected mutex lock result");
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
return (double)counter.QuadPart / (double)frequency.QuadPart;
|
}
|
||||||
|
void os_unlock_mutex(Mutex_Handle m) {
|
||||||
|
BOOL result = ReleaseMutex(m);
|
||||||
|
assert(result, "Unlock mutex failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///
|
||||||
Dynamic_Library_Handle os_load_dynamic_library(string path) {
|
// Spinlock "primitive"
|
||||||
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;
|
|
||||||
|
|
||||||
WriteFile(win32_stdout, s.data, s.count, 0, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
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 *os_make_spinlock() {
|
Spinlock *os_make_spinlock() {
|
||||||
Spinlock *l = cast(Spinlock*)alloc(sizeof(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");
|
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) {
|
bool os_compare_and_swap_8(u8 *a, u8 b, u8 old) {
|
||||||
// #Portability not sure how portable this is.
|
// #Portability not sure how portable this is.
|
||||||
return _InterlockedCompareExchange8((volatile CHAR*)a, (CHAR)b, (CHAR)old) == (CHAR)old;
|
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 <intrin.h> // #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() {
|
void os_update() {
|
||||||
|
|
||||||
local_persist Os_Window last_window;
|
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));
|
memcpy(win32_key_states, input_frame.key_states, sizeof(input_frame.key_states));
|
||||||
input_frame.number_of_events = 0;
|
input_frame.number_of_events = 0;
|
||||||
|
|
||||||
|
// #Simd ?
|
||||||
for (u64 i = 0; i < INPUT_KEY_CODE_COUNT; i++) {
|
for (u64 i = 0; i < INPUT_KEY_CODE_COUNT; i++) {
|
||||||
win32_key_states[i] &= ~(INPUT_STATE_REPEAT);
|
win32_key_states[i] &= ~(INPUT_STATE_REPEAT);
|
||||||
win32_key_states[i] &= ~(INPUT_STATE_JUST_PRESSED);
|
win32_key_states[i] &= ~(INPUT_STATE_JUST_PRESSED);
|
||||||
|
|
|
@ -5,18 +5,21 @@
|
||||||
typedef HANDLE Thread_Handle;
|
typedef HANDLE Thread_Handle;
|
||||||
typedef HMODULE Dynamic_Library_Handle;
|
typedef HMODULE Dynamic_Library_Handle;
|
||||||
typedef HWND Window_Handle;
|
typedef HWND Window_Handle;
|
||||||
|
typedef HANDLE File;
|
||||||
|
|
||||||
#elif defined(__linux__)
|
#elif defined(__linux__)
|
||||||
typedef SOMETHING Mutex_Handle;
|
typedef SOMETHING Mutex_Handle;
|
||||||
typedef SOMETHING Thread_Handle;
|
typedef SOMETHING Thread_Handle;
|
||||||
typedef SOMETHING Dynamic_Library_Handle;
|
typedef SOMETHING Dynamic_Library_Handle;
|
||||||
typedef SOMETHING Window_Handle;
|
typedef SOMETHING Window_Handle;
|
||||||
|
typedef SOMETHING File;
|
||||||
#error "Linux is not supported yet";
|
#error "Linux is not supported yet";
|
||||||
#elif defined(__APPLE__) && defined(__MACH__)
|
#elif defined(__APPLE__) && defined(__MACH__)
|
||||||
typedef SOMETHING Mutex_Handle;
|
typedef SOMETHING Mutex_Handle;
|
||||||
typedef SOMETHING Thread_Handle;
|
typedef SOMETHING Thread_Handle;
|
||||||
typedef SOMETHING Dynamic_Library_Handle;
|
typedef SOMETHING Dynamic_Library_Handle;
|
||||||
typedef SOMETHING Window_Handle;
|
typedef SOMETHING Window_Handle;
|
||||||
|
typedef SOMETHING File;
|
||||||
#error "Mac is not supported yet";
|
#error "Mac is not supported yet";
|
||||||
#else
|
#else
|
||||||
#error "Current OS not supported!";
|
#error "Current OS not supported!";
|
||||||
|
@ -128,8 +131,7 @@ Thread* os_make_thread(Thread_Proc proc);
|
||||||
void os_start_thread(Thread* t);
|
void os_start_thread(Thread* t);
|
||||||
void os_join_thread(Thread* t);
|
void os_join_thread(Thread* t);
|
||||||
|
|
||||||
void os_sleep(u32 ms);
|
|
||||||
void os_yield_thread();
|
|
||||||
|
|
||||||
///
|
///
|
||||||
// Mutex primitive
|
// Mutex primitive
|
||||||
|
@ -148,7 +150,7 @@ void os_spinlock_lock(Spinlock* l);
|
||||||
void os_spinlock_unlock(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_8 (u8 *a, u8 b, u8 old);
|
||||||
bool os_compare_and_swap_16 (u16 *a, u16 b, u16 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_64 (u64 *a, u64 b, u64 old);
|
||||||
bool os_compare_and_swap_bool(bool *a, bool b, bool 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
|
// IO
|
||||||
///
|
///
|
||||||
|
|
||||||
|
forward_global const File OS_INVALID_FILE;
|
||||||
|
|
||||||
void os_write_string_to_stdout(string s);
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
///
|
///
|
||||||
///
|
///
|
||||||
|
|
|
@ -73,6 +73,12 @@ string alloc_string(u64 count) {
|
||||||
void dealloc_string(string s) {
|
void dealloc_string(string s) {
|
||||||
dealloc(s.data);
|
dealloc(s.data);
|
||||||
}
|
}
|
||||||
|
string talloc_string(u64 count) {
|
||||||
|
push_temp_allocator();
|
||||||
|
string s = alloc_string(count);
|
||||||
|
pop_allocator();
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
// context.allocator !
|
// context.allocator !
|
||||||
string string_concat(const string left, const string right) {
|
string string_concat(const string left, const string right) {
|
||||||
|
@ -97,7 +103,6 @@ char *temp_convert_to_null_terminated_string(const string s) {
|
||||||
pop_allocator();
|
pop_allocator();
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool strings_match(string a, string b) {
|
bool strings_match(string a, string b) {
|
||||||
if (a.count != b.count) return false;
|
if (a.count != b.count) return false;
|
||||||
|
|
||||||
|
|
|
@ -203,6 +203,7 @@ void print_va_list_buffered(const string fmt, va_list args) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// context.allocator (alloc & dealloc)
|
// context.allocator (alloc & dealloc)
|
||||||
void prints(const string fmt, ...) {
|
void prints(const string fmt, ...) {
|
||||||
va_list args;
|
va_list args;
|
||||||
|
|
|
@ -190,20 +190,11 @@ void test_thread_proc1(Thread* t) {
|
||||||
os_sleep(5);
|
os_sleep(5);
|
||||||
printf("Hello from thread %llu\n", t->id);
|
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() {
|
void test_threads() {
|
||||||
Thread* t = os_make_thread(test_thread_proc1);
|
Thread* t = os_make_thread(test_thread_proc1);
|
||||||
os_start_thread(t);
|
os_start_thread(t);
|
||||||
os_sleep(10);
|
os_sleep(20);
|
||||||
printf("This should be printed in middle of thread execution\n");
|
printf("This should be printed in middle of thread execution\n");
|
||||||
os_join_thread(t);
|
os_join_thread(t);
|
||||||
printf("Thread is joined\n");
|
printf("Thread is joined\n");
|
||||||
|
@ -211,17 +202,6 @@ void test_threads() {
|
||||||
Mutex_Handle m = os_make_mutex();
|
Mutex_Handle m = os_make_mutex();
|
||||||
os_lock_mutex(m);
|
os_lock_mutex(m);
|
||||||
os_unlock_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) {
|
void test_allocator_threaded(Thread *t) {
|
||||||
|
@ -266,8 +246,8 @@ void test_strings() {
|
||||||
dealloc_string(alloc_str);
|
dealloc_string(alloc_str);
|
||||||
|
|
||||||
// Test string_concat
|
// Test string_concat
|
||||||
string str1 = const_string("Hello, ");
|
string str1 = fixed_string("Hello, ");
|
||||||
string str2 = const_string("World!");
|
string str2 = fixed_string("World!");
|
||||||
string concat_str = string_concat(str1, str2);
|
string concat_str = string_concat(str1, str2);
|
||||||
assert(concat_str.count == str1.count + str2.count, "Failed: string_concat");
|
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");
|
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
|
// No need to dealloc, it's temporary storage
|
||||||
|
|
||||||
// Test sprint
|
// Test sprint
|
||||||
string format_str = const_string("Number: %d");
|
string format_str = fixed_string("Number: %d");
|
||||||
string formatted_str = sprint(format_str, 42);
|
string formatted_str = sprint(format_str, 42);
|
||||||
char* formatted_cstr = convert_to_null_terminated_string(formatted_str);
|
char* formatted_cstr = convert_to_null_terminated_string(formatted_str);
|
||||||
assert(strcmp(formatted_cstr, "Number: 42") == 0, "Failed: sprint");
|
assert(strcmp(formatted_cstr, "Number: 42") == 0, "Failed: sprint");
|
||||||
|
@ -299,16 +279,16 @@ void test_strings() {
|
||||||
|
|
||||||
// Test print and printf (visual inspection)
|
// Test print and printf (visual inspection)
|
||||||
printf("Expected output: Hello, World!\n");
|
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");
|
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");
|
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");
|
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
|
// This should fail assert and print descriptive error
|
||||||
//printf("Expected output (printf): Hello, World!\n");
|
//printf("Expected output (printf): Hello, World!\n");
|
||||||
|
@ -327,7 +307,7 @@ void test_strings() {
|
||||||
printf("Mixed values: %d and %.2f\n", 99, 2.71);
|
printf("Mixed values: %d and %.2f\n", 99, 2.71);
|
||||||
|
|
||||||
// Test handling of empty strings
|
// 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);
|
string concat_empty_str = string_concat(empty_str, empty_str);
|
||||||
assert(concat_empty_str.count == 0, "Failed: string_concat with empty strings");
|
assert(concat_empty_str.count == 0, "Failed: string_concat with empty strings");
|
||||||
dealloc_string(concat_empty_str);
|
dealloc_string(concat_empty_str);
|
||||||
|
@ -342,35 +322,118 @@ void test_strings() {
|
||||||
dealloc_string(large_concat_str);
|
dealloc_string(large_concat_str);
|
||||||
|
|
||||||
// Test string with special characters
|
// 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);
|
cstr = convert_to_null_terminated_string(special_char_str);
|
||||||
assert(strcmp(cstr, "Special chars: \n\t\r") == 0, "Failed: special character string");
|
assert(strcmp(cstr, "Special chars: \n\t\r") == 0, "Failed: special character string");
|
||||||
dealloc(cstr);
|
dealloc(cstr);
|
||||||
|
|
||||||
string a = tprintf("Hello, %cs!\n", "balls");
|
string a = tprintf("Hello, %cs!\n", "balls");
|
||||||
string balls1 = string_view(a, 7, 5);
|
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, balls2), "String match failed");
|
||||||
assert(!strings_match(balls1, a), "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() {
|
void oogabooga_run_tests() {
|
||||||
printf("Testing allocator...\n");
|
printf("Testing allocator... ");
|
||||||
test_allocator(true);
|
test_allocator(true);
|
||||||
printf("OK!\n");
|
printf("OK!\n");
|
||||||
|
|
||||||
printf("Testing threads...\n");
|
printf("Testing threads... ");
|
||||||
test_threads();
|
test_threads();
|
||||||
printf("OK!\n");
|
printf("OK!\n");
|
||||||
|
|
||||||
printf("Testing strings...\n");
|
printf("Testing strings... ");
|
||||||
test_strings();
|
test_strings();
|
||||||
printf("OK!\n");
|
printf("OK!\n");
|
||||||
|
|
||||||
printf("Thread bombing allocator...\n");
|
printf("Thread bombing allocator... ");
|
||||||
Thread* threads[100];
|
Thread* threads[100];
|
||||||
for (int i = 0; i < 100; i++) {
|
for (int i = 0; i < 100; i++) {
|
||||||
threads[i] = os_make_thread(test_allocator_threaded);
|
threads[i] = os_make_thread(test_allocator_threaded);
|
||||||
|
@ -380,4 +443,8 @@ void oogabooga_run_tests() {
|
||||||
os_join_thread(threads[i]);
|
os_join_thread(threads[i]);
|
||||||
}
|
}
|
||||||
printf("OK!\n");
|
printf("OK!\n");
|
||||||
|
|
||||||
|
printf("Testing file IO... ");
|
||||||
|
test_file_io();
|
||||||
|
printf("OK!\n");
|
||||||
}
|
}
|
Reference in a new issue