1 | // Protocol Buffers - Google's data interchange format |
2 | // Copyright 2008 Google Inc. All rights reserved. |
3 | // https://developers.google.com/protocol-buffers/ |
4 | // |
5 | // Redistribution and use in source and binary forms, with or without |
6 | // modification, are permitted provided that the following conditions are |
7 | // met: |
8 | // |
9 | // * Redistributions of source code must retain the above copyright |
10 | // notice, this list of conditions and the following disclaimer. |
11 | // * Redistributions in binary form must reproduce the above |
12 | // copyright notice, this list of conditions and the following disclaimer |
13 | // in the documentation and/or other materials provided with the |
14 | // distribution. |
15 | // * Neither the name of Google Inc. nor the names of its |
16 | // contributors may be used to endorse or promote products derived from |
17 | // this software without specific prior written permission. |
18 | // |
19 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
20 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
21 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
22 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
23 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
24 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
25 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
26 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
27 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
28 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
29 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
30 | |
31 | // Author: kenton@google.com (Kenton Varda) |
32 | // Based on original Protocol Buffers design by |
33 | // Sanjay Ghemawat, Jeff Dean, and others. |
34 | // |
35 | // Utility class for writing text to a ZeroCopyOutputStream. |
36 | |
37 | #ifndef GOOGLE_PROTOBUF_IO_PRINTER_H__ |
38 | #define GOOGLE_PROTOBUF_IO_PRINTER_H__ |
39 | |
40 | |
41 | #include <map> |
42 | #include <string> |
43 | #include <vector> |
44 | |
45 | #include <google/protobuf/stubs/common.h> |
46 | |
47 | // Must be included last. |
48 | #include <google/protobuf/port_def.inc> |
49 | |
50 | namespace google { |
51 | namespace protobuf { |
52 | namespace io { |
53 | |
54 | class ZeroCopyOutputStream; // zero_copy_stream.h |
55 | |
56 | // Records annotations about a Printer's output. |
57 | class PROTOBUF_EXPORT AnnotationCollector { |
58 | public: |
59 | // Annotation is a offset range and a payload pair. |
60 | typedef std::pair<std::pair<size_t, size_t>, std::string> Annotation; |
61 | |
62 | // Records that the bytes in file_path beginning with begin_offset and ending |
63 | // before end_offset are associated with the SourceCodeInfo-style path. |
64 | virtual void AddAnnotation(size_t begin_offset, size_t end_offset, |
65 | const std::string& file_path, |
66 | const std::vector<int>& path) = 0; |
67 | |
68 | // TODO(gerbens) I don't see why we need virtuals here. Just a vector of |
69 | // range, payload pairs stored in a context should suffice. |
70 | virtual void AddAnnotationNew(Annotation& /* a */) {} |
71 | |
72 | virtual ~AnnotationCollector() {} |
73 | }; |
74 | |
75 | // Records annotations about a Printer's output to the given protocol buffer, |
76 | // assuming that the buffer has an ::Annotation message exposing path, |
77 | // source_file, begin and end fields. |
78 | template <typename AnnotationProto> |
79 | class AnnotationProtoCollector : public AnnotationCollector { |
80 | public: |
81 | // annotation_proto is the protocol buffer to which new Annotations should be |
82 | // added. It is not owned by the AnnotationProtoCollector. |
83 | explicit AnnotationProtoCollector(AnnotationProto* annotation_proto) |
84 | : annotation_proto_(annotation_proto) {} |
85 | |
86 | // Override for AnnotationCollector::AddAnnotation. |
87 | void AddAnnotation(size_t begin_offset, size_t end_offset, |
88 | const std::string& file_path, |
89 | const std::vector<int>& path) override { |
90 | typename AnnotationProto::Annotation* annotation = |
91 | annotation_proto_->add_annotation(); |
92 | for (int i = 0; i < path.size(); ++i) { |
93 | annotation->add_path(path[i]); |
94 | } |
95 | annotation->set_source_file(file_path); |
96 | annotation->set_begin(begin_offset); |
97 | annotation->set_end(end_offset); |
98 | } |
99 | // Override for AnnotationCollector::AddAnnotation. |
100 | void AddAnnotationNew(Annotation& a) override { |
101 | auto* annotation = annotation_proto_->add_annotation(); |
102 | annotation->ParseFromString(a.second); |
103 | annotation->set_begin(a.first.first); |
104 | annotation->set_end(a.first.second); |
105 | } |
106 | |
107 | private: |
108 | // The protocol buffer to which new annotations should be added. |
109 | AnnotationProto* const annotation_proto_; |
110 | }; |
111 | |
112 | // This simple utility class assists in code generation. It basically |
113 | // allows the caller to define a set of variables and then output some |
114 | // text with variable substitutions. Example usage: |
115 | // |
116 | // Printer printer(output, '$'); |
117 | // map<string, string> vars; |
118 | // vars["name"] = "Bob"; |
119 | // printer.Print(vars, "My name is $name$."); |
120 | // |
121 | // The above writes "My name is Bob." to the output stream. |
122 | // |
123 | // Printer aggressively enforces correct usage, crashing (with assert failures) |
124 | // in the case of undefined variables in debug builds. This helps greatly in |
125 | // debugging code which uses it. |
126 | // |
127 | // If a Printer is constructed with an AnnotationCollector, it will provide it |
128 | // with annotations that connect the Printer's output to paths that can identify |
129 | // various descriptors. In the above example, if person_ is a descriptor that |
130 | // identifies Bob, we can associate the output string "My name is Bob." with |
131 | // a source path pointing to that descriptor with: |
132 | // |
133 | // printer.Annotate("name", person_); |
134 | // |
135 | // The AnnotationCollector will be sent an annotation linking the output range |
136 | // covering "Bob" to the logical path provided by person_. Tools may use |
137 | // this association to (for example) link "Bob" in the output back to the |
138 | // source file that defined the person_ descriptor identifying Bob. |
139 | // |
140 | // Annotate can only examine variables substituted during the last call to |
141 | // Print. It is invalid to refer to a variable that was used multiple times |
142 | // in a single Print call. |
143 | // |
144 | // In full generality, one may specify a range of output text using a beginning |
145 | // substitution variable and an ending variable. The resulting annotation will |
146 | // span from the first character of the substituted value for the beginning |
147 | // variable to the last character of the substituted value for the ending |
148 | // variable. For example, the Annotate call above is equivalent to this one: |
149 | // |
150 | // printer.Annotate("name", "name", person_); |
151 | // |
152 | // This is useful if multiple variables combine to form a single span of output |
153 | // that should be annotated with the same source path. For example: |
154 | // |
155 | // Printer printer(output, '$'); |
156 | // map<string, string> vars; |
157 | // vars["first"] = "Alice"; |
158 | // vars["last"] = "Smith"; |
159 | // printer.Print(vars, "My name is $first$ $last$."); |
160 | // printer.Annotate("first", "last", person_); |
161 | // |
162 | // This code would associate the span covering "Alice Smith" in the output with |
163 | // the person_ descriptor. |
164 | // |
165 | // Note that the beginning variable must come before (or overlap with, in the |
166 | // case of zero-sized substitution values) the ending variable. |
167 | // |
168 | // It is also sometimes useful to use variables with zero-sized values as |
169 | // markers. This avoids issues with multiple references to the same variable |
170 | // and also allows annotation ranges to span literal text from the Print |
171 | // templates: |
172 | // |
173 | // Printer printer(output, '$'); |
174 | // map<string, string> vars; |
175 | // vars["foo"] = "bar"; |
176 | // vars["function"] = "call"; |
177 | // vars["mark"] = ""; |
178 | // printer.Print(vars, "$function$($foo$,$foo$)$mark$"); |
179 | // printer.Annotate("function", "mark", call_); |
180 | // |
181 | // This code associates the span covering "call(bar,bar)" in the output with the |
182 | // call_ descriptor. |
183 | |
184 | class PROTOBUF_EXPORT Printer { |
185 | public: |
186 | // Create a printer that writes text to the given output stream. Use the |
187 | // given character as the delimiter for variables. |
188 | Printer(ZeroCopyOutputStream* output, char variable_delimiter); |
189 | |
190 | // Create a printer that writes text to the given output stream. Use the |
191 | // given character as the delimiter for variables. If annotation_collector |
192 | // is not null, Printer will provide it with annotations about code written |
193 | // to the stream. annotation_collector is not owned by Printer. |
194 | Printer(ZeroCopyOutputStream* output, char variable_delimiter, |
195 | AnnotationCollector* annotation_collector); |
196 | |
197 | ~Printer(); |
198 | |
199 | // Link a substitution variable emitted by the last call to Print to the |
200 | // object described by descriptor. |
201 | template <typename SomeDescriptor> |
202 | void Annotate(const char* varname, const SomeDescriptor* descriptor) { |
203 | Annotate(varname, varname, descriptor); |
204 | } |
205 | |
206 | // Link the output range defined by the substitution variables as emitted by |
207 | // the last call to Print to the object described by descriptor. The range |
208 | // begins at begin_varname's value and ends after the last character of the |
209 | // value substituted for end_varname. |
210 | template <typename SomeDescriptor> |
211 | void Annotate(const char* begin_varname, const char* end_varname, |
212 | const SomeDescriptor* descriptor) { |
213 | if (annotation_collector_ == NULL) { |
214 | // Annotations aren't turned on for this Printer, so don't pay the cost |
215 | // of building the location path. |
216 | return; |
217 | } |
218 | std::vector<int> path; |
219 | descriptor->GetLocationPath(&path); |
220 | Annotate(begin_varname, end_varname, descriptor->file()->name(), path); |
221 | } |
222 | |
223 | // Link a substitution variable emitted by the last call to Print to the file |
224 | // with path file_name. |
225 | void Annotate(const char* varname, const std::string& file_name) { |
226 | Annotate(begin_varname: varname, end_varname: varname, file_name); |
227 | } |
228 | |
229 | // Link the output range defined by the substitution variables as emitted by |
230 | // the last call to Print to the file with path file_name. The range begins |
231 | // at begin_varname's value and ends after the last character of the value |
232 | // substituted for end_varname. |
233 | void Annotate(const char* begin_varname, const char* end_varname, |
234 | const std::string& file_name) { |
235 | if (annotation_collector_ == NULL) { |
236 | // Annotations aren't turned on for this Printer. |
237 | return; |
238 | } |
239 | std::vector<int> empty_path; |
240 | Annotate(begin_varname, end_varname, file_path: file_name, path: empty_path); |
241 | } |
242 | |
243 | // Print some text after applying variable substitutions. If a particular |
244 | // variable in the text is not defined, this will crash. Variables to be |
245 | // substituted are identified by their names surrounded by delimiter |
246 | // characters (as given to the constructor). The variable bindings are |
247 | // defined by the given map. |
248 | void Print(const std::map<std::string, std::string>& variables, |
249 | const char* text); |
250 | |
251 | // Like the first Print(), except the substitutions are given as parameters. |
252 | template <typename... Args> |
253 | void Print(const char* text, const Args&... args) { |
254 | std::map<std::string, std::string> vars; |
255 | PrintInternal(&vars, text, args...); |
256 | } |
257 | |
258 | // Indent text by two spaces. After calling Indent(), two spaces will be |
259 | // inserted at the beginning of each line of text. Indent() may be called |
260 | // multiple times to produce deeper indents. |
261 | void Indent(); |
262 | |
263 | // Reduces the current indent level by two spaces, or crashes if the indent |
264 | // level is zero. |
265 | void Outdent(); |
266 | |
267 | // Write a string to the output buffer. |
268 | // This method does not look for newlines to add indentation. |
269 | void PrintRaw(const std::string& data); |
270 | |
271 | // Write a zero-delimited string to output buffer. |
272 | // This method does not look for newlines to add indentation. |
273 | void PrintRaw(const char* data); |
274 | |
275 | // Write some bytes to the output buffer. |
276 | // This method does not look for newlines to add indentation. |
277 | void WriteRaw(const char* data, int size); |
278 | |
279 | // FormatInternal is a helper function not meant to use directly, use |
280 | // compiler::cpp::Formatter instead. This function is meant to support |
281 | // formatting text using named variables (eq. "$foo$) from a lookup map (vars) |
282 | // and variables directly supplied by arguments (eq "$1$" meaning first |
283 | // argument which is the zero index element of args). |
284 | void FormatInternal(const std::vector<std::string>& args, |
285 | const std::map<std::string, std::string>& vars, |
286 | const char* format); |
287 | |
288 | // True if any write to the underlying stream failed. (We don't just |
289 | // crash in this case because this is an I/O failure, not a programming |
290 | // error.) |
291 | bool failed() const { return failed_; } |
292 | |
293 | private: |
294 | // Link the output range defined by the substitution variables as emitted by |
295 | // the last call to Print to the object found at the SourceCodeInfo-style path |
296 | // in a file with path file_path. The range begins at the start of |
297 | // begin_varname's value and ends after the last character of the value |
298 | // substituted for end_varname. Note that begin_varname and end_varname |
299 | // may refer to the same variable. |
300 | void Annotate(const char* begin_varname, const char* end_varname, |
301 | const std::string& file_path, const std::vector<int>& path); |
302 | |
303 | // Base case |
304 | void PrintInternal(std::map<std::string, std::string>* vars, |
305 | const char* text) { |
306 | Print(variables: *vars, text); |
307 | } |
308 | |
309 | template <typename... Args> |
310 | void PrintInternal(std::map<std::string, std::string>* vars, const char* text, |
311 | const char* key, const std::string& value, |
312 | const Args&... args) { |
313 | (*vars)[key] = value; |
314 | PrintInternal(vars, text, args...); |
315 | } |
316 | |
317 | // Copy size worth of bytes from data to buffer_. |
318 | void CopyToBuffer(const char* data, int size); |
319 | |
320 | void push_back(char c) { |
321 | if (failed_) return; |
322 | if (buffer_size_ == 0) { |
323 | if (!Next()) return; |
324 | } |
325 | *buffer_++ = c; |
326 | buffer_size_--; |
327 | offset_++; |
328 | } |
329 | |
330 | bool Next(); |
331 | |
332 | inline void IndentIfAtStart(); |
333 | const char* WriteVariable( |
334 | const std::vector<std::string>& args, |
335 | const std::map<std::string, std::string>& vars, const char* format, |
336 | int* arg_index, |
337 | std::vector<AnnotationCollector::Annotation>* annotations); |
338 | |
339 | const char variable_delimiter_; |
340 | |
341 | ZeroCopyOutputStream* const output_; |
342 | char* buffer_; |
343 | int buffer_size_; |
344 | // The current position, in bytes, in the output stream. This is equivalent |
345 | // to the total number of bytes that have been written so far. This value is |
346 | // used to calculate annotation ranges in the substitutions_ map below. |
347 | size_t offset_; |
348 | |
349 | std::string indent_; |
350 | bool at_start_of_line_; |
351 | bool failed_; |
352 | |
353 | // A map from variable name to [start, end) offsets in the output buffer. |
354 | // These refer to the offsets used for a variable after the last call to |
355 | // Print. If a variable was used more than once, the entry used in |
356 | // this map is set to a negative-length span. For singly-used variables, the |
357 | // start offset is the beginning of the substitution; the end offset is the |
358 | // last byte of the substitution plus one (such that (end - start) is the |
359 | // length of the substituted string). |
360 | std::map<std::string, std::pair<size_t, size_t> > substitutions_; |
361 | |
362 | // Keeps track of the keys in substitutions_ that need to be updated when |
363 | // indents are inserted. These are keys that refer to the beginning of the |
364 | // current line. |
365 | std::vector<std::string> line_start_variables_; |
366 | |
367 | // Returns true and sets range to the substitution range in the output for |
368 | // varname if varname was used once in the last call to Print. If varname |
369 | // was not used, or if it was used multiple times, returns false (and |
370 | // fails a debug assertion). |
371 | bool GetSubstitutionRange(const char* varname, |
372 | std::pair<size_t, size_t>* range); |
373 | |
374 | // If non-null, annotation_collector_ is used to store annotations about |
375 | // generated code. |
376 | AnnotationCollector* const annotation_collector_; |
377 | |
378 | GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(Printer); |
379 | }; |
380 | |
381 | } // namespace io |
382 | } // namespace protobuf |
383 | } // namespace google |
384 | |
385 | #include <google/protobuf/port_undef.inc> |
386 | |
387 | #endif // GOOGLE_PROTOBUF_IO_PRINTER_H__ |
388 | |