#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 #define Breakpoint __debugbreak() #define force_inline __forceinline #else #define Breakpoint (*(volatile int *)0 = 0) #define force_inline inline #endif #include #include #include 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 #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 void swap(T &a, T &b){ T temp = a; a = b; b = temp; } template T max(T a, T b){ if(a > b) return a; return b; } template T min(T a, T b){ if(a > b) return b; return a; } template T clamp_top(T val, T max){ if(val > max) val = max; return val; } template T clamp_bot(T bot, T val){ if(val < bot) val = bot; return val; } template 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 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 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 copy(Allocator *a){ Array 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 tight_copy(Allocator *a){ Array 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 *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 CORE_Static Array array_make(Allocator *a, S64 size = 16){ Array 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 struct Defer_Scope { F f; Defer_Scope(F f) : f(f) {} ~Defer_Scope() { f(); } }; template Defer_Scope defer_func(F f) { return Defer_Scope(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 struct List_Node{ List_Node *next; List_Node *prev; int cap; int len; T data[]; }; template struct List{ int block_size = 0; int allocation_multiplier = 0; List_Node *first = 0; List_Node *last = 0; List_Node *first_free = 0; }; template List_Node *list_allocate_node(Allocator *arena, int size){ auto node = (List_Node *)allocate_size(arena, sizeof(List_Node) + size*sizeof(T), false); node->cap = size; node->len = 0; node->next = 0; node->prev = 0; return node; } template void list_allocate_free_node(Allocator *arena, List *list, int size){ List_Node *node = list_allocate_node(arena, size); DLL_STACK_ADD(list->first_free, node); } template void list_make_sure_there_is_room_for_item_count(Allocator *arena, List *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 *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(arena, list->block_size); list->block_size *= list->allocation_multiplier; } assert(node); DLL_QUEUE_ADD_LAST(list->first, list->last, node); } } template T *list_get(List *list, int index, List_Node **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 T *getp(List *list, int index){ return list_get(list, index); } template T get(List *list, int index){ return *list_get(list, index); } template void add(Allocator *arena, List *list, T item){ list_make_sure_there_is_room_for_item_count(arena, list, 1); list->last->data[list->last->len++] = item; } template T *add_size(Allocator *arena, List *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 void list_free_node(List *list, List_Node *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 void clear(List *list){ memory_zero(list, sizeof(List)); } template int length(List *list){ int result = 0; For_Linked_List(list->first){ result += it->len; } return result; } template void free_all_nodes(List *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 T ordered_remove(List *list, int index){ List_Node *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 T unordered_remove(List *list, int index){ List_Node *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 T pop(List *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 T *merge(Allocator *arena, List *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 struct List_Iter{ T *item; int index; List_Node *node; int node_index; }; template void advance(List_Iter *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 List_Iter iterate(List *list){ List_Iter result = {}; result.node = list->first; result.index = result.node_index = -1; advance(&result); return result; } template bool should_we_continue(List_Iter *iter){ return iter->item != 0; }