diff --git a/src/NavLights.cpp b/src/NavLights.cpp index 1e93a02c0ac..b919d149588 100644 --- a/src/NavLights.cpp +++ b/src/NavLights.cpp @@ -33,8 +33,14 @@ static vector2f get_color(Uint8 c) static inline vector2f LoadLightColorUVoffset(const std::string &spec) { - std::vector v(2); - SplitSpec(spec, v); + std::vector v; + // parse float values in the spec + SplitString(spec, ",").to_vector(v, [](std::string_view str) { + return std::atof(std::string(str).c_str()); + }); + + // ensure the spec has enough values + v.resize(2); return vector2f(v[0], v[1]); } diff --git a/src/core/StringUtils.cpp b/src/core/StringUtils.cpp index 4c984397086..4044738d9f3 100644 --- a/src/core/StringUtils.cpp +++ b/src/core/StringUtils.cpp @@ -185,32 +185,22 @@ const char *pi_strcasestr(const char *haystack, const char *needle) } } -std::vector SplitString(const std::string &source, const std::string &delim) +size_t SplitString::step(std::string_view str) { - bool stringSplitted = false; - std::vector splitted; - - size_t startPos = 0; - do { - // try to find delim - size_t delimPos = source.find(delim, startPos); - - // if delim found - if (delimPos != std::string::npos) { - std::string element = source.substr(startPos, delimPos); - splitted.push_back(element); - - // prepare next loop - startPos = delimPos + delim.length(); - } else { - // push tail and exit - splitted.push_back(source.substr(startPos)); - stringSplitted = true; - } + return m_reverse ? str.find_last_of(m_delim) : str.find_first_of(m_delim); +} - } while (!stringSplitted); +void SplitString::trim(std::string_view &str, size_t next) +{ + if (next == std::string_view::npos) { + str = {}; + return; + } - return splitted; + if (m_reverse) + str.remove_suffix((str.size() + 1) - str.find_last_not_of(m_delim, next)); + else + str.remove_prefix(str.find_first_not_of(m_delim, next)); } std::string FloatToStr(float val) diff --git a/src/core/StringUtils.h b/src/core/StringUtils.h index a46033f057a..8fa9e53ac58 100644 --- a/src/core/StringUtils.h +++ b/src/core/StringUtils.h @@ -7,6 +7,7 @@ #include #include #include +#include #include #ifdef _MSC_VER @@ -119,53 +120,92 @@ inline std::string_view strip_spaces(std::string_view &s) return s.substr(start, end); } -static inline size_t SplitSpec(const std::string &spec, std::vector &output) -{ - static const std::string delim(","); - - size_t i = 0, start = 0, end = 0; - while (end != std::string::npos) { - // get to the first non-delim char - start = spec.find_first_not_of(delim, end); - - // read the end, no more to do - if (start == std::string::npos) - break; - - // find the end - next delim or end of string - end = spec.find_first_of(delim, start); +// Utility class to split a string based on a provided set of delimiters +// Makes find_first_of / find_last_of more ergonomic to use +struct SplitString { + struct iter { + using value_type = std::string_view; + using reference = std::string_view; + + // "end" iterator + iter() : m_str(), m_parent(nullptr) {}; + // "live" iterator + iter(SplitString *parent) : + m_parent(parent), + m_str(parent->m_orig), + m_next(parent->step(m_str)) + { + } + + value_type operator*() + { + if (m_next != std::string_view::npos) + return m_parent->m_reverse ? m_str.substr(m_next + 1) : m_str.substr(0, m_next); + else + return m_str; + } + + iter &operator++() + { + m_parent->trim(m_str, m_next); + m_next = m_parent->step(m_str); + return *this; + } + + bool operator!=(const iter &rhs) { return !(*this == rhs); } + bool operator==(const iter &rhs) { + return (m_str.empty() && rhs.m_str.empty()) || + (m_parent == rhs.m_parent && m_str.size() == rhs.m_str.size()); + } + + private: + SplitString *m_parent; + std::string_view m_str; + size_t m_next = std::string_view::npos; + }; + + SplitString(std::string_view source, std::string_view delim) : + m_orig(source), m_delim(delim) + {} + + SplitString(std::string_view source, std::string_view delim, bool reverse) : + m_orig(source), m_delim(delim), m_reverse(reverse) + {} + + iter begin() { return iter(this); } + iter end() { return iter(); } + + // Split the input string to a vector of fragments using the specified type + template + std::vector to_vector() { + std::vector out; + for (auto str : *this) { + out.push_back(T(str)); + } - // extract the fragment and remember it - output[i++] = atoi(spec.substr(start, (end == std::string::npos) ? std::string::npos : end - start).c_str()); + return out; } - return i; -} - -static inline size_t SplitSpec(const std::string &spec, std::vector &output) -{ - static const std::string delim(","); - - size_t i = 0, start = 0, end = 0; - while (end != std::string::npos) { - // get to the first non-delim char - start = spec.find_first_not_of(delim, end); - - // read the end, no more to do - if (start == std::string::npos) - break; - - // find the end - next delim or end of string - end = spec.find_first_of(delim, start); - - // extract the fragment and remember it - output[i++] = atof(spec.substr(start, (end == std::string::npos) ? std::string::npos : end - start).c_str()); + // Apply the given predicate to each fragment of the string and push the result into the given container + template + void to_vector(Container &c, Predicate p) { + for (auto str : *this) { + c.push_back(p(str)); + } } - return i; -} +private: + friend struct iter; + + // find the boundary for the next token + size_t step(std::string_view str); + // remove previous substring if present + void trim(std::string_view &str, size_t next); -std::vector SplitString(const std::string &source, const std::string &delim); + std::string_view m_orig; + std::string_view m_delim; + bool m_reverse = false; +}; // 'Numeric type' to string conversions. std::string FloatToStr(float val); diff --git a/src/main.cpp b/src/main.cpp index 5cae87bd42b..2359184c003 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -122,7 +122,7 @@ extern "C" int main(int argc, char **argv) // fallthrough protect if (mode == MODE_START_AT) { // try to get start planet number - std::vector keyValue = SplitString(modeopt, "="); + std::vector keyValue = SplitString(modeopt, "=").to_vector(); // if found value if (keyValue.size() == 2) { @@ -158,7 +158,7 @@ extern "C" int main(int argc, char **argv) // for each argument for (; pos < argc; pos++) { const std::string arg(argv[pos]); - std::vector keyValue = SplitString(arg, "="); + std::vector keyValue = SplitString(arg, "=").to_vector(); // if there no key and value || key is empty || value is empty if (keyValue.size() != 2 || keyValue[0].empty() || keyValue[1].empty()) {