diff options
author | elfmz <fenix1905@tut.by> | 2022-09-03 16:39:10 +0300 |
---|---|---|
committer | elfmz <fenix1905@tut.by> | 2022-09-03 16:39:10 +0300 |
commit | 8fb5855053a33cfe8d20e5e8dd8ae141390a4971 (patch) | |
tree | 5a8eda268d75875a309c77a531dd3c44ee31f6b4 | |
parent | 09e9fa095dbfed0eea24b06c09371a6ca08673d3 (diff) |
KeyFileHelper: optional case-insensitivity
-rw-r--r-- | utils/include/KeyFileHelper.h | 28 | ||||
-rw-r--r-- | utils/include/utils.h | 1 | ||||
-rw-r--r-- | utils/src/KeyFileHelper.cpp | 60 | ||||
-rw-r--r-- | utils/src/utils.cpp | 5 |
4 files changed, 80 insertions, 14 deletions
diff --git a/utils/include/KeyFileHelper.h b/utils/include/KeyFileHelper.h index b1a9487f..5089c3d9 100644 --- a/utils/include/KeyFileHelper.h +++ b/utils/include/KeyFileHelper.h @@ -4,8 +4,20 @@ #include <vector> #include <map> -struct KeyFileValues : std::map<std::string, std::string> +struct KeyFileCmp { + inline KeyFileCmp(bool case_insensitive = false) : _case_insensitive(case_insensitive) {} + + bool operator()(const std::string& a, const std::string& b) const; + +private: + bool _case_insensitive = false; +}; + +struct KeyFileValues : std::map<std::string, std::string, KeyFileCmp> +{ + KeyFileValues(bool case_insensitive = false); + bool HasKey(const std::string &name) const; std::string GetString(const std::string &name, const char *def = "") const; std::wstring GetString(const std::string &name, const wchar_t *def) const; @@ -24,7 +36,7 @@ class KeyFileReadSection : public KeyFileValues bool _section_loaded; public: - KeyFileReadSection(const std::string &filename, const std::string §ion); + KeyFileReadSection(const std::string &filename, const std::string §ion, bool case_insensitive = false); bool SectionLoaded() const { return _section_loaded; } }; @@ -32,12 +44,18 @@ public: class KeyFileReadHelper { protected: - struct Sections : std::map<std::string, KeyFileValues> {} _kf; + struct Sections : std::map<std::string, KeyFileValues, KeyFileCmp> { + inline Sections(bool case_insensitive) : + std::map<std::string, KeyFileValues, KeyFileCmp>(KeyFileCmp(case_insensitive)) + { } + } _kf; + struct stat _filestat {}; bool _loaded = false; + bool _case_insensitive; public: - KeyFileReadHelper(const std::string &filename, const char *load_only_section = nullptr); + KeyFileReadHelper(const std::string &filename, const char *load_only_section = nullptr, bool case_insensitive = false); bool IsLoaded() const { return _loaded; } @@ -69,7 +87,7 @@ class KeyFileHelper : public KeyFileReadHelper bool _dirty; public: - KeyFileHelper(const std::string &filename, bool load = true); + KeyFileHelper(const std::string &filename, bool load = true, bool case_insensitive = false); ~KeyFileHelper(); bool Save(bool only_if_dirty = true); diff --git a/utils/include/utils.h b/utils/include/utils.h index b91b6f8f..51deb428 100644 --- a/utils/include/utils.h +++ b/utils/include/utils.h @@ -276,6 +276,7 @@ template <typename HaystackIT, typename NeedlesT> return nullptr; } +bool CaseIgnoreEngStrMatch(const std::string &str1, const std::string &str2); bool CaseIgnoreEngStrMatch(const char *str1, const char *str2, size_t len); const char *CaseIgnoreEngStrChr(const char c, const char *str, size_t len); diff --git a/utils/src/KeyFileHelper.cpp b/utils/src/KeyFileHelper.cpp index 06394ee5..fe5daacb 100644 --- a/utils/src/KeyFileHelper.cpp +++ b/utils/src/KeyFileHelper.cpp @@ -17,6 +17,41 @@ // KeyFileHelper to tell KeyFileReadHelper don't load anything static char sDontLoadLiteral[] = "]["; +// This predicate causes std::map elements to be sorted either in semi-case-insensitive mode, +// i.e. elements that differ by character case only will be together. +// Either in fully case-insensitive mode. + +bool FN_NOINLINE KeyFileCmp::operator()(const std::string& a, const std::string& b) const +{ + int case_sensitive_diff = 0; + const char *pa = a.c_str(); + const char *pb = b.c_str(); + for (size_t i = 0;; ++i) { + auto ca = pa[i]; + auto cb = pb[i]; + if (ca != cb) { + if (case_sensitive_diff == 0) { + case_sensitive_diff = (ca < cb) ? -1 : 1; + } + if (ca >= 'a' && ca <= 'z') { + ca-= 'a' - 'A'; + } + if (cb >= 'a' && cb <= 'z') { + cb-= 'a' - 'A'; + } + if (ca != cb) { + return ca < cb; + } + } + if (UNLIKELY(i == a.size() || i == b.size())) { + if ((_case_insensitive || case_sensitive_diff == 0) && a.size() != b.size()) { + return a.size() < b.size(); + } + return _case_insensitive ? false : (case_sensitive_diff < 0); + } + } +} + class KFEscaping { std::string _result; @@ -125,6 +160,10 @@ public: //////////////////////////////////////////////////////////////// +KeyFileValues::KeyFileValues(bool case_insensitive) + : std::map<std::string, std::string, KeyFileCmp>(KeyFileCmp(case_insensitive)) +{ +} bool KeyFileValues::HasKey(const std::string &name) const { @@ -363,7 +402,7 @@ static bool LoadKeyFile(const std::string &filename, struct stat &filestat, Valu /////////////////////////////////////////// -KeyFileReadSection::KeyFileReadSection(const std::string &filename, const std::string §ion) +KeyFileReadSection::KeyFileReadSection(const std::string &filename, const std::string §ion, bool case_insensitive) : _section_loaded(false) { @@ -371,7 +410,7 @@ KeyFileReadSection::KeyFileReadSection(const std::string &filename, const std::s LoadKeyFile(filename, filestat, [&] (const std::string §ion_name)->KeyFileValues * { - if (section_name == section) { + if (section_name == section || (case_insensitive && CaseIgnoreEngStrMatch(section_name, section))) { _section_loaded = true; return this; } @@ -383,7 +422,8 @@ KeyFileReadSection::KeyFileReadSection(const std::string &filename, const std::s /////////////////////////////////// -KeyFileReadHelper::KeyFileReadHelper(const std::string &filename, const char *load_only_section) +KeyFileReadHelper::KeyFileReadHelper(const std::string &filename, const char *load_only_section, bool case_insensitive) + : _kf(case_insensitive), _case_insensitive(case_insensitive) { // intentially comparing pointer values if (load_only_section == &sDontLoadLiteral[0]) { @@ -393,7 +433,8 @@ KeyFileReadHelper::KeyFileReadHelper(const std::string &filename, const char *lo _loaded = LoadKeyFile(filename, _filestat, [&] (const std::string §ion_name)->KeyFileValues * { - if (load_only_section == nullptr || section_name == load_only_section) { + if (load_only_section == nullptr || section_name == load_only_section + || (case_insensitive && CaseIgnoreEngStrMatch(section_name, load_only_section))) { return &_kf[section_name]; } @@ -436,7 +477,8 @@ std::vector<std::string> KeyFileReadHelper::EnumSectionsAt(const std::string &pa std::vector<std::string> out; for (const auto &s : _kf) { if (s.first.size() > prefix.size() - && memcmp(s.first.c_str(), prefix.c_str(), prefix.size()) == 0 + && ( (!_case_insensitive && memcmp(s.first.c_str(), prefix.c_str(), prefix.size()) == 0) || + (_case_insensitive && CaseIgnoreEngStrMatch(s.first.c_str(), prefix.c_str(), prefix.size())) ) && (recursed || strchr(s.first.c_str() + prefix.size(), '/') == nullptr)) { out.push_back(s.first); @@ -559,9 +601,9 @@ bool KeyFileReadHelper::GetBytes(std::vector<unsigned char> &out, const std::str ///////////////////////////////////////////////////////////// -KeyFileHelper::KeyFileHelper(const std::string &filename, bool load) +KeyFileHelper::KeyFileHelper(const std::string &filename, bool load, bool case_insensitive) : - KeyFileReadHelper(filename, load ? nullptr : &sDontLoadLiteral[0]), + KeyFileReadHelper(filename, load ? nullptr : &sDontLoadLiteral[0], case_insensitive), _filename(filename), _dirty(!load) { @@ -649,9 +691,9 @@ bool KeyFileHelper::RemoveSection(const std::string §ion) { if (_kf.erase(section) != 0) { _dirty = true; - return 1; + return true; } - return 0; + return false; } size_t KeyFileHelper::RemoveSectionsAt(const std::string &parent_section) diff --git a/utils/src/utils.cpp b/utils/src/utils.cpp index 4df46fd4..66e95fb3 100644 --- a/utils/src/utils.cpp +++ b/utils/src/utils.cpp @@ -287,6 +287,11 @@ static inline bool CaseIgnoreEngChrMatch(const char c1, const char c2) return true; } +bool CaseIgnoreEngStrMatch(const std::string &str1, const std::string &str2) +{ + return str1.size() == str2.size() && CaseIgnoreEngStrMatch(str1.c_str(), str2.c_str(), str1.size()); +} + bool CaseIgnoreEngStrMatch(const char *str1, const char *str2, size_t len) { for (size_t i = 0; i != len; ++i) { |