port os functions, add testing module

This commit is contained in:
Krzosa Karol
2025-02-02 17:34:06 +01:00
parent 85a6c218b2
commit 9f33ea0914
27 changed files with 1120 additions and 889 deletions

View File

@@ -4,4 +4,28 @@
#include "os_win32.c"
#else
#include "os_unix.c"
#endif
#endif
fn_test void os_test(void) {
os_date_t local_time = os_local_time();
os_date_t universal_time = os_universal_time();
debugf("local_time = %S | universal_time = %S", os_format_date(tcx->temp, local_time), os_format_date(tcx->temp, universal_time));
s8_t exe_dir = os_exe_dir(tcx->temp);
assert(exe_dir.str[exe_dir.len - 1] == '/');
s8_t exe = os_exe(tcx->temp);
s8_t cwd = os_cwd(tcx->temp);
assert(exe_dir.str[cwd.len - 1] == '/');
assert(os_is_dir(exe_dir));
assert(os_is_file(exe));
assert(os_is_dir(cwd));
assert(os_exists(exe_dir));
assert(os_exists(exe));
assert(os_exists(cwd));
assert(os_is_abs(exe_dir));
assert(os_is_abs(exe));
assert(os_is_abs(cwd));
assert(!os_is_abs(s8_lit("../path/")));
debugf("%S %S %S", exe_dir, exe, cwd);
}

View File

@@ -9,42 +9,73 @@ struct os_date_t {
u16 year;
};
fn os_date_t os_local_time_now(void);
fn os_date_t os_universal_time_now(void);
fn f64 os_milliseconds_now(void);
typedef struct os_iter_t os_iter_t;
struct os_iter_t {
s8_t abs;
s8_t rel;
s8_t name;
b8 is_directory;
b8 is_valid;
#if 0
fn u64 os_microseconds_now();
fn f64 os_seconds_now();
fn u32 os_unix_time_now();
s8_t path;
ma_arena_t *arena;
fn os_date_t os_local_time_to_universal_time();
fn os_date_t os_universal_time_to_local_time();
union {
struct w32_file_iter_t *w32;
void *platform;
};
};
fn void os_sleep_milliseconds();
typedef enum {
os_mkdir_success,
os_mkdir_file_exists,
os_mkdir_path_not_found,
os_mkdir_other_error,
} os_mkdir_t;
typedef enum {
os_write_success,
os_write_path_not_found,
os_write_other_error,
} os_write_t;
fn os_date_t os_local_time(void);
fn os_date_t os_universal_time(void);
fn s8_t os_format_date(ma_arena_t *arena, os_date_t date);
fn f64 os_seconds(void);
fn f64 os_milliseconds(void);
fn b32 os_copy(s8_t from, s8_t to, b32 overwrite);
fn b32 os_delete(s8_t path);
fn os_mkdir_t os_mkdir(s8_t path);
fn os_write_t os_write(s8_t path, s8_t content);
fn void os_advance(os_iter_t *it);
fn os_iter_t *os_iter(ma_arena_t *arena, s8_t path);
fn i64 os_mod_time(s8_t file);
fn s8_t os_abs(ma_arena_t *arena, s8_t rel);
fn s8_t os_exe(ma_arena_t *arena);
fn s8_t os_exe_dir(ma_arena_t *arena);
fn b32 os_is_dir(s8_t path);
fn b32 os_is_file(s8_t path);
fn b32 os_is_abs(s8_t path);
fn b32 os_exists(s8_t path);
fn s8_t os_cwd(ma_arena_t *arena);
fn void os_set_cwd(s8_t new_cwd);
fn s8_t os_list_files(arena, path, recursive);
fn os_file_iter_t os_iterate_files();
fn void os_advance()
fn b32 os_is_valid();
typedef i32 os_thread_fn_t(void *user_data);
typedef struct os_thread_t os_thread_t;
struct os_thread_t {
b32 exited;
i32 exit_code;
union {
struct w32_thread_t *w32;
void *ptr;
};
};
fn s8_t os_read_file(ma_arena_t *arena, s8_t path);
fn void os_write_file(s8_t path, s8_t content);
fn void os_make_dir(s8_t path);
fn os_result_t os_copy_file(s8_t from, s8_t to, b32 overwrite);
fn os_result_t os_delete_file(s8_t path);
fn i64 os_get_file_mod_time(s8_t path);
fn b32 os_path_exists(s8_t path);
fn b32 os_path_is_dir(s8_t path);
fn b32 os_path_is_file(s8_t path);
fn b32 os_path_is_abs(s8_t path);
fn b32 os_path_is_rel(s8_t path);
fn s8_t os_path_to_abs(ma_arena_t *arena, s8_t path);
fn s8_t os_path_exe(ma_arena_t *arena);
fn s8_t os_path_exe_dir(ma_arena_t *arena);
fn s8_t os_path_cwd(ma_arena_t *arena);
fn void os_path_set_cwd(s8_t path);
#endif
fn os_thread_t os_thread_create(ma_arena_t *arena, os_thread_fn_t *func, void *user_data);
fn i32 os_thread_wait_for_exit(os_thread_t *thread);

