From a96d03eaed733e918dcbab00ec572738a8acad85 Mon Sep 17 00:00:00 2001 From: Ed_ Date: Sun, 1 Dec 2024 01:40:14 -0500 Subject: [PATCH] brought over the generators of array and hashtable for c-lib gen From the old genc repo. Still need to fully check that its code is up to date --- gen_c_library/components/containers.array.hpp | 295 +++++++++++++++ .../components/containers.hashtable.hpp | 354 ++++++++++++++++++ 2 files changed, 649 insertions(+) create mode 100644 gen_c_library/components/containers.array.hpp create mode 100644 gen_c_library/components/containers.hashtable.hpp diff --git a/gen_c_library/components/containers.array.hpp b/gen_c_library/components/containers.array.hpp new file mode 100644 index 0000000..45dc684 --- /dev/null +++ b/gen_c_library/components/containers.array.hpp @@ -0,0 +1,295 @@ +#pragma once + +#include "../project/gen.hpp" + +using namespace gen; + +CodeBody gen_array_base() +{ + CodeTypedef td_header = parse_typedef( code( typedef struct ArrayHeader ArrayHeader; )); + CodeStruct header = parse_struct( code( + struct ArrayHeader + { + AllocatorInfo Allocator; + usize Capacity; + usize Num; + }; + )); + + // Code grow_formula = untyped_str( txt( "#define gen_array_grow_formula( value ) ( 2 * value + 8 )\n" )); + Code get_header = untyped_str( txt( "#define array_get_header( Type, self ) ( (ArrayHeader*)( self ) - 1)\n" )); + + return def_global_body( args( td_header, header, get_header ) ); +}; + +CodeBody gen_array( StrC type, StrC array_name ) +{ + String array_type = String::fmt_buf( GlobalAllocator, "%.*s", array_name.Len, array_name.Ptr ); + String fn = String::fmt_buf( GlobalAllocator, "%.*s", array_name.Len, array_name.Ptr ); + str_to_lower(fn.Data); + +#pragma push_macro( "GEN_ASSERT" ) +#undef GEN_ASSERT + CodeBody result = parse_global_body( token_fmt( "array_type", (StrC)array_type, "fn", (StrC)fn, "type", (StrC)type + , stringize( + typedef * ; + + _init ( AllocatorInfo allocator ); + _init_reserve ( AllocatorInfo allocator, usize capacity ); + bool _append ( * self, value ); + bool _append_items ( * self, * items, usize item_num ); + bool _append_at ( * self, item, usize idx ); + bool _append_items_at( * self, * items, usize item_num, usize idx ); + * _back ( self ); + void _clear ( self ); + bool _fill ( self, usize begin, usize end, value ); + void _free ( self ); + bool _grow ( * self, usize min_capacity ); + usize _num ( self ); + _pop ( self ); + bool _reserve ( * self, usize new_capacity ); + bool _resize ( * self, usize num ); + bool _set_capacity ( * self, usize new_capacity ); + + _init( AllocatorInfo allocator ) + { + return _init_reserve( allocator, array_grow_formula( 0 ) ); + } + + _init_reserve( AllocatorInfo allocator, usize capacity ) + { + ArrayHeader* header = cast(ArrayHeader*, alloc( allocator, sizeof(ArrayHeader) + sizeof() * capacity ) ); + + if ( header == NULL ) + return NULL; + + header->Allocator = allocator; + header->Capacity = capacity; + header->Num = 0; + + return cast( *, header + 1 ); + } + + bool _append( * self, value ) + { + ArrayHeader* header = get_header( * self ); + + if ( header->Num == header->Capacity ) + { + if ( ! _grow( self, header->Capacity)) + return false; + + header = get_header( * self ); + } + + (* self)[ header->Num ] = value; + header->Num++; + + return true; + } + + bool _append_items( * self, * items, usize item_num ) + { + ArrayHeader* header = get_header( * self ); + + if ( header->Num + item_num > header->Capacity ) + { + if ( ! _grow( self, header->Capacity + item_num )) + return false; + + header = get_header( * self ); + } + + mem_copy( (* self) + header->Num, items, sizeof() * item_num ); + header->Num += item_num; + + return true; + } + + bool _append_at( * self, item, usize idx ) + { + ArrayHeader* header = get_header( * self ); + + if ( idx >= header->Num ) + idx = header->Num - 1; + + if ( idx < 0 ) + idx = 0; + + if ( header->Capacity < header->Num + 1 ) + { + if ( ! _grow( self, header->Capacity + 1 ) ) + return false; + + header = get_header( * self ); + } + + target = (* self) + idx; + + mem_move( target + 1, target, (header->Num - idx) * sizeof() ); + header->Num++; + + return true; + } + + bool _append_items_at( * self, * items, usize item_num, usize idx ) + { + ArrayHeader* header = get_header( * self ); + + if ( idx >= header->Num ) + { + return _append_items( self, items, item_num ); + } + + if ( item_num > header->Capacity ) + { + if ( ! _grow( self, item_num + header->Capacity ) ) + return false; + + header = get_header( * self ); + } + + * target = (* self) + idx + item_num; + * src = (* self) + idx; + + mem_move( target, src, (header->Num - idx) * sizeof() ); + mem_copy( src, items, item_num * sizeof() ); + header->Num += item_num; + + return true; + } + + * _back( self ) + { + ArrayHeader* header = get_header( self ); + + if ( header->Num == 0 ) + return NULL; + + return self + header->Num - 1; + } + + void _clear( self ) + { + ArrayHeader* header = get_header( self ); + header->Num = 0; + } + + bool _fill( self, usize begin, usize end, value ) + { + ArrayHeader* header = get_header( self ); + + if ( begin < 0 || end >= header->Num ) + return false; + + for ( ssize idx = begin; idx < end; idx ++ ) + self[ idx ] = value; + + return true; + } + + void _free( self ) + { + ArrayHeader* header = get_header( self ); + free( header->Allocator, header ); + self = NULL; + } + + bool _grow( * self, usize min_capacity ) + { + ArrayHeader* header = get_header( *self ); + usize new_capacity = array_grow_formula( header->Capacity ); + + if ( new_capacity < min_capacity ) + new_capacity = min_capacity; + + return _set_capacity( self, new_capacity ); + } + + usize _num( self ) + { + return get_header(self)->Num; + } + + _pop( self ) + { + ArrayHeader* header = get_header( self ); + GEN_ASSERT( header->Num > 0 ); + + result = self[ header->Num - 1 ]; + header->Num--; + return result; + } + + void _remove_at( self, usize idx ) + { + ArrayHeader* header = get_header( self ); + GEN_ASSERT( idx < header->Num ); + + mem_move( self + idx, self + idx + 1, sizeof( ) * ( header->Num - idx - 1 ) ); + header->Num--; + } + + bool _reserve( * self, usize new_capacity ) + { + ArrayHeader* header = get_header( * self ); + + if ( header->Capacity < new_capacity ) + return _set_capacity( self, new_capacity ); + + return true; + } + + bool _resize( * self, usize num ) + { + ArrayHeader* header = get_header( * self ); + + if ( header->Capacity < num ) + { + if ( ! _grow( self, num ) ) + return false; + + header = get_header( * self ); + } + + header->Num = num; + return true; + } + + bool _set_capacity( * self, usize new_capacity ) + { + ArrayHeader* header = get_header( * self ); + + if ( new_capacity == header->Capacity ) + return true; + + if ( new_capacity < header->Num ) + header->Num = new_capacity; + + usize size = sizeof( ArrayHeader ) + sizeof( ) * new_capacity; + ArrayHeader* new_header = cast( ArrayHeader*, alloc( header->Allocator, size )); + + if ( new_header == NULL ) + return false; + + mem_move( new_header, header, sizeof( ArrayHeader ) + sizeof( ) * header->Num ); + free( header->Allocator, & header ); + + new_header->Capacity = new_capacity; + * self = cast( *, new_header + 1 ); + return true; + } + ))); +#pragma pop_macro( "GEN_ASSERT" ) + + return def_global_body( args( + def_pragma( to_str( str_fmt_buf( "region %S", array_type ))), + fmt_newline, + result, + fmt_newline, + def_pragma( to_str( str_fmt_buf( "endregion %S", array_type ))), + fmt_newline + )); +}; + +// CodeBody gen_ diff --git a/gen_c_library/components/containers.hashtable.hpp b/gen_c_library/components/containers.hashtable.hpp new file mode 100644 index 0000000..6c460a3 --- /dev/null +++ b/gen_c_library/components/containers.hashtable.hpp @@ -0,0 +1,354 @@ +#pragma once + +#include "../project/gen.hpp" +#include "containers.array.hpp" + +using namespace gen; + +CodeBody gen_hashtable_base() +{ + return parse_global_body( code( + typedef struct HT_FindResult HT_FindResult; + struct HT_FindResult + { + ssize HashIndex; + ssize PrevIndex; + ssize EntryIndex; + }; + )); +} + +CodeBody gen_hashtable( StrC type, StrC hashtable_name ) +{ + String + fn = String::make_reserve( GlobalAllocator, hashtable_name.Len + sizeof("gen") ); + fn.append_fmt( "%.*s", hashtable_name.Len, hashtable_name.Ptr ); + str_to_lower(fn.Data); + + String + tbl_type = String::make_reserve( GlobalAllocator, hashtable_name.Len + sizeof("gen") ); + tbl_type.append_fmt( "%.*s", hashtable_name.Len, hashtable_name.Ptr ); + + String name_lower = String::make( GlobalAllocator, hashtable_name ); + str_to_lower( name_lower.Data ); + + String hashtable_entry = String::fmt_buf( GlobalAllocator, "HTE_%.*s", hashtable_name.Len, hashtable_name.Ptr ); + String entry_array_name = String::fmt_buf( GlobalAllocator, "Arr_HTE_%.*s", hashtable_name.Len, hashtable_name.Ptr ); + String entry_array_fn_ns = String::fmt_buf( GlobalAllocator, "arr_hte_%.*s", name_lower.length(), name_lower.Data ); + + CodeBody hashtable_types = parse_global_body( token_fmt( + "type", (StrC) type, + "tbl_name", (StrC) hashtable_name, + "tbl_type", (StrC) tbl_type, + stringize( + typedef struct HTE_ HTE_; + struct HTE_ + { + u64 Key; + ssize Next; + Value; + }; + + typedef void (* _MapProc) ( self, u64 key, value ); + typedef void (* _MapMutProc) ( self, u64 key, * value ); + ))); + + CodeBody entry_array = gen_array( hashtable_entry, entry_array_name ); + +#pragma push_macro( "GEN_ASSERT" ) +#pragma push_macro( "GEN_ASSERT_NOT_NULL" ) +#undef GEN_ASSERT +#undef GEN_ASSERT_NOT_NULL + CodeBody hashtable_def = parse_global_body( token_fmt( + "type", (StrC) type, + "tbl_name", (StrC) hashtable_name, + "tbl_type", (StrC) tbl_type, + "fn", (StrC) fn, + "entry_type", (StrC) hashtable_entry, + "array_entry", (StrC) entry_array_name, + "fn_array", (StrC) entry_array_fn_ns, + stringize( + typedef struct ; + struct + { + Array_ssize Hashes; + Entries; + }; + + _make ( AllocatorInfo allocator ); + _make_reserve( AllocatorInfo allocator, ssize num ); + void _clear ( self ); + void _destroy ( self ); + * _get ( self, u64 key ); + void _map ( self, _MapProc map_proc ); + void _map_mut ( self, _MapMutProc map_proc ); + void _grow ( * self ); + void _rehash ( * self, ssize new_num ); + void _rehash_fast ( self ); + void _remove ( self, u64 key ); + void _remove_entry( self, ssize idx ); + void _set ( * self, u64 key, value ); + ssize _slot ( self, u64 key ); + + ssize __add_entry( self, u64 key ); + HT_FindResult __find ( self, u64 key ); + b32 __full ( self ); + + _make( AllocatorInfo allocator ) + { + + result = { NULL, NULL }; + result.Hashes = array_ssize_make( allocator ); + result.Entries = _make( allocator ); + + return result; + } + + _make_reserve( AllocatorInfo allocator, ssize num ) + { + + result = { NULL, NULL }; + result.Hashes = array_ssize_make_reserve( allocator, num ); + result.Entries = _make_reserve( allocator, num ); + + return result; + } + + void _clear( self ) + { + for ( ssize idx = 0; idx < array_header( self.Hashes )->Num; idx++ ) + self.Hashes[idx] = -1; + + array_ssize_clear( self.Hashes ); + _clear( self.Entries ); + } + + void _destroy( self ) + { + if ( self.Hashes && self.Entries ) + { + array_ssize_free( self.Hashes ); + _free( self.Entries ); + } + } + + * _get( self, u64 key ) + { + ssize idx = __find( self, key ).EntryIndex; + if ( idx > 0 ) + return & self.Entries[idx].Value; + + return NULL; + } + + void _map( self, _MapProc map_proc ) + { + GEN_ASSERT_NOT_NULL( map_proc ); + + for ( ssize idx = 0; idx < array_header( self.Entries )->Num; idx++ ) + { + map_proc( self, self.Entries[idx].Key, self.Entries[idx].Value ); + } + } + + void _map_mut( self, _MapMutProc map_proc ) + { + GEN_ASSERT_NOT_NULL( map_proc ); + + for ( ssize idx = 0; idx < array_header( self.Entries )->Num; idx++ ) + { + map_proc( self, self.Entries[idx].Key, & self.Entries[idx].Value ); + } + } + + void _grow( * self ) + { + ssize new_num = array_grow_formula( array_header( self->Entries )->Num ); + _rehash( self, new_num ); + } + + void _rehash( * self, ssize new_num ) + { + ssize idx; + ssize last_added_index; + + ArrayHeader* old_hash_header = array_header( self->Hashes ); + ArrayHeader* old_entries_header = array_header( self->Entries ); + + new_tbl = _make_reserve( old_hash_header->Allocator, old_hash_header->Num ); + + ArrayHeader* new_hash_header = array_header( new_tbl.Hashes ); + + for ( idx = 0; idx < new_hash_header->Num; idx++ ) + new_tbl.Hashes[idx] = -1; + + for ( idx = 0; idx < old_entries_header->Num; idx++ ) + { + * entry; + HT_FindResult find_result; + + if ( new_hash_header->Num == 0 ) + _grow( & new_tbl ); + + entry = & self->Entries[ idx ]; + find_result = __find( new_tbl, entry->Key ); + last_added_index = __add_entry( new_tbl, entry->Key ); + + if ( find_result.PrevIndex < 0 ) + new_tbl.Hashes[ find_result.HashIndex ] = last_added_index; + else + new_tbl.Entries[ find_result.PrevIndex ].Next = last_added_index; + + new_tbl.Entries[ last_added_index ].Next = find_result.EntryIndex; + new_tbl.Entries[ last_added_index ].Value = entry->Value; + } + + _destroy( *self ); + * self = new_tbl; + } + + void _rehash_fast( self ) + { + ssize idx; + + for ( idx = 0; idx < array_header( self.Entries )->Num; idx++ ) + self.Entries[ idx ].Next = -1; + + for ( idx = 0; idx < array_header( self.Hashes )->Num; idx++ ) + self.Hashes[ idx ] = -1; + + for ( idx = 0; idx < array_header( self.Entries )->Num; idx++ ) + { + * entry; + HT_FindResult find_result; + + entry = & self.Entries[ idx ]; + find_result = __find( self, entry->Key ); + + if ( find_result.PrevIndex < 0 ) + self.Hashes[ find_result.HashIndex ] = idx; + else + self.Entries[ find_result.PrevIndex ].Next = idx; + } + } + + void _remove( self, u64 key ) + { + HT_FindResult find_result = __find( self, key ); + + if ( find_result.EntryIndex >= 0 ) + { + _remove_at( self.Entries, find_result.EntryIndex ); + _rehash_fast( self ); + } + } + + void _remove_entry( self, ssize idx ) + { + _remove_at( self.Entries, idx ); + } + + void _set( * self, u64 key, value ) + { + ssize idx; + HT_FindResult find_result; + + if ( array_header( self->Hashes )->Num == 0 ) + _grow( self ); + + find_result = __find( * self, key ); + + if ( find_result.EntryIndex >= 0 ) + { + idx = find_result.EntryIndex; + } + else + { + idx = __add_entry( * self, key ); + + if ( find_result.PrevIndex >= 0 ) + { + self->Entries[ find_result.PrevIndex ].Next = idx; + } + else + { + self->Hashes[ find_result.HashIndex ] = idx; + } + } + + self->Entries[ idx ].Value = value; + + if ( __full( * self ) ) + _grow( self ); + } + + ssize _slot( self, u64 key ) + { + for ( ssize idx = 0; idx < array_header( self.Hashes )->Num; ++idx ) + if ( self.Hashes[ idx ] == key ) + return idx; + + return -1; + } + + ssize __add_entry( self, u64 key ) + { + ssize idx; + entry = { key, -1 }; + + idx = array_header( self.Entries )->Num; + _append( & self.Entries, entry ); + return idx; + } + + HT_FindResult __find( self, u64 key ) + { + HT_FindResult result = { -1, -1, -1 }; + + ArrayHeader* hash_header = array_header( self.Hashes ); + + if ( hash_header->Num > 0 ) + { + result.HashIndex = key % hash_header->Num; + result.EntryIndex = self.Hashes[ result.HashIndex ]; + + while ( result.EntryIndex >= 0 ) + { + if ( self.Entries[ result.EntryIndex ].Key == key ) + break; + + result.PrevIndex = result.EntryIndex; + result.EntryIndex = self.Entries[ result.EntryIndex ].Next; + } + } + + return result; + } + + b32 __full( self ) + { + ArrayHeader* hash_header = array_header( self.Hashes ); + ArrayHeader* entries_header = array_header( self.Entries ); + + return 0.75f * hash_header->Num < entries_header->Num; + } + ))); +#pragma pop_macro( "GEN_ASSERT" ) +#pragma pop_macro( "GEN_ASSERT_NOT_NULL" ) + + char const* cmt_str = str_fmt_buf( "Name: %.*s Type: %.*s" + , tbl_type.length(), tbl_type.Data + , type.Len, type.Ptr ); + + return def_global_body(args( + def_pragma( to_str( str_fmt_buf( "region %S", tbl_type ))), + fmt_newline, + hashtable_types, + fmt_newline, + entry_array, + hashtable_def, + fmt_newline, + def_pragma( to_str( str_fmt_buf( "endregion %S", tbl_type ))), + fmt_newline + )); +}