No public description

PiperOrigin-RevId: 588763891
diff --git a/centipede/BUILD b/centipede/BUILD
index d8a553e..a5736d8 100644
--- a/centipede/BUILD
+++ b/centipede/BUILD
@@ -132,9 +132,13 @@
         ":util",
         "@com_google_absl//absl/log",
         "@com_google_absl//absl/log:check",
-        "@com_google_absl//absl/status",
         "@com_google_absl//absl/strings",
         "@com_google_absl//absl/types:span",
+        "@com_google_riegeli//riegeli/base:any_dependency",
+        "@com_google_riegeli//riegeli/bytes:fd_reader",
+        "@com_google_riegeli//riegeli/bytes:reader",
+        "@com_google_riegeli//riegeli/bytes:writer",
+        "@com_google_riegeli//riegeli/lines:line_reading",
     ],
 )
 
@@ -165,11 +169,7 @@
     srcs = ["pc_info.cc"],
     hdrs = ["pc_info.h"],
     visibility = PUBLIC_API_VISIBILITY,
-    deps = [
-        ":defs",
-        "@com_google_absl//absl/log:check",
-        "@com_google_absl//absl/types:span",
-    ],
+    deps = ["@com_google_absl//absl/log:check"],
 )
 
 cc_library(
@@ -242,6 +242,8 @@
         "@com_google_absl//absl/strings:str_format",
         "@com_google_absl//absl/synchronization",
         "@com_google_absl//absl/types:span",
+        "@com_google_riegeli//riegeli/bytes:read_all",
+        "@com_google_riegeli//riegeli/bytes:write",
     ],
 )
 
@@ -290,6 +292,9 @@
         "@com_google_absl//absl/log",
         "@com_google_absl//absl/log:check",
         "@com_google_absl//absl/strings",
+        "@com_google_riegeli//riegeli/bytes:fd_writer",
+        "@com_google_riegeli//riegeli/bytes:read_all",
+        "@com_google_riegeli//riegeli/bytes:write",
     ],
 )
 
@@ -352,6 +357,9 @@
         "@com_google_absl//absl/strings:str_format",
         "@com_google_absl//absl/time",
         "@com_google_absl//absl/types:span",
+        "@com_google_riegeli//riegeli/bytes:writer",
+        "@com_google_riegeli//riegeli/csv:csv_record",
+        "@com_google_riegeli//riegeli/csv:csv_writer",
     ],
 )
 
@@ -413,6 +421,7 @@
         "@com_google_absl//absl/status",
         "@com_google_absl//absl/strings:string_view",
         "@com_google_riegeli//riegeli/base:object",
+        "@com_google_riegeli//riegeli/bytes:read_all",
         "@com_google_riegeli//riegeli/bytes:reader",
         "@com_google_riegeli//riegeli/bytes:writer",
         "@com_google_riegeli//riegeli/records:record_reader",
@@ -507,16 +516,18 @@
     visibility = PUBLIC_API_VISIBILITY,
     deps = [
         ":control_flow",
-        ":defs",
         ":feature",
         ":logging",
+        ":pc_info",
         ":symbol_table",
-        ":util",
         "@com_google_absl//absl/base:core_headers",
-        "@com_google_absl//absl/container:flat_hash_map",
         "@com_google_absl//absl/container:flat_hash_set",
+        "@com_google_absl//absl/log:check",
         "@com_google_absl//absl/strings",
         "@com_google_absl//absl/synchronization",
+        "@com_google_riegeli//riegeli/base:any_dependency",
+        "@com_google_riegeli//riegeli/bytes:string_writer",
+        "@com_google_riegeli//riegeli/bytes:writer",
     ],
 )
 
@@ -569,7 +580,6 @@
     srcs = ["remote_file.cc"],
     hdrs = ["remote_file.h"],
     deps = [
-        ":defs",
         ":logging",
         "@com_google_absl//absl/base:core_headers",
         "@com_google_absl//absl/log",
@@ -613,6 +623,8 @@
         "@com_google_absl//absl/log",
         "@com_google_absl//absl/log:check",
         "@com_google_absl//absl/strings",
+        "@com_google_riegeli//riegeli/base:any_dependency",
+        "@com_google_riegeli//riegeli/bytes:writer",
     ],
 )
 
@@ -647,6 +659,7 @@
         ":call_graph",
         ":command",
         ":control_flow",
+        ":logging",
         ":pc_info",
         ":remote_file",
         ":symbol_table",
@@ -654,6 +667,8 @@
         "@com_google_absl//absl/log",
         "@com_google_absl//absl/log:check",
         "@com_google_absl//absl/strings",
+        "@com_google_riegeli//riegeli/bytes:reader_istream",
+        "@com_google_riegeli//riegeli/bytes:writer_ostream",
     ],
 )
 
@@ -777,6 +792,9 @@
         "@com_google_absl//absl/strings:str_format",
         "@com_google_absl//absl/synchronization",
         "@com_google_absl//absl/time",
+        "@com_google_riegeli//riegeli/bytes:read_all",
+        "@com_google_riegeli//riegeli/bytes:write",
+        "@com_google_riegeli//riegeli/bytes:writer",
     ],
 )
 
@@ -838,6 +856,7 @@
         "@com_google_absl//absl/log:check",
         "@com_google_absl//absl/strings",
         "@com_google_absl//absl/time",
+        "@com_google_riegeli//riegeli/bytes:read_all",
     ],
 )
 
@@ -1060,6 +1079,7 @@
         "@com_google_absl//absl/strings:str_format",
         "@com_google_absl//absl/time",
         "@com_google_protobuf//:protobuf",
+        "@com_google_riegeli//riegeli/bytes:read_all",
     ],
 )
 
@@ -1129,6 +1149,7 @@
         ":symbol_table",
         ":test_util",
         "@com_google_googletest//:gtest_main",
+        "@com_google_riegeli//riegeli/bytes:string_reader",
     ],
 )
 
@@ -1186,6 +1207,8 @@
     deps = [
         ":symbol_table",
         "@com_google_googletest//:gtest_main",
+        "@com_google_riegeli//riegeli/bytes:string_reader",
+        "@com_google_riegeli//riegeli/bytes:string_writer",
     ],
 )
 
@@ -1301,6 +1324,7 @@
         ":stats",
         ":test_util",
         ":util",
+        "@com_google_absl//absl/log",
         "@com_google_absl//absl/log:log_entry",
         "@com_google_absl//absl/log:log_sink",
         "@com_google_absl//absl/log:log_sink_registry",
@@ -1482,12 +1506,16 @@
     name = "corpus_test",
     srcs = ["corpus_test.cc"],
     deps = [
+        ":binary_info",
+        ":call_graph",
         ":control_flow",
         ":corpus",
         ":defs",
         ":feature",
         ":feature_set",
+        ":pc_info",
         "@com_google_googletest//:gtest_main",
+        "@com_google_riegeli//riegeli/bytes:string_writer",
     ],
 )
 
@@ -1635,20 +1663,23 @@
     tags = ["not_run:arm"],
     deps = [
         ":binary_info",
-        ":centipede_interface",
+        ":centipede_callbacks",
         ":control_flow",
         ":coverage",
         ":defs",
         ":environment",
         ":feature",
-        ":logging",
+        ":mutation_input",
         ":pc_info",
         ":runner_result",
         ":symbol_table",
         ":test_util",
         ":util",
-        "@com_google_absl//absl/container:flat_hash_map",
+        "@com_google_absl//absl/container:flat_hash_set",
+        "@com_google_absl//absl/log:check",
         "@com_google_absl//absl/strings",
         "@com_google_googletest//:gtest_main",
+        "@com_google_riegeli//riegeli/bytes:string_reader",
+        "@com_google_riegeli//riegeli/bytes:string_writer",
     ],
 )
diff --git a/centipede/binary_info.cc b/centipede/binary_info.cc
index 95a6b08..8af9bd3 100644
--- a/centipede/binary_info.cc
+++ b/centipede/binary_info.cc
@@ -16,7 +16,6 @@
 
 #include <cstdlib>
 #include <filesystem>  // NOLINT
-#include <sstream>
 #include <string>
 #include <string_view>
 #include <vector>
@@ -27,9 +26,12 @@
 #include "absl/strings/str_split.h"
 #include "./centipede/command.h"
 #include "./centipede/control_flow.h"
+#include "./centipede/logging.h"
 #include "./centipede/pc_info.h"
 #include "./centipede/remote_file.h"
 #include "./centipede/util.h"
