mirror of
https://github.com/Ed94/gencpp.git
synced 2024-12-23 00:04:45 -08:00
Compare commits
No commits in common. "056a5863b81e117994b791731f8ac9c42d1eed37" and "163ad0a5112fc2ff18b0599822f7cadbac73ddda" have entirely different histories.
056a5863b8
...
163ad0a511
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@ -37,8 +37,7 @@
|
|||||||
"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,
|
||||||
|
@ -1,23 +0,0 @@
|
|||||||
#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 );
|
|
@ -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(StringHeader) + sizeof(char*);
|
usize size_req = str_length + sizeof(String::Header) + sizeof(char*);
|
||||||
|
|
||||||
if ( last->TotalUsed + ssize(size_req) > last->TotalSize )
|
if ( last->TotalUsed + ssize(size_req) > last->TotalSize )
|
||||||
{
|
{
|
||||||
|
@ -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 ];
|
||||||
|
|
||||||
tok_map_arena = init_from_memory( tok_map_mem, sizeof(tok_map_mem) );
|
init_from_memory( tok_map_arena, 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;
|
||||||
|
@ -133,7 +133,7 @@ internal
|
|||||||
void init()
|
void init()
|
||||||
{
|
{
|
||||||
Tokens = Array<Token>::init_reserve( LexArena
|
Tokens = Array<Token>::init_reserve( LexArena
|
||||||
, ( LexAllocator_Size - sizeof( ArrayHeader ) ) / sizeof(Token)
|
, ( LexAllocator_Size - sizeof( Array<Token>::Header ) ) / 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( interface_arr_mem, kilobytes(4) );
|
Arena arena; init_from_memory(arena, interface_arr_mem, kilobytes(4) );
|
||||||
Array<CodeType>::init_reserve( arena, 4 );
|
Array<CodeType>::init_reserve( arena, 4 );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
#ifdef GEN_INTELLISENSE_DIRECTIVES
|
#ifdef GEN_INTELLISENSE_DIRECTIVES
|
||||||
# pragma once
|
# pragma once
|
||||||
# include "platform.hpp"
|
|
||||||
# include "macros.hpp"
|
# include "macros.hpp"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -13,607 +13,539 @@ 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;
|
||||||
|
|
||||||
#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>
|
template<class Type>
|
||||||
struct Array
|
struct Array
|
||||||
{
|
{
|
||||||
Type* Data;
|
struct Header
|
||||||
|
{
|
||||||
|
AllocatorInfo Allocator;
|
||||||
|
usize Capacity;
|
||||||
|
usize Num;
|
||||||
|
};
|
||||||
|
|
||||||
#if 1
|
static
|
||||||
#pragma region Member Mapping
|
Array init( AllocatorInfo allocator )
|
||||||
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); }
|
return init_reserve( allocator, grow_formula(0) );
|
||||||
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); }
|
static
|
||||||
forceinline bool append(Type value) { return GEN_NS append<Type>(*this, value); }
|
Array init_reserve( AllocatorInfo allocator, ssize capacity )
|
||||||
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); }
|
Header* header = rcast( Header*, alloc( allocator, sizeof(Header) + sizeof(Type) * capacity ));
|
||||||
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; }
|
if ( header == nullptr )
|
||||||
forceinline operator Type const*() const { return Data; }
|
return { nullptr };
|
||||||
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) };
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
static
|
||||||
|
usize grow_formula( usize value )
|
||||||
|
{
|
||||||
|
return 2 * value + 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool append( Array other )
|
||||||
|
{
|
||||||
|
return append( other, other.num() );
|
||||||
|
}
|
||||||
|
|
||||||
|
bool append( Type value )
|
||||||
|
{
|
||||||
|
Header* header = get_header();
|
||||||
|
|
||||||
|
if ( header->Num == header->Capacity )
|
||||||
|
{
|
||||||
|
if ( ! grow( header->Capacity ))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
header = get_header();
|
||||||
|
}
|
||||||
|
|
||||||
|
Data[ header->Num ] = value;
|
||||||
header->Num++;
|
header->Num++;
|
||||||
|
|
||||||
return true;
|
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));
|
bool append( Type* items, usize item_num )
|
||||||
|
{
|
||||||
|
Header* header = get_header();
|
||||||
|
|
||||||
|
if ( header->Num + item_num > header->Capacity )
|
||||||
|
{
|
||||||
|
if ( ! grow( header->Capacity + item_num ))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
header = get_header();
|
||||||
|
}
|
||||||
|
|
||||||
|
mem_copy( Data + header->Num, items, item_num * sizeof(Type) );
|
||||||
header->Num += item_num;
|
header->Num += item_num;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class Type> inline
|
bool append_at( Type item, usize idx )
|
||||||
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(array, header->Capacity + 1))
|
if ( ! grow( header->Capacity + 1 ))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
header = get_header(array);
|
header = get_header();
|
||||||
}
|
}
|
||||||
|
|
||||||
Type* target = array.Data + idx;
|
Type* target = 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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (item_num > header->Capacity)
|
bool append_at( Type* items, usize item_num, usize idx )
|
||||||
{
|
{
|
||||||
if (!grow(array, header->Capacity + item_num))
|
Header* header = get_header();
|
||||||
|
|
||||||
|
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(array);
|
header = get_header();
|
||||||
}
|
}
|
||||||
|
|
||||||
Type* target = array.Data + idx + item_num;
|
Type* target = Data + idx + item_num;
|
||||||
Type* src = array.Data + idx;
|
Type* src = 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class Type> inline
|
Type& back( void )
|
||||||
Type& back(Array<Type>& array) {
|
{
|
||||||
ArrayHeader* header = get_header(array);
|
Header& header = * get_header();
|
||||||
return array.Data[header->Num - 1];
|
return Data[ header.Num - 1 ];
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class Type> inline
|
void clear( void )
|
||||||
void clear(Array<Type>& array) {
|
{
|
||||||
ArrayHeader* header = get_header(array);
|
Header& header = * get_header();
|
||||||
header->Num = 0;
|
header.Num = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class Type> inline
|
bool fill( usize begin, usize end, Type value )
|
||||||
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++ )
|
||||||
{
|
{
|
||||||
array.Data[idx] = value;
|
Data[ idx ] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class Type> inline
|
void free( void )
|
||||||
void free(Array<Type>& array) {
|
{
|
||||||
ArrayHeader* header = get_header(array);
|
Header& header = * get_header();
|
||||||
gen::free(header->Allocator, header);
|
gen::free( header.Allocator, &header );
|
||||||
array.Data = nullptr;
|
Data = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class Type> inline
|
Header* get_header( void )
|
||||||
ArrayHeader* get_header(Array<Type>& array) {
|
{
|
||||||
using NonConstType = TRemoveConst<Type>;
|
using NonConstType = TRemoveConst< Type >;
|
||||||
return rcast(ArrayHeader*, const_cast<NonConstType*>(array.Data)) - 1;
|
return rcast( Header*, const_cast<NonConstType*>(Data) ) - 1 ;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class Type> inline
|
bool grow( usize min_capacity )
|
||||||
bool grow(Array<Type>& array, usize min_capacity)
|
{
|
||||||
{
|
Header& header = * get_header();
|
||||||
ArrayHeader* header = get_header(array);
|
usize new_capacity = grow_formula( header.Capacity );
|
||||||
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(array, new_capacity);
|
return set_capacity( new_capacity );
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class Type> inline
|
usize num( void )
|
||||||
usize num(Array<Type>& array) {
|
{
|
||||||
return get_header(array)->Num;
|
return get_header()->Num;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class Type> inline
|
void pop( void )
|
||||||
void pop(Array<Type>& array) {
|
{
|
||||||
ArrayHeader* header = get_header(array);
|
Header& header = * get_header();
|
||||||
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--;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class Type> inline
|
bool reserve( usize new_capacity )
|
||||||
void remove_at(Array<Type>& array, usize idx)
|
{
|
||||||
{
|
Header& header = * get_header();
|
||||||
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));
|
if ( header.Capacity < new_capacity )
|
||||||
header->Num--;
|
return set_capacity( new_capacity );
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class Type> inline
|
bool resize( usize num )
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class Type> inline
|
bool set_capacity( usize new_capacity )
|
||||||
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 )
|
||||||
{
|
{
|
||||||
header->Num = new_capacity;
|
// Already have the memory, mine as well keep it.
|
||||||
|
header.Num = new_capacity;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
ssize size = sizeof(ArrayHeader) + sizeof(Type) * new_capacity;
|
ssize size = sizeof( Header ) + sizeof( Type ) * new_capacity;
|
||||||
ArrayHeader* new_header = rcast(ArrayHeader*, alloc(header->Allocator, size));
|
Header* new_header = rcast( Header*, alloc( header.Allocator, size ) );
|
||||||
|
|
||||||
if (new_header == nullptr)
|
if ( new_header == nullptr )
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
mem_move(new_header, header, sizeof(ArrayHeader) + sizeof(Type) * header->Num);
|
mem_move( new_header, &header, sizeof( Header ) + sizeof( Type ) * header.Num );
|
||||||
|
|
||||||
new_header->Capacity = new_capacity;
|
new_header->Capacity = new_capacity;
|
||||||
|
|
||||||
GEN_NS free(header->Allocator, header);
|
gen::free( header.Allocator, &header );
|
||||||
|
|
||||||
array.Data = rcast(Type*, new_header + 1);
|
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;
|
||||||
|
|
||||||
Array<ssize> Hashes;
|
static
|
||||||
Array<HashTableEntry<Type>> Entries;
|
HashTable init( AllocatorInfo allocator )
|
||||||
|
{
|
||||||
#if 1
|
HashTable<Type> result = init_reserve(allocator, 8);
|
||||||
#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;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Type> inline
|
static
|
||||||
HashTable<Type> hashtable_init_reserve(AllocatorInfo allocator, usize num)
|
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<HashTableEntry<Type>>::init_reserve(allocator, num);
|
result.Entries = Array<Entry>::init_reserve( allocator, num );
|
||||||
return result;
|
return result;
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Type> inline
|
|
||||||
void clear(HashTable<Type>& table) {
|
|
||||||
table.Entries.clear();
|
|
||||||
table.Hashes.fill(0, table.Hashes.num(), -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Type> inline
|
|
||||||
void destroy(HashTable<Type>& table) {
|
|
||||||
if (table.Hashes && table.Hashes.get_header()->Capacity) {
|
|
||||||
table.Hashes.free();
|
|
||||||
table.Entries.free();
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Type> inline
|
void clear( void )
|
||||||
Type* get(HashTable<Type>& table, u64 key) {
|
{
|
||||||
ssize idx = find(table, key).EntryIndex;
|
Entries.clear();
|
||||||
if (idx >= 0)
|
Hashes.fill( 0, Hashes.num(), -1);
|
||||||
return &table.Entries[idx].Value;
|
}
|
||||||
|
|
||||||
|
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;
|
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);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Type> inline
|
using MapProc = void (*)( u64 key, Type value );
|
||||||
void map_mut(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) {
|
void map( MapProc map_proc )
|
||||||
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 );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Type> inline
|
using MapMutProc = void (*)( u64 key, Type* value );
|
||||||
void grow(HashTable<Type>& table) {
|
|
||||||
ssize new_num = Array<HashTableEntry<Type>>::grow_formula(table.Entries.num());
|
|
||||||
rehash(table, new_num);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Type> inline
|
void map_mut( MapMutProc map_proc )
|
||||||
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);
|
|
||||||
|
|
||||||
for (ssize idx = 0; idx < ssize(table.Entries.num()); ++idx)
|
HashTable<Type> new_ht = init_reserve( Hashes.get_header()->Allocator, new_num );
|
||||||
|
for ( ssize idx = 0; idx < ssize(Entries.num()); ++idx )
|
||||||
{
|
{
|
||||||
HashTableFindResult find_result;
|
FindResult find_result;
|
||||||
HashTableEntry<Type>& entry = table.Entries[idx];
|
|
||||||
|
|
||||||
find_result = find(new_ht, entry.Key);
|
Entry& entry = Entries[ idx ];
|
||||||
last_added_index = add_entry(new_ht, entry.Key);
|
find_result = new_ht.find( 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(table);
|
destroy();
|
||||||
table = new_ht;
|
*this = new_ht;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Type> inline
|
void rehash_fast()
|
||||||
void rehash_fast(HashTable<Type>& table)
|
{
|
||||||
{
|
|
||||||
ssize idx;
|
ssize idx;
|
||||||
|
|
||||||
for (idx = 0; idx < ssize(table.Entries.num()); idx++)
|
for ( idx = 0; idx < ssize(Entries.num()); idx++ )
|
||||||
table.Entries[idx].Next = -1;
|
Entries[ idx ].Next = -1;
|
||||||
|
|
||||||
for (idx = 0; idx < ssize(table.Hashes.num()); idx++)
|
for ( idx = 0; idx < ssize(Hashes.num()); idx++ )
|
||||||
table.Hashes[idx] = -1;
|
Hashes[ idx ] = -1;
|
||||||
|
|
||||||
for (idx = 0; idx < ssize(table.Entries.num()); idx++)
|
for ( idx = 0; idx < ssize(Entries.num()); idx++ )
|
||||||
{
|
{
|
||||||
HashTableEntry<Type>* entry;
|
Entry* entry;
|
||||||
HashTableFindResult find_result;
|
FindResult find_result;
|
||||||
|
|
||||||
entry = &table.Entries[idx];
|
entry = & Entries[ idx ];
|
||||||
find_result = find(table, entry->Key);
|
find_result = find( entry->Key );
|
||||||
|
|
||||||
if (find_result.PrevIndex < 0)
|
if ( find_result.PrevIndex < 0 )
|
||||||
table.Hashes[find_result.HashIndex] = idx;
|
Hashes[ find_result.HashIndex ] = idx;
|
||||||
else
|
else
|
||||||
table.Entries[find_result.PrevIndex].Next = idx;
|
Entries[ find_result.PrevIndex ].Next = idx;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Type> inline
|
|
||||||
void remove(HashTable<Type>& 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<typename Type> inline
|
void remove( u64 key )
|
||||||
void remove_entry(HashTable<Type>& table, ssize idx) {
|
{
|
||||||
table.Entries.remove_at(idx);
|
FindResult find_result = find( key);
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Type> inline
|
if ( find_result.EntryIndex >= 0 )
|
||||||
void set(HashTable<Type>& table, u64 key, Type value)
|
{
|
||||||
{
|
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;
|
ssize idx;
|
||||||
HashTableFindResult find_result;
|
FindResult find_result;
|
||||||
|
|
||||||
if (full(table))
|
if ( full() )
|
||||||
grow(table);
|
grow();
|
||||||
|
|
||||||
find_result = find(table, key);
|
find_result = find( key );
|
||||||
if (find_result.EntryIndex >= 0) {
|
if ( find_result.EntryIndex >= 0 )
|
||||||
|
{
|
||||||
idx = find_result.EntryIndex;
|
idx = find_result.EntryIndex;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
idx = add_entry(table, key);
|
idx = add_entry( 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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
table.Entries[idx].Value = value;
|
Entries[ idx ].Value = value;
|
||||||
|
|
||||||
if (full(table))
|
if ( full() )
|
||||||
grow(table);
|
grow();
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Type> inline
|
ssize slot( u64 key )
|
||||||
ssize slot(HashTable<Type>& table, u64 key) {
|
{
|
||||||
for (ssize idx = 0; idx < ssize(table.Hashes.num()); ++idx)
|
for ( ssize idx = 0; idx < ssize(Hashes.num()); ++idx )
|
||||||
if (table.Hashes[idx] == key)
|
if ( Hashes[ idx ] == key )
|
||||||
return idx;
|
return idx;
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Type> inline
|
Array< ssize> Hashes;
|
||||||
ssize add_entry(HashTable<Type>& table, u64 key) {
|
Array< Entry> Entries;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
ssize add_entry( u64 key )
|
||||||
|
{
|
||||||
ssize idx;
|
ssize idx;
|
||||||
HashTableEntry<Type> entry = { key, -1 };
|
Entry entry = { key, -1 };
|
||||||
|
|
||||||
idx = table.Entries.num();
|
idx = Entries.num();
|
||||||
table.Entries.append(entry);
|
Entries.append( entry );
|
||||||
return idx;
|
return idx;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Type> inline
|
FindResult find( u64 key )
|
||||||
HashTableFindResult find(HashTable<Type>& table, u64 key)
|
|
||||||
{
|
|
||||||
HashTableFindResult result = { -1, -1, -1 };
|
|
||||||
|
|
||||||
if (table.Hashes.num() > 0)
|
|
||||||
{
|
{
|
||||||
result.HashIndex = key % table.Hashes.num();
|
FindResult result = { -1, -1, -1 };
|
||||||
result.EntryIndex = table.Hashes[result.HashIndex];
|
|
||||||
|
|
||||||
while (result.EntryIndex >= 0)
|
if ( Hashes.num() > 0 )
|
||||||
{
|
{
|
||||||
if (table.Entries[result.EntryIndex].Key == key)
|
result.HashIndex = key % Hashes.num();
|
||||||
|
result.EntryIndex = Hashes[ result.HashIndex ];
|
||||||
|
|
||||||
|
while ( result.EntryIndex >= 0 )
|
||||||
|
{
|
||||||
|
if ( Entries[ result.EntryIndex ].Key == key )
|
||||||
break;
|
break;
|
||||||
|
|
||||||
result.PrevIndex = result.EntryIndex;
|
result.PrevIndex = result.EntryIndex;
|
||||||
result.EntryIndex = table.Entries[result.EntryIndex].Next;
|
result.EntryIndex = Entries[ result.EntryIndex ].Next;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Type> inline
|
b32 full()
|
||||||
bool full(HashTable<Type>& table) {
|
{
|
||||||
usize critical_load = usize(HashTable<Type>::CriticalLoadScale * f32(table.Hashes.num()));
|
usize critical_load = usize( CriticalLoadScale * f32(Hashes.num()) );
|
||||||
b32 result = table.Entries.num() > critical_load;
|
b32 result = Entries.num() > critical_load;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
#pragma endregion HashTable
|
};
|
||||||
|
|
||||||
#pragma endregion Containers
|
#pragma endregion Containers
|
||||||
|
@ -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, ss
|
|||||||
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, ssi
|
|||||||
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 clear(Pool& pool)
|
void Pool::clear()
|
||||||
{
|
{
|
||||||
ssize actual_block_size, block_index;
|
ssize actual_block_size, block_index;
|
||||||
void* curr;
|
void* curr;
|
||||||
uptr* end;
|
uptr* end;
|
||||||
|
|
||||||
actual_block_size = pool.BlockSize + pool.BlockAlign;
|
actual_block_size = BlockSize + BlockAlign;
|
||||||
|
|
||||||
curr = pool.PhysicalStart;
|
curr = PhysicalStart;
|
||||||
for ( block_index = 0; block_index < pool.NumBlocks - 1; block_index++ )
|
for ( block_index = 0; block_index < 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 clear(Pool& pool)
|
|||||||
end = ( uptr* ) curr;
|
end = ( uptr* ) curr;
|
||||||
*end = ( uptr ) NULL;
|
*end = ( uptr ) NULL;
|
||||||
|
|
||||||
pool.FreeList = pool.PhysicalStart;
|
FreeList = PhysicalStart;
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma endregion Memory
|
#pragma endregion Memory
|
||||||
|
@ -170,180 +170,127 @@ 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 );
|
||||||
|
|
||||||
AllocatorInfo allocator_info( Arena& arena );
|
struct Arena
|
||||||
|
{
|
||||||
|
static
|
||||||
|
void* allocator_proc( void* allocator_data, AllocType type, ssize size, ssize alignment, void* old_memory, ssize old_size, u64 flags );
|
||||||
|
|
||||||
// Remove static keyword and rename allocator_proc
|
//forceinline static
|
||||||
void* arena_allocator_proc(void* allocator_data, AllocType type, ssize size, ssize alignment, void* old_memory, ssize old_size, u64 flags);
|
//Arena init_from_memory( void* start, ssize size ) {
|
||||||
|
// Arena result; GEN_NS init_from_memory( result, start, size );
|
||||||
|
// return result;
|
||||||
|
//}
|
||||||
|
|
||||||
// Add these declarations after the Arena struct
|
static
|
||||||
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 init_sub(Arena& parent, ssize size);
|
Arena result =
|
||||||
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(Arena& arena);
|
void check()
|
||||||
|
{
|
||||||
|
GEN_ASSERT( TempCount == 0 );
|
||||||
|
}
|
||||||
#pragma pop_macro("check")
|
#pragma pop_macro("check")
|
||||||
|
|
||||||
void free(Arena& arena);
|
void free()
|
||||||
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;
|
||||||
|
|
||||||
#if 1
|
operator AllocatorInfo() { return { allocator_proc, this }; }
|
||||||
#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
|
|
||||||
};
|
};
|
||||||
|
|
||||||
inline
|
void init_from_memory( Arena& arena, void* start, ssize size )
|
||||||
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
|
||||||
{
|
{
|
||||||
char memory[Size];
|
static
|
||||||
|
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 ) >;
|
||||||
@ -356,20 +303,31 @@ 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;
|
||||||
@ -378,34 +336,12 @@ struct Pool
|
|||||||
ssize TotalSize;
|
ssize TotalSize;
|
||||||
ssize NumBlocks;
|
ssize NumBlocks;
|
||||||
|
|
||||||
#pragma region Member Mapping
|
operator AllocatorInfo()
|
||||||
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 ) {
|
||||||
|
@ -4,9 +4,20 @@
|
|||||||
#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, ... )
|
||||||
{
|
{
|
||||||
constexpr ssize header_size = sizeof( StringHeader );
|
va_list va;
|
||||||
|
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 );
|
||||||
@ -14,8 +25,8 @@ String string_make_length( AllocatorInfo allocator, char const* str, ssize lengt
|
|||||||
if ( allocation == nullptr )
|
if ( allocation == nullptr )
|
||||||
return { nullptr };
|
return { nullptr };
|
||||||
|
|
||||||
StringHeader&
|
Header&
|
||||||
header = * rcast(StringHeader*, allocation);
|
header = * rcast(Header*, allocation);
|
||||||
header = { allocator, length, length };
|
header = { allocator, length, length };
|
||||||
|
|
||||||
String result = { rcast( char*, allocation) + header_size };
|
String result = { rcast( char*, allocation) + header_size };
|
||||||
@ -30,9 +41,9 @@ String string_make_length( AllocatorInfo allocator, char const* str, ssize lengt
|
|||||||
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( StringHeader );
|
constexpr ssize header_size = sizeof( Header );
|
||||||
|
|
||||||
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 );
|
||||||
@ -42,8 +53,8 @@ String string_make_reserve( AllocatorInfo allocator, ssize capacity )
|
|||||||
|
|
||||||
mem_set( allocation, 0, alloc_size );
|
mem_set( allocation, 0, alloc_size );
|
||||||
|
|
||||||
StringHeader*
|
Header*
|
||||||
header = rcast(StringHeader*, allocation);
|
header = rcast(Header*, allocation);
|
||||||
header->Allocator = allocator;
|
header->Allocator = allocator;
|
||||||
header->Capacity = capacity;
|
header->Capacity = capacity;
|
||||||
header->Length = 0;
|
header->Length = 0;
|
||||||
@ -51,4 +62,69 @@ 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
|
||||||
|
@ -19,7 +19,8 @@ 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 };
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -27,286 +28,149 @@ 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.
|
||||||
#pragma region String
|
struct String
|
||||||
struct String;
|
{
|
||||||
struct StringHeader;
|
struct Header
|
||||||
|
{
|
||||||
// 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
|
||||||
String fmt(AllocatorInfo allocator, char* buf, ssize buf_size, char const* fmt, ...) {
|
usize grow_formula( usize value )
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline
|
static
|
||||||
String string_make(AllocatorInfo allocator, char const* str) {
|
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)
|
|
||||||
{
|
{
|
||||||
append(result, parts[idx]);
|
ssize length = str ? str_len( str ) : 0;
|
||||||
|
return make_length( allocator, str, length );
|
||||||
|
}
|
||||||
|
|
||||||
if (idx < num_parts - 1)
|
static
|
||||||
append(result, glue);
|
String make( AllocatorInfo allocator, StrC str )
|
||||||
|
{
|
||||||
|
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;
|
||||||
}
|
|
||||||
|
|
||||||
inline
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
ssize curr_len = length(str);
|
|
||||||
|
|
||||||
if (!make_space_for(str, str_to_append, append_length))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
StringHeader& header = get_header(str);
|
|
||||||
|
|
||||||
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
|
static
|
||||||
bool append(String& str, StrC str_to_append) {
|
bool are_equal( String lhs, String rhs )
|
||||||
return append(str, str_to_append.Ptr, str_to_append.Len);
|
{
|
||||||
}
|
if ( lhs.length() != rhs.length() )
|
||||||
|
|
||||||
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;
|
return false;
|
||||||
|
|
||||||
for (ssize idx = 0; idx < length(lhs); ++idx)
|
for ( ssize idx = 0; idx < lhs.length(); ++idx )
|
||||||
if (lhs[idx] != rhs[idx])
|
if ( lhs[ idx ] != rhs[ idx ] )
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline
|
static
|
||||||
bool are_equal(String lhs, StrC rhs)
|
bool are_equal( String lhs, StrC rhs )
|
||||||
{
|
{
|
||||||
if (length(lhs) != (rhs.Len))
|
if ( lhs.length() != (rhs.Len) )
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
for (ssize idx = 0; idx < length(lhs); ++idx)
|
for ( ssize idx = 0; idx < lhs.length(); ++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;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline
|
char& back()
|
||||||
char& back(String& str) {
|
{
|
||||||
return str.Data[length(str) - 1];
|
return Data[ length() - 1 ];
|
||||||
}
|
}
|
||||||
|
|
||||||
inline
|
bool contains(StrC substring) const
|
||||||
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;
|
||||||
@ -316,156 +180,122 @@ bool contains(String const& str, StrC substring)
|
|||||||
|
|
||||||
for (ssize idx = 0; idx <= main_len - sub_len; ++idx)
|
for (ssize idx = 0; idx <= main_len - sub_len; ++idx)
|
||||||
{
|
{
|
||||||
if (str_compare(str.Data + idx, substring.Ptr, sub_len) == 0)
|
if (str_compare(Data + idx, substring.Ptr, sub_len) == 0)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline
|
bool contains(String const& substring) const
|
||||||
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 (length(substring) > header.Length)
|
if (substring.length() > header.Length)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
ssize main_len = header.Length;
|
ssize main_len = header.Length;
|
||||||
ssize sub_len = length(substring);
|
ssize sub_len = substring.length();
|
||||||
|
|
||||||
for (ssize idx = 0; idx <= main_len - sub_len; ++idx)
|
for (ssize idx = 0; idx <= main_len - sub_len; ++idx)
|
||||||
{
|
{
|
||||||
if (str_compare(str.Data + idx, substring.Data, sub_len) == 0)
|
if (str_compare(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;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline
|
void clear()
|
||||||
void clear(String& str) {
|
{
|
||||||
get_header(str).Length = 0;
|
get_header().Length = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline
|
String duplicate( AllocatorInfo allocator ) const
|
||||||
String duplicate(String const& str, AllocatorInfo allocator) {
|
{
|
||||||
return string_make_length(allocator, str.Data, length(str));
|
return make_length( allocator, Data, length() );
|
||||||
}
|
}
|
||||||
|
|
||||||
inline
|
void free()
|
||||||
void free(String& str) {
|
{
|
||||||
if (!str.Data)
|
if ( ! Data )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
StringHeader& header = get_header(str);
|
Header& header = get_header();
|
||||||
GEN_NS free(header.Allocator, &header);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline
|
gen::free( header.Allocator, & header );
|
||||||
StringHeader& get_header(String& str) {
|
|
||||||
return *(StringHeader*)(str.Data - sizeof(StringHeader));
|
|
||||||
}
|
|
||||||
|
|
||||||
inline
|
|
||||||
ssize length(String const& str)
|
|
||||||
{
|
|
||||||
StringHeader const& header = *rcast(StringHeader const*, str.Data - sizeof(StringHeader));
|
|
||||||
return header.Length;
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
|
Header& get_header()
|
||||||
{
|
{
|
||||||
ssize new_len, old_size, new_size;
|
return *(Header*)(Data - sizeof(Header));
|
||||||
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;
|
|
||||||
|
|
||||||
header = rcast(StringHeader*, new_ptr);
|
|
||||||
header->Allocator = allocator;
|
|
||||||
header->Capacity = new_len;
|
|
||||||
|
|
||||||
str.Data = rcast(char*, header + 1);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline
|
|
||||||
b32 starts_with(String const& str, StrC substring) {
|
|
||||||
if (substring.Len > length(str))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
b32 result = str_compare(str.Data, substring.Ptr, substring.Len) == 0;
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
s32 new_length = scanner - str.Data;
|
ssize length() const
|
||||||
|
{
|
||||||
|
Header const&
|
||||||
|
header = * rcast( Header const*, Data - sizeof( Header ));
|
||||||
|
|
||||||
if (current == '\r') {
|
return header.Length;
|
||||||
|
}
|
||||||
|
|
||||||
|
b32 starts_with( StrC substring ) const
|
||||||
|
{
|
||||||
|
if (substring.Len > length())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
b32 result = str_compare(Data, substring.Ptr, substring.Len ) == 0;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
b32 starts_with( String substring ) const
|
||||||
|
{
|
||||||
|
if (substring.length() > length())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
b32 result = str_compare(Data, substring, substring.length() - 1 ) == 0;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void skip_line()
|
||||||
|
{
|
||||||
|
#define current (*scanner)
|
||||||
|
char* scanner = Data;
|
||||||
|
while ( current != '\r' && current != '\n' )
|
||||||
|
{
|
||||||
|
++ scanner;
|
||||||
|
}
|
||||||
|
|
||||||
|
s32 new_length = scanner - Data;
|
||||||
|
|
||||||
|
if ( current == '\r' )
|
||||||
|
{
|
||||||
new_length += 1;
|
new_length += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
mem_move(str.Data, scanner, new_length);
|
mem_move( Data, scanner, new_length );
|
||||||
|
|
||||||
StringHeader* header = &get_header(str);
|
Header* header = & get_header();
|
||||||
header->Length = new_length;
|
header->Length = new_length;
|
||||||
#undef current
|
#undef current
|
||||||
}
|
}
|
||||||
|
|
||||||
inline
|
void strip_space()
|
||||||
void strip_space(String& str)
|
|
||||||
{
|
|
||||||
char* write_pos = str.Data;
|
|
||||||
char* read_pos = str.Data;
|
|
||||||
|
|
||||||
while (*read_pos)
|
|
||||||
{
|
{
|
||||||
if (!char_is_space(*read_pos))
|
char* write_pos = Data;
|
||||||
|
char* read_pos = Data;
|
||||||
|
|
||||||
|
while ( * read_pos)
|
||||||
|
{
|
||||||
|
if ( ! char_is_space( *read_pos ))
|
||||||
{
|
{
|
||||||
*write_pos = *read_pos;
|
*write_pos = *read_pos;
|
||||||
write_pos++;
|
write_pos++;
|
||||||
@ -476,74 +306,139 @@ void strip_space(String& str)
|
|||||||
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(str).Length = write_pos - str.Data;
|
get_header().Length = write_pos - Data;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline
|
void trim( char const* cut_set )
|
||||||
void trim(String& str, char const* cut_set)
|
{
|
||||||
{
|
|
||||||
ssize len = 0;
|
ssize len = 0;
|
||||||
|
|
||||||
char* start_pos = str.Data;
|
char* start_pos = Data;
|
||||||
char* end_pos = str.Data + length(str) - 1;
|
char* end_pos = Data + length() - 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 (str.Data != start_pos)
|
if ( Data != start_pos )
|
||||||
mem_move(str.Data, start_pos, len);
|
mem_move( Data, start_pos, len );
|
||||||
|
|
||||||
str.Data[len] = '\0';
|
Data[ len ] = '\0';
|
||||||
|
|
||||||
get_header(str).Length = len;
|
get_header().Length = len;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline
|
void trim_space()
|
||||||
void trim_space(String& str) {
|
{
|
||||||
trim(str, " \t\r\n\v\f");
|
return trim( " \t\r\n\v\f" );
|
||||||
}
|
}
|
||||||
|
|
||||||
inline
|
// Debug function that provides a copy of the string with whitespace characters visualized.
|
||||||
String visualize_whitespace(String const& str)
|
String visualize_whitespace() const
|
||||||
{
|
{
|
||||||
StringHeader* header = (StringHeader*)(str.Data - sizeof(StringHeader));
|
Header* header = (Header*)(Data - sizeof(Header));
|
||||||
String result = string_make_reserve(header->Allocator, length(str) * 2); // Assume worst case for space requirements.
|
|
||||||
|
|
||||||
for (char c : str) switch (c)
|
String result = make_reserve(header->Allocator, length() * 2); // Assume worst case for space requirements.
|
||||||
|
|
||||||
|
for ( char c : *this )
|
||||||
|
{
|
||||||
|
switch ( c )
|
||||||
{
|
{
|
||||||
case ' ':
|
case ' ':
|
||||||
append(result, txt("·"));
|
result.append( txt("·") );
|
||||||
break;
|
break;
|
||||||
case '\t':
|
case '\t':
|
||||||
append(result, txt("→"));
|
result.append( txt("→") );
|
||||||
break;
|
break;
|
||||||
case '\n':
|
case '\n':
|
||||||
append(result, txt("↵"));
|
result.append( txt("↵") );
|
||||||
break;
|
break;
|
||||||
case '\r':
|
case '\r':
|
||||||
append(result, txt("⏎"));
|
result.append( txt("⏎") );
|
||||||
break;
|
break;
|
||||||
case '\v':
|
case '\v':
|
||||||
append(result, txt("⇕"));
|
result.append( txt("⇕") );
|
||||||
break;
|
break;
|
||||||
case '\f':
|
case '\f':
|
||||||
append(result, txt("⌂"));
|
result.append( txt("⌂") );
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
append(result, c);
|
result.append(c);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
#pragma endregion String
|
|
||||||
|
|
||||||
struct String_POD {
|
// For-range support
|
||||||
|
|
||||||
|
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" );
|
||||||
|
Loading…
Reference in New Issue
Block a user