#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 #if ! GEN_COMPILER_C #define Array(Type) Array // #define array_init(Type, ...) array_init (__VA_ARGS__) // #define array_init_reserve(Type, ...) array_init_reserve(__VA_ARGS__) #endif 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 array_init(AllocatorInfo allocator); template Array array_init_reserve(AllocatorInfo allocator, ssize capacity); template bool append(Array& array, Array other); template bool append(Array& array, Type value); template bool append(Array& array, Type* items, usize item_num); template bool append_at(Array& array, Type item, usize idx); template bool append_at(Array& array, Type* items, usize item_num, usize idx); template Type& back(Array& array); template void clear(Array& array); template bool fill(Array& array, usize begin, usize end, Type value); template void free(Array& array); template bool grow(Array& array, usize min_capacity); template usize num(Array& array); template void pop(Array& array); template void remove_at(Array& array, usize idx); template bool reserve(Array& array, usize new_capacity); template bool resize(Array& array, usize num); template bool set_capacity(Array& array, usize new_capacity); template ArrayHeader* get_header(Array& array); 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; } 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); } 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; } #pragma endregion Member Mapping }; #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); if (idx >= header->Num) idx = header->Num - 1; if (idx < 0) idx = 0; if (header->Capacity < header->Num + 1) { if (!grow(array, header->Capacity + 1)) return false; header = get_header(array); } Type* target = array + idx; mem_move(target + 1, target, (header->Num - idx) * sizeof(Type)); header->Num++; 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) { ArrayHeader* header = get_header(array); 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) { ArrayHeader* header = get_header(array); GEN_NS free(header->Allocator, header); Type*& Data = rcast(Type*&, array); Data = nullptr; } template inline ArrayHeader* get_header(Array& array) { using NonConstType = TRemoveConst; Type* Data = array; // This should do nothing in C but in C++ gets member Data struct. 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 = rcast(Type*&, array); Data = rcast(Type*, new_header + 1); return true; } #pragma endregion Array // TODO(Ed) : This thing needs ALOT of work. #pragma region HashTable template struct HashTable; struct HashTableFindResult { ssize HashIndex; ssize PrevIndex; ssize EntryIndex; }; template struct HashTableEntry { u64 Key; ssize Next; Type Value; }; // Forward declarations for all lifted functions 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 1 #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 }; 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(table.Entries.num()); ++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(table.Entries.num()); ++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(table.Entries.num()); idx++) table.Entries[idx].Next = -1; for (idx = 0; idx < ssize(table.Hashes.num()); idx++) table.Hashes[idx] = -1; for (idx = 0; idx < ssize(table.Entries.num()); 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) { table.Entries.remove_at(find_result.EntryIndex); rehash_fast(table); } } template inline void remove_entry(HashTable& table, ssize idx) { table.Entries.remove_at(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(table.Hashes.num()); ++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; } #pragma endregion HashTable #pragma endregion Containers