Compare commits

...

6 Commits

Author SHA1 Message Date
Ed_
056a5863b8 for the future... 2024-11-30 14:34:28 -05:00
Ed_
79eb5f1f76 strings done 2024-11-30 14:13:30 -05:00
Ed_
c6cb583518 Hashtable done 2024-11-30 13:31:59 -05:00
Ed_
34eec66f35 Array done 2024-11-30 13:14:47 -05:00
Ed_
4137ebfbd8 pool done (see previous commits for context) 2024-11-30 12:27:54 -05:00
Ed_
5958dd2055 Did arena and fixedarena changes (for reducing usage of member procs) 2024-11-30 12:16:01 -05:00
11 changed files with 1300 additions and 1114 deletions

View File

@ -37,7 +37,8 @@
"propidl.h": "c", "propidl.h": "c",
"android_native_app_glue.h": "c", "android_native_app_glue.h": "c",
"raylib.h": "c", "raylib.h": "c",
"*.m": "cpp" "*.m": "cpp",
"atomic": "cpp"
}, },
"C_Cpp.intelliSenseEngineFallback": "disabled", "C_Cpp.intelliSenseEngineFallback": "disabled",
"mesonbuild.configureOnOpen": true, "mesonbuild.configureOnOpen": true,

View File

@ -0,0 +1,23 @@
#ifdef GEN_INTELLISENSE_DIRECTIVES
# pragma once
# include "../gen.hpp"
#endif
/*
Explicitly generates a resolved definition of a cpp template definition.
TODO(Ed): Needs implementing for the C-library variant.
TODO(Ed): We need a non <token> syntax subst implemtnation for Strings for this to work. It must subst keywords directly based on template parameter names.
This is only meant to be used on relatively trivial templates, where the type or numeric is mostly a 'duck' type.
It cannot parse complex template parameters.
The varadic args should correspond 1:1 with the type of objects the generator expects from the template's parameters.alignas.
*/
CodeOperator gen_operator_template( CodeTemplate template, ... );
CodeFn gen_func_template( CodeTemplate template, ... );
Code gen_class_struct_template( CodeTemplate template, ... );
Code gen_template( CodeTemplate template, ... );
Code gen_template( StrC template, StrC instantiation );

View File

