summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authornasr <nsrddyn@gmail.com>2026-10-05 22:02:56 +0200
committernasr <nsrddyn@gmail.com>2026-10-05 22:02:56 +0200
commitf6d3090f7fa76eb8e0dbe060b8490cb49c001c39 (patch)
tree433c773997e51f22738acb8b04c4fd2fc6b1bdc9
init: base
-rw-r--r--build.bat15
-rwxr-xr-xbuild.sh35
-rw-r--r--project.4coder78
-rw-r--r--source/base/base_arena.cpp165
-rw-r--r--source/base/base_arena.h94
-rw-r--r--source/base/base_core.h141
-rw-r--r--source/base/base_include.h88
-rw-r--r--source/base/base_memory.cpp10
-rw-r--r--source/base/base_memory.h42
-rw-r--r--source/base/base_platform.cpp5
-rw-r--r--source/base/base_platform.h199
-rw-r--r--source/base/base_platform_linux.cpp189
-rw-r--r--source/base/base_platform_linux_wayland.cpp55
-rw-r--r--source/base/base_platform_linux_x11.cpp76
-rw-r--r--source/base/base_platform_win32.cpp206
-rw-r--r--source/base/base_profile.cpp28
-rw-r--r--source/base/base_profile.h19
-rw-r--r--source/base/base_rand.cpp19
-rw-r--r--source/base/base_render.h27
-rw-r--r--source/base/base_render_vulkan.cpp129
-rw-r--r--source/base/base_simd.cpp7
-rw-r--r--source/base/base_stack.cpp234
-rw-r--r--source/base/base_stack.h47
-rw-r--r--source/base/base_stack_unix.cpp0
-rw-r--r--source/base/base_stack_win32.cpp0
-rw-r--r--source/base/base_string.cpp498
-rw-r--r--source/base/base_string.h240
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
2setlocal
3time /t
4
5set BUILD_DIR=build
6set SOURCE_DIR=source
7set INCLUDES=-I%SOURCE_DIR%\code -I%SOURCE_DIR% -I%SOURCE_DIR%\base
8set CFLAGS=-FC -GR- -EHa- -nologo -Zi -FS /analyze /W3 /WX -MD /DYNAMICBASE:NO
9set LIBS=user32.lib gdi32.lib kernel32.lib shell32.lib
10
11if not exist %BUILD_DIR% mkdir %BUILD_DIR%
12
13echo Building %BUILD_DIR%\program.exe...
14
15cl %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
2set -e
3
4echo
5date
6echo "author: Abdellah El Morabit"
7echo
8
9BUILD_DIR=build
10SOURCE_DIR=source
11INCLUDES="-I${SOURCE_DIR}/loo -I${SOURCE_DIR} -I${SOURCE_DIR}/base"
12CFLAGS="-g -fno-rtti -fno-exceptions -Wall -Werror -fanalyzer -no-pie -Wno-unused-function -Wno-unused-variable -O0"
13LIBS=""
14
15VULKAN=1
16WAYLAND=1
17X11=0
18
19if [ $WAYLAND -eq 1 ]; then
20 LIBS+=" $(pkg-config --libs wayland-client)"
21 CFLAGS+=" $(pkg-config --cflags wayland-client) -DWAYLAND=1 -DX11=0"
22elif [ $X11 -eq 1 ]; then
23 LIBS+=" -lX11"
24 CFLAGS+=" -DX11=1 -DWAYLAND=0"
25fi
26
27if [ $VULKAN -eq 1 ]; then
28 LIBS+=" -lvulkan"
29 CFLAGS+=" -DVULKAN=1"
30fi
31
32mkdir -p "$BUILD_DIR"
33
34echo "Building ${BUILD_DIR}/loo..."
35g++ $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 @@
1version(2);
2project_name = "loo";
3
4patterns = {
5 "*.c",
6 "*.cpp",
7 "*.h",
8 "*.m",
9 "*.bat",
10 "*.sh",
11 "*.4coder",
12 "*.jr",
13};
14
15blacklist_patterns = {
16 ".*",
17};
18
19paths = {
20 { "./source/code", .relative = true, .recursive = true, },
21 { "./source/base", .relative = true, .recursive = true, },
22};
23
24linux_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
32load_paths = {
33 .win = paths,
34 .linux = linux_paths,
35 .mac = paths,
36};
37
38commands = {
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
73fkey_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
3internal MemArena *
4unix_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
27internal void
28unix_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
39internal MemArena *
40win32_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
57internal void
58win32_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
70internal void *
71arena_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
93internal void
94arena_pop(MemArena *arena, u64 size)
95{
96 size = Min(size, arena->current_position);
97 arena->current_position -= size;
98}
99
100internal void
101arena_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
107internal void
108arena_clear(MemArena *arena)
109{
110 arena->current_position = 0;
111}
112
113internal MemArena *
114arena_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
144internal MemArena *
145arena_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
150internal ScratchArena
151scratch_start(MemArena *arena)
152{
153 ScratchArena scratch;
154 scratch.arena = arena;
155 scratch.start_position = arena->current_position;
156
157 return scratch;
158}
159
160internal void
161scratch_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
5typedef struct MemArena MemArena;
6typedef struct ScratchArena ScratchArena;
7
8// NOTE: used capacity can be calculated so we don't care
9struct MemArena {
10 u64 current_position;
11 u64 previous_position;
12 u64 capacity;
13 u8 *base_position;
14 MemArena *next;
15};
16
17struct ScratchArena {
18 MemArena *arena;
19 u64 start_position;
20};
21
22//-
23
24#if defined(PLATFORM_UNIX)
25
26internal MemArena *unix_arena_create(u64 capacity);
27internal 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
33internal void win32_arena_destroy(MemArena *arena);
34internal 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
40internal void *arena_alloc(MemArena *arena, u64 size, b32 zero);
41internal void arena_pop(MemArena *arena, u64 size);
42internal void arena_pop_to(MemArena *arena, u64 pos);
43internal void arena_clear(MemArena *arena);
44internal ScratchArena scratch_start(MemArena *arena);
45internal void scratch_end(ScratchArena *scratch);
46internal MemArena *arena_resize_block_align(MemArena *arena, void *old_memory, u64 new_size, u64 old_size,
47 umm alignment);
48internal MemArena *arena_resize_block(MemArena *arena, void *old_memory, u64 new_size, u64 old_size);
49internal 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
80typedef uint64_t u64;
81typedef uint32_t u32;
82typedef uint16_t u16;
83typedef uint8_t u8;
84
85typedef int8_t s8;
86typedef int16_t s16;
87
88typedef int32_t s32;
89typedef int64_t s64;
90
91typedef float f32;
92typedef double f64;
93
94typedef s32 b32;
95typedef s16 b16;
96typedef s8 b8;
97
98typedef uintptr_t umm;
99typedef 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
120internal void _assert(bool expression);
121
122
123internal 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 @@
1internal b32 is_pow(umm x) { return (x & (x - 1)) == 0; }
2
3internal 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.
14void __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.
22void __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
39internal b32 is_pow(umm x);
40internal 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
8typedef struct SharedLibrary SharedLibrary;
9struct SharedLibrary {
10 void *mem;
11 const char *name;
12};
13
14typedef struct Directory Directory;
15struct Directory {
16 String8List *file_names;
17};
18
19#if 0 // TODO: @WAYLAND and @X11 implementation
20typedef struct LinuxWindowHandle LinuxWindowHandle;
21struct LinuxWindowHandle {
22 Display *display;
23 Window window;
24};
25
26#define LinuxLoadSharedLibrary(library, name) unix_
27#endif
28
29//- Shared libraries
30internal void *unix_shared_library_get_proc_from_name(SharedLibrary *library, const char *name);
31internal void unix_load_shared_library(MemArena *arena, SharedLibrary *library);
32internal int unix_shared_library_close(SharedLibrary *library);
33
34internal String8 unix_get_env(const char *var);
35
36//- File I/O
37internal b32 unix_file_write(String8 data, String8 path);
38internal String8 unix_file_load(MemArena *arena, String8 path);
39internal b32 unix_file_exists(String8 path);
40internal String8List *unix_open_dir(MemArena *arena,
41 String8 *original_path); // TODO: change the return value
42
43internal s64 unix_read(int fd, const void *buf, u64 count);
44internal void unix_os_exit() __attribute__((noreturn));
45
46internal s64 unix_log(int fd, const void *buf, u64 count);
47internal 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
81internal LinuxWindowHandle x11_create_window(MemArena *arena, u32 window_height, u32 window_width,
82 u32 disp_x, u32 disp_y);
83internal void x11_draw(LinuxWindowHandle *s, s32 start_x, s32 end_x, s32 start_y, s32 end_y);
84internal s32 x11_cleanup(LinuxWindowHandle *s);
85internal 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) \
91XReadBitmapFile(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
99global_variable const u32 wayland_display_object_id = 1;
100global_variable const u16 wayland_wl_registry_event_global = 0;
101global_variable const u16 wayland_shm_pool_event_format = 0;
102global_variable const u16 wayland_wl_buffer_event_release = 0;
103global_variable const u16 wayland_xdg_wm_base_event_ping = 0;
104global_variable const u16 wayland_xdg_toplevel_event_configure = 0;
105global_variable const u16 wayland_xdg_toplevel_event_close = 1;
106global_variable const u16 wayland_xdg_surface_event_configure = 0;
107global_variable const u16 wayland_wl_display_get_registry_opcode = 1;
108global_variable const u16 wayland_wl_registry_bind_opcode = 0;
109global_variable const u16 wayland_wl_compositor_create_surface_opcode = 0;
110global_variable const u16 wayland_xdg_wm_base_pong_opcode = 3;
111global_variable const u16 wayland_xdg_surface_ack_configure_opcode = 4;
112global_variable const u16 wayland_wl_shm_create_pool_opcode = 0;
113global_variable const u16 wayland_xdg_wm_base_get_xdg_surface_opcode = 2;
114global_variable const u16 wayland_wl_shm_pool_create_buffer_opcode = 0;
115global_variable const u16 wayland_wl_surface_attach_opcode = 1;
116global_variable const u16 wayland_xdg_surface_get_toplevel_opcode = 1;
117global_variable const u16 wayland_wl_surface_commit_opcode = 6;
118global_variable const u16 wayland_wl_display_error_event = 0;
119global_variable const u32 wayland_format_xrgb8888 = 1;
120global_variable const u32 wayland_header_size = 8;
121global_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
131typedef struct Win32WindowHandle Win32WindowHandle;
132struct Win32WindowHandle {
133 HWND window_handle;
134 HINSTANCE instance;
135 HDC dc;
136};
137
138global_variable b32 Win32Running = true;
139
140#define STDIN 0
141#define STDOUT 1
142#define STDERR 2
143
144//- Shared libraries
145internal void *win32_load_library();
146internal void win32_library_get_proc();
147
148//- File I/O
149internal b32 win32_file_exists(u8 *path);
150internal b32 win32_file_write(String8 data, String8 path);
151internal String8 win32_file_load(MemArena *arena, String8 file);
152internal String8List win32_open_dir(MemArena *arena, String8 *original_path); // TODO: ...
153
154internal b32 win32_os_exit();
155internal void win32_handle_error(MemArena *arena, DWORD error);
156internal u64 safe_truncate_64(u64 value);
157
158internal 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)))
170internal void win32_logf(const char *fmt, ...) PRINTF_FORMAT;
171
172#elif COMPILER_MSVC
173internal 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
186internal LRESULT CALLBACK win32_window_proc(HWND handle, UINT messages, WPARAM wparam,
187 LPARAM lparam);
188internal 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) \
192win32_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
2internal 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//-
34internal 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
41internal 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
47internal 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
56internal b32 unix_file_exists(String8 path) { return access((const char *)path.data, F_OK) == 0; }
57
58internal 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
69internal 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?
124internal 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
145internal 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
154internal void unix_os_exit() { _exit(0); }
155
156// TODO: rewrite this
157internal 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
4internal void wayland_display_connect(MemArena *arena) {
5
6
7ScratchArena 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
13u64 xdg_runtime_dir_len = str8_xdg_runtime_dir.size;
14
15 sockaddr_un addr = {};
16addr.sun_family = AF_UNIX;
17
18 Assert(xdg_runtime_dir_len <= cstring8_length((u8 *)addr.sun_path));
19
20u64 socket_path_len = 0;
21MemCpy(addr.sun_path, xdg_runtime_dir, str8_xdg_runtime_dir.size);
22socket_path_len += xdg_runtime_dir_len;
23
24
25addr.sun_path[socket_path_len++] = '/';
26String8 str8_wayland_display_value = unix_get_env("WAYLAND_DISPLAY");
27char *wayland_display = null_terminate(arena, &str8_wayland_display_value);
28if(wayland_display == NULL)
29{
30const char *wayland_display_default = "wayland-0";
31u64 wayland_display_default_len = cstring8_length((u8 *)wayland_display_default);
32MemCpy(addr.sun_path + socket_path_len, wayland_display_default, wayland_display_default_len);
33}
34else
35{
36u64 wayland_display_len = cstring8_length((u8 *)wayland_display);
37MemCpy(addr.sun_path + socket_path_len, wayland_display, wayland_display_len);
38socket_path_len += wayland_display_len;
39}
40
41scratch_end(&env_scratch);
42
43int fd = socket(AF_UNIX, SOCK_STREAM, 0);
44if(fd == -1)
45{
46exit(1);
47}
48
49if(connect(fd, (struct sockaddr *)&addr, sizeof(addr)) == -1)
50{
51exit(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
2internal 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
58internal 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
68internal s32 x11_cleanup(LinuxWindowHandle *s)
69{
70 XCloseDisplay(s->display);
71 return 0;
72}
73
74internal 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
2internal void
3#if COMPILER_GCC || COMPILER_CLANG
4win32_logf(const char *fmt, ...) PRINTF_FORMAT
5#elif COMPILER_MSVC
6win32_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
25internal 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
39internal b32 win32_os_exit()
40{
41 ExitProcess(0);
42 return true;
43}
44
45// TODO: remove FORMAT_MESSAGE_ALLOCATE_BUFFER flag
46internal 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
61internal u64 safe_truncate_64(u64 value)
62{
63 Assert(value <= (u64)~0);
64 return (u64)value;
65}
66
67internal 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
73internal 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
94internal 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
138internal void *win32_load_library()
139{
140 // TODO: ...
141}
142
143internal void win32_library_get_proc()
144{
145 // TODO: ...
146}
147
148internal 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
169internal 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
206internal 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
2internal u64
3get_current_time() {
4
5 return 0;
6}
7
8internal Profile *
9start_profile() {
10
11
12}
13
14
15internal void
16end_profile(Profile *profile) {
17
18
19
20}
21
22internal void
23log_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
5typedef struct Profile Profile;
6struct 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
2internal u64 rand_u64(u64 constant);
3
4internal 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
5typedef struct WinParams WinParams;
6struct 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
16typedef struct Params Params;
17struct Params {
18 const char *name;
19 int width;
20 int height;
21 VkExtensionProperties *vk_properties;
22 VkInstance *vk_instance;
23};
24
25internal 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
4internal void
5vk_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, &current_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
118internal void
119render_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
6internal 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
22internal 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
35internal 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
51internal 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
64internal 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
93internal 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
131internal void *stack_push(MemStack *stack, u64 size)
132{
133 return stack_push_align(stack, size, arena_align);
134}
135
136internal 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
164internal 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
199internal 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
217global_variable String8List *stack_log; // TODO: initialize this in the mem stack callback
218
219#endif
220
221internal void
222stack_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
234internal 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
14typedef struct MemStackHeader MemStackHeader;
15typedef struct MemStack MemStack;
16
17struct MemStackHeader {
18 u8 padding;
19 u8 previous_offset;
20};
21
22struct 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
31internal MemStack *stack_push_align(MemStack *stack, u64 size, u8 alignment);
32internal MemStack *stack_resize_align(MemStack *stack, void *pointer, u64 old_size, u64 new_size,
33 u8 alignment);
34internal void *stack_push(MemStack *stack, u64 size);
35internal void stack_pop(MemStack *stack, void *pointer);
36internal void stack_clear(MemStack *stack);
37internal u8 calculate_padding(u64 pointer, u8 alignment, u64 header_size);
38internal 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 @@
1internal b32 is_nil_str8(String8 *a) { return (a == &nil_str8); }
2
3internal b32 is_nil_str8_node(String8Node *a) { return (a == &nil_str8_node); }
4
5internal b32 is_nil_str16(String16 *a) { return (a == &nil_str16); }
6
7internal b32 is_nil_str16_node(String16Node *a) { return (a == &nil_str16_node); }
8
9internal b32 is_nil_str32(String32 *a) { return (a == &nil_str32); }
10
11internal b32 is_nil_str32_node(String32Node *a) { return (a == &nil_str32_node); }
12
13internal 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
23internal 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
33internal 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
44internal 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
54internal 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
67internal 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?
87internal 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
101internal 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
114internal b32 str8_trim(String8 buffer) {
115
116 return str8_replace(buffer, str8("\n"), 0);
117}
118
119
120internal 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
130internal 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
137internal 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?
145internal 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
154internal 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
169internal 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
178internal String8 str8_zero() { return String8{0}; }
179
180// TODO: error handling
181internal 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
192internal 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
200internal 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
212internal 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
222internal 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
236internal 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
244internal 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
250internal 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
256internal 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
268internal 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
280internal 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
290internal 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
300internal b32 str8_prefix(MemArena *arena, String8 *buffer, String8 *prefix) {
301 b32 result = str8_insert(arena, buffer, prefix, 0);
302 return result;
303}
304
305internal 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
314internal 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
321internal 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
343internal 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
352internal b32 cstring_is_digit(u8 point) { return (point >= '0' && point <= '9'); }
353
354internal b32 cstring_is_alpha(u8 point)
355{
356 return ((point >= 'a' && point <= 'z') || (point >= 'A' && point <= 'Z') || (point == '_'));
357}
358
359internal b32 cstring_is_alpha_num(u8 point)
360{
361 return (cstring_is_alpha(point) || cstring_is_digit(point));
362}
363
364internal b32 cstring_is_whitespace(u8 point) { return (point == ' ' || point == '\t'); }
365
366internal b32 cstring_is_slash(u8 point) { return (point == '/' || point == '\\'); }
367
368internal b32 cstring_is_newline(u8 point) { return (point == '\n' || point == '\r'); }
369
370internal 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
389internal 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
403internal 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
413internal 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
424internal 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
436internal 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
444internal 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.
456internal 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.
471internal 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.
486internal 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
6typedef char *cstring;
7
8// - str8
9typedef struct String8 String8;
10typedef struct String16 String16;
11typedef struct String32 String32;
12
13typedef struct String8Node String8Node;
14typedef struct String16Node String16Node;
15typedef struct String32Node String32Node;
16
17typedef struct String8List String8List;
18typedef struct String16List String16List;
19typedef struct String32List String32List;
20
21//- strings
22
23struct String8 {
24 u8 *data;
25 u64 size;
26};
27
28struct String16 {
29 u16 *data;
30 u64 size;
31};
32
33struct String32 {
34 u32 *data;
35 u64 size;
36};
37
38//- nodes
39struct String8Node {
40 String8Node *next;
41 String8 value;
42};
43
44struct String16Node {
45 String16Node *next;
46 String16 string;
47};
48
49struct String32Node {
50 String32Node *next;
51 String32 value;
52};
53
54//- lists
55struct String8List {
56 String8Node *first;
57 String8Node *last;
58 u64 count;
59};
60
61struct String16List {
62 String16Node *first;
63 String16Node *last;
64 u64 count;
65};
66
67struct String32List {
68 String32Node *first;
69 String32Node *last;
70 u64 count;
71};
72
73//- nil / zero-value checks and constructors
74internal b32 is_nil_str8(String8 *a);
75internal b32 is_nil_str8_node(String8Node *a);
76internal b32 is_nil_str16(String16 *a);
77internal b32 is_nil_str16_node(String16Node *a);
78internal b32 is_nil_str32(String32 *a);
79internal b32 is_nil_str32_node(String32Node *a);
80internal String8 str8_zero();
81
82//- str8 copy / clone / append
83internal String8 str8_clone(MemArena *arena, String8 *src);
84internal void str8_copy(String8 *dest, String8 *src);
85internal b32 str8_append(String8 *dest, String8 src);
86internal b32 str8_append_char(String8 *dest, u8 src);
87internal b32 str8_insert(MemArena *arena, String8 *dest, String8 *src, u64 pos);
88
89//- str8 trim / chop
90internal void str8_trim_left(String8 *buffer);
91internal void str8_trim_right(String8 *buffer);
92internal String8 str8_chop_left(String8 buffer, u64 count);
93internal String8 str8_chop_right(String8 buffer, u64 count);
94internal b32 str8_chop_left_ptr(String8 *buffer, u64 count);
95internal b32 str8_chop_right_ptr(String8 *buffer, u64 count);
96
97//- str8 substrings, matching, search
98internal String8 str8_create_substr(String8 *buffer, u64 left_offset, u64 right_offset);
99internal b32 str8_is_match(String8 a, String8 b);
100internal b32 str8_find(String8 *buffer, u64 start_pos, String8 element, u64 *out);
101internal b32 str8_is_digit(String8 a, u64 start_pos);
102internal b32 str8_is_empty(String8 *buffer);
103
104//- str8 list
105internal void str8_list_push_node(String8List *list, String8Node *node);
106
107//- str16 / str32 chop and substrings
108internal String16 str16_chop_left(String16 buffer, u64 count);
109internal String16 str16_chop_right(String16 buffer, u64 count);
110internal String16 str16_create_substr(String16 *buffer, u64 left_offset, u64 right_offset);
111internal String32 str32_chop_left(String32 buffer, u64 count);
112internal String32 str32_chop_right(String32 buffer, u64 count);
113internal String32 str32_create_substr(String32 *buffer, u64 left_offset, u64 right_offset);
114
115//- null-termination helpers
116internal cstring null_terminate(MemArena *arena, String8 *c);
117internal String8 remove_null_terminator(MemArena *arena, String8 c);
118
119//- convert
120
121internal cstring str8_to_cstring(String8 *buffer);
122
123//- cstring character classification
124internal b32 cstring_is_digit(u8 point);
125internal b32 cstring_is_alpha(u8 point);
126internal b32 cstring_is_alpha_num(u8 point);
127internal b32 cstring_is_whitespace(u8 point);
128internal b32 cstring_is_slash(u8 point);
129internal 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
136internal b32 cstring_is_match(u8 *a, u8 *b);
137internal b32 cstring_any_match(u8 *a, u8 *b);
138internal u64 cstring8_length(u8 *c);
139internal u64 cstring16_length(u16 *c);
140internal u64 cstring32_length(u32 *c);
141internal u64 cstring8_find(u8 *buffer, u8 *element);
142internal u64 cstring8_find_new_line(u8 *buffer);
143internal 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//-
153ReadOnly global_variable String8 nil_str8 = {NULL, 0};
154ReadOnly global_variable String16 nil_str16 = {NULL, 0};
155ReadOnly global_variable String32 nil_str32 = {NULL, 0};
156
157ReadOnly global_variable String8Node nil_str8_node = {NULL, {}};
158ReadOnly global_variable String16Node nil_str16_node = {NULL, {}};
159ReadOnly global_variable String32Node nil_str32_node = {NULL, {}};
160ReadOnly global_variable String8List nil_str8_list = {NULL, {}};
161ReadOnly global_variable String16List nil_str16_list = {NULL, {}};
162ReadOnly 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
226internal b32 is_nil_str8(String8 *a);
227internal b32 is_nil_str8_node(String8Node *a);
228internal b32 is_nil_str16(String16 *a);
229internal b32 is_nil_str16_node(String16Node *a);
230internal b32 is_nil_str32(String32 *a);
231internal 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 */