Add an utility: in-place string replacement
authorqining <qining@google.com>
Wed, 27 Jul 2016 21:22:45 +0000 (17:22 -0400)
committerqining <qining@google.com>
Thu, 28 Jul 2016 16:11:34 +0000 (12:11 -0400)
test/opt/pass_utils.cpp
test/opt/pass_utils.h
test/opt/test_utils.cpp

index a6f2374..68bc1a4 100644 (file)
@@ -43,7 +43,27 @@ const char* kDebugOpcodes[] = {
     // clang-format on
 };
 
-// Returns true if the given string contains any debug opcode substring.
+}  // anonymous namespace
+
+namespace spvtools {
+
+bool FindAndReplace(std::string* process_str, const std::string find_str,
+                    const std::string replace_str) {
+  if (process_str->empty() || find_str.empty()) {
+    return false;
+  }
+  bool replaced = false;
+  // Note this algorithm has quadratic time complexity. It is OK for test cases
+  // with short strings, but might not fit in other contexts.
+  for (size_t pos = process_str->find(find_str, 0); pos != std::string::npos;
+       pos = process_str->find(find_str, pos)) {
+    process_str->replace(pos, find_str.length(), replace_str);
+    pos += replace_str.length();
+    replaced = true;
+  }
+  return replaced;
+}
+
 bool ContainsDebugOpcode(const char* inst) {
   return std::any_of(std::begin(kDebugOpcodes), std::end(kDebugOpcodes),
                      [inst](const char* op) {
@@ -51,10 +71,6 @@ bool ContainsDebugOpcode(const char* inst) {
                      });
 }
 
-}  // anonymous namespace
-
-namespace spvtools {
-
 std::string SelectiveJoin(const std::vector<const char*>& strings,
                           const std::function<bool(const char*)>& skip_dictator,
                           char delimiter) {
index 6e047db..f504eb5 100644 (file)
 
 namespace spvtools {
 
+// In-place substring replacement. Finds the |find_str| in the |process_str|
+// and replaces the found substring with |replace_str|. Returns true if at
+// least one replacement is done successfully, returns false otherwise. The
+// replaced substring won't be processed again, which means: If the
+// |replace_str| has |find_str| as its substring, that newly replaced part of
+// |process_str| won't be processed again.
+bool FindAndReplace(std::string* process_str, const std::string find_str,
+                    const std::string replace_str);
+
+// Returns true if the given string contains any debug opcode substring.
+bool ContainsDebugOpcode(const char* inst);
+
 // Returns the concatenated string from a vector of |strings|, with postfixing
 // each string with the given |delimiter|. if the |skip_dictator| returns true
 // for an original string, that string will be omitted.
index 4a96559..e9ebb31 100644 (file)
@@ -55,4 +55,64 @@ TEST(JoinNonDebugInsts, Cases) {
                  "the only remaining string"}));
 }
 
+namespace {
+struct SubstringReplacementTestCase {
+  const char* orig_str;
+  const char* find_substr;
+  const char* replace_substr;
+  const char* expected_str;
+  bool replace_should_succeed;
+};
+}
+using FindAndReplaceTest =
+    ::testing::TestWithParam<SubstringReplacementTestCase>;
+
+TEST_P(FindAndReplaceTest, SubstringReplacement) {
+  auto process = std::string(GetParam().orig_str);
+  EXPECT_EQ(GetParam().replace_should_succeed,
+            FindAndReplace(&process, GetParam().find_substr,
+                           GetParam().replace_substr))
+      << "Original string: " << GetParam().orig_str
+      << " replace: " << GetParam().find_substr
+      << " to: " << GetParam().replace_substr
+      << " should returns: " << GetParam().replace_should_succeed;
+  EXPECT_STREQ(GetParam().expected_str, process.c_str())
+      << "Original string: " << GetParam().orig_str
+      << " replace: " << GetParam().find_substr
+      << " to: " << GetParam().replace_substr
+      << " expected string: " << GetParam().expected_str;
+}
+
+INSTANTIATE_TEST_CASE_P(
+    SubstringReplacement, FindAndReplaceTest,
+    ::testing::ValuesIn(std::vector<SubstringReplacementTestCase>({
+        // orig string, find substring, replace substring, expected string,
+        // replacement happened
+        {"", "", "", "", false},
+        {"", "b", "", "", false},
+        {"", "", "c", "", false},
+        {"", "a", "b", "", false},
+
+        {"a", "", "c", "a", false},
+        {"a", "b", "c", "a", false},
+        {"a", "b", "", "a", false},
+        {"a", "a", "", "", true},
+        {"a", "a", "b", "b", true},
+
+        {"ab", "a", "b", "bb", true},
+        {"ab", "a", "", "b", true},
+        {"ab", "b", "", "a", true},
+        {"ab", "ab", "", "", true},
+        {"ab", "ab", "cd", "cd", true},
+        {"bc", "abc", "efg", "bc", false},
+
+        {"abc", "ab", "bc", "bcc", true},
+        {"abc", "ab", "", "c", true},
+        {"abc", "bc", "", "a", true},
+        {"abc", "bc", "d", "ad", true},
+        {"abc", "a", "123", "123bc", true},
+        {"abc", "ab", "a", "ac", true},
+        {"abc", "a", "aab", "aabbc", true},
+        {"abc", "abcd", "efg", "abc", false},
+    })));
 }  // anonymous namespace