@ -372,7 +372,7 @@ AllocatorInfo get_string_allocator( s32 str_length )
{ {
Arena* last = & StringArenas.back(); Arena* last = & StringArenas.back();
usize size_req = str_length + sizeof(String::Header) + sizeof(char*); usize size_req = str_length + sizeof(StringHeader) + sizeof(char*);
if ( last->TotalUsed + ssize(size_req) > last->TotalSize ) if ( last->TotalUsed + ssize(size_req) > last->TotalSize )
{ {

View File

@ -16,7 +16,7 @@ ssize token_fmt_va( char* buf, usize buf_size, s32 num_tokens, va_list va )
local_persist local_persist
char tok_map_mem[ TokenFmt_TokenMap_MemSize ]; char tok_map_mem[ TokenFmt_TokenMap_MemSize ];
init_from_memory( tok_map_arena, tok_map_mem, sizeof(tok_map_mem) ); tok_map_arena = init_from_memory( tok_map_mem, sizeof(tok_map_mem) );
tok_map = HashTable<StrC>::init( tok_map_arena ); tok_map = HashTable<StrC>::init( tok_map_arena );
s32 left = num_tokens - 1; s32 left = num_tokens - 1;

View File

@ -133,7 +133,7 @@ internal
void init() void init()
{ {
Tokens = Array<Token>::init_reserve( LexArena Tokens = Array<Token>::init_reserve( LexArena
, ( LexAllocator_Size - sizeof( Array<Token>::Header ) ) / sizeof(Token) , ( LexAllocator_Size - sizeof( ArrayHeader ) ) / sizeof(Token)
); );
defines_map_arena = Arena_256KB::init(); defines_map_arena = Arena_256KB::init();
@ -713,7 +713,7 @@ Code parse_class_struct( TokType which, bool inplace_def = false )
local_persist local_persist
char interface_arr_mem[ kilobytes(4) ] {0}; char interface_arr_mem[ kilobytes(4) ] {0};
Array<CodeType> interfaces; { Array<CodeType> interfaces; {
Arena arena; init_from_memory(arena, interface_arr_mem, kilobytes(4) ); Arena arena = init_from_memory( interface_arr_mem, kilobytes(4) );
Array<CodeType>::init_reserve( arena, 4 ); Array<CodeType>::init_reserve( arena, 4 );
} }

View File

@ -1,5 +1,6 @@
#ifdef GEN_INTELLISENSE_DIRECTIVES #ifdef GEN_INTELLISENSE_DIRECTIVES
# pragma once # pragma once
# include "platform.hpp"
# include "macros.hpp" # include "macros.hpp"
#endif #endif

View File

@ -13,539 +13,607 @@ template<class TType, usize Size> struct RemoveConst<const TType[Size]> { typede
template<class TType> template<class TType>
using TRemoveConst = typename RemoveConst<TType>::Type; using TRemoveConst = typename RemoveConst<TType>::Type;
template<class Type> #pragma region Array
struct Array struct ArrayHeader;
{ template<class Type> struct Array;
struct Header
{ 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; AllocatorInfo Allocator;
usize Capacity; usize Capacity;
usize Num; usize Num;
}; };
static template<class Type>
Array init( AllocatorInfo allocator ) struct Array
{ {
return init_reserve( allocator, grow_formula(0) ); Type* Data;
}
static #if 1
Array init_reserve( AllocatorInfo allocator, ssize capacity ) #pragma region Member Mapping
{ forceinline static Array init(AllocatorInfo allocator) { return GEN_NS array_init<Type>(allocator); }
Header* header = rcast( Header*, alloc( allocator, sizeof(Header) + sizeof(Type) * capacity )); 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); }
if ( header == nullptr ) forceinline bool append(Array other) { return GEN_NS append<Type>(*this, other); }
return { nullptr }; 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->Allocator = allocator;
header->Capacity = capacity; header->Capacity = capacity;
header->Num = 0; header->Num = 0;
return { rcast( Type*, header + 1) }; return {rcast(Type*, header + 1)};
} }
static template<class Type> inline
usize grow_formula( usize value ) usize array_grow_formula(ssize value) {
{
return 2 * value + 8; return 2 * value + 8;
} }
bool append( Array other ) template<class Type> inline
{ bool append(Array<Type>& array, Array<Type> other) {
return append( other, other.num() ); return append(array, other, num(other));
} }
bool append( Type value ) template<class Type> inline
{ bool append(Array<Type>& array, Type value)
Header* header = get_header(); {
ArrayHeader* header = get_header(array);
if ( header->Num == header->Capacity ) if (header->Num == header->Capacity)
{ {
if ( ! grow( header->Capacity )) if (!grow(array, header->Capacity))
return false; return false;
header = get_header(array);
header = get_header();
} }
Data[ header->Num ] = value; array.Data[header->Num] = value;
header->Num++; header->Num++;
return true; return true;
} }
bool append( Type* items, usize item_num ) template<class Type> inline
{ bool append(Array<Type>& array, Type* items, usize item_num)
Header* header = get_header(); {
ArrayHeader* header = get_header(array);
if ( header->Num + item_num > header->Capacity ) if (header->Num + item_num > header->Capacity)
{ {
if ( ! grow( header->Capacity + item_num )) if (!grow(array, header->Capacity + item_num))
return false; return false;
header = get_header(array);
header = get_header();
} }
mem_copy( Data + header->Num, items, item_num * sizeof(Type) ); mem_copy(array.Data + header->Num, items, item_num * sizeof(Type));
header->Num += item_num; header->Num += item_num;
return true; return true;
} }
bool append_at( Type item, usize idx ) template<class Type> inline
{ bool append_at(Array<Type>& array, Type item, usize idx)
Header* header = get_header(); {
ArrayHeader* header = get_header(array);
if ( idx >= header->Num ) if (idx >= header->Num)
idx = header->Num - 1; idx = header->Num - 1;
if ( idx < 0 ) if (idx < 0)
idx = 0; idx = 0;
if ( header->Capacity < header->Num + 1 ) if (header->Capacity < header->Num + 1)
{ {
if ( ! grow( header->Capacity + 1 )) if (!grow(array, header->Capacity + 1))
return false; return false;
header = get_header(); header = get_header(array);
} }
Type* target = Data + idx; Type* target = array.Data + idx;
mem_move( target + 1, target, (header->Num - idx) * sizeof(Type) ); mem_move(target + 1, target, (header->Num - idx) * sizeof(Type));
header->Num++; header->Num++;
return true; 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);
} }
bool append_at( Type* items, usize item_num, usize idx ) if (item_num > header->Capacity)
{ {
Header* header = get_header(); if (!grow(array, header->Capacity + item_num))
if ( idx >= header->Num )
{
return append( items, item_num );
}
if ( item_num > header->Capacity )
{
if ( ! grow( header->Capacity + item_num ) )
return false; return false;
header = get_header(); header = get_header(array);
} }
Type* target = Data + idx + item_num; Type* target = array.Data + idx + item_num;
Type* src = Data + idx; Type* src = array.Data + idx;
mem_move( target, src, (header->Num - idx) * sizeof(Type) ); mem_move(target, src, (header->Num - idx) * sizeof(Type));
mem_copy( src, items, item_num * sizeof(Type) ); mem_copy(src, items, item_num * sizeof(Type));
header->Num += item_num; header->Num += item_num;
return true; return true;
} }
Type& back( void ) template<class Type> inline
{ Type& back(Array<Type>& array) {
Header& header = * get_header(); ArrayHeader* header = get_header(array);
return Data[ header.Num - 1 ]; return array.Data[header->Num - 1];
} }
void clear( void ) template<class Type> inline
{ void clear(Array<Type>& array) {
Header& header = * get_header(); ArrayHeader* header = get_header(array);
header.Num = 0; header->Num = 0;
} }
bool fill( usize begin, usize end, Type value ) template<class Type> inline
{ bool fill(Array<Type>& array, usize begin, usize end, Type value)
Header& header = * get_header(); {
ArrayHeader* header = get_header(array);
if ( begin < 0 || end > header.Num ) if (begin < 0 || end > header->Num)
return false; return false;
for ( ssize idx = ssize(begin); idx < ssize(end); idx++ ) for (ssize idx = ssize(begin); idx < ssize(end); idx++)
{ {
Data[ idx ] = value; array.Data[idx] = value;
} }
return true; return true;
} }
void free( void ) template<class Type> inline
{ void free(Array<Type>& array) {
Header& header = * get_header(); ArrayHeader* header = get_header(array);
gen::free( header.Allocator, &header ); gen::free(header->Allocator, header);
Data = nullptr; array.Data = nullptr;
} }
Header* get_header( void ) template<class Type> inline
{ ArrayHeader* get_header(Array<Type>& array) {
using NonConstType = TRemoveConst< Type >; using NonConstType = TRemoveConst<Type>;
return rcast( Header*, const_cast<NonConstType*>(Data) ) - 1 ; return rcast(ArrayHeader*, const_cast<NonConstType*>(array.Data)) - 1;
} }
bool grow( usize min_capacity ) template<class Type> inline
{ bool grow(Array<Type>& array, usize min_capacity)
Header& header = * get_header(); {
usize new_capacity = grow_formula( header.Capacity ); ArrayHeader* header = get_header(array);
usize new_capacity = array_grow_formula<Type>(header->Capacity);
if ( new_capacity < min_capacity ) if (new_capacity < min_capacity)
new_capacity = min_capacity; new_capacity = min_capacity;
return set_capacity( new_capacity ); return set_capacity(array, new_capacity);
} }
usize num( void ) template<class Type> inline
{ usize num(Array<Type>& array) {
return get_header()->Num; return get_header(array)->Num;
} }
void pop( void ) template<class Type> inline
{ void pop(Array<Type>& array) {
Header& header = * get_header(); ArrayHeader* header = get_header(array);
GEN_ASSERT(header->Num > 0);
GEN_ASSERT( header.Num > 0 );
header.Num--;
}
void remove_at( usize idx )
{
Header* header = get_header();
GEN_ASSERT( idx < header->Num );
mem_move( header + idx, header + idx + 1, sizeof( Type ) * ( header->Num - idx - 1 ) );
header->Num--; header->Num--;
} }
bool reserve( usize new_capacity ) template<class Type> inline
{ void remove_at(Array<Type>& array, usize idx)
Header& header = * get_header(); {
ArrayHeader* header = get_header(array);
GEN_ASSERT(idx < header->Num);
if ( header.Capacity < new_capacity ) mem_move(array.Data + idx, array.Data + idx + 1, sizeof(Type) * (header->Num - idx - 1));
return set_capacity( new_capacity ); 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; return true;
} }
bool resize( usize num ) template<class Type> inline
{ bool resize(Array<Type>& array, usize num)
Header* header = get_header(); {
ArrayHeader* header = get_header(array);
if ( header->Capacity < num ) if (header->Capacity < num) {
{ if (!grow(array, num))
if ( ! grow( num ) )
return false; return false;
header = get_header(array);
header = get_header();
} }
header->Num = num; header->Num = num;
return true; return true;
} }
bool set_capacity( usize new_capacity ) template<class Type> inline
{ bool set_capacity(Array<Type>& array, usize new_capacity)
Header& header = * get_header(); {
ArrayHeader* header = get_header(array);
if ( new_capacity == header.Capacity ) if (new_capacity == header->Capacity)
return true; return true;
if ( new_capacity < header.Num ) if (new_capacity < header->Num)
{ {
// Already have the memory, mine as well keep it. header->Num = new_capacity;
header.Num = new_capacity;
return true; return true;
} }
ssize size = sizeof( Header ) + sizeof( Type ) * new_capacity; ssize size = sizeof(ArrayHeader) + sizeof(Type) * new_capacity;
Header* new_header = rcast( Header*, alloc( header.Allocator, size ) ); ArrayHeader* new_header = rcast(ArrayHeader*, alloc(header->Allocator, size));
if ( new_header == nullptr ) if (new_header == nullptr)
return false; return false;
mem_move( new_header, &header, sizeof( Header ) + sizeof( Type ) * header.Num ); mem_move(new_header, header, sizeof(ArrayHeader) + sizeof(Type) * header->Num);
new_header->Capacity = new_capacity; new_header->Capacity = new_capacity;
gen::free( header.Allocator, &header ); GEN_NS free(header->Allocator, header);
Data = rcast( Type*, new_header + 1); array.Data = rcast(Type*, new_header + 1);
return true; return true;
} }
#pragma endregion Array
Type* Data;
operator Type*()
{
return Data;
}
operator Type const*() const
{
return Data;
}
// For-range based support
Type* begin()
{
return Data;
}
Type* end()
{
return Data + get_header()->Num;
}
};
// TODO(Ed) : This thing needs ALOT of work. // TODO(Ed) : This thing needs ALOT of work.
#pragma region HashTable
template<class Type> struct HashTable;
struct HashTableFindResult {
ssize HashIndex;
ssize PrevIndex;
ssize EntryIndex;
};
template<class Type>
struct HashTableEntry {
u64 Key;
ssize Next;
Type Value;
};
// Forward declarations for all lifted functions
template<class Type> HashTable<Type> hashtable_init(AllocatorInfo allocator);
template<class Type> HashTable<Type> hashtable_init_reserve(AllocatorInfo allocator, usize num);
template<class Type> void clear(HashTable<Type>& table);
template<class Type> void destroy(HashTable<Type>& table);
template<class Type> Type* get(HashTable<Type>& table, u64 key);
template<class Type> void grow(HashTable<Type>& table);
template<class Type> void rehash(HashTable<Type>& table, ssize new_num);
template<class Type> void rehash_fast(HashTable<Type>& table);
template<class Type> void remove(HashTable<Type>& table, u64 key);
template<class Type> void remove_entry(HashTable<Type>& table, ssize idx);
template<class Type> void set(HashTable<Type>& table, u64 key, Type value);
template<class Type> ssize slot(HashTable<Type>& table, u64 key);
template<class Type> ssize add_entry(HashTable<Type>& table, u64 key);
template<class Type> HashTableFindResult find(HashTable<Type>& table, u64 key);
template<class Type> bool full(HashTable<Type>& table);
template<class Type> void map(HashTable<Type>& table, void (*map_proc)(u64 key, Type value));
template<class Type> void map_mut(HashTable<Type>& table, void (*map_proc)(u64 key, Type* value));
template<typename Type> template<typename Type>
struct HashTable 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 constexpr f32 CriticalLoadScale = 0.7f;
static Array<ssize> Hashes;
HashTable init( AllocatorInfo allocator ) Array<HashTableEntry<Type>> Entries;
{
HashTable<Type> result = init_reserve(allocator, 8);
return result;
}
static #if 1
HashTable init_reserve( AllocatorInfo allocator, usize num ) #pragma region Member Mapping
{ forceinline static HashTable init(AllocatorInfo allocator) { return GEN_NS hashtable_init<Type>(allocator); }
forceinline static HashTable init_reserve(AllocatorInfo allocator, usize num) { return GEN_NS hashtable_init_reserve<Type>(allocator, num); }
forceinline void clear() { GEN_NS clear<Type>(*this); }
forceinline void destroy() { GEN_NS destroy<Type>(*this); }
forceinline Type* get(u64 key) { return GEN_NS get<Type>(*this, key); }
forceinline void grow() { GEN_NS grow<Type>(*this); }
forceinline void rehash(ssize new_num) { GEN_NS rehash<Type>(*this, new_num); }
forceinline void rehash_fast() { GEN_NS rehash_fast<Type>(*this); }
forceinline void remove(u64 key) { GEN_NS remove<Type>(*this, key); }
forceinline void remove_entry(ssize idx) { GEN_NS remove_entry<Type>(*this, idx); }
forceinline void set(u64 key, Type value) { GEN_NS set<Type>(*this, key, value); }
forceinline ssize slot(u64 key) { return GEN_NS slot<Type>(*this, key); }
forceinline void map(void (*proc)(u64, Type)) { GEN_NS map<Type>(*this, proc); }
forceinline void map_mut(void (*proc)(u64, Type*)) { GEN_NS map_mut<Type>(*this, proc); }
#pragma endregion Member Mapping
#endif
};
template<typename Type> inline
HashTable<Type> hashtable_init(AllocatorInfo allocator) {
HashTable<Type> result = hashtable_init_reserve<Type>(allocator, 8);
return result;
}
template<typename Type> inline
HashTable<Type> hashtable_init_reserve(AllocatorInfo allocator, usize num)
{
HashTable<Type> result = { { nullptr }, { nullptr } }; HashTable<Type> result = { { nullptr }, { nullptr } };
result.Hashes = Array<ssize>::init_reserve( allocator, num ); result.Hashes = Array<ssize>::init_reserve(allocator, num);
result.Hashes.get_header()->Num = num; result.Hashes.get_header()->Num = num;
result.Hashes.resize( num ); result.Hashes.resize(num);
result.Hashes.fill( 0, num, -1); result.Hashes.fill(0, num, -1);
result.Entries = Array<Entry>::init_reserve( allocator, num ); result.Entries = Array<HashTableEntry<Type>>::init_reserve(allocator, num);
return result; return result;
} }
void clear( void ) template<typename Type> inline
{ void clear(HashTable<Type>& table) {
Entries.clear(); table.Entries.clear();
Hashes.fill( 0, Hashes.num(), -1); table.Hashes.fill(0, table.Hashes.num(), -1);
} }
void destroy( void ) template<typename Type> inline
{ void destroy(HashTable<Type>& table) {
if ( Hashes && Hashes.get_header()->Capacity ) if (table.Hashes && table.Hashes.get_header()->Capacity) {
{ table.Hashes.free();
Hashes.free(); table.Entries.free();
Entries.free();
}
} }
}
Type* get( u64 key ) template<typename Type> inline
{ Type* get(HashTable<Type>& table, u64 key) {
ssize idx = find( key ).EntryIndex; ssize idx = find(table, key).EntryIndex;
if ( idx >= 0 ) if (idx >= 0)
return & Entries[ idx ].Value; return &table.Entries[idx].Value;
return nullptr; return nullptr;
}
template<typename Type> inline
void map(HashTable<Type>& 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);
} }
}
using MapProc = void (*)( u64 key, Type value ); template<typename Type> inline
void map_mut(HashTable<Type>& table, void (*map_proc)(u64 key, Type* value)) {
GEN_ASSERT_NOT_NULL(map_proc);
void map( MapProc map_proc ) for (ssize idx = 0; idx < ssize(table.Entries.num()); ++idx) {
{ map_proc(table.Entries[idx].Key, &table.Entries[idx].Value);
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 ); template<typename Type> inline
void grow(HashTable<Type>& table) {
ssize new_num = Array<HashTableEntry<Type>>::grow_formula(table.Entries.num());
rehash(table, new_num);
}
void map_mut( MapMutProc map_proc ) template<typename Type> inline
{ void rehash(HashTable<Type>& table, ssize new_num)
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; ssize last_added_index;
HashTable<Type> new_ht = hashtable_init_reserve<Type>(table.Hashes.get_header()->Allocator, new_num);
HashTable<Type> new_ht = init_reserve( Hashes.get_header()->Allocator, new_num ); for (ssize idx = 0; idx < ssize(table.Entries.num()); ++idx)
for ( ssize idx = 0; idx < ssize(Entries.num()); ++idx )
{ {
FindResult find_result; HashTableFindResult find_result;
HashTableEntry<Type>& entry = table.Entries[idx];
Entry& entry = Entries[ idx ]; find_result = find(new_ht, entry.Key);
find_result = new_ht.find( entry.Key ); last_added_index = add_entry(new_ht, entry.Key);
last_added_index = new_ht.add_entry( entry.Key );
if ( find_result.PrevIndex < 0 ) if (find_result.PrevIndex < 0)
new_ht.Hashes[ find_result.HashIndex ] = last_added_index; new_ht.Hashes[find_result.HashIndex] = last_added_index;
else else
new_ht.Entries[ find_result.PrevIndex ].Next = last_added_index; 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].Next = find_result.EntryIndex;
new_ht.Entries[ last_added_index ].Value = entry.Value; new_ht.Entries[last_added_index].Value = entry.Value;
} }
destroy(); destroy(table);
*this = new_ht; table = new_ht;
} }
void rehash_fast() template<typename Type> inline
{ void rehash_fast(HashTable<Type>& table)
{
ssize idx; ssize idx;
for ( idx = 0; idx < ssize(Entries.num()); idx++ ) for (idx = 0; idx < ssize(table.Entries.num()); idx++)
Entries[ idx ].Next = -1; table.Entries[idx].Next = -1;
for ( idx = 0; idx < ssize(Hashes.num()); idx++ ) for (idx = 0; idx < ssize(table.Hashes.num()); idx++)
Hashes[ idx ] = -1; table.Hashes[idx] = -1;
for ( idx = 0; idx < ssize(Entries.num()); idx++ ) for (idx = 0; idx < ssize(table.Entries.num()); idx++)
{ {
Entry* entry; HashTableEntry<Type>* entry;
FindResult find_result; HashTableFindResult find_result;
entry = & Entries[ idx ]; entry = &table.Entries[idx];
find_result = find( entry->Key ); find_result = find(table, entry->Key);
if ( find_result.PrevIndex < 0 ) if (find_result.PrevIndex < 0)
Hashes[ find_result.HashIndex ] = idx; table.Hashes[find_result.HashIndex] = idx;
else else
Entries[ find_result.PrevIndex ].Next = idx; table.Entries[find_result.PrevIndex].Next = idx;
}
} }
}
void remove( u64 key ) template<typename Type> inline
{ void remove(HashTable<Type>& table, u64 key) {
FindResult find_result = find( key); HashTableFindResult find_result = find(table, key);
if ( find_result.EntryIndex >= 0 ) if (find_result.EntryIndex >= 0) {
{ table.Entries.remove_at(find_result.EntryIndex);
Entries.remove_at( find_result.EntryIndex ); rehash_fast(table);
rehash_fast();
}
} }
}
void remove_entry( ssize idx ) template<typename Type> inline
{ void remove_entry(HashTable<Type>& table, ssize idx) {
Entries.remove_at( idx ); table.Entries.remove_at(idx);
} }
void set( u64 key, Type value ) template<typename Type> inline
{ void set(HashTable<Type>& table, u64 key, Type value)
{
ssize idx; ssize idx;
FindResult find_result; HashTableFindResult find_result;
if ( full() ) if (full(table))
grow(); grow(table);
find_result = find( key ); find_result = find(table, key);
if ( find_result.EntryIndex >= 0 ) if (find_result.EntryIndex >= 0) {
{
idx = find_result.EntryIndex; idx = find_result.EntryIndex;
} }
else else
{ {
idx = add_entry( key ); idx = add_entry(table, key);
if ( find_result.PrevIndex >= 0 ) if (find_result.PrevIndex >= 0) {
{ table.Entries[find_result.PrevIndex].Next = idx;
Entries[ find_result.PrevIndex ].Next = idx;
} }
else else {
{ table.Hashes[find_result.HashIndex] = idx;
Hashes[ find_result.HashIndex ] = idx;
} }
} }
Entries[ idx ].Value = value; table.Entries[idx].Value = value;
if ( full() ) if (full(table))
grow(); grow(table);
} }
ssize slot( u64 key ) template<typename Type> inline
{ ssize slot(HashTable<Type>& table, u64 key) {
for ( ssize idx = 0; idx < ssize(Hashes.num()); ++idx ) for (ssize idx = 0; idx < ssize(table.Hashes.num()); ++idx)
if ( Hashes[ idx ] == key ) if (table.Hashes[idx] == key)
return idx; return idx;
return -1; return -1;
} }
Array< ssize> Hashes; template<typename Type> inline
Array< Entry> Entries; ssize add_entry(HashTable<Type>& table, u64 key) {
protected:
ssize add_entry( u64 key )
{
ssize idx; ssize idx;
Entry entry = { key, -1 }; HashTableEntry<Type> entry = { key, -1 };
idx = Entries.num(); idx = table.Entries.num();
Entries.append( entry ); table.Entries.append(entry);
return idx; return idx;
} }
FindResult find( u64 key ) template<typename Type> inline
{ HashTableFindResult find(HashTable<Type>& table, u64 key)
FindResult result = { -1, -1, -1 }; {
HashTableFindResult result = { -1, -1, -1 };
if ( Hashes.num() > 0 ) if (table.Hashes.num() > 0)
{ {
result.HashIndex = key % Hashes.num(); result.HashIndex = key % table.Hashes.num();
result.EntryIndex = Hashes[ result.HashIndex ]; result.EntryIndex = table.Hashes[result.HashIndex];
while ( result.EntryIndex >= 0 ) while (result.EntryIndex >= 0)
{ {
if ( Entries[ result.EntryIndex ].Key == key ) if (table.Entries[result.EntryIndex].Key == key)
break; break;
result.PrevIndex = result.EntryIndex; result.PrevIndex = result.EntryIndex;
result.EntryIndex = Entries[ result.EntryIndex ].Next; result.EntryIndex = table.Entries[result.EntryIndex].Next;
} }
} }
return result; return result;
} }
b32 full() template<typename Type> inline
{ bool full(HashTable<Type>& table) {
usize critical_load = usize( CriticalLoadScale * f32(Hashes.num()) ); usize critical_load = usize(HashTable<Type>::CriticalLoadScale * f32(table.Hashes.num()));
b32 result = Entries.num() > critical_load; b32 result = table.Entries.num() > critical_load;
return result; return result;
} }
}; #pragma endregion HashTable
#pragma endregion Containers #pragma endregion Containers

