mirror of
https://github.com/bkaradzic/bx.git
synced 2026-09-10 04:18:24 +00:00
Added scanner. (#410)
This commit is contained in:
committed by
GitHub
parent
cd6720ce9a
commit
9916e720fc
655
src/settings.cpp
655
src/settings.cpp
@@ -4,209 +4,496 @@
|
||||
*/
|
||||
|
||||
#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_STRNICMP(_s1, _s2, _len) (bx::strCmpI(_s1, _s2, _len) )
|
||||
|
||||
#define INI_IMPLEMENTATION
|
||||
|
||||
BX_PRAGMA_DIAGNOSTIC_IGNORED_CLANG_GCC("-Wunused-function");
|
||||
|
||||
BX_PRAGMA_DIAGNOSTIC_PUSH();
|
||||
BX_PRAGMA_DIAGNOSTIC_IGNORED_CLANG_GCC("-Wsign-compare");
|
||||
#include <ini/ini.h>
|
||||
BX_PRAGMA_DIAGNOSTIC_POP();
|
||||
}
|
||||
#include <bx/filepath.h>
|
||||
#include <bx/scanner.h>
|
||||
|
||||
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)
|
||||
static StringView settingsAlloc(AllocatorI* _allocator, const StringView& _str)
|
||||
{
|
||||
ini_destroy(INI_T(m_ini) );
|
||||
}
|
||||
const int32_t len = _str.getLength();
|
||||
|
||||
if (NULL == _data)
|
||||
{
|
||||
m_ini = ini_create(m_allocator);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_ini = ini_load( (const char*)_data, _len, m_allocator);
|
||||
}
|
||||
}
|
||||
|
||||
StringView 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() );
|
||||
if (INI_NOT_FOUND == section)
|
||||
if (0 == len)
|
||||
{
|
||||
section = INI_GLOBAL_SECTION;
|
||||
return StringView();
|
||||
}
|
||||
|
||||
char* ptr = (char*)alloc(_allocator, len);
|
||||
memCopy(ptr, _str.getPtr(), len);
|
||||
|
||||
return StringView(ptr, len);
|
||||
}
|
||||
|
||||
static void settingsFree(AllocatorI* _allocator, StringView& _str)
|
||||
{
|
||||
if (!_str.isEmpty() )
|
||||
{
|
||||
free(_allocator, const_cast<char*>(_str.getPtr() ) );
|
||||
}
|
||||
|
||||
_str.clear();
|
||||
}
|
||||
|
||||
static void settingsAssign(AllocatorI* _allocator, StringView& _dst, const StringView& _src)
|
||||
{
|
||||
const StringView next = settingsAlloc(_allocator, _src);
|
||||
settingsFree(_allocator, _dst);
|
||||
_dst = next;
|
||||
}
|
||||
|
||||
struct Settings::Ini
|
||||
{
|
||||
static constexpr int32_t kGlobal = 0;
|
||||
static constexpr int32_t kInvalid = -1;
|
||||
|
||||
struct Property
|
||||
{
|
||||
StringView name;
|
||||
StringView value;
|
||||
};
|
||||
|
||||
struct Section
|
||||
{
|
||||
StringView name;
|
||||
Property* props;
|
||||
uint32_t count;
|
||||
uint32_t capacity;
|
||||
};
|
||||
|
||||
void init(AllocatorI* _allocator)
|
||||
{
|
||||
m_allocator = _allocator;
|
||||
m_sections = NULL;
|
||||
m_count = 0;
|
||||
m_capacity = 0;
|
||||
|
||||
clear();
|
||||
}
|
||||
|
||||
void shutdown()
|
||||
{
|
||||
reset();
|
||||
|
||||
if (NULL != m_sections)
|
||||
{
|
||||
free(m_allocator, m_sections);
|
||||
m_sections = NULL;
|
||||
}
|
||||
|
||||
m_capacity = 0;
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
reset();
|
||||
|
||||
addSection(StringView() );
|
||||
}
|
||||
|
||||
void load(const StringView& _data)
|
||||
{
|
||||
clear();
|
||||
|
||||
Scanner scanner(_data);
|
||||
int32_t section = kGlobal;
|
||||
|
||||
while (!scanner.isDone() )
|
||||
{
|
||||
scanner.accept(Scanner::Class::Space);
|
||||
|
||||
if (scanner.isDone() )
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
Scanner line(scanner.acceptUntil(Scanner::Class::EndOfLine) );
|
||||
|
||||
// Line comment.
|
||||
if (!line.accept(';').isEmpty() )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// [section] header.
|
||||
if (!line.accept('[').isEmpty() )
|
||||
{
|
||||
line.accept(Scanner::Class::Space);
|
||||
const StringView name = strRTrimSpace(line.acceptUntil("]") );
|
||||
|
||||
if (!line.accept(']').isEmpty()
|
||||
&& !name.isEmpty() )
|
||||
{
|
||||
const int32_t existing = findSection(name);
|
||||
section = kInvalid == existing ? addSection(name) : existing;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// name = value property.
|
||||
const StringView name = strRTrimSpace(line.acceptUntil("=") );
|
||||
|
||||
if (line.accept('=').isEmpty()
|
||||
|| name.isEmpty() )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
line.accept(Scanner::Class::Space);
|
||||
setProperty(section, name, strRTrimSpace(line.acceptAll() ) );
|
||||
}
|
||||
}
|
||||
|
||||
int32_t find(const StringView& _name, StringView& _propertyName) const
|
||||
{
|
||||
const FilePath uri(_name);
|
||||
const StringView path(strTrim(uri.getPath(), "/") );
|
||||
|
||||
_propertyName = uri.getFileName();
|
||||
|
||||
if (path.isEmpty() )
|
||||
{
|
||||
return kGlobal;
|
||||
}
|
||||
|
||||
const int32_t section = findSection(path);
|
||||
|
||||
return kInvalid == section ? kGlobal : section;
|
||||
}
|
||||
|
||||
int32_t findSection(const StringView& _name) const
|
||||
{
|
||||
for (uint32_t ss = 0; ss < m_count; ++ss)
|
||||
{
|
||||
const StringView& name = m_sections[ss].name;
|
||||
|
||||
if (isEqual(name, _name, false) )
|
||||
{
|
||||
return int32_t(ss);
|
||||
}
|
||||
}
|
||||
|
||||
return kInvalid;
|
||||
}
|
||||
|
||||
int32_t addSection(const StringView& _name)
|
||||
{
|
||||
if (m_count == m_capacity)
|
||||
{
|
||||
m_capacity = 0 == m_capacity ? 8 : m_capacity * 2;
|
||||
m_sections = (Section*)realloc(m_allocator, m_sections, m_capacity * sizeof(Section) );
|
||||
}
|
||||
|
||||
Section& section = m_sections[m_count];
|
||||
section.name = settingsAlloc(m_allocator, _name);
|
||||
section.props = NULL;
|
||||
section.count = 0;
|
||||
section.capacity = 0;
|
||||
|
||||
return int32_t(m_count++);
|
||||
}
|
||||
|
||||
void removeSection(int32_t _section)
|
||||
{
|
||||
if (kGlobal >= _section
|
||||
|| uint32_t(_section) >= m_count)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
freeSection(m_sections[_section]);
|
||||
|
||||
for (uint32_t ss = uint32_t(_section); ss+1 < m_count; ++ss)
|
||||
{
|
||||
m_sections[ss] = m_sections[ss+1];
|
||||
}
|
||||
|
||||
--m_count;
|
||||
}
|
||||
|
||||
int32_t findProperty(int32_t _section, const StringView& _name) const
|
||||
{
|
||||
if (0 > _section
|
||||
|| uint32_t(_section) >= m_count)
|
||||
{
|
||||
return kInvalid;
|
||||
}
|
||||
|
||||
const Section& section = m_sections[_section];
|
||||
|
||||
for (uint32_t pp = 0; pp < section.count; ++pp)
|
||||
{
|
||||
const StringView& name = section.props[pp].name;
|
||||
|
||||
if (isEqual(name, _name, false) )
|
||||
{
|
||||
return int32_t(pp);
|
||||
}
|
||||
}
|
||||
|
||||
return kInvalid;
|
||||
}
|
||||
|
||||
void setProperty(int32_t _section, const StringView& _name, const StringView& _value)
|
||||
{
|
||||
if (0 > _section
|
||||
|| uint32_t(_section) >= m_count)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const int32_t property = findProperty(_section, _name);
|
||||
|
||||
Section& section = m_sections[_section];
|
||||
|
||||
if (kInvalid != property)
|
||||
{
|
||||
settingsAssign(m_allocator, section.props[property].value, _value);
|
||||
return;
|
||||
}
|
||||
|
||||
if (section.count == section.capacity)
|
||||
{
|
||||
section.capacity = 0 == section.capacity ? 8 : section.capacity * 2;
|
||||
section.props = (Property*)realloc(m_allocator, section.props, section.capacity * sizeof(Property) );
|
||||
}
|
||||
|
||||
Property& prop = section.props[section.count];
|
||||
prop.name = settingsAlloc(m_allocator, _name);
|
||||
prop.value = settingsAlloc(m_allocator, _value);
|
||||
|
||||
++section.count;
|
||||
}
|
||||
|
||||
void removeProperty(int32_t _section, int32_t _property)
|
||||
{
|
||||
if (0 > _section
|
||||
|| uint32_t(_section) >= m_count)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Section& section = m_sections[_section];
|
||||
|
||||
if (0 > _property
|
||||
|| uint32_t(_property) >= section.count)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
settingsFree(m_allocator, section.props[_property].name);
|
||||
settingsFree(m_allocator, section.props[_property].value);
|
||||
|
||||
for (uint32_t pp = uint32_t(_property); pp+1 < section.count; ++pp)
|
||||
{
|
||||
section.props[pp] = section.props[pp+1];
|
||||
}
|
||||
|
||||
--section.count;
|
||||
}
|
||||
|
||||
StringView getPropertyValue(int32_t _section, int32_t _property) const
|
||||
{
|
||||
if (0 > _section
|
||||
|| uint32_t(_section) >= m_count)
|
||||
{
|
||||
return StringView();
|
||||
}
|
||||
|
||||
const Section& section = m_sections[_section];
|
||||
|
||||
return (0 <= _property && uint32_t(_property) < section.count)
|
||||
? section.props[_property].value
|
||||
: StringView()
|
||||
;
|
||||
}
|
||||
|
||||
uint32_t getPropertyCount(int32_t _section) const
|
||||
{
|
||||
return (0 <= _section && uint32_t(_section) < m_count)
|
||||
? m_sections[_section].count
|
||||
: 0
|
||||
;
|
||||
}
|
||||
|
||||
int32_t save(WriterI* _writer, Error* _err) const
|
||||
{
|
||||
int32_t total = 0;
|
||||
|
||||
for (uint32_t ss = 0; ss < m_count; ++ss)
|
||||
{
|
||||
const Section& section = m_sections[ss];
|
||||
|
||||
if (!section.name.isEmpty() )
|
||||
{
|
||||
total += bx::write(_writer, "[", _err);
|
||||
total += bx::write(_writer, section.name, _err);
|
||||
total += bx::write(_writer, "]\n", _err);
|
||||
}
|
||||
|
||||
for (uint32_t pp = 0; pp < section.count; ++pp)
|
||||
{
|
||||
total += bx::write(_writer, section.props[pp].name, _err);
|
||||
total += bx::write(_writer, "=", _err);
|
||||
total += bx::write(_writer, section.props[pp].value, _err);
|
||||
total += bx::write(_writer, "\n", _err);
|
||||
}
|
||||
|
||||
if (0 != total)
|
||||
{
|
||||
total += bx::write(_writer, "\n", _err);
|
||||
}
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
void freeSection(Section& _section)
|
||||
{
|
||||
for (uint32_t pp = 0; pp < _section.count; ++pp)
|
||||
{
|
||||
settingsFree(m_allocator, _section.props[pp].name);
|
||||
settingsFree(m_allocator, _section.props[pp].value);
|
||||
}
|
||||
|
||||
if (NULL != _section.props)
|
||||
{
|
||||
free(m_allocator, _section.props);
|
||||
}
|
||||
|
||||
settingsFree(m_allocator, _section.name);
|
||||
|
||||
_section.props = NULL;
|
||||
_section.count = 0;
|
||||
_section.capacity = 0;
|
||||
}
|
||||
|
||||
void reset()
|
||||
{
|
||||
for (uint32_t ss = 0; ss < m_count; ++ss)
|
||||
{
|
||||
freeSection(m_sections[ss]);
|
||||
}
|
||||
|
||||
m_count = 0;
|
||||
}
|
||||
|
||||
AllocatorI* m_allocator;
|
||||
Section* m_sections;
|
||||
uint32_t m_count;
|
||||
uint32_t m_capacity;
|
||||
};
|
||||
|
||||
Settings::Settings(AllocatorI* _allocator, const void* _data, uint32_t _len)
|
||||
: m_allocator(_allocator)
|
||||
, m_ini(BX_NEW(_allocator, Ini) )
|
||||
{
|
||||
m_ini->init(_allocator);
|
||||
load(_data, _len);
|
||||
}
|
||||
|
||||
Settings::~Settings()
|
||||
{
|
||||
m_ini->shutdown();
|
||||
deleteObject(m_allocator, m_ini);
|
||||
}
|
||||
|
||||
void Settings::clear()
|
||||
{
|
||||
m_ini->clear();
|
||||
}
|
||||
|
||||
void Settings::load(const void* _data, uint32_t _len)
|
||||
{
|
||||
if (NULL == _data
|
||||
|| 0 == _len)
|
||||
{
|
||||
m_ini->clear();
|
||||
return;
|
||||
}
|
||||
|
||||
m_ini->load(StringView( (const char*)_data, int32_t(_len) ) );
|
||||
}
|
||||
|
||||
StringView Settings::get(const StringView& _name) const
|
||||
{
|
||||
StringView propertyName;
|
||||
const int32_t section = m_ini->find(_name, propertyName);
|
||||
|
||||
return m_ini->getPropertyValue(section, m_ini->findProperty(section, propertyName) );
|
||||
}
|
||||
|
||||
void Settings::set(const StringView& _name, const StringView& _value)
|
||||
{
|
||||
const FilePath uri(_name);
|
||||
const StringView path(strTrim(uri.getPath(), "/") );
|
||||
const StringView& fileName(uri.getFileName() );
|
||||
|
||||
int32_t section = Ini::kGlobal;
|
||||
|
||||
if (!path.isEmpty() )
|
||||
{
|
||||
section = m_ini->findSection(path);
|
||||
|
||||
if (Ini::kInvalid == section)
|
||||
{
|
||||
section = m_ini->addSection(path);
|
||||
}
|
||||
}
|
||||
|
||||
m_ini->setProperty(section, fileName, _value);
|
||||
}
|
||||
|
||||
void Settings::remove(const StringView& _name) const
|
||||
{
|
||||
StringView propertyName;
|
||||
const int32_t section = m_ini->find(_name, propertyName);
|
||||
|
||||
const int32_t property = m_ini->findProperty(section, propertyName);
|
||||
|
||||
if (Ini::kInvalid == property)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_ini->removeProperty(section, property);
|
||||
|
||||
if (Ini::kGlobal != section
|
||||
&& 0 == m_ini->getPropertyCount(section) )
|
||||
{
|
||||
m_ini->removeSection(section);
|
||||
}
|
||||
}
|
||||
|
||||
int32_t property = ini_find_property(ini, section, fileName.getPtr(), fileName.getLength() );
|
||||
if (INI_NOT_FOUND == property)
|
||||
int32_t Settings::read(ReaderSeekerI* _reader, Error* _err)
|
||||
{
|
||||
return StringView();
|
||||
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;
|
||||
}
|
||||
|
||||
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() )
|
||||
int32_t Settings::write(WriterI* _writer, Error* _err) const
|
||||
{
|
||||
section = ini_find_section(ini, path.getPtr(), path.getLength() );
|
||||
if (INI_NOT_FOUND == section)
|
||||
{
|
||||
section = ini_section_add(ini, path.getPtr(), path.getLength() );
|
||||
}
|
||||
return m_ini->save(_writer, _err);
|
||||
}
|
||||
|
||||
int32_t property = ini_find_property(ini, section, fileName.getPtr(), fileName.getLength() );
|
||||
if (INI_NOT_FOUND == property)
|
||||
int32_t read(ReaderSeekerI* _reader, Settings& _settings, Error* _err)
|
||||
{
|
||||
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() );
|
||||
if (INI_NOT_FOUND == section)
|
||||
{
|
||||
section = INI_GLOBAL_SECTION;
|
||||
}
|
||||
BX_ERROR_SCOPE(_err);
|
||||
return _settings.read(_reader, _err);
|
||||
}
|
||||
|
||||
int32_t property = ini_find_property(ini, section, fileName.getPtr(), fileName.getLength() );
|
||||
if (INI_NOT_FOUND == property)
|
||||
int32_t write(WriterI* _writer, const Settings& _settings, Error* _err)
|
||||
{
|
||||
return;
|
||||
BX_ERROR_SCOPE(_err);
|
||||
return _settings.write(_writer, _err);
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user