diff --git a/project/Bloat.cpp b/project/Bloat.cpp deleted file mode 100644 index 535130d..0000000 --- a/project/Bloat.cpp +++ /dev/null @@ -1,1757 +0,0 @@ -#define BLOAT_IMPL -#include "Bloat.hpp" - -#pragma region Macros -# include - -// NOTE: Ensure we use standard methods for these calls if we use ZPL_PICO -# if ! defined( ZPL_PICO_CUSTOM_ROUTINES ) -# if ! defined( ZPL_MODULE_CORE ) -# define _strlen strlen -# define _printf_err( fmt, ... ) fprintf( stderr, fmt, __VA_ARGS__ ) -# define _printf_err_va( fmt, va ) vfprintf( stderr, fmt, va ) -# else -# define _strlen str_len -# define _printf_err( fmt, ... ) str_fmt_out_err( fmt, __VA_ARGS__ ) -# define _printf_err_va( fmt, va ) str_fmt_out_err_va( fmt, va ) -# endif -# endif - -# include - -# if defined( ZPL_SYSTEM_UNIX ) || defined( ZPL_SYSTEM_MACOS ) -# include -# elif defined( ZPL_SYSTEM_WINDOWS ) -# if ! defined( ZPL_NO_WINDOWS_H ) -# ifndef WIN32_LEAN_AND_MEAN -# ifndef NOMINMAX -# define NOMINMAX -# endif - -# define WIN32_LEAN_AND_MEAN -# define WIN32_MEAN_AND_LEAN -# define VC_EXTRALEAN -# endif -# include -# undef NOMINMAX -# undef WIN32_LEAN_AND_MEAN -# undef WIN32_MEAN_AND_LEAN -# undef VC_EXTRALEAN -# endif -# endif -#pragma endregion Macros - - -namespace gen -{ -#pragma region Debug - void assert_handler( char const* condition, char const* file, s32 line, char const* msg, ... ) - { - _printf_err( "%s:(%d): Assert Failure: ", file, line ); - - if ( condition ) - _printf_err( "`%s` ", condition ); - - if ( msg ) - { - va_list va; - va_start( va, msg ); - _printf_err_va( msg, va ); - va_end( va ); - } - - _printf_err( "%s", "\n" ); - } - - s32 assert_crash( char const* condition ) - { - ZPL_PANIC( condition ); - return 0; - } - - #if defined( ZPL_SYSTEM_WINDOWS ) - void process_exit( u32 code ) - { - ExitProcess( code ); - } - #else - # include - - void process_exit( u32 code ) - { - exit( code ); - } - #endif -#pragma endregion Debug - -#pragma region String Ops - internal sw _scan_zpl_i64( const char* text, s32 base, s64* value ) - { - const char* text_begin = text; - s64 result = 0; - b32 negative = false; - - if ( *text == '-' ) - { - negative = true; - text++; - } - - if ( base == 16 && str_compare( text, "0x", 2 ) == 0 ) - text += 2; - - for ( ;; ) - { - s64 v; - if ( char_is_digit( *text ) ) - v = *text - '0'; - else if ( base == 16 && char_is_hex_digit( *text ) ) - v = hex_digit_to_int( *text ); - else - break; - - result *= base; - result += v; - text++; - } - - if ( value ) - { - if ( negative ) - result = -result; - *value = result; - } - - return ( text - text_begin ); - } - - // TODO : Are these good enough for characters? - global const char _num_to_char_table[] = - "0123456789" - "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "abcdefghijklmnopqrstuvwxyz" - "@$"; - - s64 str_to_i64( const char* str, char** end_ptr, s32 base ) - { - sw len; - s64 value; - - if ( ! base ) - { - if ( ( str_len( str ) > 2 ) && ( str_compare( str, "0x", 2 ) == 0 ) ) - base = 16; - else - base = 10; - } - - len = _scan_zpl_i64( str, base, &value ); - if ( end_ptr ) - *end_ptr = ( char* )str + len; - return value; - } - - void i64_to_str( s64 value, char* string, s32 base ) - { - char* buf = string; - b32 negative = false; - u64 v; - - if ( value < 0 ) - { - negative = true; - value = -value; - } - - v = zpl_cast( u64 ) value; - if ( v != 0 ) - { - while ( v > 0 ) - { - *buf++ = _num_to_char_table[ v % base ]; - v /= base; - } - } - else - { - *buf++ = '0'; - } - if ( negative ) - *buf++ = '-'; - *buf = '\0'; - str_reverse( string ); - } - - void u64_to_str( u64 value, char* string, s32 base ) - { - char* buf = string; - - if ( value ) - { - while ( value > 0 ) - { - *buf++ = _num_to_char_table[ value % base ]; - value /= base; - } - } - else - { - *buf++ = '0'; - } - *buf = '\0'; - - str_reverse( string ); - } -#pragma endregion String Ops - -#pragma region Memory - void* mem_copy( void* dest, void const* source, sw n ) - { - if ( dest == NULL ) - { - return NULL; - } - - return memcpy( dest, source, n ); - } - - void const* mem_find( void const* data, u8 c, sw n ) - { - u8 const* s = zpl_cast( u8 const* ) data; - while ( ( zpl_cast( uptr ) s & ( sizeof( uw ) - 1 ) ) && n && *s != c ) - { - s++; - n--; - } - if ( n && *s != c ) - { - sw const* w; - sw k = ZPL__ONES * c; - w = zpl_cast( sw const* ) s; - while ( n >= size_of( sw ) && ! ZPL__HAS_ZERO( *w ^ k ) ) - { - w++; - n -= size_of( sw ); - } - s = zpl_cast( u8 const* ) w; - while ( n && *s != c ) - { - s++; - n--; - } - } - - return n ? zpl_cast( void const* ) s : NULL; - } - - #define ZPL_HEAP_STATS_MAGIC 0xDEADC0DE - - struct _heap_stats - { - u32 magic; - sw used_memory; - sw alloc_count; - }; - - global _heap_stats _heap_stats_info; - - void heap_stats_init( void ) - { - zero_item( &_heap_stats_info ); - _heap_stats_info.magic = ZPL_HEAP_STATS_MAGIC; - } - - sw heap_stats_used_memory( void ) - { - ZPL_ASSERT_MSG( _heap_stats_info.magic == ZPL_HEAP_STATS_MAGIC, "heap_stats is not initialised yet, call heap_stats_init first!" ); - return _heap_stats_info.used_memory; - } - - sw heap_stats_alloc_count( void ) - { - ZPL_ASSERT_MSG( _heap_stats_info.magic == ZPL_HEAP_STATS_MAGIC, "heap_stats is not initialised yet, call heap_stats_init first!" ); - return _heap_stats_info.alloc_count; - } - - void heap_stats_check( void ) - { - ZPL_ASSERT_MSG( _heap_stats_info.magic == ZPL_HEAP_STATS_MAGIC, "heap_stats is not initialised yet, call heap_stats_init first!" ); - ZPL_ASSERT( _heap_stats_info.used_memory == 0 ); - ZPL_ASSERT( _heap_stats_info.alloc_count == 0 ); - } - - struct _heap_alloc_info - { - sw size; - void* physical_start; - }; - - void* heap_allocator_proc( void* allocator_data, AllocType type, sw size, sw alignment, void* old_memory, sw old_size, u64 flags ) - { - void* ptr = NULL; - // unused( allocator_data ); - // unused( old_size ); - if ( ! alignment ) - alignment = ZPL_DEFAULT_MEMORY_ALIGNMENT; - - #ifdef ZPL_HEAP_ANALYSIS - sw alloc_info_size = size_of( _heap_alloc_info ); - sw alloc_info_remainder = ( alloc_info_size % alignment ); - sw track_size = max( alloc_info_size, alignment ) + alloc_info_remainder; - switch ( type ) - { - case EAllocation_FREE : - { - if ( ! old_memory ) - break; - _heap_alloc_info* alloc_info = zpl_cast( _heap_alloc_info* ) old_memory - 1; - _heap_stats_info.used_memory -= alloc_info->size; - _heap_stats_info.alloc_count--; - old_memory = alloc_info->physical_start; - } - break; - case EAllocation_ALLOC : - { - size += track_size; - } - break; - default : - break; - } - #endif - - switch ( type ) - { - #if defined( ZPL_COMPILER_MSVC ) || ( defined( ZPL_COMPILER_GCC ) && defined( ZPL_SYSTEM_WINDOWS ) ) || ( defined( ZPL_COMPILER_TINYC ) && defined( ZPL_SYSTEM_WINDOWS ) ) - case EAllocation_ALLOC : - ptr = _aligned_malloc( size, alignment ); - if ( flags & ALLOCATOR_FLAG_CLEAR_TO_ZERO ) - zero_size( ptr, size ); - break; - case EAllocation_FREE : - _aligned_free( old_memory ); - break; - case EAllocation_RESIZE : - { - AllocatorInfo a = heap(); - ptr = default_resize_align( a, old_memory, old_size, size, alignment ); - } - break; - - #elif defined( ZPL_SYSTEM_LINUX ) && ! defined( ZPL_CPU_ARM ) && ! defined( ZPL_COMPILER_TINYC ) - case EAllocation_ALLOC : - { - ptr = aligned_alloc( alignment, ( size + alignment - 1 ) & ~( alignment - 1 ) ); - - if ( flags & ZPL_ALLOCATOR_FLAG_CLEAR_TO_ZERO ) - { - zero_size( ptr, size ); - } - } - break; - - case EAllocation_FREE : - { - free( old_memory ); - } - break; - - case EAllocation_RESIZE : - { - AllocatorInfo a = heap(); - ptr = default_resize_align( a, old_memory, old_size, size, alignment ); - } - break; - #else - case EAllocation_ALLOC : - { - posix_memalign( &ptr, alignment, size ); - - if ( flags & ZPL_ALLOCATOR_FLAG_CLEAR_TO_ZERO ) - { - zero_size( ptr, size ); - } - } - break; - - case EAllocation_FREE : - { - free( old_memory ); - } - break; - - case EAllocation_RESIZE : - { - AllocatorInfo a = heap(); - ptr = default_resize_align( a, old_memory, old_size, size, alignment ); - } - break; - #endif - - case EAllocation_FREE_ALL : - break; - } - - #ifdef ZPL_HEAP_ANALYSIS - if ( type == EAllocation_ALLOC ) - { - _heap_alloc_info* alloc_info = zpl_cast( _heap_alloc_info* )( zpl_cast( char* ) ptr + alloc_info_remainder ); - zero_item( alloc_info ); - alloc_info->size = size - track_size; - alloc_info->physical_start = ptr; - ptr = zpl_cast( void* )( alloc_info + 1 ); - _heap_stats_info.used_memory += alloc_info->size; - _heap_stats_info.alloc_count++; - } - #endif - - return ptr; - } - - void* Arena::allocator_proc( void* allocator_data, AllocType type, sw size, sw alignment, void* old_memory, sw old_size, u64 flags ) - { - Arena* arena = rcast(Arena*, allocator_data); - void* ptr = NULL; - - // unused( old_size ); - - switch ( type ) - { - case EAllocation_ALLOC : - { - void* end = pointer_add( arena->PhysicalStart, arena->TotalUsed ); - sw total_size = align_forward_i64( size, alignment ); - - // NOTE: Out of memory - if ( arena->TotalUsed + total_size > (sw) arena->TotalSize ) - { - // zpl__printf_err("%s", "Arena out of memory\n"); - return nullptr; - } - - ptr = align_forward( end, alignment ); - arena->TotalUsed += total_size; - - if ( flags & ALLOCATOR_FLAG_CLEAR_TO_ZERO ) - zero_size( ptr, size ); - } - break; - - case EAllocation_FREE : - // NOTE: Free all at once - // Use Temp_Arena_Memory if you want to free a block - break; - - case EAllocation_FREE_ALL : - arena->TotalUsed = 0; - break; - - case EAllocation_RESIZE : - { - // TODO : Check if ptr is on top of stack and just extend - AllocatorInfo a = arena->Backing; - ptr = default_resize_align( a, old_memory, old_size, size, alignment ); - } - break; - } - return ptr; - } - - void* Pool::allocator_proc( void* allocator_data, AllocType type, sw size, sw alignment, void* old_memory, sw old_size, u64 flags ) - { - Pool* pool = zpl_cast( Pool* ) allocator_data; - void* ptr = NULL; - - // unused( old_size ); - - switch ( type ) - { - case EAllocation_ALLOC : - { - uptr next_free; - - ZPL_ASSERT( size == pool->BlockSize ); - ZPL_ASSERT( alignment == pool->BlockAlign ); - ZPL_ASSERT( pool->FreeList != NULL ); - - next_free = *zpl_cast( uptr* ) pool->FreeList; - ptr = pool->FreeList; - pool->FreeList = zpl_cast( void* ) next_free; - pool->TotalSize += pool->BlockSize; - - if ( flags & ALLOCATOR_FLAG_CLEAR_TO_ZERO ) - zero_size( ptr, size ); - } - break; - - case EAllocation_FREE : - { - uptr* next; - if ( old_memory == NULL ) - return NULL; - - next = zpl_cast( uptr* ) old_memory; - *next = zpl_cast( uptr ) pool->FreeList; - pool->FreeList = old_memory; - pool->TotalSize -= pool->BlockSize; - } - break; - - case EAllocation_FREE_ALL : - { - sw actual_block_size, block_index; - void* curr; - uptr* end; - - actual_block_size = pool->BlockSize + pool->BlockAlign; - pool->TotalSize = 0; - - // NOTE: Init intrusive freelist - curr = pool->PhysicalStart; - for ( block_index = 0; block_index < pool->NumBlocks - 1; block_index++ ) - { - uptr* next = zpl_cast( uptr* ) curr; - *next = zpl_cast( uptr ) curr + actual_block_size; - curr = pointer_add( curr, actual_block_size ); - } - - end = zpl_cast( uptr* ) curr; - *end = zpl_cast( uptr ) NULL; - pool->FreeList = pool->PhysicalStart; - } - break; - - case EAllocation_RESIZE : - // NOTE: Cannot resize - ZPL_PANIC( "You cannot resize something allocated by with a pool." ); - break; - } - - return ptr; - } - - Pool Pool::init_align( AllocatorInfo backing, sw num_blocks, sw block_size, sw block_align ) - { - Pool pool = {}; - - sw actual_block_size, pool_size, block_index; - void *data, *curr; - uptr* end; - - pool.Backing = backing; - pool.BlockSize = block_size; - pool.BlockAlign = block_align; - pool.NumBlocks = num_blocks; - - actual_block_size = block_size + block_align; - pool_size = num_blocks * actual_block_size; - - data = alloc_align( backing, pool_size, block_align ); - - // NOTE: Init intrusive freelist - curr = data; - for ( block_index = 0; block_index < num_blocks - 1; block_index++ ) - { - uptr* next = ( uptr* ) curr; - *next = ( uptr ) curr + actual_block_size; - curr = pointer_add( curr, actual_block_size ); - } - - end = ( uptr* ) curr; - *end = ( uptr ) 0; - - pool.PhysicalStart = data; - pool.FreeList = data; - - return pool; - } -#pragma endregion Memory - -#pragma region Printing - enum - { - ZPL_FMT_MINUS = bit( 0 ), - ZPL_FMT_PLUS = bit( 1 ), - ZPL_FMT_ALT = bit( 2 ), - ZPL_FMT_SPACE = bit( 3 ), - ZPL_FMT_ZERO = bit( 4 ), - - ZPL_FMT_CHAR = bit( 5 ), - ZPL_FMT_SHORT = bit( 6 ), - ZPL_FMT_INT = bit( 7 ), - ZPL_FMT_LONG = bit( 8 ), - ZPL_FMT_LLONG = bit( 9 ), - ZPL_FMT_SIZE = bit( 10 ), - ZPL_FMT_INTPTR = bit( 11 ), - - ZPL_FMT_UNSIGNED = bit( 12 ), - ZPL_FMT_LOWER = bit( 13 ), - ZPL_FMT_UPPER = bit( 14 ), - ZPL_FMT_WIDTH = bit( 15 ), - - ZPL_FMT_DONE = bit( 30 ), - - ZPL_FMT_INTS = ZPL_FMT_CHAR | ZPL_FMT_SHORT | ZPL_FMT_INT | ZPL_FMT_LONG | ZPL_FMT_LLONG | ZPL_FMT_SIZE | ZPL_FMT_INTPTR - }; - - struct _format_info - { - s32 base; - s32 flags; - s32 width; - s32 precision; - }; - - internal sw _print_string( char* text, sw max_len, _format_info* info, char const* str ) - { - sw res = 0, len = 0; - sw remaining = max_len; - char* begin = text; - - if ( str == NULL && max_len >= 6 ) - { - res += str_copy_nulpad( text, "(null)", 6 ); - return res; - } - - if ( info && info->precision >= 0 ) - len = str_len( str, info->precision ); - else - len = str_len( str ); - - if ( info && ( info->width == 0 && info->flags & ZPL_FMT_WIDTH ) ) - { - return res; - } - - if ( info && ( info->width == 0 || info->flags & ZPL_FMT_MINUS ) ) - { - if ( info->precision > 0 ) - len = info->precision < len ? info->precision : len; - if ( res + len > max_len ) - return res; - res += str_copy_nulpad( text, str, len ); - text += res; - - if ( info->width > res ) - { - sw padding = info->width - len; - - char pad = ( info->flags & ZPL_FMT_ZERO ) ? '0' : ' '; - while ( padding-- > 0 && remaining-- > 0 ) - *text++ = pad, res++; - } - } - else - { - if ( info && ( info->width > res ) ) - { - sw padding = info->width - len; - char pad = ( info->flags & ZPL_FMT_ZERO ) ? '0' : ' '; - while ( padding-- > 0 && remaining-- > 0 ) - *text++ = pad, res++; - } - - if ( res + len > max_len ) - return res; - res += str_copy_nulpad( text, str, len ); - } - - if ( info ) - { - if ( info->flags & ZPL_FMT_UPPER ) - str_to_upper( begin ); - else if ( info->flags & ZPL_FMT_LOWER ) - str_to_lower( begin ); - } - - return res; - } - - internal sw _print_char( char* text, sw max_len, _format_info* info, char arg ) - { - char str[ 2 ] = ""; - str[ 0 ] = arg; - return _print_string( text, max_len, info, str ); - } - - internal sw _print_repeated_char( char* text, sw max_len, _format_info* info, char arg ) - { - sw res = 0; - s32 rem = ( info ) ? ( info->width > 0 ) ? info->width : 1 : 1; - res = rem; - while ( rem-- > 0 ) - *text++ = arg; - - return res; - } - - internal sw _print_i64( char* text, sw max_len, _format_info* info, s64 value ) - { - char num[ 130 ]; - i64_to_str( value, num, info ? info->base : 10 ); - return _print_string( text, max_len, info, num ); - } - - internal sw _print_u64( char* text, sw max_len, _format_info* info, u64 value ) - { - char num[ 130 ]; - u64_to_str( value, num, info ? info->base : 10 ); - return _print_string( text, max_len, info, num ); - } - - internal sw _print_f64( char* text, sw max_len, _format_info* info, b32 is_hexadecimal, f64 arg ) - { - // TODO: Handle exponent notation - sw width, len, remaining = max_len; - char* text_begin = text; - - if ( arg ) - { - u64 value; - if ( arg < 0 ) - { - if ( remaining > 1 ) - *text = '-', remaining--; - text++; - arg = -arg; - } - else if ( info->flags & ZPL_FMT_MINUS ) - { - if ( remaining > 1 ) - *text = '+', remaining--; - text++; - } - - value = zpl_cast( u64 ) arg; - len = _print_u64( text, remaining, NULL, value ); - text += len; - - if ( len >= remaining ) - remaining = min( remaining, 1 ); - else - remaining -= len; - arg -= value; - - if ( info->precision < 0 ) - info->precision = 6; - - if ( ( info->flags & ZPL_FMT_ALT ) || info->precision > 0 ) - { - s64 mult = 10; - if ( remaining > 1 ) - *text = '.', remaining--; - text++; - while ( info->precision-- > 0 ) - { - value = zpl_cast( u64 )( arg * mult ); - len = _print_u64( text, remaining, NULL, value ); - text += len; - if ( len >= remaining ) - remaining = min( remaining, 1 ); - else - remaining -= len; - arg -= zpl_cast( f64 ) value / mult; - mult *= 10; - } - } - } - else - { - if ( remaining > 1 ) - *text = '0', remaining--; - text++; - if ( info->flags & ZPL_FMT_ALT ) - { - if ( remaining > 1 ) - *text = '.', remaining--; - text++; - } - } - - width = info->width - ( text - text_begin ); - if ( width > 0 ) - { - char fill = ( info->flags & ZPL_FMT_ZERO ) ? '0' : ' '; - char* end = text + remaining - 1; - len = ( text - text_begin ); - - for ( len = ( text - text_begin ); len--; ) - { - if ( ( text_begin + len + width ) < end ) - *( text_begin + len + width ) = *( text_begin + len ); - } - - len = width; - text += len; - if ( len >= remaining ) - remaining = min( remaining, 1 ); - else - remaining -= len; - - while ( len-- ) - { - if ( text_begin + len < end ) - text_begin[ len ] = fill; - } - } - - return ( text - text_begin ); - } - - neverinline sw str_fmt_va( char* text, sw max_len, char const* fmt, va_list va ) - { - char const* text_begin = text; - sw remaining = max_len, res; - - while ( *fmt ) - { - _format_info info = { 0 }; - sw len = 0; - info.precision = -1; - - while ( *fmt && *fmt != '%' && remaining ) - *text++ = *fmt++; - - if ( *fmt == '%' ) - { - do - { - switch ( *++fmt ) - { - case '-' : - { - info.flags |= ZPL_FMT_MINUS; - break; - } - case '+' : - { - info.flags |= ZPL_FMT_PLUS; - break; - } - case '#' : - { - info.flags |= ZPL_FMT_ALT; - break; - } - case ' ' : - { - info.flags |= ZPL_FMT_SPACE; - break; - } - case '0' : - { - info.flags |= ( ZPL_FMT_ZERO | ZPL_FMT_WIDTH ); - break; - } - default : - { - info.flags |= ZPL_FMT_DONE; - break; - } - } - } while ( ! ( info.flags & ZPL_FMT_DONE ) ); - } - - // NOTE: Optional Width - if ( *fmt == '*' ) - { - int width = va_arg( va, int ); - if ( width < 0 ) - { - info.flags |= ZPL_FMT_MINUS; - info.width = -width; - } - else - { - info.width = width; - } - info.flags |= ZPL_FMT_WIDTH; - fmt++; - } - else - { - info.width = zpl_cast( s32 ) str_to_i64( fmt, zpl_cast( char** ) & fmt, 10 ); - if ( info.width != 0 ) - { - info.flags |= ZPL_FMT_WIDTH; - } - } - - // NOTE: Optional Precision - if ( *fmt == '.' ) - { - fmt++; - if ( *fmt == '*' ) - { - info.precision = va_arg( va, int ); - fmt++; - } - else - { - info.precision = zpl_cast( s32 ) str_to_i64( fmt, zpl_cast( char** ) & fmt, 10 ); - } - info.flags &= ~ZPL_FMT_ZERO; - } - - switch ( *fmt++ ) - { - case 'h' : - if ( *fmt == 'h' ) - { // hh => char - info.flags |= ZPL_FMT_CHAR; - fmt++; - } - else - { // h => short - info.flags |= ZPL_FMT_SHORT; - } - break; - - case 'l' : - if ( *fmt == 'l' ) - { // ll => long long - info.flags |= ZPL_FMT_LLONG; - fmt++; - } - else - { // l => long - info.flags |= ZPL_FMT_LONG; - } - break; - - break; - - case 'z' : // NOTE: zpl_usize - info.flags |= ZPL_FMT_UNSIGNED; - // fallthrough - case 't' : // NOTE: zpl_isize - info.flags |= ZPL_FMT_SIZE; - break; - - default : - fmt--; - break; - } - - switch ( *fmt ) - { - case 'u' : - info.flags |= ZPL_FMT_UNSIGNED; - // fallthrough - case 'd' : - case 'i' : - info.base = 10; - break; - - case 'o' : - info.base = 8; - break; - - case 'x' : - info.base = 16; - info.flags |= ( ZPL_FMT_UNSIGNED | ZPL_FMT_LOWER ); - break; - - case 'X' : - info.base = 16; - info.flags |= ( ZPL_FMT_UNSIGNED | ZPL_FMT_UPPER ); - break; - - case 'f' : - case 'F' : - case 'g' : - case 'G' : - len = _print_f64( text, remaining, &info, 0, va_arg( va, f64 ) ); - break; - - case 'a' : - case 'A' : - len = _print_f64( text, remaining, &info, 1, va_arg( va, f64 ) ); - break; - - case 'c' : - len = _print_char( text, remaining, &info, zpl_cast( char ) va_arg( va, int ) ); - break; - - case 's' : - len = _print_string( text, remaining, &info, va_arg( va, char* ) ); - break; - - case 'r' : - len = _print_repeated_char( text, remaining, &info, va_arg( va, int ) ); - break; - - case 'p' : - info.base = 16; - info.flags |= ( ZPL_FMT_LOWER | ZPL_FMT_UNSIGNED | ZPL_FMT_ALT | ZPL_FMT_INTPTR ); - break; - - case '%' : - len = _print_char( text, remaining, &info, '%' ); - break; - - default : - fmt--; - break; - } - - fmt++; - - if ( info.base != 0 ) - { - if ( info.flags & ZPL_FMT_UNSIGNED ) - { - u64 value = 0; - switch ( info.flags & ZPL_FMT_INTS ) - { - case ZPL_FMT_CHAR : - value = zpl_cast( u64 ) zpl_cast( u8 ) va_arg( va, int ); - break; - case ZPL_FMT_SHORT : - value = zpl_cast( u64 ) zpl_cast( u16 ) va_arg( va, int ); - break; - case ZPL_FMT_LONG : - value = zpl_cast( u64 ) va_arg( va, unsigned long ); - break; - case ZPL_FMT_LLONG : - value = zpl_cast( u64 ) va_arg( va, unsigned long long ); - break; - case ZPL_FMT_SIZE : - value = zpl_cast( u64 ) va_arg( va, uw ); - break; - case ZPL_FMT_INTPTR : - value = zpl_cast( u64 ) va_arg( va, uptr ); - break; - default : - value = zpl_cast( u64 ) va_arg( va, unsigned int ); - break; - } - - len = _print_u64( text, remaining, &info, value ); - } - else - { - s64 value = 0; - switch ( info.flags & ZPL_FMT_INTS ) - { - case ZPL_FMT_CHAR : - value = zpl_cast( s64 ) zpl_cast( s8 ) va_arg( va, int ); - break; - case ZPL_FMT_SHORT : - value = zpl_cast( s64 ) zpl_cast( s16 ) va_arg( va, int ); - break; - case ZPL_FMT_LONG : - value = zpl_cast( s64 ) va_arg( va, long ); - break; - case ZPL_FMT_LLONG : - value = zpl_cast( s64 ) va_arg( va, long long ); - break; - case ZPL_FMT_SIZE : - value = zpl_cast( s64 ) va_arg( va, uw ); - break; - case ZPL_FMT_INTPTR : - value = zpl_cast( s64 ) va_arg( va, uptr ); - break; - default : - value = zpl_cast( s64 ) va_arg( va, int ); - break; - } - - len = _print_i64( text, remaining, &info, value ); - } - } - - text += len; - if ( len >= remaining ) - remaining = min( remaining, 1 ); - else - remaining -= len; - } - - *text++ = '\0'; - res = ( text - text_begin ); - return ( res >= max_len || res < 0 ) ? -1 : res; - } - - char* str_fmt_buf_va( char const* fmt, va_list va ) - { - local_persist thread_local char buffer[ ZPL_PRINTF_MAXLEN ]; - str_fmt_va( buffer, size_of( buffer ), fmt, va ); - return buffer; - } - - char* str_fmt_buf( char const* fmt, ... ) - { - va_list va; - char* str; - va_start( va, fmt ); - str = str_fmt_buf_va( fmt, va ); - va_end( va ); - return str; - } - - sw str_fmt_file_va( struct FileInfo* f, char const* fmt, va_list va ) - { - local_persist thread_local char buf[ ZPL_PRINTF_MAXLEN ]; - sw len = str_fmt_va( buf, size_of( buf ), fmt, va ); - b32 res = file_write( f, buf, len - 1 ); // NOTE: prevent extra whitespace - return res ? len : -1; - } - - sw str_fmt_out_va( char const* fmt, va_list va ) - { - return str_fmt_file_va( file_get_standard( EFileStandard_OUTPUT ), fmt, va ); - } - - sw str_fmt_out_err_va( char const* fmt, va_list va ) - { - return str_fmt_file_va( file_get_standard( EFileStandard_ERROR ), fmt, va ); - } - - sw str_fmt_out_err( char const* fmt, ... ) - { - sw res; - va_list va; - va_start( va, fmt ); - res = str_fmt_out_err_va( fmt, va ); - va_end( va ); - return res; - } -#pragma endregion Printing - -#pragma region Hashing - global u32 const _crc32_table[ 256 ] = { - 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, - 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, - 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, - 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, - 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, - 0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, - 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, 0x4db26158, 0x3ab551ce, - 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, - 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, - 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, - 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, - 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, - 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, - 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, - 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, - 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, - 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, - 0x47b2cf7f, 0x30b5ffe9, 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, - 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d, - }; - - u32 crc32( void const* data, sw len ) - { - sw remaining; - u32 result = ~( zpl_cast( u32 ) 0 ); - u8 const* c = zpl_cast( u8 const* ) data; - for ( remaining = len; remaining--; c++ ) - result = ( result >> 8 ) ^ ( _crc32_table[ ( result ^ *c ) & 0xff ] ); - return ~result; - } -#pragma endregion Hashing - -#pragma region File Handling - #if defined( ZPL_SYSTEM_WINDOWS ) || defined( ZPL_SYSTEM_CYGWIN ) - - internal wchar_t* _alloc_utf8_to_ucs2( AllocatorInfo a, char const* text, sw* w_len_ ) - { - wchar_t* w_text = NULL; - sw len = 0, w_len = 0, w_len1 = 0; - if ( text == NULL ) - { - if ( w_len_ ) - *w_len_ = w_len; - return NULL; - } - len = str_len( text ); - if ( len == 0 ) - { - if ( w_len_ ) - *w_len_ = w_len; - return NULL; - } - w_len = MultiByteToWideChar( CP_UTF8, MB_ERR_INVALID_CHARS, text, zpl_cast( int ) len, NULL, 0 ); - if ( w_len == 0 ) - { - if ( w_len_ ) - *w_len_ = w_len; - return NULL; - } - w_text = alloc_array( a, wchar_t, w_len + 1 ); - w_len1 = MultiByteToWideChar( CP_UTF8, MB_ERR_INVALID_CHARS, text, zpl_cast( int ) len, w_text, zpl_cast( int ) w_len ); - if ( w_len1 == 0 ) - { - free( a, w_text ); - if ( w_len_ ) - *w_len_ = 0; - return NULL; - } - w_text[ w_len ] = 0; - if ( w_len_ ) - *w_len_ = w_len; - return w_text; - } - - internal ZPL_FILE_SEEK_PROC( _win32_file_seek ) - { - LARGE_INTEGER li_offset; - li_offset.QuadPart = offset; - if ( ! SetFilePointerEx( fd.p, li_offset, &li_offset, whence ) ) - { - return false; - } - - if ( new_offset ) - *new_offset = li_offset.QuadPart; - return true; - } - - internal ZPL_FILE_READ_AT_PROC( _win32_file_read ) - { - // unused( stop_at_newline ); - b32 result = false; - _win32_file_seek( fd, offset, ESeekWhence_BEGIN, NULL ); - DWORD size_ = zpl_cast( DWORD )( size > ZPL_I32_MAX ? ZPL_I32_MAX : size ); - DWORD bytes_read_; - if ( ReadFile( fd.p, buffer, size_, &bytes_read_, NULL ) ) - { - if ( bytes_read ) - *bytes_read = bytes_read_; - result = true; - } - - return result; - } - - internal ZPL_FILE_WRITE_AT_PROC( _win32_file_write ) - { - DWORD size_ = zpl_cast( DWORD )( size > ZPL_I32_MAX ? ZPL_I32_MAX : size ); - DWORD bytes_written_; - _win32_file_seek( fd, offset, ESeekWhence_BEGIN, NULL ); - if ( WriteFile( fd.p, buffer, size_, &bytes_written_, NULL ) ) - { - if ( bytes_written ) - *bytes_written = bytes_written_; - return true; - } - return false; - } - - internal ZPL_FILE_CLOSE_PROC( _win32_file_close ) - { - CloseHandle( fd.p ); - } - - FileOperations const default_file_operations = { _win32_file_read, _win32_file_write, _win32_file_seek, _win32_file_close }; - - neverinline ZPL_FILE_OPEN_PROC( _win32_file_open ) - { - DWORD desired_access; - DWORD creation_disposition; - void* handle; - wchar_t* w_text; - - switch ( mode & ZPL_FILE_MODES ) - { - case EFileMode_READ : - desired_access = GENERIC_READ; - creation_disposition = OPEN_EXISTING; - break; - case EFileMode_WRITE : - desired_access = GENERIC_WRITE; - creation_disposition = CREATE_ALWAYS; - break; - case EFileMode_APPEND : - desired_access = GENERIC_WRITE; - creation_disposition = OPEN_ALWAYS; - break; - case EFileMode_READ | EFileMode_RW : - desired_access = GENERIC_READ | GENERIC_WRITE; - creation_disposition = OPEN_EXISTING; - break; - case EFileMode_WRITE | EFileMode_RW : - desired_access = GENERIC_READ | GENERIC_WRITE; - creation_disposition = CREATE_ALWAYS; - break; - case EFileMode_APPEND | EFileMode_RW : - desired_access = GENERIC_READ | GENERIC_WRITE; - creation_disposition = OPEN_ALWAYS; - break; - default : - ZPL_PANIC( "Invalid file mode" ); - return EFileError_INVALID; - } - - w_text = _alloc_utf8_to_ucs2( heap(), filename, NULL ); - handle = CreateFileW( w_text, desired_access, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, creation_disposition, FILE_ATTRIBUTE_NORMAL, NULL ); - - free( heap(), w_text ); - - if ( handle == INVALID_HANDLE_VALUE ) - { - DWORD err = GetLastError(); - switch ( err ) - { - case ERROR_FILE_NOT_FOUND : - return EFileError_NOT_EXISTS; - case ERROR_FILE_EXISTS : - return EFileError_EXISTS; - case ERROR_ALREADY_EXISTS : - return EFileError_EXISTS; - case ERROR_ACCESS_DENIED : - return EFileError_PERMISSION; - } - return EFileError_INVALID; - } - - if ( mode & EFileMode_APPEND ) - { - LARGE_INTEGER offset = { { 0 } }; - if ( ! SetFilePointerEx( handle, offset, NULL, ESeekWhence_END ) ) - { - CloseHandle( handle ); - return EFileError_INVALID; - } - } - - fd->p = handle; - *ops = default_file_operations; - return EFileError_NONE; - } - - #else // POSIX - # include - - internal ZPL_FILE_SEEK_PROC( _posix_file_seek ) - { - # if defined( ZPL_SYSTEM_OSX ) - s64 res = lseek( fd.i, offset, whence ); - # else // TODO(ZaKlaus): @fixme lseek64 - s64 res = lseek( fd.i, offset, whence ); - # endif - if ( res < 0 ) - return false; - if ( new_offset ) - *new_offset = res; - return true; - } - - internal ZPL_FILE_READ_AT_PROC( _posix_file_read ) - { - unused( stop_at_newline ); - sw res = pread( fd.i, buffer, size, offset ); - if ( res < 0 ) - return false; - if ( bytes_read ) - *bytes_read = res; - return true; - } - - internal ZPL_FILE_WRITE_AT_PROC( _posix_file_write ) - { - sw res; - s64 curr_offset = 0; - _posix_file_seek( fd, 0, ESeekWhence_CURRENT, &curr_offset ); - if ( curr_offset == offset ) - { - // NOTE: Writing to stdout et al. doesn't like pwrite for numerous reasons - res = write( zpl_cast( int ) fd.i, buffer, size ); - } - else - { - res = pwrite( zpl_cast( int ) fd.i, buffer, size, offset ); - } - if ( res < 0 ) - return false; - if ( bytes_written ) - *bytes_written = res; - return true; - } - - internal ZPL_FILE_CLOSE_PROC( _posix_file_close ) - { - close( fd.i ); - } - - FileOperations const default_file_operations = { _posix_file_read, _posix_file_write, _posix_file_seek, _posix_file_close }; - - ZPL_NEVER_INLINE ZPL_FILE_OPEN_PROC( _posix_file_open ) - { - s32 os_mode; - switch ( mode & ZPL_FILE_MODES ) - { - case EFileMode_READ : - os_mode = O_RDONLY; - break; - case EFileMode_WRITE : - os_mode = O_WRONLY | O_CREAT | O_TRUNC; - break; - case EFileMode_APPEND : - os_mode = O_WRONLY | O_APPEND | O_CREAT; - break; - case EFileMode_READ | EFileMode_RW : - os_mode = O_RDWR; - break; - case EFileMode_WRITE | EFileMode_RW : - os_mode = O_RDWR | O_CREAT | O_TRUNC; - break; - case EFileMode_APPEND | EFileMode_RW : - os_mode = O_RDWR | O_APPEND | O_CREAT; - break; - default : - ZPL_PANIC( "Invalid file mode" ); - return EFileError_INVALID; - } - - fd->i = open( filename, os_mode, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH ); - if ( fd->i < 0 ) - { - // TODO: More file errors - return EFileError_INVALID; - } - - *ops = default_file_operations; - return EFileError_NONE; - } - - // POSIX - #endif - - internal void _dirinfo_free_entry( DirEntry* entry ); - - // TODO: Is this a bad idea? - global b32 _std_file_set = false; - global FileInfo _std_files[ EFileStandard_COUNT ] = { - { - { nullptr, nullptr, nullptr, nullptr }, - { nullptr }, - 0, - nullptr, - 0, - nullptr - } }; - - #if defined( ZPL_SYSTEM_WINDOWS ) || defined( ZPL_SYSTEM_CYGWIN ) - - FileInfo* file_get_standard( FileStandardType std ) - { - if ( ! _std_file_set ) - { - # define ZPL__SET_STD_FILE( type, v ) \ - _std_files[ type ].FD.p = v; \ - _std_files[ type ].Ops = default_file_operations - ZPL__SET_STD_FILE( EFileStandard_INPUT, GetStdHandle( STD_INPUT_HANDLE ) ); - ZPL__SET_STD_FILE( EFileStandard_OUTPUT, GetStdHandle( STD_OUTPUT_HANDLE ) ); - ZPL__SET_STD_FILE( EFileStandard_ERROR, GetStdHandle( STD_ERROR_HANDLE ) ); - # undef ZPL__SET_STD_FILE - _std_file_set = true; - } - return &_std_files[ std ]; - } - - // void file_connect_handle( FileInfo* file, void* handle ) - // { - // ZPL_ASSERT_NOT_NULL( file ); - // ZPL_ASSERT_NOT_NULL( handle ); - - // if ( file->is_temp ) - // return; - - // zero_item( file ); - - // file->fd.p = handle; - // file->ops = default_file_operations; - // } - - // FileError file_truncate( FileInfo* f, s64 size ) - // { - // FileError err = EFileError_NONE; - // s64 prev_offset = file_tell( f ); - // file_seek( f, size ); - // if ( ! SetEndOfFile( f ) ) - // err = EFileError_TRUNCATION_FAILURE; - // file_seek( f, prev_offset ); - // return err; - // } - - // b32 fs_exists( char const* name ) - // { - // WIN32_FIND_DATAW data; - // wchar_t* w_text; - // void* handle; - // b32 found = false; - // AllocatorInfo a = heap_allocator(); - - // w_text = _alloc_utf8_to_ucs2( a, name, NULL ); - // if ( w_text == NULL ) - // { - // return false; - // } - // handle = FindFirstFileW( w_text, &data ); - // free( a, w_text ); - // found = handle != INVALID_HANDLE_VALUE; - // if ( found ) - // FindClose( handle ); - // return found; - // } - - #else // POSIX - - FileInfo* file_get_standard( FileStandardType std ) - { - if ( ! _std_file_set ) - { - # define ZPL__SET_STD_FILE( type, v ) \ - _std_files[ type ].fd.i = v; \ - _std_files[ type ].ops = default_file_operations - ZPL__SET_STD_FILE( EFileStandard_INPUT, 0 ); - ZPL__SET_STD_FILE( EFileStandard_OUTPUT, 1 ); - ZPL__SET_STD_FILE( EFileStandard_ERROR, 2 ); - # undef ZPL__SET_STD_FILE - _std_file_set = true; - } - return &_std_files[ std ]; - } - - // FileError file_truncate( FileInfo* f, s64 size ) - // { - // FileError err = EFileError_NONE; - // int i = ftruncate( f->fd.i, size ); - // if ( i != 0 ) - // err = EFileError_TRUNCATION_FAILURE; - // return err; - // } - - // b32 fs_exists( char const* name ) - // { - // return access( name, F_OK ) != -1; - // } - - #endif - - FileError file_close( FileInfo* f ) - { - if ( ! f ) - return EFileError_INVALID; - - if ( f->Filename ) - free( heap(), zpl_cast( char* ) f->Filename ); - - #if defined( ZPL_SYSTEM_WINDOWS ) - if ( f->FD.p == INVALID_HANDLE_VALUE ) - return EFileError_INVALID; - #else - if ( f->fd.i < 0 ) - return EFileError_INVALID; - #endif - - if ( f->IsTemp ) - { - f->Ops.close( f->FD ); - return EFileError_NONE; - } - - if ( ! f->Ops.read_at ) - f->Ops = default_file_operations; - f->Ops.close( f->FD ); - - #if 0 - if ( f->Dir ) - { - _dirinfo_free_entry( f->Dir ); - mfree( f->Dir ); - f->Dir = NULL; - } - #endif - - return EFileError_NONE; - } - - FileError file_new( FileInfo* f, FileDescriptor fd, FileOperations ops, char const* filename ) - { - FileError err = EFileError_NONE; - sw len = str_len( filename ); - - f->Ops = ops; - f->FD = fd; - f->Dir = nullptr; - f->LastWriteTime = 0; - f->Filename = alloc_array( heap(), char, len + 1 ); - mem_copy( zpl_cast( char* ) f->Filename, zpl_cast( char* ) filename, len + 1 ); - - return err; - } - - FileError file_open_mode( FileInfo* f, FileMode mode, char const* filename ) - { - FileInfo file_ = - { - { nullptr, nullptr, nullptr, nullptr }, - { nullptr }, - 0, - nullptr, - 0, - nullptr - }; - - *f = file_; - FileError err; - - #if defined( ZPL_SYSTEM_WINDOWS ) || defined( ZPL_SYSTEM_CYGWIN ) - err = _win32_file_open( &f->FD, &f->Ops, mode, filename ); - #else - err = _posix_file_open( &f->fd, &f->ops, mode, filename ); - #endif - - if ( err == EFileError_NONE ) - return file_new( f, f->FD, f->Ops, filename ); - - return err; - } -#pragma endregion File Handling - -#pragma region String - String String::fmt( AllocatorInfo allocator, char* buf, sw buf_size, char const* fmt, ... ) - { - va_list va; - va_start( va, fmt ); - str_fmt_va( buf, buf_size, fmt, va ); - va_end( va ); - - return make( allocator, buf ); - } - - String String::fmt_buf( AllocatorInfo allocator, char const* fmt, ... ) - { - local_persist thread_local - char buf[ ZPL_PRINTF_MAXLEN ] = { 0 }; - - va_list va; - va_start( va, fmt ); - str_fmt_va( buf, ZPL_PRINTF_MAXLEN, fmt, va ); - va_end( va ); - - return make( allocator, buf ); - } - - bool String::append_fmt( char const* fmt, ... ) - { - sw res; - char buf[ ZPL_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 ); - } -#pragma endregion String - - namespace Memory - { - global AllocatorInfo GlobalAllocator; - global Array Global_AllocatorBuckets; - - void* Global_Allocator_Proc( void* allocator_data, AllocType type, sw size, sw alignment, void* old_memory, sw old_size, u64 flags ) - { - Arena& last = Global_AllocatorBuckets.back(); - - switch ( type ) - { - case EAllocation_ALLOC: - { - if ( last.TotalUsed + size > last.TotalSize ) - { - Arena bucket = Arena::init_from_allocator( heap(), BucketSize ); - - if ( bucket.PhysicalStart == nullptr ) - fatal( "Failed to create bucket for Global_AllocatorBuckets"); - - if ( ! Global_AllocatorBuckets.append( bucket ) ) - fatal( "Failed to append bucket to Global_AllocatorBuckets"); - - last = Global_AllocatorBuckets.back(); - } - - return alloc_align( last, size, alignment ); - } - case EAllocation_FREE: - { - // Doesn't recycle. - } - break; - case EAllocation_FREE_ALL: - { - // Memory::cleanup instead. - } - break; - case EAllocation_RESIZE: - { - if ( last.TotalUsed + size > last.TotalSize ) - { - Arena bucket = Arena::init_from_allocator( heap(), BucketSize ); - - if ( bucket.PhysicalStart == nullptr ) - fatal( "Failed to create bucket for Global_AllocatorBuckets"); - - if ( ! Global_AllocatorBuckets.append( bucket ) ) - fatal( "Failed to append bucket to Global_AllocatorBuckets"); - - last = Global_AllocatorBuckets.back(); - } - - void* result = alloc_align( last.Backing, size, alignment ); - - if ( result != nullptr && old_memory != nullptr ) - { - mem_copy( result, old_memory, old_size ); - } - - return result; - } - } - - return nullptr; - } - - void setup() - { - GlobalAllocator = AllocatorInfo { & Global_Allocator_Proc, nullptr }; - - Global_AllocatorBuckets = Array::init_reserve( heap(), 128 ); - - if ( Global_AllocatorBuckets == nullptr ) - fatal( "Failed to reserve memory for Global_AllocatorBuckets"); - - Arena bucket = Arena::init_from_allocator( heap(), BucketSize ); - - if ( bucket.PhysicalStart == nullptr ) - fatal( "Failed to create first bucket for Global_AllocatorBuckets"); - - Global_AllocatorBuckets.append( bucket ); - } - - void cleanup() - { - s32 index = 0; - s32 left = Global_AllocatorBuckets.num(); - do - { - Arena* bucket = & Global_AllocatorBuckets[ index ]; - bucket->free(); - index++; - } - while ( left--, left ); - - Global_AllocatorBuckets.free(); - } - - // namespace Memory - } -// namespace gen -} diff --git a/project/Bloat.hpp b/project/Bloat.hpp deleted file mode 100644 index 60b892d..0000000 --- a/project/Bloat.hpp +++ /dev/null @@ -1,2400 +0,0 @@ -/* - BLOAT. - - This contians all definitions not directly related to the project. -*/ -#pragma once - -#ifdef BLOAT_IMPL -# define ZPL_IMPLEMENTATION -#endif - -// TODO : This will be removed when making the library have zero dependencies. -#pragma region ZPL INCLUDE -#if __clang__ -# pragma clang diagnostic push -# pragma clang diagnostic ignored "-Wmissing-braces" -# pragma clang diagnostic ignored "-Wbraced-scalar-init" -#endif - -// # define ZPL_HEAP_ANALYSIS -# define ZPL_WRAP_IN_NAMESPACE -# define ZPL_NO_MATH_H -# define ZPL_CUSTOM_MODULES -# define ZPL_MODULE_ESSENTIALS -# define ZPL_MODULE_CORE -# define ZPL_MODULE_TIMER -# define ZPL_MODULE_HASHING -// #include "zpl.h" - -#undef alloc_item -#undef alloc_array -#undef Array -#undef heap -#undef malloc -#undef mfree -#undef ZPL_ASSERT_MSG -#undef ZPL_ASSERT_NOT_NULL -#undef ZPL_DEBUG_TRAP -#undef ZPL_PANIC - -// using zpl::b32; -// using zpl::s8; -// using zpl::s16; -// using zpl::s32; -// using zpl::s64; -// using zpl::u8; -// using zpl::u16; -// using zpl::u32; -// using zpl::u64; -// using zpl::uw; -// using zpl::sw; -// using zpl::sptr; -// using zpl::uptr; -// using zpl::f32; -// using zpl::f64; - -// using zpl::AllocType; -// using zpl::Arena; -// using zpl::AllocatorInfo; -// using zpl::ArrayHeader; -// using zpl::FileInfo; -// using zpl::FileError; -// using zpl::Pool; -// using zpl::String; - -// using zpl::EAllocation_ALLOC; -// using zpl::EAllocation_FREE; -// using zpl::EAllocation_FREE_ALL; -// using zpl::EAllocation_RESIZE; -// using zpl::EFileMode_WRITE; -// using zpl::EFileError_NONE; - -// using zpl::ZPL_ALLOCATOR_FLAG_CLEAR_TO_ZERO; - -// using zpl::align_forward; -// using zpl::align_forward_i64; -// using zpl::alloc; -// using zpl::alloc_align; -// using zpl::arena_allocator; -// using zpl::arena_init_from_memory; -// using zpl::arena_init_from_allocator; -// using zpl::arena_free; -// using zpl::assert_crash; -// using zpl::char_first_occurence; -// using zpl::char_is_alpha; -// using zpl::char_is_alphanumeric; -// using zpl::char_is_digit; -// using zpl::char_is_hex_digit; -// using zpl::char_is_space; -// using zpl::crc32; -// using zpl::free_all; -// using zpl::is_power_of_two; -// using zpl::mem_copy; -// using zpl::mem_move; -// using zpl::mem_set; -// using zpl::pointer_add; -// using zpl::pool_allocator; -// using zpl::pool_init; -// using zpl::pool_free; -// using zpl::process_exit; -// using zpl::str_compare; -// using zpl::str_copy; -// using zpl::str_fmt_buf; -// using zpl::str_fmt_va; -// using zpl::str_fmt_out_va; -// using zpl::str_fmt_out_err; -// using zpl::str_fmt_out_err_va; -// using zpl::str_len; -// using zpl::zero_size; - -#if __clang__ -# pragma clang diagnostic pop -#endif -#pragma endregion ZPL INCLUDE - - -#if __clang__ -# pragma clang diagnostic ignored "-Wunused-const-variable" -# pragma clang diagnostic ignored "-Wswitch" -# pragma clang diagnostic ignored "-Wunused-variable" -# pragma clang diagnostic ignored "-Wunknown-pragmas" -# pragma clang diagnostic ignored "-Wvarargs" -# pragma clang diagnostic ignored "-Wunused-function" -#endif - -# if defined( ZPL_HAS_ATTRIBUTE ) -# undef ZPL_HAS_ATTRIBUTE -# endif -# if defined( __has_attribute ) -# define ZPL_HAS_ATTRIBUTE( attribute ) __has_attribute( attribute ) -# else -# define ZPL_HAS_ATTRIBUTE( attribute ) ( 0 ) -# endif - -/* Platform architecture */ - -#if defined( _WIN64 ) || defined( __x86_64__ ) || defined( _M_X64 ) || defined( __64BIT__ ) || defined( __powerpc64__ ) || defined( __ppc64__ ) || defined( __aarch64__ ) -# ifndef ZPL_ARCH_64_BIT -# define ZPL_ARCH_64_BIT 1 -# endif -#else -# ifndef ZPL_ARCH_32_BIT -# define ZPL_ARCH_32_BIT 1 -# endif -#endif - -/* Platform OS */ - -#if defined( _WIN32 ) || defined( _WIN64 ) -# ifndef ZPL_SYSTEM_WINDOWS -# define ZPL_SYSTEM_WINDOWS 1 -# endif -#elif defined( __APPLE__ ) && defined( __MACH__ ) -# ifndef ZPL_SYSTEM_OSX -# define ZPL_SYSTEM_OSX 1 -# endif -# ifndef ZPL_SYSTEM_MACOS -# define ZPL_SYSTEM_MACOS 1 -# endif -# include -# if TARGET_IPHONE_SIMULATOR == 1 || TARGET_OS_IPHONE == 1 -# ifndef ZPL_SYSTEM_IOS -# define ZPL_SYSTEM_IOS 1 -# endif -# endif -#elif defined( __unix__ ) -# ifndef ZPL_SYSTEM_UNIX -# define ZPL_SYSTEM_UNIX 1 -# endif -# if defined( ANDROID ) || defined( __ANDROID__ ) -# ifndef ZPL_SYSTEM_ANDROID -# define ZPL_SYSTEM_ANDROID 1 -# endif -# ifndef ZPL_SYSTEM_LINUX -# define ZPL_SYSTEM_LINUX 1 -# endif -# elif defined( __linux__ ) -# ifndef ZPL_SYSTEM_LINUX -# define ZPL_SYSTEM_LINUX 1 -# endif -# elif defined( __FreeBSD__ ) || defined( __FreeBSD_kernel__ ) -# ifndef ZPL_SYSTEM_FREEBSD -# define ZPL_SYSTEM_FREEBSD 1 -# endif -# elif defined( __OpenBSD__ ) -# ifndef ZPL_SYSTEM_OPENBSD -# define ZPL_SYSTEM_OPENBSD 1 -# endif -# elif defined( __EMSCRIPTEN__ ) -# ifndef ZPL_SYSTEM_EMSCRIPTEN -# define ZPL_SYSTEM_EMSCRIPTEN 1 -# endif -# elif defined( __CYGWIN__ ) -# ifndef ZPL_SYSTEM_CYGWIN -# define ZPL_SYSTEM_CYGWIN 1 -# endif -# else -# error This UNIX operating system is not supported -# endif -#else -# error This operating system is not supported -#endif - -/* Platform compiler */ - -#if defined( _MSC_VER ) -# define ZPL_COMPILER_MSVC 1 -#elif defined( __GNUC__ ) -# define ZPL_COMPILER_GCC 1 -#elif defined( __clang__ ) -# define ZPL_COMPILER_CLANG 1 -#elif defined( __MINGW32__ ) -# define ZPL_COMPILER_MINGW 1 -#elif defined( __TINYC__ ) -# define ZPL_COMPILER_TINYC 1 -#else -# error Unknown compiler -#endif - -# ifndef ZPL_DEF_INLINE -# if defined( ZPL_STATIC ) -# define ZPL_DEF_INLINE -# define ZPL_IMPL_INLINE -# else -# define ZPL_DEF_INLINE static -# define ZPL_IMPL_INLINE static inline -# endif -# endif - -# if defined( ZPL_ALWAYS_INLINE ) -# undef ZPL_ALWAYS_INLINE -# endif - -#ifdef ZPL_COMPILER_MSVC -# define forceinline __forceinline -#elif defined(ZPL_COMPILER_GCC) -# define forceinline inline __attribute__((__always_inline__)) -#elif defined(ZPL_COMPILER_CLANG) -#if __has_attribute(__always_inline__) -# define forceinline inline __attribute__((__always_inline__)) -#else -# define forceinline inline -#endif -#else -# define forceinline inline -#endif - - -#ifdef ZPL_COMPILER_MSVC -# define neverinline __declspec( noinline ) -#elif defined(ZPL_COMPILER_GCC) -# define forceinline inline __attribute__( ( __noinline__ ) ) -#elif defined(ZPL_COMPILER_CLANG) -#if __has_attribute(__always_inline__) -# define forceinline inline __attribute__( ( __noinline__ ) ) -#else -# define forceinline inline -#endif -#else -# define forceinline inline -#endif - -#ifndef count_of -# define count_of( x ) ( ( size_of( x ) / size_of( 0 [ x ] ) ) / ( ( sw )( ! ( size_of( x ) % size_of( 0 [ x ] ) ) ) ) ) -#endif - -#ifndef is_between -# define is_between( x, lower, upper ) ( ( ( lower ) <= ( x ) ) && ( ( x ) <= ( upper ) ) ) -#endif - -#ifndef min -# define min( a, b ) ( ( a ) < ( b ) ? ( a ) : ( b ) ) -#endif - -#ifndef size_of -# define size_of( x ) ( sw )( sizeof( x ) ) -#endif - -#ifndef swap -# define swap( Type, a, b ) \ - do \ - { \ - Type tmp = ( a ); \ - ( a ) = ( b ); \ - ( b ) = tmp; \ - } while ( 0 ) -#endif - -#ifndef zpl_cast -# define zpl_cast( Type ) ( Type ) -#endif - -#ifndef global -# define global static // Global variables -#endif - -#ifndef internal -# define internal static // Internal linkage -#endif - -#ifndef local_persist -# define local_persist static // Local Persisting variables -#endif - -#if defined(__GNUC__) || defined(__clang__) - // Supports 0-10 arguments - #define macro_num_args_impl( _0, \ - _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ - _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, \ - N, ... \ - ) N - // _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, \ - // _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, \ - // _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, - - // ## deletes preceding comma if _VA_ARGS__ is empty (GCC, Clang) - #define macro_num_args(...) \ - macro_num_args_impl(_, ## __VA_ARGS__, \ - 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, \ - 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, \ - 0 \ - ) - // 50, 49, 48, 47, 46, 45, 44, 43, 42, 41, \ - // 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, \ - // 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, -#else - // Supports 1-10 arguments - #define macro_num_args_impl( \ - _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ - _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, \ - N, ... \ - ) N - - #define macro_num_args(...) \ - macro_num_args_impl( __VA_ARGS__, \ - 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, \ - 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 \ - ) -#endif - -#define bit( Value_ ) ( 1 << Value_ ) -#define bitfield_is_equal( Type_, Field_, Mask_ ) ( (Type_(Mask_) & Type_(Field_)) == Type_(Mask_) ) -#define ccast( Type_, Value_ ) * const_cast< Type_* >( & (Value_) ) -#define scast( Type_, Value_ ) static_cast< Type_ >( Value_ ) -#define rcast( Type_, Value_ ) reinterpret_cast< Type_ >( Value_ ) -#define pcast( Type_, Value_ ) ( * (Type_*)( & (Value_) ) ) -#define stringize_va( ... ) #__VA_ARGS__ -#define stringize( ... ) stringize_va( __VA_ARGS__ ) -#define do_once() \ -do \ -{ \ - static \ - bool Done = false; \ - if ( Done ) \ - return; \ - Done = true; \ -} \ -while(0) - -#define do_once_start \ -do \ -{ \ - static \ - bool Done = false; \ - if ( Done ) \ - break; \ - Done = true; - -#define do_once_end \ -} \ -while(0); - -#pragma region Mandatory Includes -# include -# include - -# if defined( ZPL_SYSTEM_WINDOWS ) -# include -# endif -#pragma endregion Mandatory Includes - -// #include "Banned.define.hpp" - -namespace gen -{ - constexpr - char const* Msg_Invalid_Value = "INVALID VALUE PROVIDED"; - - #pragma region Basic Types - #ifndef ZPL_U8_MIN - # define ZPL_U8_MIN 0u - # define ZPL_U8_MAX 0xffu - # define ZPL_I8_MIN ( -0x7f - 1 ) - # define ZPL_I8_MAX 0x7f - - # define ZPL_U16_MIN 0u - # define ZPL_U16_MAX 0xffffu - # define ZPL_I16_MIN ( -0x7fff - 1 ) - # define ZPL_I16_MAX 0x7fff - - # define ZPL_U32_MIN 0u - # define ZPL_U32_MAX 0xffffffffu - # define ZPL_I32_MIN ( -0x7fffffff - 1 ) - # define ZPL_I32_MAX 0x7fffffff - - # define ZPL_U64_MIN 0ull - # define ZPL_U64_MAX 0xffffffffffffffffull - # define ZPL_I64_MIN ( -0x7fffffffffffffffll - 1 ) - # define ZPL_I64_MAX 0x7fffffffffffffffll - - # if defined( ZPL_ARCH_32_BIT ) - # define ZPL_USIZE_MIN ZPL_U32_MIN - # define ZPL_USIZE_MAX ZPL_U32_MAX - # define ZPL_ISIZE_MIN ZPL_S32_MIN - # define ZPL_ISIZE_MAX ZPL_S32_MAX - # elif defined( ZPL_ARCH_64_BIT ) - # define ZPL_USIZE_MIN ZPL_U64_MIN - # define ZPL_USIZE_MAX ZPL_U64_MAX - # define ZPL_ISIZE_MIN ZPL_I64_MIN - # define ZPL_ISIZE_MAX ZPL_I64_MAX - # else - # error Unknown architecture size. This library only supports 32 bit and 64 bit architectures. - # endif - - # define ZPL_F32_MIN 1.17549435e-38f - # define ZPL_F32_MAX 3.40282347e+38f - - # define ZPL_F64_MIN 2.2250738585072014e-308 - # define ZPL_F64_MAX 1.7976931348623157e+308 - #endif - - #if defined( ZPL_COMPILER_MSVC ) - # if _MSC_VER < 1300 - typedef unsigned char u8; - typedef signed char s8; - typedef unsigned short u16; - typedef signed short s16; - typedef unsigned int u32; - typedef signed int s32; - # else - typedef unsigned __int8 u8; - typedef signed __int8 s8; - typedef unsigned __int16 u16; - typedef signed __int16 s16; - typedef unsigned __int32 u32; - typedef signed __int32 s32; - # endif - typedef unsigned __int64 u64; - typedef signed __int64 s64; - #else - # include - - typedef uint8_t u8; - typedef int8_t s8; - typedef uint16_t u16; - typedef int16_t s16; - typedef uint32_t u32; - typedef int32_t s32; - typedef uint64_t u64; - typedef int64_t s64; - #endif - - static_assert( sizeof( u8 ) == sizeof( s8 ), "sizeof(u8) != sizeof(s8)" ); - static_assert( sizeof( u16 ) == sizeof( s16 ), "sizeof(u16) != sizeof(s16)" ); - static_assert( sizeof( u32 ) == sizeof( s32 ), "sizeof(u32) != sizeof(s32)" ); - static_assert( sizeof( u64 ) == sizeof( s64 ), "sizeof(u64) != sizeof(s64)" ); - - static_assert( sizeof( u8 ) == 1, "sizeof(u8) != 1" ); - static_assert( sizeof( u16 ) == 2, "sizeof(u16) != 2" ); - static_assert( sizeof( u32 ) == 4, "sizeof(u32) != 4" ); - static_assert( sizeof( u64 ) == 8, "sizeof(u64) != 8" ); - - typedef size_t uw; - typedef ptrdiff_t sw; - - static_assert( sizeof( uw ) == sizeof( sw ), "sizeof(uw) != sizeof(sw)" ); - - // NOTE: (u)zpl_intptr is only here for semantic reasons really as this library will only support 32/64 bit OSes. - #if defined( _WIN64 ) - typedef signed __int64 sptr; - typedef unsigned __int64 uptr; - #elif defined( _WIN32 ) - // NOTE; To mark types changing their size, e.g. zpl_intptr - # ifndef _W64 - # if ! defined( __midl ) && ( defined( _X86_ ) || defined( _M_IX86 ) ) && _MSC_VER >= 1300 - # define _W64 __w64 - # else - # define _W64 - # endif - # endif - typedef _W64 signed int sptr; - typedef _W64 unsigned int uptr; - #else - typedef uintptr_t uptr; - typedef intptr_t sptr; - #endif - - static_assert( sizeof( uptr ) == sizeof( sptr ), "sizeof(uptr) != sizeof(sptr)" ); - - typedef float f32; - typedef double f64; - - static_assert( sizeof( f32 ) == 4, "sizeof(f32) != 4" ); - static_assert( sizeof( f64 ) == 8, "sizeof(f64) != 8" ); - - typedef s8 b8; - typedef s16 b16; - typedef s32 b32; - #pragma endregion Basic Types - - #pragma region Debug - #ifndef ZPL_DEBUG_TRAP - # if defined( _MSC_VER ) - # if _MSC_VER < 1300 - # define ZPL_DEBUG_TRAP() __asm int 3 /* Trap to debugger! */ - # else - # define ZPL_DEBUG_TRAP() __debugbreak() - # endif - # elif defined( ZPL_COMPILER_TINYC ) - # define ZPL_DEBUG_TRAP() process_exit( 1 ) - # else - # define ZPL_DEBUG_TRAP() __builtin_trap() - # endif - #endif - - #ifndef ZPL_ASSERT - # define ZPL_ASSERT( cond ) ZPL_ASSERT_MSG( cond, NULL ) - #endif - - #ifndef ZPL_ASSERT_MSG - # define ZPL_ASSERT_MSG( cond, msg, ... ) \ - do \ - { \ - if ( ! ( cond ) ) \ - { \ - assert_handler( #cond, __FILE__, zpl_cast( s64 ) __LINE__, msg, ##__VA_ARGS__ ); \ - ZPL_DEBUG_TRAP(); \ - } \ - } while ( 0 ) - #endif - - #ifndef ZPL_ASSERT_NOT_NULL - # define ZPL_ASSERT_NOT_NULL( ptr ) ZPL_ASSERT_MSG( ( ptr ) != NULL, #ptr " must not be NULL" ) - #endif - - // NOTE: Things that shouldn't happen with a message! - #ifndef ZPL_PANIC - # define ZPL_PANIC( msg, ... ) ZPL_ASSERT_MSG( 0, msg, ##__VA_ARGS__ ) - #endif - - void assert_handler( char const* condition, char const* file, s32 line, char const* msg, ... ); - s32 assert_crash( char const* condition ); - void process_exit( u32 code ); - #pragma endregion Debug - - #pragma region Memory - #ifndef kilobytes - # define kilobytes( x ) ( ( x ) * ( s64 )( 1024 ) ) - # define megabytes( x ) ( kilobytes( x ) * ( s64 )( 1024 ) ) - # define gigabytes( x ) ( megabytes( x ) * ( s64 )( 1024 ) ) - # define terabytes( x ) ( gigabytes( x ) * ( s64 )( 1024 ) ) - #endif - - #define ZPL__ONES ( zpl_cast( uw ) - 1 / ZPL_U8_MAX ) - #define ZPL__HIGHS ( ZPL__ONES * ( ZPL_U8_MAX / 2 + 1 ) ) - #define ZPL__HAS_ZERO( x ) ( ( ( x )-ZPL__ONES ) & ~( x )&ZPL__HIGHS ) - - //! Checks if value is power of 2. - ZPL_DEF_INLINE b32 is_power_of_two( sw x ); - - //! Aligns address to specified alignment. - ZPL_DEF_INLINE void* align_forward( void* ptr, sw alignment ); - - //! Aligns value to a specified alignment. - ZPL_DEF_INLINE s64 align_forward_i64( s64 value, sw alignment ); - - //! Moves pointer forward by bytes. - ZPL_DEF_INLINE void* pointer_add( void* ptr, sw bytes ); - - //! Copy non-overlapping memory from source to destination. - void* mem_copy( void* dest, void const* source, sw size ); - - //! Search for a constant value within the size limit at memory location. - void const* mem_find( void const* data, u8 byte_value, sw size ); - - //! Copy memory from source to destination. - ZPL_DEF_INLINE void* mem_move( void* dest, void const* source, sw size ); - - //! Set constant value at memory location with specified size. - ZPL_DEF_INLINE void* mem_set( void* data, u8 byte_value, sw size ); - - //! @param ptr Memory location to clear up. - //! @param size The size to clear up with. - ZPL_DEF_INLINE void zero_size( void* ptr, sw size ); - - #ifndef zero_item - //! Clears up an item. - # define zero_item( t ) zero_size( ( t ), size_of( *( t ) ) ) // NOTE: Pass pointer of struct - - //! Clears up an array. - # define zero_array( a, count ) zero_size( ( a ), size_of( *( a ) ) * count ) - #endif - - enum AllocType : u8 - { - EAllocation_ALLOC, - EAllocation_FREE, - EAllocation_FREE_ALL, - EAllocation_RESIZE, - }; - - using AllocatorProc = void* ( void* allocator_data, AllocType type - , sw size, sw alignment - , void* old_memory, sw old_size - , u64 flags ); - - struct AllocatorInfo - { - AllocatorProc* Proc; - void* Data; - }; - - enum AllocFlag - { - ALLOCATOR_FLAG_CLEAR_TO_ZERO = bit( 0 ), - }; - - #ifndef ZPL_DEFAULT_MEMORY_ALIGNMENT - # define ZPL_DEFAULT_MEMORY_ALIGNMENT ( 2 * size_of( void* ) ) - #endif - - #ifndef ZPL_DEFAULT_ALLOCATOR_FLAGS - # define ZPL_DEFAULT_ALLOCATOR_FLAGS ( ALLOCATOR_FLAG_CLEAR_TO_ZERO ) - #endif - - //! Allocate memory with default alignment. - ZPL_DEF_INLINE void* alloc( AllocatorInfo a, sw size ); - - //! Allocate memory with specified alignment. - ZPL_DEF_INLINE void* alloc_align( AllocatorInfo a, sw size, sw alignment ); - - //! Free allocated memory. - ZPL_DEF_INLINE void free( AllocatorInfo a, void* ptr ); - - //! Free all memory allocated by an allocator. - ZPL_DEF_INLINE void free_all( AllocatorInfo a ); - - //! Resize an allocated memory. - ZPL_DEF_INLINE void* resize( AllocatorInfo a, void* ptr, sw old_size, sw new_size ); - - //! Resize an allocated memory with specified alignment. - ZPL_DEF_INLINE void* resize_align( AllocatorInfo a, void* ptr, sw old_size, sw new_size, sw alignment ); - - #ifndef alloc_item - //! Allocate memory for an item. - # define alloc_item( allocator_, Type ) ( Type* )alloc( allocator_, size_of( Type ) ) - - //! Allocate memory for an array of items. - # define alloc_array( allocator_, Type, count ) ( Type* )alloc( allocator_, size_of( Type ) * ( count ) ) - #endif - - /* heap memory analysis tools */ - /* define ZPL_HEAP_ANALYSIS to enable this feature */ - /* call zpl_heap_stats_init at the beginning of the entry point */ - /* you can call zpl_heap_stats_check near the end of the execution to validate any possible leaks */ - void heap_stats_init( void ); - sw heap_stats_used_memory( void ); - sw heap_stats_alloc_count( void ); - void heap_stats_check( void ); - - //! Allocate/Resize memory using default options. - - //! Use this if you don't need a "fancy" resize allocation - ZPL_DEF_INLINE void* default_resize_align( AllocatorInfo a, void* ptr, sw old_size, sw new_size, sw alignment ); - - void* heap_allocator_proc( void* allocator_data, AllocType type, sw size, sw alignment, void* old_memory, sw old_size, u64 flags ); - - //! The heap allocator backed by operating system's memory manager. - constexpr AllocatorInfo heap( void ) { return { heap_allocator_proc, nullptr }; } - - #ifndef malloc - //! Helper to allocate memory using heap allocator. - # define malloc( sz ) alloc( heap(), sz ) - - //! Helper to free memory allocated by heap allocator. - # define mfree( ptr ) free( heap(), ptr ) - #endif - - ZPL_IMPL_INLINE b32 is_power_of_two( sw x ) - { - if ( x <= 0 ) - return false; - return ! ( x & ( x - 1 ) ); - } - - ZPL_IMPL_INLINE void* align_forward( void* ptr, sw alignment ) - { - uptr p; - - ZPL_ASSERT( is_power_of_two( alignment ) ); - - p = zpl_cast( uptr ) ptr; - return zpl_cast( void* )( ( p + ( alignment - 1 ) ) & ~( alignment - 1 ) ); - } - - ZPL_IMPL_INLINE s64 align_forward_i64( s64 value, sw alignment ) - { - return value + ( alignment - value % alignment ) % alignment; - } - - ZPL_IMPL_INLINE void* pointer_add( void* ptr, sw bytes ) - { - return zpl_cast( void* )( zpl_cast( u8* ) ptr + bytes ); - } - - ZPL_IMPL_INLINE void* mem_move( void* dest, void const* source, sw n ) - { - if ( dest == NULL ) - { - return NULL; - } - - u8* d = zpl_cast( u8* ) dest; - u8 const* s = zpl_cast( u8 const* ) source; - - if ( d == s ) - return d; - if ( s + n <= d || d + n <= s ) // NOTE: Non-overlapping - return mem_copy( d, s, n ); - - if ( d < s ) - { - if ( zpl_cast( uptr ) s % size_of( sw ) == zpl_cast( uptr ) d % size_of( sw ) ) - { - while ( zpl_cast( uptr ) d % size_of( sw ) ) - { - if ( ! n-- ) - return dest; - *d++ = *s++; - } - while ( n >= size_of( sw ) ) - { - *zpl_cast( sw* ) d = *zpl_cast( sw* ) s; - n -= size_of( sw ); - d += size_of( sw ); - s += size_of( sw ); - } - } - for ( ; n; n-- ) - *d++ = *s++; - } - else - { - if ( ( zpl_cast( uptr ) s % size_of( sw ) ) == ( zpl_cast( uptr ) d % size_of( sw ) ) ) - { - while ( zpl_cast( uptr )( d + n ) % size_of( sw ) ) - { - if ( ! n-- ) - return dest; - d[ n ] = s[ n ]; - } - while ( n >= size_of( sw ) ) - { - n -= size_of( sw ); - *zpl_cast( sw* )( d + n ) = *zpl_cast( sw* )( s + n ); - } - } - while ( n ) - n--, d[ n ] = s[ n ]; - } - - return dest; - } - - ZPL_IMPL_INLINE void* mem_set( void* dest, u8 c, sw n ) - { - if ( dest == NULL ) - { - return NULL; - } - - u8* s = zpl_cast( u8* ) dest; - sw k; - u32 c32 = ( ( u32 )-1 ) / 255 * c; - - if ( n == 0 ) - return dest; - s[ 0 ] = s[ n - 1 ] = c; - if ( n < 3 ) - return dest; - s[ 1 ] = s[ n - 2 ] = c; - s[ 2 ] = s[ n - 3 ] = c; - if ( n < 7 ) - return dest; - s[ 3 ] = s[ n - 4 ] = c; - if ( n < 9 ) - return dest; - - k = -zpl_cast( sptr ) s & 3; - s += k; - n -= k; - n &= -4; - - *zpl_cast( u32* )( s + 0 ) = c32; - *zpl_cast( u32* )( s + n - 4 ) = c32; - if ( n < 9 ) - return dest; - *zpl_cast( u32* )( s + 4 ) = c32; - *zpl_cast( u32* )( s + 8 ) = c32; - *zpl_cast( u32* )( s + n - 12 ) = c32; - *zpl_cast( u32* )( s + n - 8 ) = c32; - if ( n < 25 ) - return dest; - *zpl_cast( u32* )( s + 12 ) = c32; - *zpl_cast( u32* )( s + 16 ) = c32; - *zpl_cast( u32* )( s + 20 ) = c32; - *zpl_cast( u32* )( s + 24 ) = c32; - *zpl_cast( u32* )( s + n - 28 ) = c32; - *zpl_cast( u32* )( s + n - 24 ) = c32; - *zpl_cast( u32* )( s + n - 20 ) = c32; - *zpl_cast( u32* )( s + n - 16 ) = c32; - - k = 24 + ( zpl_cast( uptr ) s & 4 ); - s += k; - n -= k; - - { - u64 c64 = ( zpl_cast( u64 ) c32 << 32 ) | c32; - while ( n > 31 ) - { - *zpl_cast( u64* )( s + 0 ) = c64; - *zpl_cast( u64* )( s + 8 ) = c64; - *zpl_cast( u64* )( s + 16 ) = c64; - *zpl_cast( u64* )( s + 24 ) = c64; - - n -= 32; - s += 32; - } - } - - return dest; - } - - ZPL_IMPL_INLINE void* alloc_align( AllocatorInfo a, sw size, sw alignment ) - { - return a.Proc( a.Data, EAllocation_ALLOC, size, alignment, nullptr, 0, ZPL_DEFAULT_ALLOCATOR_FLAGS ); - } - - ZPL_IMPL_INLINE void* alloc( AllocatorInfo a, sw size ) - { - return alloc_align( a, size, ZPL_DEFAULT_MEMORY_ALIGNMENT ); - } - - ZPL_IMPL_INLINE void free( AllocatorInfo a, void* ptr ) - { - if ( ptr != nullptr ) - a.Proc( a.Data, EAllocation_FREE, 0, 0, ptr, 0, ZPL_DEFAULT_ALLOCATOR_FLAGS ); - } - - ZPL_IMPL_INLINE void free_all( AllocatorInfo a ) - { - a.Proc( a.Data, EAllocation_FREE_ALL, 0, 0, nullptr, 0, ZPL_DEFAULT_ALLOCATOR_FLAGS ); - } - - ZPL_IMPL_INLINE void* resize( AllocatorInfo a, void* ptr, sw old_size, sw new_size ) - { - return resize_align( a, ptr, old_size, new_size, ZPL_DEFAULT_MEMORY_ALIGNMENT ); - } - - ZPL_IMPL_INLINE void* resize_align( AllocatorInfo a, void* ptr, sw old_size, sw new_size, sw alignment ) - { - return a.Proc( a.Data, EAllocation_RESIZE, new_size, alignment, ptr, old_size, ZPL_DEFAULT_ALLOCATOR_FLAGS ); - } - - ZPL_IMPL_INLINE void* default_resize_align( AllocatorInfo a, void* old_memory, sw old_size, sw new_size, sw alignment ) - { - if ( ! old_memory ) - return alloc_align( a, new_size, alignment ); - - if ( new_size == 0 ) - { - free( a, old_memory ); - return nullptr; - } - - if ( new_size < old_size ) - new_size = old_size; - - if ( old_size == new_size ) - { - return old_memory; - } - else - { - void* new_memory = alloc_align( a, new_size, alignment ); - if ( ! new_memory ) - return nullptr; - mem_move( new_memory, old_memory, min( new_size, old_size ) ); - free( a, old_memory ); - return new_memory; - } - } - - ZPL_IMPL_INLINE void zero_size( void* ptr, sw size ) - { - mem_set( ptr, 0, size ); - } - - struct Arena - { - static - void* allocator_proc( void* allocator_data, AllocType type, sw size, sw alignment, void* old_memory, sw old_size, u64 flags ); - - static - Arena init_from_memory( void* start, sw size ) - { - return - { - { nullptr, nullptr }, - start, - size, - 0, - 0 - }; - } - - static - Arena init_from_allocator( AllocatorInfo backing, sw size ) - { - Arena result = - { - backing, - alloc( backing, size), - size, - 0, - 0 - }; - return result; - } - - static - Arena init_sub( Arena& parent, sw size ) - { - return init_from_allocator( parent.Backing, size ); - } - - sw alignment_of( sw alignment ) - { - sw alignment_offset, result_pointer, mask; - ZPL_ASSERT( is_power_of_two( alignment ) ); - - alignment_offset = 0; - result_pointer = (sw) PhysicalStart + TotalUsed; - mask = alignment - 1; - - if ( result_pointer & mask ) - alignment_offset = alignment - ( result_pointer & mask ); - - return alignment_offset; - } - - void check() - { - ZPL_ASSERT( TempCount == 0 ); - } - - void free() - { - if ( Backing.Proc ) - { - gen::free( Backing, PhysicalStart ); - PhysicalStart = nullptr; - } - } - - sw size_remaining( sw alignment ) - { - sw result = TotalSize - ( TotalUsed + alignment_of( alignment ) ); - return result; - } - - AllocatorInfo Backing; - void* PhysicalStart; - sw TotalSize; - sw TotalUsed; - sw TempCount; - - operator AllocatorInfo() - { - return { allocator_proc, this }; - } - }; - - struct Pool - { - static - void* allocator_proc( void* allocator_data, AllocType type, sw size, sw alignment, void* old_memory, sw old_size, u64 flags ); - - static - Pool init( AllocatorInfo backing, sw num_blocks, sw block_size ) - { - return init_align( backing, num_blocks, block_size, ZPL_DEFAULT_MEMORY_ALIGNMENT ); - } - - static - Pool init_align( AllocatorInfo backing, sw num_blocks, sw block_size, sw block_align ); - - void free() - { - if ( Backing.Proc ) - { - gen::free( Backing, PhysicalStart ); - } - } - - AllocatorInfo Backing; - void* PhysicalStart; - void* FreeList; - sw BlockSize; - sw BlockAlign; - sw TotalSize; - sw NumBlocks; - - operator AllocatorInfo() - { - return { allocator_proc, this }; - } - }; - #pragma endregion Memory - - #pragma region String Ops - ZPL_DEF_INLINE const char* char_first_occurence( const char* str, char c ); - - ZPL_DEF_INLINE b32 char_is_alpha( char c ); - ZPL_DEF_INLINE b32 char_is_alphanumeric( char c ); - ZPL_DEF_INLINE b32 char_is_digit( char c ); - ZPL_DEF_INLINE b32 char_is_hex_digit( char c ); - ZPL_DEF_INLINE b32 char_is_space( char c ); - ZPL_DEF_INLINE char char_to_lower( char c ); - ZPL_DEF_INLINE char char_to_upper( char c ); - - ZPL_DEF_INLINE s32 digit_to_int( char c ); - ZPL_DEF_INLINE s32 hex_digit_to_int( char c ); - - ZPL_DEF_INLINE s32 str_compare( const char* s1, const char* s2 ); - ZPL_DEF_INLINE s32 str_compare( const char* s1, const char* s2, sw len ); - ZPL_DEF_INLINE char* str_copy( char* dest, const char* source, sw len ); - ZPL_DEF_INLINE sw str_copy_nulpad( char* dest, const char* source, sw len ); - ZPL_DEF_INLINE sw str_len( const char* str ); - ZPL_DEF_INLINE sw str_len( const char* str, sw max_len ); - ZPL_DEF_INLINE char* str_reverse( char* str ); // NOTE: ASCII only - - // NOTE: ASCII only - ZPL_DEF_INLINE void str_to_lower( char* str ); - ZPL_DEF_INLINE void str_to_upper( char* str ); - - s64 str_to_i64( const char* str, char** end_ptr, s32 base ); // TODO : Support more than just decimal and hexadecimal - void i64_to_str( s64 value, char* string, s32 base ); - void u64_to_str( u64 value, char* string, s32 base ); - - ZPL_IMPL_INLINE const char* char_first_occurence( const char* s, char c ) - { - char ch = c; - for ( ; *s != ch; s++ ) - { - if ( *s == '\0' ) - return NULL; - } - return s; - } - - ZPL_IMPL_INLINE b32 char_is_alpha( char c ) - { - if ( ( c >= 'A' && c <= 'Z' ) || ( c >= 'a' && c <= 'z' ) ) - return true; - return false; - } - - ZPL_IMPL_INLINE b32 char_is_alphanumeric( char c ) - { - return char_is_alpha( c ) || char_is_digit( c ); - } - - ZPL_IMPL_INLINE b32 char_is_digit( char c ) - { - if ( c >= '0' && c <= '9' ) - return true; - return false; - } - - ZPL_IMPL_INLINE b32 char_is_hex_digit( char c ) - { - if ( char_is_digit( c ) || ( c >= 'a' && c <= 'f' ) || ( c >= 'A' && c <= 'F' ) ) - return true; - return false; - } - - ZPL_IMPL_INLINE b32 char_is_space( char c ) - { - if ( c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f' || c == '\v' ) - return true; - return false; - } - - ZPL_IMPL_INLINE char char_to_lower( char c ) - { - if ( c >= 'A' && c <= 'Z' ) - return 'a' + ( c - 'A' ); - return c; - } - - ZPL_IMPL_INLINE char char_to_upper( char c ) - { - if ( c >= 'a' && c <= 'z' ) - return 'A' + ( c - 'a' ); - return c; - } - - ZPL_IMPL_INLINE s32 digit_to_int( char c ) - { - return char_is_digit( c ) ? c - '0' : c - 'W'; - } - - ZPL_IMPL_INLINE s32 hex_digit_to_int( char c ) - { - if ( char_is_digit( c ) ) - return digit_to_int( c ); - else if ( is_between( c, 'a', 'f' ) ) - return c - 'a' + 10; - else if ( is_between( c, 'A', 'F' ) ) - return c - 'A' + 10; - return -1; - } - - ZPL_IMPL_INLINE s32 str_compare( const char* s1, const char* s2 ) - { - while ( *s1 && ( *s1 == *s2 ) ) - { - s1++, s2++; - } - return *( u8* )s1 - *( u8* )s2; - } - - ZPL_IMPL_INLINE s32 str_compare( const char* s1, const char* s2, sw len ) - { - for ( ; len > 0; s1++, s2++, len-- ) - { - if ( *s1 != *s2 ) - return ( ( s1 < s2 ) ? -1 : +1 ); - else if ( *s1 == '\0' ) - return 0; - } - return 0; - } - - ZPL_IMPL_INLINE char* str_copy( char* dest, const char* source, sw len ) - { - ZPL_ASSERT_NOT_NULL( dest ); - if ( source ) - { - char* str = dest; - while ( len > 0 && *source ) - { - *str++ = *source++; - len--; - } - while ( len > 0 ) - { - *str++ = '\0'; - len--; - } - } - return dest; - } - - ZPL_IMPL_INLINE sw str_copy_nulpad( char* dest, const char* source, sw len ) - { - sw result = 0; - ZPL_ASSERT_NOT_NULL( dest ); - if ( source ) - { - const char* source_start = source; - char* str = dest; - while ( len > 0 && *source ) - { - *str++ = *source++; - len--; - } - while ( len > 0 ) - { - *str++ = '\0'; - len--; - } - - result = source - source_start; - } - return result; - } - - ZPL_IMPL_INLINE sw str_len( const char* str ) - { - if ( str == NULL ) - { - return 0; - } - const char* p = str; - while ( *str ) - str++; - return str - p; - } - - ZPL_IMPL_INLINE sw str_len( const char* str, sw max_len ) - { - const char* end = zpl_cast( const char* ) mem_find( str, 0, max_len ); - if ( end ) - return end - str; - return max_len; - } - - ZPL_IMPL_INLINE char* str_reverse( char* str ) - { - sw len = str_len( str ); - char* a = str + 0; - char* b = str + len - 1; - len /= 2; - while ( len-- ) - { - swap( char, *a, *b ); - a++, b--; - } - return str; - } - - ZPL_IMPL_INLINE void str_to_lower( char* str ) - { - if ( ! str ) - return; - while ( *str ) - { - *str = char_to_lower( *str ); - str++; - } - } - - ZPL_IMPL_INLINE void str_to_upper( char* str ) - { - if ( ! str ) - return; - while ( *str ) - { - *str = char_to_upper( *str ); - str++; - } - } - #pragma endregion String Ops - - #pragma region Containers - #pragma push_macro("template") - #undef template - - template - struct Array - { - struct Header - { - AllocatorInfo Allocator; - uw Capacity; - uw Num; - }; - - static - Array init( AllocatorInfo allocator ) - { - return init_reserve( allocator, grow_formula(0) ); - } - - static - Array init_reserve( AllocatorInfo allocator, sw capacity ) - { - Header* header = rcast( Header*, alloc( allocator, sizeof(Header) + sizeof(Type) * capacity )); - - if ( header == nullptr ) - return { nullptr }; - - header->Allocator = allocator; - header->Capacity = capacity; - header->Num = 0; - - return { rcast( Type*, header + 1) }; - } - - static - uw grow_formula( uw value ) - { - return 2 * value + 8; - } - - 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++; - - return true; - } - - Type& back( void ) - { - Header& header = * get_header(); - return Data[ header.Num - 1 ]; - } - - void clear( void ) - { - Header& header = * get_header(); - header.Num = 0; - } - - bool fill( uw begin, uw end, Type value ) - { - Header& header = * get_header(); - - if ( begin < 0 || end >= header.Num ) - return false; - - for ( sw idx = begin; idx < end; idx++ ) - { - Data[ idx ] = value; - } - - return true; - } - - void free( void ) - { - Header& header = * get_header(); - gen::free( header.Allocator, &header ); - } - - Header* get_header( void ) - { - return rcast( Header*, Data ) - 1 ; - } - - bool grow( uw min_capacity ) - { - Header& header = * get_header(); - uw new_capacity = grow_formula( header.Capacity ); - - if ( new_capacity < min_capacity ) - new_capacity = min_capacity; - - return set_capacity( new_capacity ); - } - - uw num( void ) - { - return get_header()->Num; - } - - bool pop( void ) - { - Header& header = * get_header(); - - ZPL_ASSERT( header.Num > 0 ); - header.Num--; - } - - void remove_at( uw idx ) - { - Header* header = get_header(); - ZPL_ASSERT( idx < header->Num ); - - mem_move( header + idx, header + idx + 1, sizeof( Type ) * ( header->Num - idx - 1 ) ); - header->Num--; - } - - bool reserve( uw new_capacity ) - { - Header& header = * get_header(); - - if ( header.Capacity < new_capacity ) - return set_capacity( new_capacity ); - - return true; - } - - bool resize( uw num ) - { - Header* header = get_header(); - - if ( header->Capacity < num ) - { - if ( ! grow( num ) ) - return false; - - header = get_header(); - } - - header->Num = num; - return true; - } - - bool set_capacity( uw new_capacity ) - { - Header& header = * get_header(); - - if ( new_capacity == header.Capacity ) - return true; - - if ( new_capacity < header.Num ) - header.Num = new_capacity; - - sw size = sizeof( Header ) + sizeof( Type ) * new_capacity; - Header* new_header = rcast( Header*, alloc( header.Allocator, size ) ); - - if ( new_header == nullptr ) - return false; - - mem_move( new_header, &header, sizeof( Header ) + sizeof( Type ) * header.Num ); - - new_header->Capacity = new_capacity; - - gen::free( header.Allocator, &header ); - - Data = rcast( Type*, new_header + 1); - return true; - } - - 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; - } - }; - - template - struct HashTable - { - struct FindResult - { - sw HashIndex; - sw PrevIndex; - sw EntryIndex; - }; - - struct Entry - { - u64 Key; - sw Next; - Type Value; - }; - - static - HashTable init( AllocatorInfo allocator ) - { - HashTable result = { { nullptr }, { nullptr } }; - - result.Hashes = Array::init( allocator ); - result.Entries = Array::init( allocator ); - - return result; - } - - static - HashTable init_reserve( AllocatorInfo allocator, sw num ) - { - HashTable result = { { nullptr }, { nullptr } }; - - result.Hashes = Array::init_reserve( allocator, num ); - result.Hashes.get_header()->Num = num; - - result.Entries = Array::init_reserve( allocator, num ); - - return result; - } - - void clear( void ) - { - for ( sw idx = 0; idx < Hashes.num(); idx++ ) - Hashes[ idx ] = -1; - - Hashes.clear(); - Entries.clear(); - } - - void destroy( void ) - { - if ( Hashes && Hashes.get_header()->Capacity ) - Hashes.free(); - if ( Entries && Hashes.get_header()->Capacity ) - Entries.free(); - } - - Type* get( u64 key ) - { - sw idx = find( key ).EntryIndex; - if ( idx >= 0 ) - return & Entries[ idx ].Value; - - return nullptr; - } - - using MapProc = void (*)( u64 key, Type value ); - - void map( MapProc map_proc ) - { - ZPL_ASSERT_NOT_NULL( map_proc ); - - for ( sw idx = 0; idx < Entries.num(); idx++ ) - { - map_proc( Entries[ idx ].Key, Entries[ idx ].Value ); - } - } - - using MapMutProc = void (*)( u64 key, Type* value ); - - void map_mut( MapMutProc map_proc ) - { - ZPL_ASSERT_NOT_NULL( map_proc ); - - for ( sw idx = 0; idx < Entries.num(); idx++ ) - { - map_proc( Entries[ idx ].Key, & Entries[ idx ].Value ); - } - } - - void grow() - { - sw new_num = Array::grow_formula( Entries.num() ); - rehash( new_num ); - } - - void rehash( sw new_num ) - { - sw idx; - sw last_added_index; - - HashTable new_ht = init_reserve( Hashes.get_header()->Allocator, new_num ); - - Array::Header* hash_header = new_ht.Hashes.get_header(); - - for ( idx = 0; idx < new_ht.Hashes.num(); ++idx ) - new_ht.Hashes[ idx ] = -1; - - for ( idx = 0; idx < Entries.num(); ++idx ) - { - Entry& entry = Entries[ idx ]; - - FindResult find_result; - - if ( new_ht.Hashes.num() == 0 ) - new_ht.grow(); - - entry = Entries[ idx ]; - find_result = new_ht.find( entry.Key ); - last_added_index = new_ht.add_entry( entry.Key ); - - if ( find_result.PrevIndex < 0 ) - new_ht.Hashes[ find_result.HashIndex ] = last_added_index; - - else - new_ht.Entries[ find_result.PrevIndex ].Next = last_added_index; - - new_ht.Entries[ last_added_index ].Next = find_result.EntryIndex; - new_ht.Entries[ last_added_index ].Value = entry.Value; - } - - destroy(); - *this = new_ht; - } - - void rehash_fast() - { - sw idx; - - for ( idx = 0; idx < Entries.num(); idx++ ) - Entries[ idx ].Next = -1; - - for ( idx = 0; idx < Hashes.num(); idx++ ) - Hashes[ idx ] = -1; - - for ( idx = 0; idx < Entries.num(); idx++ ) - { - Entry* entry; - - FindResult find_result; - } - } - - void remove( u64 key ) - { - FindResult find_result = find( key); - - if ( find_result.EntryIndex >= 0 ) - { - Entries.remove_at( find_result.EntryIndex ); - rehash_fast(); - } - } - - void remove_entry( sw idx ) - { - Entries.remove_at( idx ); - } - - void set( u64 key, Type value ) - { - sw idx; - FindResult find_result; - - if ( Hashes.num() == 0 ) - grow(); - - find_result = find( key ); - - if ( find_result.EntryIndex >= 0 ) - { - idx = find_result.EntryIndex; - } - else - { - idx = add_entry( key ); - - if ( find_result.PrevIndex >= 0 ) - { - Entries[ find_result.PrevIndex ].Next = idx; - } - else - { - Hashes[ find_result.HashIndex ] = idx; - } - } - - Entries[ idx ].Value = value; - - if ( full() ) - grow(); - } - - sw slot( u64 key ) - { - for ( sw idx = 0; idx < Hashes.num(); ++idx ) - if ( Hashes[ idx ] == key ) - return idx; - - return -1; - } - - Array< sw> Hashes; - Array< Entry> Entries; - - protected: - - sw add_entry( u64 key ) - { - sw idx; - Entry entry = { key, -1 }; - - idx = Entries.num(); - Entries.append( entry ); - return idx; - } - - FindResult find( u64 key ) - { - FindResult result = { -1, -1, -1 }; - - if ( Hashes.num() > 0 ) - { - result.HashIndex = key % Hashes.num(); - result.EntryIndex = Hashes[ result.HashIndex ]; - - while ( result.EntryIndex >= 0 ) - { - if ( Entries[ result.EntryIndex ].Key == key ) - break; - - result.PrevIndex = result.EntryIndex; - result.EntryIndex = Entries[ result.EntryIndex ].Next; - } - } - - return result; - } - - b32 full() - { - return 0.75f * Hashes.num() < Entries.num(); - } - }; - - #pragma pop_macro("template") - #pragma endregion Containers - - #pragma region Hashing - - u32 crc32( void const* data, sw len ); - - #pragma endregion Hashing - - #pragma region String - // Constant string with length. - struct StrC - { - sw Len; - char const* Ptr; - - operator char const* () const - { - return Ptr; - } - }; - - #define txt_StrC( text ) \ - (StrC){ sizeof( text ) - 1, text } - - StrC to_StrC( char const* str ) - { - return { str_len( str ), str }; - } - - // Dynamic String - // This is directly based off the ZPL string api. - // They used a header pattern - // I kept it for simplicty of porting but its not necessary to keep it that way. - struct String - { - struct Header - { - AllocatorInfo Allocator; - sw Length; - sw Capacity; - }; - - static - String make( AllocatorInfo allocator, char const* str ) - { - sw length = str ? str_len( str ) : 0; - return make_length( allocator, str, length ); - } - - static - String make( AllocatorInfo allocator, StrC str ) - { - return make_length( allocator, str.Ptr, str.Len ); - } - - static - String make_reserve( AllocatorInfo allocator, sw capacity ) - { - constexpr sw header_size = sizeof( Header ); - - s32 alloc_size = header_size + capacity + 1; - void* allocation = alloc( allocator, alloc_size ); - - if ( allocation == nullptr ) - return { nullptr }; - - mem_set( allocation, 0, alloc_size ); - - Header* - header = rcast(Header*, allocation); - header->Allocator = allocator; - header->Capacity = capacity; - header->Length = 0; - - String result = { (char*)allocation + header_size }; - return result; - } - - static - String make_length( AllocatorInfo allocator, char const* str, sw length ) - { - constexpr sw header_size = sizeof( Header ); - - s32 alloc_size = header_size + length + 1; - void* allocation = alloc( allocator, alloc_size ); - - if ( allocation == nullptr ) - return { nullptr }; - - if ( ! str ) - mem_set( allocation, 0, alloc_size ); - - Header& - header = * rcast(Header*, allocation); - header = { allocator, length, length }; - - String result = { rcast( char*, allocation) + header_size }; - - if ( length && str ) - mem_copy( result, str, length ); - - result[ length ] = '\0'; - - return result; - } - - static - String fmt( AllocatorInfo allocator, char* buf, sw buf_size, char const* fmt, ... ); - - static - String fmt_buf( AllocatorInfo allocator, char const* fmt, ... ); - - static - String join( AllocatorInfo allocator, char const** parts, sw num_parts, char const* glue ) - { - String result = make( allocator, "" ); - - for ( sw idx = 0; idx < num_parts; ++idx ) - { - result.append( parts[ idx ] ); - - if ( idx < num_parts - 1 ) - result.append( glue ); - } - - return result; - } - - static - bool are_equal( String lhs, String rhs ) - { - if ( lhs.length() != rhs.length() ) - return false; - - for ( sw idx = 0; idx < lhs.length(); ++idx ) - if ( lhs[ idx ] != rhs[ idx ] ) - return false; - - return true; - } - - bool make_space_for( char const* str, sw add_len ) - { - sw available = avail_space(); - - // NOTE: Return if there is enough space left - if ( available >= add_len ) - { - return true; - } - else - { - sw new_len, old_size, new_size; - - void* ptr; - void* new_ptr; - - AllocatorInfo allocator = get_header().Allocator; - Header* header = nullptr; - - new_len = 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 = zpl_cast( Header* ) new_ptr; - header->Allocator = allocator; - header->Capacity = new_len; - - Data = rcast( char*, header + 1 ); - - return str; - } - } - - bool append( char const* str ) - { - return append( str, str_len( str ) ); - } - - bool append( char const* str, sw length ) - { - if ( sptr(str) > 0 ) - { - sw 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; - } - - 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, ... ); - - sw avail_space() const - { - Header const& - header = * rcast( Header const*, Data - sizeof( Header )); - - return header.Capacity - header.Length; - } - - sw capacity() const - { - Header const& - header = * rcast( Header const*, Data - sizeof( Header )); - - return header.Capacity; - } - - void clear() - { - get_header().Length = 0; - } - - String duplicate( AllocatorInfo allocator ) - { - return make_length( allocator, Data, length() ); - } - - void free() - { - if ( ! Data ) - return; - - Header& header = get_header(); - - gen::free( header.Allocator, & header ); - } - - Header& get_header() - { - return *(Header*)(Data - sizeof(Header)); - } - - sw length() const - { - Header const& - header = * rcast( Header const*, Data - sizeof( Header )); - - return header.Length; - } - - void trim( char const* cut_set ) - { - sw len = 0; - - char* start_pos = Data; - char* end_pos = Data + length() - 1; - - while ( start_pos <= end_pos && char_first_occurence( cut_set, *start_pos ) ) - start_pos++; - - while ( end_pos > start_pos && char_first_occurence( cut_set, *end_pos ) ) - end_pos--; - - len = scast( sw, ( start_pos > end_pos ) ? 0 : ( ( end_pos - start_pos ) + 1 ) ); - - if ( Data != start_pos ) - mem_move( Data, start_pos, len ); - - Data[ len ] = '\0'; - - get_header().Length = len; - } - - void trim_space() - { - return trim( " \t\r\n\v\f" ); - } - - // For-range support - - char* begin() - { - return Data; - } - - char* end() - { - Header const& - header = * rcast( Header const*, Data - sizeof( Header )); - - return Data + header.Length; - } - - operator bool() - { - return Data; - } - - 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 [] ( sw index ) - { - return Data[ index ]; - } - - char const& operator [] ( sw index ) const - { - return Data[ index ]; - } - - char* Data = nullptr; - }; - - struct String_POD - { - char* Data; - - operator String() - { - return * rcast(String*, this); - } - }; - static_assert( sizeof( String_POD ) == sizeof( String ), "String is not a POD" ); - #pragma endregion String - - #pragma region File Handling - - typedef u32 FileMode; - - enum FileModeFlag - { - EFileMode_READ = bit( 0 ), - EFileMode_WRITE = bit( 1 ), - EFileMode_APPEND = bit( 2 ), - EFileMode_RW = bit( 3 ), - ZPL_FILE_MODES = EFileMode_READ | EFileMode_WRITE | EFileMode_APPEND | EFileMode_RW, - }; - - // NOTE: Only used internally and for the file operations - enum SeekWhenceType - { - ESeekWhence_BEGIN = 0, - ESeekWhence_CURRENT = 1, - ESeekWhence_END = 2, - }; - - enum FileError - { - EFileError_NONE, - EFileError_INVALID, - EFileError_INVALID_FILENAME, - EFileError_EXISTS, - EFileError_NOT_EXISTS, - EFileError_PERMISSION, - EFileError_TRUNCATION_FAILURE, - EFileError_NOT_EMPTY, - EFileError_NAME_TOO_LONG, - EFileError_UNKNOWN, - }; - - union FileDescriptor - { - void* p; - sptr i; - uptr u; - }; - - typedef struct FileOperations FileOperations; - - #define ZPL_FILE_OPEN_PROC( name ) FileError name( FileDescriptor* fd, FileOperations* ops, FileMode mode, char const* filename ) - #define ZPL_FILE_READ_AT_PROC( name ) b32 name( FileDescriptor fd, void* buffer, sw size, s64 offset, sw* bytes_read, b32 stop_at_newline ) - #define ZPL_FILE_WRITE_AT_PROC( name ) b32 name( FileDescriptor fd, void const* buffer, sw size, s64 offset, sw* bytes_written ) - #define ZPL_FILE_SEEK_PROC( name ) b32 name( FileDescriptor fd, s64 offset, SeekWhenceType whence, s64* new_offset ) - #define ZPL_FILE_CLOSE_PROC( name ) void name( FileDescriptor fd ) - - typedef ZPL_FILE_OPEN_PROC( file_open_proc ); - typedef ZPL_FILE_READ_AT_PROC( FileReadProc ); - typedef ZPL_FILE_WRITE_AT_PROC( FileWriteProc ); - typedef ZPL_FILE_SEEK_PROC( FileSeekProc ); - typedef ZPL_FILE_CLOSE_PROC( FileCloseProc ); - - struct FileOperations - { - FileReadProc* read_at; - FileWriteProc* write_at; - FileSeekProc* seek; - FileCloseProc* close; - }; - - extern FileOperations const default_file_operations; - - typedef u64 FileTime; - - enum DirType - { - ZPL_DIR_TYPE_FILE, - ZPL_DIR_TYPE_FOLDER, - ZPL_DIR_TYPE_UNKNOWN, - }; - - struct DirInfo; - - struct DirEntry - { - char const* FileName; - DirInfo* Info; - u8 Type; - }; - - struct DirInfo - { - char const* FullPath; - DirEntry* Entries; // zpl_array - - // Internals - char** Filenames; // zpl_array - String Buffer; - }; - - struct FileInfo - { - FileOperations Ops; - FileDescriptor FD; - b32 IsTemp; - - char const* Filename; - FileTime LastWriteTime; - DirEntry* Dir; - }; - - enum FileStandardType - { - EFileStandard_INPUT, - EFileStandard_OUTPUT, - EFileStandard_ERROR, - - EFileStandard_COUNT, - }; - - /** - * Get standard file I/O. - * @param std Check zpl_file_standard_type - * @return File handle to standard I/O - */ - FileInfo* file_get_standard( FileStandardType std ); - - /** - * Closes the file - * @param file - */ - FileError file_close( FileInfo* file ); - - /** - * Returns the currently opened file's name - * @param file - */ - inline - char const* file_name( FileInfo* file ) - { - return file->Filename ? file->Filename : ""; - } - - /** - * Opens a file using a specified mode - * @param file - * @param mode Access mode to use - * @param filename - */ - FileError file_open_mode( FileInfo* file, FileMode mode, char const* filename ); - - /** - * Seeks the file cursor from the beginning of file to a specific position - * @param file - * @param offset Offset to seek to - */ - ZPL_DEF_INLINE s64 file_seek( FileInfo* file, s64 offset ); - - /** - * Returns the length from the beginning of the file we've read so far - * @param file - * @return Our current position in file - */ - ZPL_DEF_INLINE s64 file_tell( FileInfo* file ); - - /** - * Writes to a file - * @param file - * @param buffer Buffer to read from - * @param size Size to read - */ - b32 file_write( FileInfo* file, void const* buffer, sw size ); - - /** - * Writes to file at a specific offset - * @param file - * @param buffer Buffer to read from - * @param size Size to write - * @param offset Offset to write to - * @param bytes_written How much data we've actually written - */ - ZPL_DEF_INLINE b32 file_write_at( FileInfo* file, void const* buffer, sw size, s64 offset ); - - /** - * Writes to file safely - * @param file - * @param buffer Buffer to read from - * @param size Size to write - * @param offset Offset to write to - * @param bytes_written How much data we've actually written - */ - ZPL_DEF_INLINE b32 file_write_at_check( FileInfo* file, void const* buffer, sw size, s64 offset, sw* bytes_written ); - - ZPL_IMPL_INLINE s64 file_seek( FileInfo* f, s64 offset ) - { - s64 new_offset = 0; - - if ( ! f->Ops.read_at ) - f->Ops = default_file_operations; - - f->Ops.seek( f->FD, offset, ESeekWhence_BEGIN, &new_offset ); - - return new_offset; - } - - ZPL_IMPL_INLINE s64 file_tell( FileInfo* f ) - { - s64 new_offset = 0; - - if ( ! f->Ops.read_at ) - f->Ops = default_file_operations; - - f->Ops.seek( f->FD, 0, ESeekWhence_CURRENT, &new_offset ); - - return new_offset; - } - - ZPL_IMPL_INLINE b32 file_write( FileInfo* f, void const* buffer, sw size ) - { - s64 cur_offset = file_tell( f ); - b32 result = file_write_at( f, buffer, size, file_tell( f ) ); - - file_seek( f, cur_offset + size ); - - return result; - } - - ZPL_IMPL_INLINE b32 file_write_at( FileInfo* f, void const* buffer, sw size, s64 offset ) - { - return file_write_at_check( f, buffer, size, offset, NULL ); - } - - ZPL_IMPL_INLINE b32 file_write_at_check( FileInfo* f, void const* buffer, sw size, s64 offset, sw* bytes_written ) - { - if ( ! f->Ops.read_at ) - f->Ops = default_file_operations; - - return f->Ops.write_at( f->FD, buffer, size, offset, bytes_written ); - } - #pragma endregion File Handling - - #pragma region Printing - - #ifndef ZPL_PRINTF_MAXLEN - # define ZPL_PRINTF_MAXLEN 65536 - #endif - - // NOTE: A locally persisting buffer is used internally - char* str_fmt_buf( char const* fmt, ... ); - char* str_fmt_buf_va( char const* fmt, va_list va ); - sw str_fmt_va( char* str, sw n, char const* fmt, va_list va ); - sw str_fmt_out_va( char const* fmt, va_list va ); - sw str_fmt_out_err( char const* fmt, ... ); - sw str_fmt_out_err_va( char const* fmt, va_list va ); - sw str_fmt_file_va( FileInfo* f, char const* fmt, va_list va ); - #pragma endregion Printing - - namespace Memory - { - // NOTE: This limits the size of the string that can be read from a file or generated to 10 megs. - // If you are generating a string larger than this, increase the size of the bucket here. - constexpr uw BucketSize = megabytes(10); - - // Global allocator used for data with process lifetime. - extern AllocatorInfo GlobalAllocator; - - // Heap allocator is being used for now to isolate errors from being memory related (tech debt till ready to address) - // #define g_allocator heap() - - void setup(); - void cleanup(); - } - - inline - sw log_fmt(char const* fmt, ...) - { - sw res; - va_list va; - - va_start(va, fmt); - res = str_fmt_out_va(fmt, va); - va_end(va); - - return res; - } - - inline - sw fatal(char const* fmt, ...) - { - local_persist thread_local - char buf[ZPL_PRINTF_MAXLEN] = { 0 }; - - va_list va; - - #if Build_Debug - va_start(va, fmt); - str_fmt_va(buf, ZPL_PRINTF_MAXLEN, fmt, va); - va_end(va); - - assert_crash(buf); - return -1; - #else - va_start(va, fmt); - str_fmt_out_err_va( fmt, va); - va_end(va); - - exit(1); - return -1; - #endif - } - -// gen namespace -} diff --git a/project/gen.cpp b/project/gen.cpp index 84b9036..3797939 100644 --- a/project/gen.cpp +++ b/project/gen.cpp @@ -1,14 +1,1769 @@ // ReSharper disable CppClangTidyClangDiagnosticSwitchEnum #ifdef gen_time - -#include "Bloat.hpp" #include "gen.hpp" +#pragma region GENCPP DEPENDENCIES +//! If its desired to roll your own dependencies, define GENCPP_PROVIDE_DEPENDENCIES before including this file. +#ifndef GENCPP_PROVIDE_DEPENDENCIES + +#pragma region Macros +# include +// NOTE: Ensure we use standard methods for these calls if we use GEN_PICO +# if ! defined( GEN_PICO_CUSTOM_ROUTINES ) +# if ! defined( GEN_MODULE_CORE ) +# define _strlen strlen +# define _printf_err( fmt, ... ) fprintf( stderr, fmt, __VA_ARGS__ ) +# define _printf_err_va( fmt, va ) vfprintf( stderr, fmt, va ) +# else +# define _strlen str_len +# define _printf_err( fmt, ... ) str_fmt_out_err( fmt, __VA_ARGS__ ) +# define _printf_err_va( fmt, va ) str_fmt_out_err_va( fmt, va ) +# endif +# endif + +# include + +# if defined( GEN_SYSTEM_UNIX ) || defined( GEN_SYSTEM_MACOS ) +# include +# elif defined( GEN_SYSTEM_WINDOWS ) +# if ! defined( GEN_NO_WINDOWS_H ) +# ifndef WIN32_LEAN_AND_MEAN +# ifndef NOMINMAX +# define NOMINMAX +# endif + +# define WIN32_LEAN_AND_MEAN +# define WIN32_MEAN_AND_LEAN +# define VC_EXTRALEAN +# endif +# include +# undef NOMINMAX +# undef WIN32_LEAN_AND_MEAN +# undef WIN32_MEAN_AND_LEAN +# undef VC_EXTRALEAN +# endif +# endif +#pragma endregion Macros + namespace gen { - // ZPL_TABLE_DEFINE( StringTable, str_tbl_, String ); +#pragma region Debug + void assert_handler( char const* condition, char const* file, s32 line, char const* msg, ... ) + { + _printf_err( "%s:(%d): Assert Failure: ", file, line ); + if ( condition ) + _printf_err( "`%s` ", condition ); + + if ( msg ) + { + va_list va; + va_start( va, msg ); + _printf_err_va( msg, va ); + va_end( va ); + } + + _printf_err( "%s", "\n" ); + } + + s32 assert_crash( char const* condition ) + { + GEN_PANIC( condition ); + return 0; + } + + #if defined( GEN_SYSTEM_WINDOWS ) + void process_exit( u32 code ) + { + ExitProcess( code ); + } + #else + # include + + void process_exit( u32 code ) + { + exit( code ); + } + #endif +#pragma endregion Debug + +#pragma region String Ops + internal sw _scan_zpl_i64( const char* text, s32 base, s64* value ) + { + const char* text_begin = text; + s64 result = 0; + b32 negative = false; + + if ( *text == '-' ) + { + negative = true; + text++; + } + + if ( base == 16 && str_compare( text, "0x", 2 ) == 0 ) + text += 2; + + for ( ;; ) + { + s64 v; + if ( char_is_digit( *text ) ) + v = *text - '0'; + else if ( base == 16 && char_is_hex_digit( *text ) ) + v = hex_digit_to_int( *text ); + else + break; + + result *= base; + result += v; + text++; + } + + if ( value ) + { + if ( negative ) + result = -result; + *value = result; + } + + return ( text - text_begin ); + } + + // TODO : Are these good enough for characters? + global const char _num_to_char_table[] = + "0123456789" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "@$"; + + s64 str_to_i64( const char* str, char** end_ptr, s32 base ) + { + sw len; + s64 value; + + if ( ! base ) + { + if ( ( str_len( str ) > 2 ) && ( str_compare( str, "0x", 2 ) == 0 ) ) + base = 16; + else + base = 10; + } + + len = _scan_zpl_i64( str, base, &value ); + if ( end_ptr ) + *end_ptr = ( char* )str + len; + return value; + } + + void i64_to_str( s64 value, char* string, s32 base ) + { + char* buf = string; + b32 negative = false; + u64 v; + + if ( value < 0 ) + { + negative = true; + value = -value; + } + + v = zpl_cast( u64 ) value; + if ( v != 0 ) + { + while ( v > 0 ) + { + *buf++ = _num_to_char_table[ v % base ]; + v /= base; + } + } + else + { + *buf++ = '0'; + } + if ( negative ) + *buf++ = '-'; + *buf = '\0'; + str_reverse( string ); + } + + void u64_to_str( u64 value, char* string, s32 base ) + { + char* buf = string; + + if ( value ) + { + while ( value > 0 ) + { + *buf++ = _num_to_char_table[ value % base ]; + value /= base; + } + } + else + { + *buf++ = '0'; + } + *buf = '\0'; + + str_reverse( string ); + } +#pragma endregion String Ops + +#pragma region Memory + void* mem_copy( void* dest, void const* source, sw n ) + { + if ( dest == NULL ) + { + return NULL; + } + + return memcpy( dest, source, n ); + } + + void const* mem_find( void const* data, u8 c, sw n ) + { + u8 const* s = zpl_cast( u8 const* ) data; + while ( ( zpl_cast( uptr ) s & ( sizeof( uw ) - 1 ) ) && n && *s != c ) + { + s++; + n--; + } + if ( n && *s != c ) + { + sw const* w; + sw k = GEN__ONES * c; + w = zpl_cast( sw const* ) s; + while ( n >= size_of( sw ) && ! GEN__HAS_ZERO( *w ^ k ) ) + { + w++; + n -= size_of( sw ); + } + s = zpl_cast( u8 const* ) w; + while ( n && *s != c ) + { + s++; + n--; + } + } + + return n ? zpl_cast( void const* ) s : NULL; + } + + #define GEN_HEAP_STATS_MAGIC 0xDEADC0DE + + struct _heap_stats + { + u32 magic; + sw used_memory; + sw alloc_count; + }; + + global _heap_stats _heap_stats_info; + + void heap_stats_init( void ) + { + zero_item( &_heap_stats_info ); + _heap_stats_info.magic = GEN_HEAP_STATS_MAGIC; + } + + sw heap_stats_used_memory( void ) + { + GEN_ASSERT_MSG( _heap_stats_info.magic == GEN_HEAP_STATS_MAGIC, "heap_stats is not initialised yet, call heap_stats_init first!" ); + return _heap_stats_info.used_memory; + } + + sw heap_stats_alloc_count( void ) + { + GEN_ASSERT_MSG( _heap_stats_info.magic == GEN_HEAP_STATS_MAGIC, "heap_stats is not initialised yet, call heap_stats_init first!" ); + return _heap_stats_info.alloc_count; + } + + void heap_stats_check( void ) + { + GEN_ASSERT_MSG( _heap_stats_info.magic == GEN_HEAP_STATS_MAGIC, "heap_stats is not initialised yet, call heap_stats_init first!" ); + GEN_ASSERT( _heap_stats_info.used_memory == 0 ); + GEN_ASSERT( _heap_stats_info.alloc_count == 0 ); + } + + struct _heap_alloc_info + { + sw size; + void* physical_start; + }; + + void* heap_allocator_proc( void* allocator_data, AllocType type, sw size, sw alignment, void* old_memory, sw old_size, u64 flags ) + { + void* ptr = NULL; + // unused( allocator_data ); + // unused( old_size ); + if ( ! alignment ) + alignment = GEN_DEFAULT_MEMORY_ALIGNMENT; + + #ifdef GEN_HEAP_ANALYSIS + sw alloc_info_size = size_of( _heap_alloc_info ); + sw alloc_info_remainder = ( alloc_info_size % alignment ); + sw track_size = max( alloc_info_size, alignment ) + alloc_info_remainder; + switch ( type ) + { + case EAllocation_FREE : + { + if ( ! old_memory ) + break; + _heap_alloc_info* alloc_info = zpl_cast( _heap_alloc_info* ) old_memory - 1; + _heap_stats_info.used_memory -= alloc_info->size; + _heap_stats_info.alloc_count--; + old_memory = alloc_info->physical_start; + } + break; + case EAllocation_ALLOC : + { + size += track_size; + } + break; + default : + break; + } + #endif + + switch ( type ) + { + #if defined( GEN_COMPILER_MSVC ) || ( defined( GEN_COMPILER_GCC ) && defined( GEN_SYSTEM_WINDOWS ) ) || ( defined( GEN_COMPILER_TINYC ) && defined( GEN_SYSTEM_WINDOWS ) ) + case EAllocation_ALLOC : + ptr = _aligned_malloc( size, alignment ); + if ( flags & ALLOCATOR_FLAG_CLEAR_TO_ZERO ) + zero_size( ptr, size ); + break; + case EAllocation_FREE : + _aligned_free( old_memory ); + break; + case EAllocation_RESIZE : + { + AllocatorInfo a = heap(); + ptr = default_resize_align( a, old_memory, old_size, size, alignment ); + } + break; + + #elif defined( GEN_SYSTEM_LINUX ) && ! defined( GEN_CPU_ARM ) && ! defined( GEN_COMPILER_TINYC ) + case EAllocation_ALLOC : + { + ptr = aligned_alloc( alignment, ( size + alignment - 1 ) & ~( alignment - 1 ) ); + + if ( flags & GEN_ALLOCATOR_FLAG_CLEAR_TO_ZERO ) + { + zero_size( ptr, size ); + } + } + break; + + case EAllocation_FREE : + { + free( old_memory ); + } + break; + + case EAllocation_RESIZE : + { + AllocatorInfo a = heap(); + ptr = default_resize_align( a, old_memory, old_size, size, alignment ); + } + break; + #else + case EAllocation_ALLOC : + { + posix_memalign( &ptr, alignment, size ); + + if ( flags & GEN_ALLOCATOR_FLAG_CLEAR_TO_ZERO ) + { + zero_size( ptr, size ); + } + } + break; + + case EAllocation_FREE : + { + free( old_memory ); + } + break; + + case EAllocation_RESIZE : + { + AllocatorInfo a = heap(); + ptr = default_resize_align( a, old_memory, old_size, size, alignment ); + } + break; + #endif + + case EAllocation_FREE_ALL : + break; + } + + #ifdef GEN_HEAP_ANALYSIS + if ( type == EAllocation_ALLOC ) + { + _heap_alloc_info* alloc_info = zpl_cast( _heap_alloc_info* )( zpl_cast( char* ) ptr + alloc_info_remainder ); + zero_item( alloc_info ); + alloc_info->size = size - track_size; + alloc_info->physical_start = ptr; + ptr = zpl_cast( void* )( alloc_info + 1 ); + _heap_stats_info.used_memory += alloc_info->size; + _heap_stats_info.alloc_count++; + } + #endif + + return ptr; + } + + void* Arena::allocator_proc( void* allocator_data, AllocType type, sw size, sw alignment, void* old_memory, sw old_size, u64 flags ) + { + Arena* arena = rcast(Arena*, allocator_data); + void* ptr = NULL; + + // unused( old_size ); + + switch ( type ) + { + case EAllocation_ALLOC : + { + void* end = pointer_add( arena->PhysicalStart, arena->TotalUsed ); + sw total_size = align_forward_i64( size, alignment ); + + // NOTE: Out of memory + if ( arena->TotalUsed + total_size > (sw) arena->TotalSize ) + { + // zpl__printf_err("%s", "Arena out of memory\n"); + return nullptr; + } + + ptr = align_forward( end, alignment ); + arena->TotalUsed += total_size; + + if ( flags & ALLOCATOR_FLAG_CLEAR_TO_ZERO ) + zero_size( ptr, size ); + } + break; + + case EAllocation_FREE : + // NOTE: Free all at once + // Use Temp_Arena_Memory if you want to free a block + break; + + case EAllocation_FREE_ALL : + arena->TotalUsed = 0; + break; + + case EAllocation_RESIZE : + { + // TODO : Check if ptr is on top of stack and just extend + AllocatorInfo a = arena->Backing; + ptr = default_resize_align( a, old_memory, old_size, size, alignment ); + } + break; + } + return ptr; + } + + void* Pool::allocator_proc( void* allocator_data, AllocType type, sw size, sw alignment, void* old_memory, sw old_size, u64 flags ) + { + Pool* pool = zpl_cast( Pool* ) allocator_data; + void* ptr = NULL; + + // unused( old_size ); + + switch ( type ) + { + case EAllocation_ALLOC : + { + uptr next_free; + + GEN_ASSERT( size == pool->BlockSize ); + GEN_ASSERT( alignment == pool->BlockAlign ); + GEN_ASSERT( pool->FreeList != NULL ); + + next_free = *zpl_cast( uptr* ) pool->FreeList; + ptr = pool->FreeList; + pool->FreeList = zpl_cast( void* ) next_free; + pool->TotalSize += pool->BlockSize; + + if ( flags & ALLOCATOR_FLAG_CLEAR_TO_ZERO ) + zero_size( ptr, size ); + } + break; + + case EAllocation_FREE : + { + uptr* next; + if ( old_memory == NULL ) + return NULL; + + next = zpl_cast( uptr* ) old_memory; + *next = zpl_cast( uptr ) pool->FreeList; + pool->FreeList = old_memory; + pool->TotalSize -= pool->BlockSize; + } + break; + + case EAllocation_FREE_ALL : + { + sw actual_block_size, block_index; + void* curr; + uptr* end; + + actual_block_size = pool->BlockSize + pool->BlockAlign; + pool->TotalSize = 0; + + // NOTE: Init intrusive freelist + curr = pool->PhysicalStart; + for ( block_index = 0; block_index < pool->NumBlocks - 1; block_index++ ) + { + uptr* next = zpl_cast( uptr* ) curr; + *next = zpl_cast( uptr ) curr + actual_block_size; + curr = pointer_add( curr, actual_block_size ); + } + + end = zpl_cast( uptr* ) curr; + *end = zpl_cast( uptr ) NULL; + pool->FreeList = pool->PhysicalStart; + } + break; + + case EAllocation_RESIZE : + // NOTE: Cannot resize + GEN_PANIC( "You cannot resize something allocated by with a pool." ); + break; + } + + return ptr; + } + + Pool Pool::init_align( AllocatorInfo backing, sw num_blocks, sw block_size, sw block_align ) + { + Pool pool = {}; + + sw actual_block_size, pool_size, block_index; + void *data, *curr; + uptr* end; + + pool.Backing = backing; + pool.BlockSize = block_size; + pool.BlockAlign = block_align; + pool.NumBlocks = num_blocks; + + actual_block_size = block_size + block_align; + pool_size = num_blocks * actual_block_size; + + data = alloc_align( backing, pool_size, block_align ); + + // NOTE: Init intrusive freelist + curr = data; + for ( block_index = 0; block_index < num_blocks - 1; block_index++ ) + { + uptr* next = ( uptr* ) curr; + *next = ( uptr ) curr + actual_block_size; + curr = pointer_add( curr, actual_block_size ); + } + + end = ( uptr* ) curr; + *end = ( uptr ) 0; + + pool.PhysicalStart = data; + pool.FreeList = data; + + return pool; + } +#pragma endregion Memory + +#pragma region Printing + enum + { + GEN_FMT_MINUS = bit( 0 ), + GEN_FMT_PLUS = bit( 1 ), + GEN_FMT_ALT = bit( 2 ), + GEN_FMT_SPACE = bit( 3 ), + GEN_FMT_ZERO = bit( 4 ), + + GEN_FMT_CHAR = bit( 5 ), + GEN_FMT_SHORT = bit( 6 ), + GEN_FMT_INT = bit( 7 ), + GEN_FMT_LONG = bit( 8 ), + GEN_FMT_LLONG = bit( 9 ), + GEN_FMT_SIZE = bit( 10 ), + GEN_FMT_INTPTR = bit( 11 ), + + GEN_FMT_UNSIGNED = bit( 12 ), + GEN_FMT_LOWER = bit( 13 ), + GEN_FMT_UPPER = bit( 14 ), + GEN_FMT_WIDTH = bit( 15 ), + + GEN_FMT_DONE = bit( 30 ), + + GEN_FMT_INTS = GEN_FMT_CHAR | GEN_FMT_SHORT | GEN_FMT_INT | GEN_FMT_LONG | GEN_FMT_LLONG | GEN_FMT_SIZE | GEN_FMT_INTPTR + }; + + struct _format_info + { + s32 base; + s32 flags; + s32 width; + s32 precision; + }; + + internal sw _print_string( char* text, sw max_len, _format_info* info, char const* str ) + { + sw res = 0, len = 0; + sw remaining = max_len; + char* begin = text; + + if ( str == NULL && max_len >= 6 ) + { + res += str_copy_nulpad( text, "(null)", 6 ); + return res; + } + + if ( info && info->precision >= 0 ) + len = str_len( str, info->precision ); + else + len = str_len( str ); + + if ( info && ( info->width == 0 && info->flags & GEN_FMT_WIDTH ) ) + { + return res; + } + + if ( info && ( info->width == 0 || info->flags & GEN_FMT_MINUS ) ) + { + if ( info->precision > 0 ) + len = info->precision < len ? info->precision : len; + if ( res + len > max_len ) + return res; + res += str_copy_nulpad( text, str, len ); + text += res; + + if ( info->width > res ) + { + sw padding = info->width - len; + + char pad = ( info->flags & GEN_FMT_ZERO ) ? '0' : ' '; + while ( padding-- > 0 && remaining-- > 0 ) + *text++ = pad, res++; + } + } + else + { + if ( info && ( info->width > res ) ) + { + sw padding = info->width - len; + char pad = ( info->flags & GEN_FMT_ZERO ) ? '0' : ' '; + while ( padding-- > 0 && remaining-- > 0 ) + *text++ = pad, res++; + } + + if ( res + len > max_len ) + return res; + res += str_copy_nulpad( text, str, len ); + } + + if ( info ) + { + if ( info->flags & GEN_FMT_UPPER ) + str_to_upper( begin ); + else if ( info->flags & GEN_FMT_LOWER ) + str_to_lower( begin ); + } + + return res; + } + + internal sw _print_char( char* text, sw max_len, _format_info* info, char arg ) + { + char str[ 2 ] = ""; + str[ 0 ] = arg; + return _print_string( text, max_len, info, str ); + } + + internal sw _print_repeated_char( char* text, sw max_len, _format_info* info, char arg ) + { + sw res = 0; + s32 rem = ( info ) ? ( info->width > 0 ) ? info->width : 1 : 1; + res = rem; + while ( rem-- > 0 ) + *text++ = arg; + + return res; + } + + internal sw _print_i64( char* text, sw max_len, _format_info* info, s64 value ) + { + char num[ 130 ]; + i64_to_str( value, num, info ? info->base : 10 ); + return _print_string( text, max_len, info, num ); + } + + internal sw _print_u64( char* text, sw max_len, _format_info* info, u64 value ) + { + char num[ 130 ]; + u64_to_str( value, num, info ? info->base : 10 ); + return _print_string( text, max_len, info, num ); + } + + internal sw _print_f64( char* text, sw max_len, _format_info* info, b32 is_hexadecimal, f64 arg ) + { + // TODO: Handle exponent notation + sw width, len, remaining = max_len; + char* text_begin = text; + + if ( arg ) + { + u64 value; + if ( arg < 0 ) + { + if ( remaining > 1 ) + *text = '-', remaining--; + text++; + arg = -arg; + } + else if ( info->flags & GEN_FMT_MINUS ) + { + if ( remaining > 1 ) + *text = '+', remaining--; + text++; + } + + value = zpl_cast( u64 ) arg; + len = _print_u64( text, remaining, NULL, value ); + text += len; + + if ( len >= remaining ) + remaining = min( remaining, 1 ); + else + remaining -= len; + arg -= value; + + if ( info->precision < 0 ) + info->precision = 6; + + if ( ( info->flags & GEN_FMT_ALT ) || info->precision > 0 ) + { + s64 mult = 10; + if ( remaining > 1 ) + *text = '.', remaining--; + text++; + while ( info->precision-- > 0 ) + { + value = zpl_cast( u64 )( arg * mult ); + len = _print_u64( text, remaining, NULL, value ); + text += len; + if ( len >= remaining ) + remaining = min( remaining, 1 ); + else + remaining -= len; + arg -= zpl_cast( f64 ) value / mult; + mult *= 10; + } + } + } + else + { + if ( remaining > 1 ) + *text = '0', remaining--; + text++; + if ( info->flags & GEN_FMT_ALT ) + { + if ( remaining > 1 ) + *text = '.', remaining--; + text++; + } + } + + width = info->width - ( text - text_begin ); + if ( width > 0 ) + { + char fill = ( info->flags & GEN_FMT_ZERO ) ? '0' : ' '; + char* end = text + remaining - 1; + len = ( text - text_begin ); + + for ( len = ( text - text_begin ); len--; ) + { + if ( ( text_begin + len + width ) < end ) + *( text_begin + len + width ) = *( text_begin + len ); + } + + len = width; + text += len; + if ( len >= remaining ) + remaining = min( remaining, 1 ); + else + remaining -= len; + + while ( len-- ) + { + if ( text_begin + len < end ) + text_begin[ len ] = fill; + } + } + + return ( text - text_begin ); + } + + neverinline sw str_fmt_va( char* text, sw max_len, char const* fmt, va_list va ) + { + char const* text_begin = text; + sw remaining = max_len, res; + + while ( *fmt ) + { + _format_info info = { 0 }; + sw len = 0; + info.precision = -1; + + while ( *fmt && *fmt != '%' && remaining ) + *text++ = *fmt++; + + if ( *fmt == '%' ) + { + do + { + switch ( *++fmt ) + { + case '-' : + { + info.flags |= GEN_FMT_MINUS; + break; + } + case '+' : + { + info.flags |= GEN_FMT_PLUS; + break; + } + case '#' : + { + info.flags |= GEN_FMT_ALT; + break; + } + case ' ' : + { + info.flags |= GEN_FMT_SPACE; + break; + } + case '0' : + { + info.flags |= ( GEN_FMT_ZERO | GEN_FMT_WIDTH ); + break; + } + default : + { + info.flags |= GEN_FMT_DONE; + break; + } + } + } while ( ! ( info.flags & GEN_FMT_DONE ) ); + } + + // NOTE: Optional Width + if ( *fmt == '*' ) + { + int width = va_arg( va, int ); + if ( width < 0 ) + { + info.flags |= GEN_FMT_MINUS; + info.width = -width; + } + else + { + info.width = width; + } + info.flags |= GEN_FMT_WIDTH; + fmt++; + } + else + { + info.width = zpl_cast( s32 ) str_to_i64( fmt, zpl_cast( char** ) & fmt, 10 ); + if ( info.width != 0 ) + { + info.flags |= GEN_FMT_WIDTH; + } + } + + // NOTE: Optional Precision + if ( *fmt == '.' ) + { + fmt++; + if ( *fmt == '*' ) + { + info.precision = va_arg( va, int ); + fmt++; + } + else + { + info.precision = zpl_cast( s32 ) str_to_i64( fmt, zpl_cast( char** ) & fmt, 10 ); + } + info.flags &= ~GEN_FMT_ZERO; + } + + switch ( *fmt++ ) + { + case 'h' : + if ( *fmt == 'h' ) + { // hh => char + info.flags |= GEN_FMT_CHAR; + fmt++; + } + else + { // h => short + info.flags |= GEN_FMT_SHORT; + } + break; + + case 'l' : + if ( *fmt == 'l' ) + { // ll => long long + info.flags |= GEN_FMT_LLONG; + fmt++; + } + else + { // l => long + info.flags |= GEN_FMT_LONG; + } + break; + + break; + + case 'z' : // NOTE: zpl_usize + info.flags |= GEN_FMT_UNSIGNED; + // fallthrough + case 't' : // NOTE: zpl_isize + info.flags |= GEN_FMT_SIZE; + break; + + default : + fmt--; + break; + } + + switch ( *fmt ) + { + case 'u' : + info.flags |= GEN_FMT_UNSIGNED; + // fallthrough + case 'd' : + case 'i' : + info.base = 10; + break; + + case 'o' : + info.base = 8; + break; + + case 'x' : + info.base = 16; + info.flags |= ( GEN_FMT_UNSIGNED | GEN_FMT_LOWER ); + break; + + case 'X' : + info.base = 16; + info.flags |= ( GEN_FMT_UNSIGNED | GEN_FMT_UPPER ); + break; + + case 'f' : + case 'F' : + case 'g' : + case 'G' : + len = _print_f64( text, remaining, &info, 0, va_arg( va, f64 ) ); + break; + + case 'a' : + case 'A' : + len = _print_f64( text, remaining, &info, 1, va_arg( va, f64 ) ); + break; + + case 'c' : + len = _print_char( text, remaining, &info, zpl_cast( char ) va_arg( va, int ) ); + break; + + case 's' : + len = _print_string( text, remaining, &info, va_arg( va, char* ) ); + break; + + case 'r' : + len = _print_repeated_char( text, remaining, &info, va_arg( va, int ) ); + break; + + case 'p' : + info.base = 16; + info.flags |= ( GEN_FMT_LOWER | GEN_FMT_UNSIGNED | GEN_FMT_ALT | GEN_FMT_INTPTR ); + break; + + case '%' : + len = _print_char( text, remaining, &info, '%' ); + break; + + default : + fmt--; + break; + } + + fmt++; + + if ( info.base != 0 ) + { + if ( info.flags & GEN_FMT_UNSIGNED ) + { + u64 value = 0; + switch ( info.flags & GEN_FMT_INTS ) + { + case GEN_FMT_CHAR : + value = zpl_cast( u64 ) zpl_cast( u8 ) va_arg( va, int ); + break; + case GEN_FMT_SHORT : + value = zpl_cast( u64 ) zpl_cast( u16 ) va_arg( va, int ); + break; + case GEN_FMT_LONG : + value = zpl_cast( u64 ) va_arg( va, unsigned long ); + break; + case GEN_FMT_LLONG : + value = zpl_cast( u64 ) va_arg( va, unsigned long long ); + break; + case GEN_FMT_SIZE : + value = zpl_cast( u64 ) va_arg( va, uw ); + break; + case GEN_FMT_INTPTR : + value = zpl_cast( u64 ) va_arg( va, uptr ); + break; + default : + value = zpl_cast( u64 ) va_arg( va, unsigned int ); + break; + } + + len = _print_u64( text, remaining, &info, value ); + } + else + { + s64 value = 0; + switch ( info.flags & GEN_FMT_INTS ) + { + case GEN_FMT_CHAR : + value = zpl_cast( s64 ) zpl_cast( s8 ) va_arg( va, int ); + break; + case GEN_FMT_SHORT : + value = zpl_cast( s64 ) zpl_cast( s16 ) va_arg( va, int ); + break; + case GEN_FMT_LONG : + value = zpl_cast( s64 ) va_arg( va, long ); + break; + case GEN_FMT_LLONG : + value = zpl_cast( s64 ) va_arg( va, long long ); + break; + case GEN_FMT_SIZE : + value = zpl_cast( s64 ) va_arg( va, uw ); + break; + case GEN_FMT_INTPTR : + value = zpl_cast( s64 ) va_arg( va, uptr ); + break; + default : + value = zpl_cast( s64 ) va_arg( va, int ); + break; + } + + len = _print_i64( text, remaining, &info, value ); + } + } + + text += len; + if ( len >= remaining ) + remaining = min( remaining, 1 ); + else + remaining -= len; + } + + *text++ = '\0'; + res = ( text - text_begin ); + return ( res >= max_len || res < 0 ) ? -1 : res; + } + + char* str_fmt_buf_va( char const* fmt, va_list va ) + { + local_persist thread_local char buffer[ GEN_PRINTF_MAXLEN ]; + str_fmt_va( buffer, size_of( buffer ), fmt, va ); + return buffer; + } + + char* str_fmt_buf( char const* fmt, ... ) + { + va_list va; + char* str; + va_start( va, fmt ); + str = str_fmt_buf_va( fmt, va ); + va_end( va ); + return str; + } + + sw str_fmt_file_va( struct FileInfo* f, char const* fmt, va_list va ) + { + local_persist thread_local char buf[ GEN_PRINTF_MAXLEN ]; + sw len = str_fmt_va( buf, size_of( buf ), fmt, va ); + b32 res = file_write( f, buf, len - 1 ); // NOTE: prevent extra whitespace + return res ? len : -1; + } + + sw str_fmt_out_va( char const* fmt, va_list va ) + { + return str_fmt_file_va( file_get_standard( EFileStandard_OUTPUT ), fmt, va ); + } + + sw str_fmt_out_err_va( char const* fmt, va_list va ) + { + return str_fmt_file_va( file_get_standard( EFileStandard_ERROR ), fmt, va ); + } + + sw str_fmt_out_err( char const* fmt, ... ) + { + sw res; + va_list va; + va_start( va, fmt ); + res = str_fmt_out_err_va( fmt, va ); + va_end( va ); + return res; + } +#pragma endregion Printing + +#pragma region Hashing + global u32 const _crc32_table[ 256 ] = { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, + 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, + 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, + 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, + 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, + 0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, 0x4db26158, 0x3ab551ce, + 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, + 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, + 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, + 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, + 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, + 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, + 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, + 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, + 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, + 0x47b2cf7f, 0x30b5ffe9, 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d, + }; + + u32 crc32( void const* data, sw len ) + { + sw remaining; + u32 result = ~( zpl_cast( u32 ) 0 ); + u8 const* c = zpl_cast( u8 const* ) data; + for ( remaining = len; remaining--; c++ ) + result = ( result >> 8 ) ^ ( _crc32_table[ ( result ^ *c ) & 0xff ] ); + return ~result; + } +#pragma endregion Hashing + +#pragma region File Handling + #if defined( GEN_SYSTEM_WINDOWS ) || defined( GEN_SYSTEM_CYGWIN ) + + internal wchar_t* _alloc_utf8_to_ucs2( AllocatorInfo a, char const* text, sw* w_len_ ) + { + wchar_t* w_text = NULL; + sw len = 0, w_len = 0, w_len1 = 0; + if ( text == NULL ) + { + if ( w_len_ ) + *w_len_ = w_len; + return NULL; + } + len = str_len( text ); + if ( len == 0 ) + { + if ( w_len_ ) + *w_len_ = w_len; + return NULL; + } + w_len = MultiByteToWideChar( CP_UTF8, MB_ERR_INVALID_CHARS, text, zpl_cast( int ) len, NULL, 0 ); + if ( w_len == 0 ) + { + if ( w_len_ ) + *w_len_ = w_len; + return NULL; + } + w_text = alloc_array( a, wchar_t, w_len + 1 ); + w_len1 = MultiByteToWideChar( CP_UTF8, MB_ERR_INVALID_CHARS, text, zpl_cast( int ) len, w_text, zpl_cast( int ) w_len ); + if ( w_len1 == 0 ) + { + free( a, w_text ); + if ( w_len_ ) + *w_len_ = 0; + return NULL; + } + w_text[ w_len ] = 0; + if ( w_len_ ) + *w_len_ = w_len; + return w_text; + } + + internal GEN_FILE_SEEK_PROC( _win32_file_seek ) + { + LARGE_INTEGER li_offset; + li_offset.QuadPart = offset; + if ( ! SetFilePointerEx( fd.p, li_offset, &li_offset, whence ) ) + { + return false; + } + + if ( new_offset ) + *new_offset = li_offset.QuadPart; + return true; + } + + internal GEN_FILE_READ_AT_PROC( _win32_file_read ) + { + // unused( stop_at_newline ); + b32 result = false; + _win32_file_seek( fd, offset, ESeekWhence_BEGIN, NULL ); + DWORD size_ = zpl_cast( DWORD )( size > GEN_I32_MAX ? GEN_I32_MAX : size ); + DWORD bytes_read_; + if ( ReadFile( fd.p, buffer, size_, &bytes_read_, NULL ) ) + { + if ( bytes_read ) + *bytes_read = bytes_read_; + result = true; + } + + return result; + } + + internal GEN_FILE_WRITE_AT_PROC( _win32_file_write ) + { + DWORD size_ = zpl_cast( DWORD )( size > GEN_I32_MAX ? GEN_I32_MAX : size ); + DWORD bytes_written_; + _win32_file_seek( fd, offset, ESeekWhence_BEGIN, NULL ); + if ( WriteFile( fd.p, buffer, size_, &bytes_written_, NULL ) ) + { + if ( bytes_written ) + *bytes_written = bytes_written_; + return true; + } + return false; + } + + internal GEN_FILE_CLOSE_PROC( _win32_file_close ) + { + CloseHandle( fd.p ); + } + + FileOperations const default_file_operations = { _win32_file_read, _win32_file_write, _win32_file_seek, _win32_file_close }; + + neverinline GEN_FILE_OPEN_PROC( _win32_file_open ) + { + DWORD desired_access; + DWORD creation_disposition; + void* handle; + wchar_t* w_text; + + switch ( mode & GEN_FILE_MODES ) + { + case EFileMode_READ : + desired_access = GENERIC_READ; + creation_disposition = OPEN_EXISTING; + break; + case EFileMode_WRITE : + desired_access = GENERIC_WRITE; + creation_disposition = CREATE_ALWAYS; + break; + case EFileMode_APPEND : + desired_access = GENERIC_WRITE; + creation_disposition = OPEN_ALWAYS; + break; + case EFileMode_READ | EFileMode_RW : + desired_access = GENERIC_READ | GENERIC_WRITE; + creation_disposition = OPEN_EXISTING; + break; + case EFileMode_WRITE | EFileMode_RW : + desired_access = GENERIC_READ | GENERIC_WRITE; + creation_disposition = CREATE_ALWAYS; + break; + case EFileMode_APPEND | EFileMode_RW : + desired_access = GENERIC_READ | GENERIC_WRITE; + creation_disposition = OPEN_ALWAYS; + break; + default : + GEN_PANIC( "Invalid file mode" ); + return EFileError_INVALID; + } + + w_text = _alloc_utf8_to_ucs2( heap(), filename, NULL ); + handle = CreateFileW( w_text, desired_access, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, creation_disposition, FILE_ATTRIBUTE_NORMAL, NULL ); + + free( heap(), w_text ); + + if ( handle == INVALID_HANDLE_VALUE ) + { + DWORD err = GetLastError(); + switch ( err ) + { + case ERROR_FILE_NOT_FOUND : + return EFileError_NOT_EXISTS; + case ERROR_FILE_EXISTS : + return EFileError_EXISTS; + case ERROR_ALREADY_EXISTS : + return EFileError_EXISTS; + case ERROR_ACCESS_DENIED : + return EFileError_PERMISSION; + } + return EFileError_INVALID; + } + + if ( mode & EFileMode_APPEND ) + { + LARGE_INTEGER offset = { { 0 } }; + if ( ! SetFilePointerEx( handle, offset, NULL, ESeekWhence_END ) ) + { + CloseHandle( handle ); + return EFileError_INVALID; + } + } + + fd->p = handle; + *ops = default_file_operations; + return EFileError_NONE; + } + + #else // POSIX + # include + + internal GEN_FILE_SEEK_PROC( _posix_file_seek ) + { + # if defined( GEN_SYSTEM_OSX ) + s64 res = lseek( fd.i, offset, whence ); + # else // TODO(ZaKlaus): @fixme lseek64 + s64 res = lseek( fd.i, offset, whence ); + # endif + if ( res < 0 ) + return false; + if ( new_offset ) + *new_offset = res; + return true; + } + + internal GEN_FILE_READ_AT_PROC( _posix_file_read ) + { + unused( stop_at_newline ); + sw res = pread( fd.i, buffer, size, offset ); + if ( res < 0 ) + return false; + if ( bytes_read ) + *bytes_read = res; + return true; + } + + internal GEN_FILE_WRITE_AT_PROC( _posix_file_write ) + { + sw res; + s64 curr_offset = 0; + _posix_file_seek( fd, 0, ESeekWhence_CURRENT, &curr_offset ); + if ( curr_offset == offset ) + { + // NOTE: Writing to stdout et al. doesn't like pwrite for numerous reasons + res = write( zpl_cast( int ) fd.i, buffer, size ); + } + else + { + res = pwrite( zpl_cast( int ) fd.i, buffer, size, offset ); + } + if ( res < 0 ) + return false; + if ( bytes_written ) + *bytes_written = res; + return true; + } + + internal GEN_FILE_CLOSE_PROC( _posix_file_close ) + { + close( fd.i ); + } + + FileOperations const default_file_operations = { _posix_file_read, _posix_file_write, _posix_file_seek, _posix_file_close }; + + GEN_NEVER_INLINE GEN_FILE_OPEN_PROC( _posix_file_open ) + { + s32 os_mode; + switch ( mode & GEN_FILE_MODES ) + { + case EFileMode_READ : + os_mode = O_RDONLY; + break; + case EFileMode_WRITE : + os_mode = O_WRONLY | O_CREAT | O_TRUNC; + break; + case EFileMode_APPEND : + os_mode = O_WRONLY | O_APPEND | O_CREAT; + break; + case EFileMode_READ | EFileMode_RW : + os_mode = O_RDWR; + break; + case EFileMode_WRITE | EFileMode_RW : + os_mode = O_RDWR | O_CREAT | O_TRUNC; + break; + case EFileMode_APPEND | EFileMode_RW : + os_mode = O_RDWR | O_APPEND | O_CREAT; + break; + default : + GEN_PANIC( "Invalid file mode" ); + return EFileError_INVALID; + } + + fd->i = open( filename, os_mode, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH ); + if ( fd->i < 0 ) + { + // TODO : More file errors + return EFileError_INVALID; + } + + *ops = default_file_operations; + return EFileError_NONE; + } + + // POSIX + #endif + + internal void _dirinfo_free_entry( DirEntry* entry ); + + // TODO : Is this a bad idea? + global b32 _std_file_set = false; + global FileInfo _std_files[ EFileStandard_COUNT ] = { + { + { nullptr, nullptr, nullptr, nullptr }, + { nullptr }, + 0, + nullptr, + 0, + nullptr + } }; + + #if defined( GEN_SYSTEM_WINDOWS ) || defined( GEN_SYSTEM_CYGWIN ) + + FileInfo* file_get_standard( FileStandardType std ) + { + if ( ! _std_file_set ) + { + # define GEN__SET_STD_FILE( type, v ) \ + _std_files[ type ].FD.p = v; \ + _std_files[ type ].Ops = default_file_operations + GEN__SET_STD_FILE( EFileStandard_INPUT, GetStdHandle( STD_INPUT_HANDLE ) ); + GEN__SET_STD_FILE( EFileStandard_OUTPUT, GetStdHandle( STD_OUTPUT_HANDLE ) ); + GEN__SET_STD_FILE( EFileStandard_ERROR, GetStdHandle( STD_ERROR_HANDLE ) ); + # undef GEN__SET_STD_FILE + _std_file_set = true; + } + return &_std_files[ std ]; + } + + // void file_connect_handle( FileInfo* file, void* handle ) + // { + // GEN_ASSERT_NOT_NULL( file ); + // GEN_ASSERT_NOT_NULL( handle ); + + // if ( file->is_temp ) + // return; + + // zero_item( file ); + + // file->fd.p = handle; + // file->ops = default_file_operations; + // } + + // FileError file_truncate( FileInfo* f, s64 size ) + // { + // FileError err = EFileError_NONE; + // s64 prev_offset = file_tell( f ); + // file_seek( f, size ); + // if ( ! SetEndOfFile( f ) ) + // err = EFileError_TRUNCATION_FAILURE; + // file_seek( f, prev_offset ); + // return err; + // } + + // b32 fs_exists( char const* name ) + // { + // WIN32_FIND_DATAW data; + // wchar_t* w_text; + // void* handle; + // b32 found = false; + // AllocatorInfo a = heap_allocator(); + + // w_text = _alloc_utf8_to_ucs2( a, name, NULL ); + // if ( w_text == NULL ) + // { + // return false; + // } + // handle = FindFirstFileW( w_text, &data ); + // free( a, w_text ); + // found = handle != INVALID_HANDLE_VALUE; + // if ( found ) + // FindClose( handle ); + // return found; + // } + + #else // POSIX + + FileInfo* file_get_standard( FileStandardType std ) + { + if ( ! _std_file_set ) + { + # define GEN__SET_STD_FILE( type, v ) \ + _std_files[ type ].fd.i = v; \ + _std_files[ type ].ops = default_file_operations + GEN__SET_STD_FILE( EFileStandard_INPUT, 0 ); + GEN__SET_STD_FILE( EFileStandard_OUTPUT, 1 ); + GEN__SET_STD_FILE( EFileStandard_ERROR, 2 ); + # undef GEN__SET_STD_FILE + _std_file_set = true; + } + return &_std_files[ std ]; + } + + // FileError file_truncate( FileInfo* f, s64 size ) + // { + // FileError err = EFileError_NONE; + // int i = ftruncate( f->fd.i, size ); + // if ( i != 0 ) + // err = EFileError_TRUNCATION_FAILURE; + // return err; + // } + + // b32 fs_exists( char const* name ) + // { + // return access( name, F_OK ) != -1; + // } + + #endif + + FileError file_close( FileInfo* f ) + { + if ( ! f ) + return EFileError_INVALID; + + if ( f->Filename ) + free( heap(), zpl_cast( char* ) f->Filename ); + + #if defined( GEN_SYSTEM_WINDOWS ) + if ( f->FD.p == INVALID_HANDLE_VALUE ) + return EFileError_INVALID; + #else + if ( f->fd.i < 0 ) + return EFileError_INVALID; + #endif + + if ( f->IsTemp ) + { + f->Ops.close( f->FD ); + return EFileError_NONE; + } + + if ( ! f->Ops.read_at ) + f->Ops = default_file_operations; + f->Ops.close( f->FD ); + + #if 0 + if ( f->Dir ) + { + _dirinfo_free_entry( f->Dir ); + mfree( f->Dir ); + f->Dir = NULL; + } + #endif + + return EFileError_NONE; + } + + FileError file_new( FileInfo* f, FileDescriptor fd, FileOperations ops, char const* filename ) + { + FileError err = EFileError_NONE; + sw len = str_len( filename ); + + f->Ops = ops; + f->FD = fd; + f->Dir = nullptr; + f->LastWriteTime = 0; + f->Filename = alloc_array( heap(), char, len + 1 ); + mem_copy( zpl_cast( char* ) f->Filename, zpl_cast( char* ) filename, len + 1 ); + + return err; + } + + FileError file_open_mode( FileInfo* f, FileMode mode, char const* filename ) + { + FileInfo file_ = + { + { nullptr, nullptr, nullptr, nullptr }, + { nullptr }, + 0, + nullptr, + 0, + nullptr + }; + + *f = file_; + FileError err; + + #if defined( GEN_SYSTEM_WINDOWS ) || defined( GEN_SYSTEM_CYGWIN ) + err = _win32_file_open( &f->FD, &f->Ops, mode, filename ); + #else + err = _posix_file_open( &f->fd, &f->ops, mode, filename ); + #endif + + if ( err == EFileError_NONE ) + return file_new( f, f->FD, f->Ops, filename ); + + return err; + } +#pragma endregion File Handling + +#pragma region String + String String::fmt( AllocatorInfo allocator, char* buf, sw buf_size, char const* fmt, ... ) + { + va_list va; + va_start( va, fmt ); + str_fmt_va( buf, buf_size, fmt, va ); + va_end( va ); + + return make( allocator, buf ); + } + + 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, ... ) + { + sw 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 ); + } +#pragma endregion String + + namespace Memory + { + global AllocatorInfo GlobalAllocator; + global Array Global_AllocatorBuckets; + + void* Global_Allocator_Proc( void* allocator_data, AllocType type, sw size, sw alignment, void* old_memory, sw old_size, u64 flags ) + { + Arena& last = Global_AllocatorBuckets.back(); + + switch ( type ) + { + case EAllocation_ALLOC: + { + if ( last.TotalUsed + size > last.TotalSize ) + { + Arena bucket = Arena::init_from_allocator( heap(), BucketSize ); + + if ( bucket.PhysicalStart == nullptr ) + fatal( "Failed to create bucket for Global_AllocatorBuckets"); + + if ( ! Global_AllocatorBuckets.append( bucket ) ) + fatal( "Failed to append bucket to Global_AllocatorBuckets"); + + last = Global_AllocatorBuckets.back(); + } + + return alloc_align( last, size, alignment ); + } + case EAllocation_FREE: + { + // Doesn't recycle. + } + break; + case EAllocation_FREE_ALL: + { + // Memory::cleanup instead. + } + break; + case EAllocation_RESIZE: + { + if ( last.TotalUsed + size > last.TotalSize ) + { + Arena bucket = Arena::init_from_allocator( heap(), BucketSize ); + + if ( bucket.PhysicalStart == nullptr ) + fatal( "Failed to create bucket for Global_AllocatorBuckets"); + + if ( ! Global_AllocatorBuckets.append( bucket ) ) + fatal( "Failed to append bucket to Global_AllocatorBuckets"); + + last = Global_AllocatorBuckets.back(); + } + + void* result = alloc_align( last.Backing, size, alignment ); + + if ( result != nullptr && old_memory != nullptr ) + { + mem_copy( result, old_memory, old_size ); + } + + return result; + } + } + + return nullptr; + } + + void setup() + { + GlobalAllocator = AllocatorInfo { & Global_Allocator_Proc, nullptr }; + + Global_AllocatorBuckets = Array::init_reserve( heap(), 128 ); + + if ( Global_AllocatorBuckets == nullptr ) + fatal( "Failed to reserve memory for Global_AllocatorBuckets"); + + Arena bucket = Arena::init_from_allocator( heap(), BucketSize ); + + if ( bucket.PhysicalStart == nullptr ) + fatal( "Failed to create first bucket for Global_AllocatorBuckets"); + + Global_AllocatorBuckets.append( bucket ); + } + + void cleanup() + { + s32 index = 0; + s32 left = Global_AllocatorBuckets.num(); + do + { + Arena* bucket = & Global_AllocatorBuckets[ index ]; + bucket->free(); + index++; + } + while ( left--, left ); + + Global_AllocatorBuckets.free(); + } + + // namespace Memory + } +// namespace gen +} +#endif +#pragma endregion GENCPP DEPENDENCIES + +namespace gen +{ namespace StaticData { global Array< Pool > CodePools = { nullptr }; @@ -1351,7 +3106,7 @@ namespace gen s32 left = CodeEntriesArenas.num(); do { - if ( CodeEntriesArenas[index].size_remaining( ZPL_DEFAULT_MEMORY_ALIGNMENT) >= InitSize_CodeEntiresArray ) + if ( CodeEntriesArenas[index].size_remaining( GEN_DEFAULT_MEMORY_ALIGNMENT) >= InitSize_CodeEntiresArray ) allocator = CodeEntriesArenas[index]; index++; } @@ -6303,11 +8058,11 @@ namespace gen Code untyped_fmt( char const* fmt, ...) { local_persist thread_local - char buf[ZPL_PRINTF_MAXLEN] = { 0 }; + char buf[GEN_PRINTF_MAXLEN] = { 0 }; va_list va; va_start(va, fmt); - sw length = str_fmt_va(buf, ZPL_PRINTF_MAXLEN, fmt, va); + sw length = str_fmt_va(buf, GEN_PRINTF_MAXLEN, fmt, va); va_end(va); Code @@ -6322,11 +8077,11 @@ namespace gen Code untyped_token_fmt( s32 num_tokens, ... ) { local_persist thread_local - char buf[ZPL_PRINTF_MAXLEN] = { 0 }; + char buf[GEN_PRINTF_MAXLEN] = { 0 }; va_list va; va_start(va, num_tokens); - sw length = token_fmt_va(buf, ZPL_PRINTF_MAXLEN, num_tokens, va); + sw length = token_fmt_va(buf, GEN_PRINTF_MAXLEN, num_tokens, va); va_end(va); Code @@ -6349,7 +8104,7 @@ namespace gen void Builder::print_fmt( char const* fmt, ... ) { sw res; - char buf[ ZPL_PRINTF_MAXLEN ] = { 0 }; + char buf[ GEN_PRINTF_MAXLEN ] = { 0 }; va_list va; va_start( va, fmt ); diff --git a/project/gen.hpp b/project/gen.hpp index b1851c6..c748319 100644 --- a/project/gen.hpp +++ b/project/gen.hpp @@ -9,16 +9,2297 @@ #pragma once #ifdef gen_time +#pragma region GENCPP DEPENDENCIES +//! If its desired to roll your own dependencies, define GENCPP_PROVIDE_DEPENDENCIES before including this file. +#ifndef GENCPP_PROVIDE_DEPENDENCIES -#include "Bloat.hpp" +#if __clang__ +# pragma clang diagnostic ignored "-Wunused-const-variable" +# pragma clang diagnostic ignored "-Wswitch" +# pragma clang diagnostic ignored "-Wunused-variable" +# pragma clang diagnostic ignored "-Wunknown-pragmas" +# pragma clang diagnostic ignored "-Wvarargs" +# pragma clang diagnostic ignored "-Wunused-function" +#endif -// Temporarily here for debugging purposes. -#define GEN_DEFINE_LIBRARY_CODE_CONSTANTS -// #define GEN_DONT_USE_FATAL +#if defined( GEN_HAS_ATTRIBUTE ) +# undef GEN_HAS_ATTRIBUTE +#endif +#if defined( __has_attribute ) +# define GEN_HAS_ATTRIBUTE( attribute ) __has_attribute( attribute ) +#else +# define GEN_HAS_ATTRIBUTE( attribute ) ( 0 ) +#endif -#define GEN_FEATURE_PARSING -// #define GEN_FEATURE_EDITOR -// #define GEN_FEATURE_SCANNER +/* Platform architecture */ + +#if defined( _WIN64 ) || defined( __x86_64__ ) || defined( _M_X64 ) || defined( __64BIT__ ) || defined( __powerpc64__ ) || defined( __ppc64__ ) || defined( __aarch64__ ) +# ifndef GEN_ARCH_64_BIT +# define GEN_ARCH_64_BIT 1 +# endif +#else +# ifndef GEN_ARCH_32_BIT +# define GEN_ARCH_32_BIT 1 +# endif +#endif + +/* Platform OS */ + +#if defined( _WIN32 ) || defined( _WIN64 ) +# ifndef GEN_SYSTEM_WINDOWS +# define GEN_SYSTEM_WINDOWS 1 +# endif +#elif defined( __APPLE__ ) && defined( __MACH__ ) +# ifndef GEN_SYSTEM_OSX +# define GEN_SYSTEM_OSX 1 +# endif +# ifndef GEN_SYSTEM_MACOS +# define GEN_SYSTEM_MACOS 1 +# endif +# include +# if TARGET_IPHONE_SIMULATOR == 1 || TARGET_OS_IPHONE == 1 +# ifndef GEN_SYSTEM_IOS +# define GEN_SYSTEM_IOS 1 +# endif +# endif +#elif defined( __unix__ ) +# ifndef GEN_SYSTEM_UNIX +# define GEN_SYSTEM_UNIX 1 +# endif +# if defined( ANDROID ) || defined( __ANDROID__ ) +# ifndef GEN_SYSTEM_ANDROID +# define GEN_SYSTEM_ANDROID 1 +# endif +# ifndef GEN_SYSTEM_LINUX +# define GEN_SYSTEM_LINUX 1 +# endif +# elif defined( __linux__ ) +# ifndef GEN_SYSTEM_LINUX +# define GEN_SYSTEM_LINUX 1 +# endif +# elif defined( __FreeBSD__ ) || defined( __FreeBSD_kernel__ ) +# ifndef GEN_SYSTEM_FREEBSD +# define GEN_SYSTEM_FREEBSD 1 +# endif +# elif defined( __OpenBSD__ ) +# ifndef GEN_SYSTEM_OPENBSD +# define GEN_SYSTEM_OPENBSD 1 +# endif +# elif defined( __EMSCRIPTEN__ ) +# ifndef GEN_SYSTEM_EMSCRIPTEN +# define GEN_SYSTEM_EMSCRIPTEN 1 +# endif +# elif defined( __CYGWIN__ ) +# ifndef GEN_SYSTEM_CYGWIN +# define GEN_SYSTEM_CYGWIN 1 +# endif +# else +# error This UNIX operating system is not supported +# endif +#else +# error This operating system is not supported +#endif + +/* Platform compiler */ + +#if defined( _MSC_VER ) +# define GEN_COMPILER_MSVC 1 +#elif defined( __GNUC__ ) +# define GEN_COMPILER_GCC 1 +#elif defined( __clang__ ) +# define GEN_COMPILER_CLANG 1 +#elif defined( __MINGW32__ ) +# define GEN_COMPILER_MINGW 1 +#elif defined( __TINYC__ ) +# define GEN_COMPILER_TINYC 1 +#else +# error Unknown compiler +#endif + +# ifndef GEN_DEF_INLINE +# if defined( GEN_STATIC ) +# define GEN_DEF_INLINE +# define GEN_IMPL_INLINE +# else +# define GEN_DEF_INLINE static +# define GEN_IMPL_INLINE static inline +# endif +# endif + +# if defined( GEN_ALWAYS_INLINE ) +# undef GEN_ALWAYS_INLINE +# endif + +#ifdef GEN_COMPILER_MSVC +# define forceinline __forceinline +#elif defined(GEN_COMPILER_GCC) +# define forceinline inline __attribute__((__always_inline__)) +#elif defined(GEN_COMPILER_CLANG) +#if __has_attribute(__always_inline__) +# define forceinline inline __attribute__((__always_inline__)) +#else +# define forceinline inline +#endif +#else +# define forceinline inline +#endif + + +#ifdef GEN_COMPILER_MSVC +# define neverinline __declspec( noinline ) +#elif defined(GEN_COMPILER_GCC) +# define forceinline inline __attribute__( ( __noinline__ ) ) +#elif defined(GEN_COMPILER_CLANG) +#if __has_attribute(__always_inline__) +# define forceinline inline __attribute__( ( __noinline__ ) ) +#else +# define forceinline inline +#endif +#else +# define forceinline inline +#endif + +#ifndef count_of +# define count_of( x ) ( ( size_of( x ) / size_of( 0 [ x ] ) ) / ( ( sw )( ! ( size_of( x ) % size_of( 0 [ x ] ) ) ) ) ) +#endif + +#ifndef is_between +# define is_between( x, lower, upper ) ( ( ( lower ) <= ( x ) ) && ( ( x ) <= ( upper ) ) ) +#endif + +#ifndef min +# define min( a, b ) ( ( a ) < ( b ) ? ( a ) : ( b ) ) +#endif + +#ifndef size_of +# define size_of( x ) ( sw )( sizeof( x ) ) +#endif + +#ifndef swap +# define swap( Type, a, b ) \ + do \ + { \ + Type tmp = ( a ); \ + ( a ) = ( b ); \ + ( b ) = tmp; \ + } while ( 0 ) +#endif + +#ifndef zpl_cast +# define zpl_cast( Type ) ( Type ) +#endif + +#ifndef global +# define global static // Global variables +#endif + +#ifndef internal +# define internal static // Internal linkage +#endif + +#ifndef local_persist +# define local_persist static // Local Persisting variables +#endif + +#if defined(__GNUC__) || defined(__clang__) + // Supports 0-10 arguments + #define macro_num_args_impl( _0, \ + _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, \ + N, ... \ + ) N + // _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, \ + // _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, \ + // _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, + + // ## deletes preceding comma if _VA_ARGS__ is empty (GCC, Clang) + #define macro_num_args(...) \ + macro_num_args_impl(_, ## __VA_ARGS__, \ + 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, \ + 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, \ + 0 \ + ) + // 50, 49, 48, 47, 46, 45, 44, 43, 42, 41, \ + // 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, \ + // 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, +#else + // Supports 1-10 arguments + #define macro_num_args_impl( \ + _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, \ + N, ... \ + ) N + + #define macro_num_args(...) \ + macro_num_args_impl( __VA_ARGS__, \ + 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, \ + 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 \ + ) +#endif + +#define bit( Value_ ) ( 1 << Value_ ) +#define bitfield_is_equal( Type_, Field_, Mask_ ) ( (Type_(Mask_) & Type_(Field_)) == Type_(Mask_) ) +#define ccast( Type_, Value_ ) * const_cast< Type_* >( & (Value_) ) +#define scast( Type_, Value_ ) static_cast< Type_ >( Value_ ) +#define rcast( Type_, Value_ ) reinterpret_cast< Type_ >( Value_ ) +#define pcast( Type_, Value_ ) ( * (Type_*)( & (Value_) ) ) +#define stringize_va( ... ) #__VA_ARGS__ +#define stringize( ... ) stringize_va( __VA_ARGS__ ) +#define do_once() \ +do \ +{ \ + static \ + bool Done = false; \ + if ( Done ) \ + return; \ + Done = true; \ +} \ +while(0) + +#define do_once_start \ +do \ +{ \ + static \ + bool Done = false; \ + if ( Done ) \ + break; \ + Done = true; + +#define do_once_end \ +} \ +while(0); + +#pragma region Mandatory Includes +# include +# include + +# if defined( GEN_SYSTEM_WINDOWS ) +# include +# endif +#pragma endregion Mandatory Includes + +// #include "Banned.define.hpp" + +namespace gen +{ + constexpr + char const* Msg_Invalid_Value = "INVALID VALUE PROVIDED"; + + #pragma region Basic Types + #ifndef GEN_U8_MIN + # define GEN_U8_MIN 0u + # define GEN_U8_MAX 0xffu + # define GEN_I8_MIN ( -0x7f - 1 ) + # define GEN_I8_MAX 0x7f + + # define GEN_U16_MIN 0u + # define GEN_U16_MAX 0xffffu + # define GEN_I16_MIN ( -0x7fff - 1 ) + # define GEN_I16_MAX 0x7fff + + # define GEN_U32_MIN 0u + # define GEN_U32_MAX 0xffffffffu + # define GEN_I32_MIN ( -0x7fffffff - 1 ) + # define GEN_I32_MAX 0x7fffffff + + # define GEN_U64_MIN 0ull + # define GEN_U64_MAX 0xffffffffffffffffull + # define GEN_I64_MIN ( -0x7fffffffffffffffll - 1 ) + # define GEN_I64_MAX 0x7fffffffffffffffll + + # if defined( GEN_ARCH_32_BIT ) + # define GEN_USIZE_MIN GEN_U32_MIN + # define GEN_USIZE_MAX GEN_U32_MAX + # define GEN_ISIZE_MIN GEN_S32_MIN + # define GEN_ISIZE_MAX GEN_S32_MAX + # elif defined( GEN_ARCH_64_BIT ) + # define GEN_USIZE_MIN GEN_U64_MIN + # define GEN_USIZE_MAX GEN_U64_MAX + # define GEN_ISIZE_MIN GEN_I64_MIN + # define GEN_ISIZE_MAX GEN_I64_MAX + # else + # error Unknown architecture size. This library only supports 32 bit and 64 bit architectures. + # endif + + # define GEN_F32_MIN 1.17549435e-38f + # define GEN_F32_MAX 3.40282347e+38f + + # define GEN_F64_MIN 2.2250738585072014e-308 + # define GEN_F64_MAX 1.7976931348623157e+308 + #endif + + #if defined( GEN_COMPILER_MSVC ) + # if _MSC_VER < 1300 + typedef unsigned char u8; + typedef signed char s8; + typedef unsigned short u16; + typedef signed short s16; + typedef unsigned int u32; + typedef signed int s32; + # else + typedef unsigned __int8 u8; + typedef signed __int8 s8; + typedef unsigned __int16 u16; + typedef signed __int16 s16; + typedef unsigned __int32 u32; + typedef signed __int32 s32; + # endif + typedef unsigned __int64 u64; + typedef signed __int64 s64; + #else + # include + + typedef uint8_t u8; + typedef int8_t s8; + typedef uint16_t u16; + typedef int16_t s16; + typedef uint32_t u32; + typedef int32_t s32; + typedef uint64_t u64; + typedef int64_t s64; + #endif + + static_assert( sizeof( u8 ) == sizeof( s8 ), "sizeof(u8) != sizeof(s8)" ); + static_assert( sizeof( u16 ) == sizeof( s16 ), "sizeof(u16) != sizeof(s16)" ); + static_assert( sizeof( u32 ) == sizeof( s32 ), "sizeof(u32) != sizeof(s32)" ); + static_assert( sizeof( u64 ) == sizeof( s64 ), "sizeof(u64) != sizeof(s64)" ); + + static_assert( sizeof( u8 ) == 1, "sizeof(u8) != 1" ); + static_assert( sizeof( u16 ) == 2, "sizeof(u16) != 2" ); + static_assert( sizeof( u32 ) == 4, "sizeof(u32) != 4" ); + static_assert( sizeof( u64 ) == 8, "sizeof(u64) != 8" ); + + typedef size_t uw; + typedef ptrdiff_t sw; + + static_assert( sizeof( uw ) == sizeof( sw ), "sizeof(uw) != sizeof(sw)" ); + + // NOTE: (u)zpl_intptr is only here for semantic reasons really as this library will only support 32/64 bit OSes. + #if defined( _WIN64 ) + typedef signed __int64 sptr; + typedef unsigned __int64 uptr; + #elif defined( _WIN32 ) + // NOTE; To mark types changing their size, e.g. zpl_intptr + # ifndef _W64 + # if ! defined( __midl ) && ( defined( _X86_ ) || defined( _M_IX86 ) ) && _MSC_VER >= 1300 + # define _W64 __w64 + # else + # define _W64 + # endif + # endif + typedef _W64 signed int sptr; + typedef _W64 unsigned int uptr; + #else + typedef uintptr_t uptr; + typedef intptr_t sptr; + #endif + + static_assert( sizeof( uptr ) == sizeof( sptr ), "sizeof(uptr) != sizeof(sptr)" ); + + typedef float f32; + typedef double f64; + + static_assert( sizeof( f32 ) == 4, "sizeof(f32) != 4" ); + static_assert( sizeof( f64 ) == 8, "sizeof(f64) != 8" ); + + typedef s8 b8; + typedef s16 b16; + typedef s32 b32; + #pragma endregion Basic Types + + #pragma region Debug + #ifndef GEN_DEBUG_TRAP + # if defined( _MSC_VER ) + # if _MSC_VER < 1300 + # define GEN_DEBUG_TRAP() __asm int 3 /* Trap to debugger! */ + # else + # define GEN_DEBUG_TRAP() __debugbreak() + # endif + # elif defined( GEN_COMPILER_TINYC ) + # define GEN_DEBUG_TRAP() process_exit( 1 ) + # else + # define GEN_DEBUG_TRAP() __builtin_trap() + # endif + #endif + + #ifndef GEN_ASSERT + # define GEN_ASSERT( cond ) GEN_ASSERT_MSG( cond, NULL ) + #endif + + #ifndef GEN_ASSERT_MSG + # define GEN_ASSERT_MSG( cond, msg, ... ) \ + do \ + { \ + if ( ! ( cond ) ) \ + { \ + assert_handler( #cond, __FILE__, zpl_cast( s64 ) __LINE__, msg, ##__VA_ARGS__ ); \ + GEN_DEBUG_TRAP(); \ + } \ + } while ( 0 ) + #endif + + #ifndef GEN_ASSERT_NOT_NULL + # define GEN_ASSERT_NOT_NULL( ptr ) GEN_ASSERT_MSG( ( ptr ) != NULL, #ptr " must not be NULL" ) + #endif + + // NOTE: Things that shouldn't happen with a message! + #ifndef GEN_PANIC + # define GEN_PANIC( msg, ... ) GEN_ASSERT_MSG( 0, msg, ##__VA_ARGS__ ) + #endif + + void assert_handler( char const* condition, char const* file, s32 line, char const* msg, ... ); + s32 assert_crash( char const* condition ); + void process_exit( u32 code ); + #pragma endregion Debug + + #pragma region Memory + #ifndef kilobytes + # define kilobytes( x ) ( ( x ) * ( s64 )( 1024 ) ) + # define megabytes( x ) ( kilobytes( x ) * ( s64 )( 1024 ) ) + # define gigabytes( x ) ( megabytes( x ) * ( s64 )( 1024 ) ) + # define terabytes( x ) ( gigabytes( x ) * ( s64 )( 1024 ) ) + #endif + + #define GEN__ONES ( zpl_cast( uw ) - 1 / GEN_U8_MAX ) + #define GEN__HIGHS ( GEN__ONES * ( GEN_U8_MAX / 2 + 1 ) ) + #define GEN__HAS_ZERO( x ) ( ( ( x )-GEN__ONES ) & ~( x )&GEN__HIGHS ) + + //! Checks if value is power of 2. + GEN_DEF_INLINE b32 is_power_of_two( sw x ); + + //! Aligns address to specified alignment. + GEN_DEF_INLINE void* align_forward( void* ptr, sw alignment ); + + //! Aligns value to a specified alignment. + GEN_DEF_INLINE s64 align_forward_i64( s64 value, sw alignment ); + + //! Moves pointer forward by bytes. + GEN_DEF_INLINE void* pointer_add( void* ptr, sw bytes ); + + //! Copy non-overlapping memory from source to destination. + void* mem_copy( void* dest, void const* source, sw size ); + + //! Search for a constant value within the size limit at memory location. + void const* mem_find( void const* data, u8 byte_value, sw size ); + + //! Copy memory from source to destination. + GEN_DEF_INLINE void* mem_move( void* dest, void const* source, sw size ); + + //! Set constant value at memory location with specified size. + GEN_DEF_INLINE void* mem_set( void* data, u8 byte_value, sw size ); + + //! @param ptr Memory location to clear up. + //! @param size The size to clear up with. + GEN_DEF_INLINE void zero_size( void* ptr, sw size ); + + #ifndef zero_item + //! Clears up an item. + # define zero_item( t ) zero_size( ( t ), size_of( *( t ) ) ) // NOTE: Pass pointer of struct + + //! Clears up an array. + # define zero_array( a, count ) zero_size( ( a ), size_of( *( a ) ) * count ) + #endif + + enum AllocType : u8 + { + EAllocation_ALLOC, + EAllocation_FREE, + EAllocation_FREE_ALL, + EAllocation_RESIZE, + }; + + using AllocatorProc = void* ( void* allocator_data, AllocType type + , sw size, sw alignment + , void* old_memory, sw old_size + , u64 flags ); + + struct AllocatorInfo + { + AllocatorProc* Proc; + void* Data; + }; + + enum AllocFlag + { + ALLOCATOR_FLAG_CLEAR_TO_ZERO = bit( 0 ), + }; + + #ifndef GEN_DEFAULT_MEMORY_ALIGNMENT + # define GEN_DEFAULT_MEMORY_ALIGNMENT ( 2 * size_of( void* ) ) + #endif + + #ifndef GEN_DEFAULT_ALLOCATOR_FLAGS + # define GEN_DEFAULT_ALLOCATOR_FLAGS ( ALLOCATOR_FLAG_CLEAR_TO_ZERO ) + #endif + + //! Allocate memory with default alignment. + GEN_DEF_INLINE void* alloc( AllocatorInfo a, sw size ); + + //! Allocate memory with specified alignment. + GEN_DEF_INLINE void* alloc_align( AllocatorInfo a, sw size, sw alignment ); + + //! Free allocated memory. + GEN_DEF_INLINE void free( AllocatorInfo a, void* ptr ); + + //! Free all memory allocated by an allocator. + GEN_DEF_INLINE void free_all( AllocatorInfo a ); + + //! Resize an allocated memory. + GEN_DEF_INLINE void* resize( AllocatorInfo a, void* ptr, sw old_size, sw new_size ); + + //! Resize an allocated memory with specified alignment. + GEN_DEF_INLINE void* resize_align( AllocatorInfo a, void* ptr, sw old_size, sw new_size, sw alignment ); + + #ifndef alloc_item + //! Allocate memory for an item. + # define alloc_item( allocator_, Type ) ( Type* )alloc( allocator_, size_of( Type ) ) + + //! Allocate memory for an array of items. + # define alloc_array( allocator_, Type, count ) ( Type* )alloc( allocator_, size_of( Type ) * ( count ) ) + #endif + + /* heap memory analysis tools */ + /* define GEN_HEAP_ANALYSIS to enable this feature */ + /* call zpl_heap_stats_init at the beginning of the entry point */ + /* you can call zpl_heap_stats_check near the end of the execution to validate any possible leaks */ + void heap_stats_init( void ); + sw heap_stats_used_memory( void ); + sw heap_stats_alloc_count( void ); + void heap_stats_check( void ); + + //! Allocate/Resize memory using default options. + + //! Use this if you don't need a "fancy" resize allocation + GEN_DEF_INLINE void* default_resize_align( AllocatorInfo a, void* ptr, sw old_size, sw new_size, sw alignment ); + + void* heap_allocator_proc( void* allocator_data, AllocType type, sw size, sw alignment, void* old_memory, sw old_size, u64 flags ); + + //! The heap allocator backed by operating system's memory manager. + constexpr AllocatorInfo heap( void ) { return { heap_allocator_proc, nullptr }; } + + #ifndef malloc + //! Helper to allocate memory using heap allocator. + # define malloc( sz ) alloc( heap(), sz ) + + //! Helper to free memory allocated by heap allocator. + # define mfree( ptr ) free( heap(), ptr ) + #endif + + GEN_IMPL_INLINE b32 is_power_of_two( sw x ) + { + if ( x <= 0 ) + return false; + return ! ( x & ( x - 1 ) ); + } + + GEN_IMPL_INLINE void* align_forward( void* ptr, sw alignment ) + { + uptr p; + + GEN_ASSERT( is_power_of_two( alignment ) ); + + p = zpl_cast( uptr ) ptr; + return zpl_cast( void* )( ( p + ( alignment - 1 ) ) & ~( alignment - 1 ) ); + } + + GEN_IMPL_INLINE s64 align_forward_i64( s64 value, sw alignment ) + { + return value + ( alignment - value % alignment ) % alignment; + } + + GEN_IMPL_INLINE void* pointer_add( void* ptr, sw bytes ) + { + return zpl_cast( void* )( zpl_cast( u8* ) ptr + bytes ); + } + + GEN_IMPL_INLINE void* mem_move( void* dest, void const* source, sw n ) + { + if ( dest == NULL ) + { + return NULL; + } + + u8* d = zpl_cast( u8* ) dest; + u8 const* s = zpl_cast( u8 const* ) source; + + if ( d == s ) + return d; + if ( s + n <= d || d + n <= s ) // NOTE: Non-overlapping + return mem_copy( d, s, n ); + + if ( d < s ) + { + if ( zpl_cast( uptr ) s % size_of( sw ) == zpl_cast( uptr ) d % size_of( sw ) ) + { + while ( zpl_cast( uptr ) d % size_of( sw ) ) + { + if ( ! n-- ) + return dest; + *d++ = *s++; + } + while ( n >= size_of( sw ) ) + { + *zpl_cast( sw* ) d = *zpl_cast( sw* ) s; + n -= size_of( sw ); + d += size_of( sw ); + s += size_of( sw ); + } + } + for ( ; n; n-- ) + *d++ = *s++; + } + else + { + if ( ( zpl_cast( uptr ) s % size_of( sw ) ) == ( zpl_cast( uptr ) d % size_of( sw ) ) ) + { + while ( zpl_cast( uptr )( d + n ) % size_of( sw ) ) + { + if ( ! n-- ) + return dest; + d[ n ] = s[ n ]; + } + while ( n >= size_of( sw ) ) + { + n -= size_of( sw ); + *zpl_cast( sw* )( d + n ) = *zpl_cast( sw* )( s + n ); + } + } + while ( n ) + n--, d[ n ] = s[ n ]; + } + + return dest; + } + + GEN_IMPL_INLINE void* mem_set( void* dest, u8 c, sw n ) + { + if ( dest == NULL ) + { + return NULL; + } + + u8* s = zpl_cast( u8* ) dest; + sw k; + u32 c32 = ( ( u32 )-1 ) / 255 * c; + + if ( n == 0 ) + return dest; + s[ 0 ] = s[ n - 1 ] = c; + if ( n < 3 ) + return dest; + s[ 1 ] = s[ n - 2 ] = c; + s[ 2 ] = s[ n - 3 ] = c; + if ( n < 7 ) + return dest; + s[ 3 ] = s[ n - 4 ] = c; + if ( n < 9 ) + return dest; + + k = -zpl_cast( sptr ) s & 3; + s += k; + n -= k; + n &= -4; + + *zpl_cast( u32* )( s + 0 ) = c32; + *zpl_cast( u32* )( s + n - 4 ) = c32; + if ( n < 9 ) + return dest; + *zpl_cast( u32* )( s + 4 ) = c32; + *zpl_cast( u32* )( s + 8 ) = c32; + *zpl_cast( u32* )( s + n - 12 ) = c32; + *zpl_cast( u32* )( s + n - 8 ) = c32; + if ( n < 25 ) + return dest; + *zpl_cast( u32* )( s + 12 ) = c32; + *zpl_cast( u32* )( s + 16 ) = c32; + *zpl_cast( u32* )( s + 20 ) = c32; + *zpl_cast( u32* )( s + 24 ) = c32; + *zpl_cast( u32* )( s + n - 28 ) = c32; + *zpl_cast( u32* )( s + n - 24 ) = c32; + *zpl_cast( u32* )( s + n - 20 ) = c32; + *zpl_cast( u32* )( s + n - 16 ) = c32; + + k = 24 + ( zpl_cast( uptr ) s & 4 ); + s += k; + n -= k; + + { + u64 c64 = ( zpl_cast( u64 ) c32 << 32 ) | c32; + while ( n > 31 ) + { + *zpl_cast( u64* )( s + 0 ) = c64; + *zpl_cast( u64* )( s + 8 ) = c64; + *zpl_cast( u64* )( s + 16 ) = c64; + *zpl_cast( u64* )( s + 24 ) = c64; + + n -= 32; + s += 32; + } + } + + return dest; + } + + GEN_IMPL_INLINE void* alloc_align( AllocatorInfo a, sw size, sw alignment ) + { + return a.Proc( a.Data, EAllocation_ALLOC, size, alignment, nullptr, 0, GEN_DEFAULT_ALLOCATOR_FLAGS ); + } + + GEN_IMPL_INLINE void* alloc( AllocatorInfo a, sw size ) + { + return alloc_align( a, size, GEN_DEFAULT_MEMORY_ALIGNMENT ); + } + + GEN_IMPL_INLINE void free( AllocatorInfo a, void* ptr ) + { + if ( ptr != nullptr ) + a.Proc( a.Data, EAllocation_FREE, 0, 0, ptr, 0, GEN_DEFAULT_ALLOCATOR_FLAGS ); + } + + GEN_IMPL_INLINE void free_all( AllocatorInfo a ) + { + a.Proc( a.Data, EAllocation_FREE_ALL, 0, 0, nullptr, 0, GEN_DEFAULT_ALLOCATOR_FLAGS ); + } + + GEN_IMPL_INLINE void* resize( AllocatorInfo a, void* ptr, sw old_size, sw new_size ) + { + return resize_align( a, ptr, old_size, new_size, GEN_DEFAULT_MEMORY_ALIGNMENT ); + } + + GEN_IMPL_INLINE void* resize_align( AllocatorInfo a, void* ptr, sw old_size, sw new_size, sw alignment ) + { + return a.Proc( a.Data, EAllocation_RESIZE, new_size, alignment, ptr, old_size, GEN_DEFAULT_ALLOCATOR_FLAGS ); + } + + GEN_IMPL_INLINE void* default_resize_align( AllocatorInfo a, void* old_memory, sw old_size, sw new_size, sw alignment ) + { + if ( ! old_memory ) + return alloc_align( a, new_size, alignment ); + + if ( new_size == 0 ) + { + free( a, old_memory ); + return nullptr; + } + + if ( new_size < old_size ) + new_size = old_size; + + if ( old_size == new_size ) + { + return old_memory; + } + else + { + void* new_memory = alloc_align( a, new_size, alignment ); + if ( ! new_memory ) + return nullptr; + mem_move( new_memory, old_memory, min( new_size, old_size ) ); + free( a, old_memory ); + return new_memory; + } + } + + GEN_IMPL_INLINE void zero_size( void* ptr, sw size ) + { + mem_set( ptr, 0, size ); + } + + struct Arena + { + static + void* allocator_proc( void* allocator_data, AllocType type, sw size, sw alignment, void* old_memory, sw old_size, u64 flags ); + + static + Arena init_from_memory( void* start, sw size ) + { + return + { + { nullptr, nullptr }, + start, + size, + 0, + 0 + }; + } + + static + Arena init_from_allocator( AllocatorInfo backing, sw size ) + { + Arena result = + { + backing, + alloc( backing, size), + size, + 0, + 0 + }; + return result; + } + + static + Arena init_sub( Arena& parent, sw size ) + { + return init_from_allocator( parent.Backing, size ); + } + + sw alignment_of( sw alignment ) + { + sw alignment_offset, result_pointer, mask; + GEN_ASSERT( is_power_of_two( alignment ) ); + + alignment_offset = 0; + result_pointer = (sw) PhysicalStart + TotalUsed; + mask = alignment - 1; + + if ( result_pointer & mask ) + alignment_offset = alignment - ( result_pointer & mask ); + + return alignment_offset; + } + + void check() + { + GEN_ASSERT( TempCount == 0 ); + } + + void free() + { + if ( Backing.Proc ) + { + gen::free( Backing, PhysicalStart ); + PhysicalStart = nullptr; + } + } + + sw size_remaining( sw alignment ) + { + sw result = TotalSize - ( TotalUsed + alignment_of( alignment ) ); + return result; + } + + AllocatorInfo Backing; + void* PhysicalStart; + sw TotalSize; + sw TotalUsed; + sw TempCount; + + operator AllocatorInfo() + { + return { allocator_proc, this }; + } + }; + + struct Pool + { + static + void* allocator_proc( void* allocator_data, AllocType type, sw size, sw alignment, void* old_memory, sw old_size, u64 flags ); + + static + Pool init( AllocatorInfo backing, sw num_blocks, sw block_size ) + { + return init_align( backing, num_blocks, block_size, GEN_DEFAULT_MEMORY_ALIGNMENT ); + } + + static + Pool init_align( AllocatorInfo backing, sw num_blocks, sw block_size, sw block_align ); + + void free() + { + if ( Backing.Proc ) + { + gen::free( Backing, PhysicalStart ); + } + } + + AllocatorInfo Backing; + void* PhysicalStart; + void* FreeList; + sw BlockSize; + sw BlockAlign; + sw TotalSize; + sw NumBlocks; + + operator AllocatorInfo() + { + return { allocator_proc, this }; + } + }; + #pragma endregion Memory + + #pragma region String Ops + GEN_DEF_INLINE const char* char_first_occurence( const char* str, char c ); + + GEN_DEF_INLINE b32 char_is_alpha( char c ); + GEN_DEF_INLINE b32 char_is_alphanumeric( char c ); + GEN_DEF_INLINE b32 char_is_digit( char c ); + GEN_DEF_INLINE b32 char_is_hex_digit( char c ); + GEN_DEF_INLINE b32 char_is_space( char c ); + GEN_DEF_INLINE char char_to_lower( char c ); + GEN_DEF_INLINE char char_to_upper( char c ); + + GEN_DEF_INLINE s32 digit_to_int( char c ); + GEN_DEF_INLINE s32 hex_digit_to_int( char c ); + + GEN_DEF_INLINE s32 str_compare( const char* s1, const char* s2 ); + GEN_DEF_INLINE s32 str_compare( const char* s1, const char* s2, sw len ); + GEN_DEF_INLINE char* str_copy( char* dest, const char* source, sw len ); + GEN_DEF_INLINE sw str_copy_nulpad( char* dest, const char* source, sw len ); + GEN_DEF_INLINE sw str_len( const char* str ); + GEN_DEF_INLINE sw str_len( const char* str, sw max_len ); + GEN_DEF_INLINE char* str_reverse( char* str ); // NOTE: ASCII only + + // NOTE: ASCII only + GEN_DEF_INLINE void str_to_lower( char* str ); + GEN_DEF_INLINE void str_to_upper( char* str ); + + s64 str_to_i64( const char* str, char** end_ptr, s32 base ); // TODO : Support more than just decimal and hexadecimal + void i64_to_str( s64 value, char* string, s32 base ); + void u64_to_str( u64 value, char* string, s32 base ); + + GEN_IMPL_INLINE const char* char_first_occurence( const char* s, char c ) + { + char ch = c; + for ( ; *s != ch; s++ ) + { + if ( *s == '\0' ) + return NULL; + } + return s; + } + + GEN_IMPL_INLINE b32 char_is_alpha( char c ) + { + if ( ( c >= 'A' && c <= 'Z' ) || ( c >= 'a' && c <= 'z' ) ) + return true; + return false; + } + + GEN_IMPL_INLINE b32 char_is_alphanumeric( char c ) + { + return char_is_alpha( c ) || char_is_digit( c ); + } + + GEN_IMPL_INLINE b32 char_is_digit( char c ) + { + if ( c >= '0' && c <= '9' ) + return true; + return false; + } + + GEN_IMPL_INLINE b32 char_is_hex_digit( char c ) + { + if ( char_is_digit( c ) || ( c >= 'a' && c <= 'f' ) || ( c >= 'A' && c <= 'F' ) ) + return true; + return false; + } + + GEN_IMPL_INLINE b32 char_is_space( char c ) + { + if ( c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f' || c == '\v' ) + return true; + return false; + } + + GEN_IMPL_INLINE char char_to_lower( char c ) + { + if ( c >= 'A' && c <= 'Z' ) + return 'a' + ( c - 'A' ); + return c; + } + + GEN_IMPL_INLINE char char_to_upper( char c ) + { + if ( c >= 'a' && c <= 'z' ) + return 'A' + ( c - 'a' ); + return c; + } + + GEN_IMPL_INLINE s32 digit_to_int( char c ) + { + return char_is_digit( c ) ? c - '0' : c - 'W'; + } + + GEN_IMPL_INLINE s32 hex_digit_to_int( char c ) + { + if ( char_is_digit( c ) ) + return digit_to_int( c ); + else if ( is_between( c, 'a', 'f' ) ) + return c - 'a' + 10; + else if ( is_between( c, 'A', 'F' ) ) + return c - 'A' + 10; + return -1; + } + + GEN_IMPL_INLINE s32 str_compare( const char* s1, const char* s2 ) + { + while ( *s1 && ( *s1 == *s2 ) ) + { + s1++, s2++; + } + return *( u8* )s1 - *( u8* )s2; + } + + GEN_IMPL_INLINE s32 str_compare( const char* s1, const char* s2, sw len ) + { + for ( ; len > 0; s1++, s2++, len-- ) + { + if ( *s1 != *s2 ) + return ( ( s1 < s2 ) ? -1 : +1 ); + else if ( *s1 == '\0' ) + return 0; + } + return 0; + } + + GEN_IMPL_INLINE char* str_copy( char* dest, const char* source, sw len ) + { + GEN_ASSERT_NOT_NULL( dest ); + if ( source ) + { + char* str = dest; + while ( len > 0 && *source ) + { + *str++ = *source++; + len--; + } + while ( len > 0 ) + { + *str++ = '\0'; + len--; + } + } + return dest; + } + + GEN_IMPL_INLINE sw str_copy_nulpad( char* dest, const char* source, sw len ) + { + sw result = 0; + GEN_ASSERT_NOT_NULL( dest ); + if ( source ) + { + const char* source_start = source; + char* str = dest; + while ( len > 0 && *source ) + { + *str++ = *source++; + len--; + } + while ( len > 0 ) + { + *str++ = '\0'; + len--; + } + + result = source - source_start; + } + return result; + } + + GEN_IMPL_INLINE sw str_len( const char* str ) + { + if ( str == NULL ) + { + return 0; + } + const char* p = str; + while ( *str ) + str++; + return str - p; + } + + GEN_IMPL_INLINE sw str_len( const char* str, sw max_len ) + { + const char* end = zpl_cast( const char* ) mem_find( str, 0, max_len ); + if ( end ) + return end - str; + return max_len; + } + + GEN_IMPL_INLINE char* str_reverse( char* str ) + { + sw len = str_len( str ); + char* a = str + 0; + char* b = str + len - 1; + len /= 2; + while ( len-- ) + { + swap( char, *a, *b ); + a++, b--; + } + return str; + } + + GEN_IMPL_INLINE void str_to_lower( char* str ) + { + if ( ! str ) + return; + while ( *str ) + { + *str = char_to_lower( *str ); + str++; + } + } + + GEN_IMPL_INLINE void str_to_upper( char* str ) + { + if ( ! str ) + return; + while ( *str ) + { + *str = char_to_upper( *str ); + str++; + } + } + #pragma endregion String Ops + + #pragma region Containers + #pragma push_macro("template") + #undef template + + template + struct Array + { + struct Header + { + AllocatorInfo Allocator; + uw Capacity; + uw Num; + }; + + static + Array init( AllocatorInfo allocator ) + { + return init_reserve( allocator, grow_formula(0) ); + } + + static + Array init_reserve( AllocatorInfo allocator, sw capacity ) + { + Header* header = rcast( Header*, alloc( allocator, sizeof(Header) + sizeof(Type) * capacity )); + + if ( header == nullptr ) + return { nullptr }; + + header->Allocator = allocator; + header->Capacity = capacity; + header->Num = 0; + + return { rcast( Type*, header + 1) }; + } + + static + uw grow_formula( uw value ) + { + return 2 * value + 8; + } + + 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++; + + return true; + } + + Type& back( void ) + { + Header& header = * get_header(); + return Data[ header.Num - 1 ]; + } + + void clear( void ) + { + Header& header = * get_header(); + header.Num = 0; + } + + bool fill( uw begin, uw end, Type value ) + { + Header& header = * get_header(); + + if ( begin < 0 || end >= header.Num ) + return false; + + for ( sw idx = begin; idx < end; idx++ ) + { + Data[ idx ] = value; + } + + return true; + } + + void free( void ) + { + Header& header = * get_header(); + gen::free( header.Allocator, &header ); + } + + Header* get_header( void ) + { + return rcast( Header*, Data ) - 1 ; + } + + bool grow( uw min_capacity ) + { + Header& header = * get_header(); + uw new_capacity = grow_formula( header.Capacity ); + + if ( new_capacity < min_capacity ) + new_capacity = min_capacity; + + return set_capacity( new_capacity ); + } + + uw num( void ) + { + return get_header()->Num; + } + + bool pop( void ) + { + Header& header = * get_header(); + + GEN_ASSERT( header.Num > 0 ); + header.Num--; + } + + void remove_at( uw 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--; + } + + bool reserve( uw new_capacity ) + { + Header& header = * get_header(); + + if ( header.Capacity < new_capacity ) + return set_capacity( new_capacity ); + + return true; + } + + bool resize( uw num ) + { + Header* header = get_header(); + + if ( header->Capacity < num ) + { + if ( ! grow( num ) ) + return false; + + header = get_header(); + } + + header->Num = num; + return true; + } + + bool set_capacity( uw new_capacity ) + { + Header& header = * get_header(); + + if ( new_capacity == header.Capacity ) + return true; + + if ( new_capacity < header.Num ) + header.Num = new_capacity; + + sw size = sizeof( Header ) + sizeof( Type ) * new_capacity; + Header* new_header = rcast( Header*, alloc( header.Allocator, size ) ); + + if ( new_header == nullptr ) + return false; + + mem_move( new_header, &header, sizeof( Header ) + sizeof( Type ) * header.Num ); + + new_header->Capacity = new_capacity; + + gen::free( header.Allocator, &header ); + + Data = rcast( Type*, new_header + 1); + return true; + } + + 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; + } + }; + + template + struct HashTable + { + struct FindResult + { + sw HashIndex; + sw PrevIndex; + sw EntryIndex; + }; + + struct Entry + { + u64 Key; + sw Next; + Type Value; + }; + + static + HashTable init( AllocatorInfo allocator ) + { + HashTable result = { { nullptr }, { nullptr } }; + + result.Hashes = Array::init( allocator ); + result.Entries = Array::init( allocator ); + + return result; + } + + static + HashTable init_reserve( AllocatorInfo allocator, sw num ) + { + HashTable result = { { nullptr }, { nullptr } }; + + result.Hashes = Array::init_reserve( allocator, num ); + result.Hashes.get_header()->Num = num; + + result.Entries = Array::init_reserve( allocator, num ); + + return result; + } + + void clear( void ) + { + for ( sw idx = 0; idx < Hashes.num(); idx++ ) + Hashes[ idx ] = -1; + + Hashes.clear(); + Entries.clear(); + } + + void destroy( void ) + { + if ( Hashes && Hashes.get_header()->Capacity ) + Hashes.free(); + if ( Entries && Hashes.get_header()->Capacity ) + Entries.free(); + } + + Type* get( u64 key ) + { + sw idx = find( key ).EntryIndex; + if ( idx >= 0 ) + return & Entries[ idx ].Value; + + return nullptr; + } + + using MapProc = void (*)( u64 key, Type value ); + + void map( MapProc map_proc ) + { + GEN_ASSERT_NOT_NULL( map_proc ); + + for ( sw idx = 0; idx < Entries.num(); idx++ ) + { + map_proc( Entries[ idx ].Key, Entries[ idx ].Value ); + } + } + + using MapMutProc = void (*)( u64 key, Type* value ); + + void map_mut( MapMutProc map_proc ) + { + GEN_ASSERT_NOT_NULL( map_proc ); + + for ( sw idx = 0; idx < Entries.num(); idx++ ) + { + map_proc( Entries[ idx ].Key, & Entries[ idx ].Value ); + } + } + + void grow() + { + sw new_num = Array::grow_formula( Entries.num() ); + rehash( new_num ); + } + + void rehash( sw new_num ) + { + sw idx; + sw last_added_index; + + HashTable new_ht = init_reserve( Hashes.get_header()->Allocator, new_num ); + + Array::Header* hash_header = new_ht.Hashes.get_header(); + + for ( idx = 0; idx < new_ht.Hashes.num(); ++idx ) + new_ht.Hashes[ idx ] = -1; + + for ( idx = 0; idx < Entries.num(); ++idx ) + { + Entry& entry = Entries[ idx ]; + + FindResult find_result; + + if ( new_ht.Hashes.num() == 0 ) + new_ht.grow(); + + entry = Entries[ idx ]; + find_result = new_ht.find( entry.Key ); + last_added_index = new_ht.add_entry( entry.Key ); + + if ( find_result.PrevIndex < 0 ) + new_ht.Hashes[ find_result.HashIndex ] = last_added_index; + + else + new_ht.Entries[ find_result.PrevIndex ].Next = last_added_index; + + new_ht.Entries[ last_added_index ].Next = find_result.EntryIndex; + new_ht.Entries[ last_added_index ].Value = entry.Value; + } + + destroy(); + *this = new_ht; + } + + void rehash_fast() + { + sw idx; + + for ( idx = 0; idx < Entries.num(); idx++ ) + Entries[ idx ].Next = -1; + + for ( idx = 0; idx < Hashes.num(); idx++ ) + Hashes[ idx ] = -1; + + for ( idx = 0; idx < Entries.num(); idx++ ) + { + Entry* entry; + + FindResult find_result; + } + } + + void remove( u64 key ) + { + FindResult find_result = find( key); + + if ( find_result.EntryIndex >= 0 ) + { + Entries.remove_at( find_result.EntryIndex ); + rehash_fast(); + } + } + + void remove_entry( sw idx ) + { + Entries.remove_at( idx ); + } + + void set( u64 key, Type value ) + { + sw idx; + FindResult find_result; + + if ( Hashes.num() == 0 ) + grow(); + + find_result = find( key ); + + if ( find_result.EntryIndex >= 0 ) + { + idx = find_result.EntryIndex; + } + else + { + idx = add_entry( key ); + + if ( find_result.PrevIndex >= 0 ) + { + Entries[ find_result.PrevIndex ].Next = idx; + } + else + { + Hashes[ find_result.HashIndex ] = idx; + } + } + + Entries[ idx ].Value = value; + + if ( full() ) + grow(); + } + + sw slot( u64 key ) + { + for ( sw idx = 0; idx < Hashes.num(); ++idx ) + if ( Hashes[ idx ] == key ) + return idx; + + return -1; + } + + Array< sw> Hashes; + Array< Entry> Entries; + + protected: + + sw add_entry( u64 key ) + { + sw idx; + Entry entry = { key, -1 }; + + idx = Entries.num(); + Entries.append( entry ); + return idx; + } + + FindResult find( u64 key ) + { + FindResult result = { -1, -1, -1 }; + + if ( Hashes.num() > 0 ) + { + result.HashIndex = key % Hashes.num(); + result.EntryIndex = Hashes[ result.HashIndex ]; + + while ( result.EntryIndex >= 0 ) + { + if ( Entries[ result.EntryIndex ].Key == key ) + break; + + result.PrevIndex = result.EntryIndex; + result.EntryIndex = Entries[ result.EntryIndex ].Next; + } + } + + return result; + } + + b32 full() + { + return 0.75f * Hashes.num() < Entries.num(); + } + }; + + #pragma pop_macro("template") + #pragma endregion Containers + + #pragma region Hashing + + u32 crc32( void const* data, sw len ); + + #pragma endregion Hashing + + #pragma region String + // Constant string with length. + struct StrC + { + sw Len; + char const* Ptr; + + operator char const* () const + { + return Ptr; + } + }; + + #define txt_StrC( text ) \ + (StrC){ sizeof( text ) - 1, text } + + StrC to_StrC( char const* str ) + { + return { str_len( str ), str }; + } + + // Dynamic String + // This is directly based off the ZPL string api. + // They used a header pattern + // I kept it for simplicty of porting but its not necessary to keep it that way. + struct String + { + struct Header + { + AllocatorInfo Allocator; + sw Length; + sw Capacity; + }; + + static + String make( AllocatorInfo allocator, char const* str ) + { + sw length = str ? str_len( str ) : 0; + return make_length( allocator, str, length ); + } + + static + String make( AllocatorInfo allocator, StrC str ) + { + return make_length( allocator, str.Ptr, str.Len ); + } + + static + String make_reserve( AllocatorInfo allocator, sw capacity ) + { + constexpr sw header_size = sizeof( Header ); + + s32 alloc_size = header_size + capacity + 1; + void* allocation = alloc( allocator, alloc_size ); + + if ( allocation == nullptr ) + return { nullptr }; + + mem_set( allocation, 0, alloc_size ); + + Header* + header = rcast(Header*, allocation); + header->Allocator = allocator; + header->Capacity = capacity; + header->Length = 0; + + String result = { (char*)allocation + header_size }; + return result; + } + + static + String make_length( AllocatorInfo allocator, char const* str, sw length ) + { + constexpr sw header_size = sizeof( Header ); + + s32 alloc_size = header_size + length + 1; + void* allocation = alloc( allocator, alloc_size ); + + if ( allocation == nullptr ) + return { nullptr }; + + if ( ! str ) + mem_set( allocation, 0, alloc_size ); + + Header& + header = * rcast(Header*, allocation); + header = { allocator, length, length }; + + String result = { rcast( char*, allocation) + header_size }; + + if ( length && str ) + mem_copy( result, str, length ); + + result[ length ] = '\0'; + + return result; + } + + static + String fmt( AllocatorInfo allocator, char* buf, sw buf_size, char const* fmt, ... ); + + static + String fmt_buf( AllocatorInfo allocator, char const* fmt, ... ); + + static + String join( AllocatorInfo allocator, char const** parts, sw num_parts, char const* glue ) + { + String result = make( allocator, "" ); + + for ( sw idx = 0; idx < num_parts; ++idx ) + { + result.append( parts[ idx ] ); + + if ( idx < num_parts - 1 ) + result.append( glue ); + } + + return result; + } + + static + bool are_equal( String lhs, String rhs ) + { + if ( lhs.length() != rhs.length() ) + return false; + + for ( sw idx = 0; idx < lhs.length(); ++idx ) + if ( lhs[ idx ] != rhs[ idx ] ) + return false; + + return true; + } + + bool make_space_for( char const* str, sw add_len ) + { + sw available = avail_space(); + + // NOTE: Return if there is enough space left + if ( available >= add_len ) + { + return true; + } + else + { + sw new_len, old_size, new_size; + + void* ptr; + void* new_ptr; + + AllocatorInfo allocator = get_header().Allocator; + Header* header = nullptr; + + new_len = 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 = zpl_cast( Header* ) new_ptr; + header->Allocator = allocator; + header->Capacity = new_len; + + Data = rcast( char*, header + 1 ); + + return str; + } + } + + bool append( char const* str ) + { + return append( str, str_len( str ) ); + } + + bool append( char const* str, sw length ) + { + if ( sptr(str) > 0 ) + { + sw 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; + } + + 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, ... ); + + sw avail_space() const + { + Header const& + header = * rcast( Header const*, Data - sizeof( Header )); + + return header.Capacity - header.Length; + } + + sw capacity() const + { + Header const& + header = * rcast( Header const*, Data - sizeof( Header )); + + return header.Capacity; + } + + void clear() + { + get_header().Length = 0; + } + + String duplicate( AllocatorInfo allocator ) + { + return make_length( allocator, Data, length() ); + } + + void free() + { + if ( ! Data ) + return; + + Header& header = get_header(); + + gen::free( header.Allocator, & header ); + } + + Header& get_header() + { + return *(Header*)(Data - sizeof(Header)); + } + + sw length() const + { + Header const& + header = * rcast( Header const*, Data - sizeof( Header )); + + return header.Length; + } + + void trim( char const* cut_set ) + { + sw len = 0; + + char* start_pos = Data; + char* end_pos = Data + length() - 1; + + while ( start_pos <= end_pos && char_first_occurence( cut_set, *start_pos ) ) + start_pos++; + + while ( end_pos > start_pos && char_first_occurence( cut_set, *end_pos ) ) + end_pos--; + + len = scast( sw, ( start_pos > end_pos ) ? 0 : ( ( end_pos - start_pos ) + 1 ) ); + + if ( Data != start_pos ) + mem_move( Data, start_pos, len ); + + Data[ len ] = '\0'; + + get_header().Length = len; + } + + void trim_space() + { + return trim( " \t\r\n\v\f" ); + } + + // For-range support + + char* begin() + { + return Data; + } + + char* end() + { + Header const& + header = * rcast( Header const*, Data - sizeof( Header )); + + return Data + header.Length; + } + + operator bool() + { + return Data; + } + + 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 [] ( sw index ) + { + return Data[ index ]; + } + + char const& operator [] ( sw index ) const + { + return Data[ index ]; + } + + char* Data = nullptr; + }; + + struct String_POD + { + char* Data; + + operator String() + { + return * rcast(String*, this); + } + }; + static_assert( sizeof( String_POD ) == sizeof( String ), "String is not a POD" ); + #pragma endregion String + + #pragma region File Handling + + typedef u32 FileMode; + + enum FileModeFlag + { + EFileMode_READ = bit( 0 ), + EFileMode_WRITE = bit( 1 ), + EFileMode_APPEND = bit( 2 ), + EFileMode_RW = bit( 3 ), + GEN_FILE_MODES = EFileMode_READ | EFileMode_WRITE | EFileMode_APPEND | EFileMode_RW, + }; + + // NOTE: Only used internally and for the file operations + enum SeekWhenceType + { + ESeekWhence_BEGIN = 0, + ESeekWhence_CURRENT = 1, + ESeekWhence_END = 2, + }; + + enum FileError + { + EFileError_NONE, + EFileError_INVALID, + EFileError_INVALID_FILENAME, + EFileError_EXISTS, + EFileError_NOT_EXISTS, + EFileError_PERMISSION, + EFileError_TRUNCATION_FAILURE, + EFileError_NOT_EMPTY, + EFileError_NAME_TOO_LONG, + EFileError_UNKNOWN, + }; + + union FileDescriptor + { + void* p; + sptr i; + uptr u; + }; + + typedef struct FileOperations FileOperations; + + #define GEN_FILE_OPEN_PROC( name ) FileError name( FileDescriptor* fd, FileOperations* ops, FileMode mode, char const* filename ) + #define GEN_FILE_READ_AT_PROC( name ) b32 name( FileDescriptor fd, void* buffer, sw size, s64 offset, sw* bytes_read, b32 stop_at_newline ) + #define GEN_FILE_WRITE_AT_PROC( name ) b32 name( FileDescriptor fd, void const* buffer, sw size, s64 offset, sw* bytes_written ) + #define GEN_FILE_SEEK_PROC( name ) b32 name( FileDescriptor fd, s64 offset, SeekWhenceType whence, s64* new_offset ) + #define GEN_FILE_CLOSE_PROC( name ) void name( FileDescriptor fd ) + + typedef GEN_FILE_OPEN_PROC( file_open_proc ); + typedef GEN_FILE_READ_AT_PROC( FileReadProc ); + typedef GEN_FILE_WRITE_AT_PROC( FileWriteProc ); + typedef GEN_FILE_SEEK_PROC( FileSeekProc ); + typedef GEN_FILE_CLOSE_PROC( FileCloseProc ); + + struct FileOperations + { + FileReadProc* read_at; + FileWriteProc* write_at; + FileSeekProc* seek; + FileCloseProc* close; + }; + + extern FileOperations const default_file_operations; + + typedef u64 FileTime; + + enum DirType + { + GEN_DIR_TYPE_FILE, + GEN_DIR_TYPE_FOLDER, + GEN_DIR_TYPE_UNKNOWN, + }; + + struct DirInfo; + + struct DirEntry + { + char const* FileName; + DirInfo* Info; + u8 Type; + }; + + struct DirInfo + { + char const* FullPath; + DirEntry* Entries; // zpl_array + + // Internals + char** Filenames; // zpl_array + String Buffer; + }; + + struct FileInfo + { + FileOperations Ops; + FileDescriptor FD; + b32 IsTemp; + + char const* Filename; + FileTime LastWriteTime; + DirEntry* Dir; + }; + + enum FileStandardType + { + EFileStandard_INPUT, + EFileStandard_OUTPUT, + EFileStandard_ERROR, + + EFileStandard_COUNT, + }; + + /** + * Get standard file I/O. + * @param std Check zpl_file_standard_type + * @return File handle to standard I/O + */ + FileInfo* file_get_standard( FileStandardType std ); + + /** + * Closes the file + * @param file + */ + FileError file_close( FileInfo* file ); + + /** + * Returns the currently opened file's name + * @param file + */ + inline + char const* file_name( FileInfo* file ) + { + return file->Filename ? file->Filename : ""; + } + + /** + * Opens a file using a specified mode + * @param file + * @param mode Access mode to use + * @param filename + */ + FileError file_open_mode( FileInfo* file, FileMode mode, char const* filename ); + + /** + * Seeks the file cursor from the beginning of file to a specific position + * @param file + * @param offset Offset to seek to + */ + GEN_DEF_INLINE s64 file_seek( FileInfo* file, s64 offset ); + + /** + * Returns the length from the beginning of the file we've read so far + * @param file + * @return Our current position in file + */ + GEN_DEF_INLINE s64 file_tell( FileInfo* file ); + + /** + * Writes to a file + * @param file + * @param buffer Buffer to read from + * @param size Size to read + */ + b32 file_write( FileInfo* file, void const* buffer, sw size ); + + /** + * Writes to file at a specific offset + * @param file + * @param buffer Buffer to read from + * @param size Size to write + * @param offset Offset to write to + * @param bytes_written How much data we've actually written + */ + GEN_DEF_INLINE b32 file_write_at( FileInfo* file, void const* buffer, sw size, s64 offset ); + + /** + * Writes to file safely + * @param file + * @param buffer Buffer to read from + * @param size Size to write + * @param offset Offset to write to + * @param bytes_written How much data we've actually written + */ + GEN_DEF_INLINE b32 file_write_at_check( FileInfo* file, void const* buffer, sw size, s64 offset, sw* bytes_written ); + + GEN_IMPL_INLINE s64 file_seek( FileInfo* f, s64 offset ) + { + s64 new_offset = 0; + + if ( ! f->Ops.read_at ) + f->Ops = default_file_operations; + + f->Ops.seek( f->FD, offset, ESeekWhence_BEGIN, &new_offset ); + + return new_offset; + } + + GEN_IMPL_INLINE s64 file_tell( FileInfo* f ) + { + s64 new_offset = 0; + + if ( ! f->Ops.read_at ) + f->Ops = default_file_operations; + + f->Ops.seek( f->FD, 0, ESeekWhence_CURRENT, &new_offset ); + + return new_offset; + } + + GEN_IMPL_INLINE b32 file_write( FileInfo* f, void const* buffer, sw size ) + { + s64 cur_offset = file_tell( f ); + b32 result = file_write_at( f, buffer, size, file_tell( f ) ); + + file_seek( f, cur_offset + size ); + + return result; + } + + GEN_IMPL_INLINE b32 file_write_at( FileInfo* f, void const* buffer, sw size, s64 offset ) + { + return file_write_at_check( f, buffer, size, offset, NULL ); + } + + GEN_IMPL_INLINE b32 file_write_at_check( FileInfo* f, void const* buffer, sw size, s64 offset, sw* bytes_written ) + { + if ( ! f->Ops.read_at ) + f->Ops = default_file_operations; + + return f->Ops.write_at( f->FD, buffer, size, offset, bytes_written ); + } + #pragma endregion File Handling + + #pragma region Printing + + #ifndef GEN_PRINTF_MAXLEN + # define GEN_PRINTF_MAXLEN 65536 + #endif + + // NOTE: A locally persisting buffer is used internally + char* str_fmt_buf( char const* fmt, ... ); + char* str_fmt_buf_va( char const* fmt, va_list va ); + sw str_fmt_va( char* str, sw n, char const* fmt, va_list va ); + sw str_fmt_out_va( char const* fmt, va_list va ); + sw str_fmt_out_err( char const* fmt, ... ); + sw str_fmt_out_err_va( char const* fmt, va_list va ); + sw str_fmt_file_va( FileInfo* f, char const* fmt, va_list va ); + #pragma endregion Printing + + namespace Memory + { + // NOTE: This limits the size of the string that can be read from a file or generated to 10 megs. + // If you are generating a string larger than this, increase the size of the bucket here. + constexpr uw BucketSize = megabytes(10); + + // Global allocator used for data with process lifetime. + extern AllocatorInfo GlobalAllocator; + + // Heap allocator is being used for now to isolate errors from being memory related (tech debt till ready to address) + // #define g_allocator heap() + + void setup(); + void cleanup(); + } + + inline + sw log_fmt(char const* fmt, ...) + { + sw res; + va_list va; + + va_start(va, fmt); + res = str_fmt_out_va(fmt, va); + va_end(va); + + return res; + } + + inline + sw fatal(char const* fmt, ...) + { + local_persist thread_local + char buf[GEN_PRINTF_MAXLEN] = { 0 }; + + va_list va; + + #if Build_Debug + va_start(va, fmt); + str_fmt_va(buf, GEN_PRINTF_MAXLEN, fmt, va); + va_end(va); + + assert_crash(buf); + return -1; + #else + va_start(va, fmt); + str_fmt_out_err_va( fmt, va); + va_end(va); + + exit(1); + return -1; + #endif + } + +// gen namespace +} +//END: GENCPP_PROVIDE_DEPENDENCIES +#endif +#pragma endregion GENCPP DEPENDENCIES namespace gen { @@ -346,7 +2627,7 @@ namespace gen */ namespace Attribute { - #if defined(ZPL_SYSTEM_WINDOWS) || defined( __CYGWIN__ ) + #if defined(GEN_SYSTEM_WINDOWS) || defined( __CYGWIN__ ) # define GEN_API_ # define GEN_API_Export_Code __declspec(dllexport) # define GEN_API_Import_Code __declspec(dllimport) @@ -356,7 +2637,7 @@ namespace gen constexpr char const* API_Import = stringize( GEN_API_Import_Code ); constexpr char const* Keyword = stringize( GEN_Attribute_Keyword); - #elif ZPL_HAS_ATTRIBUTE( visibility ) || ZPL_GCC_VERSION_CHECK( 3, 3, 0 ) || ZPL_INTEL_VERSION_CHECK( 13, 0, 0 ) + #elif GEN_HAS_ATTRIBUTE( visibility ) || GEN_GCC_VERSION_CHECK( 3, 3, 0 ) || GEN_INTEL_VERSION_CHECK( 13, 0, 0 ) # define GEN_API_Export_Code __attribute__ ((visibility ("default"))) # define GEN_API_Import_Code __attribute__ ((visibility ("default"))) # define GEN_Attribute_Keyword __attribute__ @@ -827,12 +3108,12 @@ namespace gen StrC _token_fmt( sw num, ... ) { local_persist thread_local - char buf[ZPL_PRINTF_MAXLEN] = { 0 }; - mem_set( buf, 0, ZPL_PRINTF_MAXLEN ); + char buf[GEN_PRINTF_MAXLEN] = { 0 }; + mem_set( buf, 0, GEN_PRINTF_MAXLEN ); va_list va; va_start(va, num ); - sw result = token_fmt_va(buf, ZPL_PRINTF_MAXLEN, num, va); + sw result = token_fmt_va(buf, GEN_PRINTF_MAXLEN, num, va); va_end(va); return { result, buf }; diff --git a/test/test.cpp b/test/test.cpp index 803445a..949173c 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -1,13 +1,12 @@ -#include "Bloat.cpp" +#ifdef gen_time +#define GEN_DEFINE_LIBRARY_CODE_CONSTANTS +#define GEN_FEATURE_PARSING #include "Parsed\Array.Parsed.hpp" #include "Parsed\Buffer.Parsed.hpp" #include "Parsed\HashTable.Parsed.hpp" #include "Parsed\Ring.Parsed.hpp" #include "Parsed\Sanity.Parsed.hpp" #include "SOA.hpp" - - -#ifdef gen_time #include "gen.cpp" using namespace gen;