View File

@@ -1,20 +0,0 @@
fn_wasm_import void wasm_local_time_now(void *buff, i32 size);
fn_wasm_import void wasm_universal_time_now(void *buff, i32 size);
fn_wasm_import f64 wasm_milliseconds_now(void);
fn os_date_t os_local_time_now(void) {
os_date_t result = {0};
wasm_local_time_now(&result, sizeof(result));
return result;
}
fn os_date_t os_universal_time_now(void) {
os_date_t result = {0};
wasm_universal_time_now(&result, sizeof(result));
return result;
}
fn f64 os_milliseconds_now(void) {
return wasm_milliseconds_now();
}

View File

@@ -1,18 +1,39 @@
fn os_date_t os_local_time_now(void) {
os_date_t result = {0};
///////////////////////////////
// time
fn os_date_t os_local_time(void) {
SYSTEMTIME lt;
GetLocalTime(&lt);
result.ms = lt.wMilliseconds;
result.sec = lt.wSecond;
result.min = lt.wMinute;
result.hour = lt.wHour;
result.day = lt.wDay;
result.month = lt.wMonth;
result.year = lt.wYear;
os_date_t result = {0};
result.ms = lt.wMilliseconds;
result.sec = lt.wSecond;
result.min = lt.wMinute;
result.hour = lt.wHour;
result.day = lt.wDay;
result.month = lt.wMonth;
result.year = lt.wYear;
return result;
}
fn f64 os_seconds_now(void) {
fn os_date_t os_universal_time(void) {
SYSTEMTIME lt;
GetSystemTime(&lt);
os_date_t result = {0};
result.ms = lt.wMilliseconds;
result.sec = lt.wSecond;
result.min = lt.wMinute;
result.hour = lt.wHour;
result.day = lt.wDay;
result.month = lt.wMonth;
result.year = lt.wYear;
return result;
}
fn s8_t os_format_date(ma_arena_t *arena, os_date_t date) {
s8_t result = s8_printf(arena, "%04u-%02u-%02u %02u:%02u:%02u.%03u", date.year, date.month, date.day, date.hour, date.min, date.sec, date.ms);
return result;
}
fn f64 os_seconds(void) {
static int64_t counts_per_second;
if (counts_per_second == 0) {
LARGE_INTEGER freq;
@@ -26,9 +47,297 @@ fn f64 os_seconds_now(void) {
return result;
}
fn f64 os_get_milliseconds(void) {
f64 secs = os_seconds_now();
fn f64 os_milliseconds(void) {
f64 secs = os_seconds();
f64 result = secs * 1000;
return result;
}
fn void os_sleep(u32 milliseconds) {
Sleep(milliseconds);
}
///////////////////////////////
// files
fn b32 os_copy(s8_t from, s8_t to, b32 overwrite) {
BOOL fail_if_exists = true;
if (overwrite) fail_if_exists = false;
ma_temp_t scratch = ma_begin_scratch();
s16_t from16 = s16_from_s8(scratch.arena, from);
s16_t to16 = s16_from_s8(scratch.arena, to);
BOOL success = CopyFileW(from16.str, to16.str, fail_if_exists);
ma_end_scratch(scratch);
return success ? true : false;
}
fn b32 os_delete(s8_t path) {
ma_temp_t scratch = ma_begin_scratch();
s16_t path16 = s16_from_s8(scratch.arena, path);
BOOL success = DeleteFileW(path16.str);
ma_end_scratch(scratch);
return success ? true : false;
}
fn os_mkdir_t os_mkdir(s8_t path) {
os_mkdir_t result = os_mkdir_success;
ma_temp_t scratch = ma_begin_scratch();
s16_t path16 = s16_from_s8(scratch.arena, path);
BOOL success = CreateDirectoryW(path16.str, NULL);
if (success == 0) {
DWORD error = GetLastError();
if (error == ERROR_ALREADY_EXISTS) result = os_mkdir_file_exists;
else if (error == ERROR_PATH_NOT_FOUND) result = os_mkdir_path_not_found;
else result = os_mkdir_other_error;
}
ma_end_scratch(scratch);
return result;
}
fn os_write_t os_write(s8_t path, s8_t content) {
os_write_t result = os_write_other_error;
ma_temp_t scratch = ma_begin_scratch();
s16_t path16 = s16_from_s8(scratch.arena, path);
HANDLE handle = CreateFileW(path16.str, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (handle != INVALID_HANDLE_VALUE) {
assert(content.len == (DWORD)content.len);
DWORD bytes_written = 0;
BOOL error = WriteFile(handle, content.str, (DWORD)content.len, &bytes_written, NULL);
if (error == TRUE) {
if (bytes_written == content.len) {
result = os_write_success;
}
}
CloseHandle(handle);
} else result = os_write_path_not_found;
ma_end_scratch(scratch);
return result;
}
typedef struct w32_file_iter_t w32_file_iter_t;
struct w32_file_iter_t {
HANDLE handle;
WIN32_FIND_DATAW data;
};
fn void os_advance(os_iter_t *it) {
while (FindNextFileW(it->w32->handle, &it->w32->data) != 0) {
WIN32_FIND_DATAW *data = &it->w32->data;
// Skip '.' and '..'
if (data->cFileName[0] == '.' && data->cFileName[1] == '.' && data->cFileName[2] == 0) continue;
if (data->cFileName[0] == '.' && data->cFileName[1] == 0) continue;
it->is_directory = data->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
it->name = s8_from_s16(it->arena, s16(data->cFileName, wstr_len(data->cFileName)));
char *is_dir = it->is_directory ? "/" : "";
char *separator = it->path.str[it->path.len - 1] == '/' ? "" : "/";
it->rel = s8_printf(it->arena, "%S%s%S%s", it->path, separator, it->name, is_dir);
it->abs = os_abs(it->arena, it->rel);
it->is_valid = true;
if (it->is_directory) {
assert(it->rel.str[it->rel.len - 1] == '/');
assert(it->abs.str[it->abs.len - 1] == '/');
}
return;
}
it->is_valid = false;
DWORD error = GetLastError();
assert(error == ERROR_NO_MORE_FILES);
FindClose(it->w32->handle);
}
fn os_iter_t *os_iter(ma_arena_t *arena, s8_t path) {
os_iter_t *it = ma_push_type(arena, os_iter_t);
it->w32 = ma_push_type(arena, w32_file_iter_t);
it->arena = arena;
it->path = path;
it->is_valid = true;
ma_temp_t scratch = ma_begin_scratch1(arena);
s8_t mod_path = s8_printf(scratch.arena, "%S\\*", path);
s16_t mod_path16 = s16_from_s8(scratch.arena, mod_path);
it->w32->handle = FindFirstFileW(mod_path16.str, &it->w32->data);
if (it->w32->handle == INVALID_HANDLE_VALUE) {
it->is_valid = false;
}
if (it->is_valid) {
assert(it->w32->data.cFileName[0] == '.' && it->w32->data.cFileName[1] == 0);
os_advance(it);
}
ma_end_scratch(scratch);
return it;
}
fn i64 os_mod_time(s8_t file) {
WIN32_FIND_DATAW data;
ma_temp_t scratch = ma_begin_scratch();
s16_t wpath = s16_from_s8(scratch.arena, file);
HANDLE handle = FindFirstFileW(wpath.str, &data);
i64 result = -1;
if (handle != INVALID_HANDLE_VALUE) {
FindClose(handle);
FILETIME time = data.ftLastWriteTime;
result = (i64)time.dwHighDateTime << 32 | time.dwLowDateTime;
}
ma_end_scratch(scratch);
return result;
}
fn s8_t os_abs(ma_arena_t *arena, s8_t rel) {
const int buffer_size = 2048;
ma_temp_t scratch = ma_begin_scratch1(arena);
s16_t rel16 = s16_from_s8(scratch.arena, rel);
wchar_t *buffer = ma_push_array(scratch.arena, wchar_t, buffer_size);
DWORD written = GetFullPathNameW(rel16.str, buffer_size, buffer, 0);
assert(written != 0);
assert((i64)written < (i64)buffer_size);
s8_t result = s8_from_s16(arena, s16(buffer, written));
ma_end_scratch(scratch);
return result;
}
fn s8_t os_exe(ma_arena_t *arena) {
const int buffer_size = 2048;
ma_temp_t scratch = ma_begin_scratch1(arena);
wchar_t *buffer = ma_push_array(scratch.arena, wchar_t, buffer_size);
DWORD wsize = GetModuleFileNameW(0, buffer, buffer_size);
assert(wsize != 0);
s8_t result = s8_from_s16(arena, s16(buffer, wsize));
s8_normalize_path_unsafe(result);
ma_end_scratch(scratch);
return result;
}
fn s8_t os_exe_dir(ma_arena_t *arena) {
ma_temp_t scratch = ma_begin_scratch1(arena);
s8_t exe = os_exe(scratch.arena);
s8_t path = s8_chop_last_slash(exe);
s8_t result = s8_printf(arena, "%S/", path);
ma_end_scratch(scratch);
return result;
}
fn b32 os_is_dir(s8_t path) {
ma_temp_t scratch = ma_begin_scratch();
s16_t path16 = s16_from_s8(scratch.arena, path);
DWORD dwAttrib = GetFileAttributesW(path16.str);
ma_end_scratch(scratch);
return dwAttrib != INVALID_FILE_ATTRIBUTES && (dwAttrib & FILE_ATTRIBUTE_DIRECTORY);
}
fn b32 os_is_file(s8_t path) {
ma_temp_t scratch = ma_begin_scratch();
s16_t path16 = s16_from_s8(scratch.arena, path);
DWORD dwAttrib = GetFileAttributesW(path16.str);
ma_end_scratch(scratch);
b32 is_file = (dwAttrib & FILE_ATTRIBUTE_DIRECTORY) == 0;
return dwAttrib != INVALID_FILE_ATTRIBUTES && is_file;
}
fn b32 os_is_abs(s8_t path) {
b32 result = path.len > 3 && char_is_alphabetic(path.str[0]) && path.str[1] == ':' && path.str[2] == '/';
return result;
}
fn b32 os_exists(s8_t path) {
ma_temp_t scratch = ma_begin_scratch();
s16_t path16 = s16_from_s8(scratch.arena, path);
DWORD attribs = GetFileAttributesW(path16.str);
ma_end_scratch(scratch);
return attribs == INVALID_FILE_ATTRIBUTES ? false : true;
}
fn s8_t os_cwd(ma_arena_t *arena) {
ma_temp_t scratch = ma_begin_scratch1(arena);
const u32 buffer_size = 1024;
wchar_t *buffer = ma_push_array(scratch.arena, wchar_t, buffer_size);
DWORD wsize = GetCurrentDirectoryW(buffer_size, buffer);
assert(wsize != 0);
assert(wsize <= buffer_size);
s8_t path = s8_from_s16(scratch.arena, s16(buffer, wsize));
s8_normalize_path_unsafe(path);
s8_t result = s8_printf(arena, "%S/", path);
ma_end_scratch(scratch);
return result;
}
fn void os_set_cwd(s8_t path) {
ma_temp_t scratch = ma_begin_scratch();
s16_t path16 = s16_from_s8(scratch.arena, path);
SetCurrentDirectoryW(path16.str);
ma_end_scratch(scratch);
}
///////////////////////////////
// threading
typedef struct w32_thread_t w32_thread_t;
struct w32_thread_t {
HANDLE handle;
DWORD id;
};
fn os_thread_t os_thread_create(ma_arena_t *arena, os_thread_fn_t *func, void *user_data) {
assert(func);
LPSECURITY_ATTRIBUTES security_attribs = NULL;
SIZE_T stack_size = 0;
DWORD creation_flags = 0;
DWORD thread_id = 0;
HANDLE thread_handle = CreateThread(security_attribs, stack_size, func, user_data, creation_flags, &thread_id);
if (thread_handle == NULL) {
debugf(__FUNCTION__"CreatedThread failed: %d", GetLastError());
return (os_thread_t){.exited = true};
}
os_thread_t result = {0};
result.w32 = ma_push_type(arena, w32_thread_t);
result.w32->handle = thread_handle;
result.w32->id = thread_id;
return result;
}
fn i32 os_join(os_thread_t *thread) {
DWORD return_value_indicates_event = WaitForSingleObject(thread->w32->handle, INFINITE);
if (return_value_indicates_event != WAIT_OBJECT_0) {
debugf(__FUNCTION__".WaitForSingleObject failed: %d", GetLastError());
}
DWORD exit_code = 0;
BOOL if_fails_then_zero = GetExitCodeThread(thread->w32->handle, &exit_code);
if (if_fails_then_zero == 0) {
debugf(__FUNCTION__".GetExitCodeThread failed: %d", GetLastError());
} else {
thread->exit_code = exit_code;
}
CloseHandle(thread->w32->handle);
thread->exited = true;
return thread->exit_code;
}
// @todo:
// This will probably create 16 arenas or more XD
// fn i32 os__thread_log(void *data) {
// core_init();
// debugf("testing");
// return 0;
// }
// fn void os_test_threads(void) {
// os_thread_t threads[16];
// ma_temp_t scratch = ma_begin_scratch();
// for (int i = 0; i < lengthof(threads); i += 1) {
// threads[i] = os_thread_create(scratch.arena, os__thread_log, NULL);
// }
// for (int i = 0; i < lengthof(threads); i += 1) {
// os_join(&threads[i]);
// }
// ma_end_scratch(scratch);
// }