From 54fad631a415241e9ee15f5124f50c5486f1b362 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Branimir=20Karad=C5=BEi=C4=87?= Date: Sun, 12 Nov 2017 17:25:49 -0800 Subject: [PATCH] Added .ini settings utility. --- 3rdparty/ini/README.md | 1 + 3rdparty/ini/ini.h | 1056 ++++++++++++++++++++++++++++ 3rdparty/ini/ini.md | 333 +++++++++ include/bx/inline/readerwriter.inl | 18 +- include/bx/pixelformat.h | 3 +- include/bx/readerwriter.h | 15 +- include/bx/settings.h | 61 ++ include/bx/string.h | 9 + scripts/bx.lua | 2 + scripts/genie.lua | 4 +- src/settings.cpp | 213 ++++++ src/string.cpp | 56 +- tests/filepath_test.cpp | 8 +- tests/settings_test.cpp | 49 ++ tests/string_test.cpp | 18 + 15 files changed, 1824 insertions(+), 22 deletions(-) create mode 100644 3rdparty/ini/README.md create mode 100644 3rdparty/ini/ini.h create mode 100644 3rdparty/ini/ini.md create mode 100644 include/bx/settings.h create mode 100644 src/settings.cpp create mode 100644 tests/settings_test.cpp diff --git a/3rdparty/ini/README.md b/3rdparty/ini/README.md new file mode 100644 index 0000000..bc4d7c7 --- /dev/null +++ b/3rdparty/ini/README.md @@ -0,0 +1 @@ +https://github.com/mattiasgustavsson/libs/ diff --git a/3rdparty/ini/ini.h b/3rdparty/ini/ini.h new file mode 100644 index 0000000..90ce40d --- /dev/null +++ b/3rdparty/ini/ini.h @@ -0,0 +1,1056 @@ +/* +------------------------------------------------------------------------------ + Licensing information can be found at the end of the file. +------------------------------------------------------------------------------ + +ini.h - v1.1 - Simple ini-file reader for C/C++. + +Do this: + #define INI_IMPLEMENTATION +before you include this file in *one* C/C++ file to create the implementation. +*/ + +#ifndef ini_h +#define ini_h + +#define INI_GLOBAL_SECTION ( 0 ) +#define INI_NOT_FOUND ( -1 ) + +typedef struct ini_t ini_t; + +ini_t* ini_create( void* memctx ); +ini_t* ini_load( char const* data, void* memctx ); + +int ini_save( ini_t const* ini, char* data, int size ); +void ini_destroy( ini_t* ini ); + +int ini_section_count( ini_t const* ini ); +char const* ini_section_name( ini_t const* ini, int section ); + +int ini_property_count( ini_t const* ini, int section ); +char const* ini_property_name( ini_t const* ini, int section, int property ); +char const* ini_property_value( ini_t const* ini, int section, int property ); + +int ini_find_section( ini_t const* ini, char const* name, int name_length ); +int ini_find_property( ini_t const* ini, int section, char const* name, int name_length ); + +int ini_section_add( ini_t* ini, char const* name, int length ); +void ini_property_add( ini_t* ini, int section, char const* name, int name_length, char const* value, int value_length ); +void ini_section_remove( ini_t* ini, int section ); +void ini_property_remove( ini_t* ini, int section, int property ); + +void ini_section_name_set( ini_t* ini, int section, char const* name, int length ); +void ini_property_name_set( ini_t* ini, int section, int property, char const* name, int length ); +void ini_property_value_set( ini_t* ini, int section, int property, char const* value, int length ); + +#endif /* ini_h */ + + +/** + +Examples +======== + +Loading an ini file and retrieving values +----------------------------------------- + + #define INI_IMPLEMENTATION + #include "ini.h" + + #include + #include + + int main() + { + FILE* fp = fopen( "test.ini", "r" ); + fseek( fp, 0, SEEK_END ); + int size = ftell( fp ); + fseek( fp, 0, SEEK_SET ); + char* data = (char*) malloc( size + 1 ); + fread( data, 1, size, fp ); + data[ size ] = '\0'; + fclose( fp ); + + ini_t* ini = ini_load( data ); + free( data ); + int second_index = ini_find_property( ini, INI_GLOBAL_SECTION, "SecondSetting" ); + char const* second = ini_property_value( ini, INI_GLOBAL_SECTION, second_index ); + printf( "%s=%s\n", "SecondSetting", second ); + int section = ini_find_section( ini, "MySection" ); + int third_index = ini_find_property( ini, section, "ThirdSetting" ); + char const* third = ini_property_value( ini, section, third_index ); + printf( "%s=%s\n", "ThirdSetting", third ); + ini_destroy( ini ); + + return 0; + } + + +Creating a new ini file +----------------------- + + #define INI_IMPLEMENTATION + #include "ini.h" + + #include + #include + + int main() + { + ini_t* ini = ini_create(); + ini_property_add( ini, INI_GLOBAL_SECTION, "FirstSetting", "Test" ); + ini_property_add( ini, INI_GLOBAL_SECTION, "SecondSetting", "2" ); + int section = ini_section_add( ini, "MySection" ); + ini_property_add( ini, section, "ThirdSetting", "Three" ); + + int size = ini_save( ini, NULL, 0 ); // Find the size needed + char* data = (char*) malloc( size ); + size = ini_save( ini, data, size ); // Actually save the file + ini_destroy( ini ); + + FILE* fp = fopen( "test.ini", "w" ); + fwrite( data, 1, size, fp ); + fclose( fp ); + free( data ); + + return 0; + } + + + +API Documentation +================= + +ini.h is a small library for reading classic .ini files. It is a single-header library, and does not need any .lib files +or other binaries, or any build scripts. To use it, you just include ini.h to get the API declarations. To get the +definitions, you must include ini.h from *one* single C or C++ file, and #define the symbol `INI_IMPLEMENTATION` before +you do. + + +Customization +------------- +There are a few different things in ini.h which are configurable by #defines. The customizations only affect the +implementation, so will only need to be defined in the file where you have the #define INI_IMPLEMENTATION. + +Note that if all customizations are utilized, ini.h will include no external files whatsoever, which might be useful +if you need full control over what code is being built. + + +### Custom memory allocators + +To store the internal data structures, ini.h needs to do dynamic allocation by calling `malloc`. Programs might want to +keep track of allocations done, or use custom defined pools to allocate memory from. ini.h allows for specifying custom +memory allocation functions for `malloc` and `free`. +This is done with the following code: + + #define INI_IMPLEMENTATION + #define INI_MALLOC( ctx, size ) ( my_custom_malloc( ctx, size ) ) + #define INI_FREE( ctx, ptr ) ( my_custom_free( ctx, ptr ) ) + #include "ini.h" + +where `my_custom_malloc` and `my_custom_free` are your own memory allocation/deallocation functions. The `ctx` parameter +is an optional parameter of type `void*`. When `ini_create` or `ini_load` is called, you can pass in a `memctx` +parameter, which can be a pointer to anything you like, and which will be passed through as the `ctx` parameter to every +`INI_MALLOC`/`INI_FREE` call. For example, if you are doing memory tracking, you can pass a pointer to your tracking +data as `memctx`, and in your custom allocation/deallocation function, you can cast the `ctx` param back to the +right type, and access the tracking data. + +If no custom allocator is defined, ini.h will default to `malloc` and `free` from the C runtime library. + + +### Custom C runtime function + +The library makes use of three additional functions from the C runtime library, and for full flexibility, it allows you +to substitute them for your own. Here's an example: + + #define INI_IMPLEMENTATION + #define INI_MEMCPY( dst, src, cnt ) ( my_memcpy_func( dst, src, cnt ) ) + #define INI_STRLEN( s ) ( my_strlen_func( s ) ) + #define INI_STRICMP( s1, s2 ) ( my_stricmp_func( s1, s2 ) ) + #include "ini.h" + +If no custom function is defined, ini.h will default to the C runtime library equivalent. + + +ini_create +---------- + + ini_t* ini_create( void* memctx ) + +Instantiates a new, empty ini structure, which can be manipulated with other API calls, to fill it with data. To save it +out to an ini-file string, use `ini_save`. When no longer needed, it can be destroyed by calling `ini_destroy`. +`memctx` is a pointer to user defined data which will be passed through to the custom INI_MALLOC/INI_FREE calls. It can +be NULL if no user defined data is needed. + + +ini_load +-------- + + ini_t* ini_load( char const* data, void* memctx ) + +Parse the zero-terminated string `data` containing an ini-file, and create a new ini_t instance containing the data. +The instance can be manipulated with other API calls to enumerate sections/properties and retrieve values. When no +longer needed, it can be destroyed by calling `ini_destroy`. `memctx` is a pointer to user defined data which will be +passed through to the custom INI_MALLOC/INI_FREE calls. It can be NULL if no user defined data is needed. + + +ini_save +-------- + + int ini_save( ini_t const* ini, char* data, int size ) + +Saves an ini structure as a zero-terminated ini-file string, into the specified buffer. Returns the number of bytes +written, including the zero terminator. If `data` is NULL, nothing is written, but `ini_save` still returns the number +of bytes it would have written. If the size of `data`, as specified in the `size` parameter, is smaller than that +required, only part of the ini-file string will be written. `ini_save` still returns the number of bytes it would have +written had the buffer been large enough. + + +ini_destroy +----------- + + void ini_destroy( ini_t* ini ) + +Destroy an `ini_t` instance created by calling `ini_load` or `ini_create`, releasing the memory allocated by it. No +further API calls are valid on an `ini_t` instance after calling `ini_destroy` on it. + + +ini_section_count +----------------- + + int ini_section_count( ini_t const* ini ) + +Returns the number of sections in an ini file. There's at least one section in an ini file (the global section), but +there can be many more, each specified in the file by the section name wrapped in square brackets [ ]. + + +ini_section_name +---------------- + + char const* ini_section_name( ini_t const* ini, int section ) + +Returns the name of the section with the specified index. `section` must be non-negative and less than the value +returned by `ini_section_count`, or `ini_section_name` will return NULL. The defined constant `INI_GLOBAL_SECTION` can +be used to indicate the global section. + + +ini_property_count +------------------ + + int ini_property_count( ini_t const* ini, int section ) + +Returns the number of properties belonging to the section with the specified index. `section` must be non-negative and +less than the value returned by `ini_section_count`, or `ini_section_name` will return 0. The defined constant +`INI_GLOBAL_SECTION` can be used to indicate the global section. Properties are declared in the ini-file on he format +`name=value`. + + +ini_property_name +----------------- + + char const* ini_property_name( ini_t const* ini, int section, int property ) + +Returns the name of the property with the specified index `property` in the section with the specified index `section`. +`section` must be non-negative and less than the value returned by `ini_section_count`, and `property` must be +non-negative and less than the value returned by `ini_property_count`, or `ini_property_name` will return NULL. The +defined constant `INI_GLOBAL_SECTION` can be used to indicate the global section. + + +ini_property_value +------------------ + + char const* ini_property_value( ini_t const* ini, int section, int property ) + +Returns the value of the property with the specified index `property` in the section with the specified index `section`. +`section` must be non-negative and less than the value returned by `ini_section_count`, and `property` must be +non-negative and less than the value returned by `ini_property_count`, or `ini_property_value` will return NULL. The +defined constant `INI_GLOBAL_SECTION` can be used to indicate the global section. + + +ini_find_section +---------------- + + int ini_find_section( ini_t const* ini, char const* name, int name_length ) + +Finds the section with the specified name, and returns its index. `name_length` specifies the number of characters in +`name`, which does not have to be zero-terminated. If `name_length` is zero, the length is determined automatically, but +in this case `name` has to be zero-terminated. If no section with the specified name could be found, the value +`INI_NOT_FOUND` is returned. + + +ini_find_property +----------------- + + int ini_find_property( ini_t const* ini, int section, char const* name, int name_length ) + +Finds the property with the specified name, within the section with the specified index, and returns the index of the +property. `name_length` specifies the number of characters in `name`, which does not have to be zero-terminated. If +`name_length` is zero, the length is determined automatically, but in this case `name` has to be zero-terminated. If no +property with the specified name could be found within the specified section, the value `INI_NOT_FOUND` is returned. +`section` must be non-negative and less than the value returned by `ini_section_count`, or `ini_find_property` will +return `INI_NOT_FOUND`. The defined constant `INI_GLOBAL_SECTION` can be used to indicate the global section. + + +ini_section_add +--------------- + + int ini_section_add( ini_t* ini, char const* name, int length ) + +Adds a section with the specified name, and returns the index it was added at. There is no check done to see if a +section with the specified name already exists - multiple sections of the same name are allowed. `length` specifies the +number of characters in `name`, which does not have to be zero-terminated. If `length` is zero, the length is determined +automatically, but in this case `name` has to be zero-terminated. + + +ini_property_add +---------------- + + void ini_property_add( ini_t* ini, int section, char const* name, int name_length, char const* value, int value_length ) + +Adds a property with the specified name and value to the specified section, and returns the index it was added at. There +is no check done to see if a property with the specified name already exists - multiple properties of the same name are +allowed. `name_length` and `value_length` specifies the number of characters in `name` and `value`, which does not have +to be zero-terminated. If `name_length` or `value_length` is zero, the length is determined automatically, but in this +case `name`/`value` has to be zero-terminated. `section` must be non-negative and less than the value returned by +`ini_section_count`, or the property will not be added. The defined constant `INI_GLOBAL_SECTION` can be used to +indicate the global section. + + +ini_section_remove +------------------ + + void ini_section_remove( ini_t* ini, int section ) + +Removes the section with the specified index, and all properties within it. `section` must be non-negative and less than +the value returned by `ini_section_count`. The defined constant `INI_GLOBAL_SECTION` can be used to indicate the global +section. Note that removing a section will shuffle section indices, so that section indices you may have stored will no +longer indicate the same section as it did before the remove. Use the find functions to update your indices. + + +ini_property_remove +------------------- + + void ini_property_remove( ini_t* ini, int section, int property ) + +Removes the property with the specified index from the specified section. `section` must be non-negative and less than +the value returned by `ini_section_count`, and `property` must be non-negative and less than the value returned by +`ini_property_count`. The defined constant `INI_GLOBAL_SECTION` can be used to indicate the global section. Note that +removing a property will shuffle property indices within the specified section, so that property indices you may have +stored will no longer indicate the same property as it did before the remove. Use the find functions to update your +indices. + + +ini_section_name_set +-------------------- + + void ini_section_name_set( ini_t* ini, int section, char const* name, int length ) + +Change the name of the section with the specified index. `section` must be non-negative and less than the value returned +by `ini_section_count`. The defined constant `INI_GLOBAL_SECTION` can be used to indicate the global section. `length` +specifies the number of characters in `name`, which does not have to be zero-terminated. If `length` is zero, the length +is determined automatically, but in this case `name` has to be zero-terminated. + + +ini_property_name_set +--------------------- + + void ini_property_name_set( ini_t* ini, int section, int property, char const* name, int length ) + +Change the name of the property with the specified index in the specified section. `section` must be non-negative and +less than the value returned by `ini_section_count`, and `property` must be non-negative and less than the value +returned by `ini_property_count`. The defined constant `INI_GLOBAL_SECTION` can be used to indicate the global section. +`length` specifies the number of characters in `name`, which does not have to be zero-terminated. If `length` is zero, +the length is determined automatically, but in this case `name` has to be zero-terminated. + + +ini_property_value_set +---------------------- + + void ini_property_value_set( ini_t* ini, int section, int property, char const* value, int length ) + +Change the value of the property with the specified index in the specified section. `section` must be non-negative and +less than the value returned by `ini_section_count`, and `property` must be non-negative and less than the value +returned by `ini_property_count`. The defined constant `INI_GLOBAL_SECTION` can be used to indicate the global section. +`length` specifies the number of characters in `value`, which does not have to be zero-terminated. If `length` is zero, +the length is determined automatically, but in this case `value` has to be zero-terminated. + +**/ + + +/* +---------------------- + IMPLEMENTATION +---------------------- +*/ + +#ifdef INI_IMPLEMENTATION +#undef INI_IMPLEMENTATION + +#define INITIAL_CAPACITY ( 256 ) + +#define _CRT_NONSTDC_NO_DEPRECATE +#define _CRT_SECURE_NO_WARNINGS +#include + +#ifndef INI_MALLOC + #define _CRT_NONSTDC_NO_DEPRECATE + #define _CRT_SECURE_NO_WARNINGS + #include + #define INI_MALLOC( ctx, size ) ( malloc( size ) ) + #define INI_FREE( ctx, ptr ) ( free( ptr ) ) +#endif + +#ifndef INI_MEMCPY + #define _CRT_NONSTDC_NO_DEPRECATE + #define _CRT_SECURE_NO_WARNINGS + #include + #define INI_MEMCPY( dst, src, cnt ) ( memcpy( dst, src, cnt ) ) +#endif + +#ifndef INI_STRLEN + #define _CRT_NONSTDC_NO_DEPRECATE + #define _CRT_SECURE_NO_WARNINGS + #include + #define INI_STRLEN( s ) ( strlen( s ) ) +#endif + +#ifndef INI_STRICMP + #ifdef _WIN32 + #define _CRT_NONSTDC_NO_DEPRECATE + #define _CRT_SECURE_NO_WARNINGS + #include + #define INI_STRICMP( s1, s2 ) ( stricmp( s1, s2 ) ) + #else + #include + #define INI_STRICMP( s1, s2 ) ( strcasecmp( s1, s2 ) ) + #endif +#endif + + +struct ini_internal_section_t + { + char name[ 32 ]; + char* name_large; + }; + + +struct ini_internal_property_t + { + int section; + char name[ 32 ]; + char* name_large; + char value[ 64 ]; + char* value_large; + }; + + +struct ini_t + { + struct ini_internal_section_t* sections; + int section_capacity; + int section_count; + + struct ini_internal_property_t* properties; + int property_capacity; + int property_count; + + void* memctx; + }; + + +static int ini_internal_property_index( ini_t const* ini, int section, int property ) + { + int i; + int p; + + if( ini && section >= 0 && section < ini->section_count ) + { + p = 0; + for( i = 0; i < ini->property_count; ++i ) + { + if( ini->properties[ i ].section == section ) + { + if( p == property ) return i; + ++p; + } + } + } + + return INI_NOT_FOUND; + } + + +ini_t* ini_create( void* memctx ) + { + ini_t* ini; + + ini = (ini_t*) INI_MALLOC( memctx, sizeof( ini_t ) ); + ini->memctx = memctx; + ini->sections = (struct ini_internal_section_t*) INI_MALLOC( ini->memctx, INITIAL_CAPACITY * sizeof( ini->sections[ 0 ] ) ); + ini->section_capacity = INITIAL_CAPACITY; + ini->section_count = 1; /* global section */ + ini->sections[ 0 ].name[ 0 ] = '\0'; + ini->sections[ 0 ].name_large = 0; + ini->properties = (struct ini_internal_property_t*) INI_MALLOC( ini->memctx, INITIAL_CAPACITY * sizeof( ini->properties[ 0 ] ) ); + ini->property_capacity = INITIAL_CAPACITY; + ini->property_count = 0; + return ini; + } + + +ini_t* ini_load( char const* data, void* memctx ) + { + ini_t* ini; + char const* ptr; + int s; + char const* start; + char const* start2; + int l; + + ini = ini_create( memctx ); + + ptr = data; + if( ptr ) + { + s = 0; + while( *ptr ) + { + /* trim leading whitespace */ + while( *ptr && *ptr <=' ' ) + ++ptr; + + /* done? */ + if( !*ptr ) break; + + /* comment */ + else if( *ptr == ';' ) + { + while( *ptr && *ptr !='\n' ) + ++ptr; + } + /* section */ + else if( *ptr == '[' ) + { + ++ptr; + start = ptr; + while( *ptr && *ptr !=']' && *ptr != '\n' ) + ++ptr; + + if( *ptr == ']' ) + { + s = ini_section_add( ini, start, (int)( ptr - start) ); + ++ptr; + } + } + /* property */ + else + { + start = ptr; + while( *ptr && *ptr !='=' && *ptr != '\n' ) + ++ptr; + + if( *ptr == '=' ) + { + l = (int)( ptr - start); + ++ptr; + while( *ptr && *ptr <= ' ' && *ptr != '\n' ) + ptr++; + start2 = ptr; + while( *ptr && *ptr != '\n' ) + ++ptr; + while( *(--ptr) <= ' ' ) + (void)ptr; + ptr++; + ini_property_add( ini, s, start, l, start2, (int)( ptr - start2) ); + } + } + } + } + + return ini; + } + + +int ini_save( ini_t const* ini, char* data, int size ) + { + int s; + int p; + int i; + int l; + char* n; + int pos; + + if( ini ) + { + pos = 0; + for( s = 0; s < ini->section_count; ++s ) + { + n = ini->sections[ s ].name_large ? ini->sections[ s ].name_large : ini->sections[ s ].name; + l = (int) INI_STRLEN( n ); + if( l > 0 ) + { + if( data && pos < size ) data[ pos ] = '['; + ++pos; + for( i = 0; i < l; ++i ) + { + if( data && pos < size ) data[ pos ] = n[ i ]; + ++pos; + } + if( data && pos < size ) data[ pos ] = ']'; + ++pos; + if( data && pos < size ) data[ pos ] = '\n'; + ++pos; + } + + for( p = 0; p < ini->property_count; ++p ) + { + if( ini->properties[ p ].section == s ) + { + n = ini->properties[ p ].name_large ? ini->properties[ p ].name_large : ini->properties[ p ].name; + l = (int) INI_STRLEN( n ); + for( i = 0; i < l; ++i ) + { + if( data && pos < size ) data[ pos ] = n[ i ]; + ++pos; + } + if( data && pos < size ) data[ pos ] = '='; + ++pos; + n = ini->properties[ p ].value_large ? ini->properties[ p ].value_large : ini->properties[ p ].value; + l = (int) INI_STRLEN( n ); + for( i = 0; i < l; ++i ) + { + if( data && pos < size ) data[ pos ] = n[ i ]; + ++pos; + } + if( data && pos < size ) data[ pos ] = '\n'; + ++pos; + } + } + + if( pos > 0 ) + { + if( data && pos < size ) data[ pos ] = '\n'; + ++pos; + } + } + + if( data && pos < size ) data[ pos ] = '\0'; + ++pos; + + return pos; + } + + return 0; + } + + +void ini_destroy( ini_t* ini ) + { + int i; + + if( ini ) + { + for( i = 0; i < ini->property_count; ++i ) + { + if( ini->properties[ i ].value_large ) INI_FREE( ini->memctx, ini->properties[ i ].value_large ); + if( ini->properties[ i ].name_large ) INI_FREE( ini->memctx, ini->properties[ i ].name_large ); + } + for( i = 0; i < ini->section_count; ++i ) + if( ini->sections[ i ].name_large ) INI_FREE( ini->memctx, ini->sections[ i ].name_large ); + INI_FREE( ini->memctx, ini->properties ); + INI_FREE( ini->memctx, ini->sections ); + INI_FREE( ini->memctx, ini ); + } + } + + +int ini_section_count( ini_t const* ini ) + { + if( ini ) return ini->section_count; + return 0; + } + + +char const* ini_section_name( ini_t const* ini, int section ) + { + if( ini && section >= 0 && section < ini->section_count ) + return ini->sections[ section ].name_large ? ini->sections[ section ].name_large : ini->sections[ section ].name; + + return NULL; + } + + +int ini_property_count( ini_t const* ini, int section ) + { + int i; + int count; + + if( ini ) + { + count = 0; + for( i = 0; i < ini->property_count; ++i ) + { + if( ini->properties[ i ].section == section ) ++count; + } + return count; + } + + return 0; + } + + +char const* ini_property_name( ini_t const* ini, int section, int property ) + { + int p; + + if( ini && section >= 0 && section < ini->section_count ) + { + p = ini_internal_property_index( ini, section, property ); + if( p != INI_NOT_FOUND ) + return ini->properties[ p ].name_large ? ini->properties[ p ].name_large : ini->properties[ p ].name; + } + + return NULL; + } + + +char const* ini_property_value( ini_t const* ini, int section, int property ) + { + int p; + + if( ini && section >= 0 && section < ini->section_count ) + { + p = ini_internal_property_index( ini, section, property ); + if( p != INI_NOT_FOUND ) + return ini->properties[ p ].value_large ? ini->properties[ p ].value_large : ini->properties[ p ].value; + } + + return NULL; + } + + +int ini_find_section( ini_t const* ini, char const* name, int name_length ) + { + int i; + + if( ini && name ) + { + if( name_length <= 0 ) name_length = (int) INI_STRLEN( name ); + for( i = 0; i < ini->section_count; ++i ) + { + char const* const other = + ini->sections[ i ].name_large ? ini->sections[ i ].name_large : ini->sections[ i ].name; + if( (int) INI_STRLEN( other ) == name_length && INI_STRICMP( name, other, name_length) == 0 ) + return i; + } + } + + return INI_NOT_FOUND; + } + + +int ini_find_property( ini_t const* ini, int section, char const* name, int name_length ) + { + int i; + int c; + + if( ini && name && section >= 0 && section < ini->section_count) + { + if( name_length <= 0 ) name_length = (int) INI_STRLEN( name ); + c = 0; + for( i = 0; i < ini->property_capacity; ++i ) + { + if( ini->properties[ i ].section == section ) + { + char const* const other = + ini->properties[ i ].name_large ? ini->properties[ i ].name_large : ini->properties[ i ].name; + if( (int) INI_STRLEN( other ) == name_length && INI_STRICMP( name, other, name_length) == 0 ) + return c; + ++c; + } + } + } + + return INI_NOT_FOUND; + } + + +int ini_section_add( ini_t* ini, char const* name, int length ) + { + struct ini_internal_section_t* new_sections; + + if( ini && name ) + { + if( length <= 0 ) length = (int) INI_STRLEN( name ); + if( ini->section_count >= ini->section_capacity ) + { + ini->section_capacity *= 2; + new_sections = (struct ini_internal_section_t*) INI_MALLOC( ini->memctx, + ini->section_capacity * sizeof( ini->sections[ 0 ] ) ); + INI_MEMCPY( new_sections, ini->sections, ini->section_count * sizeof( ini->sections[ 0 ] ) ); + INI_FREE( ini->memctx, ini->sections ); + ini->sections = new_sections; + } + + ini->sections[ ini->section_count ].name_large = 0; + if( length + 1 >= sizeof( ini->sections[ 0 ].name ) ) + { + ini->sections[ ini->section_count ].name_large = (char*) INI_MALLOC( ini->memctx, (size_t) length + 1 ); + INI_MEMCPY( ini->sections[ ini->section_count ].name_large, name, (size_t) length ); + ini->sections[ ini->section_count ].name_large[ length ] = '\0'; + } + else + { + INI_MEMCPY( ini->sections[ ini->section_count ].name, name, (size_t) length ); + ini->sections[ ini->section_count ].name[ length ] = '\0'; + } + + return ini->section_count++; + } + return INI_NOT_FOUND; + } + + +void ini_property_add( ini_t* ini, int section, char const* name, int name_length, char const* value, int value_length ) + { + struct ini_internal_property_t* new_properties; + + if( ini && name && section >= 0 && section < ini->section_count ) + { + if( name_length <= 0 ) name_length = (int) INI_STRLEN( name ); + if( value_length <= 0 ) value_length = (int) INI_STRLEN( value ); + + if( ini->property_count >= ini->property_capacity ) + { + + ini->property_capacity *= 2; + new_properties = (struct ini_internal_property_t*) INI_MALLOC( ini->memctx, + ini->property_capacity * sizeof( ini->properties[ 0 ] ) ); + INI_MEMCPY( new_properties, ini->properties, ini->property_count * sizeof( ini->properties[ 0 ] ) ); + INI_FREE( ini->memctx, ini->properties ); + ini->properties = new_properties; + } + + ini->properties[ ini->property_count ].section = section; + ini->properties[ ini->property_count ].name_large = 0; + ini->properties[ ini->property_count ].value_large = 0; + + if( name_length + 1 >= sizeof( ini->properties[ 0 ].name ) ) + { + ini->properties[ ini->property_count ].name_large = (char*) INI_MALLOC( ini->memctx, (size_t) name_length + 1 ); + INI_MEMCPY( ini->properties[ ini->property_count ].name_large, name, (size_t) name_length ); + ini->properties[ ini->property_count ].name_large[ name_length ] = '\0'; + } + else + { + INI_MEMCPY( ini->properties[ ini->property_count ].name, name, (size_t) name_length ); + ini->properties[ ini->property_count ].name[ name_length ] = '\0'; + } + + if( value_length + 1 >= sizeof( ini->properties[ 0 ].value ) ) + { + ini->properties[ ini->property_count ].value_large = (char*) INI_MALLOC( ini->memctx, (size_t) value_length + 1 ); + INI_MEMCPY( ini->properties[ ini->property_count ].value_large, value, (size_t) value_length ); + ini->properties[ ini->property_count ].value_large[ value_length ] = '\0'; + } + else + { + INI_MEMCPY( ini->properties[ ini->property_count ].value, value, (size_t) value_length ); + ini->properties[ ini->property_count ].value[ value_length ] = '\0'; + } + + ++ini->property_count; + } + } + + +void ini_section_remove( ini_t* ini, int section ) + { + int p; + + if( ini && section >= 0 && section < ini->section_count ) + { + if( ini->sections[ section ].name_large ) INI_FREE( ini->memctx, ini->sections[ section ].name_large ); + for( p = ini->property_count - 1; p >= 0; --p ) + { + if( ini->properties[ p ].section == section ) + { + if( ini->properties[ p ].value_large ) INI_FREE( ini->memctx, ini->properties[ p ].value_large ); + if( ini->properties[ p ].name_large ) INI_FREE( ini->memctx, ini->properties[ p ].name_large ); + ini->properties[ p ] = ini->properties[ --ini->property_count ]; + } + } + + ini->sections[ section ] = ini->sections[ --ini->section_count ]; + + for( p = 0; p < ini->property_count; ++p ) + { + if( ini->properties[ p ].section == ini->section_count ) + ini->properties[ p ].section = section; + } + } + } + + +void ini_property_remove( ini_t* ini, int section, int property ) + { + int p; + + if( ini && section >= 0 && section < ini->section_count ) + { + p = ini_internal_property_index( ini, section, property ); + if( p != INI_NOT_FOUND ) + { + if( ini->properties[ p ].value_large ) INI_FREE( ini->memctx, ini->properties[ p ].value_large ); + if( ini->properties[ p ].name_large ) INI_FREE( ini->memctx, ini->properties[ p ].name_large ); + ini->properties[ p ] = ini->properties[ --ini->property_count ]; + return; + } + } + } + + +void ini_section_name_set( ini_t* ini, int section, char const* name, int length ) + { + if( ini && name && section >= 0 && section < ini->section_count ) + { + if( length <= 0 ) length = (int) INI_STRLEN( name ); + if( ini->sections[ section ].name_large ) INI_FREE( ini->memctx, ini->sections[ section ].name_large ); + ini->sections[ section ].name_large = 0; + + if( length + 1 >= sizeof( ini->sections[ 0 ].name ) ) + { + ini->sections[ section ].name_large = (char*) INI_MALLOC( ini->memctx, (size_t) length + 1 ); + INI_MEMCPY( ini->sections[ section ].name_large, name, (size_t) length ); + ini->sections[ section ].name_large[ length ] = '\0'; + } + else + { + INI_MEMCPY( ini->sections[ section ].name, name, (size_t) length ); + ini->sections[ section ].name[ length ] = '\0'; + } + } + } + + +void ini_property_name_set( ini_t* ini, int section, int property, char const* name, int length ) + { + int p; + + if( ini && name && section >= 0 && section < ini->section_count ) + { + if( length <= 0 ) length = (int) INI_STRLEN( name ); + p = ini_internal_property_index( ini, section, property ); + if( p != INI_NOT_FOUND ) + { + if( ini->properties[ p ].name_large ) INI_FREE( ini->memctx, ini->properties[ p ].name_large ); + ini->properties[ ini->property_count ].name_large = 0; + + if( length + 1 >= sizeof( ini->properties[ 0 ].name ) ) + { + ini->properties[ p ].name_large = (char*) INI_MALLOC( ini->memctx, (size_t) length + 1 ); + INI_MEMCPY( ini->properties[ p ].name_large, name, (size_t) length ); + ini->properties[ p ].name_large[ length ] = '\0'; + } + else + { + INI_MEMCPY( ini->properties[ p ].name, name, (size_t) length ); + ini->properties[ p ].name[ length ] = '\0'; + } + } + } + } + + +void ini_property_value_set( ini_t* ini, int section, int property, char const* value, int length ) + { + int p; + + if( ini && value && section >= 0 && section < ini->section_count ) + { + if( length <= 0 ) length = (int) INI_STRLEN( value ); + p = ini_internal_property_index( ini, section, property ); + if( p != INI_NOT_FOUND ) + { + if( ini->properties[ p ].value_large ) INI_FREE( ini->memctx, ini->properties[ p ].value_large ); + ini->properties[ ini->property_count ].value_large = 0; + + if( length + 1 >= sizeof( ini->properties[ 0 ].value ) ) + { + ini->properties[ p ].value_large = (char*) INI_MALLOC( ini->memctx, (size_t) length + 1 ); + INI_MEMCPY( ini->properties[ p ].name_large, value, (size_t) length ); + ini->properties[ p ].value_large[ length ] = '\0'; + } + else + { + INI_MEMCPY( ini->properties[ p ].value, value, (size_t) length ); + ini->properties[ p ].name[ length ] = '\0'; + } + } + } + } + + +#endif /* INI_IMPLEMENTATION */ + +/* +revision history: + 1.1 customization, added documentation, cleanup + 1.0 first publicly released version +*/ + +/* +------------------------------------------------------------------------------ + +This software is available under 2 licenses - you may choose the one you like. + +------------------------------------------------------------------------------ + +ALTERNATIVE A - MIT License + +Copyright (c) 2015 Mattias Gustavsson + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------------------ + +ALTERNATIVE B - Public Domain (www.unlicense.org) + +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. + +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +------------------------------------------------------------------------------ +*/ diff --git a/3rdparty/ini/ini.md b/3rdparty/ini/ini.md new file mode 100644 index 0000000..0b204c0 --- /dev/null +++ b/3rdparty/ini/ini.md @@ -0,0 +1,333 @@ +ini.h +===== + +Library: [ini.h](../ini.h) + + +Examples +======== + +Loading an ini file and retrieving values +----------------------------------------- + +```cpp +#define INI_IMPLEMENTATION +#include "ini.h" + +#include +#include + +int main() + { + FILE* fp = fopen( "test.ini", "r" ); + fseek( fp, 0, SEEK_END ); + int size = ftell( fp ); + fseek( fp, 0, SEEK_SET ); + char* data = (char*) malloc( size + 1 ); + fread( data, 1, size, fp ); + data[ size ] = '\0'; + fclose( fp ); + + ini_t* ini = ini_load( data ); + free( data ); + int second_index = ini_find_property( ini, INI_GLOBAL_SECTION, "SecondSetting" ); + char const* second = ini_property_value( ini, INI_GLOBAL_SECTION, second_index ); + printf( "%s=%s\n", "SecondSetting", second ); + int section = ini_find_section( ini, "MySection" ); + int third_index = ini_find_property( ini, section, "ThirdSetting" ); + char const* third = ini_property_value( ini, section, third_index ); + printf( "%s=%s\n", "ThirdSetting", third ); + ini_destroy( ini ); + + return 0; + } +``` + +Creating a new ini file +----------------------- + +```cpp +#define INI_IMPLEMENTATION +#include "ini.h" + +#include +#include + +int main() + { + ini_t* ini = ini_create(); + ini_property_add( ini, INI_GLOBAL_SECTION, "FirstSetting", "Test" ); + ini_property_add( ini, INI_GLOBAL_SECTION, "SecondSetting", "2" ); + int section = ini_section_add( ini, "MySection" ); + ini_property_add( ini, section, "ThirdSetting", "Three" ); + + int size = ini_save( ini, NULL, 0 ); // Find the size needed + char* data = (char*) malloc( size ); + size = ini_save( ini, data, size ); // Actually save the file + ini_destroy( ini ); + + FILE* fp = fopen( "test.ini", "w" ); + fwrite( data, 1, size, fp ); + fclose( fp ); + free( data ); + + return 0; + } +``` + + +API Documentation +================= + +ini.h is a small library for reading classic .ini files. It is a single-header library, and does not need any .lib files +or other binaries, or any build scripts. To use it, you just include ini.h to get the API declarations. To get the +definitions, you must include ini.h from *one* single C or C++ file, and #define the symbol `INI_IMPLEMENTATION` before +you do. + + +Customization +------------- +There are a few different things in ini.h which are configurable by #defines. The customizations only affect the +implementation, so will only need to be defined in the file where you have the #define INI_IMPLEMENTATION. + +Note that if all customizations are utilized, ini.h will include no external files whatsoever, which might be useful +if you need full control over what code is being built. + + +### Custom memory allocators + +To store the internal data structures, ini.h needs to do dynamic allocation by calling `malloc`. Programs might want to +keep track of allocations done, or use custom defined pools to allocate memory from. ini.h allows for specifying custom +memory allocation functions for `malloc` and `free`. +This is done with the following code: + + #define INI_IMPLEMENTATION + #define INI_MALLOC( ctx, size ) ( my_custom_malloc( ctx, size ) ) + #define INI_FREE( ctx, ptr ) ( my_custom_free( ctx, ptr ) ) + #include "ini.h" + +where `my_custom_malloc` and `my_custom_free` are your own memory allocation/deallocation functions. The `ctx` parameter +is an optional parameter of type `void*`. When `ini_create` or `ini_load` is called, you can pass in a `memctx` +parameter, which can be a pointer to anything you like, and which will be passed through as the `ctx` parameter to every +`INI_MALLOC`/`INI_FREE` call. For example, if you are doing memory tracking, you can pass a pointer to your tracking +data as `memctx`, and in your custom allocation/deallocation function, you can cast the `ctx` param back to the +right type, and access the tracking data. + +If no custom allocator is defined, ini.h will default to `malloc` and `free` from the C runtime library. + + +### Custom C runtime function + +The library makes use of three additional functions from the C runtime library, and for full flexibility, it allows you +to substitute them for your own. Here's an example: + + #define INI_IMPLEMENTATION + #define INI_MEMCPY( dst, src, cnt ) ( my_memcpy_func( dst, src, cnt ) ) + #define INI_STRLEN( s ) ( my_strlen_func( s ) ) + #define INI_STRICMP( s1, s2 ) ( my_stricmp_func( s1, s2 ) ) + #include "ini.h" + +If no custom function is defined, ini.h will default to the C runtime library equivalent. + + +ini_create +---------- + + ini_t* ini_create( void* memctx ) + +Instantiates a new, empty ini structure, which can be manipulated with other API calls, to fill it with data. To save it +out to an ini-file string, use `ini_save`. When no longer needed, it can be destroyed by calling `ini_destroy`. +`memctx` is a pointer to user defined data which will be passed through to the custom INI_MALLOC/INI_FREE calls. It can +be NULL if no user defined data is needed. + + +ini_load +-------- + + ini_t* ini_load( char const* data, void* memctx ) + +Parse the zero-terminated string `data` containing an ini-file, and create a new ini_t instance containing the data. +The instance can be manipulated with other API calls to enumerate sections/properties and retrieve values. When no +longer needed, it can be destroyed by calling `ini_destroy`. `memctx` is a pointer to user defined data which will be +passed through to the custom INI_MALLOC/INI_FREE calls. It can be NULL if no user defined data is needed. + + +ini_save +-------- + + int ini_save( ini_t const* ini, char* data, int size ) + +Saves an ini structure as a zero-terminated ini-file string, into the specified buffer. Returns the number of bytes +written, including the zero terminator. If `data` is NULL, nothing is written, but `ini_save` still returns the number +of bytes it would have written. If the size of `data`, as specified in the `size` parameter, is smaller than that +required, only part of the ini-file string will be written. `ini_save` still returns the number of bytes it would have +written had the buffer been large enough. + + +ini_destroy +----------- + + void ini_destroy( ini_t* ini ) + +Destroy an `ini_t` instance created by calling `ini_load` or `ini_create`, releasing the memory allocated by it. No +further API calls are valid on an `ini_t` instance after calling `ini_destroy` on it. + + +ini_section_count +----------------- + + int ini_section_count( ini_t const* ini ) + +Returns the number of sections in an ini file. There's at least one section in an ini file (the global section), but +there can be many more, each specified in the file by the section name wrapped in square brackets [ ]. + + +ini_section_name +---------------- + + char const* ini_section_name( ini_t const* ini, int section ) + +Returns the name of the section with the specified index. `section` must be non-negative and less than the value +returned by `ini_section_count`, or `ini_section_name` will return NULL. The defined constant `INI_GLOBAL_SECTION` can +be used to indicate the global section. + + +ini_property_count +------------------ + + int ini_property_count( ini_t const* ini, int section ) + +Returns the number of properties belonging to the section with the specified index. `section` must be non-negative and +less than the value returned by `ini_section_count`, or `ini_section_name` will return 0. The defined constant +`INI_GLOBAL_SECTION` can be used to indicate the global section. Properties are declared in the ini-file on he format +`name=value`. + + +ini_property_name +----------------- + + char const* ini_property_name( ini_t const* ini, int section, int property ) + +Returns the name of the property with the specified index `property` in the section with the specified index `section`. +`section` must be non-negative and less than the value returned by `ini_section_count`, and `property` must be +non-negative and less than the value returned by `ini_property_count`, or `ini_property_name` will return NULL. The +defined constant `INI_GLOBAL_SECTION` can be used to indicate the global section. + + +ini_property_value +------------------ + + char const* ini_property_value( ini_t const* ini, int section, int property ) + +Returns the value of the property with the specified index `property` in the section with the specified index `section`. +`section` must be non-negative and less than the value returned by `ini_section_count`, and `property` must be +non-negative and less than the value returned by `ini_property_count`, or `ini_property_value` will return NULL. The +defined constant `INI_GLOBAL_SECTION` can be used to indicate the global section. + + +ini_find_section +---------------- + + int ini_find_section( ini_t const* ini, char const* name, int name_length ) + +Finds the section with the specified name, and returns its index. `name_length` specifies the number of characters in +`name`, which does not have to be zero-terminated. If `name_length` is zero, the length is determined automatically, but +in this case `name` has to be zero-terminated. If no section with the specified name could be found, the value +`INI_NOT_FOUND` is returned. + + +ini_find_property +----------------- + + int ini_find_property( ini_t const* ini, int section, char const* name, int name_length ) + +Finds the property with the specified name, within the section with the specified index, and returns the index of the +property. `name_length` specifies the number of characters in `name`, which does not have to be zero-terminated. If +`name_length` is zero, the length is determined automatically, but in this case `name` has to be zero-terminated. If no +property with the specified name could be found within the specified section, the value `INI_NOT_FOUND` is returned. +`section` must be non-negative and less than the value returned by `ini_section_count`, or `ini_find_property` will +return `INI_NOT_FOUND`. The defined constant `INI_GLOBAL_SECTION` can be used to indicate the global section. + + +ini_section_add +--------------- + + int ini_section_add( ini_t* ini, char const* name, int length ) + +Adds a section with the specified name, and returns the index it was added at. There is no check done to see if a +section with the specified name already exists - multiple sections of the same name are allowed. `length` specifies the +number of characters in `name`, which does not have to be zero-terminated. If `length` is zero, the length is determined +automatically, but in this case `name` has to be zero-terminated. + + +ini_property_add +---------------- + + void ini_property_add( ini_t* ini, int section, char const* name, int name_length, char const* value, int value_length ) + +Adds a property with the specified name and value to the specified section, and returns the index it was added at. There +is no check done to see if a property with the specified name already exists - multiple properties of the same name are +allowed. `name_length` and `value_length` specifies the number of characters in `name` and `value`, which does not have +to be zero-terminated. If `name_length` or `value_length` is zero, the length is determined automatically, but in this +case `name`/`value` has to be zero-terminated. `section` must be non-negative and less than the value returned by +`ini_section_count`, or the property will not be added. The defined constant `INI_GLOBAL_SECTION` can be used to +indicate the global section. + + +ini_section_remove +------------------ + + void ini_section_remove( ini_t* ini, int section ) + +Removes the section with the specified index, and all properties within it. `section` must be non-negative and less than +the value returned by `ini_section_count`. The defined constant `INI_GLOBAL_SECTION` can be used to indicate the global +section. Note that removing a section will shuffle section indices, so that section indices you may have stored will no +longer indicate the same section as it did before the remove. Use the find functions to update your indices. + + +ini_property_remove +------------------- + + void ini_property_remove( ini_t* ini, int section, int property ) + +Removes the property with the specified index from the specified section. `section` must be non-negative and less than +the value returned by `ini_section_count`, and `property` must be non-negative and less than the value returned by +`ini_property_count`. The defined constant `INI_GLOBAL_SECTION` can be used to indicate the global section. Note that +removing a property will shuffle property indices within the specified section, so that property indices you may have +stored will no longer indicate the same property as it did before the remove. Use the find functions to update your +indices. + + +ini_section_name_set +-------------------- + + void ini_section_name_set( ini_t* ini, int section, char const* name, int length ) + +Change the name of the section with the specified index. `section` must be non-negative and less than the value returned +by `ini_section_count`. The defined constant `INI_GLOBAL_SECTION` can be used to indicate the global section. `length` +specifies the number of characters in `name`, which does not have to be zero-terminated. If `length` is zero, the length +is determined automatically, but in this case `name` has to be zero-terminated. + + +ini_property_name_set +--------------------- + + void ini_property_name_set( ini_t* ini, int section, int property, char const* name, int length ) + +Change the name of the property with the specified index in the specified section. `section` must be non-negative and +less than the value returned by `ini_section_count`, and `property` must be non-negative and less than the value +returned by `ini_property_count`. The defined constant `INI_GLOBAL_SECTION` can be used to indicate the global section. +`length` specifies the number of characters in `name`, which does not have to be zero-terminated. If `length` is zero, +the length is determined automatically, but in this case `name` has to be zero-terminated. + + +ini_property_value_set +---------------------- + + void ini_property_value_set( ini_t* ini, int section, int property, char const* value, int length ) + +Change the value of the property with the specified index in the specified section. `section` must be non-negative and +less than the value returned by `ini_section_count`, and `property` must be non-negative and less than the value +returned by `ini_property_count`. The defined constant `INI_GLOBAL_SECTION` can be used to indicate the global section. +`length` specifies the number of characters in `value`, which does not have to be zero-terminated. If `length` is zero, +the length is determined automatically, but in this case `value` has to be zero-terminated. diff --git a/include/bx/inline/readerwriter.inl b/include/bx/inline/readerwriter.inl index 8a0dc15..dcdd812 100644 --- a/include/bx/inline/readerwriter.inl +++ b/include/bx/inline/readerwriter.inl @@ -200,10 +200,10 @@ namespace bx inline MemoryWriter::MemoryWriter(MemoryBlockI* _memBlock) : m_memBlock(_memBlock) - , m_data(NULL) - , m_pos(0) - , m_top(0) - , m_size(0) + , m_data(NULL) + , m_pos(0) + , m_top(0) + , m_size(0) { } @@ -391,11 +391,19 @@ namespace bx inline int64_t getSize(SeekerI* _seeker) { int64_t offset = _seeker->seek(); - int64_t size = _seeker->seek(0, Whence::End); + int64_t size = _seeker->seek(0, Whence::End); _seeker->seek(offset, Whence::Begin); return size; } + inline int64_t getRemain(SeekerI* _seeker) + { + int64_t offset = _seeker->seek(); + int64_t size = _seeker->seek(0, Whence::End); + _seeker->seek(offset, Whence::Begin); + return size-offset; + } + inline int32_t peek(ReaderSeekerI* _reader, void* _data, int32_t _size, Error* _err) { BX_ERROR_SCOPE(_err); diff --git a/include/bx/pixelformat.h b/include/bx/pixelformat.h index a79cf3c..ee2dd15 100644 --- a/include/bx/pixelformat.h +++ b/include/bx/pixelformat.h @@ -11,9 +11,10 @@ namespace bx { + /// struct EncodingType { - enum Enum + enum Enum /// { Unorm, Int, diff --git a/include/bx/readerwriter.h b/include/bx/readerwriter.h index f5a3248..68c8227 100644 --- a/include/bx/readerwriter.h +++ b/include/bx/readerwriter.h @@ -150,7 +150,7 @@ namespace bx uint32_t m_size; }; - /// + /// Sizer writer. Dummy writter that only counts number of bytes written into it. class SizerWriter : public WriterSeekerI { public: @@ -226,7 +226,7 @@ namespace bx int64_t m_size; }; - /// + /// Static (fixed size) memory block writer. class StaticMemoryBlockWriter : public MemoryWriter { public: @@ -288,6 +288,9 @@ namespace bx /// Returns size of file. int64_t getSize(SeekerI* _seeker); + /// Returns remaining size from current offset of file. + int64_t getRemain(SeekerI* _seeker); + /// Peek data. int32_t peek(ReaderSeekerI* _reader, void* _data, int32_t _size, Error* _err = NULL); @@ -301,16 +304,16 @@ namespace bx /// Align writer stream (pads stream with zeros). int32_t align(WriterSeekerI* _writer, uint32_t _alignment, Error* _err = NULL); - /// + /// Open for read. bool open(ReaderOpenI* _reader, const FilePath& _filePath, Error* _err = NULL); - /// + /// Open fro write. bool open(WriterOpenI* _writer, const FilePath& _filePath, bool _append = false, Error* _err = NULL); - /// + /// Open process. bool open(ProcessOpenI* _process, const FilePath& _filePath, const StringView& _args, Error* _err = NULL); - /// + /// Close. void close(CloserI* _reader); } // namespace bx diff --git a/include/bx/settings.h b/include/bx/settings.h new file mode 100644 index 0000000..018e4c3 --- /dev/null +++ b/include/bx/settings.h @@ -0,0 +1,61 @@ +/* + * Copyright 2011-2017 Branimir Karadzic. All rights reserved. + * License: https://github.com/bkaradzic/bx#license-bsd-2-clause + */ + +#ifndef BX_SETTINGS_H_HEADER_GUARD +#define BX_SETTINGS_H_HEADER_GUARD + +#include "allocator.h" +#include "readerwriter.h" +#include "string.h" + +namespace bx +{ + /// + class Settings + { + public: + /// + Settings(AllocatorI* _allocator, const void* _data = NULL, uint32_t _len = 0); + + /// + ~Settings(); + + /// + void clear(); + + /// + void load(const void* _data, uint32_t _len); + + /// + const char* get(const StringView& _name) const; + + /// + void set(const StringView& _name, const StringView& _value = ""); + + /// + void remove(const StringView& _name) const; + + /// + int32_t read(ReaderSeekerI* _reader, Error* _err); + + /// + int32_t write(WriterI* _writer, Error* _err) const; + + private: + Settings(); + + AllocatorI* m_allocator; + void* m_ini; + }; + + /// + int32_t read(ReaderSeekerI* _reader, Settings& _settings, Error* _err = NULL); + + /// + int32_t write(WriterI* _writer, const Settings& _settings, Error* _err = NULL); + +} // namespace bx + +#endif // BX_SETTINGS_H_HEADER_GUARD diff --git a/include/bx/string.h b/include/bx/string.h index 358eeed..d006d3c 100644 --- a/include/bx/string.h +++ b/include/bx/string.h @@ -200,6 +200,15 @@ namespace bx /// Find substring in string. Case insensitive. Limit search to _max characters. const char* strFindI(const StringView& _str, const StringView& _find, int32_t _num = INT32_MAX); + /// + StringView strLTrim(const StringView& _str, const StringView& _chars); + + /// + StringView strRTrim(const StringView& _str, const StringView& _chars); + + /// + StringView strTrim(const StringView& _str, const StringView& _chars); + /// Find new line. Returns pointer after new line terminator. const char* strnl(const char* _str); diff --git a/scripts/bx.lua b/scripts/bx.lua index f7bc1dc..4c5ff64 100644 --- a/scripts/bx.lua +++ b/scripts/bx.lua @@ -8,6 +8,7 @@ project "bx" includedirs { path.join(BX_DIR, "include"), + path.join(BX_DIR, "3rdparty"), } files { @@ -40,6 +41,7 @@ project "bx" path.join(BX_DIR, "src/os.cpp"), path.join(BX_DIR, "src/process.cpp"), path.join(BX_DIR, "src/semaphore.cpp"), + path.join(BX_DIR, "src/settings.cpp"), path.join(BX_DIR, "src/sort.cpp"), path.join(BX_DIR, "src/string.cpp"), path.join(BX_DIR, "src/thread.cpp"), diff --git a/scripts/genie.lua b/scripts/genie.lua index c01358e..aab0a8a 100644 --- a/scripts/genie.lua +++ b/scripts/genie.lua @@ -28,8 +28,8 @@ solution "bx" language "C++" BX_DIR = path.getabsolute("..") -local BX_BUILD_DIR = path.join(BX_DIR, ".build") -local BX_THIRD_PARTY_DIR = path.join(BX_DIR, "3rdparty") +BX_BUILD_DIR = path.join(BX_DIR, ".build") +BX_THIRD_PARTY_DIR = path.join(BX_DIR, "3rdparty") dofile "toolchain.lua" toolchain(BX_BUILD_DIR, BX_THIRD_PARTY_DIR) diff --git a/src/settings.cpp b/src/settings.cpp new file mode 100644 index 0000000..af41a62 --- /dev/null +++ b/src/settings.cpp @@ -0,0 +1,213 @@ +/* + * Copyright 2011-2017 Branimir Karadzic. All rights reserved. + * License: https://github.com/bkaradzic/bx#license-bsd-2-clause + */ + +#include + +namespace +{ +#define INI_MALLOC(_ctx, _size) (BX_ALLOC(reinterpret_cast(_ctx), _size) ) +#define INI_FREE(_ctx, _ptr) (BX_FREE(reinterpret_cast(_ctx), _ptr) ) +#define INI_MEMCPY(_dst, _src, _count) (bx::memCopy(_dst, _src, _count) ) +#define INI_STRLEN(_str) (bx::strLen(_str) ) +#define INI_STRICMP(_s1, _s2, _len) (bx::strCmpI(_s1, _s2, _len) ) + +#define INI_IMPLEMENTATION + +BX_PRAGMA_DIAGNOSTIC_PUSH(); +BX_PRAGMA_DIAGNOSTIC_IGNORED_CLANG_GCC("-Wsign-compare"); +BX_PRAGMA_DIAGNOSTIC_IGNORED_CLANG_GCC("-Wunused-function"); +#include +BX_PRAGMA_DIAGNOSTIC_POP(); +} + +namespace bx +{ + +Settings::Settings(AllocatorI* _allocator, const void* _data, uint32_t _len) + : m_allocator(_allocator) + , m_ini(NULL) +{ + load(_data, _len); +} + +#define INI_T(_ptr) reinterpret_cast(_ptr) + +Settings::~Settings() +{ + ini_destroy(INI_T(m_ini) ); +} + +void Settings::clear() +{ + load(NULL, 0); +} + +void Settings::load(const void* _data, uint32_t _len) +{ + if (NULL != m_ini) + { + ini_destroy(INI_T(m_ini) ); + } + + if (NULL == _data) + { + m_ini = ini_create(m_allocator); + } + else + { + BX_UNUSED(_len); + m_ini = ini_load( (const char*)_data, m_allocator); + } +} + +const char* Settings::get(const StringView& _name) const +{ + ini_t* ini = INI_T(m_ini); + + FilePath uri(_name); + const StringView path = strTrim(uri.getPath(), "/"); + const StringView& fileName = uri.getFileName(); + + int32_t section = INI_GLOBAL_SECTION; + + if (!path.isEmpty() ) + { + section = ini_find_section(ini, path.getPtr(), path.getLength()-1); + if (INI_NOT_FOUND == section) + { + section = INI_GLOBAL_SECTION; + } + } + + int32_t property = ini_find_property(ini, section, fileName.getPtr(), fileName.getLength() ); + if (INI_NOT_FOUND == property) + { + return NULL; + } + + return ini_property_value(ini, section, property); +} + +void Settings::set(const StringView& _name, const StringView& _value) +{ + ini_t* ini = INI_T(m_ini); + + FilePath uri(_name); + const StringView path = strTrim(uri.getPath(), "/"); + const StringView& fileName = uri.getFileName(); + + int32_t section = INI_GLOBAL_SECTION; + + if (!path.isEmpty() ) + { + section = ini_find_section(ini, path.getPtr(), path.getLength()-1); + if (INI_NOT_FOUND == section) + { + section = ini_section_add(ini, path.getPtr(), path.getLength()-1); + } + } + + int32_t property = ini_find_property(ini, section, fileName.getPtr(), fileName.getLength() ); + if (INI_NOT_FOUND == property) + { + ini_property_add( + ini + , section + , fileName.getPtr() + , fileName.getLength() + , _value.getPtr() + , _value.getLength() + ); + } + else + { + ini_property_value_set( + ini + , section + , property + , _value.getPtr() + , _value.getLength() + ); + } +} + +void Settings::remove(const StringView& _name) const +{ + ini_t* ini = INI_T(m_ini); + + FilePath uri(_name); + const StringView path = strTrim(uri.getPath(), "/"); + const StringView& fileName = uri.getFileName(); + + int32_t section = INI_GLOBAL_SECTION; + + if (!path.isEmpty() ) + { + section = ini_find_section(ini, path.getPtr(), path.getLength()-1); + if (INI_NOT_FOUND == section) + { + section = INI_GLOBAL_SECTION; + } + } + + int32_t property = ini_find_property(ini, section, fileName.getPtr(), fileName.getLength() ); + if (INI_NOT_FOUND == property) + { + return; + } + + ini_property_remove(ini, section, property); + + if (INI_GLOBAL_SECTION != section + && 0 == ini_property_count(ini, section) ) + { + ini_section_remove(ini, section); + } +} + +int32_t Settings::read(ReaderSeekerI* _reader, Error* _err) +{ + int32_t size = int32_t(getRemain(_reader) ); + + void* data = BX_ALLOC(m_allocator, size); + + int32_t total = bx::read(_reader, data, size, _err); + load(data, size); + + BX_FREE(m_allocator, data); + + return total; +} + +int32_t Settings::write(WriterI* _writer, Error* _err) const +{ + ini_t* ini = INI_T(m_ini); + + int32_t size = ini_save(ini, NULL, 0); + void* data = BX_ALLOC(m_allocator, size); + + ini_save(ini, (char*)data, size); + int32_t total = bx::write(_writer, data, size-1, _err); + + BX_FREE(m_allocator, data); + + return total; +} + +#undef INI_T + +int32_t read(ReaderSeekerI* _reader, Settings& _settings, Error* _err) +{ + BX_ERROR_SCOPE(_err); + return _settings.read(_reader, _err); +} + +int32_t write(WriterI* _writer, const Settings& _settings, Error* _err) +{ + BX_ERROR_SCOPE(_err); + return _settings.write(_writer, _err); +} + +} // namespace bx diff --git a/src/string.cpp b/src/string.cpp index c9851c3..c294bd2 100644 --- a/src/string.cpp +++ b/src/string.cpp @@ -333,9 +333,9 @@ namespace bx return strCat(_dst, _dstSize, _str.getPtr(), min(_str.getLength(), _num) ); } - inline const char* strFind(const char* _str, int32_t _max, char _ch) + inline const char* strFindUnsafe(const char* _str, int32_t _len, char _ch) { - for (int32_t ii = 0, len = strLen(_str, _max); ii < len; ++ii) + for (int32_t ii = 0; ii < _len; ++ii) { if (_str[ii] == _ch) { @@ -346,14 +346,19 @@ namespace bx return NULL; } + inline const char* strFind(const char* _str, int32_t _max, char _ch) + { + return strFindUnsafe(_str, strLen(_str, _max), _ch); + } + const char* strFind(const StringView& _str, char _ch) { return strFind(_str.getPtr(), _str.getLength(), _ch); } - inline const char* strRFind(const char* _str, int32_t _max, char _ch) + inline const char* strRFindUnsafe(const char* _str, int32_t _len, char _ch) { - for (int32_t ii = strLen(_str, _max); 0 <= ii; --ii) + for (int32_t ii = _len; 0 <= ii; --ii) { if (_str[ii] == _ch) { @@ -364,6 +369,11 @@ namespace bx return NULL; } + inline const char* strRFind(const char* _str, int32_t _max, char _ch) + { + return strRFindUnsafe(_str, strLen(_str, _max), _ch); + } + const char* strRFind(const StringView& _str, char _ch) { return strRFind(_str.getPtr(), _str.getLength(), _ch); @@ -430,6 +440,44 @@ namespace bx ); } + StringView strLTrim(const StringView& _str, const StringView& _chars) + { + const char* ptr = _str.getPtr(); + const char* chars = _chars.getPtr(); + const uint32_t charsLen = _chars.getLength(); + for (uint32_t ii = 0, len = _str.getLength(); ii < len; ++ii) + { + if (NULL == strFindUnsafe(chars, charsLen, ptr[ii]) ) + { + return StringView(ptr + ii, len-ii); + } + } + + return StringView(); + } + + StringView strRTrim(const StringView& _str, const StringView& _chars) + { + const char* ptr = _str.getPtr(); + const char* chars = _chars.getPtr(); + const uint32_t charsLen = _chars.getLength(); + + for (int32_t len = _str.getLength(), ii = len-1; 0 <= ii; --ii) + { + if (NULL == strFindUnsafe(chars, charsLen, ptr[ii]) ) + { + return StringView(ptr, ii); + } + } + + return StringView(); + } + + StringView strTrim(const StringView& _str, const StringView& _chars) + { + return strLTrim(strRTrim(_str, _chars), _chars); + } + const char* strnl(const char* _str) { for (; '\0' != *_str; _str += strLen(_str, 1024) ) diff --git a/tests/filepath_test.cpp b/tests/filepath_test.cpp index 41bdd24..e8af194 100644 --- a/tests/filepath_test.cpp +++ b/tests/filepath_test.cpp @@ -80,10 +80,10 @@ static const FilePathSplit s_filePathSplit[] = { { "\\abc/def\\../..\\../test.txt", true, "/", "test.txt", "test", ".txt" }, { "/abv/gd/555/333/pod.mac", true, "/abv/gd/555/333/", "pod.mac", "pod", ".mac" }, - { "archive.tar.gz", false, "", "archive.tar.gz", "archive", ".tar.gz" }, - { "tmp/archive.tar.gz", false, "tmp/", "archive.tar.gz", "archive", ".tar.gz" }, - { "/tmp/archive.tar.gz", true, "/tmp/", "archive.tar.gz", "archive", ".tar.gz" }, - { "d:/tmp/archive.tar.gz", true, "D:/tmp/", "archive.tar.gz", "archive", ".tar.gz" }, + { "archive.tar.gz", false, "", "archive.tar.gz", "archive", ".tar.gz" }, + { "tmp/archive.tar.gz", false, "tmp/", "archive.tar.gz", "archive", ".tar.gz" }, + { "/tmp/archive.tar.gz", true, "/tmp/", "archive.tar.gz", "archive", ".tar.gz" }, + { "d:/tmp/archive.tar.gz", true, "D:/tmp/", "archive.tar.gz", "archive", ".tar.gz" }, }; TEST_CASE("FilePath", "") diff --git a/tests/settings_test.cpp b/tests/settings_test.cpp new file mode 100644 index 0000000..b173916 --- /dev/null +++ b/tests/settings_test.cpp @@ -0,0 +1,49 @@ +/* + * Copyright 2010-2017 Branimir Karadzic. All rights reserved. + * License: https://github.com/bkaradzic/bx#license-bsd-2-clause + */ + +#include "test.h" +#include +#include + +TEST_CASE("Settings", "") +{ + bx::FilePath filePath; + filePath.set(bx::TempDir::Tag); + filePath.join("settings.ini"); + + bx::DefaultAllocator allocator; + + bx::Settings settings(&allocator); + + settings.set("meh/podmac", "true"); + settings.set("test/foo/bar/abvgd", "1389"); + + bx::FileWriter writer; + if (bx::open(&writer, filePath) ) + { + bx::write(&writer, settings); + bx::close(&writer); + } + + REQUIRE(NULL == settings.get("meh") ); + REQUIRE(0 == bx::strCmp(settings.get("meh/podmac"), "true") ); + REQUIRE(0 == bx::strCmp(settings.get("test/foo/bar/abvgd"), "1389") ); + + settings.remove("meh/podmac"); + REQUIRE(NULL == settings.get("meh/podmac") ); + + settings.clear(); + + bx::FileReader reader; + if (bx::open(&reader, filePath) ) + { + bx::read(&reader, settings); + bx::close(&writer); + } + + REQUIRE(NULL == settings.get("meh") ); + REQUIRE(0 == bx::strCmp(settings.get("meh/podmac"), "true") ); + REQUIRE(0 == bx::strCmp(settings.get("test/foo/bar/abvgd"), "1389") ); +} diff --git a/tests/string_test.cpp b/tests/string_test.cpp index 38b2bf4..4c4b86e 100644 --- a/tests/string_test.cpp +++ b/tests/string_test.cpp @@ -196,6 +196,8 @@ TEST_CASE("strFind", "") REQUIRE(NULL == bx::strFind(test, "Quick1") ); REQUIRE(&test[4] == bx::strFind(bx::StringView(test, 9), "Quick") ); REQUIRE(&test[4] == bx::strFind(test, "Quick") ); + + REQUIRE(NULL == bx::strFind("vgd", 'a') ); } } @@ -361,3 +363,19 @@ TEST_CASE("StringView", "") sv.clear(); REQUIRE(0 == sv.getLength() ); } + +TEST_CASE("Trim", "") +{ + REQUIRE(0 == bx::strCmp(bx::strLTrim("abvgd", "ab"), "vgd") ); + REQUIRE(0 == bx::strCmp(bx::strLTrim("abvgd", "vagbd"), "") ); + REQUIRE(0 == bx::strCmp(bx::strLTrim("abvgd", "vgd"), "abvgd") ); + REQUIRE(0 == bx::strCmp(bx::strLTrim("/555333/podmac/", "/"), "555333/podmac/") ); + + REQUIRE(0 == bx::strCmp(bx::strRTrim("abvgd", "vagbd"), "") ); + REQUIRE(0 == bx::strCmp(bx::strRTrim("abvgd", "abv"), "abvgd") ); + REQUIRE(0 == bx::strCmp(bx::strRTrim("/555333/podmac/", "/"), "/555333/podmac") ); + + REQUIRE(0 == bx::strCmp(bx::strTrim("abvgd", "da"), "bvg") ); + REQUIRE(0 == bx::strCmp(bx::strTrim("<1389>", "<>"), "1389") ); + REQUIRE(0 == bx::strCmp(bx::strTrim("/555333/podmac/", "/"), "555333/podmac") ); +}