Adam Cozzette | 5aca728 | 2023-08-07 10:01:08 -0700 | [diff] [blame] | 1 | // Protocol Buffers - Google's data interchange format |
| 2 | // Copyright 2023 Google LLC. All rights reserved. |
Adam Cozzette | 5aca728 | 2023-08-07 10:01:08 -0700 | [diff] [blame] | 3 | // |
Hong Shin | c482a8a | 2023-11-09 09:30:34 -0800 | [diff] [blame] | 4 | // Use of this source code is governed by a BSD-style |
| 5 | // license that can be found in the LICENSE file or at |
| 6 | // https://developers.google.com/open-source/licenses/bsd |
Protobuf Team Bot | 306123e | 2022-11-04 09:25:30 -0700 | [diff] [blame] | 7 | |
Hong Shin | 096b139 | 2024-07-03 10:04:09 -0700 | [diff] [blame] | 8 | #ifndef PROTOBUF_COMPILER_HBP_OUTPUT_H_ |
| 9 | #define PROTOBUF_COMPILER_HBP_OUTPUT_H_ |
Protobuf Team Bot | 306123e | 2022-11-04 09:25:30 -0700 | [diff] [blame] | 10 | |
| 11 | #include <vector> |
| 12 | |
Mike Kruskal | a1abf83 | 2023-01-20 19:59:31 -0800 | [diff] [blame] | 13 | #include "absl/log/absl_log.h" |
Protobuf Team Bot | 306123e | 2022-11-04 09:25:30 -0700 | [diff] [blame] | 14 | #include "absl/strings/str_replace.h" |
| 15 | #include "absl/strings/substitute.h" |
Mike Kruskal | 2b011bc | 2022-11-10 11:51:09 -0800 | [diff] [blame] | 16 | #include "google/protobuf/descriptor.h" |
| 17 | #include "google/protobuf/io/zero_copy_stream.h" |
Protobuf Team Bot | 306123e | 2022-11-04 09:25:30 -0700 | [diff] [blame] | 18 | |
Hong Shin | 7a2b37f | 2024-07-09 15:42:34 -0700 | [diff] [blame] | 19 | namespace google::protobuf::hpb_generator { |
Protobuf Team Bot | 306123e | 2022-11-04 09:25:30 -0700 | [diff] [blame] | 20 | |
| 21 | class Output { |
| 22 | public: |
| 23 | Output(google::protobuf::io::ZeroCopyOutputStream* stream) : stream_(stream) {} |
| 24 | ~Output() { stream_->BackUp((int)buffer_size_); } |
| 25 | |
| 26 | template <class... Arg> |
| 27 | void operator()(absl::string_view format, const Arg&... arg) { |
| 28 | Write(absl::Substitute(format, arg...)); |
| 29 | } |
| 30 | |
| 31 | // Indentation size in characters. |
| 32 | static constexpr size_t kIndentationSize = 2; |
| 33 | |
| 34 | void Indent() { Indent(kIndentationSize); } |
| 35 | void Indent(size_t size) { indent_ += size; } |
| 36 | |
| 37 | void Outdent() { Outdent(kIndentationSize); } |
| 38 | void Outdent(size_t size) { |
| 39 | if (indent_ < size) { |
Mike Kruskal | a1abf83 | 2023-01-20 19:59:31 -0800 | [diff] [blame] | 40 | ABSL_LOG(FATAL) << "mismatched Output indent/unindent calls"; |
Protobuf Team Bot | 306123e | 2022-11-04 09:25:30 -0700 | [diff] [blame] | 41 | } |
| 42 | indent_ -= size; |
| 43 | } |
| 44 | |
| 45 | private: |
| 46 | void Write(absl::string_view data) { |
| 47 | std::string stripped; |
| 48 | if (absl::StartsWith(data, "\n ")) { |
| 49 | size_t indent = data.substr(1).find_first_not_of(' '); |
| 50 | if (indent > indent_) { |
| 51 | indent -= indent_; |
| 52 | } |
| 53 | if (indent != absl::string_view::npos) { |
| 54 | // Remove indentation from all lines. |
| 55 | auto line_prefix = data.substr(0, indent + 1); |
| 56 | // The final line has an extra newline and is indented two less, eg. |
| 57 | // R"cc( |
| 58 | // UPB_INLINE $0 $1_$2(const $1 *msg) { |
| 59 | // return $1_has_$2(msg) ? *UPB_PTR_AT(msg, $3, $0) : $4; |
| 60 | // } |
| 61 | // )cc", |
| 62 | std::string last_line_prefix = std::string(line_prefix); |
| 63 | last_line_prefix.resize(last_line_prefix.size() - 2); |
| 64 | data.remove_prefix(line_prefix.size()); |
| 65 | stripped = absl::StrReplaceAll( |
| 66 | data, {{line_prefix, "\n"}, {last_line_prefix, "\n"}}); |
| 67 | data = stripped; |
| 68 | } |
| 69 | } else { |
| 70 | WriteIndent(); |
| 71 | } |
| 72 | WriteRaw(data); |
| 73 | } |
| 74 | |
| 75 | void WriteRaw(absl::string_view data) { |
| 76 | while (!data.empty()) { |
| 77 | RefreshOutput(); |
| 78 | size_t to_write = std::min(data.size(), buffer_size_); |
| 79 | memcpy(output_buffer_, data.data(), to_write); |
| 80 | data.remove_prefix(to_write); |
| 81 | output_buffer_ += to_write; |
| 82 | buffer_size_ -= to_write; |
| 83 | } |
| 84 | } |
| 85 | |
| 86 | void WriteIndent() { |
| 87 | if (indent_ == 0) { |
| 88 | return; |
| 89 | } |
| 90 | size_t size = indent_; |
| 91 | while (size > buffer_size_) { |
| 92 | if (buffer_size_ > 0) { |
| 93 | memset(output_buffer_, ' ', buffer_size_); |
| 94 | } |
| 95 | size -= buffer_size_; |
| 96 | buffer_size_ = 0; |
| 97 | RefreshOutput(); |
| 98 | } |
| 99 | memset(output_buffer_, ' ', size); |
| 100 | output_buffer_ += size; |
| 101 | buffer_size_ -= size; |
| 102 | } |
| 103 | |
| 104 | void RefreshOutput() { |
| 105 | while (buffer_size_ == 0) { |
| 106 | void* void_buffer; |
| 107 | int size; |
| 108 | if (!stream_->Next(&void_buffer, &size)) { |
Adam Cozzette | 12c7bb0 | 2023-09-28 12:54:11 -0700 | [diff] [blame] | 109 | fprintf(stderr, "upb_generator: Failed to write to to output\n"); |
Protobuf Team Bot | 306123e | 2022-11-04 09:25:30 -0700 | [diff] [blame] | 110 | abort(); |
| 111 | } |
| 112 | output_buffer_ = static_cast<char*>(void_buffer); |
| 113 | buffer_size_ = size; |
| 114 | } |
| 115 | } |
| 116 | |
| 117 | google::protobuf::io::ZeroCopyOutputStream* stream_; |
| 118 | char* output_buffer_ = nullptr; |
| 119 | size_t buffer_size_ = 0; |
| 120 | // Current indentation size in characters. |
| 121 | size_t indent_ = 0; |
| 122 | friend class OutputIndenter; |
| 123 | }; |
| 124 | |
| 125 | class OutputIndenter { |
| 126 | public: |
| 127 | OutputIndenter(Output& output) |
| 128 | : OutputIndenter(output, Output::kIndentationSize) {} |
| 129 | OutputIndenter(Output& output, size_t indent_size) |
| 130 | : indent_size_(indent_size), output_(output) { |
| 131 | output.Indent(indent_size); |
| 132 | } |
| 133 | ~OutputIndenter() { output_.Outdent(indent_size_); } |
| 134 | |
| 135 | private: |
| 136 | size_t indent_size_; |
| 137 | Output& output_; |
| 138 | }; |
| 139 | |
Protobuf Team Bot | 306123e | 2022-11-04 09:25:30 -0700 | [diff] [blame] | 140 | std::string ToCIdent(absl::string_view str); |
| 141 | std::string ToPreproc(absl::string_view str); |
| 142 | void EmitFileWarning(const google::protobuf::FileDescriptor* file, Output& output); |
| 143 | std::string MessageName(const google::protobuf::Descriptor* descriptor); |
| 144 | std::string FileLayoutName(const google::protobuf::FileDescriptor* file); |
| 145 | std::string CHeaderFilename(const google::protobuf::FileDescriptor* file); |
Protobuf Team Bot | 306123e | 2022-11-04 09:25:30 -0700 | [diff] [blame] | 146 | |
Hong Shin | 7a2b37f | 2024-07-09 15:42:34 -0700 | [diff] [blame] | 147 | } // namespace protobuf |
| 148 | } // namespace google::hpb_generator |
Protobuf Team Bot | 306123e | 2022-11-04 09:25:30 -0700 | [diff] [blame] | 149 | |
Hong Shin | 096b139 | 2024-07-03 10:04:09 -0700 | [diff] [blame] | 150 | #endif // PROTOBUF_COMPILER_HBP_OUTPUT_H_ |