• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #include <google/protobuf/io/printer.h>
36 
37 #include <cctype>
38 
39 #include <google/protobuf/stubs/logging.h>
40 #include <google/protobuf/stubs/common.h>
41 #include <google/protobuf/io/zero_copy_stream.h>
42 
43 namespace google {
44 namespace protobuf {
45 namespace io {
46 
Printer(ZeroCopyOutputStream * output,char variable_delimiter)47 Printer::Printer(ZeroCopyOutputStream* output, char variable_delimiter)
48     : variable_delimiter_(variable_delimiter),
49       output_(output),
50       buffer_(NULL),
51       buffer_size_(0),
52       offset_(0),
53       at_start_of_line_(true),
54       failed_(false),
55       annotation_collector_(NULL) {}
56 
Printer(ZeroCopyOutputStream * output,char variable_delimiter,AnnotationCollector * annotation_collector)57 Printer::Printer(ZeroCopyOutputStream* output, char variable_delimiter,
58                  AnnotationCollector* annotation_collector)
59     : variable_delimiter_(variable_delimiter),
60       output_(output),
61       buffer_(NULL),
62       buffer_size_(0),
63       offset_(0),
64       at_start_of_line_(true),
65       failed_(false),
66       annotation_collector_(annotation_collector) {}
67 
~Printer()68 Printer::~Printer() {
69   // Only BackUp() if we have called Next() at least once and never failed.
70   if (buffer_size_ > 0 && !failed_) {
71     output_->BackUp(buffer_size_);
72   }
73 }
74 
GetSubstitutionRange(const char * varname,std::pair<size_t,size_t> * range)75 bool Printer::GetSubstitutionRange(const char* varname,
76                                    std::pair<size_t, size_t>* range) {
77   std::map<std::string, std::pair<size_t, size_t> >::const_iterator iter =
78       substitutions_.find(varname);
79   if (iter == substitutions_.end()) {
80     GOOGLE_LOG(DFATAL) << " Undefined variable in annotation: " << varname;
81     return false;
82   }
83   if (iter->second.first > iter->second.second) {
84     GOOGLE_LOG(DFATAL) << " Variable used for annotation used multiple times: "
85                 << varname;
86     return false;
87   }
88   *range = iter->second;
89   return true;
90 }
91 
Annotate(const char * begin_varname,const char * end_varname,const std::string & file_path,const std::vector<int> & path)92 void Printer::Annotate(const char* begin_varname, const char* end_varname,
93                        const std::string& file_path,
94                        const std::vector<int>& path) {
95   if (annotation_collector_ == NULL) {
96     // Can't generate signatures with this Printer.
97     return;
98   }
99   std::pair<size_t, size_t> begin, end;
100   if (!GetSubstitutionRange(begin_varname, &begin) ||
101       !GetSubstitutionRange(end_varname, &end)) {
102     return;
103   }
104   if (begin.first > end.second) {
105     GOOGLE_LOG(DFATAL) << "  Annotation has negative length from " << begin_varname
106                 << " to " << end_varname;
107   } else {
108     annotation_collector_->AddAnnotation(begin.first, end.second, file_path,
109                                          path);
110   }
111 }
112 
Print(const std::map<std::string,std::string> & variables,const char * text)113 void Printer::Print(const std::map<std::string, std::string>& variables,
114                     const char* text) {
115   int size = strlen(text);
116   int pos = 0;  // The number of bytes we've written so far.
117   substitutions_.clear();
118   line_start_variables_.clear();
119 
120   for (int i = 0; i < size; i++) {
121     if (text[i] == '\n') {
122       // Saw newline.  If there is more text, we may need to insert an indent
123       // here.  So, write what we have so far, including the '\n'.
124       WriteRaw(text + pos, i - pos + 1);
125       pos = i + 1;
126 
127       // Setting this true will cause the next WriteRaw() to insert an indent
128       // first.
129       at_start_of_line_ = true;
130       line_start_variables_.clear();
131 
132     } else if (text[i] == variable_delimiter_) {
133       // Saw the start of a variable name.
134 
135       // Write what we have so far.
136       WriteRaw(text + pos, i - pos);
137       pos = i + 1;
138 
139       // Find closing delimiter.
140       const char* end = strchr(text + pos, variable_delimiter_);
141       if (end == NULL) {
142         GOOGLE_LOG(DFATAL) << " Unclosed variable name.";
143         end = text + pos;
144       }
145       int endpos = end - text;
146 
147       std::string varname(text + pos, endpos - pos);
148       if (varname.empty()) {
149         // Two delimiters in a row reduce to a literal delimiter character.
150         WriteRaw(&variable_delimiter_, 1);
151       } else {
152         // Replace with the variable's value.
153         std::map<std::string, std::string>::const_iterator iter =
154             variables.find(varname);
155         if (iter == variables.end()) {
156           GOOGLE_LOG(DFATAL) << " Undefined variable: " << varname;
157         } else {
158           if (at_start_of_line_ && iter->second.empty()) {
159             line_start_variables_.push_back(varname);
160           }
161           WriteRaw(iter->second.data(), iter->second.size());
162           std::pair<std::map<std::string, std::pair<size_t, size_t> >::iterator,
163                     bool>
164               inserted = substitutions_.insert(std::make_pair(
165                   varname,
166                   std::make_pair(offset_ - iter->second.size(), offset_)));
167           if (!inserted.second) {
168             // This variable was used multiple times.  Make its span have
169             // negative length so we can detect it if it gets used in an
170             // annotation.
171             inserted.first->second = std::make_pair(1, 0);
172           }
173         }
174       }
175 
176       // Advance past this variable.
177       i = endpos;
178       pos = endpos + 1;
179     }
180   }
181 
182   // Write the rest.
183   WriteRaw(text + pos, size - pos);
184 }
185 
Indent()186 void Printer::Indent() { indent_ += "  "; }
187 
Outdent()188 void Printer::Outdent() {
189   if (indent_.empty()) {
190     GOOGLE_LOG(DFATAL) << " Outdent() without matching Indent().";
191     return;
192   }
193 
194   indent_.resize(indent_.size() - 2);
195 }
196 
PrintRaw(const std::string & data)197 void Printer::PrintRaw(const std::string& data) {
198   WriteRaw(data.data(), data.size());
199 }
200 
PrintRaw(const char * data)201 void Printer::PrintRaw(const char* data) {
202   if (failed_) return;
203   WriteRaw(data, strlen(data));
204 }
205 
WriteRaw(const char * data,int size)206 void Printer::WriteRaw(const char* data, int size) {
207   if (failed_) return;
208   if (size == 0) return;
209 
210   if (at_start_of_line_ && (size > 0) && (data[0] != '\n')) {
211     // Insert an indent.
212     at_start_of_line_ = false;
213     CopyToBuffer(indent_.data(), indent_.size());
214     if (failed_) return;
215     // Fix up empty variables (e.g., "{") that should be annotated as
216     // coming after the indent.
217     for (std::vector<std::string>::iterator i = line_start_variables_.begin();
218          i != line_start_variables_.end(); ++i) {
219       substitutions_[*i].first += indent_.size();
220       substitutions_[*i].second += indent_.size();
221     }
222   }
223 
224   // If we're going to write any data, clear line_start_variables_, since
225   // we've either updated them in the block above or they no longer refer to
226   // the current line.
227   line_start_variables_.clear();
228 
229   CopyToBuffer(data, size);
230 }
231 
Next()232 bool Printer::Next() {
233   do {
234     void* void_buffer;
235     if (!output_->Next(&void_buffer, &buffer_size_)) {
236       failed_ = true;
237       return false;
238     }
239     buffer_ = reinterpret_cast<char*>(void_buffer);
240   } while (buffer_size_ == 0);
241   return true;
242 }
243 
CopyToBuffer(const char * data,int size)244 void Printer::CopyToBuffer(const char* data, int size) {
245   if (failed_) return;
246   if (size == 0) return;
247 
248   while (size > buffer_size_) {
249     // Data exceeds space in the buffer.  Copy what we can and request a
250     // new buffer.
251     if (buffer_size_ > 0) {
252       memcpy(buffer_, data, buffer_size_);
253       offset_ += buffer_size_;
254       data += buffer_size_;
255       size -= buffer_size_;
256     }
257     void* void_buffer;
258     failed_ = !output_->Next(&void_buffer, &buffer_size_);
259     if (failed_) return;
260     buffer_ = reinterpret_cast<char*>(void_buffer);
261   }
262 
263   // Buffer is big enough to receive the data; copy it.
264   memcpy(buffer_, data, size);
265   buffer_ += size;
266   buffer_size_ -= size;
267   offset_ += size;
268 }
269 
IndentIfAtStart()270 void Printer::IndentIfAtStart() {
271   if (at_start_of_line_) {
272     CopyToBuffer(indent_.data(), indent_.size());
273     at_start_of_line_ = false;
274   }
275 }
276 
FormatInternal(const std::vector<std::string> & args,const std::map<std::string,std::string> & vars,const char * format)277 void Printer::FormatInternal(const std::vector<std::string>& args,
278                              const std::map<std::string, std::string>& vars,
279                              const char* format) {
280   auto save = format;
281   int arg_index = 0;
282   std::vector<AnnotationCollector::Annotation> annotations;
283   while (*format) {
284     char c = *format++;
285     switch (c) {
286       case '$':
287         format = WriteVariable(args, vars, format, &arg_index, &annotations);
288         continue;
289       case '\n':
290         at_start_of_line_ = true;
291         line_start_variables_.clear();
292         break;
293       default:
294         IndentIfAtStart();
295         break;
296     }
297     push_back(c);
298   }
299   if (arg_index != args.size()) {
300     GOOGLE_LOG(FATAL) << " Unused arguments. " << save;
301   }
302   if (!annotations.empty()) {
303     GOOGLE_LOG(FATAL) << " Annotation range is not-closed, expect $}$. " << save;
304   }
305 }
306 
WriteVariable(const std::vector<std::string> & args,const std::map<std::string,std::string> & vars,const char * format,int * arg_index,std::vector<AnnotationCollector::Annotation> * annotations)307 const char* Printer::WriteVariable(
308     const std::vector<std::string>& args,
309     const std::map<std::string, std::string>& vars, const char* format,
310     int* arg_index, std::vector<AnnotationCollector::Annotation>* annotations) {
311   auto start = format;
312   auto end = strchr(format, '$');
313   if (!end) {
314     GOOGLE_LOG(FATAL) << " Unclosed variable name.";
315   }
316   format = end + 1;
317   if (end == start) {
318     // "$$" is an escape for just '$'
319     IndentIfAtStart();
320     push_back('$');
321     return format;
322   }
323   if (*start == '{') {
324     GOOGLE_CHECK(std::isdigit(start[1]));
325     GOOGLE_CHECK_EQ(end - start, 2);
326     int idx = start[1] - '1';
327     if (idx < 0 || idx >= args.size()) {
328       GOOGLE_LOG(FATAL) << "Annotation ${" << idx + 1 << "$ is out of bounds.";
329     }
330     if (idx > *arg_index) {
331       GOOGLE_LOG(FATAL) << "Annotation arg must be in correct order as given. Expected"
332                  << " ${" << (*arg_index) + 1 << "$ got ${" << idx + 1 << "$.";
333     } else if (idx == *arg_index) {
334       (*arg_index)++;
335     }
336     IndentIfAtStart();
337     annotations->push_back({{offset_, 0}, args[idx]});
338     return format;
339   } else if (*start == '}') {
340     GOOGLE_CHECK(annotations);
341     if (annotations->empty()) {
342       GOOGLE_LOG(FATAL) << "Unexpected end of annotation found.";
343     }
344     auto& a = annotations->back();
345     a.first.second = offset_;
346     if (annotation_collector_) annotation_collector_->AddAnnotationNew(a);
347     annotations->pop_back();
348     return format;
349   }
350   auto start_var = start;
351   while (start_var < end && *start_var == ' ') start_var++;
352   if (start_var == end) {
353     GOOGLE_LOG(FATAL) << " Empty variable.";
354   }
355   auto end_var = end;
356   while (start_var < end_var && *(end_var - 1) == ' ') end_var--;
357   std::string var_name{
358       start_var, static_cast<std::string::size_type>(end_var - start_var)};
359   std::string sub;
360   if (std::isdigit(var_name[0])) {
361     GOOGLE_CHECK_EQ(var_name.size(), 1);  // No need for multi-digits
362     int idx = var_name[0] - '1';   // Start counting at 1
363     GOOGLE_CHECK_GE(idx, 0);
364     if (idx >= args.size()) {
365       GOOGLE_LOG(FATAL) << "Argument $" << idx + 1 << "$ is out of bounds.";
366     }
367     if (idx > *arg_index) {
368       GOOGLE_LOG(FATAL) << "Arguments must be used in same order as given. Expected $"
369                  << (*arg_index) + 1 << "$ got $" << idx + 1 << "$.";
370     } else if (idx == *arg_index) {
371       (*arg_index)++;
372     }
373     sub = args[idx];
374   } else {
375     auto it = vars.find(var_name);
376     if (it == vars.end()) {
377       GOOGLE_LOG(FATAL) << " Unknown variable: " << var_name << ".";
378     }
379     sub = it->second;
380   }
381 
382   // By returning here in case of empty we also skip possible spaces inside
383   // the $...$, i.e. "void$ dllexpor$ f();" -> "void f();" in the empty case.
384   if (sub.empty()) return format;
385 
386   // We're going to write something non-empty so we need a possible indent.
387   IndentIfAtStart();
388 
389   // Write the possible spaces in front.
390   CopyToBuffer(start, start_var - start);
391   // Write a non-empty substituted variable.
392   CopyToBuffer(sub.c_str(), sub.size());
393   // Finish off with writing possible trailing spaces.
394   CopyToBuffer(end_var, end - end_var);
395   return format;
396 }
397 
398 }  // namespace io
399 }  // namespace protobuf
400 }  // namespace google
401