Snap for 12309590 from c6427b61ead131a449fdebf0c3ba74cf3b4a8c4e to 24Q4-release

Change-Id: I6b7fb11dc3bea288481d3896e162ddf285b69121
diff --git a/btf_reader.cc b/btf_reader.cc
index 397d996..ad5248d 100644
--- a/btf_reader.cc
+++ b/btf_reader.cc
@@ -151,33 +151,19 @@
   maker_.Set<Node>(id, std::forward<Args>(args)...);
 }
 
-bool IsAlignedForBtf(std::string_view btf_data) {
-  return reinterpret_cast<uintptr_t>(btf_data.data()) % alignof(btf_header) ==
-         0;
-}
-
 Id Structs::Process(std::string_view btf_data) {
-  Check(sizeof(btf_header) <= btf_data.size())
-      << "BTF section too small for header";
-  if (IsAlignedForBtf(btf_data)) {
-    return ProcessAligned(btf_data);
-  }
-  // Copy the data to aligned memory.
-  // Check that minimum amount of BTF data containing just btf_header will be
-  // heap allocated and will not fit inside the std::string due to small string
-  // optimization.
   // TODO: Remove this hack once the upstream binaries have proper
   // alignment.
-  static_assert(
-      sizeof(btf_header) >= sizeof(std::string),
-      "btf_header may hit small string optimization and be misaligned");
-  const std::string aligned_btf_data(btf_data);
-  Check(IsAlignedForBtf(aligned_btf_data))
-      << "std::string with BTF data is misaligned";
-  return ProcessAligned(aligned_btf_data);
+  //
+  // Copy the data to aligned heap-allocated memory, if needed.
+  return reinterpret_cast<uintptr_t>(btf_data.data()) % alignof(btf_header) > 0
+      ? ProcessAligned(std::string(btf_data))
+      : ProcessAligned(btf_data);
 }
 
 Id Structs::ProcessAligned(std::string_view btf_data) {
+  Check(sizeof(btf_header) <= btf_data.size())
+      << "BTF section too small for header";
   const btf_header* header =
       reinterpret_cast<const btf_header*>(btf_data.data());
   Check(header->magic == 0xEB9F) << "Magic field must be 0xEB9F for BTF";
diff --git a/doc/stgdiff.md b/doc/stgdiff.md
index 62d6dd2..7f0c33b 100644
--- a/doc/stgdiff.md
+++ b/doc/stgdiff.md
@@ -133,11 +133,16 @@
 *   Loss or gain of type definitions
 *   Loss or gain of type information for symbols
 
-## Output formats
+## Output
 
 All outputs are based on a diff graph which is rooted at the comparison of two
 symbol table nodes.
 
+The `--format` and `--output` options may be repeated to obtain outputs of
+different formats.
+
+### Formats
+
 *   `plain`
 
     Serialise the diff graph via depth first search, avoiding revisiting nodes
diff --git a/proto_reader.cc b/proto_reader.cc
index f046458..264a54d 100644
--- a/proto_reader.cc
+++ b/proto_reader.cc
@@ -24,13 +24,17 @@
 #include <cerrno>
 #include <cstdint>
 #include <fstream>
+#include <limits>
 #include <map>
 #include <optional>
 #include <string>
 #include <string_view>
 #include <vector>
 
+#include <google/protobuf/io/tokenizer.h>
+#include <google/protobuf/io/zero_copy_stream.h>
 #include <google/protobuf/io/zero_copy_stream_impl.h>
+#include <google/protobuf/io/zero_copy_stream_impl_lite.h>
 #include <google/protobuf/repeated_field.h>
 #include <google/protobuf/repeated_ptr_field.h>
 #include <google/protobuf/text_format.h>
@@ -456,54 +460,70 @@
 
 const std::array<uint32_t, 3> kSupportedFormatVersions = {0, 1, 2};
 
-void CheckFormatVersion(uint32_t version, std::optional<std::string> path) {
+void CheckFormatVersion(uint32_t version) {
   Check(std::binary_search(kSupportedFormatVersions.begin(),
                            kSupportedFormatVersions.end(), version))
       << "STG format version " << version
       << " is not supported, minimum supported version: "
       << kSupportedFormatVersions.front();
   if (version != kSupportedFormatVersions.back()) {
-    auto warn = Warn();
-    warn << "STG format version " << version
-         << " is deprecated, consider upgrading stg format to latest version ("
-         << kSupportedFormatVersions.back() << ")";
-    if (path) {
-      warn << " with: stg --stg " << *path << " --output " << *path;
-    }
+    Warn() << "STG format version " << version
+           << " is deprecated, consider upgrading to the latest version ("
+           << kSupportedFormatVersions.back() << ")";
+  }
+}
+
+class ErrorSink : public google::protobuf::io::ErrorCollector {
+ public:
+  void AddError(int line, google::protobuf::io::ColumnNumber column,
+                const std::string& message) final {
+    Moan("error", line, column, message);
+  }
+  void AddWarning(int line, google::protobuf::io::ColumnNumber column,
+                  const std::string& message) final {
+    Moan("warning", line, column, message);
+  }
+
+ private:
+  static void Moan(std::string_view which, int line,
+                   google::protobuf::io::ColumnNumber column,
+                   const std::string& message) {
+    Warn() << "google::protobuf::TextFormat " << which << " at line " << (line + 1)
+           << " column " << (column + 1) << ": " << message;
+  }
+};
+
+Id ReadHelper(Runtime& runtime, Graph& graph,
+              google::protobuf::io::ZeroCopyInputStream& is) {
+  proto::STG stg;
+  {
+    const Time t(runtime, "proto.Parse");
+    ErrorSink error_sink;
+    google::protobuf::TextFormat::Parser parser;
+    parser.RecordErrorsTo(&error_sink);
+    Check(parser.Parse(&is, &stg)) << "failed to parse input as STG";
+  }
+  {
+    const Time t(runtime, "proto.Transform");
+    CheckFormatVersion(stg.version());
+    return Transformer(graph).Transform(stg);
   }
 }
 
 }  // namespace
 
 Id Read(Runtime& runtime, Graph& graph, const std::string& path) {
-  proto::STG stg;
-  {
-    const Time t(runtime, "proto.Parse");
-    std::ifstream ifs(path);
-    Check(ifs.good()) << "error opening file '" << path
-                      << "' for reading: " << Error(errno);
-    google::protobuf::io::IstreamInputStream is(&ifs);
-    google::protobuf::TextFormat::Parse(&is, &stg);
-  }
-  {
-    const Time t(runtime, "proto.Transform");
-    CheckFormatVersion(stg.version(), path);
-    return Transformer(graph).Transform(stg);
-  }
+  std::ifstream ifs(path);
+  Check(ifs.good()) << "error opening file '" << path << "' for reading: "
+                    << Error(errno);
+  google::protobuf::io::IstreamInputStream is(&ifs);
+  return ReadHelper(runtime, graph, is);
 }
 
 Id ReadFromString(Runtime& runtime, Graph& graph, std::string_view input) {
-  proto::STG stg;
-  {
-    const Time t(runtime, "proto.Parse");
-    // TODO: Pass string_view once AOSP Protobuf supports this.
-    google::protobuf::TextFormat::ParseFromString(std::string(input), &stg);
-  }
-  {
-    const Time t(runtime, "proto.Transform");
-    CheckFormatVersion(stg.version(), std::nullopt);
-    return Transformer(graph).Transform(stg);
-  }
+  Check(input.size() <= std::numeric_limits<int>::max()) << "input too big";
+  google::protobuf::io::ArrayInputStream is(input.data(), static_cast<int>(input.size()));
+  return ReadHelper(runtime, graph, is);
 }
 
 }  // namespace proto
diff --git a/test_cases/info_tests/variant/optional_empty.rs b/test_cases/info_tests/variant/optional_empty.rs
index 1cda07c..f1552c5 100644
--- a/test_cases/info_tests/variant/optional_empty.rs
+++ b/test_cases/info_tests/variant/optional_empty.rs
@@ -4,6 +4,5 @@
 pub fn is_none(opt: Option<Empty>) -> bool {
     match opt {
         None => true,
-        _ => false,
     }
 }