Make ConfigWriter simpler

- Remove varg WriteLine
- No prefix
- Add WriteVars for writing multiple lines using "="/ "+="

This refactoring is a prep for removing "ResolveVariables()" which is very slow.

Bug: 149020353
Test: atest --test-mapping system/linkerconfig
Change-Id: I6bf2204a9ac0794b0bdf8028999a3543821a26be
diff --git a/modules/configuration.cc b/modules/configuration.cc
index 555ca99..294ed0f 100644
--- a/modules/configuration.cc
+++ b/modules/configuration.cc
@@ -38,8 +38,7 @@
                    << " will be ignored.";
     } else {
       resolved_dirs[resolved_dir] = dir_to_section.second;
-      writer.WriteLine(
-          "dir.%s = %s", dir_to_section.second.c_str(), resolved_dir.c_str());
+      writer.WriteLine("dir." + dir_to_section.second + " = " + resolved_dir);
     }
   }
 
diff --git a/modules/configwriter.cc b/modules/configwriter.cc
index 0b2db69..9c1ae0e 100644
--- a/modules/configwriter.cc
+++ b/modules/configwriter.cc
@@ -16,9 +16,6 @@
 
 #include "linkerconfig/configwriter.h"
 
-#include <cstdarg>
-#include <cstdio>
-
 #include "linkerconfig/log.h"
 #include "linkerconfig/variables.h"
 
