Revert "Speed-up GN with custom OutputStream interface."

This reverts commit 2dd9331a704147be787823a169de51df8d3526bb.

Reason for revert: Breaks chromium build.

Original change's description:
> Speed-up GN with custom OutputStream interface.
>
> For legacy reasons, std::ostream is a very inefficient
> interface for appending text to an output stream, something
> that GN does a lot.
>
> This CL speeds GN by:
>
> - Introducing a new OutputStream abstract class to replace
>   std::ostream entirely. It provides the same API subset needed
>   by the rest of the code to minimize changes.
>   See gn/output_stream.h.
>
> - Adding StringOutputStream, a concrete OutputStream to store
>   the result into an std::stream. This replaces
>   std::ostringstream. See gn/output_stream.h.
>
> - Adding FileOutputStream, a concrete OutputStream to store
>   the result into a file. This replaces std::ofstream.
>   See gn/output_stream.h.
>
> - Making StringOutputBuffer an OutputStream derived class
>   as well, simplify its use as an output destination
>   for many calls.
>
> - Adjusting all call sites appropriately.
>
> - Replace `out << std::endl` statements with
>   `out << "\n"` as the type of `std::endl` is unspecified
>   by the standard and hard to guess at compile time for
>   implementing an OutputStream::operator<< overload.
>
> - Remove obsolete `#include <sstream>` statements
>   from the sources. Same for `<fstream>`.
>
> Benchmarking shows that on Linux, this saves about 6% of
> `gn gen` time for a small Fuchsia build graph
> (6.27s -> 5.90s), and that the stripped LTO-optimized
> executable, is reduced by about 50 kiB.
>
> Change-Id: I00c7e1db67c59ab57c64756382683159df0662a6
> Reviewed-on: https://gn-review.googlesource.com/c/gn/+/18140
> Reviewed-by: Dirk Pranke <dpranke@google.com>
> Commit-Queue: David Turner <digit@google.com>

TBR=dpranke@google.com,tikuta@google.com,digit@google.com,gn-scoped@luci-project-accounts.iam.gserviceaccount.com

Change-Id: I8c451f9b698f0e0fbcea843a903c5e6148e8df18
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Reviewed-on: https://gn-review.googlesource.com/c/gn/+/18160
Commit-Queue: Dirk Pranke <dpranke@google.com>
Reviewed-by: Dirk Pranke <dpranke@google.com>
Reviewed-by: Takuto Ikuta <tikuta@google.com>
diff --git a/build/gen.py b/build/gen.py
index e13d194..d1c649c 100755
--- a/build/gen.py
+++ b/build/gen.py
@@ -732,7 +732,6 @@
         'src/gn/operators.cc',
         'src/gn/output_conversion.cc',
         'src/gn/output_file.cc',
-        'src/gn/output_stream.cc',
         'src/gn/parse_node_value_adapter.cc',
         'src/gn/parse_tree.cc',
         'src/gn/parser.cc',
@@ -858,7 +857,6 @@
         'src/gn/ninja_toolchain_writer_unittest.cc',
         'src/gn/operators_unittest.cc',
         'src/gn/output_conversion_unittest.cc',
-        'src/gn/output_stream_unittest.cc',
         'src/gn/parse_tree_unittest.cc',
         'src/gn/parser_unittest.cc',
         'src/gn/path_output_unittest.cc',
diff --git a/src/gn/command_desc.cc b/src/gn/command_desc.cc
index 13a82db..b50d4e9 100644
--- a/src/gn/command_desc.cc
+++ b/src/gn/command_desc.cc
@@ -7,6 +7,7 @@
 #include <algorithm>
 #include <memory>
 #include <set>
+#include <sstream>
 
 #include "base/command_line.h"
 #include "base/json/json_writer.h"
diff --git a/src/gn/command_format.cc b/src/gn/command_format.cc
index 75c3741..1a6f9ec 100644
--- a/src/gn/command_format.cc
+++ b/src/gn/command_format.cc
@@ -6,6 +6,8 @@
 
 #include <stddef.h>
 
+#include <sstream>
+
 #include "base/command_line.h"
 #include "base/files/file_util.h"
 #include "base/json/json_reader.h"
@@ -15,7 +17,6 @@
 #include "gn/commands.h"
 #include "gn/filesystem_utils.h"
 #include "gn/input_file.h"
-#include "gn/output_stream.h"
 #include "gn/parser.h"
 #include "gn/scheduler.h"
 #include "gn/setup.h"
@@ -1239,7 +1240,7 @@
               std::string* output,
               std::string* dump_output) {
   if (dump_tree == TreeDumpMode::kPlainText) {
-    StringOutputStream os;
+    std::ostringstream os;
     RenderToText(root->GetJSONNode(), 0, os);
     *dump_output = os.str();
   } else if (dump_tree == TreeDumpMode::kJSON) {
diff --git a/src/gn/compile_commands_writer.cc b/src/gn/compile_commands_writer.cc
index 5dd8270..23d6029 100644
--- a/src/gn/compile_commands_writer.cc
+++ b/src/gn/compile_commands_writer.cc
@@ -4,6 +4,8 @@
 
 #include "gn/compile_commands_writer.h"
 
+#include <sstream>
+
 #include "base/json/string_escape.h"
 #include "base/strings/string_split.h"
 #include "base/strings/stringprintf.h"
@@ -14,7 +16,6 @@
 #include "gn/deps_iterator.h"
 #include "gn/escape.h"
 #include "gn/ninja_target_command_util.h"
-#include "gn/output_stream.h"
 #include "gn/path_output.h"
 #include "gn/string_output_buffer.h"
 #include "gn/substitution_writer.h"
@@ -61,7 +62,7 @@
                         const std::vector<T>& (ConfigValues::*getter)() const,
                         const Writer& writer) {
   std::string result;
-  StringOutputStream out;
+  std::ostringstream out;
   RecursiveTargetConfigToStream<T>(config, target, getter, writer, out);
   base::EscapeJSONString(out.str(), false, &result);
   return result;
@@ -101,7 +102,7 @@
                       const std::vector<std::string>& (ConfigValues::*getter)()
                           const) -> std::string {
     std::string result;
-    StringOutputStream out;
+    std::ostringstream out;
     WriteOneFlag(config, target, substitution, has_precompiled_headers,
                  tool_name, getter, opts, path_output, out,
                  /*write_substitution=*/false, /*indent=*/false);
@@ -132,12 +133,13 @@
 
 void WriteFile(const SourceFile& source,
                PathOutput& path_output,
-               OutputStream& out) {
+               std::ostream& out) {
+  std::ostringstream rel_source_path;
   out << "    \"file\": \"";
   path_output.WriteFile(out, source);
 }
 
-void WriteDirectory(std::string build_dir, OutputStream& out) {
+void WriteDirectory(std::string build_dir, std::ostream& out) {
   out << "\",";
   out << kPrettyPrintLineEnding;
   out << "    \"directory\": \"";
@@ -153,7 +155,7 @@
                   SourceFile::Type source_type,
                   const char* tool_name,
                   EscapeOptions opts,
-                  OutputStream& out) {
+                  std::ostream& out) {
   EscapeOptions no_quoting(opts);
   no_quoting.inhibit_quoting = true;
   const Tool* tool = target->toolchain()->GetTool(tool_name);
@@ -221,7 +223,7 @@
 
 void OutputJSON(const BuildSettings* build_settings,
                 std::vector<const Target*>& all_targets,
-                OutputStream& out) {
+                std::ostream& out) {
   out << '[';
   out << kPrettyPrintLineEnding;
   bool first = true;
@@ -289,8 +291,9 @@
 std::string CompileCommandsWriter::RenderJSON(
     const BuildSettings* build_settings,
     std::vector<const Target*>& all_targets) {
-  StringOutputStream json;
-  OutputJSON(build_settings, all_targets, json);
+  StringOutputBuffer json;
+  std::ostream out(&json);
+  OutputJSON(build_settings, all_targets, out);
   return json.str();
 }
 
@@ -307,7 +310,8 @@
     return false;
 
   StringOutputBuffer json;
-  OutputJSON(build_settings, to_write, json);
+  std::ostream output_to_json(&json);
+  OutputJSON(build_settings, to_write, output_to_json);
 
   return json.WriteToFileIfChanged(output_path, err);
 }
diff --git a/src/gn/compile_commands_writer_unittest.cc b/src/gn/compile_commands_writer_unittest.cc
index 7e2ca28..2f0294c 100644
--- a/src/gn/compile_commands_writer_unittest.cc
+++ b/src/gn/compile_commands_writer_unittest.cc
@@ -5,6 +5,7 @@
 #include "gn/compile_commands_writer.h"
 
 #include <memory>
+#include <sstream>
 #include <utility>
 
 #include "gn/config.h"
diff --git a/src/gn/config_values_extractors.cc b/src/gn/config_values_extractors.cc
index 58458ad..a369008 100644
--- a/src/gn/config_values_extractors.cc
+++ b/src/gn/config_values_extractors.cc
@@ -5,7 +5,6 @@
 #include "gn/config_values_extractors.h"
 
 #include "gn/escape.h"
-#include "gn/output_stream.h"
 
 namespace {
 
@@ -14,7 +13,7 @@
   explicit EscapedStringWriter(const EscapeOptions& escape_options)
       : escape_options_(escape_options) {}
 
-  void operator()(const std::string& s, OutputStream& out) const {
+  void operator()(const std::string& s, std::ostream& out) const {
     out << " ";
     EscapeStringToStream(out, s, escape_options_);
   }
@@ -30,7 +29,7 @@
     const Target* target,
     const std::vector<std::string>& (ConfigValues::*getter)() const,
     const EscapeOptions& escape_options,
-    OutputStream& out) {
+    std::ostream& out) {
   RecursiveTargetConfigToStream(config, target, getter,
                                 EscapedStringWriter(escape_options), out);
 }
diff --git a/src/gn/config_values_extractors.h b/src/gn/config_values_extractors.h
index ae4c862..45b1f25 100644
--- a/src/gn/config_values_extractors.h
+++ b/src/gn/config_values_extractors.h
@@ -17,8 +17,6 @@
 
 struct EscapeOptions;
 
-class OutputStream;
-
 // Provides a way to iterate through all ConfigValues applying to a given
 // target. This is more complicated than normal because the target has a list
 // of configs applying to it, and also config values on the target itself.
@@ -89,7 +87,7 @@
     const Target* target,
     const std::vector<T>& (ConfigValues::*getter)() const,
     const Writer& writer,
-    OutputStream& out) {
+    std::ostream& out) {
   std::set<T> seen;
   for (ConfigValuesIterator iter(target); !iter.done(); iter.Next()) {
     const std::vector<T>& values = ((iter.cur()).*getter)();
@@ -115,6 +113,6 @@
     const Target* target,
     const std::vector<std::string>& (ConfigValues::*getter)() const,
     const EscapeOptions& escape_options,
-    OutputStream& out);
+    std::ostream& out);
 
 #endif  // TOOLS_GN_CONFIG_VALUES_EXTRACTORS_H_
diff --git a/src/gn/config_values_extractors_unittest.cc b/src/gn/config_values_extractors_unittest.cc
index c27107d..3db4bff 100644
--- a/src/gn/config_values_extractors_unittest.cc
+++ b/src/gn/config_values_extractors_unittest.cc
@@ -2,10 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "gn/config_values_extractors.h"
+#include <sstream>
 
 #include "gn/config.h"
-#include "gn/output_stream.h"
+#include "gn/config_values_extractors.h"
 #include "gn/target.h"
 #include "gn/test_with_scope.h"
 #include "util/test/test.h"
@@ -13,13 +13,13 @@
 namespace {
 
 struct FlagWriter {
-  void operator()(const std::string& dir, OutputStream& out) const {
+  void operator()(const std::string& dir, std::ostream& out) const {
     out << dir << " ";
   }
 };
 
 struct IncludeWriter {
-  void operator()(const SourceDir& dir, OutputStream& out) const {
+  void operator()(const SourceDir& dir, std::ostream& out) const {
     out << dir.value() << " ";
   }
 };
@@ -128,7 +128,7 @@
   ASSERT_TRUE(target.OnResolved(&err));
 
   // Verify cflags by serializing.
-  StringOutputStream flag_out;
+  std::ostringstream flag_out;
   FlagWriter flag_writer;
   RecursiveTargetConfigToStream<std::string, FlagWriter>(
       kRecursiveWriterKeepDuplicates, &target, &ConfigValues::cflags,
@@ -138,7 +138,7 @@
             "--dep1-all --dep1-all-sub --dep2-all --dep2-all --dep1-direct ");
 
   // Verify include dirs by serializing.
-  StringOutputStream include_out;
+  std::ostringstream include_out;
   IncludeWriter include_writer;
   RecursiveTargetConfigToStream<SourceDir, IncludeWriter>(
       kRecursiveWriterSkipDuplicates, &target, &ConfigValues::include_dirs,
diff --git a/src/gn/eclipse_writer.cc b/src/gn/eclipse_writer.cc
index 5da2b12..0067f6d 100644
--- a/src/gn/eclipse_writer.cc
+++ b/src/gn/eclipse_writer.cc
@@ -4,6 +4,7 @@
 
 #include "gn/eclipse_writer.h"
 
+#include <fstream>
 #include <memory>
 
 #include "base/files/file_path.h"
@@ -11,7 +12,6 @@
 #include "gn/config_values_extractors.h"
 #include "gn/filesystem_utils.h"
 #include "gn/loader.h"
-#include "gn/output_stream.h"
 #include "gn/xml_element_writer.h"
 
 namespace {
@@ -37,7 +37,7 @@
 
 EclipseWriter::EclipseWriter(const BuildSettings* build_settings,
                              const Builder& builder,
-                             OutputStream& out)
+                             std::ostream& out)
     : build_settings_(build_settings), builder_(builder), out_(out) {
   languages_.push_back("C++ Source File");
   languages_.push_back("C Source File");
@@ -55,7 +55,9 @@
                                     Err* err) {
   base::FilePath file = build_settings->GetFullPath(build_settings->build_dir())
                             .AppendASCII("eclipse-cdt-settings.xml");
-  FileOutputStream file_out(FilePathToUTF8(file).c_str());
+  std::ofstream file_out;
+  file_out.open(FilePathToUTF8(file).c_str(),
+                std::ios_base::out | std::ios_base::binary);
   if (file_out.fail()) {
     *err =
         Err(Location(), "Couldn't open eclipse-cdt-settings.xml for writing");
@@ -117,7 +119,7 @@
 }
 
 void EclipseWriter::WriteCDTSettings() {
-  out_ << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
+  out_ << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << std::endl;
   XmlElementWriter cdt_properties_element(out_, "cdtprojectproperties",
                                           XmlAttributes());
 
diff --git a/src/gn/eclipse_writer.h b/src/gn/eclipse_writer.h
index 17b515f..6b71a51 100644
--- a/src/gn/eclipse_writer.h
+++ b/src/gn/eclipse_writer.h
@@ -14,7 +14,6 @@
 class BuildSettings;
 class Builder;
 class Err;
-class OutputStream;
 class Target;
 
 class EclipseWriter {
@@ -26,7 +25,7 @@
  private:
   EclipseWriter(const BuildSettings* build_settings,
                 const Builder& builder,
-                OutputStream& out);
+                std::ostream& out);
   ~EclipseWriter();
 
   void Run();
@@ -49,7 +48,7 @@
   const Builder& builder_;
 
   // The output stream for the settings file.
-  OutputStream& out_;
+  std::ostream& out_;
 
   // Eclipse languages for which the include dirs and defines apply.
   std::vector<std::string> languages_;
diff --git a/src/gn/escape.cc b/src/gn/escape.cc
index 56ae6de..687c92a 100644
--- a/src/gn/escape.cc
+++ b/src/gn/escape.cc
@@ -11,7 +11,6 @@
 #include "base/compiler_specific.h"
 #include "base/json/string_escape.h"
 #include "base/logging.h"
-#include "gn/output_stream.h"
 #include "util/build_config.h"
 
 namespace {
@@ -295,14 +294,14 @@
                      EscapeStringToString(str, options, dest, needed_quoting));
 }
 
-void EscapeStringToStream(OutputStream& out,
+void EscapeStringToStream(std::ostream& out,
                           std::string_view str,
                           const EscapeOptions& options) {
   StackOrHeapBuffer dest(str.size() * kMaxEscapedCharsPerChar);
   out.write(dest, EscapeStringToString(str, options, dest, nullptr));
 }
 
-void EscapeJSONStringToStream(OutputStream& out,
+void EscapeJSONStringToStream(std::ostream& out,
                               std::string_view str,
                               const EscapeOptions& options) {
   std::string dest;
diff --git a/src/gn/escape.h b/src/gn/escape.h
index 1fc87a5..c46d42d 100644
--- a/src/gn/escape.h
+++ b/src/gn/escape.h
@@ -5,11 +5,10 @@
 #ifndef TOOLS_GN_ESCAPE_H_
 #define TOOLS_GN_ESCAPE_H_
 
+#include <iosfwd>
 #include <string_view>
 #include <string>
 
-class OutputStream;
-
 enum EscapingMode {
   // No escaping.
   ESCAPE_NONE,
@@ -79,13 +78,13 @@
 
 // Same as EscapeString but writes the results to the given stream, saving a
 // copy.
-void EscapeStringToStream(OutputStream& out,
+void EscapeStringToStream(std::ostream& out,
                           std::string_view str,
                           const EscapeOptions& options);
 
 // Same as EscapeString but escape JSON string and writes the results to the
 // given stream, saving a copy.
-void EscapeJSONStringToStream(OutputStream& out,
+void EscapeJSONStringToStream(std::ostream& out,
                               std::string_view str,
                               const EscapeOptions& options);
 
diff --git a/src/gn/escape_unittest.cc b/src/gn/escape_unittest.cc
index 0f32075..004498e 100644
--- a/src/gn/escape_unittest.cc
+++ b/src/gn/escape_unittest.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 #include "gn/escape.h"
-#include "gn/output_stream.h"
+#include "gn/string_output_buffer.h"
 #include "util/test/test.h"
 
 TEST(Escape, Ninja) {
@@ -85,17 +85,20 @@
   opts.mode = ESCAPE_NINJA_PREFORMATTED_COMMAND;
   opts.inhibit_quoting = true;
 
-  StringOutputStream buffer;
+  StringOutputBuffer buffer;
+  std::ostream out(&buffer);
 
-  EscapeJSONStringToStream(buffer, "foo\\\" bar", opts);
+  EscapeJSONStringToStream(out, "foo\\\" bar", opts);
   EXPECT_EQ("foo\\\\\\\" bar", buffer.str());
 
-  StringOutputStream buffer1;
-  EscapeJSONStringToStream(buffer1, "foo bar\\\\", opts);
+  StringOutputBuffer buffer1;
+  std::ostream out1(&buffer1);
+  EscapeJSONStringToStream(out1, "foo bar\\\\", opts);
   EXPECT_EQ("foo bar\\\\\\\\", buffer1.str());
 
-  StringOutputStream buffer2;
-  EscapeJSONStringToStream(buffer2, "a: \"$\\b", opts);
+  StringOutputBuffer buffer2;
+  std::ostream out2(&buffer2);
+  EscapeJSONStringToStream(out2, "a: \"$\\b", opts);
   EXPECT_EQ("a: \\\"$$\\\\b", buffer2.str());
 }
 
diff --git a/src/gn/function_write_file.cc b/src/gn/function_write_file.cc
index d79f4df..709f5fb 100644
--- a/src/gn/function_write_file.cc
+++ b/src/gn/function_write_file.cc
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <sstream>
+
 #include "base/files/file_util.h"
 #include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
@@ -12,7 +14,6 @@
 #include "gn/functions.h"
 #include "gn/input_file.h"
 #include "gn/output_conversion.h"
-#include "gn/output_stream.h"
 #include "gn/parse_tree.h"
 #include "gn/scheduler.h"
 #include "gn/string_output_buffer.h"
@@ -89,7 +90,8 @@
 
   // Compute output.
   StringOutputBuffer storage;
-  ConvertValueToOutput(scope->settings(), args[1], output_conversion, storage,
+  std::ostream contents(&storage);
+  ConvertValueToOutput(scope->settings(), args[1], output_conversion, contents,
                        err);
   if (err->has_error())
     return Value();
diff --git a/src/gn/header_checker_unittest.cc b/src/gn/header_checker_unittest.cc
index 74d99cc..00a82d3 100644
--- a/src/gn/header_checker_unittest.cc
+++ b/src/gn/header_checker_unittest.cc
@@ -7,7 +7,6 @@
 
 #include "gn/config.h"
 #include "gn/header_checker.h"
-#include "gn/output_stream.h"
 #include "gn/scheduler.h"
 #include "gn/target.h"
 #include "gn/test_with_scheduler.h"
@@ -76,7 +75,7 @@
 
 }  // namespace
 
-void PrintTo(const SourceFile& source_file, OutputStream* os) {
+void PrintTo(const SourceFile& source_file, ::std::ostream* os) {
   *os << source_file.value();
 }
 
diff --git a/src/gn/ninja_action_target_writer.cc b/src/gn/ninja_action_target_writer.cc
index e479c90..0fe0c1b 100644
--- a/src/gn/ninja_action_target_writer.cc
+++ b/src/gn/ninja_action_target_writer.cc
@@ -10,7 +10,6 @@
 #include "gn/deps_iterator.h"
 #include "gn/err.h"
 #include "gn/general_tool.h"
-#include "gn/output_stream.h"
 #include "gn/pool.h"
 #include "gn/settings.h"
 #include "gn/string_utils.h"
@@ -18,7 +17,7 @@
 #include "gn/target.h"
 
 NinjaActionTargetWriter::NinjaActionTargetWriter(const Target* target,
-                                                 OutputStream& out)
+                                                 std::ostream& out)
     : NinjaTargetWriter(target, out),
       path_output_no_escaping_(
           target->settings()->build_settings()->build_dir(),
@@ -76,7 +75,7 @@
       target_->output_type() == Target::ACTION ? 1u : target_->sources().size();
   std::vector<OutputFile> input_deps = WriteInputDepsStampOrPhonyAndGetDep(
       additional_hard_deps, num_output_uses);
-  out_ << "\n";
+  out_ << std::endl;
 
   // Collects all output files for writing below.
   std::vector<OutputFile> output_files;
@@ -109,7 +108,7 @@
       path_output_.WriteFiles(out_, order_only_deps);
     }
 
-    out_ << "\n";
+    out_ << std::endl;
     if (target_->action_values().has_depfile()) {
       WriteDepfile(SourceFile());
     }
@@ -120,10 +119,10 @@
       out_ << "  pool = ";
       out_ << target_->pool().ptr->GetNinjaName(
           settings_->default_toolchain_label());
-      out_ << "\n";
+      out_ << std::endl;
     }
   }
-  out_ << "\n";
+  out_ << std::endl;
 
   // Write the phony, which doesn't need to depend on the data deps because they
   // have been added as order-only deps of the action output itself.
@@ -148,7 +147,7 @@
   EscapeOptions args_escape_options;
   args_escape_options.mode = ESCAPE_NINJA_COMMAND;
 
-  out_ << "rule " << custom_rule_name << "\n";
+  out_ << "rule " << custom_rule_name << std::endl;
 
   if (target_->action_values().uses_rsp_file()) {
     // Needs a response file. The unique_name part is for action_foreach so
@@ -159,7 +158,7 @@
     if (!target_->sources().empty())
       rspfile += ".$unique_name";
     rspfile += ".rsp";
-    out_ << "  rspfile = " << rspfile << "\n";
+    out_ << "  rspfile = " << rspfile << std::endl;
 
     // Response file contents.
     out_ << "  rspfile_content =";
@@ -169,7 +168,7 @@
       SubstitutionWriter::WriteWithNinjaVariables(arg, args_escape_options,
                                                   out_);
     }
-    out_ << "\n";
+    out_ << std::endl;
   }
 
   // The command line requires shell escaping to properly handle filenames
@@ -186,19 +185,19 @@
     out_ << " ";
     SubstitutionWriter::WriteWithNinjaVariables(arg, args_escape_options, out_);
   }
-  out_ << "\n";
+  out_ << std::endl;
   auto mnemonic = target_->action_values().mnemonic();
   if (mnemonic.empty())
     mnemonic = "ACTION";
-  out_ << "  description = " << mnemonic << " " << target_label << "\n";
-  out_ << "  restat = 1\n";
+  out_ << "  description = " << mnemonic << " " << target_label << std::endl;
+  out_ << "  restat = 1" << std::endl;
   const Tool* tool =
       target_->toolchain()->GetTool(GeneralTool::kGeneralToolAction);
   if (tool && tool->pool().ptr) {
     out_ << "  pool = ";
     out_ << tool->pool().ptr->GetNinjaName(
         settings_->default_toolchain_label());
-    out_ << "\n";
+    out_ << std::endl;
   }
 
   return custom_rule_name;
@@ -236,11 +235,11 @@
       out_ << " ||";
       path_output_.WriteFiles(out_, order_only_deps);
     }
-    out_ << "\n";
+    out_ << std::endl;
 
     // Response files require a unique name be defined.
     if (target_->action_values().uses_rsp_file())
-      out_ << "  unique_name = " << i << "\n";
+      out_ << "  unique_name = " << i << std::endl;
 
     // The required types is the union of the args and response file. This
     // might theoretically duplicate a definition if the same substitution is
@@ -264,7 +263,7 @@
       out_ << "  pool = ";
       out_ << target_->pool().ptr->GetNinjaName(
           settings_->default_toolchain_label());
-      out_ << "\n";
+      out_ << std::endl;
     }
   }
 }
@@ -290,14 +289,14 @@
       out_,
       SubstitutionWriter::ApplyPatternToSourceAsOutputFile(
           target_, settings_, target_->action_values().depfile(), source));
-  out_ << "\n";
+  out_ << std::endl;
   // Using "deps = gcc" allows Ninja to read and store the depfile content in
   // its internal database which improves performance, especially for large
   // depfiles. The use of this feature with depfiles that contain multiple
   // outputs require Ninja version 1.9.0 or newer.
   if (settings_->build_settings()->ninja_required_version() >=
       Version{1, 9, 0}) {
-    out_ << "  deps = gcc\n";
+    out_ << "  deps = gcc" << std::endl;
   }
 }
 
diff --git a/src/gn/ninja_action_target_writer.h b/src/gn/ninja_action_target_writer.h
index fe18f5b..eff087b 100644
--- a/src/gn/ninja_action_target_writer.h
+++ b/src/gn/ninja_action_target_writer.h
@@ -11,12 +11,11 @@
 #include "gn/ninja_target_writer.h"
 
 class OutputFile;
-class OutputStream;
 
 // Writes a .ninja file for a action target type.
 class NinjaActionTargetWriter : public NinjaTargetWriter {
  public:
-  NinjaActionTargetWriter(const Target* target, OutputStream& out);
+  NinjaActionTargetWriter(const Target* target, std::ostream& out);
   ~NinjaActionTargetWriter() override;
 
   void Run() override;
diff --git a/src/gn/ninja_action_target_writer_unittest.cc b/src/gn/ninja_action_target_writer_unittest.cc
index 69035fb..9b7c76f 100644
--- a/src/gn/ninja_action_target_writer_unittest.cc
+++ b/src/gn/ninja_action_target_writer_unittest.cc
@@ -3,10 +3,10 @@
 // found in the LICENSE file.
 
 #include <algorithm>
+#include <sstream>
 
 #include "gn/config.h"
 #include "gn/ninja_action_target_writer.h"
-#include "gn/output_stream.h"
 #include "gn/pool.h"
 #include "gn/substitution_list.h"
 #include "gn/target.h"
@@ -27,7 +27,7 @@
   target.SetToolchain(setup.toolchain());
   ASSERT_TRUE(target.OnResolved(&err));
 
-  StringOutputStream out;
+  std::ostringstream out;
   NinjaActionTargetWriter writer(&target, out);
 
   SourceFile source("//foo/bar.in");
@@ -57,7 +57,7 @@
   setup.build_settings()->set_python_path(
       base::FilePath(FILE_PATH_LITERAL("/usr/bin/python")));
 
-  StringOutputStream out;
+  std::ostringstream out;
   NinjaActionTargetWriter writer(&target, out);
   writer.Run();
 
@@ -99,7 +99,7 @@
   setup.build_settings()->set_python_path(
       base::FilePath(FILE_PATH_LITERAL("/usr/bin/python")));
 
-  StringOutputStream out;
+  std::ostringstream out;
   NinjaActionTargetWriter writer(&target, out);
   writer.Run();
 
@@ -141,7 +141,7 @@
   setup.build_settings()->set_python_path(
       base::FilePath(FILE_PATH_LITERAL("/usr/bin/python")));
 
-  StringOutputStream out;
+  std::ostringstream out;
   NinjaActionTargetWriter writer(&target, out);
   writer.Run();
 
@@ -198,7 +198,7 @@
   setup.build_settings()->set_python_path(
       base::FilePath(FILE_PATH_LITERAL("/usr/bin/python")));
 
-  StringOutputStream out;
+  std::ostringstream out;
   NinjaActionTargetWriter writer(&target, out);
   writer.Run();
 
@@ -269,7 +269,7 @@
   setup.build_settings()->set_python_path(
       base::FilePath(FILE_PATH_LITERAL("/usr/bin/python")));
 
-  StringOutputStream out;
+  std::ostringstream out;
   NinjaActionTargetWriter writer(&target, out);
   writer.Run();
 
@@ -337,7 +337,7 @@
       base::FilePath(FILE_PATH_LITERAL("/usr/bin/python")));
   setup.build_settings()->set_ninja_required_version(Version{1, 9, 0});
 
-  StringOutputStream out;
+  std::ostringstream out;
   NinjaActionTargetWriter writer(&target, out);
   writer.Run();
 
@@ -394,7 +394,7 @@
   setup.build_settings()->set_python_path(
       base::FilePath(FILE_PATH_LITERAL("/usr/bin/python")));
 
-  StringOutputStream out;
+  std::ostringstream out;
   NinjaActionTargetWriter writer(&target, out);
   writer.Run();
 
@@ -452,7 +452,7 @@
   setup.build_settings()->set_python_path(
       base::FilePath(FILE_PATH_LITERAL("/usr/bin/python")));
 
-  StringOutputStream out;
+  std::ostringstream out;
   NinjaActionTargetWriter writer(&target, out);
   writer.Run();
 
@@ -499,7 +499,7 @@
   ASSERT_TRUE(foo.OnResolved(&err));
 
   {
-    StringOutputStream out;
+    std::ostringstream out;
     NinjaActionTargetWriter writer(&foo, out);
     writer.Run();
 
@@ -528,7 +528,7 @@
   ASSERT_TRUE(bar.OnResolved(&err)) << err.message();
 
   {
-    StringOutputStream out;
+    std::ostringstream out;
     NinjaActionTargetWriter writer(&bar, out);
     writer.Run();
 
@@ -583,7 +583,7 @@
   ASSERT_TRUE(foo.OnResolved(&err));
 
   {
-    StringOutputStream out;
+    std::ostringstream out;
     NinjaActionTargetWriter writer(&foo, out);
     writer.Run();
 
@@ -629,7 +629,7 @@
   setup.build_settings()->set_python_path(
       base::FilePath(FILE_PATH_LITERAL("/Program Files/python")));
 
-  StringOutputStream out;
+  std::ostringstream out;
   NinjaActionTargetWriter writer(&target, out);
   writer.Run();
 
diff --git a/src/gn/ninja_binary_target_writer.cc b/src/gn/ninja_binary_target_writer.cc
index 21b10fc..c244c7c 100644
--- a/src/gn/ninja_binary_target_writer.cc
+++ b/src/gn/ninja_binary_target_writer.cc
@@ -4,6 +4,8 @@
 
 #include "gn/ninja_binary_target_writer.h"
 
+#include <sstream>
+
 #include "base/strings/string_util.h"
 #include "gn/builtin_tool.h"
 #include "gn/config_values_extractors.h"
@@ -14,7 +16,6 @@
 #include "gn/ninja_rust_binary_target_writer.h"
 #include "gn/ninja_target_command_util.h"
 #include "gn/ninja_utils.h"
-#include "gn/output_stream.h"
 #include "gn/pool.h"
 #include "gn/settings.h"
 #include "gn/string_utils.h"
@@ -34,7 +35,7 @@
 }  // namespace
 
 NinjaBinaryTargetWriter::NinjaBinaryTargetWriter(const Target* target,
-                                                 OutputStream& out)
+                                                 std::ostream& out)
     : NinjaTargetWriter(target, out),
       rule_prefix_(GetNinjaRulePrefixForToolchain(settings_)) {}
 
@@ -118,7 +119,7 @@
     path_output_.WriteFile(out_, *input);
   }
 
-  out_ << "\n";
+  out_ << std::endl;
   return {stamp_or_phony};
 }
 
@@ -291,22 +292,22 @@
     out_ << " ||";
     path_output_.WriteFiles(out_, order_only_deps);
   }
-  out_ << "\n";
+  out_ << std::endl;
 
   if (!sources.empty() && can_write_source_info) {
     out_ << "  " << "source_file_part = " << sources[0].GetName();
-    out_ << "\n";
+    out_ << std::endl;
     out_ << "  " << "source_name_part = "
          << FindFilenameNoExtension(&sources[0].value());
-    out_ << "\n";
+    out_ << std::endl;
   }
 
   if (restat_output_allowed) {
-    out_ << "  restat = 1\n";
+    out_ << "  restat = 1" << std::endl;
   }
 }
 
-void NinjaBinaryTargetWriter::WriteCustomLinkerFlags(OutputStream& out,
+void NinjaBinaryTargetWriter::WriteCustomLinkerFlags(std::ostream& out,
                                                      const Tool* tool) {
   if (tool->AsC() || (tool->AsRust() && tool->AsRust()->MayLink())) {
     // First the ldflags from the target and its config.
@@ -316,7 +317,7 @@
   }
 }
 
-void NinjaBinaryTargetWriter::WriteLibrarySearchPath(OutputStream& out,
+void NinjaBinaryTargetWriter::WriteLibrarySearchPath(std::ostream& out,
                                                      const Tool* tool) {
   // Write library search paths that have been recursively pushed
   // through the dependency tree.
@@ -350,7 +351,7 @@
 }
 
 void NinjaBinaryTargetWriter::WriteLinkerFlags(
-    OutputStream& out,
+    std::ostream& out,
     const Tool* tool,
     const SourceFile* optional_def_file) {
   // First any ldflags
@@ -364,7 +365,7 @@
   }
 }
 
-void NinjaBinaryTargetWriter::WriteLibs(OutputStream& out, const Tool* tool) {
+void NinjaBinaryTargetWriter::WriteLibs(std::ostream& out, const Tool* tool) {
   // Libraries that have been recursively pushed through the dependency tree.
   // Since we're passing these on the command line to the linker and not
   // to Ninja, we need to do shell escaping.
@@ -387,7 +388,7 @@
   }
 }
 
-void NinjaBinaryTargetWriter::WriteFrameworks(OutputStream& out,
+void NinjaBinaryTargetWriter::WriteFrameworks(std::ostream& out,
                                               const Tool* tool) {
   // Frameworks that have been recursively pushed through the dependency tree.
   FrameworksWriter writer(tool->framework_switch());
@@ -404,7 +405,7 @@
 }
 
 void NinjaBinaryTargetWriter::WriteSwiftModules(
-    OutputStream& out,
+    std::ostream& out,
     const Tool* tool,
     const std::vector<OutputFile>& swiftmodules) {
   // Since we're passing these on the command line to the linker and not
@@ -419,11 +420,11 @@
   }
 }
 
-void NinjaBinaryTargetWriter::WritePool(OutputStream& out) {
+void NinjaBinaryTargetWriter::WritePool(std::ostream& out) {
   if (target_->pool().ptr) {
     out << "  pool = ";
     out << target_->pool().ptr->GetNinjaName(
         settings_->default_toolchain_label());
-    out << "\n";
+    out << std::endl;
   }
 }
diff --git a/src/gn/ninja_binary_target_writer.h b/src/gn/ninja_binary_target_writer.h
index 56a777a..29105b4 100644
--- a/src/gn/ninja_binary_target_writer.h
+++ b/src/gn/ninja_binary_target_writer.h
@@ -11,15 +11,13 @@
 #include "gn/toolchain.h"
 #include "gn/unique_vector.h"
 
-class OutputStream;
-
 struct EscapeOptions;
 
 // Writes a .ninja file for a binary target type (an executable, a shared
 // library, or a static library).
 class NinjaBinaryTargetWriter : public NinjaTargetWriter {
  public:
-  NinjaBinaryTargetWriter(const Target* target, OutputStream& out);
+  NinjaBinaryTargetWriter(const Target* target, std::ostream& out);
   ~NinjaBinaryTargetWriter() override;
 
   void Run() override;
@@ -63,17 +61,17 @@
                               bool can_write_source_info = true,
                               bool restat_output_allowed = false);
 
-  void WriteLinkerFlags(OutputStream& out,
+  void WriteLinkerFlags(std::ostream& out,
                         const Tool* tool,
                         const SourceFile* optional_def_file);
-  void WriteCustomLinkerFlags(OutputStream& out, const Tool* tool);
-  void WriteLibrarySearchPath(OutputStream& out, const Tool* tool);
-  void WriteLibs(OutputStream& out, const Tool* tool);
-  void WriteFrameworks(OutputStream& out, const Tool* tool);
-  void WriteSwiftModules(OutputStream& out,
+  void WriteCustomLinkerFlags(std::ostream& out, const Tool* tool);
+  void WriteLibrarySearchPath(std::ostream& out, const Tool* tool);
+  void WriteLibs(std::ostream& out, const Tool* tool);
+  void WriteFrameworks(std::ostream& out, const Tool* tool);
+  void WriteSwiftModules(std::ostream& out,
                          const Tool* tool,
                          const std::vector<OutputFile>& swiftmodules);
-  void WritePool(OutputStream& out);
+  void WritePool(std::ostream& out);
 
   void AddSourceSetFiles(const Target* source_set,
                          UniqueVector<OutputFile>* obj_files) const;
diff --git a/src/gn/ninja_binary_target_writer_unittest.cc b/src/gn/ninja_binary_target_writer_unittest.cc
index 7ef54c6..29dfd33 100644
--- a/src/gn/ninja_binary_target_writer_unittest.cc
+++ b/src/gn/ninja_binary_target_writer_unittest.cc
@@ -4,7 +4,6 @@
 
 #include "gn/ninja_binary_target_writer.h"
 
-#include "gn/output_stream.h"
 #include "gn/test_with_scheduler.h"
 #include "gn/test_with_scope.h"
 #include "util/test/test.h"
@@ -29,7 +28,7 @@
   target.SetToolchain(setup.toolchain());
   ASSERT_TRUE(target.OnResolved(&err));
 
-  StringOutputStream out;
+  std::ostringstream out;
   NinjaBinaryTargetWriter writer(&target, out);
   writer.Run();
 
@@ -66,7 +65,7 @@
   target.SetToolchain(setup.toolchain());
   ASSERT_TRUE(target.OnResolved(&err));
 
-  StringOutputStream out;
+  std::ostringstream out;
   NinjaBinaryTargetWriter writer(&target, out);
   writer.Run();
 
@@ -93,7 +92,7 @@
   target.SetToolchain(setup.toolchain());
   ASSERT_TRUE(target.OnResolved(&err));
 
-  StringOutputStream out;
+  std::ostringstream out;
   NinjaBinaryTargetWriter writer(&target, out);
   writer.Run();
 
@@ -129,7 +128,7 @@
     target.SetToolchain(setup.toolchain());
     ASSERT_TRUE(target.OnResolved(&err));
 
-    StringOutputStream out;
+    std::ostringstream out;
     NinjaBinaryTargetWriter writer(&target, out);
     writer.Run();
 
@@ -165,7 +164,7 @@
     target.SetToolchain(setup.toolchain());
     ASSERT_TRUE(target.OnResolved(&err));
 
-    StringOutputStream out;
+    std::ostringstream out;
     NinjaBinaryTargetWriter writer(&target, out);
     writer.Run();
 
diff --git a/src/gn/ninja_build_writer.cc b/src/gn/ninja_build_writer.cc
index de91eaf..5349948 100644
--- a/src/gn/ninja_build_writer.cc
+++ b/src/gn/ninja_build_writer.cc
@@ -6,8 +6,10 @@
 
 #include <stddef.h>
 
+#include <fstream>
 #include <map>
 #include <set>
+#include <sstream>
 
 #include "base/command_line.h"
 #include "base/files/file_util.h"
@@ -21,7 +23,6 @@
 #include "gn/input_file_manager.h"
 #include "gn/loader.h"
 #include "gn/ninja_utils.h"
-#include "gn/output_stream.h"
 #include "gn/pool.h"
 #include "gn/scheduler.h"
 #include "gn/string_atom.h"
@@ -198,8 +199,8 @@
     const std::vector<const Target*>& all_targets,
     const Toolchain* default_toolchain,
     const std::vector<const Target*>& default_toolchain_targets,
-    OutputStream& out,
-    OutputStream& dep_out)
+    std::ostream& out,
+    std::ostream& dep_out)
     : build_settings_(build_settings),
       used_toolchains_(used_toolchains),
       all_targets_(all_targets),
@@ -253,8 +254,8 @@
     }
   }
 
-  StringOutputStream file;
-  StringOutputStream depfile;
+  std::stringstream file;
+  std::stringstream depfile;
   NinjaBuildWriter gen(build_settings, used_toolchains, all_targets,
                        default_toolchain, default_toolchain_targets, file,
                        depfile);
@@ -307,7 +308,7 @@
 // static
 std::string NinjaBuildWriter::ExtractRegenerationCommands(
     std::istream& build_ninja_in) {
-  StringOutputStream out;
+  std::ostringstream out;
   int num_blank_lines = 0;
   for (std::string line; std::getline(build_ninja_in, line);) {
     out << line << '\n';
@@ -379,7 +380,7 @@
 
   sorter.IterateOver(item_callback);
 
-  out_ << "\n";
+  out_ << std::endl;
 }
 
 void NinjaBuildWriter::WriteAllPools() {
@@ -414,9 +415,9 @@
     std::string name = pool_name(pool);
     if (name == "console")
       continue;
-    out_ << "pool " << name << "\n"
-         << "  depth = " << pool->depth() << "\n"
-         << "\n";
+    out_ << "pool " << name << std::endl
+         << "  depth = " << pool->depth() << std::endl
+         << std::endl;
   }
 }
 
@@ -452,11 +453,11 @@
 
     out_ << "subninja ";
     path_output_.WriteFile(out_, subninja);
-    out_ << "\n";
+    out_ << std::endl;
     previous_subninja = subninja;
     previous_toolchain = pair.second;
   }
-  out_ << "\n";
+  out_ << std::endl;
   return true;
 }
 