View File

@ -334,7 +334,7 @@ ssize virtual_memory_page_size( ssize* alignment_out )
#pragma endregion VirtualMemory #pragma endregion VirtualMemory
void* Arena::allocator_proc( void* allocator_data, AllocType type, ssize size, ssize alignment, void* old_memory, ssize old_size, u64 flags ) void* arena_allocator_proc( void* allocator_data, AllocType type, ssize size, ssize alignment, void* old_memory, ssize old_size, u64 flags )
{ {
Arena* arena = rcast(Arena*, allocator_data); Arena* arena = rcast(Arena*, allocator_data);
void* ptr = NULL; void* ptr = NULL;
@ -384,7 +384,7 @@ void* Arena::allocator_proc( void* allocator_data, AllocType type, ssize size, s
return ptr; return ptr;
} }
void* Pool::allocator_proc( void* allocator_data, AllocType type, ssize size, ssize alignment, void* old_memory, ssize old_size, u64 flags ) void* pool_allocator_proc( void* allocator_data, AllocType type, ssize size, ssize alignment, void* old_memory, ssize old_size, u64 flags )
{ {
Pool* pool = rcast( Pool*, allocator_data); Pool* pool = rcast( Pool*, allocator_data);
void* ptr = NULL; void* ptr = NULL;
@ -457,7 +457,7 @@ void* Pool::allocator_proc( void* allocator_data, AllocType type, ssize size, ss
return ptr; return ptr;
} }
Pool Pool::init_align( AllocatorInfo backing, ssize num_blocks, ssize block_size, ssize block_align ) Pool pool_init_align( AllocatorInfo backing, ssize num_blocks, ssize block_size, ssize block_align )
{ {
Pool pool = {}; Pool pool = {};
@ -495,16 +495,16 @@ Pool Pool::init_align( AllocatorInfo backing, ssize num_blocks, ssize block_size
return pool; return pool;
} }
void Pool::clear() void clear(Pool& pool)
{ {
ssize actual_block_size, block_index; ssize actual_block_size, block_index;
void* curr; void* curr;
uptr* end; uptr* end;
actual_block_size = BlockSize + BlockAlign; actual_block_size = pool.BlockSize + pool.BlockAlign;
curr = PhysicalStart; curr = pool.PhysicalStart;
for ( block_index = 0; block_index < NumBlocks - 1; block_index++ ) for ( block_index = 0; block_index < pool.NumBlocks - 1; block_index++ )
{ {
uptr* next = ( uptr* ) curr; uptr* next = ( uptr* ) curr;
*next = ( uptr ) curr + actual_block_size; *next = ( uptr ) curr + actual_block_size;
@ -514,7 +514,7 @@ void Pool::clear()
end = ( uptr* ) curr; end = ( uptr* ) curr;
*end = ( uptr ) NULL; *end = ( uptr ) NULL;
FreeList = PhysicalStart; pool.FreeList = pool.PhysicalStart;
} }
#pragma endregion Memory #pragma endregion Memory

View File

@ -170,127 +170,180 @@ b32 gen_vm_purge( VirtualMemory vm );
//! Retrieve VM's page size and alignment. //! Retrieve VM's page size and alignment.
ssize gen_virtual_memory_page_size( ssize* alignment_out ); ssize gen_virtual_memory_page_size( ssize* alignment_out );
#pragma region Arena
struct Arena; struct Arena;
void init_from_memory( Arena& arena, void* start, ssize size );
struct Arena AllocatorInfo allocator_info( Arena& arena );
{
static
void* allocator_proc( void* allocator_data, AllocType type, ssize size, ssize alignment, void* old_memory, ssize old_size, u64 flags );
//forceinline static // Remove static keyword and rename allocator_proc
//Arena init_from_memory( void* start, ssize size ) { void* arena_allocator_proc(void* allocator_data, AllocType type, ssize size, ssize alignment, void* old_memory, ssize old_size, u64 flags);
// Arena result; GEN_NS init_from_memory( result, start, size );
// return result;
//}
static // Add these declarations after the Arena struct
Arena init_from_allocator( AllocatorInfo backing, ssize size ) Arena init_from_allocator(AllocatorInfo backing, ssize size);
{ Arena init_from_memory( void* start, ssize size );
Arena result = Arena init_sub(Arena& parent, ssize size);
{ ssize alignment_of(Arena& arena, ssize alignment);
backing,
alloc( backing, size),
size,
0,
0
};
return result;
}
static
Arena init_sub( Arena& parent, ssize size )
{
return init_from_allocator( parent.Backing, size );
}
ssize alignment_of( ssize alignment )
{
ssize alignment_offset, result_pointer, mask;
GEN_ASSERT( is_power_of_two( alignment ) );
alignment_offset = 0;
result_pointer = (ssize) PhysicalStart + TotalUsed;
mask = alignment - 1;
if ( result_pointer & mask )
alignment_offset = alignment - ( result_pointer & mask );
return alignment_offset;
}
// This id is defined by Unreal for asserts // This id is defined by Unreal for asserts
#pragma push_macro("check") #pragma push_macro("check")
#undef check #undef check
void check() void check(Arena& arena);
{
GEN_ASSERT( TempCount == 0 );
}
#pragma pop_macro("check") #pragma pop_macro("check")
void free() void free(Arena& arena);
{ ssize size_remaining(Arena& arena, ssize alignment);
if ( Backing.Proc )
{
gen::free( Backing, PhysicalStart );
PhysicalStart = nullptr;
}
}
ssize size_remaining( ssize alignment )
{
ssize result = TotalSize - ( TotalUsed + alignment_of( alignment ) );
return result;
}
struct Arena
{
AllocatorInfo Backing; AllocatorInfo Backing;
void* PhysicalStart; void* PhysicalStart;
ssize TotalSize; ssize TotalSize;
ssize TotalUsed; ssize TotalUsed;
ssize TempCount; ssize TempCount;
operator AllocatorInfo() { return { allocator_proc, this }; } #if 1
#pragma region Member Mapping
forceinline operator AllocatorInfo() { return GEN_NS allocator_info(* this); }
forceinline static void* allocator_proc( void* allocator_data, AllocType type, ssize size, ssize alignment, void* old_memory, ssize old_size, u64 flags ) { return GEN_NS arena_allocator_proc( allocator_data, type, size, alignment, old_memory, old_size, flags ); }
forceinline static Arena init_from_memory( void* start, ssize size ) { return GEN_NS init_from_memory( start, size ); }
forceinline static Arena init_from_allocator( AllocatorInfo backing, ssize size ) { return GEN_NS init_from_allocator( backing, size ); }
forceinline static Arena init_sub( Arena& parent, ssize size ) { return GEN_NS init_from_allocator( parent.Backing, size ); }
forceinline ssize alignment_of( ssize alignment ) { return GEN_NS alignment_of(* this, alignment); }
forceinline void free() { return GEN_NS free(* this); }
forceinline ssize size_remaining( ssize alignment ) { return GEN_NS size_remaining(* this, alignment); }
// This id is defined by Unreal for asserts
#pragma push_macro("check")
#undef check
forceinline void check() { GEN_NS check(* this); }
#pragma pop_macro("check")
#pragma endregion Member Mapping
#endif
}; };
void init_from_memory( Arena& arena, void* start, ssize size ) inline
AllocatorInfo allocator_info( Arena& arena ) {
return { arena_allocator_proc, &arena };
}
inline
Arena init_from_memory( void* start, ssize size )
{ {
arena = Arena arena = {
{
{ nullptr, nullptr }, { nullptr, nullptr },
start, start,
size, size,
0, 0,
0 0
}; };
return arena;
} }
inline
Arena init_from_allocator(AllocatorInfo backing, ssize size)
{
Arena result =
{
backing,
alloc(backing, size),
size,
0,
0
};
return result;
}
inline
Arena init_sub(Arena& parent, ssize size)
{
return init_from_allocator(parent.Backing, size);
}
inline
ssize alignment_of(Arena& arena, ssize alignment)
{
ssize alignment_offset, result_pointer, mask;
GEN_ASSERT(is_power_of_two(alignment));
alignment_offset = 0;
result_pointer = (ssize)arena.PhysicalStart + arena.TotalUsed;
mask = alignment - 1;
if (result_pointer & mask)
alignment_offset = alignment - (result_pointer & mask);
return alignment_offset;
}
#pragma push_macro("check")
#undef check
inline
void check(Arena& arena)
{
GEN_ASSERT(arena.TempCount == 0);
}
#pragma pop_macro("check")
inline
void free(Arena& arena)
{
if (arena.Backing.Proc)
{
gen::free(arena.Backing, arena.PhysicalStart);
arena.PhysicalStart = nullptr;
}
}
inline
ssize size_remaining(Arena& arena, ssize alignment)
{
ssize result = arena.TotalSize - (arena.TotalUsed + alignment_of(arena, alignment));
return result;
}
#pragma endregion Arena
#pragma region FixedArena
template<s32 Size>
struct FixedArena;
template<s32 Size> AllocatorInfo allocator_info( FixedArena<Size>& fixed_arena );
template<s32 Size> FixedArena<Size> fixed_arena_init();
template<s32 Size> ssize size_remaining(FixedArena<Size>& fixed_arena, ssize alignment);
// Just a wrapper around using an arena with memory associated with its scope instead of from an allocator. // Just a wrapper around using an arena with memory associated with its scope instead of from an allocator.
// Used for static segment or stack allocations. // Used for static segment or stack allocations.
template< s32 Size > template< s32 Size >
struct FixedArena struct FixedArena
{ {
static char memory[Size];
FixedArena init()
{
FixedArena result = { Arena::init_from_memory( result.memory, Size ), {0} };
return result;
}
ssize size_remaining( ssize alignment )
{
return arena.size_remaining( alignment );
}
operator AllocatorInfo()
{
return { Arena::allocator_proc, &arena };
}
Arena arena; Arena arena;
char memory[ Size ];
#if 1
#pragma region Member Mapping
forceinline operator AllocatorInfo() { return GEN_NS allocator_info(* this); }
forceinline static FixedArena init() { FixedArena result; GEN_NS fixed_arena_init<Size>(result); return result; }
forceinline ssize size_remaining(ssize alignment) { GEN_NS size_remaining(*this, alignment); }
#pragma endregion Member Mapping
#endif
}; };
template<s32 Size> inline
AllocatorInfo allocator_info( FixedArena<Size>& fixed_arena ) { return { arena_allocator_proc, & fixed_arena.arena }; }
template<s32 Size> inline
void fixed_arena_init(FixedArena<Size>& result) {
zero_size(& result.memory[0], Size);
result.arena = init_from_memory(& result.memory[0], Size);
}
template<s32 Size> inline
ssize size_remaining(FixedArena<Size>& fixed_arena, ssize alignment) {
return size_remaining(fixed_arena.arena, alignment);
}
using Arena_1KB = FixedArena< kilobytes( 1 ) >; using Arena_1KB = FixedArena< kilobytes( 1 ) >;
using Arena_4KB = FixedArena< kilobytes( 4 ) >; using Arena_4KB = FixedArena< kilobytes( 4 ) >;
using Arena_8KB = FixedArena< kilobytes( 8 ) >; using Arena_8KB = FixedArena< kilobytes( 8 ) >;
@ -303,31 +356,20 @@ using Arena_512KB = FixedArena< kilobytes( 512 ) >;
using Arena_1MB = FixedArena< megabytes( 1 ) >; using Arena_1MB = FixedArena< megabytes( 1 ) >;
using Arena_2MB = FixedArena< megabytes( 2 ) >; using Arena_2MB = FixedArena< megabytes( 2 ) >;
using Arena_4MB = FixedArena< megabytes( 4 ) >; using Arena_4MB = FixedArena< megabytes( 4 ) >;
#pragma endregion FixedArena
#pragma region Pool
struct Pool;
AllocatorInfo allocator_info(Pool& pool);
void* pool_allocator_proc(void* allocator_data, AllocType type, ssize size, ssize alignment, void* old_memory, ssize old_size, u64 flags);
Pool pool_init(AllocatorInfo backing, ssize num_blocks, ssize block_size);
Pool pool_init_align(AllocatorInfo backing, ssize num_blocks, ssize block_size, ssize block_align);
void clear(Pool& pool);
void free(Pool& pool);
struct Pool struct Pool
{ {
static
void* allocator_proc( void* allocator_data, AllocType type, ssize size, ssize alignment, void* old_memory, ssize old_size, u64 flags );
static
Pool init( AllocatorInfo backing, ssize num_blocks, ssize block_size )
{
return init_align( backing, num_blocks, block_size, GEN_DEFAULT_MEMORY_ALIGNMENT );
}
static
Pool init_align( AllocatorInfo backing, ssize num_blocks, ssize block_size, ssize block_align );
void clear();
void free()
{
if ( Backing.Proc )
{
gen::free( Backing, PhysicalStart );
}
}
AllocatorInfo Backing; AllocatorInfo Backing;
void* PhysicalStart; void* PhysicalStart;
void* FreeList; void* FreeList;
@ -336,12 +378,34 @@ struct Pool
ssize TotalSize; ssize TotalSize;
ssize NumBlocks; ssize NumBlocks;
operator AllocatorInfo() #pragma region Member Mapping
{ forceinline operator AllocatorInfo() { return GEN_NS allocator_info(* this); }
return { allocator_proc, this };
} forceinline static void* allocator_proc(void* allocator_data, AllocType type, ssize size, ssize alignment, void* old_memory, ssize old_size, u64 flags) { return GEN_NS pool_allocator_proc(allocator_data, type, size, alignment, old_memory, old_size, flags); }
forceinline static Pool init(AllocatorInfo backing, ssize num_blocks, ssize block_size) { return GEN_NS pool_init(backing, num_blocks, block_size); }
forceinline static Pool init_align(AllocatorInfo backing, ssize num_blocks, ssize block_size, ssize block_align) { return GEN_NS pool_init_align(backing, num_blocks, block_size, block_align); }
forceinline void clear() { GEN_NS clear(* this); }
forceinline void free() { GEN_NS free(* this); }
#pragma endregion
}; };
inline
AllocatorInfo allocator_info(Pool& pool) {
return { pool_allocator_proc, &pool };
}
inline
Pool pool_init(AllocatorInfo backing, ssize num_blocks, ssize block_size) {
return pool_init_align(backing, num_blocks, block_size, GEN_DEFAULT_MEMORY_ALIGNMENT);
}
inline
void free(Pool& pool) {
if(pool.Backing.Proc) {
GEN_NS free(pool.Backing, pool.PhysicalStart);
}
}
#pragma endregion Pool
inline inline
b32 is_power_of_two( ssize x ) { b32 is_power_of_two( ssize x ) {

View File

@ -4,20 +4,9 @@
#endif #endif
#pragma region String #pragma region String
String string_make_length( AllocatorInfo allocator, char const* str, ssize length )
String String::fmt( AllocatorInfo allocator, char* buf, ssize buf_size, char const* fmt, ... )
{ {
va_list va; constexpr ssize header_size = sizeof( StringHeader );
va_start( va, fmt );
str_fmt_va( buf, buf_size, fmt, va );
va_end( va );
return make( allocator, buf );
}
String String::make_length( AllocatorInfo allocator, char const* str, ssize length )
{
constexpr ssize header_size = sizeof( Header );
s32 alloc_size = header_size + length + 1; s32 alloc_size = header_size + length + 1;
void* allocation = alloc( allocator, alloc_size ); void* allocation = alloc( allocator, alloc_size );
@ -25,8 +14,8 @@ String String::make_length( AllocatorInfo allocator, char const* str, ssize leng
if ( allocation == nullptr ) if ( allocation == nullptr )
return { nullptr }; return { nullptr };
Header& StringHeader&
header = * rcast(Header*, allocation); header = * rcast(StringHeader*, allocation);
header = { allocator, length, length }; header = { allocator, length, length };
String result = { rcast( char*, allocation) + header_size }; String result = { rcast( char*, allocation) + header_size };
@ -41,9 +30,9 @@ String String::make_length( AllocatorInfo allocator, char const* str, ssize leng
return result; return result;
} }
String String::make_reserve( AllocatorInfo allocator, ssize capacity ) String string_make_reserve( AllocatorInfo allocator, ssize capacity )
{ {
constexpr ssize header_size = sizeof( Header ); constexpr ssize header_size = sizeof( StringHeader );
s32 alloc_size = header_size + capacity + 1; s32 alloc_size = header_size + capacity + 1;
void* allocation = alloc( allocator, alloc_size ); void* allocation = alloc( allocator, alloc_size );
@ -53,8 +42,8 @@ String String::make_reserve( AllocatorInfo allocator, ssize capacity )
mem_set( allocation, 0, alloc_size ); mem_set( allocation, 0, alloc_size );
Header* StringHeader*
header = rcast(Header*, allocation); header = rcast(StringHeader*, allocation);
header->Allocator = allocator; header->Allocator = allocator;
header->Capacity = capacity; header->Capacity = capacity;
header->Length = 0; header->Length = 0;
@ -62,69 +51,4 @@ String String::make_reserve( AllocatorInfo allocator, ssize capacity )
String result = { rcast(char*, allocation) + header_size }; String result = { rcast(char*, allocation) + header_size };
return result; return result;
} }
String String::fmt_buf( AllocatorInfo allocator, char const* fmt, ... )
{
local_persist thread_local
char buf[ GEN_PRINTF_MAXLEN ] = { 0 };
va_list va;
va_start( va, fmt );
str_fmt_va( buf, GEN_PRINTF_MAXLEN, fmt, va );
va_end( va );
return make( allocator, buf );
}
bool String::append_fmt( char const* fmt, ... )
{
ssize res;
char buf[ GEN_PRINTF_MAXLEN ] = { 0 };
va_list va;
va_start( va, fmt );
res = str_fmt_va( buf, count_of( buf ) - 1, fmt, va ) - 1;
va_end( va );
return append( buf, res );
}
bool String::make_space_for( char const* str, ssize add_len )
{
ssize available = avail_space();
// NOTE: Return if there is enough space left
if ( available >= add_len )
{
return true;
}
else
{
ssize new_len, old_size, new_size;
void* ptr;
void* new_ptr;
AllocatorInfo allocator = get_header().Allocator;
Header* header = nullptr;
new_len = grow_formula( length() + add_len );
ptr = & get_header();
old_size = size_of( Header ) + length() + 1;
new_size = size_of( Header ) + new_len + 1;
new_ptr = resize( allocator, ptr, old_size, new_size );
if ( new_ptr == nullptr )
return false;
header = rcast( Header*, new_ptr);
header->Allocator = allocator;
header->Capacity = new_len;
Data = rcast( char*, header + 1 );
return true;
}
}
#pragma endregion String #pragma endregion String

View File

@ -19,8 +19,7 @@ struct StrC
#define txt( text ) StrC { sizeof( text ) - 1, ( text ) } #define txt( text ) StrC { sizeof( text ) - 1, ( text ) }
inline inline
StrC to_str( char const* str ) StrC to_str( char const* str ) {
{
return { str_len( str ), str }; return { str_len( str ), str };
} }
@ -28,149 +27,286 @@ StrC to_str( char const* str )
// This is directly based off the ZPL string api. // This is directly based off the ZPL string api.
// They used a header pattern // They used a header pattern
// I kept it for simplicty of porting but its not necessary to keep it that way. // I kept it for simplicty of porting but its not necessary to keep it that way.
struct String #pragma region String
{ struct String;
struct Header struct StringHeader;
{
// Forward declarations for all file-scope functions
String string_make(AllocatorInfo allocator, char const* str);
String string_make(AllocatorInfo allocator, StrC str);
String string_make_reserve(AllocatorInfo allocator, ssize capacity);
String string_make_length(AllocatorInfo allocator, char const* str, ssize length);
String string_fmt(AllocatorInfo allocator, char* buf, ssize buf_size, char const* fmt, ...);
String string_fmt_buf(AllocatorInfo allocator, char const* fmt, ...);
String string_join(AllocatorInfo allocator, char const** parts, ssize num_parts, char const* glue);
usize string_grow_formula(usize value);
bool are_equal(String lhs, String rhs);
bool are_equal(String lhs, StrC rhs);
bool make_space_for(String& str, char const* to_append, ssize add_len);
bool append(String& str, char c);
bool append(String& str, char const* str_to_append);
bool append(String& str, char const* str_to_append, ssize length);
bool append(String& str, StrC str_to_append);
bool append(String& str, const String other);
bool append_fmt(String& str, char const* fmt, ...);
ssize avail_space(String const& str);
char& back(String& str);
bool contains(String const& str, StrC substring);
bool contains(String const& str, String const& substring);
ssize capacity(String const& str);
void clear(String& str);
String duplicate(String const& str, AllocatorInfo allocator);
void free(String& str);
StringHeader& get_header(String& str);
ssize length(String const& str);
b32 starts_with(String const& str, StrC substring);
b32 starts_with(String const& str, String substring);
void skip_line(String& str);
void strip_space(String& str);
void trim(String& str, char const* cut_set);
void trim_space(String& str);
String visualize_whitespace(String const& str);
struct StringHeader {
AllocatorInfo Allocator; AllocatorInfo Allocator;
ssize Capacity; ssize Capacity;
ssize Length; ssize Length;
}; };
struct String
{
char* Data;
#if 1
#pragma region Member Mapping
forceinline static String make(AllocatorInfo allocator, char const* str) { return GEN_NS string_make(allocator, str); }
forceinline static String make(AllocatorInfo allocator, StrC str) { return GEN_NS string_make(allocator, str); }
forceinline static String make_reserve(AllocatorInfo allocator, ssize cap) { return GEN_NS string_make_reserve(allocator, cap); }
forceinline static String make_length(AllocatorInfo a, char const* s, ssize l) { return GEN_NS string_make_length(a, s, l); }
forceinline static String join(AllocatorInfo a, char const** p, ssize n, char const* g) { return GEN_NS string_join(a, p, n, g); }
forceinline static usize grow_formula(usize value) { return GEN_NS string_grow_formula(value); }
forceinline static bool are_equal(String lhs, String rhs) { return GEN_NS are_equal(lhs, rhs); }
forceinline static bool are_equal(String lhs, StrC rhs) { return GEN_NS are_equal(lhs, rhs); }
static static
usize grow_formula( usize value ) String fmt(AllocatorInfo allocator, char* buf, ssize buf_size, char const* fmt, ...) {
{ va_list va;
va_start(va, fmt);
str_fmt_va(buf, buf_size, fmt, va);
va_end(va);
return GEN_NS string_make(allocator, buf);
}
static
String fmt_buf(AllocatorInfo allocator, char const* fmt, ...) {
local_persist thread_local
char buf[GEN_PRINTF_MAXLEN] = { 0 };
va_list va;
va_start(va, fmt);
str_fmt_va(buf, GEN_PRINTF_MAXLEN, fmt, va);
va_end(va);
return GEN_NS string_make(allocator, buf);
}
forceinline bool make_space_for(char const* str, ssize add_len) { return GEN_NS make_space_for(*this, str, add_len); }
forceinline bool append(char c) { return GEN_NS append(*this, c); }
forceinline bool append(char const* str) { return GEN_NS append(*this, str); }
forceinline bool append(char const* str, ssize length) { return GEN_NS append(*this, str, length); }
forceinline bool append(StrC str) { return GEN_NS append(*this, str); }
forceinline bool append(const String other) { return GEN_NS append(*this, other); }
forceinline ssize avail_space() const { return GEN_NS avail_space(*this); }
forceinline char& back() { return GEN_NS back(*this); }
forceinline bool contains(StrC substring) const { return GEN_NS contains(*this, substring); }
forceinline bool contains(String const& substring) const { return GEN_NS contains(*this, substring); }
forceinline ssize capacity() const { return GEN_NS capacity(*this); }
forceinline void clear() { GEN_NS clear(*this); }
forceinline String duplicate(AllocatorInfo allocator) const { return GEN_NS duplicate(*this, allocator); }
forceinline void free() { GEN_NS free(*this); }
forceinline ssize length() const { return GEN_NS length(*this); }
forceinline b32 starts_with(StrC substring) const { return GEN_NS starts_with(*this, substring); }
forceinline b32 starts_with(String substring) const { return GEN_NS starts_with(*this, substring); }
forceinline void skip_line() { GEN_NS skip_line(*this); }
forceinline void strip_space() { GEN_NS strip_space(*this); }
forceinline void trim(char const* cut_set) { GEN_NS trim(*this, cut_set); }
forceinline void trim_space() { GEN_NS trim_space(*this); }
forceinline String visualize_whitespace() const { return GEN_NS visualize_whitespace(*this); }
forceinline StringHeader& get_header() { return GEN_NS get_header(*this); }
bool append_fmt(char const* fmt, ...) {
ssize res;
char buf[GEN_PRINTF_MAXLEN] = { 0 };
va_list va;
va_start(va, fmt);
res = str_fmt_va(buf, count_of(buf) - 1, fmt, va) - 1;
va_end(va);
return GEN_NS append(*this, buf, res);
}
forceinline operator bool() { return Data != nullptr; }
forceinline operator char*() { return Data; }
forceinline operator char const*() const { return Data; }
forceinline operator StrC() const { return { length(), Data }; }
String const& operator=(String const& other) const {
if (this == &other)
return *this;
String* this_ = ccast(String*, this);
this_->Data = other.Data;
return *this;
}
forceinline char& operator[](ssize index) { return Data[index]; }
forceinline char const& operator[](ssize index) const { return Data[index]; }
forceinline char* begin() const { return Data; }
forceinline char* end() const { return Data + length(); }
#pragma endregion Member Mapping
#endif
};
inline
usize string_grow_formula(usize value) {
// Using a very aggressive growth formula to reduce time mem_copying with recursive calls to append in this library. // Using a very aggressive growth formula to reduce time mem_copying with recursive calls to append in this library.
return 4 * value + 8; return 4 * value + 8;
} }
static inline
String make( AllocatorInfo allocator, char const* str ) String string_make(AllocatorInfo allocator, char const* str) {
ssize length = str ? str_len(str) : 0;
return string_make_length(allocator, str, length);
}
inline
String string_make(AllocatorInfo allocator, StrC str) {
return string_make_length(allocator, str.Ptr, str.Len);
}
inline
String string_fmt(AllocatorInfo allocator, char* buf, ssize buf_size, char const* fmt, ...) {
va_list va;
va_start(va, fmt);
str_fmt_va(buf, buf_size, fmt, va);
va_end(va);
return string_make(allocator, buf);
}
inline
String string_fmt_buf(AllocatorInfo allocator, char const* fmt, ...)
{
local_persist thread_local
char buf[GEN_PRINTF_MAXLEN] = { 0 };
va_list va;
va_start(va, fmt);
str_fmt_va(buf, GEN_PRINTF_MAXLEN, fmt, va);
va_end(va);
return string_make(allocator, buf);
}
inline
String string_join(AllocatorInfo allocator, char const** parts, ssize num_parts, char const* glue)
{
String result = string_make(allocator, "");
for (ssize idx = 0; idx < num_parts; ++idx)
{ {
ssize length = str ? str_len( str ) : 0; append(result, parts[idx]);
return make_length( allocator, str, length );
}
static if (idx < num_parts - 1)
String make( AllocatorInfo allocator, StrC str ) append(result, glue);
{
return make_length( allocator, str.Ptr, str.Len );
}
static
String make_reserve( AllocatorInfo allocator, ssize capacity );
static
String make_length( AllocatorInfo allocator, char const* str, ssize length );
static
String fmt( AllocatorInfo allocator, char* buf, ssize buf_size, char const* fmt, ... );
static
String fmt_buf( AllocatorInfo allocator, char const* fmt, ... );
static
String join( AllocatorInfo allocator, char const** parts, ssize num_parts, char const* glue )
{
String result = make( allocator, "" );
for ( ssize idx = 0; idx < num_parts; ++idx )
{
result.append( parts[ idx ] );
if ( idx < num_parts - 1 )
result.append( glue );
} }
return result; return result;
} }
static inline
bool are_equal( String lhs, String rhs ) bool append(String& str, char c) {
return append(str, &c, 1);
}
inline
bool append(String& str, char const* str_to_append) {
return append(str, str_to_append, str_len(str_to_append));
}
inline
bool append(String& str, char const* str_to_append, ssize append_length)
{
if (sptr(str_to_append) > 0)
{ {
if ( lhs.length() != rhs.length() ) ssize curr_len = length(str);
if (!make_space_for(str, str_to_append, append_length))
return false; return false;
for ( ssize idx = 0; idx < lhs.length(); ++idx ) StringHeader& header = get_header(str);
if ( lhs[ idx ] != rhs[ idx ] )
mem_copy(str.Data + curr_len, str_to_append, append_length);
str.Data[curr_len + append_length] = '\0';
header.Length = curr_len + append_length;
}
return str_to_append != nullptr;
}
inline
bool append(String& str, StrC str_to_append) {
return append(str, str_to_append.Ptr, str_to_append.Len);
}
inline
bool append(String& str, const String other) {
return append(str, other.Data, length(other));
}
inline
bool are_equal(String lhs, String rhs)
{
if (length(lhs) != length(rhs))
return false;
for (ssize idx = 0; idx < length(lhs); ++idx)
if (lhs[idx] != rhs[idx])
return false; return false;
return true; return true;
} }
static inline
bool are_equal( String lhs, StrC rhs ) bool are_equal(String lhs, StrC rhs)
{ {
if ( lhs.length() != (rhs.Len) ) if (length(lhs) != (rhs.Len))
return false; return false;
for ( ssize idx = 0; idx < lhs.length(); ++idx ) for (ssize idx = 0; idx < length(lhs); ++idx)
if ( lhs[idx] != rhs[idx] ) if (lhs[idx] != rhs[idx])
return false; return false;
return true; return true;
} }
bool make_space_for( char const* str, ssize add_len );
bool append( char c )
{
return append( & c, 1 );
}
bool append( char const* str )
{
return append( str, str_len( str ) );
}
bool append( char const* str, ssize length )
{
if ( sptr(str) > 0 )
{
ssize curr_len = this->length();
if ( ! make_space_for( str, length ) )
return false;
Header& header = get_header();
mem_copy( Data + curr_len, str, length );
Data[ curr_len + length ] = '\0';
header.Length = curr_len + length;
}
return str != nullptr;
}
bool append( StrC str)
{
return append( str.Ptr, str.Len );
}
bool append( const String other )
{
return append( other.Data, other.length() );
}
bool append_fmt( char const* fmt, ... );
ssize avail_space() const
{
Header const&
header = * rcast( Header const*, Data - sizeof( Header ));
inline
ssize avail_space(String const& str) {
StringHeader const& header = *rcast(StringHeader const*, str.Data - sizeof(StringHeader));
return header.Capacity - header.Length; return header.Capacity - header.Length;
} }
char& back() inline
{ char& back(String& str) {
return Data[ length() - 1 ]; return str.Data[length(str) - 1];
} }
bool contains(StrC substring) const inline
{ bool contains(String const& str, StrC substring)
Header const& header = * rcast( Header const*, Data - sizeof( Header )); {
StringHeader const& header = *rcast(StringHeader const*, str.Data - sizeof(StringHeader));
if (substring.Len > header.Length) if (substring.Len > header.Length)
return false; return false;
@ -180,122 +316,156 @@ struct String
for (ssize idx = 0; idx <= main_len - sub_len; ++idx) for (ssize idx = 0; idx <= main_len - sub_len; ++idx)
{ {
if (str_compare(Data + idx, substring.Ptr, sub_len) == 0) if (str_compare(str.Data + idx, substring.Ptr, sub_len) == 0)
return true; return true;
} }
return false; return false;
} }
bool contains(String const& substring) const inline
{ bool contains(String const& str, String const& substring)
Header const& header = * rcast( Header const*, Data - sizeof( Header )); {
StringHeader const& header = *rcast(StringHeader const*, str.Data - sizeof(StringHeader));
if (substring.length() > header.Length) if (length(substring) > header.Length)
return false; return false;
ssize main_len = header.Length; ssize main_len = header.Length;
ssize sub_len = substring.length(); ssize sub_len = length(substring);
for (ssize idx = 0; idx <= main_len - sub_len; ++idx) for (ssize idx = 0; idx <= main_len - sub_len; ++idx)
{ {
if (str_compare(Data + idx, substring.Data, sub_len) == 0) if (str_compare(str.Data + idx, substring.Data, sub_len) == 0)
return true; return true;
} }
return false; return false;
} }
ssize capacity() const
{
Header const&
header = * rcast( Header const*, Data - sizeof( Header ));
inline
ssize capacity(String const& str) {
StringHeader const& header = *rcast(StringHeader const*, str.Data - sizeof(StringHeader));
return header.Capacity; return header.Capacity;
} }
void clear() inline
{ void clear(String& str) {
get_header().Length = 0; get_header(str).Length = 0;
} }
String duplicate( AllocatorInfo allocator ) const inline
{ String duplicate(String const& str, AllocatorInfo allocator) {
return make_length( allocator, Data, length() ); return string_make_length(allocator, str.Data, length(str));
} }
void free() inline
{ void free(String& str) {
if ( ! Data ) if (!str.Data)
return; return;
Header& header = get_header(); StringHeader& header = get_header(str);
GEN_NS free(header.Allocator, &header);
}
gen::free( header.Allocator, & header ); inline
} StringHeader& get_header(String& str) {
return *(StringHeader*)(str.Data - sizeof(StringHeader));
Header& get_header() }
{
return *(Header*)(Data - sizeof(Header));
}
ssize length() const
{
Header const&
header = * rcast( Header const*, Data - sizeof( Header ));
inline
ssize length(String const& str)
{
StringHeader const& header = *rcast(StringHeader const*, str.Data - sizeof(StringHeader));
return header.Length; return header.Length;
} }
b32 starts_with( StrC substring ) const inline
bool make_space_for(String& str, char const* to_append, ssize add_len)
{
ssize available = avail_space(str);
if (available >= add_len) {
return true;
}
else
{ {
if (substring.Len > length()) ssize new_len, old_size, new_size;
void* ptr;
void* new_ptr;
AllocatorInfo allocator = get_header(str).Allocator;
StringHeader* header = nullptr;
new_len = string_grow_formula(length(str) + add_len);
ptr = &get_header(str);
old_size = size_of(StringHeader) + length(str) + 1;
new_size = size_of(StringHeader) + new_len + 1;
new_ptr = resize(allocator, ptr, old_size, new_size);
if (new_ptr == nullptr)
return false; return false;
b32 result = str_compare(Data, substring.Ptr, substring.Len ) == 0; header = rcast(StringHeader*, new_ptr);
return result; header->Allocator = allocator;
} header->Capacity = new_len;
b32 starts_with( String substring ) const str.Data = rcast(char*, header + 1);
{
if (substring.length() > length()) return true;
}
}
inline
b32 starts_with(String const& str, StrC substring) {
if (substring.Len > length(str))
return false; return false;
b32 result = str_compare(Data, substring, substring.length() - 1 ) == 0; b32 result = str_compare(str.Data, substring.Ptr, substring.Len) == 0;
return result; return result;
}
inline
b32 starts_with(String const& str, String substring) {
if (length(substring) > length(str))
return false;
b32 result = str_compare(str.Data, substring.Data, length(substring) - 1) == 0;
return result;
}
inline
void skip_line(String& str)
{
#define current (*scanner)
char* scanner = str.Data;
while (current != '\r' && current != '\n') {
++scanner;
} }
void skip_line() s32 new_length = scanner - str.Data;
{
#define current (*scanner)
char* scanner = Data;
while ( current != '\r' && current != '\n' )
{
++ scanner;
}
s32 new_length = scanner - Data; if (current == '\r') {
if ( current == '\r' )
{
new_length += 1; new_length += 1;
} }
mem_move( Data, scanner, new_length ); mem_move(str.Data, scanner, new_length);
Header* header = & get_header(); StringHeader* header = &get_header(str);
header->Length = new_length; header->Length = new_length;
#undef current #undef current
} }
void strip_space() inline
{ void strip_space(String& str)
char* write_pos = Data; {
char* read_pos = Data; char* write_pos = str.Data;
char* read_pos = str.Data;
while ( * read_pos) while (*read_pos)
{ {
if ( ! char_is_space( *read_pos )) if (!char_is_space(*read_pos))
{ {
*write_pos = *read_pos; *write_pos = *read_pos;
write_pos++; write_pos++;
@ -306,139 +476,74 @@ struct String
write_pos[0] = '\0'; // Null-terminate the modified string write_pos[0] = '\0'; // Null-terminate the modified string
// Update the length if needed // Update the length if needed
get_header().Length = write_pos - Data; get_header(str).Length = write_pos - str.Data;
} }
void trim( char const* cut_set ) inline
{ void trim(String& str, char const* cut_set)
{
ssize len = 0; ssize len = 0;
char* start_pos = Data; char* start_pos = str.Data;
char* end_pos = Data + length() - 1; char* end_pos = str.Data + length(str) - 1;
while ( start_pos <= end_pos && char_first_occurence( cut_set, *start_pos ) ) while (start_pos <= end_pos && char_first_occurence(cut_set, *start_pos))
start_pos++; start_pos++;
while ( end_pos > start_pos && char_first_occurence( cut_set, *end_pos ) ) while (end_pos > start_pos && char_first_occurence(cut_set, *end_pos))
end_pos--; end_pos--;
len = scast( ssize, ( start_pos > end_pos ) ? 0 : ( ( end_pos - start_pos ) + 1 ) ); len = scast(ssize, (start_pos > end_pos) ? 0 : ((end_pos - start_pos) + 1));
if ( Data != start_pos ) if (str.Data != start_pos)
mem_move( Data, start_pos, len ); mem_move(str.Data, start_pos, len);
Data[ len ] = '\0'; str.Data[len] = '\0';
get_header().Length = len; get_header(str).Length = len;
} }
void trim_space() inline
{ void trim_space(String& str) {
return trim( " \t\r\n\v\f" ); trim(str, " \t\r\n\v\f");
} }
// Debug function that provides a copy of the string with whitespace characters visualized. inline
String visualize_whitespace() const String visualize_whitespace(String const& str)
{ {
Header* header = (Header*)(Data - sizeof(Header)); StringHeader* header = (StringHeader*)(str.Data - sizeof(StringHeader));
String result = string_make_reserve(header->Allocator, length(str) * 2); // Assume worst case for space requirements.
String result = make_reserve(header->Allocator, length() * 2); // Assume worst case for space requirements. for (char c : str) switch (c)
for ( char c : *this )
{
switch ( c )
{ {
case ' ': case ' ':
result.append( txt("·") ); append(result, txt("·"));
break; break;
case '\t': case '\t':
result.append( txt("") ); append(result, txt(""));
break; break;
case '\n': case '\n':
result.append( txt("") ); append(result, txt(""));
break; break;
case '\r': case '\r':
result.append( txt("") ); append(result, txt(""));
break; break;
case '\v': case '\v':
result.append( txt("") ); append(result, txt(""));
break; break;
case '\f': case '\f':
result.append( txt("") ); append(result, txt(""));
break; break;
default: default:
result.append(c); append(result, c);
break; break;
} }
}
return result; return result;
} }
#pragma endregion String
// For-range support struct String_POD {
char* begin() const
{
return Data;
}
char* end() const
{
Header const&
header = * rcast( Header const*, Data - sizeof( Header ));
return Data + header.Length;
}
operator bool()
{
return Data != nullptr;
}
operator char* ()
{
return Data;
}
operator char const* () const
{
return Data;
}
operator StrC() const
{
return { length(), Data };
}
// Used with cached strings
// Essentially makes the string a string view.
String const& operator = ( String const& other ) const
{
if ( this == & other )
return *this;
String*
this_ = ccast(String*, this);
this_->Data = other.Data;
return *this;
}
char& operator [] ( ssize index )
{
return Data[ index ];
}
char const& operator [] ( ssize index ) const
{
return Data[ index ];
}
char* Data;
};
struct String_POD
{
char* Data; char* Data;
}; };
static_assert( sizeof( String_POD ) == sizeof( String ), "String is not a POD" ); static_assert( sizeof( String_POD ) == sizeof( String ), "String is not a POD" );