+#include "riegeli/bytes/reader_istream.h"
+#include "riegeli/bytes/writer_ostream.h"
 
 namespace centipede {
 
@@ -121,31 +123,25 @@
 }
 
 void BinaryInfo::Read(std::string_view dir) {
-  std::string symbol_table_contents;
   // TODO(b/295978603): move calculation of paths into WorkDir class.
-  RemoteFileGetContents(std::filesystem::path(dir).append(kSymbolTableFileName),
-                        symbol_table_contents);
-  std::istringstream symbol_table_stream(symbol_table_contents);
-  symbols.ReadFromLLVMSymbolizer(symbol_table_stream);
+  symbols.ReadFromLLVMSymbolizer(CreateRiegeliFileReader(
+      std::filesystem::path(dir).append(kSymbolTableFileName).native()));
 
-  std::string pc_table_contents;
-  RemoteFileGetContents(std::filesystem::path(dir).append(kPCTableFileName),
-                        pc_table_contents);
-  std::istringstream pc_table_stream(pc_table_contents);
+  riegeli::ReaderIStream pc_table_stream(CreateRiegeliFileReader(
+      std::filesystem::path(dir).append(kPCTableFileName).native()));
   pc_table = ReadPcTable(pc_table_stream);
+  CHECK(pc_table_stream.close()) << VV(pc_table_stream.status());
 }
 
 void BinaryInfo::Write(std::string_view dir) {
-  std::ostringstream symbol_table_stream;
-  symbols.WriteToLLVMSymbolizer(symbol_table_stream);
   // TODO(b/295978603): move calculation of paths into WorkDir class.
-  RemoteFileSetContents(std::filesystem::path(dir).append(kSymbolTableFileName),
-                        symbol_table_stream.str());
+  symbols.WriteToLLVMSymbolizer(CreateRiegeliFileWriter(
+      std::filesystem::path(dir).append(kSymbolTableFileName).native()));
 
-  std::ostringstream pc_table_stream;
+  riegeli::WriterOStream pc_table_stream(CreateRiegeliFileWriter(
+      std::filesystem::path(dir).append(kPCTableFileName).native()));
   WritePcTable(pc_table, pc_table_stream);
-  RemoteFileSetContents(std::filesystem::path(dir).append(kPCTableFileName),
-                        pc_table_stream.str());
+  CHECK(pc_table_stream.close()) << VV(pc_table_stream.status());
 }
 
 }  // namespace centipede
diff --git a/centipede/binary_info_test.cc b/centipede/binary_info_test.cc
index c0eba16..0ab6149 100644
--- a/centipede/binary_info_test.cc
+++ b/centipede/binary_info_test.cc
@@ -14,20 +14,20 @@
 
 #include "./centipede/binary_info.h"
 
-#include <sstream>
-#include <string>
+#include <string_view>
 
 #include "gtest/gtest.h"
 #include "./centipede/pc_info.h"
 #include "./centipede/symbol_table.h"
 #include "./centipede/test_util.h"