@@ -674,22 +675,22 @@
       }
     }
   }
-  out_ << "\n";
+  out_ << std::endl;
 
   if (default_target) {
     // Use the short name when available
     if (written_rules.find(StringAtom("default")) != written_rules.end()) {
-      out_ << "\ndefault default\n";
+      out_ << "\ndefault default" << std::endl;
     } else if (default_target->has_dependency_output()) {
       // If the default target does not have a dependency output file or phony,
       // then the target specified as default is a no-op. We omit the default
       // statement entirely to avoid ninja runtime failure.
       out_ << "\ndefault ";
       path_output_.WriteFile(out_, default_target->dependency_output());
-      out_ << "\n";
+      out_ << std::endl;
     }
   } else if (!default_toolchain_targets_.empty()) {
-    out_ << "\ndefault all\n";
+    out_ << "\ndefault all" << std::endl;
   }
 
   return true;
@@ -710,5 +711,5 @@
   if (target->has_dependency_output()) {
     path_output_.WriteFile(out_, target->dependency_output());
   }
-  out_ << "\n";
+  out_ << std::endl;
 }
diff --git a/src/gn/ninja_build_writer.h b/src/gn/ninja_build_writer.h
index 77d891c..c21769a 100644
--- a/src/gn/ninja_build_writer.h
+++ b/src/gn/ninja_build_writer.h
@@ -35,8 +35,8 @@
                    const std::vector<const Target*>& all_targets,
                    const Toolchain* default_toolchain,
                    const std::vector<const Target*>& default_toolchain_targets,
-                   OutputStream& out,
-                   OutputStream& dep_out);
+                   std::ostream& out,
+                   std::ostream& dep_out);
   ~NinjaBuildWriter();
 
   // The design of this class is that this static factory function takes the
@@ -92,8 +92,8 @@
   const Toolchain* default_toolchain_;
   const std::vector<const Target*>& default_toolchain_targets_;
 
-  OutputStream& out_;
-  OutputStream& dep_out_;
+  std::ostream& out_;
+  std::ostream& dep_out_;
   PathOutput path_output_;
 
   NinjaBuildWriter(const NinjaBuildWriter&) = delete;
diff --git a/src/gn/ninja_build_writer_unittest.cc b/src/gn/ninja_build_writer_unittest.cc
index 5511147..493ba40 100644
--- a/src/gn/ninja_build_writer_unittest.cc
+++ b/src/gn/ninja_build_writer_unittest.cc
@@ -2,10 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "gn/ninja_build_writer.h"
+#include <fstream>
+#include <sstream>
+
 #include "base/command_line.h"
 #include "base/files/file_util.h"
-#include "gn/output_stream.h"
+#include "gn/ninja_build_writer.h"
 #include "gn/pool.h"
 #include "gn/scheduler.h"
 #include "gn/switches.h"
@@ -133,8 +135,8 @@
 
   std::vector<const Target*> targets = {&target_foo, &target_bar, &target_baz};
 
-  StringOutputStream ninja_out;
-  StringOutputStream depfile_out;
+  std::ostringstream ninja_out;
+  std::ostringstream depfile_out;
 
   NinjaBuildWriter writer(setup.build_settings(), used_toolchains, targets,
                           setup.toolchain(), targets, ninja_out, depfile_out);
@@ -209,8 +211,8 @@
 
   std::vector<const Target*> targets = {&target_foo};
 
-  StringOutputStream ninja_out;
-  StringOutputStream depfile_out;
+  std::stringstream ninja_out;
+  std::ostringstream depfile_out;
 
   NinjaBuildWriter writer(setup.build_settings(), used_toolchains, targets,
                           setup.toolchain(), targets, ninja_out, depfile_out);
@@ -239,9 +241,8 @@
   EXPECT_SNIPPET(ninja_out_str, expected_root_target);
   EXPECT_SNIPPET(ninja_out_str, expected_default);
 
-  std::istringstream ninja_in(ninja_out.str());
   std::string commands =
-      NinjaBuildWriter::ExtractRegenerationCommands(ninja_in);
+      NinjaBuildWriter::ExtractRegenerationCommands(ninja_out);
   EXPECT_SNIPPET(commands, expected_rule_gn);
   EXPECT_SNIPPET(commands, expected_build_ninja_stamp);
   EXPECT_SNIPPET(commands, expected_build_ninja);
@@ -254,17 +255,18 @@
 }
 
 TEST_F(NinjaBuildWriterTest, ExtractRegenerationCommands_DefaultStream) {
-  std::istringstream ninja_in;
+  std::ifstream ninja_in;
   EXPECT_EQ(NinjaBuildWriter::ExtractRegenerationCommands(ninja_in), "");
 }
 
 TEST_F(NinjaBuildWriterTest, ExtractRegenerationCommands_StreamError) {
-  std::istringstream ninja_in("/does/not/exist");
+  std::ifstream ninja_in("/does/not/exist");
   EXPECT_EQ(NinjaBuildWriter::ExtractRegenerationCommands(ninja_in), "");
 }
 
 TEST_F(NinjaBuildWriterTest, ExtractRegenerationCommands_IncompleteNinja) {
-  std::istringstream ninja_in("foo\nbar\nbaz\nbif\n");
+  std::stringstream ninja_in;
+  ninja_in << "foo\nbar\nbaz\nbif\n";
   EXPECT_EQ(NinjaBuildWriter::ExtractRegenerationCommands(ninja_in), "");
 }
 
@@ -285,8 +287,8 @@
   std::unordered_map<const Settings*, const Toolchain*> used_toolchains;
   used_toolchains[setup.settings()] = setup.toolchain();
   std::vector<const Target*> targets;
-  StringOutputStream ninja_out;
-  StringOutputStream depfile_out;
+  std::ostringstream ninja_out;
+  std::ostringstream depfile_out;
   NinjaBuildWriter writer(setup.build_settings(), used_toolchains, targets,
                           setup.toolchain(), targets, ninja_out, depfile_out);
   ASSERT_TRUE(writer.Run(&err));
@@ -318,8 +320,8 @@
   std::unordered_map<const Settings*, const Toolchain*> used_toolchains;
   used_toolchains[setup.settings()] = setup.toolchain();
   std::vector<const Target*> targets = {&target_foo, &target_bar};
-  StringOutputStream ninja_out;
-  StringOutputStream depfile_out;
+  std::ostringstream ninja_out;
+  std::ostringstream depfile_out;
   NinjaBuildWriter writer(setup.build_settings(), used_toolchains, targets,
                           setup.toolchain(), targets, ninja_out, depfile_out);
   ASSERT_FALSE(writer.Run(&err));
diff --git a/src/gn/ninja_bundle_data_target_writer.cc b/src/gn/ninja_bundle_data_target_writer.cc
index 362344d..4ba1733 100644
--- a/src/gn/ninja_bundle_data_target_writer.cc
+++ b/src/gn/ninja_bundle_data_target_writer.cc
@@ -9,7 +9,7 @@
 #include "gn/target.h"
 
 NinjaBundleDataTargetWriter::NinjaBundleDataTargetWriter(const Target* target,
-                                                         OutputStream& out)
+                                                         std::ostream& out)
     : NinjaTargetWriter(target, out) {}
 
 NinjaBundleDataTargetWriter::~NinjaBundleDataTargetWriter() = default;
diff --git a/src/gn/ninja_bundle_data_target_writer.h b/src/gn/ninja_bundle_data_target_writer.h
index de1e3fc..720c593 100644
--- a/src/gn/ninja_bundle_data_target_writer.h
+++ b/src/gn/ninja_bundle_data_target_writer.h
@@ -10,7 +10,7 @@
 // Writes a .ninja file for a bundle_data target type.
 class NinjaBundleDataTargetWriter : public NinjaTargetWriter {
  public:
-  NinjaBundleDataTargetWriter(const Target* target, OutputStream& out);
+  NinjaBundleDataTargetWriter(const Target* target, std::ostream& out);
   ~NinjaBundleDataTargetWriter() override;
 
   void Run() override;
diff --git a/src/gn/ninja_bundle_data_target_writer_unittest.cc b/src/gn/ninja_bundle_data_target_writer_unittest.cc
index 4de32bf..9f90245 100644
--- a/src/gn/ninja_bundle_data_target_writer_unittest.cc
+++ b/src/gn/ninja_bundle_data_target_writer_unittest.cc
@@ -5,8 +5,8 @@
 #include "gn/ninja_bundle_data_target_writer.h"
 
 #include <algorithm>
+#include <sstream>
 
-#include "gn/output_stream.h"
 #include "gn/target.h"
 #include "gn/test_with_scope.h"
 #include "util/test/test.h"
@@ -41,7 +41,7 @@
   bundle_data.visibility().SetPublic();
   ASSERT_TRUE(bundle_data.OnResolved(&err));
 
-  StringOutputStream out;
+  std::ostringstream out;
   NinjaBundleDataTargetWriter writer(&bundle_data, out);
   writer.Run();
 
diff --git a/src/gn/ninja_c_binary_target_writer.cc b/src/gn/ninja_c_binary_target_writer.cc
index 207741a..96907f7 100644
--- a/src/gn/ninja_c_binary_target_writer.cc
+++ b/src/gn/ninja_c_binary_target_writer.cc
@@ -9,6 +9,7 @@
 
 #include <cstring>
 #include <set>
+#include <sstream>
 
 #include "base/strings/string_util.h"
 #include "gn/c_substitution_type.h"
@@ -20,7 +21,6 @@
 #include "gn/general_tool.h"
 #include "gn/ninja_target_command_util.h"
 #include "gn/ninja_utils.h"
-#include "gn/output_stream.h"
 #include "gn/pool.h"
 #include "gn/scheduler.h"
 #include "gn/settings.h"
@@ -122,7 +122,7 @@
 }  // namespace
 
 NinjaCBinaryTargetWriter::NinjaCBinaryTargetWriter(const Target* target,
-                                                   OutputStream& out)
+                                                   std::ostream& out)
     : NinjaBinaryTargetWriter(target, out),
       tool_(target->toolchain()->GetToolForTargetFinalOutputAsC(target)) {}
 
@@ -267,7 +267,7 @@
       }
     }
 
-    out_ << "\n";
+    out_ << std::endl;
   }
 }
 
@@ -388,7 +388,7 @@
 
   // Write two blank lines to help separate the PCH build lines from the
   // regular source build lines.
-  out_ << "\n\n";
+  out_ << std::endl << std::endl;
 }
 
 void NinjaCBinaryTargetWriter::WriteWindowsPCHCommand(
@@ -424,8 +424,7 @@
 
   // Write two blank lines to help separate the PCH build lines from the
   // regular source build lines.
-  out_ << "\n"
-       << "\n";
+  out_ << std::endl << std::endl;
 }
 
 void NinjaCBinaryTargetWriter::WriteSources(
@@ -501,7 +500,7 @@
     }
   }
 
-  out_ << "\n";
+  out_ << std::endl;
 }
 
 void NinjaCBinaryTargetWriter::WriteSwiftSources(
@@ -538,7 +537,7 @@
                          /*can_write_source_info=*/false,
                          /*restat_output_allowed=*/true);
 
-  out_ << "\n";
+  out_ << std::endl;
 }
 
 void NinjaCBinaryTargetWriter::WriteSourceSetStamp(
@@ -694,7 +693,7 @@
   WriteOrderOnlyDependencies(classified_deps.non_linkable_deps);
 
   // End of the link "build" line.
-  out_ << "\n";
+  out_ << std::endl;
 
   // The remaining things go in the inner scope of the link line.
   if (target_->output_type() == Target::EXECUTABLE ||
@@ -702,22 +701,22 @@
       target_->output_type() == Target::LOADABLE_MODULE) {
     out_ << "  ldflags =";
     WriteLinkerFlags(out_, tool_, optional_def_file);
-    out_ << "\n";
+    out_ << std::endl;
     out_ << "  libs =";
     WriteLibs(out_, tool_);
-    out_ << "\n";
+    out_ << std::endl;
     out_ << "  frameworks =";
     WriteFrameworks(out_, tool_);
-    out_ << "\n";
+    out_ << std::endl;
     out_ << "  swiftmodules =";
     WriteSwiftModules(out_, tool_, swiftmodules);
-    out_ << "\n";
+    out_ << std::endl;
   } else if (target_->output_type() == Target::STATIC_LIBRARY) {
     out_ << "  arflags =";
     RecursiveTargetConfigStringsToStream(kRecursiveWriterKeepDuplicates,
                                          target_, &ConfigValues::arflags,
                                          GetFlagOptions(), out_);
-    out_ << "\n";
+    out_ << std::endl;
   }
   WriteOutputSubstitutions();
   WriteLibsList("solibs", solibs);
@@ -733,7 +732,7 @@
   if (!output_extension.empty()) {
     out_ << " " << output_extension;
   }
-  out_ << "\n";
+  out_ << std::endl;
 
   const std::string output_dir = SubstitutionWriter::GetLinkerSubstitution(
       target_, tool_, &SubstitutionOutputDir);
@@ -741,7 +740,7 @@
   if (!output_dir.empty()) {
     out_ << " " << output_dir;
   }
-  out_ << "\n";
+  out_ << std::endl;
 }
 
 void NinjaCBinaryTargetWriter::WriteLibsList(
@@ -755,7 +754,7 @@
                     settings_->build_settings()->root_path_utf8(),
                     ESCAPE_NINJA_COMMAND);
   output.WriteFiles(out_, libs);
