From 7c33f8f7df321e2406f324f9aaa3890620a192e5 Mon Sep 17 00:00:00 2001 From: demostanis Date: Sat, 22 Oct 2022 16:31:59 +0200 Subject: [PATCH] AK: Add SplitBehavior::KeepTrailingSeparator with tests --- AK/String.cpp | 6 ++++-- AK/StringUtils.h | 9 ++++++++- AK/StringView.cpp | 3 ++- AK/StringView.h | 9 +++++++-- Tests/AK/TestStringView.cpp | 5 +++++ 5 files changed, 26 insertions(+), 6 deletions(-) diff --git a/AK/String.cpp b/AK/String.cpp index 2497e8c8b42..3c63b70b62a 100644 --- a/AK/String.cpp +++ b/AK/String.cpp @@ -110,12 +110,13 @@ Vector String::split_limit(char separator, size_t limit, SplitBehavior s Vector v; size_t substart = 0; bool keep_empty = has_flag(split_behavior, SplitBehavior::KeepEmpty); + bool keep_separator = has_flag(split_behavior, SplitBehavior::KeepTrailingSeparator); for (size_t i = 0; i < length() && (v.size() + 1) != limit; ++i) { char ch = characters()[i]; if (ch == separator) { size_t sublen = i - substart; if (sublen != 0 || keep_empty) - v.append(substring(substart, sublen)); + v.append(substring(substart, keep_separator ? sublen + 1 : sublen)); substart = i + 1; } } @@ -133,12 +134,13 @@ Vector String::split_view(Function separator, SplitBehav Vector v; size_t substart = 0; bool keep_empty = has_flag(split_behavior, SplitBehavior::KeepEmpty); + bool keep_separator = has_flag(split_behavior, SplitBehavior::KeepTrailingSeparator); for (size_t i = 0; i < length(); ++i) { char ch = characters()[i]; if (separator(ch)) { size_t sublen = i - substart; if (sublen != 0 || keep_empty) - v.append(substring_view(substart, sublen)); + v.append(substring_view(substart, keep_separator ? sublen + 1 : sublen)); substart = i + 1; } } diff --git a/AK/StringUtils.h b/AK/StringUtils.h index 60dde9c359b..883196ecfbe 100644 --- a/AK/StringUtils.h +++ b/AK/StringUtils.h @@ -40,10 +40,17 @@ enum class TrimWhitespace { }; enum class SplitBehavior : unsigned { + // Neither keep empty substrings nor keep the trailing separator. + // This is the default behavior if unspecified. Nothing = 0, + // If two separators follow each other without any characters - // in between, keep a "" in the resulting vector. + // in between, keep a "" in the resulting vector. (or only the + // separator if KeepTrailingSeparator is used) KeepEmpty = 1, + + // Do not strip off the separator at the end of the string. + KeepTrailingSeparator = 2, }; AK_ENUM_BITWISE_OPERATORS(SplitBehavior); diff --git a/AK/StringView.cpp b/AK/StringView.cpp index 1b906d7970f..3840e7c7f5a 100644 --- a/AK/StringView.cpp +++ b/AK/StringView.cpp @@ -272,12 +272,13 @@ Vector StringView::split_view_if(Function const& predica Vector v; size_t substart = 0; bool keep_empty = has_flag(split_behavior, SplitBehavior::KeepEmpty); + bool keep_separator = has_flag(split_behavior, SplitBehavior::KeepTrailingSeparator); for (size_t i = 0; i < length(); ++i) { char ch = characters_without_null_termination()[i]; if (predicate(ch)) { size_t sublen = i - substart; if (sublen != 0 || keep_empty) - v.append(substring_view(substart, sublen)); + v.append(substring_view(substart, keep_separator ? sublen + 1 : sublen)); substart = i + 1; } } diff --git a/AK/StringView.h b/AK/StringView.h index e9d58c31985..8e65691e5a3 100644 --- a/AK/StringView.h +++ b/AK/StringView.h @@ -170,11 +170,16 @@ public: auto maybe_separator_index = find(separator); bool keep_empty = has_flag(split_behavior, SplitBehavior::KeepEmpty); + bool keep_separator = has_flag(split_behavior, SplitBehavior::KeepTrailingSeparator); while (maybe_separator_index.has_value()) { auto separator_index = maybe_separator_index.value(); auto part_with_separator = view.substring_view(0, separator_index + separator.length()); - if (keep_empty || separator_index > 0) - callback(part_with_separator.substring_view(0, separator_index)); + if (keep_empty || separator_index > 0) { + if (keep_separator) + callback(part_with_separator); + else + callback(part_with_separator.substring_view(0, separator_index)); + } view = view.substring_view_starting_after_substring(part_with_separator); maybe_separator_index = view.find(separator); } diff --git a/Tests/AK/TestStringView.cpp b/Tests/AK/TestStringView.cpp index 6aafffe82ba..9d5bc18e0a6 100644 --- a/Tests/AK/TestStringView.cpp +++ b/Tests/AK/TestStringView.cpp @@ -163,6 +163,11 @@ TEST_CASE(split_view) EXPECT_EQ(test_string_view.split_view_if(predicate, SplitBehavior::KeepEmpty), Vector({ "a"sv, ""sv, "b"sv, "c"sv, "d"sv })); EXPECT_EQ(test_string_view.split_view_if(predicate), Vector({ "a"sv, "b"sv, "c"sv, "d"sv })); EXPECT_EQ(test_string_view.split_view_if(predicate, SplitBehavior::KeepEmpty), Vector({ "a"sv, ""sv, "b"sv, "c"sv, "d"sv })); + + test_string_view = "a,,,b"sv; + EXPECT_EQ(test_string_view.split_view(","sv, SplitBehavior::KeepEmpty), Vector({ "a"sv, ""sv, ""sv, "b"sv })); + EXPECT_EQ(test_string_view.split_view(","sv, SplitBehavior::KeepTrailingSeparator), Vector({ "a,"sv, "b"sv })); + EXPECT_EQ(test_string_view.split_view(","sv, SplitBehavior::KeepTrailingSeparator | SplitBehavior::KeepEmpty), Vector({ "a,"sv, ","sv, ","sv, "b"sv })); } TEST_CASE(constexpr_stuff)