@@ -26,45 +23,18 @@
 namespace linkerconfig {
 namespace modules {
 
-void ConfigWriter::SetPrefix(const std::string& prefix) {
-  prefix_ = prefix;
-}
-
-void ConfigWriter::ResetPrefix() {
-  prefix_ = "";
-}
-
-void ConfigWriter::WriteLine(const char* format, ...) {
-  va_list args_for_length, args;
-
-  va_start(args, format);
-  va_copy(args_for_length, args);
-
-  int length = vsnprintf(nullptr, 0, format, args_for_length);
-  va_end(args_for_length);
-
-  if (length < 0) {
-    LOG(ERROR) << "Failed to get length of the string with format " << format;
-    va_end(args);
-    return;
+void ConfigWriter::WriteVars(const std::string& var,
+                             const std::vector<std::string>& values) {
+  bool is_first = true;
+  for (const auto& value : values) {
+    WriteLine(var + (is_first ? " = " : " += ") + value);
+    is_first = false;
   }
-
-  std::unique_ptr<char[]> formatted_string(new char[length + 1]);
-
-  int res = vsnprintf(formatted_string.get(), length + 1, format, args);
-  va_end(args);
-
-  if (res < 0) {
-    LOG(ERROR) << "Failed to write a string with format " << format;
-    return;
-  }
-
-  WriteLine(std::string(formatted_string.get()));
 }
 
 void ConfigWriter::WriteLine(const std::string& line) {
-  auto resolved_line = Variables::ResolveVariables(prefix_ + line);
-  content_ << resolved_line << std::endl;
+  auto resolved_line = Variables::ResolveVariables(line);
+  content_ << resolved_line << '\n';
 }
 
 std::string ConfigWriter::ToString() {
diff --git a/modules/include/linkerconfig/configwriter.h b/modules/include/linkerconfig/configwriter.h
index 5f93e5f..a3016f7 100644
--- a/modules/include/linkerconfig/configwriter.h
+++ b/modules/include/linkerconfig/configwriter.h
@@ -17,6 +17,7 @@
 
 #include <sstream>
 #include <string>
+#include <vector>
 
 namespace android {
 namespace linkerconfig {
@@ -24,15 +25,12 @@
 
 class ConfigWriter {
  public:
-  void SetPrefix(const std::string& prefix);
-  void ResetPrefix();
+  void WriteVars(const std::string& var, const std::vector<std::string>& values);
   void WriteLine(const std::string& line);
-  void WriteLine(const char* format, ...);
   std::string ToString();
 
  private:
   std::stringstream content_;
-  std::string prefix_;
 };
 
 }  // namespace modules
diff --git a/modules/link.cc b/modules/link.cc
index 4c0e2e9..b22ce22 100644
--- a/modules/link.cc
+++ b/modules/link.cc
@@ -34,23 +34,16 @@
 }
 
 void Link::WriteConfig(ConfigWriter& writer) const {
-  writer.SetPrefix("namespace." + origin_namespace_ + ".link." +
-                   target_namespace_);
+  const auto prefix =
+      "namespace." + origin_namespace_ + ".link." + target_namespace_ + ".";
   if (allow_all_shared_libs_) {
-    writer.WriteLine(".allow_all_shared_libs = true");
+    writer.WriteLine(prefix + "allow_all_shared_libs = true");
   } else if (!shared_libs_.empty()) {
-    bool is_first = true;
-
-    for (auto& lib_name : shared_libs_) {
-      writer.WriteLine(
-          ".shared_libs %s %s", is_first ? "=" : "+=", lib_name.c_str());
-      is_first = false;
-    }
+    writer.WriteVars(prefix + "shared_libs", shared_libs_);
   } else {
     LOG(WARNING) << "Ignored empty shared libs link from " << origin_namespace_
                  << " to " << target_namespace_;
   }
-  writer.ResetPrefix();
 }
 
 }  // namespace modules
diff --git a/modules/namespace.cc b/modules/namespace.cc
index 81f84b9..b553c46 100644
--- a/modules/namespace.cc
+++ b/modules/namespace.cc
@@ -50,17 +50,6 @@
   ns.AddRequires(apex_info.require_libs);
 }
 
-void Namespace::WritePathString(ConfigWriter& writer,
-                                const std::string& path_type,
-                                const std::vector<std::string>& path_list) {
-  std::string prefix = path_type + ".paths ";
-  bool is_first = true;
-  for (auto& path : path_list) {
-    writer.WriteLine(prefix + (is_first ? "= " : "+= ") + path);
-    is_first = false;
-  }
-}
-
 Link& Namespace::GetLink(const std::string& target_namespace) {
   for (auto& link : links_) {
     if (link.To() == target_namespace) {
@@ -71,25 +60,19 @@
 }
 
 void Namespace::WriteConfig(ConfigWriter& writer) {
-  writer.SetPrefix("namespace." + name_ + ".");
+  const auto prefix = "namespace." + name_ + ".";
 
-  writer.WriteLine("isolated = %s", is_isolated_ ? "true" : "false");
+  writer.WriteLine(prefix + "isolated = " + (is_isolated_ ? "true" : "false"));
 
   if (is_visible_) {
-    writer.WriteLine("visible = true");
+    writer.WriteLine(prefix + "visible = true");
   }
 
-  WritePathString(writer, "search", search_paths_);
-  WritePathString(writer, "permitted", permitted_paths_);
-  WritePathString(writer, "asan.search", asan_search_paths_);
-  WritePathString(writer, "asan.permitted", asan_permitted_paths_);
-
-  bool is_first = true;
-  for (const auto& whitelisted : whitelisted_) {
-    writer.WriteLine(
-        "whitelisted %s %s", is_first ? "=" : "+=", whitelisted.c_str());
-    is_first = false;
-  }
+  writer.WriteVars(prefix + "search.paths", search_paths_);
+  writer.WriteVars(prefix + "permitted.paths", permitted_paths_);
+  writer.WriteVars(prefix + "asan.search.paths", asan_search_paths_);
+  writer.WriteVars(prefix + "asan.permitted.paths", asan_permitted_paths_);
+  writer.WriteVars(prefix + "whitelisted", whitelisted_);
 
   if (!links_.empty()) {
     std::vector<std::string> link_list;
@@ -97,14 +80,12 @@
     for (const auto& link : links_) {
       link_list.push_back(link.To());
     }
-    writer.WriteLine("links = " + android::base::Join(link_list, ","));
+    writer.WriteLine(prefix + "links = " + android::base::Join(link_list, ","));
 
     for (const auto& link : links_) {
       link.WriteConfig(writer);
     }
   }
-
-  writer.ResetPrefix();
 }
 
 void Namespace::AddSearchPath(const std::string& path, AsanPath path_from_asan) {
diff --git a/modules/section.cc b/modules/section.cc
index c665d56..325a3d3 100644
--- a/modules/section.cc
+++ b/modules/section.cc
@@ -33,7 +33,7 @@
 namespace linkerconfig {
 namespace modules {
 void Section::WriteConfig(ConfigWriter& writer) {
-  writer.WriteLine("[%s]", name_.c_str());
+  writer.WriteLine("[" + name_ + "]");
 
   std::sort(namespaces_.begin(),
             namespaces_.end(),
diff --git a/modules/tests/configwriter_test.cc b/modules/tests/configwriter_test.cc
index 4c5c9fa..23c903d 100644
--- a/modules/tests/configwriter_test.cc
+++ b/modules/tests/configwriter_test.cc
@@ -21,12 +21,6 @@
 
 constexpr const char* kSampleContent = "Lorem ipsum dolor sit amet";
 
-constexpr const char* kExpectedResultWithPrefix =
-    R"(Lorem ipsum dolor sit amet
-SAMPLE.TEST.Text : Lorem ipsum dolor sit amet
-end of context
-)";
-
 constexpr const char* kExpectedResultWithVariables =
     R"(/Value/Test
 /Invalid_Value/Path
@@ -41,24 +35,6 @@
   ASSERT_EQ(writer.ToString(), "Lorem ipsum dolor sit amet\n");
 }
 
-TEST(linkerconfig_configwriter, write_with_format) {
-  android::linkerconfig::modules::ConfigWriter writer;
-  writer.WriteLine("Sample text(%d) : %s", 10, kSampleContent);
-  ASSERT_EQ(writer.ToString(),
-            "Sample text(10) : Lorem ipsum dolor sit amet\n");
-}
-
-TEST(linkerconfig_configwriter, write_with_prefix) {
-  android::linkerconfig::modules::ConfigWriter writer;
-  writer.WriteLine(kSampleContent);
-  writer.SetPrefix("SAMPLE.TEST.");
-  writer.WriteLine("Text : %s", kSampleContent);
-  writer.ResetPrefix();
-  writer.WriteLine("end of context");
-
-  ASSERT_EQ(writer.ToString(), kExpectedResultWithPrefix);
-}
-
 TEST(linkerconfig_configwriter, replace_variable) {
   android::linkerconfig::modules::ConfigWriter writer;
 
@@ -70,4 +46,18 @@
   writer.WriteLine("/@{Invalid_Key:}/Path2");
 
   ASSERT_EQ(writer.ToString(), kExpectedResultWithVariables);
+}
+
+TEST(linkerconfig_configwriter, WriteVars) {
+  android::linkerconfig::modules::ConfigWriter writer;
+
+  writer.WriteVars("var1", {"value1", "value2"});
+  writer.WriteVars("var2", {"value1"});
+  writer.WriteVars("var3", {});
+
+  ASSERT_EQ(
+      "var1 = value1\n"
+      "var1 += value2\n"
+      "var2 = value1\n",
+      writer.ToString());
 }
\ No newline at end of file