-  out_ << "\n";
+  out_ << std::endl;
 }
 
 void NinjaCBinaryTargetWriter::WriteOrderOnlyDependencies(
diff --git a/src/gn/ninja_c_binary_target_writer.h b/src/gn/ninja_c_binary_target_writer.h
index 6a553fc..f60790f 100644
--- a/src/gn/ninja_c_binary_target_writer.h
+++ b/src/gn/ninja_c_binary_target_writer.h
@@ -10,8 +10,6 @@
 #include "gn/toolchain.h"
 #include "gn/unique_vector.h"
 
-class OutputStream;
-
 struct EscapeOptions;
 struct ModuleDep;
 
@@ -19,7 +17,7 @@
 // library, or a static library).
 class NinjaCBinaryTargetWriter : public NinjaBinaryTargetWriter {
  public:
-  NinjaCBinaryTargetWriter(const Target* target, OutputStream& out);
+  NinjaCBinaryTargetWriter(const Target* target, std::ostream& out);
   ~NinjaCBinaryTargetWriter() override;
 
   void Run() override;
diff --git a/src/gn/ninja_c_binary_target_writer_unittest.cc b/src/gn/ninja_c_binary_target_writer_unittest.cc
index f8d1130..8d2a388 100644
--- a/src/gn/ninja_c_binary_target_writer_unittest.cc
+++ b/src/gn/ninja_c_binary_target_writer_unittest.cc
@@ -5,11 +5,11 @@
 #include "gn/ninja_c_binary_target_writer.h"
 
 #include <memory>
+#include <sstream>
 #include <utility>
 
 #include "gn/config.h"
 #include "gn/ninja_target_command_util.h"
-#include "gn/output_stream.h"
 #include "gn/pool.h"
 #include "gn/scheduler.h"
 #include "gn/target.h"
@@ -40,7 +40,7 @@
 
   // Source set itself.
   {
-    StringOutputStream out;
+    std::ostringstream out;
     NinjaCBinaryTargetWriter writer(&target, out);
     writer.Run();
 
@@ -75,7 +75,7 @@
   ASSERT_TRUE(shlib_target.OnResolved(&err));
 
   {
-    StringOutputStream out;
+    std::ostringstream out;
     NinjaCBinaryTargetWriter writer(&shlib_target, out);
     writer.Run();
 
@@ -112,7 +112,7 @@
   ASSERT_TRUE(stlib_target.OnResolved(&err));
 
   {
-    StringOutputStream out;
+    std::ostringstream out;
     NinjaCBinaryTargetWriter writer(&stlib_target, out);
     writer.Run();
 
@@ -138,7 +138,7 @@
   // Make the static library 'complete', which means it should be linked.
   stlib_target.set_complete_static_lib(true);
   {
-    StringOutputStream out;
+    std::ostringstream out;
     NinjaCBinaryTargetWriter writer(&stlib_target, out);
     writer.Run();
 
@@ -175,7 +175,7 @@
   target.config_values().defines().push_back("STR_DEF=\"ABCD-1\"");
   ASSERT_TRUE(target.OnResolved(&err));
 
-  StringOutputStream out;
+  std::ostringstream out;
   NinjaCBinaryTargetWriter writer(&target, out);
   writer.Run();
 
@@ -199,7 +199,7 @@
   target.config_values().arflags().push_back("--asdf");
   ASSERT_TRUE(target.OnResolved(&err));
 
-  StringOutputStream out;
+  std::ostringstream out;
   NinjaCBinaryTargetWriter writer(&target, out);
   writer.Run();
 
@@ -248,7 +248,7 @@
   // should link in the dependent object files as if the dependent target
   // were a source set.
   {
-    StringOutputStream out;
+    std::ostringstream out;
     NinjaCBinaryTargetWriter writer(&target, out);
     writer.Run();
 
@@ -280,7 +280,7 @@
 
   // Dependent complete static libraries should not be linked directly.
   {
-    StringOutputStream out;
+    std::ostringstream out;
     NinjaCBinaryTargetWriter writer(&target, out);
     writer.Run();
 
@@ -333,7 +333,7 @@
   target.SetToolchain(setup.toolchain());
   ASSERT_TRUE(target.OnResolved(&err));
 
-  StringOutputStream out;
+  std::ostringstream out;
   NinjaCBinaryTargetWriter writer(&target, out);
   writer.Run();
 
@@ -401,7 +401,7 @@
   gen_obj.SetToolchain(setup.toolchain());
   ASSERT_TRUE(gen_obj.OnResolved(&err));
 
-  StringOutputStream obj_out;
+  std::ostringstream obj_out;
   NinjaCBinaryTargetWriter obj_writer(&gen_obj, obj_out);
   obj_writer.Run();
 
@@ -440,7 +440,7 @@
   gen_lib.SetToolchain(setup.toolchain());
   ASSERT_TRUE(gen_lib.OnResolved(&err));
 
-  StringOutputStream lib_out;
+  std::ostringstream lib_out;
   NinjaCBinaryTargetWriter lib_writer(&gen_lib, lib_out);
   lib_writer.Run();
 
@@ -479,7 +479,7 @@
   executable.SetToolchain(setup.toolchain());
   ASSERT_TRUE(executable.OnResolved(&err)) << err.message();
 
-  StringOutputStream final_out;
+  std::ostringstream final_out;
   NinjaCBinaryTargetWriter final_writer(&executable, final_out);
   final_writer.Run();
 
@@ -527,7 +527,7 @@
   target.SetToolchain(setup.toolchain());
   ASSERT_TRUE(target.OnResolved(&err));
 
-  StringOutputStream out;
+  std::ostringstream out;
   NinjaCBinaryTargetWriter writer(&target, out);
   writer.Run();
 
@@ -598,7 +598,7 @@
   target.SetToolchain(setup.toolchain());
   ASSERT_TRUE(target.OnResolved(&err));
 
-  StringOutputStream out;
+  std::ostringstream out;
   NinjaCBinaryTargetWriter writer(&target, out);
   writer.Run();
 
@@ -642,7 +642,7 @@
   target.SetToolchain(setup.toolchain());
   ASSERT_TRUE(target.OnResolved(&err));
 
-  StringOutputStream out;
+  std::ostringstream out;
   NinjaCBinaryTargetWriter writer(&target, out);
   writer.Run();
 
@@ -698,7 +698,7 @@
   ASSERT_TRUE(inter.OnResolved(&err)) << err.message();
 
   // Write out the intermediate target.
-  StringOutputStream inter_out;
+  std::ostringstream inter_out;
   NinjaCBinaryTargetWriter inter_writer(&inter, inter_out);
   inter_writer.Run();
 
@@ -732,7 +732,7 @@
   exe.source_types_used().Set(SourceFile::SOURCE_CPP);
   ASSERT_TRUE(exe.OnResolved(&err));
 
-  StringOutputStream final_out;
+  std::ostringstream final_out;
   NinjaCBinaryTargetWriter final_writer(&exe, final_out);
   final_writer.Run();
 
@@ -779,7 +779,7 @@
   shared_lib.source_types_used().Set(SourceFile::SOURCE_DEF);
   ASSERT_TRUE(shared_lib.OnResolved(&err));
 
-  StringOutputStream out;
+  std::ostringstream out;
   NinjaCBinaryTargetWriter writer(&shared_lib, out);
   writer.Run();
 
@@ -819,7 +819,7 @@
   loadable_module.source_types_used().Set(SourceFile::SOURCE_CPP);
   ASSERT_TRUE(loadable_module.OnResolved(&err)) << err.message();
 
-  StringOutputStream out;
+  std::ostringstream out;
   NinjaCBinaryTargetWriter writer(&loadable_module, out);
   writer.Run();
 
@@ -855,7 +855,7 @@
   exe.source_types_used().Set(SourceFile::SOURCE_CPP);
   ASSERT_TRUE(exe.OnResolved(&err)) << err.message();
 
-  StringOutputStream final_out;
+  std::ostringstream final_out;
   NinjaCBinaryTargetWriter final_writer(&exe, final_out);
   final_writer.Run();
 
@@ -937,7 +937,7 @@
     no_pch_target.SetToolchain(&pch_toolchain);
     ASSERT_TRUE(no_pch_target.OnResolved(&err));
 
-    StringOutputStream out;
+    std::ostringstream out;
     NinjaCBinaryTargetWriter writer(&no_pch_target, out);
     writer.Run();
 
@@ -979,7 +979,7 @@
     pch_target.SetToolchain(&pch_toolchain);
     ASSERT_TRUE(pch_target.OnResolved(&err));
 
-    StringOutputStream out;
+    std::ostringstream out;
     NinjaCBinaryTargetWriter writer(&pch_target, out);
     writer.Run();
 
@@ -1083,7 +1083,7 @@
     no_pch_target.SetToolchain(&pch_toolchain);
     ASSERT_TRUE(no_pch_target.OnResolved(&err));
 
-    StringOutputStream out;
+    std::ostringstream out;
     NinjaCBinaryTargetWriter writer(&no_pch_target, out);
     writer.Run();
 
@@ -1125,7 +1125,7 @@
     pch_target.SetToolchain(&pch_toolchain);
     ASSERT_TRUE(pch_target.OnResolved(&err));
 
-    StringOutputStream out;
+    std::ostringstream out;
     NinjaCBinaryTargetWriter writer(&pch_target, out);
     writer.Run();
 
@@ -1185,7 +1185,7 @@
 
   scheduler().SuppressOutputForTesting(true);
 
-  StringOutputStream out;
+  std::ostringstream out;
   NinjaCBinaryTargetWriter writer(&target, out);
   writer.Run();
 
@@ -1213,7 +1213,7 @@
     target.SetToolchain(setup.toolchain());
     ASSERT_TRUE(target.OnResolved(&err));
 
-    StringOutputStream out;
+    std::ostringstream out;
     NinjaCBinaryTargetWriter writer(&target, out);
     writer.Run();
 
@@ -1251,7 +1251,7 @@
     target.SetToolchain(setup.toolchain());
     ASSERT_TRUE(target.OnResolved(&err));
 
-    StringOutputStream out;
+    std::ostringstream out;
     NinjaCBinaryTargetWriter writer(&target, out);
     writer.Run();
 
@@ -1288,7 +1288,7 @@
     target.SetToolchain(setup.toolchain());
     ASSERT_TRUE(target.OnResolved(&err));
 
-    StringOutputStream out;
+    std::ostringstream out;
     NinjaCBinaryTargetWriter writer(&target, out);
     writer.Run();
 
@@ -1343,7 +1343,7 @@
     target.SetToolchain(setup.toolchain());
     ASSERT_TRUE(target.OnResolved(&err));
 
-    StringOutputStream out;
+    std::ostringstream out;
     NinjaCBinaryTargetWriter writer(&target, out);
     writer.Run();
 
@@ -1400,7 +1400,7 @@
   target.SetToolchain(setup.toolchain());
   ASSERT_TRUE(target.OnResolved(&err));
 
-  StringOutputStream out;
+  std::ostringstream out;
   NinjaCBinaryTargetWriter writer(&target, out);
   writer.Run();
 
@@ -1577,7 +1577,7 @@
   target.SetToolchain(setup.toolchain());
   ASSERT_TRUE(target.OnResolved(&err));
 
-  StringOutputStream out;
+  std::ostringstream out;
   NinjaCBinaryTargetWriter writer(&target, out);
   writer.Run();
 
@@ -1745,7 +1745,7 @@
   target.SetToolchain(setup.toolchain());
   ASSERT_TRUE(target.OnResolved(&err));
 
-  StringOutputStream out;
+  std::ostringstream out;
   NinjaCBinaryTargetWriter writer(&target, out);
   writer.Run();
 
@@ -1822,7 +1822,7 @@
   target.SetToolchain(setup.toolchain());
   ASSERT_TRUE(target.OnResolved(&err));
 
-  StringOutputStream out;
+  std::ostringstream out;
   NinjaCBinaryTargetWriter writer(&target, out);
   writer.Run();
 
@@ -1925,7 +1925,7 @@
   target.SetToolchain(setup.toolchain());
   ASSERT_TRUE(target.OnResolved(&err));
 
-  StringOutputStream out;
+  std::ostringstream out;
   NinjaCBinaryTargetWriter writer(&target, out);
   writer.Run();
 
@@ -2021,7 +2021,7 @@
   target.SetToolchain(setup.toolchain());
   ASSERT_TRUE(target.OnResolved(&err));
 
-  StringOutputStream out;
+  std::ostringstream out;
   NinjaCBinaryTargetWriter writer(&target, out);
   writer.Run();
 
@@ -2119,7 +2119,7 @@
   target.SetToolchain(setup.toolchain());
   ASSERT_TRUE(target.OnResolved(&err));
 
-  StringOutputStream out;
+  std::ostringstream out;
   NinjaCBinaryTargetWriter writer(&target, out);
   writer.Run();
 
@@ -2229,7 +2229,7 @@
   target.SetToolchain(setup.toolchain());
   ASSERT_TRUE(target.OnResolved(&err));
 
-  StringOutputStream out;
+  std::ostringstream out;
   NinjaCBinaryTargetWriter writer(&target, out);
   writer.Run();
 
@@ -2279,7 +2279,7 @@
   target.source_types_used().Set(SourceFile::SOURCE_MODULEMAP);
   ASSERT_TRUE(target.OnResolved(&err));
 
-  StringOutputStream out;
+  std::ostringstream out;
   NinjaCBinaryTargetWriter writer(&target, out);
   writer.Run();
 
@@ -2326,7 +2326,7 @@
   ASSERT_TRUE(foo_target.OnResolved(&err));
 
   {
-    StringOutputStream out;
+    std::ostringstream out;
     NinjaCBinaryTargetWriter writer(&foo_target, out);
     writer.Run();
 
@@ -2364,7 +2364,7 @@
     bar_target.SetToolchain(setup.toolchain());
     ASSERT_TRUE(bar_target.OnResolved(&err));
 
-    StringOutputStream out;
+    std::ostringstream out;
     NinjaCBinaryTargetWriter writer(&bar_target, out);
     writer.Run();
 
@@ -2410,7 +2410,7 @@
     bar_target.SetToolchain(setup.toolchain());
     ASSERT_TRUE(bar_target.OnResolved(&err));
 
-    StringOutputStream out;
+    std::ostringstream out;
     NinjaCBinaryTargetWriter writer(&bar_target, out);
     writer.Run();
 
@@ -2445,7 +2445,7 @@
     bar_target.SetToolchain(setup.toolchain());
     ASSERT_TRUE(bar_target.OnResolved(&err));
 
-    StringOutputStream out;
+    std::ostringstream out;
     NinjaCBinaryTargetWriter writer(&bar_target, out);
     writer.Run();
 
@@ -2544,7 +2544,7 @@
 
   // The library first.
   {
-    StringOutputStream out;
+    std::ostringstream out;
     NinjaCBinaryTargetWriter writer(&target, out);
     writer.Run();
 
@@ -2589,7 +2589,7 @@
 
   // A second library to make sure the depender includes both.
   {
-    StringOutputStream out;
+    std::ostringstream out;
     NinjaCBinaryTargetWriter writer(&target2, out);
     writer.Run();
 
@@ -2633,7 +2633,7 @@
   // A third library that depends on one of the previous static libraries, to
   // check module_deps_no_self.
   {
-    StringOutputStream out;
+    std::ostringstream out;
     NinjaCBinaryTargetWriter writer(&target3, out);
     writer.Run();
 
@@ -2674,7 +2674,7 @@
 
   // Then the executable that depends on it.
   {
-    StringOutputStream out;
+    std::ostringstream out;
     NinjaCBinaryTargetWriter writer(&depender, out);
     writer.Run();
 
@@ -2736,7 +2736,7 @@
   target.SetToolchain(&toolchain_with_toc);
   ASSERT_TRUE(target.OnResolved(&err));
 
-  StringOutputStream out;
+  std::ostringstream out;
   NinjaCBinaryTargetWriter writer(&target, out);
   writer.Run();
 
@@ -2786,7 +2786,7 @@
   target.SetToolchain(setup.toolchain());
   ASSERT_TRUE(target.OnResolved(&err));
 
-  StringOutputStream out;
+  std::ostringstream out;
   NinjaBinaryTargetWriter writer(&target, out);
   writer.Run();
 
diff --git a/src/gn/ninja_copy_target_writer.cc b/src/gn/ninja_copy_target_writer.cc
index 5be189b..47657c0 100644
--- a/src/gn/ninja_copy_target_writer.cc
+++ b/src/gn/ninja_copy_target_writer.cc
@@ -8,7 +8,6 @@
 #include "gn/general_tool.h"
 #include "gn/ninja_utils.h"
 #include "gn/output_file.h"
-#include "gn/output_stream.h"
 #include "gn/scheduler.h"
 #include "gn/string_utils.h"
 #include "gn/substitution_list.h"
@@ -17,7 +16,7 @@
 #include "gn/toolchain.h"
 
 NinjaCopyTargetWriter::NinjaCopyTargetWriter(const Target* target,
-                                             OutputStream& out)
+                                             std::ostream& out)
     : NinjaTargetWriter(target, out) {}
 
 NinjaCopyTargetWriter::~NinjaCopyTargetWriter() = default;
@@ -56,7 +55,7 @@
 
   std::vector<OutputFile> output_files;
   WriteCopyRules(&output_files);
-  out_ << "\n";
+  out_ << std::endl;
   WriteStampOrPhonyForTarget(output_files, std::vector<OutputFile>());
 }
 
@@ -125,6 +124,6 @@
       path_output_.WriteFiles(out_, input_deps);
       path_output_.WriteFiles(out_, data_outs);
     }
-    out_ << "\n";
+    out_ << std::endl;
   }
 }
diff --git a/src/gn/ninja_copy_target_writer.h b/src/gn/ninja_copy_target_writer.h
index 0717fc8..95d50b4 100644
--- a/src/gn/ninja_copy_target_writer.h
+++ b/src/gn/ninja_copy_target_writer.h
@@ -7,12 +7,10 @@
 
 #include "gn/ninja_target_writer.h"
 
-class OutputStream;
-
 // Writes a .ninja file for a copy target type.
 class NinjaCopyTargetWriter : public NinjaTargetWriter {
  public:
-  NinjaCopyTargetWriter(const Target* target, OutputStream& out);
+  NinjaCopyTargetWriter(const Target* target, std::ostream& out);
   ~NinjaCopyTargetWriter() override;
 
   void Run() override;
diff --git a/src/gn/ninja_copy_target_writer_unittest.cc b/src/gn/ninja_copy_target_writer_unittest.cc
index 3870ac3..1b5077a 100644
--- a/src/gn/ninja_copy_target_writer_unittest.cc
+++ b/src/gn/ninja_copy_target_writer_unittest.cc
@@ -3,9 +3,9 @@
 // found in the LICENSE file.
 
 #include <algorithm>
+#include <sstream>
 
 #include "gn/ninja_copy_target_writer.h"
-#include "gn/output_stream.h"
 #include "gn/target.h"
 #include "gn/test_with_scope.h"
 #include "util/test/test.h"
@@ -27,7 +27,7 @@
   target.SetToolchain(setup.toolchain());
   ASSERT_TRUE(target.OnResolved(&err));
 
-  StringOutputStream out;
+  std::ostringstream out;
   NinjaCopyTargetWriter writer(&target, out);
   writer.Run();
 
@@ -56,7 +56,7 @@
   target.SetToolchain(setup.toolchain());
   ASSERT_TRUE(target.OnResolved(&err));
 
-  StringOutputStream out;
+  std::ostringstream out;
   NinjaCopyTargetWriter writer(&target, out);
   writer.Run();
 
@@ -81,7 +81,7 @@
   target.SetToolchain(setup.toolchain());
   ASSERT_TRUE(target.OnResolved(&err));
 
-  StringOutputStream out;
+  std::ostringstream out;
   NinjaCopyTargetWriter writer(&target, out);
   writer.Run();
 
@@ -113,7 +113,7 @@
   target.SetToolchain(setup.toolchain());
   ASSERT_TRUE(target.OnResolved(&err));
 
-  StringOutputStream out;
+  std::ostringstream out;
   NinjaCopyTargetWriter writer(&target, out);
   writer.Run();
 
diff --git a/src/gn/ninja_create_bundle_target_writer.cc b/src/gn/ninja_create_bundle_target_writer.cc
index 13fb1d8..09299c7 100644
--- a/src/gn/ninja_create_bundle_target_writer.cc
+++ b/src/gn/ninja_create_bundle_target_writer.cc
@@ -12,7 +12,6 @@
 #include "gn/general_tool.h"
 #include "gn/ninja_utils.h"
 #include "gn/output_file.h"
-#include "gn/output_stream.h"
 #include "gn/scheduler.h"
 #include "gn/substitution_writer.h"
 #include "gn/target.h"
@@ -69,7 +68,7 @@
 
 NinjaCreateBundleTargetWriter::NinjaCreateBundleTargetWriter(
     const Target* target,
-    OutputStream& out)
+    std::ostream& out)
     : NinjaTargetWriter(target, out) {}
 
 NinjaCreateBundleTargetWriter::~NinjaCreateBundleTargetWriter() = default;
@@ -114,7 +113,7 @@
                  target_->bundle_data().GetBundleRootDirOutput(settings_)));
   out_ << ": " << BuiltinTool::kBuiltinToolPhony << " ";
   out_ << target_->dependency_output().value();
-  out_ << "\n";
+  out_ << std::endl;
 }
 
 std::string NinjaCreateBundleTargetWriter::WritePostProcessingRuleDefinition() {
@@ -126,7 +125,7 @@
   base::ReplaceChars(custom_rule_name, ":/()", "_", &custom_rule_name);
   custom_rule_name.append("_post_processing_rule");
 
-  out_ << "rule " << custom_rule_name << "\n";
+  out_ << "rule " << custom_rule_name << std::endl;
   out_ << "  command = ";
   path_output_.WriteFile(out_, settings_->build_settings()->python_path());
   out_ << " ";
@@ -140,10 +139,10 @@
     out_ << " ";
     SubstitutionWriter::WriteWithNinjaVariables(arg, args_escape_options, out_);
   }
-  out_ << "\n";
-  out_ << "  description = POST PROCESSING " << target_label << "\n";
-  out_ << "  restat = 1\n";
-  out_ << "\n";
+  out_ << std::endl;
+  out_ << "  description = POST PROCESSING " << target_label << std::endl;
+  out_ << "  restat = 1" << std::endl;
+  out_ << std::endl;
 
   return custom_rule_name;
 }
@@ -184,7 +183,7 @@
       path_output_.WriteFiles(out_, order_only_deps);
     }
 
-    out_ << "\n";
+    out_ << std::endl;
   }
 }
 
@@ -225,7 +224,7 @@
       out_ << " ||";
       path_output_.WriteFiles(out_, order_only_deps);
     }
-    out_ << "\n";
+    out_ << std::endl;
     return;
   }
 
@@ -262,14 +261,15 @@
     path_output_.WriteFiles(out_, order_only_deps);
   }
 
-  out_ << "\n";
+  out_ << std::endl;
 
-  out_ << "  product_type = " << target_->bundle_data().product_type() << "\n";
+  out_ << "  product_type = " << target_->bundle_data().product_type()
+       << std::endl;
 
   if (partial_info_plist != OutputFile()) {
     out_ << "  partial_info_plist = ";
     path_output_.WriteFile(out_, partial_info_plist);
-    out_ << "\n";
+    out_ << std::endl;
   }
 
   const std::vector<SubstitutionPattern>& flags =
@@ -283,7 +283,7 @@
       SubstitutionWriter::WriteWithNinjaVariables(flag, args_escape_options,
                                                   out_);
     }
-    out_ << "\n";
+    out_ << std::endl;
   }
 }
 
@@ -325,7 +325,7 @@
       path_output_.WriteFile(out_, target->dependency_output());
     }
   }
-  out_ << "\n";
+  out_ << std::endl;
   return xcassets_input_stamp_or_phony;
 }
 
@@ -355,7 +355,7 @@
   out_ << ": " << post_processing_rule_name;
   out_ << " | ";
   path_output_.WriteFile(out_, post_processing_input_stamp_file);
-  out_ << "\n";
+  out_ << std::endl;
 }
 
 OutputFile
@@ -411,6 +411,6 @@
     out_ << " ||";
     path_output_.WriteFiles(out_, order_only_deps);
   }
-  out_ << "\n";
+  out_ << std::endl;
   return stamp_or_phony;
 }
diff --git a/src/gn/ninja_create_bundle_target_writer.h b/src/gn/ninja_create_bundle_target_writer.h
index ce68fa2..ee27557 100644
--- a/src/gn/ninja_create_bundle_target_writer.h
+++ b/src/gn/ninja_create_bundle_target_writer.h
@@ -12,7 +12,7 @@
 // Writes a .ninja file for a bundle_data target type.
 class NinjaCreateBundleTargetWriter : public NinjaTargetWriter {
  public:
-  NinjaCreateBundleTargetWriter(const Target* target, OutputStream& out);
+  NinjaCreateBundleTargetWriter(const Target* target, std::ostream& out);
   ~NinjaCreateBundleTargetWriter() override;
 
   void Run() override;
diff --git a/src/gn/ninja_create_bundle_target_writer_unittest.cc b/src/gn/ninja_create_bundle_target_writer_unittest.cc
index 917f047..d79b719 100644
--- a/src/gn/ninja_create_bundle_target_writer_unittest.cc
+++ b/src/gn/ninja_create_bundle_target_writer_unittest.cc
@@ -6,8 +6,8 @@
 
 #include <algorithm>
 #include <memory>
+#include <sstream>
 
-#include "gn/output_stream.h"
 #include "gn/target.h"
 #include "gn/test_with_scope.h"
 #include "util/test/test.h"
@@ -70,7 +70,7 @@
   create_bundle.SetToolchain(setup.toolchain());
   ASSERT_TRUE(create_bundle.OnResolved(&err));
 
-  StringOutputStream out;
+  std::ostringstream out;
   NinjaCreateBundleTargetWriter writer(&create_bundle, out);
   writer.Run();
 
@@ -119,7 +119,7 @@
   create_bundle.SetToolchain(setup.toolchain());
   ASSERT_TRUE(create_bundle.OnResolved(&err));
 
-  StringOutputStream out;
+  std::ostringstream out;
   NinjaCreateBundleTargetWriter writer(&create_bundle, out);
   writer.Run();
 
@@ -160,7 +160,7 @@
   create_bundle.SetToolchain(setup.toolchain());
   ASSERT_TRUE(create_bundle.OnResolved(&err));
 
-  StringOutputStream out;
+  std::ostringstream out;
   NinjaCreateBundleTargetWriter writer(&create_bundle, out);
   writer.Run();
 
@@ -224,7 +224,7 @@
   create_bundle.SetToolchain(setup.toolchain());
   ASSERT_TRUE(create_bundle.OnResolved(&err));
 
-  StringOutputStream out;
+  std::ostringstream out;
   NinjaCreateBundleTargetWriter writer(&create_bundle, out);
   writer.Run();
 
@@ -268,7 +268,7 @@
 
   ASSERT_TRUE(create_bundle.OnResolved(&err));
 
-  StringOutputStream out;
+  std::ostringstream out;
   NinjaCreateBundleTargetWriter writer(&create_bundle, out);
   writer.Run();
 
@@ -388,7 +388,7 @@
   create_bundle.SetToolchain(setup.toolchain());
   ASSERT_TRUE(create_bundle.OnResolved(&err));
 
-  StringOutputStream out;
+  std::ostringstream out;
   NinjaCreateBundleTargetWriter writer(&create_bundle, out);
   writer.Run();
 
@@ -470,7 +470,7 @@
   create_bundle.SetToolchain(setup.toolchain());
   ASSERT_TRUE(create_bundle.OnResolved(&err));
 
-  StringOutputStream out;
+  std::ostringstream out;
   NinjaCreateBundleTargetWriter writer(&create_bundle, out);
   writer.Run();
 
@@ -553,7 +553,7 @@
   create_bundle.SetToolchain(setup.toolchain());
   ASSERT_TRUE(create_bundle.OnResolved(&err));
 
-  StringOutputStream out;
+  std::ostringstream out;
   NinjaCreateBundleTargetWriter writer(&create_bundle, out);
   writer.Run();
 
diff --git a/src/gn/ninja_generated_file_target_writer.cc b/src/gn/ninja_generated_file_target_writer.cc
index 81ac730..6de8207 100644
--- a/src/gn/ninja_generated_file_target_writer.cc
+++ b/src/gn/ninja_generated_file_target_writer.cc
@@ -6,7 +6,6 @@
 
 #include "gn/output_conversion.h"
 #include "gn/output_file.h"
-#include "gn/output_stream.h"
 #include "gn/scheduler.h"
 #include "gn/settings.h"
 #include "gn/string_output_buffer.h"
@@ -16,7 +15,7 @@
 
 NinjaGeneratedFileTargetWriter::NinjaGeneratedFileTargetWriter(
     const Target* target,
-    OutputStream& out)
+    std::ostream& out)
     : NinjaTargetWriter(target, out) {}
 
 NinjaGeneratedFileTargetWriter::~NinjaGeneratedFileTargetWriter() = default;
