From 486e93f418413dfad211bd349863b1991b264d53 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Wed, 30 Apr 2025 23:18:46 +0200 Subject: [PATCH] Fix fmt::replace_all with empty from and add unit tests --- Utilities/StrFmt.cpp | 6 ++ Utilities/StrUtil.h | 23 ++++- rpcs3/tests/test_fmt.cpp | 195 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 223 insertions(+), 1 deletion(-) diff --git a/Utilities/StrFmt.cpp b/Utilities/StrFmt.cpp index 0b9baa048e..ec69294934 100644 --- a/Utilities/StrFmt.cpp +++ b/Utilities/StrFmt.cpp @@ -748,6 +748,12 @@ void fmt::raw_append(std::string& out, const char* fmt, const fmt_type_info* sup std::string fmt::replace_all(std::string_view src, std::string_view from, std::string_view to, usz count) { + if (src.empty()) + return {}; + + if (from.empty() || count == 0) + return std::string(src); + std::string target; target.reserve(src.size() + to.size()); diff --git a/Utilities/StrUtil.h b/Utilities/StrUtil.h index 4614f6f623..2ef5a25ad7 100644 --- a/Utilities/StrUtil.h +++ b/Utilities/StrUtil.h @@ -39,11 +39,15 @@ std::string get_file_extension(const std::string& file_path); namespace fmt { - std::string replace_all(std::string_view src, std::string_view from, std::string_view to, usz count = -1); + // Replaces all occurrences of 'from' with 'to' until 'count' substrings were replaced. + std::string replace_all(std::string_view src, std::string_view from, std::string_view to, usz count = umax); template std::string replace_all(std::string src, const std::pair (&list)[list_size]) { + if constexpr (list_size == 0) + return src; + for (usz pos = 0; pos < src.length(); ++pos) { for (usz i = 0; i < list_size; ++i) @@ -71,6 +75,9 @@ namespace fmt template std::string replace_all(std::string src, const std::pair> (&list)[list_size]) { + if constexpr (list_size == 0) + return src; + for (usz pos = 0; pos < src.length(); ++pos) { for (usz i = 0; i < list_size; ++i) @@ -99,6 +106,9 @@ namespace fmt static inline std::string replace_all(std::string src, const std::vector>& list) { + if (list.empty()) + return src; + for (usz pos = 0; pos < src.length(); ++pos) { for (usz i = 0; i < list.size(); ++i) @@ -123,9 +133,16 @@ namespace fmt return src; } + // Splits the string into a vector of strings using the separators. The vector may contain empty strings unless is_skip_empty is true. std::vector split(std::string_view source, std::initializer_list separators, bool is_skip_empty = true); + + // Removes all preceding and trailing characters specified by 'values' from 'source'. std::string trim(const std::string& source, std::string_view values = " \t"); + + // Removes all preceding characters specified by 'values' from 'source'. std::string trim_front(const std::string& source, std::string_view values = " \t"); + + // Removes all trailing characters specified by 'values' from 'source'. void trim_back(std::string& source, std::string_view values = " \t"); template @@ -175,9 +192,13 @@ namespace fmt return result; } + // Returns the string transformed to uppercase std::string to_upper(std::string_view string); + + // Returns the string transformed to lowercase std::string to_lower(std::string_view string); + // Returns the string shortened to length std::string truncate(std::string_view src, usz length); bool match(const std::string& source, const std::string& mask); diff --git a/rpcs3/tests/test_fmt.cpp b/rpcs3/tests/test_fmt.cpp index 5ffa842567..f7cd8c7f58 100644 --- a/rpcs3/tests/test_fmt.cpp +++ b/rpcs3/tests/test_fmt.cpp @@ -145,6 +145,201 @@ namespace fmt EXPECT_EQ(str, fmt::truncate(str, str.size() + 1)); } + TEST(StrUtil, test_replace_all) + { + EXPECT_EQ(""s, fmt::replace_all("", "", "")); + EXPECT_EQ(""s, fmt::replace_all("", "", " ")); + EXPECT_EQ(""s, fmt::replace_all("", "", "word")); + EXPECT_EQ(""s, fmt::replace_all("", " ", "")); + EXPECT_EQ(""s, fmt::replace_all("", "word", "")); + EXPECT_EQ(""s, fmt::replace_all("", "word", "drow")); + + EXPECT_EQ(" "s, fmt::replace_all(" ", "", "")); + EXPECT_EQ(" "s, fmt::replace_all(" ", "", " ")); + EXPECT_EQ(" "s, fmt::replace_all(" ", "", "word")); + EXPECT_EQ(""s, fmt::replace_all(" ", " ", "")); + EXPECT_EQ(" "s, fmt::replace_all(" ", "word", "")); + EXPECT_EQ(" "s, fmt::replace_all(" ", "word", "drow")); + + EXPECT_EQ("word"s, fmt::replace_all("word", "", "")); + EXPECT_EQ("word"s, fmt::replace_all("word", "", " ")); + EXPECT_EQ("word"s, fmt::replace_all("word", "", "word")); + EXPECT_EQ("word"s, fmt::replace_all("word", " ", "")); + EXPECT_EQ(""s, fmt::replace_all("word", "word", "")); + EXPECT_EQ("drow"s, fmt::replace_all("word", "word", "drow")); + + EXPECT_EQ(" word "s, fmt::replace_all(" word ", "", "")); + EXPECT_EQ(" word "s, fmt::replace_all(" word ", "", " ")); + EXPECT_EQ(" word "s, fmt::replace_all(" word ", "", "word")); + EXPECT_EQ("word"s, fmt::replace_all(" word ", " ", "")); + EXPECT_EQ(" "s, fmt::replace_all(" word ", "word", "")); + EXPECT_EQ(" drow "s, fmt::replace_all(" word ", "word", "drow")); + + EXPECT_EQ("word word"s, fmt::replace_all("word word", "", "")); + EXPECT_EQ("word word"s, fmt::replace_all("word word", "", " ")); + EXPECT_EQ("word word"s, fmt::replace_all("word word", "", "word")); + EXPECT_EQ("wordword"s, fmt::replace_all("word word", " ", "")); + EXPECT_EQ(" "s, fmt::replace_all("word word", "word", "")); + EXPECT_EQ("drow drow"s, fmt::replace_all("word word", "word", "drow")); + + // Test count + EXPECT_EQ("word word word"s, fmt::replace_all("word word word", "word", "drow", 0)); + EXPECT_EQ("drow word word"s, fmt::replace_all("word word word", "word", "drow", 1)); + EXPECT_EQ("drow drow word"s, fmt::replace_all("word word word", "word", "drow", 2)); + EXPECT_EQ("drow drow drow"s, fmt::replace_all("word word word", "word", "drow", 3)); + EXPECT_EQ("drow drow drow"s, fmt::replace_all("word word word", "word", "drow", umax)); + EXPECT_EQ("drow drow drow"s, fmt::replace_all("word word word", "word", "drow", -1)); + } + + TEST(StrUtil, test_split) + { + using vec = std::vector; + + EXPECT_EQ(vec{""}, fmt::split("", {}, false)); + EXPECT_EQ(vec{""}, fmt::split("", {""}, false)); + EXPECT_EQ(vec{""}, fmt::split("", {" "}, false)); + EXPECT_EQ(vec{""}, fmt::split("", {"a"}, false)); + EXPECT_EQ(vec{""}, fmt::split("", {"a "}, false)); + EXPECT_EQ(vec{""}, fmt::split("", {"a b"}, false)); + EXPECT_EQ(vec{""}, fmt::split("", {"a", " "}, false)); + EXPECT_EQ(vec{""}, fmt::split("", {"a", " ", "b"}, false)); + + EXPECT_EQ(vec{" "}, fmt::split(" ", {}, false)); + EXPECT_EQ(vec{" "}, fmt::split(" ", {""}, false)); + EXPECT_EQ(vec{""}, fmt::split(" ", {" "}, false)); + EXPECT_EQ(vec{" "}, fmt::split(" ", {"a"}, false)); + EXPECT_EQ(vec{" "}, fmt::split(" ", {"a "}, false)); + EXPECT_EQ(vec{" "}, fmt::split(" ", {"a b"}, false)); + EXPECT_EQ(vec{""}, fmt::split(" ", {"a", " "}, false)); + EXPECT_EQ(vec{""}, fmt::split(" ", {"a", " ", "b"}, false)); + + EXPECT_EQ(vec{" "}, fmt::split(" ", {}, false)); + EXPECT_EQ(vec{" "}, fmt::split(" ", {""}, false)); + EXPECT_EQ(vec({"", ""}), fmt::split(" ", {" "}, false)); + EXPECT_EQ(vec{" "}, fmt::split(" ", {"a"}, false)); + EXPECT_EQ(vec{" "}, fmt::split(" ", {"a "}, false)); + EXPECT_EQ(vec{" "}, fmt::split(" ", {"a b"}, false)); + EXPECT_EQ(vec({"", ""}), fmt::split(" ", {"a", " "}, false)); + EXPECT_EQ(vec({"", ""}), fmt::split(" ", {"a", " ", "b"}, false)); + + EXPECT_EQ(vec{"a"}, fmt::split("a", {}, false)); + EXPECT_EQ(vec{"a"}, fmt::split("a", {""}, false)); + EXPECT_EQ(vec{"a"}, fmt::split("a", {" "}, false)); + EXPECT_EQ(vec{""}, fmt::split("a", {"a"}, false)); + EXPECT_EQ(vec{"a"}, fmt::split("a", {"a "}, false)); + EXPECT_EQ(vec{"a"}, fmt::split("a", {"a b"}, false)); + EXPECT_EQ(vec{""}, fmt::split("a", {"a", " "}, false)); + EXPECT_EQ(vec{""}, fmt::split("a", {"a", " ", "b"}, false)); + + EXPECT_EQ(vec{"aa"}, fmt::split("aa", {}, false)); + EXPECT_EQ(vec{"aa"}, fmt::split("aa", {""}, false)); + EXPECT_EQ(vec{"aa"}, fmt::split("aa", {" "}, false)); + EXPECT_EQ(vec({"", ""}), fmt::split("aa", {"a"}, false)); + EXPECT_EQ(vec{"aa"}, fmt::split("aa", {"a "}, false)); + EXPECT_EQ(vec{"aa"}, fmt::split("aa", {"a b"}, false)); + EXPECT_EQ(vec({"", ""}), fmt::split("aa", {"a", " "}, false)); + EXPECT_EQ(vec({"", ""}), fmt::split("aa", {"a", " ", "b"}, false)); + + EXPECT_EQ(vec{"a b"}, fmt::split("a b", {}, false)); + EXPECT_EQ(vec{"a b"}, fmt::split("a b", {""}, false)); + EXPECT_EQ(vec({"a", "b"}), fmt::split("a b", {" "}, false)); + EXPECT_EQ(vec({"", " b"}), fmt::split("a b", {"a"}, false)); + EXPECT_EQ(vec({"", "b"}), fmt::split("a b", {"a "}, false)); + EXPECT_EQ(vec{""}, fmt::split("a b", {"a b"}, false)); + EXPECT_EQ(vec({"", "", "b"}), fmt::split("a b", {"a", " "}, false)); + EXPECT_EQ(vec({"", "", ""}), fmt::split("a b", {"a", " ", "b"}, false)); + + EXPECT_EQ(vec{"a b c c b a"}, fmt::split("a b c c b a", {}, false)); + EXPECT_EQ(vec{"a b c c b a"}, fmt::split("a b c c b a", {""}, false)); + EXPECT_EQ(vec({"a", "b", "c", "c", "b", "a"}), fmt::split("a b c c b a", {" "}, false)); + EXPECT_EQ(vec({"", " b c c b "}), fmt::split("a b c c b a", {"a"}, false)); + EXPECT_EQ(vec({"", "b c c b a"}), fmt::split("a b c c b a", {"a "}, false)); + EXPECT_EQ(vec({"", " c c b a"}), fmt::split("a b c c b a", {"a b"}, false)); + EXPECT_EQ(vec({"", "", "b", "c", "c", "b", ""}), fmt::split("a b c c b a", {"a", " "}, false)); + EXPECT_EQ(vec({"", "", "", "", "c", "c", "", "", ""}), fmt::split("a b c c b a", {"a", " ", "b"}, false)); + + EXPECT_EQ(vec{" This is a test! "}, fmt::split(" This is a test! ", {}, false)); + EXPECT_EQ(vec{" This is a test! "}, fmt::split(" This is a test! ", {""}, false)); + EXPECT_EQ(vec({"", "This", "is", "a", "test!"}), fmt::split(" This is a test! ", {" "}, false)); + EXPECT_EQ(vec({" This is ", " test! "}), fmt::split(" This is a test! ", {"a"}, false)); + EXPECT_EQ(vec({" This is ", "test! "}), fmt::split(" This is a test! ", {"a "}, false)); + EXPECT_EQ(vec{" This is a test! "}, fmt::split(" This is a test! ", {"a b"}, false)); + EXPECT_EQ(vec({"", "This", "is", "", "", "test!"}), fmt::split(" This is a test! ", {"a", " "}, false)); + EXPECT_EQ(vec({"", "This", "is", "", "", "test!"}), fmt::split(" This is a test! ", {"a", " ", "b"}, false)); + + EXPECT_EQ(vec{}, fmt::split("", {}, true)); + EXPECT_EQ(vec{}, fmt::split("", {""}, true)); + EXPECT_EQ(vec{}, fmt::split("", {" "}, true)); + EXPECT_EQ(vec{}, fmt::split("", {"a"}, true)); + EXPECT_EQ(vec{}, fmt::split("", {"a "}, true)); + EXPECT_EQ(vec{}, fmt::split("", {"a b"}, true)); + EXPECT_EQ(vec{}, fmt::split("", {"a", " "}, true)); + EXPECT_EQ(vec{}, fmt::split("", {"a", " ", "b"}, true)); + + EXPECT_EQ(vec{" "}, fmt::split(" ", {}, true)); + EXPECT_EQ(vec{" "}, fmt::split(" ", {""}, true)); + EXPECT_EQ(vec{}, fmt::split(" ", {" "}, true)); + EXPECT_EQ(vec{" "}, fmt::split(" ", {"a"}, true)); + EXPECT_EQ(vec{" "}, fmt::split(" ", {"a "}, true)); + EXPECT_EQ(vec{" "}, fmt::split(" ", {"a b"}, true)); + EXPECT_EQ(vec{}, fmt::split(" ", {"a", " "}, true)); + EXPECT_EQ(vec{}, fmt::split(" ", {"a", " ", "b"}, true)); + + EXPECT_EQ(vec{" "}, fmt::split(" ", {}, true)); + EXPECT_EQ(vec{" "}, fmt::split(" ", {""}, true)); + EXPECT_EQ(vec{}, fmt::split(" ", {" "}, true)); + EXPECT_EQ(vec{" "}, fmt::split(" ", {"a"}, true)); + EXPECT_EQ(vec{" "}, fmt::split(" ", {"a "}, true)); + EXPECT_EQ(vec{" "}, fmt::split(" ", {"a b"}, true)); + EXPECT_EQ(vec{}, fmt::split(" ", {"a", " "}, true)); + EXPECT_EQ(vec{}, fmt::split(" ", {"a", " ", "b"}, true)); + + EXPECT_EQ(vec{"a"}, fmt::split("a", {}, true)); + EXPECT_EQ(vec{"a"}, fmt::split("a", {""}, true)); + EXPECT_EQ(vec{"a"}, fmt::split("a", {" "}, true)); + EXPECT_EQ(vec{}, fmt::split("a", {"a"}, true)); + EXPECT_EQ(vec{"a"}, fmt::split("a", {"a "}, true)); + EXPECT_EQ(vec{"a"}, fmt::split("a", {"a b"}, true)); + EXPECT_EQ(vec{}, fmt::split("a", {"a", " "}, true)); + EXPECT_EQ(vec{}, fmt::split("a", {"a", " ", "b"}, true)); + + EXPECT_EQ(vec{"aa"}, fmt::split("aa", {}, true)); + EXPECT_EQ(vec{"aa"}, fmt::split("aa", {""}, true)); + EXPECT_EQ(vec{"aa"}, fmt::split("aa", {" "}, true)); + EXPECT_EQ(vec{}, fmt::split("aa", {"a"}, true)); + EXPECT_EQ(vec{"aa"}, fmt::split("aa", {"a "}, true)); + EXPECT_EQ(vec{"aa"}, fmt::split("aa", {"a b"}, true)); + EXPECT_EQ(vec{}, fmt::split("aa", {"a", " "}, true)); + EXPECT_EQ(vec{}, fmt::split("aa", {"a", " ", "b"}, true)); + + EXPECT_EQ(vec{"a b"}, fmt::split("a b", {}, true)); + EXPECT_EQ(vec{"a b"}, fmt::split("a b", {""}, true)); + EXPECT_EQ(vec({"a", "b"}), fmt::split("a b", {" "}, true)); + EXPECT_EQ(vec{" b"}, fmt::split("a b", {"a"}, true)); + EXPECT_EQ(vec{"b"}, fmt::split("a b", {"a "}, true)); + EXPECT_EQ(vec{}, fmt::split("a b", {"a b"}, true)); + EXPECT_EQ(vec{"b"}, fmt::split("a b", {"a", " "}, true)); + EXPECT_EQ(vec{}, fmt::split("a b", {"a", " ", "b"}, true)); + + EXPECT_EQ(vec{"a b c c b a"}, fmt::split("a b c c b a", {}, true)); + EXPECT_EQ(vec{"a b c c b a"}, fmt::split("a b c c b a", {""}, true)); + EXPECT_EQ(vec({"a", "b", "c", "c", "b", "a"}), fmt::split("a b c c b a", {" "}, true)); + EXPECT_EQ(vec{" b c c b "}, fmt::split("a b c c b a", {"a"}, true)); + EXPECT_EQ(vec{"b c c b a"}, fmt::split("a b c c b a", {"a "}, true)); + EXPECT_EQ(vec{" c c b a"}, fmt::split("a b c c b a", {"a b"}, true)); + EXPECT_EQ(vec({"b", "c", "c", "b"}), fmt::split("a b c c b a", {"a", " "}, true)); + EXPECT_EQ(vec({"c", "c"}), fmt::split("a b c c b a", {"a", " ", "b"}, true)); + + EXPECT_EQ(vec{" This is a test! "}, fmt::split(" This is a test! ", {}, true)); + EXPECT_EQ(vec{" This is a test! "}, fmt::split(" This is a test! ", {""}, true)); + EXPECT_EQ(vec({"This", "is", "a", "test!"}), fmt::split(" This is a test! ", {" "}, true)); + EXPECT_EQ(vec({" This is ", " test! "}), fmt::split(" This is a test! ", {"a"}, true)); + EXPECT_EQ(vec({" This is ", "test! "}), fmt::split(" This is a test! ", {"a "}, true)); + EXPECT_EQ(vec{" This is a test! "}, fmt::split(" This is a test! ", {"a b"}, true)); + EXPECT_EQ(vec({"This", "is", "test!"}), fmt::split(" This is a test! ", {"a", " "}, true)); + EXPECT_EQ(vec({"This", "is", "test!"}), fmt::split(" This is a test! ", {"a", " ", "b"}, true)); + } + TEST(StrUtil, test_get_file_extension) { EXPECT_EQ(""s, get_file_extension(""));