2024-06-28 12:07:02 +02:00
2024-06-28 18:50:30 +02:00
/*
Usage :
// We need to use macro const_string to convert literal to string
string fmt = const_string ( " Pointer address: 0x%x " ) ;
print ( fmt , cast ( u64 ) & a ) ; // Print to stdout
// Format a string and allocate with context.allocator
string a = sprint ( " Hello, %cs! \n " , " balls " ) ; // %cs for char*
string balls = const_string ( " balls " ) ;
// tprint for temporary allocation
string b = tprint ( const_string ( " Hello, %s! \n " ) , balls ) ; // %s for string
// Allocate a new string of length 12 (with context allocator)
string c = alloc_string ( 12 ) ;
dealloc_string ( c ) ;
// We can use raw char* for format with printf/sprintf/tprintf
printf ( " Hello, %! \n " , balls ) ;
// concatenation
string concatenated = string_concat ( a , b ) ;
// Use temporary memory to make a null-terminated copy of fixed-length string
char * cstring = temp_convert_to_null_terminated_string ( balls ) ;
// To convert a cstring to string (using same memory)
string s ;
s . data = ( u8 * ) cstring ;
s . count = strlen ( cstring ) ;
// String matching
bool match = strings_match ( a , b ) ;
// View into "Hello, balls!\n" from index 7 with a count of 5; "balls"
string balls2 = string_view ( a , 7 , 5 ) ;
*/
2024-06-28 12:07:02 +02:00
void * memcpy ( void * , const void * , size_t ) ;
2024-06-28 18:50:30 +02:00
void * talloc ( u64 ) ;
2024-06-28 12:07:02 +02:00
typedef struct string {
u64 count ;
2024-06-28 18:50:30 +02:00
u8 * data ;
2024-06-28 12:07:02 +02:00
} string ;
void push_temp_allocator ( ) ;
# define cstr const_string
2024-06-28 18:50:30 +02:00
# define const_string(s) (string){ length_of_null_terminated_string(s), (u8*)s }
2024-06-28 12:07:02 +02:00
inline u64 length_of_null_terminated_string ( const char * cstring ) {
u64 len = 0 ;
while ( * cstring ! = 0 ) {
len + = 1 ;
cstring + = 1 ;
}
return len ;
}
2024-06-28 18:50:30 +02:00
string alloc_string ( u64 count ) {
string s ;
s . count = count ;
s . data = cast ( u8 * ) alloc ( count ) ;
return s ;
}
void dealloc_string ( string s ) {
dealloc ( s . data ) ;
}
2024-06-28 12:07:02 +02:00
// context.allocator !
2024-06-28 18:50:30 +02:00
string string_concat ( const string left , const string right ) {
2024-06-28 12:07:02 +02:00
string result ;
result . count = left . count + right . count ;
result . data = cast ( u8 * ) alloc ( result . count ) ;
memcpy ( result . data , left . data , left . count ) ;
memcpy ( result . data + left . count , right . data , right . count ) ;
return result ;
}
// context.allocator !
2024-06-28 18:50:30 +02:00
char * convert_to_null_terminated_string ( const string s ) {
2024-06-28 12:07:02 +02:00
char * cstring = cast ( char * ) alloc ( s . count + 1 ) ;
memcpy ( cstring , s . data , s . count ) ;
cstring [ s . count ] = 0 ;
return cstring ;
}
2024-06-28 18:50:30 +02:00
char * temp_convert_to_null_terminated_string ( const string s ) {
2024-06-28 12:07:02 +02:00
push_temp_allocator ( ) ;
char * c = convert_to_null_terminated_string ( s ) ;
pop_allocator ( ) ;
return c ;
2024-06-28 18:50:30 +02:00
}
inline int crt_sprintf ( char * str , const char * format , . . . ) ;
int vsnprintf ( char * buffer , size_t n , const char * fmt , va_list args ) ;
bool is_pointer_valid ( void * p ) ;
u64 format_string_to_buffer ( char * buffer , u64 count , const char * fmt , va_list args ) {
if ( ! buffer ) count = UINT64_MAX ;
const char * p = fmt ;
char * bufp = buffer ;
while ( * p ! = ' \0 ' & & ( bufp - buffer ) < count - 1 ) {
if ( * p = = ' % ' ) {
p + = 1 ;
if ( * p = = ' s ' ) {
// We replace %s formatting with our fixed length string
p + = 1 ;
string s = va_arg ( args , string ) ;
assert ( s . count < ( 1024ULL * 1024ULL * 1024ULL * 256ULL ) , " Ypu passed something else than a fixed-length 'string' to %%s. Maybe you passed a char* and should do %%cs instead? " ) ;
for ( u64 i = 0 ; i < s . count & & ( bufp - buffer ) < count - 1 ; i + + ) {
if ( buffer ) * bufp = s . data [ i ] ;
bufp + = 1 ;
}
} else if ( * p = = ' c ' & & * ( p + 1 ) = = ' s ' ) {
// We extend the standard formatting and add %cs so we can format c strings if we need to
p + = 2 ;
char * s = va_arg ( args , char * ) ;
assert ( is_pointer_valid ( s ) , " You passed an invalid pointer to %%cs " ) ;
u64 len = 0 ;
while ( * s ! = ' \0 ' & & ( bufp - buffer ) < count - 1 ) {
assert ( is_pointer_valid ( s ) & & len < ( 1024ULL * 1024ULL * 1024ULL * 1ULL ) , " The argument passed to %%cs is either way too big, missing null-termination or simply not a char*. " ) ;
if ( buffer ) {
* bufp = * s ;
}
s + = 1 ;
bufp + = 1 ;
len + = 1 ;
assert ( is_pointer_valid ( s ) & & len < ( 1024ULL * 1024ULL * 1024ULL * 1ULL ) , " The argument passed to %%cs is either way too big, missing null-termination or simply not a char*. " ) ;
}
} else {
// Fallback to standard vsnprintf
char temp_buffer [ 512 ] ;
char format_specifier [ 64 ] ;
int specifier_len = 0 ;
format_specifier [ specifier_len + + ] = ' % ' ;
while ( * p ! = ' \0 ' & & strchr ( " diuoxXfFeEgGaAcCpn% " , * p ) = = NULL ) {
format_specifier [ specifier_len + + ] = * p + + ;
}
if ( * p ! = ' \0 ' ) {
format_specifier [ specifier_len + + ] = * p + + ;
}
format_specifier [ specifier_len ] = ' \0 ' ;
int temp_len = vsnprintf ( temp_buffer , sizeof ( temp_buffer ) , format_specifier , args ) ;
switch ( format_specifier [ specifier_len - 1 ] ) {
case ' d ' : case ' i ' : va_arg ( args , int ) ; break ;
case ' u ' : case ' x ' : case ' X ' : case ' o ' : va_arg ( args , unsigned int ) ; break ;
case ' f ' : case ' F ' : case ' e ' : case ' E ' : case ' g ' : case ' G ' : case ' a ' : case ' A ' : va_arg ( args , double ) ; break ;
case ' c ' : va_arg ( args , int ) ; break ;
case ' s ' : va_arg ( args , char * ) ; break ;
case ' p ' : va_arg ( args , void * ) ; break ;
case ' n ' : va_arg ( args , int * ) ; break ;
default : break ;
}
if ( temp_len < 0 ) {
return - 1 ; // Error in formatting
}
for ( int i = 0 ; i < temp_len & & ( bufp - buffer ) < count - 1 ; i + + ) {
if ( buffer ) * bufp = temp_buffer [ i ] ;
bufp + = 1 ;
}
}
} else {
if ( buffer ) {
* bufp = * p ;
}
bufp + = 1 ;
p + = 1 ;
}
}
if ( buffer ) * bufp = ' \0 ' ;
return bufp - buffer ;
}
string sprint_null_terminated_string_va_list_to_buffer ( const char * fmt , va_list args , void * buffer , u64 count ) {
u64 formatted_length = format_string_to_buffer ( ( char * ) buffer , count , fmt , args ) ;
string result ;
result . data = ( u8 * ) buffer ;
if ( formatted_length > = 0 & & formatted_length < count ) {
result . count = formatted_length ;
} else {
result . count = count - 1 ;
}
return result ;
}
string sprint_va_list_to_buffer ( const string fmt , va_list args , void * buffer , u64 count ) {
char * fmt_cstring = temp_convert_to_null_terminated_string ( fmt ) ;
return sprint_null_terminated_string_va_list_to_buffer ( fmt_cstring , args , buffer , count ) ;
}
// context.allocator
string sprint_va_list ( const string fmt , va_list args ) {
char * fmt_cstring = temp_convert_to_null_terminated_string ( fmt ) ;
u64 count = format_string_to_buffer ( NULL , 0 , fmt_cstring , args ) + 1 ;
char * buffer = NULL ;
buffer = ( char * ) alloc ( count ) ;
return sprint_null_terminated_string_va_list_to_buffer ( fmt_cstring , args , buffer , count ) ;
}
// context.allocator
string sprint ( const string fmt , . . . ) {
va_list args = 0 ;
va_start ( args , fmt ) ;
string s = sprint_va_list ( fmt , args ) ;
va_end ( args ) ;
return s ;
}
// temp allocator
string tprint ( const string fmt , . . . ) {
va_list args = 0 ;
va_start ( args , fmt ) ;
push_temp_allocator ( ) ;
string s = sprint_va_list ( fmt , args ) ;
pop_allocator ( ) ;
va_end ( args ) ;
return s ;
}
// context.allocator
string sprintf ( const char * fmt , . . . ) {
string sfmt ;
sfmt . data = cast ( u8 * ) fmt ;
sfmt . count = strlen ( fmt ) ;
va_list args ;
va_start ( args , fmt ) ;
string s = sprint_va_list ( sfmt , args ) ;
va_end ( args ) ;
return s ;
}
// temp allocator
string tprintf ( const char * fmt , . . . ) {
string sfmt ;
sfmt . data = cast ( u8 * ) fmt ;
sfmt . count = strlen ( fmt ) ;
va_list args ;
va_start ( args , fmt ) ;
push_temp_allocator ( ) ;
string s = sprint_va_list ( sfmt , args ) ;
pop_allocator ( ) ;
va_end ( args ) ;
return s ;
}
bool strings_match ( string a , string b ) {
if ( a . count ! = b . count ) return false ;
// Count match, pointer match: they are the same
if ( a . data = = b . data ) return true ;
return memcmp ( a . data , b . data , a . count ) = = 0 ;
}
string string_view ( string s , u64 start_index , u64 count ) {
assert ( start_index < s . count , " array_view start_index % out of range for string count % " , start_index , s . count ) ;
assert ( count > 0 , " array_view count must be more than 0 " ) ;
assert ( start_index + count < = s . count , " array_view start_index + count is out of range " ) ;
string result ;
result . data = s . data + start_index ;
result . count = count ;
return result ;
}
// Defined in os interface
void print ( string s , . . . ) ;