@@ -90,8 +89,9 @@
 
   // Compute output.
   StringOutputBuffer storage;
-  ConvertValueToOutput(settings_, contents, target_->output_conversion(),
-                       storage, &err);
+  std::ostream out(&storage);
+  ConvertValueToOutput(settings_, contents, target_->output_conversion(), out,
+                       &err);
 
   if (err.has_error()) {
     g_scheduler->FailWithError(err);
diff --git a/src/gn/ninja_generated_file_target_writer.h b/src/gn/ninja_generated_file_target_writer.h
index 90e64d4..3103388 100644
--- a/src/gn/ninja_generated_file_target_writer.h
+++ b/src/gn/ninja_generated_file_target_writer.h
@@ -10,7 +10,7 @@
 // Writes a .ninja file for a group target type.
 class NinjaGeneratedFileTargetWriter : public NinjaTargetWriter {
  public:
-  NinjaGeneratedFileTargetWriter(const Target* target, OutputStream& out);
+  NinjaGeneratedFileTargetWriter(const Target* target, std::ostream& out);
   ~NinjaGeneratedFileTargetWriter() override;
 
   void Run() override;
diff --git a/src/gn/ninja_generated_file_target_writer_unittest.cc b/src/gn/ninja_generated_file_target_writer_unittest.cc
index 119f4ea..481db80 100644
--- a/src/gn/ninja_generated_file_target_writer_unittest.cc
+++ b/src/gn/ninja_generated_file_target_writer_unittest.cc
@@ -4,7 +4,6 @@
 
 #include "gn/ninja_generated_file_target_writer.h"
 
-#include "gn/output_stream.h"
 #include "gn/source_file.h"
 #include "gn/target.h"
 #include "gn/test_with_scheduler.h"
@@ -59,7 +58,7 @@
   target.SetToolchain(setup.toolchain());
   ASSERT_TRUE(target.OnResolved(&err)) << err.message();
 
-  StringOutputStream out;
+  std::ostringstream out;
   NinjaGeneratedFileTargetWriter writer(&target, out);
   writer.Run();
 
diff --git a/src/gn/ninja_group_target_writer.cc b/src/gn/ninja_group_target_writer.cc
index 3dde553..7db1a3a 100644
--- a/src/gn/ninja_group_target_writer.cc
+++ b/src/gn/ninja_group_target_writer.cc
@@ -11,7 +11,7 @@
 #include "gn/target.h"
 
 NinjaGroupTargetWriter::NinjaGroupTargetWriter(const Target* target,
-                                               OutputStream& out)
+                                               std::ostream& out)
     : NinjaTargetWriter(target, out) {}
 
 NinjaGroupTargetWriter::~NinjaGroupTargetWriter() = default;
diff --git a/src/gn/ninja_group_target_writer.h b/src/gn/ninja_group_target_writer.h
index b286bce..7a3f211 100644
--- a/src/gn/ninja_group_target_writer.h
+++ b/src/gn/ninja_group_target_writer.h
@@ -10,7 +10,7 @@
 // Writes a .ninja file for a group target type.
 class NinjaGroupTargetWriter : public NinjaTargetWriter {
  public:
-  NinjaGroupTargetWriter(const Target* target, OutputStream& out);
+  NinjaGroupTargetWriter(const Target* target, std::ostream& out);
   ~NinjaGroupTargetWriter() override;
 
   void Run() override;
diff --git a/src/gn/ninja_group_target_writer_unittest.cc b/src/gn/ninja_group_target_writer_unittest.cc
index e61b2f8..ec9f72d 100644
--- a/src/gn/ninja_group_target_writer_unittest.cc
+++ b/src/gn/ninja_group_target_writer_unittest.cc
@@ -3,7 +3,6 @@
 // found in the LICENSE file.
 
 #include "gn/ninja_group_target_writer.h"
-#include "gn/output_stream.h"
 #include "gn/target.h"
 #include "gn/test_with_scope.h"
 #include "util/test/test.h"
@@ -50,7 +49,7 @@
   target.SetToolchain(setup.toolchain());
   ASSERT_TRUE(target.OnResolved(&err));
 
-  StringOutputStream out;
+  std::ostringstream out;
   NinjaGroupTargetWriter writer(&target, out);
   writer.Run();
 
diff --git a/src/gn/ninja_rust_binary_target_writer.cc b/src/gn/ninja_rust_binary_target_writer.cc
index e4aafe8..ba6b416 100644
--- a/src/gn/ninja_rust_binary_target_writer.cc
+++ b/src/gn/ninja_rust_binary_target_writer.cc
@@ -4,6 +4,8 @@
 
 #include "gn/ninja_rust_binary_target_writer.h"
 
+#include <sstream>
+
 #include "base/strings/string_util.h"
 #include "gn/deps_iterator.h"
 #include "gn/filesystem_utils.h"
@@ -27,16 +29,16 @@
 void WriteVar(const char* name,
               const std::string& value,
               EscapeOptions opts,
-              OutputStream& out) {
+              std::ostream& out) {
   out << name << " = ";
   EscapeStringToStream(out, value, opts);
-  out << "\n";
+  out << std::endl;
 }
 
 void WriteCrateVars(const Target* target,
                     const Tool* tool,
                     EscapeOptions opts,
-                    OutputStream& out) {
+                    std::ostream& out) {
   WriteVar(kRustSubstitutionCrateName.ninja_name,
            target->rust_values().crate_name(), opts, out);
 
@@ -98,7 +100,7 @@
 }  // namespace
 
 NinjaRustBinaryTargetWriter::NinjaRustBinaryTargetWriter(const Target* target,
-                                                         OutputStream& out)
+                                                         std::ostream& out)
     : NinjaBinaryTargetWriter(target, out),
       tool_(target->toolchain()->GetToolForTargetFinalOutputAsRust(target)) {}
 
@@ -263,7 +265,7 @@
     out_ << " ";
     path_output_.WriteFile(out_, OutputFile(settings_->build_settings(), data));
   }
-  out_ << "\n";
+  out_ << std::endl;
 }
 
 void NinjaRustBinaryTargetWriter::WriteExternsAndDeps(
@@ -351,7 +353,7 @@
     }
   }
 
-  out_ << "\n";
+  out_ << std::endl;
   out_ << "  rustdeps =";
 
   for (const SourceDir& dir : private_extern_dirs) {
@@ -383,11 +385,11 @@
   WriteFrameworks(out_, tool_);
   WriteSwiftModules(out_, tool_, swiftmodules);
 
-  out_ << "\n";
+  out_ << std::endl;
   out_ << "  ldflags =";
   // If rustc will invoke a linker, linker flags need to be forwarded through to
   // the linker.
   WriteCustomLinkerFlags(out_, tool_);
 
-  out_ << "\n";
+  out_ << std::endl;
 }
