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