#ifdef GEN_INTELLISENSE_DIRECTIVES # pragma once # include "printing.hpp" #endif #pragma region Containers template<class TType> struct RemoveConst { typedef TType Type; }; template<class TType> struct RemoveConst<const TType> { typedef TType Type; }; template<class TType> struct RemoveConst<const TType[]> { typedef TType Type[]; }; template<class TType, usize Size> struct RemoveConst<const TType[Size]> { typedef TType Type[Size]; }; template<class TType> using TRemoveConst = typename RemoveConst<TType>::Type; #pragma region Array struct ArrayHeader; template<class Type> struct Array; template<class Type> Array<Type> array_init(AllocatorInfo allocator); template<class Type> Array<Type> array_init_reserve(AllocatorInfo allocator, ssize capacity); template<class Type> usize array_grow_formula(ssize value); template<class Type> bool append(Array<Type>& array, Array<Type> other); template<class Type> bool append(Array<Type>& array, Type value); template<class Type> bool append(Array<Type>& array, Type* items, usize item_num); template<class Type> bool append_at(Array<Type>& array, Type item, usize idx); template<class Type> bool append_at(Array<Type>& array, Type* items, usize item_num, usize idx); template<class Type> Type& back(Array<Type>& array); template<class Type> void clear(Array<Type>& array); template<class Type> bool fill(Array<Type>& array, usize begin, usize end, Type value); template<class Type> void free(Array<Type>& array); template<class Type> bool grow(Array<Type>& array, usize min_capacity); template<class Type> usize num(Array<Type>& array); template<class Type> void pop(Array<Type>& array); template<class Type> void remove_at(Array<Type>& array, usize idx); template<class Type> bool reserve(Array<Type>& array, usize new_capacity); template<class Type> bool resize(Array<Type>& array, usize num); template<class Type> bool set_capacity(Array<Type>& array, usize new_capacity); template<class Type> ArrayHeader* get_header(Array<Type>& array); struct ArrayHeader { AllocatorInfo Allocator; usize Capacity; usize Num; }; template<class Type> struct Array { Type* Data; #if 1 #pragma region Member Mapping forceinline static Array init(AllocatorInfo allocator) { return GEN_NS array_init<Type>(allocator); } forceinline static Array init_reserve(AllocatorInfo allocator, ssize capacity) { return GEN_NS array_init_reserve<Type>(allocator, capacity); } forceinline static usize grow_formula(ssize value) { return GEN_NS array_grow_formula<Type>(value); } forceinline bool append(Array other) { return GEN_NS append<Type>(*this, other); } forceinline bool append(Type value) { return GEN_NS append<Type>(*this, value); } forceinline bool append(Type* items, usize item_num) { return GEN_NS append<Type>(*this, items, item_num); } forceinline bool append_at(Type item, usize idx) { return GEN_NS append_at<Type>(*this, item, idx); } forceinline bool append_at(Type* items, usize item_num, usize idx) { return GEN_NS append_at<Type>(*this, items, item_num, idx); } forceinline Type& back() { return GEN_NS back<Type>(*this); } forceinline void clear() { GEN_NS clear<Type>(*this); } forceinline bool fill(usize begin, usize end, Type value) { return GEN_NS fill<Type>(*this, begin, end, value); } forceinline void free() { GEN_NS free<Type>(*this); } forceinline ArrayHeader* get_header() { return GEN_NS get_header<Type>(*this); } forceinline bool grow(usize min_capacity) { return GEN_NS grow<Type>(*this, min_capacity); } forceinline usize num() { return GEN_NS num<Type>(*this); } forceinline void pop() { GEN_NS pop<Type>(*this); } forceinline void remove_at(usize idx) { GEN_NS remove_at<Type>(*this, idx); } forceinline bool reserve(usize new_capacity) { return GEN_NS reserve<Type>(*this, new_capacity); } forceinline bool resize(usize num) { return GEN_NS resize<Type>(*this, num); } forceinline bool set_capacity(usize new_capacity) { return GEN_NS set_capacity<Type>(*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<class Type> inline Array<Type> array_init(AllocatorInfo allocator) { return array_init_reserve<Type>(allocator, array_grow_formula<Type>(0)); } template<class Type> inline Array<Type> 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)}; } template<class Type> inline usize array_grow_formula(ssize value) { return 2 * value + 8; } template<class Type> inline bool append(Array<Type>& array, Array<Type> other) { return append(array, other, num(other)); } template<class Type> inline bool append(Array<Type>& 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.Data[header->Num] = value; header->Num++; return true; } template<class Type> inline bool append(Array<Type>& 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<class Type> inline bool append_at(Array<Type>& 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.Data + idx; mem_move(target + 1, target, (header->Num - idx) * sizeof(Type)); header->Num++; return true; } template<class Type> inline bool append_at(Array<Type>& 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<class Type> inline Type& back(Array<Type>& array) { ArrayHeader* header = get_header(array); return array.Data[header->Num - 1]; } template<class Type> inline void clear(Array<Type>& array) { ArrayHeader* header = get_header(array); header->Num = 0; } template<class Type> inline bool fill(Array<Type>& 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.Data[idx] = value; } return true; } template<class Type> inline void free(Array<Type>& array) { ArrayHeader* header = get_header(array); gen::free(header->Allocator, header); array.Data = nullptr; } template<class Type> inline ArrayHeader* get_header(Array<Type>& array) { using NonConstType = TRemoveConst<Type>; return rcast(ArrayHeader*, const_cast<NonConstType*>(array.Data)) - 1; } template<class Type> inline bool grow(Array<Type>& array, usize min_capacity) { ArrayHeader* header = get_header(array); usize new_capacity = array_grow_formula<Type>(header->Capacity); if (new_capacity < min_capacity) new_capacity = min_capacity; return set_capacity(array, new_capacity); } template<class Type> inline usize num(Array<Type>& array) { return get_header(array)->Num; } template<class Type> inline void pop(Array<Type>& array) { ArrayHeader* header = get_header(array); GEN_ASSERT(header->Num > 0); header->Num--; } template<class Type> inline void remove_at(Array<Type>& array, usize idx) { ArrayHeader* header = get_header(array); GEN_ASSERT(idx < header->Num); mem_move(array.Data + idx, array.Data + idx + 1, sizeof(Type) * (header->Num - idx - 1)); header->Num--; } template<class Type> inline bool reserve(Array<Type>& array, usize new_capacity) { ArrayHeader* header = get_header(array); if (header->Capacity < new_capacity) return set_capacity(array, new_capacity); return true; } template<class Type> inline bool resize(Array<Type>& 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<class Type> inline bool set_capacity(Array<Type>& 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::free(header->Allocator, header); array.Data = rcast(Type*, new_header + 1); return true; } #pragma endregion Array // TODO(Ed) : This thing needs ALOT of work. template<typename Type> struct HashTable { struct FindResult { ssize HashIndex; ssize PrevIndex; ssize EntryIndex; }; struct Entry { u64 Key; ssize Next; Type Value; }; static constexpr f32 CriticalLoadScale = 0.7f; static HashTable init( AllocatorInfo allocator ) { HashTable<Type> result = init_reserve(allocator, 8); return result; } static HashTable init_reserve( AllocatorInfo allocator, usize num ) { HashTable<Type> result = { { nullptr }, { nullptr } }; result.Hashes = Array<ssize>::init_reserve( allocator, num ); result.Hashes.get_header()->Num = num; result.Hashes.resize( num ); result.Hashes.fill( 0, num, -1); result.Entries = Array<Entry>::init_reserve( allocator, num ); return result; } void clear( void ) { Entries.clear(); Hashes.fill( 0, Hashes.num(), -1); } void destroy( void ) { if ( Hashes && Hashes.get_header()->Capacity ) { Hashes.free(); Entries.free(); } } Type* get( u64 key ) { ssize idx = find( key ).EntryIndex; if ( idx >= 0 ) return & Entries[ idx ].Value; return nullptr; } using MapProc = void (*)( u64 key, Type value ); void map( MapProc map_proc ) { GEN_ASSERT_NOT_NULL( map_proc ); for ( ssize idx = 0; idx < ssize(Entries.num()); ++idx ) { map_proc( Entries[ idx ].Key, Entries[ idx ].Value ); } } using MapMutProc = void (*)( u64 key, Type* value ); void map_mut( MapMutProc map_proc ) { GEN_ASSERT_NOT_NULL( map_proc ); for ( ssize idx = 0; idx < ssize(Entries.num()); ++idx ) { map_proc( Entries[ idx ].Key, & Entries[ idx ].Value ); } } void grow() { ssize new_num = Array<Entry>::grow_formula( Entries.num() ); rehash( new_num ); } void rehash( ssize new_num ) { ssize last_added_index; HashTable<Type> new_ht = init_reserve( Hashes.get_header()->Allocator, new_num ); for ( ssize idx = 0; idx < ssize(Entries.num()); ++idx ) { FindResult find_result; Entry& entry = Entries[ idx ]; find_result = new_ht.find( entry.Key ); last_added_index = new_ht.add_entry( 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(); *this = new_ht; } void rehash_fast() { ssize idx; for ( idx = 0; idx < ssize(Entries.num()); idx++ ) Entries[ idx ].Next = -1; for ( idx = 0; idx < ssize(Hashes.num()); idx++ ) Hashes[ idx ] = -1; for ( idx = 0; idx < ssize(Entries.num()); idx++ ) { Entry* entry; FindResult find_result; entry = & Entries[ idx ]; find_result = find( entry->Key ); if ( find_result.PrevIndex < 0 ) Hashes[ find_result.HashIndex ] = idx; else Entries[ find_result.PrevIndex ].Next = idx; } } void remove( u64 key ) { FindResult find_result = find( key); if ( find_result.EntryIndex >= 0 ) { Entries.remove_at( find_result.EntryIndex ); rehash_fast(); } } void remove_entry( ssize idx ) { Entries.remove_at( idx ); } void set( u64 key, Type value ) { ssize idx; FindResult find_result; if ( full() ) grow(); find_result = find( key ); if ( find_result.EntryIndex >= 0 ) { idx = find_result.EntryIndex; } else { idx = add_entry( key ); if ( find_result.PrevIndex >= 0 ) { Entries[ find_result.PrevIndex ].Next = idx; } else { Hashes[ find_result.HashIndex ] = idx; } } Entries[ idx ].Value = value; if ( full() ) grow(); } ssize slot( u64 key ) { for ( ssize idx = 0; idx < ssize(Hashes.num()); ++idx ) if ( Hashes[ idx ] == key ) return idx; return -1; } Array< ssize> Hashes; Array< Entry> Entries; protected: ssize add_entry( u64 key ) { ssize idx; Entry entry = { key, -1 }; idx = Entries.num(); Entries.append( entry ); return idx; } FindResult find( u64 key ) { FindResult result = { -1, -1, -1 }; if ( Hashes.num() > 0 ) { result.HashIndex = key % Hashes.num(); result.EntryIndex = Hashes[ result.HashIndex ]; while ( result.EntryIndex >= 0 ) { if ( Entries[ result.EntryIndex ].Key == key ) break; result.PrevIndex = result.EntryIndex; result.EntryIndex = Entries[ result.EntryIndex ].Next; } } return result; } b32 full() { usize critical_load = usize( CriticalLoadScale * f32(Hashes.num()) ); b32 result = Entries.num() > critical_load; return result; } }; #pragma endregion Containers