#ifdef GEN_INTELLISENSE_DIRECTIVES # pragma once # include "printing.hpp" #endif #pragma region Containers template struct RemoveConst { typedef TType Type; }; template struct RemoveConst { typedef TType Type; }; template struct RemoveConst { typedef TType Type[]; }; template struct RemoveConst { typedef TType Type[Size]; }; template using TRemoveConst = typename RemoveConst::Type; #pragma region Array #define Array(Type) Array // #define array_init(Type, ...) array_init (__VA_ARGS__) // #define array_init_reserve(Type, ...) array_init_reserve(__VA_ARGS__) struct ArrayHeader; #if GEN_SUPPORT_CPP_MEMBER_FEATURES template struct Array; #else template using Array = Type*; #endif usize array_grow_formula(ssize value); template Array(Type) array_init (AllocatorInfo allocator); template Array(Type) array_init_reserve(AllocatorInfo allocator, ssize capacity); template bool append (Array(Type)* array, Array(Type) other); template bool append (Array(Type)* array, Type value); template bool append (Array(Type)* array, Type* items, usize item_num); template bool append_at (Array(Type)* array, Type item, usize idx); template bool append_at (Array(Type)* array, Type* items, usize item_num, usize idx); template Type* back (Array(Type) array); template void clear (Array(Type) array); template bool fill (Array(Type) array, usize begin, usize end, Type value); template void free (Array(Type)* array); template bool grow (Array(Type)* array, usize min_capacity); template usize num (Array(Type) array); template void pop (Array(Type) array); template void remove_at (Array(Type) array, usize idx); template bool reserve (Array(Type)* array, usize new_capacity); template bool resize (Array(Type)* array, usize num); template bool set_capacity (Array(Type)* array, usize new_capacity); template ArrayHeader* get_header (Array(Type) array); struct ArrayHeader { AllocatorInfo Allocator; usize Capacity; usize Num; }; #if GEN_SUPPORT_CPP_MEMBER_FEATURES template struct Array { Type* Data; #pragma region Member Mapping forceinline static Array init(AllocatorInfo allocator) { return GEN_NS array_init(allocator); } forceinline static Array init_reserve(AllocatorInfo allocator, ssize capacity) { return GEN_NS array_init_reserve(allocator, capacity); } forceinline static usize grow_formula(ssize value) { return GEN_NS array_grow_formula(value); } forceinline bool append(Array other) { return GEN_NS append(this, other); } forceinline bool append(Type value) { return GEN_NS append(this, value); } forceinline bool append(Type* items, usize item_num) { return GEN_NS append(this, items, item_num); } forceinline bool append_at(Type item, usize idx) { return GEN_NS append_at(this, item, idx); } forceinline bool append_at(Type* items, usize item_num, usize idx) { return GEN_NS append_at(this, items, item_num, idx); } forceinline Type* back() { return GEN_NS back(* this); } forceinline void clear() { GEN_NS clear(* this); } forceinline bool fill(usize begin, usize end, Type value) { return GEN_NS fill(* this, begin, end, value); } forceinline void free() { GEN_NS free(this); } forceinline ArrayHeader* get_header() { return GEN_NS get_header(* this); } forceinline bool grow(usize min_capacity) { return GEN_NS grow(this, min_capacity); } forceinline usize num() { return GEN_NS num(*this); } forceinline void pop() { GEN_NS pop(* this); } forceinline void remove_at(usize idx) { GEN_NS remove_at(* this, idx); } forceinline bool reserve(usize new_capacity) { return GEN_NS reserve(this, new_capacity); } forceinline bool resize(usize num) { return GEN_NS resize(this, num); } forceinline bool set_capacity(usize new_capacity) { return GEN_NS set_capacity(this, new_capacity); } #pragma endregion Member Mapping forceinline operator Type*() { return Data; } forceinline operator Type const*() const { return Data; } forceinline Type* begin() { return Data; } forceinline Type* end() { return Data + get_header()->Num; } forceinline Type& operator[](ssize index) { return Data[index]; } forceinline Type const& operator[](ssize index) const { return Data[index]; } }; #endif #if GEN_SUPPORT_CPP_REFERENCES template bool append(Array& array, Array other) { return GEN_NS append( & array, other ); } template bool append(Array& array, Type value) { return GEN_NS append( & array, value ); } template bool append(Array& array, Type* items, usize item_num) { return GEN_NS append( & array, items, item_num ); } template bool append_at(Array& array, Type item, usize idx) { return GEN_NS append_at( & array, item, idx ); } template bool append_at(Array& array, Type* items, usize item_num, usize idx) { return GEN_NS append_at( & array, items, item_num, idx ); } template void free(Array& array) { return GEN_NS free( & array ); } template bool grow(Array& array, usize min_capacity) { return GEN_NS grow( & array, min_capacity); } template bool reserve(Array& array, usize new_capacity) { return GEN_NS reserve( & array, new_capacity); } template bool resize(Array& array, usize num) { return GEN_NS resize( & array, num); } template bool set_capacity(Array& array, usize new_capacity) { return GEN_NS set_capacity( & array, new_capacity); } template forceinline Type* begin(Array& array) { return array; } template forceinline Type* end(Array& array) { return array + get_header(array)->Num; } template forceinline Type* next(Array& array, Type* entry) { return entry + 1; } #else template forceinline Type* begin(Array array) { return array; } template forceinline Type* end(Array array) { return array + get_header(array)->Num; } template forceinline Type* next(Array array, Type* entry) { return entry + 1; } #endif template inline Array array_init(AllocatorInfo allocator) { return array_init_reserve(allocator, array_grow_formula(0)); } template inline Array array_init_reserve(AllocatorInfo allocator, ssize capacity) { ArrayHeader* header = rcast(ArrayHeader*, alloc(allocator, sizeof(ArrayHeader) + sizeof(Type) * capacity)); if (header == nullptr) return {nullptr}; header->Allocator = allocator; header->Capacity = capacity; header->Num = 0; return {rcast(Type*, header + 1)}; } usize array_grow_formula(ssize value) { return 2 * value + 8; } template inline bool append(Array* array, Array other) { return append(array, other, num(other)); } template inline bool append(Array* array, Type value) { ArrayHeader* header = get_header(* array); if (header->Num == header->Capacity) { if (!grow(array, header->Capacity)) return false; header = get_header(* array); } (*array)[ header->Num] = value; header->Num++; return true; } template inline bool append(Array* array, Type* items, usize item_num) { ArrayHeader* header = get_header(array); if (header->Num + item_num > header->Capacity) { if (!grow(array, header->Capacity + item_num)) return false; header = get_header(array); } mem_copy(array.Data + header->Num, items, item_num * sizeof(Type)); header->Num += item_num; return true; } template inline bool append_at(Array* array, Type item, usize idx) { ArrayHeader* header = get_header(* array); ssize slot = idx; if (slot >= header->Num) slot = header->Num - 1; if (slot < 0) slot = 0; if (header->Capacity < header->Num + 1) { if ( ! grow(array, header->Capacity + 1)) return false; header = get_header(* array); } Type* target = &(*array)[slot]; mem_move(target + 1, target, (header->Num - slot) * sizeof(Type)); header->Num++; header = get_header(* array); return true; } template inline bool append_at(Array* array, Type* items, usize item_num, usize idx) { ArrayHeader* header = get_header(array); if (idx >= header->Num) { return append(array, items, item_num); } if (item_num > header->Capacity) { if (! grow(array, header->Capacity + item_num)) return false; header = get_header(array); } Type* target = array.Data + idx + item_num; Type* src = array.Data + idx; mem_move(target, src, (header->Num - idx) * sizeof(Type)); mem_copy(src, items, item_num * sizeof(Type)); header->Num += item_num; return true; } template inline Type* back(Array* array) { GEN_ASSERT(array != nullptr); ArrayHeader* header = get_header(* array); if (header->Num <= 0) return nullptr; return & (*array)[header->Num - 1]; } template inline void clear(Array array) { ArrayHeader* header = get_header(array); header->Num = 0; } template inline bool fill(Array array, usize begin, usize end, Type value) { ArrayHeader* header = get_header(array); if (begin < 0 || end > header->Num) return false; for (ssize idx = ssize(begin); idx < ssize(end); idx++) { array[idx] = value; } return true; } template inline void free(Array* array) { GEN_ASSERT(array != nullptr); ArrayHeader* header = get_header(* array); GEN_NS free(header->Allocator, header); Type** Data = (Type**)array; *Data = nullptr; } template forceinline ArrayHeader* get_header(Array array) { Type* Data = array; using NonConstType = TRemoveConst; return rcast(ArrayHeader*, const_cast(Data)) - 1; } template inline bool grow(Array* array, usize min_capacity) { ArrayHeader* header = get_header(* array); usize new_capacity = array_grow_formula(header->Capacity); if (new_capacity < min_capacity) new_capacity = min_capacity; return set_capacity(array, new_capacity); } template inline usize num(Array array) { return get_header(array)->Num; } template inline void pop(Array array) { ArrayHeader* header = get_header(array); GEN_ASSERT(header->Num > 0); header->Num--; } template inline void remove_at(Array array, usize idx) { ArrayHeader* header = get_header(array); GEN_ASSERT(idx < header->Num); mem_move(array + idx, array + idx + 1, sizeof(Type) * (header->Num - idx - 1)); header->Num--; } template inline bool reserve(Array* array, usize new_capacity) { ArrayHeader* header = get_header(array); if (header->Capacity < new_capacity) return set_capacity(array, new_capacity); return true; } template inline bool resize(Array* array, usize num) { ArrayHeader* header = get_header(* array); if (header->Capacity < num) { if (! grow( array, num)) return false; header = get_header(* array); } header->Num = num; return true; } template inline bool set_capacity(Array* array, usize new_capacity) { ArrayHeader* header = get_header(* array); if (new_capacity == header->Capacity) return true; if (new_capacity < header->Num) { header->Num = new_capacity; return true; } ssize size = sizeof(ArrayHeader) + sizeof(Type) * new_capacity; ArrayHeader* new_header = rcast(ArrayHeader*, alloc(header->Allocator, size)); if (new_header == nullptr) return false; mem_move(new_header, header, sizeof(ArrayHeader) + sizeof(Type) * header->Num); new_header->Capacity = new_capacity; GEN_NS free(header->Allocator, header); Type** Data = (Type**)array; * Data = rcast(Type*, new_header + 1); return true; } #pragma endregion Array // TODO(Ed) : This thing needs ALOT of work. #pragma region HashTable #define HashTable(Type) HashTable template struct HashTable; struct HashTableFindResult { ssize HashIndex; ssize PrevIndex; ssize EntryIndex; }; template struct HashTableEntry { u64 Key; ssize Next; Type Value; }; #define HashTableEntry(Type) HashTableEntry template HashTable hashtable_init(AllocatorInfo allocator); template HashTable hashtable_init_reserve(AllocatorInfo allocator, usize num); template void clear (HashTable table); template void destroy (HashTable* table); template Type* get (HashTable table, u64 key); template void grow (HashTable* table); template void rehash (HashTable* table, ssize new_num); template void rehash_fast (HashTable table); template void remove (HashTable table, u64 key); template void remove_entry (HashTable table, ssize idx); template void set (HashTable* table, u64 key, Type value); template ssize slot (HashTable table, u64 key); template ssize add_entry (HashTable* table, u64 key); template HashTableFindResult find (HashTable table, u64 key); template bool full (HashTable table); template void map (HashTable table, void (*map_proc)(u64 key, Type value)); template void map_mut (HashTable table, void (*map_proc)(u64 key, Type* value)); static constexpr f32 HashTable_CriticalLoadScale = 0.7f; template struct HashTable { Array Hashes; Array> Entries; #if GEN_SUPPORT_CPP_MEMBER_FEATURES #pragma region Member Mapping forceinline static HashTable init(AllocatorInfo allocator) { return GEN_NS hashtable_init(allocator); } forceinline static HashTable init_reserve(AllocatorInfo allocator, usize num) { return GEN_NS hashtable_init_reserve(allocator, num); } forceinline void clear() { GEN_NS clear(*this); } forceinline void destroy() { GEN_NS destroy(*this); } forceinline Type* get(u64 key) { return GEN_NS get(*this, key); } forceinline void grow() { GEN_NS grow(*this); } forceinline void rehash(ssize new_num) { GEN_NS rehash(*this, new_num); } forceinline void rehash_fast() { GEN_NS rehash_fast(*this); } forceinline void remove(u64 key) { GEN_NS remove(*this, key); } forceinline void remove_entry(ssize idx) { GEN_NS remove_entry(*this, idx); } forceinline void set(u64 key, Type value) { GEN_NS set(*this, key, value); } forceinline ssize slot(u64 key) { return GEN_NS slot(*this, key); } forceinline void map(void (*proc)(u64, Type)) { GEN_NS map(*this, proc); } forceinline void map_mut(void (*proc)(u64, Type*)) { GEN_NS map_mut(*this, proc); } #pragma endregion Member Mapping #endif }; #if GEN_SUPPORT_CPP_REFERENCES template void destroy (HashTable& table) { destroy(& table); } template void grow (HashTable& table) { grow(& table); } template void rehash (HashTable& table, ssize new_num) { rehash(& table, new_num); } template void set (HashTable& table, u64 key, Type value) { set(& table, key, value); } template ssize add_entry(HashTable& table, u64 key) { add_entry(& table, key); } #endif template inline HashTable hashtable_init(AllocatorInfo allocator) { HashTable result = hashtable_init_reserve(allocator, 8); return result; } template inline HashTable hashtable_init_reserve(AllocatorInfo allocator, usize num) { HashTable result = { { nullptr }, { nullptr } }; result.Hashes = array_init_reserve(allocator, num); get_header(result.Hashes)->Num = num; resize(& result.Hashes, num); fill(result.Hashes, 0, num, -1); result.Entries = array_init_reserve>(allocator, num); return result; } template inline void clear(HashTable table) { clear(table.Entries); fill(table.Hashes, 0, num(table.Hashes), -1); } template inline void destroy(HashTable* table) { if (table->Hashes && get_header(table->Hashes)->Capacity) { free(& table->Hashes); free(& table->Entries); } } template inline Type* get(HashTable table, u64 key) { ssize idx = find(table, key).EntryIndex; if (idx >= 0) return & table.Entries[idx].Value; return nullptr; } template inline void map(HashTable table, void (*map_proc)(u64 key, Type value)) { GEN_ASSERT_NOT_NULL(map_proc); for (ssize idx = 0; idx < ssize(num(table.Entries)); ++idx) { map_proc(table.Entries[idx].Key, table.Entries[idx].Value); } } template inline void map_mut(HashTable table, void (*map_proc)(u64 key, Type* value)) { GEN_ASSERT_NOT_NULL(map_proc); for (ssize idx = 0; idx < ssize(num(table.Entries)); ++idx) { map_proc(table.Entries[idx].Key, & table.Entries[idx].Value); } } template inline void grow(HashTable* table) { ssize new_num = array_grow_formula(num(table->Entries)); rehash(table, new_num); } template inline void rehash(HashTable* table, ssize new_num) { ssize last_added_index; HashTable new_ht = hashtable_init_reserve(get_header(table->Hashes)->Allocator, new_num); for (ssize idx = 0; idx < ssize(num(table->Entries)); ++idx) { HashTableFindResult find_result; HashTableEntry& entry = table->Entries[idx]; find_result = find(new_ht, entry.Key); last_added_index = add_entry(& new_ht, entry.Key); if (find_result.PrevIndex < 0) new_ht.Hashes[find_result.HashIndex] = last_added_index; else new_ht.Entries[find_result.PrevIndex].Next = last_added_index; new_ht.Entries[last_added_index].Next = find_result.EntryIndex; new_ht.Entries[last_added_index].Value = entry.Value; } destroy(table); * table = new_ht; } template inline void rehash_fast(HashTable table) { ssize idx; for (idx = 0; idx < ssize(num(table.Entries)); idx++) table.Entries[idx].Next = -1; for (idx = 0; idx < ssize(num(table.Hashes)); idx++) table.Hashes[idx] = -1; for (idx = 0; idx < ssize(num(table.Entries)); idx++) { HashTableEntry* entry; HashTableFindResult find_result; entry = &table.Entries[idx]; find_result = find(table, entry->Key); if (find_result.PrevIndex < 0) table.Hashes[find_result.HashIndex] = idx; else table.Entries[find_result.PrevIndex].Next = idx; } } template inline void remove(HashTable table, u64 key) { HashTableFindResult find_result = find(table, key); if (find_result.EntryIndex >= 0) { remove_at(table.Entries, find_result.EntryIndex); rehash_fast(table); } } template inline void remove_entry(HashTable table, ssize idx) { remove_at(table.Entries, idx); } template inline void set(HashTable* table, u64 key, Type value) { ssize idx; HashTableFindResult find_result; if (full(* table)) grow(table); find_result = find(* table, key); if (find_result.EntryIndex >= 0) { idx = find_result.EntryIndex; } else { idx = add_entry(table, key); if (find_result.PrevIndex >= 0) { table->Entries[find_result.PrevIndex].Next = idx; } else { table->Hashes[find_result.HashIndex] = idx; } } table->Entries[idx].Value = value; if (full(* table)) grow(table); } template inline ssize slot(HashTable table, u64 key) { for (ssize idx = 0; idx < ssize(num(table.Hashes)); ++idx) if (table.Hashes[idx] == key) return idx; return -1; } template inline ssize add_entry(HashTable* table, u64 key) { ssize idx; HashTableEntry entry = { key, -1 }; idx = num(table->Entries); append( & table->Entries, entry); return idx; } template inline HashTableFindResult find(HashTable table, u64 key) { HashTableFindResult result = { -1, -1, -1 }; if (num(table.Hashes) > 0) { result.HashIndex = key % num(table.Hashes); result.EntryIndex = table.Hashes[result.HashIndex]; while (result.EntryIndex >= 0) { if (table.Entries[result.EntryIndex].Key == key) break; result.PrevIndex = result.EntryIndex; result.EntryIndex = table.Entries[result.EntryIndex].Next; } } return result; } template inline bool full(HashTable table) { usize critical_load = usize(HashTable_CriticalLoadScale * f32(num(table.Hashes))); b32 result = num(table.Entries) > critical_load; return result; } #define hashtable_init(Type, allocator) hashtable_init(allocator) #define hashtable_init_reserve(Type, allocator, num) hashtable_init_reserve(allocator, num) #define hashtable_clear(Type, table) clear(table) #define hashtable_destroy(Type, table) destroy(table) #define hashtable_get(Type, table, key) get(table, key) #define hashtable_grow(Type, table) grow(table) #define hashtable_rehash(Type, table, new_num) rehash(table, new_num) #define hashtable_rehash_fast(Type, table) rehash_fast(table) #define hashtable_remove(Type, table, key) remove(table, key) #define hashtable_remove_entry(Type, table, idx) remove_entry(table, idx) #define hashtable_set(Type, table, key, value) set(table, key, value) #define hashtable_slot(Type, table, key) slot(table, key) #define hashtable_add_entry(Type, table, key) add_entry(table, key) #define hashtable_find(Type, table, key) find(table, key) #define hashtable_full(Type, table) full(table) #define hashtable_map(Type, table, map_proc) map(table, map_proc) #define hashtable_map_mut(Type, table, map_proc) map_mut(table, map_proc) #pragma endregion HashTable #pragma endregion Containers