diff --git a/source/mijin/util/string.hpp b/source/mijin/util/string.hpp index df7646f..76752a7 100644 --- a/source/mijin/util/string.hpp +++ b/source/mijin/util/string.hpp @@ -5,7 +5,7 @@ #define MIJIN_UTIL_STRING_HPP_INCLUDED 1 #include -#include +#include #include #include #include @@ -28,6 +28,12 @@ namespace mijin // public types // +struct SplitOptions +{ + std::size_t limitParts = std::numeric_limits::max(); + bool ignoreEmpty = true; +}; + // // public functions // @@ -56,11 +62,74 @@ namespace detail { template std::vector> splitImpl(std::basic_string_view stringView, - std::basic_string_view seperator) + std::basic_string_view separator, + const SplitOptions& options) { - return std::views::split(stringView, seperator) - | std::views::transform([](auto val) { return std::string_view(val); }) - | std::ranges::to(); + using sv_t = std::basic_string_view; + + MIJIN_ASSERT(options.limitParts > 0, "Cannot split to zero parts."); + MIJIN_ASSERT(!separator.empty(), "Separator cannot be empty."); + + if (separator.empty()) + { + return {}; + } + + std::vector result; + if (options.limitParts <= 1) + { + result.push_back(stringView); + return result; + } + auto start = stringView.begin(); + auto pos = start; + const auto end = stringView.end(); + + auto seperatorFound = [&]() + { + return pos <= (end - separator.size()) && sv_t(pos, pos + separator.size()) == separator; + }; + + while (pos != end) + { + if (seperatorFound()) + { + if (!options.ignoreEmpty || pos != start) + { + result.emplace_back(start, pos); + } + start = pos = (pos + separator.size()); + if (result.size() == options.limitParts - 1) + { + if (options.ignoreEmpty) + { + while (seperatorFound()) + { + pos += separator.size(); + } + } + result.emplace_back(pos, end); + return result; + } + if (!options.ignoreEmpty && pos == end) + { + // skipped a separator at the very end, add an empty entry + result.emplace_back(""); + return result; + } + } + else + { + ++pos; + } + } + + if (start != end) + { + result.emplace_back(start, end); + } + + return result; } template @@ -87,9 +156,10 @@ static const TChar SPACE = TChar(' '); } template -[[nodiscard]] auto split(TLeft&& left, TRight&& right) +[[nodiscard]] auto split(TLeft&& stringView, TRight&& separator, const SplitOptions& options = {}) { - return detail::splitImpl(std::basic_string_view(std::forward(left)), std::basic_string_view(std::forward(right))); + return detail::splitImpl(std::basic_string_view(std::forward(stringView)), + std::basic_string_view(std::forward(separator)), options); } template