1 /* 2 * Copyright (c) 2009-2021, Google LLC 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * * Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * * Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * * Neither the name of Google LLC nor the 13 * names of its contributors may be used to endorse or promote products 14 * derived from this software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY DIRECT, 20 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28 #ifndef UPB_PROTOS_GENERATOR_OUTPUT_H 29 #define UPB_PROTOS_GENERATOR_OUTPUT_H 30 31 #include <vector> 32 33 #include "absl/log/absl_log.h" 34 #include "absl/strings/str_replace.h" 35 #include "absl/strings/substitute.h" 36 #include "google/protobuf/descriptor.h" 37 #include "google/protobuf/io/zero_copy_stream.h" 38 39 namespace protos_generator { 40 41 class Output { 42 public: Output(google::protobuf::io::ZeroCopyOutputStream * stream)43 Output(google::protobuf::io::ZeroCopyOutputStream* stream) : stream_(stream) {} ~Output()44 ~Output() { stream_->BackUp((int)buffer_size_); } 45 46 template <class... Arg> operator()47 void operator()(absl::string_view format, const Arg&... arg) { 48 Write(absl::Substitute(format, arg...)); 49 } 50 51 // Indentation size in characters. 52 static constexpr size_t kIndentationSize = 2; 53 Indent()54 void Indent() { Indent(kIndentationSize); } Indent(size_t size)55 void Indent(size_t size) { indent_ += size; } 56 Outdent()57 void Outdent() { Outdent(kIndentationSize); } Outdent(size_t size)58 void Outdent(size_t size) { 59 if (indent_ < size) { 60 ABSL_LOG(FATAL) << "mismatched Output indent/unindent calls"; 61 } 62 indent_ -= size; 63 } 64 65 private: Write(absl::string_view data)66 void Write(absl::string_view data) { 67 std::string stripped; 68 if (absl::StartsWith(data, "\n ")) { 69 size_t indent = data.substr(1).find_first_not_of(' '); 70 if (indent > indent_) { 71 indent -= indent_; 72 } 73 if (indent != absl::string_view::npos) { 74 // Remove indentation from all lines. 75 auto line_prefix = data.substr(0, indent + 1); 76 // The final line has an extra newline and is indented two less, eg. 77 // R"cc( 78 // UPB_INLINE $0 $1_$2(const $1 *msg) { 79 // return $1_has_$2(msg) ? *UPB_PTR_AT(msg, $3, $0) : $4; 80 // } 81 // )cc", 82 std::string last_line_prefix = std::string(line_prefix); 83 last_line_prefix.resize(last_line_prefix.size() - 2); 84 data.remove_prefix(line_prefix.size()); 85 stripped = absl::StrReplaceAll( 86 data, {{line_prefix, "\n"}, {last_line_prefix, "\n"}}); 87 data = stripped; 88 } 89 } else { 90 WriteIndent(); 91 } 92 WriteRaw(data); 93 } 94 WriteRaw(absl::string_view data)95 void WriteRaw(absl::string_view data) { 96 while (!data.empty()) { 97 RefreshOutput(); 98 size_t to_write = std::min(data.size(), buffer_size_); 99 memcpy(output_buffer_, data.data(), to_write); 100 data.remove_prefix(to_write); 101 output_buffer_ += to_write; 102 buffer_size_ -= to_write; 103 } 104 } 105 WriteIndent()106 void WriteIndent() { 107 if (indent_ == 0) { 108 return; 109 } 110 size_t size = indent_; 111 while (size > buffer_size_) { 112 if (buffer_size_ > 0) { 113 memset(output_buffer_, ' ', buffer_size_); 114 } 115 size -= buffer_size_; 116 buffer_size_ = 0; 117 RefreshOutput(); 118 } 119 memset(output_buffer_, ' ', size); 120 output_buffer_ += size; 121 buffer_size_ -= size; 122 } 123 RefreshOutput()124 void RefreshOutput() { 125 while (buffer_size_ == 0) { 126 void* void_buffer; 127 int size; 128 if (!stream_->Next(&void_buffer, &size)) { 129 fprintf(stderr, "upbc: Failed to write to to output\n"); 130 abort(); 131 } 132 output_buffer_ = static_cast<char*>(void_buffer); 133 buffer_size_ = size; 134 } 135 } 136 137 google::protobuf::io::ZeroCopyOutputStream* stream_; 138 char* output_buffer_ = nullptr; 139 size_t buffer_size_ = 0; 140 // Current indentation size in characters. 141 size_t indent_ = 0; 142 friend class OutputIndenter; 143 }; 144 145 class OutputIndenter { 146 public: OutputIndenter(Output & output)147 OutputIndenter(Output& output) 148 : OutputIndenter(output, Output::kIndentationSize) {} OutputIndenter(Output & output,size_t indent_size)149 OutputIndenter(Output& output, size_t indent_size) 150 : indent_size_(indent_size), output_(output) { 151 output.Indent(indent_size); 152 } ~OutputIndenter()153 ~OutputIndenter() { output_.Outdent(indent_size_); } 154 155 private: 156 size_t indent_size_; 157 Output& output_; 158 }; 159 160 std::string StripExtension(absl::string_view fname); 161 std::string ToCIdent(absl::string_view str); 162 std::string ToPreproc(absl::string_view str); 163 void EmitFileWarning(const google::protobuf::FileDescriptor* file, Output& output); 164 std::string MessageName(const google::protobuf::Descriptor* descriptor); 165 std::string FileLayoutName(const google::protobuf::FileDescriptor* file); 166 std::string CHeaderFilename(const google::protobuf::FileDescriptor* file); 167 std::string CSourceFilename(const google::protobuf::FileDescriptor* file); 168 169 } // namespace protos_generator 170 171 #endif // UPB_PROTOS_GENERATOR_OUTPUT_H 172