Added .ini settings utility.

This commit is contained in:
Branimir Karadžić
2017-11-12 17:25:49 -08:00
parent 495cfb3ffd
commit 54fad631a4
15 changed files with 1824 additions and 22 deletions

1
3rdparty/ini/README.md vendored Normal file
View File

@@ -0,0 +1 @@
https://github.com/mattiasgustavsson/libs/

1056
3rdparty/ini/ini.h vendored Normal file

File diff suppressed because it is too large Load Diff

333
3rdparty/ini/ini.md vendored Normal file
View File

@@ -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 <stdio.h>
#include <stdlib.h>
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 <stdio.h>
#include <stdlib.h>
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.

View File

@@ -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);

View File

@@ -11,9 +11,10 @@
namespace bx
{
///
struct EncodingType
{
enum Enum
enum Enum ///
{
Unorm,
Int,

View File

@@ -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

61
include/bx/settings.h Normal file
View File

@@ -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

View File

@@ -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);

View File

@@ -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"),

View File

@@ -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)

213
src/settings.cpp Normal file
View File

@@ -0,0 +1,213 @@
/*
* Copyright 2011-2017 Branimir Karadzic. All rights reserved.
* License: https://github.com/bkaradzic/bx#license-bsd-2-clause
*/
#include <bx/settings.h>
namespace
{
#define INI_MALLOC(_ctx, _size) (BX_ALLOC(reinterpret_cast<bx::AllocatorI*>(_ctx), _size) )
#define INI_FREE(_ctx, _ptr) (BX_FREE(reinterpret_cast<bx::AllocatorI*>(_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 <ini/ini.h>
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<ini_t*>(_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

View File

@@ -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) )

View File

@@ -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", "")

49
tests/settings_test.cpp Normal file
View File

@@ -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 <bx/settings.h>
#include <bx/file.h>
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") );
}

View File

@@ -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") );
}