+#include "riegeli/bytes/string_reader.h"
 
 namespace centipede {
 namespace {
 
 TEST(BinaryInfoTest, SerializesAndDeserializesBinaryInfoSuccessfully) {
   PCTable input_pcs = {{.pc = 0, .flags = 1}, {.pc = 2, .flags = 3}};
-  std::string input_symbols =
+  const std::string_view input_symbols =
       R"(FunctionOne
     source/location/one.cc:1:0
 
@@ -35,9 +35,8 @@
     source/location/two.cc:2:0
 
 )";
-  std::istringstream input_stream(input_symbols);
   SymbolTable symbol_table;
-  symbol_table.ReadFromLLVMSymbolizer(input_stream);
+  symbol_table.ReadFromLLVMSymbolizer(riegeli::StringReader(input_symbols));
   BinaryInfo input = {.pc_table = input_pcs, .symbols = symbol_table};
 
   auto temp_dir = GetTestTempDir(test_info_->name());
diff --git a/centipede/blob_file.cc b/centipede/blob_file.cc
index fd9bbc2..6d2542e 100644
--- a/centipede/blob_file.cc
+++ b/centipede/blob_file.cc
@@ -16,7 +16,9 @@
 
 #include <cstddef>
 #include <memory>
+#include <string>
 #include <string_view>
+#include <utility>
 #include <vector>
 
 #include "absl/log/check.h"
@@ -28,6 +30,7 @@
 #include "./centipede/remote_file.h"
 #include "./centipede/util.h"
 #include "riegeli/base/object.h"
+#include "riegeli/bytes/read_all.h"
 #include "riegeli/bytes/reader.h"
 #include "riegeli/bytes/writer.h"
 #include "riegeli/records/record_reader.h"
@@ -47,7 +50,7 @@
 class SimpleBlobFileReader : public BlobFileReader {
  public:
   ~SimpleBlobFileReader() override {
-    if (file_ && !closed_) {
+    if (open_ && !closed_) {
       // Virtual resolution is off in dtors, so use a specific Close().
       CHECK_OK(SimpleBlobFileReader::Close());
     }
@@ -55,22 +58,23 @@
 
   absl::Status Open(std::string_view path) override {
     if (closed_) return absl::FailedPreconditionError("already closed");
-    if (file_) return absl::FailedPreconditionError("already open");
-    file_ = RemoteFileOpen(path, "r");
-    if (file_ == nullptr) return absl::UnknownError("can't open file");
+    if (open_) return absl::FailedPreconditionError("already open");
+    auto reader = CreateRiegeliFileReader(path);
+    if (!reader->ok()) return reader->status();
+    open_ = true;
     // Read the entire file at once.
     // It may be useful to read the file in chunks, but if we are going
     // to migrate to something else, it's not important here.
-    ByteArray raw_bytes;
-    RemoteFileRead(file_, raw_bytes);
-    RemoteFileClose(file_);  // close the file here, we won't need it.
-    UnpackBytesFromAppendFile(raw_bytes, &unpacked_blobs_);
+    std::string raw_bytes;
+    // close the file here, we won't need it.
+    absl::Status status = riegeli::ReadAll(std::move(reader), raw_bytes);
+    UnpackBytesFromAppendFile(AsByteSpan(raw_bytes), &unpacked_blobs_);
     return absl::OkStatus();
   }
 
   absl::Status Read(ByteSpan &blob) override {
     if (closed_) return absl::FailedPreconditionError("already closed");
-    if (!file_) return absl::FailedPreconditionError("was not open");
+    if (!open_) return absl::FailedPreconditionError("was not open");
     if (next_to_read_blob_index_ == unpacked_blobs_.size())
       return absl::OutOfRangeError("no more blobs");
     if (next_to_read_blob_index_ != 0)  // Clear the previous blob to save RAM.
@@ -83,14 +87,14 @@
   // Closes the file (it must be open).
   absl::Status Close() override {
     if (closed_) return absl::FailedPreconditionError("already closed");
-    if (!file_) return absl::FailedPreconditionError("was not open");
+    if (!open_) return absl::FailedPreconditionError("was not open");
     closed_ = true;
     // Nothing to do here, we've already closed the underlying file (in Open()).
     return absl::OkStatus();
   }
 
  private:
-  RemoteFile *file_ = nullptr;
+  bool open_ = false;
   bool closed_ = false;
   std::vector<ByteArray> unpacked_blobs_;
   size_t next_to_read_blob_index_ = 0;
@@ -100,7 +104,7 @@
 class SimpleBlobFileWriter : public BlobFileWriter {
  public:
   ~SimpleBlobFileWriter() override {
-    if (file_ && !closed_) {
+    if (writer_ && !closed_) {
       // Virtual resolution is off in dtors, so use a specific Close().
       CHECK_OK(SimpleBlobFileWriter::Close());
     }
@@ -109,34 +113,31 @@
   absl::Status Open(std::string_view path, std::string_view mode) override {
     CHECK(mode == "w" || mode == "a") << VV(mode);
     if (closed_) return absl::FailedPreconditionError("already closed");
-    if (file_) return absl::FailedPreconditionError("already open");
-    file_ = RemoteFileOpen(path, mode.data());
-    if (file_ == nullptr) return absl::UnknownError("can't open file");
+    if (writer_) return absl::FailedPreconditionError("already open");
+    writer_ = CreateRiegeliFileWriter(path, mode == "a");
+    if (!writer_->ok()) return std::exchange(writer_, nullptr)->status();
     return absl::OkStatus();
   }
 
   absl::Status Write(ByteSpan blob) override {
     if (closed_) return absl::FailedPreconditionError("already closed");
-    if (!file_) return absl::FailedPreconditionError("was not open");
-    // TODO(kcc): [as-needed] This copy from a span to vector is clumsy. Change
-    //  RemoteFileAppend to accept a span.
-    ByteArray bytes(blob.begin(), blob.end());
-    ByteArray packed = PackBytesForAppendFile(bytes);
-    RemoteFileAppend(file_, packed);
-
+    if (!writer_) return absl::FailedPreconditionError("was not open");
+    if (!writer_->Write(AsStringView(PackBytesForAppendFile(blob)))) {
+      return writer_->status();
+    }
     return absl::OkStatus();
   }
 
   absl::Status Close() override {
     if (closed_) return absl::FailedPreconditionError("already closed");
-    if (!file_) return absl::FailedPreconditionError("was not open");
+    if (!writer_) return absl::FailedPreconditionError("was not open");
     closed_ = true;
-    RemoteFileClose(file_);
+    if (!writer_->Close()) return writer_->status();
     return absl::OkStatus();
   }
 
  private:
-  RemoteFile *file_ = nullptr;
+  std::unique_ptr<riegeli::Writer> writer_;
   bool closed_ = false;
 };
 
diff --git a/centipede/centipede.cc b/centipede/centipede.cc
index 685e8c3..53943c3 100644
--- a/centipede/centipede.cc
+++ b/centipede/centipede.cc
@@ -47,7 +47,7 @@
 #include <cmath>
 #include <cstddef>
 #include <cstdlib>
-#include <filesystem>
+#include <filesystem>  // NOLINT
 #include <functional>
 #include <iostream>
 #include <memory>
@@ -55,6 +55,7 @@
 #include <sstream>
 #include <string>
 #include <string_view>
+#include <utility>
 #include <vector>
 
 #include "absl/base/attributes.h"
@@ -91,6 +92,9 @@
 #include "./centipede/stats.h"
 #include "./centipede/util.h"
 #include "./centipede/workdir.h"
+#include "riegeli/bytes/read_all.h"
+#include "riegeli/bytes/write.h"
+#include "riegeli/bytes/writer.h"
 
 namespace centipede {
 
@@ -178,19 +182,18 @@
     auto appender = DefaultBlobFileWriterFactory(env.riegeli);
     CHECK_OK(appender->Open(corpus_path, "a"))
         << "Failed to open corpus file: " << corpus_path;
-    ByteArray shard_data;
     for (const auto &path : sharded_paths[shard]) {
       std::string input;
-      RemoteFileGetContents(path, input);
+      CHECK_OK(riegeli::ReadAll(CreateRiegeliFileReader(path), input));
       if (input.empty() || existing_hashes.contains(Hash(input))) {
         ++inputs_ignored;
         continue;
       }
-      CHECK_OK(appender->Write(ByteArray{input.begin(), input.end()}));
+      CHECK_OK(appender->Write(AsByteSpan(input)));
       ++inputs_added;
     }
     LOG(INFO) << VV(shard) << VV(inputs_added) << VV(inputs_ignored)
-              << VV(num_shard_bytes) << VV(shard_data.size());
+              << VV(num_shard_bytes);
   }
   CHECK_EQ(total_paths, inputs_added + inputs_ignored);
 }
@@ -459,10 +462,9 @@
             << VV(coverage_path);
   auto pci_vec = fs_.ToCoveragePCs();
   Coverage coverage(pc_table_, pci_vec);
-  std::stringstream out;
-  out << "# " << description << ":\n\n";
-  coverage.Print(symbols_, out);
-  RemoteFileSetContents(coverage_path, out.str());
+  auto out = CreateRiegeliFileWriter(coverage_path);
+  out->Write("# ", description, ":\n\n");
+  coverage.Print(symbols_, std::move(out));
 }
 
 void Centipede::GenerateCorpusStats(std::string_view filename_annotation,
@@ -470,10 +472,9 @@
   auto stats_path = wd_.CorpusStatsPath(filename_annotation);
   LOG(INFO) << "Generate corpus stats: " << description << " "
             << VV(stats_path);
-  std::ostringstream os;
-  os << "# " << description << ":\n\n";
-  corpus_.PrintStats(os, fs_);
-  RemoteFileSetContents(stats_path, os.str());
+  auto out = CreateRiegeliFileWriter(stats_path);
+  out->Write("# ", description, ":\n\n");
+  corpus_.PrintStats(fs_, std::move(out));
 }
 
 // TODO(nedwill): add integration test once tests are refactored per b/255660879
@@ -525,19 +526,19 @@
   class ReportDumper : public RUsageProfiler::ReportSink {
    public:
     explicit ReportDumper(std::string_view path)
-        : file_{RemoteFileOpen(path, "w")} {
-      CHECK(file_ != nullptr) << VV(path);
+        : out_(CreateRiegeliFileWriter(path)) {
+      CHECK(out_->ok()) << VV(out_->status());
     }
 
-    ~ReportDumper() override { RemoteFileClose(file_); }
+    ~ReportDumper() override { CHECK(out_->Close()) << VV(out_->status()); }
 
-    ReportDumper &operator<<(const std::string &fragment) override {
-      RemoteFileAppend(file_, ByteArray{fragment.cbegin(), fragment.cend()});
+    ReportDumper &operator<<(std::string_view fragment) override {
+      CHECK(out_->Write(fragment)) << VV(out_->status());
       return *this;
     }
 
    private:
-    RemoteFile *file_;
+    std::unique_ptr<riegeli::Writer> out_;
   };
 
   const auto &snapshot = rusage_profiler_.TakeSnapshot(
@@ -818,10 +819,9 @@
                 << "\nFailure        : "
                 << one_input_batch_result.failure_description()
                 << "\nSaving input to: " << file_path;
-      auto *file = RemoteFileOpen(file_path, "w");  // overwrites existing file.
-      CHECK(file != nullptr) << log_prefix << "Failed to open " << file_path;
-      RemoteFileAppend(file, one_input);
-      RemoteFileClose(file);
+      CHECK_OK(riegeli::Write(AsStringView(one_input),
+                              CreateRiegeliFileWriter(file_path)))
+          << log_prefix;
       return;
     }
   }
@@ -850,10 +850,9 @@
     auto hash = Hash(one_input);
     std::string file_path = std::filesystem::path(save_dir).append(
         absl::StrFormat("input-%010d-%s", i, hash));
-    auto *file = RemoteFileOpen(file_path, "w");
-    CHECK(file != nullptr) << log_prefix << "Failed to open " << file_path;
-    RemoteFileAppend(file, one_input);
-    RemoteFileClose(file);
+    CHECK_OK(riegeli::Write(AsStringView(one_input),
+                            CreateRiegeliFileWriter(file_path)))
+        << log_prefix;
   }
 }
 
diff --git a/centipede/config_file.cc b/centipede/config_file.cc
index 01fa635..58a5a33 100644
--- a/centipede/config_file.cc
+++ b/centipede/config_file.cc
@@ -37,6 +37,9 @@
 #include "./centipede/logging.h"
 #include "./centipede/remote_file.h"
 #include "./centipede/util.h"
+#include "riegeli/bytes/fd_writer.h"
+#include "riegeli/bytes/read_all.h"
+#include "riegeli/bytes/write.h"
 
 // TODO(ussuri): Move these flags next to main() ASAP. They are here
 //  only temporarily to simplify the APIs and implementation in V1.
@@ -143,7 +146,8 @@
   if (!path.empty() && !std::filesystem::exists(path)) {  // assume remote
     // Read the remote file.
     std::string contents;
-    RemoteFileGetContents(path, contents);
+    CHECK_OK(
+        riegeli::ReadAll(CreateRiegeliFileReader(path.native()), contents));
 
     // Save a temporary local copy.
     const std::filesystem::path tmp_dir = TemporaryLocalDirPath();
@@ -151,7 +155,7 @@
     LOG(INFO) << "Localizing remote config: " << VV(path) << VV(local_path);
     // NOTE: Ignore "Remote" in the API names here: the paths are always local.
     RemoteMkdir(tmp_dir.c_str());
-    RemoteFileSetContents(local_path, contents);
+    CHECK_OK(riegeli::Write(contents, riegeli::FdWriter(local_path.native())));
 
     // Augment the argv to point at the local copy and ensure it is cleaned up.
     replacements.emplace_back(path.c_str(), local_path.c_str());
@@ -224,7 +228,8 @@
     } else {
       file_contents = flags_str;
     }
-    RemoteFileSetContents(path, file_contents);
+    CHECK_OK(
+        riegeli::Write(file_contents, CreateRiegeliFileWriter(path.native())));
   }
 
   return path;
diff --git a/centipede/corpus.cc b/centipede/corpus.cc
index 0dd532a..9bb46a3 100644
--- a/centipede/corpus.cc
+++ b/centipede/corpus.cc
@@ -17,8 +17,8 @@
 #include <algorithm>
 #include <cstddef>
 #include <cstdint>
-#include <ostream>
 #include <string>
+#include <string_view>
 #include <utility>
 #include <vector>
 
@@ -33,6 +33,8 @@
 #include "./centipede/feature_set.h"
 #include "./centipede/logging.h"  // IWYU pragma: keep
 #include "./centipede/util.h"
+#include "riegeli/base/any_dependency.h"
+#include "riegeli/bytes/writer.h"
 
 namespace centipede {
 
@@ -130,25 +132,27 @@
   return records_[random % records_.size()];
 }
 
-void Corpus::PrintStats(std::ostream &out, const FeatureSet &fs) {
-  out << "{\n";
-  out << "  \"num_inputs\": " << records_.size() << ",\n";
-  out << "  \"corpus_stats\": [\n";
-  std::string before_record;
+void Corpus::PrintStats(const FeatureSet &fs,
+                        riegeli::AnyDependencyRef<riegeli::Writer *> out) {
+  out->Write("{\n");
+  out->Write("  \"num_inputs\": ", records_.size(), ",\n");
+  out->Write("  \"corpus_stats\": [\n");
+  std::string_view before_record;
   for (const auto &record : records_) {
-    out << before_record;
+    out->Write(before_record);
     before_record = ",\n";
-    out << "    {\"size\": " << record.data.size() << ", ";
-    out << "\"frequencies\": [";
-    std::string before_feature;
+    out->Write("    {\"size\": ", record.data.size(), ", ");
+    out->Write("\"frequencies\": [");
+    std::string_view before_feature;
     for (const auto feature : record.features) {
-      out << before_feature;
+      out->Write(before_feature);
       before_feature = ", ";
-      out << fs.Frequency(feature);
+      out->Write(fs.Frequency(feature));
     }
-    out << "]}";
+    out->Write("]}");
   }
-  out << "\n  ]\n}\n";
+  out->Write("\n  ]\n}\n");
+  if (out.is_owning()) CHECK(out->Close()) << VV(out->status());
 }
 
 std::string Corpus::MemoryUsageString() const {
diff --git a/centipede/corpus.h b/centipede/corpus.h
index 6d81369..5375b96 100644
--- a/centipede/corpus.h
+++ b/centipede/corpus.h
@@ -17,18 +17,19 @@
 
 #include <cstddef>
 #include <cstdint>
-#include <ostream>
 #include <string>
 #include <utility>
 #include <vector>
 
+#include "absl/log/check.h"
 #include "./centipede/binary_info.h"
-#include "./centipede/control_flow.h"
 #include "./centipede/defs.h"
 #include "./centipede/execution_metadata.h"
 #include "./centipede/feature.h"
 #include "./centipede/feature_set.h"
 #include "./centipede/util.h"
+#include "riegeli/base/any_dependency.h"
+#include "riegeli/bytes/writer.h"
 
 namespace centipede {
 
@@ -141,7 +142,8 @@
   // Logging.
 
   // Prints corpus stats in JSON format to `out` using `fs` for frequencies.
-  void PrintStats(std::ostream &out, const FeatureSet &fs);
+  void PrintStats(const FeatureSet &fs,
+                  riegeli::AnyDependencyRef<riegeli::Writer *> out);
   // Returns a string used for logging the corpus memory usage.
   std::string MemoryUsageString() const;
 
diff --git a/centipede/corpus_test.cc b/centipede/corpus_test.cc
index a1bbcdc..7231044 100644
--- a/centipede/corpus_test.cc
+++ b/centipede/corpus_test.cc
@@ -17,15 +17,18 @@
 #include <algorithm>
 #include <cstddef>
 #include <cstdint>
-#include <sstream>
 #include <string>
 #include <vector>
 
 #include "gtest/gtest.h"
+#include "./centipede/binary_info.h"
+#include "./centipede/call_graph.h"
 #include "./centipede/control_flow.h"
 #include "./centipede/defs.h"
 #include "./centipede/feature.h"
 #include "./centipede/feature_set.h"
+#include "./centipede/pc_info.h"
+#include "riegeli/bytes/string_writer.h"
 
 namespace centipede {
 namespace {
@@ -59,9 +62,9 @@
   corpus.Add({1, 2, 3}, features1, {}, fs, coverage_frontier);
   fs.IncrementFrequencies(features2);
   corpus.Add({4, 5}, features2, {}, fs, coverage_frontier);
-  std::ostringstream os;
-  corpus.PrintStats(os, fs);
-  EXPECT_EQ(os.str(),
+  std::string str;
+  corpus.PrintStats(fs, riegeli::StringWriter(&str));
+  EXPECT_EQ(str,
             R"({
   "num_inputs": 2,
   "corpus_stats": [
diff --git a/centipede/coverage.cc b/centipede/coverage.cc
index cd02294..17ea919 100644
--- a/centipede/coverage.cc
+++ b/centipede/coverage.cc
@@ -17,17 +17,24 @@
 #include <string.h>
 
 #include <cstdint>
-#include <filesystem>
 #include <limits>
+#include <string>
 #include <string_view>
+#include <utility>
+#include <vector>
 
 #include "absl/container/flat_hash_set.h"
+#include "absl/log/check.h"
 #include "absl/strings/str_split.h"
 #include "absl/synchronization/mutex.h"
-#include "./centipede/defs.h"
+#include "./centipede/control_flow.h"
+#include "./centipede/feature.h"
 #include "./centipede/logging.h"
+#include "./centipede/pc_info.h"
 #include "./centipede/symbol_table.h"
-#include "./centipede/util.h"
+#include "riegeli/base/any_dependency.h"
+#include "riegeli/bytes/string_writer.h"
+#include "riegeli/bytes/writer.h"
 
 namespace centipede {
 
@@ -82,26 +89,28 @@
   }
 }
 
-void Coverage::Print(const SymbolTable &symbols, std::ostream &out) {
+void Coverage::Print(const SymbolTable &symbols,
+                     riegeli::AnyDependencyRef<riegeli::Writer *> out) {
   // Print symbolized function names for all covered functions.
   for (auto pc_index : fully_covered_funcs) {
-    out << "FULL: " << symbols.full_description(pc_index) << "\n";
+    out->Write("FULL: ", symbols.full_description(pc_index), '\n');
   }
   // Same for uncovered functions.
   for (auto pc_index : uncovered_funcs) {
-    out << "NONE: " << symbols.full_description(pc_index) << "\n";
+    out->Write("NONE: ", symbols.full_description(pc_index), '\n');
   }
   // For every partially covered function, first print its name,
   // then print its covered edges, then uncovered edges.
   for (auto &pcf : partially_covered_funcs) {
-    out << "PARTIAL: " << symbols.full_description(pcf.covered[0]) << "\n";
+    out->Write("PARTIAL: ", symbols.full_description(pcf.covered[0]), '\n');
     for (auto pc_index : pcf.covered) {
-      out << "  + " << symbols.full_description(pc_index) << "\n";
+      out->Write("  + ", symbols.full_description(pc_index), '\n');
     }
     for (auto pc_index : pcf.uncovered) {
-      out << "  - " << symbols.full_description(pc_index) << "\n";
+      out->Write("  - ", symbols.full_description(pc_index), '\n');
     }
   }
+  if (out.is_owning()) CHECK(out->Close()) << VV(out->status());
 }
 
 //---------------------- NewCoverageLogger
@@ -109,16 +118,18 @@
   if (pc_table_.empty()) return "";  // Fast-path return (symbolization is off).
   absl::MutexLock l(&mu_);
   if (!observed_indices_.insert(pc_index).second) return "";
-  std::ostringstream os;
+  riegeli::StringWriter out;
   if (pc_index >= pc_table_.size()) {
-    os << "FUNC/EDGE index: " << pc_index;
+    out.Write("FUNC/EDGE index: ", pc_index);
+    out.Close();
   } else {
-    os << (pc_table_[pc_index].has_flag(PCInfo::kFuncEntry) ? "FUNC: "
-                                                            : "EDGE: ");
-    os << symbols_.full_description(pc_index);
-    if (!observed_descriptions_.insert(os.str()).second) return "";
+    out.Write(
+        pc_table_[pc_index].has_flag(PCInfo::kFuncEntry) ? "FUNC: " : "EDGE: ",
+        symbols_.full_description(pc_index));
+    out.Close();
+    if (!observed_descriptions_.insert(out.dest()).second) return "";
   }
-  return os.str();
+  return std::move(out.dest());
 }
 
 FunctionFilter::FunctionFilter(std::string_view functions_to_filter,
diff --git a/centipede/coverage.h b/centipede/coverage.h
index 9c04e7d..fbf2c42 100644
--- a/centipede/coverage.h
+++ b/centipede/coverage.h
@@ -19,18 +19,19 @@
 
 #include <algorithm>
 #include <cstdint>
-#include <ostream>
 #include <string>
 #include <string_view>
 #include <vector>
 
 #include "absl/base/thread_annotations.h"
-#include "absl/container/flat_hash_map.h"
 #include "absl/container/flat_hash_set.h"
+#include "absl/log/check.h"
 #include "absl/synchronization/mutex.h"
 #include "./centipede/control_flow.h"
 #include "./centipede/feature.h"
-#include "./centipede/logging.h"
+#include "./centipede/pc_info.h"
+#include "riegeli/base/any_dependency.h"
+#include "riegeli/bytes/writer.h"
 
 namespace centipede {
 
@@ -48,7 +49,8 @@
            const PCIndexVec &pci_vec);
 
   // Prints in human-readable form to `out` using `symbols`.
-  void Print(const SymbolTable &symbols, std::ostream &out);
+  void Print(const SymbolTable &symbols,
+             riegeli::AnyDependencyRef<riegeli::Writer *> out);
 
   // Returns true if the function is fully covered. pc_index is for a function
   // entry.
diff --git a/centipede/coverage_test.cc b/centipede/coverage_test.cc
index 1984359..3b6cbe6 100644
--- a/centipede/coverage_test.cc
+++ b/centipede/coverage_test.cc
@@ -17,11 +17,10 @@
 #include <stdio.h>
 #include <unistd.h>
 
-#include <algorithm>
 #include <cstddef>
 #include <cstdint>
 #include <cstdlib>
-#include <filesystem>
+#include <filesystem>  // NOLINT
 #include <iostream>
 #include <string>
 #include <string_view>
@@ -30,20 +29,23 @@
 
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
-#include "absl/container/flat_hash_map.h"
+#include "absl/container/flat_hash_set.h"
+#include "absl/log/check.h"
 #include "absl/strings/str_cat.h"
 #include "./centipede/binary_info.h"
-#include "./centipede/centipede_interface.h"
+#include "./centipede/centipede_callbacks.h"
 #include "./centipede/control_flow.h"
 #include "./centipede/defs.h"
 #include "./centipede/environment.h"
 #include "./centipede/feature.h"
-#include "./centipede/logging.h"
+#include "./centipede/mutation_input.h"
 #include "./centipede/pc_info.h"
 #include "./centipede/runner_result.h"
 #include "./centipede/symbol_table.h"
 #include "./centipede/test_util.h"
 #include "./centipede/util.h"
+#include "riegeli/bytes/string_reader.h"
+#include "riegeli/bytes/string_writer.h"
 
 namespace centipede {
 namespace {
@@ -52,7 +54,7 @@
 // A, BB, CCC.
 // A and BB have one control flow edge each.
 // CCC has 3 edges.
-const char *symbolizer_output =
+constexpr std::string_view symbolizer_output =
     "A\n"
     "a.cc:1:0\n"
     "\n"
@@ -86,8 +88,7 @@
 TEST(Coverage, SymbolTable) {
   // Initialize and test SymbolTable.
   SymbolTable symbols;
-  std::istringstream iss(symbolizer_output);
-  symbols.ReadFromLLVMSymbolizer(iss);
+  symbols.ReadFromLLVMSymbolizer(riegeli::StringReader(symbolizer_output));
   EXPECT_EQ(symbols.size(), 6U);
   EXPECT_EQ(symbols.func(1), "BB");
   EXPECT_EQ(symbols.location(2), "ccc.cc:1:0");
@@ -98,10 +99,9 @@
     // Tests coverage output for PCIndexVec = {0, 2},
     // i.e. the covered edges are 'A' and the entry of 'CCC'.
     Coverage cov(g_pc_table, {0, 2});
-    cov.Print(symbols, std::cout);
-    std::ostringstream os;
-    cov.Print(symbols, os);
-    std::string str = os.str();
+    std::string str;
+    cov.Print(symbols, riegeli::StringWriter(&str));
+    std::cout << str;
     EXPECT_THAT(str, testing::HasSubstr("FULL: A a.cc:1:0"));
     EXPECT_THAT(str, testing::HasSubstr("NONE: BB bb.cc:1:0"));
     EXPECT_THAT(str, testing::HasSubstr("PARTIAL: CCC ccc.cc:1:0"));
@@ -112,9 +112,8 @@
   {
     // Same as above, but for PCIndexVec = {1, 2, 3},
     Coverage cov(g_pc_table, {1, 2, 3});
-    std::ostringstream os;
-    cov.Print(symbols, os);
-    std::string str = os.str();
+    std::string str;
+    cov.Print(symbols, riegeli::StringWriter(&str));
     EXPECT_THAT(str, testing::HasSubstr("FULL: BB bb.cc:1:0"));
     EXPECT_THAT(str, testing::HasSubstr("NONE: A a.cc:1:0"));
     EXPECT_THAT(str, testing::HasSubstr("PARTIAL: CCC ccc.cc:1:0"));
@@ -146,8 +145,7 @@
 
 TEST(Coverage, CoverageLogger) {
   SymbolTable symbols;
-  std::istringstream iss(symbolizer_output);
-  symbols.ReadFromLLVMSymbolizer(iss);
+  symbols.ReadFromLLVMSymbolizer(riegeli::StringReader(symbolizer_output));
   CoverageLogger logger(g_pc_table, symbols);
   // First time logging pc_index=0.
   EXPECT_EQ(logger.ObserveAndDescribeIfNew(0), "FUNC: A a.cc:1:0");
diff --git a/centipede/environment.cc b/centipede/environment.cc
index ad6f2f9..bb9fe74 100644
--- a/centipede/environment.cc
+++ b/centipede/environment.cc
@@ -20,6 +20,7 @@
 #include <cstddef>
 #include <cstdint>
 #include <string>
+#include <system_error>  // NOLINT
 #include <vector>
 
 #include "absl/container/flat_hash_map.h"
@@ -32,6 +33,7 @@
 #include "./centipede/knobs.h"
 #include "./centipede/logging.h"
 #include "./centipede/remote_file.h"
+#include "riegeli/bytes/read_all.h"
 
 namespace centipede {
 
@@ -189,14 +191,11 @@
 void Environment::ReadKnobsFileIfSpecified() {
   const std::string_view knobs_file_path = knobs_file;
   if (knobs_file_path.empty()) return;
-  ByteArray knob_bytes;
-  auto *f = RemoteFileOpen(knobs_file, "r");
-  CHECK(f) << "Failed to open remote file " << knobs_file;
-  RemoteFileRead(f, knob_bytes);
-  RemoteFileClose(f);
+  std::string knob_bytes;
+  CHECK_OK(riegeli::ReadAll(CreateRiegeliFileReader(knobs_file), knob_bytes));
   VLOG(1) << "Knobs: " << knob_bytes.size() << " knobs read from "
           << knobs_file;
-  knobs.Set(knob_bytes);
+  knobs.Set(AsByteSpan(knob_bytes));
   knobs.ForEachKnob([](std::string_view name, Knobs::value_type value) {
     VLOG(1) << "knob " << name << ": " << static_cast<uint32_t>(value);
   });
diff --git a/centipede/pc_info.cc b/centipede/pc_info.cc
index 7e06e93..3567917 100644
--- a/centipede/pc_info.cc
+++ b/centipede/pc_info.cc
@@ -14,40 +14,27 @@
 
 #include "./centipede/pc_info.h"
 
-#include <cstddef>
+#include <ios>
 #include <istream>
-#include <iterator>
 #include <ostream>
-#include <string>
 
 #include "absl/log/check.h"
-#include "absl/types/span.h"
-#include "./centipede/defs.h"
 
 namespace centipede {
 
-bool PCInfo::operator==(const PCInfo &rhs) const {
-  return this->pc == rhs.pc && this->flags == rhs.flags;
-}
-
 PCTable ReadPcTable(std::istream &in) {
-  std::string input_string(std::istreambuf_iterator<char>(in), {});
-
-  ByteArray pc_infos_as_bytes(input_string.begin(), input_string.end());
-  CHECK_EQ(pc_infos_as_bytes.size() % sizeof(PCInfo), 0);
-  size_t pc_table_size = pc_infos_as_bytes.size() / sizeof(PCInfo);
-  const auto *pc_infos = reinterpret_cast<PCInfo *>(pc_infos_as_bytes.data());
-  PCTable pc_table{pc_infos, pc_infos + pc_table_size};
-  CHECK_EQ(pc_table.size(), pc_table_size);
-
+  in.seekg(0, std::ios_base::end);
+  auto size = in.tellg();
+  in.seekg(0, std::ios_base::beg);
+  CHECK_EQ(size % sizeof(PCInfo), 0);
+  PCTable pc_table(size / sizeof(PCInfo));
+  in.read(reinterpret_cast<char *>(pc_table.data()), size);
   return pc_table;
 }
 
 void WritePcTable(const PCTable &pc_table, std::ostream &out) {
-  auto pc_infos_as_bytes =
-      absl::Span<const char>(reinterpret_cast<const char *>(pc_table.data()),
-                             sizeof(PCInfo) * pc_table.size());
-  out.write(pc_infos_as_bytes.data(), pc_infos_as_bytes.size());
+  out.write(reinterpret_cast<const char *>(pc_table.data()),
+            pc_table.size() * sizeof(PCInfo));
 }
 
 }  // namespace centipede
diff --git a/centipede/pc_info.h b/centipede/pc_info.h
index de747fa..a1607f5 100644
--- a/centipede/pc_info.h
+++ b/centipede/pc_info.h
@@ -38,7 +38,9 @@
 
   bool has_flag(PCFlags f) const { return flags & f; }
 
-  bool operator==(const PCInfo &rhs) const;
+  friend bool operator==(const PCInfo &lhs, const PCInfo &rhs) {
+    return lhs.pc == rhs.pc && lhs.flags == rhs.flags;
+  }
 };
 
 // Array of PCInfo-s.
diff --git a/centipede/remote_file.cc b/centipede/remote_file.cc
index 825b5fe..a74c4df 100644
--- a/centipede/remote_file.cc
+++ b/centipede/remote_file.cc
@@ -19,7 +19,6 @@
 
 #include <glob.h>
 
-#include <cstdio>
 #include <filesystem>  // NOLINT
 #include <memory>
 #include <string>
@@ -30,7 +29,6 @@
 #include "absl/base/attributes.h"
 #include "absl/log/check.h"
 #include "absl/log/log.h"
-#include "./centipede/defs.h"
 #include "./centipede/logging.h"
 #include "riegeli/bytes/fd_reader.h"
 #include "riegeli/bytes/fd_writer.h"
@@ -52,79 +50,6 @@
   CHECK(!error) << VV(path) << VV(error);
 }
 
-ABSL_ATTRIBUTE_WEAK RemoteFile *RemoteFileOpen(std::string_view path,
-                                               const char *mode) {
-  CHECK(!path.empty());
-  FILE *f = std::fopen(path.data(), mode);
-  return reinterpret_cast<RemoteFile *>(f);
-}
-
-ABSL_ATTRIBUTE_WEAK void RemoteFileClose(RemoteFile *f) {
-  CHECK(f != nullptr);
-  std::fclose(reinterpret_cast<FILE *>(f));
-}
-
-ABSL_ATTRIBUTE_WEAK void RemoteFileAppend(RemoteFile *f, const ByteArray &ba) {
-  CHECK(f != nullptr);
-  auto *file = reinterpret_cast<FILE *>(f);
-  constexpr auto elt_size = sizeof(ba[0]);
-  const auto elts_to_write = ba.size();
-  const auto elts_written =
-      std::fwrite(ba.data(), elt_size, elts_to_write, file);
-  CHECK_EQ(elts_written, elts_to_write);
-}
-
-// Does not need weak attribute as the implementation depends on
-// RemoteFileAppend(RemoteFile *, ByteArray).
-void RemoteFileAppend(RemoteFile *f, const std::string &contents) {
-  CHECK(f != nullptr);
-  ByteArray contents_ba{contents.cbegin(), contents.cend()};
-  RemoteFileAppend(f, contents_ba);
-}
-
-ABSL_ATTRIBUTE_WEAK void RemoteFileRead(RemoteFile *f, ByteArray &ba) {
-  CHECK(f != nullptr);
-  auto *file = reinterpret_cast<FILE *>(f);
-  std::fseek(file, 0, SEEK_END);  // seek to end
-  const auto file_size = std::ftell(file);
-  std::fseek(file, 0, SEEK_SET);  // seek back to start
-  constexpr auto elt_size = sizeof(ba[0]);
-  CHECK_EQ(file_size % elt_size, 0) << VV(file_size) << VV(elt_size);
-  const auto elts_to_read = file_size / elt_size;
-  ba.resize(elts_to_read);
-  const auto elts_read = std::fread(ba.data(), elt_size, elts_to_read, file);
-  CHECK_EQ(elts_read, elts_to_read);
-}
-
-// Does not need weak attribute as the implementation depends on
-// RemoteFileRead(RemoteFile *, ByteArray).
-void RemoteFileRead(RemoteFile *f, std::string &contents) {
-  CHECK(f != nullptr);
-  ByteArray contents_ba;
-  RemoteFileRead(f, contents_ba);
-  contents.assign(contents_ba.cbegin(), contents_ba.cend());
-}
-
-// Does not need weak attribute as the implementation depends on
-// RemoteFileAppend(RemoteFile *, std::string).
-void RemoteFileSetContents(const std::filesystem::path &path,
-                           const std::string &contents) {
-  auto *file = RemoteFileOpen(path.c_str(), "w");
-  CHECK(file != nullptr) << VV(path);
-  RemoteFileAppend(file, contents);
-  RemoteFileClose(file);
-}
-
-// Does not need weak attribute as the implementation depends on
-// RemoteFileRead(RemoteFile *, std::string).
-void RemoteFileGetContents(const std::filesystem::path &path,
-                           std::string &contents) {
-  auto *file = RemoteFileOpen(path.c_str(), "r");
-  CHECK(file != nullptr) << VV(path);
-  RemoteFileRead(file, contents);
-  RemoteFileClose(file);
-}
-
 ABSL_ATTRIBUTE_WEAK bool RemotePathExists(std::string_view path) {
   return std::filesystem::exists(path);
 }
diff --git a/centipede/remote_file.h b/centipede/remote_file.h
index a400174..979d876 100644
--- a/centipede/remote_file.h
+++ b/centipede/remote_file.h
@@ -22,51 +22,20 @@
 #ifndef THIRD_PARTY_CENTIPEDE_REMOTE_FILE_H_
 #define THIRD_PARTY_CENTIPEDE_REMOTE_FILE_H_
 
-#include <filesystem>  // NOLINT
+#include <memory>
 #include <string>
 #include <string_view>
 #include <vector>
 
-#include "./centipede/defs.h"
 #include "riegeli/bytes/reader.h"
 #include "riegeli/bytes/writer.h"
 
 namespace centipede {
 
-// An opaque file handle.
-struct RemoteFile {};
-
-// Opens a (potentially remote) file 'file_path' and returns a handle to it.
-// Supported modes: "r", "a", "w", same as in C FILE API.
-RemoteFile *RemoteFileOpen(std::string_view file_path, const char *mode);
-
-// Closes the file previously opened by RemoteFileOpen.
-void RemoteFileClose(RemoteFile *f);
-
-// Appends bytes from 'ba' to 'f'.
-void RemoteFileAppend(RemoteFile *f, const ByteArray &ba);
-
-// Appends characters from 'contents' to 'f'.
-void RemoteFileAppend(RemoteFile *f, const std::string &contents);
-
-// Reads all current contents of 'f' into 'ba'.
-void RemoteFileRead(RemoteFile *f, ByteArray &ba);
-
-// Reads all current contents of 'f' into 'contents'.
-void RemoteFileRead(RemoteFile *f, std::string &contents);
-
 // Creates a (potentially remote) directory 'dir_path'.
 // No-op if the directory already exists.
 void RemoteMkdir(std::string_view dir_path);
 
-// Sets the contents of the file at 'path' to 'contents'.
-void RemoteFileSetContents(const std::filesystem::path &path,
-                           const std::string &contents);
-
-// Reads the contents of the file at 'path' into 'contents'.
-void RemoteFileGetContents(const std::filesystem::path &path,
-                           std::string &contents);
-
 // Returns true if `path` exists.
 bool RemotePathExists(std::string_view path);
 
@@ -86,7 +55,7 @@
 // If `append` is `true`, writes will append to the end of the file if it
 // exists. If `false, the file will be truncated to empty if it exists.
 std::unique_ptr<riegeli::Writer> CreateRiegeliFileWriter(
-    std::string_view file_path, bool append);
+    std::string_view file_path, bool append = false);
 
 }  // namespace centipede
 
diff --git a/centipede/rusage_profiler.cc b/centipede/rusage_profiler.cc
index 2d718ba..69b8748 100644
--- a/centipede/rusage_profiler.cc
+++ b/centipede/rusage_profiler.cc
@@ -14,6 +14,7 @@
 
 #include "./centipede/rusage_profiler.h"
 
+#include <algorithm>
 #include <atomic>
 #include <cmath>
 #include <cstdint>
@@ -22,6 +23,7 @@
 #include <memory>
 #include <ostream>
 #include <string>
+#include <string_view>
 #include <thread>  // NOLINT
 #include <utility>
 
@@ -504,9 +506,9 @@
       }
     }
 
-    ReportLogger& operator<<(const std::string& fragment) override {
+    ReportLogger& operator<<(std::string_view fragment) override {
       const auto last_newline = fragment.rfind('\n');
-      if (last_newline == std::string::npos) {
+      if (last_newline == std::string_view::npos) {
         // Accumulate no-'\n' fragments: LOG() always wraps around.
         buffer_ += fragment;
       } else {
diff --git a/centipede/rusage_profiler.h b/centipede/rusage_profiler.h
index 2f1e2a8..e21b81f 100644
--- a/centipede/rusage_profiler.h
+++ b/centipede/rusage_profiler.h
@@ -213,7 +213,9 @@
 #include <memory>
 #include <ostream>
 #include <string>
+#include <string_view>
 
+#include "absl/base/thread_annotations.h"
 #include "absl/synchronization/mutex.h"
 #include "absl/time/time.h"
 #include "./centipede/rusage_stats.h"
@@ -288,7 +290,7 @@
   class ReportSink {
    public:
     virtual ~ReportSink() = default;
-    virtual ReportSink& operator<<(const std::string& fragment) = 0;
+    virtual ReportSink& operator<<(std::string_view fragment) = 0;
   };
 
   //----------------------------------------------------------------------------
diff --git a/centipede/rusage_profiler_test.cc b/centipede/rusage_profiler_test.cc
index 64ea044..93a5a48 100644
--- a/centipede/rusage_profiler_test.cc
+++ b/centipede/rusage_profiler_test.cc
@@ -19,6 +19,7 @@
 #include <cmath>
 #include <cstdint>
 #include <string>
+#include <string_view>
 
 #include "gtest/gtest.h"
 #include "absl/flags/flag.h"
@@ -230,7 +231,7 @@
   class ReportCapture : public RUsageProfiler::ReportSink {
    public:
     ~ReportCapture() override = default;
-    ReportCapture& operator<<(const std::string& fragment) override {
+    ReportCapture& operator<<(std::string_view fragment) override {
       LOG(INFO).NoPrefix() << fragment;
       return *this;
     }
diff --git a/centipede/seed_corpus_maker_lib.cc b/centipede/seed_corpus_maker_lib.cc
index 87d8975..40f9ca6 100644
--- a/centipede/seed_corpus_maker_lib.cc
+++ b/centipede/seed_corpus_maker_lib.cc
@@ -52,6 +52,7 @@
 #include "./centipede/util.h"
 #include "./centipede/workdir.h"
 #include "google/protobuf/text_format.h"
+#include "riegeli/bytes/read_all.h"
 
 // TODO(ussuri): Implement a smarter on-the-fly sampling to avoid having to
 //  load all of a source's elements into RAM only to pick some of them. That
@@ -76,7 +77,8 @@
     LOG(INFO) << "Config spec points at an existing file; trying to parse "
                  "textproto config from it: "
               << VV(config_spec);
-    RemoteFileGetContents(config_spec, config_str);
+    CHECK_OK(
+        riegeli::ReadAll(CreateRiegeliFileReader(config_spec), config_str));
     LOG(INFO) << "Raw config read from file:\n" << config_str;
     base_dir = std::filesystem::path{config_spec}.parent_path();
   } else {
diff --git a/centipede/stats.cc b/centipede/stats.cc
index 04a8ee6..b149511 100644
--- a/centipede/stats.cc
+++ b/centipede/stats.cc
@@ -15,8 +15,6 @@
 #include "./centipede/stats.h"
 
 #include <algorithm>
-#include <cinttypes>
-#include <cmath>
 #include <cstdint>
 #include <cstdlib>
 #include <cstring>
@@ -25,6 +23,7 @@
 #include <ios>
 #include <iosfwd>
 #include <limits>
+#include <memory>
 #include <numeric>
 #include <sstream>
 #include <string>
@@ -36,13 +35,14 @@
 #include "absl/strings/ascii.h"
 #include "absl/strings/str_cat.h"
 #include "absl/strings/str_format.h"
-#include "absl/strings/substitute.h"
 #include "absl/time/time.h"
 #include "absl/types/span.h"
 #include "./centipede/environment.h"
 #include "./centipede/logging.h"
 #include "./centipede/remote_file.h"
 #include "./centipede/workdir.h"
+#include "riegeli/bytes/writer.h"
+#include "riegeli/csv/csv_writer.h"
 
 namespace centipede {
 
@@ -153,8 +153,9 @@
 //                           StatsCsvFileAppender
 
 StatsCsvFileAppender::~StatsCsvFileAppender() {
-  for (const auto &[group_name, file] : files_) {
-    RemoteFileClose(file);
+  for (const auto &[group_name, csv_writer_with_record] : csv_writers_) {
+    CHECK(csv_writer_with_record.csv_writer->Close())
+        << VV(csv_writer_with_record.csv_writer->status());
   }
 }
 
@@ -163,32 +164,34 @@
   if (!csv_header_.empty()) return;
 
   for (const auto &field : fields) {
-    std::string col_names;
     switch (field.aggregation) {
       case Stats::Aggregation::kMinMax:
-        col_names = absl::Substitute("$0_Min,$0_Max,", field.name);
+        csv_header_.Add(absl::StrCat(field.name, "_Min"),
+                        absl::StrCat(field.name, "_Max"));
         break;
       case Stats::Aggregation::kMinMaxAvg:
-        col_names = absl::Substitute("$0_Min,$0_Max,$0_Avg,", field.name);
+        csv_header_.Add(absl::StrCat(field.name, "_Min"),
+                        absl::StrCat(field.name, "_Max"),
+                        absl::StrCat(field.name, "_Avg"));
         break;
     }
-    absl::StrAppend(&csv_header_, col_names);
   }
-  absl::StrAppend(&csv_header_, "\n");
 }
 
 void StatsCsvFileAppender::SetCurrGroup(const Environment &master_env) {
-  RemoteFile *&file = files_[master_env.experiment_name];
-  if (file == nullptr) {
+  curr_csv_writer_ = &csv_writers_[master_env.experiment_name];
+  if (curr_csv_writer_->csv_writer == nullptr) {
     const std::string filename =
         WorkDir{master_env}.FuzzingStatsPath(master_env.experiment_name);
-    // TODO(ussuri): Append, not overwrite, so restarts keep accumulating.
-    //  This will require writing the CSV header only if the file is brand new.
-    file = RemoteFileOpen(filename, "w");
-    CHECK(file != nullptr) << VV(filename);
-    RemoteFileAppend(file, csv_header_);
+    auto writer = CreateRiegeliFileWriter(filename, /*append=*/true);
+    CHECK(writer->ok()) << VV(writer->status());
+    riegeli::CsvWriterBase::Options options;
+    if (writer->pos() == 0) options.set_header(csv_header_);
+    curr_csv_writer_->csv_writer =
+        std::make_unique<riegeli::CsvWriter<std::unique_ptr<riegeli::Writer>>>(
+            std::move(writer), std::move(options));
+    curr_csv_writer_->record.reserve(csv_header_.size());
   }
-  curr_file_ = file;
 }
 
 void StatsCsvFileAppender::SetCurrField(const Stats::FieldInfo &field_info) {
@@ -207,17 +210,17 @@
     avg += value;
   }
   if (!values.empty()) avg /= values.size();
-  std::string values_str;
   switch (curr_field_info_.aggregation) {
     case Stats::Aggregation::kMinMax:
-      values_str = absl::StrFormat("%" PRIu64 ",%" PRIu64 ",", min, max);
+      curr_csv_writer_->record.push_back(absl::StrCat(min));
+      curr_csv_writer_->record.push_back(absl::StrCat(max));
       break;
     case Stats::Aggregation::kMinMaxAvg:
-      values_str =
-          absl::StrFormat("%" PRIu64 ",%" PRIu64 ",%.1Lf,", min, max, avg);
+      curr_csv_writer_->record.push_back(absl::StrCat(min));
+      curr_csv_writer_->record.push_back(absl::StrCat(max));
+      curr_csv_writer_->record.push_back(absl::StrFormat("%.1f", avg));
       break;
   }
-  RemoteFileAppend(curr_file_, values_str);
 }
 
 void StatsCsvFileAppender::ReportFlags(const GroupToFlags &group_to_flags) {
@@ -226,8 +229,10 @@
 }
 
 void StatsCsvFileAppender::DoneFieldSamplesBatch() {
-  for (const auto &[group_name, file] : files_) {
-    RemoteFileAppend(file, "\n");
+  for (auto &[group_name, csv_writer_with_record] : csv_writers_) {
+    csv_writer_with_record.csv_writer->WriteRecord(
+        csv_writer_with_record.record);
+    csv_writer_with_record.record.clear();
   }
 }
 
diff --git a/centipede/stats.h b/centipede/stats.h
index 37ed90c..24928fa 100644
--- a/centipede/stats.h
+++ b/centipede/stats.h
@@ -1,5 +1,4 @@
 // Copyright 2022 The Centipede Authors.
-//
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
 // You may obtain a copy of the License at
@@ -19,6 +18,7 @@
 #include <cstdint>
 #include <cstdlib>
 #include <initializer_list>
+#include <memory>
 #include <ostream>
 #include <sstream>
 #include <string>
@@ -29,7 +29,8 @@
 #include "absl/container/flat_hash_map.h"
 #include "absl/types/span.h"
 #include "./centipede/environment.h"
-#include "./centipede/remote_file.h"
+#include "riegeli/csv/csv_record.h"
+#include "riegeli/csv/csv_writer.h"
 
 namespace centipede {
 
@@ -195,6 +196,11 @@
   ~StatsCsvFileAppender() override;
 
  private:
+  struct CsvWriterWithRecord {
+    std::unique_ptr<riegeli::CsvWriterBase> csv_writer;
+    std::vector<std::string> record;
+  };
+
   void PreAnnounceFields(
       std::initializer_list<Stats::FieldInfo> fields) override;
   void SetCurrGroup(const Environment &master_env) override;
@@ -203,9 +209,10 @@
   void DoneFieldSamplesBatch() override;
   void ReportFlags(const GroupToFlags &group_to_flags) override;
 
-  std::string csv_header_;
-  absl::flat_hash_map<std::string /*group_name*/, RemoteFile *> files_;
-  RemoteFile *curr_file_;
+  riegeli::CsvHeader csv_header_;
+  absl::flat_hash_map<std::string /*group_name*/, CsvWriterWithRecord>
+      csv_writers_;
+  CsvWriterWithRecord *curr_csv_writer_;
   Stats::FieldInfo curr_field_info_;
 };
 
diff --git a/centipede/stats_test.cc b/centipede/stats_test.cc
index de47fbc..d5237a8 100644
--- a/centipede/stats_test.cc
+++ b/centipede/stats_test.cc
@@ -24,6 +24,7 @@
 
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
+#include "absl/log/log.h"
 #include "absl/log/log_entry.h"
 #include "absl/log/log_sink.h"
 #include "absl/log/log_sink_registry.h"
@@ -212,13 +213,13 @@
       workdir / "fuzzing-stats-.000000.ExperimentB.csv",
   };
   const std::vector<std::string_view> kExpectedCsvContents = {
-      R"(NumCoveredPcs_Min,NumCoveredPcs_Max,NumCoveredPcs_Avg,NumExecs_Min,NumExecs_Max,NumExecs_Avg,CorpusSize_Min,CorpusSize_Max,CorpusSize_Avg,MaxEltSize_Min,MaxEltSize_Max,MaxEltSize_Avg,AvgEltSize_Min,AvgEltSize_Max,AvgEltSize_Avg,UnixMicros_Min,UnixMicros_Max,
-10,25,17.5,100,102,101.0,1000,3000,2000.0,1,5,3.0,1,3,2.0,1000000,1000002,
-11,26,18.5,101,103,102.0,1001,3001,2001.0,2,6,4.0,2,4,3.0,1000001,1000003,
+      R"(NumCoveredPcs_Min,NumCoveredPcs_Max,NumCoveredPcs_Avg,NumExecs_Min,NumExecs_Max,NumExecs_Avg,CorpusSize_Min,CorpusSize_Max,CorpusSize_Avg,MaxEltSize_Min,MaxEltSize_Max,MaxEltSize_Avg,AvgEltSize_Min,AvgEltSize_Max,AvgEltSize_Avg,UnixMicros_Min,UnixMicros_Max
+10,25,17.5,100,102,101.0,1000,3000,2000.0,1,5,3.0,1,3,2.0,1000000,1000002
+11,26,18.5,101,103,102.0,1001,3001,2001.0,2,6,4.0,2,4,3.0,1000001,1000003
 )",
-      R"(NumCoveredPcs_Min,NumCoveredPcs_Max,NumCoveredPcs_Avg,NumExecs_Min,NumExecs_Max,NumExecs_Avg,CorpusSize_Min,CorpusSize_Max,CorpusSize_Avg,MaxEltSize_Min,MaxEltSize_Max,MaxEltSize_Avg,AvgEltSize_Min,AvgEltSize_Max,AvgEltSize_Avg,UnixMicros_Min,UnixMicros_Max,
-15,40,27.5,101,103,102.0,2000,4000,3000.0,3,7,5.0,2,4,3.0,1000001,1000003,
-16,41,28.5,102,104,103.0,2001,4001,3001.0,4,8,6.0,3,5,4.0,1000002,1000004,
+      R"(NumCoveredPcs_Min,NumCoveredPcs_Max,NumCoveredPcs_Avg,NumExecs_Min,NumExecs_Max,NumExecs_Avg,CorpusSize_Min,CorpusSize_Max,CorpusSize_Avg,MaxEltSize_Min,MaxEltSize_Max,MaxEltSize_Avg,AvgEltSize_Min,AvgEltSize_Max,AvgEltSize_Avg,UnixMicros_Min,UnixMicros_Max
+15,40,27.5,101,103,102.0,2000,4000,3000.0,3,7,5.0,2,4,3.0,1000001,1000003
+16,41,28.5,102,104,103.0,2001,4001,3001.0,4,8,6.0,3,5,4.0,1000002,1000004
 )",
   };
 
diff --git a/centipede/symbol_table.cc b/centipede/symbol_table.cc
index b69ed98..2945df4 100644
--- a/centipede/symbol_table.cc
+++ b/centipede/symbol_table.cc
@@ -15,9 +15,7 @@
 #include "./centipede/symbol_table.h"
 
 #include <cstdlib>
-#include <filesystem>
-#include <fstream>
-#include <ostream>
+#include <filesystem>  // NOLINT
 #include <string>
 #include <string_view>
 #include <vector>
@@ -35,6 +33,11 @@
 #include "./centipede/logging.h"
 #include "./centipede/pc_info.h"
 #include "./centipede/util.h"
+#include "riegeli/base/any_dependency.h"
+#include "riegeli/bytes/fd_reader.h"
+#include "riegeli/bytes/reader.h"
+#include "riegeli/bytes/writer.h"
+#include "riegeli/lines/line_reading.h"
 
 namespace centipede {
 
@@ -42,31 +45,33 @@
   return this->entries_ == other.entries_;
 }
 
-void SymbolTable::ReadFromLLVMSymbolizer(std::istream &in) {
+void SymbolTable::ReadFromLLVMSymbolizer(
+    riegeli::AnyDependencyRef<riegeli::Reader *> in) {
   // We remove some useless file prefixes for better human readability.
   const std::string_view file_prefixes_to_remove[] = {"/proc/self/cwd/", "./"};
-  while (in) {
-    // We (mostly) blindly trust the input format is correct.
-    std::string func, file, empty;
-    std::getline(in, func);
-    std::getline(in, file);
-    std::getline(in, empty);
+  std::string func, file, empty;
+  // We (mostly) blindly trust the input format is correct.
+  while (riegeli::ReadLine(*in, func) && riegeli::ReadLine(*in, file) &&
+         riegeli::ReadLine(*in, empty)) {
     CHECK(empty.empty()) << "Unexpected symbolizer output format: " << VV(func)
                          << VV(file) << VV(empty);
-    if (!in) break;
+    std::string_view file_view = file;
     for (auto &bad_prefix : file_prefixes_to_remove) {
-      file = absl::StripPrefix(file, bad_prefix);
+      file_view = absl::StripPrefix(file_view, bad_prefix);
     }
-    AddEntry(func, file);
+    AddEntry(func, file_view);
   }
+  if (in.is_owning()) CHECK(in->Close()) << VV(in->status());
 }
 
-void SymbolTable::WriteToLLVMSymbolizer(std::ostream &out) {
+void SymbolTable::WriteToLLVMSymbolizer(
+    riegeli::AnyDependencyRef<riegeli::Writer *> out) {
   for (const Entry &entry : entries_) {
-    out << entry.func << '\n';
-    out << entry.file_line_col() << '\n';
-    out << std::endl;
+    out->Write(entry.func, '\n');
+    out->Write(entry.file_line_col(), '\n');
+    out->Write('\n');
   }
+  if (out.is_owning()) CHECK(out->Close()) << VV(out->status());
 }
 
 void SymbolTable::GetSymbolsFromOneDso(absl::Span<const PCInfo> pc_infos,
@@ -102,9 +107,8 @@
     return;
   }
   // Get and process the symbolizer output.
-  std::ifstream symbolizer_output(std::string{symbols_path});
   size_t old_size = size();
-  ReadFromLLVMSymbolizer(symbolizer_output);
+  ReadFromLLVMSymbolizer(riegeli::FdReader(symbols_path));
   std::filesystem::remove(pcs_path);
   std::filesystem::remove(symbols_path);
   size_t new_size = size();
diff --git a/centipede/symbol_table.h b/centipede/symbol_table.h
index b08d81b..a6dab6e 100644
--- a/centipede/symbol_table.h
+++ b/centipede/symbol_table.h
@@ -16,22 +16,18 @@
 #define THIRD_PARTY_CENTIPEDE_SYMBOL_TABLE_H_
 
 #include <cstddef>
-#include <istream>
-#include <ostream>
 #include <string>
 #include <string_view>
 #include <vector>
 
-#include "absl/log/check.h"
-#include "absl/log/log.h"
-#include "absl/status/status.h"
 #include "absl/strings/match.h"
-#include "absl/strings/numbers.h"
 #include "absl/strings/str_cat.h"
-#include "absl/strings/str_split.h"
 #include "absl/types/span.h"
 #include "./centipede/control_flow.h"
 #include "./centipede/pc_info.h"
+#include "riegeli/base/any_dependency.h"
+#include "riegeli/bytes/reader.h"
+#include "riegeli/bytes/writer.h"
 
 namespace centipede {
 
@@ -69,11 +65,11 @@
   //   SourceCodeLocation
   //   <empty line>
   // If called multiple times, this function will append symbols to `this`.
-  void ReadFromLLVMSymbolizer(std::istream &in);
+  void ReadFromLLVMSymbolizer(riegeli::AnyDependencyRef<riegeli::Reader *> in);
 
-  // Writes the contents of `this` to `path` in the same format as read by
+  // Writes the contents of `this` to `out` in the same format as read by
   // `ReadFromLLVMSymbolizer`.
-  void WriteToLLVMSymbolizer(std::ostream &out);
+  void WriteToLLVMSymbolizer(riegeli::AnyDependencyRef<riegeli::Writer *> out);
 
   // Invokes `symbolizer_path --no-inlines` on all binaries from `dso_table`,
   // pipes through it all PCs in pc_table that correspond to each of the
diff --git a/centipede/symbol_table_test.cc b/centipede/symbol_table_test.cc
index 0f9fea4..75a8473 100644
--- a/centipede/symbol_table_test.cc
+++ b/centipede/symbol_table_test.cc
@@ -14,16 +14,18 @@
 
 #include "./centipede/symbol_table.h"
 
-#include <sstream>
 #include <string>
+#include <string_view>
 
 #include "gtest/gtest.h"
+#include "riegeli/bytes/string_reader.h"
+#include "riegeli/bytes/string_writer.h"
 
 namespace centipede {
 namespace {
 
 TEST(SymbolTableTest, SerializesAndDeserializesCorrectly) {
-  std::string input =
+  const std::string_view input =
       R"(FunctionOne
     source/location/one.cc:1:0
 
@@ -31,30 +33,26 @@
     source/location/two.cc:2:0
 
 )";
-  std::istringstream input_stream(input);
   SymbolTable symbol_table;
+  symbol_table.ReadFromLLVMSymbolizer(riegeli::StringReader(input));
 
-  symbol_table.ReadFromLLVMSymbolizer(input_stream);
-
-  std::ostringstream output_stream;
-  symbol_table.WriteToLLVMSymbolizer(output_stream);
-  EXPECT_EQ(input, output_stream.str());
+  std::string output;
+  symbol_table.WriteToLLVMSymbolizer(riegeli::StringWriter(&output));
+  EXPECT_EQ(input, output);
 }
 
 TEST(SymbolTableTest, SerializesAndDeserializesCorrectlyWithUnknownFile) {
-  std::string input =
+  const std::string_view input =
       R"(?
     ?
 
 )";
-  std::istringstream input_stream(input);
   SymbolTable symbol_table;
+  symbol_table.ReadFromLLVMSymbolizer(riegeli::StringReader(input));
 
-  symbol_table.ReadFromLLVMSymbolizer(input_stream);
-
-  std::ostringstream output_stream;
-  symbol_table.WriteToLLVMSymbolizer(output_stream);
-  EXPECT_EQ(input, output_stream.str());
+  std::string output;
+  symbol_table.WriteToLLVMSymbolizer(riegeli::StringWriter(&output));
+  EXPECT_EQ(input, output);
 }
 
 }  // namespace
diff --git a/centipede/util.cc b/centipede/util.cc
index c629c7d..906bb50 100644
--- a/centipede/util.cc
+++ b/centipede/util.cc
@@ -55,6 +55,8 @@
 #include "./centipede/feature.h"
 #include "./centipede/logging.h"
 #include "./centipede/remote_file.h"
+#include "riegeli/bytes/read_all.h"
+#include "riegeli/bytes/write.h"
 
 namespace centipede {
 
@@ -140,13 +142,14 @@
 void WriteToRemoteHashedFileInDir(std::string_view dir_path, ByteSpan data) {
   if (dir_path.empty()) return;
   std::string file_path = std::filesystem::path(dir_path).append(Hash(data));
-  RemoteFileSetContents(file_path, std::string(data.begin(), data.end()));
+  CHECK_OK(
+      riegeli::Write(AsStringView(data), CreateRiegeliFileWriter(file_path)));
 }
 
 std::string HashOfFileContents(std::string_view file_path) {
   if (file_path.empty()) return "";
   std::string file_contents;
-  RemoteFileGetContents(std::filesystem::path(file_path), file_contents);
+  CHECK_OK(riegeli::ReadAll(CreateRiegeliFileReader(file_path), file_contents));
   return Hash(file_contents);
 }
 
@@ -218,7 +221,7 @@
 //
 // This is simple and efficient, but I wonder if there is a ready-to-use
 // standard open-source alternative. Or should we just use tar?
-ByteArray PackBytesForAppendFile(const ByteArray &data) {
+ByteArray PackBytesForAppendFile(ByteSpan data) {
   ByteArray res;
   auto hash = Hash(data);
   CHECK_EQ(hash.size(), kHashLen);
@@ -234,7 +237,7 @@
 }
 
 // Reverse to a sequence of PackBytesForAppendFile() appended to each other.
-void UnpackBytesFromAppendFile(const ByteArray &packed_data,
+void UnpackBytesFromAppendFile(ByteSpan packed_data,
                                std::vector<ByteArray> *unpacked,
                                std::vector<std::string> *hashes) {
   auto pos = packed_data.cbegin();
diff --git a/centipede/util.h b/centipede/util.h
index 4a705da..580ce9f 100644
--- a/centipede/util.h
+++ b/centipede/util.h
@@ -141,12 +141,12 @@
 // TODO(kcc): [impl] is there a lightweight equivalent in the open-source world?
 //  tar sounds too heavy.
 // TODO(kcc): [impl] investigate https://github.com/google/riegeli.
-ByteArray PackBytesForAppendFile(const ByteArray &data);
+ByteArray PackBytesForAppendFile(ByteSpan data);
 // Unpacks `packed_data` into `unpacked` and `hashes`.
 // `packed_data` is multiple data packed by PackBytesForAppendFile()
 // and merged together.
 // `unpacked` or `hashes` can be nullptr.
-void UnpackBytesFromAppendFile(const ByteArray &packed_data,
+void UnpackBytesFromAppendFile(ByteSpan packed_data,
                                std::vector<ByteArray> *unpacked,
                                std::vector<std::string> *hashes = nullptr);
 // Append the bytes from 'hash' to 'ba'.