diff --git a/src/gn/ninja_rust_binary_target_writer.h b/src/gn/ninja_rust_binary_target_writer.h
index 7966fc7..83e1203 100644
--- a/src/gn/ninja_rust_binary_target_writer.h
+++ b/src/gn/ninja_rust_binary_target_writer.h
@@ -14,7 +14,7 @@
 // library, or a static library).
 class NinjaRustBinaryTargetWriter : public NinjaBinaryTargetWriter {
  public:
-  NinjaRustBinaryTargetWriter(const Target* target, OutputStream& out);
+  NinjaRustBinaryTargetWriter(const Target* target, std::ostream& out);
   ~NinjaRustBinaryTargetWriter() override;
 
   void Run() override;
diff --git a/src/gn/ninja_rust_binary_target_writer_unittest.cc b/src/gn/ninja_rust_binary_target_writer_unittest.cc
index 2497ac6..461cbd8 100644
--- a/src/gn/ninja_rust_binary_target_writer_unittest.cc
+++ b/src/gn/ninja_rust_binary_target_writer_unittest.cc
@@ -6,7 +6,6 @@
 
 #include "gn/config.h"
 #include "gn/label_ptr.h"
-#include "gn/output_stream.h"
 #include "gn/pool.h"
 #include "gn/rust_values.h"
 #include "gn/scheduler.h"
@@ -50,7 +49,7 @@
   ASSERT_TRUE(target.OnResolved(&err));
 
   {
-    StringOutputStream out;
+    std::ostringstream out;
     NinjaRustBinaryTargetWriter writer(&target, out);
     writer.Run();
 
@@ -105,7 +104,7 @@
   ASSERT_TRUE(private_rlib.OnResolved(&err));
 
   {
-    StringOutputStream out;
+    std::ostringstream out;
     NinjaRustBinaryTargetWriter writer(&private_rlib, out);
     writer.Run();
 
@@ -147,7 +146,7 @@
   ASSERT_TRUE(far_public_rlib.OnResolved(&err));
 
   {
-    StringOutputStream out;
+    std::ostringstream out;
     NinjaRustBinaryTargetWriter writer(&far_public_rlib, out);
     writer.Run();
 
@@ -189,7 +188,7 @@
   ASSERT_TRUE(public_rlib.OnResolved(&err));
 
   {
-    StringOutputStream out;
+    std::ostringstream out;
     NinjaRustBinaryTargetWriter writer(&public_rlib, out);
     writer.Run();
 
@@ -245,7 +244,7 @@
   ASSERT_TRUE(target.OnResolved(&err));
 
   {
-    StringOutputStream out;
+    std::ostringstream out;
     NinjaRustBinaryTargetWriter writer(&target, out);
     writer.Run();
 
@@ -298,7 +297,7 @@
   ASSERT_TRUE(private_inside_dylib.OnResolved(&err));
 
   {
-    StringOutputStream out;
+    std::ostringstream out;
     NinjaRustBinaryTargetWriter writer(&private_inside_dylib, out);
     writer.Run();
 
@@ -339,7 +338,7 @@
   ASSERT_TRUE(inside_dylib.OnResolved(&err));
 
   {
-    StringOutputStream out;
+    std::ostringstream out;
     NinjaRustBinaryTargetWriter writer(&inside_dylib, out);
     writer.Run();
 
@@ -383,7 +382,7 @@
   ASSERT_TRUE(dylib.OnResolved(&err));
 
   {
-    StringOutputStream out;
+    std::ostringstream out;
     NinjaRustBinaryTargetWriter writer(&dylib, out);
     writer.Run();
 
@@ -456,7 +455,7 @@
   ASSERT_TRUE(target.OnResolved(&err));
 
   {
-    StringOutputStream out;
+    std::ostringstream out;
     NinjaRustBinaryTargetWriter writer(&target, out);
     writer.Run();
 
@@ -508,7 +507,7 @@
   ASSERT_TRUE(procmacro.OnResolved(&err));
 
   {
-    StringOutputStream out;
+    std::ostringstream out;
     NinjaRustBinaryTargetWriter writer(&procmacro, out);
     writer.Run();
 
@@ -564,7 +563,7 @@
   ASSERT_TRUE(rlib.OnResolved(&err));
 
   {
-    StringOutputStream out;
+    std::ostringstream out;
     NinjaRustBinaryTargetWriter writer(&rlib, out);
     writer.Run();
 
@@ -611,7 +610,7 @@
   ASSERT_TRUE(target.OnResolved(&err));
 
   {
-    StringOutputStream out;
+    std::ostringstream out;
     NinjaRustBinaryTargetWriter writer(&target, out);
     writer.Run();
 
@@ -703,7 +702,7 @@
   ASSERT_TRUE(target.OnResolved(&err));
 
   {
-    StringOutputStream out;
+    std::ostringstream out;
     NinjaRustBinaryTargetWriter writer(&target, out);
     writer.Run();
 
@@ -806,7 +805,7 @@
   ASSERT_TRUE(nonrust.OnResolved(&err));
 
   {
-    StringOutputStream out;
+    std::ostringstream out;
     NinjaRustBinaryTargetWriter writer(&nonrust, out);
     writer.Run();
 
@@ -853,7 +852,7 @@
   ASSERT_TRUE(nonrust_only.OnResolved(&err));
 
   {
-    StringOutputStream out;
+    std::ostringstream out;
     NinjaRustBinaryTargetWriter writer(&nonrust_only, out);
     writer.Run();
 
@@ -894,7 +893,7 @@
   ASSERT_TRUE(rstaticlib.OnResolved(&err));
 
   {
-    StringOutputStream out;
+    std::ostringstream out;
     NinjaRustBinaryTargetWriter writer(&rstaticlib, out);
     writer.Run();
 
@@ -1066,7 +1065,7 @@
   target.SetToolchain(setup.toolchain());
   ASSERT_TRUE(target.OnResolved(&err));
 
-  StringOutputStream out;
+  std::ostringstream out;
   NinjaRustBinaryTargetWriter writer(&target, out);
   writer.Run();
 
@@ -1132,7 +1131,7 @@
   ASSERT_TRUE(target.OnResolved(&err));
 
   {
-    StringOutputStream out;
+    std::ostringstream out;
     NinjaRustBinaryTargetWriter writer(&target, out);
     writer.Run();
 
@@ -1199,7 +1198,7 @@
   ASSERT_TRUE(target.OnResolved(&err));
 
   {
-    StringOutputStream out;
+    std::ostringstream out;
     NinjaRustBinaryTargetWriter writer(&target, out);
     writer.Run();
 
@@ -1299,7 +1298,7 @@
   ASSERT_TRUE(rlib.OnResolved(&err));
 
   {
-    StringOutputStream out;
+    std::ostringstream out;
     NinjaRustBinaryTargetWriter writer(&rlib, out);
     writer.Run();
 
@@ -1383,7 +1382,7 @@
   ASSERT_TRUE(procmacro.OnResolved(&err));
 
   {
-    StringOutputStream out;
+    std::ostringstream out;
     NinjaRustBinaryTargetWriter writer(&procmacro, out);
     writer.Run();
 
@@ -1430,7 +1429,7 @@
   ASSERT_TRUE(target.OnResolved(&err));
 
   {
-    StringOutputStream out;
+    std::ostringstream out;
     NinjaRustBinaryTargetWriter writer(&target, out);
     writer.Run();
 
@@ -1476,7 +1475,7 @@
   ASSERT_TRUE(rlib.OnResolved(&err));
 
   {
-    StringOutputStream out;
+    std::ostringstream out;
     NinjaRustBinaryTargetWriter writer(&rlib, out);
     writer.Run();
 
@@ -1525,7 +1524,7 @@
   ASSERT_TRUE(target.OnResolved(&err));
 
   {
-    StringOutputStream out;
+    std::ostringstream out;
     NinjaRustBinaryTargetWriter writer(&target, out);
     writer.Run();
 
@@ -1580,7 +1579,7 @@
   ASSERT_TRUE(target.OnResolved(&err));
 
   {
-    StringOutputStream out;
+    std::ostringstream out;
     NinjaRustBinaryTargetWriter writer(&target, out);
     writer.Run();
 
@@ -1629,7 +1628,7 @@
   ASSERT_TRUE(target.OnResolved(&err));
 
   {
-    StringOutputStream out;
+    std::ostringstream out;
     NinjaRustBinaryTargetWriter writer(&target, out);
     writer.Run();
 
@@ -1677,7 +1676,7 @@
   cdylib.SetToolchain(setup.toolchain());
   ASSERT_TRUE(cdylib.OnResolved(&err));
   {
-    StringOutputStream out;
+    std::ostringstream out;
     NinjaRustBinaryTargetWriter writer(&cdylib, out);
     writer.Run();
     const char expected[] =
@@ -1717,7 +1716,7 @@
   target.SetToolchain(setup.toolchain());
   ASSERT_TRUE(target.OnResolved(&err));
   {
-    StringOutputStream out;
+    std::ostringstream out;
     NinjaRustBinaryTargetWriter writer(&target, out);
     writer.Run();
 
@@ -1794,7 +1793,7 @@
   ASSERT_TRUE(target.OnResolved(&err));
 
   {
-    StringOutputStream out;
+    std::ostringstream out;
     NinjaRustBinaryTargetWriter writer(&target, out);
     writer.Run();
 
@@ -1879,7 +1878,7 @@
   ASSERT_TRUE(target.OnResolved(&err));
 
   {
-    StringOutputStream out;
+    std::ostringstream out;
     NinjaRustBinaryTargetWriter writer(&target, out);
     writer.Run();
 
@@ -1933,7 +1932,7 @@
   target.SetToolchain(setup.toolchain());
   ASSERT_TRUE(target.OnResolved(&err));
 
-  StringOutputStream out;
+  std::ostringstream out;
   NinjaBinaryTargetWriter writer(&target, out);
   writer.Run();
 
@@ -2006,7 +2005,7 @@
   target.SetToolchain(setup.toolchain());
   ASSERT_TRUE(target.OnResolved(&err));
 
-  StringOutputStream out;
+  std::ostringstream out;
   NinjaRustBinaryTargetWriter writer(&target, out);
   writer.Run();
 
@@ -2064,7 +2063,7 @@
   target.SetToolchain(setup.toolchain());
   ASSERT_TRUE(target.OnResolved(&err));
 
-  StringOutputStream out;
+  std::ostringstream out;
   NinjaRustBinaryTargetWriter writer(&target, out);
   writer.Run();
 
diff --git a/src/gn/ninja_target_command_util.cc b/src/gn/ninja_target_command_util.cc
index 4b78677..a691ae3 100644
--- a/src/gn/ninja_target_command_util.cc
+++ b/src/gn/ninja_target_command_util.cc
@@ -50,7 +50,7 @@
                       const,
                   EscapeOptions flag_escape_options,
                   PathOutput& path_output,
-                  OutputStream& out,
+                  std::ostream& out,
                   bool write_substitution,
                   bool indent) {
   if (!target->toolchain()->substitution_bits().used.count(subst_enum))
@@ -103,7 +103,7 @@
   }
 
   if (write_substitution)
-    out << "\n";
+    out << std::endl;
 }
 
 void GetPCHOutputFiles(const Target* target,
diff --git a/src/gn/ninja_target_command_util.h b/src/gn/ninja_target_command_util.h
index e754ef7..93666fb 100644
--- a/src/gn/ninja_target_command_util.h
+++ b/src/gn/ninja_target_command_util.h
@@ -12,7 +12,6 @@
 #include "gn/escape.h"
 #include "gn/filesystem_utils.h"
 #include "gn/frameworks_utils.h"
-#include "gn/output_stream.h"
 #include "gn/path_output.h"
 #include "gn/target.h"
 #include "gn/toolchain.h"
@@ -22,7 +21,7 @@
   DefineWriter() { options.mode = ESCAPE_NINJA_COMMAND; }
   DefineWriter(EscapingMode mode) { options.mode = mode; }
 
-  void operator()(const std::string& s, OutputStream& out) const {
+  void operator()(const std::string& s, std::ostream& out) const {
     out << " ";
     EscapeStringToStream(out, "-D" + s, options);
   }
@@ -36,8 +35,8 @@
 
   ~FrameworkDirsWriter() = default;
 
-  void operator()(const SourceDir& d, OutputStream& out) const {
-    StringOutputStream path_out;
+  void operator()(const SourceDir& d, std::ostream& out) const {
+    std::ostringstream path_out;
     path_output_.WriteDir(path_out, d, PathOutput::DIR_NO_LAST_SLASH);
     const std::string& path = path_out.str();
     if (path[0] == '"')
@@ -58,7 +57,7 @@
     options_.mode = mode;
   }
 
-  void operator()(const std::string& s, OutputStream& out) const {
+  void operator()(const std::string& s, std::ostream& out) const {
     out << " " << tool_switch_;
     std::string_view framework_name = GetFrameworkName(s);
     EscapeStringToStream(out, framework_name, options_);
@@ -72,8 +71,8 @@
   explicit IncludeWriter(PathOutput& path_output) : path_output_(path_output) {}
   ~IncludeWriter() = default;
 
-  void operator()(const SourceDir& d, OutputStream& out) const {
-    StringOutputStream path_out;
+  void operator()(const SourceDir& d, std::ostream& out) const {
+    std::ostringstream path_out;
     path_output_.WriteDir(path_out, d, PathOutput::DIR_NO_LAST_SLASH);
     const std::string& path = path_out.str();
     if (path[0] == '"')
@@ -102,7 +101,7 @@
                       const,
                   EscapeOptions flag_escape_options,
                   PathOutput& path_output,
-                  OutputStream& out,
+                  std::ostream& out,
                   bool write_substitution = true,
                   bool indent = false);
 
diff --git a/src/gn/ninja_target_command_util_unittest.cc b/src/gn/ninja_target_command_util_unittest.cc
index 6b2a9f2..8da62d3 100644
--- a/src/gn/ninja_target_command_util_unittest.cc
+++ b/src/gn/ninja_target_command_util_unittest.cc
@@ -5,8 +5,8 @@
 #include "gn/ninja_target_command_util.h"
 
 #include <algorithm>
+#include <sstream>
 
-#include "gn/output_stream.h"
 #include "util/build_config.h"
 #include "util/test/test.h"
 
@@ -16,7 +16,7 @@
 // the generated output as a string.
 template <typename Writer, typename Item>
 std::string FormatWithWriter(Writer writer, std::vector<Item> items) {
-  StringOutputStream out;
+  std::ostringstream out;
   for (const Item& item : items) {
     writer(item, out);
   }
@@ -36,7 +36,7 @@
   // see the difference in the error message (by default the error message
   // would just be "formatted == expected").
   if (formatted != expected) {
-    StringOutputStream stream;
+    std::ostringstream stream;
     stream << '"' << expected << "\" == \"" << formatted << '"';
     std::string message = stream.str();
 
diff --git a/src/gn/ninja_target_writer.cc b/src/gn/ninja_target_writer.cc
index 163840f..01a1407 100644
--- a/src/gn/ninja_target_writer.cc
+++ b/src/gn/ninja_target_writer.cc
@@ -4,6 +4,8 @@
 
 #include "gn/ninja_target_writer.h"
 
+#include <sstream>
+
 #include "base/files/file_util.h"
 #include "base/strings/string_util.h"
 #include "gn/builtin_tool.h"
@@ -23,7 +25,6 @@
 #include "gn/ninja_target_command_util.h"
 #include "gn/ninja_utils.h"
 #include "gn/output_file.h"
-#include "gn/output_stream.h"
 #include "gn/rust_substitution_type.h"
 #include "gn/scheduler.h"
 #include "gn/string_output_buffer.h"
@@ -32,7 +33,7 @@
 #include "gn/target.h"
 #include "gn/trace.h"
 
-NinjaTargetWriter::NinjaTargetWriter(const Target* target, OutputStream& out)
+NinjaTargetWriter::NinjaTargetWriter(const Target* target, std::ostream& out)
     : settings_(target->settings()),
       target_(target),
       out_(out),
@@ -110,7 +111,8 @@
 
   // It's ridiculously faster to write to a string and then write that to
   // disk in one operation than to use an fstream here.
-  StringOutputBuffer rules;
+  StringOutputBuffer storage;
+  std::ostream rules(&storage);
 
   // Call out to the correct sub-type of writer. Binary targets need to be
   // written to separate files for compiler flag scoping, but other target
@@ -174,7 +176,7 @@
     SourceFile ninja_file = GetNinjaFileForTarget(target);
     base::FilePath full_ninja_file =
         settings->build_settings()->GetFullPath(ninja_file);
-    rules.WriteToFileIfChanged(full_ninja_file, nullptr);
+    storage.WriteToFileIfChanged(full_ninja_file, nullptr);
 
     EscapeOptions options;
     options.mode = ESCAPE_NINJA;
@@ -189,7 +191,7 @@
   }
 
   // No separate file required, just return the rules.
-  return rules.str();
+  return storage.str();
 }
 
 void NinjaTargetWriter::WriteEscapedSubstitution(const Substitution* type) {
@@ -199,7 +201,7 @@
   out_ << type->ninja_name << " = ";
   EscapeStringToStream(
       out_, SubstitutionWriter::GetTargetSubstitution(target_, type), opts);
-  out_ << "\n";
+  out_ << std::endl;
 }
 
 void NinjaTargetWriter::WriteSharedVars(const SubstitutionBits& bits) {
@@ -256,7 +258,7 @@
   // If we wrote any vars, separate them from the rest of the file that follows
   // with a blank line.
   if (written_anything)
-    out_ << "\n";
+    out_ << std::endl;
 }
 
 void NinjaTargetWriter::WriteCCompilerVars(const SubstitutionBits& bits,
@@ -270,7 +272,7 @@
     RecursiveTargetConfigToStream<std::string>(kRecursiveWriterSkipDuplicates,
                                                target_, &ConfigValues::defines,
                                                DefineWriter(), out_);
-    out_ << "\n";
+    out_ << std::endl;
   }
 
   // Framework search path.
@@ -288,7 +290,7 @@
         FrameworkDirsWriter(framework_dirs_output,
                             tool->framework_dir_switch()),
         out_);
-    out_ << "\n";
+    out_ << std::endl;
   }
 
   // Include directories.
@@ -302,7 +304,7 @@
     RecursiveTargetConfigToStream<SourceDir>(
         kRecursiveWriterSkipDuplicates, target_, &ConfigValues::include_dirs,
         IncludeWriter(include_path_output), out_);
-    out_ << "\n";
+    out_ << std::endl;
   }
 
   bool has_precompiled_headers =
@@ -368,7 +370,7 @@
         out_ << "  ";
       out_ << CSubstitutionSwiftModuleName.ninja_name << " = ";
       EscapeStringToStream(out_, target_->swift_values().module_name(), opts);
-      out_ << "\n";
+      out_ << std::endl;
     }
 
     if (bits.used.count(&CSubstitutionSwiftBridgeHeader)) {
@@ -380,7 +382,7 @@
       } else {
         out_ << R"("")";
       }
-      out_ << "\n";
+      out_ << std::endl;
     }
 
     if (bits.used.count(&CSubstitutionSwiftModuleDirs)) {
@@ -400,7 +402,7 @@
       for (const SourceDir& swiftmodule_dir : swiftmodule_dirs) {
         swiftmodule_path_writer(swiftmodule_dir, out_);
       }
-      out_ << "\n";
+      out_ << std::endl;
     }
 
     WriteOneFlag(kRecursiveWriterKeepDuplicates, target_,
@@ -619,5 +621,5 @@
     out_ << " ||";
     path_output_.WriteFiles(out_, order_only_deps);
   }
-  out_ << "\n";
+  out_ << std::endl;
 }
diff --git a/src/gn/ninja_target_writer.h b/src/gn/ninja_target_writer.h
index 4ae2515..68bc0a3 100644
--- a/src/gn/ninja_target_writer.h
+++ b/src/gn/ninja_target_writer.h
@@ -12,7 +12,6 @@
 #include "gn/substitution_type.h"
 
 class OutputFile;
-class OutputStream;
 class Settings;
 class Target;
 struct SubstitutionBits;
@@ -21,7 +20,7 @@
 // generated by the NinjaBuildWriter.
 class NinjaTargetWriter {
  public:
-  NinjaTargetWriter(const Target* target, OutputStream& out);
+  NinjaTargetWriter(const Target* target, std::ostream& out);
   virtual ~NinjaTargetWriter();
 
   // Returns a ResolvedTargetData that can be used to retrieve information
@@ -102,7 +101,7 @@
 
   const Settings* settings_;  // Non-owning.
   const Target* target_;      // Non-owning.
-  OutputStream& out_;
+  std::ostream& out_;
   PathOutput path_output_;
 
   // Write a Ninja output file to out_, and also add it to |*ninja_outputs_|
diff --git a/src/gn/ninja_target_writer_unittest.cc b/src/gn/ninja_target_writer_unittest.cc
index d2472a9..ae691d7 100644
--- a/src/gn/ninja_target_writer_unittest.cc
+++ b/src/gn/ninja_target_writer_unittest.cc
@@ -2,9 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "gn/ninja_target_writer.h"
+#include <sstream>
+
 #include "gn/ninja_action_target_writer.h"
-#include "gn/output_stream.h"
+#include "gn/ninja_target_writer.h"
 #include "gn/target.h"
 #include "gn/test_with_scope.h"
 #include "util/test/test.h"
@@ -15,7 +16,7 @@
  public:
   TestingNinjaTargetWriter(const Target* target,
                            const Toolchain* toolchain,
-                           OutputStream& out)
+                           std::ostream& out)
       : NinjaTargetWriter(target, out) {}
 
   void Run() override {}
@@ -43,7 +44,7 @@
   base_target.action_values().set_script(SourceFile("//foo/script.py"));
   ASSERT_TRUE(base_target.OnResolved(&err));
 
-  StringOutputStream stream;
+  std::ostringstream stream;
   TestingNinjaTargetWriter writer(&base_target, setup.toolchain(), stream);
 
   const auto* resolved_ptr = &writer.resolved();
@@ -66,7 +67,7 @@
   ASSERT_TRUE(base_target.OnResolved(&err));
 
   ResolvedTargetData resolved;
-  StringOutputStream stream;
+  std::ostringstream stream;
   TestingNinjaTargetWriter writer(&base_target, setup.toolchain(), stream);
   writer.SetResolvedTargetData(&resolved);
 
@@ -110,7 +111,7 @@
 
   // Input deps for the base (should be only the script itself).
   {
-    StringOutputStream stream;
+    std::ostringstream stream;
     TestingNinjaTargetWriter writer(&base_target, setup.toolchain(), stream);
     std::vector<OutputFile> dep = writer.WriteInputDepsStampOrPhonyAndGetDep(
         std::vector<const Target*>(), 10u);
@@ -124,7 +125,7 @@
 
   // Input deps for the target (should depend on the base).
   {
-    StringOutputStream stream;
+    std::ostringstream stream;
     TestingNinjaTargetWriter writer(&target, setup.toolchain(), stream);
     std::vector<OutputFile> dep = writer.WriteInputDepsStampOrPhonyAndGetDep(
         std::vector<const Target*>(), 10u);
@@ -136,7 +137,7 @@
   }
 
   {
-    StringOutputStream stream;
+    std::ostringstream stream;
     NinjaActionTargetWriter writer(&action, stream);
     writer.Run();
     EXPECT_EQ(
@@ -155,7 +156,7 @@
   // Input deps for action which should depend on the base since its a hard dep
   // that is a (indirect) dependency, as well as the the action source.
   {
-    StringOutputStream stream;
+    std::ostringstream stream;
     TestingNinjaTargetWriter writer(&action, setup.toolchain(), stream);
     std::vector<OutputFile> dep = writer.WriteInputDepsStampOrPhonyAndGetDep(
         std::vector<const Target*>(), 10u);
@@ -207,7 +208,7 @@
 
   // Input deps for the base (should be only the script itself).
   {
-    StringOutputStream stream;
+    std::ostringstream stream;
     TestingNinjaTargetWriter writer(&base_target, setup.toolchain(), stream);
     std::vector<OutputFile> dep = writer.WriteInputDepsStampOrPhonyAndGetDep(
         std::vector<const Target*>(), 10u);
@@ -221,7 +222,7 @@
 
   // Input deps for the target (should depend on the base).
   {
-    StringOutputStream stream;
+    std::ostringstream stream;
     TestingNinjaTargetWriter writer(&target, setup.toolchain(), stream);
     std::vector<OutputFile> dep = writer.WriteInputDepsStampOrPhonyAndGetDep(
         std::vector<const Target*>(), 10u);
@@ -233,7 +234,7 @@
   }
 
   {
-    StringOutputStream stream;
+    std::ostringstream stream;
     NinjaActionTargetWriter writer(&action, stream);
     writer.Run();
     EXPECT_EQ(
@@ -252,7 +253,7 @@
   // Input deps for action which should depend on the base since its a hard dep
   // that is a (indirect) dependency, as well as the the action source.
   {
-    StringOutputStream stream;
+    std::ostringstream stream;
     TestingNinjaTargetWriter writer(&action, setup.toolchain(), stream);
     std::vector<OutputFile> dep = writer.WriteInputDepsStampOrPhonyAndGetDep(
         std::vector<const Target*>(), 10u);
@@ -288,7 +289,7 @@
   target.SetToolchain(setup.toolchain());
   ASSERT_TRUE(target.OnResolved(&err));
 
-  StringOutputStream stream;
+  std::ostringstream stream;
   TestingNinjaTargetWriter writer(&target, setup.toolchain(), stream);
   std::vector<OutputFile> dep = writer.WriteInputDepsStampOrPhonyAndGetDep(
       std::vector<const Target*>(), 10u);
diff --git a/src/gn/ninja_toolchain_writer.cc b/src/gn/ninja_toolchain_writer.cc
index 4d68eeb..917d068 100644
--- a/src/gn/ninja_toolchain_writer.cc
+++ b/src/gn/ninja_toolchain_writer.cc
@@ -4,6 +4,8 @@
 
 #include "gn/ninja_toolchain_writer.h"
 
+#include <fstream>
+
 #include "base/files/file_util.h"
 #include "base/strings/stringize_macros.h"
 #include "gn/build_settings.h"
@@ -12,7 +14,6 @@
 #include "gn/filesystem_utils.h"
 #include "gn/general_tool.h"
 #include "gn/ninja_utils.h"
-#include "gn/output_stream.h"
 #include "gn/pool.h"
 #include "gn/settings.h"
 #include "gn/substitution_writer.h"
@@ -28,7 +29,7 @@
 
 NinjaToolchainWriter::NinjaToolchainWriter(const Settings* settings,
                                            const Toolchain* toolchain,
-                                           OutputStream& out)
+                                           std::ostream& out)
     : settings_(settings),
       toolchain_(toolchain),
       out_(out),
@@ -49,7 +50,7 @@
     }
     WriteToolRule(tool.second.get(), rule_prefix);
   }
-  out_ << "\n";
+  out_ << std::endl;
 
   for (const auto& pair : rules)
     out_ << pair.second;
@@ -67,7 +68,9 @@
 
   base::CreateDirectory(ninja_file.DirName());
 
-  FileOutputStream file(FilePathToUTF8(ninja_file).c_str());
+  std::ofstream file;
+  file.open(FilePathToUTF8(ninja_file).c_str(),
+            std::ios_base::out | std::ios_base::binary);
   if (file.fail())
     return false;
 
@@ -78,7 +81,7 @@
 
 void NinjaToolchainWriter::WriteToolRule(Tool* tool,
                                          const std::string& rule_prefix) {
-  out_ << "rule " << rule_prefix << tool->name() << "\n";
+  out_ << "rule " << rule_prefix << tool->name() << std::endl;
 
   // Rules explicitly include shell commands, so don't try to escape.
   EscapeOptions options;
@@ -96,26 +99,26 @@
       // GCC-style deps require a depfile.
       if (!c_tool->depfile().empty()) {
         WriteRulePattern("depfile", tool->depfile(), options);
-        out_ << kIndent << "deps = gcc\n";
+        out_ << kIndent << "deps = gcc" << std::endl;
       }
     } else if (c_tool->depsformat() == CTool::DEPS_MSVC) {
       // MSVC deps don't have a depfile.
-      out_ << kIndent << "deps = msvc\n";
+      out_ << kIndent << "deps = msvc" << std::endl;
     }
   } else if (!tool->depfile().empty()) {
     WriteRulePattern("depfile", tool->depfile(), options);
-    out_ << kIndent << "deps = gcc\n";
+    out_ << kIndent << "deps = gcc" << std::endl;
   }
 
   // Use pool is specified.
   if (tool->pool().ptr) {
     std::string pool_name =
         tool->pool().ptr->GetNinjaName(settings_->default_toolchain_label());
-    out_ << kIndent << "pool = " << pool_name << "\n";
+    out_ << kIndent << "pool = " << pool_name << std::endl;
   }
 
   if (tool->restat())
-    out_ << kIndent << "restat = 1\n";
+    out_ << kIndent << "restat = 1" << std::endl;
 }
 
 void NinjaToolchainWriter::WriteRulePattern(const char* name,
@@ -125,7 +128,7 @@
     return;
   out_ << kIndent << name << " = ";
   SubstitutionWriter::WriteWithNinjaVariables(pattern, options, out_);
-  out_ << "\n";
+  out_ << std::endl;
 }
 
 void NinjaToolchainWriter::WriteCommandRulePattern(
@@ -138,5 +141,5 @@
   if (!launcher.empty())
     out_ << launcher << " ";
   SubstitutionWriter::WriteWithNinjaVariables(command, options, out_);
-  out_ << "\n";
+  out_ << std::endl;
 }
diff --git a/src/gn/ninja_toolchain_writer.h b/src/gn/ninja_toolchain_writer.h
index b7fd2ee..cbc7c68 100644
--- a/src/gn/ninja_toolchain_writer.h
+++ b/src/gn/ninja_toolchain_writer.h
@@ -34,7 +34,7 @@
 
   NinjaToolchainWriter(const Settings* settings,
                        const Toolchain* toolchain,
-                       OutputStream& out);
+                       std::ostream& out);
   ~NinjaToolchainWriter();
 
   void Run(const std::vector<NinjaWriter::TargetRulePair>& extra_rules);
@@ -51,7 +51,7 @@
 
   const Settings* settings_;
   const Toolchain* toolchain_;
-  OutputStream& out_;
+  std::ostream& out_;
   PathOutput path_output_;
 
   NinjaToolchainWriter(const NinjaToolchainWriter&) = delete;
diff --git a/src/gn/ninja_toolchain_writer_unittest.cc b/src/gn/ninja_toolchain_writer_unittest.cc
index 56f22db..863c174 100644
--- a/src/gn/ninja_toolchain_writer_unittest.cc
+++ b/src/gn/ninja_toolchain_writer_unittest.cc
@@ -2,15 +2,16 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <sstream>
+
 #include "gn/ninja_toolchain_writer.h"
-#include "gn/output_stream.h"
 #include "gn/test_with_scope.h"
 #include "util/test/test.h"
 
 TEST(NinjaToolchainWriter, WriteToolRule) {
   TestWithScope setup;
 
-  StringOutputStream stream;
+  std::ostringstream stream;
   NinjaToolchainWriter writer(setup.settings(), setup.toolchain(), stream);
   writer.WriteToolRule(setup.toolchain()->GetTool(CTool::kCToolCc),
                        std::string("prefix_"));
@@ -25,7 +26,7 @@
 TEST(NinjaToolchainWriter, WriteToolRuleWithLauncher) {
   TestWithScope setup;
 
-  StringOutputStream stream;
+  std::ostringstream stream;
   NinjaToolchainWriter writer(setup.settings(), setup.toolchain(), stream);
   writer.WriteToolRule(setup.toolchain()->GetTool(CTool::kCToolCxx),
                        std::string("prefix_"));
diff --git a/src/gn/output_conversion.cc b/src/gn/output_conversion.cc
index 10b397c..971e740 100644
--- a/src/gn/output_conversion.cc
+++ b/src/gn/output_conversion.cc
@@ -4,29 +4,28 @@
 
 #include "gn/output_conversion.h"
 
-#include "gn/output_stream.h"
 #include "gn/settings.h"
 #include "gn/value.h"
 
 namespace {
 
-void ToString(const Value& output, OutputStream& out) {
+void ToString(const Value& output, std::ostream& out) {
   out << output.ToString(false);
 }
 
-void ToStringQuoted(const Value& output, OutputStream& out) {
+void ToStringQuoted(const Value& output, std::ostream& out) {
   out << "\"" << output.ToString(false) << "\"";
 }
 
-void Indent(int indent, OutputStream& out) {
+void Indent(int indent, std::ostream& out) {
   for (int i = 0; i < indent; ++i)
     out << "  ";
 }
 
 // Forward declare so it can be used recursively.
-void RenderScopeToJSON(const Value& output, OutputStream& out, int indent);
+void RenderScopeToJSON(const Value& output, std::ostream& out, int indent);
 
-void RenderListToJSON(const Value& output, OutputStream& out, int indent) {
+void RenderListToJSON(const Value& output, std::ostream& out, int indent) {
   assert(indent > 0);
   bool first = true;
   out << "[\n";
@@ -47,7 +46,7 @@
   out << "]";
 }
 
-void RenderScopeToJSON(const Value& output, OutputStream& out, int indent) {
+void RenderScopeToJSON(const Value& output, std::ostream& out, int indent) {
   assert(indent > 0);
   Scope::KeyValueMap scope_values;
   output.scope_value()->GetCurrentScopeValues(&scope_values);
@@ -71,14 +70,14 @@
   out << "}";
 }
 
-void OutputListLines(const Value& output, OutputStream& out) {
+void OutputListLines(const Value& output, std::ostream& out) {
   assert(output.type() == Value::LIST);
   const std::vector<Value>& list = output.list_value();
   for (const auto& cur : list)
     out << cur.ToString(false) << "\n";
 }
 
-void OutputString(const Value& output, OutputStream& out) {
+void OutputString(const Value& output, std::ostream& out) {
   if (output.type() == Value::NONE)
     return;
   if (output.type() == Value::STRING) {
@@ -88,7 +87,7 @@
   ToStringQuoted(output, out);
 }
 
-void OutputValue(const Value& output, OutputStream& out) {
+void OutputValue(const Value& output, std::ostream& out) {
   if (output.type() == Value::NONE)
     return;
   if (output.type() == Value::STRING) {
@@ -100,7 +99,7 @@
 
 // The direct Value::ToString call wraps the scope in '{}', which we don't want
 // here for the top-level scope being output.
-void OutputScope(const Value& output, OutputStream& out) {
+void OutputScope(const Value& output, std::ostream& out) {
   Scope::KeyValueMap scope_values;
   output.scope_value()->GetCurrentScopeValues(&scope_values);
   for (const auto& pair : scope_values) {
@@ -108,14 +107,14 @@
   }
 }
 
-void OutputDefault(const Value& output, OutputStream& out) {
+void OutputDefault(const Value& output, std::ostream& out) {
   if (output.type() == Value::LIST)
     OutputListLines(output, out);
   else
     ToString(output, out);
 }
 
-void OutputJSON(const Value& output, OutputStream& out) {
+void OutputJSON(const Value& output, std::ostream& out) {
   if (output.type() == Value::SCOPE) {
     RenderScopeToJSON(output, out, /*indent=*/1);
     return;
@@ -130,7 +129,7 @@
 void DoConvertValueToOutput(const Value& output,
                             const std::string& output_conversion,
                             const Value& original_output_conversion,
-                            OutputStream& out,
+                            std::ostream& out,
                             Err* err) {
   if (output_conversion == "") {
     OutputDefault(output, out);
@@ -164,7 +163,7 @@
 void ConvertValueToOutput(const Settings* settings,
                           const Value& output,
                           const Value& output_conversion,
-                          OutputStream& out,
+                          std::ostream& out,
                           Err* err) {
   if (output_conversion.type() == Value::NONE) {
     OutputDefault(output, out);
diff --git a/src/gn/output_conversion.h b/src/gn/output_conversion.h
index 3e72fe1..09ca254 100644
--- a/src/gn/output_conversion.h
+++ b/src/gn/output_conversion.h
@@ -9,7 +9,6 @@
 #include <string>
 
 class Err;
-class OutputStream;
 class Settings;
 class Value;
 
@@ -21,7 +20,7 @@
 void ConvertValueToOutput(const Settings* settings,
                           const Value& output,
                           const Value& output_conversion_value,
-                          OutputStream& out,
+                          std::ostream& out,
                           Err* err);
 
 #endif  // TOOLS_GN_OUTPUT_CONVERSION_H_
diff --git a/src/gn/output_conversion_unittest.cc b/src/gn/output_conversion_unittest.cc
index 44fc1da..43fdad5 100644
--- a/src/gn/output_conversion_unittest.cc
+++ b/src/gn/output_conversion_unittest.cc
@@ -4,9 +4,10 @@
 
 #include "gn/output_conversion.h"
 
+#include <sstream>
+
 #include "gn/err.h"
 #include "gn/input_conversion.h"
-#include "gn/output_stream.h"
 #include "gn/scope.h"
 #include "gn/template.h"
 #include "gn/test_with_scheduler.h"
@@ -37,7 +38,7 @@
   output.list_value().push_back(Value(nullptr, "foo"));
   output.list_value().push_back(Value(nullptr, ""));
   output.list_value().push_back(Value(nullptr, "bar"));
-  StringOutputStream result;
+  std::ostringstream result;
   ConvertValueToOutput(settings(), output, Value(nullptr, "list lines"), result,
                        &err);
 
@@ -48,7 +49,7 @@
 TEST_F(OutputConversionTest, String) {
   Err err;
   Value output(nullptr, "foo bar");
-  StringOutputStream result;
+  std::ostringstream result;
   ConvertValueToOutput(settings(), output, Value(nullptr, "string"), result,
                        &err);
 
@@ -59,7 +60,7 @@
 TEST_F(OutputConversionTest, StringInt) {
   Err err;
   Value output(nullptr, int64_t(6));
-  StringOutputStream result;
+  std::ostringstream result;
   ConvertValueToOutput(settings(), output, Value(nullptr, "string"), result,
                        &err);
 
@@ -70,7 +71,7 @@
 TEST_F(OutputConversionTest, StringBool) {
   Err err;
   Value output(nullptr, true);
-  StringOutputStream result;
+  std::ostringstream result;
   ConvertValueToOutput(settings(), output, Value(nullptr, "string"), result,
                        &err);
 
@@ -84,7 +85,7 @@
   output.list_value().push_back(Value(nullptr, "foo"));
   output.list_value().push_back(Value(nullptr, "bar"));
   output.list_value().push_back(Value(nullptr, int64_t(6)));
-  StringOutputStream result;
+  std::ostringstream result;
   ConvertValueToOutput(settings(), output, Value(nullptr, "string"), result,
                        &err);
 
@@ -102,7 +103,7 @@
   std::string_view private_var_name("_private");
   new_scope->SetValue(private_var_name, value, nullptr);
 
-  StringOutputStream result;
+  std::ostringstream result;
   ConvertValueToOutput(settings(), Value(nullptr, std::move(new_scope)),
                        Value(nullptr, "string"), result, &err);
   EXPECT_FALSE(err.has_error());
@@ -112,7 +113,7 @@
 TEST_F(OutputConversionTest, ValueString) {
   Err err;
   Value output(nullptr, "foo bar");
-  StringOutputStream result;
+  std::ostringstream result;
   ConvertValueToOutput(settings(), output, Value(nullptr, "value"), result,
                        &err);
 
@@ -123,7 +124,7 @@
 TEST_F(OutputConversionTest, ValueInt) {
   Err err;
   Value output(nullptr, int64_t(6));
-  StringOutputStream result;
+  std::ostringstream result;
   ConvertValueToOutput(settings(), output, Value(nullptr, "value"), result,
                        &err);
 
@@ -134,7 +135,7 @@
 TEST_F(OutputConversionTest, ValueBool) {
   Err err;
   Value output(nullptr, true);
-  StringOutputStream result;
+  std::ostringstream result;
   ConvertValueToOutput(settings(), output, Value(nullptr, "value"), result,
                        &err);
 
@@ -148,7 +149,7 @@
   output.list_value().push_back(Value(nullptr, "foo"));
   output.list_value().push_back(Value(nullptr, "bar"));
   output.list_value().push_back(Value(nullptr, int64_t(6)));
-  StringOutputStream result;
+  std::ostringstream result;
   ConvertValueToOutput(settings(), output, Value(nullptr, "value"), result,
                        &err);
 
@@ -166,7 +167,7 @@
   std::string_view private_var_name("_private");
   new_scope->SetValue(private_var_name, value, nullptr);
 
-  StringOutputStream result;
+  std::ostringstream result;
   ConvertValueToOutput(settings(), Value(nullptr, std::move(new_scope)),
                        Value(nullptr, "value"), result, &err);
   EXPECT_FALSE(err.has_error());
@@ -207,7 +208,7 @@
     ]
   }
 })*");
-  StringOutputStream result;
+  std::ostringstream result;
   ConvertValueToOutput(settings(), Value(nullptr, std::move(new_scope)),
                        Value(nullptr, "json"), result, &err);
   EXPECT_FALSE(err.has_error());
@@ -216,7 +217,7 @@
 
 TEST_F(OutputConversionTest, ValueEmpty) {
   Err err;
-  StringOutputStream result;
+  std::ostringstream result;
   ConvertValueToOutput(settings(), Value(), Value(nullptr, ""), result, &err);
   EXPECT_FALSE(err.has_error());
   EXPECT_EQ(result.str(), "<void>");
@@ -225,7 +226,7 @@
 TEST_F(OutputConversionTest, DefaultValue) {
   Err err;
   Value output(nullptr, "foo bar");
-  StringOutputStream result;
+  std::ostringstream result;
   ConvertValueToOutput(settings(), output, Value(nullptr, ""), result, &err);
 
   EXPECT_FALSE(err.has_error());
@@ -239,7 +240,7 @@
   output.list_value().push_back(Value(nullptr, "foo"));
   output.list_value().push_back(Value(nullptr, ""));
   output.list_value().push_back(Value(nullptr, "bar"));
-  StringOutputStream result;
+  std::ostringstream result;
   ConvertValueToOutput(settings(), output, Value(nullptr, ""), result, &err);
 
   EXPECT_FALSE(err.has_error());
@@ -253,7 +254,7 @@
                                      Value(nullptr, "string"), &err);
   EXPECT_FALSE(err.has_error());
 
-  StringOutputStream reverse;
+  std::ostringstream reverse;
   ConvertValueToOutput(settings(), result, Value(nullptr, "string"), reverse,
                        &err);
   EXPECT_FALSE(err.has_error());
@@ -268,7 +269,7 @@
                                      Value(nullptr, "list lines"), &err);
   EXPECT_FALSE(err.has_error());
 
-  StringOutputStream reverse;
+  std::ostringstream reverse;
   ConvertValueToOutput(settings(), result, Value(nullptr, "list lines"),
                        reverse, &err);
   EXPECT_FALSE(err.has_error());
@@ -283,7 +284,7 @@
                                      Value(nullptr, "value"), &err);
   EXPECT_FALSE(err.has_error());
 
-  StringOutputStream reverse;
+  std::ostringstream reverse;
   ConvertValueToOutput(settings(), result, Value(nullptr, "value"), reverse,
                        &err);
   EXPECT_FALSE(err.has_error());
@@ -298,7 +299,7 @@
                                      Value(nullptr, "value"), &err);
   EXPECT_FALSE(err.has_error());
 
-  StringOutputStream reverse;
+  std::ostringstream reverse;
   ConvertValueToOutput(settings(), result, Value(nullptr, "value"), reverse,
                        &err);
   EXPECT_FALSE(err.has_error());
@@ -313,7 +314,7 @@
                                      Value(nullptr, "value"), &err);
   EXPECT_FALSE(err.has_error());
 
-  StringOutputStream reverse;
+  std::ostringstream reverse;
   ConvertValueToOutput(settings(), result, Value(nullptr, "value"), reverse,
                        &err);
   EXPECT_FALSE(err.has_error());
@@ -328,7 +329,7 @@
                                      Value(nullptr, "scope"), &err);
   EXPECT_FALSE(err.has_error());
 
-  StringOutputStream reverse;
+  std::ostringstream reverse;
   ConvertValueToOutput(settings(), result, Value(nullptr, "scope"), reverse,
                        &err);
   EXPECT_FALSE(err.has_error());
@@ -342,7 +343,7 @@
                                      Value(nullptr, "value"), &err);
   EXPECT_FALSE(err.has_error());
 
-  StringOutputStream reverse;
+  std::ostringstream reverse;
   ConvertValueToOutput(settings(), result, Value(nullptr, "value"), reverse,
                        &err);
   EXPECT_FALSE(err.has_error());
diff --git a/src/gn/output_stream.cc b/src/gn/output_stream.cc
deleted file mode 100644
index 119aaab..0000000
--- a/src/gn/output_stream.cc
+++ /dev/null
@@ -1,82 +0,0 @@
-// Copyright (c) 2025 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "gn/output_stream.h"
-
-#include <limits.h>
-
-OutputStream& OutputStream::operator<<(unsigned long long value) {
-  const size_t buffer_size = 24;
-  char buffer[buffer_size];
-  char* end = buffer + buffer_size;
-  char* pos = end;
-  do {
-    *(--pos) = '0' + static_cast<char>(value % 10);
-    value /= 10;
-  } while (value != 0);
-  write(pos, static_cast<size_t>(end - pos));
-  return *this;
-}
-
-OutputStream& OutputStream::operator<<(long long value) {
-  const size_t buffer_size = 24;
-  char buffer[buffer_size];
-  char* end = buffer + buffer_size;
-  char* pos = end;
-
-  bool has_sign = (value < 0);
-  if (has_sign) {
-    // NOTE: |LLONG_MIN == -LLONG_MIN| must be handled here.
-    if (value == LLONG_MIN) {
-      *(--pos) = '8';
-      value /= 10;
-    }
-    value = -value;
-  }
-
-  do {
-    *(--pos) = '0' + static_cast<char>(value % 10);
-    value /= 10;
-  } while (value != 0);
-  if (has_sign)
-    *(--pos) = '-';
-  write(pos, static_cast<size_t>(end - pos));
-  return *this;
-}
-
-OutputStream& OutputStream::operator<<(unsigned value) {
-  return *this << static_cast<unsigned long long>(value);
-}
-
-OutputStream& OutputStream::operator<<(int value) {
-  return *this << static_cast<long long>(value);
-}
-
-OutputStream& OutputStream::operator<<(unsigned long value) {
-  return *this << static_cast<unsigned long long>(value);
-}
-
-OutputStream& OutputStream::operator<<(long value) {
-  return *this << static_cast<long long>(value);
-}
-
-FileOutputStream::FileOutputStream(const char* utf8_path) {
-  file_ = fopen(utf8_path, "rw");
-}
-
-FileOutputStream::~FileOutputStream() {
-  fclose(file_);
-}
-
-bool FileOutputStream::fail() const {
-  return ferror(file_) != 0;
-}
-
-void FileOutputStream::write(const char* str, size_t len) {
-  fwrite(str, 1, len, file_);
-}
-
-void FileOutputStream::put(char ch) {
-  fputc(ch, file_);
-}
diff --git a/src/gn/output_stream.h b/src/gn/output_stream.h
deleted file mode 100644
index 336c56d..0000000
--- a/src/gn/output_stream.h
+++ /dev/null
@@ -1,109 +0,0 @@
-// Copyright (c) 2025 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef TOOLS_GN_OUTPUT_STREAM_H_
-#define TOOLS_GN_OUTPUT_STREAM_H_
-
-#include <cstdint>
-#include <cstdio>
-#include <cstring>
-#include <string>
-
-// GN generates a lot of text that it sends to various
-// output streams. Initially, this was done using std::ostream
-// but this interface (and implementation) is inefficient due
-// to legacy feature requirements that GN does not need.
-//
-// OutputStream is an abstract interface for an output stream
-// that provides a subset of the std::ostream API, but performs
-// far faster. In practice, using it results in 6% faster
-// `gn gen` times for large build plans that generate huge
-// Ninja build plans.
-class OutputStream {
- public:
-  virtual ~OutputStream() {}
-
-  // Add |len| bytes of data to the output stream.
-  virtual void write(const char* str, size_t len) = 0;
-
-  // Add a single byte of data to the output stream.
-  virtual void put(char ch) = 0;
-
-  // Convenience helpers for C literals and standard strings.
-  void write(const char* str) { write(str, ::strlen(str)); }
-  void write(const std::string& str) { write(str.data(), str.size()); }
-
-  // Operator << overload for std::ostream compatibility.
-  OutputStream& operator<<(char ch) {
-    put(ch);
-    return *this;
-  }
-  OutputStream& operator<<(const char* str) {
-    write(str);
-    return *this;
-  }
-  OutputStream& operator<<(const std::string& str) {
-    write(str);
-    return *this;
-  }
-  OutputStream& operator<<(const std::string_view& str) {
-    write(str.data(), str.size());
-    return *this;
-  }
-
-  // Add decimal representations to the output stream.
-  OutputStream& operator<<(int value);
-  OutputStream& operator<<(long value);
-  OutputStream& operator<<(long long value);
-  OutputStream& operator<<(unsigned value);
-  OutputStream& operator<<(unsigned long value);
-  OutputStream& operator<<(unsigned long long value);
-};
-
-// A StringOutputStream stores all input into an std::string.
-// This is a replacement for std::ostringstream.
-class StringOutputStream : public OutputStream {
- public:
-  // Constructor creates empty string.
-  StringOutputStream() {}
-
-  virtual ~StringOutputStream() {}
-
-  // Retrieve reference to result.
-  const std::string str() const { return str_; }
-
-  // Move result out of the instance.
-  std::string release() { return std::move(str_); }
-
-  // OutputStream overrides
-  void write(const char* str, size_t len) override { str_.append(str, len); }
-  void put(char ch) override { str_.push_back(ch); }
-
- protected:
-  std::string str_;
-};
-
-// A FileOutputStream writes all input into a file.
-class FileOutputStream : public OutputStream {
- public:
-  // Constructor opens a FILE instance in binary mode.
-  // Use fail() after the call to verify for errors.
-  FileOutputStream(const char* utf8_path);
-
-  // Destructor closes the FILE instance.
-  virtual ~FileOutputStream();
-
-  // Return true if an error occured during construction
-  // or a write or put call.
-  bool fail() const;
-
-  // OutputStream overrides.
-  void write(const char* str, size_t len) override;
-  void put(char ch) override;
-
- protected:
-  FILE* file_ = nullptr;
-};
-
-#endif  // TOOLS_GN_OUTPUT_STREAM_H_
diff --git a/src/gn/output_stream_unittest.cc b/src/gn/output_stream_unittest.cc
deleted file mode 100644
index 115e9e9..0000000
--- a/src/gn/output_stream_unittest.cc
+++ /dev/null
@@ -1,98 +0,0 @@
-// Copyright (c) 2025 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "gn/output_stream.h"
-
-#include <limits>
-
-#include "util/test/test.h"
-
-TEST(OutputStream, AppendIntDecimals) {
-  static const int kValues[] = {
-      0, 1, -1, 12345678, -12345678, INT_MIN, INT_MAX,
-  };
-  for (const auto value : kValues) {
-    char expected[20];
-    snprintf(expected, sizeof(expected), "%d", value);
-
-    StringOutputStream s;
-    s << value;
-    EXPECT_EQ(s.str(), expected) << value;
-  }
-}
-
-TEST(OutputStream, AppendUIntDecimals) {
-  static const unsigned kValues[] = {
-      0,
-      1,
-      12345678,
-      UINT_MAX,
-  };
-  for (const auto value : kValues) {
-    char expected[20];
-    snprintf(expected, sizeof(expected), "%u", value);
-
-    StringOutputStream s;
-    s << value;
-    EXPECT_EQ(s.str(), expected) << value;
-  }
-}
-
-TEST(OutputStream, AppendLongDecimals) {
-  static const long kValues[] = {
-      0, 1, -1, 12345678, -12345678, INT_MIN, INT_MAX, LONG_MIN, LONG_MAX,
-  };
-  for (const auto value : kValues) {
-    char expected[32];
-    snprintf(expected, sizeof(expected), "%ld", value);
-
-    StringOutputStream s;
-    s << value;
-
-    EXPECT_EQ(s.str(), expected) << value;
-  }
-}
-
-TEST(OutputStream, AppendULongDecimals) {
-  static const unsigned long kValues[] = {
-      0, 1, 12345678, UINT_MAX, ULONG_MAX,
-  };
-  for (const auto value : kValues) {
-    char expected[32];
-    snprintf(expected, sizeof(expected), "%lu", value);
-
-    StringOutputStream s;
-    s << value;
-    EXPECT_EQ(s.str(), expected) << value;
-  }
-}
-
-TEST(OutputStream, AppendLongLongDecimals) {
-  static const long long kValues[] = {
-      0,       1,        -1,       12345678,  -12345678, INT_MIN,
-      INT_MAX, LONG_MIN, LONG_MAX, LLONG_MIN, LLONG_MAX,
-  };
-  for (const auto value : kValues) {
-    char expected[48];
-    snprintf(expected, sizeof(expected), "%lld", value);
-
-    StringOutputStream s;
-    s << value;
-    EXPECT_EQ(s.str(), expected) << value;
-  }
-}
-
-TEST(OutputStream, AppendULongLongDecimals) {
-  static const unsigned long long kValues[] = {
-      0, 1, 12345678, UINT_MAX, ULONG_MAX, ULLONG_MAX,
-  };
-  for (const auto value : kValues) {
-    char expected[48];
-    snprintf(expected, sizeof(expected), "%llu", value);
-
-    StringOutputStream s;
-    s << value;
-    EXPECT_EQ(s.str(), expected) << value;
-  }
-}
diff --git a/src/gn/parser.cc b/src/gn/parser.cc
index b05cebb..96739a5 100644
--- a/src/gn/parser.cc
+++ b/src/gn/parser.cc
@@ -10,7 +10,6 @@
 #include "base/logging.h"
 #include "gn/functions.h"
 #include "gn/operators.h"
-#include "gn/output_stream.h"
 #include "gn/token.h"
 
 const char kGrammar_Help[] =
@@ -897,21 +896,23 @@
   return std::string(value, ' ');
 }
 
-void RenderToText(const base::Value& node, int indent_level, OutputStream& os) {
+void RenderToText(const base::Value& node,
+                  int indent_level,
+                  std::ostringstream& os) {
   const base::Value* child = node.FindKey(std::string("child"));
   std::string node_type(node.FindKey("type")->GetString());
   if (node_type == "ACCESSOR") {
     // AccessorNode is a bit special, in that it holds a Token, not a ParseNode
     // for the base.
-    os << IndentFor(indent_level) << node_type << "\n";
+    os << IndentFor(indent_level) << node_type << std::endl;
     os << IndentFor(indent_level + 1) << node.FindKey("value")->GetString()
-       << "\n";
+       << std::endl;
   } else {
     os << IndentFor(indent_level) << node_type;
     if (node.FindKey("value")) {
       os << "(" << node.FindKey("value")->GetString() << ")";
     }
-    os << "\n";
+    os << std::endl;
   }
   if (node.FindKey(kJsonBeforeComment)) {
     for (auto& v : node.FindKey(kJsonBeforeComment)->GetList()) {
diff --git a/src/gn/parser.h b/src/gn/parser.h
index f92429c..b323028 100644
--- a/src/gn/parser.h
+++ b/src/gn/parser.h
@@ -15,8 +15,6 @@
 #include "gn/err.h"
 #include "gn/parse_tree.h"
 
-class OutputStream;
-
 extern const char kGrammar_Help[];
 
 struct ParserHelper;
@@ -153,6 +151,8 @@
 
 // Renders parse subtree as a formatted text, indenting by the given number of
 // spaces.
-void RenderToText(const base::Value& node, int indent_level, OutputStream& os);
+void RenderToText(const base::Value& node,
+                  int indent_level,
+                  std::ostringstream& os);
 
 #endif  // TOOLS_GN_PARSER_H_
diff --git a/src/gn/parser_unittest.cc b/src/gn/parser_unittest.cc
index d850551..8cfdb8d 100644
--- a/src/gn/parser_unittest.cc
+++ b/src/gn/parser_unittest.cc
@@ -2,9 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "gn/parser.h"
+#include <sstream>
+
 #include "gn/input_file.h"
-#include "gn/output_stream.h"
+#include "gn/parser.h"
 #include "gn/tokenizer.h"
 #include "util/test/test.h"
 
@@ -29,7 +30,7 @@
     err.PrintToStdout();
   ASSERT_TRUE(result);
 
-  StringOutputStream collector;
+  std::ostringstream collector;
   RenderToText(result->GetJSONNode(), 0, collector);
 
   EXPECT_EQ(expected, collector.str());
@@ -45,7 +46,7 @@
   std::unique_ptr<ParseNode> result = Parser::ParseExpression(tokens, &err);
   ASSERT_TRUE(result);
 
-  StringOutputStream collector;
+  std::ostringstream collector;
   RenderToText(result->GetJSONNode(), 0, collector);
 
   EXPECT_EQ(expected, collector.str());
diff --git a/src/gn/path_output.cc b/src/gn/path_output.cc
index 74c336d..86c92e6 100644
--- a/src/gn/path_output.cc
+++ b/src/gn/path_output.cc
@@ -7,7 +7,6 @@
 #include "base/strings/string_util.h"
 #include "gn/filesystem_utils.h"
 #include "gn/output_file.h"
-#include "gn/output_stream.h"
 #include "gn/string_utils.h"
 #include "util/build_config.h"
 
@@ -23,11 +22,11 @@
 
 PathOutput::~PathOutput() = default;
 
-void PathOutput::WriteFile(OutputStream& out, const SourceFile& file) const {
+void PathOutput::WriteFile(std::ostream& out, const SourceFile& file) const {
   WritePathStr(out, file.value());
 }
 
-void PathOutput::WriteDir(OutputStream& out,
+void PathOutput::WriteDir(std::ostream& out,
                           const SourceDir& dir,
                           DirSlashEnding slash_ending) const {
   if (dir.value() == "/") {
@@ -70,12 +69,12 @@
   }
 }
 
-void PathOutput::WriteFile(OutputStream& out, const OutputFile& file) const {
+void PathOutput::WriteFile(std::ostream& out, const OutputFile& file) const {
   // Here we assume that the path is already preprocessed.
   EscapeStringToStream(out, file.value(), options_);
 }
 
-void PathOutput::WriteFiles(OutputStream& out,
+void PathOutput::WriteFiles(std::ostream& out,
                             const std::vector<SourceFile>& files) const {
   for (const auto& file : files) {
     out << " ";
@@ -83,7 +82,7 @@
   }
 }
 
-void PathOutput::WriteFiles(OutputStream& out,
+void PathOutput::WriteFiles(std::ostream& out,
                             const std::vector<OutputFile>& files) const {
   for (const auto& file : files) {
     out << " ";
@@ -91,7 +90,7 @@
   }
 }
 
-void PathOutput::WriteFiles(OutputStream& out,
+void PathOutput::WriteFiles(std::ostream& out,
                             const UniqueVector<OutputFile>& files) const {
   for (const auto& file : files) {
     out << " ";
@@ -99,7 +98,7 @@
   }
 }
 
-void PathOutput::WriteDir(OutputStream& out,
+void PathOutput::WriteDir(std::ostream& out,
                           const OutputFile& file,
                           DirSlashEnding slash_ending) const {
   DCHECK(file.value().empty() || file.value()[file.value().size() - 1] == '/');
@@ -123,13 +122,13 @@
   }
 }
 
-void PathOutput::WriteFile(OutputStream& out,
+void PathOutput::WriteFile(std::ostream& out,
                            const base::FilePath& file) const {
   // Assume native file paths are always absolute.
   EscapeStringToStream(out, FilePathToUTF8(file), options_);
 }
 
-void PathOutput::WriteSourceRelativeString(OutputStream& out,
+void PathOutput::WriteSourceRelativeString(std::ostream& out,
                                            std::string_view str) const {
   if (options_.mode == ESCAPE_NINJA_COMMAND) {
     // Shell escaping needs an intermediate string since it may end up
@@ -151,7 +150,7 @@
   }
 }
 
-void PathOutput::WritePathStr(OutputStream& out, std::string_view str) const {
+void PathOutput::WritePathStr(std::ostream& out, std::string_view str) const {
   DCHECK(str.size() > 0 && str[0] == '/');
 
   if (str.substr(0, current_dir_.value().size()) ==
diff --git a/src/gn/path_output.h b/src/gn/path_output.h
index 3fced0a..3b973d6 100644
--- a/src/gn/path_output.h
+++ b/src/gn/path_output.h
@@ -14,7 +14,6 @@
 #include "gn/unique_vector.h"
 
 class OutputFile;
-class OutputStream;
 class SourceFile;
 
 namespace base {
@@ -49,35 +48,35 @@
   void set_inhibit_quoting(bool iq) { options_.inhibit_quoting = iq; }
   void set_escape_platform(EscapingPlatform p) { options_.platform = p; }
 
-  void WriteFile(OutputStream& out, const SourceFile& file) const;
-  void WriteFile(OutputStream& out, const OutputFile& file) const;
-  void WriteFile(OutputStream& out, const base::FilePath& file) const;
+  void WriteFile(std::ostream& out, const SourceFile& file) const;
+  void WriteFile(std::ostream& out, const OutputFile& file) const;
+  void WriteFile(std::ostream& out, const base::FilePath& file) const;
 
   // Writes the given SourceFiles/OutputFiles with spaces separating them. This
   // will also write an initial space before the first item.
-  void WriteFiles(OutputStream& out, const std::vector<SourceFile>& file) const;
-  void WriteFiles(OutputStream& out,
+  void WriteFiles(std::ostream& out, const std::vector<SourceFile>& file) const;
+  void WriteFiles(std::ostream& out,
                   const std::vector<OutputFile>& files) const;
-  void WriteFiles(OutputStream& out,
+  void WriteFiles(std::ostream& out,
                   const UniqueVector<OutputFile>& files) const;
 
   // This variant assumes the dir ends in a trailing slash or is empty.
-  void WriteDir(OutputStream& out,
+  void WriteDir(std::ostream& out,
                 const SourceDir& dir,
                 DirSlashEnding slash_ending) const;
 
-  void WriteDir(OutputStream& out,
+  void WriteDir(std::ostream& out,
                 const OutputFile& file,
                 DirSlashEnding slash_ending) const;
 
   // Backend for WriteFile and WriteDir. This appends the given file or
   // directory string to the file.
-  void WritePathStr(OutputStream& out, std::string_view str) const;
+  void WritePathStr(std::ostream& out, std::string_view str) const;
 
  private:
   // Takes the given string and writes it out, appending to the inverse
   // current dir. This assumes leading slashes have been trimmed.
-  void WriteSourceRelativeString(OutputStream& out, std::string_view str) const;
+  void WriteSourceRelativeString(std::ostream& out, std::string_view str) const;
 
   SourceDir current_dir_;
 
diff --git a/src/gn/path_output_unittest.cc b/src/gn/path_output_unittest.cc
index e137522..1debb64 100644
--- a/src/gn/path_output_unittest.cc
+++ b/src/gn/path_output_unittest.cc
@@ -2,10 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "gn/path_output.h"
+#include <sstream>
+
 #include "base/files/file_path.h"
 #include "gn/output_file.h"
-#include "gn/output_stream.h"
+#include "gn/path_output.h"
 #include "gn/source_dir.h"
 #include "gn/source_file.h"
 #include "util/build_config.h"
@@ -17,19 +18,19 @@
   PathOutput writer(build_dir, source_root, ESCAPE_NONE);
   {
     // Normal source-root path.
-    StringOutputStream out;
+    std::ostringstream out;
     writer.WriteFile(out, SourceFile("//foo/bar.cc"));
     EXPECT_EQ("../../foo/bar.cc", out.str());
   }
   {
     // File in the root dir.
-    StringOutputStream out;
+    std::ostringstream out;
     writer.WriteFile(out, SourceFile("//foo.cc"));
     EXPECT_EQ("../../foo.cc", out.str());
   }
   {
     // Files in the output dir.
-    StringOutputStream out;
+    std::ostringstream out;
     writer.WriteFile(out, SourceFile("//out/Debug/foo.cc"));
     out << " ";
     writer.WriteFile(out, SourceFile("//out/Debug/bar/baz.cc"));
@@ -38,14 +39,14 @@
 #if defined(OS_WIN)
   {
     // System-absolute path.
-    StringOutputStream out;
+    std::ostringstream out;
     writer.WriteFile(out, SourceFile("/C:/foo/bar.cc"));
     EXPECT_EQ("C:/foo/bar.cc", out.str());
   }
 #else
   {
     // System-absolute path.
-    StringOutputStream out;
+    std::ostringstream out;
     writer.WriteFile(out, SourceFile("/foo/bar.cc"));
     EXPECT_EQ("/foo/bar.cc", out.str());
   }
@@ -59,13 +60,13 @@
   PathOutput writer(build_dir, source_root, ESCAPE_NONE);
   {
     // Normal source-root path.
-    StringOutputStream out;
+    std::ostringstream out;
     writer.WriteFile(out, SourceFile("//foo/bar.cc"));
     EXPECT_EQ("foo/bar.cc", out.str());
   }
   {
     // File in the root dir.
-    StringOutputStream out;
+    std::ostringstream out;
     writer.WriteFile(out, SourceFile("//foo.cc"));
     EXPECT_EQ("foo.cc", out.str());
   }
@@ -77,13 +78,13 @@
   PathOutput writer(build_dir, source_root, ESCAPE_NINJA);
   {
     // Spaces and $ in filenames.
-    StringOutputStream out;
+    std::ostringstream out;
     writer.WriteFile(out, SourceFile("//foo/foo bar$.cc"));
     EXPECT_EQ("../../foo/foo$ bar$$.cc", out.str());
   }
   {
     // Not other weird stuff
-    StringOutputStream out;
+    std::ostringstream out;
     writer.WriteFile(out, SourceFile("//foo/\"foo\".cc"));
     EXPECT_EQ("../../foo/\"foo\".cc", out.str());
   }
@@ -97,7 +98,7 @@
   // Spaces in filenames should get quoted on Windows.
   writer.set_escape_platform(ESCAPE_PLATFORM_WIN);
   {
-    StringOutputStream out;
+    std::ostringstream out;
     writer.WriteFile(out, SourceFile("//foo/foo bar.cc"));
     EXPECT_EQ("\"../../foo/foo$ bar.cc\"", out.str());
   }
@@ -105,7 +106,7 @@
   // Spaces in filenames should get escaped on Posix.
   writer.set_escape_platform(ESCAPE_PLATFORM_POSIX);
   {
-    StringOutputStream out;
+    std::ostringstream out;
     writer.WriteFile(out, SourceFile("//foo/foo bar.cc"));
     EXPECT_EQ("../../foo/foo\\$ bar.cc", out.str());
   }
@@ -113,7 +114,7 @@
   // Quotes should get blackslash-escaped on Windows and Posix.
   writer.set_escape_platform(ESCAPE_PLATFORM_WIN);
   {
-    StringOutputStream out;
+    std::ostringstream out;
     writer.WriteFile(out, SourceFile("//foo/\"foobar\".cc"));
     // Our Windows code currently quotes the whole thing in this case for
     // code simplicity, even though it's strictly unnecessary. This might
@@ -122,7 +123,7 @@
   }
   writer.set_escape_platform(ESCAPE_PLATFORM_POSIX);
   {
-    StringOutputStream out;
+    std::ostringstream out;
     writer.WriteFile(out, SourceFile("//foo/\"foobar\".cc"));
     EXPECT_EQ("../../foo/\\\"foobar\\\".cc", out.str());
   }
@@ -130,13 +131,13 @@
   // Backslashes should get escaped on non-Windows and preserved on Windows.
   writer.set_escape_platform(ESCAPE_PLATFORM_WIN);
   {
-    StringOutputStream out;
+    std::ostringstream out;
     writer.WriteFile(out, OutputFile("foo\\bar.cc"));
     EXPECT_EQ("foo\\bar.cc", out.str());
   }
   writer.set_escape_platform(ESCAPE_PLATFORM_POSIX);
   {
-    StringOutputStream out;
+    std::ostringstream out;
     writer.WriteFile(out, OutputFile("foo\\bar.cc"));
     EXPECT_EQ("foo\\\\bar.cc", out.str());
   }
@@ -151,7 +152,7 @@
   writer.set_escape_platform(ESCAPE_PLATFORM_WIN);
   {
     // We should get unescaped spaces in the output with no quotes.
-    StringOutputStream out;
+    std::ostringstream out;
     writer.WriteFile(out, SourceFile("//foo/foo bar.cc"));
     EXPECT_EQ("../../foo/foo$ bar.cc", out.str());
   }
@@ -159,7 +160,7 @@
   writer.set_escape_platform(ESCAPE_PLATFORM_POSIX);
   {
     // Escapes the space.
-    StringOutputStream out;
+    std::ostringstream out;
     writer.WriteFile(out, SourceFile("//foo/foo bar.cc"));
     EXPECT_EQ("../../foo/foo\\$ bar.cc", out.str());
   }
@@ -171,13 +172,13 @@
     std::string_view source_root("/source/root");
     PathOutput writer(build_dir, source_root, ESCAPE_NINJA);
     {
-      StringOutputStream out;
+      std::ostringstream out;
       writer.WriteDir(out, SourceDir("//foo/bar/"),
                       PathOutput::DIR_INCLUDE_LAST_SLASH);
       EXPECT_EQ("../../foo/bar/", out.str());
     }
     {
-      StringOutputStream out;
+      std::ostringstream out;
       writer.WriteDir(out, SourceDir("//foo/bar/"),
                       PathOutput::DIR_NO_LAST_SLASH);
       EXPECT_EQ("../../foo/bar", out.str());
@@ -185,54 +186,54 @@
 
     // Output source root dir.
     {
-      StringOutputStream out;
+      std::ostringstream out;
       writer.WriteDir(out, SourceDir("//"), PathOutput::DIR_INCLUDE_LAST_SLASH);
       EXPECT_EQ("../../", out.str());
     }
     {
-      StringOutputStream out;
+      std::ostringstream out;
       writer.WriteDir(out, SourceDir("//"), PathOutput::DIR_NO_LAST_SLASH);
       EXPECT_EQ("../..", out.str());
     }
 
     // Output system root dir.
     {
-      StringOutputStream out;
+      std::ostringstream out;
       writer.WriteDir(out, SourceDir("/"), PathOutput::DIR_INCLUDE_LAST_SLASH);
       EXPECT_EQ("/", out.str());
     }
     {
-      StringOutputStream out;
+      std::ostringstream out;
       writer.WriteDir(out, SourceDir("/"), PathOutput::DIR_INCLUDE_LAST_SLASH);
       EXPECT_EQ("/", out.str());
     }
     {
-      StringOutputStream out;
+      std::ostringstream out;
       writer.WriteDir(out, SourceDir("/"), PathOutput::DIR_NO_LAST_SLASH);
       EXPECT_EQ("/.", out.str());
     }
 
     // Output inside current dir.
     {
-      StringOutputStream out;
+      std::ostringstream out;
       writer.WriteDir(out, SourceDir("//out/Debug/"),
                       PathOutput::DIR_INCLUDE_LAST_SLASH);
       EXPECT_EQ("./", out.str());
     }
     {
-      StringOutputStream out;
+      std::ostringstream out;
       writer.WriteDir(out, SourceDir("//out/Debug/"),
                       PathOutput::DIR_NO_LAST_SLASH);
       EXPECT_EQ(".", out.str());
     }
     {
-      StringOutputStream out;
+      std::ostringstream out;
       writer.WriteDir(out, SourceDir("//out/Debug/foo/"),
                       PathOutput::DIR_INCLUDE_LAST_SLASH);
       EXPECT_EQ("foo/", out.str());
     }
     {
-      StringOutputStream out;
+      std::ostringstream out;
       writer.WriteDir(out, SourceDir("//out/Debug/foo/"),
                       PathOutput::DIR_NO_LAST_SLASH);
       EXPECT_EQ("foo", out.str());
@@ -240,18 +241,18 @@
 
     // WriteDir using an OutputFile.
     {
-      StringOutputStream out;
+      std::ostringstream out;
       writer.WriteDir(out, OutputFile("foo/"),
                       PathOutput::DIR_INCLUDE_LAST_SLASH);
       EXPECT_EQ("foo/", out.str());
     }
     {
-      StringOutputStream out;
+      std::ostringstream out;
       writer.WriteDir(out, OutputFile("foo/"), PathOutput::DIR_NO_LAST_SLASH);
       EXPECT_EQ("foo", out.str());
     }
     {
-      StringOutputStream out;
+      std::ostringstream out;
       writer.WriteDir(out, OutputFile(), PathOutput::DIR_INCLUDE_LAST_SLASH);
       EXPECT_EQ("", out.str());
     }
@@ -261,13 +262,13 @@
     std::string_view source_root("/source/root");
     PathOutput root_writer(SourceDir("//"), source_root, ESCAPE_NINJA);
     {
-      StringOutputStream out;
+      std::ostringstream out;
       root_writer.WriteDir(out, SourceDir("//"),
                            PathOutput::DIR_INCLUDE_LAST_SLASH);
       EXPECT_EQ("./", out.str());
     }
     {
-      StringOutputStream out;
+      std::ostringstream out;
       root_writer.WriteDir(out, SourceDir("//"), PathOutput::DIR_NO_LAST_SLASH);
       EXPECT_EQ(".", out.str());
     }
diff --git a/src/gn/pool.cc b/src/gn/pool.cc
index 740f1d2..60049a1 100644
--- a/src/gn/pool.cc
+++ b/src/gn/pool.cc
@@ -4,8 +4,9 @@
 
 #include "gn/pool.h"
 
+#include <sstream>
+
 #include "base/logging.h"
-#include "gn/output_stream.h"
 
 Pool::~Pool() = default;
 
@@ -24,7 +25,7 @@
 }
 
 std::string Pool::GetNinjaName(bool include_toolchain) const {
-  StringOutputStream buffer;
+  std::ostringstream buffer;
   if (include_toolchain) {
     DCHECK(label().toolchain_dir().is_source_absolute());
     std::string toolchain_dir = label().toolchain_dir().value();
diff --git a/src/gn/qt_creator_writer.cc b/src/gn/qt_creator_writer.cc
index 2e9ee0b..94fed8f 100644
--- a/src/gn/qt_creator_writer.cc
+++ b/src/gn/qt_creator_writer.cc
@@ -6,6 +6,7 @@
 
 #include <optional>
 #include <set>
+#include <sstream>
 #include <string>
 
 #include "base/files/file_path.h"
@@ -270,10 +271,11 @@
 void QtCreatorWriter::GenerateFile(const base::FilePath::CharType* suffix,
                                    const std::set<std::string>& items) {
   const base::FilePath file_path = project_prefix_.AddExtension(suffix);
-  StringOutputBuffer output;
+  StringOutputBuffer storage;
+  std::ostream output(&storage);
   for (const std::string& item : items)
-    output << item << "\n";
-  output.WriteToFileIfChanged(file_path, &err_);
+    output << item << std::endl;
+  storage.WriteToFileIfChanged(file_path, &err_);
 }
 
 void QtCreatorWriter::Run() {
diff --git a/src/gn/runtime_deps.cc b/src/gn/runtime_deps.cc
index e121183..546d63e 100644
--- a/src/gn/runtime_deps.cc
+++ b/src/gn/runtime_deps.cc
@@ -6,6 +6,7 @@
 
 #include <map>
 #include <set>
+#include <sstream>
 
 #include "base/command_line.h"
 #include "base/files/file_util.h"
@@ -211,12 +212,13 @@
   base::FilePath data_deps_file =
       target->settings()->build_settings()->GetFullPath(output_as_source);
 
-  StringOutputBuffer contents;
+  StringOutputBuffer storage;
+  std::ostream contents(&storage);
   for (const auto& pair : ComputeRuntimeDeps(target))
-    contents << pair.first.value() << "\n";
+    contents << pair.first.value() << std::endl;
 
   ScopedTrace trace(TraceItem::TRACE_FILE_WRITE, output_as_source.value());
-  return contents.WriteToFileIfChanged(data_deps_file, err);
+  return storage.WriteToFileIfChanged(data_deps_file, err);
 }
 
 }  // namespace
diff --git a/src/gn/rust_project_writer.cc b/src/gn/rust_project_writer.cc
index 840c211..6c9aef8 100644
--- a/src/gn/rust_project_writer.cc
+++ b/src/gn/rust_project_writer.cc
@@ -4,7 +4,9 @@
 
 #include "gn/rust_project_writer.h"
 
+#include <fstream>
 #include <optional>
+#include <sstream>
 #include <tuple>
 
 #include "base/json/string_escape.h"
@@ -70,7 +72,9 @@
   std::vector<const Target*> all_targets = builder.GetAllResolvedTargets();
 
   StringOutputBuffer out_buffer;
-  RenderJSON(build_settings, all_targets, out_buffer);
+  std::ostream out(&out_buffer);
+
+  RenderJSON(build_settings, all_targets, out);
   return out_buffer.WriteToFileIfChanged(output_path, err);
 }
 
@@ -244,7 +248,7 @@
 void WriteCrates(const BuildSettings* build_settings,
                  CrateList& crate_list,
                  std::optional<std::string>& sysroot,
-                 OutputStream& rust_project) {
+                 std::ostream& rust_project) {
   rust_project << "{" NEWLINE;
 
   // If a sysroot was found, then that can be used to tell rust-analyzer where
@@ -386,7 +390,7 @@
 
 void RustProjectWriter::RenderJSON(const BuildSettings* build_settings,
                                    std::vector<const Target*>& all_targets,
-                                   OutputStream& rust_project) {
+                                   std::ostream& rust_project) {
   TargetIndexMap lookup;
   CrateList crate_list;
   std::optional<std::string> rust_sysroot;
diff --git a/src/gn/rust_project_writer.h b/src/gn/rust_project_writer.h
index 8af7bf1..3fbdedb 100644
--- a/src/gn/rust_project_writer.h
+++ b/src/gn/rust_project_writer.h
@@ -10,7 +10,6 @@
 
 class Builder;
 class BuildSettings;
-class OutputStream;
 
 // rust-project.json is an output format describing the rust build graph. It is
 // used by rust-analyzer (a LSP server), similar to compile-commands.json.
@@ -28,7 +27,7 @@
                                Err* err);
   static void RenderJSON(const BuildSettings* build_settings,
                          std::vector<const Target*>& all_targets,
-                         OutputStream& rust_project);
+                         std::ostream& rust_project);
 
  private:
   // This function visits the deps graph of a target in a DFS fashion.
diff --git a/src/gn/rust_project_writer_helpers.h b/src/gn/rust_project_writer_helpers.h
index 864b7d0..3073fd5 100644
--- a/src/gn/rust_project_writer_helpers.h
+++ b/src/gn/rust_project_writer_helpers.h
@@ -5,7 +5,9 @@
 #ifndef TOOLS_GN_RUST_PROJECT_WRITER_HELPERS_H_
 #define TOOLS_GN_RUST_PROJECT_WRITER_HELPERS_H_
 
+#include <fstream>
 #include <optional>
+#include <sstream>
 #include <string>
 #include <string_view>
 #include <tuple>
@@ -17,8 +19,6 @@
 #include "gn/source_file.h"
 #include "gn/target.h"
 
-class OutputStream;
-
 // These are internal types and helper functions for RustProjectWriter that have
 // been extracted for easier testability.
 
@@ -130,7 +130,7 @@
 void WriteCrates(const BuildSettings* build_settings,
                  CrateList& crate_list,
                  std::optional<std::string>& sysroot,
-                 OutputStream& rust_project);
+                 std::ostream& rust_project);
 
 // Assemble the compiler arguments for the given GN Target.
 std::vector<std::string> ExtractCompilerArgs(const Target* target);
diff --git a/src/gn/rust_project_writer_helpers_unittest.cc b/src/gn/rust_project_writer_helpers_unittest.cc
index 13d6b47..87ccd83 100644
--- a/src/gn/rust_project_writer_helpers_unittest.cc
+++ b/src/gn/rust_project_writer_helpers_unittest.cc
@@ -40,7 +40,7 @@
   crates.push_back(dep);
   crates.push_back(target);
 
-  StringOutputStream stream;
+  std::ostringstream stream;
   WriteCrates(setup.build_settings(), crates, sysroot, stream);
   std::string out = stream.str();
 #if defined(OS_WIN)
@@ -101,7 +101,7 @@
   std::optional<std::string> sysroot = "sysroot";
   CrateList crates;
 
-  StringOutputStream stream;
+  std::ostringstream stream;
   WriteCrates(setup.build_settings(), crates, sysroot, stream);
   std::string out = stream.str();
 #if defined(OS_WIN)
diff --git a/src/gn/rust_project_writer_unittest.cc b/src/gn/rust_project_writer_unittest.cc
index 66948e3..f0b806b 100644
--- a/src/gn/rust_project_writer_unittest.cc
+++ b/src/gn/rust_project_writer_unittest.cc
@@ -6,7 +6,6 @@
 #include "base/files/file_path.h"
 #include "base/strings/string_util.h"
 #include "gn/filesystem_utils.h"
-#include "gn/output_stream.h"
 #include "gn/substitution_list.h"
 #include "gn/target.h"
 #include "gn/test_with_scheduler.h"
@@ -42,7 +41,7 @@
   target.SetToolchain(setup.toolchain());
   ASSERT_TRUE(target.OnResolved(&err));
 
-  StringOutputStream stream;
+  std::ostringstream stream;
   std::vector<const Target*> targets;
   targets.push_back(&target);
   RustProjectWriter::RenderJSON(setup.build_settings(), targets, stream);
@@ -107,7 +106,7 @@
   target.SetToolchain(setup.toolchain());
   ASSERT_TRUE(target.OnResolved(&err));
 
-  StringOutputStream stream;
+  std::ostringstream stream;
   std::vector<const Target*> targets;
   targets.push_back(&target);
   RustProjectWriter::RenderJSON(setup.build_settings(), targets, stream);
@@ -205,7 +204,7 @@
   target.SetToolchain(setup.toolchain());
   ASSERT_TRUE(target.OnResolved(&err));
 
-  StringOutputStream stream;
+  std::ostringstream stream;
   std::vector<const Target*> targets;
   targets.push_back(&target);
   RustProjectWriter::RenderJSON(setup.build_settings(), targets, stream);
@@ -342,7 +341,7 @@
   target.SetToolchain(setup.toolchain());
   ASSERT_TRUE(target.OnResolved(&err));
 
-  StringOutputStream stream;
+  std::ostringstream stream;
   std::vector<const Target*> targets;
   targets.push_back(&target);
   RustProjectWriter::RenderJSON(setup.build_settings(), targets, stream);
@@ -445,7 +444,7 @@
   target.SetToolchain(setup.toolchain());
   ASSERT_TRUE(target.OnResolved(&err));
 
-  StringOutputStream stream;
+  std::ostringstream stream;
   std::vector<const Target*> targets;
   targets.push_back(&target);
   RustProjectWriter::RenderJSON(setup.build_settings(), targets, stream);
@@ -500,7 +499,7 @@
   target.SetToolchain(setup.toolchain());
   ASSERT_TRUE(target.OnResolved(&err));
 
-  StringOutputStream stream;
+  std::ostringstream stream;
   std::vector<const Target*> targets;
   targets.push_back(&target);
   RustProjectWriter::RenderJSON(setup.build_settings(), targets, stream);
@@ -555,7 +554,7 @@
   target.SetToolchain(setup.toolchain());
   ASSERT_TRUE(target.OnResolved(&err));
 
-  StringOutputStream stream;
+  std::ostringstream stream;
   std::vector<const Target*> targets;
   targets.push_back(&target);
   RustProjectWriter::RenderJSON(setup.build_settings(), targets, stream);
@@ -611,7 +610,7 @@
   target.SetToolchain(setup.toolchain());
   ASSERT_TRUE(target.OnResolved(&err));
 
-  StringOutputStream stream;
+  std::ostringstream stream;
   std::vector<const Target*> targets;
   targets.push_back(&target);
   RustProjectWriter::RenderJSON(setup.build_settings(), targets, stream);
diff --git a/src/gn/setup.cc b/src/gn/setup.cc
index d907a6c..912f378 100644
--- a/src/gn/setup.cc
+++ b/src/gn/setup.cc
@@ -8,6 +8,7 @@
 
 #include <algorithm>
 #include <memory>
+#include <sstream>
 #include <utility>
 
 #include "base/command_line.h"
diff --git a/src/gn/string_output_buffer.cc b/src/gn/string_output_buffer.cc
index 53af53e..c5d91ff 100644
--- a/src/gn/string_output_buffer.cc
+++ b/src/gn/string_output_buffer.cc
@@ -4,14 +4,13 @@
 
 #include "gn/string_output_buffer.h"
 
-#include <fstream>
-
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
 #include "gn/err.h"
 #include "gn/file_writer.h"
 #include "gn/filesystem_utils.h"
-#include "gn/output_stream.h"
+
+#include <fstream>
 
 std::string StringOutputBuffer::str() const {
   std::string result;
diff --git a/src/gn/string_output_buffer.h b/src/gn/string_output_buffer.h
index 04e27dd..2338860 100644
--- a/src/gn/string_output_buffer.h
+++ b/src/gn/string_output_buffer.h
@@ -7,18 +7,16 @@
 
 #include <array>
 #include <memory>
+#include <streambuf>
 #include <string>
 #include <string_view>
 #include <vector>
 
-#include "gn/output_stream.h"
-
 namespace base {
 class FilePath;
 }  // namespace base
 
 class Err;
-class OutputStream;
 
 // An append-only very large storage area for string data. Useful for the parts
 // of GN that need to generate huge output files (e.g. --ide=json will create
@@ -30,11 +28,11 @@
 //
 //   2) Use operator<<, or Append() to append data to the instance.
 //
-//   3) Alternatively, create an OutputStream that takes its address as
+//   3) Alternatively, create an std::ostream that takes its address as
 //      argument, then use the output stream as usual to append data to it.
 //
 //      StringOutputBuffer storage;
-//      OutputStream out(&storage);
+//      std::ostream out(&storage);
 //      out << "Hello world!";
 //
 //   4) Use ContentsEqual() to compare the instance's content with that of a
@@ -42,7 +40,7 @@
 //
 //   5) Use WriteToFile() to write the content to a given file.
 //
-class StringOutputBuffer : public OutputStream {
+class StringOutputBuffer : public std::streambuf {
  public:
   StringOutputBuffer() = default;
 
@@ -74,9 +72,18 @@
 
   static size_t GetPageSizeForTesting() { return kPageSize; }
 
-  // OutputStream overrides
-  void put(char ch) override { Append(ch); }
-  void write(const char* str, size_t len) override { Append(str, len); }
+ protected:
+  // Called by std::ostream to write |n| chars from |s|.
+  std::streamsize xsputn(const char* s, std::streamsize n) override {
+    Append(s, static_cast<size_t>(n));
+    return n;
+  }
+
+  // Called by std::ostream to write a single character.
+  int_type overflow(int_type ch) override {
+    Append(static_cast<char>(ch));
+    return 1;
+  }
 
  private:
   // Return the number of free bytes in the current page.
diff --git a/src/gn/string_output_buffer_unittest.cc b/src/gn/string_output_buffer_unittest.cc
index b80a3be..6dcb741 100644
--- a/src/gn/string_output_buffer_unittest.cc
+++ b/src/gn/string_output_buffer_unittest.cc
@@ -69,11 +69,12 @@
   const size_t span_size = data_size / num_spans;
 
   StringOutputBuffer buffer;
+  std::ostream out(&buffer);
 
   for (size_t n = 0; n < num_spans; ++n) {
     size_t start_offset = n * span_size;
     size_t end_offset = std::min(start_offset + span_size, data.size());
-    buffer << std::string_view(&data[start_offset], end_offset - start_offset);
+    out << std::string_view(&data[start_offset], end_offset - start_offset);
   }
 
   EXPECT_EQ(data.size(), buffer.size());
diff --git a/src/gn/substitution_writer.cc b/src/gn/substitution_writer.cc
index f49abc4..c9624d7 100644
--- a/src/gn/substitution_writer.cc
+++ b/src/gn/substitution_writer.cc
@@ -9,7 +9,6 @@
 #include "gn/escape.h"
 #include "gn/filesystem_utils.h"
 #include "gn/output_file.h"
-#include "gn/output_stream.h"
 #include "gn/rust_substitution_type.h"
 #include "gn/rust_tool.h"
 #include "gn/settings.h"
@@ -151,7 +150,7 @@
 void SubstitutionWriter::WriteWithNinjaVariables(
     const SubstitutionPattern& pattern,
     const EscapeOptions& escape_options,
-    OutputStream& out) {
+    std::ostream& out) {
   // The result needs to be quoted as if it was one string, but the $ for
   // the inserted Ninja variables can't be escaped. So write to a buffer with
   // no quoting, and then quote the whole thing if necessary.
@@ -323,7 +322,7 @@
     const SourceFile& source,
     const std::vector<const Substitution*>& types,
     const EscapeOptions& escape_options,
-    OutputStream& out) {
+    std::ostream& out) {
   for (const auto& type : types) {
     // Don't write SOURCE since that just maps to Ninja's $in variable, which
     // is implicit in the rule. RESPONSE_FILE_NAME is written separately
@@ -336,7 +335,7 @@
           GetSourceSubstitution(target, settings, source, type, OUTPUT_RELATIVE,
                                 settings->build_settings()->build_dir()),
           escape_options);
-      out << "\n";
+      out << std::endl;
     }
   }
 }
diff --git a/src/gn/substitution_writer.h b/src/gn/substitution_writer.h
index 73f5cda..95e4b94 100644
--- a/src/gn/substitution_writer.h
+++ b/src/gn/substitution_writer.h
@@ -13,7 +13,6 @@
 
 struct EscapeOptions;
 class OutputFile;
-class OutputStream;
 class Settings;
 class SourceDir;
 class SourceFile;
@@ -63,7 +62,7 @@
   // Ninja variables replacing the patterns.
   static void WriteWithNinjaVariables(const SubstitutionPattern& pattern,
                                       const EscapeOptions& escape_options,
-                                      OutputStream& out);
+                                      std::ostream& out);
 
   // NOP substitutions ---------------------------------------------------------
 
@@ -158,7 +157,7 @@
       const SourceFile& source,
       const std::vector<const Substitution*>& types,
       const EscapeOptions& escape_options,
-      OutputStream& out);
+      std::ostream& out);
 
   // Extracts the given type of substitution related to a source file from the
   // given source file. If output_style is OUTPUT_RELATIVE, relative_to
diff --git a/src/gn/substitution_writer_unittest.cc b/src/gn/substitution_writer_unittest.cc
index 152adb5..eaa521a 100644
--- a/src/gn/substitution_writer_unittest.cc
+++ b/src/gn/substitution_writer_unittest.cc
@@ -2,13 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "gn/substitution_writer.h"
+#include <sstream>
+
 #include "gn/c_substitution_type.h"
 #include "gn/err.h"
 #include "gn/escape.h"
-#include "gn/output_stream.h"
 #include "gn/substitution_list.h"
 #include "gn/substitution_pattern.h"
+#include "gn/substitution_writer.h"
 #include "gn/target.h"
 #include "gn/test_with_scope.h"
 #include "util/build_config.h"
@@ -76,7 +77,7 @@
   EscapeOptions options;
   options.mode = ESCAPE_NONE;
 
-  StringOutputStream out;
+  std::ostringstream out;
   SubstitutionWriter::WriteNinjaVariablesForSource(
       nullptr, setup.settings(), SourceFile("//foo/bar/baz.txt"), types,
       options, out);
@@ -99,7 +100,7 @@
   EscapeOptions options;
   options.mode = ESCAPE_NONE;
 
-  StringOutputStream out;
+  std::ostringstream out;
   SubstitutionWriter::WriteWithNinjaVariables(pattern, options, out);
 
   EXPECT_EQ("-i ${in} --out=bar\"${source_name_part}\".o", out.str());
diff --git a/src/gn/trace.cc b/src/gn/trace.cc
index 8a16f14..5f07353 100644
--- a/src/gn/trace.cc
+++ b/src/gn/trace.cc
@@ -9,6 +9,7 @@
 #include <algorithm>
 #include <map>
 #include <mutex>
+#include <sstream>
 #include <vector>
 
 #include "base/command_line.h"
@@ -19,7 +20,6 @@
 #include "base/strings/stringprintf.h"
 #include "gn/filesystem_utils.h"
 #include "gn/label.h"
-#include "gn/output_stream.h"
 
 namespace {
 
@@ -72,18 +72,18 @@
   return a.total_duration > b.total_duration;
 }
 
-void SummarizeParses(std::vector<const TraceItem*>& loads, OutputStream& out) {
+void SummarizeParses(std::vector<const TraceItem*>& loads, std::ostream& out) {
   out << "File parse times: (time in ms, name)\n";
 
   std::sort(loads.begin(), loads.end(), &DurationGreater);
   for (auto* load : loads) {
     out << base::StringPrintf(" %8.2f  ", load->delta().InMillisecondsF());
-    out << load->name() << "\n";
+    out << load->name() << std::endl;
   }
 }
 
 void SummarizeCoalesced(std::vector<const TraceItem*>& items,
-                        OutputStream& out) {
+                        std::ostream& out) {
   // Group by file name.
   std::map<std::string, Coalesced> coalesced;
   for (auto* item : items) {
@@ -101,18 +101,18 @@
 
   for (const auto& cur : sorted) {
     out << base::StringPrintf(" %8.2f  %d  ", cur.total_duration, cur.count);
-    out << *cur.name_ptr << "\n";
+    out << *cur.name_ptr << std::endl;
   }
 }
 
 void SummarizeFileExecs(std::vector<const TraceItem*>& execs,
-                        OutputStream& out) {
+                        std::ostream& out) {
   out << "File execute times: (total time in ms, # executions, name)\n";
   SummarizeCoalesced(execs, out);
 }
 
 void SummarizeScriptExecs(std::vector<const TraceItem*>& execs,
-                          OutputStream& out) {
+                          std::ostream& out) {
   out << "Script execute times: (total time in ms, # executions, name)\n";
   SummarizeCoalesced(execs, out);
 }
@@ -223,13 +223,13 @@
     }
   }
 
-  StringOutputStream out;
+  std::ostringstream out;
   SummarizeParses(parses, out);
-  out << "\n";
+  out << std::endl;
   SummarizeFileExecs(file_execs, out);
-  out << "\n";
+  out << std::endl;
   SummarizeScriptExecs(script_execs, out);
-  out << "\n";
+  out << std::endl;
 
   // Generally there will only be one header check, but it's theoretically
   // possible for more than one to run if more than one build is going in
@@ -248,7 +248,7 @@
 }
 
 void SaveTraces(const base::FilePath& file_name) {
-  StringOutputStream out;
+  std::ostringstream out;
 
   out << "{\"traceEvents\":[";
 
diff --git a/src/gn/visual_studio_writer.cc b/src/gn/visual_studio_writer.cc
index 19e903d..243fedd 100644
--- a/src/gn/visual_studio_writer.cc
+++ b/src/gn/visual_studio_writer.cc
@@ -22,7 +22,6 @@
 #include "gn/deps_iterator.h"
 #include "gn/filesystem_utils.h"
 #include "gn/label_pattern.h"
-#include "gn/output_stream.h"
 #include "gn/parse_tree.h"
 #include "gn/path_output.h"
 #include "gn/standard_out.h"
@@ -39,7 +38,7 @@
 namespace {
 
 struct SemicolonSeparatedWriter {
-  void operator()(const std::string& value, OutputStream& out) const {
+  void operator()(const std::string& value, std::ostream& out) const {
     out << XmlEscape(value) + ';';
   }
 };
@@ -49,7 +48,7 @@
       : path_output_(path_output) {}
   ~IncludeDirWriter() = default;
 
-  void operator()(const SourceDir& dir, OutputStream& out) const {
+  void operator()(const SourceDir& dir, std::ostream& out) const {
     path_output_.WriteDir(out, dir, PathOutput::DIR_NO_LAST_SLASH);
     out << ";";
   }
@@ -62,7 +61,7 @@
       : path_output_(path_output), source_file_(source_file) {}
   ~SourceFileWriter() = default;
 
-  void operator()(OutputStream& out) const {
+  void operator()(std::ostream& out) const {
     path_output_.WriteFile(out, source_file_);
   }
 
@@ -429,8 +428,9 @@
       project_config_platform));
 
   StringOutputBuffer vcxproj_storage;
+  std::ostream vcxproj_string_out(&vcxproj_storage);
   SourceFileCompileTypePairs source_types;
-  if (!WriteProjectFileContents(vcxproj_storage, *projects_.back(), target,
+  if (!WriteProjectFileContents(vcxproj_string_out, *projects_.back(), target,
                                 ninja_extra_args, ninja_executable,
                                 &source_types, err)) {
     projects_.pop_back();
@@ -446,12 +446,13 @@
   base::FilePath filters_path = UTF8ToFilePath(vcxproj_path_str + ".filters");
 
   StringOutputBuffer filters_storage;
-  WriteFiltersFileContents(filters_storage, target, source_types);
+  std::ostream filters_string_out(&filters_storage);
+  WriteFiltersFileContents(filters_string_out, target, source_types);
   return filters_storage.WriteToFileIfChanged(filters_path, err);
 }
 
 bool VisualStudioWriter::WriteProjectFileContents(
-    OutputStream& out,
+    std::ostream& out,
     const SolutionProject& solution_project,
     const Target* target,
     const std::string& ninja_extra_args,
@@ -462,7 +463,7 @@
       GetBuildDirForTargetAsSourceDir(target, BuildDirType::OBJ),
       build_settings_->root_path_utf8(), EscapingMode::ESCAPE_NONE);
 
-  out << "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
+  out << "<?xml version=\"1.0\" encoding=\"utf-8\"?>" << std::endl;
   XmlElementWriter project(
       out, "Project",
       XmlAttributes("DefaultTargets", "Build")
@@ -684,16 +685,16 @@
 }
 
 void VisualStudioWriter::WriteFiltersFileContents(
-    OutputStream& out,
+    std::ostream& out,
     const Target* target,
     const SourceFileCompileTypePairs& source_types) {
-  out << "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
+  out << "<?xml version=\"1.0\" encoding=\"utf-8\"?>" << std::endl;
   XmlElementWriter project(
       out, "Project",
       XmlAttributes("ToolsVersion", "4.0")
           .add("xmlns", "http://schemas.microsoft.com/developer/msbuild/2003"));
 
-  StringOutputStream files_out;
+  std::ostringstream files_out;
 
   {
     std::unique_ptr<XmlElementWriter> filters_group =
@@ -717,7 +718,7 @@
           file_and_type.compile_type, "Include",
           SourceFileWriter(file_path_output, *file_and_type.file));
 
-      StringOutputStream target_relative_out;
+      std::ostringstream target_relative_out;
       filter_path_output.WriteFile(target_relative_out, *file_and_type.file);
       std::string target_relative_path = target_relative_out.str();
       ConvertPathToSystem(&target_relative_path);
@@ -755,7 +756,8 @@
   base::FilePath sln_path = build_settings_->GetFullPath(sln_file);
 
   StringOutputBuffer storage;
-  WriteSolutionFileContents(storage, sln_path.DirName());
+  std::ostream string_out(&storage);
+  WriteSolutionFileContents(string_out, sln_path.DirName());
 
   // Only write the content to the file if it's different. That is
   // both a performance optimization and more importantly, prevents
@@ -764,64 +766,66 @@
 }
 
 void VisualStudioWriter::WriteSolutionFileContents(
-    OutputStream& out,
+    std::ostream& out,
     const base::FilePath& solution_dir_path) {
-  out << "Microsoft Visual Studio Solution File, Format Version 12.00\n";
-  out << "# " << version_string_ << "\n";
+  out << "Microsoft Visual Studio Solution File, Format Version 12.00"
+      << std::endl;
+  out << "# " << version_string_ << std::endl;
 
   SourceDir solution_dir(FilePathToUTF8(solution_dir_path));
   for (const std::unique_ptr<SolutionEntry>& folder : folders_) {
     out << "Project(\"" << kGuidTypeFolder << "\") = \"(" << folder->name
         << ")\", \"" << RebasePath(folder->path, solution_dir) << "\", \""
-        << folder->guid << "\"\n";
-    out << "EndProject\n";
+        << folder->guid << "\"" << std::endl;
+    out << "EndProject" << std::endl;
   }
 
   for (const std::unique_ptr<SolutionProject>& project : projects_) {
     out << "Project(\"" << kGuidTypeProject << "\") = \"" << project->name
         << "\", \"" << RebasePath(project->path, solution_dir) << "\", \""
-        << project->guid << "\"\n";
-    out << "EndProject\n";
+        << project->guid << "\"" << std::endl;
+    out << "EndProject" << std::endl;
   }
 
-  out << "Global\n";
+  out << "Global" << std::endl;
 
   out << "\tGlobalSection(SolutionConfigurationPlatforms) = preSolution"
-      << "\n";
+      << std::endl;
   const std::string config_mode_prefix = std::string(kConfigurationName) + '|';
   const std::string config_mode = config_mode_prefix + config_platform_;
-  out << "\t\t" << config_mode << " = " << config_mode << "\n";
-  out << "\tEndGlobalSection\n";
+  out << "\t\t" << config_mode << " = " << config_mode << std::endl;
+  out << "\tEndGlobalSection" << std::endl;
 
-  out << "\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n";
+  out << "\tGlobalSection(ProjectConfigurationPlatforms) = postSolution"
+      << std::endl;
   for (const std::unique_ptr<SolutionProject>& project : projects_) {
     const std::string project_config_mode =
         config_mode_prefix + project->config_platform;
     out << "\t\t" << project->guid << '.' << config_mode
-        << ".ActiveCfg = " << project_config_mode << "\n";
+        << ".ActiveCfg = " << project_config_mode << std::endl;
     out << "\t\t" << project->guid << '.' << config_mode
-        << ".Build.0 = " << project_config_mode << "\n";
+        << ".Build.0 = " << project_config_mode << std::endl;
   }
-  out << "\tEndGlobalSection\n";
+  out << "\tEndGlobalSection" << std::endl;
 
-  out << "\tGlobalSection(SolutionProperties) = preSolution\n";
-  out << "\t\tHideSolutionNode = FALSE\n";
-  out << "\tEndGlobalSection\n";
+  out << "\tGlobalSection(SolutionProperties) = preSolution" << std::endl;
+  out << "\t\tHideSolutionNode = FALSE" << std::endl;
+  out << "\tEndGlobalSection" << std::endl;
 
-  out << "\tGlobalSection(NestedProjects) = preSolution\n";
+  out << "\tGlobalSection(NestedProjects) = preSolution" << std::endl;
   for (const std::unique_ptr<SolutionEntry>& folder : folders_) {
     if (folder->parent_folder) {
       out << "\t\t" << folder->guid << " = " << folder->parent_folder->guid
-          << "\n";
+          << std::endl;
     }
   }
   for (const std::unique_ptr<SolutionProject>& project : projects_) {
     out << "\t\t" << project->guid << " = " << project->parent_folder->guid
-        << "\n";
+        << std::endl;
   }
-  out << "\tEndGlobalSection\n";
+  out << "\tEndGlobalSection" << std::endl;
 
-  out << "EndGlobal\n";
+  out << "EndGlobal" << std::endl;
 }
 
 void VisualStudioWriter::ResolveSolutionFolders() {
@@ -925,7 +929,7 @@
 
 std::pair<std::string, bool> VisualStudioWriter::GetNinjaTarget(
     const Target* target) {
-  StringOutputStream ninja_target_out;
+  std::ostringstream ninja_target_out;
   bool is_phony = false;
   OutputFile output_file;
   if (target->has_dependency_output_file()) {
diff --git a/src/gn/visual_studio_writer.h b/src/gn/visual_studio_writer.h
index e725744..7161481 100644
--- a/src/gn/visual_studio_writer.h
+++ b/src/gn/visual_studio_writer.h
@@ -113,18 +113,18 @@
                          const std::string& ninja_extra_args,
                          const std::string& ninja_executable,
                          Err* err);
-  bool WriteProjectFileContents(OutputStream& out,
+  bool WriteProjectFileContents(std::ostream& out,
                                 const SolutionProject& solution_project,
                                 const Target* target,
                                 const std::string& ninja_extra_args,
                                 const std::string& ninja_executable,
                                 SourceFileCompileTypePairs* source_types,
                                 Err* err);
-  void WriteFiltersFileContents(OutputStream& out,
+  void WriteFiltersFileContents(std::ostream& out,
                                 const Target* target,
                                 const SourceFileCompileTypePairs& source_types);
   bool WriteSolutionFile(const std::string& sln_name, Err* err);
-  void WriteSolutionFileContents(OutputStream& out,
+  void WriteSolutionFileContents(std::ostream& out,
                                  const base::FilePath& solution_dir_path);
 
   // Resolves all solution folders (parent folders for projects) into |folders_|
diff --git a/src/gn/visual_studio_writer_unittest.cc b/src/gn/visual_studio_writer_unittest.cc
index b4abaed..01271a3 100644
--- a/src/gn/visual_studio_writer_unittest.cc
+++ b/src/gn/visual_studio_writer_unittest.cc
@@ -7,7 +7,6 @@
 #include <memory>
 
 #include "base/strings/string_util.h"
-#include "gn/output_stream.h"
 #include "gn/test_with_scope.h"
 #include "gn/visual_studio_utils.h"
 #include "util/test/test.h"
@@ -192,7 +191,7 @@
 
   VisualStudioWriter::SourceFileCompileTypePairs source_types;
 
-  StringOutputStream file_contents;
+  std::stringstream file_contents;
   writer.WriteProjectFileContents(file_contents, *writer.projects_.back(),
                                   &target, "", "", &source_types, &err);
 
@@ -227,7 +226,7 @@
 
   VisualStudioWriter::SourceFileCompileTypePairs source_types;
 
-  StringOutputStream file_contents_without_flag;
+  std::stringstream file_contents_without_flag;
   writer.WriteProjectFileContents(file_contents_without_flag,
                                   *writer.projects_.back(), &target, "", "",
                                   &source_types, &err);
@@ -236,7 +235,7 @@
   ASSERT_NE(file_contents_without_flag.str().find("call ninja.exe"),
             std::string::npos);
 
-  StringOutputStream file_contents_with_flag;
+  std::stringstream file_contents_with_flag;
   writer.WriteProjectFileContents(file_contents_with_flag,
                                   *writer.projects_.back(), &target, "",
                                   "ninja_wrapper.exe", &source_types, &err);
diff --git a/src/gn/xcode_object.cc b/src/gn/xcode_object.cc
index 58543ee..4399c55 100644
--- a/src/gn/xcode_object.cc
+++ b/src/gn/xcode_object.cc
@@ -4,15 +4,15 @@
 
 #include "gn/xcode_object.h"
 
-#include <cstring>
+#include <iomanip>
 #include <iterator>
 #include <memory>
+#include <sstream>
 #include <utility>
 
 #include "base/logging.h"
 #include "base/strings/string_util.h"
 #include "gn/filesystem_utils.h"
-#include "gn/output_stream.h"
 
 // Helper methods -------------------------------------------------------------
 
@@ -51,7 +51,7 @@
   if (!StringNeedEscaping(string))
     return string;
 
-  StringOutputStream buffer;
+  std::stringstream buffer;
   buffer << '"';
   for (char c : string) {
     if (c <= 31) {
@@ -75,12 +75,10 @@
         case '\f':
           buffer << "\\f";
           break;
-        default: {
-          char buff[10];
-          ::snprintf(buff, sizeof(buff), "\\U%04x", static_cast<unsigned>(c));
-          buffer << buff;
+        default:
+          buffer << std::hex << std::setw(4) << std::left << "\\U"
+                 << static_cast<unsigned>(c);
           break;
-        }
       }
     } else {
       if (c == '"' || c == '\\')
@@ -173,37 +171,37 @@
   explicit NoReference(const PBXObject* value) : value(value) {}
 };
 
-void PrintValue(OutputStream& out, IndentRules rules, unsigned value) {
+void PrintValue(std::ostream& out, IndentRules rules, unsigned value) {
   out << value;
 }
 
-void PrintValue(OutputStream& out, IndentRules rules, const char* value) {
+void PrintValue(std::ostream& out, IndentRules rules, const char* value) {
   out << EncodeString(value);
 }
 
-void PrintValue(OutputStream& out,
+void PrintValue(std::ostream& out,
                 IndentRules rules,
                 const std::string& value) {
   out << EncodeString(value);
 }
 
-void PrintValue(OutputStream& out, IndentRules rules, const NoReference& obj) {
+void PrintValue(std::ostream& out, IndentRules rules, const NoReference& obj) {
   out << obj.value->id();
 }
 
-void PrintValue(OutputStream& out, IndentRules rules, const PBXObject* value) {
+void PrintValue(std::ostream& out, IndentRules rules, const PBXObject* value) {
   out << value->Reference();
 }
 
 template <typename ObjectClass>
-void PrintValue(OutputStream& out,
+void PrintValue(std::ostream& out,
                 IndentRules rules,
                 const std::unique_ptr<ObjectClass>& value) {
   PrintValue(out, rules, value.get());
 }
 
 template <typename ValueType>
-void PrintValue(OutputStream& out,
+void PrintValue(std::ostream& out,
                 IndentRules rules,
                 const std::vector<ValueType>& values) {
   IndentRules sub_rule{rules.one_line, rules.level + 1};
@@ -222,7 +220,7 @@
 }
 
 template <typename ValueType>
-void PrintValue(OutputStream& out,
+void PrintValue(std::ostream& out,
                 IndentRules rules,
                 const std::map<std::string, ValueType>& values) {
   IndentRules sub_rule{rules.one_line, rules.level + 1};
@@ -242,7 +240,7 @@
 }
 
 template <typename ValueType>
-void PrintProperty(OutputStream& out,
+void PrintProperty(std::ostream& out,
                    IndentRules rules,
                    const char* name,
                    ValueType&& value) {
@@ -445,7 +443,7 @@
   return PBXAggregateTargetClass;
 }
 
-void PBXAggregateTarget::Print(OutputStream& out, unsigned indent) const {
+void PBXAggregateTarget::Print(std::ostream& out, unsigned indent) const {
   const std::string indent_str(indent, '\t');
   const IndentRules rules = {false, indent + 1};
   out << indent_str << Reference() << " = {\n";
@@ -477,7 +475,7 @@
   return file_reference_->Name() + " in " + build_phase_->Name();
 }
 
-void PBXBuildFile::Print(OutputStream& out, unsigned indent) const {
+void PBXBuildFile::Print(std::ostream& out, unsigned indent) const {
   const std::string indent_str(indent, '\t');
   const IndentRules rules = {true, 0};
   out << indent_str << Reference() << " = {";
@@ -501,7 +499,7 @@
   return "PBXContainerItemProxy";
 }
 
-void PBXContainerItemProxy::Print(OutputStream& out, unsigned indent) const {
+void PBXContainerItemProxy::Print(std::ostream& out, unsigned indent) const {
   const std::string indent_str(indent, '\t');
   const IndentRules rules = {false, indent + 1};
   out << indent_str << Reference() << " = {\n";
@@ -534,7 +532,7 @@
   return !name_.empty() ? name_ : path_;
 }
 
-void PBXFileReference::Print(OutputStream& out, unsigned indent) const {
+void PBXFileReference::Print(std::ostream& out, unsigned indent) const {
   const std::string indent_str(indent, '\t');
   const IndentRules rules = {true, 0};
   out << indent_str << Reference() << " = {";
@@ -574,7 +572,7 @@
   return "Frameworks";
 }
 
-void PBXFrameworksBuildPhase::Print(OutputStream& out, unsigned indent) const {
+void PBXFrameworksBuildPhase::Print(std::ostream& out, unsigned indent) const {
   const std::string indent_str(indent, '\t');
   const IndentRules rules = {false, indent + 1};
   out << indent_str << Reference() << " = {\n";
@@ -665,7 +663,7 @@
   }
 }
 
-void PBXGroup::Print(OutputStream& out, unsigned indent) const {
+void PBXGroup::Print(std::ostream& out, unsigned indent) const {
   const std::string indent_str(indent, '\t');
   const IndentRules rules = {false, indent + 1};
   out << indent_str << Reference() << " = {\n";
@@ -757,7 +755,7 @@
   return PBXNativeTargetClass;
 }
 
-void PBXNativeTarget::Print(OutputStream& out, unsigned indent) const {
+void PBXNativeTarget::Print(std::ostream& out, unsigned indent) const {
   const std::string indent_str(indent, '\t');
   const IndentRules rules = {false, indent + 1};
   out << indent_str << Reference() << " = {\n";
@@ -926,7 +924,7 @@
     target->Visit(visitor);
   }
 }
-void PBXProject::Print(OutputStream& out, unsigned indent) const {
+void PBXProject::Print(std::ostream& out, unsigned indent) const {
   const std::string indent_str(indent, '\t');
   const IndentRules rules = {false, indent + 1};
   out << indent_str << Reference() << " = {\n";
@@ -960,7 +958,7 @@
   return "Resources";
 }
 
-void PBXResourcesBuildPhase::Print(OutputStream& out, unsigned indent) const {
+void PBXResourcesBuildPhase::Print(std::ostream& out, unsigned indent) const {
   const std::string indent_str(indent, '\t');
   const IndentRules rules = {false, indent + 1};
   out << indent_str << Reference() << " = {\n";
@@ -989,7 +987,7 @@
   return name_;
 }
 
-void PBXShellScriptBuildPhase::Print(OutputStream& out, unsigned indent) const {
+void PBXShellScriptBuildPhase::Print(std::ostream& out, unsigned indent) const {
   const std::string indent_str(indent, '\t');
   const IndentRules rules = {false, indent + 1};
   out << indent_str << Reference() << " = {\n";
@@ -1021,7 +1019,7 @@
   return "Sources";
 }
 
-void PBXSourcesBuildPhase::Print(OutputStream& out, unsigned indent) const {
+void PBXSourcesBuildPhase::Print(std::ostream& out, unsigned indent) const {
   const std::string indent_str(indent, '\t');
   const IndentRules rules = {false, indent + 1};
   out << indent_str << Reference() << " = {\n";
@@ -1057,7 +1055,7 @@
   container_item_proxy_->Visit(visitor);
 }
 
-void PBXTargetDependency::Print(OutputStream& out, unsigned indent) const {
+void PBXTargetDependency::Print(std::ostream& out, unsigned indent) const {
   const std::string indent_str(indent, '\t');
   const IndentRules rules = {false, indent + 1};
   out << indent_str << Reference() << " = {\n";
@@ -1083,7 +1081,7 @@
   return name_;
 }
 
-void XCBuildConfiguration::Print(OutputStream& out, unsigned indent) const {
+void XCBuildConfiguration::Print(std::ostream& out, unsigned indent) const {
   const std::string indent_str(indent, '\t');
   const IndentRules rules = {false, indent + 1};
   out << indent_str << Reference() << " = {\n";
@@ -1135,7 +1133,7 @@
   }
 }
 
-void XCConfigurationList::Print(OutputStream& out, unsigned indent) const {
+void XCConfigurationList::Print(std::ostream& out, unsigned indent) const {
   const std::string indent_str(indent, '\t');
   const IndentRules rules = {false, indent + 1};
   out << indent_str << Reference() << " = {\n";
diff --git a/src/gn/xcode_object.h b/src/gn/xcode_object.h
index 8bad659..076e993 100644
--- a/src/gn/xcode_object.h
+++ b/src/gn/xcode_object.h
@@ -45,7 +45,6 @@
 
 // Forward-declarations -------------------------------------------------------
 
-class OutputStream;
 class PBXAggregateTarget;
 class PBXBuildFile;
 class PBXBuildPhase;
@@ -109,7 +108,7 @@
   virtual std::string Comment() const;
   virtual void Visit(PBXObjectVisitor& visitor);
   virtual void Visit(PBXObjectVisitorConst& visitor) const;
-  virtual void Print(OutputStream& out, unsigned indent) const = 0;
+  virtual void Print(std::ostream& out, unsigned indent) const = 0;
 
  private:
   std::string id_;
@@ -181,7 +180,7 @@
 
   // PBXObject implementation.
   PBXObjectClass Class() const override;
-  void Print(OutputStream& out, unsigned indent) const override;
+  void Print(std::ostream& out, unsigned indent) const override;
 
  private:
   PBXAggregateTarget(const PBXAggregateTarget&) = delete;
@@ -199,7 +198,7 @@
   // PBXObject implementation.
   PBXObjectClass Class() const override;
   std::string Name() const override;
-  void Print(OutputStream& out, unsigned indent) const override;
+  void Print(std::ostream& out, unsigned indent) const override;
 
  private:
   const PBXFileReference* file_reference_ = nullptr;
@@ -218,7 +217,7 @@
   // PBXObject implementation.
   PBXObjectClass Class() const override;
   std::string Name() const override;
-  void Print(OutputStream& out, unsigned indent) const override;
+  void Print(std::ostream& out, unsigned indent) const override;
 
  private:
   const PBXProject* project_ = nullptr;
@@ -241,7 +240,7 @@
   PBXObjectClass Class() const override;
   std::string Name() const override;
   std::string Comment() const override;
-  void Print(OutputStream& out, unsigned indent) const override;
+  void Print(std::ostream& out, unsigned indent) const override;
 
   const std::string& path() const { return path_; }
 
@@ -264,7 +263,7 @@
   // PBXObject implementation.
   PBXObjectClass Class() const override;
   std::string Name() const override;
-  void Print(OutputStream& out, unsigned indent) const override;
+  void Print(std::ostream& out, unsigned indent) const override;
 
  private:
   PBXFrameworksBuildPhase(const PBXFrameworksBuildPhase&) = delete;
@@ -296,7 +295,7 @@
   std::string Name() const override;
   void Visit(PBXObjectVisitor& visitor) override;
   void Visit(PBXObjectVisitorConst& visitor) const override;
-  void Print(OutputStream& out, unsigned indent) const override;
+  void Print(std::ostream& out, unsigned indent) const override;
 
   // Returns whether the current PBXGroup should sort last when sorting
   // children of a PBXGroup. This should only be used for the "Products"
@@ -354,7 +353,7 @@
 
   // PBXObject implementation.
   PBXObjectClass Class() const override;
-  void Print(OutputStream& out, unsigned indent) const override;
+  void Print(std::ostream& out, unsigned indent) const override;
 
  private:
   const PBXFileReference* product_reference_ = nullptr;
@@ -403,7 +402,7 @@
   std::string Comment() const override;
   void Visit(PBXObjectVisitor& visitor) override;
   void Visit(PBXObjectVisitorConst& visitor) const override;
-  void Print(OutputStream& out, unsigned indent) const override;
+  void Print(std::ostream& out, unsigned indent) const override;
 
  private:
   PBXAttributes attributes_;
@@ -432,7 +431,7 @@
   // PBXObject implementation.
   PBXObjectClass Class() const override;
   std::string Name() const override;
-  void Print(OutputStream& out, unsigned indent) const override;
+  void Print(std::ostream& out, unsigned indent) const override;
 
  private:
   PBXResourcesBuildPhase(const PBXResourcesBuildPhase&) = delete;
@@ -450,7 +449,7 @@
   // PBXObject implementation.
   PBXObjectClass Class() const override;
   std::string Name() const override;
-  void Print(OutputStream& out, unsigned indent) const override;
+  void Print(std::ostream& out, unsigned indent) const override;
 
  private:
   std::string name_;
@@ -470,7 +469,7 @@
   // PBXObject implementation.
   PBXObjectClass Class() const override;
   std::string Name() const override;
-  void Print(OutputStream& out, unsigned indent) const override;
+  void Print(std::ostream& out, unsigned indent) const override;
 
  private:
   PBXSourcesBuildPhase(const PBXSourcesBuildPhase&) = delete;
@@ -490,7 +489,7 @@
   std::string Name() const override;
   void Visit(PBXObjectVisitor& visitor) override;
   void Visit(PBXObjectVisitorConst& visitor) const override;
-  void Print(OutputStream& out, unsigned indent) const override;
+  void Print(std::ostream& out, unsigned indent) const override;
 
  private:
   const PBXTarget* target_ = nullptr;
@@ -511,7 +510,7 @@
   // PBXObject implementation.
   PBXObjectClass Class() const override;
   std::string Name() const override;
-  void Print(OutputStream& out, unsigned indent) const override;
+  void Print(std::ostream& out, unsigned indent) const override;
 
  private:
   PBXAttributes attributes_;
@@ -535,7 +534,7 @@
   std::string Name() const override;
   void Visit(PBXObjectVisitor& visitor) override;
   void Visit(PBXObjectVisitorConst& visitor) const override;
-  void Print(OutputStream& out, unsigned indent) const override;
+  void Print(std::ostream& out, unsigned indent) const override;
 
  private:
   std::vector<std::unique_ptr<XCBuildConfiguration>> configurations_;
diff --git a/src/gn/xcode_writer.cc b/src/gn/xcode_writer.cc
index 85b6c08..ea84a3d 100644
--- a/src/gn/xcode_writer.cc
+++ b/src/gn/xcode_writer.cc
@@ -9,6 +9,7 @@
 #include <map>
 #include <memory>
 #include <optional>
+#include <sstream>
 #include <string>
 #include <string_view>
 #include <utility>
@@ -30,7 +31,6 @@
 #include "gn/filesystem_utils.h"
 #include "gn/item.h"
 #include "gn/loader.h"
-#include "gn/output_stream.h"
 #include "gn/scheduler.h"
 #include "gn/settings.h"
 #include "gn/source_file.h"
@@ -551,7 +551,8 @@
   if (source_file.is_null())
     return false;
 
-  StringOutputBuffer out;
+  StringOutputBuffer storage;
+  std::ostream out(&storage);
   out << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
       << "<Workspace\n"
       << "   version = \"1.0\">\n"
@@ -560,8 +561,8 @@
       << "   </FileRef>\n"
       << "</Workspace>\n";
 
-  return out.WriteToFileIfChanged(build_settings_->GetFullPath(source_file),
-                                  err);
+  return storage.WriteToFileIfChanged(build_settings_->GetFullPath(source_file),
+                                      err);
 }
 
 bool XcodeWorkspace::WriteSettingsFile(const std::string& name,
@@ -573,7 +574,8 @@
   if (source_file.is_null())
     return false;
 
-  StringOutputBuffer out;
+  StringOutputBuffer storage;
+  std::ostream out(&storage);
   out << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
       << "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" "
       << "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n"
@@ -591,8 +593,8 @@
 
   out << "</dict>\n" << "</plist>\n";
 
-  return out.WriteToFileIfChanged(build_settings_->GetFullPath(source_file),
-                                  err);
+  return storage.WriteToFileIfChanged(build_settings_->GetFullPath(source_file),
+                                      err);
 }
 
 // Class responsible for constructing and writing the .xcodeproj from the
@@ -658,7 +660,7 @@
   std::string GetConfigOutputDir(std::string_view output_dir);
 
   // Generates the content of the .xcodeproj file into |out|.
-  void WriteFileContent(OutputStream& out) const;
+  void WriteFileContent(std::ostream& out) const;
 
   // Returns whether the file should be added to the project.
   bool ShouldIncludeFileInProject(const SourceFile& source) const;
@@ -914,11 +916,12 @@
   if (pbxproj_file.is_null())
     return false;
 
-  StringOutputBuffer pbxproj_out;
-  WriteFileContent(pbxproj_out);
+  StringOutputBuffer storage;
+  std::ostream pbxproj_string_out(&storage);
+  WriteFileContent(pbxproj_string_out);
 
-  if (!pbxproj_out.WriteToFileIfChanged(
-          build_settings_->GetFullPath(pbxproj_file), err)) {
+  if (!storage.WriteToFileIfChanged(build_settings_->GetFullPath(pbxproj_file),
+                                    err)) {
     return false;
   }
 
@@ -1059,7 +1062,7 @@
                     build_settings_->root_path_utf8());
 }
 
-void XcodeProject::WriteFileContent(OutputStream& out) const {
+void XcodeProject::WriteFileContent(std::ostream& out) const {
   out << "// !$*UTF8*$!\n"
       << "{\n"
       << "\tarchiveVersion = 1;\n"
diff --git a/src/gn/xml_element_writer.cc b/src/gn/xml_element_writer.cc
index 4ee5820..f888915 100644
--- a/src/gn/xml_element_writer.cc
+++ b/src/gn/xml_element_writer.cc
@@ -19,12 +19,12 @@
   return *this;
 }
 
-XmlElementWriter::XmlElementWriter(OutputStream& out,
+XmlElementWriter::XmlElementWriter(std::ostream& out,
                                    const std::string& tag,
                                    const XmlAttributes& attributes)
     : XmlElementWriter(out, tag, attributes, 0) {}
 
-XmlElementWriter::XmlElementWriter(OutputStream& out,
+XmlElementWriter::XmlElementWriter(std::ostream& out,
                                    const std::string& tag,
                                    const XmlAttributes& attributes,
                                    int indent)
@@ -42,11 +42,11 @@
   if (!opening_tag_finished_) {
     // The XML spec does not require a space before the closing slash. However,
     // Eclipse is unable to parse XML settings files if there is no space.
-    out_ << " />\n";
+    out_ << " />" << std::endl;
   } else {
     if (!one_line_)
       out_ << std::string(indent_, ' ');
-    out_ << "</" << tag_ << ">\n";
+    out_ << "</" << tag_ << '>' << std::endl;
   }
 }
 
@@ -67,13 +67,13 @@
   return std::make_unique<XmlElementWriter>(out_, tag, attributes, indent_ + 2);
 }
 
-OutputStream& XmlElementWriter::StartContent(bool start_new_line) {
+std::ostream& XmlElementWriter::StartContent(bool start_new_line) {
   if (!opening_tag_finished_) {
     out_ << '>';
     opening_tag_finished_ = true;
 
     if (start_new_line && one_line_) {
-      out_ << "\n";
+      out_ << std::endl;
       one_line_ = false;
     }
   }
diff --git a/src/gn/xml_element_writer.h b/src/gn/xml_element_writer.h
index e23581f..1bf9b46 100644
--- a/src/gn/xml_element_writer.h
+++ b/src/gn/xml_element_writer.h
@@ -12,8 +12,6 @@
 #include <utility>
 #include <vector>
 
-#include "gn/output_stream.h"
-
 // Vector of XML attribute key-value pairs.
 class XmlAttributes
     : public std::vector<std::pair<std::string_view, std::string_view>> {
@@ -31,11 +29,11 @@
  public:
   // Starts new XML element. This constructor adds no indentation and is
   // designed for XML root element.
-  XmlElementWriter(OutputStream& out,
+  XmlElementWriter(std::ostream& out,
                    const std::string& tag,
                    const XmlAttributes& attributes);
   // Starts new XML element with specified indentation.
-  XmlElementWriter(OutputStream& out,
+  XmlElementWriter(std::ostream& out,
                    const std::string& tag,
                    const XmlAttributes& attributes,
                    int indent);
@@ -43,7 +41,7 @@
   // that allows writing XML element with single attribute without copying
   // attribute value.
   template <class Writer>
-  XmlElementWriter(OutputStream& out,
+  XmlElementWriter(std::ostream& out,
                    const std::string& tag,
                    const std::string& attribute_name,
                    const Writer& attribute_value_writer,
@@ -68,12 +66,12 @@
   // Finishes opening tag if it isn't finished yet and optionally starts new
   // document line. Returns the stream where XML element content can be written.
   // This is an alternative to Text() and SubElement() methods.
-  OutputStream& StartContent(bool start_new_line);
+  std::ostream& StartContent(bool start_new_line);
 
  private:
   // Output stream. XmlElementWriter objects for XML element and its
   // sub-elements share the same output stream.
-  OutputStream& out_;
+  std::ostream& out_;
 
   // XML element tag name.
   std::string tag_;
@@ -92,7 +90,7 @@
 };
 
 template <class Writer>
-XmlElementWriter::XmlElementWriter(OutputStream& out,
+XmlElementWriter::XmlElementWriter(std::ostream& out,
                                    const std::string& tag,
                                    const std::string& attribute_name,
                                    const Writer& attribute_value_writer,
diff --git a/src/gn/xml_element_writer_unittest.cc b/src/gn/xml_element_writer_unittest.cc
index 9ad9ed3..6a4cfed 100644
--- a/src/gn/xml_element_writer_unittest.cc
+++ b/src/gn/xml_element_writer_unittest.cc
@@ -4,7 +4,8 @@
 
 #include "gn/xml_element_writer.h"
 
-#include "gn/output_stream.h"
+#include <sstream>
+
 #include "util/test/test.h"
 
 namespace {
@@ -12,7 +13,7 @@
 class MockValueWriter {
  public:
   explicit MockValueWriter(const std::string& value) : value_(value) {}
-  void operator()(OutputStream& out) const { out << value_; }
+  void operator()(std::ostream& out) const { out << value_; }
 
  private:
   std::string value_;
@@ -21,24 +22,24 @@
 }  // namespace
 
 TEST(XmlElementWriter, EmptyElement) {
-  StringOutputStream out;
+  std::ostringstream out;
   { XmlElementWriter writer(out, "foo", XmlAttributes()); }
   EXPECT_EQ("<foo />\n", out.str());
 
-  StringOutputStream out_attr;
+  std::ostringstream out_attr;
   {
     XmlElementWriter writer(out_attr, "foo",
                             XmlAttributes("bar", "abc").add("baz", "123"));
   }
   EXPECT_EQ("<foo bar=\"abc\" baz=\"123\" />\n", out_attr.str());
 
-  StringOutputStream out_indent;
+  std::ostringstream out_indent;
   {
     XmlElementWriter writer(out_indent, "foo", XmlAttributes("bar", "baz"), 2);
   }
   EXPECT_EQ("  <foo bar=\"baz\" />\n", out_indent.str());
 
-  StringOutputStream out_writer;
+  std::ostringstream out_writer;
   {
     XmlElementWriter writer(out_writer, "foo", "bar", MockValueWriter("baz"),
                             2);
@@ -47,7 +48,7 @@
 }
 
 TEST(XmlElementWriter, ElementWithText) {
-  StringOutputStream out;
+  std::ostringstream out;
   {
     XmlElementWriter writer(out, "foo", XmlAttributes("bar", "baz"));
     writer.Text("Hello world!");
@@ -56,7 +57,7 @@
 }
 
 TEST(XmlElementWriter, SubElements) {
-  StringOutputStream out;
+  std::ostringstream out;
   {
     XmlElementWriter writer(out, "root", XmlAttributes("aaa", "000"));
     writer.SubElement("foo", XmlAttributes());
@@ -76,7 +77,7 @@
 }
 
 TEST(XmlElementWriter, StartContent) {
-  StringOutputStream out;
+  std::ostringstream out;
   {
     XmlElementWriter writer(out, "foo", XmlAttributes("bar", "baz"));
     writer.StartContent(false) << "Hello world!";