Optimize allocations in project saving and editor rendering

Refactor IniFile storage to keep keys and values in unified SmallVector-backed
records, avoiding parallel vector growth and allocations for common small
configurations.

Use std::string_view by value throughout the IniFile input API to prevent
temporary std::string allocations for keys, value names, paths, and comments.
Write serialized components directly to streams instead of constructing a
temporary string for every line.

Return ProjectBuildConfiguration by const reference to avoid copying it during
project saves, and use inline storage for DocumentView visual lines to avoid
heap allocations while rendering the code editor minimap.

Add IniFile tests covering parsing, mutation, ordered serialization, and a
byte-for-byte ecode project-state round trip.
This commit is contained in:
Martín Lucas Golini
2026-08-13 16:03:04 -03:00
parent 74d9fa660c
commit a2c9adca9a
6 changed files with 264 additions and 228 deletions
+51 -59
View File
@@ -16,12 +16,12 @@
#ifndef EE_SYSTEM_INIFILE_HPP
#define EE_SYSTEM_INIFILE_HPP
#include <eepp/core/small_vector.hpp>
#include <eepp/system/iostream.hpp>
#include <map>
#include <string>
#include <string_view>
#include <unordered_map>
#include <vector>
namespace EE { namespace System {
@@ -32,13 +32,13 @@ class EE_API IniFile {
enum errors { noID = -1 };
/** Initialize and load the ini file from path */
IniFile( const std::string& iniPath = "", bool autoLoad = true );
IniFile( std::string_view iniPath = {}, bool autoLoad = true );
/** Initialize and load the ini file from memory */
IniFile( const Uint8* RAWData, const Uint32& size, bool autoLoad = true );
/** Initialize and load the ini file from a pack file */
IniFile( Pack* Pack, const std::string& iniPackPath, bool autoLoad = true );
IniFile( Pack* Pack, std::string_view iniPackPath, bool autoLoad = true );
/** Initialize and load the ini file from a stream */
IniFile( IOStream& stream, bool autoLoad = true );
@@ -46,19 +46,19 @@ class EE_API IniFile {
virtual ~IniFile() {}
/** Loads an ini file from path */
bool loadFromFile( const std::string& iniPath );
bool loadFromFile( std::string_view iniPath );
/** Loads an ini file from memory */
bool loadFromMemory( const Uint8* RAWData, const Uint32& size );
/** Loads an ini file from a pack file */
bool loadFromPack( Pack* Pack, const std::string& iniPackPath );
bool loadFromPack( Pack* Pack, std::string_view iniPackPath );
/** Loads an ini file from a stream */
bool loadFromStream( IOStream& stream );
/** Sets mPath of ini file to read and write from. */
void path( const std::string& newPath ) { mPath = newPath; }
void path( std::string_view newPath ) { mPath.assign( newPath ); }
/** @return The ini file path */
const std::string& path() const { return mPath; }
@@ -77,22 +77,16 @@ class EE_API IniFile {
void clear();
/** @return index of specified key, or noID if not found. */
long findKey( const std::string& keyname ) const;
long findKey( const std::string_view& keyname ) const;
long findKey( std::string_view keyname ) const;
/** @return index of specified value, in the specified key, or noID if not found. */
long findValue( unsigned const keyID, const std::string& valuename ) const;
long findValue( unsigned const keyID, const std::string_view& valuename ) const;
long findValue( unsigned const keyID, std::string_view valuename ) const;
/** @return number of Keys currently in the ini. */
unsigned getNumKeys() const { return (unsigned int)mNames.size(); }
unsigned getNumKeys() const { return (unsigned int)mKeys.size(); }
/** Add a key name. */
unsigned addKeyName( const std::string& keyname );
unsigned addKeyName( const std::string_view& keyname );
unsigned addKeyName( std::string_view keyname );
/** @return key Names by index. */
std::string getKeyName( unsigned const keyID ) const;
@@ -101,45 +95,45 @@ class EE_API IniFile {
unsigned getNumValues( unsigned const keyID );
/** @return number of values stored for specified key from its name. */
unsigned getNumValues( const std::string& keyname );
unsigned getNumValues( std::string_view keyname );
/** @return value name by index for a given keyname or keyID. */
std::string getValueName( unsigned const keyID, unsigned const valueID ) const;
/** @return A value name from keyname and valueId */
std::string getValueName( const std::string& keyname, unsigned const valueID ) const;
std::string getValueName( std::string_view keyname, unsigned const valueID ) const;
/** Gets value of [keyname] valuename =.
** Overloaded to return std::string, int, and double.
** @return defValue if key/value not found otherwise the value obtained. */
std::string getValue( unsigned const keyID, unsigned const valueID,
const std::string& defValue = "" ) const;
std::string_view defValue = {} ) const;
/** Gets a value from a keyname and valuename */
std::string getValue( const std::string& keyname, const std::string& valuename,
const std::string& defValue = "" ) const;
std::string getValue( std::string_view keyname, std::string_view valuename,
std::string_view defValue = {} ) const;
/** Gets the value as integer */
int getValueI( const std::string& keyname, const std::string& valuename,
int getValueI( std::string_view keyname, std::string_view valuename,
int const defValue = 0 ) const;
/** Gets the value as an unsigned long */
unsigned long getValueU( const std::string& keyname, const std::string& valuename,
unsigned long getValueU( std::string_view keyname, std::string_view valuename,
unsigned long const defValue = 0 ) const;
/** Gets the value as boolean */
bool getValueB( const std::string& keyname, const std::string& valuename,
bool getValueB( std::string_view keyname, std::string_view valuename,
bool const defValue = false ) const;
/** Gets the value as double */
double getValueF( const std::string& keyname, const std::string& valuename,
double getValueF( std::string_view keyname, std::string_view valuename,
double const defValue = 0.0 ) const;
/** Sets value of [keyname] valuename =.
** Specify the optional parameter as false (0) if you do not want it to create
** the key if it doesn't exist. @return true if data entered, false otherwise.
** Overloaded to accept std::string, int, and double. */
bool setValue( unsigned const keyID, unsigned const valueID, const std::string& value );
bool setValue( unsigned const keyID, unsigned const valueID, std::string_view value );
/** Sets the value from a keyname and a valuename
* @param keyname The key name
@@ -147,11 +141,8 @@ class EE_API IniFile {
* @param value The value to assign
* @param create If true it will create the keyname if doesn't exists
*/
bool setValue( const std::string& keyname, const std::string& valuename,
const std::string& value, bool create = true );
bool setValue( const std::string_view& keyname, const std::string_view& valuename,
const std::string_view& value, bool create = true );
bool setValue( std::string_view keyname, std::string_view valuename, std::string_view value,
bool create = true );
/** Sets a integer value from a keyname and a valuename
* @param keyname The key name
@@ -159,7 +150,7 @@ class EE_API IniFile {
* @param value The value to assign
* @param create If true it will create the keyname if doesn't exists
*/
bool setValueI( const std::string& keyname, const std::string& valuename, int const value,
bool setValueI( std::string_view keyname, std::string_view valuename, int const value,
bool create = true );
/** Sets a unsigned long value from a keyname and a valuename
@@ -168,8 +159,8 @@ class EE_API IniFile {
* @param value The value to assign
* @param create If true it will create the keyname if doesn't exists
*/
bool setValueU( const std::string& keyname, const std::string& valuename,
unsigned long const value, bool create = true );
bool setValueU( std::string_view keyname, std::string_view valuename, unsigned long const value,
bool create = true );
/** Sets a boolean value from a keyname and a valuename
* @param keyname The key name
@@ -177,7 +168,7 @@ class EE_API IniFile {
* @param value The value to assign
* @param create If true it will create the keyname if doesn't exists
*/
bool setValueB( const std::string& keyname, const std::string& valuename, bool const value,
bool setValueB( std::string_view keyname, std::string_view valuename, bool const value,
bool create = true ) {
return setValueI( keyname, valuename, int( value ), create );
}
@@ -188,19 +179,19 @@ class EE_API IniFile {
* @param value The value to assign
* @param create If true it will create the keyname if doesn't exists
*/
bool setValueF( const std::string& keyname, const std::string& valuename, double const value,
bool setValueF( std::string_view keyname, std::string_view valuename, double const value,
bool create = true );
/** Sets a formatted value from a keyname and a valuename */
bool setValueV( const std::string& keyname, const std::string& valuename, char* format, ... );
bool setValueV( std::string_view keyname, std::string_view valuename, char* format, ... );
/** Deletes specified value.
** @return true if value existed and deleted, false otherwise. */
bool deleteValue( const std::string& keyname, const std::string& valuename );
bool deleteValue( std::string_view keyname, std::string_view valuename );
/** Deletes specified key and all values contained within.
** @return true if key existed and deleted, false otherwise. */
bool deleteKey( const std::string& keyname );
bool deleteKey( std::string_view keyname );
/** Header comment functions.
** Header comments are those comments before the first key.
@@ -208,7 +199,7 @@ class EE_API IniFile {
unsigned numHeaderComments() { return (unsigned int)mComments.size(); }
/** Add a header comment. */
void addHeaderComment( const std::string& comment );
void addHeaderComment( std::string_view comment );
/** Return a header comment. */
std::string getHeaderComment( unsigned const commentID ) const;
@@ -221,12 +212,12 @@ class EE_API IniFile {
std::map<std::string, std::string> getKeyMap( const unsigned& keyID ) const;
std::map<std::string, std::string> getKeyMap( const std::string& keyname ) const;
std::map<std::string, std::string> getKeyMap( std::string_view keyname ) const;
std::unordered_map<std::string, std::string> getKeyUnorderedMap( const unsigned& keyID ) const;
std::unordered_map<std::string, std::string>
getKeyUnorderedMap( const std::string& keyname ) const;
getKeyUnorderedMap( std::string_view keyname ) const;
/** Key comment functions.
** Key comments are those comments within a key. Any comments
@@ -235,44 +226,45 @@ class EE_API IniFile {
** the IniFile::writeFile() is called.
** Number of key comments. */
unsigned getNumKeyComments( unsigned const keyID ) const;
unsigned getNumKeyComments( const std::string& keyname ) const;
unsigned getNumKeyComments( std::string_view keyname ) const;
/** Add a key comment.*/
bool addKeyComment( unsigned const keyID, const std::string& comment );
bool addKeyComment( unsigned const keyID, const std::string_view& comment );
bool addKeyComment( const std::string& keyname, const std::string& comment );
bool addKeyComment( const std::string_view& keyname, const std::string_view& comment );
bool addKeyComment( unsigned const keyID, std::string_view comment );
bool addKeyComment( std::string_view keyname, std::string_view comment );
/** Return a key comment. */
std::string getKeyComment( unsigned const keyID, unsigned const commentID ) const;
std::string getKeyComment( const std::string& keyname, unsigned const commentID ) const;
std::string getKeyComment( std::string_view keyname, unsigned const commentID ) const;
/** Delete a key comment. */
bool deleteKeyComment( unsigned const keyID, unsigned const commentID );
bool deleteKeyComment( const std::string& keyname, unsigned const commentID );
bool deleteKeyComment( std::string_view keyname, unsigned const commentID );
/** Delete all comments for a key. */
bool deleteKeyComments( unsigned const keyID );
bool deleteKeyComments( const std::string& keyname );
bool deleteKeyComments( std::string_view keyname );
bool iniParsed() { return mIniRead; }
bool keyExists( const std::string& keyname ) const;
bool keyExists( std::string_view keyname ) const;
bool keyValueExists( const std::string& keyname, const std::string& valuename ) const;
bool keyValueExists( std::string_view keyname, std::string_view valuename ) const;
private:
bool mIniRead{ false };
std::string mPath;
std::string mBuffer;
struct key {
std::vector<std::string> names;
std::vector<std::string> values;
std::vector<std::string> comments;
struct value {
std::string name;
std::string data;
};
std::vector<key> mKeys;
std::vector<std::string> mNames;
std::vector<std::string> mComments;
struct key {
std::string name;
SmallVector<value, 2> values;
SmallVector<std::string, 1> comments;
};
SmallVector<key, 4> mKeys;
SmallVector<std::string, 2> mComments;
};
}} // namespace EE::System
+1 -1
View File
@@ -37,7 +37,7 @@ class EE_API DocumentView {
struct VisibleLineInfo {
VisibleIndex visibleIndex{ VisibleIndex::invalid };
Float paddingStart{ 0 };
std::vector<TextPosition> visualLines;
SmallVector<TextPosition, 2> visualLines;
};
struct VisibleLineRange {