diff options
| author | nasr <nsrddyn@gmail.com> | 2026-10-05 22:02:56 +0200 |
|---|---|---|
| committer | nasr <nsrddyn@gmail.com> | 2026-10-05 22:02:56 +0200 |
| commit | f6d3090f7fa76eb8e0dbe060b8490cb49c001c39 (patch) | |
| tree | 433c773997e51f22738acb8b04c4fd2fc6b1bdc9 | |
init: base
27 files changed, 2646 insertions, 0 deletions
diff --git a/build.bat b/build.bat new file mode 100644 index 0000000..3569429 --- /dev/null +++ b/build.bat | |||
| @@ -0,0 +1,15 @@ | |||
| 1 | @echo off | ||
| 2 | setlocal | ||
| 3 | time /t | ||
| 4 | |||
| 5 | set BUILD_DIR=build | ||
| 6 | set SOURCE_DIR=source | ||
| 7 | set INCLUDES=-I%SOURCE_DIR%\code -I%SOURCE_DIR% -I%SOURCE_DIR%\base | ||
| 8 | set CFLAGS=-FC -GR- -EHa- -nologo -Zi -FS /analyze /W3 /WX -MD /DYNAMICBASE:NO | ||
| 9 | set LIBS=user32.lib gdi32.lib kernel32.lib shell32.lib | ||
| 10 | |||
| 11 | if not exist %BUILD_DIR% mkdir %BUILD_DIR% | ||
| 12 | |||
| 13 | echo Building %BUILD_DIR%\program.exe... | ||
| 14 | |||
| 15 | cl %INCLUDES% %CFLAGS% %SOURCE_DIR%\code\compiler.cpp /Fe%BUILD_DIR%\compiler.exe %LIBS% | ||
diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..096cffd --- /dev/null +++ b/build.sh | |||
| @@ -0,0 +1,35 @@ | |||
| 1 | #!/bin/bash | ||
| 2 | set -e | ||
| 3 | |||
| 4 | echo | ||
| 5 | date | ||
| 6 | echo "author: Abdellah El Morabit" | ||
| 7 | echo | ||
| 8 | |||
| 9 | BUILD_DIR=build | ||
| 10 | SOURCE_DIR=source | ||
| 11 | INCLUDES="-I${SOURCE_DIR}/loo -I${SOURCE_DIR} -I${SOURCE_DIR}/base" | ||
| 12 | CFLAGS="-g -fno-rtti -fno-exceptions -Wall -Werror -fanalyzer -no-pie -Wno-unused-function -Wno-unused-variable -O0" | ||
| 13 | LIBS="" | ||
| 14 | |||
| 15 | VULKAN=1 | ||
| 16 | WAYLAND=1 | ||
| 17 | X11=0 | ||
| 18 | |||
| 19 | if [ $WAYLAND -eq 1 ]; then | ||
| 20 | LIBS+=" $(pkg-config --libs wayland-client)" | ||
| 21 | CFLAGS+=" $(pkg-config --cflags wayland-client) -DWAYLAND=1 -DX11=0" | ||
| 22 | elif [ $X11 -eq 1 ]; then | ||
| 23 | LIBS+=" -lX11" | ||
| 24 | CFLAGS+=" -DX11=1 -DWAYLAND=0" | ||
| 25 | fi | ||
| 26 | |||
| 27 | if [ $VULKAN -eq 1 ]; then | ||
| 28 | LIBS+=" -lvulkan" | ||
| 29 | CFLAGS+=" -DVULKAN=1" | ||
| 30 | fi | ||
| 31 | |||
| 32 | mkdir -p "$BUILD_DIR" | ||
| 33 | |||
| 34 | echo "Building ${BUILD_DIR}/loo..." | ||
| 35 | g++ $INCLUDES $CFLAGS "${SOURCE_DIR}/loo/loo.cpp" -o "${BUILD_DIR}/loo" $LIBS \ No newline at end of file | ||
diff --git a/project.4coder b/project.4coder new file mode 100644 index 0000000..e392f5b --- /dev/null +++ b/project.4coder | |||
| @@ -0,0 +1,78 @@ | |||
| 1 | version(2); | ||
| 2 | project_name = "loo"; | ||
| 3 | |||
| 4 | patterns = { | ||
| 5 | "*.c", | ||
| 6 | "*.cpp", | ||
| 7 | "*.h", | ||
| 8 | "*.m", | ||
| 9 | "*.bat", | ||
| 10 | "*.sh", | ||
| 11 | "*.4coder", | ||
| 12 | "*.jr", | ||
| 13 | }; | ||
| 14 | |||
| 15 | blacklist_patterns = { | ||
| 16 | ".*", | ||
| 17 | }; | ||
| 18 | |||
| 19 | paths = { | ||
| 20 | { "./source/code", .relative = true, .recursive = true, }, | ||
| 21 | { "./source/base", .relative = true, .recursive = true, }, | ||
| 22 | }; | ||
| 23 | |||
| 24 | linux_paths = { | ||
| 25 | { "./source/code", .relative = true, .recursive = true, }, | ||
| 26 | { "./source/base", .relative = true, .recursive = true, }, | ||
| 27 | { "/usr/include", .relative = false, .recursive = false, }, | ||
| 28 | { "/usr/include/vulkan", .relative = false, .recursive = false, }, | ||
| 29 | { "/usr/include/X11", .relative = false, .recursive = false, }, | ||
| 30 | }; | ||
| 31 | |||
| 32 | load_paths = { | ||
| 33 | .win = paths, | ||
| 34 | .linux = linux_paths, | ||
| 35 | .mac = paths, | ||
| 36 | }; | ||
| 37 | |||
| 38 | commands = { | ||
| 39 | .build = { | ||
| 40 | .linux = "./build.sh", | ||
| 41 | .mac = "./build.sh", | ||
| 42 | .out = "*compilation*", | ||
| 43 | .footer_panel = false, | ||
| 44 | .save_dirty_files = true, | ||
| 45 | .cursor_at_end = true, | ||
| 46 | }, | ||
| 47 | .run = { | ||
| 48 | .linux = "./build/loo", | ||
| 49 | .mac = "./build/loo", | ||
| 50 | .out = "*compilation*", | ||
| 51 | .footer_panel = false, | ||
| 52 | .save_dirty_files = true, | ||
| 53 | .cursor_at_end = true, | ||
| 54 | }, | ||
| 55 | .test_build = { | ||
| 56 | .linux = "./test.sh", | ||
| 57 | .mac = "./test.sh", | ||
| 58 | .out = "*compilation*", | ||
| 59 | .footer_panel = false, | ||
| 60 | .save_dirty_files = true, | ||
| 61 | .cursor_at_end = true, | ||
| 62 | }, | ||
| 63 | .test_run = { | ||
| 64 | .linux = "setarch $(uname -m) -R ./build/test", // disables address randomization | ||
| 65 | .mac = "./test/build", | ||
| 66 | .out = "*compilation*", | ||
| 67 | .footer_panel = false, | ||
| 68 | .save_dirty_files = true, | ||
| 69 | .cursor_at_end = true, | ||
| 70 | }, | ||
| 71 | }; | ||
| 72 | |||
| 73 | fkey_command = { | ||
| 74 | .F1 = "build", | ||
| 75 | .F2 = "run", | ||
| 76 | .F3 = "test_build", | ||
| 77 | .F4 = "test_run", | ||
| 78 | }; \ No newline at end of file | ||
diff --git a/source/base/base_arena.cpp b/source/base/base_arena.cpp new file mode 100644 index 0000000..b4235e6 --- /dev/null +++ b/source/base/base_arena.cpp | |||
| @@ -0,0 +1,165 @@ | |||
| 1 | #if PLATFORM_UNIX | ||
| 2 | |||
| 3 | internal MemArena * | ||
| 4 | unix_arena_create(u64 capacity) | ||
| 5 | { | ||
| 6 | MemArena *arena = (MemArena *)mmap( | ||
| 7 | /* kernel decides where to throw the arena */ | ||
| 8 | NULL, | ||
| 9 | capacity + sizeof(MemArena), | ||
| 10 | PROT_READ | PROT_WRITE, | ||
| 11 | MAP_SHARED | MAP_ANONYMOUS, | ||
| 12 | -1, | ||
| 13 | 0); | ||
| 14 | |||
| 15 | if(arena == MAP_FAILED) { | ||
| 16 | return NULL; | ||
| 17 | } | ||
| 18 | |||
| 19 | arena->capacity = capacity; | ||
| 20 | arena->base_position = (u8 *)arena + sizeof(MemArena); | ||
| 21 | arena->current_position = 0; | ||
| 22 | arena->previous_position = 0; | ||
| 23 | |||
| 24 | return arena; | ||
| 25 | } | ||
| 26 | |||
| 27 | internal void | ||
| 28 | unix_arena_destroy(MemArena *arena) | ||
| 29 | { | ||
| 30 | if(!arena) { | ||
| 31 | return; | ||
| 32 | } | ||
| 33 | |||
| 34 | munmap(arena, arena->capacity + sizeof(MemArena)); | ||
| 35 | } | ||
| 36 | |||
| 37 | #elif PLATFORM_WINDOWS | ||
| 38 | |||
| 39 | internal MemArena * | ||
| 40 | win32_arena_create(u64 capacity) | ||
| 41 | { | ||
| 42 | MemArena *arena = (MemArena *)VirtualAlloc(NULL, | ||
| 43 | capacity + sizeof(MemArena), | ||
| 44 | MEM_COMMIT | MEM_RESERVE, | ||
| 45 | PAGE_READWRITE); | ||
| 46 | if(!arena) { | ||
| 47 | return NULL; | ||
| 48 | } | ||
| 49 | |||
| 50 | arena->capacity = capacity; | ||
| 51 | arena->base_position = (u8 *)arena + sizeof(MemArena); | ||
| 52 | arena->current_position = 0; | ||
| 53 | arena->previous_position = 0; | ||
| 54 | return arena; | ||
| 55 | } | ||
| 56 | |||
| 57 | internal void | ||
| 58 | win32_arena_destroy(MemArena *arena) | ||
| 59 | { | ||
| 60 | if(!arena) { | ||
| 61 | return; | ||
| 62 | } | ||
| 63 | |||
| 64 | VirtualFree(arena, 0, MEM_RELEASE); | ||
| 65 | return; | ||
| 66 | } | ||
| 67 | |||
| 68 | #endif | ||
| 69 | |||
| 70 | internal void * | ||
| 71 | arena_alloc(MemArena *arena, u64 size, b32 zero) | ||
| 72 | { | ||
| 73 | Assert(arena); | ||
| 74 | |||
| 75 | u64 aligned = Align(arena->current_position, arena_align); | ||
| 76 | u64 new_pos = aligned + size; | ||
| 77 | |||
| 78 | AssertLog((new_pos <= arena->capacity), "max arena capacity reached\n"); | ||
| 79 | |||
| 80 | |||
| 81 | void *out = arena->base_position + aligned; | ||
| 82 | |||
| 83 | arena->previous_position = arena->current_position; | ||
| 84 | arena->current_position = aligned + size; | ||
| 85 | |||
| 86 | if(zero) { | ||
| 87 | MemSetZero(out, size); | ||
| 88 | } | ||
| 89 | |||
| 90 | return out; | ||
| 91 | } | ||
| 92 | |||
| 93 | internal void | ||
| 94 | arena_pop(MemArena *arena, u64 size) | ||
| 95 | { | ||
| 96 | size = Min(size, arena->current_position); | ||
| 97 | arena->current_position -= size; | ||
| 98 | } | ||
| 99 | |||
| 100 | internal void | ||
| 101 | arena_pop_to(MemArena *arena, u64 pos) | ||
| 102 | { | ||
| 103 | u64 size = pos < arena->current_position ? arena->current_position - pos : 0; | ||
| 104 | arena_pop(arena, size); | ||
| 105 | } | ||
| 106 | |||
| 107 | internal void | ||
| 108 | arena_clear(MemArena *arena) | ||
| 109 | { | ||
| 110 | arena->current_position = 0; | ||
| 111 | } | ||
| 112 | |||
| 113 | internal MemArena * | ||
| 114 | arena_resize_block_align(MemArena *arena, void *old_memory, u64 new_size, u64 old_size, umm alignment) | ||
| 115 | { | ||
| 116 | u8 *old_mem = (u8 *)old_memory; | ||
| 117 | |||
| 118 | if(is_pow(alignment)) { | ||
| 119 | arena->current_position = Align(arena->current_position, alignment); | ||
| 120 | } | ||
| 121 | |||
| 122 | if(old_memory == NULL || old_size == 0) { | ||
| 123 | return (MemArena *)arena_alloc(arena, new_size, 0); | ||
| 124 | } else if((old_mem >= arena->base_position && old_mem < arena->base_position + arena->current_position)) { // NOTE: does not find it self after the current position | ||
| 125 | if((arena->base_position + arena->previous_position) == old_memory) { | ||
| 126 | arena->current_position = arena->previous_position + new_size; | ||
| 127 | if(new_size > old_size) { | ||
| 128 | MemSet(&arena->current_position, new_size - old_size); | ||
| 129 | } | ||
| 130 | |||
| 131 | return (MemArena *)old_memory; | ||
| 132 | } else { | ||
| 133 | void *new_memory = arena_alloc(arena, new_size, 0); | ||
| 134 | umm copy_size = old_size < new_size ? old_size : new_size; | ||
| 135 | memmove(new_memory, old_mem, copy_size); | ||
| 136 | } | ||
| 137 | } else { | ||
| 138 | AssertLog(false, "Failed to resize memory.\n"); | ||
| 139 | } | ||
| 140 | |||
| 141 | return NULL; | ||
| 142 | } | ||
| 143 | |||
| 144 | internal MemArena * | ||
| 145 | arena_resize_block(MemArena *arena, void *old_memory, u64 new_size, u64 old_size) | ||
| 146 | { | ||
| 147 | return arena_resize_block_align(arena, old_memory, new_size, old_size, arena_align); | ||
| 148 | } | ||
| 149 | |||
| 150 | internal ScratchArena | ||
| 151 | scratch_start(MemArena *arena) | ||
| 152 | { | ||
| 153 | ScratchArena scratch; | ||
| 154 | scratch.arena = arena; | ||
| 155 | scratch.start_position = arena->current_position; | ||
| 156 | |||
| 157 | return scratch; | ||
| 158 | } | ||
| 159 | |||
| 160 | internal void | ||
| 161 | scratch_end(ScratchArena *scratch) | ||
| 162 | { | ||
| 163 | scratch->arena->current_position = scratch->start_position; | ||
| 164 | } | ||
| 165 | |||
diff --git a/source/base/base_arena.h b/source/base/base_arena.h new file mode 100644 index 0000000..125d043 --- /dev/null +++ b/source/base/base_arena.h | |||
| @@ -0,0 +1,94 @@ | |||
| 1 | #ifndef BASE_ARENA_H | ||
| 2 | #define BASE_ARENA_H | ||
| 3 | |||
| 4 | //- arena | ||
| 5 | typedef struct MemArena MemArena; | ||
| 6 | typedef struct ScratchArena ScratchArena; | ||
| 7 | |||
| 8 | // NOTE: used capacity can be calculated so we don't care | ||
| 9 | struct MemArena { | ||
| 10 | u64 current_position; | ||
| 11 | u64 previous_position; | ||
| 12 | u64 capacity; | ||
| 13 | u8 *base_position; | ||
| 14 | MemArena *next; | ||
| 15 | }; | ||
| 16 | |||
| 17 | struct ScratchArena { | ||
| 18 | MemArena *arena; | ||
| 19 | u64 start_position; | ||
| 20 | }; | ||
| 21 | |||
| 22 | //- | ||
| 23 | |||
| 24 | #if defined(PLATFORM_UNIX) | ||
| 25 | |||
| 26 | internal MemArena *unix_arena_create(u64 capacity); | ||
| 27 | internal void unix_arena_destroy(MemArena *arena); | ||
| 28 | |||
| 29 | #define ArenaCreate(capacity) unix_arena_create(capacity) | ||
| 30 | #define ArenaDestroy(arena) unix_arena_destroy(arena) | ||
| 31 | #elif defined(PLATFORM_WINDOWS) | ||
| 32 | |||
| 33 | internal void win32_arena_destroy(MemArena *arena); | ||
| 34 | internal MemArena *win32_arena_create(u64 capacity); | ||
| 35 | |||
| 36 | #define ArenaCreate(capacity) win32_arena_create(capacity) | ||
| 37 | #define ArenaDestroy(arena) win32_arena_destroy(arena) | ||
| 38 | #endif | ||
| 39 | |||
| 40 | internal void *arena_alloc(MemArena *arena, u64 size, b32 zero); | ||
| 41 | internal void arena_pop(MemArena *arena, u64 size); | ||
| 42 | internal void arena_pop_to(MemArena *arena, u64 pos); | ||
| 43 | internal void arena_clear(MemArena *arena); | ||
| 44 | internal ScratchArena scratch_start(MemArena *arena); | ||
| 45 | internal void scratch_end(ScratchArena *scratch); | ||
| 46 | internal MemArena *arena_resize_block_align(MemArena *arena, void *old_memory, u64 new_size, u64 old_size, | ||
| 47 | umm alignment); | ||
| 48 | internal MemArena *arena_resize_block(MemArena *arena, void *old_memory, u64 new_size, u64 old_size); | ||
| 49 | internal u64 arena_calculate_used_capacity(MemArena *arena); | ||
| 50 | |||
| 51 | #define Align(pointer, alignment) align((u64)(pointer), (umm)(alignment)) | ||
| 52 | #define PushStruct(arena, type) (type *)arena_alloc((arena), sizeof(type), 0) | ||
| 53 | #define PushStructZero(arena, type) (type *)arena_alloc((arena), sizeof(type), 1) | ||
| 54 | #define PushArray(arena, type, len) (type *)arena_alloc((arena), sizeof(type) * (len), 0) | ||
| 55 | #define PushArrayZero(arena, type, len) (type *)arena_alloc((arena), sizeof(type) * (len), 1) | ||
| 56 | #define ArenaResize(arena, old_memory, new_size, old_size) arena_resize_block(arena, old_memory, new_size, old_size) // takes the address of the allocated block in the arena and grows it with a new size | ||
| 57 | #define ArenaPopLastPush(arena) arena_pop_to(arena, arena->previous_position) | ||
| 58 | |||
| 59 | |||
| 60 | // claude magic HAHHAHAHAH | ||
| 61 | #define LogArena(arena_expr) \ | ||
| 62 | do { \ | ||
| 63 | MemArena *_a = (arena_expr); \ | ||
| 64 | if (!_a) { \ | ||
| 65 | Log("[arena] %s = NULL (%s:%d)\n", #arena_expr, __FILE__, __LINE__); \ | ||
| 66 | break; \ | ||
| 67 | } \ | ||
| 68 | unsigned long long _cap = (unsigned long long)_a->capacity; \ | ||
| 69 | unsigned long long _cur = (unsigned long long)_a->current_position; \ | ||
| 70 | unsigned long long _prev = (unsigned long long)_a->previous_position; \ | ||
| 71 | unsigned long long _free = _cur <= _cap ? _cap - _cur : 0; \ | ||
| 72 | unsigned long long _last = _cur >= _prev ? _cur - _prev : 0; \ | ||
| 73 | double _pct = _cap ? (100.0 * (double)_cur / (double)_cap) : 0.0; \ | ||
| 74 | u8 *_expected_base = (u8 *)_a + sizeof(MemArena); \ | ||
| 75 | Log("[arena] %s @ %p (%s:%d)\n" \ | ||
| 76 | " base_position : %p .. %p%s\n" \ | ||
| 77 | " capacity : %llu bytes\n" \ | ||
| 78 | " current_position : %llu (used %.2f%%)\n" \ | ||
| 79 | " previous_position : %llu\n" \ | ||
| 80 | " last alloc size : %llu bytes (current - previous)\n" \ | ||
| 81 | " free : %llu bytes (capacity - current)\n" \ | ||
| 82 | " next write addr : %p\n", \ | ||
| 83 | #arena_expr, (void *)_a, __FILE__, __LINE__, \ | ||
| 84 | (void *)_a->base_position, (void *)(_a->base_position + _cap), \ | ||
| 85 | _a->base_position == _expected_base ? "" : " <-- base != arena + sizeof(MemArena)", \ | ||
| 86 | _cap, _cur, _pct, _prev, _last, _free, \ | ||
| 87 | (void *)(_a->base_position + _cur)); \ | ||
| 88 | if (_cur > _cap) \ | ||
| 89 | Log(" !! OVERFLOW: current_position exceeds capacity by %llu bytes\n", _cur - _cap); \ | ||
| 90 | if (_prev > _cur) \ | ||
| 91 | Log(" !! previous_position (%llu) > current_position (%llu)\n", _prev, _cur); \ | ||
| 92 | } while (0) | ||
| 93 | |||
| 94 | #endif /* BASE_ARENA_H */ | ||
diff --git a/source/base/base_core.h b/source/base/base_core.h new file mode 100644 index 0000000..a4531a5 --- /dev/null +++ b/source/base/base_core.h | |||
| @@ -0,0 +1,141 @@ | |||
| 1 | #ifndef BASE_CORE_H | ||
| 2 | #define BASE_CORE_H | ||
| 3 | |||
| 4 | //- compiler definitions | ||
| 5 | #if defined(_MSC_VER) | ||
| 6 | #define COMPILER_MSVC 1 | ||
| 7 | #elif defined(__clang__) | ||
| 8 | #define COMPILER_CLANG 1 | ||
| 9 | #elif defined(__GNUC__) | ||
| 10 | #define COMPILER_GCC 1 | ||
| 11 | #else | ||
| 12 | #error Unsupported compiler | ||
| 13 | #endif | ||
| 14 | |||
| 15 | //- architecture definitions | ||
| 16 | |||
| 17 | #if defined(__arm__) || defined(__aarch64__) | ||
| 18 | #define ARCH_ARM 1 | ||
| 19 | #elif defined(__i386__) || defined(__x86_64__) | ||
| 20 | #define ARCH_x86_64 1 | ||
| 21 | #endif | ||
| 22 | |||
| 23 | //- compiler definitions | ||
| 24 | |||
| 25 | //- core stuff | ||
| 26 | |||
| 27 | /* assert an expression and output the file and the line */ | ||
| 28 | #define internal static | ||
| 29 | #define global_variable static | ||
| 30 | #define local_persist static | ||
| 31 | |||
| 32 | #define arena_align (2 * sizeof(void *)) | ||
| 33 | |||
| 34 | #define Min(a, b) (((a) < (b)) ? (a) : (b)) | ||
| 35 | #define Max(a, b) (((a) > (b)) ? (a) : (b)) | ||
| 36 | |||
| 37 | #define Unused(x) (void)(x) | ||
| 38 | #define Nil 0 | ||
| 39 | |||
| 40 | #define Deprecated __attribute__((__deprecated__)) | ||
| 41 | |||
| 42 | //- breakpoints | ||
| 43 | #if ARCH_ARM | ||
| 44 | #define breakpoint() __asm__ volatile("brk #0") | ||
| 45 | #elif ARCH_x86_64 | ||
| 46 | |||
| 47 | #if COMPILER_GCC | ||
| 48 | #define breakpoint() __asm__ volatile("int3") | ||
| 49 | #endif | ||
| 50 | #endif | ||
| 51 | |||
| 52 | #if COMPILER_MSVC | ||
| 53 | #include <intrin.h> | ||
| 54 | #define breakpoint() __debugbreak() | ||
| 55 | #endif | ||
| 56 | |||
| 57 | //- | ||
| 58 | #define MemCpy(dest, src, len) memcpy((dest), (src), (len)) | ||
| 59 | #define MemSet(dest, len) memset((dest), (1), (len)) | ||
| 60 | #define MemSetZero(dest, len) memset((dest), (0), (len)) | ||
| 61 | |||
| 62 | //- | ||
| 63 | #if COMPILER_MSVC | ||
| 64 | #pragma section("read_only", read) | ||
| 65 | #define ReadOnly __declspec(allocate("read_only")) | ||
| 66 | #elif COMPILER_GCC || COMPILER_CLANG | ||
| 67 | |||
| 68 | #if PLATFORM_APPLE | ||
| 69 | #define ReadOnly __attribute__((section("__TEXT,__const"))) | ||
| 70 | #else | ||
| 71 | #define ReadOnly __attribute__((section(".const"))) | ||
| 72 | #endif // PLATFORM_APPLE | ||
| 73 | |||
| 74 | #endif | ||
| 75 | |||
| 76 | //- | ||
| 77 | |||
| 78 | //- types | ||
| 79 | |||
| 80 | typedef uint64_t u64; | ||
| 81 | typedef uint32_t u32; | ||
| 82 | typedef uint16_t u16; | ||
| 83 | typedef uint8_t u8; | ||
| 84 | |||
| 85 | typedef int8_t s8; | ||
| 86 | typedef int16_t s16; | ||
| 87 | |||
| 88 | typedef int32_t s32; | ||
| 89 | typedef int64_t s64; | ||
| 90 | |||
| 91 | typedef float f32; | ||
| 92 | typedef double f64; | ||
| 93 | |||
| 94 | typedef s32 b32; | ||
| 95 | typedef s16 b16; | ||
| 96 | typedef s8 b8; | ||
| 97 | |||
| 98 | typedef uintptr_t umm; | ||
| 99 | typedef intptr_t smm; | ||
| 100 | |||
| 101 | //- disable certain msvc errors | ||
| 102 | #if COMPILER_MSVC | ||
| 103 | #pragma warning(disable : 6246) // disable the same name within a certain scope | ||
| 104 | #endif | ||
| 105 | |||
| 106 | //- | ||
| 107 | |||
| 108 | #define Assert(expression) _assert(expression) | ||
| 109 | |||
| 110 | // NOTE: apparantly in macros this strinifys whataever you put after the # | ||
| 111 | #define AssertLog(expression, ...) do { \ | ||
| 112 | if (!expression) { \ | ||
| 113 | Log("%s:%d: %s\n", __FILE__, __LINE__, #expression); \ | ||
| 114 | Log(__VA_ARGS__);\ | ||
| 115 | Assert(false);\ | ||
| 116 | }\ | ||
| 117 | } \ | ||
| 118 | while(0) | ||
| 119 | |||
| 120 | internal void _assert(bool expression); | ||
| 121 | |||
| 122 | |||
| 123 | internal void _assert(bool expression) | ||
| 124 | { | ||
| 125 | |||
| 126 | if (!expression) { | ||
| 127 | breakpoint(); | ||
| 128 | #if COMPILER_MSVC | ||
| 129 | __fastfail(0); | ||
| 130 | #else | ||
| 131 | __builtin_trap(); // NOTE: always stop, breakpoint() is empty on arm without DEBUG | ||
| 132 | #endif | ||
| 133 | } | ||
| 134 | } | ||
| 135 | |||
| 136 | |||
| 137 | |||
| 138 | |||
| 139 | |||
| 140 | |||
| 141 | #endif // BASE_CORE_H | ||
diff --git a/source/base/base_include.h b/source/base/base_include.h new file mode 100644 index 0000000..9c928f2 --- /dev/null +++ b/source/base/base_include.h | |||
| @@ -0,0 +1,88 @@ | |||
| 1 | #ifndef BASE_INCLUDE_H | ||
| 2 | #define BASE_INCLUDE_H | ||
| 3 | |||
| 4 | #if defined(__linux__) || defined(__APPLE__) | ||
| 5 | // #message "PLATFORM LINUX APPLE" | ||
| 6 | #define PLATFORM_UNIX 1 | ||
| 7 | #define PLATFORM_LINUX 1 | ||
| 8 | #elif defined(__linux__) && !defined(__APPLE__) | ||
| 9 | #message "PLATFORM LINUX" | ||
| 10 | #define PLATFORM_LINUX 1 | ||
| 11 | #elif defined(_WIN32) || defined(_WIN64) | ||
| 12 | #message "PLATFORM WINDOWS" | ||
| 13 | #define PLATFORM_WINDOWS 1 | ||
| 14 | #elif defined(__APPLE__) | ||
| 15 | #define PLATFORM_APPLE 1 | ||
| 16 | #message "PLATFORM APPLE" | ||
| 17 | #else | ||
| 18 | #message platform unknown | ||
| 19 | #endif // platform definitions | ||
| 20 | |||
| 21 | #if PLATFORM_UNIX | ||
| 22 | #include <unistd.h> | ||
| 23 | #include <dirent.h> | ||
| 24 | #include <sys/syscall.h> | ||
| 25 | #include <sys/mman.h> | ||
| 26 | #include <sys/socket.h> // needed for making a connection to displays (wayland) | ||
| 27 | #include <sys/stat.h> | ||
| 28 | #include <sys/types.h> | ||
| 29 | #include <sys/un.h> | ||
| 30 | #include <fcntl.h> | ||
| 31 | #include <dlfcn.h> | ||
| 32 | #if PLATFORM_LINUX | ||
| 33 | |||
| 34 | #if X11 | ||
| 35 | #include <X11/X.h> | ||
| 36 | #include <X11/Xlib.h> | ||
| 37 | #include <X11/keysym.h> | ||
| 38 | #elif WAYLAND | ||
| 39 | #include <wayland-client.h> | ||
| 40 | #endif | ||
| 41 | |||
| 42 | #endif // PLATFORM_LINUX | ||
| 43 | #elif PLATFORM_WINDOWS | ||
| 44 | #define _WIN32_WINNT 0x0601 | ||
| 45 | #include <windows.h> | ||
| 46 | #endif // PLATFORM_UNIX - PLATFORM_WINDOWS | ||
| 47 | |||
| 48 | #if VULKAN | ||
| 49 | #include <vulkan/vulkan.h> | ||
| 50 | #elif OPENGL | ||
| 51 | ... | ||
| 52 | #endif | ||
| 53 | |||
| 54 | //- | ||
| 55 | |||
| 56 | #include <time.h> | ||
| 57 | #include <math.h> | ||
| 58 | #include <string.h> | ||
| 59 | #include <stdint.h> | ||
| 60 | #include <stddef.h> | ||
| 61 | #include <stdio.h> | ||
| 62 | #include <stdarg.h> | ||
| 63 | |||
| 64 | //- | ||
| 65 | #include "base_core.h" | ||
| 66 | #include "base_memory.h" | ||
| 67 | |||
| 68 | #include "base_arena.h" | ||
| 69 | #include "base_stack.h" | ||
| 70 | #include "base_string.h" | ||
| 71 | #include "base_platform.h" | ||
| 72 | #include "base_render.h" | ||
| 73 | |||
| 74 | #include "base_memory.cpp" | ||
| 75 | #include "base_arena.cpp" | ||
| 76 | #include "base_stack.cpp" | ||
| 77 | #include "base_string.cpp" | ||
| 78 | #include "base_platform.cpp" | ||
| 79 | |||
| 80 | #include "base_rand.cpp" | ||
| 81 | #include "base_simd.cpp" | ||
| 82 | |||
| 83 | #if VULKAN | ||
| 84 | |||
| 85 | #include "base_render_vulkan.cpp" | ||
| 86 | #endif | ||
| 87 | |||
| 88 | #endif // BASE_INCLUDE_H | ||
diff --git a/source/base/base_memory.cpp b/source/base/base_memory.cpp new file mode 100644 index 0000000..e99c174 --- /dev/null +++ b/source/base/base_memory.cpp | |||
| @@ -0,0 +1,10 @@ | |||
| 1 | internal b32 is_pow(umm x) { return (x & (x - 1)) == 0; } | ||
| 2 | |||
| 3 | internal u64 align(u64 pointer, umm alignment) | ||
| 4 | { | ||
| 5 | if ((alignment & (alignment - 1)) != 0) { | ||
| 6 | return pointer; | ||
| 7 | } | ||
| 8 | |||
| 9 | return (pointer + alignment - 1) & ~(alignment - 1); | ||
| 10 | } | ||
diff --git a/source/base/base_memory.h b/source/base/base_memory.h new file mode 100644 index 0000000..eb9ac85 --- /dev/null +++ b/source/base/base_memory.h | |||
| @@ -0,0 +1,42 @@ | |||
| 1 | #ifndef BASE_MEMORY_H | ||
| 2 | #define BASE_MEMORY_H | ||
| 3 | |||
| 4 | //- asan | ||
| 5 | |||
| 6 | // Marks memory region [addr, addr+size) as unaddressable. | ||
| 7 | // This memory must be previously allocated by the user program. Accessing | ||
| 8 | // addresses in this region from instrumented code is forbidden until | ||
| 9 | // this region is unpoisoned. This function is not guaranteed to poison | ||
| 10 | // the whole region - it may poison only subregion of [addr, addr+size) due | ||
| 11 | // to ASan alignment restrictions. | ||
| 12 | // Method is NOT thread-safe in the sense that no two threads can | ||
| 13 | // (un)poison memory in the same memory region simultaneously. | ||
| 14 | void __asan_poison_memory_region(void const volatile *addr, size_t size); | ||
| 15 | // Marks memory region [addr, addr+size) as addressable. | ||
| 16 | // This memory must be previously allocated by the user program. Accessing | ||
| 17 | // addresses in this region is allowed until this region is poisoned again. | ||
| 18 | // This function may unpoison a superregion of [addr, addr+size) due to | ||
| 19 | // ASan alignment restrictions. | ||
| 20 | // Method is NOT thread-safe in the sense that no two threads can | ||
| 21 | // (un)poison memory in the same memory region simultaneously. | ||
| 22 | void __asan_unpoison_memory_region(void const volatile *addr, size_t size); | ||
| 23 | |||
| 24 | // User code should use macros instead of functions. | ||
| 25 | #if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__) | ||
| 26 | #define ASAN_POISON_MEMORY_REGION(addr, size) __asan_poison_memory_region((addr), (size)) | ||
| 27 | #define ASAN_UNPOISON_MEMORY_REGION(addr, size) __asan_unpoison_memory_region((addr), (size)) | ||
| 28 | #else | ||
| 29 | #define ASAN_POISON_MEMORY_REGION(addr, size) ((void)(addr), (void)(size)) | ||
| 30 | #define ASAN_UNPOISON_MEMORY_REGION(addr, size) ((void)(addr), (void)(size)) | ||
| 31 | #endif | ||
| 32 | |||
| 33 | //- helper functions | ||
| 34 | |||
| 35 | #define KiB(n) (((u64)(n)) << 10) | ||
| 36 | #define MiB(n) (((u64)(n)) << 20) | ||
| 37 | #define GiB(n) (((u64)(n)) << 30) | ||
| 38 | |||
| 39 | internal b32 is_pow(umm x); | ||
| 40 | internal u64 align(u64 pointer, umm alignment); | ||
| 41 | |||
| 42 | #endif // BASE_MEMORY_H | ||
diff --git a/source/base/base_platform.cpp b/source/base/base_platform.cpp new file mode 100644 index 0000000..ae5b2e9 --- /dev/null +++ b/source/base/base_platform.cpp | |||
| @@ -0,0 +1,5 @@ | |||
| 1 | #if PLATFORM_LINUX | ||
| 2 | #include "base_platform_linux.cpp" | ||
| 3 | #elif PLATFORM_WINDOWS | ||
| 4 | #include "base_platform_win32.cpp" | ||
| 5 | #endif \ No newline at end of file | ||
diff --git a/source/base/base_platform.h b/source/base/base_platform.h new file mode 100644 index 0000000..aa7ba4b --- /dev/null +++ b/source/base/base_platform.h | |||
| @@ -0,0 +1,199 @@ | |||
| 1 | #ifndef BASE_PLATFORM_H | ||
| 2 | #define BASE_PLATFORM_H | ||
| 3 | |||
| 4 | //- Linux | ||
| 5 | #if PLATFORM_LINUX || PLATFORM_UNIX | ||
| 6 | |||
| 7 | //- Types | ||
| 8 | typedef struct SharedLibrary SharedLibrary; | ||
| 9 | struct SharedLibrary { | ||
| 10 | void *mem; | ||
| 11 | const char *name; | ||
| 12 | }; | ||
| 13 | |||
| 14 | typedef struct Directory Directory; | ||
| 15 | struct Directory { | ||
| 16 | String8List *file_names; | ||
| 17 | }; | ||
| 18 | |||
| 19 | #if 0 // TODO: @WAYLAND and @X11 implementation | ||
| 20 | typedef struct LinuxWindowHandle LinuxWindowHandle; | ||
| 21 | struct LinuxWindowHandle { | ||
| 22 | Display *display; | ||
| 23 | Window window; | ||
| 24 | }; | ||
| 25 | |||
| 26 | #define LinuxLoadSharedLibrary(library, name) unix_ | ||
| 27 | #endif | ||
| 28 | |||
| 29 | //- Shared libraries | ||
| 30 | internal void *unix_shared_library_get_proc_from_name(SharedLibrary *library, const char *name); | ||
| 31 | internal void unix_load_shared_library(MemArena *arena, SharedLibrary *library); | ||
| 32 | internal int unix_shared_library_close(SharedLibrary *library); | ||
| 33 | |||
| 34 | internal String8 unix_get_env(const char *var); | ||
| 35 | |||
| 36 | //- File I/O | ||
| 37 | internal b32 unix_file_write(String8 data, String8 path); | ||
| 38 | internal String8 unix_file_load(MemArena *arena, String8 path); | ||
| 39 | internal b32 unix_file_exists(String8 path); | ||
| 40 | internal String8List *unix_open_dir(MemArena *arena, | ||
| 41 | String8 *original_path); // TODO: change the return value | ||
| 42 | |||
| 43 | internal s64 unix_read(int fd, const void *buf, u64 count); | ||
| 44 | internal void unix_os_exit() __attribute__((noreturn)); | ||
| 45 | |||
| 46 | internal s64 unix_log(int fd, const void *buf, u64 count); | ||
| 47 | internal void unix_log_with_args(const char *format, ...); | ||
| 48 | |||
| 49 | #define FileLoad(arena, path) unix_file_load(arena, path) | ||
| 50 | #define FileWrite(data, path) unix_file_write(data, path) | ||
| 51 | #define FileExists(path) unix_file_exists(path) | ||
| 52 | #define DirLoad(arena, path) unix_open_dir(arena, path) | ||
| 53 | |||
| 54 | #define OSExit() unix_os_exit(); | ||
| 55 | |||
| 56 | #if COMPILER_GCC || COMPILER_CLANG | ||
| 57 | // TODO: __VA_ARGS__ CLANG | ||
| 58 | #define Log(...) unix_log_with_args(__VA_ARGS__) | ||
| 59 | #define LogStr8(message) \ | ||
| 60 | Log(Str8Fmt, Str8Arg(message)) // NOTE: for this to wwork we need the /analyze argument | ||
| 61 | #endif // COMPILER_GCC || COMPILER_CLANG | ||
| 62 | |||
| 63 | //- Entrypoint and platform window layer | ||
| 64 | #define ENTRYPOINT() int main(int count, char **args) | ||
| 65 | |||
| 66 | #define PlatformCreateWindow(arena, height, width, display_x, display_y) \ | ||
| 67 | LinuxCreateWindow(arena, height, width, display_x, display_y) | ||
| 68 | |||
| 69 | #define PlatformGetDisplayWidth(display, screen_num) \ | ||
| 70 | LinuxDisplayWdith(display, screen_num) // defined externally | ||
| 71 | |||
| 72 | #define PlatformGetDisplayHeight(display, screen_num) \ | ||
| 73 | LinuxDisplayHeight(display, screen_num) // defined externally | ||
| 74 | |||
| 75 | #define PlatformLoadBitmapFromFile(display, window, path, width, height, out, hotspot_x, \ | ||
| 76 | hotspot_y) \ | ||
| 77 | LinuxLoadBitMapFromFile(display, window, path, width, height, out, hotspot_x, hotspot_y) | ||
| 78 | |||
| 79 | //- X11 | ||
| 80 | #if X11 | ||
| 81 | internal LinuxWindowHandle x11_create_window(MemArena *arena, u32 window_height, u32 window_width, | ||
| 82 | u32 disp_x, u32 disp_y); | ||
| 83 | internal void x11_draw(LinuxWindowHandle *s, s32 start_x, s32 end_x, s32 start_y, s32 end_y); | ||
| 84 | internal s32 x11_cleanup(LinuxWindowHandle *s); | ||
| 85 | internal s32 x11_read_input(); | ||
| 86 | |||
| 87 | #define LinuxCreateWindow(arena, height, width, x, y) x11_create_window(arena, height, width, x, y) | ||
| 88 | #define LinuxGetDisplayWdith(display, screen_num) DisplayWidth(display, screen_num) | ||
| 89 | #define LinuxGetDisplayHeight(display, screen_num) DisplayHeight(display, screen_num); | ||
| 90 | #define LinuxLoadBitMapFromFile(display, window, path, width, height, out, hotspot_x, hotspot_y) \ | ||
| 91 | XReadBitmapFile(display, window, ) | ||
| 92 | |||
| 93 | #include "base_platform_linux_x11.cpp" | ||
| 94 | |||
| 95 | //- Wayland | ||
| 96 | #elif WAYLAND | ||
| 97 | |||
| 98 | //- protocol values defined in the wayland library. it's a library load less i guess | ||
| 99 | global_variable const u32 wayland_display_object_id = 1; | ||
| 100 | global_variable const u16 wayland_wl_registry_event_global = 0; | ||
| 101 | global_variable const u16 wayland_shm_pool_event_format = 0; | ||
| 102 | global_variable const u16 wayland_wl_buffer_event_release = 0; | ||
| 103 | global_variable const u16 wayland_xdg_wm_base_event_ping = 0; | ||
| 104 | global_variable const u16 wayland_xdg_toplevel_event_configure = 0; | ||
| 105 | global_variable const u16 wayland_xdg_toplevel_event_close = 1; | ||
| 106 | global_variable const u16 wayland_xdg_surface_event_configure = 0; | ||
| 107 | global_variable const u16 wayland_wl_display_get_registry_opcode = 1; | ||
| 108 | global_variable const u16 wayland_wl_registry_bind_opcode = 0; | ||
| 109 | global_variable const u16 wayland_wl_compositor_create_surface_opcode = 0; | ||
| 110 | global_variable const u16 wayland_xdg_wm_base_pong_opcode = 3; | ||
| 111 | global_variable const u16 wayland_xdg_surface_ack_configure_opcode = 4; | ||
| 112 | global_variable const u16 wayland_wl_shm_create_pool_opcode = 0; | ||
| 113 | global_variable const u16 wayland_xdg_wm_base_get_xdg_surface_opcode = 2; | ||
| 114 | global_variable const u16 wayland_wl_shm_pool_create_buffer_opcode = 0; | ||
| 115 | global_variable const u16 wayland_wl_surface_attach_opcode = 1; | ||
| 116 | global_variable const u16 wayland_xdg_surface_get_toplevel_opcode = 1; | ||
| 117 | global_variable const u16 wayland_wl_surface_commit_opcode = 6; | ||
| 118 | global_variable const u16 wayland_wl_display_error_event = 0; | ||
| 119 | global_variable const u32 wayland_format_xrgb8888 = 1; | ||
| 120 | global_variable const u32 wayland_header_size = 8; | ||
| 121 | global_variable const u32 color_channels = 4; | ||
| 122 | |||
| 123 | #include "base_platform_linux_wayland.cpp" | ||
| 124 | |||
| 125 | #endif // X11 and Wayland -- set by build params | ||
| 126 | |||
| 127 | //- Windows | ||
| 128 | #elif PLATFORM_WINDOWS | ||
| 129 | |||
| 130 | //- Types | ||
| 131 | typedef struct Win32WindowHandle Win32WindowHandle; | ||
| 132 | struct Win32WindowHandle { | ||
| 133 | HWND window_handle; | ||
| 134 | HINSTANCE instance; | ||
| 135 | HDC dc; | ||
| 136 | }; | ||
| 137 | |||
| 138 | global_variable b32 Win32Running = true; | ||
| 139 | |||
| 140 | #define STDIN 0 | ||
| 141 | #define STDOUT 1 | ||
| 142 | #define STDERR 2 | ||
| 143 | |||
| 144 | //- Shared libraries | ||
| 145 | internal void *win32_load_library(); | ||
| 146 | internal void win32_library_get_proc(); | ||
| 147 | |||
| 148 | //- File I/O | ||
| 149 | internal b32 win32_file_exists(u8 *path); | ||
| 150 | internal b32 win32_file_write(String8 data, String8 path); | ||
| 151 | internal String8 win32_file_load(MemArena *arena, String8 file); | ||
| 152 | internal String8List win32_open_dir(MemArena *arena, String8 *original_path); // TODO: ... | ||
| 153 | |||
| 154 | internal b32 win32_os_exit(); | ||
| 155 | internal void win32_handle_error(MemArena *arena, DWORD error); | ||
| 156 | internal u64 safe_truncate_64(u64 value); | ||
| 157 | |||
| 158 | internal void win32_log(String8List messages); | ||
| 159 | |||
| 160 | #define FileLoad(arena, path) win32_file_load(arena, path) | ||
| 161 | #define FileWrite(data, path) win32_file_write(data, path) | ||
| 162 | #define FileExists(path) win32_file_exists(path) | ||
| 163 | #define Win32FileExists(path) win32_file_exists(path) | ||
| 164 | #define DirLoad(arena, path) win32_open_dir(arena, original_path); | ||
| 165 | #define OSExit() win32_os_exit(); | ||
| 166 | |||
| 167 | #if COMPILER_GCC || COMPILER_CLANG | ||
| 168 | |||
| 169 | #define PRINTF_FORMAT(fmt, args) __attribute__((format(printf, fmt, args))) | ||
| 170 | internal void win32_logf(const char *fmt, ...) PRINTF_FORMAT; | ||
| 171 | |||
| 172 | #elif COMPILER_MSVC | ||
| 173 | internal void win32_logf(_Printf_format_string_ const char *fmt, ...); | ||
| 174 | |||
| 175 | #define Log(fmt, ...) win32_logf(fmt, __VA_ARGS__) | ||
| 176 | |||
| 177 | #endif // COMPILER_GCC || COMPILER_CLANG | ||
| 178 | |||
| 179 | //- Entrypoint and platform window layer | ||
| 180 | #if COMPILER_MSVC | ||
| 181 | #pragma warning(disable : 28251) // disabled the annotations warning that are defined in winbase.h | ||
| 182 | #define ENTRYPOINT(hinstance, prev_instance, cmd_line, show_cmd) \ | ||
| 183 | int WINAPI WinMain(hinstance, prev_instance, cmd_line, show_cmd) | ||
| 184 | #endif // COMPIPILER_MSVC DISABLE WARNING | ||
| 185 | |||
| 186 | internal LRESULT CALLBACK win32_window_proc(HWND handle, UINT messages, WPARAM wparam, | ||
| 187 | LPARAM lparam); | ||
| 188 | internal HWND win32_create_window(HINSTANCE instance, int height, int width, int display_x, | ||
| 189 | int display_y, const char *window_name); | ||
| 190 | |||
| 191 | #define Win32CreateWindow(instance, height, width, display_x, display_y) \ | ||
| 192 | win32_create_window(instance, height, width, display_x, display_y) | ||
| 193 | |||
| 194 | #define PlatformCreateWindow(instance, height, width, display_x, display_y) \ | ||
| 195 | Win32CreateWindow(instance, height, width, display_x, display_y) | ||
| 196 | |||
| 197 | #endif // PLATFORM_LINUX || PLATFORM_WINDOWS | ||
| 198 | |||
| 199 | #endif // BASE_PLATFORM_H | ||
diff --git a/source/base/base_platform_linux.cpp b/source/base/base_platform_linux.cpp new file mode 100644 index 0000000..8fb190a --- /dev/null +++ b/source/base/base_platform_linux.cpp | |||
| @@ -0,0 +1,189 @@ | |||
| 1 | |||
| 2 | internal String8 unix_get_env(const char *var) { | ||
| 3 | |||
| 4 | AssertLog(environ, "Environment not loaded"); | ||
| 5 | |||
| 6 | String8 buffer = {0}; | ||
| 7 | u64 index = 0; | ||
| 8 | char *current = environ[index]; | ||
| 9 | while(environ[index] != NULL) { | ||
| 10 | if(CstringAnyMatch(environ[index], var)) { | ||
| 11 | String8 buffer = str8(environ[index]); | ||
| 12 | |||
| 13 | u64 equal_sign_pos = 0; | ||
| 14 | b32 found_equal_sign = str8_find(&buffer, 0, str8("="), &equal_sign_pos); | ||
| 15 | |||
| 16 | if(equal_sign_pos) { | ||
| 17 | buffer = str8_chop_left(buffer, equal_sign_pos); | ||
| 18 | } | ||
| 19 | |||
| 20 | |||
| 21 | } else { | ||
| 22 | Assert(0); | ||
| 23 | } | ||
| 24 | ++index; | ||
| 25 | } | ||
| 26 | return buffer; | ||
| 27 | |||
| 28 | |||
| 29 | } | ||
| 30 | |||
| 31 | |||
| 32 | |||
| 33 | //- | ||
| 34 | internal void unix_load_shared_library(MemArena *arena, SharedLibrary *library) { | ||
| 35 | |||
| 36 | void *data = dlopen(library->name, RTLD_LAZY | RTLD_LOCAL); | ||
| 37 | AssertLog(data, "failed to load shared object %s", library->name); // Assert and print the error that get's reported to dlerror | ||
| 38 | library->mem = data; | ||
| 39 | } | ||
| 40 | |||
| 41 | internal void *unix_shared_library_get_proc_from_name(SharedLibrary *library, const char *name) | ||
| 42 | { | ||
| 43 | void *symbol = dlsym(library->mem, name); | ||
| 44 | return symbol; | ||
| 45 | } | ||
| 46 | |||
| 47 | internal int unix_shared_library_close(SharedLibrary *library) | ||
| 48 | { | ||
| 49 | int result = dlclose(library); | ||
| 50 | AssertLog(result, "failed to close the library"); | ||
| 51 | |||
| 52 | return result; | ||
| 53 | } | ||
| 54 | |||
| 55 | //- implementations | ||
| 56 | internal b32 unix_file_exists(String8 path) { return access((const char *)path.data, F_OK) == 0; } | ||
| 57 | |||
| 58 | internal b32 unix_relative_path(MemArena *arena, String8 *path) { | ||
| 59 | |||
| 60 | String8 forward_slash = str8("./"); | ||
| 61 | b32 insertion_result = str8_insert(arena, path, &forward_slash, 0); | ||
| 62 | if(!insertion_result) { | ||
| 63 | breakpoint(); | ||
| 64 | } | ||
| 65 | return insertion_result; | ||
| 66 | |||
| 67 | } | ||
| 68 | |||
| 69 | internal String8 unix_file_load(MemArena *arena, String8 path) | ||
| 70 | { | ||
| 71 | if (path.size <= 0) { | ||
| 72 | Log("Can't find the file we we're supposed to open. This is an issue because we we're supposed to break way earlier"); | ||
| 73 | return {}; | ||
| 74 | } | ||
| 75 | |||
| 76 | String8 result = {0}; | ||
| 77 | struct stat sbuf = {0}; | ||
| 78 | |||
| 79 | if (!path.data) { | ||
| 80 | Log("Invalid path file"); | ||
| 81 | return str8_zero(); | ||
| 82 | } | ||
| 83 | |||
| 84 | |||
| 85 | const char *null_terminated_path = null_terminate(arena, &path); | ||
| 86 | |||
| 87 | s32 file = open(null_terminated_path, O_RDONLY); | ||
| 88 | if (file == -1) { | ||
| 89 | Log("An error occurred. File was not loaded. error code: ", Str8Fmt , Str8Arg(path)); | ||
| 90 | return str8_zero(); | ||
| 91 | } | ||
| 92 | |||
| 93 | if (fstat(file, &sbuf) == -1) { | ||
| 94 | close(file); | ||
| 95 | return str8_zero(); | ||
| 96 | } | ||
| 97 | |||
| 98 | result = PushString8(arena, (u64)sbuf.st_size); | ||
| 99 | |||
| 100 | u64 file_size = (u64)sbuf.st_size; | ||
| 101 | result.size = file_size; | ||
| 102 | |||
| 103 | u64 total_bytes_read = 0; | ||
| 104 | |||
| 105 | while(total_bytes_read < file_size) { | ||
| 106 | |||
| 107 | s64 bytes = read(file, result.data + total_bytes_read, file_size - total_bytes_read); | ||
| 108 | if (bytes == -1) { | ||
| 109 | Log("Failed, reading file was interrupted"); | ||
| 110 | return str8_zero(); | ||
| 111 | } | ||
| 112 | |||
| 113 | if(bytes == 0) break; | ||
| 114 | total_bytes_read += (u64)bytes; | ||
| 115 | } | ||
| 116 | |||
| 117 | result.size = total_bytes_read; | ||
| 118 | |||
| 119 | close(file); | ||
| 120 | return result; | ||
| 121 | } | ||
| 122 | |||
| 123 | // TODO: why was this returning a str8 before? | ||
| 124 | internal b32 unix_file_write(String8 data, String8 path) | ||
| 125 | { | ||
| 126 | s32 file = open((const char *)path.data, O_WRONLY | O_CREAT | O_TRUNC, 0644); | ||
| 127 | if (file == -1) { | ||
| 128 | return false; | ||
| 129 | } | ||
| 130 | |||
| 131 | u64 written = 0; | ||
| 132 | while (written < data.size) { | ||
| 133 | s64 err = write(file, data.data + written, data.size - written); | ||
| 134 | if (err == -1) { | ||
| 135 | close(file); | ||
| 136 | return false; | ||
| 137 | } | ||
| 138 | written += err; | ||
| 139 | } | ||
| 140 | |||
| 141 | close(file); | ||
| 142 | return true; | ||
| 143 | } | ||
| 144 | |||
| 145 | internal void unix_log_with_args(const char *format, ...) | ||
| 146 | { | ||
| 147 | va_list arguments; | ||
| 148 | va_start(arguments, format); | ||
| 149 | vprintf(format, arguments); | ||
| 150 | fflush(stdout); | ||
| 151 | va_end(arguments); | ||
| 152 | } | ||
| 153 | |||
| 154 | internal void unix_os_exit() { _exit(0); } | ||
| 155 | |||
| 156 | // TODO: rewrite this | ||
| 157 | internal String8List *unix_open_dir(MemArena *arena, String8 *path) | ||
| 158 | { | ||
| 159 | String8List *files_list = PushStructZero(arena, String8List); | ||
| 160 | files_list->first = &nil_str8_node; | ||
| 161 | files_list->last = &nil_str8_node; | ||
| 162 | |||
| 163 | cstring cpath = null_terminate(arena, path); | ||
| 164 | DIR *dir = opendir(cpath); | ||
| 165 | if (!dir) { | ||
| 166 | Log("opendir(%s) failed\n", cpath); | ||
| 167 | return files_list; | ||
| 168 | } | ||
| 169 | |||
| 170 | struct dirent *entry; | ||
| 171 | while ((entry = readdir(dir)) != NULL) { | ||
| 172 | if (entry->d_type != DT_REG) { // TODO: should symbolic files be handles aswell? | ||
| 173 | continue; | ||
| 174 | } | ||
| 175 | |||
| 176 | String8 name = str8(entry->d_name); | ||
| 177 | String8Node *file_node = PushStruct(arena, String8Node); | ||
| 178 | *file_node = ToString8Node(str8_clone(arena, &name)); | ||
| 179 | str8_list_push_node(files_list, file_node); | ||
| 180 | } | ||
| 181 | |||
| 182 | FOR_STR8_NODE(files_list, current) { | ||
| 183 | Log("Logging files names"); | ||
| 184 | LogStr8(current->value); | ||
| 185 | } | ||
| 186 | |||
| 187 | closedir(dir); | ||
| 188 | return files_list; | ||
| 189 | } \ No newline at end of file | ||
diff --git a/source/base/base_platform_linux_wayland.cpp b/source/base/base_platform_linux_wayland.cpp new file mode 100644 index 0000000..cd1fde8 --- /dev/null +++ b/source/base/base_platform_linux_wayland.cpp | |||
| @@ -0,0 +1,55 @@ | |||
| 1 | // TODO: replace unix_get_env with a generic macro | ||
| 2 | |||
| 3 | //- wayland | ||
| 4 | internal void wayland_display_connect(MemArena *arena) { | ||
| 5 | |||
| 6 | |||
| 7 | ScratchArena env_scratch = scratch_start(arena); | ||
| 8 | |||
| 9 | |||
| 10 | String8 str8_xdg_runtime_dir = unix_get_env("XDG_RUNTIME_DIR"); | ||
| 11 | char *xdg_runtime_dir = null_terminate(arena, &str8_xdg_runtime_dir); | ||
| 12 | |||
| 13 | u64 xdg_runtime_dir_len = str8_xdg_runtime_dir.size; | ||
| 14 | |||
| 15 | sockaddr_un addr = {}; | ||
| 16 | addr.sun_family = AF_UNIX; | ||
| 17 | |||
| 18 | Assert(xdg_runtime_dir_len <= cstring8_length((u8 *)addr.sun_path)); | ||
| 19 | |||
| 20 | u64 socket_path_len = 0; | ||
| 21 | MemCpy(addr.sun_path, xdg_runtime_dir, str8_xdg_runtime_dir.size); | ||
| 22 | socket_path_len += xdg_runtime_dir_len; | ||
| 23 | |||
| 24 | |||
| 25 | addr.sun_path[socket_path_len++] = '/'; | ||
| 26 | String8 str8_wayland_display_value = unix_get_env("WAYLAND_DISPLAY"); | ||
| 27 | char *wayland_display = null_terminate(arena, &str8_wayland_display_value); | ||
| 28 | if(wayland_display == NULL) | ||
| 29 | { | ||
| 30 | const char *wayland_display_default = "wayland-0"; | ||
| 31 | u64 wayland_display_default_len = cstring8_length((u8 *)wayland_display_default); | ||
| 32 | MemCpy(addr.sun_path + socket_path_len, wayland_display_default, wayland_display_default_len); | ||
| 33 | } | ||
| 34 | else | ||
| 35 | { | ||
| 36 | u64 wayland_display_len = cstring8_length((u8 *)wayland_display); | ||
| 37 | MemCpy(addr.sun_path + socket_path_len, wayland_display, wayland_display_len); | ||
| 38 | socket_path_len += wayland_display_len; | ||
| 39 | } | ||
| 40 | |||
| 41 | scratch_end(&env_scratch); | ||
| 42 | |||
| 43 | int fd = socket(AF_UNIX, SOCK_STREAM, 0); | ||
| 44 | if(fd == -1) | ||
| 45 | { | ||
| 46 | exit(1); | ||
| 47 | } | ||
| 48 | |||
| 49 | if(connect(fd, (struct sockaddr *)&addr, sizeof(addr)) == -1) | ||
| 50 | { | ||
| 51 | exit(1); | ||
| 52 | } | ||
| 53 | |||
| 54 | |||
| 55 | } \ No newline at end of file | ||
diff --git a/source/base/base_platform_linux_x11.cpp b/source/base/base_platform_linux_x11.cpp new file mode 100644 index 0000000..62b5cbf --- /dev/null +++ b/source/base/base_platform_linux_x11.cpp | |||
| @@ -0,0 +1,76 @@ | |||
| 1 | #ifdef X11 // @compile param | ||
| 2 | internal LinuxWindowHandle x11_create_window(MemArena *arena, u32 window_height, u32 window_width, | ||
| 3 | u32 display_x, u32 display_y) | ||
| 4 | { | ||
| 5 | |||
| 6 | Display *main_display = XOpenDisplay(0); | ||
| 7 | if (!main_display) { | ||
| 8 | Log("Error! Failed to open x11 display. Are you on X11?"); | ||
| 9 | } else { | ||
| 10 | Log("Successfully opened x11 display?\n"); | ||
| 11 | Log("Display: %p\n", main_display); | ||
| 12 | } | ||
| 13 | |||
| 14 | Window root = XDefaultRootWindow(main_display); | ||
| 15 | int screen = DefaultScreen(main_display); | ||
| 16 | |||
| 17 | Visual *v = DefaultVisual(main_display, screen); | ||
| 18 | |||
| 19 | XSetWindowAttributes wa = {.background_pixmap = None, | ||
| 20 | .background_pixel = | ||
| 21 | BlackPixel(main_display, DefaultScreen(main_display)), | ||
| 22 | .border_pixmap = CopyFromParent, | ||
| 23 | .border_pixel = 0, | ||
| 24 | .bit_gravity = ForgetGravity, | ||
| 25 | .win_gravity = NorthWestGravity, | ||
| 26 | .backing_store = NotUseful, | ||
| 27 | .backing_planes = 1, | ||
| 28 | .backing_pixel = 0, | ||
| 29 | .save_under = False, | ||
| 30 | .event_mask = 0, | ||
| 31 | .do_not_propagate_mask = 0, | ||
| 32 | .override_redirect = False, | ||
| 33 | .colormap = CopyFromParent, | ||
| 34 | .cursor = None}; | ||
| 35 | |||
| 36 | Window window = XCreateWindow(main_display, // display | ||
| 37 | root, // parent | ||
| 38 | display_x, // x | ||
| 39 | display_y, // y | ||
| 40 | window_width, // width | ||
| 41 | window_height, // height | ||
| 42 | 0, // border_width | ||
| 43 | CopyFromParent, // depth | ||
| 44 | CopyFromParent, // class | ||
| 45 | v, // depth | ||
| 46 | CWBackPixel, // visual | ||
| 47 | &wa); | ||
| 48 | |||
| 49 | XSetWindowBorder(main_display, window, 60); | ||
| 50 | XSelectInput(main_display, window, | ||
| 51 | ExposureMask | StructureNotifyMask | KeyReleaseMask | KeyPressMask); | ||
| 52 | XMapWindow(main_display, window); | ||
| 53 | |||
| 54 | LinuxWindowHandle out = {main_display, window}; | ||
| 55 | return out; | ||
| 56 | } | ||
| 57 | |||
| 58 | internal void x11_draw(LinuxWindowHandle *s, s32 start_x, s32 end_x, s32 start_y, s32 end_y) | ||
| 59 | { | ||
| 60 | |||
| 61 | u32 color = 0x55ffaaff; | ||
| 62 | GC gc = XCreateGC(s->display, s->window, 0, Nil); | ||
| 63 | XSetForeground(s->display, gc, color); | ||
| 64 | |||
| 65 | XFreeGC(s->display, gc); | ||
| 66 | } | ||
| 67 | |||
| 68 | internal s32 x11_cleanup(LinuxWindowHandle *s) | ||
| 69 | { | ||
| 70 | XCloseDisplay(s->display); | ||
| 71 | return 0; | ||
| 72 | } | ||
| 73 | |||
| 74 | internal s32 x11_read_input() { return 0; } | ||
| 75 | |||
| 76 | #endif // X11 \ No newline at end of file | ||
diff --git a/source/base/base_platform_win32.cpp b/source/base/base_platform_win32.cpp new file mode 100644 index 0000000..1b576ee --- /dev/null +++ b/source/base/base_platform_win32.cpp | |||
| @@ -0,0 +1,206 @@ | |||
| 1 | |||
| 2 | internal void | ||
| 3 | #if COMPILER_GCC || COMPILER_CLANG | ||
| 4 | win32_logf(const char *fmt, ...) PRINTF_FORMAT | ||
| 5 | #elif COMPILER_MSVC | ||
| 6 | win32_logf(_Printf_format_string_ const char *fmt, ...) | ||
| 7 | #endif | ||
| 8 | { | ||
| 9 | char buffer[4096]; | ||
| 10 | va_list args; | ||
| 11 | va_start(args, fmt); | ||
| 12 | int len = vsnprintf(buffer, sizeof(buffer), fmt, args); | ||
| 13 | va_end(args); | ||
| 14 | |||
| 15 | if (len < 0) | ||
| 16 | return; | ||
| 17 | if ((u64)len >= sizeof(buffer)) | ||
| 18 | len = sizeof(buffer) - 1; | ||
| 19 | |||
| 20 | HANDLE handle_stdout = GetStdHandle(STD_OUTPUT_HANDLE); | ||
| 21 | DWORD written = 0; | ||
| 22 | WriteConsoleA(handle_stdout, buffer, (DWORD)len, &written, NULL); | ||
| 23 | } | ||
| 24 | |||
| 25 | internal void win32_log(String8List messages) | ||
| 26 | { | ||
| 27 | HANDLE handle_stdout = GetStdHandle(STD_OUTPUT_HANDLE); | ||
| 28 | u64 count = 0; | ||
| 29 | for (String8Node *node = messages.first; count < messages.count; node = node->next) { | ||
| 30 | DWORD written = 0; | ||
| 31 | WriteConsoleA(handle_stdout, node->value.data, (DWORD)(node->value.size), &written, NULL); | ||
| 32 | ++count; | ||
| 33 | } | ||
| 34 | } | ||
| 35 | |||
| 36 | // exit win32 | ||
| 37 | // returns a boolean. i don't know what to do with the boolean if it fails hahahahhaa | ||
| 38 | // not like you can exit hahahahaha0 | ||
| 39 | internal b32 win32_os_exit() | ||
| 40 | { | ||
| 41 | ExitProcess(0); | ||
| 42 | return true; | ||
| 43 | } | ||
| 44 | |||
| 45 | // TODO: remove FORMAT_MESSAGE_ALLOCATE_BUFFER flag | ||
| 46 | internal void win32_handle_error(MemArena *arena, DWORD error) | ||
| 47 | { | ||
| 48 | ScratchArena scratch = scratch_start(arena); | ||
| 49 | String8 error_buffer = PushString8(arena, 255); | ||
| 50 | DWORD error_result = GetLastError(); | ||
| 51 | |||
| 52 | DWORD p_buffer = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, | ||
| 53 | NULL, error_result, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), | ||
| 54 | (LPTSTR)&error_buffer.data, 255, NULL); | ||
| 55 | |||
| 56 | Log("Error code: %lu\n", error_result); | ||
| 57 | Log("Error formatted message: %.*s\n", Str8Arg(error_buffer)); | ||
| 58 | scratch_end(&scratch); | ||
| 59 | } | ||
| 60 | |||
| 61 | internal u64 safe_truncate_64(u64 value) | ||
| 62 | { | ||
| 63 | Assert(value <= (u64)~0); | ||
| 64 | return (u64)value; | ||
| 65 | } | ||
| 66 | |||
| 67 | internal b32 win32_file_exists(u8 *path) | ||
| 68 | { | ||
| 69 | DWORD attributes = GetFileAttributesA((LPCSTR)path); | ||
| 70 | return ((attributes != INVALID_FILE_ATTRIBUTES) && !(attributes & FILE_ATTRIBUTE_DIRECTORY)); | ||
| 71 | } | ||
| 72 | |||
| 73 | internal b32 win32_file_write(String8 data, String8 path) | ||
| 74 | { | ||
| 75 | b32 result = false; | ||
| 76 | |||
| 77 | HANDLE file_handle = CreateFileA((LPCSTR)path.data, GENERIC_WRITE, 0, 0, CREATE_ALWAYS, 0, 0); | ||
| 78 | |||
| 79 | if (file_handle != INVALID_HANDLE_VALUE) { | ||
| 80 | DWORD bytes_written; | ||
| 81 | if (WriteFile(file_handle, data.data, (DWORD)data.size, &bytes_written, 0)) { | ||
| 82 | result = (bytes_written == data.size); | ||
| 83 | } else { | ||
| 84 | Log("Writing the file was unsuccessful"); | ||
| 85 | } | ||
| 86 | CloseHandle(file_handle); | ||
| 87 | } else { | ||
| 88 | Log("File handle has the INVALID_HANDLE_VALUE flag."); | ||
| 89 | } | ||
| 90 | |||
| 91 | return result; | ||
| 92 | } | ||
| 93 | |||
| 94 | internal String8 win32_file_load(MemArena *arena, String8 file) | ||
| 95 | { | ||
| 96 | String8 result = {0}; | ||
| 97 | HANDLE file_handle = | ||
| 98 | CreateFileA((LPCSTR)file.data, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0); | ||
| 99 | |||
| 100 | if (file_handle == INVALID_HANDLE_VALUE) { | ||
| 101 | Log("Failed to open file\n"); | ||
| 102 | return result; | ||
| 103 | } | ||
| 104 | |||
| 105 | LARGE_INTEGER size; | ||
| 106 | |||
| 107 | DWORD get_file_size_result = GetFileSizeEx(file_handle, &size); | ||
| 108 | if (!get_file_size_result) { | ||
| 109 | Log("Failed to get file size"); | ||
| 110 | win32_handle_error(arena, get_file_size_result); | ||
| 111 | CloseHandle(file_handle); | ||
| 112 | return result; | ||
| 113 | } | ||
| 114 | |||
| 115 | ScratchArena file_load_scratch = scratch_start(arena); | ||
| 116 | u32 file_size32 = (u32)safe_truncate_64(size.QuadPart); | ||
| 117 | result = PushString8(arena, file_size32); | ||
| 118 | |||
| 119 | // return value in case of success should be true | ||
| 120 | |||
| 121 | DWORD bytes_read = 0; | ||
| 122 | ; | ||
| 123 | DWORD read_file_result = ReadFile(file_handle, (LPSTR)result.data, file_size32, &bytes_read, 0); | ||
| 124 | |||
| 125 | if (!read_file_result || (file_size32 != bytes_read)) { | ||
| 126 | Log("Failed to read file\n"); | ||
| 127 | win32_handle_error(arena, read_file_result); | ||
| 128 | CloseHandle(file_handle); | ||
| 129 | return result; | ||
| 130 | } | ||
| 131 | |||
| 132 | result.size = file_size32; | ||
| 133 | // TODO: find out if we want to free this or not. | ||
| 134 | CloseHandle(file_handle); | ||
| 135 | return result; | ||
| 136 | } | ||
| 137 | |||
| 138 | internal void *win32_load_library() | ||
| 139 | { | ||
| 140 | // TODO: ... | ||
| 141 | } | ||
| 142 | |||
| 143 | internal void win32_library_get_proc() | ||
| 144 | { | ||
| 145 | // TODO: ... | ||
| 146 | } | ||
| 147 | |||
| 148 | internal LRESULT CALLBACK win32_window_proc(HWND handle, UINT messages, WPARAM wparam, | ||
| 149 | LPARAM lparam) | ||
| 150 | { | ||
| 151 | |||
| 152 | switch (messages) { | ||
| 153 | case WM_DESTROY: | ||
| 154 | Win32Running = false; | ||
| 155 | break; | ||
| 156 | case WM_PAINT: { | ||
| 157 | } break; | ||
| 158 | case WM_SYSKEYDOWN: | ||
| 159 | case WM_SYSKEYUP: | ||
| 160 | case WM_KEYDOWN: | ||
| 161 | case WM_KEYUP: { | ||
| 162 | // TODO: ... | ||
| 163 | } break; | ||
| 164 | } | ||
| 165 | |||
| 166 | return DefWindowProcA(handle, messages, wparam, lparam); | ||
| 167 | } | ||
| 168 | |||
| 169 | internal HWND win32_create_window(HINSTANCE instance, int height, int width, int display_x, | ||
| 170 | int display_y, const char *window_name) | ||
| 171 | { | ||
| 172 | Win32Running = true; | ||
| 173 | WNDCLASS window_class = {0}; | ||
| 174 | |||
| 175 | // TODO(nasr): check if CS_OWNDC CS_HREDRAW CS_VEREDRAW still matter | ||
| 176 | window_class.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW; | ||
| 177 | window_class.lpfnWndProc = win32_window_proc; | ||
| 178 | window_class.hInstance = instance; | ||
| 179 | window_class.lpszClassName = window_name; | ||
| 180 | |||
| 181 | HWND window_handle = {}; | ||
| 182 | if (RegisterClass(&window_class)) { | ||
| 183 | window_handle = CreateWindowEx(0, window_class.lpszClassName, window_name, | ||
| 184 | WS_OVERLAPPEDWINDOW | WS_VISIBLE, height, width, display_x, | ||
| 185 | display_y, 0, 0, instance, 0); | ||
| 186 | } | ||
| 187 | |||
| 188 | Assert(window_handle); | ||
| 189 | ShowWindow(window_handle, true); | ||
| 190 | |||
| 191 | MSG message = {}; | ||
| 192 | while (Win32Running) { | ||
| 193 | Log("status : %d", Win32Running); | ||
| 194 | b32 result = GetMessage(&message, 0, 0, 0); | ||
| 195 | if (result > 0) { | ||
| 196 | TranslateMessage(&message); | ||
| 197 | DispatchMessage(&message); | ||
| 198 | } else { | ||
| 199 | break; | ||
| 200 | } | ||
| 201 | } // cleanup get's handled by the OS, so there is no need worry about that | ||
| 202 | |||
| 203 | return window_handle; | ||
| 204 | } | ||
| 205 | |||
| 206 | internal String8List win32_open_dir(MemArena *arena, String8 *original_path) {} | ||
diff --git a/source/base/base_profile.cpp b/source/base/base_profile.cpp new file mode 100644 index 0000000..7400723 --- /dev/null +++ b/source/base/base_profile.cpp | |||
| @@ -0,0 +1,28 @@ | |||
| 1 | |||
| 2 | internal u64 | ||
| 3 | get_current_time() { | ||
| 4 | |||
| 5 | return 0; | ||
| 6 | } | ||
| 7 | |||
| 8 | internal Profile * | ||
| 9 | start_profile() { | ||
| 10 | |||
| 11 | |||
| 12 | } | ||
| 13 | |||
| 14 | |||
| 15 | internal void | ||
| 16 | end_profile(Profile *profile) { | ||
| 17 | |||
| 18 | |||
| 19 | |||
| 20 | } | ||
| 21 | |||
| 22 | internal void | ||
| 23 | log_profile(Profile *profile) { | ||
| 24 | |||
| 25 | |||
| 26 | |||
| 27 | |||
| 28 | } | ||
diff --git a/source/base/base_profile.h b/source/base/base_profile.h new file mode 100644 index 0000000..475e1f9 --- /dev/null +++ b/source/base/base_profile.h | |||
| @@ -0,0 +1,19 @@ | |||
| 1 | #ifndef BASE_PROFILE_H | ||
| 2 | #define BASE_PROFILE_H | ||
| 3 | |||
| 4 | |||
| 5 | typedef struct Profile Profile; | ||
| 6 | struct Profile { | ||
| 7 | |||
| 8 | f32 start; | ||
| 9 | f32 end; | ||
| 10 | // TODO: is there a way to measure the cpu cycles? | ||
| 11 | // frame time if graphics is enabled | ||
| 12 | |||
| 13 | #if GRAPHICS | ||
| 14 | f32 frame_time; | ||
| 15 | #endif | ||
| 16 | |||
| 17 | }; | ||
| 18 | |||
| 19 | #endif // BASE_PROFILE_H \ No newline at end of file | ||
diff --git a/source/base/base_rand.cpp b/source/base/base_rand.cpp new file mode 100644 index 0000000..cf8d63a --- /dev/null +++ b/source/base/base_rand.cpp | |||
| @@ -0,0 +1,19 @@ | |||
| 1 | #define RAND_CONSTANT 6364136223846793005 | ||
| 2 | internal u64 rand_u64(u64 constant); | ||
| 3 | |||
| 4 | internal u64 rand_u64(u64 constant) | ||
| 5 | { | ||
| 6 | time_t current_time = time(0); | ||
| 7 | constant = current_time ^ constant; | ||
| 8 | |||
| 9 | constant += 0x9e3779b97f4a7c15; | ||
| 10 | |||
| 11 | u64 z = constant; | ||
| 12 | z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9; | ||
| 13 | z = (z ^ (z >> 27)) * 0x94d049bb133111eb; | ||
| 14 | z = z ^ (z >> 31); | ||
| 15 | |||
| 16 | return z; | ||
| 17 | } | ||
| 18 | |||
| 19 | // TODO: bah i don't know if this is good \ No newline at end of file | ||
diff --git a/source/base/base_render.h b/source/base/base_render.h new file mode 100644 index 0000000..c3d2c33 --- /dev/null +++ b/source/base/base_render.h | |||
| @@ -0,0 +1,27 @@ | |||
| 1 | #ifndef BASE_RENDER | ||
| 2 | #define BASE_RENDER | ||
| 3 | |||
| 4 | //- generic window description | ||
| 5 | typedef struct WinParams WinParams; | ||
| 6 | struct WinParams { | ||
| 7 | void *window_handle; | ||
| 8 | int width; | ||
| 9 | int height; | ||
| 10 | int display_x; | ||
| 11 | int display_y; | ||
| 12 | const char *name; | ||
| 13 | }; | ||
| 14 | |||
| 15 | |||
| 16 | typedef struct Params Params; | ||
| 17 | struct Params { | ||
| 18 | const char *name; | ||
| 19 | int width; | ||
| 20 | int height; | ||
| 21 | VkExtensionProperties *vk_properties; | ||
| 22 | VkInstance *vk_instance; | ||
| 23 | }; | ||
| 24 | |||
| 25 | internal void vk_render_entrypoint(MemArena *arena, WinParams *engine, Params *window); | ||
| 26 | |||
| 27 | #endif // BASE_RENDER \ No newline at end of file | ||
diff --git a/source/base/base_render_vulkan.cpp b/source/base/base_render_vulkan.cpp new file mode 100644 index 0000000..9295d00 --- /dev/null +++ b/source/base/base_render_vulkan.cpp | |||
| @@ -0,0 +1,129 @@ | |||
| 1 | #if 0 | ||
| 2 | #define VK_NO_PROTOTYPES | ||
| 3 | |||
| 4 | internal void | ||
| 5 | vk_render_entrypoint(MemArena *arena, WinParams *window, Params *params) | ||
| 6 | { | ||
| 7 | |||
| 8 | // NOTE: requires #define VK_NO_PROTOTYPES | ||
| 9 | // NOTE: dlopen searches for a path starting with a '/' afterwards it starts searching other directories | ||
| 10 | // thats why the param is called path here. but we pass the name in stead | ||
| 11 | // https://tldp.org/HOWTO/Program-Library-HOWTO/dl-libraries.html | ||
| 12 | |||
| 13 | |||
| 14 | |||
| 15 | VkApplicationInfo app_info{}; // used for creating a vkInstance | ||
| 16 | VkInstanceCreateInfo create_info{}; | ||
| 17 | |||
| 18 | // fill in app info | ||
| 19 | { | ||
| 20 | app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; | ||
| 21 | app_info.pApplicationName = params->name; | ||
| 22 | app_info.applicationVersion = VK_MAKE_VERSION(1, 0, 0); | ||
| 23 | app_info.pEngineName = window->name; | ||
| 24 | app_info.engineVersion = VK_MAKE_VERSION(1, 0, 0); | ||
| 25 | app_info.apiVersion = VK_API_VERSION_1_0; | ||
| 26 | } | ||
| 27 | |||
| 28 | // fill in create info | ||
| 29 | { | ||
| 30 | create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; | ||
| 31 | create_info.pApplicationInfo = &app_info; | ||
| 32 | |||
| 33 | #if 0 | ||
| 34 | // TODO: fill in the extenions for windows. | ||
| 35 | // where do we find these? | ||
| 36 | create_info.enabledExtensionCount = ...; | ||
| 37 | create_info.ppEnabledExtensionNames = ...; | ||
| 38 | #endif | ||
| 39 | } | ||
| 40 | |||
| 41 | #ifdef BASE_DEBUG | ||
| 42 | bool enable_validation_layer = true; | ||
| 43 | #else | ||
| 44 | bool enable_validation_layer = false; | ||
| 45 | #endif | ||
| 46 | |||
| 47 | |||
| 48 | bool layer_found = false; | ||
| 49 | // check validation support | ||
| 50 | ScratchArena scratch = scratch_start(arena); | ||
| 51 | |||
| 52 | |||
| 53 | // gather the validation layers and available layers | ||
| 54 | u32 layer_count; | ||
| 55 | vkEnumerateInstanceLayerProperties(&layer_count, NULL); | ||
| 56 | VkLayerProperties *available_layers; | ||
| 57 | String8List *validation_layers = PushStructZero(arena, String8List); | ||
| 58 | String8Node vk_layer_khronos_validation_node = ToString8Node(str8("VK_LAYER_KHRONOS_validation")); | ||
| 59 | str8_list_push_node(validation_layers, &vk_layer_khronos_validation_node); | ||
| 60 | available_layers = PushArrayZero(arena, VkLayerProperties, layer_count); | ||
| 61 | vkEnumerateInstanceLayerProperties(&layer_count, available_layers); | ||
| 62 | |||
| 63 | for(String8Node *current_node = validation_layers->first; !is_nil_str8_node(current_node); current_node = current_node->next) { | ||
| 64 | for(u32 index = 0; index < layer_count; ++index) { | ||
| 65 | VkLayerProperties *current_layer = &available_layers[index]; | ||
| 66 | String8 current_layer_name = str8(current_layer->layerName); | ||
| 67 | if(str8_is_match(current_node->value, current_layer_name)) { | ||
| 68 | layer_found = true; | ||
| 69 | break; | ||
| 70 | } | ||
| 71 | } | ||
| 72 | } | ||
| 73 | |||
| 74 | // check if we found that specific layer and we can continue | ||
| 75 | |||
| 76 | if(enable_validation_layer) { // check the avalaiobe layers after creating the instance | ||
| 77 | if(!layer_found) { | ||
| 78 | Log("requested validation layer not found!"); | ||
| 79 | } | ||
| 80 | |||
| 81 | |||
| 82 | // extract names, null terminated | ||
| 83 | String8 *names_in_one_str8_lit = PushStructZero(arena, String8); | ||
| 84 | for(u32 index = 0; index < layer_count; ++index) { | ||
| 85 | VkLayerProperties *current_layer = &available_layers[index]; | ||
| 86 | String8 current_layer_name; | ||
| 87 | if(current_layer != NULL) { | ||
| 88 | current_layer_name = str8(current_layer->layerName); | ||
| 89 | } else { | ||
| 90 | breakpoint(); | ||
| 91 | } | ||
| 92 | str8_append(names_in_one_str8_lit, ¤t_layer_name); | ||
| 93 | } | ||
| 94 | |||
| 95 | create_info.enabledLayerCount = layer_count; | ||
| 96 | create_info.ppEnabledLayerNames = (const char *)names_in_one_str8_lit->data; // TODO: is this fixed??? | ||
| 97 | } | ||
| 98 | |||
| 99 | VkInstance instance = VK_NULL_HANDLE; | ||
| 100 | VkResult result = vkCreateInstance(&create_info, NULL, &instance); | ||
| 101 | |||
| 102 | if(result != VK_SUCCESS) { // check the return code of the instance creation | ||
| 103 | Log("Failed to create a VkInstance"); | ||
| 104 | } else { | ||
| 105 | scratch_end(&scratch); | ||
| 106 | |||
| 107 | { | ||
| 108 | // TODO: window polling untill it closes | ||
| 109 | } | ||
| 110 | } | ||
| 111 | |||
| 112 | if(instance != VK_NULL_HANDLE) { | ||
| 113 | vkDestroyInstance(instance, nullptr); | ||
| 114 | } | ||
| 115 | } | ||
| 116 | |||
| 117 | |||
| 118 | internal void | ||
| 119 | render_loop() | ||
| 120 | { | ||
| 121 | |||
| 122 | |||
| 123 | |||
| 124 | |||
| 125 | } | ||
| 126 | |||
| 127 | |||
| 128 | |||
| 129 | #endif \ No newline at end of file | ||
diff --git a/source/base/base_simd.cpp b/source/base/base_simd.cpp new file mode 100644 index 0000000..96b610e --- /dev/null +++ b/source/base/base_simd.cpp | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | #if 0 | ||
| 2 | #include <arm_neon.h> | ||
| 3 | #endif // ARCH_ARM | ||
| 4 | |||
| 5 | #if 0 | ||
| 6 | #include <intrin.h> | ||
| 7 | #endif | ||
diff --git a/source/base/base_stack.cpp b/source/base/base_stack.cpp new file mode 100644 index 0000000..bf3f045 --- /dev/null +++ b/source/base/base_stack.cpp | |||
| @@ -0,0 +1,234 @@ | |||
| 1 | |||
| 2 | // TODO: the previous position of the stack header never get's set | ||
| 3 | // TODO: renmae current_position in arena.cpp and arena.h to current_address or vice versa | ||
| 4 | |||
| 5 | #if PLATFORM_LINUX | ||
| 6 | internal MemStack *unix_stack_create(u64 capacity) | ||
| 7 | { | ||
| 8 | MemStack *stack = (MemStack *)mmap(0, capacity + sizeof(MemStack), PROT_READ | PROT_WRITE, | ||
| 9 | MAP_SHARED | MAP_ANONYMOUS, -1, 0); | ||
| 10 | |||
| 11 | if (stack == MAP_FAILED) { | ||
| 12 | return NULL; | ||
| 13 | } | ||
| 14 | |||
| 15 | stack->capacity = capacity; | ||
| 16 | stack->base_position = (u8 *)stack + sizeof(MemStack); | ||
| 17 | stack->current_offset = 0; | ||
| 18 | |||
| 19 | return stack; | ||
| 20 | } | ||
| 21 | |||
| 22 | internal void unix_stack_destroy(MemStack *stack) | ||
| 23 | { | ||
| 24 | if (!stack) { | ||
| 25 | return; | ||
| 26 | } | ||
| 27 | |||
| 28 | munmap(stack, stack->capacity + sizeof(MemStack)); | ||
| 29 | } | ||
| 30 | #endif // PLATFORM_LINUX | ||
| 31 | |||
| 32 | #if PLATFORM_WINDOWS | ||
| 33 | |||
| 34 | // request a page from the operating system | ||
| 35 | internal MemStack *win32_stack_create(u64 capacity) | ||
| 36 | { | ||
| 37 | MemStack *stack = (MemStack *)VirtualAlloc(NULL, capacity + sizeof(MemStack), | ||
| 38 | MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); | ||
| 39 | |||
| 40 | if (!stack) { | ||
| 41 | Log("Failed to receive a memory address from the operating system"); | ||
| 42 | return NULL; | ||
| 43 | } | ||
| 44 | |||
| 45 | stack->capacity = capacity; | ||
| 46 | stack->base_position = (u8 *)stack + sizeof(MemStack); | ||
| 47 | stack->current_offset = 0; | ||
| 48 | return stack; | ||
| 49 | } | ||
| 50 | |||
| 51 | internal void win32_stack_destroy(MemStack *stack) | ||
| 52 | { | ||
| 53 | b32 result = VirtualFree(stack, | ||
| 54 | 0, // stands for the byte of committed pages | ||
| 55 | MEM_RELEASE); | ||
| 56 | |||
| 57 | if (!result) { | ||
| 58 | Log("Failed to receive a memory address from the operating system"); | ||
| 59 | } | ||
| 60 | } | ||
| 61 | |||
| 62 | #endif // PLATFORM_WINDOWS | ||
| 63 | |||
| 64 | internal u8 calculate_padding(u64 pointer, u8 alignment, u64 header_size) | ||
| 65 | { | ||
| 66 | u8 modulo, padding; | ||
| 67 | |||
| 68 | if (!is_pow(alignment)) { | ||
| 69 | return 0; | ||
| 70 | } | ||
| 71 | |||
| 72 | modulo = pointer & (u8)(alignment - 1); | ||
| 73 | |||
| 74 | padding = 0; | ||
| 75 | |||
| 76 | if (0 != modulo) { | ||
| 77 | padding = alignment - modulo; | ||
| 78 | } | ||
| 79 | |||
| 80 | if (padding < header_size) { | ||
| 81 | header_size -= padding; | ||
| 82 | |||
| 83 | if ((header_size & (alignment - 1)) != 0) { | ||
| 84 | padding += alignment * (1 + (u8)(header_size / alignment)); | ||
| 85 | } else { | ||
| 86 | padding += alignment * ((u8)(header_size / alignment)); | ||
| 87 | } | ||
| 88 | } | ||
| 89 | |||
| 90 | return padding; | ||
| 91 | } | ||
| 92 | |||
| 93 | internal MemStack *stack_push_align(MemStack *stack, u64 size, u8 alignment) | ||
| 94 | { | ||
| 95 | // NOTE: satifisying static analyzer | ||
| 96 | AssertLog((stack != NULL), "STACK IS NULL"); | ||
| 97 | u8 padding = 0; | ||
| 98 | |||
| 99 | // NOTE: this shouln't be possible because we have the allignment predefined | ||
| 100 | // so it's bassically a useless check | ||
| 101 | |||
| 102 | if (!is_pow(alignment)) { | ||
| 103 | breakpoint(); | ||
| 104 | return (0); | ||
| 105 | } | ||
| 106 | |||
| 107 | if (alignment > 128) { // NOTE: where does the 128 come from? | ||
| 108 | alignment = 128; | ||
| 109 | } | ||
| 110 | |||
| 111 | u64 current_address; | ||
| 112 | current_address = (u64)stack->base_position + stack->current_offset; // NOTE: we already we're checking if the stack existed | ||
| 113 | padding = calculate_padding(current_address, alignment, sizeof(MemStackHeader)); | ||
| 114 | |||
| 115 | if (stack->current_offset + padding + size > stack->capacity) { | ||
| 116 | breakpoint(); | ||
| 117 | return 0; | ||
| 118 | } | ||
| 119 | |||
| 120 | stack->current_offset += padding; | ||
| 121 | |||
| 122 | u64 next_address = current_address + (u64)padding; | ||
| 123 | MemStackHeader *header = (MemStackHeader *)(next_address - sizeof(MemStackHeader)); | ||
| 124 | header->padding = padding; | ||
| 125 | |||
| 126 | stack->current_offset += size; | ||
| 127 | |||
| 128 | return (MemStack *)MemSet((MemStack *)next_address, size); | ||
| 129 | } | ||
| 130 | |||
| 131 | internal void *stack_push(MemStack *stack, u64 size) | ||
| 132 | { | ||
| 133 | return stack_push_align(stack, size, arena_align); | ||
| 134 | } | ||
| 135 | |||
| 136 | internal void stack_pop(MemStack *stack, void *pointer) | ||
| 137 | { | ||
| 138 | if (pointer != NULL) { | ||
| 139 | u64 start, end, current_address; | ||
| 140 | MemStackHeader *header; | ||
| 141 | u64 prev_offset; | ||
| 142 | |||
| 143 | start = (u64)stack->base_position; | ||
| 144 | end = start + (u64)stack->capacity; | ||
| 145 | current_address = (u64)pointer; | ||
| 146 | |||
| 147 | if (!(start <= current_address && current_address < end)) { | ||
| 148 | if (0 && "Out of bounds memory address passed to stack allocator (free)") { | ||
| 149 | return; | ||
| 150 | } | ||
| 151 | return; | ||
| 152 | } | ||
| 153 | |||
| 154 | if (current_address >= start + (u64)stack->current_offset) { | ||
| 155 | return; | ||
| 156 | } | ||
| 157 | |||
| 158 | header = (MemStackHeader *)(current_address - sizeof(MemStackHeader)); | ||
| 159 | prev_offset = (size_t)(current_address - (u64)header->padding - start); | ||
| 160 | stack->current_offset = prev_offset; | ||
| 161 | } | ||
| 162 | } | ||
| 163 | |||
| 164 | internal MemStack *stack_resize_align(MemStack *stack, void *pointer, u64 old_size, u64 new_size, | ||
| 165 | u8 alignment) | ||
| 166 | { | ||
| 167 | if (pointer == NULL) { | ||
| 168 | return stack_push_align(stack, new_size, alignment); | ||
| 169 | } else if (new_size == 0) { | ||
| 170 | stack_pop(stack, pointer); | ||
| 171 | return NULL; | ||
| 172 | } | ||
| 173 | |||
| 174 | u64 start, end, current_address; | ||
| 175 | u64 min_size = old_size < new_size ? old_size : new_size; | ||
| 176 | void *new_pointer; | ||
| 177 | |||
| 178 | start = (u64)stack->base_position; | ||
| 179 | end = start + (u64)stack->capacity; | ||
| 180 | current_address = (u64)pointer; | ||
| 181 | if (!(start <= current_address && current_address < end)) { | ||
| 182 | return NULL; | ||
| 183 | } | ||
| 184 | |||
| 185 | if (current_address >= start + (u64)stack->current_offset) { | ||
| 186 | return NULL; | ||
| 187 | } | ||
| 188 | |||
| 189 | if (old_size == new_size) { | ||
| 190 | return (MemStack *)pointer; | ||
| 191 | } | ||
| 192 | |||
| 193 | new_pointer = stack_push_align(stack, new_size, alignment); | ||
| 194 | AssertLog((new_pointer != NULL), "stack_push_align failed in stack_resize_align"); | ||
| 195 | memmove(new_pointer, pointer, min_size); | ||
| 196 | return (MemStack *)new_pointer; | ||
| 197 | } | ||
| 198 | |||
| 199 | internal MemStack *arena_to_stack(MemArena *arena, u64 requested_size) | ||
| 200 | { | ||
| 201 | |||
| 202 | Assert(arena); | ||
| 203 | |||
| 204 | MemStack *stack = PushStruct(arena, MemStack); | ||
| 205 | u8 *buffer = PushArray(arena, u8, requested_size); | ||
| 206 | |||
| 207 | stack->header = 0; | ||
| 208 | stack->base_position = buffer; | ||
| 209 | stack->capacity = requested_size; | ||
| 210 | stack->current_offset = 0; | ||
| 211 | |||
| 212 | return stack; | ||
| 213 | } | ||
| 214 | |||
| 215 | #if 0 | ||
| 216 | #if MEM_DEBUG | ||
| 217 | global_variable String8List *stack_log; // TODO: initialize this in the mem stack callback | ||
| 218 | |||
| 219 | #endif | ||
| 220 | |||
| 221 | internal void | ||
| 222 | stack_debug_view(MemArena *global_arena, MemStack *stack) | ||
| 223 | { | ||
| 224 | |||
| 225 | if(!stack_log) | ||
| 226 | { | ||
| 227 | stack_log = PushStruct(global_arena, String8List); | ||
| 228 | } | ||
| 229 | |||
| 230 | |||
| 231 | } | ||
| 232 | #endif | ||
| 233 | |||
| 234 | internal void stack_clear(MemStack *stack) { stack->current_offset = 0; } | ||
diff --git a/source/base/base_stack.h b/source/base/base_stack.h new file mode 100644 index 0000000..9b69e66 --- /dev/null +++ b/source/base/base_stack.h | |||
| @@ -0,0 +1,47 @@ | |||
| 1 | #ifndef BASE_STACK_H | ||
| 2 | #define BASE_STACK_H | ||
| 3 | |||
| 4 | #if defined(PLATFORM_UNIX) | ||
| 5 | #define StackCreate(size) unix_stack_create(size) | ||
| 6 | #define StackDestroy(stack) unix_stack_destroy(stack) | ||
| 7 | #elif defined(PLATFORM_WINDOWS) | ||
| 8 | #define StackCreate(size) win32_stack_create(size) | ||
| 9 | #define StackDestroy(stack) win32_stack_destroy(stack) | ||
| 10 | #endif | ||
| 11 | |||
| 12 | //- | ||
| 13 | |||
| 14 | typedef struct MemStackHeader MemStackHeader; | ||
| 15 | typedef struct MemStack MemStack; | ||
| 16 | |||
| 17 | struct MemStackHeader { | ||
| 18 | u8 padding; | ||
| 19 | u8 previous_offset; | ||
| 20 | }; | ||
| 21 | |||
| 22 | struct MemStack { | ||
| 23 | MemStackHeader *header; | ||
| 24 | u64 current_offset; | ||
| 25 | u64 capacity; | ||
| 26 | u8 *base_position; // TODO: why is this a (u8 *)? | ||
| 27 | }; | ||
| 28 | |||
| 29 | //- | ||
| 30 | |||
| 31 | internal MemStack *stack_push_align(MemStack *stack, u64 size, u8 alignment); | ||
| 32 | internal MemStack *stack_resize_align(MemStack *stack, void *pointer, u64 old_size, u64 new_size, | ||
| 33 | u8 alignment); | ||
| 34 | internal void *stack_push(MemStack *stack, u64 size); | ||
| 35 | internal void stack_pop(MemStack *stack, void *pointer); | ||
| 36 | internal void stack_clear(MemStack *stack); | ||
| 37 | internal u8 calculate_padding(u64 pointer, u8 alignment, u64 header_size); | ||
| 38 | internal MemStack *arena_to_stack(MemArena *arena, u64 size, u64 alignment); | ||
| 39 | |||
| 40 | |||
| 41 | #define ArenaToStack(arena, size) arena_to_stack(arena, size); | ||
| 42 | #define StackPushStruct(stack, type) (type *)stack_push((stack), sizeof(type)) | ||
| 43 | #define StackPushArray(stack, type, len) (type *)stack_push((stack), sizeof(type) * (len)) | ||
| 44 | #define StackPop(stack, pointer) stack_pop(stack, pointer) | ||
| 45 | #define StackClear(stack) stack_clear(stack) | ||
| 46 | |||
| 47 | #endif // BASE_STACK_H | ||
diff --git a/source/base/base_stack_unix.cpp b/source/base/base_stack_unix.cpp new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/source/base/base_stack_unix.cpp | |||
diff --git a/source/base/base_stack_win32.cpp b/source/base/base_stack_win32.cpp new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/source/base/base_stack_win32.cpp | |||
diff --git a/source/base/base_string.cpp b/source/base/base_string.cpp new file mode 100644 index 0000000..5b006ff --- /dev/null +++ b/source/base/base_string.cpp | |||
| @@ -0,0 +1,498 @@ | |||
| 1 | internal b32 is_nil_str8(String8 *a) { return (a == &nil_str8); } | ||
| 2 | |||
| 3 | internal b32 is_nil_str8_node(String8Node *a) { return (a == &nil_str8_node); } | ||
| 4 | |||
| 5 | internal b32 is_nil_str16(String16 *a) { return (a == &nil_str16); } | ||
| 6 | |||
| 7 | internal b32 is_nil_str16_node(String16Node *a) { return (a == &nil_str16_node); } | ||
| 8 | |||
| 9 | internal b32 is_nil_str32(String32 *a) { return (a == &nil_str32); } | ||
| 10 | |||
| 11 | internal b32 is_nil_str32_node(String32Node *a) { return (a == &nil_str32_node); } | ||
| 12 | |||
| 13 | internal void str8_trim_left(String8 *buffer) | ||
| 14 | { | ||
| 15 | u64 index = 0; | ||
| 16 | while (cstring_is_whitespace(buffer->data[index])) { | ||
| 17 | ++index; | ||
| 18 | } | ||
| 19 | |||
| 20 | str8_chop_left(*buffer, index); | ||
| 21 | } | ||
| 22 | |||
| 23 | internal void str16_trim_left(String16 *buffer) | ||
| 24 | { | ||
| 25 | u64 index = 0; | ||
| 26 | while (cstring_is_whitespace(buffer->data[index])) { | ||
| 27 | ++index; | ||
| 28 | } | ||
| 29 | |||
| 30 | str16_chop_left(*buffer, index); | ||
| 31 | } | ||
| 32 | |||
| 33 | internal void str32_trim_left(String32 *buffer) | ||
| 34 | { | ||
| 35 | u64 index = 0; | ||
| 36 | |||
| 37 | while (cstring_is_whitespace(buffer->data[index])) { | ||
| 38 | ++index; | ||
| 39 | } | ||
| 40 | |||
| 41 | str32_chop_left(*buffer, index); | ||
| 42 | } | ||
| 43 | |||
| 44 | internal void str8_trim_right(String8 *buffer) | ||
| 45 | { | ||
| 46 | u64 index = buffer->size; | ||
| 47 | while (cstring_is_whitespace(buffer->data[index])) { | ||
| 48 | --index; | ||
| 49 | } | ||
| 50 | |||
| 51 | str8_chop_right(*buffer, index); | ||
| 52 | } | ||
| 53 | |||
| 54 | internal b32 str8_is_match(String8 a, String8 b) | ||
| 55 | { | ||
| 56 | if (a.size != b.size) { | ||
| 57 | return false; | ||
| 58 | } | ||
| 59 | |||
| 60 | if(a.size == 0) { // no need to check if b.size is 0 because both sizes are already equal | ||
| 61 | return true; | ||
| 62 | } | ||
| 63 | |||
| 64 | return (b32)(memcmp(a.data, b.data, b.size) == 0); | ||
| 65 | } | ||
| 66 | |||
| 67 | internal b32 str8_find_all(MemArena *arena, String8 *buffer, u64 start_pos, String8 element, String8List *out) | ||
| 68 | { | ||
| 69 | u64 count = 0; // checks if any we're found to pass that as a error handling mechanism | ||
| 70 | u64 found_pos = 0; | ||
| 71 | |||
| 72 | while (str8_find(buffer, start_pos, element, &found_pos)) { | ||
| 73 | String8Node *node = PushStruct(arena, String8Node); | ||
| 74 | *node = ToString8Node(str8_create_substr(buffer, found_pos, element.size)); | ||
| 75 | str8_list_push_node(out, node); | ||
| 76 | |||
| 77 | ++count; | ||
| 78 | start_pos = found_pos + element.size; | ||
| 79 | } | ||
| 80 | |||
| 81 | if (!count) | ||
| 82 | return false; | ||
| 83 | return true; | ||
| 84 | } | ||
| 85 | |||
| 86 | // TODO: does this work like it should? | ||
| 87 | internal b32 str8_find(String8 *buffer, u64 start_pos, String8 element, u64 *out) | ||
| 88 | { | ||
| 89 | for (u64 index = start_pos; index < buffer->size - element.size; ++index) { | ||
| 90 | String8 view = str8_chop_left(*buffer, index); | ||
| 91 | view = str8_chop_right(view, view.size - element.size); | ||
| 92 | if (str8_is_match(view, element)) { | ||
| 93 | *out = index; | ||
| 94 | return true; | ||
| 95 | } | ||
| 96 | } | ||
| 97 | |||
| 98 | return false; | ||
| 99 | } | ||
| 100 | |||
| 101 | internal b32 str8_replace(String8 buffer, String8 element, u64 start_pos) { | ||
| 102 | |||
| 103 | u64 offset = 0; | ||
| 104 | b32 result = str8_find(&buffer, start_pos, element, &offset); | ||
| 105 | if(!result) return false; | ||
| 106 | |||
| 107 | for(u64 index = 0; index < element.size; ++index) { | ||
| 108 | buffer.data[start_pos + index] = element.data[index]; | ||
| 109 | } | ||
| 110 | |||
| 111 | return true; | ||
| 112 | } | ||
| 113 | |||
| 114 | internal b32 str8_trim(String8 buffer) { | ||
| 115 | |||
| 116 | return str8_replace(buffer, str8("\n"), 0); | ||
| 117 | } | ||
| 118 | |||
| 119 | |||
| 120 | internal b32 str8_is_digit(String8 a, u64 start_pos) | ||
| 121 | { | ||
| 122 | for (u64 index = start_pos; index < a.size; ++index) { | ||
| 123 | if (!cstring_is_digit(a.data[index])) | ||
| 124 | return false; | ||
| 125 | } | ||
| 126 | |||
| 127 | return true; | ||
| 128 | } | ||
| 129 | |||
| 130 | internal void str8_copy(String8 *dest, String8 *src) | ||
| 131 | { | ||
| 132 | MemCpy(dest->data, src->data, src->size); | ||
| 133 | Assert(memcmp(dest->data, src->data, src->size) == 0); | ||
| 134 | dest->size = src->size; | ||
| 135 | } | ||
| 136 | |||
| 137 | internal String8 str8_clone(MemArena *arena, String8 *src) | ||
| 138 | { | ||
| 139 | String8 clone = PushString8(arena, src->size); | ||
| 140 | str8_copy(&clone, src); | ||
| 141 | return clone; | ||
| 142 | } | ||
| 143 | |||
| 144 | // you cant just do this you have to reallocat that thing no? | ||
| 145 | internal b32 str8_append_char(String8 *dest, u8 src) | ||
| 146 | { | ||
| 147 | if (is_nil_str8(dest) || src == 0) | ||
| 148 | return false; | ||
| 149 | dest->data[dest->size] = src; | ||
| 150 | dest->size += 1; | ||
| 151 | return true; | ||
| 152 | } | ||
| 153 | |||
| 154 | internal int str8_to_int(String8 *buffer) | ||
| 155 | { | ||
| 156 | |||
| 157 | int result = 0; | ||
| 158 | |||
| 159 | for (u64 index = 0; index < buffer->size; ++index) { | ||
| 160 | |||
| 161 | if (buffer->data[index] < 'Z' && buffer->data[index] > 'A') { | ||
| 162 | result = buffer->data[index] - '0'; | ||
| 163 | result *= 10; | ||
| 164 | } | ||
| 165 | } | ||
| 166 | return result; | ||
| 167 | } | ||
| 168 | |||
| 169 | internal b32 str8_append(String8 *dest, String8 src) | ||
| 170 | { | ||
| 171 | if (is_nil_str8(dest) || is_nil_str8(&src)) | ||
| 172 | return false; | ||
| 173 | MemCpy(dest->data + dest->size, src.data, src.size); | ||
| 174 | dest->size += src.size; | ||
| 175 | return true; | ||
| 176 | } | ||
| 177 | |||
| 178 | internal String8 str8_zero() { return String8{0}; } | ||
| 179 | |||
| 180 | // TODO: error handling | ||
| 181 | internal b32 str8_chop_left_ptr(String8 *buffer, u64 count) | ||
| 182 | { | ||
| 183 | if (buffer->size < count) return false; | ||
| 184 | |||
| 185 | buffer->data += count; | ||
| 186 | buffer->size -= count; | ||
| 187 | return true; | ||
| 188 | |||
| 189 | |||
| 190 | } | ||
| 191 | |||
| 192 | internal b32 str8_chop_right_ptr(String8 *buffer, u64 count) | ||
| 193 | { | ||
| 194 | if (buffer->size < count) return false; | ||
| 195 | |||
| 196 | buffer->size -= count; | ||
| 197 | return true; | ||
| 198 | } | ||
| 199 | |||
| 200 | internal String8 str8_chop_left(String8 buffer, u64 count) | ||
| 201 | { | ||
| 202 | if (buffer.size < count) { | ||
| 203 | return buffer; | ||
| 204 | } | ||
| 205 | |||
| 206 | buffer.data += count; | ||
| 207 | buffer.size -= count; | ||
| 208 | |||
| 209 | return buffer; | ||
| 210 | } | ||
| 211 | |||
| 212 | internal String8 str8_chop_right(String8 buffer, u64 count) | ||
| 213 | { | ||
| 214 | if (buffer.size < count) { | ||
| 215 | return buffer; | ||
| 216 | } | ||
| 217 | |||
| 218 | buffer.size -= count; | ||
| 219 | return buffer; | ||
| 220 | } | ||
| 221 | |||
| 222 | internal void str8_list_push_node(String8List *list, String8Node *node) | ||
| 223 | { | ||
| 224 | if (list) { | ||
| 225 | if (list->first == NULL) { | ||
| 226 | list->first = node; | ||
| 227 | list->last = node; | ||
| 228 | } else { | ||
| 229 | list->last->next = node; | ||
| 230 | list->last = node; | ||
| 231 | } | ||
| 232 | list->count += 1; | ||
| 233 | } | ||
| 234 | } | ||
| 235 | |||
| 236 | internal String8 str8_create_substr(String8 *buffer, u64 left_offset, u64 right_offset) | ||
| 237 | { | ||
| 238 | return str8_chop_right(str8_chop_left(*buffer, left_offset), | ||
| 239 | (buffer->size - left_offset) - | ||
| 240 | right_offset); // we need to remove all of the excess data to only | ||
| 241 | // retain the substring we want | ||
| 242 | } | ||
| 243 | |||
| 244 | internal String16 str16_create_substr(String16 *buffer, u64 left_offset, u64 right_offset) | ||
| 245 | { | ||
| 246 | return str16_chop_right(str16_chop_left(*buffer, left_offset), | ||
| 247 | (buffer->size - left_offset) - right_offset); | ||
| 248 | } | ||
| 249 | |||
| 250 | internal String32 str32_create_substr(String32 *buffer, u64 left_offset, u64 right_offset) | ||
| 251 | { | ||
| 252 | return str32_chop_right(str32_chop_left(*buffer, left_offset), | ||
| 253 | (buffer->size - left_offset) - right_offset); | ||
| 254 | } | ||
| 255 | |||
| 256 | internal String16 str16_chop_left(String16 buffer, u64 count) | ||
| 257 | { | ||
| 258 | if (buffer.size < count) { | ||
| 259 | return buffer; | ||
| 260 | } | ||
| 261 | |||
| 262 | for (u64 idx = 0; idx < count; ++idx) { | ||
| 263 | buffer.data++; | ||
| 264 | } | ||
| 265 | return buffer; | ||
| 266 | } | ||
| 267 | |||
| 268 | internal String32 str32_chop_left(String32 buffer, u64 count) | ||
| 269 | { | ||
| 270 | if (buffer.size < count) { | ||
| 271 | return buffer; | ||
| 272 | } | ||
| 273 | |||
| 274 | for (u64 idx = 0; idx < count; ++idx) { | ||
| 275 | buffer.data++; | ||
| 276 | } | ||
| 277 | return buffer; | ||
| 278 | } | ||
| 279 | |||
| 280 | internal String16 str16_chop_right(String16 buffer, u64 count) | ||
| 281 | { | ||
| 282 | if (buffer.size < count) { | ||
| 283 | return buffer; | ||
| 284 | } | ||
| 285 | |||
| 286 | buffer.size -= count; | ||
| 287 | return buffer; | ||
| 288 | } | ||
| 289 | |||
| 290 | internal String32 str32_chop_right(String32 buffer, u64 count) | ||
| 291 | { | ||
| 292 | if (buffer.size < count) { | ||
| 293 | return buffer; | ||
| 294 | } | ||
| 295 | |||
| 296 | buffer.size -= count; | ||
| 297 | return buffer; | ||
| 298 | } | ||
| 299 | |||
| 300 | internal b32 str8_prefix(MemArena *arena, String8 *buffer, String8 *prefix) { | ||
| 301 | b32 result = str8_insert(arena, buffer, prefix, 0); | ||
| 302 | return result; | ||
| 303 | } | ||
| 304 | |||
| 305 | internal b32 str8_starts_with(String8 buffer, String8 prefix) { | ||
| 306 | |||
| 307 | if(buffer.size < prefix.size) { | ||
| 308 | return false; | ||
| 309 | } | ||
| 310 | |||
| 311 | return str8_is_match(String8{buffer.data, prefix.size}, prefix); | ||
| 312 | } | ||
| 313 | |||
| 314 | internal b32 str8_is_empty(String8 *buffer) | ||
| 315 | { | ||
| 316 | if (cstring8_length(buffer->data) == 0 || buffer->size == 0) | ||
| 317 | return true; // the values can't be lower than 0 | ||
| 318 | return false; | ||
| 319 | } | ||
| 320 | |||
| 321 | internal b32 str8_insert(MemArena *arena, String8 *dest, String8 *src, u64 pos) | ||
| 322 | { | ||
| 323 | if (dest->size < pos) | ||
| 324 | return false; | ||
| 325 | |||
| 326 | String8 result = PushString8(arena, dest->size + src->size); | ||
| 327 | result.size = 0; | ||
| 328 | |||
| 329 | String8 head = str8_create_substr(dest, 0, pos); | ||
| 330 | String8 tail = str8_create_substr(dest, pos, dest->size); | ||
| 331 | |||
| 332 | if (!str8_append(&result, head)) | ||
| 333 | return false; | ||
| 334 | if (!str8_append(&result, *src)) | ||
| 335 | return false; | ||
| 336 | if (!str8_append(&result, tail)) | ||
| 337 | return false; | ||
| 338 | |||
| 339 | *dest = result; | ||
| 340 | return true; | ||
| 341 | } | ||
| 342 | |||
| 343 | internal cstring str8_to_cstring(MemArena *arena, String8 *buffer) | ||
| 344 | { | ||
| 345 | |||
| 346 | cstring null_terminated_buffer = null_terminate(arena, buffer); | ||
| 347 | return (cstring)null_terminated_buffer; | ||
| 348 | } | ||
| 349 | |||
| 350 | //- cstring implementations | ||
| 351 | |||
| 352 | internal b32 cstring_is_digit(u8 point) { return (point >= '0' && point <= '9'); } | ||
| 353 | |||
| 354 | internal b32 cstring_is_alpha(u8 point) | ||
| 355 | { | ||
| 356 | return ((point >= 'a' && point <= 'z') || (point >= 'A' && point <= 'Z') || (point == '_')); | ||
| 357 | } | ||
| 358 | |||
| 359 | internal b32 cstring_is_alpha_num(u8 point) | ||
| 360 | { | ||
| 361 | return (cstring_is_alpha(point) || cstring_is_digit(point)); | ||
| 362 | } | ||
| 363 | |||
| 364 | internal b32 cstring_is_whitespace(u8 point) { return (point == ' ' || point == '\t'); } | ||
| 365 | |||
| 366 | internal b32 cstring_is_slash(u8 point) { return (point == '/' || point == '\\'); } | ||
| 367 | |||
| 368 | internal b32 cstring_is_newline(u8 point) { return (point == '\n' || point == '\r'); } | ||
| 369 | |||
| 370 | internal b32 cstring_is_match(u8 *a, u8 *b) | ||
| 371 | { | ||
| 372 | u64 as = cstring8_length(a); | ||
| 373 | u64 bs = cstring8_length(b); | ||
| 374 | |||
| 375 | if (as != bs) { | ||
| 376 | return false; | ||
| 377 | } | ||
| 378 | |||
| 379 | for (u64 idx = 0; idx < as; ++idx) { | ||
| 380 | if (a[idx] != b[idx]) { | ||
| 381 | return false; | ||
| 382 | } | ||
| 383 | } | ||
| 384 | return true; | ||
| 385 | } | ||
| 386 | |||
| 387 | |||
| 388 | // NOTE: ignores the biggest buffer size | ||
| 389 | internal b32 cstring_any_match(u8 *a, u8 *b) | ||
| 390 | { | ||
| 391 | |||
| 392 | |||
| 393 | |||
| 394 | u64 lenght = cstring8_length(b); | ||
| 395 | for (u64 idx = 0; idx < lenght; ++idx) { | ||
| 396 | if (a[idx] != b[idx]) { | ||
| 397 | return false; | ||
| 398 | } | ||
| 399 | } | ||
| 400 | return true; | ||
| 401 | } | ||
| 402 | |||
| 403 | internal u64 cstring8_length(u8 *c) | ||
| 404 | { | ||
| 405 | u8 *p = c; | ||
| 406 | if (!c) | ||
| 407 | return 0; | ||
| 408 | for (; *c != 0; ++c) | ||
| 409 | ; | ||
| 410 | return (u64)(c - p); | ||
| 411 | } | ||
| 412 | |||
| 413 | internal u64 cstring16_length(u16 *c) | ||
| 414 | { | ||
| 415 | u16 *p = c; | ||
| 416 | |||
| 417 | if (!c) | ||
| 418 | return 0; | ||
| 419 | for (; *c != 0; ++c) | ||
| 420 | ; | ||
| 421 | return (u64)(p - c); | ||
| 422 | } | ||
| 423 | |||
| 424 | internal u64 cstring32_length(u32 *c) | ||
| 425 | { | ||
| 426 | |||
| 427 | u32 *p = c; | ||
| 428 | |||
| 429 | if (!c) | ||
| 430 | return 0; | ||
| 431 | for (; *c != 0; ++c) | ||
| 432 | ; | ||
| 433 | return (u64)(p - c); | ||
| 434 | } | ||
| 435 | |||
| 436 | internal cstring null_terminate(MemArena *arena, String8 *buffer) | ||
| 437 | { | ||
| 438 | char *text = (char *)arena_alloc(arena, buffer->size + 1, 0); | ||
| 439 | MemCpy(text, buffer->data, buffer->size); | ||
| 440 | text[buffer->size] = '\0'; | ||
| 441 | return (cstring)text; | ||
| 442 | } | ||
| 443 | |||
| 444 | internal String8 remove_null_terminator(MemArena *arena, String8 c) | ||
| 445 | { | ||
| 446 | Assert(arena); | ||
| 447 | if ((c.size > 0) && (c.data[c.size - 1] == '\0')) { | ||
| 448 | String8 chopped = {c.data, c.size - 1}; | ||
| 449 | return chopped; | ||
| 450 | } | ||
| 451 | |||
| 452 | return c; | ||
| 453 | } | ||
| 454 | |||
| 455 | // returns the index of where to find it. if not found returns 0. | ||
| 456 | internal u64 cstring8_find(u8 *buffer, u8 *element) | ||
| 457 | { | ||
| 458 | u8 *c = buffer; | ||
| 459 | while (c != 0) { | ||
| 460 | if (!cstring_is_match(buffer, element)) { | ||
| 461 | return c - buffer; | ||
| 462 | } else { | ||
| 463 | ++c; | ||
| 464 | } | ||
| 465 | } | ||
| 466 | |||
| 467 | return 0; | ||
| 468 | } | ||
| 469 | |||
| 470 | // returns the index of where to find it. if not found returns 0. | ||
| 471 | internal u64 cstring8_find_new_line(u8 *buffer) | ||
| 472 | { | ||
| 473 | u8 *c = buffer; | ||
| 474 | while (*c != 0) { | ||
| 475 | if (cstring_is_newline(*c)) { | ||
| 476 | return c - buffer; | ||
| 477 | } else { | ||
| 478 | ++c; | ||
| 479 | } | ||
| 480 | } | ||
| 481 | |||
| 482 | return 0; | ||
| 483 | } | ||
| 484 | |||
| 485 | // returns the index of where to find it. if not found returns 0. | ||
| 486 | internal u64 cstring8_find_needle(u8 *buffer, u8 needle) | ||
| 487 | { | ||
| 488 | u8 *c = buffer; | ||
| 489 | while (*c != 0) { | ||
| 490 | if (*c == needle) { | ||
| 491 | return c - buffer; | ||
| 492 | } else { | ||
| 493 | ++c; | ||
| 494 | } | ||
| 495 | } | ||
| 496 | |||
| 497 | return 0; | ||
| 498 | } | ||
diff --git a/source/base/base_string.h b/source/base/base_string.h new file mode 100644 index 0000000..4fdd4d5 --- /dev/null +++ b/source/base/base_string.h | |||
| @@ -0,0 +1,240 @@ | |||
| 1 | #ifndef BASE_STRING_H | ||
| 2 | #define BASE_STRING_H | ||
| 3 | |||
| 4 | //- cstring | ||
| 5 | |||
| 6 | typedef char *cstring; | ||
| 7 | |||
| 8 | // - str8 | ||
| 9 | typedef struct String8 String8; | ||
| 10 | typedef struct String16 String16; | ||
| 11 | typedef struct String32 String32; | ||
| 12 | |||
| 13 | typedef struct String8Node String8Node; | ||
| 14 | typedef struct String16Node String16Node; | ||
| 15 | typedef struct String32Node String32Node; | ||
| 16 | |||
| 17 | typedef struct String8List String8List; | ||
| 18 | typedef struct String16List String16List; | ||
| 19 | typedef struct String32List String32List; | ||
| 20 | |||
| 21 | //- strings | ||
| 22 | |||
| 23 | struct String8 { | ||
| 24 | u8 *data; | ||
| 25 | u64 size; | ||
| 26 | }; | ||
| 27 | |||
| 28 | struct String16 { | ||
| 29 | u16 *data; | ||
| 30 | u64 size; | ||
| 31 | }; | ||
| 32 | |||
| 33 | struct String32 { | ||
| 34 | u32 *data; | ||
| 35 | u64 size; | ||
| 36 | }; | ||
| 37 | |||
| 38 | //- nodes | ||
| 39 | struct String8Node { | ||
| 40 | String8Node *next; | ||
| 41 | String8 value; | ||
| 42 | }; | ||
| 43 | |||
| 44 | struct String16Node { | ||
| 45 | String16Node *next; | ||
| 46 | String16 string; | ||
| 47 | }; | ||
| 48 | |||
| 49 | struct String32Node { | ||
| 50 | String32Node *next; | ||
| 51 | String32 value; | ||
| 52 | }; | ||
| 53 | |||
| 54 | //- lists | ||
| 55 | struct String8List { | ||
| 56 | String8Node *first; | ||
| 57 | String8Node *last; | ||
| 58 | u64 count; | ||
| 59 | }; | ||
| 60 | |||
| 61 | struct String16List { | ||
| 62 | String16Node *first; | ||
| 63 | String16Node *last; | ||
| 64 | u64 count; | ||
| 65 | }; | ||
| 66 | |||
| 67 | struct String32List { | ||
| 68 | String32Node *first; | ||
| 69 | String32Node *last; | ||
| 70 | u64 count; | ||
| 71 | }; | ||
| 72 | |||
| 73 | //- nil / zero-value checks and constructors | ||
| 74 | internal b32 is_nil_str8(String8 *a); | ||
| 75 | internal b32 is_nil_str8_node(String8Node *a); | ||
| 76 | internal b32 is_nil_str16(String16 *a); | ||
| 77 | internal b32 is_nil_str16_node(String16Node *a); | ||
| 78 | internal b32 is_nil_str32(String32 *a); | ||
| 79 | internal b32 is_nil_str32_node(String32Node *a); | ||
| 80 | internal String8 str8_zero(); | ||
| 81 | |||
| 82 | //- str8 copy / clone / append | ||
| 83 | internal String8 str8_clone(MemArena *arena, String8 *src); | ||
| 84 | internal void str8_copy(String8 *dest, String8 *src); | ||
| 85 | internal b32 str8_append(String8 *dest, String8 src); | ||
| 86 | internal b32 str8_append_char(String8 *dest, u8 src); | ||
| 87 | internal b32 str8_insert(MemArena *arena, String8 *dest, String8 *src, u64 pos); | ||
| 88 | |||
| 89 | //- str8 trim / chop | ||
| 90 | internal void str8_trim_left(String8 *buffer); | ||
| 91 | internal void str8_trim_right(String8 *buffer); | ||
| 92 | internal String8 str8_chop_left(String8 buffer, u64 count); | ||
| 93 | internal String8 str8_chop_right(String8 buffer, u64 count); | ||
| 94 | internal b32 str8_chop_left_ptr(String8 *buffer, u64 count); | ||
| 95 | internal b32 str8_chop_right_ptr(String8 *buffer, u64 count); | ||
| 96 | |||
| 97 | //- str8 substrings, matching, search | ||
| 98 | internal String8 str8_create_substr(String8 *buffer, u64 left_offset, u64 right_offset); | ||
| 99 | internal b32 str8_is_match(String8 a, String8 b); | ||
| 100 | internal b32 str8_find(String8 *buffer, u64 start_pos, String8 element, u64 *out); | ||
| 101 | internal b32 str8_is_digit(String8 a, u64 start_pos); | ||
| 102 | internal b32 str8_is_empty(String8 *buffer); | ||
| 103 | |||
| 104 | //- str8 list | ||
| 105 | internal void str8_list_push_node(String8List *list, String8Node *node); | ||
| 106 | |||
| 107 | //- str16 / str32 chop and substrings | ||
| 108 | internal String16 str16_chop_left(String16 buffer, u64 count); | ||
| 109 | internal String16 str16_chop_right(String16 buffer, u64 count); | ||
| 110 | internal String16 str16_create_substr(String16 *buffer, u64 left_offset, u64 right_offset); | ||
| 111 | internal String32 str32_chop_left(String32 buffer, u64 count); | ||
| 112 | internal String32 str32_chop_right(String32 buffer, u64 count); | ||
| 113 | internal String32 str32_create_substr(String32 *buffer, u64 left_offset, u64 right_offset); | ||
| 114 | |||
| 115 | //- null-termination helpers | ||
| 116 | internal cstring null_terminate(MemArena *arena, String8 *c); | ||
| 117 | internal String8 remove_null_terminator(MemArena *arena, String8 c); | ||
| 118 | |||
| 119 | //- convert | ||
| 120 | |||
| 121 | internal cstring str8_to_cstring(String8 *buffer); | ||
| 122 | |||
| 123 | //- cstring character classification | ||
| 124 | internal b32 cstring_is_digit(u8 point); | ||
| 125 | internal b32 cstring_is_alpha(u8 point); | ||
| 126 | internal b32 cstring_is_alpha_num(u8 point); | ||
| 127 | internal b32 cstring_is_whitespace(u8 point); | ||
| 128 | internal b32 cstring_is_slash(u8 point); | ||
| 129 | internal b32 cstring_is_newline(u8 point); | ||
| 130 | |||
| 131 | #define CstringIsMatch(a, b) cstring_is_match((u8 *)(a), (u8 *)(b)) | ||
| 132 | #define CstringAnyMatch(a, b) cstring_any_match((u8 *)(a), (u8 *)(b)) | ||
| 133 | |||
| 134 | |||
| 135 | //- cstring matching, length, search | ||
| 136 | internal b32 cstring_is_match(u8 *a, u8 *b); | ||
| 137 | internal b32 cstring_any_match(u8 *a, u8 *b); | ||
| 138 | internal u64 cstring8_length(u8 *c); | ||
| 139 | internal u64 cstring16_length(u16 *c); | ||
| 140 | internal u64 cstring32_length(u32 *c); | ||
| 141 | internal u64 cstring8_find(u8 *buffer, u8 *element); | ||
| 142 | internal u64 cstring8_find_new_line(u8 *buffer); | ||
| 143 | internal u64 cstring8_find_needle(u8 *buffer, u8 needle); | ||
| 144 | |||
| 145 | //- | ||
| 146 | |||
| 147 | #if COMPILER_GCC | ||
| 148 | #pragma GCC diagnostic push | ||
| 149 | #pragma GCC diagnostic ignored "-Wconversion" | ||
| 150 | #endif | ||
| 151 | |||
| 152 | //- | ||
| 153 | ReadOnly global_variable String8 nil_str8 = {NULL, 0}; | ||
| 154 | ReadOnly global_variable String16 nil_str16 = {NULL, 0}; | ||
| 155 | ReadOnly global_variable String32 nil_str32 = {NULL, 0}; | ||
| 156 | |||
| 157 | ReadOnly global_variable String8Node nil_str8_node = {NULL, {}}; | ||
| 158 | ReadOnly global_variable String16Node nil_str16_node = {NULL, {}}; | ||
| 159 | ReadOnly global_variable String32Node nil_str32_node = {NULL, {}}; | ||
| 160 | ReadOnly global_variable String8List nil_str8_list = {NULL, {}}; | ||
| 161 | ReadOnly global_variable String16List nil_str16_list = {NULL, {}}; | ||
| 162 | ReadOnly global_variable String32List nil_str32_list = {NULL, {}}; | ||
| 163 | |||
| 164 | //- macros | ||
| 165 | #define PushString8(arena, count) {(u8 *)(PushArrayZero(arena, u8, (count))), (u64)(count)} | ||
| 166 | #define PushString16(arena, count) {(u16 *)(PushArrayZero(arena, u16, (count))), (u64)(count)} | ||
| 167 | #define PushString32(arena, count) {(u32 *)(PushArrayZero(arena, u32, (count))), (u64)(count)} | ||
| 168 | |||
| 169 | // TODO: is this null terminated? this is generally a concept i struggle with. when is the string | ||
| 170 | // null terminated and when not. because in thi case I am the one passing a string "/" or something | ||
| 171 | // like that. is that null terminated or not? | ||
| 172 | #define ToString8(data) \ | ||
| 173 | String8 { (u8 *)(data), (u64)(cstring8_length((u8 *)data)) } | ||
| 174 | #define ToString16(data) \ | ||
| 175 | String16 { (u16 *)(data), (u64)(cstring16_length((u16 *)data)) } | ||
| 176 | #define ToString32(data) \ | ||
| 177 | String32 { (u32 *)(data), (u64)(cstring32_length((u32 *)data)) } | ||
| 178 | |||
| 179 | #define str8(buffer) ToString8(buffer) | ||
| 180 | #define str16(buffer) ToString16(buffer) | ||
| 181 | #define str32(buffer) ToString32(buffer) | ||
| 182 | |||
| 183 | #define ToString8Size(data, size) \ | ||
| 184 | String8 { (u8 *)(data), (u64)(size) } | ||
| 185 | #define ToString16Size(data, size) \ | ||
| 186 | String16 { (u16 *)(data), (u64)(size) } | ||
| 187 | #define ToString32Size(data, size) \ | ||
| 188 | String32 { (u32 *)(data), (u64)(size) } | ||
| 189 | |||
| 190 | #define ToString8Node(string) \ | ||
| 191 | String8Node { &nil_str8_node, string } | ||
| 192 | #define ToString16Node(string) \ | ||
| 193 | String16Node { &nil_str8_node, string } | ||
| 194 | #define ToString32Node(string) \ | ||
| 195 | String32Node { &nil_str8_node, string } | ||
| 196 | |||
| 197 | #define str8_node(text) ToString8Node(str8(text)) | ||
| 198 | #define str16_node(text) ToString16Node(str16(text)) | ||
| 199 | #define str32_node(text) ToString32Node(str32(text)) | ||
| 200 | |||
| 201 | #define LOOP(expr, exec) for (; expr; exec) | ||
| 202 | |||
| 203 | // arbitratry amount of parameters can be passed to this function | ||
| 204 | // TODO: put the index name as a paramter so that the reference can be made later without it being | ||
| 205 | // so hidden | ||
| 206 | |||
| 207 | #define FOR_STR8_CHOP_LEFT(index, buffer, count) FOR_STR8_CHOP(index, buffer, count, str8_chop_left) | ||
| 208 | |||
| 209 | #define FOR_STR8_CHOP_RIGHT(index, buffer, count) \ | ||
| 210 | FOR_STR8_CHOP(index, buffer, count, str8_chop_right) | ||
| 211 | |||
| 212 | // NOTE: we face an issue with returning an rvalue. to solve this we should only use the pointer | ||
| 213 | // functions that we create (str8_chop_left_ptr and str8_chop_right_ptr) | ||
| 214 | #define FOR_STR8_CHOP(index, buffer, count, function) \ | ||
| 215 | for (u64 index = 0; (index < (buffer.size)) && ((buffer.size) > (count)); \ | ||
| 216 | function((buffer), (count)), ++index) | ||
| 217 | |||
| 218 | #define FOR_STR8_ARB_FN(expr, function, ...) LOOP(expr, function(__VA_ARGS__)) | ||
| 219 | |||
| 220 | #define FOR_STR8_MATCH(buffa, buffb) LOOP(str8_is_match(buffa, buffb), str8_chop_left(buffa, 1)) | ||
| 221 | |||
| 222 | #define FOR_STR8_NODE(str8_list, current) \ | ||
| 223 | for (String8Node *current = str8_list->first; !is_nil_str8_node(current); \ | ||
| 224 | current = current->next) | ||
| 225 | |||
| 226 | internal b32 is_nil_str8(String8 *a); | ||
| 227 | internal b32 is_nil_str8_node(String8Node *a); | ||
| 228 | internal b32 is_nil_str16(String16 *a); | ||
| 229 | internal b32 is_nil_str16_node(String16Node *a); | ||
| 230 | internal b32 is_nil_str32(String32 *a); | ||
| 231 | internal b32 is_nil_str32_node(String32Node *a); | ||
| 232 | |||
| 233 | #if COMPILER_GCC | ||
| 234 | #pragma GCC diagnostic pop | ||
| 235 | #endif | ||
| 236 | |||
| 237 | #define Str8Fmt "%.*s" | ||
| 238 | #define Str8Arg(buffer) (unsigned int)((buffer).size), (char *)((buffer).data) | ||
| 239 | |||
| 240 | #endif /* BASE_STRING_H */ | ||
