From b4b518f005236d95a7935d1fde0c50f2f69fa35f Mon Sep 17 00:00:00 2001 From: Ed_ Date: Sun, 2 Apr 2023 12:35:14 -0400 Subject: [PATCH] WIP: Array generation test. Still need gen.cpp impl. --- Readme.md | 27 ++- project/Bloat.hpp | 14 ++ project/gen.cpp | 114 +++++++++---- project/gen.hpp | 34 ++-- test/Array.cpp | 3 + test/Array.hpp | 376 ++++++++++++++++++++++++++++++++++++++++++ test/gen/math.gen.hpp | 24 --- test/math.hpp | 27 ++- test/test.cpp | 2 + thirdparty/zpl.h | 49 +++--- 10 files changed, 566 insertions(+), 104 deletions(-) create mode 100644 test/Array.cpp create mode 100644 test/Array.hpp delete mode 100644 test/gen/math.gen.hpp diff --git a/Readme.md b/Readme.md index 164a361..0bf8bf5 100644 --- a/Readme.md +++ b/Readme.md @@ -9,15 +9,16 @@ Version 1 will have c and a subset of c++ features available to it. A metaprogram is built to generate files before the main program is built. We'll term runtime for this program as `gen_time`. The metaprogram's core implementation are within `gen.hpp` and `gen.cpp` in the project directory. -`gen.cpp` \`s `main()` will expect to call one `gen_main()` which the user will have to define once for their program. There they will dictate everything that should be generated. +`gen.cpp` \`s `main()` is defined as `gen_main()` which the user will have to define once for their program. There they will dictate everything that should be generated. In order to keep the locality of this code within the same files the following pattern may be used: Within `program.cpp` : ```cpp -#ifdef gen_time #include "gen.hpp" +#ifdef gen_time + ... u32 gen_main() @@ -37,19 +38,31 @@ This is ofc entirely optional and the metaprogram can be quite separated from th ## Building -To fill in. +An example of building is provided in the test directory. + +There are two meson build files the one within test is the program's build specification. +The other one in the gen directory within test is the metaprogram's build specification. + +Both of them build the same source file: `test.cpp`. The only differences between them is that gen need a different relative path to the include directories and defines the macro definition: `gen_time`. + +This method is setup where all the metaprogram's code are the within the same files as the regular program's code. +If in your use case, decide to have exclusive separation or partial separation of the metaprogam's code from the program's code files then your build configuration would need to change to reflect that (specifically the sources). ## Why Macros in c/c++ are usually painful to debug, and templates can be unless your on a monsterous IDE (and even then fail often). -Unfortunately most programming langauges opt the approach of internally processing the generated code immediately within the AST or not expose it to the user in a nice way to even introspect as a text file. Stage metaprogramming doesn't have this problem, since its entire purpose is to create those generated files. +Templates also have a heavy cost to compile-times due to their recursive nature of expansion if complex code is getting generated, or if heavy type checking system is used (assertsion require expansion, etc). -This is technically what the macro preprocessor does in a basic form, however naturally its easier to deal with for more complex generation. +Unfortunately most programming langauges opt the approach of internally processing the generated code immediately within the AST or not expose it to the user in a nice way to even introspect as a text file. -The drawback naturally is generation functions at face value harder to grasp than something following a template pattern (for simple generation). This drawback becomes less valid the more complex the code generation becomes. +Stage metaprogramming doesn't have this problem, since its entire purpose is to create those generated files that the final program will reference instead. -Thus a rule of thumb is if its a simple define you can get away with just the preprocessor, or if the templates being used don't break the debugger, this is most likely not neded. +This is technically what the macro preprocessor does in a basic form, however a proper metaprogram for generation is easier to deal with for more complex generation. + +The drawback naturally is generation functions, at face value, are harder to grasp than something following a template pattern (for simple generation). This drawback becomes less valid the more complex the code generation becomes. + +Thus a rule of thumb is if its a simple definition you can get away with just the preprocessor `#define`, or if the templates being used don't break the debugger or your compile times, this is most likely not neded. However, if the code being generated becomes complex, or from a datatable or database, this will be easier to deal with. diff --git a/project/Bloat.hpp b/project/Bloat.hpp index dde36a2..d4f718a 100644 --- a/project/Bloat.hpp +++ b/project/Bloat.hpp @@ -57,6 +57,7 @@ #define scast( Type_, Value_ ) static_cast< Type_ >( Value_ ) #define rcast( Type_, Value_ ) reinterpret_cast< Type_ >( Value_ ) #define pcast( Type_, Value_ ) ( * (Type_*)( & Value_ ) ) +#define txt( Value_ ) ZPL_STRINGIFY_EX( Value_ ) #define do_once() \ do \ @@ -69,6 +70,19 @@ do \ } \ while(0) \ +#define do_once_start \ +do \ +{ \ + static \ + bool Done = false; \ + if ( Done ) \ + break; \ + Done = true; + +#define do_once_end \ +} \ +while(0); + using Line = char*; using Array_Line = array( Line ); diff --git a/project/gen.cpp b/project/gen.cpp index 63ede1b..5719430 100644 --- a/project/gen.cpp +++ b/project/gen.cpp @@ -1,15 +1,21 @@ #include "Bloat.hpp" #include "gen.hpp" +#define gen_time #ifdef gen_time namespace gen { + void init() + { + + } + ct Code make() { return { Code::Invalid, nullptr, nullptr, { nullptr } }; } - Code decl_type( char const* name, Code specifiers, Code type ) + Code decl_type( char const* name, Code type, Code specifiers ) { Code result = make(); @@ -47,7 +53,7 @@ namespace gen return result; } - Code make_parameters( s32 num, ... ) + Code def_parameters( s32 num, ... ) { if (num <= 0) fatal("TT::make_paramters: num is %d", num); @@ -85,30 +91,12 @@ namespace gen return result; } - Code make_fmt(char const* fmt, ...) - { - local_persist thread_local - char buf[ZPL_PRINTF_MAXLEN] = { 0 }; - - va_list va; - va_start(va, fmt); - zpl_snprintf_va(buf, ZPL_PRINTF_MAXLEN, fmt, va); - va_end(va); - - Code - result = make(); - result.Name = string_make( g_allocator, fmt ); - result.Type = Code::Untyped; - result.Content = string_make( g_allocator, buf ); - - return result; - } - - Code make_function( char const* name + Code def_function( char const* name , Code specifiers , Code params , Code ret_type - , Code body ) + , Code body + ) { Code result = make(); @@ -130,7 +118,22 @@ namespace gen return result; } - Code make_specifiers( u32 num, ... ) + Code def_function_body( u32 num, ... ) + { + + } + + Code def_namespace( char const* name, Code body ) + { + + } + + Code def_namespace_body( u32 num, ... ) + { + + } + + Code def_specifiers( u32 num, ... ) { if ( num <= 0 ) fatal("gen::make_specifier: num cannot be zero."); @@ -165,7 +168,22 @@ namespace gen return result; } - Code make_type( char const* name ) + Code def_struct( char const* name, Code body, Code parent, Code specifiers ) + { + + } + + Code def_struct_body( u32 num, ... ) + { + + } + + Code def_variable( char const* name, Code type, Code value, Code specifiers ) + { + + } + + Code def_type( char const* name ) { Code result = make(); @@ -175,6 +193,33 @@ namespace gen return result; } + + Code untyped_fmt(char const* fmt, ...) + { + local_persist thread_local + char buf[ZPL_PRINTF_MAXLEN] = { 0 }; + + va_list va; + va_start(va, fmt); + zpl_snprintf_va(buf, ZPL_PRINTF_MAXLEN, fmt, va); + va_end(va); + + Code + result = make(); + result.Name = string_make( g_allocator, fmt ); + result.Type = Code::Untyped; + result.Content = string_make( g_allocator, buf ); + + return result; + } + + Code token_fmt( char const* fmt, ... ) + { + + } + + + string Code::to_string() { string result = string_make( g_allocator, "" ); @@ -232,6 +277,9 @@ namespace gen } break; + case Function_Body: + break; + case Parameters: { result = string_append_fmt( result, "%s %s", Entries[0].to_string(), Name ); @@ -247,10 +295,6 @@ namespace gen } break; - case Struct: - fatal("NOT SUPPORTED YET"); - break; - case Function: { u32 index = 0; @@ -288,8 +332,16 @@ namespace gen result = string_append_fmt( result, "%s", Content ); break; + case Struct: + fatal("NOT SUPPORTED YET"); + break; + + case Struct_Body: + fatal("NOT SUPPORTED YET"); + break; + case Variable: - // result = string_append_fmt( result, "%s", ) + fatal("NOT SUPPORTED YET"); break; case Typename: @@ -300,6 +352,8 @@ namespace gen return result; } + + void Builder::print( Code code ) { Buffer = string_append_fmt( Buffer, "%s\n\n", code.to_string() ); diff --git a/project/gen.hpp b/project/gen.hpp index 2b4791b..1b900e8 100644 --- a/project/gen.hpp +++ b/project/gen.hpp @@ -20,6 +20,7 @@ namespace gen API_Export, // Vendor specific way to dynamic export External_Linkage, // extern Internal_Linkage, // static (within unit file) + Static_Member, // static (within sturct/class) Local_Persist, // static (within function) Thread_Local, // thread_local @@ -62,7 +63,9 @@ namespace gen Decl_Function, Parameters, // Used with functions. Struct, + Struct_Body, Function, + Function_Body, Specifiers, Variable, Typename, @@ -172,7 +175,11 @@ namespace gen Using_Code_POD; }; - Code decl_type( char const* name, Code specifiers, Code type); + constexpr Code UnusedCode = { Code::Unused, nullptr, nullptr, { nullptr } }; + + void init(); + + Code decl_type( char const* name, Code type, Code specifiers = UnusedCode ); Code decl_fn( char const* name , Code specifiers @@ -180,26 +187,33 @@ namespace gen , Code ret_type ); - Code make_parameters( s32 num, ... ); - - Code make_fmt( char const* fmt, ... ); + Code def_parameters( s32 num, ... ); - Code make_function( char const* name + Code def_function( char const* name , Code specifiers , Code params , Code ret_type , Code body ); + Code def_function_body( u32 num, ... ); - Code make_specifiers( u32 num , ... ); + Code def_namespace( char const* name, Code body ); + Code def_namespace_body( u32 num, ... ); - // Code make_variable( char const* name, char const* type ); + Code def_specifiers( u32 num , ... ); - // Code make_template( Code subject, u32 num_dependents, ... ); + Code def_struct( char const* name, Code body, Code parent = UnusedCode, Code specifiers = UnusedCode ); + Code def_struct_body( u32 num, ... ); - Code make_type( char const* name ); + Code def_variable( char const* name, Code type, Code value = UnusedCode, Code specifiers = UnusedCode ); - // Code make_using( char const* name, char const* type ); + Code def_type( char const* name ); + + Code def_using( char const* name, Code type ); + + Code untyped_fmt( char const* fmt, ... ); + + Code token_fmt( char const* fmt, ... ); struct Builder diff --git a/test/Array.cpp b/test/Array.cpp new file mode 100644 index 0000000..6ffa06c --- /dev/null +++ b/test/Array.cpp @@ -0,0 +1,3 @@ +#include Array.cpp + + diff --git a/test/Array.hpp b/test/Array.hpp new file mode 100644 index 0000000..6ccc4f4 --- /dev/null +++ b/test/Array.hpp @@ -0,0 +1,376 @@ +/* + This is based of the array container implementation in the zpl.h library. +*/ + +#pragma once + +#include "Bloat.hpp" +#include "gen.hpp" + +#ifdef gen_time + using namespace gen; + + Code gen__array_base() + { + // Make these global consts to be accessed anywhere... + Code t_sw = def_type( txt(sw) ); + Code t_uw = def_type( txt(uw) ); + Code t_allocator = def_type( txt(allocator) ); + + Code header; + { + Code num = def_variable( "Num", t_uw ); + Code capacity = def_variable( "Capacity", t_uw ); + Code allocator_var = def_variable( "Allocator", t_allocator ); + Code header_body = def_struct_body( 3, num, capacity, allocator_var ); + + header = def_struct( "ArrayHeader", header_body ); + } + + Code grow_formula; + { + Code spec = def_specifiers(1, Specifier::Inline); + Code params = def_parameters(1, "value", t_uw ); + Code body = untyped_fmt( "\t""return 2 * value * 8;" ); + + grow_formula = def_function( "grow_formula", spec, params, t_sw, body ); + } + + Code base_body = def_struct_body(2, header, grow_formula); + Code base = def_struct( "ArrayBase", base_body ); + return base; + } + + #define gen_array( Type_ ) gen__array( #Type_, sizeof(Type_), a_base ) + Code gen__array( char const* type_str, s32 type_size, Code parent ) + { + // Make these global consts to be accessed anywhere... + Code t_uw = def_type( txt(uw) ); + Code t_sw = def_type( txt(sw) ); + Code t_bool = def_type( txt(bool) ); + Code t_allocator = def_type( txt(allocator) ); + Code t_void = def_type( txt(void) ); + + Code v_nullptr = untyped_fmt( "nullptr" ); + + Code spec_ct = def_specifiers(1, Specifier::Constexpr ); + Code spec_inline = def_specifiers(1, Specifier::Inline ); + + Code type = def_type( type_str ); + Code ptr_type = def_type( string_sprintf_buf( g_allocator, "%s*", type_str ) ); + Code ref_type = def_type( string_sprintf_buf( g_allocator, "%s&", type_str ) ); + + string name = string_sprintf_buf( g_allocator, "Array_%s", type_str ); + + // From ArrayBase + Code t_header = def_type( "Header" ); + Code ptr_header = def_type( "Header*" ); + Code ref_header = def_type( "Header&" ); + + Code array_def; + { + Code using_type = def_using( "type", type ); + Code data = def_variable( "Data", ptr_type ); + + Code init; + { + Code params = def_parameters( 1, "mem_hanlder", t_allocator ); + Code body = untyped_fmt( "\t""return init_reserve( mem_handler, grow_formula(0) );" ); + + init = def_function( "init", UnusedCode, params, t_bool, body ); + } + + Code init_reserve; + { + Code params = def_parameters( 2, "mem_handler", ref_type, "capacity", t_sw ); + Code body; + { + Code header_value = untyped_fmt( + "rcast( Header*, alloc( mem_handler, sizeof( Header ) + sizeof(type) + capacity ))" + ); + Code header = def_variable( "", ptr_header, header_value ); + + Code null_check = untyped_fmt( + "\t" "if (header == nullptr)" + "\n\t\t" "return false;" + ); + + Code header_init = untyped_fmt( + "\n\t" "header->Num = 0;" + "\n\t" "header->Capacity = capacity;" + "\n\t" "header->Allocator = mem_handler;" + ); + + Code assign_data = untyped_fmt( + "\t" "Data = rcast( %s, header + 1 );", ptr_type + ); + + Code ret_true = untyped_fmt( "\t""return true" ); + + body = def_function_body( 5 + , header + , null_check + , header_init + , assign_data + , ret_true + ); + } + + init_reserve = def_function( "init_reserve", UnusedCode, params, t_bool, body ); + } + + Code free; + { + Code body = untyped_fmt( + "\t" "Header& header = get_header();" + "\n\t" "::free( header.Allocator, & get_header() );" + ); + + free = def_function( "free", UnusedCode, UnusedCode, t_void, body ); + } + + Code append; + { + Code params = def_parameters( 1, "value", type ); + Code body; + { + Code header = def_variable( "", ref_header, untyped_fmt( "get_header()") ); + + Code check_cap = untyped_fmt( + "\t" "if ( header.Capacity < header.Num + 1 )" + "\n\t\t" "if ( ! grow(0) )" + "\n\t\t\t" "return false;" + ); + + Code assign = untyped_fmt( + "\t" "Data[ header.Num ] = value;" + "\t\n" "header.Num++;" + "\n" + "\n\t" "return true;" + ); + + body = def_function_body( 3, header, check_cap, assign ); + } + + append = def_function( "append", UnusedCode, params, t_void, body ); + } + + Code back; + { + Code body = untyped_fmt( + "\t" "Header& header = get_header();" + "\n\t" "return data[ header.Num - 1 ];" + ); + + back = def_function( "back", UnusedCode, UnusedCode, type, body ); + } + + Code clear; + { + Code body = untyped_fmt( "\t""get_header().Num = 0;" ); + + clear = def_function( "clear", UnusedCode, UnusedCode, t_void, body ); + } + + Code fill; + { + Code params = def_parameters( 3, "begin", t_uw, "end", t_uw, "value", type ); + Code body; + { + Code header = def_variable( "", ref_header, untyped_fmt( "get_header()") ); + + Code check = untyped_fmt( + "\t" "if ( begin < 0 || end >= header.Num )" + "\n\t\t" "fatal( \"Range out of bounds\" );" + ); + + Code iter = untyped_fmt( + "\t" "for ( sw index = begin; index < end; index++ )" + "\n\t\t" "Data[index] = vallue;" + ); + + body = def_function_body( 3, header, check, iter ); + } + + fill = def_function( "fill", UnusedCode, params, t_void, body ); + } + + Code get_header; + { + Code body = untyped_fmt( "\t""return pcast( Header, Data - 1 );" ); + + get_header = def_function( "get_header", spec_inline, UnusedCode, ref_header, body ); + } + + Code grow; + { + Code param = def_parameters( 1, "min_capacity", t_uw ); + Code body; + { + Code header = def_variable( "header", ref_header, untyped_fmt("get_header") ); + Code new_capacity = def_variable( "new_capacity", t_uw, untyped_fmt("grow_formula( header.Capacity )") ); + + Code check_n_set = untyped_fmt( + "\t" "if ( new_capacity < min_capacity )" + "\n\t\t" "new_capacity = min_capacity;" + ); + + Code ret = untyped_fmt( "\t" "return set_capacity( new_capacity );" ); + + body = def_function_body( 4, header, new_capacity, check_n_set, ret ); + } + + grow = def_function( "grow", UnusedCode, param, t_bool, body ); + } + + Code pop; + { + Code body; + { + Code header = def_variable( "header", ref_header, untyped_fmt("get_header()") ); + + Code assertion = untyped_fmt( "\t" "assert( header.Num > 0 );" ); + Code decrement = untyped_fmt( "\t" "header.Num--; " ); + + body = def_function_body( 3, header, assertion, decrement ); + } + + pop = def_function( "pop", UnusedCode, UnusedCode, t_void, body ); + } + + Code reserve; + { + Code params = def_parameters( 1, "new_capacity", t_uw ); + Code body; + { + Code header = def_variable( "header", ref_header, untyped_fmt("get_header()") ); + + Code check_n_set = untyped_fmt( + "\t" "if ( header.Capacity < new_capacity )" + "\n\t\t" "return set_capacity( new_capacity );" + ); + + Code ret = untyped_fmt( "\t" "return true" ); + + body = def_function_body( 3, header, check_n_set, ret ); + } + + reserve = def_function( "reserve", UnusedCode, params, t_bool, body ); + } + + Code resize; + { + Code param = def_parameters( 1, "new_num", t_uw ); + + Code body; + { + Code header = def_variable( "header", ref_header, untyped_fmt("get_header()") ); + + Code check_n_grow = untyped_fmt( + "\t" "if ( header.Capacity < new_num )" + "\n\t\t" "if ( ! grow( new_num) )" + "\n\t\t\t" "return false;" + ); + + Code set_n_ret = untyped_fmt( + "\t" "header.Count = new_num;" + "\n\t" "return true;" + ); + + body = def_function_body( 3, header, check_n_grow, set_n_ret ); + } + + resize = def_function( "resize", UnusedCode, param, t_bool, body ); + } + + Code set_capacity; + { + Code param = def_parameters( 1, "capacity", t_uw ); + + Code body; + { + Code header = def_variable( "header", ref_header, untyped_fmt("get_header()") ); + + Code checks = untyped_fmt( + "\t" "if ( capacity == header.Capacity )" + "\n\t\t" "return true;" + "\n" + "\n\t" "if ( capacity < header.Num )" + "\n\t\t" "header.Num = capacity;" + ); + + Code size = def_variable( "size", t_uw, untyped_fmt("sizeof(Header) + sizeof(type) * capacity")); + Code new_header = def_variable( "new_header", ptr_header, untyped_fmt("rcast( Header*, alloc( header.Allocator, size ));")); + + Code check_n_move = untyped_fmt( + "\t""if ( new_header == nullptr )" + "\n\t\t""return false;" + "\n" + "\n\t""memmove( new_header, & header, sizeof(Header) + sizeof(type) * header.Num );" + ); + + Code set_free_ret = untyped_fmt( + "\t" "new_header->Allocator = header.Allocator;" + "\n\t" "new_header->Num = header.Num;" + "\n\t" "new_header->Capacity = header.Capacity;" + "\n" + "\n\t" "zpl_free( header );" + "\n" + "\n\t" "*Data = new_header + 1;" + "\n" + "\n\t" "return true;" + ); + + body = def_function_body( 6, header, checks, size, new_header, check_n_move, set_free_ret ); + } + + set_capacity = def_function( "set_capacity", UnusedCode, param, t_bool, body ); + } + + Code body = def_struct_body( 15 + , using_type + , data + + , init + , init_reserve + , append + , back + , clear + , fill + , free + , get_header + , grow + , pop + , reserve + , resize + , set_capacity + ); + + array_def = def_struct( name, body, parent ); + } + + return array_def; + } + + u32 gen_array_file() + { + Code a_base = gen__array_base(); + + Code a_u32 = gen_array( u32 ); + Code a_cstr = gen_array( char const* ); + + Builder + arraygen; + arraygen.open( "Array.gen.hpp" ); + arraygen.print( a_u32 ); + arraygen.print( a_cstr ); + arraygen.write(); + return 0; + } +#endif + +#ifndef gen_time +# include "Array.gen.hpp" + +# define array( Type_ ) array_##Type_ +#endif diff --git a/test/gen/math.gen.hpp b/test/gen/math.gen.hpp deleted file mode 100644 index 4c95f93..0000000 --- a/test/gen/math.gen.hpp +++ /dev/null @@ -1,24 +0,0 @@ -inline -u8 square(u8 value) -{ - return value * value; -} - -inline -u16 square(u16 value) -{ - return value * value; -} - -inline -u32 square(u32 value) -{ - return value * value; -} - -inline -u64 square(u64 value) -{ - return value * value; -} - diff --git a/test/math.hpp b/test/math.hpp index 913cdd7..8d5f6c5 100644 --- a/test/math.hpp +++ b/test/math.hpp @@ -12,7 +12,7 @@ What it should generate: inline - type square_#type( type value ) + type square( type value ) { return value * value; } @@ -20,19 +20,18 @@ #define gen_square( Type_ ) gen__square( #Type_ ) Code gen__square( char const* type ) { - Code integral_type = make_type( type ); + Code integral_type = def_type( type ); - string name = string_sprintf( g_allocator, (char*)sprintf_buf, ZPL_PRINTF_MAXLEN, "square", type ); - Code specifiers = make_specifiers( 1, Specifier::Inline ); - Code params = make_parameters( 1, "value", integral_type ); - Code ret_stmt = make_fmt( "\treturn value * value;" ); + string name = string_sprintf( g_allocator, (char*)sprintf_buf, ZPL_PRINTF_MAXLEN, "square", type ); - Code result = make_function( name, - specifiers, - params, - integral_type, - ret_stmt - ); + Code result; + { + Code params = def_parameters( 1, "value", integral_type ); + Code specifiers = def_specifiers( 1, Specifier::Inline ); + Code ret_stmt = untyped_fmt( "\treturn value * value;" ); + + result = def_function( name, specifiers, params, integral_type, ret_stmt ); + } if ( ! result ) fatal( "Failed to generate square function for: %s", type ); @@ -60,7 +59,7 @@ #endif #ifndef gen_time - #include "math.gen.hpp" - #undef square +# include "math.gen.hpp" +# undef square #endif diff --git a/test/test.cpp b/test/test.cpp index 4049250..74a2853 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -12,6 +12,8 @@ int gen_main() zpl_printf("\nPress any key after attaching to process\n"); getchar(); + gen::init() + int result = gen_math(); Memory::cleanup(); diff --git a/thirdparty/zpl.h b/thirdparty/zpl.h index e14afe4..1e77756 100644 --- a/thirdparty/zpl.h +++ b/thirdparty/zpl.h @@ -3948,24 +3948,28 @@ License: } \ } while (0) - ZPL_IMPL_INLINE b8 zpl__array_set_capacity(void **a_array, sw capacity) { - array_header *h = ZPL_ARRAY_HEADER(*a_array); - if (capacity == h->capacity) return true; - if (capacity < h->count) h->count = capacity; - sw size = size_of(array_header) + h->elem_size * capacity; - array_header *nh = zpl_cast(array_header *) alloc(h->allocator, size); - if (!nh) return false; - zpl_memmove(nh, h, size_of(array_header) + h->elem_size * h->count); - nh->allocator = h->allocator; - nh->elem_size = h->elem_size; - nh->count = h->count; - nh->capacity = capacity; - free(h->allocator, h); - *a_array = nh + 1; - return true; - } - - #define array_set_capacity(x, capacity) zpl__array_set_capacity(zpl_cast(void **) & (x), (capacity)) + ZPL_IMPL_INLINE b8 zpl__array_set_capacity( void** a_array, sw capacity ) + { + array_header* h = ZPL_ARRAY_HEADER( *a_array ); + if ( capacity == h->capacity ) + return true; + if ( capacity < h->count ) + h->count = capacity; + sw size = size_of( array_header ) + h->elem_size * capacity; + array_header* nh = zpl_cast( array_header* ) alloc( h->allocator, size ); + if ( ! nh ) + return false; + zpl_memmove( nh, h, size_of( array_header ) + h->elem_size * h->count ); + nh->allocator = h->allocator; + nh->elem_size = h->elem_size; + nh->count = h->count; + nh->capacity = capacity; + free( h->allocator, h ); + *a_array = nh + 1; + return true; + } + + #define array_set_capacity( x, capacity ) zpl__array_set_capacity( zpl_cast( void** ) & ( x ), ( capacity ) ) ZPL_IMPL_INLINE b8 zpl__array_grow(void **x, sw min_capacity) { sw new_capacity = ZPL_ARRAY_GROW_FORMULA(array_capacity(*x)); @@ -3982,7 +3986,14 @@ License: return true; } - #define array_append(x, item) (zpl__array_append_helper(zpl_cast(void **) & (x)) && (((x)[array_count(x)++] = (item)), true)) + #define array_append(x, item) \ + ( \ + zpl__array_append_helper(zpl_cast(void **) & (x)) \ + && ( \ + ((x)[array_count(x)++] = (item)) \ + , true \ + ) \ + ) ZPL_IMPL_INLINE b8 zpl__array_append_at_helper(void **x, sw ind) { if (ind >= array_count(*x)) ind = array_count(*x) - 1;