Skip to content

Commit

Permalink
New for-loop based SplitString utility
Browse files Browse the repository at this point in the history
- Splits arbitrary input string_view based on delimiter characters
- Replaces old SplitSpec/SplitString functionalities
- General-purpose high-reliability string processing utility
  • Loading branch information
sturnclaw committed Sep 8, 2023
1 parent a746eb7 commit 6775451
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 68 deletions.
10 changes: 8 additions & 2 deletions src/NavLights.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,14 @@ static vector2f get_color(Uint8 c)

static inline vector2f LoadLightColorUVoffset(const std::string &spec)
{
std::vector<float> v(2);
SplitSpec(spec, v);
std::vector<float> 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]);
}

Expand Down
36 changes: 13 additions & 23 deletions src/core/StringUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -185,32 +185,22 @@ const char *pi_strcasestr(const char *haystack, const char *needle)
}
}

std::vector<std::string> SplitString(const std::string &source, const std::string &delim)
size_t SplitString::step(std::string_view str)
{
bool stringSplitted = false;
std::vector<std::string> 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)
Expand Down
122 changes: 81 additions & 41 deletions src/core/StringUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <string>
#include <string_view>
#include <vector>
#include <iterator>
#include <cstdint>

#ifdef _MSC_VER
Expand Down Expand Up @@ -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<int> &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<typename T = std::string_view>
std::vector<T> to_vector() {
std::vector<T> 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<float> &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<typename Container, typename Predicate>
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<std::string> 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);
Expand Down
4 changes: 2 additions & 2 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<std::string> keyValue = SplitString(modeopt, "=");
std::vector<std::string> keyValue = SplitString(modeopt, "=").to_vector<std::string>();

// if found value
if (keyValue.size() == 2) {
Expand Down Expand Up @@ -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<std::string> keyValue = SplitString(arg, "=");
std::vector<std::string> keyValue = SplitString(arg, "=").to_vector<std::string>();

// if there no key and value || key is empty || value is empty
if (keyValue.size() != 2 || keyValue[0].empty() || keyValue[1].empty()) {
Expand Down

0 comments on commit 6775451

Please sign in to comment.