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