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