diff --git a/.gitignore b/.gitignore index cc717d3..3443096 100644 --- a/.gitignore +++ b/.gitignore @@ -1,55 +1,57 @@ -*.code-workspace -*.raddbgi -**/worlds -data -uid -*.rdbg -*.exe -*.pdb -.build/ -*.sublime-workspace -*.sublime-project -*.vs -*.sln -*.vcxproj -*.vcxproj.* -*.log -*.ini -*.VC.db -*.obj -*.ilk -*.spall -release/ -output.txt -*.blend1 -renderdoc.cap -*.abs -*.asf -env.jai -save.txt -settings.txt -*_BRAIN -*_PLAYTEST -*_SYNC -*_TAG_LEGEND -*_LOCAL_CHANGES -*charlie.code-workspace -!proj/arcane/windows_glslc.exe -!proj/arcane/linux_glslc -!proj/arcane/macos_glslc -*TRACES/ - -fetch.bat -thing.bat -*crash-* -*.dmp -*crash.txt -*crash-uid.txt -*crash-version.txt -output2.txt -test_doc.vkn - -*.focus-config - -*keybinds -*.rdi \ No newline at end of file +*.code-workspace +*.raddbgi +**/worlds +data +uid +*.rdbg +*.exe +*.pdb +.build/ +*.sublime-workspace +*.sublime-project +*.vs +*.sln +*.vcxproj +*.vcxproj.* +*.log +*.ini +*.VC.db +*.obj +*.ilk +*.spall +release/ +output.txt +*.blend1 +renderdoc.cap +*.abs +*.asf +env.jai +save.txt +settings.txt +*_BRAIN +*_PLAYTEST +*_SYNC +*_TAG_LEGEND +*_LOCAL_CHANGES +*charlie.code-workspace +!proj/arcane/windows_glslc.exe +!proj/arcane/linux_glslc +!proj/arcane/macos_glslc +*TRACES/ + +fetch.bat +thing.bat +*crash-* +*.dmp +*crash.txt +*crash-uid.txt +*crash-version.txt +output2.txt +test_doc.vkn + +*.focus-config + +*keybinds +*.rdi + +.vscode \ No newline at end of file diff --git a/build.c b/build.c index 7b3254c..f0fc461 100644 --- a/build.c +++ b/build.c @@ -1,8 +1,9 @@ + /// // Build config stuff #define VERY_DEBUG 0 -#define RUN_TESTS 1 +#define RUN_TESTS 0 typedef struct Context_Extra { int monkee; diff --git a/build_release.bat b/build_release.bat index dc025a8..c21a8e9 100644 --- a/build_release.bat +++ b/build_release.bat @@ -7,7 +7,7 @@ pushd build mkdir release pushd release -cl ../../main.c /Zi /Fecgame.exe /Ox /DRELEASE /std:c11 +cl ../../build.c /Zi /Fecgame.exe /Ox /DRELEASE /MD /std:c11 popd popd \ No newline at end of file diff --git a/main.c b/main.c index 3bd6ab4..7f20293 100644 --- a/main.c +++ b/main.c @@ -1,11 +1,11 @@ int oogabooga_main(int argc, char **argv) { - + print(cstr("This is our print! %i\n"), 5); // alloc calls to context.allocator.proc which by default is set to the // heap allocator in memory.c - int *a = cast(int*)alloc(sizeof(int)); + int *a = (int*)alloc(sizeof(int)); dealloc(a); // We can do an old school memory dump to save our game with the global variables @@ -37,8 +37,6 @@ int oogabooga_main(int argc, char **argv) { context.extra.monkee = 69; - - int hello; hello = 5; diff --git a/oogabooga/base.c b/oogabooga/base.c index 8536f3b..e1f41fb 100644 --- a/oogabooga/base.c +++ b/oogabooga/base.c @@ -21,43 +21,59 @@ typedef u8 bool; #define thread_local _Thread_local +#define ifnt(x) if (!(x)) + #ifdef _MSC_VER - #define os_debug_break __debugbreak + inline void os_break() { + __debugbreak(); + int *a = 0; + *a = 5; + } #else #error "Only msvc compiler supported at the moment"; #endif -#define assert(cond, ...) if (!(cond)) { printf("Assertion failed for condition: " #cond ". Message: " __VA_ARGS__); os_debug_break(); } + +void printf(const char* fmt, ...); +#define STR_HELPER(x) #x +#define STR(x) STR_HELPER(x) +#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 cast(t) (t) /// -// inline -// (Credit to chatgpt, so it might not be 100% correct) +// Compiler specific stuff #ifdef _MSC_VER // Microsoft Visual C++ #define inline __forceinline + #define COMPILER_HAS_MEMCPY_INTRINSICS 1 #elif defined(__GNUC__) || defined(__GNUG__) // GNU GCC/G++ #define inline __attribute__((always_inline)) inline + #define COMPILER_HAS_MEMCPY_INTRINSICS 1 #elif defined(__clang__) // Clang/LLVM #define inline __attribute__((always_inline)) inline + #define COMPILER_HAS_MEMCPY_INTRINSICS 1 #elif defined(__INTEL_COMPILER) || defined(__ICC) // Intel C++ Compiler #define inline __forceinline + #define COMPILER_HAS_MEMCPY_INTRINSICS 1 #elif defined(__BORLANDC__) // Borland C++ #define inline __inline #elif defined(__MINGW32__) || defined(__MINGW64__) // MinGW (Minimalist GNU for Windows) #define inline __attribute__((always_inline)) inline + #define COMPILER_HAS_MEMCPY_INTRINSICS 1 #elif defined(__SUNPRO_C) || defined(__SUNPRO_CC) // Oracle Solaris Studio #define inline inline __attribute__((always_inline)) #elif defined(__IBMC__) || defined(__IBMCPP__) // IBM XL C/C++ Compiler #define inline __attribute__((always_inline)) inline + #define COMPILER_HAS_MEMCPY_INTRINSICS 1 #elif defined(__PGI) // Portland Group Compiler #define inline inline __attribute__((always_inline)) @@ -120,3 +136,5 @@ void push_allocator(Allocator a) { push_context(c); } void pop_allocator() { pop_context(); } + + diff --git a/oogabooga/memory.c b/oogabooga/memory.c index 984e23c..4fde1cd 100644 --- a/oogabooga/memory.c +++ b/oogabooga/memory.c @@ -1,5 +1,37 @@ + +void* program_memory = 0; +u64 program_memory_size = 0; + +#ifndef INIT_MEMORY_SIZE + #define INIT_MEMORY_SIZE (1024*50) +#endif +// We may need to allocate stuff in initialization time before the heap is ready. +// That's what this is for. +u8 init_memory_arena[INIT_MEMORY_SIZE]; +u8 *init_memory_head = init_memory_arena; + +void* initialization_allocator_proc(u64 size, void *p, Allocator_Message message) { + switch (message) { + case ALLOCATOR_ALLOCATE: { + p = init_memory_head; + init_memory_head += size; + + if (init_memory_head >= ((u8*)init_memory_arena+INIT_MEMORY_SIZE)) { + os_write_string_to_stdout(cstr("Out of initialization memory! Please provide more by increasing INIT_MEMORY_SIZE")); + os_break(); + } + return p; + break; + } + case ALLOCATOR_DEALLOCATE: { + return 0; + } + } + return 0; +} + /// /// // Basic general heap allocator, free list @@ -34,7 +66,7 @@ typedef struct { Heap_Block *heap_head; bool heap_initted = false; -Mutex_Handle heap_mutex; // This is terrible but I don't care for now +Spinlock *heap_lock; // This is terrible but I don't care for now u64 get_heap_block_size_excluding_metadata(Heap_Block *block) { @@ -47,6 +79,17 @@ u64 get_heap_block_size_including_metadata(Heap_Block *block) { bool is_pointer_in_program_memory(void *p) { return (u8*)p >= (u8*)program_memory && (u8*)p<((u8*)program_memory+program_memory_size); } +bool is_pointer_in_stack(void* p) { + void* stack_base = os_get_stack_base(); + void* stack_limit = os_get_stack_limit(); + return (uintptr_t)p >= (uintptr_t)stack_limit && (uintptr_t)p < (uintptr_t)stack_base; +} +bool is_pointer_in_static_memory(void* p) { + return (uintptr_t)p >= (uintptr_t)os.static_memory_start && (uintptr_t)p < (uintptr_t)os.static_memory_end; +} +bool is_pointer_valid(void *p) { + return is_pointer_in_program_memory(p) || is_pointer_in_stack(p) || is_pointer_in_static_memory(p); +} // Meant for debug void santiy_check_free_node_tree(Heap_Block *block) { @@ -137,7 +180,6 @@ Heap_Block *make_heap_block(Heap_Block *parent, u64 size) { u64 minimum_size = ((u8*)block+size) - (u8*)program_memory + 1; u64 new_program_size = (cast(u64)(minimum_size * 1.5)); assert(new_program_size >= minimum_size, "Bröd"); - printf("Growing program memory to %llu bytes\n", new_program_size); const u64 ATTEMPTS = 1000; for (u64 i = 0; i <= ATTEMPTS; i++) { if (program_memory_size >= new_program_size) break; // Another thread might have resized already, causing it to fail here. @@ -158,9 +200,10 @@ Heap_Block *make_heap_block(Heap_Block *parent, u64 size) { } void heap_init() { - heap_head = make_heap_block(0, DEFAULT_HEAP_BLOCK_SIZE); - heap_mutex = os_make_mutex(); + if (heap_initted) return; heap_initted = true; + heap_head = make_heap_block(0, DEFAULT_HEAP_BLOCK_SIZE); + heap_lock = os_make_spinlock(); } void *heap_alloc(u64 size) { @@ -168,7 +211,7 @@ void *heap_alloc(u64 size) { if (!heap_initted) heap_init(); // #Sync #Speed oof - os_lock_mutex(heap_mutex); + os_spinlock_lock(heap_lock); size += sizeof(Heap_Allocation_Metadata); @@ -257,7 +300,7 @@ void *heap_alloc(u64 size) { #endif // #Sync #Speed oof - os_unlock_mutex(heap_mutex); + os_spinlock_unlock(heap_lock); return ((u8*)meta)+sizeof(Heap_Allocation_Metadata); } @@ -266,15 +309,15 @@ void heap_dealloc(void *p) { if (!heap_initted) heap_init(); - os_lock_mutex(heap_mutex); + os_spinlock_lock(heap_lock); assert(is_pointer_in_program_memory(p), "Garbage pointer; out of program memory bounds!"); p = (u8*)p-sizeof(Heap_Allocation_Metadata); Heap_Allocation_Metadata *meta = (Heap_Allocation_Metadata*)(p); // If > 256GB then prolly not legit lol - assert(meta->size < 1024ULL*1024ULL*1024ULL*256ULL, "Garbage pointer passed to heap_dealloc !!!"); - assert(is_pointer_in_program_memory(meta->block), "Garbage pointer passed to heap_dealloc !!!"); + assert(meta->size < 1024ULL*1024ULL*1024ULL*256ULL, "Garbage pointer passed to heap_dealloc !!! Or could be corrupted memory."); + assert(is_pointer_in_program_memory(meta->block), "Garbage pointer passed to heap_dealloc !!! Or could be corrupted memory."); // Yoink meta data before we start overwriting it Heap_Block *block = meta->block; @@ -327,38 +370,8 @@ void heap_dealloc(void *p) { #endif // #Sync #Speed oof - os_unlock_mutex(heap_mutex); + os_spinlock_unlock(heap_lock); } -void log_heap() { - os_lock_mutex(heap_mutex); - printf("\nHEAP:\n"); - - Heap_Block *block = heap_head; - - while (block != 0) { - - printf("\tBLOCK @ 0x%I64x, %llu bytes\n", (u64)block, block->size); - - Heap_Free_Node *node = block->free_head; - - u64 total_free = 0; - - while (node != 0) { - - printf("\t\tFREE NODE @ 0x%I64x, %llu bytes\n", (u64)node, node->size); - - total_free += node->size; - - node = node->next; - } - - printf("\t TOTAL FREE: %llu\n\n", total_free); - - block = block->next; - } - os_unlock_mutex(heap_mutex); -} - /// /// @@ -416,7 +429,7 @@ void* talloc(u64 size) { if ((u8*)temporary_storage_pointer >= (u8*)temporary_storage+TEMPORARY_STORAGE_SIZE) { if (!has_warned_temporary_storage_overflow) { - printf("WARNING: temporary storage was overflown, we wrap around at the start.\n"); + os_write_string_to_stdout(cstr("WARNING: temporary storage was overflown, we wrap around at the start.\n")); } temporary_storage_pointer = temporary_storage; return talloc(size);; @@ -437,4 +450,4 @@ void reset_temporary_storage() { void push_temp_allocator() { if (!temporary_storage_initted) temporary_storage_init(); push_allocator(temp); -} +} \ No newline at end of file diff --git a/oogabooga/oogabooga.c b/oogabooga/oogabooga.c index 42ed9fa..f4a4799 100644 --- a/oogabooga/oogabooga.c +++ b/oogabooga/oogabooga.c @@ -1,4 +1,19 @@ +#ifdef _WIN32 + #include + #define OS_WINDOWS +#elif defined(__linux__) + // Include whatever #Incomplete #Portability + #define OS_LINUX + #error "Linux is not supported yet"; +#elif defined(__APPLE__) && defined(__MACH__) + // Include whatever #Incomplete #Portability + #define OS_MAC + #error "Mac is not supported yet"; +#else + #error "Current OS not supported!"; +#endif + #include "base.c" #include "string.c" @@ -9,6 +24,7 @@ + #ifdef OS_WINDOWS #include "os_impl_windows.c" #elif defined (OS_LINUX) @@ -33,9 +49,13 @@ void oogabooga_init(u64 program_memory_size) { #define RUN_TESTS 0 #endif + +int oogabooga_main(int argc, char **argv); + int main(int argc, char **argv) { - printf("Ooga booga program started\n"); + context.allocator.proc = initialization_allocator_proc; oogabooga_init(INITIAL_PROGRAM_MEMORY_SIZE); + printf("Ooga booga program started\n"); // This can be disabled in build.c #if RUN_TESTS diff --git a/oogabooga/os_impl_windows.c b/oogabooga/os_impl_windows.c index c5727db..d6bb9d1 100644 --- a/oogabooga/os_impl_windows.c +++ b/oogabooga/os_impl_windows.c @@ -25,6 +25,23 @@ void os_init(u64 program_memory_size) { GetSystemInfo(&si); os.granularity = cast(u64)si.dwAllocationGranularity; os.page_size = cast(u64)si.dwPageSize; + + os.static_memory_start = 0; + os.static_memory_end = 0; + + MEMORY_BASIC_INFORMATION mbi; + + + unsigned char* addr = 0; + while (VirtualQuery(addr, &mbi, sizeof(mbi))) { + if (mbi.Type == MEM_IMAGE) { + if (os.static_memory_start == NULL) { + os.static_memory_start = mbi.BaseAddress; + } + os.static_memory_end = (unsigned char*)mbi.BaseAddress + mbi.RegionSize; + } + addr += mbi.RegionSize; + } program_memory_mutex = os_make_mutex(); @@ -35,16 +52,17 @@ void os_init(u64 program_memory_size) { heap_allocator.proc = heap_allocator_proc; heap_allocator.data = 0; + heap_init(); context.allocator = heap_allocator; os.crt = os_load_dynamic_library(const_string("msvcrt.dll")); assert(os.crt != 0, "Could not load win32 crt library. Might be compiled with non-msvc? #Incomplete #Portability"); - os.crt_vprintf = (Crt_Vprintf_Proc)os_dynamic_library_load_symbol(os.crt, const_string("vprintf")); - assert(os.crt_vprintf, "Missing vprintf in crt"); - os.crt_printf = (Crt_Printf_Proc)os_dynamic_library_load_symbol(os.crt, const_string("printf")); - assert(os.crt_printf, "Missing printf in crt"); os.crt_vsnprintf = (Crt_Vsnprintf_Proc)os_dynamic_library_load_symbol(os.crt, const_string("vsnprintf")); assert(os.crt_vsnprintf, "Missing vsnprintf in crt"); + os.crt_vprintf = (Crt_Vprintf_Proc)os_dynamic_library_load_symbol(os.crt, const_string("vprintf")); + assert(os.crt_vprintf, "Missing vprintf in crt"); + os.crt_vsprintf = (Crt_Vsprintf_Proc)os_dynamic_library_load_symbol(os.crt, const_string("vsprintf")); + assert(os.crt_vsprintf, "Missing vsprintf in crt"); os.crt_memcpy = (Crt_Memcpy_Proc)os_dynamic_library_load_symbol(os.crt, const_string("memcpy")); assert(os.crt_memcpy, "Missing memcpy in crt"); os.crt_memcmp = (Crt_Memcmp_Proc)os_dynamic_library_load_symbol(os.crt, const_string("memcmp")); @@ -204,4 +222,59 @@ void os_write_string_to_stdout(string s) { 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 *l = cast(Spinlock*)alloc(sizeof(Spinlock)); + l->locked = false; + return l; +} +void os_spinlock_lock(Spinlock *l) { + while (true) { + bool expected = false; + if (os_compare_and_swap_bool(&l->locked, true, expected)) { + return; + } + while (l->locked) { + // spinny boi + } + } +} + +void os_spinlock_unlock(Spinlock *l) { + bool expected = true; + bool success = os_compare_and_swap_bool(&l->locked, false, expected); + assert(success, "This thread should have acquired the spinlock but compare_and_swap failed"); +} + +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; +} + +bool os_compare_and_swap_16(u16 *a, u16 b, u16 old) { + return InterlockedCompareExchange16((volatile SHORT*)a, (SHORT)b, (SHORT)old) == (SHORT)old; +} + +bool os_compare_and_swap_32(u32 *a, u32 b, u32 old) { + return InterlockedCompareExchange((volatile LONG*)a, (LONG)b, (LONG)old) == (LONG)old; +} + +bool os_compare_and_swap_64(u64 *a, u64 b, u64 old) { + return InterlockedCompareExchange64((volatile LONG64*)a, (LONG64)b, (LONG64)old) == (LONG64)old; +} + +bool os_compare_and_swap_bool(bool *a, bool b, bool old) { + return os_compare_and_swap_8(cast(u8*)a, cast(u8)b, cast(u8)old); } \ No newline at end of file diff --git a/oogabooga/os_interface.c b/oogabooga/os_interface.c index c6e855d..712969b 100644 --- a/oogabooga/os_interface.c +++ b/oogabooga/os_interface.c @@ -1,24 +1,17 @@ #ifdef _WIN32 - #include - #define OS_WINDOWS - typedef HANDLE Mutex_Handle; typedef HANDLE Thread_Handle; typedef HMODULE Dynamic_Library_Handle; #elif defined(__linux__) - // Include whatever #Incomplete #Portability - #define OS_LINUX typedef SOMETHING Mutex_Handle; typedef SOMETHING Thread_Handle; typedef SOMETHING Dynamic_Library_Handle; #error "Linux is not supported yet"; #elif defined(__APPLE__) && defined(__MACH__) - // Include whatever #Incomplete #Portability - #define OS_MAC typedef SOMETHING Mutex_Handle; typedef SOMETHING Thread_Handle; typedef SOMETHING Dynamic_Library_Handle; @@ -34,16 +27,12 @@ #define _INTSIZEOF(n) ((sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1)) -#define oogabooga_va_start(ap, v) (ap = (va_list)&v + _INTSIZEOF(v)) -#define oogabooga_va_arg(ap, t) (*(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t))) -#define oogabooga_va_end(ap) (ap = (va_list)0) - -typedef void* (__cdecl *Crt_Memcpy_Proc) (void*, const void*, size_t); -typedef int (__cdecl *Crt_Memcmp_Proc) (const void*, const void*, size_t); -typedef void* (__cdecl *Crt_Memset_Proc) (void*, int, size_t); -typedef int (__cdecl *Crt_Printf_Proc) (const char*, ...); +typedef void* (__cdecl *Crt_Memcpy_Proc) (void*, const void*, size_t); +typedef int (__cdecl *Crt_Memcmp_Proc) (const void*, const void*, size_t); +typedef void* (__cdecl *Crt_Memset_Proc) (void*, int, size_t); typedef int (__cdecl *Crt_Vprintf_Proc) (const char*, va_list); -typedef int (__cdecl *Crt_Vsnprintf_Proc) (char*, size_t, const char*, va_list); +typedef int (__cdecl *Crt_Vsnprintf_Proc) (char*, size_t, const char*, va_list); +typedef int (__cdecl *Crt_Vsprintf_Proc) (char*, const char*, va_list); typedef struct Os_Info { u64 page_size; @@ -54,66 +43,61 @@ typedef struct Os_Info { Crt_Memcpy_Proc crt_memcpy; Crt_Memcmp_Proc crt_memcmp; Crt_Memset_Proc crt_memset; - Crt_Printf_Proc crt_printf; // #Cleanup remove after we have our own print - Crt_Vprintf_Proc crt_vprintf; // #Cleanup remove after we have our own print + Crt_Vprintf_Proc crt_vprintf; Crt_Vsnprintf_Proc crt_vsnprintf; + Crt_Vsprintf_Proc crt_vsprintf; + + void *static_memory_start, *static_memory_end; } Os_Info; Os_Info os; -inline void* naive_memcpy(void* dest, const void* source, size_t size) { - for (u64 i = 0; i < (u64)size; i++) ((u8*)dest)[i] = ((u8*)source)[i]; - return dest; +inline int crt_vprintf(const char* fmt, va_list args) { + return os.crt_vprintf(fmt, args); } -inline void* memcpy(void* dest, const void* source, size_t size) { - if (!os.crt_memcpy) return naive_memcpy(dest, source, size); - return os.crt_memcpy(dest, source, size); -} -inline int naive_memcmp(const void* a, const void* b, size_t amount) { - // I don't understand the return value of memcmp but I also dont care - for (u64 i = 0; i < (u64)amount; i++) { - if (((u8*)a)[i] != ((u8*)b)[i]) return -1; - } - return 0; -} -inline int memcmp(const void* a, const void* b, size_t amount) { - if (!os.crt_memcmp) return naive_memcmp(a, b, amount); - return os.crt_memcmp(a, b, amount); -} -inline void* naive_memset(void* dest, int value, size_t amount) { - for (u64 i = 0; i < (u64)amount; i++) ((u8*)dest)[i] = (u8)value; - return dest; -} -inline void* memset(void* dest, int value, size_t amount) { - if (!os.crt_memset) return naive_memset(dest, value, amount); - return os.crt_memset(dest, value, amount); -} -inline int printf(const char* fmt, ...) { - char fast_buffer[8196]; - char *large_buffer = 0; - va_list args; - oogabooga_va_start(args, fmt); - int r = vprintf(fmt, args); - oogabooga_va_end(args); - - return r; -} -void os_write_string_to_stdout(string s); -inline int vprintf(const char* fmt, va_list args) { - if (os.crt_vprintf) return os.crt_vprintf(fmt, args); - else { - os_write_string_to_stdout(cstr(fmt)); - os_write_string_to_stdout(cstr(" ")); +#if !defined(COMPILER_HAS_MEMCPY_INTRINSICS) || defined(DEBUG) + inline void* naive_memcpy(void* dest, const void* source, size_t size) { + for (u64 i = 0; i < (u64)size; i++) ((u8*)dest)[i] = ((u8*)source)[i]; + return dest; + } + inline void* memcpy(void* dest, const void* source, size_t size) { + if (!os.crt_memcpy) return naive_memcpy(dest, source, size); + return os.crt_memcpy(dest, source, size); + } + inline int naive_memcmp(const void* a, const void* b, size_t amount) { + // I don't understand the return value of memcmp but I also dont care + for (u64 i = 0; i < (u64)amount; i++) { + if (((u8*)a)[i] != ((u8*)b)[i]) return -1; + } return 0; } -} + inline int memcmp(const void* a, const void* b, size_t amount) { + if (!os.crt_memcmp) return naive_memcmp(a, b, amount); + return os.crt_memcmp(a, b, amount); + } + inline void* naive_memset(void* dest, int value, size_t amount) { + for (u64 i = 0; i < (u64)amount; i++) ((u8*)dest)[i] = (u8)value; + return dest; + } + inline void* memset(void* dest, int value, size_t amount) { + if (!os.crt_memset) return naive_memset(dest, value, amount); + return os.crt_memset(dest, value, amount); + } +#endif + inline int vsnprintf(char* buffer, size_t n, const char* fmt, va_list args) { - os.crt_vsnprintf(buffer, n, fmt, args); + return os.crt_vsnprintf(buffer, n, fmt, args); +} + +inline int crt_sprintf(char *str, const char *format, ...) { + va_list args; + va_start(args, format); + int r = os.crt_vsprintf(str, format, args); + va_end(args); + return r; } -void* program_memory = 0; -u64 program_memory_size = 0; Mutex_Handle program_memory_mutex = 0; bool os_grow_program_memory(size_t new_size); @@ -152,6 +136,24 @@ void os_destroy_mutex(Mutex_Handle m); void os_lock_mutex(Mutex_Handle m); void os_unlock_mutex(Mutex_Handle m); +/// +// Spinlock primitive +typedef struct Spinlock { + bool locked; +} Spinlock; +Spinlock *os_make_spinlock(); +void os_spinlock_lock(Spinlock* l); +void os_spinlock_unlock(Spinlock* l); + +/// +// Sync 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); +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); + /// /// @@ -178,4 +180,67 @@ void os_unload_dynamic_library(Dynamic_Library_Handle l); // IO /// -void os_write_string_to_stdout(string s); \ No newline at end of file +void os_write_string_to_stdout(string s); + +// context.allocator (alloc & dealloc) +void print_va_list(const string fmt, va_list args) { + string s = sprint_va_list(fmt, args); + os_write_string_to_stdout(s); + dealloc(s.data); +} + +// print for 'string' and printf for 'char*' + +#define PRINT_BUFFER_SIZE 4096 +// Avoids all and any allocations but overhead in speed and memory. +// Need this for standard printing so we don't get infinite recursions. +// (for example something in memory might fail assert and it needs to print that) +void print_va_list_buffered(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_write_string_to_stdout(s); + + current.count -= size; + current.data += size; + } +} + +// context.allocator (alloc & dealloc) +void print(const string fmt, ...) { + va_list args; + va_start(args, fmt); + print_va_list_buffered(fmt, args); + va_end(args); +} +// context.allocator (alloc & dealloc) +void printf(const char* fmt, ...) { + va_list args; + va_start(args, fmt); + string s; + s.data = cast(u8*)fmt; + s.count = strlen(fmt); + print_va_list_buffered(s, args); + va_end(args); +} + + +/// +/// +// Memory +/// +void* os_get_stack_base(); +void* os_get_stack_limit(); diff --git a/oogabooga/string.c b/oogabooga/string.c index 670376a..fd6c218 100644 --- a/oogabooga/string.c +++ b/oogabooga/string.c @@ -1,15 +1,57 @@ +/* +Usage: + + // We need to use macro const_string to convert literal to string + string fmt = const_string("Pointer address: 0x%x"); + print(fmt, cast(u64)&a); // Print to stdout + + // Format a string and allocate with context.allocator + string a = sprint("Hello, %cs!\n", "balls"); // %cs for char* + + string balls = const_string("balls"); + // tprint for temporary allocation + string b = tprint(const_string("Hello, %s!\n"), balls); // %s for string + + // Allocate a new string of length 12 (with context allocator) + string c = alloc_string(12); + dealloc_string(c); + + // We can use raw char* for format with printf/sprintf/tprintf + printf("Hello, %!\n", balls); + + // concatenation + string concatenated = string_concat(a, b); + + // Use temporary memory to make a null-terminated copy of fixed-length string + char* cstring = temp_convert_to_null_terminated_string(balls); + + // To convert a cstring to string (using same memory) + string s; + s.data = (u8*)cstring; + s.count = strlen(cstring); + + // String matching + bool match = strings_match(a, b); + + // View into "Hello, balls!\n" from index 7 with a count of 5; "balls" + string balls2 = string_view(a, 7, 5); + + +*/ + void * memcpy (void *,const void *,size_t); +void* talloc(u64); typedef struct string { - u8 *data; u64 count; + u8 *data; } string; void push_temp_allocator(); #define cstr const_string -#define const_string(s) (string){ (u8*)s, length_of_null_terminated_string(s) } +#define const_string(s) (string){ length_of_null_terminated_string(s), (u8*)s } inline u64 length_of_null_terminated_string(const char* cstring) { u64 len = 0; @@ -20,8 +62,18 @@ inline u64 length_of_null_terminated_string(const char* cstring) { return len; } +string alloc_string(u64 count) { + string s; + s.count = count; + s.data = cast(u8*)alloc(count); + return s; +} +void dealloc_string(string s) { + dealloc(s.data); +} + // context.allocator ! -string string_concat(string left, string right) { +string string_concat(const string left, const string right) { string result; result.count = left.count + right.count; result.data = cast(u8*)alloc(result.count); @@ -30,16 +82,204 @@ string string_concat(string left, string right) { return result; } // context.allocator ! -char *convert_to_null_terminated_string(string s) { +char *convert_to_null_terminated_string(const string s) { char *cstring = cast(char*)alloc(s.count+1); memcpy(cstring, s.data, s.count); cstring[s.count] = 0; return cstring; } -char *temp_convert_to_null_terminated_string(string s) { +char *temp_convert_to_null_terminated_string(const string s) { push_temp_allocator(); char *c = convert_to_null_terminated_string(s); pop_allocator(); return c; -} \ No newline at end of file +} +inline int crt_sprintf(char *str, const char *format, ...); +int vsnprintf(char* buffer, size_t n, const char* fmt, va_list args); + +bool is_pointer_valid(void *p); +u64 format_string_to_buffer(char* buffer, u64 count, const char* fmt, va_list args) { + if (!buffer) count = UINT64_MAX; + const char* p = fmt; + char* bufp = buffer; + while (*p != '\0' && (bufp - buffer) < count - 1) { + if (*p == '%') { + p += 1; + if (*p == 's') { + // We replace %s formatting with our fixed length string + p += 1; + string s = va_arg(args, string); + assert(s.count < (1024ULL*1024ULL*1024ULL*256ULL), "Ypu passed something else than a fixed-length 'string' to %%s. Maybe you passed a char* and should do %%cs instead?"); + for (u64 i = 0; i < s.count && (bufp - buffer) < count - 1; i++) { + if (buffer) *bufp = s.data[i]; + bufp += 1; + } + } else if (*p == 'c' && *(p+1) == 's') { + // We extend the standard formatting and add %cs so we can format c strings if we need to + p += 2; + char* s = va_arg(args, char*); + assert(is_pointer_valid(s), "You passed an invalid pointer to %%cs"); + u64 len = 0; + while (*s != '\0' && (bufp - buffer) < count - 1) { + assert(is_pointer_valid(s) && len < (1024ULL*1024ULL*1024ULL*1ULL), "The argument passed to %%cs is either way too big, missing null-termination or simply not a char*."); + if (buffer) { + *bufp = *s; + } + s += 1; + bufp += 1; + len += 1; + assert(is_pointer_valid(s) && len < (1024ULL*1024ULL*1024ULL*1ULL), "The argument passed to %%cs is either way too big, missing null-termination or simply not a char*."); + } + } else { + // Fallback to standard vsnprintf + char temp_buffer[512]; + char format_specifier[64]; + int specifier_len = 0; + format_specifier[specifier_len++] = '%'; + + while (*p != '\0' && strchr("diuoxXfFeEgGaAcCpn%", *p) == NULL) { + format_specifier[specifier_len++] = *p++; + } + if (*p != '\0') { + format_specifier[specifier_len++] = *p++; + } + format_specifier[specifier_len] = '\0'; + + int temp_len = vsnprintf(temp_buffer, sizeof(temp_buffer), format_specifier, args); + switch (format_specifier[specifier_len - 1]) { + case 'd': case 'i': va_arg(args, int); break; + case 'u': case 'x': case 'X': case 'o': va_arg(args, unsigned int); break; + case 'f': case 'F': case 'e': case 'E': case 'g': case 'G': case 'a': case 'A': va_arg(args, double); break; + case 'c': va_arg(args, int); break; + case 's': va_arg(args, char*); break; + case 'p': va_arg(args, void*); break; + case 'n': va_arg(args, int*); break; + default: break; + } + + if (temp_len < 0) { + return -1; // Error in formatting + } + + for (int i = 0; i < temp_len && (bufp - buffer) < count - 1; i++) { + if (buffer) *bufp = temp_buffer[i]; + bufp += 1; + } + } + } else { + if (buffer) { + *bufp = *p; + } + bufp += 1; + p += 1; + } + } + if (buffer) *bufp = '\0'; + + return bufp - buffer; +} +string sprint_null_terminated_string_va_list_to_buffer(const char *fmt, va_list args, void* buffer, u64 count) { + u64 formatted_length = format_string_to_buffer((char*)buffer, count, fmt, args); + + string result; + result.data = (u8*)buffer; + + if (formatted_length >= 0 && formatted_length < count) { + result.count = formatted_length; + } else { + result.count = count - 1; + } + + return result; +} +string sprint_va_list_to_buffer(const string fmt, va_list args, void* buffer, u64 count) { + + char* fmt_cstring = temp_convert_to_null_terminated_string(fmt); + return sprint_null_terminated_string_va_list_to_buffer(fmt_cstring, args, buffer, count); +} +// context.allocator +string sprint_va_list(const string fmt, va_list args) { + + char* fmt_cstring = temp_convert_to_null_terminated_string(fmt); + u64 count = format_string_to_buffer(NULL, 0, fmt_cstring, args) + 1; + + char* buffer = NULL; + + buffer = (char*)alloc(count); + + return sprint_null_terminated_string_va_list_to_buffer(fmt_cstring, args, buffer, count); +} + +// context.allocator +string sprint(const string fmt, ...) { + va_list args = 0; + va_start(args, fmt); + string s = sprint_va_list(fmt, args); + va_end(args); + return s; +} + +// temp allocator +string tprint(const string fmt, ...) { + va_list args = 0; + va_start(args, fmt); + push_temp_allocator(); + string s = sprint_va_list(fmt, args); + pop_allocator(); + va_end(args); + return s; +} + +// context.allocator +string sprintf(const char *fmt, ...) { + string sfmt; + sfmt.data = cast(u8*)fmt; + sfmt.count = strlen(fmt); + + va_list args; + va_start(args, fmt); + string s = sprint_va_list(sfmt, args); + va_end(args); + + return s; +} +// temp allocator +string tprintf(const char *fmt, ...) { + string sfmt; + sfmt.data = cast(u8*)fmt; + sfmt.count = strlen(fmt); + + va_list args; + va_start(args, fmt); + push_temp_allocator(); + string s = sprint_va_list(sfmt, args); + pop_allocator(); + va_end(args); + + return s; +} + +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; +} + +string string_view(string s, u64 start_index, u64 count) { + assert(start_index < s.count, "array_view start_index % out of range for string count %", start_index, s.count); + assert(count > 0, "array_view count must be more than 0"); + assert(start_index + count <= s.count, "array_view start_index + count is out of range"); + + string result; + result.data = s.data+start_index; + result.count = count; + + return result; +} + +// Defined in os interface +void print(string s, ...); \ No newline at end of file diff --git a/oogabooga/tests.c b/oogabooga/tests.c index 641d662..4aa619d 100644 --- a/oogabooga/tests.c +++ b/oogabooga/tests.c @@ -1,4 +1,33 @@ +void log_heap() { + os_spinlock_lock(heap_lock); + printf("\nHEAP:\n"); + + Heap_Block *block = heap_head; + + while (block != 0) { + + printf("\tBLOCK @ 0x%I64x, %llu bytes\n", (u64)block, block->size); + + Heap_Free_Node *node = block->free_head; + + u64 total_free = 0; + + while (node != 0) { + + printf("\t\tFREE NODE @ 0x%I64x, %llu bytes\n", (u64)node, node->size); + + total_free += node->size; + + node = node->next; + } + + printf("\t TOTAL FREE: %llu\n\n", total_free); + + block = block->next; + } + os_spinlock_unlock(heap_lock); +} void test_allocator(bool do_log_heap) { // Basic allocation and free @@ -70,6 +99,8 @@ void test_allocator(bool do_log_heap) { } + reset_temporary_storage(); + push_allocator(temp); int* foo = (int*)alloc(72); @@ -222,10 +253,108 @@ void test_allocator_threaded(Thread *t) { } void test_strings() { - string s = (string){ (u8*)"Ooga booga", length_of_null_terminated_string("Ooga booga") }; - string a = const_string("Ooga booga"); + // Test length_of_null_terminated_string + assert(length_of_null_terminated_string("Test") == 4, "Failed: length_of_null_terminated_string"); + assert(length_of_null_terminated_string("") == 0, "Failed: length_of_null_terminated_string"); + + // Test alloc_string and dealloc_string + string alloc_str = alloc_string(10); + assert(alloc_str.data != NULL, "Failed: alloc_string"); + assert(alloc_str.count == 10, "Failed: alloc_string"); + dealloc_string(alloc_str); + + // Test string_concat + string str1 = const_string("Hello, "); + string str2 = const_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"); + dealloc_string(concat_str); + + // Test convert_to_null_terminated_string + char* cstr = convert_to_null_terminated_string(str1); + assert(strcmp(cstr, "Hello, ") == 0, "Failed: convert_to_null_terminated_string"); + dealloc(cstr); + + // Test temp_convert_to_null_terminated_string + cstr = temp_convert_to_null_terminated_string(str2); + assert(strcmp(cstr, "World!") == 0, "Failed: temp_convert_to_null_terminated_string"); + // No need to dealloc, it's temporary storage + + // Test sprint + string format_str = const_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"); + dealloc(formatted_str.data); + dealloc(formatted_cstr); + + // Test tprint + string temp_formatted_str = tprint(format_str, 100); + formatted_cstr = temp_convert_to_null_terminated_string(temp_formatted_str); + assert(strcmp(formatted_cstr, "Number: 100") == 0, "Failed: tprint"); + // No need to dealloc, it's temporary storage + + // Test print and printf (visual inspection) + printf("Expected output: Hello, World!\n"); + print(const_string("Hello, %s!\n"), const_string("World")); + + printf("Expected output: Number: 1234\n"); + print(const_string("Number: %d\n"), 1234); + + printf("Expected output: Number: 1234\n"); + print(const_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); + + // This should fail assert and print descriptive error + //printf("Expected output (printf): Hello, World!\n"); + //printf("Hello, %cs!\n", 5); + // This should fail assert and print descriptive error + // printf("Expected output (printf): Hello, World!\n"); + // printf("Hello, %s!\n", "World"); + + printf("Expected output (printf): Hello, World!\n"); + printf("Hello, %s!\n", cstr("World")); + + printf("Expected output (printf): Number: 5678\n"); + printf("Number: %d\n", 5678); + + printf("Expected output (printf): Mixed values: 99 and 2.71\n"); + printf("Mixed values: %d and %.2f\n", 99, 2.71); + + // Test handling of empty strings + string empty_str = const_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); + + // Test very large strings (performance test) + string large_str1 = alloc_string(1024 * 1024); + string large_str2 = alloc_string(1024 * 1024); + string large_concat_str = string_concat(large_str1, large_str2); + assert(large_concat_str.count == 2 * 1024 * 1024, "Failed: large string_concat"); + dealloc_string(large_str1); + dealloc_string(large_str2); + dealloc_string(large_concat_str); + + // Test string with special characters + string special_char_str = const_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"); + + assert(strings_match(balls1, balls2), "String match failed"); + assert(!strings_match(balls1, a), "String match failed"); } + + void oogabooga_run_tests() { printf("Testing allocator...\n"); test_allocator(true);