From f6d3090f7fa76eb8e0dbe060b8490cb49c001c39 Mon Sep 17 00:00:00 2001 From: nasr Date: Mon, 5 Oct 2026 22:02:56 +0200 Subject: init: base --- source/base/base_arena.cpp | 165 +++++++++ source/base/base_arena.h | 94 ++++++ source/base/base_core.h | 141 ++++++++ source/base/base_include.h | 88 +++++ source/base/base_memory.cpp | 10 + source/base/base_memory.h | 42 +++ source/base/base_platform.cpp | 5 + source/base/base_platform.h | 199 +++++++++++ source/base/base_platform_linux.cpp | 189 +++++++++++ source/base/base_platform_linux_wayland.cpp | 55 +++ source/base/base_platform_linux_x11.cpp | 76 +++++ source/base/base_platform_win32.cpp | 206 ++++++++++++ source/base/base_profile.cpp | 28 ++ source/base/base_profile.h | 19 ++ source/base/base_rand.cpp | 19 ++ source/base/base_render.h | 27 ++ source/base/base_render_vulkan.cpp | 129 +++++++ source/base/base_simd.cpp | 7 + source/base/base_stack.cpp | 234 +++++++++++++ source/base/base_stack.h | 47 +++ source/base/base_stack_unix.cpp | 0 source/base/base_stack_win32.cpp | 0 source/base/base_string.cpp | 498 ++++++++++++++++++++++++++++ source/base/base_string.h | 240 ++++++++++++++ 24 files changed, 2518 insertions(+) create mode 100644 source/base/base_arena.cpp create mode 100644 source/base/base_arena.h create mode 100644 source/base/base_core.h create mode 100644 source/base/base_include.h create mode 100644 source/base/base_memory.cpp create mode 100644 source/base/base_memory.h create mode 100644 source/base/base_platform.cpp create mode 100644 source/base/base_platform.h create mode 100644 source/base/base_platform_linux.cpp create mode 100644 source/base/base_platform_linux_wayland.cpp create mode 100644 source/base/base_platform_linux_x11.cpp create mode 100644 source/base/base_platform_win32.cpp create mode 100644 source/base/base_profile.cpp create mode 100644 source/base/base_profile.h create mode 100644 source/base/base_rand.cpp create mode 100644 source/base/base_render.h create mode 100644 source/base/base_render_vulkan.cpp create mode 100644 source/base/base_simd.cpp create mode 100644 source/base/base_stack.cpp create mode 100644 source/base/base_stack.h create mode 100644 source/base/base_stack_unix.cpp create mode 100644 source/base/base_stack_win32.cpp create mode 100644 source/base/base_string.cpp create mode 100644 source/base/base_string.h (limited to 'source') 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 @@ +#if PLATFORM_UNIX + +internal MemArena * +unix_arena_create(u64 capacity) +{ + MemArena *arena = (MemArena *)mmap( + /* kernel decides where to throw the arena */ + NULL, + capacity + sizeof(MemArena), + PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_ANONYMOUS, + -1, + 0); + + if(arena == MAP_FAILED) { + return NULL; + } + + arena->capacity = capacity; + arena->base_position = (u8 *)arena + sizeof(MemArena); + arena->current_position = 0; + arena->previous_position = 0; + + return arena; +} + +internal void +unix_arena_destroy(MemArena *arena) +{ + if(!arena) { + return; + } + + munmap(arena, arena->capacity + sizeof(MemArena)); +} + +#elif PLATFORM_WINDOWS + +internal MemArena * +win32_arena_create(u64 capacity) +{ + MemArena *arena = (MemArena *)VirtualAlloc(NULL, + capacity + sizeof(MemArena), + MEM_COMMIT | MEM_RESERVE, + PAGE_READWRITE); + if(!arena) { + return NULL; + } + + arena->capacity = capacity; + arena->base_position = (u8 *)arena + sizeof(MemArena); + arena->current_position = 0; + arena->previous_position = 0; + return arena; +} + +internal void +win32_arena_destroy(MemArena *arena) +{ + if(!arena) { + return; + } + + VirtualFree(arena, 0, MEM_RELEASE); + return; +} + +#endif + +internal void * +arena_alloc(MemArena *arena, u64 size, b32 zero) +{ + Assert(arena); + + u64 aligned = Align(arena->current_position, arena_align); + u64 new_pos = aligned + size; + + AssertLog((new_pos <= arena->capacity), "max arena capacity reached\n"); + + + void *out = arena->base_position + aligned; + + arena->previous_position = arena->current_position; + arena->current_position = aligned + size; + + if(zero) { + MemSetZero(out, size); + } + + return out; +} + +internal void +arena_pop(MemArena *arena, u64 size) +{ + size = Min(size, arena->current_position); + arena->current_position -= size; +} + +internal void +arena_pop_to(MemArena *arena, u64 pos) +{ + u64 size = pos < arena->current_position ? arena->current_position - pos : 0; + arena_pop(arena, size); +} + +internal void +arena_clear(MemArena *arena) +{ + arena->current_position = 0; +} + +internal MemArena * +arena_resize_block_align(MemArena *arena, void *old_memory, u64 new_size, u64 old_size, umm alignment) +{ + u8 *old_mem = (u8 *)old_memory; + + if(is_pow(alignment)) { + arena->current_position = Align(arena->current_position, alignment); + } + + if(old_memory == NULL || old_size == 0) { + return (MemArena *)arena_alloc(arena, new_size, 0); + } 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 + if((arena->base_position + arena->previous_position) == old_memory) { + arena->current_position = arena->previous_position + new_size; + if(new_size > old_size) { + MemSet(&arena->current_position, new_size - old_size); + } + + return (MemArena *)old_memory; + } else { + void *new_memory = arena_alloc(arena, new_size, 0); + umm copy_size = old_size < new_size ? old_size : new_size; + memmove(new_memory, old_mem, copy_size); + } + } else { + AssertLog(false, "Failed to resize memory.\n"); + } + + return NULL; +} + +internal MemArena * +arena_resize_block(MemArena *arena, void *old_memory, u64 new_size, u64 old_size) +{ + return arena_resize_block_align(arena, old_memory, new_size, old_size, arena_align); +} + +internal ScratchArena +scratch_start(MemArena *arena) +{ + ScratchArena scratch; + scratch.arena = arena; + scratch.start_position = arena->current_position; + + return scratch; +} + +internal void +scratch_end(ScratchArena *scratch) +{ + scratch->arena->current_position = scratch->start_position; +} + 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 @@ +#ifndef BASE_ARENA_H +#define BASE_ARENA_H + +//- arena +typedef struct MemArena MemArena; +typedef struct ScratchArena ScratchArena; + +// NOTE: used capacity can be calculated so we don't care +struct MemArena { + u64 current_position; + u64 previous_position; + u64 capacity; + u8 *base_position; + MemArena *next; +}; + +struct ScratchArena { + MemArena *arena; + u64 start_position; +}; + +//- + +#if defined(PLATFORM_UNIX) + +internal MemArena *unix_arena_create(u64 capacity); +internal void unix_arena_destroy(MemArena *arena); + +#define ArenaCreate(capacity) unix_arena_create(capacity) +#define ArenaDestroy(arena) unix_arena_destroy(arena) +#elif defined(PLATFORM_WINDOWS) + +internal void win32_arena_destroy(MemArena *arena); +internal MemArena *win32_arena_create(u64 capacity); + +#define ArenaCreate(capacity) win32_arena_create(capacity) +#define ArenaDestroy(arena) win32_arena_destroy(arena) +#endif + +internal void *arena_alloc(MemArena *arena, u64 size, b32 zero); +internal void arena_pop(MemArena *arena, u64 size); +internal void arena_pop_to(MemArena *arena, u64 pos); +internal void arena_clear(MemArena *arena); +internal ScratchArena scratch_start(MemArena *arena); +internal void scratch_end(ScratchArena *scratch); +internal MemArena *arena_resize_block_align(MemArena *arena, void *old_memory, u64 new_size, u64 old_size, + umm alignment); +internal MemArena *arena_resize_block(MemArena *arena, void *old_memory, u64 new_size, u64 old_size); +internal u64 arena_calculate_used_capacity(MemArena *arena); + +#define Align(pointer, alignment) align((u64)(pointer), (umm)(alignment)) +#define PushStruct(arena, type) (type *)arena_alloc((arena), sizeof(type), 0) +#define PushStructZero(arena, type) (type *)arena_alloc((arena), sizeof(type), 1) +#define PushArray(arena, type, len) (type *)arena_alloc((arena), sizeof(type) * (len), 0) +#define PushArrayZero(arena, type, len) (type *)arena_alloc((arena), sizeof(type) * (len), 1) +#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 +#define ArenaPopLastPush(arena) arena_pop_to(arena, arena->previous_position) + + +// claude magic HAHHAHAHAH +#define LogArena(arena_expr) \ + do { \ + MemArena *_a = (arena_expr); \ + if (!_a) { \ + Log("[arena] %s = NULL (%s:%d)\n", #arena_expr, __FILE__, __LINE__); \ + break; \ + } \ + unsigned long long _cap = (unsigned long long)_a->capacity; \ + unsigned long long _cur = (unsigned long long)_a->current_position; \ + unsigned long long _prev = (unsigned long long)_a->previous_position; \ + unsigned long long _free = _cur <= _cap ? _cap - _cur : 0; \ + unsigned long long _last = _cur >= _prev ? _cur - _prev : 0; \ + double _pct = _cap ? (100.0 * (double)_cur / (double)_cap) : 0.0; \ + u8 *_expected_base = (u8 *)_a + sizeof(MemArena); \ + Log("[arena] %s @ %p (%s:%d)\n" \ + " base_position : %p .. %p%s\n" \ + " capacity : %llu bytes\n" \ + " current_position : %llu (used %.2f%%)\n" \ + " previous_position : %llu\n" \ + " last alloc size : %llu bytes (current - previous)\n" \ + " free : %llu bytes (capacity - current)\n" \ + " next write addr : %p\n", \ + #arena_expr, (void *)_a, __FILE__, __LINE__, \ + (void *)_a->base_position, (void *)(_a->base_position + _cap), \ + _a->base_position == _expected_base ? "" : " <-- base != arena + sizeof(MemArena)", \ + _cap, _cur, _pct, _prev, _last, _free, \ + (void *)(_a->base_position + _cur)); \ + if (_cur > _cap) \ + Log(" !! OVERFLOW: current_position exceeds capacity by %llu bytes\n", _cur - _cap); \ + if (_prev > _cur) \ + Log(" !! previous_position (%llu) > current_position (%llu)\n", _prev, _cur); \ + } while (0) + +#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 @@ +#ifndef BASE_CORE_H +#define BASE_CORE_H + +//- compiler definitions +#if defined(_MSC_VER) +#define COMPILER_MSVC 1 +#elif defined(__clang__) +#define COMPILER_CLANG 1 +#elif defined(__GNUC__) +#define COMPILER_GCC 1 +#else +#error Unsupported compiler +#endif + +//- architecture definitions + +#if defined(__arm__) || defined(__aarch64__) +#define ARCH_ARM 1 +#elif defined(__i386__) || defined(__x86_64__) +#define ARCH_x86_64 1 +#endif + +//- compiler definitions + +//- core stuff + +/* assert an expression and output the file and the line */ +#define internal static +#define global_variable static +#define local_persist static + +#define arena_align (2 * sizeof(void *)) + +#define Min(a, b) (((a) < (b)) ? (a) : (b)) +#define Max(a, b) (((a) > (b)) ? (a) : (b)) + +#define Unused(x) (void)(x) +#define Nil 0 + +#define Deprecated __attribute__((__deprecated__)) + +//- breakpoints +#if ARCH_ARM +#define breakpoint() __asm__ volatile("brk #0") +#elif ARCH_x86_64 + +#if COMPILER_GCC +#define breakpoint() __asm__ volatile("int3") +#endif +#endif + +#if COMPILER_MSVC +#include +#define breakpoint() __debugbreak() +#endif + +//- +#define MemCpy(dest, src, len) memcpy((dest), (src), (len)) +#define MemSet(dest, len) memset((dest), (1), (len)) +#define MemSetZero(dest, len) memset((dest), (0), (len)) + +//- +#if COMPILER_MSVC +#pragma section("read_only", read) +#define ReadOnly __declspec(allocate("read_only")) +#elif COMPILER_GCC || COMPILER_CLANG + +#if PLATFORM_APPLE +#define ReadOnly __attribute__((section("__TEXT,__const"))) +#else +#define ReadOnly __attribute__((section(".const"))) +#endif // PLATFORM_APPLE + +#endif + +//- + +//- types + +typedef uint64_t u64; +typedef uint32_t u32; +typedef uint16_t u16; +typedef uint8_t u8; + +typedef int8_t s8; +typedef int16_t s16; + +typedef int32_t s32; +typedef int64_t s64; + +typedef float f32; +typedef double f64; + +typedef s32 b32; +typedef s16 b16; +typedef s8 b8; + +typedef uintptr_t umm; +typedef intptr_t smm; + +//- disable certain msvc errors +#if COMPILER_MSVC +#pragma warning(disable : 6246) // disable the same name within a certain scope +#endif + +//- + +#define Assert(expression) _assert(expression) + +// NOTE: apparantly in macros this strinifys whataever you put after the # +#define AssertLog(expression, ...) do { \ + if (!expression) { \ + Log("%s:%d: %s\n", __FILE__, __LINE__, #expression); \ + Log(__VA_ARGS__);\ + Assert(false);\ + }\ + } \ + while(0) + +internal void _assert(bool expression); + + +internal void _assert(bool expression) +{ + + if (!expression) { + breakpoint(); +#if COMPILER_MSVC + __fastfail(0); +#else + __builtin_trap(); // NOTE: always stop, breakpoint() is empty on arm without DEBUG +#endif + } +} + + + + + + +#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 @@ +#ifndef BASE_INCLUDE_H +#define BASE_INCLUDE_H + +#if defined(__linux__) || defined(__APPLE__) +// #message "PLATFORM LINUX APPLE" +#define PLATFORM_UNIX 1 +#define PLATFORM_LINUX 1 +#elif defined(__linux__) && !defined(__APPLE__) +#message "PLATFORM LINUX" +#define PLATFORM_LINUX 1 +#elif defined(_WIN32) || defined(_WIN64) +#message "PLATFORM WINDOWS" +#define PLATFORM_WINDOWS 1 +#elif defined(__APPLE__) +#define PLATFORM_APPLE 1 +#message "PLATFORM APPLE" +#else +#message platform unknown +#endif // platform definitions + +#if PLATFORM_UNIX +#include +#include +#include +#include +#include // needed for making a connection to displays (wayland) +#include +#include +#include +#include +#include +#if PLATFORM_LINUX + +#if X11 +#include +#include +#include +#elif WAYLAND +#include +#endif + +#endif // PLATFORM_LINUX +#elif PLATFORM_WINDOWS +#define _WIN32_WINNT 0x0601 +#include +#endif // PLATFORM_UNIX - PLATFORM_WINDOWS + +#if VULKAN +#include +#elif OPENGL +... +#endif + +//- + +#include +#include +#include +#include +#include +#include +#include + +//- +#include "base_core.h" +#include "base_memory.h" + +#include "base_arena.h" +#include "base_stack.h" +#include "base_string.h" +#include "base_platform.h" +#include "base_render.h" + +#include "base_memory.cpp" +#include "base_arena.cpp" +#include "base_stack.cpp" +#include "base_string.cpp" +#include "base_platform.cpp" + +#include "base_rand.cpp" +#include "base_simd.cpp" + +#if VULKAN + +#include "base_render_vulkan.cpp" +#endif + +#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 @@ +internal b32 is_pow(umm x) { return (x & (x - 1)) == 0; } + +internal u64 align(u64 pointer, umm alignment) +{ + if ((alignment & (alignment - 1)) != 0) { + return pointer; + } + + return (pointer + alignment - 1) & ~(alignment - 1); +} 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 @@ +#ifndef BASE_MEMORY_H +#define BASE_MEMORY_H + +//- asan + +// Marks memory region [addr, addr+size) as unaddressable. +// This memory must be previously allocated by the user program. Accessing +// addresses in this region from instrumented code is forbidden until +// this region is unpoisoned. This function is not guaranteed to poison +// the whole region - it may poison only subregion of [addr, addr+size) due +// to ASan alignment restrictions. +// Method is NOT thread-safe in the sense that no two threads can +// (un)poison memory in the same memory region simultaneously. +void __asan_poison_memory_region(void const volatile *addr, size_t size); +// Marks memory region [addr, addr+size) as addressable. +// This memory must be previously allocated by the user program. Accessing +// addresses in this region is allowed until this region is poisoned again. +// This function may unpoison a superregion of [addr, addr+size) due to +// ASan alignment restrictions. +// Method is NOT thread-safe in the sense that no two threads can +// (un)poison memory in the same memory region simultaneously. +void __asan_unpoison_memory_region(void const volatile *addr, size_t size); + +// User code should use macros instead of functions. +#if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__) +#define ASAN_POISON_MEMORY_REGION(addr, size) __asan_poison_memory_region((addr), (size)) +#define ASAN_UNPOISON_MEMORY_REGION(addr, size) __asan_unpoison_memory_region((addr), (size)) +#else +#define ASAN_POISON_MEMORY_REGION(addr, size) ((void)(addr), (void)(size)) +#define ASAN_UNPOISON_MEMORY_REGION(addr, size) ((void)(addr), (void)(size)) +#endif + +//- helper functions + +#define KiB(n) (((u64)(n)) << 10) +#define MiB(n) (((u64)(n)) << 20) +#define GiB(n) (((u64)(n)) << 30) + +internal b32 is_pow(umm x); +internal u64 align(u64 pointer, umm alignment); + +#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 @@ +#if PLATFORM_LINUX +#include "base_platform_linux.cpp" +#elif PLATFORM_WINDOWS +#include "base_platform_win32.cpp" +#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 @@ +#ifndef BASE_PLATFORM_H +#define BASE_PLATFORM_H + +//- Linux +#if PLATFORM_LINUX || PLATFORM_UNIX + +//- Types +typedef struct SharedLibrary SharedLibrary; +struct SharedLibrary { + void *mem; + const char *name; +}; + +typedef struct Directory Directory; +struct Directory { + String8List *file_names; +}; + +#if 0 // TODO: @WAYLAND and @X11 implementation +typedef struct LinuxWindowHandle LinuxWindowHandle; +struct LinuxWindowHandle { + Display *display; + Window window; +}; + +#define LinuxLoadSharedLibrary(library, name) unix_ +#endif + +//- Shared libraries +internal void *unix_shared_library_get_proc_from_name(SharedLibrary *library, const char *name); +internal void unix_load_shared_library(MemArena *arena, SharedLibrary *library); +internal int unix_shared_library_close(SharedLibrary *library); + +internal String8 unix_get_env(const char *var); + +//- File I/O +internal b32 unix_file_write(String8 data, String8 path); +internal String8 unix_file_load(MemArena *arena, String8 path); +internal b32 unix_file_exists(String8 path); +internal String8List *unix_open_dir(MemArena *arena, + String8 *original_path); // TODO: change the return value + +internal s64 unix_read(int fd, const void *buf, u64 count); +internal void unix_os_exit() __attribute__((noreturn)); + +internal s64 unix_log(int fd, const void *buf, u64 count); +internal void unix_log_with_args(const char *format, ...); + +#define FileLoad(arena, path) unix_file_load(arena, path) +#define FileWrite(data, path) unix_file_write(data, path) +#define FileExists(path) unix_file_exists(path) +#define DirLoad(arena, path) unix_open_dir(arena, path) + +#define OSExit() unix_os_exit(); + +#if COMPILER_GCC || COMPILER_CLANG +// TODO: __VA_ARGS__ CLANG +#define Log(...) unix_log_with_args(__VA_ARGS__) +#define LogStr8(message) \ + Log(Str8Fmt, Str8Arg(message)) // NOTE: for this to wwork we need the /analyze argument +#endif // COMPILER_GCC || COMPILER_CLANG + +//- Entrypoint and platform window layer +#define ENTRYPOINT() int main(int count, char **args) + +#define PlatformCreateWindow(arena, height, width, display_x, display_y) \ + LinuxCreateWindow(arena, height, width, display_x, display_y) + +#define PlatformGetDisplayWidth(display, screen_num) \ + LinuxDisplayWdith(display, screen_num) // defined externally + +#define PlatformGetDisplayHeight(display, screen_num) \ + LinuxDisplayHeight(display, screen_num) // defined externally + +#define PlatformLoadBitmapFromFile(display, window, path, width, height, out, hotspot_x, \ + hotspot_y) \ + LinuxLoadBitMapFromFile(display, window, path, width, height, out, hotspot_x, hotspot_y) + +//- X11 +#if X11 +internal LinuxWindowHandle x11_create_window(MemArena *arena, u32 window_height, u32 window_width, + u32 disp_x, u32 disp_y); +internal void x11_draw(LinuxWindowHandle *s, s32 start_x, s32 end_x, s32 start_y, s32 end_y); +internal s32 x11_cleanup(LinuxWindowHandle *s); +internal s32 x11_read_input(); + +#define LinuxCreateWindow(arena, height, width, x, y) x11_create_window(arena, height, width, x, y) +#define LinuxGetDisplayWdith(display, screen_num) DisplayWidth(display, screen_num) +#define LinuxGetDisplayHeight(display, screen_num) DisplayHeight(display, screen_num); +#define LinuxLoadBitMapFromFile(display, window, path, width, height, out, hotspot_x, hotspot_y) \ +XReadBitmapFile(display, window, ) + +#include "base_platform_linux_x11.cpp" + +//- Wayland +#elif WAYLAND + +//- protocol values defined in the wayland library. it's a library load less i guess +global_variable const u32 wayland_display_object_id = 1; +global_variable const u16 wayland_wl_registry_event_global = 0; +global_variable const u16 wayland_shm_pool_event_format = 0; +global_variable const u16 wayland_wl_buffer_event_release = 0; +global_variable const u16 wayland_xdg_wm_base_event_ping = 0; +global_variable const u16 wayland_xdg_toplevel_event_configure = 0; +global_variable const u16 wayland_xdg_toplevel_event_close = 1; +global_variable const u16 wayland_xdg_surface_event_configure = 0; +global_variable const u16 wayland_wl_display_get_registry_opcode = 1; +global_variable const u16 wayland_wl_registry_bind_opcode = 0; +global_variable const u16 wayland_wl_compositor_create_surface_opcode = 0; +global_variable const u16 wayland_xdg_wm_base_pong_opcode = 3; +global_variable const u16 wayland_xdg_surface_ack_configure_opcode = 4; +global_variable const u16 wayland_wl_shm_create_pool_opcode = 0; +global_variable const u16 wayland_xdg_wm_base_get_xdg_surface_opcode = 2; +global_variable const u16 wayland_wl_shm_pool_create_buffer_opcode = 0; +global_variable const u16 wayland_wl_surface_attach_opcode = 1; +global_variable const u16 wayland_xdg_surface_get_toplevel_opcode = 1; +global_variable const u16 wayland_wl_surface_commit_opcode = 6; +global_variable const u16 wayland_wl_display_error_event = 0; +global_variable const u32 wayland_format_xrgb8888 = 1; +global_variable const u32 wayland_header_size = 8; +global_variable const u32 color_channels = 4; + +#include "base_platform_linux_wayland.cpp" + +#endif // X11 and Wayland -- set by build params + +//- Windows +#elif PLATFORM_WINDOWS + +//- Types +typedef struct Win32WindowHandle Win32WindowHandle; +struct Win32WindowHandle { + HWND window_handle; + HINSTANCE instance; + HDC dc; +}; + +global_variable b32 Win32Running = true; + +#define STDIN 0 +#define STDOUT 1 +#define STDERR 2 + +//- Shared libraries +internal void *win32_load_library(); +internal void win32_library_get_proc(); + +//- File I/O +internal b32 win32_file_exists(u8 *path); +internal b32 win32_file_write(String8 data, String8 path); +internal String8 win32_file_load(MemArena *arena, String8 file); +internal String8List win32_open_dir(MemArena *arena, String8 *original_path); // TODO: ... + +internal b32 win32_os_exit(); +internal void win32_handle_error(MemArena *arena, DWORD error); +internal u64 safe_truncate_64(u64 value); + +internal void win32_log(String8List messages); + +#define FileLoad(arena, path) win32_file_load(arena, path) +#define FileWrite(data, path) win32_file_write(data, path) +#define FileExists(path) win32_file_exists(path) +#define Win32FileExists(path) win32_file_exists(path) +#define DirLoad(arena, path) win32_open_dir(arena, original_path); +#define OSExit() win32_os_exit(); + +#if COMPILER_GCC || COMPILER_CLANG + +#define PRINTF_FORMAT(fmt, args) __attribute__((format(printf, fmt, args))) +internal void win32_logf(const char *fmt, ...) PRINTF_FORMAT; + +#elif COMPILER_MSVC +internal void win32_logf(_Printf_format_string_ const char *fmt, ...); + +#define Log(fmt, ...) win32_logf(fmt, __VA_ARGS__) + +#endif // COMPILER_GCC || COMPILER_CLANG + +//- Entrypoint and platform window layer +#if COMPILER_MSVC +#pragma warning(disable : 28251) // disabled the annotations warning that are defined in winbase.h +#define ENTRYPOINT(hinstance, prev_instance, cmd_line, show_cmd) \ + int WINAPI WinMain(hinstance, prev_instance, cmd_line, show_cmd) +#endif // COMPIPILER_MSVC DISABLE WARNING + +internal LRESULT CALLBACK win32_window_proc(HWND handle, UINT messages, WPARAM wparam, + LPARAM lparam); +internal HWND win32_create_window(HINSTANCE instance, int height, int width, int display_x, + int display_y, const char *window_name); + +#define Win32CreateWindow(instance, height, width, display_x, display_y) \ +win32_create_window(instance, height, width, display_x, display_y) + +#define PlatformCreateWindow(instance, height, width, display_x, display_y) \ + Win32CreateWindow(instance, height, width, display_x, display_y) + +#endif // PLATFORM_LINUX || PLATFORM_WINDOWS + +#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 @@ + +internal String8 unix_get_env(const char *var) { + + AssertLog(environ, "Environment not loaded"); + + String8 buffer = {0}; + u64 index = 0; + char *current = environ[index]; + while(environ[index] != NULL) { + if(CstringAnyMatch(environ[index], var)) { + String8 buffer = str8(environ[index]); + + u64 equal_sign_pos = 0; + b32 found_equal_sign = str8_find(&buffer, 0, str8("="), &equal_sign_pos); + + if(equal_sign_pos) { + buffer = str8_chop_left(buffer, equal_sign_pos); + } + + + } else { + Assert(0); + } + ++index; + } + return buffer; + + +} + + + +//- +internal void unix_load_shared_library(MemArena *arena, SharedLibrary *library) { + + void *data = dlopen(library->name, RTLD_LAZY | RTLD_LOCAL); + AssertLog(data, "failed to load shared object %s", library->name); // Assert and print the error that get's reported to dlerror + library->mem = data; +} + +internal void *unix_shared_library_get_proc_from_name(SharedLibrary *library, const char *name) +{ + void *symbol = dlsym(library->mem, name); + return symbol; +} + +internal int unix_shared_library_close(SharedLibrary *library) +{ + int result = dlclose(library); + AssertLog(result, "failed to close the library"); + + return result; +} + +//- implementations +internal b32 unix_file_exists(String8 path) { return access((const char *)path.data, F_OK) == 0; } + +internal b32 unix_relative_path(MemArena *arena, String8 *path) { + + String8 forward_slash = str8("./"); + b32 insertion_result = str8_insert(arena, path, &forward_slash, 0); + if(!insertion_result) { + breakpoint(); + } + return insertion_result; + +} + +internal String8 unix_file_load(MemArena *arena, String8 path) +{ + if (path.size <= 0) { + 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"); + return {}; + } + + String8 result = {0}; + struct stat sbuf = {0}; + + if (!path.data) { + Log("Invalid path file"); + return str8_zero(); + } + + + const char *null_terminated_path = null_terminate(arena, &path); + + s32 file = open(null_terminated_path, O_RDONLY); + if (file == -1) { + Log("An error occurred. File was not loaded. error code: ", Str8Fmt , Str8Arg(path)); + return str8_zero(); + } + + if (fstat(file, &sbuf) == -1) { + close(file); + return str8_zero(); + } + + result = PushString8(arena, (u64)sbuf.st_size); + + u64 file_size = (u64)sbuf.st_size; + result.size = file_size; + + u64 total_bytes_read = 0; + + while(total_bytes_read < file_size) { + + s64 bytes = read(file, result.data + total_bytes_read, file_size - total_bytes_read); + if (bytes == -1) { + Log("Failed, reading file was interrupted"); + return str8_zero(); + } + + if(bytes == 0) break; + total_bytes_read += (u64)bytes; + } + + result.size = total_bytes_read; + + close(file); + return result; +} + +// TODO: why was this returning a str8 before? +internal b32 unix_file_write(String8 data, String8 path) +{ + s32 file = open((const char *)path.data, O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (file == -1) { + return false; + } + + u64 written = 0; + while (written < data.size) { + s64 err = write(file, data.data + written, data.size - written); + if (err == -1) { + close(file); + return false; + } + written += err; + } + + close(file); + return true; +} + +internal void unix_log_with_args(const char *format, ...) +{ + va_list arguments; + va_start(arguments, format); + vprintf(format, arguments); + fflush(stdout); + va_end(arguments); +} + +internal void unix_os_exit() { _exit(0); } + +// TODO: rewrite this +internal String8List *unix_open_dir(MemArena *arena, String8 *path) +{ + String8List *files_list = PushStructZero(arena, String8List); + files_list->first = &nil_str8_node; + files_list->last = &nil_str8_node; + + cstring cpath = null_terminate(arena, path); + DIR *dir = opendir(cpath); + if (!dir) { + Log("opendir(%s) failed\n", cpath); + return files_list; + } + + struct dirent *entry; + while ((entry = readdir(dir)) != NULL) { + if (entry->d_type != DT_REG) { // TODO: should symbolic files be handles aswell? + continue; + } + + String8 name = str8(entry->d_name); + String8Node *file_node = PushStruct(arena, String8Node); + *file_node = ToString8Node(str8_clone(arena, &name)); + str8_list_push_node(files_list, file_node); + } + + FOR_STR8_NODE(files_list, current) { + Log("Logging files names"); + LogStr8(current->value); + } + + closedir(dir); + return files_list; +} \ 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 @@ +// TODO: replace unix_get_env with a generic macro + +//- wayland +internal void wayland_display_connect(MemArena *arena) { + + +ScratchArena env_scratch = scratch_start(arena); + + + String8 str8_xdg_runtime_dir = unix_get_env("XDG_RUNTIME_DIR"); + char *xdg_runtime_dir = null_terminate(arena, &str8_xdg_runtime_dir); + +u64 xdg_runtime_dir_len = str8_xdg_runtime_dir.size; + + sockaddr_un addr = {}; +addr.sun_family = AF_UNIX; + + Assert(xdg_runtime_dir_len <= cstring8_length((u8 *)addr.sun_path)); + +u64 socket_path_len = 0; +MemCpy(addr.sun_path, xdg_runtime_dir, str8_xdg_runtime_dir.size); +socket_path_len += xdg_runtime_dir_len; + + +addr.sun_path[socket_path_len++] = '/'; +String8 str8_wayland_display_value = unix_get_env("WAYLAND_DISPLAY"); +char *wayland_display = null_terminate(arena, &str8_wayland_display_value); +if(wayland_display == NULL) +{ +const char *wayland_display_default = "wayland-0"; +u64 wayland_display_default_len = cstring8_length((u8 *)wayland_display_default); +MemCpy(addr.sun_path + socket_path_len, wayland_display_default, wayland_display_default_len); +} +else +{ +u64 wayland_display_len = cstring8_length((u8 *)wayland_display); +MemCpy(addr.sun_path + socket_path_len, wayland_display, wayland_display_len); +socket_path_len += wayland_display_len; +} + +scratch_end(&env_scratch); + +int fd = socket(AF_UNIX, SOCK_STREAM, 0); +if(fd == -1) +{ +exit(1); +} + +if(connect(fd, (struct sockaddr *)&addr, sizeof(addr)) == -1) +{ +exit(1); +} + + +} \ 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 @@ +#ifdef X11 // @compile param +internal LinuxWindowHandle x11_create_window(MemArena *arena, u32 window_height, u32 window_width, + u32 display_x, u32 display_y) +{ + + Display *main_display = XOpenDisplay(0); + if (!main_display) { + Log("Error! Failed to open x11 display. Are you on X11?"); + } else { + Log("Successfully opened x11 display?\n"); + Log("Display: %p\n", main_display); + } + + Window root = XDefaultRootWindow(main_display); + int screen = DefaultScreen(main_display); + + Visual *v = DefaultVisual(main_display, screen); + + XSetWindowAttributes wa = {.background_pixmap = None, + .background_pixel = + BlackPixel(main_display, DefaultScreen(main_display)), + .border_pixmap = CopyFromParent, + .border_pixel = 0, + .bit_gravity = ForgetGravity, + .win_gravity = NorthWestGravity, + .backing_store = NotUseful, + .backing_planes = 1, + .backing_pixel = 0, + .save_under = False, + .event_mask = 0, + .do_not_propagate_mask = 0, + .override_redirect = False, + .colormap = CopyFromParent, + .cursor = None}; + + Window window = XCreateWindow(main_display, // display + root, // parent + display_x, // x + display_y, // y + window_width, // width + window_height, // height + 0, // border_width + CopyFromParent, // depth + CopyFromParent, // class + v, // depth + CWBackPixel, // visual + &wa); + + XSetWindowBorder(main_display, window, 60); + XSelectInput(main_display, window, + ExposureMask | StructureNotifyMask | KeyReleaseMask | KeyPressMask); + XMapWindow(main_display, window); + + LinuxWindowHandle out = {main_display, window}; + return out; +} + +internal void x11_draw(LinuxWindowHandle *s, s32 start_x, s32 end_x, s32 start_y, s32 end_y) +{ + + u32 color = 0x55ffaaff; + GC gc = XCreateGC(s->display, s->window, 0, Nil); + XSetForeground(s->display, gc, color); + + XFreeGC(s->display, gc); +} + +internal s32 x11_cleanup(LinuxWindowHandle *s) +{ + XCloseDisplay(s->display); + return 0; +} + +internal s32 x11_read_input() { return 0; } + +#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 @@ + +internal void +#if COMPILER_GCC || COMPILER_CLANG +win32_logf(const char *fmt, ...) PRINTF_FORMAT +#elif COMPILER_MSVC +win32_logf(_Printf_format_string_ const char *fmt, ...) +#endif +{ + char buffer[4096]; + va_list args; + va_start(args, fmt); + int len = vsnprintf(buffer, sizeof(buffer), fmt, args); + va_end(args); + + if (len < 0) + return; + if ((u64)len >= sizeof(buffer)) + len = sizeof(buffer) - 1; + + HANDLE handle_stdout = GetStdHandle(STD_OUTPUT_HANDLE); + DWORD written = 0; + WriteConsoleA(handle_stdout, buffer, (DWORD)len, &written, NULL); +} + +internal void win32_log(String8List messages) +{ + HANDLE handle_stdout = GetStdHandle(STD_OUTPUT_HANDLE); + u64 count = 0; + for (String8Node *node = messages.first; count < messages.count; node = node->next) { + DWORD written = 0; + WriteConsoleA(handle_stdout, node->value.data, (DWORD)(node->value.size), &written, NULL); + ++count; + } +} + +// exit win32 +// returns a boolean. i don't know what to do with the boolean if it fails hahahahhaa +// not like you can exit hahahahaha0 +internal b32 win32_os_exit() +{ + ExitProcess(0); + return true; +} + +// TODO: remove FORMAT_MESSAGE_ALLOCATE_BUFFER flag +internal void win32_handle_error(MemArena *arena, DWORD error) +{ + ScratchArena scratch = scratch_start(arena); + String8 error_buffer = PushString8(arena, 255); + DWORD error_result = GetLastError(); + + DWORD p_buffer = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, + NULL, error_result, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR)&error_buffer.data, 255, NULL); + + Log("Error code: %lu\n", error_result); + Log("Error formatted message: %.*s\n", Str8Arg(error_buffer)); + scratch_end(&scratch); +} + +internal u64 safe_truncate_64(u64 value) +{ + Assert(value <= (u64)~0); + return (u64)value; +} + +internal b32 win32_file_exists(u8 *path) +{ + DWORD attributes = GetFileAttributesA((LPCSTR)path); + return ((attributes != INVALID_FILE_ATTRIBUTES) && !(attributes & FILE_ATTRIBUTE_DIRECTORY)); +} + +internal b32 win32_file_write(String8 data, String8 path) +{ + b32 result = false; + + HANDLE file_handle = CreateFileA((LPCSTR)path.data, GENERIC_WRITE, 0, 0, CREATE_ALWAYS, 0, 0); + + if (file_handle != INVALID_HANDLE_VALUE) { + DWORD bytes_written; + if (WriteFile(file_handle, data.data, (DWORD)data.size, &bytes_written, 0)) { + result = (bytes_written == data.size); + } else { + Log("Writing the file was unsuccessful"); + } + CloseHandle(file_handle); + } else { + Log("File handle has the INVALID_HANDLE_VALUE flag."); + } + + return result; +} + +internal String8 win32_file_load(MemArena *arena, String8 file) +{ + String8 result = {0}; + HANDLE file_handle = + CreateFileA((LPCSTR)file.data, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0); + + if (file_handle == INVALID_HANDLE_VALUE) { + Log("Failed to open file\n"); + return result; + } + + LARGE_INTEGER size; + + DWORD get_file_size_result = GetFileSizeEx(file_handle, &size); + if (!get_file_size_result) { + Log("Failed to get file size"); + win32_handle_error(arena, get_file_size_result); + CloseHandle(file_handle); + return result; + } + + ScratchArena file_load_scratch = scratch_start(arena); + u32 file_size32 = (u32)safe_truncate_64(size.QuadPart); + result = PushString8(arena, file_size32); + + // return value in case of success should be true + + DWORD bytes_read = 0; + ; + DWORD read_file_result = ReadFile(file_handle, (LPSTR)result.data, file_size32, &bytes_read, 0); + + if (!read_file_result || (file_size32 != bytes_read)) { + Log("Failed to read file\n"); + win32_handle_error(arena, read_file_result); + CloseHandle(file_handle); + return result; + } + + result.size = file_size32; + // TODO: find out if we want to free this or not. + CloseHandle(file_handle); + return result; +} + +internal void *win32_load_library() +{ + // TODO: ... +} + +internal void win32_library_get_proc() +{ + // TODO: ... +} + +internal LRESULT CALLBACK win32_window_proc(HWND handle, UINT messages, WPARAM wparam, + LPARAM lparam) +{ + + switch (messages) { + case WM_DESTROY: + Win32Running = false; + break; + case WM_PAINT: { + } break; + case WM_SYSKEYDOWN: + case WM_SYSKEYUP: + case WM_KEYDOWN: + case WM_KEYUP: { + // TODO: ... + } break; + } + + return DefWindowProcA(handle, messages, wparam, lparam); +} + +internal HWND win32_create_window(HINSTANCE instance, int height, int width, int display_x, + int display_y, const char *window_name) +{ + Win32Running = true; + WNDCLASS window_class = {0}; + + // TODO(nasr): check if CS_OWNDC CS_HREDRAW CS_VEREDRAW still matter + window_class.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW; + window_class.lpfnWndProc = win32_window_proc; + window_class.hInstance = instance; + window_class.lpszClassName = window_name; + + HWND window_handle = {}; + if (RegisterClass(&window_class)) { + window_handle = CreateWindowEx(0, window_class.lpszClassName, window_name, + WS_OVERLAPPEDWINDOW | WS_VISIBLE, height, width, display_x, + display_y, 0, 0, instance, 0); + } + + Assert(window_handle); + ShowWindow(window_handle, true); + + MSG message = {}; + while (Win32Running) { + Log("status : %d", Win32Running); + b32 result = GetMessage(&message, 0, 0, 0); + if (result > 0) { + TranslateMessage(&message); + DispatchMessage(&message); + } else { + break; + } + } // cleanup get's handled by the OS, so there is no need worry about that + + return window_handle; +} + +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 @@ + +internal u64 +get_current_time() { + + return 0; +} + +internal Profile * +start_profile() { + + +} + + +internal void +end_profile(Profile *profile) { + + + +} + +internal void +log_profile(Profile *profile) { + + + + +} 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 @@ +#ifndef BASE_PROFILE_H +#define BASE_PROFILE_H + + +typedef struct Profile Profile; +struct Profile { + + f32 start; + f32 end; + // TODO: is there a way to measure the cpu cycles? + // frame time if graphics is enabled + +#if GRAPHICS + f32 frame_time; +#endif + +}; + +#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 @@ +#define RAND_CONSTANT 6364136223846793005 +internal u64 rand_u64(u64 constant); + +internal u64 rand_u64(u64 constant) +{ + time_t current_time = time(0); + constant = current_time ^ constant; + + constant += 0x9e3779b97f4a7c15; + + u64 z = constant; + z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9; + z = (z ^ (z >> 27)) * 0x94d049bb133111eb; + z = z ^ (z >> 31); + + return z; +} + +// 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 @@ +#ifndef BASE_RENDER +#define BASE_RENDER + +//- generic window description +typedef struct WinParams WinParams; +struct WinParams { + void *window_handle; + int width; + int height; + int display_x; + int display_y; + const char *name; +}; + + +typedef struct Params Params; +struct Params { + const char *name; + int width; + int height; + VkExtensionProperties *vk_properties; + VkInstance *vk_instance; +}; + +internal void vk_render_entrypoint(MemArena *arena, WinParams *engine, Params *window); + +#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 @@ +#if 0 +#define VK_NO_PROTOTYPES + +internal void +vk_render_entrypoint(MemArena *arena, WinParams *window, Params *params) +{ + + // NOTE: requires #define VK_NO_PROTOTYPES + // NOTE: dlopen searches for a path starting with a '/' afterwards it starts searching other directories + // thats why the param is called path here. but we pass the name in stead + // https://tldp.org/HOWTO/Program-Library-HOWTO/dl-libraries.html + + + + VkApplicationInfo app_info{}; // used for creating a vkInstance + VkInstanceCreateInfo create_info{}; + + // fill in app info + { + app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; + app_info.pApplicationName = params->name; + app_info.applicationVersion = VK_MAKE_VERSION(1, 0, 0); + app_info.pEngineName = window->name; + app_info.engineVersion = VK_MAKE_VERSION(1, 0, 0); + app_info.apiVersion = VK_API_VERSION_1_0; + } + + // fill in create info + { + create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; + create_info.pApplicationInfo = &app_info; + +#if 0 + // TODO: fill in the extenions for windows. + // where do we find these? + create_info.enabledExtensionCount = ...; + create_info.ppEnabledExtensionNames = ...; +#endif + } + +#ifdef BASE_DEBUG + bool enable_validation_layer = true; +#else + bool enable_validation_layer = false; +#endif + + + bool layer_found = false; + // check validation support + ScratchArena scratch = scratch_start(arena); + + + // gather the validation layers and available layers + u32 layer_count; + vkEnumerateInstanceLayerProperties(&layer_count, NULL); + VkLayerProperties *available_layers; + String8List *validation_layers = PushStructZero(arena, String8List); + String8Node vk_layer_khronos_validation_node = ToString8Node(str8("VK_LAYER_KHRONOS_validation")); + str8_list_push_node(validation_layers, &vk_layer_khronos_validation_node); + available_layers = PushArrayZero(arena, VkLayerProperties, layer_count); + vkEnumerateInstanceLayerProperties(&layer_count, available_layers); + + for(String8Node *current_node = validation_layers->first; !is_nil_str8_node(current_node); current_node = current_node->next) { + for(u32 index = 0; index < layer_count; ++index) { + VkLayerProperties *current_layer = &available_layers[index]; + String8 current_layer_name = str8(current_layer->layerName); + if(str8_is_match(current_node->value, current_layer_name)) { + layer_found = true; + break; + } + } + } + + // check if we found that specific layer and we can continue + + if(enable_validation_layer) { // check the avalaiobe layers after creating the instance + if(!layer_found) { + Log("requested validation layer not found!"); + } + + + // extract names, null terminated + String8 *names_in_one_str8_lit = PushStructZero(arena, String8); + for(u32 index = 0; index < layer_count; ++index) { + VkLayerProperties *current_layer = &available_layers[index]; + String8 current_layer_name; + if(current_layer != NULL) { + current_layer_name = str8(current_layer->layerName); + } else { + breakpoint(); + } + str8_append(names_in_one_str8_lit, ¤t_layer_name); + } + + create_info.enabledLayerCount = layer_count; + create_info.ppEnabledLayerNames = (const char *)names_in_one_str8_lit->data; // TODO: is this fixed??? + } + + VkInstance instance = VK_NULL_HANDLE; + VkResult result = vkCreateInstance(&create_info, NULL, &instance); + + if(result != VK_SUCCESS) { // check the return code of the instance creation + Log("Failed to create a VkInstance"); + } else { + scratch_end(&scratch); + + { + // TODO: window polling untill it closes + } + } + + if(instance != VK_NULL_HANDLE) { + vkDestroyInstance(instance, nullptr); + } +} + + +internal void +render_loop() +{ + + + + +} + + + +#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 @@ +#if 0 +#include +#endif // ARCH_ARM + +#if 0 +#include +#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 @@ + +// TODO: the previous position of the stack header never get's set +// TODO: renmae current_position in arena.cpp and arena.h to current_address or vice versa + +#if PLATFORM_LINUX +internal MemStack *unix_stack_create(u64 capacity) +{ + MemStack *stack = (MemStack *)mmap(0, capacity + sizeof(MemStack), PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_ANONYMOUS, -1, 0); + + if (stack == MAP_FAILED) { + return NULL; + } + + stack->capacity = capacity; + stack->base_position = (u8 *)stack + sizeof(MemStack); + stack->current_offset = 0; + + return stack; +} + +internal void unix_stack_destroy(MemStack *stack) +{ + if (!stack) { + return; + } + + munmap(stack, stack->capacity + sizeof(MemStack)); +} +#endif // PLATFORM_LINUX + +#if PLATFORM_WINDOWS + +// request a page from the operating system +internal MemStack *win32_stack_create(u64 capacity) +{ + MemStack *stack = (MemStack *)VirtualAlloc(NULL, capacity + sizeof(MemStack), + MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); + + if (!stack) { + Log("Failed to receive a memory address from the operating system"); + return NULL; + } + + stack->capacity = capacity; + stack->base_position = (u8 *)stack + sizeof(MemStack); + stack->current_offset = 0; + return stack; +} + +internal void win32_stack_destroy(MemStack *stack) +{ + b32 result = VirtualFree(stack, + 0, // stands for the byte of committed pages + MEM_RELEASE); + + if (!result) { + Log("Failed to receive a memory address from the operating system"); + } +} + +#endif // PLATFORM_WINDOWS + +internal u8 calculate_padding(u64 pointer, u8 alignment, u64 header_size) +{ + u8 modulo, padding; + + if (!is_pow(alignment)) { + return 0; + } + + modulo = pointer & (u8)(alignment - 1); + + padding = 0; + + if (0 != modulo) { + padding = alignment - modulo; + } + + if (padding < header_size) { + header_size -= padding; + + if ((header_size & (alignment - 1)) != 0) { + padding += alignment * (1 + (u8)(header_size / alignment)); + } else { + padding += alignment * ((u8)(header_size / alignment)); + } + } + + return padding; +} + +internal MemStack *stack_push_align(MemStack *stack, u64 size, u8 alignment) +{ + // NOTE: satifisying static analyzer + AssertLog((stack != NULL), "STACK IS NULL"); + u8 padding = 0; + + // NOTE: this shouln't be possible because we have the allignment predefined + // so it's bassically a useless check + + if (!is_pow(alignment)) { + breakpoint(); + return (0); + } + + if (alignment > 128) { // NOTE: where does the 128 come from? + alignment = 128; + } + + u64 current_address; + current_address = (u64)stack->base_position + stack->current_offset; // NOTE: we already we're checking if the stack existed + padding = calculate_padding(current_address, alignment, sizeof(MemStackHeader)); + + if (stack->current_offset + padding + size > stack->capacity) { + breakpoint(); + return 0; + } + + stack->current_offset += padding; + + u64 next_address = current_address + (u64)padding; + MemStackHeader *header = (MemStackHeader *)(next_address - sizeof(MemStackHeader)); + header->padding = padding; + + stack->current_offset += size; + + return (MemStack *)MemSet((MemStack *)next_address, size); +} + +internal void *stack_push(MemStack *stack, u64 size) +{ + return stack_push_align(stack, size, arena_align); +} + +internal void stack_pop(MemStack *stack, void *pointer) +{ + if (pointer != NULL) { + u64 start, end, current_address; + MemStackHeader *header; + u64 prev_offset; + + start = (u64)stack->base_position; + end = start + (u64)stack->capacity; + current_address = (u64)pointer; + + if (!(start <= current_address && current_address < end)) { + if (0 && "Out of bounds memory address passed to stack allocator (free)") { + return; + } + return; + } + + if (current_address >= start + (u64)stack->current_offset) { + return; + } + + header = (MemStackHeader *)(current_address - sizeof(MemStackHeader)); + prev_offset = (size_t)(current_address - (u64)header->padding - start); + stack->current_offset = prev_offset; + } +} + +internal MemStack *stack_resize_align(MemStack *stack, void *pointer, u64 old_size, u64 new_size, + u8 alignment) +{ + if (pointer == NULL) { + return stack_push_align(stack, new_size, alignment); + } else if (new_size == 0) { + stack_pop(stack, pointer); + return NULL; + } + + u64 start, end, current_address; + u64 min_size = old_size < new_size ? old_size : new_size; + void *new_pointer; + + start = (u64)stack->base_position; + end = start + (u64)stack->capacity; + current_address = (u64)pointer; + if (!(start <= current_address && current_address < end)) { + return NULL; + } + + if (current_address >= start + (u64)stack->current_offset) { + return NULL; + } + + if (old_size == new_size) { + return (MemStack *)pointer; + } + + new_pointer = stack_push_align(stack, new_size, alignment); + AssertLog((new_pointer != NULL), "stack_push_align failed in stack_resize_align"); + memmove(new_pointer, pointer, min_size); + return (MemStack *)new_pointer; +} + +internal MemStack *arena_to_stack(MemArena *arena, u64 requested_size) +{ + + Assert(arena); + + MemStack *stack = PushStruct(arena, MemStack); + u8 *buffer = PushArray(arena, u8, requested_size); + + stack->header = 0; + stack->base_position = buffer; + stack->capacity = requested_size; + stack->current_offset = 0; + + return stack; +} + +#if 0 +#if MEM_DEBUG +global_variable String8List *stack_log; // TODO: initialize this in the mem stack callback + +#endif + +internal void +stack_debug_view(MemArena *global_arena, MemStack *stack) +{ + + if(!stack_log) + { + stack_log = PushStruct(global_arena, String8List); + } + + +} +#endif + +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 @@ +#ifndef BASE_STACK_H +#define BASE_STACK_H + +#if defined(PLATFORM_UNIX) +#define StackCreate(size) unix_stack_create(size) +#define StackDestroy(stack) unix_stack_destroy(stack) +#elif defined(PLATFORM_WINDOWS) +#define StackCreate(size) win32_stack_create(size) +#define StackDestroy(stack) win32_stack_destroy(stack) +#endif + +//- + +typedef struct MemStackHeader MemStackHeader; +typedef struct MemStack MemStack; + +struct MemStackHeader { + u8 padding; + u8 previous_offset; +}; + +struct MemStack { + MemStackHeader *header; + u64 current_offset; + u64 capacity; + u8 *base_position; // TODO: why is this a (u8 *)? +}; + +//- + +internal MemStack *stack_push_align(MemStack *stack, u64 size, u8 alignment); +internal MemStack *stack_resize_align(MemStack *stack, void *pointer, u64 old_size, u64 new_size, + u8 alignment); +internal void *stack_push(MemStack *stack, u64 size); +internal void stack_pop(MemStack *stack, void *pointer); +internal void stack_clear(MemStack *stack); +internal u8 calculate_padding(u64 pointer, u8 alignment, u64 header_size); +internal MemStack *arena_to_stack(MemArena *arena, u64 size, u64 alignment); + + +#define ArenaToStack(arena, size) arena_to_stack(arena, size); +#define StackPushStruct(stack, type) (type *)stack_push((stack), sizeof(type)) +#define StackPushArray(stack, type, len) (type *)stack_push((stack), sizeof(type) * (len)) +#define StackPop(stack, pointer) stack_pop(stack, pointer) +#define StackClear(stack) stack_clear(stack) + +#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 diff --git a/source/base/base_stack_win32.cpp b/source/base/base_stack_win32.cpp new file mode 100644 index 0000000..e69de29 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 @@ +internal b32 is_nil_str8(String8 *a) { return (a == &nil_str8); } + +internal b32 is_nil_str8_node(String8Node *a) { return (a == &nil_str8_node); } + +internal b32 is_nil_str16(String16 *a) { return (a == &nil_str16); } + +internal b32 is_nil_str16_node(String16Node *a) { return (a == &nil_str16_node); } + +internal b32 is_nil_str32(String32 *a) { return (a == &nil_str32); } + +internal b32 is_nil_str32_node(String32Node *a) { return (a == &nil_str32_node); } + +internal void str8_trim_left(String8 *buffer) +{ + u64 index = 0; + while (cstring_is_whitespace(buffer->data[index])) { + ++index; + } + + str8_chop_left(*buffer, index); +} + +internal void str16_trim_left(String16 *buffer) +{ + u64 index = 0; + while (cstring_is_whitespace(buffer->data[index])) { + ++index; + } + + str16_chop_left(*buffer, index); +} + +internal void str32_trim_left(String32 *buffer) +{ + u64 index = 0; + + while (cstring_is_whitespace(buffer->data[index])) { + ++index; + } + + str32_chop_left(*buffer, index); +} + +internal void str8_trim_right(String8 *buffer) +{ + u64 index = buffer->size; + while (cstring_is_whitespace(buffer->data[index])) { + --index; + } + + str8_chop_right(*buffer, index); +} + +internal b32 str8_is_match(String8 a, String8 b) +{ + if (a.size != b.size) { + return false; + } + + if(a.size == 0) { // no need to check if b.size is 0 because both sizes are already equal + return true; + } + + return (b32)(memcmp(a.data, b.data, b.size) == 0); +} + +internal b32 str8_find_all(MemArena *arena, String8 *buffer, u64 start_pos, String8 element, String8List *out) +{ + u64 count = 0; // checks if any we're found to pass that as a error handling mechanism + u64 found_pos = 0; + + while (str8_find(buffer, start_pos, element, &found_pos)) { + String8Node *node = PushStruct(arena, String8Node); + *node = ToString8Node(str8_create_substr(buffer, found_pos, element.size)); + str8_list_push_node(out, node); + + ++count; + start_pos = found_pos + element.size; + } + + if (!count) + return false; + return true; +} + +// TODO: does this work like it should? +internal b32 str8_find(String8 *buffer, u64 start_pos, String8 element, u64 *out) +{ + for (u64 index = start_pos; index < buffer->size - element.size; ++index) { + String8 view = str8_chop_left(*buffer, index); + view = str8_chop_right(view, view.size - element.size); + if (str8_is_match(view, element)) { + *out = index; + return true; + } + } + + return false; +} + +internal b32 str8_replace(String8 buffer, String8 element, u64 start_pos) { + + u64 offset = 0; + b32 result = str8_find(&buffer, start_pos, element, &offset); + if(!result) return false; + + for(u64 index = 0; index < element.size; ++index) { + buffer.data[start_pos + index] = element.data[index]; + } + + return true; +} + +internal b32 str8_trim(String8 buffer) { + + return str8_replace(buffer, str8("\n"), 0); +} + + +internal b32 str8_is_digit(String8 a, u64 start_pos) +{ + for (u64 index = start_pos; index < a.size; ++index) { + if (!cstring_is_digit(a.data[index])) + return false; + } + + return true; +} + +internal void str8_copy(String8 *dest, String8 *src) +{ + MemCpy(dest->data, src->data, src->size); + Assert(memcmp(dest->data, src->data, src->size) == 0); + dest->size = src->size; +} + +internal String8 str8_clone(MemArena *arena, String8 *src) +{ + String8 clone = PushString8(arena, src->size); + str8_copy(&clone, src); + return clone; +} + +// you cant just do this you have to reallocat that thing no? +internal b32 str8_append_char(String8 *dest, u8 src) +{ + if (is_nil_str8(dest) || src == 0) + return false; + dest->data[dest->size] = src; + dest->size += 1; + return true; +} + +internal int str8_to_int(String8 *buffer) +{ + + int result = 0; + + for (u64 index = 0; index < buffer->size; ++index) { + + if (buffer->data[index] < 'Z' && buffer->data[index] > 'A') { + result = buffer->data[index] - '0'; + result *= 10; + } + } + return result; +} + +internal b32 str8_append(String8 *dest, String8 src) +{ + if (is_nil_str8(dest) || is_nil_str8(&src)) + return false; + MemCpy(dest->data + dest->size, src.data, src.size); + dest->size += src.size; + return true; +} + +internal String8 str8_zero() { return String8{0}; } + +// TODO: error handling +internal b32 str8_chop_left_ptr(String8 *buffer, u64 count) +{ + if (buffer->size < count) return false; + + buffer->data += count; + buffer->size -= count; + return true; + + +} + +internal b32 str8_chop_right_ptr(String8 *buffer, u64 count) +{ + if (buffer->size < count) return false; + + buffer->size -= count; + return true; +} + +internal String8 str8_chop_left(String8 buffer, u64 count) +{ + if (buffer.size < count) { + return buffer; + } + + buffer.data += count; + buffer.size -= count; + + return buffer; +} + +internal String8 str8_chop_right(String8 buffer, u64 count) +{ + if (buffer.size < count) { + return buffer; + } + + buffer.size -= count; + return buffer; +} + +internal void str8_list_push_node(String8List *list, String8Node *node) +{ + if (list) { + if (list->first == NULL) { + list->first = node; + list->last = node; + } else { + list->last->next = node; + list->last = node; + } + list->count += 1; + } +} + +internal String8 str8_create_substr(String8 *buffer, u64 left_offset, u64 right_offset) +{ + return str8_chop_right(str8_chop_left(*buffer, left_offset), + (buffer->size - left_offset) - + right_offset); // we need to remove all of the excess data to only + // retain the substring we want +} + +internal String16 str16_create_substr(String16 *buffer, u64 left_offset, u64 right_offset) +{ + return str16_chop_right(str16_chop_left(*buffer, left_offset), + (buffer->size - left_offset) - right_offset); +} + +internal String32 str32_create_substr(String32 *buffer, u64 left_offset, u64 right_offset) +{ + return str32_chop_right(str32_chop_left(*buffer, left_offset), + (buffer->size - left_offset) - right_offset); +} + +internal String16 str16_chop_left(String16 buffer, u64 count) +{ + if (buffer.size < count) { + return buffer; + } + + for (u64 idx = 0; idx < count; ++idx) { + buffer.data++; + } + return buffer; +} + +internal String32 str32_chop_left(String32 buffer, u64 count) +{ + if (buffer.size < count) { + return buffer; + } + + for (u64 idx = 0; idx < count; ++idx) { + buffer.data++; + } + return buffer; +} + +internal String16 str16_chop_right(String16 buffer, u64 count) +{ + if (buffer.size < count) { + return buffer; + } + + buffer.size -= count; + return buffer; +} + +internal String32 str32_chop_right(String32 buffer, u64 count) +{ + if (buffer.size < count) { + return buffer; + } + + buffer.size -= count; + return buffer; +} + +internal b32 str8_prefix(MemArena *arena, String8 *buffer, String8 *prefix) { + b32 result = str8_insert(arena, buffer, prefix, 0); + return result; +} + +internal b32 str8_starts_with(String8 buffer, String8 prefix) { + + if(buffer.size < prefix.size) { + return false; + } + + return str8_is_match(String8{buffer.data, prefix.size}, prefix); +} + +internal b32 str8_is_empty(String8 *buffer) +{ + if (cstring8_length(buffer->data) == 0 || buffer->size == 0) + return true; // the values can't be lower than 0 + return false; +} + +internal b32 str8_insert(MemArena *arena, String8 *dest, String8 *src, u64 pos) +{ + if (dest->size < pos) + return false; + + String8 result = PushString8(arena, dest->size + src->size); + result.size = 0; + + String8 head = str8_create_substr(dest, 0, pos); + String8 tail = str8_create_substr(dest, pos, dest->size); + + if (!str8_append(&result, head)) + return false; + if (!str8_append(&result, *src)) + return false; + if (!str8_append(&result, tail)) + return false; + + *dest = result; + return true; +} + +internal cstring str8_to_cstring(MemArena *arena, String8 *buffer) +{ + + cstring null_terminated_buffer = null_terminate(arena, buffer); + return (cstring)null_terminated_buffer; +} + +//- cstring implementations + +internal b32 cstring_is_digit(u8 point) { return (point >= '0' && point <= '9'); } + +internal b32 cstring_is_alpha(u8 point) +{ + return ((point >= 'a' && point <= 'z') || (point >= 'A' && point <= 'Z') || (point == '_')); +} + +internal b32 cstring_is_alpha_num(u8 point) +{ + return (cstring_is_alpha(point) || cstring_is_digit(point)); +} + +internal b32 cstring_is_whitespace(u8 point) { return (point == ' ' || point == '\t'); } + +internal b32 cstring_is_slash(u8 point) { return (point == '/' || point == '\\'); } + +internal b32 cstring_is_newline(u8 point) { return (point == '\n' || point == '\r'); } + +internal b32 cstring_is_match(u8 *a, u8 *b) +{ + u64 as = cstring8_length(a); + u64 bs = cstring8_length(b); + + if (as != bs) { + return false; + } + + for (u64 idx = 0; idx < as; ++idx) { + if (a[idx] != b[idx]) { + return false; + } + } + return true; +} + + +// NOTE: ignores the biggest buffer size +internal b32 cstring_any_match(u8 *a, u8 *b) +{ + + + + u64 lenght = cstring8_length(b); + for (u64 idx = 0; idx < lenght; ++idx) { + if (a[idx] != b[idx]) { + return false; + } + } + return true; +} + +internal u64 cstring8_length(u8 *c) +{ + u8 *p = c; + if (!c) + return 0; + for (; *c != 0; ++c) + ; + return (u64)(c - p); +} + +internal u64 cstring16_length(u16 *c) +{ + u16 *p = c; + + if (!c) + return 0; + for (; *c != 0; ++c) + ; + return (u64)(p - c); +} + +internal u64 cstring32_length(u32 *c) +{ + + u32 *p = c; + + if (!c) + return 0; + for (; *c != 0; ++c) + ; + return (u64)(p - c); +} + +internal cstring null_terminate(MemArena *arena, String8 *buffer) +{ + char *text = (char *)arena_alloc(arena, buffer->size + 1, 0); + MemCpy(text, buffer->data, buffer->size); + text[buffer->size] = '\0'; + return (cstring)text; +} + +internal String8 remove_null_terminator(MemArena *arena, String8 c) +{ + Assert(arena); + if ((c.size > 0) && (c.data[c.size - 1] == '\0')) { + String8 chopped = {c.data, c.size - 1}; + return chopped; + } + + return c; +} + +// returns the index of where to find it. if not found returns 0. +internal u64 cstring8_find(u8 *buffer, u8 *element) +{ + u8 *c = buffer; + while (c != 0) { + if (!cstring_is_match(buffer, element)) { + return c - buffer; + } else { + ++c; + } + } + + return 0; +} + +// returns the index of where to find it. if not found returns 0. +internal u64 cstring8_find_new_line(u8 *buffer) +{ + u8 *c = buffer; + while (*c != 0) { + if (cstring_is_newline(*c)) { + return c - buffer; + } else { + ++c; + } + } + + return 0; +} + +// returns the index of where to find it. if not found returns 0. +internal u64 cstring8_find_needle(u8 *buffer, u8 needle) +{ + u8 *c = buffer; + while (*c != 0) { + if (*c == needle) { + return c - buffer; + } else { + ++c; + } + } + + return 0; +} 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 @@ +#ifndef BASE_STRING_H +#define BASE_STRING_H + +//- cstring + +typedef char *cstring; + +// - str8 +typedef struct String8 String8; +typedef struct String16 String16; +typedef struct String32 String32; + +typedef struct String8Node String8Node; +typedef struct String16Node String16Node; +typedef struct String32Node String32Node; + +typedef struct String8List String8List; +typedef struct String16List String16List; +typedef struct String32List String32List; + +//- strings + +struct String8 { + u8 *data; + u64 size; +}; + +struct String16 { + u16 *data; + u64 size; +}; + +struct String32 { + u32 *data; + u64 size; +}; + +//- nodes +struct String8Node { + String8Node *next; + String8 value; +}; + +struct String16Node { + String16Node *next; + String16 string; +}; + +struct String32Node { + String32Node *next; + String32 value; +}; + +//- lists +struct String8List { + String8Node *first; + String8Node *last; + u64 count; +}; + +struct String16List { + String16Node *first; + String16Node *last; + u64 count; +}; + +struct String32List { + String32Node *first; + String32Node *last; + u64 count; +}; + +//- nil / zero-value checks and constructors +internal b32 is_nil_str8(String8 *a); +internal b32 is_nil_str8_node(String8Node *a); +internal b32 is_nil_str16(String16 *a); +internal b32 is_nil_str16_node(String16Node *a); +internal b32 is_nil_str32(String32 *a); +internal b32 is_nil_str32_node(String32Node *a); +internal String8 str8_zero(); + +//- str8 copy / clone / append +internal String8 str8_clone(MemArena *arena, String8 *src); +internal void str8_copy(String8 *dest, String8 *src); +internal b32 str8_append(String8 *dest, String8 src); +internal b32 str8_append_char(String8 *dest, u8 src); +internal b32 str8_insert(MemArena *arena, String8 *dest, String8 *src, u64 pos); + +//- str8 trim / chop +internal void str8_trim_left(String8 *buffer); +internal void str8_trim_right(String8 *buffer); +internal String8 str8_chop_left(String8 buffer, u64 count); +internal String8 str8_chop_right(String8 buffer, u64 count); +internal b32 str8_chop_left_ptr(String8 *buffer, u64 count); +internal b32 str8_chop_right_ptr(String8 *buffer, u64 count); + +//- str8 substrings, matching, search +internal String8 str8_create_substr(String8 *buffer, u64 left_offset, u64 right_offset); +internal b32 str8_is_match(String8 a, String8 b); +internal b32 str8_find(String8 *buffer, u64 start_pos, String8 element, u64 *out); +internal b32 str8_is_digit(String8 a, u64 start_pos); +internal b32 str8_is_empty(String8 *buffer); + +//- str8 list +internal void str8_list_push_node(String8List *list, String8Node *node); + +//- str16 / str32 chop and substrings +internal String16 str16_chop_left(String16 buffer, u64 count); +internal String16 str16_chop_right(String16 buffer, u64 count); +internal String16 str16_create_substr(String16 *buffer, u64 left_offset, u64 right_offset); +internal String32 str32_chop_left(String32 buffer, u64 count); +internal String32 str32_chop_right(String32 buffer, u64 count); +internal String32 str32_create_substr(String32 *buffer, u64 left_offset, u64 right_offset); + +//- null-termination helpers +internal cstring null_terminate(MemArena *arena, String8 *c); +internal String8 remove_null_terminator(MemArena *arena, String8 c); + +//- convert + +internal cstring str8_to_cstring(String8 *buffer); + +//- cstring character classification +internal b32 cstring_is_digit(u8 point); +internal b32 cstring_is_alpha(u8 point); +internal b32 cstring_is_alpha_num(u8 point); +internal b32 cstring_is_whitespace(u8 point); +internal b32 cstring_is_slash(u8 point); +internal b32 cstring_is_newline(u8 point); + +#define CstringIsMatch(a, b) cstring_is_match((u8 *)(a), (u8 *)(b)) +#define CstringAnyMatch(a, b) cstring_any_match((u8 *)(a), (u8 *)(b)) + + +//- cstring matching, length, search +internal b32 cstring_is_match(u8 *a, u8 *b); +internal b32 cstring_any_match(u8 *a, u8 *b); +internal u64 cstring8_length(u8 *c); +internal u64 cstring16_length(u16 *c); +internal u64 cstring32_length(u32 *c); +internal u64 cstring8_find(u8 *buffer, u8 *element); +internal u64 cstring8_find_new_line(u8 *buffer); +internal u64 cstring8_find_needle(u8 *buffer, u8 needle); + +//- + +#if COMPILER_GCC +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wconversion" +#endif + +//- +ReadOnly global_variable String8 nil_str8 = {NULL, 0}; +ReadOnly global_variable String16 nil_str16 = {NULL, 0}; +ReadOnly global_variable String32 nil_str32 = {NULL, 0}; + +ReadOnly global_variable String8Node nil_str8_node = {NULL, {}}; +ReadOnly global_variable String16Node nil_str16_node = {NULL, {}}; +ReadOnly global_variable String32Node nil_str32_node = {NULL, {}}; +ReadOnly global_variable String8List nil_str8_list = {NULL, {}}; +ReadOnly global_variable String16List nil_str16_list = {NULL, {}}; +ReadOnly global_variable String32List nil_str32_list = {NULL, {}}; + +//- macros +#define PushString8(arena, count) {(u8 *)(PushArrayZero(arena, u8, (count))), (u64)(count)} +#define PushString16(arena, count) {(u16 *)(PushArrayZero(arena, u16, (count))), (u64)(count)} +#define PushString32(arena, count) {(u32 *)(PushArrayZero(arena, u32, (count))), (u64)(count)} + +// TODO: is this null terminated? this is generally a concept i struggle with. when is the string +// null terminated and when not. because in thi case I am the one passing a string "/" or something +// like that. is that null terminated or not? +#define ToString8(data) \ + String8 { (u8 *)(data), (u64)(cstring8_length((u8 *)data)) } +#define ToString16(data) \ + String16 { (u16 *)(data), (u64)(cstring16_length((u16 *)data)) } +#define ToString32(data) \ + String32 { (u32 *)(data), (u64)(cstring32_length((u32 *)data)) } + +#define str8(buffer) ToString8(buffer) +#define str16(buffer) ToString16(buffer) +#define str32(buffer) ToString32(buffer) + +#define ToString8Size(data, size) \ + String8 { (u8 *)(data), (u64)(size) } +#define ToString16Size(data, size) \ + String16 { (u16 *)(data), (u64)(size) } +#define ToString32Size(data, size) \ + String32 { (u32 *)(data), (u64)(size) } + +#define ToString8Node(string) \ + String8Node { &nil_str8_node, string } +#define ToString16Node(string) \ + String16Node { &nil_str8_node, string } +#define ToString32Node(string) \ + String32Node { &nil_str8_node, string } + +#define str8_node(text) ToString8Node(str8(text)) +#define str16_node(text) ToString16Node(str16(text)) +#define str32_node(text) ToString32Node(str32(text)) + +#define LOOP(expr, exec) for (; expr; exec) + +// arbitratry amount of parameters can be passed to this function +// TODO: put the index name as a paramter so that the reference can be made later without it being +// so hidden + +#define FOR_STR8_CHOP_LEFT(index, buffer, count) FOR_STR8_CHOP(index, buffer, count, str8_chop_left) + +#define FOR_STR8_CHOP_RIGHT(index, buffer, count) \ + FOR_STR8_CHOP(index, buffer, count, str8_chop_right) + +// NOTE: we face an issue with returning an rvalue. to solve this we should only use the pointer +// functions that we create (str8_chop_left_ptr and str8_chop_right_ptr) +#define FOR_STR8_CHOP(index, buffer, count, function) \ + for (u64 index = 0; (index < (buffer.size)) && ((buffer.size) > (count)); \ + function((buffer), (count)), ++index) + +#define FOR_STR8_ARB_FN(expr, function, ...) LOOP(expr, function(__VA_ARGS__)) + +#define FOR_STR8_MATCH(buffa, buffb) LOOP(str8_is_match(buffa, buffb), str8_chop_left(buffa, 1)) + +#define FOR_STR8_NODE(str8_list, current) \ + for (String8Node *current = str8_list->first; !is_nil_str8_node(current); \ + current = current->next) + +internal b32 is_nil_str8(String8 *a); +internal b32 is_nil_str8_node(String8Node *a); +internal b32 is_nil_str16(String16 *a); +internal b32 is_nil_str16_node(String16Node *a); +internal b32 is_nil_str32(String32 *a); +internal b32 is_nil_str32_node(String32Node *a); + +#if COMPILER_GCC +#pragma GCC diagnostic pop +#endif + +#define Str8Fmt "%.*s" +#define Str8Arg(buffer) (unsigned int)((buffer).size), (char *)((buffer).data) + +#endif /* BASE_STRING_H */ -- cgit v1.3.1