diff --git a/src/google/protobuf/compiler/BUILD.bazel b/src/google/protobuf/compiler/BUILD.bazel index af05903a6a..d60d78f65f 100644 --- a/src/google/protobuf/compiler/BUILD.bazel +++ b/src/google/protobuf/compiler/BUILD.bazel @@ -83,6 +83,7 @@ cc_library( ":code_generator", ":importer", "//src/google/protobuf:protobuf_nowkt", + "@com_google_absl//absl/container:btree", "@com_google_absl//absl/strings", "@com_google_absl//absl/strings:str_format", ], diff --git a/src/google/protobuf/compiler/command_line_interface.cc b/src/google/protobuf/compiler/command_line_interface.cc index 7cff1e3563..c2a6ef48fe 100644 --- a/src/google/protobuf/compiler/command_line_interface.cc +++ b/src/google/protobuf/compiler/command_line_interface.cc @@ -34,6 +34,7 @@ #include "google/protobuf/compiler/command_line_interface.h" +#include "absl/container/btree_map.h" #include "absl/container/flat_hash_map.h" #include "google/protobuf/stubs/platform_macros.h" @@ -427,7 +428,7 @@ class CommandLineInterface::GeneratorContextImpl : public GeneratorContext { // The files_ field maps from path keys to file content values. It's a map // instead of an unordered_map so that files are written in order (good when // writing zips). - absl::flat_hash_map files_; + absl::btree_map files_; const std::vector& parsed_files_; bool had_error_; }; @@ -805,95 +806,95 @@ CommandLineInterface::MemoryOutputStream::~MemoryOutputStream() { } it->second.swap(data_); - } else { - // This was an OpenForInsert(). + return; + } + // This was an OpenForInsert(). - // If the data doesn't end with a clean line break, add one. - if (!data_.empty() && data_[data_.size() - 1] != '\n') { - data_.push_back('\n'); - } + // If the data doesn't end with a clean line break, add one. + if (!data_.empty() && data_[data_.size() - 1] != '\n') { + data_.push_back('\n'); + } - // Find the file we are going to insert into. - if (!already_present) { - std::cerr << filename_ - << ": Tried to insert into file that doesn't exist." - << std::endl; - directory_->had_error_ = true; - return; - } - std::string* target = &it->second; + // Find the file we are going to insert into. + if (!already_present) { + std::cerr << filename_ << ": Tried to insert into file that doesn't exist." + << std::endl; + directory_->had_error_ = true; + return; + } + std::string* target = &it->second; - // Find the insertion point. - std::string magic_string = - absl::Substitute("@@protoc_insertion_point($0)", insertion_point_); - std::string::size_type pos = target->find(magic_string); + // Find the insertion point. + std::string magic_string = + absl::Substitute("@@protoc_insertion_point($0)", insertion_point_); + std::string::size_type pos = target->find(magic_string); - if (pos == std::string::npos) { - std::cerr << filename_ << ": insertion point \"" << insertion_point_ - << "\" not found." << std::endl; - directory_->had_error_ = true; - return; - } + if (pos == std::string::npos) { + std::cerr << filename_ << ": insertion point \"" << insertion_point_ + << "\" not found." << std::endl; + directory_->had_error_ = true; + return; + } - if ((pos > 3) && (target->substr(pos - 3, 2) == "/*")) { - // Support for inline "/* @@protoc_insertion_point() */" - pos = pos - 3; + if ((pos > 3) && (target->substr(pos - 3, 2) == "/*")) { + // Support for inline "/* @@protoc_insertion_point() */" + pos = pos - 3; + } else { + // Seek backwards to the beginning of the line, which is where we will + // insert the data. Note that this has the effect of pushing the + // insertion point down, so the data is inserted before it. This is + // intentional because it means that multiple insertions at the same point + // will end up in the expected order in the final output. + pos = target->find_last_of('\n', pos); + if (pos == std::string::npos) { + // Insertion point is on the first line. + pos = 0; } else { - // Seek backwards to the beginning of the line, which is where we will - // insert the data. Note that this has the effect of pushing the - // insertion point down, so the data is inserted before it. This is - // intentional because it means that multiple insertions at the same point - // will end up in the expected order in the final output. - pos = target->find_last_of('\n', pos); - if (pos == std::string::npos) { - // Insertion point is on the first line. - pos = 0; - } else { - // Advance to character after '\n'. - ++pos; - } + // Advance to character after '\n'. + ++pos; } + } - // Extract indent. - std::string indent_(*target, pos, - target->find_first_not_of(" \t", pos) - pos); + // Extract indent. + std::string indent_(*target, pos, + target->find_first_not_of(" \t", pos) - pos); - if (indent_.empty()) { - // No indent. This makes things easier. - target->insert(pos, data_); - UpdateMetadata(data_, pos, data_.size(), 0); - } else { - // Calculate how much space we need. - int indent_size = 0; - for (int i = 0; i < data_.size(); i++) { - if (data_[i] == '\n') indent_size += indent_.size(); - } + if (indent_.empty()) { + // No indent. This makes things easier. + target->insert(pos, data_); + UpdateMetadata(data_, pos, data_.size(), 0); + return; + } + // Calculate how much space we need. + int indent_size = 0; + for (int i = 0; i < data_.size(); i++) { + if (data_[i] == '\n') indent_size += indent_.size(); + } - // Make a hole for it. - target->insert(pos, data_.size() + indent_size, '\0'); - - // Now copy in the data. - std::string::size_type data_pos = 0; - char* target_ptr = &(*target)[pos]; - while (data_pos < data_.size()) { - // Copy indent. - memcpy(target_ptr, indent_.data(), indent_.size()); - target_ptr += indent_.size(); - - // Copy line from data_. - // We already guaranteed that data_ ends with a newline (above), so this - // search can't fail. - std::string::size_type line_length = - data_.find_first_of('\n', data_pos) + 1 - data_pos; - memcpy(target_ptr, data_.data() + data_pos, line_length); - target_ptr += line_length; - data_pos += line_length; - } - UpdateMetadata(data_, pos, data_.size() + indent_size, indent_.size()); + // Make a hole for it. + target->insert(pos, data_.size() + indent_size, '\0'); - GOOGLE_CHECK_EQ(target_ptr, &(*target)[pos] + data_.size() + indent_size); - } + // Now copy in the data. + std::string::size_type data_pos = 0; + char* target_ptr = &(*target)[pos]; + while (data_pos < data_.size()) { + // Copy indent. + memcpy(target_ptr, indent_.data(), indent_.size()); + target_ptr += indent_.size(); + + // Copy line from data_. + // We already guaranteed that data_ ends with a newline (above), so this + // search can't fail. + std::string::size_type line_length = + data_.find_first_of('\n', data_pos) + 1 - data_pos; + memcpy(target_ptr, data_.data() + data_pos, line_length); + target_ptr += line_length; + data_pos += line_length; } + + GOOGLE_CHECK_EQ(target_ptr, &(*target)[pos] + data_.size() + indent_size); + + UpdateMetadata(data_, pos, data_.size() + indent_size, indent_.size()); } // ===================================================================