• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2023 Google LLC.  All rights reserved.
3 //
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
7 
8 #ifndef PROTOBUF_COMPILER_HBP_OUTPUT_H_
9 #define PROTOBUF_COMPILER_HBP_OUTPUT_H_
10 
11 #include <vector>
12 
13 #include "absl/log/absl_log.h"
14 #include "absl/strings/str_replace.h"
15 #include "absl/strings/substitute.h"
16 #include "google/protobuf/descriptor.h"
17 #include "google/protobuf/io/zero_copy_stream.h"
18 
19 namespace google::protobuf::hpb_generator {
20 
21 class Output {
22  public:
Output(google::protobuf::io::ZeroCopyOutputStream * stream)23   Output(google::protobuf::io::ZeroCopyOutputStream* stream) : stream_(stream) {}
~Output()24   ~Output() { stream_->BackUp((int)buffer_size_); }
25 
26   template <class... Arg>
operator()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 
Indent()34   void Indent() { Indent(kIndentationSize); }
Indent(size_t size)35   void Indent(size_t size) { indent_ += size; }
36 
Outdent()37   void Outdent() { Outdent(kIndentationSize); }
Outdent(size_t size)38   void Outdent(size_t size) {
39     if (indent_ < size) {
40       ABSL_LOG(FATAL) << "mismatched Output indent/unindent calls";
41     }
42     indent_ -= size;
43   }
44 
45  private:
Write(absl::string_view data)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 
WriteRaw(absl::string_view data)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 
WriteIndent()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 
RefreshOutput()104   void RefreshOutput() {
105     while (buffer_size_ == 0) {
106       void* void_buffer;
107       int size;
108       if (!stream_->Next(&void_buffer, &size)) {
109         fprintf(stderr, "upb_generator: Failed to write to to output\n");
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:
OutputIndenter(Output & output)127   OutputIndenter(Output& output)
128       : OutputIndenter(output, Output::kIndentationSize) {}
OutputIndenter(Output & output,size_t indent_size)129   OutputIndenter(Output& output, size_t indent_size)
130       : indent_size_(indent_size), output_(output) {
131     output.Indent(indent_size);
132   }
~OutputIndenter()133   ~OutputIndenter() { output_.Outdent(indent_size_); }
134 
135  private:
136   size_t indent_size_;
137   Output& output_;
138 };
139 
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);
146 
147 }  // namespace protobuf
148 }  // namespace google::hpb_generator
149 
150 #endif  // PROTOBUF_COMPILER_HBP_OUTPUT_H_
151