1239 lines
46 KiB
C++
1239 lines
46 KiB
C++
#define CORE_BASE
|
|
|
|
#if defined(__clang__)
|
|
# define COMPILER_CLANG 1
|
|
# if defined(_WIN32)
|
|
# define OS_WINDOWS 1
|
|
# elif defined(__linux__)
|
|
# define OS_LINUX 1
|
|
# else
|
|
# error Couldnt figure out the platform automatically
|
|
# endif
|
|
#elif defined(_MSC_VER)
|
|
# define COMPILER_MSVC 1
|
|
# define OS_WINDOWS 1
|
|
#elif defined(__GNUC__)
|
|
# define COMPILER_GCC 1
|
|
# if defined(__linux__)
|
|
# define OS_LINUX 1
|
|
# endif
|
|
#else
|
|
# error Couldnt figure out the compiler
|
|
#endif
|
|
|
|
#if defined(OS_MAC)
|
|
#define OS_UNIX 1
|
|
#endif
|
|
#if defined(OS_LINUX)
|
|
#define OS_UNIX 1
|
|
#endif
|
|
|
|
#if !defined(COMPILER_MSVC)
|
|
# define COMPILER_MSVC 0
|
|
#endif
|
|
#if !defined(COMPILER_GCC)
|
|
# define COMPILER_GCC 0
|
|
#endif
|
|
#if !defined(COMPILER_CLANG)
|
|
# define COMPILER_CLANG 0
|
|
#endif
|
|
#if !defined(OS_WINDOWS)
|
|
# define OS_WINDOWS 0
|
|
#endif
|
|
#if !defined(OS_LINUX)
|
|
# define OS_LINUX 0
|
|
#endif
|
|
#if !defined(OS_MAC)
|
|
# define OS_MAC 0
|
|
#endif
|
|
#if !defined(OS_UNIX)
|
|
# define OS_UNIX 0
|
|
#endif
|
|
|
|
#if OS_WINDOWS
|
|
#define OS_EXE ".exe"
|
|
#define OS_NAME "Win32"_s
|
|
#define OS_NAME_LOWER "win32"_s
|
|
#elif OS_LINUX
|
|
#define OS_EXE ".out"
|
|
#define OS_NAME "Linux"_s
|
|
#define OS_NAME_LOWER "linux"_s
|
|
#elif OS_MAC
|
|
#define OS_EXE ".out"
|
|
#define OS_NAME "Mac"_s
|
|
#define OS_NAME_LOWER "mac"_s
|
|
#else
|
|
#error Couldnt figure out the OS with C macros!
|
|
#endif
|
|
|
|
#if OS_WINDOWS
|
|
#define NOMINMAX
|
|
#define _CRT_SECURE_NO_WARNINGS
|
|
#include <windows.h>
|
|
#define Breakpoint __debugbreak()
|
|
#define force_inline __forceinline
|
|
#else
|
|
#define Breakpoint (*(volatile int *)0 = 0)
|
|
#define force_inline inline
|
|
#endif
|
|
|
|
#include <stdlib.h>
|
|
#include <float.h>
|
|
#include <stdint.h>
|
|
typedef int8_t S8;
|
|
typedef int16_t S16;
|
|
typedef int32_t S32;
|
|
typedef int64_t S64;
|
|
typedef uint8_t U8;
|
|
typedef uint16_t U16;
|
|
typedef uint32_t U32;
|
|
typedef uint64_t U64;
|
|
typedef S8 B8;
|
|
typedef S16 B16;
|
|
typedef S32 B32;
|
|
typedef S64 B64;
|
|
|
|
typedef float F32;
|
|
typedef double F64;
|
|
|
|
#define U64MAX UINT64_MAX
|
|
#define U32MAX UINT32_MAX
|
|
#define U16MAX UINT16_MAX
|
|
#define U8MAX UINT8_MAX
|
|
#define U64MIN 0
|
|
#define U32MIN 0
|
|
#define U16MIN 0
|
|
#define U8MIN 0
|
|
#define S64MAX INT64_MAX
|
|
#define S64MIN INT64_MIN
|
|
#define S32MAX INT32_MAX
|
|
#define S32MIN INT32_MIN
|
|
#define S16MAX INT16_MAX
|
|
#define S16MIN INT16_MIN
|
|
#define S8MAX INT8_MAX
|
|
#define S8MIN INT8_MIN
|
|
#define F32MAX FLT_MAX
|
|
#define F32MIN FLT_MIN
|
|
#define F64MAX DBL_MAX
|
|
#define F64MIN DBL_MIN
|
|
|
|
#define api
|
|
#define CORE_Static static
|
|
#define global static
|
|
#define assert(x) do{if(!(x))Breakpoint;}while(0)
|
|
#define assert_message(x,...) assert(x)
|
|
#define invalid_codepath assert_message(0, "Invalid codepath")
|
|
#define invalid_return do{assert_message(0, "Invalid codepath"); return {};}while(0)
|
|
#define invalid_default_case default: invalid_codepath
|
|
#define not_implemented assert_message(0, "Not implemented")
|
|
#define unused(x) ((void)x)
|
|
#define buff_cap(x) (sizeof(x)/sizeof((x)[0]))
|
|
#define is_flag_set(val,flag) ((val) & (flag))
|
|
#define set_flag(val,flag) ((val) |= (flag))
|
|
#define unset_flag(val,flag) ((val) &= (~(flag)))
|
|
#define bit_flag(x) (1ull << (x))
|
|
#define kib(x) ((x)*1024llu)
|
|
#define mib(x) (kib(x)*1024llu)
|
|
#define gib(x) (mib(x)*1024llu)
|
|
#define JOIN1(X,Y) X##Y
|
|
#define JOIN(X,Y) JOIN1(X,Y)
|
|
#define string_expand(x) (int)x.len, x.str
|
|
|
|
struct String{
|
|
U8 *str;
|
|
S64 len;
|
|
};
|
|
|
|
union Intern_String{ // Basically just String
|
|
String s;
|
|
struct{ U8 *str; S64 len; };
|
|
};
|
|
|
|
struct Allocator {
|
|
typedef void *Allocate(Allocator *, size_t);
|
|
typedef void Deallocate(Allocator *, void *p);
|
|
|
|
Allocate *allocate;
|
|
Deallocate *deallocate;
|
|
};
|
|
|
|
global String string_null = {(U8 *)"null", 4};
|
|
#include <stdio.h>
|
|
#define STB_SPRINTF_IMPLEMENTATION
|
|
#include "stb_sprintf.h"
|
|
#define snprintf stbsp_snprintf
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Utilities
|
|
//-----------------------------------------------------------------------------
|
|
CORE_Static size_t
|
|
get_align_offset(size_t size, size_t align){
|
|
size_t mask = align - 1;
|
|
size_t val = size & mask;
|
|
if(val){
|
|
val = align - val;
|
|
}
|
|
return val;
|
|
}
|
|
|
|
CORE_Static size_t
|
|
align_up(size_t size, size_t align){
|
|
size_t result = size + get_align_offset(size, align);
|
|
return result;
|
|
}
|
|
|
|
CORE_Static size_t
|
|
align_down(size_t size, size_t align){
|
|
size += 1; // Make sure 8 when align is 8 doesn't get rounded down to 0
|
|
size_t result = size - (align - get_align_offset(size, align));
|
|
return result;
|
|
}
|
|
|
|
CORE_Static void
|
|
memory_copy(void *dst, void *src, size_t size){
|
|
U8 *d = (U8*)dst;
|
|
U8 *s = (U8*)src;
|
|
for(size_t i = 0; i < size; i++){
|
|
d[i] = s[i];
|
|
}
|
|
}
|
|
|
|
CORE_Static void
|
|
memory_zero(void *p, size_t size){
|
|
U8 *pp = (U8 *)p;
|
|
for(size_t i = 0; i < size; i++)
|
|
pp[i] = 0;
|
|
}
|
|
|
|
template<class T>
|
|
void swap(T &a, T &b){
|
|
T temp = a;
|
|
a = b;
|
|
b = temp;
|
|
}
|
|
|
|
template<class T>
|
|
T max(T a, T b){
|
|
if(a > b) return a;
|
|
return b;
|
|
}
|
|
|
|
template<class T>
|
|
T min(T a, T b){
|
|
if(a > b) return b;
|
|
return a;
|
|
}
|
|
|
|
template<class T>
|
|
T clamp_top(T val, T max){
|
|
if(val > max) val = max;
|
|
return val;
|
|
}
|
|
|
|
template<class T>
|
|
T clamp_bot(T bot, T val){
|
|
if(val < bot) val = bot;
|
|
return val;
|
|
}
|
|
|
|
template<class T>
|
|
T clamp(T min, T val, T max){
|
|
if(val > max) val = max;
|
|
if(val < min) val = min;
|
|
return val;
|
|
}
|
|
|
|
CORE_Static U64
|
|
hash_string(String string) {
|
|
U64 hash = (U64)14695981039346656037ULL;
|
|
for (U64 i = 0; i < string.len; i++) {
|
|
hash = hash ^ (U64)(string.str[i]);
|
|
hash = hash * (U64)1099511628211ULL;
|
|
}
|
|
return hash;
|
|
}
|
|
|
|
CORE_Static U64
|
|
hash_u64(U64 x) {
|
|
x *= 0xff51afd7ed558ccd;
|
|
x ^= x >> 32;
|
|
return x;
|
|
}
|
|
|
|
CORE_Static U64
|
|
hash_ptr(const void *ptr) {
|
|
return hash_u64((uintptr_t)ptr);
|
|
}
|
|
|
|
CORE_Static U64
|
|
hash_mix(U64 x, U64 y) {
|
|
// @note: murmur hash 3 mixer but I add the 'y'
|
|
// which means it's probably bad, hopefully better
|
|
// then some random scribble I could do
|
|
x ^= (y >> 33);
|
|
x *= 0xff51afd7ed558ccd;
|
|
x ^= (x >> 33);
|
|
x *= 0xc4ceb9fe1a85ec53;
|
|
x ^= (y >> 33);
|
|
return x;
|
|
}
|
|
|
|
CORE_Static U64
|
|
is_pow2(U64 x) {
|
|
assert(x != 0);
|
|
B32 result = (x & (x - 1llu)) == 0;
|
|
return result;
|
|
}
|
|
|
|
CORE_Static U64
|
|
wrap_around_pow2(U64 x, U64 power_of_2) {
|
|
assert(is_pow2(power_of_2));
|
|
U64 r = (((x)&((power_of_2)-1llu)));
|
|
return r;
|
|
}
|
|
|
|
force_inline String
|
|
operator""_s(const char *str, size_t size){
|
|
return String{(U8 *)str, (S64)size};
|
|
}
|
|
|
|
force_inline B32
|
|
operator==(Intern_String a, Intern_String b){
|
|
return a.str == b.str;
|
|
}
|
|
|
|
force_inline B32
|
|
operator!=(Intern_String a, Intern_String b){
|
|
B32 result = a.str == b.str;
|
|
return !result;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Very cool macros. Since these are macros it's recommended to wrap them
|
|
// in a function and not use directly
|
|
//----- -----------------------------------------------------------------------
|
|
#define SLL_QUEUE_ADD_MOD(f, l, n, next) \
|
|
do { \
|
|
if ((f) == 0) { \
|
|
(f) = (l) = (n); \
|
|
} else { \
|
|
(l) = (l)->next = (n); \
|
|
} \
|
|
} while (0)
|
|
#define SLL_QUEUE_ADD(f, l, n) SLL_QUEUE_ADD_MOD(f, l, n, next)
|
|
|
|
#define SLL_QUEUE_POP_FIRST_MOD(f, l, next) \
|
|
do { \
|
|
if ((f) == (l)) { \
|
|
(f) = (l) = 0; \
|
|
} else { \
|
|
(f) = (f)->next; \
|
|
} \
|
|
} while (0)
|
|
#define SLL_QUEUE_POP_FIRST(f, l) SLL_QUEUE_POP_FIRST_MOD(f, l, next)
|
|
|
|
#define SLL_STACK_ADD_MOD(stack_base, new_stack_base, next) \
|
|
do { \
|
|
(new_stack_base)->next = (stack_base); \
|
|
(stack_base) = (new_stack_base); \
|
|
} while (0)
|
|
#define SLL_STACK_ADD(stack_base, new_stack_base) SLL_STACK_ADD_MOD(stack_base, new_stack_base, next)
|
|
|
|
#define SLL_STACK_POP(stack_base) \
|
|
do { \
|
|
if (stack_base) { \
|
|
auto(N) = (stack_base); \
|
|
(stack_base) = (stack_base)->next; \
|
|
(N)->next = 0; \
|
|
} \
|
|
} while (0)
|
|
|
|
#define DLL_QUEUE_ADD_LAST_MOD(f, l, node, next, prev) \
|
|
do { \
|
|
if ((f) == 0) { \
|
|
(f) = (l) = (node); \
|
|
(node)->prev = 0; \
|
|
(node)->next = 0; \
|
|
} else { \
|
|
(l)->next = (node); \
|
|
(node)->prev = (l); \
|
|
(node)->next = 0; \
|
|
(l) = (node); \
|
|
} \
|
|
} while (0)
|
|
#define DLL_QUEUE_ADD_LAST(f, l, node) DLL_QUEUE_ADD_LAST_MOD(f, l, node, next, prev)
|
|
#define DLL_QUEUE_ADD(f, l, node) DLL_QUEUE_ADD_LAST(f, l, node)
|
|
#define DLL_QUEUE_REMOVE_MOD(first, last, node, next, prev) \
|
|
do { \
|
|
if ((first) == (last)) { \
|
|
assert_message((node) == (first), "Macro assert failed"); \
|
|
(first) = (last) = 0; \
|
|
} else if ((last) == (node)) { \
|
|
(last) = (last)->prev; \
|
|
(last)->next = 0; \
|
|
} else if ((first) == (node)) { \
|
|
(first) = (first)->next; \
|
|
(first)->prev = 0; \
|
|
} else { \
|
|
(node)->prev->next = (node)->next; \
|
|
(node)->next->prev = (node)->prev; \
|
|
} \
|
|
} while (0)
|
|
#define DLL_QUEUE_REMOVE(first, last, node) DLL_QUEUE_REMOVE_MOD(first, last, node, next, prev)
|
|
|
|
#define DLL_STACK_ADD_MOD(first, node, next, prev) \
|
|
do { \
|
|
(node)->next = (first); \
|
|
if ((first)) \
|
|
(first)->prev = (node); \
|
|
(first) = (node); \
|
|
(node)->prev = 0; \
|
|
} while (0)
|
|
#define DLL_STACK_ADD(first, node) DLL_STACK_ADD_MOD(first, node, next, prev)
|
|
#define DLL_STACK_REMOVE_MOD(first, node, next, prev) \
|
|
do { \
|
|
if ((node) == (first)) { \
|
|
(first) = (first)->next; \
|
|
if ((first)) \
|
|
(first)->prev = 0; \
|
|
} else { \
|
|
(node)->prev->next = (node)->next; \
|
|
if ((node)->next) \
|
|
(node)->next->prev = (node)->prev; \
|
|
} \
|
|
} while (0)
|
|
#define DLL_STACK_REMOVE(first, node) DLL_STACK_REMOVE_MOD(first, node, next, prev)
|
|
|
|
#define For_Linked_List_Named(a,it) for(auto *it = (a); it; it=it->next) // @todo: reference?
|
|
#define For_Linked_List(a) For_Linked_List_Named(a,it)
|
|
#define For_Named(a,it) for(auto &it : (a))
|
|
#define For(a) For_Named((a),it)
|
|
|
|
#define Iter_Named(list, it) for(auto it = iterate(list); should_we_continue(&it); advance(&it))
|
|
#define Iter(list) Iter_Named(list, it)
|
|
|
|
#define allocate_array(a, T, size,...) (T *)allocate_size(a, sizeof(T)*(size),##__VA_ARGS__)
|
|
#define allocate_struct(a, T, ...) allocate_array(a, T, 1,##__VA_ARGS__)
|
|
CORE_Static void *allocate_size(Allocator *allocator, size_t size, bool zero_memory = true) {
|
|
void *result = allocator->allocate(allocator, size);
|
|
if (zero_memory) {
|
|
memory_zero(result, size);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
CORE_Static void deallocate(Allocator *allocator, void *p) {
|
|
assert(p);
|
|
allocator->deallocate(allocator, p);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Memory OS
|
|
//-----------------------------------------------------------------------------
|
|
struct OS_Memory{
|
|
size_t commit, reserve;
|
|
U8 *data;
|
|
};
|
|
CORE_Static OS_Memory os_reserve(size_t size);
|
|
CORE_Static B32 os_commit(OS_Memory *m, size_t size);
|
|
CORE_Static void os_release(OS_Memory *m);
|
|
CORE_Static B32 os_decommit_pos(OS_Memory *m, size_t pos);
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Memory arenas
|
|
//-----------------------------------------------------------------------------
|
|
global const size_t default_reserve_size = gib(4);
|
|
global const size_t default_alignment = 8;
|
|
global const size_t additional_commit_size = mib(1);
|
|
struct Arena : Allocator {
|
|
OS_Memory memory;
|
|
size_t alignment;
|
|
size_t len;
|
|
String debug_string;
|
|
};
|
|
|
|
CORE_Static void
|
|
arena_pop_pos(Arena *arena, size_t pos){
|
|
pos = clamp_top(pos, arena->len);
|
|
arena->len = pos;
|
|
}
|
|
|
|
CORE_Static void *
|
|
arena_pop(Arena *arena, size_t size){
|
|
size = clamp_top(size, arena->len);
|
|
arena->len -= size;
|
|
return arena->memory.data + arena->len;
|
|
}
|
|
|
|
CORE_Static void
|
|
arena_release(Arena *arena){
|
|
os_release(&arena->memory);
|
|
}
|
|
|
|
CORE_Static void
|
|
arena_clear(Arena *arena){
|
|
arena_pop_pos(arena, 0);
|
|
}
|
|
|
|
CORE_Static void
|
|
deallocate_stub(Allocator *, void *) {
|
|
}
|
|
|
|
CORE_Static void *
|
|
arena_push_size(Arena *a, size_t size){
|
|
size_t generous_size = size + a->alignment;
|
|
if(a->len+generous_size>a->memory.commit){
|
|
assert(a->memory.reserve > 0);
|
|
B32 result = os_commit(&a->memory, generous_size+additional_commit_size);
|
|
assert(result);
|
|
}
|
|
|
|
a->len = align_up(a->len, a->alignment);
|
|
assert(a->memory.reserve > a->len + size);
|
|
void *result = (U8*)a->memory.data + a->len;
|
|
a->len += size;
|
|
|
|
return result;
|
|
}
|
|
|
|
CORE_Static Arena
|
|
push_arena(Allocator *allocator, size_t size, String debug_name) {
|
|
Arena result = {};
|
|
result.memory.data = (U8 *)allocate_size(allocator, size);
|
|
result.memory.reserve = size;
|
|
result.alignment = default_alignment;
|
|
result.debug_string = debug_name;
|
|
result.allocate = (Allocator::Allocate *)arena_push_size;
|
|
result.deallocate = (Allocator::Deallocate *)deallocate_stub;
|
|
return result;
|
|
}
|
|
|
|
CORE_Static void
|
|
arena_init(Arena *a, String debug_name){
|
|
a->memory = os_reserve(default_reserve_size);
|
|
a->alignment = default_alignment;
|
|
a->debug_string = debug_name;
|
|
a->allocate = (Allocator::Allocate *)arena_push_size;
|
|
a->deallocate = (Allocator::Deallocate *)deallocate_stub;
|
|
}
|
|
|
|
CORE_Static Arena
|
|
arena_sub(Arena *base, size_t size, String debug_name) {
|
|
Arena result = {};
|
|
result.memory.data = (U8 *)arena_push_size(base, size);
|
|
result.memory.commit = size;
|
|
result.memory.reserve = size;
|
|
result.alignment = default_alignment;
|
|
result.len = 0;
|
|
return result;
|
|
}
|
|
|
|
enum Log_Kind{Log_Kind_Normal_No_NewLine, Log_Kind_Normal, Log_Kind_Error, Log_Kind_Trace};
|
|
typedef void Log_Proc(Log_Kind kind, String string, char *file, int line);
|
|
//-----------------------------------------------------------------------------
|
|
// Thread Context
|
|
//-----------------------------------------------------------------------------
|
|
struct Thread_Ctx{
|
|
Arena scratch[2];
|
|
Log_Proc *log_proc;
|
|
int thread_index;
|
|
|
|
int line;
|
|
char *file;
|
|
};
|
|
|
|
thread_local Thread_Ctx thread_ctx;
|
|
|
|
#define REPORT_ALLOCATIONS 0
|
|
#define report_file_and_line() report__file_and_line(__FILE__, __LINE__)
|
|
force_inline void
|
|
report__file_and_line(const char *file, int line){
|
|
thread_ctx.file = (char *)file;
|
|
thread_ctx.line = line;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Implicit scratch stack
|
|
//-----------------------------------------------------------------------------
|
|
struct Scratch{
|
|
size_t saved_pos;
|
|
Arena *arena;
|
|
|
|
Scratch(Arena *conflict = 0){
|
|
if(conflict == thread_ctx.scratch){
|
|
arena = thread_ctx.scratch + 1;
|
|
}
|
|
else {
|
|
arena = thread_ctx.scratch;
|
|
}
|
|
saved_pos = arena->len;
|
|
}
|
|
~Scratch(){
|
|
arena_pop_pos(arena, saved_pos);
|
|
}
|
|
force_inline operator Arena*(){ return arena; }
|
|
force_inline operator Allocator*(){ return arena; }
|
|
|
|
// @Note: Disable copy constructors, cause it caused lots of confusing errors
|
|
// Where it passed scratch instead of the arena into the constructor
|
|
// which is an error
|
|
private:
|
|
Scratch(Scratch &arena);
|
|
Scratch(Scratch &arena, Scratch &a2);
|
|
};
|
|
|
|
CORE_Static void
|
|
thread_ctx_init(){
|
|
arena_init(thread_ctx.scratch, "Scratch1"_s);
|
|
arena_init(thread_ctx.scratch+1, "Scratch2"_s);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Array
|
|
//-----------------------------------------------------------------------------
|
|
template<class T>
|
|
struct Array{
|
|
Allocator *allocator;
|
|
T *data;
|
|
S64 cap;
|
|
S64 len;
|
|
|
|
T *push_empty(S64 count = 1){
|
|
grow(count);
|
|
T *result = data + len;
|
|
len += count;
|
|
return result;
|
|
}
|
|
|
|
T *push_empty_zero(S64 count = 1){
|
|
T *result = push_empty(count);
|
|
memory_zero(result, count*sizeof(T));
|
|
return result;
|
|
}
|
|
|
|
void grow(S64 required_size){
|
|
if(cap == 0){
|
|
S64 new_cap = max(required_size*2, (S64)16);
|
|
data = allocate_array(allocator, T, new_cap);
|
|
cap = new_cap;
|
|
}
|
|
else if(len + required_size > cap){
|
|
U64 new_cap = max(cap * 2, len+required_size+1);
|
|
T *new_data = allocate_array(allocator, T, new_cap);
|
|
memory_copy(new_data, data, cap*sizeof(T));
|
|
deallocate(allocator, data);
|
|
data = new_data;
|
|
cap = new_cap;
|
|
}
|
|
}
|
|
|
|
S64 get_index(T *item){
|
|
assert((data <= item) && ((data + len) > item));
|
|
size_t offset = item - data;
|
|
return (S64)offset;
|
|
}
|
|
|
|
void add(Array<T> items){
|
|
For(items){
|
|
add(it);
|
|
}
|
|
}
|
|
|
|
void add(T item){
|
|
grow(1);
|
|
data[len++] = item;
|
|
}
|
|
|
|
S64 addi(T item){
|
|
S64 result = len;
|
|
grow(1);
|
|
data[len++] = item;
|
|
return result;
|
|
}
|
|
|
|
void unordered_remove(T *item){
|
|
assert(len > 0);
|
|
assert((data <= item) && ((data + len) > item));
|
|
*item = data[--len];
|
|
}
|
|
|
|
void init(Allocator *a, S64 size = 16){
|
|
allocator = a;
|
|
data = allocate_array(a, T, size);
|
|
cap = size;
|
|
}
|
|
|
|
Array<T> copy(Allocator *a){
|
|
Array<T> result = {};
|
|
result.len = len;
|
|
result.cap = len*2;
|
|
result.allocator = a;
|
|
result.data = allocate_array(a, T, result.cap);
|
|
memory_copy(result.data, data, sizeof(T)*result.len);
|
|
return result;
|
|
}
|
|
|
|
Array<T> tight_copy(Allocator *a){
|
|
Array<T> result = {};
|
|
result.len = len;
|
|
result.cap = len;
|
|
result.allocator = 0;
|
|
result.data = allocate_array(a, T, len);
|
|
memory_copy(result.data, data, sizeof(T)*len);
|
|
return result;
|
|
}
|
|
|
|
force_inline B32 is_last(T *item){ return item == last(); }
|
|
force_inline B32 is_first(T *item){ return item == begin(); }
|
|
force_inline void clear(){ len = 0; }
|
|
force_inline T pop() { return data[--len]; }
|
|
force_inline T *last() { return data + len - 1; }
|
|
force_inline T *begin() { return data; }
|
|
force_inline T *end () { return data + len; }
|
|
force_inline T &operator[](S64 i){ assert(i >= 0 && i < cap); return data[i]; }
|
|
|
|
struct Array_Iter{
|
|
Array<T> *array;
|
|
S64 i;
|
|
T *item;
|
|
|
|
force_inline void next(){ i+=1; item = &array->data[i]; }
|
|
force_inline B32 is_valid(){ return i < array->len; }
|
|
};
|
|
|
|
force_inline Array_Iter iter(){ return {this, 0, begin()};}
|
|
#define For_It_Named(array, it) for(auto it = (array).iter(); it.is_valid(); it.next())
|
|
#define For_It(array) For_It_Named(array, it)
|
|
};
|
|
|
|
|
|
template<class T>
|
|
CORE_Static Array<T>
|
|
array_make(Allocator *a, S64 size = 16){
|
|
Array<T> result = {};
|
|
result.init(a, size);
|
|
return result;
|
|
}
|
|
|
|
#include "base_string.cpp"
|
|
//-----------------------------------------------------------------------------
|
|
// Logging
|
|
//-----------------------------------------------------------------------------
|
|
#define log_info(...) handle_log_message(Log_Kind_Normal, __LINE__, __FILE__,##__VA_ARGS__)
|
|
#define log_info_no_nl(...) handle_log_message(Log_Kind_Normal_No_NewLine, __LINE__, __FILE__,##__VA_ARGS__)
|
|
#define log_trace(...) handle_log_message(Log_Kind_Trace, __LINE__, __FILE__,##__VA_ARGS__)
|
|
#define log_error(...) handle_log_message(Log_Kind_Error, __LINE__, __FILE__,##__VA_ARGS__)
|
|
CORE_Static void
|
|
handle_log_message(Log_Kind kind, int line, const char *file, const char *str, ...){
|
|
if(kind == Log_Kind_Trace) return;
|
|
|
|
Scratch scratch;
|
|
STRING_FMT(scratch, str, message);
|
|
if(thread_ctx.log_proc) thread_ctx.log_proc(kind, message, (char *)file, line);
|
|
else{
|
|
printf("%s", message.str);
|
|
if(kind != Log_Kind_Normal_No_NewLine){
|
|
printf("\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Defer
|
|
// http://www.gingerbill.org/article/2015/08/19/defer-in-cpp/
|
|
//-----------------------------------------------------------------------------
|
|
template <typename F>
|
|
struct Defer_Scope {
|
|
F f;
|
|
Defer_Scope(F f) : f(f) {}
|
|
~Defer_Scope() { f(); }
|
|
};
|
|
|
|
template <typename F>
|
|
Defer_Scope<F> defer_func(F f) {
|
|
return Defer_Scope<F>(f);
|
|
}
|
|
#define DEFER_1(x, y) x##y
|
|
#define DEFER_2(x, y) DEFER_1(x, y)
|
|
#define DEFER_3(x) DEFER_2(x, __COUNTER__)
|
|
#define defer(code) auto DEFER_3(_defer_) = defer_func([&](){code;})
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Map
|
|
//-----------------------------------------------------------------------------
|
|
struct Map_Key_Value{
|
|
int occupied;
|
|
U64 key;
|
|
void *value;
|
|
};
|
|
|
|
struct Map{
|
|
Allocator *allocator;
|
|
Map_Key_Value *data;
|
|
S64 len;
|
|
S64 cap;
|
|
};
|
|
CORE_Static void map_insert(Map *map, U64 key, void *val);
|
|
|
|
CORE_Static void
|
|
map_grow(Map *map, S64 new_size){
|
|
new_size = max((S64)16, new_size);
|
|
assert(new_size > map->cap);
|
|
assert(is_pow2(new_size));
|
|
assert(map->allocator);
|
|
|
|
Map new_map = {};
|
|
new_map.data = allocate_array(map->allocator, Map_Key_Value, new_size);
|
|
new_map.cap = new_size;
|
|
new_map.allocator = map->allocator;
|
|
|
|
for(S64 i = 0; i < map->cap; i++){
|
|
if(map->data[i].occupied){
|
|
map_insert(&new_map, map->data[i].key, map->data[i].value);
|
|
}
|
|
}
|
|
if(map->data) deallocate(map->allocator, map->data);
|
|
*map = new_map;
|
|
}
|
|
|
|
CORE_Static Map
|
|
map_make(Allocator *a, S64 size){
|
|
Map result = {a};
|
|
map_grow(&result, size);
|
|
return result;
|
|
}
|
|
|
|
CORE_Static void
|
|
map_insert(Map *map, U64 key, void *val){
|
|
assert(val);
|
|
assert(key);
|
|
// if(key == 0) key+=1;
|
|
|
|
if((2*map->len) + 1 > map->cap){
|
|
map_grow(map, 2*map->cap);
|
|
}
|
|
|
|
U64 hash = hash_u64(key);
|
|
U64 index = wrap_around_pow2(hash, map->cap);
|
|
U64 i = index;
|
|
for(;;){
|
|
if(map->data[i].occupied == false){
|
|
map->len++;
|
|
map->data[i].occupied = true;
|
|
map->data[i].key = key;
|
|
map->data[i].value = val;
|
|
return;
|
|
}
|
|
else if(map->data[i].key == key){
|
|
map->data[i].value = val;
|
|
return;
|
|
}
|
|
|
|
i = wrap_around_pow2(i+1, map->cap);
|
|
if(i == map->cap){
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
CORE_Static Map_Key_Value *
|
|
map_base_get(Map *map, U64 key){
|
|
if(map->len == 0) return 0;
|
|
assert(key);
|
|
|
|
U64 hash = hash_u64(key);
|
|
U64 index = wrap_around_pow2(hash, map->cap);
|
|
U64 i = index;
|
|
for(;;){
|
|
if(map->data[i].key == key){
|
|
return map->data + i;
|
|
}
|
|
else if(map->data[i].key == 0){
|
|
return 0;
|
|
}
|
|
|
|
i = wrap_around_pow2(i+1, map->cap);
|
|
if(i == map->cap){
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
CORE_Static void *
|
|
map_get(Map *map, U64 key){
|
|
Map_Key_Value *result = map_base_get(map, key);
|
|
if(result && result->occupied) return result->value;
|
|
return 0;
|
|
}
|
|
|
|
CORE_Static void *
|
|
map_remove(Map *map, U64 key){
|
|
Map_Key_Value *kv = map_base_get(map, key);
|
|
if(kv){
|
|
kv->occupied = false;
|
|
return kv->value;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
CORE_Static void *
|
|
map_get(Map *map, void *pointer){
|
|
return map_get(map, (U64)pointer);
|
|
}
|
|
|
|
CORE_Static void *
|
|
map_get(Map *map, Intern_String string){
|
|
return map_get(map, hash_string(string.s));
|
|
}
|
|
|
|
CORE_Static void
|
|
map_insert(Map *map, void *key, void *value){
|
|
map_insert(map, (U64)key, value);
|
|
}
|
|
|
|
CORE_Static void
|
|
map_insert(Map *map, Intern_String key, void *value){
|
|
map_insert(map, hash_string(key.s), value);
|
|
}
|
|
|
|
CORE_Static void
|
|
map_test(){
|
|
Scratch scratch;
|
|
Map map = {scratch};
|
|
const size_t size = 1025;
|
|
for(size_t i = 1; i < size; i++){
|
|
map_insert(&map, i, (void *)i);
|
|
}
|
|
for(size_t i = 1; i < size; i++){
|
|
size_t val = (size_t)map_get(&map, i);
|
|
assert(val == i);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// String intern
|
|
//-----------------------------------------------------------------------------
|
|
struct Intern_Table{
|
|
Allocator *string_allocator;
|
|
Map map;
|
|
U8 *first_keyword;
|
|
U8 *last_keyword;
|
|
};
|
|
|
|
CORE_Static Intern_Table
|
|
intern_table_make(Allocator *string_allocator, Allocator *map_allocator, S64 initial_size = 32){
|
|
Intern_Table result = {};
|
|
result.map = map_make(map_allocator, initial_size);
|
|
result.string_allocator = string_allocator;
|
|
return result;
|
|
}
|
|
|
|
CORE_Static Intern_String
|
|
intern_string(Intern_Table *t, String string){
|
|
assert(t->string_allocator);
|
|
U64 hash = hash_string(string);
|
|
U8 *slot = (U8 *)map_get(&t->map, hash);
|
|
if(slot){
|
|
// @todo: Is this a cast bug: *(slot-sizeof(S64))? slot is u8 so truncates?
|
|
Intern_String result = {{slot, *(slot-sizeof(S64))}};
|
|
return result;
|
|
}
|
|
|
|
S64 *len_address = (S64 *)allocate_size(t->string_allocator, string.len+1+sizeof(S64), false);
|
|
*len_address = string.len;
|
|
|
|
U8 *string_address = (U8 *)(len_address + 1);
|
|
memory_copy(string_address, string.str, string.len);
|
|
string_address[string.len] = 0;
|
|
|
|
map_insert(&t->map, hash, string_address);
|
|
Intern_String result = {{string_address, *len_address}};
|
|
|
|
return result;
|
|
}
|
|
|
|
CORE_Static void
|
|
test_intern_table(){
|
|
Scratch scratch;
|
|
Intern_Table table = intern_table_make(scratch, scratch);
|
|
Intern_String intern1 = intern_string(&table, "Thing"_s);
|
|
Intern_String intern2 = intern_string(&table, "Thing"_s);
|
|
Intern_String intern3 = intern_string(&table, "Not Thing"_s);
|
|
assert(intern1.str == intern2.str);
|
|
assert(intern3.str != intern2.str);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Array List
|
|
// @todo(krzosa): If even one item got removed from block
|
|
// the block should go on free list
|
|
//-----------------------------------------------------------------------------
|
|
const int LIST_DEFAULT_BLOCK_SIZE = 32;
|
|
const int LIST_DEFAULT_ALLOCATION_MUL = 2;
|
|
|
|
template<class T>
|
|
struct List_Node{
|
|
List_Node<T> *next;
|
|
List_Node<T> *prev;
|
|
int cap;
|
|
int len;
|
|
T data[];
|
|
};
|
|
|
|
template<class T>
|
|
struct List{
|
|
int block_size = 0;
|
|
int allocation_multiplier = 0;
|
|
List_Node<T> *first = 0;
|
|
List_Node<T> *last = 0;
|
|
List_Node<T> *first_free = 0;
|
|
};
|
|
|
|
template<class T>
|
|
List_Node<T> *list_allocate_node(Allocator *arena, int size){
|
|
auto node = (List_Node<T> *)allocate_size(arena, sizeof(List_Node<T>) + size*sizeof(T), false);
|
|
node->cap = size;
|
|
node->len = 0;
|
|
node->next = 0;
|
|
node->prev = 0;
|
|
return node;
|
|
}
|
|
|
|
template<class T>
|
|
void list_allocate_free_node(Allocator *arena, List<T> *list, int size){
|
|
List_Node<T> *node = list_allocate_node<T>(arena, size);
|
|
DLL_STACK_ADD(list->first_free, node);
|
|
}
|
|
|
|
template<class T>
|
|
void list_make_sure_there_is_room_for_item_count(Allocator *arena, List<T> *list, int item_count){
|
|
if(list->last == 0 || list->last->len + item_count > list->last->cap){
|
|
// Not enough space we need to get a new block
|
|
List_Node<T> *node = 0;
|
|
|
|
// Iterate the free list to check if we have a block of required size there
|
|
For_Linked_List(list->first_free){
|
|
if(it->cap >= item_count){
|
|
DLL_STACK_REMOVE(list->first_free, it);
|
|
node = it;
|
|
node->len = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// We don't have a block on the free list need to allocate
|
|
if(!node){
|
|
// Set default values if not initialized
|
|
if(!list->allocation_multiplier) list->allocation_multiplier = LIST_DEFAULT_ALLOCATION_MUL;
|
|
if(!list->block_size) list->block_size = LIST_DEFAULT_BLOCK_SIZE;
|
|
|
|
if(item_count > list->block_size)
|
|
list->block_size = item_count*2;
|
|
node = list_allocate_node<T>(arena, list->block_size);
|
|
list->block_size *= list->allocation_multiplier;
|
|
}
|
|
|
|
assert(node);
|
|
DLL_QUEUE_ADD_LAST(list->first, list->last, node);
|
|
}
|
|
}
|
|
|
|
template<class T>
|
|
T *list_get(List<T> *list, int index, List_Node<T> **node = 0, int *in_block_index = 0){
|
|
if(list){
|
|
int i = 0;
|
|
For_Linked_List(list->first){
|
|
int lookup_i = index - i;
|
|
if(lookup_i < it->len) {
|
|
if(node) *node = it;
|
|
if(in_block_index) *in_block_index = lookup_i;
|
|
return it->data + lookup_i;
|
|
}
|
|
i += it->len;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
template<class T>
|
|
T *getp(List<T> *list, int index){
|
|
return list_get(list, index);
|
|
}
|
|
|
|
template<class T>
|
|
T get(List<T> *list, int index){
|
|
return *list_get(list, index);
|
|
}
|
|
|
|
template<class T>
|
|
void add(Allocator *arena, List<T> *list, T item){
|
|
list_make_sure_there_is_room_for_item_count(arena, list, 1);
|
|
list->last->data[list->last->len++] = item;
|
|
}
|
|
|
|
template<class T>
|
|
T *add_size(Allocator *arena, List<T> *list, int count = 1, int zero_memory = 0){
|
|
list_make_sure_there_is_room_for_item_count(arena, list, count);
|
|
T *result = list->last->data + list->last->len;
|
|
list->last->len += count;
|
|
|
|
if(zero_memory){
|
|
memory_zero(result, sizeof(T)*count);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
template<class T>
|
|
void list_free_node(List<T> *list, List_Node<T> *node){
|
|
#if 1
|
|
// Make sure it's actually in list list
|
|
bool found = false;
|
|
For_Linked_List(list->first){
|
|
if(it == node){
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
assert(found);
|
|
#endif
|
|
|
|
DLL_QUEUE_REMOVE(list->first, list->last, node);
|
|
DLL_STACK_ADD(list->first_free, node);
|
|
}
|
|
|
|
template<class T>
|
|
void clear(List<T> *list){
|
|
memory_zero(list, sizeof(List<T>));
|
|
}
|
|
|
|
template<class T>
|
|
int length(List<T> *list){
|
|
int result = 0;
|
|
For_Linked_List(list->first){
|
|
result += it->len;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
template<class T>
|
|
void free_all_nodes(List<T> *list){
|
|
assert(!list->last->next);
|
|
assert(!list->first->prev);
|
|
list->last->next = list->first_free;
|
|
if(list->first_free) list->first_free->prev = list->last;
|
|
list->first_free = list->first;
|
|
list->last = list->first = 0;
|
|
}
|
|
|
|
template<class T>
|
|
T ordered_remove(List<T> *list, int index){
|
|
List_Node<T> *node; int in_block_index;
|
|
T *data = list_get(list, index, &node, &in_block_index);
|
|
|
|
assert_message(data, "Trying to unordered_remove element that's outside of the List");
|
|
if(!data)
|
|
return {};
|
|
|
|
T result = *data;
|
|
|
|
// Check if we need to deallocate the block
|
|
if(node->len == 1) {
|
|
list_free_node(list, node);
|
|
return result;
|
|
}
|
|
|
|
// We need to move part of the block to fill the new empty spot
|
|
int right_count = (--node->len) - in_block_index;
|
|
memory_copy(data, data+1, sizeof(T)*right_count);
|
|
return result;
|
|
}
|
|
|
|
template<class T>
|
|
T unordered_remove(List<T> *list, int index){
|
|
List_Node<T> *node;
|
|
T *data = list_get(list, index, &node);
|
|
|
|
assert_message(data, "Trying to unordered_remove element that's outside of the List");
|
|
if(!data)
|
|
return {};
|
|
|
|
assert(node->len);
|
|
assert(node->cap);
|
|
|
|
// Swap
|
|
T result = *data;
|
|
*data = node->data[node->len - 1];
|
|
|
|
node->len -= 1;
|
|
if(node->len == 0){
|
|
list_free_node(list, node);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
template<class T>
|
|
T pop(List<T> *list){
|
|
assert(list->last != 0);
|
|
assert(list->last->len > 0);
|
|
T result = list->last->data[--list->last->len];
|
|
if(list->last->len == 0){
|
|
list_free_node(list, list->last);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
template<class T>
|
|
T *merge(Allocator *arena, List<T> *list){
|
|
int len = length(list);
|
|
T *result = allocate_size(arena, T, len, false);
|
|
|
|
int i = 0;
|
|
For_Linked_List(list->first){
|
|
memory_copy(result + i, it->data, it->len*sizeof(T));
|
|
i += it->len;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
template<class T>
|
|
struct List_Iter{
|
|
T *item;
|
|
int index;
|
|
List_Node<T> *node;
|
|
int node_index;
|
|
};
|
|
|
|
template<class T>
|
|
void advance(List_Iter<T> *iter){
|
|
if(!iter->node) return;
|
|
|
|
if(iter->node_index + 1 >= iter->node->len){
|
|
iter->node = iter->node->next;
|
|
iter->node_index = -1;
|
|
iter->item = 0;
|
|
}
|
|
|
|
if(iter->node){
|
|
iter->node_index += 1;
|
|
iter->index += 1;
|
|
iter->item = iter->node->data + iter->node_index;
|
|
}
|
|
}
|
|
|
|
template<class T>
|
|
List_Iter<T> iterate(List<T> *list){
|
|
List_Iter<T> result = {};
|
|
result.node = list->first;
|
|
result.index = result.node_index = -1;
|
|
advance(&result);
|
|
return result;
|
|
}
|
|
|
|
template<class T>
|
|
bool should_we_continue(List_Iter<T> *iter){
|
|
return iter->item != 0;
|
|
}
|